NLP(Natural Language Processing) ศาสตร์(ไม่)ใหม่ ศาสตร์แห่งเจได: แยกประเภทอีเมลล์ด้วยพลังฟอร์ซ

Mr.P L
mmp-li
Published in
6 min readFeb 1, 2019

Be the force be with you…. อ่าวผิดเรื่อง

บทความสุดท้ายของ Machine Learning แล้วโดยบทนี้จะเป็นการนำ ML Algo มาทดสอบใช้งานกับศาสตร์ด้านอื่นๆบ้างนอกจากใส่ตัวเลขแล้วทำนายกรุ๊ปหรือตัวเลขออกมา โดยวันนี้จะโฟกัสที่ “การประมวลผลด้านภาษา”

NLP คืออะไร ? เกี่ยวอะไรกับเจได ?

NLP ย่อมาจาก Natural Language Processing ซึ่งไม่เกี่ยวอะไรกับเจไดเลยสักอย่าง แต่ถ้าเราเอา “NLP” ไปเสิร์จใน google ผลลัพธ์คือ

พลังจิตแบบเจได

จึงเป็นที่มาว่าทำไมเวลาคนพูดถึง NLP จะต้องมีมุก Join the dark side

(By Dr. Ekapol Chuangsuwanich)

เออ…จอยก็ได้

กลับเข้าเรื่อง

ประวัติความเป็นมา

จุดมุ่งหมายของการทำ NLP คือ : “การให้คอมพิวเตอร์เข้าใจภาษามนุษย์”

โดยเริ่มต้นเมื่อปี 1950s -1980s

เป็นยุคแรกๆของ NLP ถูกจำกัดวงอยู่ในนักภาษาศาสตร์ และวิธีในการให้คอมเข้าใจโดยการใช้วิธี “Rule-Based” (การใช้ if-else เพื่อบอกว่านี้คือไวยากรณ์,ความหมาย) โดยงานในการตีความหมายภาษาจะถูกแบ่งออกมาเป็นงานย่อยๆ เช่น ตัดคำ / แปลความหมาย ข้อดีของมันคือ “ไม่ต้องมี Dataset” คุณเขียนกฏออกมา เอาไปใช้ได้เลย ข้อเสียคือ คุณจะต้องรู้โครงสร้างของภาษา (เป็นนักภาษาศาสตร์)

ในปี 1981s-2009s

เป็นยุคที่สอง ยุคที่คอมพิวเตอร์เริ่มมีความเร็วมากขึ้น (ตามกฏของ Moore’s law) โดย NLP เริ่มจะรู้จักกับ Machine Learning (Statistic Based) โดยเริ่มนำ ML Algo มาใช้เช่น Decision Tree หรือ การทำ POS (part-of-speech tagging) ก็เริ่มใช้ hidden Markov models ทำให้มีความแม่นยำมากขึ้น ทุกๆคนที่ทำ NLP ก็เปลี่ยนมาใช้ Statistic Based กันหมด ข้อดีคือแม่นยำมากขึ้นกว่าเดิม ข้อเสียคือ “จำเป็นต้องมี Dataset” ทำให้ต้องหา data มาฝึกมัน

และปี 2010-ปัจจุบัน

ยุคที่สาม ยุคที่ Deep Neural Network กำลังเป็นที่นิยมเพราะคอมพิวเตอร์เริ่มมีความเร็วสูงแล้ว มี Data จำนวนมากแล้ว ทำให้ NN เหมาะแก่การนำมาใช้กับด้าน NLP เช่น การสร้าง language modeling , การทำ parsing เล่นเป็นที่นิยมมากในตอนนี้คือ word embeddings คือการหา semantic กับข้อความนั้นๆ

ในปัจจุบันการแก้ไขปัญหา High Level สามารถทำได้โดยตรงโดยไม่ต้องแตก task ออกย่อยๆอีกแล้ว เหมือนแต่ก่อนอีกต่อไป ทำให้งาน NLP เริ่มเห็นแสงสว่างที่ปลายทางนั้นเอง

Rule-Based VS Statistic-Based

Rule-based ถูกจำกัดวงอยู่ในหมู่นักภาษาศาสตร์ แม้จะใช้งานง่ายกว่า ไม่ต้องพึ่ง Dataset แต่ยากต่อการจะพัฒนาต่อ เพราะเมื่องานเริ่มซับซ้อนมากยิ่งขึ้น การทำ Rule ก็จะยิ่งซับซ้อนและยากมากยิ่งขึ้นนั้นเอง

Statistic-based สามารถทนต่อความหลากหลายของภาษาได้ เช่น คำที่สะกดผิดหรือคำที่หายไปได้ และเมื่อมีการพัฒนาต่อก็เพียงแค่เปลี่ยน data ที่ใช้ในการ train ก็ได้ ver. ใหม่ออกมาแล้ว แต่วิธีนี้ต้องใช้ Data หากไร้ Data ก็ไร้ค่า

สายย่อยๆของ NLP คือเยอะและยังท้าทายสำหรับภาษาไทย

สำหรับใครที่คิดว่าสาย NLP ไม่มีอะไรท้าทาย ไม่มีอะไรแปลกใหม่หรือคิดว่าต่างประเทศเจาะสายนี้จนพรุน คุณคิดผิดมาก เพราะสายย่อยๆของ NLP ในแต่ละภาษานั้นมีขั้นตอนคล้ายๆกันแต่ว่า …. แต่ละภาษามันมี Syntax ของมัน ….

อะไรที่ต่างประเทศบอกว่า Easy ภาษาไทยจะกลายเป็น Medium ทันที

ไม่เชื่อเว้ยยยยยยยยยยย

ยกตัวอย่างเช่น การตัดคำ (Word Segmentation) คือการแยกประโยคยาวๆออกมาเป็นคำๆ ในต่างประเทศไม่ต้องทำ Word Segment …. เพราะประโยคบ้านเขามี Space bar แล้วนั้นเอง เช่น I love you ก็ไม่ต้องตัดแล้ว มันแยกแต่ละคำมาแล้ว

แต่ทีนี้มาที่ประเทศไทย

ภาพจากวิดีโอวิชา NLP ของ Dr.Ekkapol https://www.youtube.com/watch?v=-3qG8ndG09w&t=243

คำว่า “ตากลม” ตัดยังไงดี ? ตา/กลม ก็ได้ คำที่ประโยค หรือจะ ตาก/ลม ก็ได้

ปวดหัวเลย

ส่วนประกอบหลักๆของการทำ NLP

แต่นี้ก็ปี 2019 แล้วแน่นอนว่าอะไรๆมันก็ต้องง่ายขึ้นแน่นอนเรามีอัลกอริทึมมีแม่นยำ มี Tool ช่วยจัดการข้อมูล มี Library ช่วยทำให้ชีวิตง่ายขึ้น แต่เราต้องไม่ลืมว่าเบื้องหลังของที่เราใช้พวกนี้เกิดจากอะไร และ Component หรือ Math be hide Algorithm ทำงานยังไงบ้างนั้นเอง

ลงมือทดลอง

วันนี้จะมาแปลกๆหน่อยคือการทำตามบทความของต่างประเทศ(ได้ 4.7k claps)เรื่องของเรื่องคือตอนต้นปี 2018 ผมอ่านบทความนี้

พออ่านแล้วก็จับใจความได้ว่า….ไม่รู้เรื่องเลยเว้ยยย 555555 มันเขียนภาษาอังกฤษจริงๆเหรอ….เพราะตอนนั้นไม่ได้ทำ Ai ไม่เคยเล่น ML เลย แต่ตอนนี้กลับมาอีกรอบ ก็เข้าใจมากขึ้นนิดนึง #แหะ

โดยวันนี้จะใช้ SGD (บทความ SGD ที่เคยเขียนไว้ก่อนหน้านี้) และก่อนจบจะทดลอง

PyThaiNLP library on Python ที่เป็นผลงานของคนไทยนั้นเอง

ซึ่ง

dataset วันนี้ที่จะใช้มีชื่อว่า The 20 newsgroups เป็น text dataset ปกติแล้ว sklearn จะมี dataset ไว้ให้ในตัวแล้ว ซึ่งเราจะเอา dataset ชุดนี้จะเอามาจำแนกว่าเป็นข่าวประเภทไหนนั้นเอง

เริ่ม

เปิด Jupyter ขึ้นมาแล้วเริ่ม Code กัน !

ขั้นตอนทำการโหลด dataset มาก่อน ซึ่งจากโค้ดข้างล่างจะสังเกตุว่ามันมีอยู่ใน sklearn แล้วนั้นเอง (เหมือน dataset ดอกไม้ / digit numbers)

from sklearn.datasets import fetch_20newsgroups
twenty_train = fetch_20newsgroups(subset=’train’, shuffle=True)
twenty_test = fetch_20newsgroups(subset=’test’, shuffle=True)
twenty_train.target_names

ซึ่งจะได้ผลลัพธ์คือ Class ที่มีใน dataset นี้มีอะไรบ้างนั้นเอง

คลาสไม่เยอะมาก

ทีนี้ลองดูหน่อยว่า dataset มันมีลักษณะอย่างไรบ้างด้วยคำสั่งง่ายๆ

print(twenty_train.target_names[twenty_train.target[5]])print(twenty_train.data[5])

ก็จะได้ผลลัพธ์คือ ข้อความนั้นๆคือคลาสอะไร และ เนื้อหามีอะไรบ้าง

เป็นข่าวเกี่ยวกับการเมือง&ปืน

ขั้นตอนถัดไป ตัว ML เนี่ยจะต้องแปลงตัวอักษรเป็นตัวเลขก่อนซึ่งวิธีการแปลงก็มีเยอะมาก จะใช้ 1hot ก็ได้แต่การ Classification ของ text จะต้องใช้ bag-of-word แปลเป็นไทยง่ายๆคือคลังคำศัพท์ โดยหลักการทำงานของมันคือจะมีตัดคำ (Segmentation) [ถ้าภาษาอังกฤษก็จะตัดด้วย space bar] จากนั้นก็จะนับว่าคำๆนั้นปรากฏขึ้นทั้งหมดกี่ครั้งในแต่ละเมลล์ โดยแต่ละคำจะมี ID ที่มันไม่เหมือนกัน

Each unique word in our dictionary will correspond to a feature

โดยการนำเอาข้อมูลทั้งหมดไปใส่ในคลังเราสะด้วยฟังก์ชั่น CountVectorizer()

from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(twenty_train.data)
X_train_counts.shape #จะได้จำนวนเมล = 11314, จำนวนคำทั้งหมดในเมล = 130107

ขั้นตอนถัดไปคือ TF-IDF ขั้นตอนนี้บทความที่เรายกตัวอย่างมาได้เขียนไว้ดีมากๆ

TF: Just counting the number of words in each document has 1 issue: it will give more weightage to longer documents than shorter documents. To avoid this, we can use frequency (TF — Term Frequencies) i.e. #count(word) / #Total words, in each document.

TF-IDF: Finally, we can even reduce the weightage of more common words like (the, is, an etc.) which occurs in all document. This is called as TF-IDF i.e Term Frequency times inverse document frequency.

TF : ปกติการนับจำนวนคำที่ปรากฏใน 1 เมลล์(bag-of-word)เนี่ยปัญหาข้อเดียวของมันคือ เมลล์ที่ยาวเนี่ยจะมีน้ำหนักของคำที่เยอะกว่าเมลล์ที่สั้น ทางแก้ไขคือการใช้ความถี่ (Term Frequencies) ของคำที่ปรากฏขึ้นในเมลล์นั้นเอง เช่น จำนวนคำที่ปรากฏ/จำนวนคำทั้งหมดในแต่ละเมลล์

TF-IDF : ในขั้นตอนสุดท้าย เราสามารถลดน้ำหนักของคำปกติทั่วๆไปเช่น the,is,and ได้ทั้งหมดในเมลล์ซึ่งเรียกวิธีนี้ว่า TF-IDF (Term Frequency times inverse document frequency) ซึ่ง วิธีนี้จะต้องทำต่อจาก CountVectorizer นั้นเอง

from sklearn.feature_extraction.text import TfidfTransformer
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)

ทำไมต้องใช้ 2 ตัวนี้ ?

ขั้นแรกเราสร้างคลังคำศัพท์ขึ้นมาแล้ว (Bag-of-Word ด้วย CountVectorizer) แต่ละคำก็จะมีจำนวนคำที่ปรากฏขึ้นมา แต่ “ CountVectorizer just counts the word frequencies. Simple as that.” ซึ่งมันก็จะเกิดปัญหาเมลล์สั้นกับยาวความถี่ของคำจะมีน้ำหนักไม่เท่ากัน เราจึงนำเอา TF-IDF มาจัดการต่อเพื่อเพิ่มความถูกต้องในการนับด้วยการตัดคำปกติทิ้ง(the,is,an เป็นต้น) แล้วหาความถี่ของคำนั้นๆจากเมลล์ทั้งหมด

ได้เวลารัน Machine Learning Algorithm

วันนี้อัลกอที่เราเลือกคือ SGD ให้เราเรียก lib ที่จะใช้งานวันนี้มาก่อนคือ Pipeline ,SGD , Numpy

from sklearn.pipeline import Pipeline
from sklearn.linear_model import SGDClassifier
import numpy as np

จากนั้นเรียกใช้ Pipeline ** Pipeline คือ ** เป็นฟังก์ชั่นหนึ่งที่เอาไว้รวมฟังก์ชั่นอื่นๆไว้ หลักการทำงานง่ายๆ คือทำงานตามฟังก์ชั่นที่ยัดเอาไว้

text_clf_svm = Pipeline([(‘vect’, CountVectorizer()),
(‘tfidf’, TfidfTransformer()),
(‘clf-svm’, SGDClassifier(loss=’hinge’, penalty=’l2', alpha=1e-3, n_iter=5, random_state=42)),])

เช่นโค้ดนี้ เมื่อเราใช้งานการ classification มันจะรับ input เข้ามาจากนั้นทำ CountVectorizer -> Tfidf -> Classification เป็นลำดับ

จากนั้นก็เทรนโมเดลแล้วทดสอบด้วยชุดข้อมูลเทส

text_clf_svm.fit(twenty_train.data, twenty_train.target)
predicted_svm = text_clf_svm.predict(twenty_test.data)

ความแม่นยำคือ

np.mean(predicted_svm == twenty_test.target)
โอเค ไม่ได้เลวร้ายอะไรมาก

จากนั้นก็เพิ่มความสามารถให้โมเดลไปอีกขั้นด้วย GridSearchCV จะได้เทสสกอร์ประมาณ

from sklearn.model_selection import GridSearchCV #ใช้วิธี กาเดี้ยนดีเซ้น
parameters_svm = {'vect__ngram_range': [(1, 1), (1, 2)],
'tfidf__use_idf': (True, False),
'clf-svm__alpha': (1e-2, 1e-3),}
gs_clf_svm = GridSearchCV(text_clf_svm, parameters_svm, n_jobs=-1)
gs_clf_svm = gs_clf_svm.fit(twenty_train.data, twenty_train.target)
gs_clf_svm.best_score_
อันนี้เป็นแค่คะแนนสำหรับตอนฝึก

จากนั้นลองเทสอีกสักรอบ จะได้ความแม่นยำประมาณ

predicted_gs_svm = gs_clf_svm.predict(twenty_test.data)
np.mean(predicted_gs_svm == twenty_test.target)
เพิ่มขึ้นมานิดหน่อย

การทดลองที่ 2 PyThaiNLP

PyThaiNLP เป็น library สำหรับทำงานด้าน NLP สำหรับภาษาไทยเพราะถ้าคุณใช้ word embedding ตัวดังๆเนี่ยส่วนมากจะไม่รับภาษาไทย กลุ่มคนไทยกลุ่มหนึ่ง จึงได้สร้าง lib ตัวนี้ขึ้นมาเพื่อใช้งานด้าน NLP นั้นเอง ซึ่งจะเป็นฟังก์ชั่นสำเร็จรูปเลย

ทดลองการตัดคำ

คือการแบ่งประโยคออกมาเป็นคำๆโดยที่แต่ละคำยังมีความหมาย ไม่ได้ตัดมั่วๆ

from pythainlp.tokenize import word_tokenize
text=’ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด’

โดยผมเลือกใช้การตัดคำแบบ Default จะได้ผลลัพธ์

ซึ่งแบบ Default ที่ผมเขียนคือรูปแบบการตัด จะตัดแบบอัลกอตัวไหนนั้นเอง (ผมใช้ newmm นั้นเอง) ** ปัจจุบัน Default เป็น atta-cut แล้ว **

ดูเพิ่มเติมที่ : https://thainlp.org/pythainlp/docs/1.7/api/tokenizer.html

2. ทดลองการวัดความรู้สึกของประโยค

***** ปัจจุบัน PyThaiNLP ตัด Sentiment ออกไปแล้ว ********

ต่อมาลองวัดความรู้สึกของประโยค นั้นคือการบอกว่าประโยคนั้นๆเป็นประโยคที่เป็นบวก (positive) หรือเป็นลบ (negative)

from pythainlp.sentiment import sentiment

ด้วยคำสั่ง sentiment เราจะได้ว่าประโยคนั้นๆมีความรู้สึกเป็นบวกหรือลบ เราจะลองด้วยคำสวยๆก็จะได้คำตอบประมาณนี้

text=”วันนี้อากาศดีจัง”
sentiment(text)
positive

แล้วลองด้วยคำแย่ๆ ให้ความรู้สึกเป็นลบ

text=”วันนี้แย่มากๆ”
sentiment(text)
negative

ดูเพิ่มเติม : https://thainlp.org/pythainlp/docs/1.7/api/sentiment.html

3. ทดลอง Thai2Vec

เป็นการเปลี่ยนคำให้เป็นตัวเลข !! (ไม่ใช่แบบ 1 hot นะจ้ะ) แต่จะแปลงคำให้เป็น vector ทำให้ตัวมันเองมีตำแหน่งเป็นของตัวเองในกราฟ

from pythainlp.word_vector import thai2vec
model=thai2vec.get_model()

จากนั้นลองแปลงคำเป็น vector

vec1 = model.wv.word_vec(‘หมา’)
vec2 = model.wv.word_vec(‘แมว’)
print(‘word1 =’,vec1,’word2 =’,vec2)

จะได้ผลลัพธ์ประมาณนี้

เอามาทำอะไร ?

เอาแบบที่ผมคิดว่าง่ายที่สุดคือ หาว่าทั้ง 2 คำนั้นคล้ายกันหรือไม่ ? เช่น พระราชา กับ ผู้ชาย ถ้าเราใช้ cosine_similarity ก็จะได้ว่าทั้ง 2 มันคล้ายกันขนาดไหนประมาณนี้

บทสรุป NLP

ที่จริงแล้ว NLP ไม่ได้ยากอย่างที่คิดและในไทยกำลังโตมากๆด้วย (สังเกตุได้ว่ารับสมัคร Data science แล้วจะต้องมีสกิล NLP, Data mining แทบทุกที่) หากใครกำลังจะทำงานด้านนี้จริงๆก็อยากจะให้ศึกษา NLP ไว้ด้วย ไม่ต้องลึกมาก แค่เบื้องต้นพอให้เข้าใจเท่านั้นก็เพียงพอแล้ว

ฝากกลุ่ม Thailand NLP ไว้ด้วยนะครับ

บทสรุปส่งท้ายบทความ Machine Learning

นี้เป็นบทความสุดท้ายของเจ้า ML แล้ว (ทั้งหมด 11บทความ) สังเกตุว่าในบทความแรกๆ [KNN/Evaluate/Train&test] จะเขียนแบบแปลกๆ เพราะในช่วงนั้นพึ่งกำลังฝึกเขียนอะไรแบบนี้ มีแต่คำสุภาพ 555555 หลังๆเริ่มกันเองมากขึ้น เริ่มเขียนด้วยความสนุก สักพักเริ่มจริงจัง ไปสอบใบ Cert (ช่วง Decistion Tree) ทำให้บทความเริ่มเข้มข้น เริ่มใช้เทคนิคพลังพิเศษมากขึ้นเรื่อย (คนเขียนยัง งงๆเองเลยบ้างครั้ง) สุดท้ายนี้หากใครกำลังฝึกฝนทักษะ ML / สนใจด้าน ML ก็หวังเป็นอย่างสูงว่าบทความทั้งหมดนี้จะสร้างประโยชน์แก่ท่านไม่มากก็รน้อย หากผิดพลาดประการใดขออภัยมา ณ ทีนี้ด้วย

บทความถัดไปจะเป็น :

Deep Learning แบบสามัญชน

Github : https://github.com/peeratpop/Machine_Learning_101

Medium : https://medium.com/@pingloaf

Linkedin : https://www.linkedin.com/in/peerat-limkonchotiwat/

บทความนี้เป็นส่วนหนึ่งของบทความ

เริ่มเรียน Machine/Deep Learning 0–100 (Introduction)

--

--

Mr.P L
mmp-li
Editor for

Lifestyle of Programmer & IoT (Node-RED|Blynk) & Data Science (ML,DL,NLP) and Whatever I want to do | cat can coding too | Ph.D. -> VISTEC -> IST