บทความนี้ให้คำแนะนำเชิงลึกเกี่ยวกับวิธีการสร้างระบบ RAG หลายโหมดโดยใช้ Milvus และวิธีการเปิดความเป็นไปได้ต่างๆ สำหรับระบบ AI
การถูกจำกัดให้ใช้รูปแบบข้อมูลเพียงรูปแบบเดียวนั้นไม่เพียงพออีกต่อไป เนื่องจากธุรกิจต่างๆ พึ่งพาข้อมูลมากขึ้นในการตัดสินใจที่สำคัญ จึงจำเป็นต้องมีความสามารถในการเปรียบเทียบข้อมูลในรูปแบบที่แตกต่างกัน โชคดีที่ระบบ AI ดั้งเดิมที่ถูกจำกัดให้ใช้ประเภทข้อมูลเพียงประเภทเดียวได้หลีกทางให้กับระบบมัลติโหมดที่สามารถเข้าใจและประมวลผลข้อมูลที่ซับซ้อนได้
ระบบการค้นหาหลายโหมดและ การเรียกค้นหลายโหมดด้วยการสร้างเสริม (RAG) แสดงให้เห็นถึงความก้าวหน้าอย่างมากในสาขานี้เมื่อไม่นานนี้ ระบบเหล่านี้ประมวลผลข้อมูลหลายประเภท รวมทั้งข้อความ รูปภาพ และเสียง เพื่อให้เกิดการตอบสนองที่คำนึงถึงบริบท
ในโพสต์บล็อกนี้ เราจะพูดถึงวิธีที่นักพัฒนาสามารถสร้างระบบ RAG แบบมัลติโมดัลของตนเองโดยใช้ Milvus นอกจากนี้ เราจะแนะนำคุณเกี่ยวกับการสร้างระบบที่สามารถจัดการข้อมูลข้อความและรูปภาพ โดยเฉพาะอย่างยิ่ง การค้นหาความคล้ายคลึง และใช้ประโยชน์จากโมเดลภาษาเพื่อปรับแต่งเอาต์พุต ดังนั้น มาเริ่มกันเลย
ฐานข้อมูลเวกเตอร์เป็นฐานข้อมูลประเภทพิเศษที่ใช้สำหรับจัดเก็บ จัดทำดัชนี และเรียกค้นข้อมูลแบบเวกเตอร์ ซึ่งเป็นการแสดงข้อมูลทางคณิตศาสตร์ที่ช่วยให้คุณเปรียบเทียบข้อมูลได้ไม่เพียงแค่เพื่อความเท่าเทียมกันเท่านั้น แต่ยังเพื่อความคล้ายคลึงกันทางความหมายอีกด้วย
Milvus ช่วยให้นักพัฒนาสามารถจัดเตรียมโซลูชันที่ยืดหยุ่นสำหรับการจัดการและสอบถามข้อมูลเวกเตอร์ขนาดใหญ่ได้ ประสิทธิภาพดังกล่าวทำให้ Milvus เป็นตัวเลือกที่เหมาะสำหรับนักพัฒนาที่สร้างแอปพลิเคชันโดยใช้โมเดลการเรียนรู้เชิงลึก เช่น การค้นหาแบบเพิ่มขยาย (RAG) การค้นหาแบบหลายโหมด กลไกแนะนำ และการตรวจจับความผิดปกติ
Milvus นำเสนอตัวเลือกการใช้งานที่หลากหลายเพื่อให้ตรงกับความต้องการของนักพัฒนา
ก่อนที่จะสร้างระบบ สิ่งที่สำคัญคือต้องทำความเข้าใจ RAG แบบดั้งเดิมที่ใช้ข้อความและวิวัฒนาการไปเป็น RAG หลายโหมด
Retrieval Augmented Generation (RAG) เป็นวิธีการดึงข้อมูลเชิงบริบทจากแหล่งภายนอกและสร้างผลลัพธ์ที่แม่นยำยิ่งขึ้นจากโมเดลภาษาขนาดใหญ่ (LLM) RAG แบบดั้งเดิมเป็นกลยุทธ์ที่มีประสิทธิภาพสูงในการปรับปรุงผลลัพธ์ LLM แต่ยังคงจำกัดอยู่แค่ข้อมูลข้อความเท่านั้น ในแอปพลิเคชันในโลกแห่งความเป็นจริงหลายๆ รายการ ข้อมูลมีขอบเขตกว้างไกลเกินกว่าข้อความ โดยการรวมรูปภาพ แผนภูมิ และรูปแบบอื่นๆ เข้าด้วยกันจะให้บริบทที่สำคัญ
RAG หลายโหมด จะแก้ไขข้อจำกัดข้างต้นโดยเปิดใช้งานประเภทข้อมูลที่แตกต่างกัน ทำให้มีบริบทที่ดีกว่าสำหรับ LLM
กล่าวอย่างง่ายๆ ในระบบ RAG หลายโหมด ส่วนประกอบการค้นคืนจะค้นหาข้อมูลที่เกี่ยวข้องจากโหมดข้อมูลที่แตกต่างกัน และส่วนประกอบการสร้างจะสร้างผลลัพธ์ที่แม่นยำยิ่งขึ้นตามข้อมูลที่ค้นคืนมา
การฝังเวกเตอร์ และ การค้นหาความคล้ายคลึงกัน เป็นแนวคิดพื้นฐานสองประการของ RAG แบบหลายโหมด มาทำความเข้าใจทั้งสองแนวคิดกัน
ตามที่ได้กล่าวไว้ การฝังเวกเตอร์เป็นการแสดงข้อมูลทางคณิตศาสตร์/ตัวเลข เครื่องจักรใช้การแสดงนี้เพื่อทำความเข้าใจความหมายเชิงความหมายของประเภทข้อมูลต่างๆ เช่น ข้อความ รูปภาพ และเสียง
เมื่อใช้การประมวลผลภาษาธรรมชาติ (NLP) ชิ้นส่วนของเอกสารจะถูกแปลงเป็นเวกเตอร์ และคำที่มีความหมายคล้ายกันจะถูกแมปไปยังจุดใกล้เคียงในพื้นที่เวกเตอร์ เช่นเดียวกับรูปภาพ ซึ่งเอ็มเบ็ดดิ้งแสดงถึงคุณลักษณะเชิงความหมาย สิ่งนี้ช่วยให้เราเข้าใจเมตริกต่างๆ เช่น สี พื้นผิว และรูปร่างของวัตถุในรูปแบบตัวเลข
เป้าหมายหลักของการใช้การฝังเวกเตอร์คือการช่วยรักษาความสัมพันธ์และความคล้ายคลึงกันระหว่างข้อมูลชิ้นต่างๆ
การค้นหาความคล้ายคลึงใช้เพื่อค้นหาและระบุตำแหน่งข้อมูลในชุดข้อมูลที่กำหนด ในบริบทของการฝังเวกเตอร์ การค้นหาความคล้ายคลึงจะค้นหาเวกเตอร์ในชุดข้อมูลที่กำหนดซึ่งใกล้เคียงกับเวกเตอร์แบบสอบถามมากที่สุด
ต่อไปนี้เป็นวิธีการบางประการที่ใช้กันทั่วไปในการวัดความคล้ายคลึงกันระหว่างเวกเตอร์:
การเลือกวิธีการวัดความคล้ายคลึงกันโดยปกติจะขึ้นอยู่กับข้อมูลเฉพาะแอปพลิเคชันและวิธีที่นักพัฒนาเข้าถึงปัญหา
เมื่อทำการค้นหาความคล้ายคลึงกันในชุดข้อมูลขนาดใหญ่ พลังในการคำนวณและทรัพยากรที่จำเป็นจะสูงมาก นี่คือจุดที่อัลกอริทึมเพื่อนบ้านใกล้เคียงที่สุด (ANN) เข้ามามีบทบาท อัลกอริทึม ANN ใช้เพื่อแลกเปลี่ยนความแม่นยำเพียงเล็กน้อยหรือปริมาณเล็กน้อยเพื่ออัปเกรดความเร็วอย่างมีนัยสำคัญ ซึ่งทำให้เป็นตัวเลือกที่เหมาะสมสำหรับแอปพลิเคชันขนาดใหญ่
Milvus ยังใช้อัลกอริทึม ANN ขั้นสูง รวมถึง HNSW และ DiskANN เพื่อค้นหาความคล้ายคลึงอย่างมีประสิทธิภาพบนชุดข้อมูลฝังเวกเตอร์ขนาดใหญ่ ช่วยให้นักพัฒนาสามารถค้นหาจุดข้อมูลที่เกี่ยวข้องได้อย่างรวดเร็ว นอกจากนี้ Milvus ยังรองรับอัลกอริทึมการจัดทำดัชนีอื่นๆ เช่น HSNW, IVF, CAGRA เป็นต้น ทำให้เป็นโซลูชันการค้นหาเวกเตอร์ที่มีประสิทธิภาพมากขึ้น
ตอนนี้เราได้เรียนรู้แนวคิดแล้ว ถึงเวลาสร้างระบบ RAG หลายโหมดโดยใช้ Milvus สำหรับตัวอย่างนี้ เราจะใช้ Milvus Lite (เวอร์ชันน้ำหนักเบาของ Milvus เหมาะสำหรับการทดลองและการสร้างต้นแบบ) สำหรับการจัดเก็บและเรียกค้นเวกเตอร์ BGE สำหรับการประมวลผลและการฝังภาพที่แม่นยำ และ GPT-4o สำหรับการจัดอันดับผลลัพธ์ขั้นสูง
ขั้นแรก คุณจะต้องมีอินสแตนซ์ Milvus เพื่อจัดเก็บข้อมูลของคุณ คุณสามารถตั้งค่า Milvus Lite โดยใช้ pip เรียกใช้อินสแตนซ์ภายในเครื่องโดยใช้ Docker หรือสมัครบัญชี Milvus ที่โฮสต์ฟรีผ่าน Zilliz Cloud
ประการที่สอง คุณต้องมีปริญญา LLM สำหรับท่อ RAG ของคุณ ดังนั้นไปที่
จากนั้นสร้างไดเร็กทอรีใหม่และ Python
สำหรับบทช่วยสอนนี้ คุณจะต้องติดตั้งด้วย
pip install -U pymilvus
pip install --upgrade pymilvus openai datasets opencv-python timm einops ftfy peft tqdm git clone https://github.com/FlagOpen/FlagEmbedding.git pip install -e FlagEmbedding
คำสั่งต่อไปนี้จะดาวน์โหลดข้อมูลตัวอย่างและแตกไฟล์ไปยังโฟลเดอร์ในเครื่อง “./images_folder” ซึ่งประกอบด้วย:
wget https://github.com/milvus-io/bootcamp/releases/download/data/amazon_reviews_2023_subset.tar.gz tar -xzf amazon_reviews_2023_subset.tar.gz
เราจะใช้โมเดล BGE แบบมองเห็นได้ “bge-visualized-base-en-v1.5” เพื่อสร้างการฝังภาพสำหรับทั้งรูปภาพและข้อความ
ดาวน์โหลดน้ำหนักจาก HuggingFace ตอนนี้
wget https://huggingface.co/BAAI/bge-visualized/resolve/main/Visualized_base_en_v1.5.pth
จากนั้นมาสร้างตัวเข้ารหัสกัน
import torch from visual_bge.modeling import Visualized_BGE class Encoder: def __init__(self, model_name: str, model_path: str): self.model = Visualized_BGE(model_name_bge=model_name, model_weight=model_path) self.model.eval() def encode_query(self, image_path: str, text: str) -> list[float]: with torch.no_grad(): query_emb = self.model.encode(image=image_path, text=text) return query_emb.tolist()[0] def encode_image(self, image_path: str) -> list[float]: with torch.no_grad(): query_emb = self.model.encode(image=image_path) return query_emb.tolist()[0] model_name = "BAAI/bge-base-en-v1.5" model_path = "./Visualized_base_en_v1.5.pth" # Change to your own value if using a different model path encoder = Encoder(model_name, model_path)
หัวข้อนี้จะแนะนำคุณเกี่ยวกับวิธีการโหลดภาพตัวอย่างลงในฐานข้อมูลของเราพร้อมกับการฝังที่สอดคล้องกัน
สร้างการฝังตัว
ขั้นแรก เราต้องสร้างการฝังสำหรับรูปภาพทั้งหมดในชุดข้อมูล
โหลดรูปภาพทั้งหมดจากไดเร็กทอรีข้อมูลและแปลงให้เป็นไฟล์ฝัง
import os from tqdm import tqdm from glob import glob data_dir = ( "./images_folder" # Change to your own value if using a different data directory ) image_list = glob( os.path.join(data_dir, "images", "*.jpg") ) # We will only use images ending with ".jpg" image_dict = {} for image_path in tqdm(image_list, desc="Generating image embeddings: "): try: image_dict[image_path] = encoder.encode_image(image_path) except Exception as e: print(f"Failed to generate embedding for {image_path}. Skipped.") continue print("Number of encoded images:", len(image_dict))
ในส่วนนี้ เราจะค้นหารูปภาพที่เกี่ยวข้องโดยใช้แบบสอบถามหลายโหมดก่อน จากนั้นจึงใช้บริการ LLM เพื่อจัดอันดับผลลัพธ์ที่เรียกค้นใหม่ และค้นหาผลลัพธ์ที่ดีที่สุดพร้อมคำอธิบาย
การค้นหาแบบหลายโหมด
ตอนนี้เราพร้อมที่จะทำการค้นหาหลายโหมดขั้นสูงด้วยคำค้นหาที่ประกอบด้วยคำแนะนำที่เป็นรูปภาพและข้อความ
query_image = os.path.join( data_dir, "leopard.jpg" ) # Change to your own query image path query_text = "phone case with this image theme" query_vec = encoder.encode_query(image_path=query_image, text=query_text) search_results = milvus_client.search( collection_name=collection_name, data=[query_vec], output_fields=["image_path"], limit=9, # Max number of search results to return search_params={"metric_type": "COSINE", "params": {}}, # Search parameters )[0] retrieved_images = [hit.get("entity").get("image_path") for hit in search_results] print(retrieved_images)
ผลลัพธ์แสดงดังด้านล่างนี้:
['./images_folder/images/518Gj1WQ-RL._AC_.jpg', './images_folder/images/41n00AOfWhL._AC_.jpg'
จัดอันดับผลลัพธ์ใหม่ด้วย GPT-4o
ตอนนี้เราจะใช้ GPT-4o เพื่อจัดอันดับรูปภาพที่ดึงมาและค้นหาผลลัพธ์ที่ตรงกันมากที่สุด LLM จะอธิบายด้วยว่าทำไมจึงจัดอันดับแบบนั้น
1. สร้างมุมมองแบบพาโนรามา
import numpy as np import cv2 img_height = 300 img_width = 300 row_count = 3 def create_panoramic_view(query_image_path: str, retrieved_images: list) -> np.ndarray: """ creates a 5x5 panoramic view image from a list of images args: images: list of images to be combined returns: np.ndarray: the panoramic view image """ panoramic_width = img_width * row_count panoramic_height = img_height * row_count panoramic_image = np.full( (panoramic_height, panoramic_width, 3), 255, dtype=np.uint8 ) # create and resize the query image with a blue border query_image_null = np.full((panoramic_height, img_width, 3), 255, dtype=np.uint8) query_image = Image.open(query_image_path).convert("RGB") query_array = np.array(query_image)[:, :, ::-1] resized_image = cv2.resize(query_array, (img_width, img_height)) border_size = 10 blue = (255, 0, 0) # blue color in BGR bordered_query_image = cv2.copyMakeBorder( resized_image, border_size, border_size, border_size, border_size, cv2.BORDER_CONSTANT, value=blue, ) query_image_null[img_height * 2 : img_height * 3, 0:img_width] = cv2.resize( bordered_query_image, (img_width, img_height) ) # add text "query" below the query image text = "query" font_scale = 1 font_thickness = 2 text_org = (10, img_height * 3 + 30) cv2.putText( query_image_null, text, text_org, cv2.FONT_HERSHEY_SIMPLEX, font_scale, blue, font_thickness, cv2.LINE_AA, ) # combine the rest of the images into the panoramic view retrieved_imgs = [ np.array(Image.open(img).convert("RGB"))[:, :, ::-1] for img in retrieved_images ] for i, image in enumerate(retrieved_imgs): image = cv2.resize(image, (img_width - 4, img_height - 4)) row = i // row_count col = i % row_count start_row = row * img_height start_col = col * img_width border_size = 2 bordered_image = cv2.copyMakeBorder( image, border_size, border_size, border_size, border_size, cv2.BORDER_CONSTANT, value=(0, 0, 0), ) panoramic_image[ start_row : start_row + img_height, start_col : start_col + img_width ] = bordered_image # add red index numbers to each image text = str(i) org = (start_col + 50, start_row + 30) (font_width, font_height), baseline = cv2.getTextSize( text, cv2.FONT_HERSHEY_SIMPLEX, 1, 2 ) top_left = (org[0] - 48, start_row + 2) bottom_right = (org[0] - 48 + font_width + 5, org[1] + baseline + 5) cv2.rectangle( panoramic_image, top_left, bottom_right, (255, 255, 255), cv2.FILLED ) cv2.putText( panoramic_image, text, (start_col + 10, start_row + 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA, ) # combine the query image with the panoramic view panoramic_image = np.hstack([query_image_null, panoramic_image]) return panoramic_image
2. รวมรูปภาพสอบถามและรูปภาพที่เรียกค้นได้พร้อมดัชนีในมุมมองแบบพาโนรามา
from PIL import Image combined_image_path = os.path.join(data_dir, "combined_image.jpg") panoramic_image = create_panoramic_view(query_image, retrieved_images) cv2.imwrite(combined_image_path, panoramic_image) combined_image = Image.open(combined_image_path) show_combined_image = combined_image.resize((300, 300)) show_combined_image.show()
3. จัดลำดับผลลัพธ์ใหม่และให้คำอธิบาย
เราจะส่งภาพรวมทั้งหมดไปยังบริการ LLM แบบหลายโหมดพร้อมคำแนะนำที่เหมาะสมเพื่อจัดอันดับผลลัพธ์ที่เรียกค้นพร้อมคำอธิบาย หมายเหตุ: หากต้องการเปิดใช้งาน GPT-4o เป็น LLM คุณต้องเตรียม
import requests import base64 openai_api_key = "sk-***" # Change to your OpenAI API Key def generate_ranking_explanation( combined_image_path: str, caption: str, infos: dict = None ) -> tuple[list[int], str]: with open(combined_image_path, "rb") as image_file: base64_image = base64.b64encode(image_file.read()).decode("utf-8") information = ( "You are responsible for ranking results for a Composed Image Retrieval. " "The user retrieves an image with an 'instruction' indicating their retrieval intent. " "For example, if the user queries a red car with the instruction 'change this car to blue,' a similar type of car in blue would be ranked higher in the results. " "Now you would receive instruction and query image with blue border. Every item has its red index number in its top left. Do not misunderstand it. " f"User instruction: {caption} \n\n" ) # add additional information for each image if infos: for i, info in enumerate(infos["product"]): information += f"{i}. {info}\n" information += ( "Provide a new ranked list of indices from most suitable to least suitable, followed by an explanation for the top 1 most suitable item only. " "The format of the response has to be 'Ranked list: []' with the indices in brackets as integers, followed by 'Reasons:' plus the explanation why this most fit user's query intent." ) headers = { "Content-Type": "application/json", "Authorization": f"Bearer {openai_api_key}", } payload = { "model": "gpt-4o", "messages": [ { "role": "user", "content": [ {"type": "text", "text": information}, { "type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}, }, ], } ], "max_tokens": 300, } response = requests.post( "https://api.openai.com/v1/chat/completions", headers=headers, json=payload ) result = response.json()["choices"][0]["message"]["content"] # parse the ranked indices from the response start_idx = result.find("[") end_idx = result.find("]") ranked_indices_str = result[start_idx + 1 : end_idx].split(",") ranked_indices = [int(index.strip()) for index in ranked_indices_str] # extract explanation explanation = result[end_idx + 1 :].strip() return ranked_indices, explanation
รับดัชนีภาพหลังจากการจัดอันดับและเหตุผลสำหรับผลลัพธ์ที่ดีที่สุด:
ranked_indices, explanation = generate_ranking_explanation( combined_image_path, query_text )
4. แสดงผลลัพธ์ที่ดีที่สุดพร้อมคำอธิบาย
print(explanation) best_index = ranked_indices[0] best_img = Image.open(retrieved_images[best_index]) best_img = best_img.resize((150, 150)) best_img.show()
ผลลัพธ์:
Reasons: The most suitable item for the user's query intent is index 6 because the instruction specifies a phone case with the theme of the image, which is a leopard. The phone case with index 6 has a thematic design resembling the leopard pattern, making it the closest match to the user's request for a phone case with the image theme.
ลองดูโค้ดทั้งหมดได้ใน สมุดบันทึกนี้ หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีเริ่มการสาธิตออนไลน์ด้วยบทช่วยสอนนี้ โปรดดูที่
ในโพสต์บล็อกนี้ เราจะพูดถึงการสร้างระบบ RAG หลายโหมดโดยใช้
โซลูชัน RAG แบบหลายโหมดเปิดโอกาสให้ระบบ AI สามารถเข้าใจและประมวลผลข้อมูลในรูปแบบต่างๆ ได้อย่างง่ายดาย โซลูชันทั่วไปบางประการได้แก่ เครื่องมือค้นหารูปภาพที่ได้รับการปรับปรุง ผลลัพธ์ตามบริบทที่ดีขึ้น และอื่นๆ อีกมากมาย