สร้าง Search ของตัวเองด้วย Python CLI + Vector Database

สวัสดีครับเพื่อนๆ โปรแกรมเมอร์ ทุกคน

วันนี้ผมจะมาคุยเรื่องสนุกๆ ที่จะช่วยให้เราค้นหาข้อมูลจากกองไฟล์ text จำนวนมาก ๆ ได้อย่างฉลาดขึ้นกันนะครับ นั่นก็คือการสร้างระบบ Search ของตัวเองง่ายๆ ด้วย Python CLI พร้อมใช้ Vector Database มาช่วยจัดการข้อมูลนี่แหละครับ มันไม่ยากอย่างที่คิด เลยนะ ลองมาดูกัน

เตรียมข้อมูลจากไฟล์ของเราก่อน

อันดับแรกสุด เลยนะครับ เราต้องมีไฟล์ text ที่อยากจะค้นหาก่อน เช่น ไฟล์เอกสาร ไฟล์บันทึก หรืออะไรก็ได้นะครับ สมมติว่าผมมีไฟล์ doc1.txt กับ doc2.txt ที่มีเนื้อหาประมาณนี้นะ

# doc1.txt
# บทความนี้พูดถึงการเขียนโปรแกรม Python สำหรับจัดการไฟล์\
# และใช้ Context Manager เพื่อความปลอดภัย

# doc2.txt
# การค้นหาข้อมูลด้วย AI และ Vector Database กำลังเป็นที่นิยมมาก\
# มันช่วยให้เราเจอสิ่งที่ต้องการได้รวดเร็ว

จากนั้น นะครับ เราก็เขียนโค้ด Python CLI ง่ายๆ เพื่ออ่านไฟล์พวกนี้เข้ามา แบบนี้ครับ

import os

def read_text_files(directory):
    texts = []
    filenames = []
    for filename in os.listdir(directory):
        if filename.endswith(".txt"):
            filepath = os.path.join(directory, filename)
            with open(filepath, "r", encoding="utf-8") as f:
                texts.append(f.read())
                filenames.append(filename)
    return texts, filenames

if __name__ == "__main__":
    # สมมติว่าไฟล์ของเราอยู่ในโฟลเดอร์ 'data' นะครับ
    data_directory = "data"
    if not os.path.exists(data_directory):
        os.makedirs(data_directory)
        # สร้างไฟล์ตัวอย่าง
        with open(os.path.join(data_directory, "doc1.txt"), "w", encoding="utf-8") as f:
            f.write("บทความนี้พูดถึงการเขียนโปรแกรม Python สำหรับจัดการไฟล์และใช้ Context Manager เพื่อความปลอดภัย")
        with open(os.path.join(data_directory, "doc2.txt"), "w", encoding="utf-8") as f:
            f.write("การค้นหาข้อมูลด้วย AI และ Vector Database กำลังเป็นที่นิยมมาก มันช่วยให้เราเจอสิ่งที่ต้องการได้รวดเร็ว")

    all_texts, all_filenames = read_text_files(data_directory)
    print("อ่านข้อมูลได้แล้ว:", len(all_texts), "ไฟล์")
    # Output: อ่านข้อมูลได้แล้ว: 2 ไฟล์

แปลงข้อความเป็น Vector (Embedding)

พอเรามีข้อความแล้วนะครับ ขั้นตอนต่อไปคือการแปลงข้อความพวกนี้ให้เป็นตัวเลขที่คอมพิวเตอร์เข้าใจ ซึ่งเราเรียกมันว่า "Embedding" หรือ "Vector" นั่นเองครับ การทำแบบนี้จะช่วยให้คอมรู้ว่าข้อความไหนมีความหมายคล้ายกันนะ ในตัวอย่างนี้ ผมจะใช้ไลบรารีง่ายๆ อย่างsentence-transformers มาช่วยนะครับ ถ้าไม่มีก็ pip install sentence-transformers ได้เลย

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2') # โหลดโมเดลสำหรับสร้าง Embedding

def get_embeddings(texts):
    embeddings = model.encode(texts)
    return embeddings

if __name__ == "__main__":
    # ... โค้ดอ่านไฟล์ด้านบน ...
    all_texts, all_filenames = read_text_files(data_directory)

    text_embeddings = get_embeddings(all_texts)
    print("แปลงข้อความเป็น Embedding ได้แล้ว:", text_embeddings.shape)
    # Output: แปลงข้อความเป็น Embedding ได้แล้ว: (2, 384) (ถ้ามี 2 ไฟล์และโมเดลสร้าง 384 มิติ)

เก็บ Embedding ใน Vector Database (แบบง่ายๆ)

ทีนี้ เรามี Vector ของข้อความแต่ละอันแล้วนะครับ เราก็จะเอาไปเก็บไว้ในที่ที่เหมาะสม ซึ่งก็คือ Vector Database นั่นเองครับ ในโลกจริงเราอาจจะใช้ Faiss, Pinecone, Weaviate อะไรพวกนี้ แต่วันนี้เราจะทำแบบง่ายๆ ใน Memory ไปก่อน เพื่อให้เห็นภาพนะครับ แบบนี้นะ

from sklearn.metrics.pairwise import cosine_similarity # ต้องมี scikit-learn: pip install scikit-learn

class SimpleVectorDB:
    def __init__(self):
        self.vectors = []
        self.metadatas = [] # เก็บชื่อไฟล์ หรือข้อมูลอื่นๆ ที่เกี่ยวข้อง

    def add_vector(self, vector, metadata):
        self.vectors.append(vector)
        self.metadatas.append(metadata)

    def search(self, query_vector, top_k=1):
        # ค้นหา vector ที่ใกล้ที่สุด โดยใช้ Cosine Similarity

        similarities = cosine_similarity([query_vector], self.vectors)[0]

        # เรียงลำดับตามความคล้ายมากไปน้อย
        sorted_indices = similarities.argsort()[::-1]

        results = []
        for i in sorted_indices[:top_k]:
            results.append({
                "score": similarities[i],
                "metadata": self.metadatas[i],
                # "vector": self.vectors[i] # อาจจะแสดง vector ด้วยก็ได้
            })
        return results

if __name__ == "__main__":
    # ... โค้ดอ่านไฟล์และสร้าง Embedding ด้านบน ...

    all_texts, all_filenames = read_text_files(data_directory)
    text_embeddings = get_embeddings(all_texts)

    my_vector_db = SimpleVectorDB()
    for i, emb in enumerate(text_embeddings):
        my_vector_db.add_vector(emb, {"filename": all_filenames[i], "text_sample": all_texts[i][:50] + "..."})

    print("เก็บ Embedding เข้า DB แล้ว")

ค้นหาข้อมูลด้วยคำถามของเรา

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

if __name__ == "__main__":
    # ... โค้ดสร้าง DB ด้านบน ...

    query_text = "การเขียนโปรแกรมที่ปลอดภัย"
    query_embedding = get_embeddings([query_text])[0] # แปลงคำถามเป็น Vector

    search_results = my_vector_db.search(query_embedding, top_k=2)

    print("\
ผลลัพธ์การค้นหาสำหรับคำถาม:", query_text)
    for result in search_results:
        print(f"- ไฟล์: {result['metadata']['filename']}, คะแนนความคล้าย: {result['score']:.4f}")
        print(f"  เนื้อหาบางส่วน: {result['metadata']['text_sample']}")

    # ตัวอย่างผลลัพธ์ (อาจแตกต่างกันขึ้นอยู่กับโมเดลและข้อมูล)
    # ผลลัพธ์การค้นหาสำหรับคำถาม: การเขียนโปรแกรมที่ปลอดภัย
    # - ไฟล์: doc1.txt, คะแนนความคล้าย: 0.7890
    #   เนื้อหาบางส่วน: บทความนี้พูดถึงการเขียนโปรแกรม Python สำหรับจัดกา...
    # - ไฟล์: doc2.txt, คะแนนความคล้าย: 0.3540
    #   เนื้อหาบางส่วน: การค้นหาข้อมูลด้วย AI และ Vector Database กำลังเป...

เห็นไหมครับ ไม่ยากเลยใช่ไหม แค่นี้เราก็ได้ระบบค้นหาข้อมูลแบบ Semantic Search ง่ายๆ ด้วย Python CLI แล้วนะครับเพื่อนๆ ลองเอาไปปรับใช้กับโปรเจกต์ ของตัวเองได้เลยนะ ผมว่ามันมีประโยชน์มากๆ เลยแหละครับ ลองโหลดโค้ดไปเล่นดูนะครับ ถ้าติดตรงไหน ก็ถามมาได้เลยนะ

แหล่งที่มา

Read more

คิวบา: มนต์เสน่ห์บนเส้นทางแห่งความท้าทาย – วิกฤตพลังงานและแรงกดดันจากสหรัฐฯ

คิวบา: มนต์เสน่ห์บนเส้นทางแห่งความท้าทาย – วิกฤตพลังงานและแรงกดดันจากสหรัฐฯ

เจาะลึกสถานการณ์ล่าสุดของคิวบา ทั้งวิกฤตไฟฟ้าดับครั้งใหญ่จากปัญหาพลังงาน และแรงกดดันจากสหรัฐฯ ภายใต้การนำของทรัมป์ อนาคตของเกาะปฏิวัติแห่งนี้จะเป็นอย่างไร?

By ทีมงาน devdog
ละครไทย: ถอดรหัสเสน่ห์ "พลอยน้ำเพชร" และปรากฏการณ์บันเทิงที่ไม่เคยจางหาย

ละครไทย: ถอดรหัสเสน่ห์ "พลอยน้ำเพชร" และปรากฏการณ์บันเทิงที่ไม่เคยจางหาย

สำรวจความเข้มข้นของละคร "พลอยน้ำเพชร" จากช่องวัน 31 พร้อมเจาะลึกตอนที่ 17-20 และเสน่ห์ของละครไทยที่ครองใจผู้ชมทั่วโลก

By ทีมงาน devdog
ชนนพัฒฐ์ นาคสั้ว: สส.สงขลา กับประเด็นร้อนคดีเว็บพนันออนไลน์ที่ DSI กำลังจับตา

ชนนพัฒฐ์ นาคสั้ว: สส.สงขลา กับประเด็นร้อนคดีเว็บพนันออนไลน์ที่ DSI กำลังจับตา

เจาะลึกประเด็นร้อน ชนนพัฒฐ์ นาคสั้ว สส.สงขลา พรรคกล้าธรรม กับกระแสข่าวพาดพิงถึงเครือข่ายเว็บพนันออนไลน์ที่ DSI กำลังสอบสวน เปิดความท้าทายต่อบทบาทผู้แทนราษฎร

By ทีมงาน devdog
เจาะลึก "ณัฐธิดา เล็กอุดากร" หลานเนวินชิดชอบ สส. อายุน้อยสุด ผู้พร้อมสร้างอนาคตใหม่ให้บุรีรัมย์

เจาะลึก "ณัฐธิดา เล็กอุดากร" หลานเนวินชิดชอบ สส. อายุน้อยสุด ผู้พร้อมสร้างอนาคตใหม่ให้บุรีรัมย์

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

By ทีมงาน devdog