RAG چندوجهی با CLIP و GPT-4 Vision

RAG چندوجهی با CLIP و GPT-4 Vision

vision, embedding, rag
preview

RAG چندوجهی با CLIP Embeddings و GPT-4 Vision #

استفاده از سیستم‌های RAG چندوجهی با افزودن حالت‌های اضافی به RAG های ساده‌ی مبتنی بر متن٬ قابلیت‌ LLMها در پاسخ‌دهی به سوالات را با ارائه زمینه اضافی و پایه‌گذاری داده‌های متنی برای درک بهتر، بهبود می‌بخشد.

با اتخاذ رویکرد ارایه شده در پست ساخت اپلیکیشن تطبیق لباس، ما تصاویر را برای جستجوی شباهت میان آنها امبدینگ می‌کنیم و از فرآیند از دست دادن اطلاعات در کپشن‌نویسی متنی جلوگیری می‌کنیم تا دقت بازیابی را افزایش دهیم.

استفاده از CLIP-based embeddings همچنین امکان fine-tune با داده‌های خاص یا به‌روزرسانی با تصاویر دیده‌نشده را نیز فراهم می‌کند.

این تکنیک از طریق جستجو در یک پایگاه دانش سازمانی با تصاویر فنی ارائه‌شده توسط کاربر برای ارائه اطلاعات مرتبط نشان داده می‌شود.

ابتدا پکیج های مربوطه را نصب کنید.

1#installations
2%pip install clip
3%pip install torch
4%pip install pillow
5%pip install faiss-cpu
6%pip install numpy
7%pip install git+https://github.com/openai/CLIP.git
8%pip install openai

سپس تمام بسته‌های مورد نیاز را وارد کنید.

برای اجرای کدهای زیر ابتدا باید یک کلید API را از طریق پنل کاربری گیلاس تولید کنید. برای این کار ابتدا یک حساب کاربری جدید بسازید یا اگر صاحب حساب کاربری هستید وارد پنل کاربری خود شوید. سپس، به صفحه کلید API بروید و با کلیک روی دکمه “ساخت کلید API” یک کلید جدید برای دسترسی به Gilas API بسازید.
 1# model imports
 2import faiss
 3import json
 4import torch
 5from openai import OpenAI
 6import torch.nn as nn
 7from torch.utils.data import DataLoader
 8import clip
 9
10# helper imports
11from tqdm import tqdm
12import json
13import os
14import numpy as np
15import pickle
16from typing import List, Union, Tuple
17
18# visualisation imports
19from PIL import Image
20import matplotlib.pyplot as plt
21import base64
22
23client = OpenAI(
24    api_key=os.environ.get(("GILAS_API_KEY", "<کلید API خود را اینجا بسازید https://dashboard.gilas.io/apiKey>")), 
25    base_url="https://api.gilas.io/v1/" # Gilas APIs
26)

حالا بیایید مدل CLIP را بارگذاری کنیم.

1#load model on device. The device you are running inference/training on is either a CPU or GPU if you have.
2device = "cpu"
3model, preprocess = clip.load("ViT-B/32",device=device)

حال آماده هستیم تا:

  1. پایگاه داده‌ی امبدینگ تصاویر را ایجاد می‌کنیم
  2. یک پرس و جو به مدل vision را آماده می‌کنیم
  3. جستجوی معنایی را انجام می‌دهیم
  4. پرس و جو کاربر را به تصویر ارسال می‌کنیم

ایجاد پایگاه داده‌ی امبدینگ تصویر #

در این مرحله پایگاه دانش امبدینگ تصویر خود را از یک دایرکتوری از تصاویر ایجاد خواهیم کرد. ما ازین پایگاه دانش برای ارایه اطلاعات در مورد تصاویری که کاربر آپلود می‌کند استفاده می‌کنیم.

ما همچنین یک فایل description.json داریم که برای هر تصویر در پایگاه دانش ما یک ورودی دارد. این فایل دارای دو کلید است: ‘image_path’ و ‘description’ که هر تصویر را به یک توضیح مفید از آن تصویر برای کمک به پاسخ‌دهی به سوال کاربر نگاشت می‌کند.

تابع زیر برای دریافت تمام تصاویر موجود در یک دایرکتوری خاص است.

 1def get_image_paths(directory: str, number: int = None) -> List[str]:
 2    image_paths = []
 3    count = 0
 4    for filename in os.listdir(directory):
 5        if filename.endswith('.jpeg'):
 6            image_paths.append(os.path.join(directory, filename))
 7            if number is not None and count == number:
 8                return [image_paths[-1]]
 9            count += 1
10    return image_paths
11direc = 'image_database/'
12image_paths = get_image_paths(direc)

در مرحله بعد، یک تابع برای دریافت امبدینگ‌های تصویر از مدل CLIP می‌نویسیم. ابتدا تصویر را با استفاده از تابع preprocess که قبلاً دریافت کردیم، پیش‌پردازش می‌کنیم. این تابع چندین کار از جمله تغییر اندازه، نرمال‌سازی، تنظیم کانال رنگ و غیره انجام می‌دهد تا اطمینان حاصل کند که ورودی به مدل CLIP از فرمت و ابعاد صحیح برخوردار است،

سپس این تصاویر پیش‌پردازش شده را با هم ترکیب می‌کنیم تا بتوانیم آنها را به مدل به صورت یکجا و نه در یک حلقه ارسال کنیم. و در نهایت خروجی مدل که یک آرایه از امبدینگ‌ها است را برمی‌گردانیم.

1def get_features_from_image_path(image_paths):
2  images = [preprocess(Image.open(image_path).convert("RGB")) for image_path in image_paths]
3  image_input = torch.tensor(np.stack(images))
4  with torch.no_grad():
5    image_features = model.encode_image(image_input).float()
6  return image_features
7image_features = get_features_from_image_path(image_paths)

اکنون می‌توانیم پایگاه داده امبدینگ خود را ایجاد کنیم.

1index = faiss.IndexFlatIP(image_features.shape[1])
2index.add(image_features)

و همچنین json خود را برای نگاشت تصویر-توضیح ایمپورت کرده و یک لیست از jsonها ایجاد می‌کنیم. همچنین یک تابع کمکی برای جستجو در این لیست برای یک تصویر خاص ایجاد می‌کنیم تا بتوانیم توضیح آن تصویر را بدست آوریم.

 1data = []
 2image_path = 'train1.jpeg'
 3with open('description.json', 'r') as file:
 4    for line in file:
 5        data.append(json.loads(line))
 6def find_entry(data, key, value):
 7    for entry in data:
 8        if entry.get(key) == value:
 9            return entry
10    return None

حال بیایید یک تصویر نمونه را نمایش دهیم که توسط کاربر آپلود شده است. این یک قطعه فناوری است که در CES 2024 رونمایی شد. این دستگاه DELTA Pro Ultra Whole House Battery Generator نام دارد.

1im = Image.open(image_path)
2plt.imshow(im)
3plt.show()
training image

پرس و جوی مدل #

حالا بیایید ببینیم GPT-4 Vision (که قبلاً این فناوری را ندیده است) آن را چگونه برچسب‌گذاری می‌کند.

ابتدا باید یک تابع برای کدگذاری تصویرمان به صورت base64 بنویسیم زیرا این فرمتی است که مدل انتظار دریافت آن را دارد. سپس یک تابع image_query ایجاد می‌کنیم تا به ما اجازه دهد با ورودی تصویر به LLM پرس و جو کنیم.

 1def encode_image(image_path):
 2    with open(image_path, 'rb') as image_file:
 3        encoded_image = base64.b64encode(image_file.read())
 4        return encoded_image.decode('utf-8')
 5
 6def image_query(query, image_path):
 7    response = client.chat.completions.create(
 8        model='gpt-4-vision-preview',
 9        messages=[
10            {
11            "role": "user",
12            "content": [
13                {
14                "type": "text",
15                "text": query,
16                },
17                {
18                "type": "image_url",
19                "image_url": {
20                    "url": f"data:image/jpeg;base64,{encode_image(image_path)}",
21                },
22                }
23            ],
24            }
25        ],
26        max_tokens=300,
27    )
28    # Extract relevant features from the response
29    return response.choices[0].message.content
30image_query('Write a short label of what is show in this image?', image_path)
'Autonomous Delivery Robot'

همانطور که می‌بینیم، مدل تلاش می‌کند از اطلاعاتی که آموزش دیده است استفاده کند اما به دلیل ندیدن چیزی مشابه به تصویر بالا در داده‌های آموزشی خود شی داخل تصویر را اشتباه تشخصی می‌دهد. این به این دلیل است که تصویر مبهم است و استنتاج آن دشوار است.

جستجوی معنایی #

حالا بیایید جستجوی شباهت را برای یافتن دو تصویر مشابه در پایگاه دانش خود انجام دهیم. این کار را با دریافت امبدینگ‌های مسیر تصویر ورودی کاربر، بازیابی ایندکس‌ها و فاصله‌های تصاویر مشابه در پایگاه داده خود انجام می‌دهیم. فاصله میان امبدینگ‌ها شاخص ما برای شباهت خواهد بود و فاصله کمتر به معنای شباهت بیشتر است. سپس نتایج جستجو را بر اساس فاصله به ترتیب نزولی مرتب می‌کنیم.

1image_search_embedding = get_features_from_image_path([image_path])
2distances, indices = index.search(image_search_embedding.reshape(1, -1), 2) #2 signifies the number of topmost similar images to bring back
3distances = distances[0]
4indices = indices[0]
5indices_distances = list(zip(indices, distances))
6indices_distances.sort(key=lambda x: x[1], reverse=True)

بیایید ببینیم چه چیزی برگردانده است (این تصاویر را به ترتیب شباهت نمایش می‌دهیم):

1#display similar images
2for idx, distance in indices_distances:
3    print(idx)
4    path = get_image_paths(direc, idx)[0]
5    im = Image.open(path)
6    plt.imshow(im)
7    plt.show()
training image
training image

می‌بینیم که دو تصویر را برگردانده است که شامل DELTA Pro Ultra Whole House Battery Generator هستند. در یکی از تصاویر نیز پس‌زمینه‌ای وجود دارد که ممکن است مدل را منحرف کند اما مدل موفق به یافتن تصویر صحیح شده است.

پرس و جو کاربر #

حالا با انتخاب شبیه‌ترین تصویر می‌خواهیم آنرا و همراه توضیحاتی که از آن تصویر در پایگاه داده خود داریم در کنار پرس و جو کاربر از آن تصویر به مدل ارسال کنیم تا کاربر بتواند در مورد آن تصویر سوالاتی را از مدل بپرسد. اینجا جایی است که توانایی vision مدل حایز اهمیت می‌شود، جایی که می‌توانید سوالات عمومی که مدل به طور خاص برای آنها آموزش ندیده است را از مدل بپرسید تا جواب‌هایی با دقت بالا دریافت کنید.

در مثال زیر، ما در مورد ظرفیت آیتم مورد نظر سوال خواهیم کرد.

 1similar_path = get_image_paths(direc, indices_distances[0][0])[0]
 2element = find_entry(data, 'image_path', similar_path)
 3
 4user_query = 'What is the capacity of this item?'
 5prompt = f"""
 6Below is a user query, I want you to answer the query using the description and image provided.
 7
 8user query:
 9{user_query}
10
11description:
12{element['description']}
13"""
14image_query(prompt, similar_path)
'The portable home battery DELTA Pro has a base capacity of 3.6kWh. This capacity can be expanded up to 25kWh with additional batteries. The image showcases the DELTA Pro, which has an impressive 3600W power capacity for AC output as well.'

و می‌بینیم که مدل قادر به پاسخ دادن به سوال است هرچند که هیچ اطلاعاتی در مورد این محصول خاص نداشته و تنها با اتکا به اطلاعات بازیابی شده از پایگاه داده امبدینگ و توضیحات مرتبط با تصایر قادر به پاسخگویی به سوالات است. این تنها با تطبیق مستقیم تصاویر و از آنجا جمع‌آوری توضیحات مرتبط به عنوان زمینه ممکن بود.

نتیجه‌گیری #

در این پست، ما نحوه استفاده از مدل CLIP، یک مثال از ایجاد پایگاه داده امبدینگ تصویر با استفاده از مدل CLIP، انجام جستجوی معنایی و در نهایت ارائه یک پرس و جو کاربر برای پاسخ به سوال را ارایه دادیم.