เครื่องแปลภาษา (Machine Translation) คือ โปรแกรม หรือ เครื่องแปลภาษา เป็นส่วนหนึ่งของงานด้านการประมวลผลภาษาธรรมชาติ (Natural Language Processing) เป็นงานที่ท้าทายข้อจำกัดทางด้านภาษาของมนุษย์ เพื่อให้ผู้คนสามารถติดต่อสื่อสารกันได้โดยไม่มีกำแพงทางภาษามาขวางกั้นไว้
เครื่องแปลภาษา เริ่มต้นโดยใช้ความรู้ทางด้านภาษาศาสตร์มาประยุกต์กับทางคอมพิวเตอร์เขียนกฎสำหรับแปลภาษาในคอมพิวเตอร์ เรียกการแปลแบบนี้ว่า Rule-based machine translation (RBMT) ซึ่งพอเวลาผ่านไปสักระยะผู้คนเริ่มเห็นข้อจำกัดการแปลด้วยเทคนิคด้วยกฎ เพราะภาษามนุษย์มีไวยากรณ์มากมายและยากที่จะเขียนให้ครอบคลุม ต่อมาจึงมีเทคนิค Statistical machine translation (SMT) แปลด้วยสถิติ และล่าสุดเป็น Neural machine translation (NMT) ที่ใช้เทคนิคการเรียนรู้เชิงลึก (Deep learning) สามารถแปลได้แม่นยำที่สุดในปัจจุบัน
เพื่อให้เข้าใจ Rule-based machine translation (RBMT) มากยิ่งขึ้น เรามาลองเขียนโปรแกรมแปลภาษาอังกฤษเป็นภาษาไทยด้วยกฎ หรือ English-Thai Rule-based machine translation กัน
เนื่องจากเราจะเขียนขึ้นมาใหม่ ไม่อิงเอกสารด้านนี้ที่เคยใช้งานมา และ ใช้เครื่องมือในปัจจุบันช่วยทำโปรแกรมแปลภาษาอังกฤษเป็นภาษาไทยด้วยกฎ
สิ่งที่ต้องเตรียม
- พจนานุกรมคำศัพท์ภาษาอังกฤษ - ภาษาไทย เราใช้พจนานุกรมอิเล็กทรอนิกส์ไทย - อังกฤษ LEXiTRON จากบทความ เขียนโปรแกรมแปลภาษา dictionary บน Python 3
- รับประโยคภาษาอังกฤษ 1 ประโยคเข้ามา
- ตัดประโยคภาษาอังกฤษ
- นำคำไปผ่าน Stemmer ให้เรียบร้อย
- นำประโยคที่ผ่านการตัดคำไปหาชนิดของคำ (part of speech)
- นำแต่ละคู่คำกับชนิดของคำ ไปค้นคำแปลจากพจานานุกรมภาษาอังกฤษ -> ภาษาไทย ให้ได้คู่คำแปลพร้อมกับชนิดของคำ
- นำคู่คำแปลพร้อมกับชนิดของคำ มาจัดเรียงจากไวยากรณ์ภาษาอังกฤษให้เป็นภาษาไทย
- นำคำแปลที่ผ่านการจัดเรียงมาแสดงผลจะได้คำแปล
- NLTK - ใช้ตัดคำภาษาอังกฤษ, Stemmer และชนิดของคำ
- sqlite3 - ใช้สำหรับเชื่อมต่อกับพจนานุกรมคำศัพท์ภาษาอังกฤษ - ภาษาไทย
import nltk
nltk.download('averaged_perceptron_tagger')
nltk.download('universal_tagset')
nltk.download('punkt')
nltk.download('stopwords')
nltk.download('wordnet')
ส่วนที่ 1 แปลภาษาแบบคำต่อคำตามชนิดของคำ
ค้นคำแปลจากคำศัพท์และตามชนิดของคำอย่างแรก เขียนฟังก์ชันค้นหาคำจากฐานข้อมูลพจนานุกรมคำศัพท์ภาษาอังกฤษ - ภาษาไทย ซึ่งเป็นฐานข้อมูล SQLite
ในไฟล์ lexitron.db จะมี TABLE ชื่อ eng2thai ประกอบไปด้วย id, esearch, eentry, tentry,ecat,ethai,esyn และ eant เราจะค้นทั้งคำศัพท์และตามชนิดของคำ เราจึงต้องค้นจาก esearch กับ ecat
โดยเขียนเป็นฟังก์ชันสำหรับค้น ชื่อ word2thai ได้ดังนี้
import sqlite3 conn = sqlite3.connect('lexitron.db') c = conn.cursor() def word2thai(word:str,tag:str)->str: word = word.lower() if tag == '': find = c.execute("SELECT * FROM eng2thai WHERE esearch='%s'" % (word)) else: find = c.execute("SELECT * FROM eng2thai WHERE esearch='%s' and ecat='%s'" % (word,tag)) for row in find: if '(' not in row[3] and 'คำ' not in row[3]: return row[3]ฟังก์ชันสำหรับทำ Stemmer
เราใช้ NLTK ในการทำ Stemmer โดยเราเขียนฟังก์ชันชื่อ prepro ไว้
from nltk.stem.porter import PorterStemmer stemmer = PorterStemmer() from nltk.stem import WordNetLemmatizer lemmatizer = WordNetLemmatizer() from nltk.corpus import stopwords st = stopwords.words('english') from nltk.stem.snowball import SnowballStemmer snowBallStemmer = SnowballStemmer("english", ignore_stopwords=True) from nltk.stem.lancaster import * lancasterStemmer = LancasterStemmer() def prepro(listword:list)->list: temp= [stemmer.stem(i) for i in listword] sn = [snowBallStemmer.stem(word) for word in temp] la = [lancasterStemmer.stem(i) for i in sn] return [i for i in listword]
ต่อไปเรามาเขียนฟังก์ชันสำหรับแปลคำกับชนิดคำ ภาษาอังกฤษเป็นภาษาไทยกัน
แปลคำตามชนิดคำจากภาษาอังกฤษเป็นภาษาไทย
สมมติ เรามีประโยค
sent = "I love you."เราเอามาแปลงเป็นตัวพิมพ์เล็กทั้งหมด
sent = sent.lower()จากนั้นนำมาตัดคำด้วยคำสั่ง
import nltkแล้วนำมาหา part of speech โดยเราจะใช้ Universal Part-of-Speech Tagset เพื่อให้ง่ายต่อการเทียบ part of speech ในการหาคำแปลตามชนิดของคำจากฐานข้อมูลพจนานุกรมคำศัพท์ภาษาอังกฤษ - ภาษาไทย
token = nltk.word_tokenize(sent)
tag = pos_tag(token,tagset='universal')เนื่องจาก part of speech ของ Universal Part-of-Speech Tagset กับ LEXiTRON มีค่าไม่ตรงกัน เราจึงต้องเขียนตัวแปลจาก part of speech ของ Universal Part-of-Speech Tagset ให้เป็น part of speech ของ LEXiTRON เพื่อให้เวลาค้นหาคำ เจอคำแปลที่ตรงกับไวยากรณ์ที่เขียน
เราสร้าง DICT สำหรับเทียบ part of speech ของ Universal Part-of-Speech Tagset กับ part of speech ของ LEXiTRON ขึ้นมา
tag2lexitron = {
"ADJ" : "ADJ",
"ADP" : "PREP",
"ADV" : "ADV",
"AUX" : "AUX",
"CCONJ" : "CONJ",
"DET" : "DET",
"INTJ" : "INT",
"NOUN" : "N",
"NUM" : "",
"PART" : "",
"PRON" : "PRON",
"PROPN" : "",
"PUNCT" : "",
"SCONJ" : "CONJ",
"SYM" : "",
"VERB" : "VERB", # VT VI
"X" : "",
"." : "",
"CONJ" : "CONJ",
"PRT":"PREP"
}
จากนั้น เขียนฟังก์ชันแปลกัน
def translate(sent:str)->list:
sent = sent.lower()
token = nltk.word_tokenize(sent)
tag = [(i[0],tag2lexitron[i[1]]) for i in nltk.pos_tag(token,tagset='universal')]
thai = [word2thai(i[0],i[1]) for i in tag]
print(tag)
print(thai)
return thai
ลองเรียกใช้
print(translate("my phone"))
ผลลัพธ์
[('my', 'PRON'), ('phone', 'N')]
['ของฉัน', 'ูโทรศัพท์']
['ของฉัน', 'ูโทรศัพท์']
ตอนนี้เราสามารถแปลภาษาแบบคำเทียบคำได้แล้ว แต่พอลองประโยคแบบนี้
print(translate("I lost phone."))
ผลลัพธ์
[('i', 'N'), ('lost', 'VERB'), ('phone', 'N'), ('.', '')]
[None, None, 'ูโทรศัพท์', None]
[None, None, 'ูโทรศัพท์', None]
จะเห็นที่เป็น None เพราะค้นจาก lexitron.db ไม่เจอ เราจึงต้องเขียนพจนานุกรมเพิ่มเติมใส่ลงไปนอกจากเหนือจากของ LEXiTRON
MY_DICT = {
"i,N" : "ฉัน",
"lost,VERB" : "สูญเสีย"
}
และปรับปรุงฟังก์ชัน word2thai ได้ดังนี้
def word2thai(word:str,tag:str)->str:
word = word.lower()
if word+","+tag in list(MY_DICT.keys()): # ถ้ามีคำแปลและ postag นี้ใน dict
return MY_DICT[word+","+tag]
elif word == '.':
return ''
elif tag == '':
find = c.execute("SELECT * FROM eng2thai WHERE esearch='%s'" % (word))
else:
find = c.execute("SELECT * FROM eng2thai WHERE esearch='%s' and ecat='%s'" % (word,tag))
for row in find:
if '(' not in row[3] and 'คำ' not in row[3]:
return row[3]
ลอง
print(translate("I lost phone."))
ผลลัพธ์
[('i', 'N'), ('lost', 'VERB'), ('phone', 'N'), ('.', '')]
['ฉัน', 'สูญเสีย', 'ูโทรศัพท์', '']
['ฉัน', 'สูญเสีย', 'ูโทรศัพท์', '']
ลองเล่น CoLab ส่วนแปลคำต่อคำ ได้ที่ https://colab.research.google.com/drive/1_hjtUxlVRWwJdK4QhlxwnwWTVDApCHsl
ส่วนที่ 2 แปลภาษาตามกฎไวยากรณ์
พอเรามาลองประโยคที่ซับซ้อนขึ้นมาprint(translate("I lost my phone."))
ผลลัพธ์
[('i', 'N'), ('lost', 'VERB'), ('my', 'PRON'), ('phone', 'N'), ('.', '')]
['ฉัน', 'สูญเสีย', 'ของฉัน', 'ูโทรศัพท์', '']
['ฉัน', 'สูญเสีย', 'ของฉัน', 'ูโทรศัพท์', '']
จะเห็นได้ว่า การแปลภาษาแบบคำต่อคำ ไม่สามารถแปลให้เข้าใจได้เมื่อประโยคมีความซับซ้อยขึ้น จึงต้องเขียนกฎเทียบไวยากรณ์ภาษาอังกฤษกับภาษาไทยเพิ่มขึ้นมา
ก่อนอื่น เรามาเทียบไวยากรณ์ที่ได้จากประโยค I lost my phone. กัน
I lost my phone.
N VERB PRON N
ฉัน สูญเสีย ของฉัน โทรศัพท์
แปลให้คำแปลออกมาเข้าใจง่ายตามภาษาไทยจะได้
N VERB N PRON
ฉัน สูญเสีย โทรศัพท์ ของฉัน
ดังนั้น "N VERB PRON N" ควรจัดเรียงใหม่เป็น "N VERB N PRON" เพื่อให้ประโยคภาษาอังกฤษที่แปลออกมาอ่านเข้าใจในภาษาไทยส่วนโค้ด เราจะเทียบไวยากรณ์อย่างไร หลาย ๆ คนคงนึกถึง CFG เทียบ Tree ไวยากรณ์ แต่บทความนี้เราเน้นความง่ายเป็นหลัก โดยใช้การเทียบไวยากรณ์กับไวยากรณ์เลย
พอได้ไวยากรณ์ที่เทียบกันแล้วเราจึงเขียนกฎขึ้นมาได้ดังนี้
rule = {
"N VERB PRON N2" : "N VERB N2 PRON" # N2 คือ Noun ตัวที่ 2
}
เขียนนำไวยากรณ์ไปเทียบ การทำงาน คือ นำ part of speech ออกมาเป็น str แล้วใช้ rule เทียบ แล้วส่งไวยากรณ์ที่จัดเรียงไว้ที่ตรงกับกฎไวยากรณ์ออกมาจัดเรียงประโยคอีกครั้ง
def sent2thai(listsent:list)->list: # จัดเรียงประโยคภาษาอังกฤษ -> ภาษาไทย [(คำ , ชนิดของคำ),...]
i_d = {}
g = []
d = {}
for i in listsent:
#print(i)
word = i[0]
tag = i[1]
temp=""
if tag not in list(i_d.keys()):
temp = tag
i_d[tag]=2
else:
temp = tag + str(i_d[tag])
i_d[tag]+=1
d[temp] = word
g.append(temp)
g=' '.join(g)
if g in list(rule.keys()):
g = rule[g]
listnew = g.split(' ')
sent = [d[i] for i in listnew]
return sent
ไปปรับแต่ง translate โดยเปลี่ยนแปลงโค้ดเป็น
def translate(sent:str)->list:
sent = sent.lower()
token = nltk.word_tokenize(sent)
tag = [(i[0],tag2lexitron[i[1]]) for i in nltk.pos_tag(token,tagset='universal')]
thai = [(word2thai(i[0],i[1]), i [1]) for i in tag]
print(tag)
print(thai)
return sent2thai(thai)
ลองเรียกใช้งาน
print(translate("I lost my phone"))
ผลลัพธ์
[('i', 'N'), ('lost', 'VERB'), ('my', 'PRON'), ('phone', 'N')]
[('ฉัน', 'N'), ('สูญเสีย', 'VERB'), ('ของฉัน', 'PRON'), ('ูโทรศัพท์', 'N')]
N VERB PRON N2
['ฉัน', 'สูญเสีย', 'ูโทรศัพท์', 'ของฉัน']
เรียบร้อย ประโยคที่เราแปลสามารถจัดเรียงคำแปลตามกฎไวยากรณ์ได้อย่างสวยงาม
ลองเล่น CoLab ส่วนที่ 2 แปลภาษาตามกฎไวยากรณ์ ได้ที่ https://colab.research.google.com/drive/1xO9Tt21U06vacyjSn9RSJx03dn9O9daP
เนื่องจากภาษามีกฎไวยากรณ์เยอะมาก ๆ และข้อยกเว้นมากมาย เช่น สำนวนต่าง ๆ เป็นต้น ยากที่จะเขียนกฎไวยากรณ์แปลได้ครบทุกไวยากรณ์และครบทุกกฎ การแปลภาษาด้วยเครื่องทางสถิติ (statistical machine translation) จึงได้รับความนิยมในยุคต่อมา เนื่องจากเราไม่ต้องมาเขียนไวยากรณ์เทียบแบบนี้
สำนวน "raining cats and dogs" เป็นอีกข้อยกเว้น หากเราจะทำโปรแกรมแปลภาษาอังกฤษเป็นภาษาไทยด้วยกฎไวยากรณ์ ที่มารูป : File:George Cruikshank - Very unpleasant Weather.jpg |
- ปรับปรุงวิธีการแปลให้การแปลดียิ่งขึ้น
- ปรับปรุงการประมวลผลคำก่อนนำไปแปล เช่น ตัด -ing หรือ -ed ทิ้ง, แปลคำกริยาให้เป็นช่องที่ 1 เป็นต้น
- ปรับปรุงให้รองรับการแปลสำนวน
- เปลี่ยนเป็นแปลจากภาษาไทยเป็นภาษาอังกฤษ (ยาก)
นักศึกษาสาขาวิทยาการคอมพิวเตอร์และสารสนเทศ
คณะวิทยาศาสตร์ประยุกต์และวิศวกรรมศาสตร์
มหาวิทยาลัยขอนแก่น วิทยาเขตหนองคาย
0 ความคิดเห็น:
แสดงความคิดเห็น
แสดงความคิดเห็นได้ครับ :)