สร้าง Model แปลภาษาไทย — อังกฤษ

Tarit Witworrasaul
7 min readJun 10, 2023

--

Idea ในการทำ Project นี้มาจากที่เห็นคนเริ่มใช้ ChatGPT ในการแปลภาษา รวมถึงใช้ในการแก้ประโยคต่าง ๆ ซึ่งมักจะได้การแปลที่มีคุณภาพกว่า Google Translate ดังนั้นเลยอยากลองพัฒนา Model แปลภาษาขึ้นมา และการมี Model ที่ Opensource ก็จะทำให้คนอื่นสามารถนำไปใช้งานและพัฒนาต่อยอดไปใน Application อื่น ๆ ได้

ซึ่ง model แปลภาษา th-en ตอนนี้ที่เป็น opensource ที่ดีที่สุดคือ SCB_1M-MT_OPUS+TBASE จาก airesearch ซึ่งได้ BLEU score บน IWSLT 2015 อยู่ที่ 28.3

ดังนั้นเป้าหมายของผมก็คือทำ model ที่ดีกว่าตัวที่ opensource อยู่ปัจจุบัน (ยังไม่หวังชนะพวก API เสียเงิน พวกนั้นเขามีเงินจ้างทีมวิจัย+ซื้อการ์ดจอแรง ๆ อาจจะเป็นเป้าหมายที่สูงไปหน่อย) พอมาพิจารณาความเป็นไปได้ก็คิดว่าด้วย Pretrained Model ในปัจจุบัน ถ้าเอามา finetune ก็น่าจะทำ BLEU score ที่ดีกว่า Model opensource เดิมได้

Metrics

จาก Intro ด้านบนก็น่าจะเห็นกันไปแล้วว่า Model แปลภาษาจะถูกวัดความสามารถโดยใช้ BLEU score (Bilingual Evaluation Understudy) ซึ่งเป็นการคำนวนว่าการแปลของ Model นั้นใกล้เคียงกับประโยคที่ถูกต้องมากแค่ไหน โดยเทียบคำที่เหมือนกันที่ n-gram = 1–4 และนำมาเฉลี่ยกัน

โดยสูตรนี้จะต่างจาก Precision ปกติตรง Clip(Num n-grams matches) โดยจำนวนคำที่ match จะต้องไม่เกินจำนวนคำที่อยู่ใน Reference

ตัวอย่างเช่น
Reference:
I drive a car
model predict ออกมาว่า:
car car car car
ถ้าใช้สูตร precision ปกติจะได้ = 4/4 = 1 หรือว่าถูกทั้งหมด
แต่ถ้าคำนวนตาม BLEU score จะได้
1/4 เพราะ Reference มีคำว่า car แค่คำเดียว

Data Preparation

ตัว Dataset Machine Translation ภาษาไทย-อังกฤษมีหลัก ๆ อยู่ 2 ตัวก็คือ scb-mt-en-th-2020 และ OPUS ซึ่งตัว OPUS เป็น Dataset ที่มีอยู่เกือบทุกภาษา! โดยเราจะไปโหลดจากเว็บ OPUS โดยตรงก็ได้ หรือว่าถ้าอยากได้แบบง่าย ๆ ก็โหลดจาก Vistec ที่ปล่อยไว้เป็น CSV ให้เรียบร้อย

ก่อนจะเอา Data ไป Train model เราก็ต้อง Clean Data ซะก่อน โดยเฉพาะกับภาษาไทยที่บางครั้งอาจจะเห็นว่าเขียนเหมือนกัน แต่จริง ๆ แล้วเป็นตัวอักษรคนละตัว แต่เวลาเอาไป tokenize และแปลงเป็น input_ids จะออกมาเป็นคนละตัวกัน

ใช้ขั้นตอนดังนี้ (Code, Based on thai2nmt)

  1. ตัด Whitespace หน้า/หลังประโยค
  2. HTML unescape แปลง HTML Entities กลับมาเป็น Charactor ปกติ (ex: " —> “)
  3. Normalize Unicode เนื่องจากตัวอักษรที่เป็น Unicode อาจมีหลายรูปแบบแต่หมายถึงตัวอักษร / สัญลักษณ์ตัวเดียวกัน (ex: ℌ, H) เลยต้อง Normalize มาให้เป็นตัวเดียวกัน
  4. Normalize Thai Text Ex. สระ แ- สามารถพิมพ์ได้ด้วย สระแอ ตัวเดียว หรือ สระเอ 2 ตัว (เ เ -) หรือ คำว่า น้ำ ที่เขียนได้ทั้ง น้ำ (น, ้ , ำ) , นำ้ (น, ำ, ้) หรือ นํ้า (น, ํ , ้ , า)

หลังจาก Clean Text แล้วก็จะมีบางคู่ประโยคที่ต้องตัดออก โดย

  1. คู่ประโยคที่มี String เปล่า (“”)
  2. คู่ประโยคที่ภาษาไทยไม่มีตัวอักษรภาษาไทย (มีแต่ตัวอักษรภาษาอังกฤษ) อันนี้พบว่าบางคู่ประโยค โดยเฉพาะ opus-kde4 จะมี Text ที่เป็นภาษาอังกฤษทั้งคู่แต่เขียนไม่เหมือนกันอยู่
Text เป็นอังกฤษทั้งคู่ แต่เขียนไม่เหมือนกัน

3. Text ที่มี Unicode Control codes บางตัว จะเป็นตัวอักษรที่อ่านไม่ออก (ส่วนมากเจอใน opus-opensubtitles)

Text ภาษาไทยอ่านไม่ออก
บางคู่ประโยคมีมี Text แค่ภาษาเดียว

Exploratory Data Analysis

เนื่องจากว่าตอน Train Model Machine Translation เราจะต้องกำหนด Sequence Length หรือความยาวของประโยคที่จะเอาเข้าไป Train ซึ่งในแต่ละ Batch ทุกประโยคจะต้องมีความยาวเท่ากัน

ดังนั้นเราจะมาดู Distribution ของความยาวประโยคเมื่อ tokenize ออกมาแล้วกัน เพื่อดูว่าส่วนใหญ่ประโยคยาวเท่าไร

Note: ข้อมูลตรงนี้มาจากการใช้ NLLB Tokenizer ตัดประโยคนะครับ

Distribution ของความยาวประโยคในแต่ละ File
Distribution ของความยาวประโยคทั้งหมด (SCB+OPUS)

ซึ่งก็จะเห็นว่าประโยคส่วนมากจะยาวไม่เกิน 50 Tokens ดังนั้นผมเลยตั้ง Sequence Length ให้ไม่เกิน 64

Model

สำหรับ Tools ที่ใช้ Train Model ที่เป็น architecture Transformer ก็คงหนีไม่พ้น Huggingface transformers ซึ่งถ้าใครอยากลอง Reproduce ผลการทดลองนี้ก็ดู Code ทั้งหมดได้บน https://github.com/wtarit/th-en-machine-translation

mT5

Model ตัวแรกที่ลองคือ mT5 จาก Google ซึ่งใน Dataset มาการใช้ภาษาไทย Train อยู่ 1.14% (อาจจะดูน้อย แต่ mT5 นั้น train จาก dataset 107 ภาษา) ซึ่ง Google ก็ได้ Release mT5 ออกมา 5 ขนาดได้แก่ mT5-Small (300M parameters), mT5-Base (580M parameters), mT5-Large (1.2B parameters), mT5-XL (3.7B parameters), mT5-XXL (13B parameters) เลยลองนำ mT5-Small มา finetune บน dataset SCB ดู

Distribution ของภาษาที่ใช้ Pretrain mT5 (https://arxiv.org/pdf/2010.11934.pdf)

โดยก่อนจะ finetune นั้นผมจะ Tokenize dataset แล้ว Save เก็บไว้เพื่อต้องมา Run ซ้ำหรือ Train ไปแล้วเกิด Error ต้อง Train ใหม่จะได้ไม่ต้องเสียเวลา Tokenize data ใหม่

หลังจากเริ่ม Train ก็มีปัญหาเกิดขึ้นทันที นั้นก็คือ loss เป็น 0.0

{'loss': 0.0, 'learning_rate': 4.973326944577544e-05, 'epoch': 0.04}

ซึ่งเอาไป search ดูก็พบว่าเป็นเพราะใช้ fp16 train ซึ่งมีปัญหาที่ Implementation ของ mT5 ใน Huggingface (ที่ Huggingface บอกว่าแก้แล้วแต่เห็นหลาย ๆ คนรวมถึงผมก็ยังเป็นอยู่) วิธีแก้ก็คือไปใช้ bf16 ที่มี range ของตัวเลขกว้างกว่า (แต่จะใช้ได้เฉพาะบน GPU Nvidia ที่เป็น Ampere หรือใหม่กว่า) หรือใช้ fp32 ปกติ

พอเปลี่ยนไปใช้ bf16 แทนแล้วก็ finetune ไปได้ 5 epochs ก็พบว่า BLEU score นั้นต่ำกว่าที่คิด ใน Validation Set อยู่ที่ 13.15 และพอเอาไปลองกับ Test set อยู่ที่ 12.14 ซึ่งตรงนี้ดูจาก Training และ Evaluation Loss แล้วก็คิดว่าถ้า Train ต่อ Model ก็น่าจะดีขึ้นอีก

Training และ Evaluation Loss ช่วง epochs 1–5

No Language Left Behind (NLLB)

เนื่องจาก mT5 ออกมาตั้งแต่ปี 2020 ในโลกของวงการ NLP ปัจจุบันที่อะไรออกมาได้อาทิตย์เดียวก็ถือว่าเก่าแล้ว ผมเลยคิดว่ามันน่าจะต้องมี Pretrained Model อะไรที่ดีกว่านี้สิ เลยมาเจอ NLLB จาก Meta ที่ออกมาเมื่อปี 2022 เป็น Model สำหรับแปลภาษาโดยเฉพาะที่แปลได้ถึง 200 ภาษารวมถึงภาษาไทยด้วย! ลองใช้ Model ตัวเล็กสุด (600M Parameter) ก็พบว่าได้ BLEU score ถึง 24.03 โดยไม่ต้องทำอะไรเลย ก็เลยคิดว่านี่น่าจะเป็นจุดเริ่มต้นที่ดีกว่า mT5 ในการเอาไป finetune

การ finetune ก็เหมือนกับ mT5 เลยคือ Tokenize dataset เก็บไว้ก่อนแล้วเอาไป Train โดยตอนแรกใช้แค่ SCB dataset ก็พบว่า BLEU score กลับลดลงเหลือ 21.7

ซึ่งจากที่คุยกับ mentor ก็คิดว่าเป็นเพราะ SCB dataset มีข้อมูลที่เป็นภาษาพูด/บทสนทนาอยู่น้อยที่ 300,280 ประโยค หรือประมาณ 30% ของ Dataset ทั้งหมด (Taskmaster-1, NUS SMS, Mozilla Common Voice) พอทดสอบกับ iwslt_2015 ที่เป็นภาษาพูด Model เลยทำงานได้ไม่ดี

ก็เลยลองเพิ่ม dataset เป็น scb+opus พอ Train ไปได้ 3 epochs BLEU score ดีขึ้นเป็น 27.37 ดูแล้วมาถูกทาง และดูจาก Training/Evaluation Loss ก็คิดว่า Train ต่อได้อีก

NLLB Training/Evaluation Loss

แต่ปัญหาคือมัน Train นานมาก ๆๆ ทั้งหมดนี่ใช้เวลาไป 78 ชม. (Train บน RTX3080 16GB Laptop)

LoRA

จากที่เห็นคน finetune LLaMA และ LLM อื่น ๆ ด้วย LoRA แล้วเลยคิดว่าน่าจะเอามาลองใช้ดู

วิธีการของทำงาน LoRA นั้นจะ freeze weight ทั้งหมดของ model และ inject parameter ใหม่เข้าไปใน Transformer layer และ Update แค่ Parameter นั้น

Credit: https://lightning.ai/pages/community/tutorial/lora-llm/

อีกเทคนิคหนึ่งคือการแยก Weight Matrix เป็น 2 ตัว ที่เมื่อคูณกันแล้วจะได้ ΔW (ΔW = WA x WB) เนื่องจากใน Paper นี้พบว่า Weight ของ Model นั้นมี Rank ต่ำ ทำให้ Decompose ออกมาเป็น Matrix ขนาดเล็กได้

Credit: https://youtu.be/dA-NhCtrrVE

ส่วนค่า r นั้นคือ hyperparameter ที่ต้องกำหนด โดยถ้า r มาก Trainable Parameter ก็จะมากขึ้น

และข้อดีของ LoRA อีกอย่างคือเราสามารถ Save แค่ Weight ที่ inject ได้เข้าไปและเอามารวมกับ Base Model ทีหลังได้ ทำให้ได้ Weight ที่มีขนาดเล็ก

โดย Library ที่ผมจะใช้ในการ finetune ด้วย LoRA ก็คือ Huggingface PEFT แต่มันไม่ support NLLB ดังนั้นเราจะต้องใส่ target_modules เอง (ส่วนที่จะ Train) ผมเลือก query projection, value projection (Training Code)

peft_config = LoraConfig(
task_type=TaskType.SEQ_2_SEQ_LM,
inference_mode=False,
r=8,
lora_alpha=32,
lora_dropout=0.1,
target_modules=["q_proj", "v_proj"],
)

ส่วน rank=8 โดยอันนี้เลือกมาตาม paper ของ LoRA ที่เขาทดลองมาว่าการใช้ rank=64 ก็ไม่ได้ทำให้ accuracy ดีขึ้น

https://arxiv.org/pdf/2106.09685.pdf Page 10

หลังจาก Train ไปได้ 9 epochs ได้ BLEU Score บน Validation Set อยู่ที่ 24 ซึ่งต่ำกว่า Model ที่ Train ทั้ง Model แค่ 3 epochs เลยตัดสินใจหยุด Train (ดู Training Logs ได้บน Weights & Biases)

NLLB-LoRA Training/Evaluation Loss, BLEU Score

พอเอาไปทดสอบบน Test Set ได้ BLEU Score อยู่ที่ 24.23 ซึ่งพบว่าแทบจะไม่ได้ดีขึ้นจาก Baseline เลย ซึ่งตรงนี้อาจจะต้องลองปรับ rank ดูว่าจะทำให้ model เรียนรู้ได้ดีขึ้นไหม

Model Result

ตัวอย่างประโยคจากการแปลด้วย NLLB-600M, NLLB-Finetuned (SCB+OPUS) และ Model จาก AIResearch

Source: โยว่ และนี้คือเสียงจากเด็กวัด
NLLB-600M: Oh, and this is the voice of the nun.
NLLB-Finetuned: Yo, and this is the voice of the boy.
AIResearch: Yo, and this is the voice from the temple boy.

Source: วันเสาร์อาทิตย์นี้อยากไปเที่ยวไหน
NLLB-600M: Where do you want to go this Saturday?
NLLB-Finetuned: What holiday would you like to go to this weekend?
AIResearch: What weekend would you like to go?

Source: สวัสดีครับพี่เนม
NLLB-600M: Hey, what’s up, Ned?
NLLB-Finetuned: Hello, Nam.
AIResearch: Hello, Nem.

Source: เย็นนี้เราน่าจะออกไปกินข้าวข้างนอก
NLLB-600M: We should go out for dinner tonight.
NLLB-Finetuned: We should go out for dinner this evening.
AIResearch: We should go out for dinner this evening.

Source: ขอทราบนามสกุลคุณสมชายค่ะ
NLLB-600M: I’d like to know your last name, Mr. Prince.
NLLB-Finetuned: May I have your full name?
AIResearch: What’s your last name, Somchai?

ซึ่งจากผลการแปล NLLB-Finetuned จะมีความใกล้เคียงกับ Model จาก AIResearch ซึ่งน่าจะเป็นเพราะใช้ Dataset เดียวกัน

Deployment

การ Deploy ผมเลือกใช้ Huggingface Space เพราะว่า Free (สำคัญสุด) และให้ Resource เยอะ โดยเฉพาะ Ram 16GB ทำให้ Deploy Model ใหญ่ ๆ ได้ ซึ่งถ้าใครอยากทดลองก็สามารถเข้าไปได้ที่ https://huggingface.co/spaces/wtarit/nllb-th-en-translation

https://huggingface.co/spaces/wtarit/nllb-th-en-translation

Improvement

วิธีการที่ผมคิดว่าจะสามารถทำให้ Model ทำงานได้ดีขึ้นได้ แต่ไม่ได้ลองด้วยเหตุผลทางด้านเวลา และเงิน

เพิ่ม Dataset

จากการปรึกษา Mentor มีไอเดียว่าใช้ ChatGPT มา Generate Data (แนว ๆ Alpaca ที่เอา Data จาก text-davinci มา train) ปัญหาคือพอคำนวณ cost แล้ว แพงมาก ลองคำนวณคร่าว ๆ 1 ล้านประโยค ใช้ประมาณ 300 USD (assume 1 ประโยคใช้ 150 tokens, gpt-3.5-turbo $0.002 / 1K tokens)

อีกไอเดียนึงคือใช้ Youtube Subtitles จากช่องที่มี Subtitles ทั้ง 2 ภาษา แต่พบปัญหาคือพอเอา Subtitles ไทย-อังกฤษมา align กันตามเวลาของคลิป มีบางอันที่ประโยคไทยกับอังกฤษตัดมาไม่ตรงกัน (เช่นไทยเขียนเป็นประโยคยาว อังกฤษตัดเป็น 2 ช่วง)

คัด Data ที่คุณภาพต่ำออก

สังเกตว่า Dataset ที่มาจากการ Web Scraping หลาย ๆ อันจะ Align มาไม่ตรงกันเช่น Data จาก Wikipedia ใน SCB

EN: “FIFA 15” received positive reviews across all platforms, although the PC version in particular was criticized for the amount of bugs that were featured at release.
TH:
และเป็นเกมแรกในซีรีส์”ฟีฟ่า” ที่ได้รับสิทธิอย่างเต็มที่ในพรีเมียร์ลีก “ฟีฟ่า 15” ได้รับคำวิจารณ์ในเชิงบวกในทุกระบบแพลตฟอร์ม แม้ว่าในเวอร์ชันพีซีจะได้รับการวิจารณ์ถึงอาการบั๊กที่เกิดขึ้นบ่อยครั้งในช่วงเปิดตัวอยู่ก็ตาม

บางประโยคแปลผิด เช่น ข้อมูลที่มาจากการ Crowdsourcing (ตัวอย่างจาก SCB generated_reviews_crowd)

EN: This was my book club’s choice for the spring 2012.
TH: นี่คือหนังสือเล่มโปรดของฉันในฤดูร้อนปี 2012

ข้อมูลจาก NUS_SMS ที่ถึงเป็นภาษาอังกฤษแต่เป็นแบบ Singlish

NUS_SMS Sample

Train Model ที่ให้ขึ้น

  1. Train model ให้นานขึ้น
  2. ลอง Train NLLB ตัวที่มี Parameter เพิ่มมากขึ้น

Conclusion

สรุปผลลัพธ์ Model ที่ได้ลอง Train ไป

ถึงแม้ว่า Project นี้จะยังไม่ได้ผลลัพธ์ตามเป้าหมายที่ตั้งใจไว้ แต่ก็ถือว่าใกล้เคียงมาก ๆ ส่วนที่ยังไม่ได้ Train ต่อนั้นเพราะอยากจะ Clean Data ให้ดีกว่านี้แล้วค่อย Train (ตามคติที่ว่า Garbage in — Garbage out) และการ Train แต่ละครั้งมันใช้เวลานาน

ในด้าน Tools ที่ใช้งานอาจจะสังเกตว่า Project นี้มีความเป็น Huggingface Ecosystem มาก ๆ แต่ว่ามันใช้ง่ายจริง ๆ แนะนำให้ลองสำหรับคนที่จะทำ NLP (แต่ปัจจุบัน Huggingface transformers ก็ Support Data แบบอื่น ๆ ด้วยนะ)

และสำหรับคนที่กำลังมองหา Model Machine Translation ไปใช้งานก็หวังว่า Model นี้จะเป็น Option หนึ่งให้พิจารณาครับ (แนะนำให้ลอง Evaluate ด้วยประโยคใน Domain ที่จะใช้งาน)

--

--