จาก PDF ภาษาไทยสู่ดัชนีค้นหา: OCR การแบ่งชิ้น และช่องว่างที่หายไป
วิธีเปลี่ยนเอกสารสแกนภาษาไทยเป็นชิ้นข้อความที่ดัชนีค้นหาหรือโมเดลภาษาใช้ได้ ตั้งแต่การอ่านหน้าไปจนถึงจุดตัดชิ้น เมื่อข้อความไม่มีช่องว่างให้ตัด
· อ่าน 2 นาที
เอกสารต้นทางภาษาไทยที่องค์กรอยากค้นหาส่วนใหญ่มาในรูปสแกน หนังสือเวียนราชการ สัญญา รายงานการประชุม และรายงานประจำปีรุ่นเก่า คือ PDF ของกระดาษ และฉบับที่มีชั้นข้อความก็มักมีชั้นข้อความที่เสีย พออ่านหน้าออกมาแล้ว ข้อความไม่มีช่องว่างระหว่างคำและไม่มีจุดระหว่างประโยค ตัวแบ่งชิ้นที่เขียนไว้สำหรับภาษาอังกฤษจึงตัดผิดที่
บทความนี้พาเอกสารภาษาไทยเดินจากสแกนไปจนเป็นชิ้นในดัชนี ครอบคลุมการอ่านหน้าเป็นโครงสร้าง เหตุที่หน้าต่างความยาวคงที่ใช้กับภาษาไทยไม่ได้ ควรตัดตรงไหนแทน ต้องเก็บอะไรติดไปกับแต่ละชิ้น และต้องปรับรูปอะไรก่อนทำดัชนี ตัวอย่างใช้ Paxa OCR Lite เป็นตัวอ่าน ส่วนวิธีแบ่งชิ้นใช้ได้กับตัวอ่านใดก็ตามที่คืนโครงสร้าง
สแกนและชั้นข้อความที่เสีย#
PDF ภาษาไทยเป็นได้สองอย่าง สแกนไม่มีข้อความเลย และทุกตัวอักษรบนหน้าต้องอ่านจากพิกเซล PDF ที่สร้างจากไฟล์ดิจิทัลมีชั้นข้อความ และชั้นข้อความภาษาไทยเสียในแบบที่คู่มือดึงข้อความรวบรวมไว้ คือวรรณยุกต์ที่เก็บผิดลำดับ ฟอนต์รุ่นเก่าที่จับคู่ไบต์ผิดตัวอักษร และคอลัมน์ที่กลับมาสลับกัน ทั้งสองกรณีลงเอยที่เดียวกัน คือต้องอ่านหน้าเป็นหน้า
ไปป์ไลน์ที่ลองชั้นข้อความก่อนแล้วค่อยสำรองด้วย OCR เมื่อล้มเหลว ต้องมีวิธีตรวจว่าล้มเหลว และชั้นข้อความภาษาไทยที่เสียก็ยังดูเป็นข้อความสำหรับทุกการตรวจ ยกเว้นให้คนไทยอ่าน การอ่านทุกหน้าด้วยวิธีเดียวกันมีต้นทุนไม่กี่เครดิตต่อหน้า และตัดเงื่อนไขแยกทางนั้นทิ้งไป
อ่านเอกสารเป็นบล็อก#
ขอโครงสร้างตั้งแต่ขั้นอ่าน เมื่อตั้ง output เป็น "structured" POST /v1/ocr จะคืนแต่ละหน้าเป็นบล็อกที่ระบุชนิดตามลำดับการอ่าน คือหัวข้อพร้อมระดับ ย่อหน้า รายการพร้อมข้อย่อย ตารางเป็นแถวของช่อง และรูปพร้อมคำบรรยาย บรรทัดด้านล่างคือบล็อกแบบที่หน้าหนึ่งของระเบียบคืนกลับมา
{ "type": "heading", "level": 1, "text": "ระเบียบการเบิกค่าเดินทาง" }
{ "type": "heading", "level": 2, "text": "หมวด ๒ อัตราค่าพาหนะ" }
{ "type": "paragraph", "text": "ข้อ ๗ การเดินทางโดยรถไฟให้เบิกได้ตามที่จ่ายจริง ..." }
{ "type": "table", "rows": [["ประเภท", "อัตรา (บาท/กม.)"], ["รถยนต์ส่วนตัว", "๔"], ["รถจักรยานยนต์", "๒"]] }หนึ่งคำขออ่านได้ไม่เกิน 50 หน้าและ 10 MiB เอกสารที่ยาวกว่านั้นส่งเป็นส่วน ๆ ให้เลขหน้าต่อเนื่องข้ามส่วน เพราะเลขหน้าคือสิ่งที่การอ้างอิงชี้ไป ผลลัพธ์แบบ Markdown มีการอ่านชุดเดียวกัน โดยโครงสร้างอยู่ในรูปเครื่องหมายหัวข้อและตารางแบบขีดตั้ง ไปป์ไลน์ที่แยกวิเคราะห์ Markdown อยู่แล้วเริ่มจากตรงนั้นได้ ส่วนบล็อกช่วยให้ไม่ต้องแยกวิเคราะห์เลย
ทำไมหน้าต่างความยาวคงที่ใช้กับภาษาไทยไม่ได้#
ตัวแบ่งชิ้นทั่วไปตัดหน้าต่างยาวไม่กี่ร้อยตัวอักษร เลื่อนไปข้างหน้าโดยซ้อนทับกันบางส่วน และเชื่อว่าจุดตัดที่ใกล้ช่องว่างหรือจุดจะใกล้กับจุดแบ่งความหมาย ภาษาอังกฤษให้ช่องว่างทุกห้าตัวอักษรและจุดทุกยี่สิบคำ ภาษาไทยให้ช่องว่างทุกวลีและไม่มีอะไรระหว่างคำ หน้าต่างที่จบตามจำนวนตัวอักษรจึงจบกลางคำเกือบทุกครั้ง และจบกลางประโยคแทบทุกครั้ง
การซ้อนทับซ่อนความเสียหายไว้โดยไม่ได้แก้ คำที่ถูกตัดครึ่งไปปรากฏเต็มคำในชิ้นถัดไป ส่วนครึ่งที่ค้างในชิ้นนี้คือสายอักขระที่ไม่มีคำค้นใดตรงกัน ประโยคที่ถูกแยกออกเป็นสองชิ้นมีประธานอยู่ชิ้นหนึ่งและข้อสรุปอยู่อีกชิ้น การค้นคืนที่ได้ชิ้นใดชิ้นหนึ่งมาก็ได้เศษที่ตอบอะไรไม่ได้
ตัวแบ่งชิ้นที่นับโทเค็นก็มีปัญหาเดียวกันเพียงถอยไปหนึ่งขั้น ขอบเขตโทเค็นเป็นคุณสมบัติของตัวตัดโทเค็น และมันไม่รู้ว่าคำภาษาไทยจบตรงไหนมากไปกว่าการนับตัวอักษร
ตัดตามโครงสร้างที่ตัวอ่านคืนมา#
เอกสารมีจุดแบ่งของตัวเองอยู่แล้ว หัวข้อเปิดส่วน ย่อหน้าคือหน่วยความหมายที่ผู้เขียนเลือกไว้ และตารางคือวัตถุชิ้นเดียว ตัวแบ่งชิ้นที่เดินตามบล็อกต้องการกฎสามข้อ และไม่ต้องใช้พจนานุกรมภาษาไทยเลย
- ย่อหน้าคือชิ้นตั้งต้น รวมย่อหน้าสั้น ๆ ที่ต่อกันภายใต้หัวข้อเดียวจนถึงเพดานขนาด และห้ามรวมข้ามหัวข้อ
- ตารางคือหนึ่งชิ้นทั้งตาราง โดยเก็บแถวหัวตารางไว้ แถวเดี่ยว ๆ เสียชื่อคอลัมน์ไป และตารางที่ถูกแบ่งตามขนาดเสียแถวที่ตอบคำถามไป
- รายการคือหนึ่งชิ้นเมื่อพอดีเพดาน เมื่อไม่พอ ให้แบ่งระหว่างข้อ อย่าแบ่งกลางข้อ
ย่อหน้าที่เกินเพดานด้วยตัวเองคือที่เดียวที่ต้องตัดกลางร้อยแก้ว ให้ตัดที่วรรค ซึ่งอย่างน้อยเป็นจุดแบ่งวลีและบ่อยครั้งเป็นจุดจบประโยค และเลือกวรรคที่ใกล้เพดานที่สุดจากด้านล่าง วิธีนี้เก็บวลีไว้ด้วยกัน และไม่มีวันตกกลางคำ บทความเรื่องการตัดคำอธิบายว่าทำไมการตัดกลางคำในภาษาไทยจึงแย่กว่าการตัดกลางประโยค
เพดานขนาดกำหนดโดยโมเดลที่อ่านชิ้นและจำนวนชิ้นที่คืนต่อคำถาม ให้ตั้งเป็นจำนวนตัวอักษรไทย โดยวัดจากคลังเอกสารจริง ตัวอักษรไทยหนึ่งตัวบรรจุส่วนของคำมากกว่าตัวอักษรละติน และเพดานที่ปรับบนร้อยแก้วภาษาอังกฤษจะหลวมเกินไปสำหรับภาษาไทย
เก็บสายหัวข้อและเลขหน้าติดไปด้วย#
ย่อหน้าภายใต้ หมวด ๒ อัตราค่าพาหนะ มีความหมายต่างจากย่อหน้าเดียวกันภายใต้ หมวด ๓ ให้เติมสายหัวข้อไว้หน้าข้อความของแต่ละชิ้นเมื่อส่งไปทำเวกเตอร์ และเก็บสายหัวข้อเป็นฟิลด์สำหรับแสดงผล เก็บเลขหน้าด้วย ผู้ใช้ที่ถามว่ากฎข้อนี้มาจากไหนต้องการเลขหน้า และผู้ตรวจที่ตรวจผลการอ่านต้องการเปิดสแกนที่หน้านั้น
{
"doc": "travel-regulation-2569.pdf",
"page": 3,
"trail": ["ระเบียบการเบิกค่าเดินทาง", "หมวด ๒ อัตราค่าพาหนะ"],
"text": "ข้อ ๗ การเดินทางโดยรถไฟให้เบิกได้ตามที่จ่ายจริง ...",
"match": "ข้อ 7 การเดินทางโดยรถไฟให้เบิกได้ตามที่จ่ายจริง ..."
}เลขข้ออย่าง ข้อ ๗ ควรมีฟิลด์ของตัวเองในระเบียบหรือสัญญา เพราะคำถามมักระบุเลขข้อ ตัวอ่านคืนเลขตามที่พิมพ์ ไปป์ไลน์อ่านมันจากต้นย่อหน้า
ปรับรูปสำเนาสำหรับจับคู่ เก็บต้นฉบับไว้#
เอกสารภาษาไทยมีค่าเดียวกันหลายแบบเขียน และคำค้นใช้แบบใดแบบหนึ่ง ให้สร้างสำเนาที่สองของแต่ละชิ้นไว้จับคู่ และปล่อยต้นฉบับไว้แสดงผลและอ้างอิง
- เลขไทยเป็นเลขอารบิก ๒๕๖๙ กับ 2569 คือปีเดียวกัน และเอกสารหนึ่งหน้าปนทั้งสองแบบ
- ปีพุทธศักราชคงไว้ตามที่พิมพ์ และเก็บปีคริสต์ศักราชในฟิลด์แยก การลบ 543 ในตัวข้อความทำให้การอ้างอิงเพี้ยน
- สระอำเป็นรหัสเดียว เอกสารบางฉบับเก็บเป็นนิคหิตตามด้วยสระอา ซึ่งการปรับรูปยูนิโค้ดไม่แตะ และสองแบบนี้ไม่มีวันตรงกัน
- ยูนิโค้ด NFC ซึ่งแก้ลำดับของเครื่องหมายซ้อนที่ชั้นข้อความเสียสร้างขึ้น
- ช่องว่างยุบเหลือหนึ่งช่อง และลบอักขระความกว้างศูนย์ ตัวอ่านไม่คืนอักขระพวกนี้เลย ส่วนชั้นข้อความคืนมาเยอะ
ปรับรูปคำค้นด้วยวิธีเดียวกัน ความไม่ตรงกันระหว่างสองฝั่งคือสาเหตุที่พบบ่อยที่สุดที่คำซึ่งเห็นอยู่บนหน้ากลับหาไม่เจอ
ดัชนีคำ ดัชนีเวกเตอร์ หรือทั้งคู่#
ดัชนีเวกเตอร์ฝังชิ้นเป็นเวกเตอร์และไม่ต้องรู้ขอบเขตคำ โมเดลฝังอ่านตัวอักษร และจุดตัดชิ้นด้านบนคือการตัดสินใจเฉพาะภาษาไทยเพียงข้อเดียว ดัชนีคำเก็บเทอม และเทอมคือคำ นั่นหมายความว่าดัชนีคำภาษาไทยต้องมีตัวตัดคำทั้งฝั่งชิ้นและฝั่งคำค้น ใช้ตัวเดียวกันทั้งสองฝั่ง และต้องสร้างดัชนีใหม่เมื่อเปลี่ยนตัวตัดคำ
สายอักขระตรงตัวคือจุดที่ฝั่งดัชนีคำคุ้มค่า เลขข้อ เลขที่เอกสาร เลขประจำตัวผู้เสียภาษี จำนวนเงิน และชื่อ คือสิ่งที่ดัชนีเวกเตอร์เบลอและดัชนีเทอมจับได้แม่น ระบบผสมที่ให้คะแนนทั้งสองฝั่งแล้วรวมรายการรับมือกับระเบียบได้ดี และสำเนาที่ปรับรูปแล้วคือสิ่งที่ฝั่งดัชนีคำใช้ทำดัชนี
รายงานห้าสิบหน้าอ่านแล้วเสียเท่าไร#
หน้าหนึ่งคิด 6.5 เครดิตไม่ว่าจะมีอะไรอยู่ และรู้จำนวนหน้าก่อนคิดค่าใช้จ่าย รายงาน 50 หน้าคือ 325 เครดิต หรือ $0.33 อ่านเอกสารครั้งเดียว แล้วสร้างชิ้นใหม่จากบล็อกที่เก็บไว้ทุกครั้งที่กฎการแบ่งชิ้นเปลี่ยน อ่านหน้าซ้ำเฉพาะเมื่อหน้านั้นเปลี่ยน
เก็บบล็อกไว้ด้วย นอกเหนือจากชิ้น เพดานขนาด รูปแบบสายหัวข้อ และการปรับรูป ล้วนเปลี่ยนไประหว่างจูนดัชนี และแต่ละครั้งคือการสร้างใหม่จากบล็อกที่เก็บไว้โดยไม่มีค่าใช้จ่าย
ขั้นต่อไป#
- อ่านคู่มือ OCRสำหรับรูปแบบผลลัพธ์และขีดจำกัด และเอกสารอ้างอิงสำหรับฟิลด์ของบล็อก
- อ่านบทความเรื่องการดึงข้อความเพื่อดูว่าชั้นข้อความภาษาไทยผิดตรงไหน
- เปิดเครื่องมือเอกสารเพื่ออ่านหน้าหนึ่งและดูบล็อกที่คืนกลับมา