ข้ามไปยังเนื้อหา
KoishiAI
EN
← กลับไปยังบทความทั้งหมด
nlp thai-nlp pythainlp python tutorial how-this-site-is-built

วัดภาษาไทยด้วยโปรแกรม: ทำไม .split() กับ crfcut ถึงหลอกคุณ และ "วรรค" ช่วยได้อย่างไร

บทเรียนจากการสร้างระบบวัดความเป็นธรรมชาติของบทความไทย: กับดักการนับคำและตัดประโยคที่พังแบบเงียบๆ และวิธีวัดจังหวะภาษาไทยด้วยหน่วยที่ภาษามีจริง

KoishiAI · บรรณาธิการ: เกียรติดำรง ตรีครุธพันธ์ · · 20 นาทีในการอ่าน
บทความนี้ AI เขียนจากแหล่งอ้างอิง ผ่านการตรวจสอบข้อเท็จจริงและกลั่นกรองโดยบรรณาธิการ วิธีทำงาน · มาตรฐาน · แจ้งข้อผิดพลาด
กราฟแท่งเทียบสัดส่วนวรรคสั้นระหว่างข่าวไทยที่นักข่าวเขียน (0.404-0.611) กับบทความที่ AI เขียน (0.158-0.400) ซึ่งแทบไม่ทับกัน
กราฟโดย KoishiAI จากการวัดจริง

ถ้าคุณเคยเขียนโค้ดวิเคราะห์ข้อความภาษาอังกฤษ คุณจะติดนิสัยสองอย่างมา: นับคำด้วยการแยกช่องว่าง และนับประโยคด้วยเครื่องหมายจุด พอเจอภาษาไทย นิสัยทั้งสองอย่างจะทรยศคุณทันที และที่ร้ายคือมันทรยศแบบเงียบๆ โค้ดไม่พัง ตัวเลขออกมาสวย แค่ผิดหมด

บทความนี้เล่าจากประสบการณ์จริงของเว็บนี้เอง: เราสร้างระบบวัด “ความเป็นธรรมชาติ” ของบทความไทยที่ AI เขียน เทียบกับข่าวไทยที่นักข่าวจริงเขียน แล้วพบว่าก่อนจะวัดอะไรได้เลย ต้องตอบคำถามพื้นฐานที่สุดให้ถูกก่อน — หน่วยของภาษาไทยคืออะไรกันแน่?

กับดักที่ 1 — .split() นับคำไม่ได้ เพราะไทยไม่เว้นวรรคระหว่างคำ

ภาษาไทยเขียนติดกันทั้งวรรค ไม่มีช่องว่างระหว่างคำ ดังนั้น len(text.split()) ที่ใช้ได้ดีในภาษาอังกฤษ จะนับ “วรรคทั้งวรรค” เป็นหนึ่งคำ

เราเจอเรื่องนี้ด้วยตัวเอง ครั้งแรกที่เทียบความยาวบทความสองภาษา ระบบรายงานว่าบทความไทย “สั้นกว่า” ฉบับอังกฤษมาก ทั้งที่เนื้อหาเดียวกัน เพราะฝั่งอังกฤษนับคำจริง แต่ฝั่งไทยนับวรรค

วิธีที่ถูกคือใช้ตัวตัดคำ (word tokenizer) ของ PyThaiNLP:

from pythainlp import word_tokenize

text = "ภาษาไทยเขียนติดกันทั้งประโยค"
print(len(text.split()))          # 1  <- ผิด
print(len(word_tokenize(text)))   # 6  <- ["ภาษาไทย", "เขียน", "ติดกัน", ...]

เอนจินเริ่มต้นชื่อ newmm เป็นแบบอิงพจนานุกรม เร็วพอจะรันในโปรดักชันได้สบาย

กับดักที่ 2 — crfcut ตัดประโยคได้ จนกระทั่งเจอคำอังกฤษ

ปัญหาถัดมาลึกกว่านั้น: ภาษาไทยไม่มีเครื่องหมายจบประโยค ไม่มีจุด ไม่มีอะไรบอกว่าประโยคจบตรงไหน PyThaiNLP จึงมีตัวตัดประโยคชื่อ crfcut ซึ่งเป็นโมเดล CRF ที่เรียนรู้จากการเว้นวรรคของคนจริง ฟังดูดี แต่ในทางปฏิบัติมีกับดักซ้อนกันสองชั้น

ชั้นแรกคือ dependency ที่พังเงียบ — crfcut ต้องมีแพ็กเกจ python-crfsuite ติดตั้งอยู่ ถ้าไม่มี PyThaiNLP จะไม่ error แต่ถอยไปตัดด้วยช่องว่างแทนโดยไม่บอกใคร ผลคือได้ “หนึ่งประโยคต่อหนึ่งคำ” แล้วสถิติทั้งกระดานก็เพี้ยนโดยไม่มีใครสังเกต

ชั้นที่สองคือข้อความจริงมีคำอังกฤษปนเสมอ — บทความไอทีภาษาไทยเต็มไปด้วยชื่อผลิตภัณฑ์และศัพท์เทคนิคภาษาอังกฤษ เราวัดพฤติกรรม crfcut กับย่อหน้าข่าวจริง: ข้อความไทยล้วนตัดถูก 3 ใน 3 จุด แต่พอย่อหน้ามีชื่อผลิตภัณฑ์ภาษาอังกฤษแทรกเข้ามา ความแม่นตกเหลือ 1 ใน 4 ลองแก้ด้วยการ mask คำอังกฤษก่อนตัดก็ไม่รอด เพราะคราวนี้มันสร้างขอบเขตประโยคขึ้นเองกลางชื่อสินค้า เช่นหั่น “Gemini 3.6” ขาดจาก “Flash”

บทเรียนที่แพงที่สุดคือมันไม่ได้แค่ผิด แต่ผิดแบบดูดี เศษ segment ที่ crfcut สร้างขึ้นมา เคยทำให้บทความชิ้นหนึ่งของเราได้คะแนน “สัดส่วนประโยคสั้น” 0.472 ซึ่งดีที่สุดในชุดทดสอบ ทั้งที่พอวัดใหม่ด้วยวิธีที่ตรงไปตรงมา ค่าจริงคือ 0.258 — ตัวเลขสวยจากเครื่องมือพัง อันตรายกว่าตัวเลขแย่จากเครื่องมือดีเสมอ

ทางออก — วัดในหน่วยที่ภาษาไทยมีจริง: วรรค

ถอยหลังมาหนึ่งก้าวแล้วถามใหม่: ผู้เขียนภาษาไทยทำเครื่องหมายอะไรไว้ในข้อความบ้าง? คำตอบเดียวที่มีคือ วรรค — ช่องว่างที่ตั้งใจเคาะเพื่อแบ่งจังหวะการอ่าน แทนที่จะฝืนหา “ประโยค” ที่ภาษาไม่ได้กำกับไว้ เราเปลี่ยนไปวัดในหน่วยวรรคแทน แล้วพบว่าไม่ต้องใช้โมเดลอะไรเลย:

import re

# ตัดเฉพาะช่องว่างที่ "ขนาบด้วยอักษรไทยทั้งสองฝั่ง"
THAI = "ก-๛"
CLAUSE_SPLIT = re.compile(rf"(?<=[{THAI}]) (?=[{THAI}])")

def thai_clauses(paragraph: str) -> list[str]:
    return [c.strip() for c in CLAUSE_SPLIT.split(paragraph) if c.strip()]

ความฉลาดอยู่ที่ lookbehind กับ lookahead คู่นี้: ช่องว่างใน “Gemini 3.6 Flash” หรือ “$0.30 ต่อล้านโทเค็น” จะไม่ถูกตัด เพราะมีอย่างน้อยหนึ่งฝั่งไม่ใช่อักษรไทย ชื่อผลิตภัณฑ์กับตัวเลขจึงรอดมาทั้งก้อน โดยไม่ต้องเขียน exception list สักบรรทัด

แล้ววัดอะไรจากวรรคได้บ้าง

พอได้หน่วยที่ไว้ใจได้ ก็สร้างตัวชี้วัดง่ายๆ ได้สามตัว:

  1. สัดส่วนวรรคสั้น — วรรคที่ยาวไม่เกิน 10 คำ (นับด้วย newmm) คิดเป็นกี่ส่วนของทั้งหมด
  2. ค่าความแปรปรวนของความยาววรรค (CV) — ยิ่งสูง จังหวะยิ่งหลากหลาย
  3. จำนวนคำต่อวรรคเฉลี่ย

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

ตัวชี้วัด (ระดับวรรค)ข่าวไทยที่คนเขียน (มัธยฐาน)บทความ AI ของเรา
สัดส่วนวรรคสั้น ≤ 10 คำ0.611 (ต่ำสุด 0.404)0.158 – 0.400
CV ความยาววรรค0.866ต่ำกว่าเกณฑ์เกือบทุกชิ้น
คำต่อวรรค10.7 (สูงสุด 18.65)ยาวกว่าอย่างชัดเจน

แถวแรกคือช่องว่างที่ชัดที่สุดที่เราเคยวัดได้: บทความ AI ของเรา 4 ใน 5 ชิ้น มีสัดส่วนวรรคสั้นต่ำกว่าข่าวจริงทุกชิ้นในคลังอ้างอิง แปลเป็นภาษาคนคือ AI เขียนไทยเป็นวรรคยาวเนิบเสมอกันหมด ส่วนนักข่าวจริงสลับสั้นยาวตลอดเวลา สิ่งที่เราเคยเรียกกันลอยๆ ว่า “จังหวะภาษาไทย” ตอนนี้เป็นตัวเลขที่วัดซ้ำได้ ไม่ใช่ความรู้สึก

โบนัส: กติกาเว้นวรรคก็เขียนเป็น regex ได้

การเว้นวรรคภาษาไทยมีแบบแผนอยู่แล้วตามหลักภาษาและแนวทางกองบรรณาธิการ และส่วนใหญ่ตรวจอัตโนมัติได้ด้วย regex ตรงๆ:

RULES = [
    ("หลังไม้ยมก ๆ ต้องเว้นวรรค",  r"[^\s\)\]\.,:;]"),
    ("หลังคอมม่าต้องเว้นวรรค",     r",(?!\d{3}\b)\S"),   # ยกเว้น 1,000
    ("ห้ามเว้นวรรคในวงเล็บ",       r"\(\s|\s\)"),
    ("เลขกับ % เขียนติดกัน",        r"\d\s%"),
]

สี่บรรทัดนี้จับข้อผิดพลาดการพิมพ์ภาษาไทยที่พบบ่อยที่สุดได้ โดยไม่ต้องเรียกโมเดลภาษาแม้แต่ครั้งเดียว

สามบทเรียนที่อยากให้จำ

หนึ่ง — หน่วยของเครื่องมือไม่ใช่หน่วยของภาษา อย่าฝืนวัด “ประโยค” ในภาษาที่ไม่ได้ทำเครื่องหมายประโยค จงวัดวรรคที่ผู้เขียนทำเครื่องหมายไว้จริง

สอง — เครื่องมือที่พังเงียบ อันตรายกว่าเครื่องมือที่พังดัง ทั้ง crfcut ที่ถอยไปตัดช่องว่างเมื่อขาด dependency และเศษ segment ที่ทำให้คะแนนดูดีขึ้นทั้งที่ข้อมูลผิด ระบบวัดที่ดีต้องสงสัยตัวเลขที่สวยเกินเหตุก่อนเสมอ

สาม — เกณฑ์ต้องมาจากการวัด ไม่ใช่การเดา ครั้งแรกเราตั้งเป้าจากตัวอย่างเพียง 5 ชิ้นแล้วได้เป้าผิดทิศไปสองตัว กว่าจะรู้ก็ตอนวัดกับข่าวจริง 29 ชิ้น และต้องอ่านผลอย่างถ่อมตัวด้วย เพราะข่าวจริงเองก็ยังตกบางเกณฑ์ การตกเกณฑ์จึงแปลว่า “แย่กว่าข่าวจริงส่วนใหญ่ในแกนนั้น” ไม่ใช่ “ใช้ไม่ได้”

ทั้งหมดนี้ไม่ใช่ทฤษฎี มันรันอยู่จริงทุกวันในระบบผลิตบทความของเว็บที่คุณกำลังอ่านอยู่ — เป็นไม้บรรทัดที่ตัดสินว่าดราฟต์ไหนผ่าน ดราฟต์ไหนต้องกลับไปแก้ ถ้าคุณกำลังสร้างระบบ NLP ภาษาไทยของตัวเอง หวังว่าแผลของเราจะช่วยให้คุณไม่ต้องเจ็บซ้ำที่เดิม

แหล่งข้อมูล

  1. PyThaiNLP — ไลบรารีประมวลผลภาษาไทยแบบโอเพนซอร์ส
  2. PyThaiNLP บน GitHub

คำถามที่พบบ่อย

ทำไมใช้ .split() นับคำภาษาไทยไม่ได้?
เพราะภาษาไทยเขียนติดกันทั้งวรรคโดยไม่มีช่องว่างระหว่างคำ การแยกด้วยช่องว่างจึงนับทั้งวรรคเป็นหนึ่งคำ ต้องใช้ตัวตัดคำอย่าง pythainlp.word_tokenize ซึ่งใช้เอนจิน newmm เป็นค่าเริ่มต้น
crfcut ของ PyThaiNLP มีข้อจำกัดอะไร?
สองอย่าง หนึ่งคือถ้าไม่ได้ติดตั้ง python-crfsuite มันจะถอยไปตัดด้วยช่องว่างแบบเงียบๆ โดยไม่แจ้งเตือน สองคือเมื่อข้อความมีคำภาษาอังกฤษปน ความแม่นยำในการหาขอบเขตประโยคตกลงอย่างมาก จากการวัดของเราเหลือถูก 1 ใน 4 จุด
วรรคคืออะไร และทำไมถึงใช้วัดแทนประโยค?
วรรคคือหน่วยข้อความที่คั่นด้วยการเว้นวรรค ซึ่งเป็นเครื่องหมายเดียวที่ผู้เขียนภาษาไทยตั้งใจใส่ไว้จริง ต่างจาก "ประโยค" ที่ภาษาไทยไม่มีเครื่องหมายกำกับ การวัดในหน่วยวรรคจึงไม่ต้องใช้โมเดลทำนายและได้ผลที่ตรวจสอบซ้ำได้เสมอ
จะเริ่มวัดข้อความไทยของตัวเองต้องใช้อะไรบ้าง?
ติดตั้ง pythainlp กับ python-crfsuite แล้วใช้ word_tokenize นับคำ ส่วนการตัดวรรคใช้ regex ตัดช่องว่างที่ขนาบด้วยอักษรไทยสองฝั่งตามตัวอย่างในบทความ จากนั้นคำนวณสัดส่วนวรรคสั้น ความแปรปรวนของความยาววรรค และคำต่อวรรคได้ทันที