Single-note music classification by Convolutional Neural Networks

Naphat Sae.
3 min readJul 6, 2022

--

ต่อเนื่องจากการพัฒนาโมเดลแยกเสียงโน๊ตในดนตรีคลาสสิคโดย PyTorch ซึ่งเขียนไว้ใน blog ที่แล้ว [ตามไปอ่านได้ที่นี่เลย] พบว่า Task เดิมมีความซับซ้อนเกินไปโมเดลจึงไม่ได้มีการเรียนรู้ที่น่าพึงพอใจ

แต่ก่อนอื่น ขอขายของ ทุกท่านสามารถเข้าไปลองใช้โมเดลได้ [ตามลิงก์นี้]

หรือเข้าไปดู code ได้ตาม [ลิงก์นี้เลย]

Task ใหม่ในรอบนี้จึงปรับความยากของโจทย์ ได้แก่

  1. เปลี่ยนเป็นการ predict single note ของ MIDI file ที่มีระยะเวลาแค่ 1 วินาที เพื่อลดขนาดและความซับซ้อนของข้อมูล
  2. ทำการจำแนกแค่ 12 โน๊ตหลักจากเดิมที่จะจำแนกโน๊ตทั้ง 88 ตัว เช่น โมเดลจะมอง A1, A2, A3 ว่าเป็น A เท่านั้น

Dataset

ข้อมูลที่นำมาเทรนจะเป็นไฟล์เสียงที่ generate มาจาก MIDI flies ประกอบไปด้วยเสียงโน๊ตตั้งแต่ C0-B9 ที่เล่นโดยเครื่องดนตรีต่างชนิดกัน มีไฟล์เสียงทั้งสิ้น 26643 ไฟล

หลังจากรับค่า sample และ sample rate ของไฟล์เสียงด้วย torchaudio เราจะทำ data pre-processed เพื่อที่โมเดลจะได้เรียนรู้ข้อมูลที่อยู่ในรูปแบบเดียวกัน ซึ่งเราจะ pre-processed 3 แบบ เริ่มจากการ resample เพื่อปรับเสียงให้มีความละเอียดที่ 16000 Hz

ตามด้วยการ mix down ในกรณีที่ไฟล์บางไฟล์มีการแยก channal ซ้าย/ขวา เราจะรวมเสียงให้มาอยู่ใน monochannal โมเดลจะได้รับค่าในรูปแบบเดียวกัน

ในส่วนสุดท้าย เราได้นำเสียงที่ผ่านการ resample และ mix down แล้ว เข้าไปใน Mel Spectrogram ซึ่งเป็น Short-Time Fourier transform ที่เน้นแค่คลื่นเสียงต่ำอันเสียงออกมาเป็นค่าที่ใกล้เคียงกับค่าที่มนุษย์ได้ยิน

ที่จริงเรายังสามารถ take log ลงไปเพื่อให้ range ของข้อมูลไม่กว้างและกระโดดมาถึง 20000 แต่เนื่องจากการ predict note เราดูแค่ frequence เสียงที่โดดขึ้นมา จึงไม่ได้มีผลต่อการทำนายมากนัก กลับกัน หากเป็น speech ก็จะทำให้เกิดปัญหาตามมาได้

ตัวอย่าง heatmap ของ data ที่ผ่านการ pre-processed มาแล้ว

ซึ่งเราจะให้โมเดลรับข้อมูล tensor ของ data ที่มี size เป็น (128,50) แล้วส่งเข้าโมเดลก่อนจะ predict ค่ามาเป็น index ของ classmap ตั้งแต่ 0–12

classmap=[“C”,”C#”,”D”,”D#”,”E”,”F”,”F#”,”G”,”G#”,”A”,”A#”,”B”]

Model

ในการเทรนโมเดล เราได้ใช้ตัว Dataloader เข้ามาช่วนนำไฟล์ข้อมูลและ label ส่งลงไปใน model หลังจากนั้นก็นำผลที่ model ทำนายออกมาไปคำนวน loss function ด้วย CrossEntropyLoss และวัด Accuracy ด้วย Accuracy_score ของ sklearn

ที่เลือกใช้ CrossEntropyLoss เพราะเราจะให้โมเดลมีการทายค่าโน๊ตออกมาความเป็นไปได้ที่จะเป็นโน๊ตนั้น แล้วเลือกเอา Index ที่มีค่ามากสุดมาตอบ จึงเหมาะกับ CrossEntropyLoss ที่คำนวน Error จากความน่าจะเป็นที่ต่างกันของ y และ yhat อันมี class หลายตัว

ซึ่งภายใน model เราได้เลือกใช้ตัว Convolution block เพื่อขยาย channal ออกมาจาก 128 -> 256 -> 512 ตามลำดับ โดยแต่ละ Block ประกอบด้วย Convolution1D ที่มากรองหาค่าที่สำคัญ นอกจากนี้เรายังใช้ BatchNorm1D มา normalization ข้อมูลก่อนออกจาก layer ก่อนจะใช้ Activation ReLU เพื่อจะกำจัดค่าที่น้อยกว่า 0 ออกเป็นตอนสุดท้ายเพราะความถี่เสียงมีค่าติดลบไม่ได้

สุดท้ายเราจะ flatten data แล้วเข้า linear เพื่อแปลงค่าให้ออกมาเป็นความน่าจะเป็นของโน๊ต 12 ตัว และเลือกช่องที่โมเดลให้ค่ามากที่สุดเป็นคำตอบที่โมเดลตอบออกมา

Result

เราได้ทำการเทรนไปทั้ง 20 Epochs ก็ได้ผล loss และ acucuracy ดังกราฟ

ผล loss และ Accuracy ของแต่ละ epoch

พบว่าค่า loss มีการลดลงและเริ่มคงที่หลังจากเทรนไปซักระยะ ในขณะที่ acucuracy ก็เพิ่มขึ้นเรื่องๆและคงที่เช่นกัน เป็นสัญญาณว่าโมเดลของเราเริ่มมาถูกทางแล้ว ซึ่งเราได้เลือกค่า loss และ accuracy จาก epoch ตัวอย่างออกมาเขียนลงตาราง ได้ดังนี้

เราจึงเลือกโมเดลที่เทรนไปทั้งหมด 20 epoch มา deploy เป็นเว็บแอพที่ให้คนมาอัพไฟล์เสียงและ predict โน๊ต

Deploy

หลังจากที่โมเดลเทรนเรียบร้อยจนผลออกมาเป็นที่น่าพึงพอใจ เราก็ได้นำโมเดลมาใช้งานจริงผ่านตัว Streamlit แบบ local ซึ่งได้ทำหน้าตาของเว็บออกมาประมาณนี้เลย

ภาพการใช้งานหน้าเว็บ

จะเห็นได้ว่าผู้ใช้สามารถเลือกอัพโหลดไฟล์ในเครื่องแล้วกด Predict ให้โมเดลทายโน๊ตออกมาได้ทันที! ซึ่งเว็บมีการทำงานเบื้องหลังใน app.py ดังนี้

ความท้าทายอยู่ที่โมเดลของเราอ่านไฟล์เสียงที่ได้จาก st.file_uploader() โดยตรงไม่ได้ จึงต้องสร้าง tmp file ให้เก็บอยู่ในเครื่องก่อน แล้วค่อยใช้ OS ไปลบเอาหลังจากประมวลผลเสร็จแล้ว

Lesson learn

ในการทดลองทำรอบสอง รู้สึกว่าตัวเองได้เรียนรู้คอนเสปหลายอย่างมากขึ้น เช่น

  1. การทำ data pre-processed โดนลงรายละเอียดทั้งการ resample และ ทำการ mix down ทั้งสอง channal ซึ่งทำให้ data ที่เข้าไปในโมเดลเรียนรู้จากข้อมูลที่มีรูปแบบเดียวกัน
  2. ได้ลองใช้ BatchNorm เพื่อเป็นตัวช่วยในการเรียนรู้ของโมเดลให้เร็วมากขึ้นและป้องกันการ overfitting

future works

  • อาจจะปรับ sample rate ให้พอดีกับ frequency ของเสียงที่มีความถี่สูงสุด เนื่องจาก frequency สูงสุดตอนนี้คือ G#9 (12543 Hz) จึงควรใช้ sample rate เป็น 25000 Hz จะมีความละเอียดมากกว่า
  • เพิ่มการ take log ใน mel spectrogram เพื่อให้ range ของข้อมูลไม่ต่างกันมากเกินไป
  • อาจจะลองเทรนโมเดลใน epochs ที่มากขึ้น เพื่อดูว่า model จะสามารถพัฒนาต่อไปจนกว่าจะเข้าสู่ overfitting ตอนไหน
  • ทดลองเทรนกับ Data ที่มีความซับซ้อนมากขึ้น เช่น มีความยาวของเสียงมากขึ้นหรือเป็นไฟล์เสียงที่อัดจากดนตรีจริงๆ
  • อาจจะลองใช้รูปแบบโมเดลนี้กับการ classification เสียงประเภทอื่นๆ เช่น แยกประเภทดนตรี

--

--

No responses yet