Author: patrawi27

  • สรุป Live “นายอาร์ม x เท้ง ณัฐพงษ์” ในมุมมองคนทำงานราชการ: ทำไมรัฐบาลดิจิทัลถึงยังไม่เกิด?

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

    1. ทำไม “รัฐบาลดิจิทัล” ถึงไม่เกิดสักที? (ปัญหาโครงสร้าง)

    • ขาดเจ้าภาพตัวจริง: นโยบายมีมานานแต่ไม่มีใครรับผิดชอบเบ็ดเสร็จ (Single App Digital ID ไม่เกิดเพราะเหตุนี้)
    • ติดกับดักระบบราชการ (Silo): ต่างคนต่างหวงข้อมูล อ้าง PDPA ผิดพรบ ทำให้ DGA (สพร.) สั่งการข้ามหน่วยงานยาก แม้แต่นายกฯ สั่งก็เช็คยากว่าใครทำตามบ้าง เพราะจัดซื้อจัดจ้างเยอะมาก (ระดับ สำนักไปจนถึงระดับ กระทรวง)
    • ปัญหาใหญ่คือ “TOR” และ “การจัดซื้อจัดจ้าง”:
      • การเขียน TOR ปัจจุบันขาดมาตรฐาน ต่างคนต่างจ้าง ทำให้โครงการบวมและตรวจสอบยาก
      • นอกจากเรื่อง TOR แล้วราชการก็ขาดคนที่มีความสามารถ ทำให้ต้องจ้างคนนอกมาเขียน TOR ( ที่ปรึกษา ) ซึ่งคนเหล่านี้เก่งจริงแต่ไม่ใช่คนที่คลุกคลีอยู่ในงานทำให้ขาดการกำหนด TOR
      • ทางแก้: คุณเท้งคิดว่าต้องแก้ที่ต้นน้ำ (กรมบัญชีกลาง) กำหนดมาตรฐานกลาง (Standard Design System) บังคับใช้ Open Source หรือ Microservices ล็อกสเปกขั้นต่ำให้ชัดเจน ใครจะประมูลต้องผ่านเกณฑ์นี้
      • เปลี่ยนวิธีคิดจากซื้อโครงการ (Project) เป็นการเช่าใช้บริการ (Utility/Subscription) เพื่อแก้ปัญหาการขึ้น Cloud
      • เรื่อง Cloud นายอาร์มถามหลายอย่างเช่น
      • ปัญหามีหลายอย่างมาก
        • เอาข้อมูลฝากข้างนอกไม่ได้
        • จนท ไม่ชำนาญ
        • ระบบเก่าอยู่บน on premise ขึ้น cloud ไม่ได้
        • ระเบียบจัดซื้อจัดจ้าง TOR
      • แต่ว่าจริงๆ พวกระเบียบและกฎสามารถให้ทำได้แล้วแต่ว่าคนส่วนใหญ่ที่มาเขียน TOR อาจจะไม่ใช่ผู้ที่ถนัดหรือไม่ชัวร์จึงเลือกเขียนแบบเดิมๆ

    2. โครงสร้างพื้นฐาน: Digital ID และ Data Sharing

    • THAID vs NDID:
      • THAID: รัฐทำได้ดีเรื่องการยืนยันตัวตน (Authentication) แต่ยังขาดเรื่องการแชร์ข้อมูล
      • NDID: โมเดลดีมาก (แบบ Estonia) ออกแบบมาเพื่อ Data Sharing แต่ปัจจุบันรันโดยเอกชน มีค่าธรรมเนียมแพง (200 ล้าน/ปี) ทำให้คนใช้น้อย
    • ข้อเสนอ: รัฐควรเข้าไปดูแล NDID (อาจจะ Takeover หรืออุดหนุน) เปลี่ยนให้เป็น Infrastructure ฟรีของประเทศ เพื่อให้ข้อมูลไหลเวียนได้จริง โดยมี “ทางรัฐ” เป็น Portal กลาง

    3. การปราบอาชญากรรมไซเบอร์ (Scammers & Fraud)

    • Scammer ต้นทุนต่ำ กำไรสูง: โจรใช้เทคโนโลยีขยายวงกว้างได้ง่าย แต่ตำรวจตามจับยากเพราะติดขัดเรื่องข้อมูล
    • Data Bureau & CFR: ต้องมีถังข้อมูลกลาง (Central Fraud Registry) เพื่อตรวจสอบความผิดปกติ
    • การเชื่อมข้อมูล: ใช้เทคนิค Hashing/Surrogate Key เพื่อเชื่อมข้อมูลหลังบ้านโดยไม่ละเมิด Privacy (รู้ว่าเป็นใครเมื่อเจอความผิดปกติ)
    • คุณเท้งบอกว่า สมัยก่อนเวลาฟอกเงิน พวกนี้จะฟอกผ่านคริปโตแต่ตอนหลังเน้นเปลี่ยนไปฟอกผ่านทองคำ
    • อุปสรรคหน้างาน: ตำรวจและอัยการยังติดเรื่องเอกสารกระดาษ (Hard Copy) กฎหมาย (ป.วิ.อาญา) ต้องแก้ให้รองรับหลักฐานดิจิทัลเต็มรูปแบบ
    • ปัญหาอีกด้านของคนปฏิบัติ เพราะการถ่ายทอดโยบาย นั้น เจ้าหน้าที่คุ้นชินกับแบบเดิมๆ
      • คุณเท้งเสนอว่าตัดตัวกลางออกก็อาจจะเป็น Solution (ตัดคนปฏิบัติงาน)
    • แต่ว่ากลุ่ม เปราะบาง เช่น คนแก่ อะ ถ้า รัฐดิจิตอลก็ใช้ เครื่องkiosk กับเจ้าหน้าที่ไปเฝ้าเครื่องให้พวกเขาก้ได้เหมือนกัน

    4. การใช้ AI และ Tech แก้ปัญหาประเทศ

    • Anti-Corruption: Gen AI ยังไม่เหมาะ (เพราะมั่วข้อมูลได้/Hallucination) ต้องใช้ Neural Network เน้น Classification ตรวจจับความผิดปกติ
    • PM 2.5: ใช้ภาพถ่ายดาวเทียม + AI Processing หาจุดเผาข้ามพรมแดน เพื่อใช้เป็นหลักฐานและบังคับใช้กฎหมาย
    • Smart Farming: รัฐทำหน้าที่เป็น Platform จับคู่เกษตรกรกับผู้ให้เช่าเครื่องจักร (Matching Demand/Supply) แจกคูปองอุดหนุนค่าเช่าแทนการซื้อแจก

    5. วิสัยทัศน์การทำงานและ Lifestyle (คุณเท้ง)

    • เปลี่ยนจาก Project เป็น Product: รัฐต้องเลิกจ้างแบบ Waterfall แล้วหันมาสร้างทีม Digital (Dream Team) ที่ทำงานแบบ Agile มี Product Manager (PO) ดูแลโปรดักต์ต่อเนื่อง
    • สไตล์การทำงาน: เน้นกระจายอำนาจ (Delegation) ทำงานเป็นทีมแบบ Chief of Staff ไม่รู้ต้องถามผู้รู้จริง
    • นโยบายต่างประเทศ: ต้องกอบกู้ที่ยืนของไทยในเวทีโลก (Global Standing)
    • Lifestyle: ชอบอ่านหนังสือ, ออกกำลังกาย, ดูหนัง (Interstellar, Designated Survivor)

    6. อื่นๆ

    • นายอาร์มบ่นกับคุณเท้งว่า ทำไมเป๋าตังค์ ถึงได้สิทธิ์ทำใบขับขี่สากลไม่ใช่ทางรัฐ
    • นายอาร์มถามคุณเท้งเลือก Recommender System ที่ Netflix ใช้ ( System Design ) ซึ่งก็ตอบตามที่เรียนที่ Edinburgh เป๊ะๆ

    ในฐานะข้าราชการ ผมเห็นด้วยเรื่องปัญหาการเขียน TOR มากๆครับ เพราะว่าโดยทั่วไปแล้ว TOR ของราชการจะถูกเขียนออกมาให้มี Scope ที่กว้างไว้ก่อนเพราะต่างคนก็กลัวโดนกฎหมายเล่นงานกันทั้งนั้น ซึ่งมันทำให้ผู้เข้าประมูลสามารถเลือกใช้ผลิตภัณฑ์ที่ตนถนัดแต่อาจจะไม่ใช่มาตรฐานสากลได้เช่นกัน ไหนจะการฮั้วการประมูลโดยส่งคนเข้ามาบิดแข่งแต่ว่าตั้งราคาให้น้อยกว่าไม่มากส่งผลให้บริษัทที่ได้ดีลกันไว้แล้วได้รับงานนั้นไป

    ก็หวังว่าเนื้อหาที่ผมแชร์ในวันนี้จะช่วยให้เพื่อนๆ เห็นภาพกันมากขึ้นนะครับ

  • ทำความรู้จัก n8n: ระบบ Workflow Automation

    ทำความรู้จัก n8n: ระบบ Workflow Automation

    ตั้งแต่ช่วงต้นปีที่ผ่านมา ผมเชื่อว่าทุกคนต้องเคยเลื่อนผ่าน ไม่ว่าจะเป็น Youtube Short, Facebook Reel , Instragram หรือ เจ้าแม่ short video อย่าง Tiktok ผมเชื่อว่าทุกคนต้องเคยเห็นคลิปวีดิโอที่ทำมาจาก AI ต่างๆนานา อย่างแน่นอนใช่ไหมครับ

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

    ด้วยเหตุนี้ผมจึงตัดสินใจที่ลองหาข้อมูล ละก็ไปเจอว่า เออ มันมี Tools ตัวหนึ่งที่เอาไว้ทำ Workflow Automation ชื่อ n8n (เอน-เอ็ท-เอน) นั้นเอง

    ผมจึงหยิบเจ้าซอฟแวร์ตัวเนี้ย ไหนมาลองเล่นบ้างสิ ไปๆมาๆ ก็รู้สึกว่า มันเป็น Low Code ที่สะดวกมากกกกก และสามารถเชื่อมต่อกับโลกอินเตอร์เน็ตได้อย่างไร้ขีดจำกัดเลยทีเดียว

    และมันก็ทำให้ผมสามารถนำไปประยุกต์ใช้กับงานราชการได้อย่างดีเยี่ยมเลยละครับ ระบบงานที่ขึ้นชื่อเรื่องความเยอะ และ ต้องลีนยิ่งกว่า Startup เสียอีก 555+

    เนื้อหาในบทความนี้ผมจะมาเล่า และ อธิบายว่า n8n คืออะไรแล้วมันดียังไงกันครับ และแถมด้วยการพาดู Workflow แบบ basic ที่ใครๆก็สามารถนำไปลองทำได้

    Table of Content

    1. ทำความรู้จัก N8N
    2. ทำไม n8n ถึงเหมาะกับงานราชการ
    3. n8n ให้บริการรูปแบบไหนบ้าง ( Cloud & Self-hosted )
    4. ข้อควรระวังในการใช้งานข้อมูล
    5. มาลองติดตั้ง n8n กันเถ๊อะ
    6. ทดลองทำ Workflow ง่ายๆ 1 อัน
    7. บทสรุป

    1. ทำความรู้จัก n8n

    n8n (เอ็น-เอท-เอ็น) เป็นระบบให้บริการทางด้านซอฟแวร์สำหรับการทำกระบวนการอัตโนมัติ (Workflow Automation) ที่ผสมผสานความสามารถของ AI กับกระบวนการทางด้านธุรกิจได้อย่างลงตัว

    ด้วยความที่ซอฟแวร์ไม่ต้องพึ่งพาทักษะในด้านการเขียนโปรแกรมมากนัก ทำให้รอยต่อระหว่างฝั่งธุรกิจ (business) และ ฝั่งนักพัฒนา (developer) นั้นแคบลง และช่วยให้เกิดความหยืดยุ่นเป็นอย่างมากกับบริษัท

    บน n8n นั้น มีสิ่งที่เรียกว่า Nodes เพื่อใช้ในการเชื่อมโยงแพลทฟอร์มอื่นๆมากกว่า 500 รายการซึ่งจะช่วยให้คุณสามารถเข้าถึงและเชื่อมโยงสิ่งๆต่างรอบตัวคุณได้อย่างมีประสิทธิภาพสุดๆ

    2. ทำไม n8n ถึงเหมาะกับงานราชการ

    หลายท่านที่ทำงานในราชการมันทั้งจำเจ วกวน และวุ่นวายแค่ไหน ?

    ทั้งงานเอกสาร การประชุม การส่งไปอบรม และอื่นๆอีกร้อยแปดพันเก้าที่แสนจะวุ่นวาย

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

    ลองจินตนาการตามผมนะครับ คุณมีเวลาให้ตัวเองมากขึ้น สามารถพัฒนาคุณภาพชีวิตตัวเองเอ่ย คุณภาพทีมของคุณเอ่ย หรือไปจนถึงพัฒนาการทำงานในระดับองค์กรดูสิ

    หรือไม่ต้องพัฒนาลงพัฒนาอะไรหรอก เอาแค่มีเวลาได้นอนตีพุงอยู่บ้าน ดู Netflix ชิวๆ และเลิกงานตรงเวลา แค่นี้ชีวิตก็โคตรจะสบายแล้วครับผมว่า

    ผมลองมาคิดคร่าวๆ ซัก 5-10 ไอเดีย ที่จะช่วยลดงานข้าราชการทั้งด้านเอกสาร ด้านภาคบริการประชาชน และด้านพัฒนาตนเอง เช่น

    1. Workflow ที่จะช่วยงานของทีม HR ในการเพิ่มผู้ใช้งานระบบได้เร็วขึ้น (Onboarding Workflow Automation)
    2. Workflow สำหรับแจ้งเตือนเมื่อระบบมีปัญหา (Alerting Workflow)
    3. Workflow สำหรับแจ้งซ่อมผ่านทางระบบแชทไลน์ (Support Service Automation Workflow)
    4. Chatbot สำหรับถามตอบให้ลูกค้าไปยังระบบได้ถูกต้อง (Intelligence Chatbot) ที่กรมบังคับคดี เรามีระบบเยอะมาก เยอะจนงง เยอะจนคนในยังลืมว่าระบบอะไร Silo ไปอี๊กกก
    5. Workflow สำหรับทำเรื่องเปลียนรหัสผ่านสำหรับผู้ใช้งานภายใน (Forget Password Automation Workflow).

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

    เอาละหลังจากเราสาธยายความดีงามของมันมามากพอละ ปะ ไปดูว่า n8n มีแบบไหนบ้าง

    3. n8n ให้บริการรูปแบบไหนบ้าง ( Cloud & Self-hosted )

    n8n มีรูปแบบการให้บริการหลัก ๆ สองแบบได้แก่ Self-hosted และ Cloud Subscription เรามาเริ่มกันที่ Cloud Subscription กันก่อน

    Cloud Subscription

    สำหรับเวอร์ชั่นนี้เป็น iPaaS (Integration Platform as a Service) เหมือนกับพวก Make หรือ Zapier เลย ก็คือเราเสียเงินค่า Subscription เราก็สามารถเข้าไปใช้งานได้ทันทีเลย เรียกได้ว่าใช้เงินแก้ปัญหา

    ข้อดีเด่น ๆ ของ Cloud Subscription คือ:

    เริ่มงานได้ไว: ช่วยให้คุณสามารถโฟกัสกับการสร้าง Workflow ได้ทันที โดยไม่ต้องเสียเวลาไปกับการตั้งค่าระบบพื้นฐาน

    ความสะดวกสบาย: คุณไม่ต้องกังวลเรื่องการติดตั้งเซิร์ฟเวอร์, การดูแลความปลอดภัย, การจัดการ SSL Certificate ประจำปี หรือการอัปเดตเวอร์ชันซอฟต์แวร์ เพราะ n8n เป็นผู้ดูแลจัดการให้ทั้งหมด

    ทีนี้เราลองมาดูด้าน Self-hosted กันบ้างดีกว่า ว่ามันต่างกันอย่างไร

    Self-hosted

    บริการนี้จะเป็นการที่เรา ติดตั้ง n8n เองตั้งแต่ ตั้งค่า Virtual Machine หรือ VM ติดตั้ง Tools ต่างๆ จนไปถึงลง ซอฟแวร์ n8n เองทั้งหมด

    แม้ฟังดูเหมือนว่าจะฟรี แต่มันก็ไม่ได้ฟรีแบบจริงๆร้อยเปอร์เซ็นต์หรอกครับ ที่บอกว่าเหมือนจะฟรี ก็เพราะ n8n เขาจดสิทธิบัตร software ของเขา โดย based on fair-code model. นั้นเอง

    Fair Code Model คือการจด license แบบที่เราสามารถเอา sourcecode ของเขาไปใช้ได้ แก้ไขปรับปรุงได้ แต่ก็อาจจะมีเงื่อนไขบางอย่างที่ไม่ได้รับการรับรองจากทาง OSI Approved Licenses เช่นการเก็บข้อมูลเป็นต้น

    อย่างไรก็ตาม การเลือกใช้ Cloud Subscription หรือ Self-hosted นั้น ก็ขึ้นอยู่กับหลายปัจจัย อาทิ เช่น ความปลอดภัย การธรรมาภิบาลข้อมูล (Data Governance) และระดับความลับของข้อมูลเป็นต้น

    เพราะฉะนั้นแล้ว เรามาดูกันดีกว่าว่า n8n แต่ละแบบมีการเก็บข้อมูลอย่างไรกันบ้าง เพื่อใช้ประกอบการตัดสินใจในการเลือกวิธีการใช้งาน

    4. ข้อควรระวังในการใช้งานข้อมูล

    สำหรับ n8n แบบ Self-hosted

    1. รหัสข้อผิดพลาดและข้อความ (Error codes & Messages): สำหรับการรันเวิร์กโฟลว์ที่ล้มเหลว ( Failed Executions ) โดยจะไม่เก็บข้อมูล Payload และข้อมูลจาก Custom Nodes (เช่น หากคุณมี Web Server Node ที่เรียกใช้บน n8n)
    2. รายงานข้อผิดพลาด (Error Reports): สำหรับกรณีที่ แอพ n8n เกิดข้อผิดพลาดร้ายแรง หรือปัญหาเชิงเทคนิคของ API
    3. โครงสร้างของเวิร์กโฟลว์ (Workflow Structure): เช่น สำหรับ Workflow นี้ใช้ Node อะไรบ้างเป็นต้น เช่น Gmail, Azure เป็นต้น
    4. พารามิเตอร์ของ Node (Node Parameters): จะเก็บ Operation กับ Resource เท่านั้น (ถ้ามี) และ สำหรับ HTTP Request Node ก็จะเก็บ Domain, Path และ Method โดยข้อมูลส่วนบุคคลจะทำให้เป็นแบบปกปิดข้อมูลแทน
    5. สถานะการทำงานของเวิร์กโฟลว์ (Workflow Execution Data):
      • สถานะผลลัพธ์การทำงาน (เช่น 200 OK, 404 Not Found, 500 Internal Server Error)
      • รหัสผู้ใช้งานที่เรียกใช้ Workflow นั้นบน n8n
      • การโหลดข้อมูลครั้งแรกของเวิร์กโฟลว์จากแหล่งข้อมูลภายนอก
      • การรันเวิร์กโฟลว์ที่ประสบความสำเร็จครั้งแรกในโหมด Production (ไม่ใช่การรันด้วยตนเอง)
    6. Domain ของ Webhook Calls (หากมีการระบุ): โดยไม่รวม Subdomain
    7. รายละเอียดการใช้งานหน้าจอผู้ใช้ (UI Usage): เช่น การนำทาง (Navigation) หรือการค้นหาใน Nodes Panel
    8. ข้อมูลการวินิจฉัย (Diagnostic Information): เช่น n8n version, การตั้งค่าที่เลือก (Selected Settings) อย่าง DB_TYPE, N8N_VERSION_NOTIFICATIONS_ENABLED, N8N_DISABLE_PRODUCTION_MAIN_PROCESS, ตัวแปรการรัน (Execution Variables), ระบบปฏิบัติการ (OS), RAM, และ CPU, Anonymous Instance ID, และ IP Address
    9. IP Address

    จะเห็นได้ว่าถึงแม้ n8n จะพยายามหลีกเลี่ยงการเก็บข้อมูลส่วนบุคคลที่อาจขัดต่อ พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล (PDPA) หรือ GDPR แต่ก็ยังมีการเก็บข้อมูลทางเทคนิคและการใช้งานอยู่พอสมควร ซึ่งคุณควรพิจารณาในบริบทขององค์กรของคุณ

    สำหรับ n8n แบบ Cloud Subscription

    ในส่วนของ Cloud Subscription นั้น n8n จะมีการเก็บข้อมูลพื้นฐานเช่นเดียวกับ Self-hosted ทุกประการ และจะ เพิ่มเติม การเก็บข้อมูลบางส่วนเพื่อวัตถุประสงค์ในการปรับปรุงบริการและประสิทธิภาพของ AI ดังนี้:

    1. การจับการใช้งานหน้าจอ (Screen Recording Capture) เพื่อดูว่าผู้ใช้งานมีการเรียกใช้ node หรือการแสดงค่าอย่างไรบ้าง โดยปราศจากการเก็บข้อมูลส่วนบุคคล (ลบภายใน 21 วัน)
    2. การให้ AI chat เรียนรู้ ข้อมูล ณ​ ปัจจุบัน อย่าง workflow, context data I/O และ โค้ด ที่เขียนลงไปใน n8n เพื่อเพิ่มประสิทธิภาพในด้านประสบการณ์ผู้ใช้งาน (ลบภายใน 30 วัน)

    5. มาลองติดตั้ง n8n กันเถ๊อะ

    ก่อนอื่นเลยเราต้องมาอธิบายกันก่อนว่าอะไรคือ Docker และ อะไรคือ Docker Compose เพื่อที่เราจะได้เข้าใจเครื่องมือพื้นฐานสำคัญก่อนดำดิ่งสู่ของจริง

    Docker คืออะไร ?

    • Docker เป็น Open-Platform รูปแบบหนึ่งที่ให้นักพัฒนาสามารถที่จะ พัฒนา ใช้คำสั่ง และ ย้าย Application ของพวกเขาได้อย่างรวดเร็ว
    • Docker ช่วยให้เราสามารถรัน Applications ของเราแยกกันบนโครงสร้างพื้นฐาน (Infrastructure)เดียวกันได้ ช่วยห้เราสามารถ deliver งานได้ไวขึ้นและสามารถทำให้เราใช้สิ่งที่คนอื่นสร้างไว้แล้ว อย่าง Image เพื่อทำให้ขึ้นงานได้ไวอีกด้วย

    Docker Compose

    • เป็นเครื่องมือที่ใช้ในการกำหนดและรัน container หลายๆอันพร้อมๆกันได้
    • โดยปกติ เราจะต้องมานั่งรัน docker build และ docker run ทีละ Dockerfile ใช่ไหมครับ มันช้า เขาก็เลยสร้างเครื่องมือตัวนี้ให้เราสามารถเขียน docker-compose.yml ไฟล์เดียวแล้วรัน containers ขึ้นมาพร้อมกันๆหลายๆอันได้เลย

    สำหรับบทความนี้เราจะไม่ได้อธิบายวิธีการติดตั้ง Docker และ Docker Compose นะครับ แต่ทุกท่านสามารถดูวิธีการติดตั้งได้โดยคลิกที่นี้

    DigitalOcean Droplet

    ใน Tutorial นี้เพื่อให้เหมือนกับของจริงเวลาคุณได้เครื่อง VM จาก Cloud ภาครัฐ GDCC หรือ Cloud จากทีม Network ของคุณสร้างให้ เราจะใช้ VM จากทาง DigitalOcean Droplets นะครับ

    DigitalOcean Droplets

    • Virtual Machine ที่เป็น linux-based ออนไลน์อยู่บน hardware เสมือนจริงที่ทาง DigitalOcean เป็นคนสร้างให้

    สำหรับบทความนี้เพื่อนๆสามารถดูวิธีการติดตั้ง Droplet โดยคลิกลิ้งค์นีได้เลยนะครับ หากไม่สะดวกภาษาอังกฤษ ผมแนะนำว่าให้ Copy เนื้อหาแล้วเอาไปแปะให้ AI แปลให้จะสะดวกมากๆเลยครับ

    สมมติว่าคุณได้ Droplet มาเรียบร้อยแล้ว ขั้นตอนแรกคือการเชื่อมต่อไปยังเซิร์ฟเวอร์ของคุณผ่าน Terminal

    ssh -i ~/.ssh/<public_ssh_key> <username>@<droplet_ip>
    

    ถ้าคุณเลือกว่าจะใส่ passphrase ระบบก็จะถามเราก็ใส่ passphrase ให้ถูกตามที่เรากรอก หรือถ้าเราเป็นคนขี้เกียจก็ ssh-add <ssh_private_key> ไปก็จะไม่ต้องใส่ passphrase ทุกรอบ

    เมื่อ log in เข้ามาแล้ว ผมแนะนำว่าให้สร้าง user ใหม่แทนนะครับเพื่อความปลอดภัย และหลีกเลี่ยงการใช้ Root user

    สมมติว่าคุณได้ Droplet มาเรียบร้อยแล้ว ขั้นตอนแรกคือการเชื่อมต่อไปยังเซิร์ฟเวอร์ของคุณผ่าน Terminal:

    adduser <username>
    

    ระบบก็จะให้เรากรอกข้อมูลต่างๆ เราก็ค่อยๆกรอกไป ก็จะมีทั้ง รหัสผ่าน, ชื่อ, ที่ทำงาน, เบอร์โทรศัพท์ เป็นต้น

    จากนั้นเราก็ให้สิทธิ์ admin กับ user ของเรา

    usermod -aG sudo <username>
    

    ทีนี้เราก็สามารถเรียกใช้คำสั่งที่ต้องอาศัยสิทธิ์​ super user ได้แล้ว โดยสามารถใส่ sudo ด้านหน้าเพื่อใช้ง

    จากนั้นเราก็ทำการสร้าง ssh key อันใหม่สำหรับ user นี้เพื่อที่เราจะได้ ssh จากเครื่องเราได้และไม่ต้องผ่าน root อีกต่อไป Add Public Key Authentication.

    เมื่อทำเสร็จแล้วก็ exit ออกจากเครื่อง Vm และทำการ ssh เข้าไปใหม่ด้วย user ที่เราสร้างนั้นเอง

    ssh -i ~/.ssh/id_n8n_admin <username>@<droplet_ip>
    

    Clone Project และเตรียม Docker Volumes

    เอาหละเมื่อได้ user ใหม่แล้วเราก็ต้องทำการ clone project ลงมา

    git clone https://github.com/n8n-io/n8n-docker-caddy.git
    

    เมื่อ clone เสร็จแล้ว เราก็​จะทำการเปลี่ยน directory ก่อน

    cd n8n-docker-caddy
    ls
    

    สำหรับตรงจุดเดี๋ยวเราจะวนกับมาอธิบายเพิ่มเติมหลังจากที่เราได้เปิดดู docker-compose.yml


    เพื่อให้เวลาเรา build docker container เป็นไปอย่างรวดเร็วและข้อมูลคงอยู่ถาวร เราควรสร้าง Docker Volume เก็บไว้ โดยในที่นี้เราจะสร้าง 2 ตัว นั้นคือสำหรับ caddy เพื่อเก็บ cache และ n8n_data นั้นเอง

    sudo docker volume create caddy_data
    sudo docker volume create n8n_data
    

    จากนั้นเพื่อให้ n8n ของเรารันในฐานะ Web Application ได้ เราก็จะต้องเปิด port 80 สำหรับ non-secure traffic และ 443 สำหรับ secure traffic ด้วยการเรียกใช้คำสั่งด้านล่างจะเป็นการเปิด port ใน Firewallครับ

    sudo ufw allow 80
    sudo ufw allow 443
    

    ละก็มาถึงส่วนที่สำคัญที่สุดนั้นคือไฟล์ docker-compose.yml

     version: "3.7"
    
    services:
      caddy:
        image: caddy:latest
        restart: unless-stopped
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - caddy_data:/data
          - ${DATA_FOLDER}/caddy_config:/config
          - ${DATA_FOLDER}/caddy_config/Caddyfile:/etc/caddy/Caddyfile
    
      n8n:
        image: docker.n8n.io/n8nio/n8n
        restart: always
        ports:
          - 5678:5678
        environment:
          - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
          - N8N_PORT=5678
          - N8N_PROTOCOL=https
          - NODE_ENV=production
          - WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
          - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
        volumes:
          - n8n_data:/home/node/.n8n
          - ${DATA_FOLDER}/local_files:/files
    
    volumes:
      caddy_data:
        external: true
      n8n_data:
        external: true
    

    services: ส่วนนี้ใช้กำหนดบริการ (Services) ต่างๆ ที่จะถูกสร้างเป็น Container โดยในที่นี้คือ Caddy (Web Server/Reverse Proxy) และ n8n

    volumes: กำหนด External Volumes ที่เราสร้างไว้ก่อนหน้านี้ (caddy_data และ n8n_data) เพื่อใช้เป็น Persistent Storage ให้กับ Container

    image: ระบุ Docker Image ที่จะใช้สำหรับสร้าง Container ซึ่งจะดึงมาจาก Docker Hub

    restart: เป็น Tag ที่กำหนดพฤติกรรมการ Restart ของ Container หากหยุดทำงาน

    • unless-stopped (สำหรับ Caddy): Container จะ Restart อัตโนมัติ ยกเว้นคุณเป็นคนสั่งหยุดเอง
    • always (สำหรับ n8n): Container จะ Restart อัตโนมัติเสมอ ไม่ว่าจะหยุดทำงานด้วยสาเหตุใดก็ตาม
    • ข้อแตกต่างคือ หากคุณใช้คำสั่ง docker compose stop แบบ always จะรีสตาร์ทเองทันที แต่ unless-stopped คุณต้องสั่ง docker compose up ใหม่

    ports: ใช้กำหนดการเชื่อมโยง Port ระหว่าง Container ภายใน กับ Port ของเครื่อง VM ภายนอก (เช่น - 80:80 คือ Port 80 ภายนอกเชื่อมไป Port 80 ภายใน Container)

    environment: ใช้กำหนดตัวแปรสภาพแวดล้อม (Environment Variables) ภายใน Container ซึ่งจำเป็นสำหรับการตั้งค่าการทำงานของ n8n (เช่น Host, Port, Protocol, Timezone และ Webhook URL)

    เรามาดูกำหนดค่าใน .env แต่ละอันกันดีกว่าครับ

    # Replace <directory-path> with the path where you created folders earlier
    DATA_FOLDER=/<directory-path>/n8n-docker-caddy
    
    # The top level domain to serve from, this should be the same as the subdomain you created above
    DOMAIN_NAME=example.com
    
    # The subdomain to serve from
    SUBDOMAIN=n8n
    
    # DOMAIN_NAME and SUBDOMAIN combined decide where n8n will be reachable from
    # above example would result in: https://n8n.example.com
    
    # Optional timezone to set which gets used by Cron-Node by default
    # If not set New York time will be used
    GENERIC_TIMEZONE=Europe/Berlin
    
    # The email address to use for the SSL certificate creation
    SSL_EMAIL=example@example.com
    

    ถ้าเป็นของผม ผมก็จะกรอกแบบนี้ ข้อควรระวัง ตอนติดตั้งจริงๆ เนี้ยควรใช้ เป็น /etc เพื่อความปลอดภัยนะครับ

    # Replace <directory-path> with the path where you created folders earlier
    DATA_FOLDER=/demo/n8n-docker-caddy # ผมแนะนำว่าให้สร้างที่อื่นนะครับของจริง ผมสร้างใน /etc
    
    # The top level domain to serve from, this should be the same as the subdomain you created above
    DOMAIN_NAME=patrawi.com
    
    # The subdomain to serve from
    SUBDOMAIN=n8n
    
    # DOMAIN_NAME and SUBDOMAIN combined decide where n8n will be reachable from
    # above example would result in: https://n8n.example.com
    
    # Optional timezone to set which gets used by Cron-Node by default
    # If not set New York time will be used
    GENERIC_TIMEZONE=Asia/Bangkok
    
    # The email address to use for the SSL certificate creation
    # SSL_EMAIL=example@example.com
    

    เมื่อกำหนดเสร็จแล้วเราก็ต้องเข้าไปแก้ใน Caddy ต่อ เพื่อที่ Caddy จะได้ Reverse Proxy ทุก Ports ไปยัง Port 5678

    n8n.<domain>.<suffix> {
        reverse_proxy n8n:5678 {
          flush_interval -1
        }
    }
    

    สำหรับตรงนี้ผมก็จะแก้เป็น n8n.patrawi.com ที่เป็น domain ที่ผมซื้อไว้นั้นเอง อย่างไรก็ตาม หากเป็นการใช้งานในหน่วยงานของท่าน คุณจะต้องแจ้งทีม Network ให้เขากำหนด subdomain และ DNS recordให้เราอีกทีนะครับ เพื่อที่ DNS มองเห็น IP Address ของ VM ของคุณครับ

    เริ่มต้นและตั้งค่า DNS

    เมื่อแก้ Config ทั้งหมดเสร็จแล้ว ได้เวลา Spin up Container ของเราแล้วเย้

    sudo docker compose up -d
    

    แต่ว่าพอคุณ เปิดไปที่ dns ที่คุณกำหนดแล้ว คุณก็จะพบว่าข้อความ Can’t Reach This Server สาเหตุก็เป็นเพราะว่า DNS ของเรานั้นยังไม่ได้นำไปผูกกับ IP Address ที่ DigitalOcean นั้นเองเราก็จะต้องไปทำตรงนั้นกันก่อน


    *หมายเหตุ ถ้าเราให้ทีม network ของหน่วยงานเราเป็นคนสร้าง vm ขึ้นมาให้ตรงจุดนี้จะไม่มีปัญหานะครับ แต่จะไปมีปัญหาเรื่อง SSL/TLS แทนเดี๋ยวผมจะวนมาอธิบายให้ฟังอีกทีครับ

    ขั้นตอนการตั้งค่า DNS บน DigitalOcean (หรือผู้ให้บริการ DNS ของคุณ):

    1. เข้าไปที่ Dashboard ของ Droplet และเลือก Networking

    2. เลือก Tab Domains และใส่ Domain ที่ซื้อมา

    3. กรอก Domain ที่คุณซื้อมาลงในช่องว่าง

    4. สร้าง A Record แล้วเขียน Hostname เป็น n8n.patrawi.com (หรือ Subdomain ที่คุณต้องการ) และ Value ก็เลือกเป็น IP Address ของ VM ที่เราสร้าง

    เนื่องจากว่าปัจจุบัน blog นี้เป็น blog ที่บริหารและดูโดย WordPress รวมไปถึง Domainname ด้วย เพราะฉะนั้นผมจึงเลือกให้ WordPress เป็นคนบริหาร Domainname ด้วยเหตุนี้ผมจึงไม่มี NS Record ที่ชี้ไปที่ DigitalOcean

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

    หลังจากที่คุณตั้งค่า DNS ที่ผู้ให้บริการ Domain ของคุณเรียบร้อยแล้ว (เช่นในกรณีของผมที่ซื้อ Domain ผ่าน WordPress) คุณจะต้องแจ้งผู้ให้บริการ DNS ทราบว่า การเรียก Subdomain n8n นี้ ให้ Redirect ไปยัง IP Address ของ Droplet ของคุณ

    เมื่อดำเนินการเสร็จสิ้น คุณจะต้องรอสักระยะเพื่อให้ DNS Server ต่าง ๆ ทั่วโลกอัปเดตข้อมูล (Propagation) คุณสามารถตรวจสอบสถานะได้โดยใช้คำสั่ง

    dig <dns_name>
    

    หากขึ้นว่า NXDOMAIN หมายความว่า DNS Server นั้นยังไม่รู้จัก Domain ของคุณ ต้องรอต่อไปจนกว่าจะขึ้นว่า NOERROR นั่นแปลว่าคุณพร้อมใช้งานแล้วครับ!

    ทดสอบการติดตั้งและข้อสังเกตเกี่ยวกับ SSL/TLS

    เมื่อคุณลองเข้าถึง DNS Server ดังกล่าว และเห็นหน้าตาดังกล่าว:

    ยินดีด้วย! คุณติดตั้ง n8n server สำเร็จแล้ว!

    แต่เดี๋ยวก่อน! ก่อนที่เราจะจบบทความส่วนนี้ จำเรื่อง SSL/TLS ที่ผมทักไว้ได้ไหมครับ? ปัญหาคือ ปกติเมื่อเราใช้บริการผู้ให้บริการ DNS และ Server อย่าง DigitalOcean หรือ WordPress (ในกรณีของผม) พวกเขาจะดูแลจัดการเรื่อง SSL/TLS (ใบรับรองความปลอดภัย) ให้เรียบร้อย ทำให้ไม่มีปัญหาอะไร

    แต่ถ้าในกรณีที่คุณติดตั้ง n8n บนเครื่อง VM ที่คุณ Hypervise บน Hardware ของคุณเองล่ะ?

    ใช่ครับ! คุณจะต้องเป็นคนจัดการส่วนนี้เองทั้งหมด (เช่น การขอใบรับรอง SSL/TLS และการตั้งค่าให้ Web Server ใช้งาน) แต่ส่วนตัวผมคิดว่าหากลงรายละเอียดส่วนนี้ไปในตอนนี้ บทความจะยาวเกินไป และอาจทำให้เนื้อหาหลักเรื่องการติดตั้ง n8n เสียโฟกัสครับ

    สำหรับโพสต์นี้ การติดตั้ง n8n บน VM ถือว่าเสร็จสมบูรณ์แล้วครับ! และในส่วนสุดท้ายของบทความนี้ เมื่อเราติดตั้งสำเร็จแล้ว ก็ถึงเวลาที่จะมาลองสร้าง Workflow ง่ายๆ กันบ้าง เพื่อให้คุณเห็นภาพการทำงานจริงครับ

    6. ทดลองทำ Workflow ง่ายๆ 1 อัน

    ในส่วนนี้ ผมจะพาทำ Workflow ง่ายๆอย่างการดึงฟีดข่าวจาก Blognone แล้วนำไปโพสต์ใน Discord เพื่อที่เราจะได้ติดตามข่าวสารได้อย่างตลอดเวลาและตามทันโลกที่หมุนเร็วเสียยิ่งกว่าอะไร

    หลังจากที่เรากรอก Username, Password, Email ของ Admin เราก็จะเข้ามาที่หน้า Dashboard ที่หน้าตาอาจจะไม่เหมือนกับของผมหรอก แต่องค์ประกอบโดยรวมน่าจะคล้ายๆกัน

    1. สร้าง Workflow ใหม่

    จากนั้นให้เพื่อนๆคลิกไปที่ปุ่ม Create Workflow เพื่อสร้างหน้า Workflow อันใหม่ครับ

    ทุกคนจะได้หน้า freeform แบบนี้ครับผม โดยมี สี่เหลี่ยมบวกอยู่ตรงกลาง

    2. ตั้งชื่อ Workflow

    คลิกตรง My workflow เพื่อทำการเปลี่ยนชื่อให้สื่อความหมาย เพราะเวลาเรากลับมาแก้เราจได้รู้ว่าอันไหนเป็นอันไหนนะครับ

    แก้เสร็จแล้วก็ กดปุ่มตรงกลางได้เลยยย

    3. ตั้งค่า Trigger (RSS Feed)

    เมื่อกดแล้วจะมี แทบขึ้นมาด้านขวาใช่ไหมครับ ตรงส่วนนี้เราเรียกว่า Trigger อธิบายแบบง่ายก็คือเราจะให้ Workflow นี้เริ่มทำงานอย่างไร เดี๋ยวผมจะมีอีก โพสที่มาขยายความตรงนี้นะคับ แต่เบื้องต้นให้ทุกคนเลือก On App Event

    เมื่อคลิกไปแล้วก็จะได้ ช่อง Search มาพร้อมกับหลายชื่อ Node ต่างๆ สำหรับการ Integration นะครับ สำหรับเนื้อหาวันนี้ขอให้พิมพ์​ RSS ในช่องค้นหาครับผม และเลือก RSS Feed Trigger

    จากในรูปนี้ให้เราอย่าพึ่งไปสนใจตัว Poll Times โฟกัสไปที่ Feed URL ก่อน สำหรับในช่องนี้ ผมเลือกใช้ RSS Feed ของทาง blognone

    จากนั้นก็ลองกด Fetch Test Event เพื่อทดสอบผลลัพธ์

    เมื่อกดแล้วด้านขวาก็จะเป็น json ที่เราเรียกออกมานั้นเอง

    แต่อย่างที่ทุกคนเห็นเรายังไม่สามารถเอาลักษณะนี้ไปใช้ได้ เราต้องมีการจัดรูปแบบ JSON ก่อน ให้เพื่อนๆปิด ฟอร์มนี้ไปก่อนนะครับแล้วกด + เพื่อเพิ่มเมนู Data Transformation ต่อไป

    4. จัดรูปแบบข้อมูลด้วย Data Transformation (Edit Fields)

    การทำ Data Transformation คือการที่เราแก้ไข กรอง และแปรงข้อมูลให้อยู่ในรุปแบบต่างๆแทนนะครับ

    ในหน้า Data Transformation ให้เลือก “Edit Field”

    ในฟอร์มนี้ คุณสามารถ Drag & Drop Field ที่คุณต้องการใช้จากข้อมูล JSON ทางด้านซ้าย มาวางในช่องว่างตรงกลางได้เลยครับ

    ว่าแต่ แล้ว JSON คืออะไร ผมขอแวะอธิบายตรงนี้นิสสนึงนะครับ เพื่อไม่ให้เพื่อนๆสงสัย

    JSON คืออะไร

    JSON ย่อมาจาก JavaScript Object Notation เป็นรูปแบบข้อมูลประเภทหนึ่งที่นิยมใช้ในการส่งผ่านข้อมูลบน Protocol HTTP นั้นเอง โดยจะมีลักษณะการเก็บข้อมูลเป็น Key (ชื่อข้อมูล) และ Value (ค่าของข้อมูล)

    ตัวอย่างเช่น: หากมี Key คือ “Title” และ Value เป็น “Alibaba Cloud อัปเดตโมเดล Qwen3-235B-A22B เขียนโค้ดได้ระดับเดียวกับ Kimi K2 แต่ต้นทุนถูกลง” นั่นหมายความว่าหัวข้อข่าวคือข้อความนั้นนั่นเอง

    5. เชื่อมต่อและส่งข้อมูลไปยัง Discord

    โอเคเรากลับมาต่อที่เนื้อหากันดีกว่า หลังจากที่เราได้ มา 2 Node แล้วทีนี้ก็ถึงเวลาส่งข้อมูลกันแล้วครับ

    ให้เพื่อนๆ กด + แล้วก็จะได้หน้าตาแบบด้านล่างมาใช่ไหมครับ

    พิมพ์ลงไปว่า discord ดังรูป และเลือก เมนู Discord

    จากนั้นให้ทำการมองหาคำว่า Send a Message แล้วคลิกไปได้เลยครับ

    เพื่อนจะได้หน้าตา มาแบบนี้ใช่ไหมครับ ให้เพื่อนๆ เลือก ตรง Connection Type แล้วมองหาคำว่า Webhook

    ถามว่าทไมต้องเป็ฯ Webhook นอกจากจะเป็นวิธีที่เร็วและสะดวกที่สุดในการทำ RSS Feed Message Channel แล้ว ยังเป็นวิธีที่ง่ายที่สุดเมื่อเทียบกับอีก 2 ข้อนั้นเองครับ

    เมื่อเลือกมาแล้วให้เพื่อนๆ คลิก Create new Credential

    6. สร้าง Webhook URL จาก Discord

    ตอนนี้เราต้องสลับไปที่แอปพลิเคชัน Discord เพื่อไปคัดลอก Webhook URL มา

    เข้าไปที่ Text Channel ที่เราสนใจ และคลิกตรงรูปฟันเฟือง

    เลือก Tab Integration แล้ว กด Webhook

    หลังจากนั้นให้กด New Webhook

    เปลี่ยนชื่อ และ กด Copy Webhook URL

    7. วาง Webhook URL และจัดรูปแบบข้อความใน n8n

    Paste Webhook URL ที่คัดลอกมาจาก Discord ลงในช่องว่าง

    จัดรูปแบบข้อความที่จะโพสต์ใน Discord Node ของคุณ (เช่น การดึง Field Title หรือ Link จากขั้นตอน Data Transformation มาใช้ในข้อความ)

    สิ้นสุด Workflow แรก!

    และเราก็ทำ Workflow แรกเสร็จเรียบร้อยแล้ว หลังจากที่คุณตั้งค่าและจัดรูปแบบข้อความเสร็จ อย่างลืมกด Save และเปิดใช้งาน Workflow กันด้วยนะครับ

    7. บทสรุป

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

    ถ้าเพื่อนๆสงสัยอะไร สามารถสอบถามได้ใน Comment ได้เลยครับ ยินดีตอบครับผม

    แล้วในบทความถัดไป ผมจะมีเรื่องอะไรมาอธิบาย อย่าลืมติดตามกันนะครับ

    Related Article

    1. https://data-espresso.com/install-n8n-local-with-docker/
    2. https://docs.n8n.io/hosting/
    3. https://docs.n8n.io/hosting/installation/server-setups/digital-ocean/
    4. https://www.blognone.com/node/feed

  • รีวิวทุน ก.พ. UIS: Part 1 การเตรียมตัว

    รีวิวทุน ก.พ. UIS: Part 1 การเตรียมตัว

    สวัสดีครับ ขออนุญาตแนะนำตัวอีกซักครั้ง ผม พีรวัส วารีธัญญรัตน์ เรียกสั้นๆว่า พีร์ ก็พอ ผมเป็นนักเรียนทุน ก.พ. ทุนเพื่อดึงดูดผู้มีศักยภาพสูงที่กำลังศึกษาอยู่ในสถาบันการศึกษาในประเทศ (ทุน UIS)

    หลายๆท่านคงสงสัยว่าทุนอะไร ? มีทุนนี้ด้วยหรอ ? ละต่างจากทุนบุคคลทั่วไปยังไง ? วันนี้ผมจะมาแชร์ประสบการณ์ว่าเจอทุนนี้ได้อย่างไร สอบยากไหม ต้องฝึกงานหรือเปล่า พร้อมแล้วไปลุยกันเล๊ยย

    Table of Contents (ปรับปรุง)

    1. ทุน UIS คืออะไร? และผมหาทุนนี้เจอได้อย่างไร
    2. ด่านแรกสู่ทุน UIS: เกณฑ์การสมัครและภาพรวมกระบวนการ
      • คุณสมบัติของผู้มีสิทธิ์สมัครสอบ
      • เอกสารและหลักฐานที่ต้องเตรียม
      • ลำดับและช่วงเวลาของกระบวนการ
      • ภาพรวมโครงสร้างการคัดเลือก (5 ด่าน)
    3. เจาะลึกด่านที่ 1: การสอบข้อเขียน
      • เนื้อหาวิชาสอบข้อเขียน
      • รีวิวจากประสบการณ์: การเตรียมตัวและบรรยากาศวันสอบ
    4. เจาะลึกด่านที่ 2: การสอบภาคปฏิบัติ หรือการฝึกงาน และการเขียนเรียงความ
    5. เจาะลึกด่านที่ 3: การอภิปรายกลุ่ม
    6. เจาะลึกด่านที่ 4: การสอบสัมภาษณ์
    7. ก้าวสุดท้าย: การทำสัญญา
    8. บทสรุป Part 1 และบทถัดไปของ “การเดินทางกับทุน UIS”

    ทุน UIS คืออะไร? และผมหาทุนนี้เจอได้อย่างไร

    ทุน UIS

    เพื่อนคงสงสัยว่าทุน UIS คืออะไร ไม่ค่อยเห็นคนพูดถึง แล้วมันต่างจากทุนบุคคลทั่วไปอย่างไร ทุน UIS หรือ Undergraduate Intelligence Scholarship Program เป็นทุนที่มอบให้กับนิสิต/นักศึกษาในระดับปริญญาตรีปีสุดท้ายที่ที่มีความมุ่งมั่นอยากจะพัฒนาประเทศ

    ซึ่งเมื่อเรียนจบแล้ว จะได้รับการบรรจุราชการและเริ่มทำงานทันทีตามความต้องการของหน่วยงานที่ร้องขอตำแหน่งที่เราสมัคร ก่อนไปศึกษาต่อในระดับชั้นปริญญาโทในต่างประเทศ เพื่อที่จะได้นำความรู้กลับมาพัฒนาประเทศต่อไป อ้างอิงจาก ทุน UIS บน ก.พ.

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

    ด้วยความที่ว่าก็ช่วงนั้นก็ยังว่างอยู่พอดี เพราะว่า เป็นช่วงปิดเทอมย่อย ก็เลยอะๆ ลองสมัครหน่อยไม่เสียหายอะไรหรอก และนั้นก็เป็นการตัดสินใจที่จะเปลี่ยนผมไปตลอดกาล

    ด่านแรกสู่ทุน UIS: เกณฑ์การสมัครและภาพรวมกระบวนการ

    คุณสมบัติของผู้มีสิทธิ์สมัครสอบ

    1. ต้องไม่มีลักษณะต้องห้ามตาม มาตรา 36 แห่งพระราชบัญญติระเบียบข้าราชการพลเรือน พ.ศ 2551 เช่น ห้ามเป็นบุคคลล้มละลาย สามารถอ่านข้อมูลเพิ่มเติม คลิกที่นี้เพื่ออ่านเพิ่มเติมได้เลยครับ
    2. เป็นผู้ที่กำลังจะศึกษาในชั้นปีสุดท้ายของปริญญาตรี และหลักสูตรที่เรียนก็ต้องเข้าข่ายในสาขาวิชาตามที่กำหนดในคุณสมบัติเฉพาะแต่ละหน่วยทุน เช่น อย่างผมจะสมัครตำแหน่ง นักวิชาการคอมพิวเตอร์ก็ต้องเรียนอะไรซักอย่างที่ใกล้เคียง เช่น Software Engineering / Computer Science / AI / Computer Engineering เป็นต้น
    3. ในส่วนของ GPA อาจจะต้องเช็คอีกทีขึ้นอยู่กับสาขาที่เรียนอยู่อย่างของผมก็คือไม่ต่ำกว่า 2.75
    4. ต้องไม่มีการรับทุนอื่นที่มีข้อผูกพันในการชดใช้ทุน
    5. เป็นคนดี 🙂

    เอกสารและหลักฐานที่ต้องเตรียม

    1. ใบสมัครที่อัปโหลดรูปถ่ายจากระบบรับสมัครสอบ
    2. หนังสือรับรองสถานภาพการศึกษา
    3. สำเนาระเบียนแสดงผลการเรียน Transcript
    4. หนังสือรับรองความประพฤติ
    5. เอกสารอื่นๆ อย่างสำเนาเปลี่ยนชื่อ-สกุลเป็นต้น ถ้ามีผลคะแนนภาษาอังกฤษ ก็แนบมาด้วยก็ได้นะครับพวก IELTS TOEFL TOEIC
    6. แบบตรวจสอบเอกสารประกอบการสมัครสอบ และลงลายมือชื่อให้ครบถ้วน

    ระยะเวลาในแต่ละช่วง

    • ประกาศรับสมัคร มักจะเป็นช่วงปลายปี อย่างถ้าของปี 2568 ก็จะประกาศรับสมัคร ตอนธันวาคม 2567
    • สอบข้อเขียน ช่วงเดือนกุมภาพันธ์ ตามสถานที่สอบ ในกรุงเทพ หรือ ปริมณฑล
    • กิจกรรมการฝึกงาน 6 สัปดาห์ช่วง พฤษภาคม ถึง มิถุนายน
      • หมายเหตุ ถ้าน้องๆ คนไหนที่เรียนพวกวิศวฯ คอม มันจะต้องมีฝึกงานตอนปี 3 ใช่ไหมครับ แนะนำว่าให้ทำเรื่องกับ ก.พ. และหน่วยงานที่เกี่ยวข้องว่าเราขอฝึกงานรวบไปเลย จะได้ไม่เกิดการซ้ำซ้อนครับผม อย่างของผมก็ ขอฝึกรวบไปเลย 2 เดือนกว่า ครับ
    • กิจกรรมก่อนการประเมินความ เหมาะสมของบุคคลเพื่อรับทุน ช่วง กรกฎาคม
    • สัมภาษณ์ กรกฎาคม
    • ทำสัญญา ธันวาคม

    ภาพรวมโครงสร้างการคัดเลือก

    สำหรับการสอบนั้นจะแบ่งออกเป็นสองส่วนหลักๆได้แก่

    1. สอบข้อเขียน (ทุกคนสอบเหมือนกันหมด)
    2. สอบภาคปฏิบัติ หรือ การฝึกงาน และการเขียนเรียงความ
    3. ทดสอบ EQ และการอภิปรายกลุ่ม
    4. สอบสัมภาษณ์

    เจาะลึกด่านที่ 1: การสอบข้อเขียน

    การสอบข้อเขียนนั้นจะแบ่งออกเป็นข้อสอบสองชุดได้แก่

    1. วิชาภาษาอังกฤษ
    2.  วิชาความสามารถทั่วไปเชิงวิชาการ

    วิชาภาษาอังกฤษ
    ข้อสอบภาษาอังกฤษในมุมมองผม ผมรู้สึกว่าเหมือนกับสอบข้อสอบ CU-TEP โดยจะมีเนื้อหาข้อสอบดังนี้

    • Vocabulary and Expression
    • Error Recognition
    • Reading Comprehension

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

    รีวิวจากผู้เขียน และการเตรียมตัว


    ด้วยความที่ว่าร้างมือจากการทำข้อสอบไปนานพอสมควร (3 ปี ตั้งแต่สอบเข้ามหาวิทยาลัย) ผมก็เลยต้องไปคุ้ยๆข้อสอบเก่ามานั่งอ่านทบทวน บวกกับดูแนวข้อสอบ ก.พ. เก่าๆ และดูข้อสอบ CU-TEP เพราะตอนนั้นไม่แน่ใจเลยว่าตัวข้อสอบจะหน้าตาเป็นอย่างไรก็เลยเกร๊งหลายๆแนวเอาไว้

    ตอนไปสอบ ปี 2565 ตอนนั้นถ้าจำไม่ผิดน่าจะเป็นที่โรงเรียนสวนดุสิตครับและวันนั้นคือคนสอบคือเยอะมากกกก (ก ไก่ ล้านตัว) ด้วยความที่สถานที่สอบไม่ได้อยู่ติดกับรถไฟฟ้า ที่ผมโดยสารเป็นประจำ ผมกับแม่จึงเลือกที่จะเอารถไป แม้ว่าคนจะเยอะ แต่ด้านในก็พอมีที่จอดอยู่บ้างนะครับ แต่ถ้าไปสายมาก ๆก็อาจจะไม่เหลือเพราะฉะนั้นก็ระมัดระวังกันด้วยนะครับ

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

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

    Vocabulary and Expression ก็จะประมาณว่ามีช่องว่างให้เติมคำอะไรลงไปถึงจะถูกต้อง หรือคำนี้แปลว่าอะไร Error Recognition ก็จะเป็นข้อสอบที่ให้เราหาว่าจากประโยคข้างต้นมีตรงไหนที่ Grammar ผิด, ใช้ Tense ไม่ถูกต้อง, หรือใช้รูปแบบคำไม่ถูกต้อง เช่น ควรใส่ Noun ดันไปใส่ Adjective และส่วนสุดท้ายก็คือ Reading Comprehension ที่จะให้บทความ มาอ่านและเราก็ต้องทำความเข้าใจ ก่อนที่จะไปตอบคำถามด้านล่างนั่นเอง

    ต่อไปก็วิชาความสามารถทั่วไปเชิงวิชาการ
    โดยรวมข้อสอบก็จะเป็นการวิเคราะห์และการใช้เหตุผล มีให้อ่านพารากราฟ ภาษาไทยยาวตั้งแต่ 1/4 ของหน้ากระดาษถึงครึ่งหน้ากระดาษแล้วก็ถามว่า ผู้เขียนรู้สึกอย่างไร อะไรที่เป็นเหตุและผลของเนื้อเรื่องเป็นต้น ครับ อาจจะมีพวกข้อสอบประเภท ลำดับง่ายๆให้ทำด้วยเช่นกัน

    สำหรับผมแล้ว ข้อสอบที่เป็นอ่านภาษาไทย ไม่ได้ยากมากถ้าเราค่อยๆอ่านและตีความโดยยึดตามเนื้อหาที่ให้อ่าน อย่าเผลอเอาความคิดตัวเองไปใส่นะครับ เพราะมันจะผิดให้ยึดตามสิ่งที่โจทย์ให้เท่านั้นนะครับ สำคัญมาก !!!

    ส่วนข้อสอบลำดับอนุกรม หรือพวกเปอร์เซ็นต์คือข้อสอบประมาณ มัธยม ซึ่งผมเชื่อว่าถ้าฝึกทำอยู่เป็นประจำก็ทำได้แน่นอนครับ

    ต่อไปก็จะเป็นเนื้อหาเกี่ยวกับการสอบภาคปฏิบัติหรือ การฝึกงานนะครับ ซึ่งการที่จะได้ไปฝึกงาน แปลว่าเราต้องผ่านการสอบข้อเขียนมาเรียบร้อยแล้วนั่นเองครับ

    เจาะลึกด่านที่ 2: การสอบภาคปฏิบัติ หรือการฝึกงาน และการเขียนเรียงความ

    กรมบังคับคดี ศูนย์เทคโนโลยีสารสนเทศและการสื่อสาร

    ถึงผมจะเรียกว่ามันคือการสอบภาคปฏิบัติก็เถอะแต่จริง ๆ แล้วก็คือการให้ไปลองฝึกงาน เพื่อดูว่าหน่วยงานที่ร้องขอตำแหน่งเราไปเนี่ย เขาทำงานแบบไหนกันมีเนื้องานอะไร แล้วประมาณไหนบ้าง

    อย่างของผม ตอนที่ไปฝึกงานก็ ไปฝึกงานที่กรมบังคับคดี ศูนย์เทคโนโลยีสารสนเทศและการสื่อสารเป็นระยะเวลา 6 สัปดาห์ แต่เนื่องจากว่าผมสมัครหน่วยทุนเดียวและก็ต้องฝึกงานของที่มหาลัยด้วย ผมก็เลยฝึกงานทั้งหมด 9 สัปดาห์แทนครับ เพื่อเก็บชั่วโมงของมหาวิทยาลัยด้วยเช่นกัน

    ตลอดระยะเวลา 6 สัปดาห์ (ที่เหลือขอไม่นับละกันครับ เพราะเป็นของฝึกงานที่คณะที่ผมเรียนแล้ว) ด้วยความว่าเป็นงานด้าน IT งานส่วนใหญ่ที่ผมเข้าไปเรียนรู้เนื้องานก็จะเป็นพวก Database & Web development, Network & Security, IT Support

    ตอนนั้นด้วยความว่า ไอ้เรามันก็คนร้อนวิชาอยู่พอตัว ก็เลยขอเขาลอง POC (Proof Of Concept) เกี่ยวกับการทำ Line Chatbot ด้วย Line Messaging API เพื่อใช้แจ้งรายการอาหารในแต่ละสัปดาห์ก่อนที่จะหมด 5 สัปดาห์พอดี

    Proof of Concept Line Messaging API

    ช่วงนั้นก็ถือว่าสนุกมาก เพราะเป็นงานที่ชอบ บวกกับมีความท้าทายหลายๆเรื่องที่จะพบได้แค่ในหน้างานราชการ ซึ่งแตกต่างกับการทำงานแบบเอกชน

    นอกจากนี้ผมแนะนำว่าเราก็ควรจะมีปฏิสัมพันธ์กับพี่ๆที่ทำงานไว้ด้วย เพราะอย่าลืมว่าหลังจากฝึกงานเสร็จแล้ว ถ้าเราเลือกที่นี้เพื่อใช้ทุนระยะที่ 1 เราก็จะต้องกลับมาเจอพวกเขาอีก การที่ทำความรู้จักไว้เนืองๆ ก็จะเป็นการดีกว่า อีกทั้งยังเป็นการแสดงถึงความสามารถในการปรับตัวเข้ากับผู้อื่นได้เช่นกัน

    เมื่อฝึกงานเสร็จแล้วก็จะได้รับการประเมินจากทางพี่เลี้ยงและให้เราเขียนเรียงความ ซึ่งก็จะเป็นการเขียนเรียงความเกี่ยวกับ “คุณได้อะไรจากการฝึกงาน” ซึ่งตอนนั้นที่เขียนก็เขียนไปเกือบ ๆ 1 หน้ากระดาษเลยครับ

    หลังจากนั้นพอผ่านมาซักระยะ ก.พ.ก็จะเรียกตัวเราให้ไปทำ การอภิปรายกลุ่มกับเพื่อนที่มีสิทธิ์ได้รับทุน ก.พ. และ สอบสัมภาษณ์ที่ สำนักงาน ก.พ. ในลำดับต่อไปครับ

    บรรยากาศการทำงาน

    เจาะลึกด่านที่ 3: การอภิปรายกลุ่ม

    หลังจาก ฝึกงานเสร็จแล้ว ก.พ. ก็จะนัดวันให้เราเข้าไปทำกิจกรรม อภิปรายกลุ่ม (Group Disccusion)โดยจะเป็นการที่เราและเพื่อน ๆ จะต้องช่วยกันทำงานกลุ่มและนำเสนอให้กับกรรมการ 2-3 ท่าน

    ซึ่งสำหรับกิจกรรมนี้เขาต้องการประเมินว่า ถ้าต้องทำงานร่วมกับ ผู้อื่นที่พึ่งจะเคยเจอกันครั้งแรก เราสามารถปรับตัวเข้ากับสังคม สิ่งแวดล้อม และการแก้ปัญหาเฉพาะหน้าได้ดีแค่ไหนครับ

    ส่วนตัวแนะนำว่า คอยหาช่องไฟพูดและผลัดกันพูดกับเพื่อน ๆ อย่าปล่อยให้ Dead Air จนนานเกินไป อาจจะต้องมีบางจังหวะที่เราต้องนำ และบางจังหวะที่เราต้องตามเพื่อนครับ ตอนรุ่นผมจะเป็นออกแบบโลโก้ UIS ครับ
    สำหรับ Session นี้ก็เหมือนกับการวัดว่าเรามีทักษะความเป็นผู้นำ และผู้ตาม เป็นคนแบบไหน สำหรับกรรมการด้วย

    เจาะลึกด่านที่ 4: การสอบสัมภาษณ์

    และก็มาถึงส่วนสุดท้าย การสอมสัมภาษณ์ ใน Session นี้เราจะถูกถาม และต้องตอบทั้งเป็นภาษาอังกฤษ และ ภาษาไทย โดยจะมีกรรมการ 5 ท่าน ได้แก่ นักจิตวิทยา นักวิชาการที่เกี่ยวข้องกับหน่วยงานที่เลือก (ของผมเป็น ผอ. ศูนย์เทคโนโลยีสารสนเทศและการสื่อสาร) เจ้าหน้าที่ของ ก.พ. และผู้เชี่ยวชาญด้านภาษาอังกฤษ

    โดยการสัมภาษณ์จะเริ่มจากให้เราแนะนำตัวเราก่อน ก็ขายตัวเองไปเลยครับ พรีเซ้นท์ตัวเองได้เลยเต็มที่ จะมีถามว่าครอบครัวเป็นไงบ้าง ทำไมถึงอยากรับทุนนี้ ถ้าสมมติว่ากลับมาจากเรียนต่อ ป.โทแล้ว จะทำอะไร อีก 10 ปีข้างหน้า เราเห็นตัวเองเป็นอย่างไร? ตอนฝึกงานได้อะไรบ้าง? อาจจะมีถามว่าหน่วยงานที่ไปฝึกงานเขาทำอะไรบ้าง? ถ้าไปเรียน ป.โท จะเรียนต่อด้านอะไร?

    สำหรับ Session นี้ใจเย็น ๆ ไม่ต้องเครียดครับค่อย ๆ ตอบไป มันอาจจะดูกดดัน เพราะว่ามัน 5 รุม 1 เนาะ 555 ในส่วนของคำถามภาษาอังกฤษก็พูดไปเลยครับ ถึงจะเป็นผู้เชี่ยวชาญภาษาอังกฤษ แต่สำหรับ session นี้เขาต้องการให้เรากล้าสื่อสารเป็นหลัก เพราะฉะนั้นคิดอะไรได้ก็ตอบไปเลยครับ อย่ากลัว

    ก้าวสุดท้าย: การทำสัญญา

    หากเราผ่านการสัมภาษณ์และประกาศผลว่าติด ทาง ก.พ. ก็จะชี้แจงและให้เราดูสัญญาเพื่อเตรียมทำสัญญารับทุนระยะที่ 1 และ เมื่อทำสัญญาแล้ว ทาง ก.พ. ก็จะ ให้ทุนรัฐบาลเพื่อศึกษาในชั้นปีสุดท้ายจนสำเร็จการศึกษาระดับปริญญาตรี ตามโครงสร้างหลักสูตร ทั้งนี้ ต้องใช้ระยะเวลาศึกษาไม่เกิน 1 ปี

    บทสรุป Part 1 และบทถัดไปของ “การเดินทางกับทุน UIS”

    จบไปแล้วนะครับสำหรับพาร์ทที่ 1เนื้อหาในส่วนนี้ก็จะพูดถึงแค่การสมัครและการสอบเท่านั้น ถ้าชอบก็อย่าลืมกดไลก์ และติดตามรอฟังบทถัดไปได้เลยครับ สำหรับบทหน้าผมจะมารีวิวว่าตลอดระยะเวลาเกือบ 2 ปีที่ทำงานราชการในฐานะนักเรียนทุน UIS เป็นอย่างไร แล้วเจอกันครับ

    References

    1. รายละเอียดเกี่ยวกับทุน
    2. รายชื่อตำแหน่งที่เปิดรับ
    3. กำหนดการประจำปี 2568

  • Main Series: Part 2 เข้าใจ Classification ใน Supervised Learning

    Main Series: Part 2 เข้าใจ Classification ใน Supervised Learning

    และแล้วก็ผ่านมาได้ 2 อาทิตย์แล้วนะครับ หลังจากที่ผมได้เขียนเกี่ยวกับ Linear Regression ในบทความก่อนหน้าไป

    ผมว่าเพื่อนๆก็คงอยากจะรู้แล้วว่า Model ที่เหลืออยู่ของ Supervised Learning เนี้ยคืออะไรบ้าง เพราะฉะนั้นผมว่า นี้ก็คงจะเป็นฤกษ์งามยามดีที่เราจะเปิด post ใหม่แล้วละครับ

    หลักจากที่ Blog ที่แล้วเราได้เรียนรู้เกี่ยวกับ Linear Regression ทั้ง Simple และ Complex ไปแล้ว วันนี้เรามาดูอีกเทคนิคที่นิยมทำเพื่อแยกประเภท หรือแบ่งออกเป็น Classes อย่าง Classification กันดีกว่าครับ

    คุณเคยมีประสบการณ์ที่ต้องเดาหรือเลือกคำตอบ yes/no ใช่/ไม่ใช่ ไหมครับ หรือ ในตอนที่คุณรู้สึกอยากจะเปลี่ยนโปรโทรศัพท์ อยู่ๆค่ายมือถือก็เลือกโทรมาหาคุณเพื่อขายโปรโมชั่น คุณเคยสงสัยไหมว่าพวกเขารู้ได้อย่างไร ติดกล้องในห้องเราหรือเปล่านะ หรือบางทีคุณอาจจะคิดว่ามันก็แค่ บังเอิญ ?

    พูดกันตามตรงข้อมูลเหล่านี้มันมีเบื้องลึกเบื้องหลังมากกว่านั้น และวันนี้ผมก็มีคำตอบมาให้พวกคุณได้คลายความสงสัยเสียที เมื่อพร้อมแล้วไปลุยกั๊นน

    Table of Content


    1. What is Classification ?
    2. General Classification Algorithms
    3. Let’s Implement them (Python Coding)
    4. Imbalance Dataset
    5. Evaluation
    6. Conclusion

    What is Classification

    Classification เป็นหนึ่งในประเภท Algorithms ภายใต้วิธีการสอนแบบ Supervised Learning โดยมีเป้าที่จะจัดหมวดหมู่ข้อมูล (Categorization) หรือ การทำนายกลุ่ม (Class Prediction) ที่เราต้องการทำนายค่า โดยใส่ค่าบางอย่างเข้าไปเช่น X (ข้อมูลดิบ) แล้วได้ค่า y (ประเภท) ออกมานั้นแหละคือ Classification Algorithm.

    สำหรับ Classification เองก็สามารถแบ่งย่อยได้เป็นสองกลุ่มใหญ่ได้แก่

    • Binary Classification: ประเภทที่ได้คำตอบออกมาสองค่า ก็คือ 0 กับ 1 เช่น Yes/No Spam/ Not Spam, Disease/ No Disease
    • Multi-class Classification: ประเภทที่จัดกลุ่มข้อมูลออกเป็นมากกว่าแค่ 2 ค่า ยกตัวอย่างเช่น ประเภทของสิ่งมีชีวิต แบ่งตาม Specie หรือ ตาม Phylum พวกผลไม้ และ พวกประเภทตัวเลขต่างๆ

    ก่อนที่เราจะไปดูว่าใน Classification มี Algorithm อะไรบ้าง เรามาดูนิยามคำศัพท์กันก่อน เพื่อที่เมื่อเข้า Session ถัดไปแล้วก็จะได้ไม่งงกันครับ

    ระระรู้ได้ไงกันว่าฉันกำลังจะยกเลิกโปรมือถือ
    คำศัพท์ความหมาย
    Features/Independent Variables (ตัวแปรอิสระ)คุณลักษณะต่างๆ ของข้อมูล ที่เราใช้เป็น ‘วัตถุดิบ’ ในการทำนาย เช่น อายุ, เพศ, จำนวนบุหรี่ที่สูบ (X)
    Labels/Classes/Dependent Variables (ตัวแปรตาม)ตัวแปรที่จะได้รับผลกระทบหากเราเปลี่ยนแปลงวัตถุดิบ y (ผลลัพธ์ที่เราต้องการทำนาย, ประเภท)
    Training Data, Testing Dataข้อมูลสำหรับ ‘สอน’ ให้โมเดลฉลาด และ ข้อมูลสำหรับ ‘ทดสอบ’ ว่าโมเดลที่เราสอนมานั้นเก่งแค่ไหน
    Algorithmสูตร หรือ กระบวนการ’ที่ใช้ในการเรียนรู้จากข้อมูลเพื่อสร้าง ‘โมเดล’ ขึ้นมา
    Predictionการทำนาย หรือการคาดคะเน
    Churn Analysisการทำนายลักษณะของลูกค้าที่มีแนวโน้มกำลังจะยกเลิกบริการหรือมีแนวโน้มจะไม่กลับมาซื้อสินค้าหรือบริการซ้ำอีก

    General Classification Algorithm

    สำหรับการทำ Classification Supervised Learning แล้ว ในปัจจุบันมีอัลกอริทึมหลายแบบมาก ซึ่งแต่ละอัลกอริทึมก็มีจุดแข็งและจุดอ่อนของตัวเองแตกต่างกันออกไป โดยขึ้นอยู่กับวิธีใช้ของผู้ใช้งาน ทั้งนี้เราก็ควรจะรู้จักแต่ละตัวไว้บ้างเพื่อให้เราไม่ติดกับดับ No Free Lunch ตามที่เราได้กล่าวไปในบทความก่อนหน้านี้

    มาเริ่มกันที่ตัวแรกและถือว่าเป็นตัวพื้นฐานสุดเลยอย่าง Logistic Regression

    ทุกคนอาจจะสงสัยทำไมชื่อมันคล้ายกับตัวก่อนหน้าอย่าง Linear Regression เลย สองอัลกอริทึมนี้เป็นอะไรกันหรือเปล่า ? คำตอบก็คือ ใช่ครับ! สองตัวนี้มีความเกี่ยวข้องกันนั้นก็คือใช้พื้นฐานทางสมการทางคณิตศาสตร์เหมือนกัน นั้นคือสมการเส้นตรง แต่วัตถุประสงค์แตกต่างกันอย่างสิ้นเชิงเลยครับ

    Linear Regression: ใช้ทำนายค่าตัวเลขที่ต่อเนื่องไปเรื่อยๆ (เช่น ราคาบ้าน, ยอดขาย)

    Logistic Regression: ใช้ทำนายความน่าจะเป็นเพื่อ “จัดกลุ่ม” หรือ “ตัดสินใจ” ว่าข้อมูลนั้นควรเป็นคลาสไหน (เช่น ใช่/ไม่ใช่, ป่วย/ไม่ป่วย, สแปม/ไม่ใช่สแปม)

    Logistic Regression

    แต่มีจุดต่างตรงที่ Logistic Regression จะนำ สมการเส้นตรงไปจำกัดขอบเขต ด้วย Sigmoid Function หรือ Logistic Function เพื่อให้ผลลัพธ์อยู่ในช่วง 0 ถึง 1 เสมอ

    เมื่อข้อมูลเราอยู่ระหว่าง 0 ถึง 1 นั้นหมายความว่ายิ่งข้อมูลมีค่าเข้าใกล้ 1 มากเท่าไหร่นั้นแปลว่าข้อมูลเหล่านั้นก็จะสามารถติดอยู่ในคลาส ใช่ หรือ 1 นั้นเอง

    ในสมการของ Sigmoid Function เพื่อนๆอาจจะสงสัยว่าตัว e คืออะไร ตัว e เป็นค่าคงที่ในวิชาคณิตศาสตร์ที่เรียกว่า Euler Value ซึ่งจะมีค่าเท่ากับ 2.71828 นั้นเอง

    Sigmoid Function

    ข้อดี (Pros) 👍

    • เข้าใจง่าย สามารถเขียนเป็นรูปสมการและอธิบายได้อย่างเข้าใจ
    • ผลลัพธ์ตีความได้ (แปลงออกมาเป็นความน่าจะเป็นได้)
    • ประสิทธิภาพที่ดีและสามารถใช้เป็น Baseline ของการทำ Binary Classification

    ข้อเสีย (Cons) 👎

    • อ่อนไหวต่อ Outliers: ข้อมูลที่โดดไปจากกลุ่มมากๆ อาจส่งผลกระทบต่อการลากเส้นตัดสินใจได้
    • หากข้อมูลที่มี Features เยอะอาจจะทำได้อย่างไม่เต็มประสิทธิภาพ

    def logistic_regression_model(df_X_scaled, y):
        X_trained, X_test, y_trained, y_test = train_test_split(df_X_scaled, y, test_size = 0.2, train_size=0.8, random_state = 42)
    
        lr = LogisticRegression(random_state = 42, max_iter= 1000)
        lr.fit(X_trained, y_trained)
    
        evaluation_model('Logistic Regression',lr,y_test, X_test)
        return
    

    Decision Tree

    Photo by vee terzy on Pexels.com

    อัลกอริทึมที่สองสำหรับการทำ Classification นั้นคือ Decision Tree (ต้นไม้ตัดสินใจ) ครับ อันที่จริงแล้วโมเดลนี้สามารถใช้ร่วมกันระหว่าง Regression หรือ Classification ก็ได้ โดยมีวัตถุประสงค์เพื่อที่จะทำนายค่า y โดยอ้างอิงวิธีที่มนุษย์ใช้ในการตัดสินใจ โดยแบ่งการตัดสินใจเป็นเงื่อนไขเล็กๆ เช่น สมมติเราจะซื้อของ 1 ชิ้น สมองก็จะค่อยๆแบ่งออกเป็นคำถามเล็กเพื่อตอบคำถามหลัก​ (ซื้อหรือไม่ซื้อ) เป็น สเปคดีไหม ? สีใช่ที่ชอบไหม ? ซื้อมาเอาไปทำไร ? คุ้มไหม ? เป็นต้น

    ข้อดี

    • ง่ายต่อการเข้าใจ สามารถสร้างเป็นกราฟได้
    • เตรียม data ง่าย บางครั้งแทบไม่ต้องใส่ข้อมูลในช่วงที่หายไป หรือเอา NULL ออกก็ได้ (แต่เราควรทำให้ข้อมูลสะอาดที่สุดนะครับเพื่อลดการผิดพลาดของการนำไปใช้)
    • ทำงานได้ดีมากกับข้อมูลที่มีความหลากหลายในด้าน features
    • ค่า Cost สำหรับการรัน Model. จะเป็น log(n) สำหรับจำนวน data ที่ใช้สอน โมเดล ส่งผลให้สามารถทดสอบกับข้อมูลจำนวนมากได้

    ข้อเสีย

    • การทำนายของ Decision Tree จะไม่ได้อยู่ในลักษณะที่เป็นตัวเลข ต่อเนื่อง Continuous number แต่จะเป็นค่าคงที่เฉพาะเอง ซึ่งนั้นหมายความว่าตัวโมเดลไม่เหมาะที่จะใช้ทำนายข้อมูลที่ต้องการดูแนวโน้มหรือ เทรนด์ (Extrapolation)
    • ด้วยความที่บางครั้ง Decision Tree ชอบสร้าง แขนงการตัดสินใจที่ซับซ้อนจนมากเกินไป อาจเกิดเหตุการณ์ Overfitting ได้ ซึ่งเราอาจจะเปลี่ยนไปใช้ Random Forest แทนเพื่อแก้ปัญหานี้
    • หากข้อมูลมี Outlier ผิดปกติเยอะอาจส่งผลต่อโครงสร้างของ Decision Tree นำไปสู่การทำนายที่ให้ผลลัพธ์ที่ผิดปกติได้
    def decision_tree_model(df_X_scaled, y):
        X_trained, X_test, y_trained, y_test = train_test_split(df_X_scaled, y, test_size = 0.2, train_size=0.8, random_state = 42)
    
        clf = DecisionTreeClassifier(random_state=42)
    
    
        clf.fit(X_trained,y_trained)
    
        evaluation_model('Decision Tree',clf, y_test, X_test)
        return
    
    

    Random Forest

    Random Forest

    อัลกอริทึม ถัดไปที่เป็นการต่อยอดจาก Decision Tree นั้นคือ Random Forest อัลกอรึทึมนี้เกิดจากการที่เรานำผลลัพธ์มากมายจาก Decision Trees หลายๆอัน และสรุปออกมาเป็นผลลัพธ์เดียว

    โดย Random Forest ถือว่าเป็นส่วนหนึ่งของ Ensumble Learning หรือ กลุ่มอัลกอริทึมที่เกิดจากการเทรนอัลกอริทึมเดิมซ้ำๆ หลายรอบ บนข้อมูลชุดเดียวกันจนได้ผลลัพธ์ โดยในแต่ละครั้งก็จะจัดสัดสวนข้อมูลที่ใช้เทรนไม่เท่ากันด้วย

    โมลเดลนี้สามารถนำไปใช้ได้ทั้งใน Regression หรือ Classification ก็ได้

    ข้อดี

    • ด้วยความที่ข้อมูลถูกเทรนซ้ำๆ ด้วยการแบ่งสัดส่วนที่ไม่เหมือนกัน (Bootstrap Aggregating (Bagging)) ส่งผลให้ความแม่นยำนั้นสูงเช่นกัน จินตนการเหมือนกับที่เราถามคำถามเดียวกันกับฝูงชน แล้วเราค่อยสรุปออกมาเป็นคำตอบเดียวจากหลายๆคำตอบนั้นเอง
    • จากที่ได้กล่าวใน โมเดลที่แล้วก็คือช่วยป้องกันการเกิด Overfitting
    • มีความหยืดหยุ่นมากในการทำ Hyper-parameters Tuning

    ข้อเสีย

    • ความยากในการตีความ (Interpretability) เพราะต้นไม้แต่ละค้นนั้นก็จะมีการเลือกใช้ Features ไม่เหมือนกัน (Feature Randomness)
    • มีต้นทุนสูงเนื่องจากว่าต้องผ่านการคำนวณหลายครั้ง
    def random_forest_model(df_X_scaled, y):
    
        X_trained, X_test, y_trained, y_test = train_test_split(df_X_scaled, y, test_size = 0.2, train_size=0.8, random_state = 42)
    
        rfc = RandomForestClassifier(random_state = 42)
        rfc.fit(X_trained, y_trained)
    
        evaluation_model('Random Forest',rfc, y_test, X_test)
        return
    

    K Nearest Neighbours

    อีกหนึ่งโมเดลที่เป็นที่นิยมและใช้งานง่ายมากๆ นั้นคือ knn หรือ K Nearest Neighbour โมเดลที่จะหาทำนายค่า จะดึงข้อมูลที่ใกล้เคียงตัวมันเองที่สุด K ตัว แล้วก็ค่อยดูว่าจาก K ตัวเป็น Class อะไรบ้าง

    KNN สามารถประยุกต์ใช้ได้ทั้งใน Classification หรือ Regression ก็ได้โดยที่

    • เราจะหาผลโหวตที่มากที่สุด หากเราทำ Classification
    • เราจะหาค่าเฉลี่ยของ K ทุกตัวหากเราทำ Regression เพื่อ Predict บางอย่าง

    ข้อดี

    • Implement ได้ง่าย และ Train ง่าย เพราะตัวอัลกอริทึมเองนั้นจำข้อมูลอย่างเดียวในช่วงที่ Train ข้อมูล ( Lazy Learner )
    • ใช้กับ Test Data หรือ Data ใหม่ที่โมเดลไม่เคยเห็นได้ดี

    ข้อเสีย

    • อัลกอริทึมนี้ sensitive มาก ถ้าเจอข้อมูลที่ Outlier เยอะๆ Missing Value, หรือ NaN ละก็เตรียมตัวระเบิดได้เลย
    • KNN ทำงานได้ช้ามากเมื่อมีข้อมูลเยอะ (Poor Scalability) เพราะทุกครั้งที่ ทำการทำนายมันจะต้องคำนวณระยะทุกๆจุด ใช่ครับมีล้านจุดก็ทำนายล้านที ต่างจากเพื่อนๆของมันที่ Train มาก่อนแล้ว
    • KNN จะทำได้ไม่ดีนักหากข้อมูลของเรามี Features เยอะมากๆ คุณลองคิดดูสิถ้าสมมติว่าข้อมูลมี 50 features มันก็จะดูห่างกันมาก ดูไม่ได้ใกล้เคียงกับใครเลย
    • การกำหนด K เหมือนจะง่าย แต่จริงๆ แล้วอาจจะให้ค่าที่ต่างกันหากไม่ระวัง
    def knn_model(df_X_scaled, y):
        X_trained, X_test, y_trained, y_test = train_test_split(df_X_scaled, y, test_size = 0.2, train_size=0.8, random_state = 42)
    
        knn = KNeighborsClassifier()
        knn.fit(X_trained,y_trained)
    
        evaluation_model('K-nearest neighbourhood', knn ,y_test, X_test)
        return
    

    Support Vector Machines

    สำหรับอัลกอริทึมสุดท้ายสำหรับบทความนี้ นั้นคือ อัลกอริทึมที่มีชื่อว่า SVM หรือ Support Vector Machines ซึ่งเป็นอัลกอที่มีความยืดหยุ่นมาก เมื่อข้อมูลมีความซับซ้อน หลาย Feature แต่จำนวน data ดันน้อย

    หลักการของ SVM คือการพยายามสร้าง ‘ถนน’ ที่กว้างที่สุดเท่าที่จะเป็นไปได้เพื่อแบ่งกลุ่มข้อมูลออกจากกัน โดยเส้นขอบถนนทั้งสองข้างก็คือ Support Vectors นั่นเอง

    ข้อดี

    • มีความยืดหยุ่นมาก
    • ทำงานได้ดีกับ ข้อมูลที่มี Dimensions เยอะแต่จำนวน data น้อย
    • สามารถจัดการกับข้อมูลที่ไม่เป็นเชิงเส้นได้ดีเยี่ยมด้วยเทคนิค Kernel Trick ถามว่า Kernel Trick คืออะไรมันก็คือการที่คุณพยายามจะแก้ปัญหาที่ไม่สามารถลาก เส้นตรงเส้นเดียวเพื่อแบ่งของได้ ยกตัวอย่างเช่น สมมติมี ถั่วสีน้ำเงินกับ สีเขียวอยู่บนโต๊ะปนๆกันอยู่ ถ้ามองก็จะเห็นเป็น 2 มิติใช่ไหมครับแยกไม่ได้เลย การใช้ Kernel Trick ก็เหมือนการที่คุณทุบใต้โต๊ะแล้วถั่วก็ลอยขึ้น อยู่ในมุมมอง 3 มิติ ส่งผลให้คุณสามารถใช้กระดาษ ( Hyperplane ) เพื่อสอดเข้าไประหว่างกลางเพื่อทำการแบ่ง นั้นแหละคือ Kernel Trick

    ข้อเสีย

    • อาจทำงานได้ไม่ดีกับชุดข้อมูลขนาดใหญ่มาก ๆ (เนื่องจากการคำนวณที่ซับซ้อน) หรืออาจต้องใช้เวลาในการปรับแต่ง Kernel ที่เหมาะสมเพื่อจัดการกับข้อมูลที่ไม่เป็นเชิงเส้น
    SVM
    def svc_model(df_X_scaled, y):
        X_trained, X_test,  y_trained, y_test = train_test_split(df_X_scaled, y, test_size = 0.2, train_size=0.8, random_state = 42)
        svc = SVC(random_state = 42, probability=True)
        svc.fit(X_trained, y_trained)
    
        evaluation_model('Support Vector Machines', svc,y_test, X_test)
        return
    

    หลังจากที่เราได้เรียนรู้อัลกอริทึมต่างๆ ขั้นตอนตอไปก็คือลองลงมือ Implement จริงดู ไปกันเล๊ยย

    Let’s Implement them w8 Python Code

    สำหรับรอบนี้ผมเลือกใช้ ข้อมูลชุดที่มีชื่อว่า framingham.csv สำหรับการ Train นะครับเป็นข้อมูลที่เกี่ยวกับคนไข้ที่ป่วยและมีแนวโน้มจะเป็นโรคหลอดเลือดหัวใจในระยะ 10 ปี หรือไม่

    ข้อมูลนี้จะประกอบไปด้วย 4,240 records และ 16 Columns เรามาลองดูแต่ละ Column กันดีกว่าครับ

    คำศัพท์ความหมาย
    maleระบุเพศของผู้ป่วย ชาย 1 หญิง 0
    ageอายุของผู้ป่วย
    educationระดับการศึกษามี (1-4)
    currentSmokerสถานะว่าสูบบุหรี่อยู่หรือไม่ 0 คือไม่สูบและ 1 คือยังสูบ
    cigsPerDayจำนวนบุหรี่ที่สูบในแต่ละวัน
    BPMedsระบุว่าผู้ป่วยรับประทานยาลดความดันโลหิต (1) หรือไม่ (0)
    prevalentStrokeระบุว่าผู้ป่วยมีประวัติเป็น Stroke หรือไม่ 1 คือเป็น 0 คือไม่
    prevalentHypระบุว่าผู้ป่วยมีประวัติเป็น Hypertension
    diabetesระบุว่าผู้ป่วยมีประวัติเป็นโรคเบาหวาน (1) หรือ ไม่ (0)
    totCholปริมาณโคเลสเตอรอลของผู้ปวย
    sysBPความดันโลหิตซิสโตลิกของผู้ป่วย
    BMIBMI ของผู้ป่วย
    heartRateอัตราการเต้นของหัวใจผู้ป่วย
    glucoseระดับน้ำตาลกลูโคสของผู้ป่วย
    TenYearCHDระบุผลลัพธ์ว่าผู้ป่วยมีแนวโน้มจะเป็นโรคหลอดเลือดหัวใจในระยะ 10 ปี หรือไม่
    diaBPความดันโลหิตไดแอสโตลิกของผู้ป่วย

    หลังจากรู้แล้วว่าแต่ละ Columns คือค่าอะไรเรามาลุยกันเลยดีกว่าครับทุกคน โดยเป้าหมายของเราก็คือ การสร้างโมเดลเพื่อทำนายความเสี่ยงของการเกิดโรคหลอดเลือดหัวใจในอีก 10 ปีข้างหน้าให้ได้ ป่ะลุยกัน 🔥

    Preprocessing

    ขั้นแรกเราก็ต้องโหลด dataset กันก่อนด้วย Library Pandas

    df = pd.read_csv('framingham.csv', decimal=',', sep=',', header =0)
    

    หลังจากนั้นก็ทำการ drop Null Value ออกด้วย dropna()

    สำหรับบทความนี้เราจะ drop ข้อมูลที่เป็น Null ออกไปเพื่อความกระชับของเนื้อหา แต่ในโลกของความเป็นจริงนั้น เราไม่ควรที่จะทำเช่นนั้น เพราะมีโอกาสทำให้ผลลัพธ์ของโมเดลเพี้ยนได้

    วิธีการที่ดีกว่าในกรณีเช่นนี้ ก็คือการที่เราแทนค่าลงไปด้วย Mean/Meadian Imputation จะช่วยให้โมเดลทำงานได้ดีกว่าครับ

    df.dropna(inplace=True)
    

    ลองตรวจว่าข้อมูลผลลัพธ์ผู้ป่วยกระจายตัวแบบใด

    print(df['TenYearCHD'].value_counts(normalize=True))
    
    

    ทำไมต้องใส่ normalize = True ?

    เพราะว่า โดยปกติแล้ว value_counts จะคืนค่าที่มีมากที่สุดใน columnที่เราเลือกก่อนเสมอและอยู่ในลักษณะของความถี่ธรรมดา อย่างไรก็ตาม หากเราต้องการจะเปรียบเทียบว่าข้อมูลมันเอียงไปทางใดทางหนึ่งหรือไม่นั้น (ข้อมูลไม่ใช่กระจายตัวสม่ำเสมอ) การใส่ normalize = True จะแปลงตัวเลขให้เป็นความถี่สัมพัทธ์ มีค่า [0,1] แทน ซึ่งง่ายต่อการตรวจสอบและทำความเข้าใจ

    เดี๋ยวเราจะกลับมาดูค่านี้กันไหมนะครับ ไปดูตรงอื่นๆกันก่อน

    เนื่องจาก .csv ที่ผมโหลดมา ดันให้ sysBP เป็น object type ซะงั้นผมก็เลยต้องแปลงค่าเสียก่อน

    df['sysBP'] = pd.to_numeric(df['sysBP'])
    

    หลังจากนั้นเราก็มาทำการ clean outlier กันต่อครับ โดยผมเลือกใช้วิธี เช็คด้วย IQR (Inter Quartile Range) และแทนค่าค่าที่เกินขอบเขตแทนที่จะลบออกครับ เพราะ Dataset จะหายไปราวๆ 15% หรือ 800 rows เลยทีเดียวถ้าเราไม่เก็บค่าพวกนี้ไว้ ซึ่งอาจส่งผลต่อ metrics ต่างๆได้

      features = ['male', 'age', 'cigsPerDay' , 'totChol', 'sysBP', 'glucose' ]
        y = df['TenYearCHD']
        X_processed = df[features].copy()
        for col in features:
                X_processed[col] = cap_outliers_iqr(X_processed[col])
    
    def cap_outliers_iqr(df_column):
    
        q1 = df_column.quantile(0.25)
        q3 = df_column.quantile(0.75)
        IQR = q3 - q1
        lower_bound = q1 - 1.5*IQR
        higher_bound = q3+ 1.5*IQR
    
        capped_column = np.where(df_column < lower_bound, lower_bound, df_column)
    
        capped_column = np.where(capped_column > higher_bound, higher_bound, capped_column)
        return pd.Series(capped_column, index = df_column.index)
    

    หลังจากนั้นเราก็มาทำการแปลง scale ข้อมูลด้วย StandardScaler เพื่อให้ข้อมูลอยู่บนบรรทัดฐานเดียวกันป้องกันการทำนายผิดพลาด

        scaler = StandardScaler()
        X_scaled = scaler.fit_transform(X_processed)
        f_X_scaled = pd.DataFrame(X_scaled, columns =X_processed.columns)
    

    StandardScaler เป็นหนึ่งใน feature scaling เทคนิคที่จะทำให้ข้อมูลของเรามีค่าเฉลี่ย เป็น 0 และ มีค่าการกระจายตัวของข้อมูลเป็น 1 ตามหลักการกระจายตัวปกติ วิธีนี้เป็นวิธีที่เหมาะต่ออัลกอริทึมอย่าง SVM และ Logistic Regression เพราะทั้งคู่ต้องการให้ข้อมูลอยู่ในลักษณะกระจายตัวปกติ

    ถึงแม้ว่าการทำ Scaling จะไม่ส่งผลโดยตรงต่อประสิทธิภาพของโมเดลประเภท Tree-based อย่าง Decision Tree หรือ Random Forest แต่การทำไว้ก็ถือเป็น Good Practice เมื่อเราต้องการทดลองหลายๆ โมเดลพร้อมกันครับ

    และสุดท้าย แบ่งข้อมูลเป็นสัดส่วน

    X_trained, X_test, y_trained, y_test = train_test_split(df_X_scaled, y, test_size = 0.2, train_size=0.8, random_state = 42)
    

    ในอนาคตเราจะมีเจาะลึกวิธีการแบ่งข้อมูลด้วยเทคนิคอื่นๆกันครับ ตอนนี้เราใช้ Train Test Split ไปก่อน

    Train Models

    models = {
            'Logistic Regression': LogisticRegression(random_state=42, max_iter=1000),
            'Decision Tree': DecisionTreeClassifier(random_state=42),
            'Random Forest': RandomForestClassifier(random_state=42),
            'K-Nearest Neighbors': KNeighborsClassifier(),
            'Support Vector Machine': SVC(random_state=42, probability=True)
        }
        for model_name, model in models.items():
            model.fit(X_trained, y_trained)
    

    อย่าลืมใส่ probability=True เพื่อให้เราสามารถอ่านค่า AUC ได้

    Score

    เมื่อเรา Train เสร็จแล้วก็ถึงเวลาวัดผลแล้วละ

    def evaluation_model(model_name, model,y_test,X_test):
        y_pred = model.predict(X_test)
        cm = confusion_matrix(y_test, y_pred)
        print(f'Model : {model_name}')
        print(cm)
        print(f'Accuracy Score: {accuracy_score(y_test, y_pred)}')
        print(f'Precision Score: {precision_score(y_test, y_pred)}')
        print(f'Recall Score: {recall_score(y_test,y_pred)}')
        print(f'F1 Score: {f1_score(y_test, y_pred)}')
        print(classification_report(y_test,y_pred,target_names=['NO CHD (0)', 'CHF (1)']))
        if hasattr(model, 'predict_proba'):
            y_pred_proba = model.predict_proba(X_test)[:, 1] # Probability of the positive class (1)
            roc_auc = roc_auc_score(y_test, y_pred_proba)
            print(f"ROC AUC Score: {roc_auc:.4f}")
        else:
            print("ROC AUC Score: Not available (model does not provide probabilities directly).")
        print(f"{'='*50}\n")
    

    อะอะ ก่อนไปดูผลลัพธ์เรามาทำความรู้จัก Metrics ต่างกันก่อนดีกว่า

    เริ่มกันที่ตัวแรกเลย Accuracy

    Accuracy

    Accuracy คือ metric ที่ใช้งานง่ายที่สุด ตามชื่่เลยครับว่า ความแม่นยำ บอกว่าโมเดลเราทำนายถูกทั้งหมดกี่ % จากตาราง Confusion Matrix เราสามารถคำนวณได้ด้วยสูตรข้างล่างนี้

    ถ้าแบบทางการหน่อย เราจะเขียนสูตรว่า accuracy = (TP + TN)/ N โดยค่า accuracy จะมีค่าอยู่ระหว่าง 0-1 ยิ่งเข้าใกล้ 1 แปลว่าโมเดลเราทำนายผลได้ดีมาก

    Precision

    จริงๆ อันนี้ก็แปลว่าความแม่นยำ เหมือนกัน แต่จะมีความหมายคนละแบบ นิยามของ Precision คือความน่าจะเป็นที่โมเดลทำนาย CHD คิดเป็นจากการทำนายถูกทั้งหมดกี่ % หรือก็คือ ถ้าชี้หน้าว่า คนๆนี้ป่วย โอกาสที่ป่วยก็คือ Precision% นั้นเอง

    แทนค่าในสมการ precision = TP / (TP + FP)

    Recall

    นิยามของ Recall คือความน่าจะเป็นที่โมเดลสามารถตรวจจับ คนเป็น CHD จากจำนวน CHD ทั้งหมดใน ผลรวมคอลั่มแรกของ confusion matrix) สมมติในโรงพยาบาลมีคนไข้ 100 คน ถ้าตรวจพบแค่ 70 ก็คือมี ค่า Recal ที่ 70%

    แทนค่าในสมการ recall = TP / (TP + FN)

    F1-Score

    F1-Score คือค่าเฉลี่ยแบบ harmonic mean ระหว่าง precision และ recall นักวิจัยสร้าง F1 ขึ้นมาเพื่อเป็น single metric ที่วัดความสามารถของโมเดล (ไม่ต้องเลือกระหว่าง precision, recall เพราะเฉลี่ยให้แล้ว) เพราะตามความเป็นจริง Precision และ Recall จะสวนทางกัน เขาก็เลยคิด F1-Score เพื่อปิดจุดอ่อนนี้

    • การดูแต่ค่า Accuracy อย่างเดียวมีโอกาสอธิบายการทำนายผิดพลาดได้
    • ในทางปฏิบัติเราจะดูค่า precision, recall, F1 ร่วมกับ accuracy เสมอ โดยเฉพาะอย่างยิ่งเวลาเจอกับปัญหา imbalanced classification i.e. y {0,1} มีสัดส่วนไม่เท่ากับ 50:50

    AUC

    AUC ย่อมาจาก “Area Under Curve” AUC มีค่าอยู่ระหว่าง 0-1 ยิ่งเข้าใกล้ 1 แปลว่าโมเดลทำนาย Y ได้ดี เป็นอีก 1 ใน metrics สำคัญมากๆในงาน Datascience

    ด้านล่างคือผลลัพธ์ที่ได้ของแต่ละโมเดล

    เอาละหลังจากรู้จัก Metrics ต่างๆละเรามาดูผลลัพธ์กันเล๊ยยย~~~

    Model : Logistic Regression
    [[605   5]
     [117   5]]
    Accuracy Score: 0.8333333333333334
    Precision Score: 0.5
    Recall Score: 0.040983606557377046
    F1 Score: 0.07575757575757576
                  precision    recall  f1-score   support
    
      NO CHD (0)       0.84      0.99      0.91       610
         CHF (1)       0.50      0.04      0.08       122
    
        accuracy                           0.83       732
       macro avg       0.67      0.52      0.49       732
    weighted avg       0.78      0.83      0.77       732
    
    ROC AUC Score: 0.7078
    ==================================================
    

    Model : Decision Tree
    [[528  82]
     [ 89  33]]
    Accuracy Score: 0.7663934426229508
    Precision Score: 0.28695652173913044
    Recall Score: 0.27049180327868855
    F1 Score: 0.27848101265822783
                  precision    recall  f1-score   support
    
      NO CHD (0)       0.86      0.87      0.86       610
         CHF (1)       0.29      0.27      0.28       122
    
        accuracy                           0.77       732
       macro avg       0.57      0.57      0.57       732
    weighted avg       0.76      0.77      0.76       732
    
    ROC AUC Score: 0.5680
    ==================================================
    

    Model : Random Forest
    [[600  10]
     [110  12]]
    Accuracy Score: 0.8360655737704918
    Precision Score: 0.5454545454545454
    Recall Score: 0.09836065573770492
    F1 Score: 0.16666666666666666
                  precision    recall  f1-score   support
    
      NO CHD (0)       0.85      0.98      0.91       610
         CHF (1)       0.55      0.10      0.17       122
    
        accuracy                           0.84       732
       macro avg       0.70      0.54      0.54       732
    weighted avg       0.80      0.84      0.79       732
    
    ROC AUC Score: 0.6859
    ==================================================
    

    Model : K-Nearest Neighbors
    [[588  22]
     [104  18]]
    Accuracy Score: 0.8278688524590164
    Precision Score: 0.45
    Recall Score: 0.14754098360655737
    F1 Score: 0.2222222222222222
                  precision    recall  f1-score   support
    
      NO CHD (0)       0.85      0.96      0.90       610
         CHF (1)       0.45      0.15      0.22       122
    
        accuracy                           0.83       732
       macro avg       0.65      0.56      0.56       732
    weighted avg       0.78      0.83      0.79       732
    
    ROC AUC Score: 0.6241
    ==================================================
    
    

    Model : Support Vector Machine
    [[610   0]
     [119   3]]
    Accuracy Score: 0.837431693989071
    Precision Score: 1.0
    Recall Score: 0.02459016393442623
    F1 Score: 0.048
                  precision    recall  f1-score   support
    
      NO CHD (0)       0.84      1.00      0.91       610
         CHF (1)       1.00      0.02      0.05       122
    
        accuracy                           0.84       732
       macro avg       0.92      0.51      0.48       732
    weighted avg       0.86      0.84      0.77       732
    
    ROC AUC Score: 0.6525
    ==================================================
    

    หลังจากที่เราได้ผลลัพธ์ของแต่ละโมเดลมาเรียบร้อยแล้ว ก่อนที่จะไปประเมินผลลัพธ์ของแต่ละโมเดลกัน เรามาทำความเข้าใจกันก่อนว่า ทำไมผมถึงทักว่าเราจะกลับมาดูตรง value_count กันครับ

    Imbalanced Dataset

    หากเราลองดูค่า Relative Frequency ของ dataset นี้

    TenYearCHD
    0    0.847648
    1    0.152352
    

    คุณเห็นอะไรไหมครับ ใช่ ข้อมูลของคนที่ไม่ได้ป่วยเป็นโรคหลอดเลือดหัวใจ คิดเป็น 85% ของข้อมูลนี้ นั้นแปลว่าถ้าผมแค่นั่งเดา 100 ครั้งว่า คนนี้ไม่เป็นโรคหลอดเลือดหัวใจ ผมก็เดาถูกตั้ง 85% เลยนะครับ

    เพราะฉะนั้นการดูแต่ค่า Accuracy อย่างเดียวก็อาจจะไม่สามารถบอกได้ว่า โมเดลนี้เป็นโมเดลที่ให้ผลลัพธ์ที่ดีทีสุดแล้วหรือยังนั้นเอง

    และในกรณีนี้เมื่อมี Class หนึ่งที่เยอะผิดปกติ โมเดลก็มีแนวโน้มที่จะเรียนรู้และเดาข้อมูลที่มีเยอะกว่าได้แม่นยำกว่าส่งผลให้โอกาสที่จะทายข้อมูลอีกด้านนั้นลดลง

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

    ในบทความถัดๆไป เราจะมีดูกันว่าจะมีวิธีการแก้ปัญหาอย่างไรในกรณีที่ข้อมูลไม่เท่ากันแบบนี้อีก เพื่อลดความผิดพลาดของโมเดลในการทำนายผลกันนะครับ

    Evaluation

    เรามาเริ่มกันที่ Logistic Regression กันก่อน

    โมเดล ClassificationAccuracy (ความแม่นยำรวม)Precision (Class 1)Recall (Class 1)F1-Score (Class 1)ROC AUC Score
    Logistic Regression0.8360.50.0410.0760.7078
    Decision Tree0.7660.2870.270.2780.568
    Random Forest0.8360.5450.0980.1670.6859
    K-Nearest Neighbors0.8280.450.1480.2220.6241
    Support Vector Machine (SVM)0.83710.0250.0480.6525

    จากตารางข้างต้นจะเห็นได้ว่า Model ที่มีค่า ROC AUC มากที่สุดนั้นคือ Logistic Regression รองลงมาเป้น Random Forest นั้นแปลว่าสามารถจำแนกผู้ป่วยกับไม่ป่วยได้ค่อนข้างดีในหลากหลายสถานการณ์ สำหรับ Logistic Regresssion

    อย่างไรก็ตามด้วยค่า Recall ที่แปลว่าคุณเดาถูกกี่ครั้งจากคนที่ป่วยทั้งหมด ดันได้คะแนนสำหรับ Class 1 แค่ 0.04 นั้นแปลว่าถ้ามี 100 คน จะบอกว่าป่วยได้แค 4 คนซึ่งนี้มันต่ำมากๆ

    แม้ว่า Decision Tree จะมีค่า Recall สูงที่สุดใน 5 โมเดล แต่ก็มีค่า Precision ที่ต่ำที่สุดในกลุ่มเช่นกันก็คือถ้าทายว่าป่วยไม่ป่วยดันทายถูกแค่ 28% เท่านั้นอง

    สำหรับ Random Forest นั้นแม้ว่าค่า Accuracy กับ ค่า ROC AUC จะทำได้ดีแต่ดันไปตกม้าตายที่ค่า Recall ที่ได้แค่ 0.1 เหมือนจะดีกว่า Logistic แต่ก็ยังทำผลงานได้แย่อยู่ดี เกินกว่าจะนำออกไปใช้จริงได้

    ในส่วนของ KNN โดยภาพรวมแล้วไม่ได้หวือหวา หรือเด่นไปกว่าโมเดลไหนเลยครับ

    และสุดท้าย SVM ที่มีคะแนน Precision = 1 เลยทีเดียวแปลว่าเดาว่าเป็นกี่ครั้งก็คือถูกหมดแต่ว่า ดันมีค่า Recall แค่ 0.025 ก็คือ ถ้ามี 100 คนมันจะเดาถูกแค่ 3 คน แต่ทุกครั้งที่ทำนายคือถูกชัวร์ แต่มันก็ต่ำเกินไปแปลว่ามันมีโอกาสข้ามคนป่วยถึง 97 คนเลยทีเดียว

    Conclusion

    สรุปแล้วในภาพรวมเมื่อสรุปและประเมินผลที่ได้โมเดลที่น่าจะทำผลงานได้ดีที่สุดหากมีการนำไปใช้จริงก็จะเป็น Logistic Regression ด้วยคะแนน ROC AUC ที่มากที่สุดนั้นเอง อย่างไรก็ตามด้วยค่า AUC แค่ 0.7 หรือ 70% นั้นถือว่ายังไม่ดีพอ (ถ้าดีควรจะมีค่ามากกว่า 80%) ซึ่งเป็นปัญหาทีเกิดจากการที่ข้อมูลที่เรามีนั้นไม่ได้สมดุลระหว่างผู้ป่วยที่เป็นโรคหัวใจ หรือไม่เป็น

    ด้วยผลดังกล่าว อาจส่งผลให้โมเดลของเราดูมีค่าความแม่นยำ (Accuracy) ที่สูงแต่กลับไม่สามารถนำไปใช้ประโยชน์ได้จริงหากหน้างานต้องการความละเอียดอ่อน ลองคิดดูสิครับถ้าเราเอาโมเดลนี้ไปใช้ แล้วมันดันข้ามคนป่วยจริงๆไป ผมว่ามันเป็นอะไรที่เรารับไม่ได้แน่ๆครับ

    ดังนั้น ในบทความข้างหน้า หลังจากที่เขียน Main Serie หมดแล้ว ผู้เขียนจะมานำเสนอวิธีการและประยุกต์ใช้เทคนิคต่างๆเพื่อจัดการกับปัญหาเหล่านี้

    เพื่อให้โมเดลของเราไม่เพียงแค่ทำนายได้แม่นยำในภาพรวม แต่ยังสามารถตรวจจับ Class สำคัญได้อย่างมีประสิทธิภาพมากยิ่งขึ้น!”

    References

    1. Random Forest
    2. เจาะลึก Random Forest !!!— Part 2 of “รู้จัก Decision Tree, Random Forest, และ​ XGBoost!!!”
    3. ข้อดี-ข้อเสีย ของแต่ละ Machine Learning Algorithm
    4. Scikit-Learn Official Website

  • Main Series: Part 1 ทำความเข้าใจหลักการ Linear Regressionใน Machine Learning

    Main Series: Part 1 ทำความเข้าใจหลักการ Linear Regressionใน Machine Learning

    เป็นเวลาหลายเดือนที่ผมเรียนจบจาก Data Science Bootcamp กับ Ad’ Toy และได้เริ่มเขียน Posts หลายๆอัน หลังจากมัวไต่แรงก์ TFT Set 14 ผมว่ามันถึงเวลาอันสมควรแล้วที่จะเขียน Post เพื่อทบทวนและแชร์ไอเดียเกี่ยวกับ Classical Machine Learning ที่ผมได้เรียนในคอร์สกับ Ad’ Toy

    แต่ครั่นจะให้ผมแค่อธิบายสิ่งที่ได้เรียนมาในคอร์สก็ดูจะเบสิคเกินไป ผมจะพาทุกท่านไปดูการทำและวิเคราะห์ไอเดียผ่าน data ใน kaggle แทนเพื่อให้ทุกท่านได้เห็นภาพมากขึ้น

    เนื้อหาส่วนนี้จะแบ่งออกเป็น 4 พาร์ทเพื่อไม่ให้ยาวเกินไปนะครับ

    โดยในส่วนของ Main Series นั้นเราจะโฟกัสไปที่การแตะๆ Walkthrough เนื้อหาภาพรวมกันเสียก่อน แล้วเดี๋ยวเราค่อยมาเจาะลึกกันสำหรับแต่ละ Algorithms ใน Series ถัด

    Table of Content


    Introduction: Why Classical Machine Learning Matters ?

    ในยุคที่ปัญญาประดิษฐ์ ( AI ) กลายเป็นส่วนหนึ่งของการดำรงชีวิต ทั้งในด้านการเพิ่มประสิทธิภาพและคุณภาพของงาน ไปจนถึงการทดแทนแรงงานมนุษย์ในหลายภาคส่วน

    ความเข้าใจในรากฐานของเทคโนโลยีเหล่านี้ โดยเฉพาะ Machine Learning (ML) จึงเป็นสิ่งสำคัญอย่างมาก

    การเรียนรู้ Classical Machine Learning ไม่เพียงช่วยให้เรามองเห็นเบื้องหลังการทำงานของระบบอัจฉริยะ แต่ยังเป็นจุดเริ่มต้นที่แข็งแกร่งในการต่อยอดสู่เทคโนโลยีสมัยใหม่อย่าง Deep Learning และ Generative AI เช่นกัน

    แล้ว Machine Learning คืออะไร? โดยพื้นฐานแล้ว Machine Learning คือศาสตร์แขนงหนึ่งของวิทยาการคอมพิวเตอร์ที่เกี่ยวข้องกับการทำให้คอมพิวเตอร์สามารถเรียนรู้จากข้อมูลได้เอง

    โดยไม่จำเป็นต้องถูกโปรแกรมให้ทำงานแบบชัดเจนในทุกขั้นตอน เมื่อทำการสอนเสร็จแล้ว เราก็จะได้ Model ในรูปแบบสมการ หรือไม่เป็นรูปแบบสมการที่ใช้ในการทำนาย การจัดกลุ่มและอื่นๆได้อีกเช่นกัน

    Machine Learning

    สำหรับ Machine Learning แล้วก็จะแบ่งเป็น Classic และ Modern โดยเราจะโฟกัสไปที่ Classic กันก่อน

    สำหรับ Classical Machine Learning นั้นสามารถแบ่งเป็น 2 กลุ่มใหญ่ๆได้แก่

    1. Supervised Learning 
    2. Unsupervised Learning
    3. Reinforcement Learning

    Supervised Learning 

    เป็นวิธีการสอนรูปแบบหนึ่งที่เราจะให้คอมพิวเตอร์เรียนรู้จากข้อมูลที่มีหัวตาราง (labeled data) โดยประกอบไปด้วย 2 รูปแบบได้แก่

    1. Regression
    2. Classification

    Unsupervised Learning

    อีกหนึ่งวิธีการสอนที่จะให้ data แบบไม่มี metadata เพื่อหารูปแบบ หรือสรุปข้อมูลออกมา โดยรุปแบบการเรียนรู้นี้จะนิยมใช้เทคนิค Clustering และ Principal Component Analysis หรือ PCA 

    Reinforcement Learning

    รุปแบการสอนที่เน้นให้ Agent หรือตัวโมเดลสำรวจ สภาพแวดล้อม environment และเลือกกระทำการใดๆต่าง Action โดยที่การกระทำเหล่านั้นจะมีผลลัพธ์ตามมาไม่ว่าจะเป็ฯ รางวัล Reward หรือ บทลงโทษ Penalty

    การสอนรูปแบบนี้มีวัตถุประสงค์เพื่อให้ Agent นั้น ได้รับรางวัลมากที่สุด หรือ ได้รับบทลงโทษน้อยที่สุด โดยโมเดลที่มีชื่อเสียงผ่านการสอนด้วยวิธีนี้ก็จะมี AlphaGo เป็นตัวอย่าง

    ในบทความนี้ผมจะพูดถึงแค่ 1 ใน รูปแบบของ Supervised Learning นั้นคือการทำ Linear Regression เสียก่อน เพื่อไม่ให้ยาวจนเกินไปนะครับ เกริ่นกันมามากพอละ ป่ะลุยกั๊นนนนน

    Linear Regression

    ก่อนที่เราจะไปพูดถึง Linear Regression นั้นเราควรเข้าใจกับคำว่า สหสัมพันธ์ (Correlation) เสียก่อน  

    Correlation เป็นการวัดความสัมพันธ์เชิงเส้นตรงระหว่างตัวแปรสองตัว โดยจะมีค่าอยู่ระหว่าง [-1,1] เท่านั้น

    Correlation บอกได้แค่ตัวแปรสองตัวนี้มีความสัมพันธ์กันระดับไหน วัดออกมาเป็นตัวเลขระหว่าง [-1, +1] เครื่องหมายบวกลบบอกทิศทางความสัมพันธ์ของตัวแปร ถ้าไม่มีความสัมพันธ์เลย correlation จะเท่ากับศูนย์ (หรือประมาณ +/- 0.1)

    Linear Regression เป็นสมการ หรือโมเดลรูปแบบนึงที่ใช้ประเมินคุณภาพของความสัมพันธ์ (relationship) โดยมักจะมาในรูปแบบ สมการเส้นต้น หรือ สมการหลายตัวแปร

    y = mx + b
    

    หรือ

    y = b0 + b1*x1 + b2*x2 + b3*x3 + .. + bk*xk  
    

    โดย algorithm นี้จะเป็น Alogirthm ที่เหมาะสมก็ต่อเมื่อง error ที่เกิดขึ้นรอบๆ เส้นตรงดังกล่าวต่ำนั้นเอง

    Linear Regression เหมือนเป็น add-on ต่อยอดจากการหาค่า Correlation ว่าถ้าตัวแปร x เปลี่ยนเท่านี้ตัวแปร y จะ เปลี่ยนไปเท่าไหร่นั้นเอง

    ตัว m ในสมการเราจะเรียกว่า Regression Coefficient และ b คือ Interception point

    Dataset & How We Preprocess Data

    สำหรับ ตัวอย่างนี้ผมจะใช้ dataset ที่ชื่อว่า AirQualityUCI นะครับสามารถคลิกลิ้งค์ตรงนี้เพื่อเข้าไปหาต้นทางได้เลย

    ก่อนโหลด dataset เราก็ต้องทำความเข้าใจแต่ละ Columns ก่อนว่าคืออะไรกันบ้างครับ

    FeatureDescription
    Dateวันที่วัดค่า
    Timeเวลาที่วัดค่า
    CO(GT)ความเข้มข้นของ Carbon Monoxide ในหน่วย (µg/m³).
    PT08.S1(CO)ค่าที่ได้จากการวัด Carbon Monoxide จากเครื่องวัด
    NMHC(GT)ความเข้มข้นของ non-methane hydrocarbons (NMHC) (µg/m³).
    C6H6(GT)ความเข้มข้นของ benzene (C6H6) in the air (µg/m³).
    PT08.S2(NMHC)ค่าที่ได้จากการวัด non-methane hydrocarbons (NMHC) จากเครื่องวัด
    NOx(GT)ความเข้มข้นของ nitrogen oxides (NOx) in the air (µg/m³).
    PT08.S3(NOx)ค่าที่ได้จากการวัด NOx จากเครื่องวัด
    NO2(GT)ความเข้มข้นของ nitrogen dioxide (NO2) in the air (µg/m³).
    df=pd.read_csv(filepath_or_buffer='AirQualityUCI.csv', sep=',', decimal=',', header=0)
    

    เนื่องจากไฟล์ต้นทางเป็นไฟล์ .csv เพราะฉะนั้นเราก็เรียกใช้ method .read_csv จาก library pandas เพื่อใช้อ่านโดยกำหนค่าตาม filepath (ให้ง่ายก็ลากไฟล์ที่จะอ่านมาไว้โฟลเดอร์เดียวกัน) และกำหนด separator (อักขระทีใช้ในการแบ่งข้อมูลใน csv ) และ decimal (อักขระที่ใช้แทนจุดทศนิยม) โดยกำหนดให้ header อยู่ที่แถว 0

    เมื่ออ่านค่าได้แล้วเราก็มาดูข้อมูลคร่าวๆกันเถอะ

    ⁠df.head() และ df.describe()

    df.head()
    df.describe()

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

    ในข้อมูลจะเห็นว่ามี 2 Columns ที่ไม่มีชื่อใช่ไหมครับ เพราะฉะนั้นเราก็ต้อง Drop 2 Columns นั้นออกด้วยคำสั่งด้านล่าง

    df = df.loc[:, ~df.columns.str.contains("^Unnamed")]
    

    คำสั่ง loc ใน pandas หมายถึงการเข้าถึงข้อมูลใน DataFrame โดยอาศัยการกำหนด rows หรือ columns หรือจะใช้ค่า boolean arrays ก็ได้เช่นกัน

    จากคำอธิบาย Dataset ใน Kaggle ได้บอกว่าบางค่าในแต่ละเดือนก็ไม่สามารถบันทึกได้เขาก็เลยใส่ -200 มาเพราะฉะนั้นเราก็ต้องลบออกเช่นกัน

    df = df.replace(-200,np.nan).dropna()
    

    ก็คือผมเปลี่ยน -200 ทั้งหมดให้เป็น np.nan ก่อนแล้วก็ค่อย drop NaN ทิ้ง

    โอเคเราได้ทำการ clean data เรียบร้อยแล้ว ในขั้นตอนถัดไปเราก็สามาถนำข้อมูลนี้ไปใช้ train ได้แล้วครับ

    Simple Linear Regression

    จากข้อมูลผมจะทำการเริ่มจากใช้ ตัวแปรตัวเดียวเพื่อหาความสัมพันธ์ของข้อมูลก่อน ในกรณีนี้จะเลือก y เป็น NMHC และ x เป็น C6H6(GT)

    y = np.array(df['NMHC(GT)']).reshape(-1,1)
    

    และ

    X = np.array(df['C6H6(GT)']).reshape(-1,1)
    

    Split Data

    เมื่อได้ X และ y เป็นที่เรียบร้อยแล้วเราก็นำข้อมูลดังกล่าวมา split โดยผลแบ่งเป็น ใช้ฝึก 80% และ ใช้ทดสอบ 20% และสาเหตุที่ใส่ค่า random_state = 42 เพราะว่าจะให้ได้ผลลัพธ์การสุ่มแบ่งข้อมูลเหมือนเดิมในทุกๆครั้ง

    X_trained, X_test, y_trained, y_test = train_test_split(X, y,train_size=0.8, test_size=0.2, random_state=42)

    Train and Validate

    หลังจากนั้นเราก็จะนำ data ที่แบ่งมา train กัน

    lnr = LinearRegression()
    lnr.fit(X_trained, y_trained)
    y_pred = lnr.predict(X_test)
    
    1. สร้างฟังก์ชั่น linear regression ด้วยฟังก์ชั่น LinearRegression
    2. นำ data X_trained และ y_trained มา train
    3. ทำนายค่า X_test ด้วย predict()

    จากนั้นก็ทำการหาค่า metrics ต่างๆ

    print("Mean Absolute Error Trained ", mean_absolute_error(y_trained, y_trained_pred))
    print("Mean Absolute Error Test ", mean_absolute_error(y_test, y_pred))
    

    จะได้ผลลัพธ์ดังนี้

    # Mean Absolute Error Trained  61.831639475521364
    # Mean Absolute Error Test  60.9002976731785

    หน่วยที่ใช้วัดจะเป็นหน่วย µg/m³ ไมโครกรัม ต่อ ลูกบาศก์เมตร หากดูจากค่า trained MAE และ test MAE นั้นถือว่ามีค่าใกล้กันแต่แปลว่า model เราค่อนข้าง generalised

    อย่างไรก็ตาม MAE เป็นค่าที่ยิ่งใกล้ 0 จะยิ่งให้ผลลัพธ์ที่ดีเพราะแปลว่าเราอ่านค่าคลาดเคลื่อนน้อย แต่นี้ MAE ตั้ง 60 แปลว่า simple linear regression model ไม่ตอบโจทย์กับ dataset นี้

    เพราะฉะนั้นเรามาลองหาค่าใหม่แต่ใช้เป็น multi-varaible linear regression กันอีกรอบ

    Multi-variable Linear Regression

      y = np.array(df['NMHC(GT)']).reshape(-1,1)
        df_pollutant = df[['PT08.S2(NMHC)', 'C6H6(GT)','T', 'RH', 'AH', 'NO2(GT)', 'NOx(GT)', 'CO(GT)']]
        X = np.array(df_pollutant).reshape(-1, len(df_pollutant.columns))
        X_trained, X_test, y_trained, y_test = train_test_split(X, y,train_size=0.8, test_size=0.2, random_state=42)
        lnr = LinearRegression()
        lnr.fit(X_trained, y_trained)
        y_pred = lnr.predict(X_test)
        y_trained_pred = lnr.predict(X_trained)
        print("Mean Absolute Error Trained ", mean_absolute_error(y_trained, y_trained_pred))
        print("Mean Absolute Error Test ", mean_absolute_error(y_test, y_pred))
    

    เราก็ได้ผลลัพธ์ดังนี้

    # Mean Absolute Error Trained  59.31405073871236
    # Mean Absolute Error Test  57.858497022746086

    สังเกตว่า แม้เราจะเพิ่มตัวแปรเข้าไปเป็นจำนวนมากแต่ค่า MAE กับลดลงเพียง 2-3 µg/m³ เท่านั้นเอง ซึ่งก็ยังคลาดเคลื่อนเยอะมาก

    ลองจินตนาการว่าค่าที่แท้จริงได้ 7 µg/m³ แต่ดันอ่านได้ 64-66 µg/m³ ดูสิครับทำนายกันผิดผลาดแน่นอน

    No Free Lunch

    จากตัวอย่างข้างต้นทำให้เราได้ข้อสรุปว่าการที่เราจะ train model อะไรซักอย่างนั้นจะต้องลองหลายๆ model เพื่อหา model ที่ดีที่สุดทั้งในเรื่องของเวลาและค่าใช้จ่าย

    ซึ่งนี้เป็นไปตามหลักการที่ว่า ไม่มีโมเดลไหนเก่งที่สุด และสามารถตอบโจทย์ได้ทุกปัญหา หรือ No Free Lunch บางทีอาจจะมีโมเดลอื่นอย่าง Decision Tree, Random Forest และ อื่นๆ ที่จะสามารถนำ data นี้ไปทำนายได้อย่างมีประสิทธิภาพมากขึ้นไปอีกได้เช่นกัน

    Conclusion & Evaluation

    สรุป dataset นี้ไม่เหมาะที่จะใช้ Linear Regression model ในการทำนายค่าเนื่องจากว่ามีผลลัพธ์ที่ไม่น่าพึ่งพอใจและเสี่ยงตอนความคลาดเคลื่อนสูง

    แม้ว่าจะมีกาารเพิ่มตัวแปรเข้าไปแล้วก็ตาม ทั้งนี้ dataset นี้อาจเหมาะกับโมเดลอื่นๆมากกว่า

    จากที่ ผมลองดูมา dataset ใน kaggle ที่ดูแล้วน่าจะใช้ Linear Regression ได้ก็น่าจะเป็น California Housing Price ครับเพื่อนๆก็สามารถลอง ขั้นตอน cleansing data (preprocessing) บวกกับ การสร้าง Linear Regression Model ที่ผมเขียนไปลองดูกันได้ ลองแล้วได้อย่างไรก็อย่าลืมมาแชร์กันนะครับ

    Scatter Plot ของ y_pred, X_test

    แล้วเพื่อนๆ เคยเจอโปรจคไหนที่มีปัญหา No Free Lunch อย่างนี้ไหมครับ

    Sourcecode

    https://gist.github.com/patrawi/4af831586d73e492362cb2a19148f79d.js

    References

    # ทำความรู้จัก “Linear Regression” Algorithm ที่คนทำ Machine Learning ยังไงก็ต้องได้ใช้!
    # ทำนายราคาบ้าน Boston ด้วย Linear Regression

  • ใช้ Google AI Studio แปลง PDF เป็น CSV

    ใช้ Google AI Studio แปลง PDF เป็น CSV


    ตั้งแต่อดีตจนถึงปัจจุบัน นักวิทยาศาสตร์ข้อมูล หรือ Data Scienctist ล้วนแล้วแต่ต้องเคยเจอปัญหาที่ว่าข้อมูลนั้นอยู่ในรูปกายภาพ(physical) แทนที่จะเป็นลักษณะดิจิตอล (digital) 

    ซึ่งข้อมูลเหล่านี้สร้างความรำคาญให้แก่พวกเขาเป็นอย่างมาก ในอดีต ก่อนที่จะมีนวัตกรรมอย่าง OCR (Optical Character Recognition) ที่เกิดจากการประยุกต์ใช้ Machine Learning เหล่า Data Scientist ต้องรับมือกับข้อมูลกายภาพด้วยการคีย์ข้อมูลด้วยมือ ไม่ว่าจะผ่านหน้าจอ Console, Web Application หรือ GUI ต่างๆ

    วิธีดังกล่าวนั้นมีโอกาสเกิด Human Error เป็นอย่างสูง ลองจินตนาการดูสิ คุณกำลังนั่งกรอกข้อมูลเหล่านี้ประมาณ ตี 2-3 อดหลับอดนอน นั่งกรอกตารางข้อมูลเหล่านี้มากกว่า 100 หน้ามาแล้ว 3 วัน 

    คุณที่กำลังขมักเขม้นกรอกข้อมูล

    ซึ่งมันแทบจะเป็นไปไม่ได้เลยที่คุณจะกรอกโดยไม่ผิด และเมื่อกรอกเสร็จคุณก็ต้องมาเสียเวลานั่งแก้ข้อมูลเหล่านี้ เสียทั้งเวลา เสียทั้งค่าใช้จ่าย และอื่นๆที่จะตามมาอีกมากมาย

    ความยุ่งยากในการนำเข้าข้อมูลนี้ ทำให้หลายคนเลือกที่จะละทิ้งข้อมูลไปอย่างน่าเสียดาย เพราะมองว่ายากเกินกว่าจะนำไปใช้ประโยชน์ได้ สุดท้ายข้อมูลอันมีค่าเหล่านี้ก็กลายเป็นเพียง ‘ขยะดิจิทัล’ ที่ไม่มีใครสามารถนำไปประยุกต์ใช้ได้เลย

    แล้วถ้าเกิดว่าวันนึง มันดันมีเทคโนโลยีที่สามารถแก้ปัญหาตรงนี้ได้ละ เทคโนโลยีที่จะช่วยให้เราสแกนเอกสารและทำออกมาเป็นไฟล์พร้อมนำเข้าข้อมูล บนโปรแกรต่างๆไม่ว่าจะเป็น Spreadsheet, Excel หรือ โปรแกรมจากการเขียนโค้ดอย่าง python หรือ R คุณลองคิดดูสิว่าขยะเหล่านี้จะมีค่าแค่ไหน ? เพราะฉะนั้น วันนี้ผมจะพาทุกท่านมารู้จักกับ AI จากทาง Google อย่าง Gemini Flash 2.5 ที่เราสามารถทดสอบ บนGoogle AI Studio ซึ่งจะเข้ามาช่วยแก้ปัญหาและอุด Pain Point นี้กันครับ

    ในช่วงแรกที่เหล่า Data Scientists ต้องแก้ปัญหานี้ พวกเขาก็พยายามค้นหาวิธีการต่างๆ อาทิเช่น การ Scan ข้อมูลเหล่านั้นเป็น PDF Copy/Paste บ้างละ ใช้โปรแกรม online สำหรับแปลง PDF ไปเป็น CSV บ้างละ ไปจนถึงขั้นยอมเสียเงินจำนวนมากเพื่อซื้อโปรแกรมมาปิดจุดอ่อนนี้ 

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

    ด้วยการมาถึงของยุคที่ Generative AI เป็นที่รู้จักแพร่หลาย บริษัท Google ก็ไม่น้อยหน้าปล่อย แพลทฟอร์ม ออกมาให้ผู้ใช้งานอย่างเราๆได้ทดลองใช้ฟรี โดยเราสามารถเข้าไปลองเล่น Model ต่างๆของทาง Google ผ่านทาง Google AI Studio 

    ทำไมต้อง Google AI Studio ทั้งๆ ที่เจ้าอื่นก็มีแพลตฟอร์มทดลองให้ใช้เหมือนกัน? คำตอบง่ายๆ คือ ‘เพราะมันฟรี’ ผู้ใช้งาน Gemini เวอร์ชั่นปกติอาจเข้าถึงได้แค่ Gemini Flash แต่ใน Google AI Studio คุณสามารถใช้งาน Gemini Pro ได้ฟรีไม่จำกัด นี่คือข้อได้เปรียบสำคัญที่ไม่อาจมองข้าม

    แถมเรายังสามารถปรับแต่งค่าต่างๆเพื่อให้ AI ตอบคำถามตามความต้องการของเราได้ เช่น ตั้งค่า Temperature ต่ำ หรือ สูง เพื่อกำหนดความสร้างสรรค์ของ AI กำหนด output length ว่าจะให้ตอบได้ไม่เกินครั้งละเท่าไหร่ ไปจนถึงการกำหนด Top P เพื่อควบคุมความหลากหลายของคำตอบ โดยให้ AI เลือกจากกลุ่มคำที่มีความน่าจะเป็นสูงสุด

    อีกหนึ่งสิ่งที่ น่าประทับใจมากๆของ Google AI Studio ก็คือจำนวน Context window ที่ให้มาอย่างจุใจถึง 1 ล้าน ส่งผลให้การคุยของผู้ใช้งานนั้นราบรื่นอย่างไร้ปัญหา และด้วยเหตุนี้ Google AI Studio จึงเหมาะมากที่เราจะใช้เพื่อ POC concept ของเราสำหรับประยุกต์ใช้ในอนาคตนั้นเอง

    สำหรับจุดประสงค์ในการทำครั้งนี้ก็คือการที่เราสามารถอ่านไฟล์สแกน PDF ได้เป็นจำนวนมากเพื่อที่จะดึงข้อมูลจากกระดาษออกมาและ insert เข้าฐานข้อมูล

    ด้วยเหตุนี้ เพื่อให้ได้มาซึ่ง tools ที่ตอบโจทย์เรามากที่สุด จึงมีความจำเป็นว่าเราต้องทดลองใช้ Model ที่กูเกิ้ลมีนั้นมาทดสอบเสียก่อน โดยในเคสนี้เราจะทดสอบด้วยโมเดล 2.5 Flash และ 2.5 Pro

    ความแตกต่างระหว่าง Pro Model & Flash Model 

    Pro Model – เป็น โมเดลที่ถูกสร้างมาสำหรับการคิดอย่างเป็นตรรกะ (reasoning), การโค้ดดิ้ง (Coding) และความเข้าใจในหลากหลายมิติ ( Multimodal understanding ) เพื่อใช้ในการคิดวิเคราะห์ปัญหาที่ซับซ้อน ส่งผลให้มีค่า Latency ที่สูงเพราะ Model จำเป็นต้องคิดไปทีละ Step ก่อนที่จะได้รับคำตอบ

    Flash Model – สำหรับ Flash Model นั้นถูกออกแบบมาให้ใช้กับงานประมวลผลเป็นจำนวนมากเช่น ไฟล์ PDF หลายๆไฟล์ งานที่ต้องการ latency ตำ่และรับ input ได้เยอะ, หรือ agentic use cases

    จากข้อมูลข้างต้นเราจึงควรเลือกใช้ Flash Model เพราะด้วยความที่ Latency ต่ำ + กับเหมาะทำงานซ้ำๆ ถือว่าตอบโจทย์ของเราเป็นอย่างมาก

    หลังจากที่เลือก Model เรียบร้อยแล้วเรามาดูขั้นตอนในการทำงานก่อนดีกว่า

    Flowchart

    จาก Flowchart ข้างต้นในขั้นตอนแรกนั้นเราต้องสแกนเอกสารก่อนและข้างล่างนี้ก็คือเอกสารที่เราต้องการอ่านนั้นเอง

    ใน ช่วง Proof cf Concept นั้น เราไม่จำเป็นที่จะต้องใช้ API_KEY โดยเราสามารถ Log in Gmail และใช้งานบน แพลทฟอร์ม Google AI Studio ได้เลย

    ⁠จากนั้นทำการอัพโหลดรูปภาพเข้าไป และพิมพ์ System Instruction ดังนี้

    จากนั้นทำการกด Run และรอผลัพธ์

    จะเห็นได้ว่าเราได้ข้อมูลในรูปแบบ CSV พร้อมนำไปใช้ในขั้นตอนต่อไปได้ทันที! แต่หากเราไม่ได้กำหนด System Instruction หรือ Prompt ที่ชัดเจนและรัดกุมแล้วละก็ เราอาจได้ข้อมูลในรูปแบบที่ไม่ใช่ CSV หรือได้ Format ที่ไม่น่าพึงพอใจ เพราะฉะนั้น ‘หลักการ Prompt Engineering’ จึงสำคัญอย่างยิ่งมากในการดึงศักยภาพของ AI ออกมาใช้ได้อย่างเต็มที่.

    สิ่งที่ใช้ในการคำนวณและวิเคราะห์เอกสาร PDF เหล่านี้ แท้จริงแล้วก็คือ โมเดลภาษาขนาดใหญ่ (LLM) อย่าง Gemini ที่ทำหน้าที่หลักในการแปลงข้อมูลจากไฟล์ให้มาเป็น CSV ตาม Prompt ที่เรากำหนด

    อย่างไรก็ตาม กว่าที่จะมาเป็น CSV อย่างที่เราเห็นนั้น ตัว Google AI Studio ก็ต้องแปลงไฟล์แนบในรูป PDF ให้ AI ‘เข้าใจ’ ได้ก่อน

    โดยในขั้นแรก ระบบจะทำการตรวจสอบว่า Input ที่ส่งมาเป็นไฟล์แนบ (เช่น PDF หรือรูปภาพ) หรือไม่ ถ้าใช่ ระบบจะส่งไฟล์นั้นไปประมวลผลด้วย OCR (Optical Character Recognition) ก่อน กระบวนการ OCR นี้จะแปลงภาพของตัวอักษรใน PDF ให้กลายเป็น ข้อความดิจิทัล (Text) ที่คอมพิวเตอร์อ่านได้

    จากนั้น ข้อความดิจิทัลที่ได้จะถูกนำเข้าสู่กระบวนการ Tokenization ซึ่งเป็นการแบ่งข้อความยาวๆ ออกเป็นหน่วยย่อยๆ ที่เรียกว่า ‘โทเคน (Tokens)’ (เช่น คำ, ส่วนของคำ, หรือเครื่องหมายวรรคตอน)

    ต่อมา โทเคนเหล่านี้จะถูกแปลงไปสู่รูปแบบที่ AI สามารถประมวลผลได้ ซึ่งก็คือ ‘Embedding’ ครับ พูดง่ายๆ คือ Embedding คือการแปลงโทเคนแต่ละหน่วยให้เป็นตัวเลขเวกเตอร์ ซึ่งเป็นชุดตัวเลขที่แสดงถึงความหมายและความสัมพันธ์ของโทเคนนั้นๆ ในเชิงคณิตศาสตร์ ทำให้ AI สามารถเข้าใจ และ วัดความใกล้เคียง ของความหมายระหว่างคำต่างๆ ได้

    หลังจากนั้น ข้อมูลที่เป็นตัวเลขเวกเตอร์ (Embeddings) เหล่านี้จะถูกส่งเข้าสู่ โมเดล AI ซึ่งโดยทั่วไป จะประกอบด้วยส่วนหลักๆ คือ Encoder และ Decoder

    • Encoder ทำหน้าที่ประมวลผล Input (ในที่นี้คือ Embeddings ของข้อความจาก PDF) เพื่อสร้างความเข้าใจบริบทและความหมายทั้งหมดของข้อความนั้นๆ
    • Decoder จะใช้ความเข้าใจที่ได้จาก Encoder และคำสั่งจาก Prompt ของเรา (เช่น แปลงเป็น CSV) เพื่อสร้าง Output ออกมาเป็นลำดับของโทเคน ซึ่งต่อมาจะถูกแปลงกลับเป็นข้อความในรูปแบบที่เราต้องการหรือข้อความที่มนุษย์อ่านได้ (เช่น CSV) นั่นเอง

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

    พูดกันมาขนาดนี้แล้ว ละ OCR คืออะไร? OCR (Optical Character Recognition) คือหนึ่งในกระบวนการสำคัญของ Machine Learning ที่ทำหน้าที่แปลง รูปภาพของตัวอักษร (เช่น ตัวอักษรบนไฟล์ PDF ที่มาจากการสแกน) ให้กลายเป็น ข้อความดิจิทัล ที่คอมพิวเตอร์สามารถอ่านและประมวลผลได้ โดยมันจะวิเคราะห์แพทเทิร์นของพิกเซลในรูปภาพเพื่อระบุว่าเป็นตัวอักษรใด แล้วจึงแปลงให้เป็นข้อความที่แก้ไขหรือค้นหาได้

    หลังจากนั้นก็รับค่านั้นและทำการแปลงกับออกมาเป็นรูปด้วย library ต่างๆ เช่น opencv เป็นต้น 

    หลักจากที่ได้อธิบายว่า AI นั้นมันน่าอัศจรรย์มากขนาดไหน เพราะหลังจากนี้เราไม่จำเป็นต้องหามรุ่งหามค่ำนั่งกรอกข้อมูลกันเป็นพันลวันแล้ว เรายังสามารถต่อยอดทำให้มัันอัตโนมัติได้ยิ่งขึ้นไปอีกด้วยนะAIM

    หากเราเข้าไปที่ตรงหน้า chat ที่เราคุยกับ gemini ใน google ai studio

    เราสามารถเปลี่ยน chat ที่เราคุยกับ gemini ให้เป็น code ที่ใช้รันผ่าน Terminal, หน้าเว็บไซต์ หรือจะเป็นยิงผ่าน Postman ก็ได้เหมือนกัน

    แต่ว่าการที่จะทำให้โค้ดนี้สมบูรณ์ได้นั้นเราต้องใช้ GEMINI_API_KEY ซึ่งถือเป็นอีกหนึ่งปัจจัยสำคัญเลย โดยการที่จะหามาได้นั้นเราก็แค่กดปุ่มด้านบนที่เขียนว่า Get API Key

    หลังจากนั้นให้ทำการกด + Create API Key google ai studio จะทำการค้นหา Google Cloud Project

    หากเพื่อนๆ พึ่งเคยสร้างโปรแกรมนี้ครั้งแรก เราแนะนำว่าให้เข้าไปสมัครและเปิดใช้งานในหน้า Google Cloud Project ก่อน  https://cloud.google.com/?hl=en  และเลือกไปที่ Console หากเราได้ทำการล้อคอิน Gmail เรียบร้อยแล้ว

    เลือกเมนู New Project

    ทำการตั้งชื่อ Project name และเลือก Location (สามารถใช้ no organization เป็น default ได้)

    รอจนหน้า Google Cloud Project แจ้งว่าเสร็จแล้วให้เราค้นหาในช่องค้นหาด้านบนว่า Gemini API และทำการ Enable ซะ

    หลังจากที่ Enable แล้วให้กลับไปที่หน้า Google AI Studio และทำตามขั้นตอนการ Get API Key

    ทีนี้เราก็จะเห็น Project ที่พึ่งสร้างแล้วกดคลิกเพื่อเลือกและกด Create API key in existing project

    และเราก็จะได้ API Key เพื่อใช้เรียก service แล้ว แต่ระวังไว้ด้วยนะว่าอย่าเผลอแชร์ให้คนอื่นละไม่งั้นคุณจะโดนบิลอ่วมเลยละ

    หลังจากนั้นก็กลับมาที่โค้ด copy และ วางในเครื่อง local เพื่อทดสอบเสียก่อน โดยเราสามารถเอา API_KEY ที่ได้วางแทนตรงนี้ได้เลยย

    แต่การทำอย่างงั้นเรียกได้ว่าไม่ปลอดภัยเอาซะเลย เพราะฉะนั้นเราควรที่จะใช้ library อย่าง dotenv เพื่อมาอุดปัญหานี้โดย dotenv จะทำอ่านค่าบางอย่างจากไฟล์ที่ชื่อว่า .env ซึ่งเป็นไฟล์ที่เก็บตัวแปรต่างๆที่เราไม่อยากให้คนนอกรู้นั้นเอง

    ทีนี้เราก็สามารถทดสอบโค้ดที่ google ai studio ให้มาได้แล้ว เย้

    # To run this code you need to install the following dependencies:
    # pip install google-genai
    
    import base64
    import os
    from google import genai
    from google.genai import types
    
    
    def generate():
        client = genai.Client(
            api_key=os.environ.get("GEMINI_API_KEY"),
        )
    
        model = "gemini-2.5-pro-preview-05-06"
        contents = [
            types.Content(
                role="user",
                parts=[
                    types.Part.from_text(text="""INSERT_INPUT_HERE"""),
                ],
            ),
        ]
        generate_content_config = types.GenerateContentConfig(
            response_mime_type="text/plain",
            system_instruction=[
                types.Part.from_text(text="""Extract data into CSV format where | separates columns. Use this exact column order:  
    {'|'.join(CSV_COLUMNS)}.  
    
    Rules:  
    1. Keep f1 always 0.  
    1.1 Always keep {NUM_COLUMNS} columns 
    2. Extract doc_no as the second column and datedoc as the fourth. Do not swap these.  
    3. Blank fields must contain 0.  
    4.  If which row  you can't understand skip it.
    Example:
    {'|'.join(CSV_COLUMNS)}
    1|01038/2559|0|1/7/2559|รายงานการสำรองข้อมูลประจำเดือน มิย.59|งานสารบรรณ (สบ.)|กองบริหารการคลัง (กค.)|เดินเรื่องเอง เดินเรื่องเอง|0
    2|กบ0026/1222|0|30/6/2559|ขอส่งเงินค่าไฟฟ้าที่พักอาศัยเจ้าหน้าที่ ประจำเดือน พค.59|งานสารบรรณ (สบ.)|กองบริหารการคลัง (กค.)|0|0
    Always make it {NUM_COLUMNS} columns
    Ensure the output always aligns exactly to this structure."""),
            ],
        )
    
        for chunk in client.models.generate_content_stream(
            model=model,
            contents=contents,
            config=generate_content_config,
        ):
            print(chunk.text, end="")
    
    if __name__ == "__main__":
        generate()
    

    อย่างไรก็ดีโค้ดที่ gemini ให้มานั้นยังทำงานได้แค่กับการ pass ค่า text เข้าไปได้เท่านั้นแถมยังเป็นการรันแบบ ถามตอบแค่ครั้งเดียว ซึ่ง โค้ดดังกล่าวนั้นยังไม่ตอบโจทย์ที่เราต้องนำมาแก้ไขเลย ผมก็เลยมีการแก้โค้ด โดยทำการเพิ่ม method ที่สามารถรับไฟล์เข้าไปเพื่อให้ Gemini Flash Model อ่านผ่าน OCR ได้นั้นเอง

    และเมื่อทำการอ่านค่าได้แล้วเราก็ต้องเพิ่มโค้ดอีกนิดนึงว่าแทนที่จะอ่านทีละไฟล์เราก็สร้าง loop ให้โค้ดของเราอ่านผ่านโฟลเดอร์ที่เก็บไฟล์เอาไว้จนกว่าจะหมดแปลงค่าออกมาเป็น csv format ให้เรานั้นเอง

    เมื่อ ถอดค่าจากไฟล์สแกนได้แล้ว เราก็บันทึกลง .csv ที่เตรียมไว้เพื่อที่เราจะเอาข้อมูลดังกล่าวไปใช้ในขั้นตอน INSERT ข้อมูลลงฐานข้อมูลได้ต่อไปนั้นเอง

    Sourcecode 

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

    ผมได้ทดลองให้คุณดูแล้วว่า AI นั้นมีประโยชน์มากแค่ไหนหากเราเข้าใจและใช้มันได้อย่างมีประสิทธิภาพ ผมว่ามันถึงตาคุณแล้วละที่จะต้องลองนำไปประยุกต์ใช้ เพื่อให้งานราชการของเรามีประสิทธิภาพมากขึ้นครับ คุณอาจจะลองเริ่มจากลองใช้ Google AI Studio ก่อนก็ได้ครับมันฟรี ลดกำแพงการศึกษาไปอี๊ก อยากให้คุณได้ลองสัมผัสนะครับ ละจะติดใจแน่นอน

    References

    For Gemini API – https://ai.google.dev/gemini-api/docs

    For Encoder/Decoder – https://www.youtube.com/watch?v=Q7mS1VHm3Yw&t=7834s

  • การใช้ FileZilla เพื่อจัดการไฟล์อย่างมืออาชีพ

    การใช้ FileZilla เพื่อจัดการไฟล์อย่างมืออาชีพ


    Table of Content


    บทนำ: เริ่มต้นการเดินทางกับ FileZilla

    ห่างหายกันไปนานเลยนะครับกับบล็อก! วันนี้ผมกลับมาพร้อมกับคู่มือแนะนำวิธีการใช้โปรแกรม FileZilla ซึ่งเป็นเครื่องมือที่ทรงพลังมากสำหรับการจัดการไฟล์ พร้อมทั้งจะขออธิบาย Architecture ที่อยู่เบื้องหลังให้เข้าใจกันมากขึ้นด้วยครับ พอดีผมต้องเขียนเอกสารถ่ายทอดงานให้ทีมอยู่แล้ว ก็เลยถือโอกาสนี้เรียบเรียงและนำมาแบ่งปันในบล็อกนี้ด้วยเลยครับ หวังว่าจะเป็นประโยชน์กับเพื่อนๆ นะครับ

    ก่อนอื่นเลย เพื่อนๆ คงเคยได้ยินคำว่า FTP หรือ File Transfer Protocol กันมาบ้างใช่ไหมครับ? FTP เป็นเทคโนโลยีที่ถูกสร้างขึ้นมาเพื่อปฏิวัติวงการการจัดเก็บและโอนย้ายไฟล์เลยก็ว่าได้ และเจ้า FTP นี่แหละครับ คือเทคโนโลยีสำคัญที่อยู่เบื้องหลังการทำงานของ FileZilla นั่นเอง

    FTP และ FileZilla คืออะไร? ทำความเข้าใจพื้นฐาน

    FTP หรือ File Transfer Protocol เป็นโปรโตคอล (Protocol) หรือข้อกำหนดมาตรฐานรูปแบบหนึ่งที่ใช้ในการส่งผ่านไฟล์ระหว่างคอมพิวเตอร์สองเครื่องผ่านเครือข่ายอินเทอร์เน็ตหรือ LAN โดย FTP เคยเป็นโปรโตคอลที่ใช้กันอย่างแพร่หลายและถือเป็นมาตรฐานของอุตสาหกรรมอยู่ยุคหนึ่งเลยทีเดียวครับ จนกระทั่งการมาของ SFTP (SSH File Transfer Protocol) ซึ่งมีความปลอดภัยสูงกว่า FTP แบบดั้งเดิมมาก ทำให้ความนิยมของ FTP แบบเดิมลดน้อยลงไป แต่ก็ยังมีการใช้งานกันอยู่ครับ

    FileZilla เป็นซอฟต์แวร์ (Software) ประเภท Open-Source ที่พัฒนาโดยคุณ Tim Kosse และทีมงาน FileZilla ถูกออกแบบบนสถาปัตยกรรมแบบ Client-Server ทำหน้าที่หลักในการอำนวยความสะดวกในการโอนย้ายไฟล์ระหว่างเครื่องคอมพิวเตอร์ของผู้ใช้ (Local) กับเครื่องเซิร์ฟเวอร์ (Server) หรือในทางกลับกันก็ทำได้เช่นกัน FileZilla นั้นมีให้เลือกใช้ทั้งแบบฟรี (FileZilla Client และ FileZilla Server) และแบบเสียเงิน (FileZilla Pro) ซึ่งมีฟีเจอร์เพิ่มเติมสำหรับผู้ใช้งานระดับสูงครับ

    เมื่อเข้าใจคอนเซ็ปต์คร่าวๆ แล้ว เรามาเริ่มติดตั้ง FileZilla Client กันเลยดีกว่าครับ!

    มาเริ่มต้นกันเลย! (วิธีติดตั้ง FileZilla Client)

    1. ดาวน์โหลดโปรแกรม: ขั้นแรก ให้เพื่อนๆ ทำการดาวน์โหลดโปรแกรม FileZilla Client จากเว็บไซต์ทางการโดยตรง เพื่อความปลอดภัยและได้เวอร์ชันล่าสุดครับ (แนะนำ: https://filezilla-project.org/)

    https://filezilla-project.org/

    2. เริ่มการติดตั้ง: เมื่อดาวน์โหลดไฟล์ติดตั้ง (.exe สำหรับ Windows หรือไฟล์ที่เหมาะสมสำหรับ OS อื่นๆ) มาแล้ว ให้ดับเบิลคลิกเพื่อเริ่มกระบวนการติดตั้งครับ ให้ทำตามขั้นตอนที่ Wizard แนะนำไปเรื่อยๆ โดยส่วนใหญ่เราสามารถกด “Next” หรือ “Agree” ได้เลยครับ

    3. เสร็จสิ้นการติดตั้ง: เมื่อติดตั้งเสร็จสิ้นแล้ว เราก็พร้อมเปิดโปรแกรม FileZilla Client ขึ้นมาใช้งานกันได้เลยครับ! หน้าตาโปรแกรมเมื่อเปิดครั้งแรกจะเป็นประมาณนี้

    คู่มือย้ายไฟล์: จาก Web Server (Local) สู่ File Server (Remote) ด้วย FileZilla

    ในสถานการณ์นี้ เราจะจำลองว่า FileZilla Client ของเรากำลังทำงานอยู่บน Web Server (หรือเครื่องที่เราเข้าถึงไฟล์ของ Web Server ได้โดยตรง) ซึ่งจะแสดงผลเป็น “Local Site” (ฝั่งซ้าย) ใน FileZilla และเราต้องการย้ายไฟล์จาก Web Server นี้ไปยัง File Server ปลายทาง ซึ่งเราจะเชื่อมต่อและให้แสดงผลเป็น “Remote Site” (ฝั่งขวา) ครับ

    เพื่อให้เห็นภาพรวมของขั้นตอน ผมขอวาดเป็น Flowchart ง่ายๆ ด้วยภาษา Mermaid ให้ดูกันก่อนนะครับ:

    Code snippet

    graph TD
        A[เริ่มต้น] --> B(เปิดโปรแกรม FileZilla บน Web Server)
        B --> C{เชื่อมต่อ File Server ปลายทางผ่าน Site Manager}
        C --> D[เลือกไฟล์/โฟลเดอร์บน Web Server (Local Site) ที่ต้องการย้าย]
        D --> E[ลากไฟล์/โฟลเดอร์จาก Web Server (Local Site) ไปยัง File Server (Remote Site)]
        E --> F[ตรวจสอบความเรียบร้อยและสิ้นสุด]
    

    เอาล่ะครับ มาดูขั้นตอนแบบละเอียดกันเลย:

    1. เชื่อมต่อ File Server ปลายทาง (Remote Site):

    2. หากยังไม่เคยสร้าง Site มาก่อน หรือต้องการเพิ่มการเชื่อมต่อใหม่ ให้คลิกที่ปุ่ม New Site ครับ (ภาพปุ่ม New Site)จากนั้น ตั้งชื่อ Site ของคุณ (เช่น “My File Server”) แล้วกรอกรายละเอียดการเชื่อมต่อที่สำคัญในแท็บ “General” ครับ:

    • Host: ป้อน IP Address หรือชื่อ Hostname ของ File Server ปลายทาง
    • Username: ชื่อผู้ใช้สำหรับเข้า File Server
    • Password: รหัสผ่านสำหรับเข้า File Server

    3. ทำความเข้าใจโปรโตคอลก่อนเชื่อมต่อ (สำคัญมาก!): ก่อนที่เราจะกด “Connect” ผมอยากให้เราทำความเข้าใจเกี่ยวกับ “Protocol” (โปรโตคอล) ที่จะใช้เชื่อมต่อกันสักนิดครับ เพราะมีผลต่อความปลอดภัยของข้อมูลเรามากๆ

    • SFTP (SSH File Transfer Protocol): ชื่อเต็มคือ SSH File Transfer Protocol (คนละตัวกับ FTPS นะครับ!) ตัวนี้จะแตกต่างจาก FTP โดยสิ้นเชิงครับ SFTP ไม่ได้ทำงานบนฐานของ FTP แต่ทำงานผ่านโปรโตคอล SSH (Secure Shell) ซึ่งมีความปลอดภัยสูงมาก โดยปกติเราจะใช้พอร์ต 22 (พอร์ตเดียวกับ SSH) ในการเชื่อมต่อ วิธีนี้ปลอดภัยกว่า FTP มาก เพราะมีการเข้ารหัสข้อมูลทั้งหมดที่ส่งผ่าน ทำให้มั่นใจได้ว่าข้อมูลของเราปลอดภัยครับ ผมแนะนำให้เลือกใช้ SFTP หาก File Server ของคุณรองรับครับ

    • Storj (Decentralized Cloud Storage): ตัวเลือกนี้เป็นโปรโตคอลสำหรับการเชื่อมต่อกับบริการ Cloud Storage แบบกระจายศูนย์ที่ชื่อว่า Storj ครับ ซึ่งมักจะเป็นฟีเจอร์ใน FileZilla Pro (เวอร์ชันเสียเงิน) แทนที่จะเป็นการโอนไฟล์ไปยัง Remote Server แบบเดิมๆ ก็จะเป็นการโอนไฟล์ไปยัง Cloud Storage ตาม Region ต่างๆ แทนครับให้เพื่อนๆ เลือก Protocol ที่เหมาะสม (แนะนำ SFTP) จากนั้นตรวจสอบ Port ให้ถูกต้อง (SFTP มักจะเป็น Port 22)

    • Logon Type (ประเภทการเข้าสู่ระบบ): ผมแนะนำให้เลือกเป็น Ask for password (ถามรหัสผ่านทุกครั้ง) เพื่อความปลอดภัยสูงสุดครับ เพราะถ้าเลือกเป็น Normal โปรแกรมจะบันทึกรหัสผ่านไว้ ซึ่งหากใครเข้าใช้คอมพิวเตอร์หรือ FileZilla ของเราได้ ก็จะสามารถเชื่อมต่อเซิร์ฟเวอร์ได้ทันที เสี่ยงต่อการถูกแฮกได้ครับ (ภาพตัวเลือก Protocol และ Logon Type ใน Site Manager)

    4. เริ่มการเชื่อมต่อ:

    • เมื่อกรอกข้อมูลครบถ้วนและถูกต้องแล้ว กดปุ่ม Connect ได้เลยครับ FileZilla จะพยายามเชื่อมต่อไปยัง File Server ปลายทาง
    • หากเชื่อมต่อสำเร็จ คุณจะเห็นข้อความสถานะการเชื่อมต่อที่ดี และในหน้าต่างด้านขวา (Remote Site) จะแสดงไฟล์และโฟลเดอร์บน File Server ครับ (ภาพแสดงการเชื่อมต่อสำเร็จ และหน้าต่าง Remote Site ที่มีไฟล์)

    5. เตรียมไฟล์และเริ่มโอนย้าย:

    • ตอนนี้ มาดูหน้าต่างโปรแกรม FileZilla กันครับ:
      • ฝั่งซ้าย (Local Site): นี่คือไฟล์และโฟลเดอร์บน Web Server ของคุณ (เครื่องที่คุณกำลังเปิด FileZilla อยู่ หรือเครื่องที่ FileZilla มองเห็นไฟล์เป็น Local)
      • ฝั่งขวา (Remote Site): นี่คือไฟล์และโฟลเดอร์บน File Server ปลายทาง ที่คุณเพิ่งเชื่อมต่อเข้าไป คุณสามารถค้นหาไฟล์หรือโฟลเดอร์ได้หลายวิธีครับ ไม่ว่าจะเป็นการพิมพ์ Path Directory โดยตรง, การคลิกไล่ดูตามโฟลเดอร์ (Browse), หรือใช้ฟังก์ชันค้นหา (หากมี)
    • ในหน้าต่าง Local Site (ด้านซ้าย – Web Server ของคุณ) ให้ไปยังโฟลเดอร์ที่มีไฟล์ที่คุณต้องการจะย้ายไปยัง File Server ครับ (ภาพแสดงการเลือกไฟล์ใน Local Site)
    • เลือกไฟล์หรือโฟลเดอร์ที่ต้องการจากฝั่ง Local Site (Web Server) จากนั้น ลาก (drag and drop) ไฟล์หรือโฟลเดอร์เหล่านั้นข้ามไปวางยังตำแหน่งที่ต้องการในฝั่ง Remote Site (File Server) ครับ หรือจะคลิกขวาที่ไฟล์/โฟลเดอร์ต้นทางแล้วเลือก “Upload” ก็ได้เช่นกัน
    • รอจนกว่ากระบวนการโอนย้ายไฟล์จะเสร็จสิ้น คุณสามารถดูความคืบหน้าได้ที่แถบด้านล่างของโปรแกรมครับ

    6. ตรวจสอบความถูกต้อง:

    • หลังจากโอนย้ายไฟล์เสร็จสิ้น ควรตรวจสอบความถูกต้องของไฟล์บน File Server (Remote Site) อีกครั้งครับ
    • คุณสามารถตรวจสอบจำนวนไฟล์และขนาดรวมของโฟลเดอร์ได้ในแถบสถานะของ FileZilla หรือโดยการคลิกขวาที่โฟลเดอร์แล้วเลือก ‘Properties’ (คุณสมบัติ) ทั้งบน Web Server (Local Site) และบน File Server (Remote Site) เพื่อเปรียบเทียบครับ

    เยี่ยมไปเลยครับ! ตอนนี้ไฟล์ของคุณก็ถูกย้ายจาก Web Server ไปยัง File Server เรียบร้อยแล้ว

    สำรวจฟีเจอร์น่าใช้บน FileZilla Client

    FileZilla Client ไม่ได้มีดีแค่การโอนไฟล์นะครับ ยังมีเมนูและฟีเจอร์ย่อยๆ ที่น่าสนใจอีกเพียบ ลองมาดูกันครับ:

    • FILE (ไฟล์): สำหรับแท็บนี้ นอกจาก Site Manager ที่เราใช้กันไปแล้ว ยังใช้สำหรับเปิดแท็บการเชื่อมต่อใหม่ (New tab), นำเข้า (Import) หรือส่งออก (Export) การตั้งค่า Site Manager จาก FileZilla เครื่องอื่นก็ได้ ทำให้ไม่ต้องตั้งค่าใหม่ทั้งหมดเมื่อย้ายเครื่องครับ
    • EDIT (แก้ไข): ในแท็บนี้จะมีตัวเลือกสำหรับการตั้งค่าต่างๆ ของโปรแกรม (Settings) เช่น การแก้ไขการตั้งค่าพอร์ตเริ่มต้น, การตั้งค่า Network Configuration Wizard เพื่อช่วยตรวจสอบการตั้งค่า Firewall และ Router ว่าถูกต้องหรือไม่ และยังมีตัวเลือกสำหรับล้างข้อมูลส่วนตัว (Clear private data) เช่น ประวัติการเชื่อมต่อและรหัสผ่านที่บันทึกไว้ (ถ้ามี) ครับ
    • VIEW (มุมมอง): แท็บนี้ใช้สำหรับปรับแต่งการแสดงผลส่วนต่างๆ ของโปรแกรม FileZilla เช่น ซ่อนหรือแสดงแถบ Local, Remote, แถบสถานะ, หรือคิวการถ่ายโอนไฟล์ ทำให้เราปรับหน้าจอให้เหมาะสมกับการใช้งานของเราได้ครับ
    • TRANSFER (การถ่ายโอน): แท็บนี้ใช้เพื่อจัดการกับกระบวนการถ่ายโอนไฟล์ในปัจจุบัน เช่น ดูสถานะการถ่ายโอน, ไฟล์ที่อยู่ในคิวรอการโอนย้าย, ประมวลผลคิวอีกครั้ง (Process Queue) และยังสามารถตั้งค่าประเภทการถ่ายโอนเริ่มต้น (Default transfer type) ได้ด้วยครับ
    • SERVER (เซิร์ฟเวอร์): แท็บนี้มีคำสั่งที่เกี่ยวข้องกับเซิร์ฟเวอร์ที่เรากำลังเชื่อมต่ออยู่ เช่น การป้อนคำสั่ง FTP โดยตรง (Enter custom command), การบังคับให้แสดงรายการไฟล์ในโฟลเดอร์อีกครั้ง (Refresh), หรือการค้นหาไฟล์บนเซิร์ฟเวอร์ (Search remote files) ครับ
    • BOOKMARKS (บุ๊กมาร์ก): หากคุณต้องเข้าถึงโฟลเดอร์ใดโฟลเดอร์หนึ่งบน Remote Server บ่อยๆ คุณสามารถเพิ่มเป็นบุ๊กมาร์กได้จากเมนูนี้ครับ ทำให้คลิกเพียงครั้งเดียวก็เข้าไปยังโฟลเดอร์นั้นได้ทันที สะดวกสุดๆ!

    หลังจากที่เราเชื่อมต่อเซิร์ฟเวอร์ได้แล้ว เราก็สามารถโยนไฟล์ข้ามไปมาได้อย่างอิสระ หรือจะใช้ฟีเจอร์เด็ดอีกอย่างคือการแก้ไขไฟล์บนเซิร์ฟเวอร์โดยตรงก็ได้เช่นกันครับ

    ยกตัวอย่างเช่น บางครั้งเราอาจต้องการเข้าไปแก้ไข Source Code หรือไฟล์ Config เล็กๆ น้อยๆ อย่างเร่งด่วน เราสามารถคลิกขวาที่ไฟล์บน Remote Server แล้วเลือก View/Edit ได้เลย FileZilla จะดาวน์โหลดไฟล์นั้นมาเก็บไว้ชั่วคราว ให้เราเปิดแก้ไขด้วยโปรแกรม Editor ที่เราตั้งค่าไว้ เมื่อเราแก้ไขและกด Save ไฟล์นั้น FileZilla จะตรวจจับการเปลี่ยนแปลงและถามว่าจะอัปโหลดไฟล์ที่แก้ไขกลับไปยังเซิร์ฟเวอร์หรือไม่ เป็นฟีเจอร์ที่ช่วยประหยัดเวลาได้มากโขเลยครับ

    FileZilla Server คืออะไร? ใครควรใช้?

    นอกจาก FileZilla Client ที่เราเน้นใช้งานกันแล้ว ยังมี FileZilla Server ด้วยนะครับ ซึ่งตามชื่อเลยครับ ตัวนี้จะทำหน้าที่เป็น “เซิร์ฟเวอร์” ให้ผู้อื่นเชื่อมต่อเข้ามาเพื่อรับส่งไฟล์ FileZilla Server รองรับโปรโตคอล FTP, FTPS (FTP over SSL/TLS), และ SFTP (สำหรับเวอร์ชัน Pro หรือเมื่อคอมไพล์เองกับไลบรารีที่รองรับ) เพื่อให้มั่นใจว่าไฟล์ที่ส่งผ่านนั้นถูกเข้ารหัสและปลอดภัย

    โดยในหน้าจอการตั้งค่า (GUI – Graphical User Interface) ของ FileZilla Server นั้นก็จะมีฟีเจอร์ให้ปรับแต่งได้มากมาย เช่น การสร้าง User และ Group, การกำหนดสิทธิ์การเข้าถึงโฟลเดอร์, การจำกัดความเร็ว, การกรอง IP Address และอื่นๆ อีกมากมายครับ เหมาะสำหรับคนที่ต้องการตั้ง FTP Server ส่วนตัว หรือใช้ภายในองค์กรครับ

    จริงๆ แล้วยังมี FileZilla Pro ที่รวมความสามารถทั้ง Client และ Server พร้อมฟีเจอร์ขั้นสูงอีกมากมาย แต่เนื่องจากผมเองยังไม่เคยได้ใช้งานเวอร์ชัน Pro อย่างเต็มรูปแบบ จึงขอละรายละเอียดในส่วนนี้ไว้ก่อนนะครับ

    โหมดของ FTP: Active และ Passive ต่างกันอย่างไร?

    สำหรับเพื่อนๆ ที่ใช้ FTP (แบบดั้งเดิมหรือ FTPS) อาจจะเคยสงสัยเกี่ยวกับ “โหมด” การเชื่อมต่อของ FTP ซึ่งหลักๆ แล้วมีอยู่ 2 โหมด คือ Active Mode และ Passive Mode ครับ สองโหมดนี้มีความแตกต่างกันในเรื่องของวิธีการที่ Client และ Server สร้างช่องทางการสื่อสารสำหรับส่งข้อมูล (Data Channel) ครับ

    1. Active Mode (แอคทีฟโหมด): ในโหมดนี้ Client จะเป็นคนเริ่มต้นการเชื่อมต่อ Command Channel (ช่องทางส่งคำสั่ง) ไปยัง Server จากนั้น Client จะ “บอก” Server ว่าจะให้ Server เชื่อมต่อ Data Channel กลับมาหา Client ที่ IP Address และ Port ไหน เมื่อ Server ได้รับข้อมูลแล้ว Server ก็จะเป็นฝ่าย “เริ่มต้น” การเชื่อมต่อ Data Channel กลับมายัง Client ครับ ปัญหามักจะเกิดถ้า Client อยู่หลัง Firewall หรือ NAT Router ซึ่งอาจจะบล็อกการเชื่อมต่อที่เข้ามาจาก Server ครับ
    2. Passive Mode (พาสซีฟโหมด) (เป็นค่าเริ่มต้นใน FileZilla): เพื่อแก้ไขปัญหาของ Active Mode จึงเกิด Passive Mode ขึ้นมาครับ ในโหมดนี้ Client จะเป็นคนเริ่มต้นทั้ง Command Channel และ Data Channel ครับ โดย Client จะส่งคำสั่ง PASV ไปยัง Server จากนั้น Server จะตอบกลับมาพร้อมกับ IP Address และ Port Number ที่ Server ได้เปิดรอไว้สำหรับ Data Channel แล้ว Client ก็จะใช้ข้อมูลนั้นในการเริ่มต้นการเชื่อมต่อ Data Channel ไปยัง Server ครับ วิธีนี้มักจะทำงานได้ดีกว่าเมื่อ Client อยู่หลัง Firewall เพราะ Client เป็นฝ่ายเริ่มการเชื่อมต่อออกไปทั้งหมด

    โดยทั่วไปแล้ว FileZilla Client จะตั้งค่าเริ่มต้นเป็น Passive Mode ซึ่งมักจะไม่มีปัญหาในการใช้งานครับ

    บทสรุป: ก้าวต่อไปกับการจัดการไฟล์ของคุณ

    การมาถึงของเทคโนโลยี FTP และโปรแกรมอย่าง FileZilla ได้ทำให้การถ่ายโอนไฟล์ระหว่างเครื่องคอมพิวเตอร์และเซิร์ฟเวอร์เป็นเรื่องที่ง่าย สะดวก และมีประสิทธิภาพมากขึ้นอย่างเห็นได้ชัด ไม่ว่าคุณจะเป็นนักพัฒนาเว็บไซต์, ผู้ดูแลระบบ, หรือผู้ใช้งานทั่วไปที่ต้องการจัดการไฟล์บนเซิร์ฟเวอร์ FileZilla ก็เป็นเครื่องมือหนึ่งที่ตอบโจทย์ได้อย่างยอดเยี่ยมครับ

    สำหรับก้าวต่อไปคุณสามารถประยุกต์ Concept นี้ไปใช้กับการเขียนโค้ด หรือว่าประยุกต์กับ การเขียนสคริปต์ หรือ ทำ Automation เพื่อให้การถ่ายโอนข้อมูลมีประสิทธิภาพไร้รอยต่อมากยิ่งขึ้น

    หวังว่าคู่มือนี้จะเป็นประโยชน์และช่วยให้เพื่อนๆ เข้าใจวิธีการใช้งานโปรแกรม FileZilla ได้ดียิ่งขึ้นนะครับ ลองนำไปปรับใช้กับการทำงานของตัวเองดู แล้วจะพบว่าการจัดการไฟล์บนเซิร์ฟเวอร์ไม่ใช่เรื่องยากอีกต่อไป! หากมีคำถามหรือข้อเสนอแนะเพิ่มเติม สามารถคอมเมนต์ไว้ได้เลยนะครับ แล้วพบกันใหม่ในบทความหน้าครับ!

    Reference

  • เพิ่มประสิทธิภาพชีวิตด้วย AI: แนวคิดจาก World Economic Forum

    เพิ่มประสิทธิภาพชีวิตด้วย AI: แนวคิดจาก World Economic Forum

    สอง สามวันก่อนผมนั่งไถฟีดเฟสบุ๊คแล้วก็ได้เห็นรูปภาพ Skills Quadrant ที่ว่าด้วยทักษะที่จำเป็นสำหรับทศวรรษถัดไป

    ทำให้ผมรู้สึกว่าน่าสนใจที่จะมาลองวิเคราะห์ว่า ทำไม ผู้คนในงาน World Economic Forum ที่จัดที่เมืองดาวอส ประเทศสวิตเซอร์แลนด์ ถึงคิดเช่นนั้นกันะ

    เรามาเริ่มที่ทักษะแรกกันเลยยย

    Technology Skill

    AI and Big Data

    ในปัจจุบัน ปัญญาประดิษฐ์ หรือ AI เป็นหนึ่งในเครื่องมือที่ได้รับความนิยมอย่างล้นหลาม ด้วยความสามารถที่ล้นเหลือ สามารถช่วยเราเพิ่มประสิทธิภาพ เพิ่ม productivity และอีกหลายสิ่งที่มาช่วยอำนวยความสะดวกในชีวิตประจำวัน

    การมาถึงของ Generative AI เช่น ChatGPT, Google Gemini, X Grok, และ Anthropic Claude ยิ่งทำให้ AI เป็นเหมือนมิตรสหายคู่ใจที่ไม่ว่าจะถามอะไรก็ให้คำตอบได้เสมอ ทำงานจิปาถะได้ทั้งนั้นตั้งแต่วางแผนงานในแต่ละสัปดาห์ไปจนถึงเป็นคนให้คำซัพพอร์ทในช่วงที่รู้สึกเปราะบางเช่นกัน

    แต่การที่เราใช้ Gen AI เหล่านี้โดยไม่รู้เทคโนโลยีเบื้องหลัง หรือวิธีการใช้งานมันอย่างละเอียดก็อาจนำมาซึ่งปัญหา (hallucination) หรือ ไม่สามารถดึงประสิทธิภาพของพวกมันได้อย่างเต็มที่นัก

    เพราะฉะนั้นหากเรารู้วิธีการใช้มัน รวมไปถึงหลักการพื้นฐานของ AI และ Big Data เรียนรู้วิธีการใช้ Few Shot Prompting,รู้ว่า Model แต่ละชนิดเหมาะสมกับงานแบบไหน รู้ว่า Platform อย่าง make หรือ n8n สามารถใช้ทำงานประเภท automate ได้ ก็จะยิ่งทำให้เราดึงประสิทธิภาพออกมาได้มากขึ้น ทำให้เราเข้าถึงประสบการณ์ที่ขับเคลื่อนด้วย AI ได้ยิ่งขึ้นอีก้ดวย

    Technological Literacy

    ในโลกที่เทคโนโลยีเป็นดั่งพระเอกในทุกๆภาคอุตสาหกรรม การที่เราสามารถเข้าใจเกี่ยวกับเทคโนโลยีที่ร่วมสมัยได้ก็จะยิ่งทำให้เราไม่ถูกสิ่งเหล่านี้กลืนโดยคลื่นแห่งการเปลี่ยนแปลง หรือถูกแย่งงาน หากเราเข้าใจว่าอะไรคือ Generative AI, รู้วิธีการใช้งานเพื่อใช้พวกมันในการ boost productivity เช่นใช้ NotebookLM ในการสรุปเนื้อหาจากหนังสือ ถามตอบเพื่อสร้างการเรียนรู้ที่มี Interactive มากขึ้น

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

    Self-efficacy

    Curiosity and Lifelong Learning

    ส่วนตัวข้อนี้สำหรับผมนั้นสำคัญยิ่งกว่าข้อไหนๆเลย เพราะว่าความรู้จากยุคก่อนคงไม่สามารถทำให้เราเอาตัวรอดจากเทคโนโลยีที่หมุนผ่านอย่างรวดเร็วได้อีกแล้ว การที่เป็นคนช่างสงสัย ใฝ่รู้ และฝักใฝ่ที่จะร่ำเรียนวิชา มีทักษะใน unlearn relearn reskill ด้วยทักษะเหล่านี้ ก็จะทำให้เราสามารถเอาตัวรอดได้แน่นอน เช่น ความรู้ในการเขียน Pascal จากอดีตก็คงจะไม่สามารถหยุดยั้งการถูก AI เข้ามาแย่งงานได้อีกแล้ว หากเรารู้จักที่เริ่มนับจาก 0 เพื่อเรียนรู้ใหม่ๆอีกครั้ง ก็จะทำให้คุณรอดแน่นอน

    Resilience, flexibility, and agility

    การเป็นคนที่ยืดหยุ่น ล้มแล้วลุกไว รับมือกับอุปสรรคที่ไม่สามารถคาดเดาได้ในปัจจุบัน ก็เป็นอีกหนึ่งทักษะที่จำเป็นเช่นกัน เพราะในเมื่อคุณจำเป็นที่จะต้อง unlearn และ relearn บ่อยครั้งคุณก็ต้องพบกับทางตัน หรือว่ารู้สึกว่าตัวเองเดินผิดทาง ซึ่งนั้นย่อมมาสู่ความรู้สึกเสียใจแน่นอน เพราะฉะนั้นหากคุณมีทักษะเหล่านี้ก็จะช่วยให้คุณไม่เสียเวลามากและพร้อมที่จะลองใหม่อีกครั้งเช่นกัน

    Motivation and self-awareness

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

    Cognitive Skills

    System skills

    การมีทักษะทางความคิดในการมองภาพใหญ่ หรือ มองหลายๆอย่างเป็นองค์รวมก็เป็นสิ่งจำเป็นเช่นกัน คุณ ลองนึกดูสิ ในโลกที่มี เครื่องมือในการใช้านเยอะมาก เช่น ในด้าน Technology มี tool ใหม่ๆเกิดขึ้นเป็นจำนวนมาก หรือมีภาษาเกิดขึ้นเป็นจำนวนมาก หากเรามัวแต่โฟกัสทุกอย่างเราก็จะรู้สึก overwhelm และ หมดกำลังใจได้ในที่สุด การที่เราเข้าใจเทคโนโลยี เข้าใจโลก และตัวเอง มองภาพให้เป็นองค์รวมก็จะช่วยให้เราเข้าใจระบบและสามารถเลือกตัดสินใจได้อย่างถูกต้องในระยะยาวได้นั้นเอง

    Analytic thinking

    นอกจากทักษะในการมองภาพใหญ่แล้ว การที่เรามี problem solving skill และ ทักษะในการ analze องค์ประกอบนั้นมีส่วนช่วยอย่างมากให้คุณเข้าใจว่าคุณกำลังทำอะไรอยู่ เข้าใจว่าตอนนี้สิ่งแวดล้อมรอบตัวคุณเป็นเช่นไร การที่คุณอย่างมีตรรรกะ เป็นระเบียบและอ้างอิงตามหลักฐานที่มี จะยิ่งช่วยให้คุณเข้าใจเหตุการณืในปัจจุบันได้ดีขึ้น

    Creative Thinking

    นอกจากทักษะการมองภาพรวม และการมองเฉพาะเจาะจง สิ่งที่จะช่วยให้การวิเคราะห์ทั้งสองแบบมีประสิทธิภาพมากขึ้นได้นั้น ก็คงนี้ไม่พ้นทักษะเชิงความคิดสร้างสรรค์ การเติมแต่งไอเดียให้มีความหลากหลาย ทดลองตั้งสมมติฐานใหม่ๆ ลองผิดลองถูก ก็จะยิ่งช่วยให้เราได้รับโอกาส หรือเรียนรู้สิ่งใหม่ๆตามมาเช่นกัน

    Team Managements

    Talent Management

    หากคุณเป็นหัวหน้า การมีทักษะในการบริหารทีมก็เป็นอีกหนึ่งสิ่งจำเป็นเ่นกันในยุคที่โอกาสมีให้คว้าอย่างมากมาย การรู้จักบริหารคนเก่งและคนดี จะยิ่งช่วยให้สามารถรักษาเสถียรภาพภายในองค์กร ได้อย่างยิ่ง สอนพวกเขา รักษาและพัฒนา ทำให้พวกเขามีความสุขและรู้สึก productive เป็น safe zone ให้พวกเขาก็จะยิ่งทำให้เรารักษาเขาไว้ได้นานขึ้น

    Working with others

    Empathy and Active Listening

    การเป็นผู้ฟังที่ดีและรู้จักเห็นอกเห็นใจ ก็เป็นอีกหนึ่งทักษะสำคัญเช่นกัน ในบางครั้ง เราไม่รู้หรอกว่าเพื่อนร่วมงานเผชิญอะไรมาบ้างในชีวิต หรือมี background ชีวิตอย่างไร การที่เรารู้จักเห็นอกเห็นใจผู้อื่นเอาใจเขามาใส่ใจเราก็จะช่วยให้การใช้ชีวิตนั้นดำเนินได้อย่างราบรื่นมากขึ้น 


    การรู้จักที่จะฟักมากกว่าการพูดก็จะยิ่งช่วยให้เราได้เรียนรู้สิ่งใหม่ๆ มีเวลาไตร่ตรองและช่วยให้รักษาความสัมพันธ์ได้ดียิ่งขึ้น

    Leadership and Social Influence

    ภาวะผู้นำก็เป็นอีกหนึ่งทักษะจำเป็นในโลกที่อะไรหลายๆอย่างก็ไม่แน่นอนเช่นี้ การที่เรารู้ว่าจะชี้นำและพัฒนาผู้อื่นพาพวกเขาไปสู่จุดที่ดีกว่าย่อมเป็นสิ่งจำเป็นสำหรับยุคนี้เช่นกัน

    Reference

  • 14 Labs จากคอร์ส OpenCV Bootcamp ที่คุณสามารถเรียนได้ฟรี

    14 Labs จากคอร์ส OpenCV Bootcamp ที่คุณสามารถเรียนได้ฟรี

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

    OpenCV จึงเป็น 1 ใน library tools ที่นิยมใช้ในการทำ Computer Visioning ด้วยภาษา python เพราะใช้งานง่ายและมีตัวอย่าง ( case study ) มากมายบนโลกอินเทอร์เน็ต

    ด้วยความอยากรู้ผมจึงลองค้นหาในอินเทอร์เน็ตเกี่ยวกับ OpenCV ว่าจะลองเรียนอันไหนดีละบังเอิญว่าบนเว็บไซต์  OpenCV เขามีคอร์สฟรีให้เรียนด้วยแฮะก็เลยลองสมัครละก็ลงเรียนดู

    โดยการที่จะได้ certificate จากทาง OpenCV university นั้นจะมี 2 ระดับก็คือ ระดับ honor (80%) กับ pass (60%) โดยวิธีการ grading จะเป็นการดูวีดิโอ และตอบ Quiz ในแต่ละ labs ให้ถูกต้อง

    OpenCV Bootcamp Certificate

    ระหว่างที่ผมนั่งเรียนอยู่ประมาณ 2-3 วัน มีเบอร์แปลกโทรมา เราก็นึกว่ามิจฉาชีพ แต่จริงๆแล้วเป็นเหมือนทีมงานของ OpenCV เขาโทรมาแนะแนวและแนะนำว่าเขามีคอร์สอื่นๆด้วยนะ ละก็แอบขายของเสียเงินนิดนึง รวมทั้งบอกว่าถ้าติดปัญหาอะไรสามารถ email มาได้เลย รู้สึกประทับใจมาก 555

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

    Table of Content


    Course Introduction

    ในส่วนของพาร์ทแรกคอร์สจะแนะนำว่าต้องทำ quiz ได้เท่าไหร่ถึงจะได้ certificate และก็ให้ดูวีดิโอบทสัมภาษณ์คุณ Satya Mallick  CEO ของ OpenCV.org และผมสรุปจากวีดิโอได้ดังนี้

    • คุณควรที่จะเริ่มต้นจากพื้นฐานทางด้าน AI เสียก่อนที่จะกระโดดไปในหัวข้อที่มีรายละเอียดเชิงลึกที่ซับซ้อน
    • PyTorch, OpenCV, Tensorflow and keras คือสิ่งที่เราควรรู้
    • เมื่อเรียนรู้เกี่ยวกับ Deep Learning แล้วเราควรที่จะลองประยุกต์ใช้
    • Developer ส่วนใหญ่มักโฟกัสไปที่ภาษา python เนื่องจากการใช้งานที่สะดวกในการเข้าถึงเนื้อหา Deep Learning ทั้งที่ภาษา C ก็สามารถทำได้เช่นกัน
    • อย่าลืมที่จะติดตาม Trend ใหม่ๆเพื่ออัพเดทความรู้ของตัวเอง

    Getting Started with Images

    ปกติการใช้งาน OpenCV จะนิยมใช้กับ Numpy เพื่อให้มาจัดการ list of pixel ในภาพที่อ่านด้วย OpenCV ซึ่งก่อนที่เราจะไปทำจะสามารถนำไปประยุกต์ใช้ในงาน เช่น face recognition system, self driving car, หรือ งานด้าน medical เราควรที่จะทำความเข้าใจกับ ฟังก์ชั่นพื้นฐานเสียก่อน

    1. How do we read image ?

    ใน OpenCV นั้นเราสามารถอ่าน ไฟล์รูปภาพได้หลายนามสกุล ไม่ว่าจะเป็น JPG, PNG ,WEBP ทั้งยังสามารถเลือกตัวแปรเพื่อระบุว่าจะอ่านเป็นแบบโทนสีแบบใด เช่น สีเทา (grayscale) ภาพสี (colorful) หรือ โหลดภาพแบบเทียบเท่ากับที่ input เข้ามีค่า alpha สำหรับค่า opacity ก็ได้

    cb_img = cv2.imread(filename, flag)

     flag สามารถ pass ค่า int หรือ constant variable ใน OpenCV ก็ได้

    FlagConstant VariableDescription
    -1cv2.IMREAD_UNCHANGEDLoads image as-is, including transparency (alpha channel)
    0cv2.IMREAD_GRAYSCALELoads image in grayscale (black & white)
    1cv2.IMREAD_COLORLoads a color image (BGR format)
    ตารางค่า flag

    ใน OpenCV, ภาพที่โหลดด้วย cv2.imread() จะถูกเก็บเป็น NumPy array และใช้ BGR (Blue-Green-Red) แทนที่จะเป็น RGB (Red-Green-Blue) ซึ่งแตกต่างจากไลบรารีอื่นๆ เช่น Matplotlib หรือ PIL

    Numpy Array ที่ได้จากการอ่านภาพ

    2. Reading some attributes 

    เราสามาถเรียก shape หรือ dtype เพื่อดู height & width กับ ประเภทข้อมูลได้จากค่าที่ return กลับมา

    cb_img.shape ## (18,18)
    cb_img.dtype ## uint8

    3. Displaying Image with matplotlib

    หากเราต้องการแสดงผลภาพที่อ่านจาก imread เราสามารถใช้ library matplotlib ในการอ่านค่า numpy array แต่บางครั้งหากเราอ่านภาพสีขาวดำเราจำเป็นที่จะต้องกำหนด parameter cmap เพื่อให้แสดงผลได้ถูกต้อง

    plt.imshow(cb_img, cmap="gray")
    ภาพ grayscale ที่แสดงด้วย ฟังก์ชั่น imshow()

    4. Working with Color Images

    โดยปกติ หากเราพิมพ์ค่า attribute shape ของภาพขาวดำ (cv2.IMREAD_GRAYSCALE), ผลลัพธ์ที่ได้จะมีเพียง (height, width) เท่านั้น เนื่องจากภาพไม่มีช่องสี (color channel). แต่ถ้าเป็นภาพสี (cv2.IMREAD_COLOR), เราจะได้ค่า property ตัวที่ 3 นั้นก็คือ จำนวนช่องสี (Color Channels) ซึ่งใน OpenCV โดยปกติจะเป็น BGR (Blue, Green, Red)

    print("Image size (H, W, C) is:", coke_img.shape) ## (700,700,3)

    ซึ่งถ้าเราเอาภาพสีที่อ่านด้วย imread ไปแสดงผลด้วย matplotlib จะทำให้สีภาพเปลี่ยนไปเนื่องจาก format BGR นั้นเอง เช่น 

    เพื่อให้ได้ค่าที่ถูกต้องเราต้องเอา NumPy array มาทำ array slicing ก่อน

    coke_img_channels_reversed = coke_img[:, :, ::-1]

    เพียงเท่านี้เราก็จะได้ภาพสีที่ตรงกับ format ที่นิยมทั่วไป อย่าง RGB แล้ว

    5. Splitting and Merging Color Channels

    ในบางกรณีเรามีความจำเป็นต้องแยกสีของภาพ เพื่อประมวลผลที่ละช่อง ซึ่งการใช้ทั้ง BGR channel ดูจะเกินความจำเป็นไปหน่อย ด้วยเหตุนี้ OpenCV มีฟังก์ชัน cv2.split() เพื่อแยกช่องสีออกจากกัน และ cv2.merge() เพื่อรวมช่องสีกลับเข้าด้วยกัน

    b, g, r = cv2.split(img)  # แยกภาพออกเป็น B, G, R channel

    เราสามารถใช้ cv2.merge() เพื่อรวมช่องสี (Color Channels) กลับเป็นภาพเดิมได้ โดยส่งค่าช่องสีเป็น tuple เป็นอินพุต เช่น

    img_merged = cv2.merge((r, g, b))  # รวมกลับเป็นภาพ แต่สลับเป็น RGB

    จากตรงนี้จะเห็นว่าเราสามารถเลือกวาง order ใหม่ได้ ก่อน merge เพื่อที่เราจะได้ไม่ต้องทำ array slicing

    6. Converting Color Spaces

    เราสามารถเรียกใช้งานฟังก์ชั่น cvtColor เพื่อแปลง Numpy Array นั้นไปอยู่ใน color channel ที่เราต้องการได้ เช่นจาก BGR → RGB หรือ BGR → Gray เป็นต้น สามารถดูตัวอย่างค่าคงที่ในตารางด้านล่าง

    cv2.cvtColor(img_NZ_bgr, cv2.COLOR_BGR2HSV)

    ส่วนใหญ่แล้วก็จะมีค่าตัวแปรที่นิยมใช้ได้ดังนี้

    CodeDescription
    cv2.COLOR_BGR2RGBConverts BGR → RGB
    cv2.COLOR_RGB2BGRConverts RGB → BGR
    cv2.COLOR_BGR2GRAYConverts BGR → Grayscale
    cv2.COLOR_RGB2GRAYConverts RGB → Grayscale
    cv2.COLOR_GRAY2BGRConverts Grayscale → BGR
    cv2.COLOR_GRAY2RGBConverts Grayscale → RGB
    cv2.COLOR_BGR2HSVConverts BGR → HSV
    cv2.COLOR_RGB2HSVConverts RGB → HSV
    cv2.COLOR_HSV2BGRConverts HSV → BGR
    cv2.COLOR_HSV2RGBConverts HSV → RGB

    7. Saving your modified image

    หลังจากที่เราประมวลผลภาพแล้วเราสามารถ save Numpy Array กลับไปเป็นไฟล์ภาพได้ด้วย ฟังก์ชั่น cv2.imwrite()

    cv2.imwrite("New_Zealand_Lake.png", img_NZ_bgr)

    Basic Image Manipulation

    8. Changing Images Pixels

    การที่เราอ่านไฟล์ภาพด้วย imread() และได้ค่าเป็น NumPy array นั้นแปลว่าเราสามารถแก้ไขภาพด้วยการเปลี่ยนค่าในแต่ละ index ได้ เช่น 

    cb_img[2,2] = 200 ## เปลี่ยนจาก 0-> 200
    ภาพที่ NumPy Array ถูกแก้ไข

    การเปลี่ยนค่าพิกเซลนี้สามารถใช้เพื่อการปรับแต่งภาพ เช่น การทำ Filter, Cropping, Masking หรือ Highlight พื้นที่บางจุด

    cropped_region = img_rgb[200:400, 300:600]

    จากโค้ดด้านบน คือการเลือก แถว 200 – 399 กับ คอลัมน์ 300 – 599 หรือก็คือเอาภาพในช่วง (300,200) ถึง (599,300)นั้นเอง 

    ภาพที่ยังไม่ได้ผ่านการ crop
    ภาพที่ผ่านการ crop แล้ว

    Caution ! ใน opencv กับ matplotlib จะให้ (0,0) อยู่มุมบนซ้ายของภาพ

    9. Resizing Image

    เราสามารถเรียกใช้คำสั่ง resize เพื่อปรับขนาดภาพ โดยอาศัย parameters ดังนี้

    dst = resize(src,dsize[, dst[,fx[,fy[,interpolation]]]])
    ParameterDescription
    srcไฟล์ภาพต้นฉบับ (NumPy array)
    dsizeขนาดของภาพที่ต้องการ (width, height) (ระบุขนาดใหม่โดยตรง)
    fx, fyตัวคูณขนาดภาพ (scale factor) ในแนวนอน (fx) และแนวตั้ง (fy) (ใช้เมื่อไม่กำหนด dsize)
    interpolationวิธีการปรับขนาด เช่น cv2.INTER_LINEAR, cv2.INTER_CUBIC

    โดยการปรับขนาดภาพสามารถทำได้สองวิธี ดังนี้

    “หากต้องการกำหนดขนาดภาพแบบกำหนดเอง (เช่น 600×2000 พิกเซล)”

    resized_image = cv2.resize(cropped_region, (600, 2000))

    “หากต้องการปรับขนาดโดยใช้ Scale Factor (fx, fy) แทน dsize

    ในกรณีที่ใส่ dsize เป็น None เราสามาถใส่เป็น pass fx, fy ที่เป็น scale factor ในแนวนอนและแนวตั้ง ซึ่งหากค่า fx != fy จะทำให้ภาพ scale ผิดปกติได้ 
    ตัวอย่างเช่น

    resized_cropped_region_2x = cv2.resize(cropped_region, None, fx=1, fy=10)
    ภาพที่ถูกปรับแล้ว
    
    

    ⚠ ข้อสังเกต:

    • ถ้า fx ≠ fy ภาพอาจผิดสัดส่วน (ภาพบิดเบี้ยว)
    • ถ้า dsize ถูกตั้งค่าเป็น None, OpenCV จะใช้ fx และ fy แทน

    10. Flipping Image

    เราสามารถ หมุนภาพไปในทิศทางต่างๆ ได้ด้วยคำสั่ง flip()

    dst = cv.flip(src, flipCode)
    ParameterDescription
    srcไฟล์ภาพต้นฉบับ (NumPy array)
    flipCodeค่าตัวเลขเพื่อระบุว่าจะ หมุนรอบแกนอะไร เช่น 0 สำหรับหมุนรอบแกน x ค่าบวก เช่น 1 หมุนรอบแกน y -1 สำหรับหมุนทั้งสองแกน

    Image Annotation

    หลังจากที่เรารู้วิธีการจัดการกับรูปภาพแล้ว เรามาดูว่าถ้าหากเราอยากวาดหรือทำรอยบางอย่างบนภาพ รวมไปถึงการวางข้อความต้องทำอย่างไรกันน

    11. Draw Line

    เราสามารถเรียกใช้ method line() เพื่อเขียนเส้นบางอย่างบน ภาพที่เรา input ได้

    img = cv2.line(img, pt1, pt2,color[, thickness[,lineType[, shift}}})
    ParameterDescription
    img ไฟล์ภาพต้นฉบับ (NumPy array)ไฟล์ภาพต้นฉบับ (NumPy array)
    pt1
    จุดเริ่มต้นของเส้น (x, y)
    pt2จุดสิ้นสุดของเส้น (x, y)
    colorสีของเส้น (Tuple: (R, G, B))
    thicknessความหนาของเส้น (ค่าเริ่มต้น = 1 ไม่สามารถใช้ค่าลบได้)
    lineTypeประเภทของเส้น ค่าเริ่มต้นคือ 8 สำหรับ 8-connected line
    cv2.line(imageLine, (200,100), (400,100), (0,0,255), thickness = 5, lineType = cv2.LINE_AA)

    12. Draw a Circle

    cv2.circle(imageCircle, c, r, (0,0,255), thicknesss=5, lineType=cv2.LINE_AA)
    ParameterDescription
    img ไฟล์ภาพต้นฉบับ (NumPy array)ไฟล์ภาพต้นฉบับ (NumPy array)
    cจุดศูนย์กลางวงกลม (x, y)
    rรัศมี
    colorสีของเส้น (Tuple: (R, G, B))
    thicknessความหนาของเส้น (ค่าเริ่มต้น = 1 ไม่สามารถใช้ค่าลบได้)
    lineTypeประเภทของเส้น ค่าเริ่มต้นคือ 8 สำหรับ 8-connected line

    parameter ก็คล้ายๆกับเส้นตรงเลยแต่จะเปลี่ยนเป็น จุดศูนย์กลางกับรัศมีแทน

    13. Draw a Rectangle

    cv2.rectangle(imageRectangle, (500,100), (700,600), (255,0,255), thickness = 5, lineType=cv2.LINE_8)

    สำหรับการวาดเป็น รูป 4 เหลี่ยม ใช้ parameters เหมือนกับ line เลยแต่ต่างตรงที่ว่า จุดเริ่มกับจุดจบ จะใช้บอกว่าจะคลุมตั้งแต่จุดไหนถึงจุดไหน

    14. Add Text in an Image

    เราสามารถเพิ่มข้อความประกอบบนรูปภาพด้วยคำสั่ง putText()

    cv2.putText(imageText, text,org, fontFace, fontScale, fontColor, fontThickness, cv2.LINE_AA)

    Here’s the formatted table for clarity and readability:

    ParameterDescription
    imageTextไฟล์รูปภาพต้นฉบับ (NumPy array)
    textข้อความที่ต้องการใส่ในภาพ
    orgตำแหน่งเริ่มต้นของข้อความ (x, y)
    fontFaceรูปแบบฟอนต์ (เช่น cv2.FONT_HERSHEY_PLAIN)
    fontScaleขนาดของฟอนต์
    fontColorสีของฟอนต์ (Tuple: (R, G, B))
    fontThicknessความหนาของตัวหนังสือ
    lineTypeประเภทของเส้น

    Basic Image Enhancement

    นอกจากการใช้งาน OpenCV ในด้าน Machine Learning และ Computer Vision แล้ว เรายังสามารถนำ OpenCV มาประยุกต์ใช้ในงานตกแต่งและปรับปรุงคุณภาพของภาพได้ เช่น การเพิ่ม ความสว่าง (Brightness), ความคมชัด (Contrast) หรือ การทำ Masking เพื่อไฮไลต์ส่วนที่สำคัญของภาพ

    15. Adding Brightness

    เราสามารถเรียกใช้ ฟังกชั่น add() หรือ substract() เพื่อปรับความ brightness ของภาพ ได้

    matrix = np.ones(img_rgb.shape, dtype="uint8") * 50 
    
    img_rgb_brighter = cv2.add(img_rgb, matrix)
    img_rgb_darker   = cv2.subtract(img_rgb, matrix)

    จากตัวอย่าง เราสร้างเมทริกซ์ (matrix) โดยใช้ขนาดเดียวกับภาพต้นฉบับ และกำหนดประเภทข้อมูลเป็น uint8 จากนั้นนำเมทริกซ์นี้มาคูณด้วยค่าที่ต้องการ แล้วส่งผลลัพธ์เข้าไปในเมทอด เพื่อปรับความสว่างของภาพ

    โดย add() และ substract จะรับ parameters สองตัวได้แก่ NumPy Array ที่เราจะใช้เป็นฐาน และ Numpy Array ที่จะเข้ามาปรับปรุง

    ภาพ การเปลี่ยนแปลงหากเราใช้ add() หรือ substract()

    16. ปรับ Contrast ด้วย cv2.multiply()

    เราสามารถปรับค่า Contrast ของภาพโดยใช้ cv2.multiply(), แต่ต้องระวังว่าเมื่อค่าพิกเซลที่คูณกันเกิน 255 อาจเกิด overflow ซึ่งทำให้สีของภาพเพี้ยนได้ ดังนั้น ควรใช้ numpy.clip() เพื่อลดค่าพิกเซลให้อยู่ในช่วง [0, 255]

    matrix_low_contrast = np.ones(img_rgb.shape) * 0.8
    matrix_high_contast = np.ones(img_rgb.shape) * 1.2
    
    img_rgb_lower  = np.uint8(cv2.multiply(np.float64(img_rgb), matrix_low_contrast))
    img_rgb_higher = np.uint8(np.clip(cv2.multiply(np.float64(img_rgb), matrix_high_contast), 0, 255))

    การลด Contrast ใช้ตัวคูณ < 1 ส่วนการเพิ่ม Contrast ใช้ตัวคูณ > 1 และใช้ clip() เพื่อให้ค่าพิกเซลไม่เกินช่วงที่กำหนด

    หรือเราสามารถเลือกใช้เป็น method threshold() ซึ่งจะเปลี่ยนค่าใน NumPy Array ให้เป็น 255 หากเกินค่าที่เรากำหนด และ 0 ในกรณีที่ต่ำกว่า

    retval, img_thresh = cv2.threshold(img_read, 100, 255, cv2.THRESH_BINARY)

    หากค่าพิกเซลเกิน 100 ก็ให้ตั้งค่าเป็น 255 หรือสีขาว ถ้าน้อยกว่าก็เป็น 0 หรือ สีดำ ซึ่งเราสามารถกำหนดวิธีการตั้งค่าเมื่อเกิน THRESHOLD ผ่าน parameters ตัวสุดท้าายอย่าง type

    Thresholding TypeDescription
    cv2.THRESH_BINARYPixels ≥ thresh → 255, else 0.
    cv2.THRESH_BINARY_INVInverse: Pixels ≥ thresh → 0, else 255.
    cv2.THRESH_TRUNCPixels ≥ thresh → set to thresh, else unchanged.
    cv2.THRESH_TOZEROPixels < thresh → set to 0, else unchanged.
    cv2.THRESH_TOZERO_INVPixels ≥ thresh → set to 0, else unchanged.

    Thresholding สามารถนำไปใช้กับการแยกภาพออกจากพื้นหลัง (Background Removal), OCR, X-ray และ MRI scan ได้ แต่ต้องใช้กับ ภาพ Grayscale เท่านั้น

    ในบางกรณี การใช้ cv2.threshold() อาจไม่เพียงพอ เช่น ถ้าภาพมีแสงที่ไม่สม่ำเสมอ เราสามารถใช้ cv2.adaptiveThreshold() เพื่อให้ OpenCV คำนวณค่า Threshold ในแต่ละส่วนของภาพโดยอัตโนมัติ โดยภายใต้ฟังก์ชั่นดังกล่าวคือสูตรดังนี้

    Adaptive Thresholding เหมาะสำหรับภาพที่มีแสงไม่สม่ำเสมอ เช่น เอกสารที่มีเงา หรือภาพ X-ray ที่ต้องการแยกโครงสร้างที่มีความเข้มแตกต่างกัน

    ภาพกระดาษโน้ตเปียโนนที่มีความขุ่นมัว (แสงไม่เท่ากัน)

    17. การทำ  BITWISE AND OR XOR NOT

    method ตระกูล bitwise-{and,or, xor, not} นั้นช่วยให้การ เอาภาพสองภาพมาซ้อนกันนั้นทำไปได้อย่างมีประสิทธิภาพ เช่นการ ทำ masking (การซ่อนหรือเลือกบางส่วนของภาพ) แต่การทำ bitwise นั้นเหมาะใช้กับภาพขาวดำ (Grayscale) หรือภาพที่เป็น Binary Mask (ค่าพิกเซลเป็น 0 หรือ 255 เท่านั้น)

    ภาพสองภาพที่จะมาทำ Masking

    Bitwise AND Operator

    Bitwise OR Operator

    Bitwise XOR Operator

    โดยใน แล๊ปจะพาดูการทำ Masking ของรูป brand Coca Cola กับภาพสี

    กระบวนการทั้งหมด

    โดยในตอนแรกนั้น เราจะเริ่มจากการแปลงด้วย cvtColor เพื่อเปลี่ยนเป็น grayscale ก่อน

    จากนั้นก็ใช้ ภาพด้านบนเป็น mask ให้กับภาพหลากสีที่เป็น background เราก็จะได้

    จากกนั้นให้ ให้ bitwise_not กลับ coca cola brand เพื่อให้สีขาวไปอยู่ด้านนอกตัวโลโก้ เพื่อที่เราจะเอามา ทำ masking

    จากนั้นก็เอาภาพสองภาพมารวมกันโดย add() ก็จะได้ภาพสุดท้าย

    ภาพสุดท้าย
    img_bgr =cv2.imread("coca-cola-logo.png")

    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
    img_background_bgr = cv2.imread("checkerboard_color.png")
    img_background_rgb = cv2.cvtColor(img_background_bgr, cv2.COLOR_BGR2RGB)

    img_background_rgb = cv2.resize(img_background_rgb, (logo_w, logo_h), interpolation=cv2.INTER_AREA)
    img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
    retval, img_mask = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
    img_mask_inv = cv2.bitwise_not(img_mask)
    img_background = cv2.bitwise_and(img_background_rgb, img_background_rgb, mask = img_mask)
    img_foreground = cv2.bitwise_and(img_rgb, img_rgb, mask = img_mask_inv)
    result = cv2.add(img_background, img_foreground)

    Accessing the Camera

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

    18. How to use your camera via opencv

    น OpenCV เราสามารถใช้ cv2.VideoCapture() เพื่อเปิดกล้องและอ่านข้อมูลภาพแบบวิดีโอ ซึ่งสามารถนำไปประยุกต์ใช้ในงาน Object Tracking, Face Detection, หรือ Motion Detection ได้

    import cv2
    import sys


    s = 0
    if len(sys.argv) > 1:
    s = sys.argv[1]

    source = cv2.VideoCapture(s)

    win_name = 'Camera Preview'
    cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)

    while cv2.waitKey(1) != 27:
    has_frame, frame = source.read()
    if not has_frame:
    break
    cv2.imshow(win_name, frame)

    source.release()
    cv2.destroyWindow(win_name)

    Video Writing

    การบันทึก (save) หรือดึงข้อมูลวิดีโอ (extract) เป็นสิ่งสำคัญในหลายงาน เช่น กล้องวงจรปิด (CCTV) ที่ต้องบันทึกช่วงเวลาสำคัญเพื่อใช้เป็นหลักฐาน หรือการประมวลผลวิดีโอในงานคอมพิวเตอร์วิทัศน์ เช่น Object Tracking หรือ Motion Detection

    ปกติแล้วเราก็จะใช้ VideoCapture อ่านไฟล์ วีดิโอแต่ต้องระวังว่า frame ที่เรา read มานั้นจะให้ภาพใน color channel BGR เราต้องทำ slicing ก่อน

    19 Writing Video using OpenCV

    ใน OpenCV เราสามารถใช้ cv2.VideoWriter() เพื่อบันทึกวิดีโอที่ถูกอ่านหรือประมวลผล โดยต้องกำหนดชื่อไฟล์, รูปแบบการบีบอัด (codec), อัตราเฟรม (FPS) และขนาดเฟรมของวิดีโอ

    VideoWriter object = cv.VideoWriter(filename, fourcc, fps, frameSize )
    
    ParameterDescription
    filenameชื่อไฟล์output
    fourcc4-character code ที่ใช้ compress frames ซึ่งเราสามารถดูโค้ดได้บน เว็บไซต์ documentation ของทาง OpenCV ได้
    fpsอัตราเฟรมต่อวินาที (Frames Per Second) ของวิดีโอที่สร้าง
    frameSizeขนาดของเฟรมวิดีโอ (width, height)
    # Default resolutions of the frame are obtained.
    # Convert the resolutions from float to integer.
    frame_width = int(cap.get(3))
    frame_height = int(cap.get(4))
    
    # Define the codec and create VideoWriter object.
    out_avi = cv2.VideoWriter("race_car_out.avi", cv2.VideoWriter_fourcc("M", "J", "P", "G"), 10, (frame_width, frame_height))
    
    out_mp4 = cv2.VideoWriter("race_car_out.mp4", cv2.VideoWriter_fourcc(*"XVID"), 10, (frame_width, frame_height))
    # Read until video is completed
    while cap.isOpened():
        # Capture frame-by-frame
        ret, frame = cap.read()
    
        if ret:
            # Write the frame to the output files
            out_avi.write(frame)
            out_mp4.write(frame)
    
        # Break the loop
        else:
            break
    # When everything done, release the VideoCapture and VideoWriter objects
    cap.release()
    out_avi.release()
    out_mp4.release()
    

    🔹 เปรียบเทียบ Codec ที่นิยมใช้

    CodecFile FormatDescription
    "XVID".aviใช้กันอย่างแพร่หลาย, ขนาดไฟล์ไม่ใหญ่เกินไป
    "MP4V".mp4รองรับการใช้งานบนอุปกรณ์ทั่วไป
    "MJPG".aviใช้สำหรับการบันทึกภาพที่ไม่ต้องการบีบอัดมาก
    "H264".mp4บีบอัดได้ดี, ใช้พื้นที่น้อย

    Image Filtering ( Edging Detection )

    20. Canny Edge Detection

    Edge Detection เป็นเทคนิคที่ช่วยระบุ ขอบของวัตถุในภาพ โดยตรวจหาการเปลี่ยนแปลงของสีหรือความเข้มของพิกเซล ซึ่งสามารถใช้ในการระบุตำแหน่ง มุม (Corners) หรือ ขอบวัตถุ (Edges) ได้ง่ายขึ้น

    ซึ่งการทำ Edge Detection มีหลายวิธี เช่น

    MethodDescription
    Laplacian Operatorใช้ SecondOrder Derivative ตรวจจับขอบได้ไว แต่ไวต่อ Noise
    Sobel Operatorใช้ First-Order Derivative คำนวณแนวขอบในแนวแกน X และ Y
    Canny Edge Detectionวิธีที่นิยมใช้มากที่สุด เพราะให้ผลลัพธ์ที่คมชัด แต่มีค่าใช้จ่ายสูงกว่าวิธีอื่น

    โดยใน โค้ดตัวอย่างนี้จะมีเรียกใช้ฟังก์ชั่น Canny() เพื่อให้ดูตัวอย่างภาพ และ  วิธีการหา corners หรือ key features ในภาพ ด้วยวิธี Shi-Tomasi Corner Detection ( goodFeaturesToTrack() )

    “การทำ Edge Detection จะมีประสิทธิภาพกับภาพแบบ Grayscale”

    import cv2
    import sys
    import numpy
    
    PREVIEW  = 0  # Preview Mode
    BLUR     = 1  # Blurring Filter
    FEATURES = 2  # Corner Feature Detector
    CANNY    = 3  # Canny Edge Detector
    
    feature_params = dict(maxCorners=500, qualityLevel=0.2, minDistance=15, blockSize=9)
    s = 0
    if len(sys.argv) > 1:
        s = sys.argv[1]
    
    image_filter = PREVIEW
    alive = True
    
    win_name = "Camera Filters"
    cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
    result = None
    
    source = cv2.VideoCapture(s)
    
    while alive:
        has_frame, frame = source.read()
        if not has_frame:
            break
    
        frame = cv2.flip(frame, 1)
    
        if image_filter == PREVIEW:
            result = frame
        elif image_filter == CANNY:
            result = cv2.Canny(frame, 80, 150)
        elif image_filter == BLUR:
            result = cv2.blur(frame, (13, 13))
        elif image_filter == FEATURES:
            result = frame
            frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            corners = cv2.goodFeaturesToTrack(frame_gray, **feature_params)
            if corners is not None:
                for x, y in numpy.float32(corners).reshape(-1, 2):
                    cv2.circle(result, (x, y), 10, (0, 255, 0), 1)
    
        cv2.imshow(win_name, result)
    
        key = cv2.waitKey(1)
        if key == ord("Q") or key == ord("q") or key == 27:
            alive = False
        elif key == ord("C") or key == ord("c"):
            image_filter = CANNY
        elif key == ord("B") or key == ord("b"):
            image_filter = BLUR
        elif key == ord("F") or key == ord("f"):
            image_filter = FEATURES
        elif key == ord("P") or key == ord("p"):
            image_filter = PREVIEW
    
    source.release()
    cv2.destroyWindow(win_name)
    
    จุดวงกลมสีเขียวที่เกิดจากการที่มีการเปลียนแปลงของพิกเซลอย่างมาก

    Image Feature and Alignment

    Image Alignment คือกระบวนการนำภาพที่มุมมองแตกต่างกันมาปรับให้ตรงกัน โดยใช้เทคนิคการจับคู่ฟีเจอร์ (Feature Matching) และการแปลง Homography

    หลักการสำคัญที่ใช้ใน Image Alignment คือ Homography Transformation, ซึ่งเป็นการแปลงเชิงเรขาคณิตที่ใช้ในการ แมปจุดที่ตรงกันระหว่างสองภาพ โดยต้องใช้ จุดที่ตรงกันอย่างน้อย 4 จุด ในการคำนวณ Homography Matrix (H).

    จากตัวอย่างในวีดิโอเขาจะทำการอ่านภาพสองมุม และทำการหา key features (ในที่นี้ใช้ ORB feature Oriented Fast and Rotated brief ซึ่งนิยมใช้ในการทำ image matching, object recognition, และ image alignment)

    orb = cv2.ORB_create(MAX_NUM_FEATURES) ### MAX_NUM_FEATURES คือบอกว่าอยากได้กี่ point
    keypoints1, descriptors1 = orb.detectAndCompute(im1_gray, None) ## หา keypoint และ compute descriptor
    keypoints2, descriptors2 = orb.detectAndCompute(im2_gray, None)
    
    # Display
    im1_display = cv2.drawKeypoints(im1, keypoints1, outImage=np.array([]), 
                                    color=(255, 0, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
    
    im2_display = cv2.drawKeypoints(im2, keypoints2, outImage=np.array([]), 
                                    color=(255, 0, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
    
    ParametersDescription
    key-pointจุดเด่นในภาพ
    descriptorfeatured vector
    ผลลัพธ์จากโค้ดด้านบน

    เมื่อเราได้ key features แล้วก็นำมาทำ features matching

    # Match features.
    matcher = cv2.DescriptorMatcher_create(cv2.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING)
    
    # Converting to list for sorting as tuples are immutable objects.
    matches = list(matcher.match(descriptors1, descriptors2, None))
    
    # Sort matches by score
    matches.sort(key=lambda x: x.distance, reverse=False)
    
    # Remove not so good matches
    numGoodMatches = int(len(matches) * 0.1)
    matches = matches[:numGoodMatches]
    # Draw top matches
    im_matches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None)
    
    plt.figure(figsize=[40, 10])
    plt.imshow(im_matches);plt.axis("off");plt.title("Original Form")
    
    Matching key features

    เมื่อได้จุดที่ match กันแล้วเราก็สามารถใช้การแปลงเชิงเรขาคณิตได้

    # Extract location of good matches
    points1 = np.zeros((len(matches), 2), dtype=np.float32)
    points2 = np.zeros((len(matches), 2), dtype=np.float32)
    
    for i, match in enumerate(matches):
        points1[i, :] = keypoints1[match.queryIdx].pt
        points2[i, :] = keypoints2[match.trainIdx].pt
    
    # Find homography
    h, mask = cv2.findHomography(points2, points1, cv2.RANSAC)
    # Use homography to warp image
    height, width, channels = im1.shape
    im2_reg = cv2.warpPerspective(im2, h, (width, height))
    
    # Display results
    plt.figure(figsize=[20, 10])
    plt.subplot(121);plt.imshow(im1);    plt.axis("off");plt.title("Original Form")
    plt.subplot(122);plt.imshow(im2_reg);plt.axis("off");
    plt.title("Scanned Form")
    
    ผลลัพธ์สุดท้าย

    Panorama

    22. Create a panorama

    Panorama คือการรวมภาพหลายๆ ภาพเข้าด้วยกันเพื่อสร้างภาพที่กว้างขึ้น ซึ่งเป็นเทคนิคที่ใช้ในงานด้าน Image Alignment และ Feature Matching

    โดยปกติแล้วการทำภาพ Panoramas ต้องอาศัย 4 ขั้นตอนได้แก่

    StepProcess
    Detect Featureค้นหา Key Features ในแต่ละภาพ
    Match Keypointsจับคู่ Key Features ระหว่างภาพ
    Estimate Homographyคำนวณ Homography Matrix เพื่อต่อภาพเข้าด้วยกัน
    Blend Images Smoothlyรวมภาพและทำให้ขอบภาพต่อกันอย่างแนบเนียน

    แต่ในตัวอย่างของ Course นี้จะใช้ built in function จาก Stitch Class ซึ่งเป็น automated high-level API ใช้งานง่าย สะดวกเหมาะกับการเรียนรู้พื้นฐาน แต่ ไม่เหมาะในกรณีที่ต้องการดัดแปลงภาพมากๆ หรือภาพ ไม่ได้ มีจุดต่อกันหลายจๆ จุด

    # Stitch Images
    stitcher = cv2.Stitcher_create()
    status, result = stitcher.stitch(images)
    
    if status == 0:
        plt.figure(figsize=[30,10])
        plt.imshow(result)
    
    ภาพ Panorama

    HDR

    23. How to Make HDR Image using OpenCV

    HDR (High Dynamic Range) เป็นเทคนิคที่ช่วยให้ภาพมี ช่วงสีและความสว่างที่กว้างขึ้น ทำให้สามารถเก็บรายละเอียดได้ทั้งในพื้นที่มืดและสว่างของภาพ

    หลักการของ HDR คือการรวมภาพที่ถ่ายด้วย ค่า Exposure (EV) ต่างกัน เพื่อสร้างภาพที่มีรายละเอียดครบถ้วน

    ในการสร้างภาพ HDR ในแล๊ปจะแบ่งออกเป็น 5 ขั้นตอน

    1. อ่านชุดภาพที่มีค่า Exposure แตกต่างกัน

    def readImagesAndTimes():
        # List of file names
        filenames = ["img_0.033.jpg", "img_0.25.jpg", "img_2.5.jpg", "img_15.jpg"]
    
        # List of exposure times
        times = np.array([1 / 30.0, 0.25, 2.5, 15.0], dtype=np.float32)
    
        # Read images
        images = []
        for filename in filenames:
            im = cv2.imread(filename)
            images.append(im)
    
        return images, times
    
    List of Array ของภาพในแต่ละ exposure

    2. Align ภาพให้ตรงกันด้วย createAlignMTB()

    # Read images and exposure times
    images, times = readImagesAndTimes()
    
    # Align Images
    alignMTB = cv2.createAlignMTB()
    alignMTB.process(images, images)
    
    • การจัดแนว (Alignment) ของภาพเป็นสิ่งสำคัญ เพราะหากภาพไม่ตรงกัน ภาพ HDR ที่ได้จะมี Ghosting
    • เทคนิคที่ใช้คือ Median Threshold Bitmap (MTB), ซึ่งทำงานโดยแปลงภาพเป็น Binary Mask และใช้ Bit-Shifting เพื่อจัดแนวภาพ

    3. คำนวณ Camera Response Function (CRF)

    # Find Camera Response Function (CRF)
    calibrateDebevec = cv2.createCalibrateDebevec()
    responseDebevec = calibrateDebevec.process(images, times)
    
    # Plot CRF
    x = np.arange(256, dtype=np.uint8)
    y = np.squeeze(responseDebevec)
    
    ax = plt.figure(figsize=(30, 10))
    plt.title("Debevec Inverse Camera Response Function", fontsize=24)
    plt.xlabel("Measured Pixel Value", fontsize=22)
    plt.ylabel("Calibrated Intensity", fontsize=22)
    plt.xlim([0, 260])
    plt.grid()
    plt.plot(x, y[:, 0], "b", x, y[:, 1], "g", x, y[:, 2], "r")
    
    • ฟังก์ชัน cv2.createCalibrateDebevec() คำนวณ Camera Response Function (CRF) เพื่อลดความผิดเพี้ยนของแสงในภาพ
    • ค่าที่ได้จาก CRF จะช่วยให้การรวมภาพ HDR แม่นยำขึ้น ลด Overexposure

    4. สร้าง HDR Image

    # Merge images into an HDR linear image
    mergeDebevec = cv2.createMergeDebevec()
    hdrDebevec = mergeDebevec.process(images, times, responseDebevec)
    
    • ฟังก์ชัน cv2.createMergeDebevec() รวมภาพทั้งหมดเป็น HDR โดยใช้ข้อมูลจาก CRF
    • ค่า exposure_times คือเวลาการเปิดรับแสงของแต่ละภาพ ซึ่งช่วยให้ระบบรู้ว่าภาพไหนสว่างกว่ากัน

    5. ปรับโทนสีด้วย Tone Mapping

    # Tonemap using Drago's method to obtain 24-bit color image
    tonemapDrago = cv2.createTonemapDrago(1.0, 0.7)
    ldrDrago = tonemapDrago.process(hdrDebevec)
    ldrDrago = 3 * ldrDrago
    
    # Saving image
    cv2.imwrite("ldr-Drago.jpg", 255*ldrDrago)
    
    # Plotting image
    plt.figure(figsize=(20, 10));plt.imshow(np.clip(ldrDrago, 0, 1)[:,:,::-1]);plt.axis("off");
    
    • การทำ Tone Mapping ช่วยปรับโทนสีให้สมดุลกับหน้าจอทั่วไป เพราะภาพ HDR มีค่าความสว่างสูงกว่าปกติ
    • OpenCV มีหลาย Algorithm เช่น Drago, Reinhard, Mantiuk ซึ่งให้ผลลัพธ์ที่แตกต่างกัน
    MethodDescription
    Dragoเหมาะกับภาพที่มี contrast สูง, ควบคุม Dynamic Range ได้ดี
    Reinhardปรับโทนสีให้สมดุลกับ การมองเห็นของมนุษย์
    Mantiukเน้นการ เพิ่มรายละเอียดของภาพ

    Object Tracking

    24. Real-time Tracking Object

    Object Tracking คือกระบวนการติดตาม ตำแหน่งของวัตถุในวิดีโอ โดยใช้เฟรมแรกเป็นจุดเริ่มต้น และคำนวณตำแหน่งของวัตถุในเฟรมต่อๆ ไป

    ใน OpenCV มีหลาย Algorithm ที่ใช้ในการติดตามวัตถุ ซึ่งแต่ละแบบมีจุดแข็งและจุดอ่อนที่แตกต่างกัน

    AlgorithmDescriptionProsCons
    BOOSTINGใช้ AdaBoost Classifierใช้งานง่ายช้าและแม่นยำต่ำ
    MILใช้ Multiple Instance Learningทนต่อการเปลี่ยนแปลงของวัตถุอาจเกิด Drift ได้
    KCFใช้ Kernelized Correlation Filtersเร็วและแม่นยำกว่าทั่วไปไม่รองรับ Occlusion
    CSRTเน้นความแม่นยำสูงแม่นยำมากกว่าทุกตัวช้ากว่า KCF
    TLDใช้การเรียนรู้แบบ Self-learningฟื้นตัวจาก Occlusion ได้ดีFalse Positives เยอะ
    MEDIANFLOWใช้ Optical Flow คำนวณความแตกต่างของพิกเซลแม่นยำสูงมากใช้งานได้เฉพาะการเคลื่อนที่ Smooth
    GOTURNใช้ Convolutional Neural Network (CNN)ทนต่อการเปลี่ยนแปลงของวัตถุต้องการ Pre-trained Model
    MOSSEใช้ Minimum Output Sum of Squared Errorเร็วที่สุด เหมาะกับ real-timeความแม่นยำต่ำ

    โค้ดด้านล่างจะเป็นการที่การอ่าน frame แรกและทำ กรอบสี่เหลี่ยมมล้อมบริเวรณที่วัตถุขยับ

    เริ่มแรกก็จะเป็นฟังก์ชั่นที่เอาไว้วาดกรอบสำหรับติดตามวัตถุในวีดิโอและสำหรับเอาข้อความแสดง

    video_input_file_name = "race_car.mp4"
    
    
    def drawRectangle(frame, bbox):
    p1 = (int(bbox[0]), int(bbox[1]))
    p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
    cv2.rectangle(frame, p1, p2, (255, 0, 0), 2, 1)
    
    
    def displayRectangle(frame, bbox):
    plt.figure(figsize=(20, 10))
    frameCopy = frame.copy()
    drawRectangle(frameCopy, bbox)
    frameCopy = cv2.cvtColor(frameCopy, cv2.COLOR_RGB2BGR)
    plt.imshow(frameCopy)
    plt.axis("off")
    
    
    def drawText(frame, txt, location, color=(50, 170, 50)):
    cv2.putText(frame, txt, location, cv2.FONT_HERSHEY_SIMPLEX, 1, color, 3)
    # Set up tracker
    tracker_types = [
    "BOOSTING",
    "MIL",
    "KCF",
    "CSRT",
    "TLD",
    "MEDIANFLOW",
    "GOTURN",
    "MOSSE",
    ]
    

    เลือกประเภท Algorithm และอ่านเฟรมแรก

    tracker_type = tracker_types[2]
    
    if tracker_type == "BOOSTING":
    tracker = cv2.legacy.TrackerBoosting.create()
    elif tracker_type == "MIL":
    tracker = cv2.legacy.TrackerMIL.create()
    elif tracker_type == "KCF":
    tracker = cv2.TrackerKCF.create()
    elif tracker_type == "CSRT":
    tracker = cv2.TrackerCSRT.create()
    elif tracker_type == "TLD":
    tracker = cv2.legacy.TrackerTLD.create()
    elif tracker_type == "MEDIANFLOW":
    tracker = cv2.legacy.TrackerMedianFlow.create()
    elif tracker_type == "GOTURN":
    tracker = cv2.TrackerGOTURN.create()
    else:
    tracker = cv2.legacy.TrackerMOSSE.create()
    # Read video
    video = cv2.VideoCapture(video_input_file_name)
    ok, frame = video.read()
    
    

    วน While Loop เพื่ออ่านแต่ละเฟรมและทำการบันทึกวีดิโอใหม่

    # Exit if video not opened
    if not video.isOpened():
    print("Could not open video")
    sys.exit()
    else:
    width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
    
    video_output_file_name = "race_car-" + tracker_type + ".mp4"
    video_out = cv2.VideoWriter(video_output_file_name, cv2.VideoWriter_fourcc(*"XVID"), 10, (width, height))
    
    video_output_file_name
    # Define a bounding box
    bbox = (1300, 405, 160, 120)
    # bbox = cv2.selectROI(frame, False)
    # print(bbox)
    displayRectangle(frame, bbox)
    ok = tracker.init(frame, bbox)
    while True:
    ok, frame = video.read()
    
    if not ok:
    break
    
    # Start timer
    timer = cv2.getTickCount()
    
    # Update tracker
    ok, bbox = tracker.update(frame)
    
    # Calculate Frames per second (FPS)
    fps = cv2.getTickFrequency() / (cv2.getTickCount() - timer)
    
    # Draw bounding box
    if ok:
    drawRectangle(frame, bbox)
    else:
    drawText(frame, "Tracking failure detected", (80, 140), (0, 0, 255))
    
    # Display Info
    drawText(frame, tracker_type + " Tracker", (80, 60))
    drawText(frame, "FPS : " + str(int(fps)), (80, 100))
    
    # Write frame to video
    video_out.write(frame)
    
    video.release()
    video_out.release()
    
    
    ตัวอย่างวีดิโอจาก OpenCV

    25. Real Time Face Detection

    Face Detection

    Face Detection เป็นเทคนิคที่ช่วยให้ระบบสามารถระบุใบหน้าของบุคคลในภาพหรือวีดิโอแบบเรียลไทม์ได้ โดยอาศัยฟังก์ชั่น cv2.dnn.readNetFromCaffe() ที่ใช้ในการดาวน์โหลดโมเดลที่เราฝึกไว้ล่วงหน้าแล้วได้

    ในตัวอย่างนี้ เราจะใช้ Single Shot MultiBox Detector (SSD) ซึ่งเป็นโมเดลที่ถูกเทรนมาสำหรับ Face Detection และสามารถรันแบบเรียลไทม์ได้

    ขั้นตอนการทำ

    โหลด Pre-trained Model

    เราควรใช้ cv2.dnn.blobFromImage() เพื่อแปลงภาพให้เหมาะสมกับโมเดล Deep Learning

    import cv2
    
    # โหลดโมเดลที่เทรนไว้ล่วงหน้า (SSD)
    net = cv2.dnn.readNetFromCaffe("deploy.prototxt", "res10_300x300_ssd_iter_140000_fp16.caffemodel")
    
    

    เปิดกล้องหรือโหลดวิดีโอ

    cap = cv2.VideoCapture(0)  # ใช้กล้องเว็บแคม
    

    ประมวลผลเฟรมและตรวจจับใบหน้า

    while cv2.waitKey(1) != 27:  # กด ESC เพื่อออก
        has_frame, frame = cap.read()
        if not has_frame:
            break
    
        frame = cv2.flip(frame, 1)  # แก้ปัญหา Mirror Effect ของกล้อง
    
        # สร้าง 4D blob สำหรับการทำ Neural Network Processing
        blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0), swapRB=False, crop=False)
    
        # Run model เพื่อตรวจจับใบหน้า
        net.setInput(blob)
        detections = net.forward()
    

    วาดกรอบรอบใบหน้าที่ตรวจพบ

    ตั้งค่า Confidence ว่าถ้า เกิน 0.7 แปลว่าเป็นใบหน้าให้สร้างกรอบสีเขียว

     for i in range(detections.shape[2]):
            confidence = detections[0, 0, i, 2]
    
            if confidence > 0.7:  # กำหนด Threshold เพื่อกรองค่าที่ไม่แม่นยำออก
                # คำนวณตำแหน่งของกรอบใบหน้า
                h, w = frame.shape[:2]
                box = detections[0, 0, i, 3:7] * [w, h, w, h]
                x1, y1, x2, y2 = box.astype("int")
    
                # วาดกรอบสีเขียวรอบใบหน้า
                cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
    
                # แสดง Confidence Score
                label = f"Confidence: {confidence:.2f}"
                cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
    

    แสดงผลและปิดโปรแกรมเมื่อกด ESC

     cv2.imshow("Face Detection", frame)
    
    cap.release()
    cv2.destroyAllWindows()
    
    
    ในแล๊ปไม่มีรูปประกอบเลยเอาเขียนมารันเอารูปตัวเอง 555

    26. What is behind. SSD Model

    โมเดล SSD MobileNet ใช้เทคนิค Convolutional Neural Network (CNN) เพื่อตรวจจับใบหน้าในภาพ โดยใช้ Bounding Box และ Confidence Score

    SSD MobileNet ถูกออกแบบมาให้มีขนาดเบา ใช้งานได้รวดเร็ว และเหมาะสำหรับอุปกรณ์ที่มีข้อจำกัดด้านพลังประมวลผล เช่น Mobile Devices หรือ Embedded Systems”

    TF Object Detection

    27. TF Object Detection

    Object Detection คือกระบวนการระบุและจำแนกวัตถุภายในภาพหรือวิดีโอ ซึ่งเป็นพื้นฐานของ AI Vision เช่น Face Recognition, Autonomous Vehicles และ Security Systems

    ใน Lab นี้ เราจะใช้ SSD MobileNet V2 ซึ่งเป็นโมเดลที่มีประสิทธิภาพสูงและเบาสำหรับการตรวจจับวัตถุแบบเรียลไทม์

    ดาวน์โหลดและติดตั้งโมเดล

    import os
    import urllib.request
    import tarfile
    
    # ตรวจสอบว่ามีโฟลเดอร์ models หรือยัง
    if not os.path.isdir("models"):
        os.mkdir("models")
    
    # ตั้งค่าชื่อไฟล์โมเดล
    model_url = "http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz"
    model_path = "models/ssd_mobilenet_v2_coco_2018_03_29.tar.gz"
    
    # ดาวน์โหลดโมเดลหากยังไม่มี
    if not os.path.isfile(model_path):
        urllib.request.urlretrieve(model_url, model_path)
    
        # แตกไฟล์
        with tarfile.open(model_path, "r:gz") as tar:
            tar.extractall("models")
    
        # ลบไฟล์ tar.gz หลังจากแตกไฟล์เสร็จ
        os.remove(model_path)
    
    

    SSD MobileNet V2 สามารถดาวน์โหลดจาก TensorFlow Model Zoo และใช้งานได้ทันที

    โหลดโมเดลเข้าสู่ OpenCV

    เราสามารถใช้ โมเดลจาก Tensorflow ด้วยการเรียก cv2.dnn.readNetFromTensorFlow()

    modelFile  = os.path.join("models", "ssd_mobilenet_v2_coco_2018_03_29", "frozen_inference_graph.pb")
    configFile = os.path.join("models", "ssd_mobilenet_v2_coco_2018_03_29.pbtxt")
    # Read the Tensorflow network
    net = cv2.dnn.readNetFromTensorflow(modelFile, configFile)
    
    

    อ่านภาพและแปลงเป็นไฟล์ blob

    # โหลดภาพตัวอย่าง
    frame = cv2.imread("sample_image.jpg")
    
    # แปลงภาพเป็น Blob
    blob = cv2.dnn.blobFromImage(frame, 1.0, size=(300, 300), mean=(127.5, 127.5, 127.5), swapRB=True, crop=False)
    
    # ส่งข้อมูลเข้าโมเดล
    net.setInput(blob)
    detections = net.forward()
    

    ตรวจจับวัตถุและดึงข้อมูลพร้อมทั้งเขียนข้อความประกอบวัตถุนั้น

    ตรวจจับวัตถุและกำหนดกรอบ Bounding Box เฉพาะวัตถุที่มีค่า Confidence สูงกว่า 25%

    import numpy as np
    
    rows, cols = frame.shape[:2]
    threshold = 0.25  # Confidence threshold
    
    for i in range(detections.shape[2]):
        # ดึงค่าความมั่นใจของการตรวจจับ
        confidence = detections[0, 0, i, 2]
    
        if confidence > threshold:
            classId = int(detections[0, 0, i, 1])
            x = int(detections[0, 0, i, 3] * cols)
            y = int(detections[0, 0, i, 4] * rows)
            w = int(detections[0, 0, i, 5] * cols - x)
            h = int(detections[0, 0, i, 6] * rows - y)
    
            # วาดกรอบและแสดงชื่อวัตถุ
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
            label = f"Class {classId} - {confidence:.2f}"
            cv2.putText(frame, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 1)
    

    แสดงผลของภาพ

    import matplotlib.pyplot as plt
    
    plt.figure(figsize=(8, 6))
    plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    plt.axis("off")
    plt.title("Object Detection Result")
    plt.show()
    
    ผลลัพธ์

    Pose Estimation using OpenPose

    28. Realtime Multi-Person 2D Pose Estimation using Part Affinity Field

    Pose Estimation คือการติดตาม จุดสำคัญของร่างกาย (Keypoints) ของบุคคลในภาพหรือวิดีโอแบบเรียลไทม์ ซึ่งสามารถนำไปใช้ในงาน กีฬา, การเคลื่อนไหวของมนุษย์ (Human Motion Analysis), หรือแม้แต่ Virtual Reality (VR)

    โดยในแล๊ปนี้เราจะใช้ OpenPose Deep Learning Model ที่สามารถตรวจจับ Key-points ได้หลายคน ( Multi-Person) โดยใช้ Part Affinity Fields (PAFs)

    โหลด Pre-trained Model

    ขั้นแรกเราต้องโหลดโมเดลที่ถูกเทรนมาแล้วกับข้อมูลขนาดใหญ่เพื่อนำมาจับท่าทางของคน

    import cv2
    
    # โหลดไฟล์โมเดลจาก OpenPose
    protoFile = "pose_deploy_linevec_faster_4_stages.prototxt"
    weightsFile = "pose_iter_160000.caffemodel"
    
    # โหลดโมเดลเข้าสู่ OpenCV
    net = cv2.dnn.readNetFromCaffe(protoFile, weightsFile)
    

    อ่านภาพและแปลงเป็น Blob

    # อ่านภาพจากกล้องหรือไฟล์
    frame = cv2.imread("person.jpg")
    
    # แปลงภาพเป็น Blob
    blob = cv2.dnn.blobFromImage(frame, 1.0 / 255, (368, 368), (0, 0, 0), swapRB=False, crop=False)
    
    # ส่งข้อมูลเข้าโมเดล
    net.setInput(blob)
    output = net.forward()
    

    อย่าลืมแปลงภาพเป็น blob สำหรับการทำ Deep Learning

    ดึง Keypoints ที่ตรวจพบ

    กรอง keypoint ที่อยู่ต่ำกว่าค่า threshold ออกด้วย cv2.minMaxLoc()

    import numpy as np
    
    nPoints = 15  # จำนวน Keypoints ในโมเดล OpenPose
    h, w = frame.shape[:2]
    
    # คำนวณ Scale Factor
    scaleX = w / output.shape[3]
    scaleY = h / output.shape[2]
    
    # เก็บตำแหน่ง Keypoints
    points = []
    
    # ค่าความมั่นใจขั้นต่ำ
    threshold = 0.1
    
    for i in range(nPoints):
        probMap = output[0, i, :, :]
        minVal, prob, minLoc, point = cv2.minMaxLoc(probMap)
    
        x = int(point[0] * scaleX)
        y = int(point[1] * scaleY)
    
        if prob > threshold:
            points.append((x, y))
        else:
            points.append(None)
    

    วาด Key-points และ Skeleton

    ค้ดนี้ใช้ cv2.circle() เพื่อวาด Keypoints และ cv2.line() เพื่อเชื่อมต่อ key-points เป็น Skeleton

    POSE_PAIRS = [
        [0, 1], [1, 2], [2, 3], [3, 4], [1, 5], [5, 6], [6, 7],
        [1, 14], [14, 8], [8, 9], [9, 10], [14, 11], [11, 12], [12, 13]
    ]
    
    # วาด Keypoints
    for i, p in enumerate(points):
        if p:
            cv2.circle(frame, p, 5, (0, 255, 0), -1)
            cv2.putText(frame, str(i), p, cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
    
    # วาด Skeleton
    for pair in POSE_PAIRS:
        partA, partB = pair
    
        if points[partA] and points[partB]:
            cv2.line(frame, points[partA], points[partB], (255, 0, 0), 2)
    

    แสดงผลลัพธ์ ด้วย MatplotLib

    import matplotlib.pyplot as plt
    
    plt.figure(figsize=(8, 6))
    plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    plt.axis("off")
    plt.title("Pose Estimation Result")
    plt.show()
    
    รูปภาพการ draw line กับ skeleton

    จบกันไปแล้วนะครับกับ สิ่งที่ผมได้รู้จากคอร์สฟรีที่ OpenCV University เกี่ยวกับ OpenCV โดยในหัวข้อนี้จะเน้นไปที่การอธิบาย โค้ดเป็นหลักอาจะไม่ได้ลงลึกในด้านวิชาคณิตสาสตร์ ขอผมไปศึกษาก่อนแล้วจะกลับมาเขียนในหัวข้อถัดไป ละเพื่อนๆคิดว่าจากตัวอย่างทั้งหมด อันไหนดูน่าจะประยุกต์ใช้ได้ง่ายสุดครับลองมาแชร์กันได้ครับ

    References

    https://opencv.org/

    https://docs.opencv.org/4.x/index.html

  • 20 Blocks Essential for WordPress Content Creation

    20 Blocks Essential for WordPress Content Creation

    ผมได้มีโอกาสเรียนคอร์ส Web For Impact Batch 05 กับ Ad’Toy จาก Datarockie ซึ่งผมชอบแนวคิดที่ว่า เราควรที่จะมีเว็บไซต์เป็นของตัวเองไว้สำหรับเป็นบ้านที่แท้จริงเพื่อการแชร์ไอเดีย หรือเก็บบางสิ่งบางอย่างที่เรามองว่าเป็นทรัพย์สินของเรา ที่ไม่ใช่การไปอาศัยอยู่ใต้ชายคาของใคร อาทิ เช่น Facebook Page, Medium Blog, และ อื่นๆ อีกมากมาย

    คุณลองนึกดูสิ หากวันใดวันหนึ่งเพจของคุณถูกปิดตัวลง เนื้อหาที่คุณลงไว้ทั้งหมดก็จะหายไปพร้อมกับเพจของคุณ ซึ่งไม่สามารถที่จะกู้กลับมาได้ และการไปเริ่มต้นใหม่ก็ต้อง เริ่มหา members หายอด reachs ใหม่ทั้งหมดหมดเลยซึ่งนั้นก็ดูจะไม่แฟร์เอาซะเลยใช่ไหมครับ

    แนวคิดนี้ทำให้ผมตั้งสินใจสมัคร Paid Plan ของ WordPress เพื่อมาแชร์ไอเดียหรือสิ่งที่ผมได้เรียนรู้ในชีวิตประจำวัน (หยุดเขียนไปตั้ง 1 เดือนแหนะ แซดเวอร์ งานยุ่งไม่ไหว​)

    จากการลองผิดลองถูกมา 3-4 โพสต์ ผมเลยอยากที่จะมาแชร์ 20 blocks ที่ผมคิดว่าจำเป็นสำหรับการรังสรรค์งานออกมาจากที่แอดทอยแนะนำในคอร์สและที่ผมลองหาดูเอง

    ก่อนที่จะเข้าถึงเนื้อหาผมอยากจะบอกก่อนว่าในเนื้อหาที่จะพูดถึงแค่ประเภท block นะครับจะไม่ได้เจาะลึกว่าแต่ละ block มี toolbar ให้ใช้อะไรบ้างในส่วนนี้ท่านสามารถอ่านต่อได้ผ่านการคลิกตรงนี้ได้เลยครับ

    Table of Content


    • Basic Content Blocks
    • Interactive & Layout Blocks
    • Advanced & Specialty Blocks
    • Workflow ในการวางโครงสร้าง Content

    Basic Content Blocks

    1. Paragraph Blocks

    block ที่ใช้งานแทบจะเรียกว่าทุกๆ จุดของหน้าpost เพื่อการเขียนเนื้อหาบทความของทุกคน

    สามารถเริ่มพิมพ์อะไรก็ได้เลย

    2. Heading Blocks

    block สำหรับการขึ้นหัวข้อใหม่ หรือว่า เนื้อหาพาร์ทใหม่ การใช้ heading block นั้นมีส่วนช่วยให้ผู้ใช้งานอ่านหรือเลือกหัวข้อได้ง่ายขึ้น และยังเป็นผลดีต่อเว็บไซต์ อีกด้วย ( search engines )

    Shortcut: Heading block สามารถเพิ่มได้หลายวิธี เช่น พิมพ์​ # หรือ /h{1-6}

    heading

    3. List Blocks

    List blocks ใช้สำหรับการแสดงรายการ เพื่อจัดการ content ให้เป็นระเบียบยิ่งขึ้น โดยสามารถใช้สร้าง bulleted point หรือ numbered list ก็ได้

    List block

    Shortcut: เราสามารถพิม์ /list หรือ พิมพ์ – เพื่อสร้าง bulleted point กับ พิมพ์ตัวเลขเพื่อสร้าง numbered list

    4. Quote Blocks

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

    Shortcut: /quote หรือ > เพื่อเรียกใช้งาน

    Quote Block

    5. Image Blocks

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

    Shortcut: สามารถพิมพ์ /image

    Image Block

    6. Gallery Blocks

    block สำหรับใช้โชว์รูปภาพเหมือน Image Block แต่จุดแตกต่างกับ Image block ก็คือ gallery block สามารถโชว์ภาพได้มากกว่า 1 ภาพ เหมาะสำหรับใช้กับเว็บไซต์ อาหาร หรือ เว็บไซต์ช่างภาพที่ต้องการโชว์รูปซึ่งเป็นผลงานของเว็บไซต์ เราสามารถปรับจำนวน แถว และ หลัก ของ Gallery Block ได้ว่าอยากได้เท่าไหร่เพื่อให้เหมาะกับการแสดงผลบนหน้าเว็บไซต์

    Shortcut: /gallery เพื่อใช้งานได้

    ข้อควรระวัง

    การใช้งาน Gallery Block มีประโยชน์อย่างมากในการจัดชุดภาพขนาดใหญ่ แต่ก็ควรระวังหากไฟล์ภาพมีขนาดใหญ่เกินไปซึ่งสามารถส่งผลต่อการโหลดภาพ แนะนำว่าภาพควรจะเป็นนามสกุล .webp เพื่อให้ได้ขนาดที่เล็กและโหลดเร็วเป็นผลดีต่อเว็บไซต์

    Gallery Block

    7. Video Blocks

    ใช้สำหรับการอัพโหลด วีดิโอ ใน หน้า page หรือ post เพื่อใช้สำหรับประกอบบทความหรือโฆษณา แต่ในการใช้ Video Block นั้นผู้ใช้งานต้องอัพเกรดเป็นเวอร์ชั่น Premium จึงจะสามารถใช้งาน block ดังกล่าวได้

    Shortcut: /video

    ใช้งาน เราจะมีตัวเลือก 3 options ได้แก่ Upload, Media Library, Insert from URL

    • Upload: ใช้สำหรับการเลือก Video จากบนเครื่องของเรา
    • Media Library: ใช้สำหรับการเลือก Video ที่อยู่บน WordPress
    • Insert from URL: ใช้สำหรับการเลือกวีดิโอโดยการแปะ url ที่อยู่บนอินเทอร์เน็ตเพื่อใช้งาน
    • การใช้ Video block นั้นเหมาะกับการที่เราจะดึงดูดความสนใจของผู้ใช้งาน แต่เราก็ต้องคำนึกถึงขนาดไฟล์และความเร็วอินเทอร์เน็ตของผู้ใช้งานไว้ด้วยเช่นกัน ไม่อย่างนั้นจะเป็นการไปลดประสบการณ์การใช้งานของผู้ใช้งานแทน
    Video Block

    8. Table Blocks

    Table block ใช้สำหรับสร้างตารางเพื่อแสดงค่าบางอย่างประกอบบทความ โดยเราสามารถเลือกได้ว่าจะเอากี่แถว กี่คอลัมน์

    Shortcut: /table

    • หากต้องการสร้างตารางที่มีความซับซ้อนขึ้นเช่น สามารถ เรียงลำดับข้อมูล หรือแบ่งหน้าได้อาจจะมองเป็นการใช้ plugin เสริมอย่าง TablePress
    Table Block

    9. File Block

    block สำหรับวาง file ที่สามารถดาวน์โหลดได้ โดยไฟล์ดังกล่าวจะต้องเป็นนามสกุลที่ wordpress ซัพพอร์ทจึงจะสามารถอัพโหลดได้

    Shortcut: /file

    File Block

    10. Audio Block

    ใช้สำหรับการวางไฟล์ประเภทเสียงต่าง เช่น เพลง, พอดแคสท์, และอื่นๆ เพื่อประกอบบทความ

    Shortcut: /audio

    Interactive & Layout Blocks

    ในพาทย์นี้ block ทั้งหมดจะเป็น block ที่ใช้สำหรับการจัดหน้าและ action ต่างๆ เพื่อทำให้เว็บไซต์ของเรามีความ dynamic มากขึ้นโดยเราจะขอเริ่มจาก

    11. Button Blocks

    block สำหรับการสร้างปุ่ม เพื่อใช้ interact กับหน้าเว็บไซต์ โดยเราสามารถเพิ่มข้อความ ลิ้งค์ หรือตกแต่งได้ ตัวอย่างเช่น เราอยากให้เขาคลิ้กเพื่อออกไปหน้าเว็บไซต์อื่นๆ เป็นต้น

    Shortcut: /button

    Button Block

    12. Column Blocks

    block สำหรับการจัดหน้า โดย block นี้สามารถเพิ่ม ข้อความ สื่อต่างๆ, และสามารถใส่ได้สูงสุด 6 columns ใน 1 แถว โดยเราสามารถเลือกได้ว่าจะเอากี่ column จากค่าเริ่มต้น

    Shortcut: /columns

    Column Block

    13. Cover Blocks

    block สำหรับเพิ่มรูปภาพหรือวีดิโอที่สามารถเขียนข้อความบนภาพได้

    Shortcut: /cover

    Cover Block

    14. Spacer Blocks

    block สำหรับ เพิ่มช่องว่างระหว่าง block อื่นๆ ยกตัวอย่างเช่น หากคุณอยากได้ช่องว่างในแนวตั้ง ก็ใช้ vertical spacer และ ถ้าอยากได้ช่องว่างในแนวนอน ก็เรียก horizontal spacer

    Shortcut: /spacer

    Spacer Block

    15. Group Blocks

    block สำหรับจับกลุ่ม block อื่นๆ เพื่อให้ง่ายต่อการจัดระเบียบ content บนหน้าโพสต์หรือเพจ โดยเราสามารถ ตั้งค่าพื้นหลัง, สี, ช่องว่าง ให้หลากหลายขึ้น

    Shortcut: /group

    Group Block

    16. Media & Text Blocks

    block ที่เราสามารถวางไฟล์ภาพ หรือวีดิโอ พร้อมๆกับ ข้อความ ประกอบได้ ช่วยเพิ่มความชัดเจนให้กับวิดีโอที่จะสื่อได้ดียิ่งขึ้น เราสามารถตั้งค่าสีพื้นหลังและปรับตำแหน่งของข้อความ

    Shortcut: /media

    Media & Text

    17. Embed Blocks

    block ที่ให้คุณสามารถเพิ่มเนื้อหาจากแหล่งอื่นๆบนเว็บไซต์ของคุณได้ด้วยการวางลิ้งค์ใน block อย่างไรก็ตามเราต้องศึกษาเพิ่มเติมว่าเว็บไซต์ไหนบ้างที่อนุญาตให้เราสามารถเอาลิ้งค์จากเว็บเข้ามาแปะบนหน้าเว็บเราได้ เช่น YouTube, Spotify และ Twitter

    Shortcut: /embed

    Embed Block

    Advanced & Specialty Blocks

    18. Shortcode Blocks

    block สำหรับใส่โค้ดไม่ว่าจะเป็น embed file หรือการสร้าง object บ้างอย่างที่ซับซ้อนได้

    Shortcut: /shortcode

    Shortcode Block

    19. HTML Blocks

    block ที่อนุญาตให้เราเขียน htmlได้โดยสามารถเขียน styling เองได้ตามใจ

    Shortcut: /html

    html block

    20. Query Loop Blocks

    block ที่ใช้ในการลูป content บางอย่างที่มีลักษณะเป็นกลุ่ม เช่น โพสต์ของเราเป็นต้น โดยสามารถสร้าง template มาลองรับเพื่อให้ทุกอันแสดงผลเหมือนกัน

    Shortcut: /query

    Query Block

    Workflow ในการวางโครงสร้าง Content

    สำหรับผมเวลาเอา content ที่เตรียมใน notion หรือ จาก notes ต่างๆ มาขึ้นใน wordpress ก็จะแบ่งตามนี้

    • ใช้ heading block เพื่อแบ่งพาร์ท
    • ใช้ paragraph block เพื่อเขียนเนื้อหา
    • ใช้ image block หรือ video block เพื่อให้เห็นภาพมากขึ้นหรือเข้าใจง่ายขึ้น
    • ใช้ spacer block เพื่อจัดหน้าให้ดูอ่านง่าย
    • ใช้ code block ในการวาง code แทนการแคปหน้าจอ

    จบกันไปแล้วนะครับสำหรับ 20 blocks ที่คุณควรรู้จักบน WordPress แล้วคุณละ ใช้ block ไหนบ่อยที่สุด หรือถ้าหากมีไอเดียหรือ block อะไรที่น่าสนใจสามารถแชร์กันได้ใต้ comments ได้เลยครับ

    Reference

    WordPress Documentation