How to know percent your sound?

เคยสงสัยกันบ้างไหมครับว่าตัวเองออกสำเนียงภาษาได้ดีมากน้อยแค่ไหนตอนฝึกพูดภาษา ถ้าคุณสงสัยแปลว่าเราเป็นพวกเดียวกัน

hoggyna
5 min readJun 13, 2023

สวัสดีครับ! โปรเจกต์นี้เป็นส่วนหนึ่งของโครงการ AI Builder 2023 เป็นการเรียนรู้ครั้งแรกในเรื่องของ AI เลยครับโปรเจกต์นี้อาจจะมีข้อผิดพลาดบ้างก็ขออภัยด้วยครับ งั้นเรามาเริ่มกันเลยดีกว่าครับ

Table Of Content!

· จุดเริ่มต้นของโปรเจกต์!
· ทำไมต้องโปรเจกนี้? ( Problem statement )
· Metrics and baselines
· ขั้นตอนการทำ
· Create Data(สร้างข้อมูล)
· Data Cleaning ( ทำความสะอาดข้อมูล )
· หลักการทำงานที่predict data
· Test non native speaker ( ทดสอบเสียงคนที่ไม่ใช่เจ้าของภาษา )
· Test native speaker ( ทดสอบเสียงคนที่เป็นเจ้าของภาษา )
· สรุปจากการ Test Non native speaker และ Native speaker
· Finetune model
· Deployment
· Future!🌈🌈
· Special Thanks💫💫
· Link💫💫
· Credit💫💫

จุดเริ่มต้นของโปรเจกต์!

ผมเริ่มมาจากการที่ผมนั่งคิดนอนคิดว่า จะทำโปรเจกต์อะไรดี เผอิญว่าช่วงนั้นผมมีโอกาสได้ไปเล่นกับน้อง ( น้องผมอายุเพียง2ขวบ ) แล้วน้องก็พูดไม่ซัด! เอ้ย ชัด! หลังจากนั้นผมก็มาคิดต่อยอดเกี่ยวกับการพูดที่ใกล้ๆกันมีอะไรบ้าง แล้วช่วงนั้นผมสังเกตเห็นว่าเวลาที่ผมเรียนภาษาจีนพอถึง การพูดที่เป็นประโยคสำเนียงของผมมันแปลกๆกับเจ้าของภาษาผมเลยลองไปฟังคำๆนึงของเจ้าของภาษา ทำให้ผมรู้ว่าสำเนียงของเขากับผมต่างกันอย่างมาก จึงเป็นโปรเจกต์นี้ขึ้นมาครับ

ทำไมต้องโปรเจกนี้? ( Problem statement )

เพราะผมคิดว่าทุกคนบนโลกนี้มีปัญหาเกี่ยวกับภาษา เป็นอย่างมากบางคนฝึกพูดภาษาแทบตายแต่ไม่รู้ว่าสำเนียงที่พูดออกมามีความเหมือนหรือคล้ายมากน้อยแค่ไหน เพราะฉะนั้นก็เลยเกิดเป็นโปรเจกนี้ขึ้นมาครับ

Metrics and baselines

ผมใช้การตรวจสอบความแม่นยำโดยใช้ recall score ,precission score และ f1 score ในการทดสอบโมเดลและทดสอบdata ทั้งหมด และ มี baselines คือ Dynamic Time Warping (DTW)

ขั้นตอนการทำ

ในส่วนแรกอยากให้รู้เกี่ยวกับ Dynamic Time Warping ( DTW ) เป็นเทคนิคทางคณิตศาสตร์ที่ใช้ในการวัดความคล้ายคลึงระหว่างเสียงที่มีความยาวและความเกี่ยวข้องกันแตกต่างไป หลักการทำงานของ DTW คือการหาความยาวเสียงที่เหมาะสมที่สุดในการจับคู่ซึ่งกันและกัน

หาโมเดลมาใช้งานและทดลองการทำงานของโมเดลว่าเป็นยังไงบ้าง จนมาเจอโมเดล https://huggingface.co/jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh-cn ตัวนี้มีโค๊ดอธิบายและหลักการให้ศึกษา ผมเลยหยิบโมเดลตัวนี้มาทดสอบโมเดลว่าเป็นยังไง

Create Data(สร้างข้อมูล)

ในส่วนต่อมาเราต้องหาข้อมูลเพื่อมาทดสอบโมเดลว่ามีการ predict ได้ดีมากน้อยแค่ไหน โดยแบ่งข้อมูลออกมาเป็น 2 ตัว คือ Non native speaker และ
Native speaker
ในส่วนของข้อมูลเราโหลดไฟล์ txt เข้ามาอ่านไฟล์ด้วย pandas และ ใช้ฟั่งชั่น concat เชื่อมทุกไฟล์ txt เข้าด้วยกัน

df = pd.read_csv('/content/latic/SCRIPT/Testing_Text_Script/0010.TXT', sep='\t', header=None, names=['id', 'sentence'], dtype=str)
df2 = pd.read_csv('/content/latic/SCRIPT/Testing_Text_Script/0011.TXT', sep='\t', header=None, names=['id', 'sentence'], dtype=str)
df3 = pd.read_csv('/content/latic/SCRIPT/Testing_Text_Script/0012.TXT', sep='\t', header=None, names=['id', 'sentence'], dtype=str)
df4 = pd.read_csv('/content/latic/SCRIPT/Testing_Text_Script/0013.TXT', sep='\t', header=None, names=['id', 'sentence'], dtype=str)
df = pd.concat([df,df2,df3,df4],ignore_index=True)
ผลลัพธ์จากโปรแกรมข้างต้น

จากภาพด้านบนจะพบว่า id ยังไม่เชื่อมกับ path ของ google colab เราจึงต้องเชื่อม id ให้เข้ากับ path ของ google colab

import os
import pandas as pd

# Define the directory containing the .wav files and the DataFrame
wav_dir = '/content/wav'

# Create a dictionary to store the file paths
file_dict = {}

# Loop through the .wav files in the directory
for filename in os.listdir(wav_dir):
if filename.endswith('.WAV'):
# Extract the id from the filename (assuming the filename is in the format 'id.wav')
id = filename.split('.')[0]
# Store the file path in the dictionary
file_dict[id] = os.path.join(wav_dir, filename)
# print(file_dict)
# Add a new column to the DataFrame to store the file paths
df['path'] = df['id'].map(file_dict)

# Print the resulting DataFrame
df
ผลลัพธ์จากโปรแกรมข้างต้น

จากการที่เราข้อมูลเสร็จเป็นที่เรียบร้อยเราจะต้องแบ่งข้อมูลออกเป็น 3 ชนิดมีดังนี้
1. test data
2. validation data
3. train data
และเราต้องเพิ่มในส่วนของ labels ( labels จะมี2ค่าคือ 0 และ 1 โดยที่ 1 คือเสียงพูดกับประโยคตรงกัน และ 0 คือเสียงพูดกับประโยคไม่ตรงกัน)

เราจะทำการ split training set 80% test set 10% validation set 10%

import numpy as np
from sklearn.model_selection import train_test_split
train_df, test_df = train_test_split( df, test_size=0.8, random_state=42)
val_df, test_df = train_test_split( test_df, test_size=0.2, random_state=42)
ตัวอย่างข้อมูล

Data Cleaning ( ทำความสะอาดข้อมูล )

เราโหลดข้อมูลด้วย pandas พอเราจัดข้อมูลอยู่ในรูป csv ไว้เรียบร้อยแล้วในประโยคของไฟล์ csv จะมีพวกอักษรพิเศษ เราต้องกำจัดอักษรพิเศษทิ้งให้หมด โดยใช้ re ในการลบ ( Clean data ทั้ง Non native speaker และ Native Speaker )

import re
df["sentence"] = df["sentence"].apply(lambda x: re.sub("[a-zA-Z\<\>()“”—— !?:;./ ! 《》:”“!,。]", "", x))
หลัง Clean Data

ลองสังเกตข้อมูลจากรูปของ Create Data จะมีพวกอักษรพิเศษแต่พอเรา
Clean Data ไปแล้วจะไม่มีอักษรพิเศษ

หลักการทำงานที่predict data

อย่างแรกคือการอ่านไฟล์เสียงโดยใช้ librosa

import librosa
def speech_file_to_array_fn(path):

speech_array, sampling_rate = librosa.load(path, sr=16_000)
return speech_array

ต่อมาคือ คลาส Realwav จะทำการโหลดโมเดล Wav2Vec2 และโปรเซสเซอร์ (processor) ที่ใช้ในการประมวลผลข้อมูลเสียง โดยในที่นี้โมเดลและโปรเซสเซอร์ที่ใช้มาจาก "jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh-cn"

เมื่องคลาส Realwav ที่ใช้ฟังชั่น sound_and_sentence เพื่อประเมินคะแนนความถูกต้องของประโยคที่ออกเสียงด้วยเสียงที่พูดออกมา ( sound ) และประโยค
( sentence ) โดยเมธอดนี้จะทำการแปลงเสียงเป็นข้อความโดยใช้โมเดล Wav2Vec2 และ คำนวณคะแนนความถูกต้องของประโยคที่ได้ ซึ่งเมธอดจะคืนค่าคะแนนความถูกต้องของประโยค (sentence score) และคะแนนจะแปลงเป็นค่าไรบารี (0 หรือ 1) ที่บอกว่าประโยคมีความถูกต้องหรือไม่ (ถ้า predict ออกมาได้มากกว่า0.5 จะได้ค่า
ไรบารีเป็น 1 )

class Realwav:
def __init__(self):
MODEL_ID = "jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh-cn"
self.processor = Wav2Vec2Processor.from_pretrained(MODEL_ID)
self.model = Wav2Vec2ForCTC.from_pretrained(MODEL_ID)

def sound_and_sentence(self,sound,sentence):
inputs = self.processor(sound, sampling_rate=16_000, return_tensors="pt", padding=True)
with torch.no_grad():
logits = self.model(inputs.input_values, attention_mask=inputs.attention_mask).logits

probs = torch.softmax(logits, dim=-1)
ref_ids = self.processor(text=sentence)["input_ids"]
scores = []
ref_count = 0
pred_ids = torch.argmax(logits[0], dim=-1)
for seq_idx in range(pred_ids.shape[0]):
if pred_ids[seq_idx] != 0:
print(f"position of the word {sentence[ref_count]}: {seq_idx}")
ref_id = ref_ids[ref_count]
conf_score = probs[0, seq_idx, ref_id].tolist()
scores.append(conf_score)
print(conf_score)
ref_count += 1
if ref_count >= len(ref_ids):
break
sentence_score = np.mean(scores)
return sentence_score, int(sentence_score > 0.5)

Test non native speaker ( ทดสอบเสียงคนที่ไม่ใช่เจ้าของภาษา )

เราจะสร้าง list ที่มีชื่อว่า predicts เอามาเก็บค่าที่โมเดล predict ออกมาได้

predicts = []
for i in range(len(a)):
sample = a.iloc[i]
id = sample["id"]
sentence = sample["sentence"]
pathwav = pathtowav(id)
filesound = speech_file_to_array_fn(pathwav)
output = rw.sound_and_sentence(filesound,sentence)
predicts.append(output[1])

โดยผลที่ออกมาเราจะมีคะแนนทั้งหมด3แบบคือ recall score , precission score
และ f1 score

ภาพด้านบนเป็นผลคะแนนที่มาจากnonNatvie
คะแนนที่ออกมาของ non native

Test native speaker ( ทดสอบเสียงคนที่เป็นเจ้าของภาษา )

เราจะสร้าง list ที่มีชื่อว่า predicts เอามาเก็บค่าที่โมเดลpredictออกมาได้

predicts = []
for i in range(len(dfa)):
sample = dfa.iloc[i]
id = sample["UtteranceID"]
sentence = sample["Transcription"]
pathwav = pathtowav(id)
filesound = speech_file_to_array_fn(pathwav)
output = rw.sound_and_sentence(filesound,sentence)
predicts.append(output)

โดยผลที่ออกมาเราจะมีคะแนนทั้งหมด3แบบคือ recall score , precission score
และ f1 score

คะแนนที่ออกมาของ native speaker

สรุปจากการ Test Non native speaker และ Native speaker

ถ้าเราดูที่คะแนนของ Native speaker จะมีคะแนนที่มากกว่า Non native speaker เป็นอย่างมาก เลยสรุปได้ดังนี้ว่าโมเดล jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh-cn predictคนที่เป็น Non native speaker ได้ไม่ดี เราเลยต้องไป Finetune model โดยใช้ข้อมูลของคน Non native speaker เพิ่ม

สรุปคะแนนระหว่าง Non native speaker กับ Native speaker

Finetune model

Import library ที่ต้องใช้มาให้หมด

from transformers import Wav2Vec2CTCTokenizer
from transformers import Wav2Vec2FeatureExtractor
from transformers import Wav2Vec2Processor
from transformers import Wav2Vec2ForCTC
from transformers import TrainingArguments
from transformers import Trainer

เตรียม train data กับ validation data ที่อยู่ใน csv ให้พร้อม และ ไฟล์เสียงให้อยู่ใน google colab ให้พร้อม

ให้google colab เชื่อมกับ google drive

from google.colab import drive
drive.mount('/content/drive')

ใช้สำหรับการดึงข้อมูลของเซ็กชันล่าสุดจากไดเรกทอรีของโมเดล (model checkpoint) ที่ระบุให้มาในรูปแบบของพาธ (path) ฟังก์ชัน get_last_checkpoint ที่นำเข้าจาก transformers.trainer_utils ใช้ในการดึงเซ็กชันล่าสุดจากไดเรกทอรีโมเดลที่กำหนดไว้

from transformers.trainer_utils import get_last_checkpoint
checkpoint = get_last_checkpoint("/content/drive/MyDrive/model_checkpoints/checkpoint-1600")

เตรียมข้อมูลในการ Finetune model

from datasets import load_dataset, load_metric
common_voice_train = load_dataset("csv", data_files=['/content/train_df.csv'], split='train' )
common_voice_test = load_dataset("csv", data_files=['/content/val_df.csv'], split='train' )

ตั่งค่า model เพื่อที่จะ Finetune model

model = Wav2Vec2ForCTC.from_pretrained(
"facebook/wav2vec2-large-xlsr-53",
attention_dropout=0.1,
hidden_dropout=0.1,
feat_proj_dropout=0.0,
mask_time_prob=0.05,
layerdrop=0.1,
gradient_checkpointing=True,
ctc_loss_reduction="mean",
pad_token_id=processor.tokenizer.pad_token_id,
vocab_size=len(processor.tokenizer)
)

ตั้งค่า batch_size ไว้ที่8 และเทรนทั้งหมด 100 epoch

training_args = TrainingArguments(
output_dir=output_models_dir,
group_by_length=True,
per_device_train_batch_size=8,
gradient_accumulation_steps=2,
evaluation_strategy="steps",
num_train_epochs=100,
fp16=True,
save_steps=400,
eval_steps=400,
logging_steps=400,
learning_rate=3e-4,
warmup_steps=500,
save_total_limit=2,
)

trainer = Trainer(
model=model,
data_collator=data_collator,
args=training_args,
compute_metrics=compute_metrics,
train_dataset=common_voice_train,
eval_dataset=common_voice_test,
tokenizer=processor.feature_extractor,
)

พร้อมที่จะ Finetune model

trainer.train(resume_from_checkpoint=checkpoint)

ผลที่finetune ออกมา

เอาโมเดลที่ไปfinetune ไปหาค่า recall score ,precision score
และf1 score ได้ผลลัพธ์ดังนี้

คะแนนจากการ Finetune model

การ Finetune model ในครั้งนี้เกิดข้อผิดพลาดขึ้นผลคะแนนที่นำมาเทรนออกมาแย่มากๆเลยต้องกลับไปแก้ข้อมูลและทำการ Finetune model ใหม่

Deployment

ผม deploy ผ่าน webapp ของ streamlit เป็นที่เรียบร้อยแล้วสามารถเข้าไปเล่นหรือทดลองทดสอบเสียงตัวเองได้แล้ว https://hoggyna-ai-builder-deploymentapp-bue02s.streamlit.app/

ขั้นตอนแรก:เลือกคำที่อยากจะลองทดสอบคำภาษาจีน
ขั้นตอนที่สอง : คลิ๊กที่รูปไมโครโฟน พูดคำภาษาจีนที่เลือกเอาไว้(อัดเสียงเป็นเวลา2วินาที)

ใน steamlit จะมีเสียงตัวอย่างให้ฟัง และสามารถ ลองPredictเล่นได้

Future!🌈🌈

  • ผมจะเรียนรู้ ฝึกฝน และพยายาม ในเรื่องAI ต่อไปเรื่อยๆ

Special Thanks💫💫

  • https://www.facebook.com/aibuildersx/
  • ขอขอบคุณเพื่อนๆและพี่ๆ Mentor / TA AI Builders 2023 คอยช่วยเหลือและดูแลในการเรียนรู้ของผมครั้งนี้
  • ผมสนุกมากๆกับ10สัปดาห์ที่ทำai กับ AI Builder มันมากครับรีเสิร์ชทุกวันนั่งอ่านโค้ดแทบทุกวัน หยุมหัวตัวเองบ่อยมาก!!!!

Link💫💫

Credit💫💫

และอาจจะเป็นการแนะนำตัวที่ช้าไปหน่อย สวัสดีครับ ผมชื่อฮ๊อกกี้ กำลังศึกษาอยู่ที่โรงเรียนสาธิต มหาวิทยาลัยศรีนครินทรวิโรฒ ประสานมิตร (ฝ่ายมัธยม) ครับ
อยู่ชั้นมัธยมปีที่6

ฝากติดตาม blog ตอนต่อไปของผมด้วยนะครับ!

--

--

No responses yet