<jupyter_start><jupyter_text>ในงาน NLP การใช้งานจริง เช่น การรับ Input จาก User สิ่งที่เราจะได้พบเจออยู่ตลอด คือ User กรอกข้อมูลผิด ยิ่งข้อมูลที่ไม่ใช่ข้อมูลที่มีโครงสร้าง Structure Data, ไม่มี Data Type ตัวเลข หรือวันเวลา ที่มีรูปแบบแน่นอน แต่เป็นข้อความ Free Text ที่มีคำสะกดผิดปนอยู่ ทำให้การ Validate ข้อมูล อาจจะทำได้ยาก จากใน ep ที่แล้ว เราได้ใช้งาน PyThaiNLP ตัดคำภาษาไทย ตัดข้อความยาว ๆ Tokenization ออกมาเป็น Token เรียบร้อยแล้ว ใน ep นี้ เราจะมาดูว่า แต่ละ Token นั่นสะกดถูกหรือไหม Spellchecker รวมไปถึงแนะนำ และแก้ไขให้ถูกต้อง Spelling Correction ก่อนที่จะนำไปป้อนให้โมเดลในงานวิเคราะห์ทางด้าน NLP ต่อไป  0. Install<jupyter_code>%reload_ext autoreload
%autoreload 2
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Install Library ที่จำเป็น แล้ว Restart Runtime<jupyter_code># dev version
# !pip install https://github.com/PyThaiNLP/pythainlp/archive/dev.zip

# release version 
! pip install pythainlp<jupyter_output>Requirement already satisfied: pythainlp in /usr/local/lib/python3.6/dist-packages (2.1.4)
Requirement already satisfied: nltk>=3.3 in /usr/local/lib/python3.6/dist-packages (from pythainlp) (3.4.5)
Requirement already satisfied: requests>=2.22.0 in /usr/local/lib/python3.6/dist-packages (from pythainlp) (2.22.0)
Requirement already satisfied: tqdm>=4.1 in /usr/local/lib/python3.6/dist-packages (from pythainlp) (4.28.1)
Requirement already satisfied: dill>=0.3.0 in /usr/local/lib/python3.6/dist-packages (from pythainlp) (0.3.1.1)
Requirement already satisfied: tinydb>=3.0 in /usr/local/lib/python3.6/dist-packages (from pythainlp) (3.15.2)
Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from nltk>=3.3->pythainlp) (1.12.0)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests>=2.22.0->pythainlp) (1.24.3)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist[...]<jupyter_text>Restart Runtime เพื่อให้ใช้ Library เวอร์ชัน ที่เพิ่ง Install ลงไป ในเคสนี้ เราจะปิด Warning ไว้ก่อน จะได้อ่านผลลัพท์ง่าย<jupyter_code>import warnings
warnings.filterwarnings('ignore')<jupyter_output><empty_output><jupyter_text>1. Import Import Library ที่จะใช้ ในที่นี้คือ PyThaiNLP<jupyter_code>import pythainlp

pythainlp.__version__<jupyter_output><empty_output><jupyter_text>2. PyThaiNLP Spellchecker  2.1 Spellchecker โปรแกรมตรวจตัวสะกด  โดย Default แล้ว [Spellchecker](https://www.bualabs.com/archives/119/what-is-nlp-natural-language-processing-nlp-task-in-thai-nlp-ep-1/) ของ PyThaiNLP จะใช้อัลกอริทึม ของ <NAME> ที่จะหารายการคำใกล้เคียงจาก Dictionary โดยใช้จำนวนอักษรที่ผิด 1, 2, ... ตัวอักษร ผสมกับความน่าจะเป็น จากความถี่ของคำนั้นที่ปรากฎใน [Corpus](https://www.bualabs.com/archives/119/what-is-nlp-natural-language-processing-nlp-task-in-thai-nlp-ep-1/) โดยไม่ได้ใช้ Context หรือคำแวดล้อมที่มาก่อนหน้า หรือต่อจากนั้น และไม่ได้ใช้ตำแหน่งปุ่มใกล้เคียง บน Keyboard มาคำนวนความน่าจะเป็น<jupyter_code>from pythainlp import spell<jupyter_output><empty_output><jupyter_text>แสดงรายการคำ ทุกคำที่เป็นไปได้ ใน Dictionary เรียงตามความน่าจะเป็น<jupyter_code>spell("รั๊ว") # รัว ไม้ตรี
spell("กวาา") 
spell("น้ำสม")
spell("ศาธารณสุบ")<jupyter_output><empty_output><jupyter_text>ตัวเลขปน<jupyter_code>spell("ร้อ4")<jupyter_output><empty_output><jupyter_text>ปน ภาษาอังกฤษ ตัว o โอ<jupyter_code>spell("รั้o")<jupyter_output><empty_output><jupyter_text>ปน ภาษาอังกฤษ ตัว h<jupyter_code>spell("ชhอน")<jupyter_output><empty_output><jupyter_text>ภาษาอังกฤษล้วน ไม่ work เนื่องจาก โดย Default แล้ว Dictionary ของ PyThaiNLP มีแต่ภาษาไทย<jupyter_code>spell("miraclx")<jupyter_output><empty_output><jupyter_text>2.2 Spellchecker ด้วย Custom Dictionary และ Word Frequency โดย Default จะใช้ Dictionary จาก Thai National Corpus (TNC) ซึ่งจะมีคำศัพท์ ใน Corpus จำนวนเกือบ 4 หมื่นคำ (Default)<jupyter_code>from pythainlp.spell import NorvigSpellChecker
checker = NorvigSpellChecker()  # use default filter (remove any word with number or non-Thai character)
len(checker.dictionary())<jupyter_output><empty_output><jupyter_text>ดูตัวอย่าง คำศัพท์ และ ความถี่ ที่อยู่ใน Dictionary<jupyter_code>list(checker.dictionary())[:20]<jupyter_output><empty_output><jupyter_text>เราสามารถกำหนด Custom Dictionary เพิ่มคำที่เราต้องการได้เอง ในเคสนี้จะใช้ Thai Textbook Corpus (ttc) แทน tnc<jupyter_code>from pythainlp.corpus import ttc  # Thai Textbook Corpus
# ttc??
# NorvigSpellChecker??
checker = NorvigSpellChecker(custom_dict=ttc.word_freqs())
checker.spell("เจียง")<jupyter_output><empty_output><jupyter_text>นับจำนวนคำทั้งหมดใน Dictionary จาก ttc จะเห็นว่าไม่เท่ากัน เนื่องจากการสร้าง Spellchecker จะมีกำหนด Parameter เพื่อกรอง Filter คำศัพท์ ที่มีความถี่น้อยเกินไป, ความยาวน้อยเกินไป, ยาวเกินไปทิ้งไป, etc. ก่อนที่จะนำไปใช้<jupyter_code>len(checker.dictionary()), len(ttc.word_freqs())<jupyter_output><empty_output><jupyter_text>ดูตัวอย่าง คำศัพท์จาก ttc ที่ filter มาแล้ว<jupyter_code>list(checker.dictionary())[0:20]<jupyter_output><empty_output><jupyter_text>เราสามารถเลือก กำหนด parameter ของ Dictionary ของ Spellchecker ได้ตามต้องการ ดังนี้NorvigSpellChecker* custom_dict (str) – list of tuple (คำศัพท์, ความถี่) สำหรับสร้าง Dictionary ที่จะใช้ใน Spellchecker (Default คือ Thai National Corpus TNC ที่ตามเงื่อนไข Default ด้านล่าง แล้วจะมีคำศัพท์ประมาณ 40,000 คำ)* min_freq (int) – ความถี่น้อยที่สุดจะเอาไว้ ใน Dictionary (default = 2)* min_len (int) – ความยาวน้อยที่สุดที่จะเอาไว้ ใน Dictionary (เป็นตัวอักษร) (default = 2)* max_len (int) – ความยาวสูงสุดของคำที่จะเอาไว้ ใน Dictionary (default = 40)* dict_filter (func) – ฟังก์ชันที่จะกรอง Dictionary โดย Default แล้วจะลบคำที่มีตัวเลข และคำที่ไม่ใช่ภาษาไทยทิ้งไป จาก Dictionary ถ้าไม่ต้องการใช้ Filter ให้ใส่ Noneในเคสนี้ไม่กำหนด custom_dict คือกลับไปใช้ TNC<jupyter_code>checker = NorvigSpellChecker(min_freq=5, min_len=2, max_len=15)
len(checker.dictionary())<jupyter_output><empty_output><jupyter_text>เอา TNC มาใช้ แบบเต็ม ๆ ไม่กรองตัวเลข และตัวอักษรภาษาอื่น ๆ<jupyter_code>checker_no_filter = NorvigSpellChecker(dict_filter=None)  # use no filter
len(checker_no_filter.dictionary())<jupyter_output><empty_output><jupyter_text>ตัวอย่างการกรอง Filter ที่ซับซ้อนขึ้นกว่า ความยาว ความถี่ ด้วย dict_filter เช่น ฟังก์ชันลบคำศัพท์ที่มี อักษร ไปยาลน้อย<jupyter_code>def remove_smallpiyan(word):
    return False if "ฯ" in word else True

checker_custom_filter = NorvigSpellChecker(dict_filter=remove_smallpiyan)  # use custom filter
len(checker_custom_filter.dictionary())<jupyter_output><empty_output><jupyter_text>3. PyThaiNLP Spelling Correction ส่วน Spelling Correction คือการเลือกคำที่มีความน่าจะเป็นสูงสุด ในที่นี้ คือ คำแรกของ List รายการที่ได้จาก Spellchecker ด้านบน<jupyter_code>from pythainlp import correct
correct("รั๊ว") # รัว ไม้ตรี
correct("กวาา") 
correct("น้ำสม")
correct("ศาธารณสุบ")<jupyter_output><empty_output><jupyter_text>ตัวเลขปน<jupyter_code>correct("ร้อ4")<jupyter_output><empty_output><jupyter_text>ปน ภาษาอังกฤษ ตัว o โอ<jupyter_code>correct("รั้o")<jupyter_output><empty_output><jupyter_text>ปน ภาษาอังกฤษ ตัว h<jupyter_code>correct("ชhอน")<jupyter_output><empty_output><jupyter_text>ภาษาอังกฤษล้วน ไม่ work เนื่องจาก โดย Default แล้ว Dictionary ของ PyThaiNLP มีแต่ภาษาไทย<jupyter_code>correct("miraclx")<jupyter_output><empty_output><jupyter_text>4. Normalize ข้อความ บางทีข้อความอาจจะมีการใส่ตัวอักษรซ้ำ ๆ เบิ้ลมา เราต้องจัดการ ลบสระ วรรณยุกต์ที่ซ้ำซ้อน ทิ้งไป ดังตัวอย่าง<jupyter_code>from pythainlp.util import normalize
# normalize??
normalize("กิิิ่ง") == "กิ่ง"  # กิ่ง สระ อิ 3 ตัว
normalize("ก้าาาน") == "ก้าน"  # ก้าน สระ อา 3 ตัว
normalize("ก้้้าน") == "ก้าน"   # ก้าน ไม้โท 3 ตัว<jupyter_output><empty_output><jupyter_text>พยัญชนะไม่เกี่ยว<jupyter_code>normalize("ก้าาานน") == "ก้าน"  
normalize("ก้านน") == "ก้าน"  
normalize("สััั้้้น") == "สั้น"  # สั้น ไม้หันอากาศ 3 ตัว และ ไม้โท 3 ตัว<jupyter_output><empty_output><jupyter_text>ต้องสระ หรือ วรรณยุกต์ เดียวกัน ซ้ำ ๆ<jupyter_code>normalize("ใใบ") == "ใบ"  
normalize("ไใบ") == "ใบ"<jupyter_output><empty_output><jupyter_text>สระเอ 2 ตัว แปลงเป็น สระแอ<jupyter_code>normalize("เเปลก") == "แปลก"  # เ เ ป ล ก  vs แ ป ล ก<jupyter_output><empty_output><jupyter_text>Credit * https://www.thainlp.org/pythainlp/tutorials/notebooks/pythainlp_get_started.html* http://www.norvig.com/spell-correct.html* https://thainlp.org/pythainlp/docs/2.0/api/spell.html* http://www.arts.chula.ac.th/ling/tnc/* https://www.facebook.com/photo.php?fbid=363640477387469&set=gm.434330506948445&type=3&permPage=1<jupyter_code><jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15623899518722856, 'fraction_numerical': 0.020072778495128535, 'mean_word_length': 9.142857142857142, 'pattern_counts': {'":': 0, '<': 114, '<?xml version=': 0, '>': 124, 'https://': 6, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '28', 'dir': 'jupyter-structured-clean-dedup', 'id': '288244', 'license': 'MIT', 'n_tokens_mistral': 4885, 'n_tokens_neox': 5110, 'n_words': 554, 'path': 'nbs/27c_pythainlp_spellchecking.ipynb', 'repo_name': 'gnoparus/bualabs', 'size': '8525'}