ทำ Data Extraction จาก image โดยใช้ LLM Multimodal

ทำ Data Extraction จาก image โดยใช้ LLM Multimodal

ในการทำ Data Extraction จากรูปภาพ เช่น สลิปใบเสร็จ, บัตรประชาชน, หรือแบบฟอร์มกระดาษ วิธีดั้งเดิมมักใช้ OCR (Optical Character Recognition) ร่วมกับการเขียน rule หรือ regex เพื่อแยกข้อมูลออกมา ซึ่งยุ่งยากหรือเมื่อรูปแบบข้อมูลเปลี่ยน

ซึ่งจริงๆแล้วเรามีอีกทางเลือกคือ LLM Multimodal
ซึ่งสามารถ "เข้าใจภาพ" และ "ตอบคำถาม" ได้โดยตรง

แนวคิด

Multimodal LLM สามารถประมวลผลทั้ง ข้อความและภาพ พร้อมกัน ทำให้เราสามารถป้อนภาพเข้าไปพร้อมกับ prompt เช่น:

ภาพนี้คือใบเสร็จร้านอาหาร กรุณาแยกข้อมูลออกมาเป็น JSON: ชื่อร้าน, วันที่, รายการอาหาร, ราคารวม

Code Example

Model

สำหรับรอบนี้เราจะลองใช้ตัว gemma3-12b-vision

examples/official/deploy/fastapi-exllamav2-gemma3-12b-vision at main · float16-cloud/examples
Contribute to float16-cloud/examples development by creating an account on GitHub.

สามารถทำตาม Getting Started ใน README ได้เลยสำหรับใครอยากลอง deploy model เอง

Code

GitHub - bossthanawat/data-extraction-llm
Contribute to bossthanawat/data-extraction-llm development by creating an account on GitHub.

client-gemma3.ts

  1. Set prompt ให้ชัดเจนว่ารูปนี้คืออะไร ต้องการ filed อะไรบ้างพร้อมยกตัวอย่าง ถ้าไม่มีต้องทำอย่างไร และบอกว่าต้อง return ในรูปแบบ JSON เท่านั้น ซึ่งในที่นี้เราจะมีการบอกเพิ่มด้วยว่าต้อง ดูเรื่องของ category ด้วยว่าคืออะไร ถ้าเป็น invoice ค่อยให้ดึง field ที่เราต้องการออกมา แต่ถ้าไม่ใช่ให้บอกว่ามันคืออะไร
This is an image of a document. Please analyze the document and return a JSON object that strictly follows the schema below:

{
  "category": string,                    // Document category: "invoice", "receipt", or "other-[description]"
  "result": object | null           // Invoice data if category is "invoice", otherwise null
}

If the document category is "invoice", return the result as:
{
  "category": "invoice",
  "result": {
    "invoice_number": string,              // e.g. "INV-2024-00123"
    "invoice_date": string,             // Date as shown in document (any format: DD/MM/YYYY, MM-DD-YYYY, YYYY-MM-DD, etc.)
    "due_date": string | null,        // Date as shown in document (any format) or null if not found
    "total_amount": number | null,    // Final total amount (e.g. 1234.50) in INR
    "items": [
      {
        "description": string,        // Name of the product or service
        "quantity": number | null,
        "unit_price": number | null,
        "line_total": number | null
      }
    ]
  }
}

If the document category is NOT "invoice", return the result as:
{
  "category": "receipt" | "other-[description]",
  "result": null
}

DOCUMENT CATEGORY GUIDELINES:
- "invoice": Only tax invoices, billing invoices, commercial invoices
- "receipt": Payment receipts, transaction receipts
- "other-[description]": For any other content, describe what you see after "other-"
  Examples: "other-id_card", "other-child_photo", "other-certificate", "other-bill", "other-menu", "other-text_document"

IMPORTANT INSTRUCTIONS:
- Only extract invoice data if the document category is clearly "invoice"
- For receipts, use exactly "receipt" as the category
- For anything else, use "other-" followed by a brief English description of what you see
- For dates, extract them exactly as they appear in the document - do not convert or reformat
- For all non-invoice documents, set result to null
- Do not guess or fabricate values
- Return ONLY the JSON object directly. Do not wrap it in markdown code blocks (${"`"}json${"`"})
- Do not include any explanation, comments, or extra text before or after the JSON
- Your response should start with { and end with }
- The response must be valid JSON that can be parsed directly
  1. โยน image ผ่าน type image_url ใน content message คู่ไปกลับ
  const { data } = await axios.post(
    `${process.env.BASE_URL}/chat/completions`,
    {
      model: "gemma3-12b-vision",
      messages: [
        {
          role: "user",
          content: [
            {
              type: "text",
              text: prompt,
            },
            {
              type: "image_url",
              image_url: { url: `data:image/jpeg;base64,${image}` },
            },
          ],
        },
      ],
    },
    {
      headers: {
        Authorization: `Bearer ${process.env.API_KEY}`,
        "Content-Type": "application/json",
      },
    }
  );

client-openai.ts

สำหรับกรณีใช้แบบ Model อื่นที่ OpenAI Completions และรองรับ Multimodal

Result

{
  "category": "invoice",
  "result": {
    "invoice_number": "INV-005",
    "invoice_date": "Jun 22, 2021",
    "due_date": "Jun 27, 2021",
    "total_amount": 1564,
    "items": [
      {
        "description": "Desktop furniture",
        "quantity": 1,
        "unit_price": 232,
        "line_total": 232
      },
      {
        "description": "Plumbing and electrical services",
        "quantity": 2,
        "unit_price": 514,
        "line_total": 1028
      },
      {
        "description": "Water tank repair works",
        "quantity": 2,
        "unit_price": 152,
        "line_total": 304
      }
    ]
  }
}

{
  "category": "invoice",
  "result": {
    "invoice_number": "100",
    "invoice_date": "1/30/23",
    "due_date": "1/30/23",
    "total_amount": 420,
    "items": [
      {
        "description": "20\" x 30\" hanging frames",
        "quantity": 10,
        "unit_price": 15,
        "line_total": 150
      },
      {
        "description": "5\" x 7\" standing frames",
        "quantity": 50,
        "unit_price": 5,
        "line_total": 250
      }
    ]
  }
{
  "category": "receipt",
  "result": null
}

ข้อดี

  • ใช้ได้กับหลายรูปแบบ (แม้ layout จะเปลี่ยน)
  • เจาะจงข้อมูลที่ต้องการได้ผ่าน prompt
  • ไม่ต้องเขียน regex หรือ template เฉพาะเอกสาร
  • สามารถเพิ่ม condition เข้าไปได้เช่น ถ้าเป็น a ถึงค่อยหยิบ b

ข้อควรระวัง

  • ราคาค่อนข้างสูง (คิดตาม token)
  • ความแม่นยำขึ้นอยู่กับ prompt และความชัดของภาพ
  • ควรมี fallback หรือ validation เผื่อโมเดลตอบผิด

ถ้าเปรียบเทียบไปทำในรูปแบบ OCR

  1. OCR: แปลงรูปภาพให้เป็น text
  2. OCR Post-processing: ปรับปรุงข้อความที่ได้จาก OCR ให้ถูกต้องมากขึ้น
  3. Text Extraction & Cleaning: ดึงข้อความสำคัญและทำความสะอาดข้อมูล
  4. Document Structure Understanding: เข้าใจโครงสร้างเอกสาร (ใบเสร็จ, ใบเบิก, รายงานอนุมัติ)
  5. Named Entity Recognition (NER): ระบุว่านี้ชื่อบริษัท, วันที่, จำนวนเงิน, เลขที่เอกสาร

Conclusion

การใช้ LLM Multimodal สำหรับ Data Extraction จากภาพเป็นแนวทางที่ดีมากหากโดยเฉพาะในกรณีที่เอกสารมีฟิลด์จำนวนมาก แล้วเราอาจจะอยากหยิบแค่บางฟิลด์ที่เฉพาะจง แล้ว layout อาจจะไม่มีความแน่นอน หรือ condition บางอย่างในการจะดึงข้อมูล เราสามารถควบคุมผ่าน prompt ได้ โดยไม่ต้องสร้างระบบ parsing ที่ซับซ้อน ทำให้เราไม่จำเป็นต้องไปยึดติดอีกต่อไปว่าการทำแบบนี้ต้องทำ OCR อย่างเดียว แล้วมานั้งทำ regex หรือมานั้งวิเคราะห์ต่อเพื่อดึงข้อมูลที่เราต้องการจริงๆออกมา

จากตัวอย่างที่ยกมาจะเป็น Data Extraction ออกมาแบบง่ายๆ ซึ่งจริงๆแล้วยังสามารถทำได้อีกหลากหลายท่า จะเพิ่ม condition บางอย่าง จะเอาไปทำ Classification/Validation ก็ได้


Float16

Managed GPU resource platform for developers. Experience the cheapest serverless GPU in spot mode and the fastest GPU endpoint in deploy mode.

Connect with us:

Read more

Self-Hosted LLMs for Enterprise #4

Self-Hosted LLMs for Enterprise #4

ตอนสุดท้ายแล้วนะครับ สำหรับการ deploy llm model ใช้งานเอง หลังจากที่แล้ว Setup ตัว service และ tools ต่างๆที่ต้องการครบถ้วนแล้ว เรามาลุยกันต่อเลยครับในการ download model และทำ API Endpoint สำหรับใครที่เพิ่งเข้ามาอ่านตอนนี้เป็นตอนแรก สามารถติดตามตอนก่อนหน้าได้

By Weerasak Suwannapong
Self-Hosted LLMs for Enterprise #3

Self-Hosted LLMs for Enterprise #3

สำหรับใครที่เพิ่งเข้ามาเจอตอนนี้สามารถย้อนกลับไปอ่าน 2 ตอนก่อนหน้าได้ที่ https://blog.float16.cloud/self-hosted-llms-for-enterprise-1/ https://blog.float16.cloud/self-hosted-llms-for-enterprise-2/ 2 ตอนที่ผ่านมา เราได้ติดตั้ง GPU Driver และเชื่อม GPU เข้ากับ Docker ได้เรียบร้อยแล้ว บทนี้เราจะมาติดตั้งเครื

By Weerasak Suwannapong
Self-Hosted LLMs for Enterprise #2

Self-Hosted LLMs for Enterprise #2

ในตอนที่แล้ว เราได้ติดตั้ง NVIDIA GPU Driver ให้พร้อมใช้งานบนเครื่อง EC2 (g5g.xlarge) ที่ใช้ Ubuntu 24.04 LTS บทนี้เราจะทำให้ GPU ที่ติดตั้งไว้สามารถใช้งานได้ภายใน Docker container เพื่อเตรียมต่อยอดไปยังการสร้าง LLM API ด้วย llama.cpp อ่านตอนแรกได้

By Weerasak Suwannapong
Self-Hosted LLMs for Enterprise #1

Self-Hosted LLMs for Enterprise #1

ในยุคที่ Generative AI กลายเป็นผู้ช่วยประจำวันของใครหลายคน ไม่ว่าจะเขียนโค้ด ตอบคำถาม หรือสรุปรายงาน หลายองค์กรเริ่มสนใจจะ ติดตั้งและใช้งาน LLM ภายในเอง เพื่อความเป็นส่วนตัว ความยืดหยุ่น และควบคุมต้นทุน ซีรีส์นี้จะพาไปตั้งค่าระบบทีละขั้น ตั้งแต่

By Weerasak Suwannapong