這篇文章是與 Rafael Guedes 共同撰寫的。
簡介
傳統模型只能處理單一類型的數據,例如文本、圖像或表格數據。多模態(Multimodality)是人工智慧(AI)研究界的一個熱門概念,指的是模型能夠同時從多種數據類型中學習。這項新技術(雖然不算全新,但在過去幾個月中有了顯著改善)有許多潛在的應用,將改變許多產品的用戶體驗。
一個好的例子是未來搜索引擎的運作方式,使用者可以使用文本、圖像、音頻等多種方式輸入查詢。另一個例子是改善AI驅動的客戶支持系統,支持語音和文本輸入。在電子商務中,這些技術正在通過允許用戶使用圖像和文本進行搜索來增強產品發現。我們將以後者作為本文的案例研究。
前沿的AI研究實驗室每個月都在推出幾個支持多模態的模型。OpenAI的CLIP和DALL-E,以及Salesforce的BLIP-2,都是結合了圖像和文本的模型。Meta的ImageBind則將多模態概念擴展到六種模態(文本、音頻、深度、熱成像、圖像和慣性測量單元)。
在本文中,我們將探討BLIP-2,解釋其架構、損失函數的運作方式以及訓練過程。我們還將展示一個實際的使用案例,結合BLIP-2和Gemini,創建一個多模態的時尚搜索代理,幫助顧客根據文本或文本和圖像提示找到最佳服裝。
如往常一樣,代碼可在我們的GitHub上獲得。
BLIP-2:一個多模態模型
BLIP-2(Bootstrapped Language-Image Pre-Training)是一個視覺-語言模型,旨在解決視覺問題回答或基於圖像和文本輸入的多模態推理等任務。正如我們將看到的,這個模型是為了解決視覺-語言領域的兩個主要挑戰而開發的:
- 通過使用凍結的預訓練視覺編碼器和大型語言模型(LLMs)來降低計算成本,與視覺和語言網絡的聯合訓練相比,顯著減少了所需的訓練資源。
- 通過引入Q-Former來改善視覺-語言對齊。Q-Former使視覺和文本嵌入更接近,從而提高推理任務的性能,並能夠執行多模態檢索。
架構
BLIP-2的架構遵循模塊化設計,整合了三個模塊:
- 視覺編碼器是一個凍結的視覺模型,例如ViT,從輸入圖像中提取視覺嵌入(然後用於下游任務)。
- 查詢變壓器(Q-Former)是這個架構的關鍵。它由一個可訓練的輕量級變壓器組成,作為視覺和語言模型之間的中介層。它負責從視覺嵌入生成上下文化的查詢,以便語言模型能夠有效處理。
- LLM是一個凍結的預訓練大型語言模型,處理精煉的視覺嵌入以生成文本描述或答案。
損失函數
BLIP-2有三個損失函數來訓練Q-Former模塊:
- 圖像-文本對比損失強調視覺和文本嵌入之間的對齊,通過最大化配對圖像-文本表示的相似性,同時推開不相似的配對。
- 圖像-文本匹配損失是一種二元分類損失,旨在讓模型學習細緻的對齊,通過預測文本描述是否與圖像匹配(正面,即目標=1)或不匹配(負面,即目標=0)。
- 基於圖像的文本生成損失是一種交叉熵損失,用於LLMs來預測序列中下一個標記的概率。Q-Former架構不允許圖像嵌入和文本標記之間的交互;因此,文本必須僅根據視覺信息生成,迫使模型提取相關的視覺特徵。
對於圖像-文本對比損失和圖像-文本匹配損失,作者使用了批內負樣本抽樣,這意味著如果我們的批次大小為512,每個圖像-文本配對有一個正樣本和511個負樣本。這種方法提高了效率,因為負樣本來自批次,無需搜索整個數據集。它還提供了更具多樣性的比較,導致更好的梯度估計和更快的收斂。
訓練過程
BLIP-2的訓練由兩個階段組成:
第一階段 – 啟動視覺-語言表示:
- 模型接收作為輸入的圖像,這些圖像通過凍結的視覺編碼器轉換為嵌入。
- 與這些圖像一起,模型接收其文本描述,這些描述也被轉換為嵌入。
- Q-Former使用圖像-文本對比損失進行訓練,確保視覺嵌入與其相應的文本嵌入緊密對齊,並遠離不匹配的文本描述。同時,圖像-文本匹配損失幫助模型發展細緻的表示,學習分類給定文本是否正確描述圖像。
第二階段 – 啟動視覺到語言的生成:
- 預訓練的語言模型被整合到架構中,以根據先前學習的表示生成文本。
- 焦點從對齊轉向文本生成,使用基於圖像的文本生成損失,這提高了模型的推理和文本生成能力。
使用BLIP-2和Gemini創建多模態時尚搜索代理
在這一部分,我們將利用BLIP-2的多模態能力,建立一個時尚助手搜索代理,能夠接收文本和/或圖像輸入並返回建議。對於代理的對話能力,我們將使用托管在Vertex AI上的Gemini 1.5 Pro,並為界面構建一個Streamlit應用。
在這個使用案例中使用的時尚數據集是根據MIT許可證授權的,可以通過以下鏈接訪問:時尚產品圖像數據集。它包含超過44,000張時尚產品的圖像。
實現這一目標的第一步是設置一個向量數據庫(Vector DB)。這使得代理能夠根據商店中可用商品的圖像嵌入以及來自輸入的文本或圖像嵌入進行向量化搜索。我們使用docker和docker-compose來幫助我們設置環境:
Docker-Compose與Postgres(數據庫)和PGVector擴展,允許向量化搜索。
services:
postgres:
container_name: container-pg
image: ankane/pgvector
hostname: localhost
ports:
- "5432:5432"
env_file:
- ./env/postgres.env
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
pgadmin:
container_name: container-pgadmin
image: dpage/pgadmin4
depends_on:
- postgres
ports:
- "5050:80"
env_file:
- ./env/pgadmin.env
restart: unless-stopped
volumes:
postgres-data:
Postgres env文件包含登錄數據庫的變量。
POSTGRES_DB=postgres
POSTGRES_USER=admin
POSTGRES_PASSWORD=root
Pgadmin env文件包含登錄UI以手動查詢數據庫的變量(可選)。
[email protected]
PGADMIN_DEFAULT_PASSWORD=root
連接env文件包含使用Langchain連接PGVector的所有組件。
DRIVER=psycopg
HOST=localhost
PORT=5432
DATABASE=postgres
USERNAME=admin
PASSWORD=root
一旦向量數據庫設置並運行(docker-compose up -d),就可以創建代理和工具來執行多模態搜索。我們構建了兩個代理來解決這個使用案例:一個用於理解用戶的請求,另一個用於提供建議:
- 分類器負責接收來自顧客的輸入消息,並提取用戶正在尋找的衣物類別,例如T恤、褲子、鞋子、運動衫或襯衫。它還將返回顧客想要的商品數量,以便我們從向量數據庫中檢索精確的數量。
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_google_vertexai import ChatVertexAI
from pydantic import BaseModel, Field
class ClassifierOutput(BaseModel):
"""
模型輸出的數據結構。
"""
category: list = Field(
description="要搜索的衣物類別列表('t-shirt', 'pants', 'shoes', 'jersey', 'shirt')。"
)
number_of_items: int = Field(description="我們應該檢索的商品數量。")
class Classifier:
"""
用於分類輸入文本的分類器類。
"""
def __init__(self, model: ChatVertexAI) -> None:
"""
通過創建鏈來初始化Chain類。
參數:
model (ChatVertexAI): LLM模型。
"""
super().__init__()
parser = PydanticOutputParser(pydantic_object=ClassifierOutput)
text_prompt = """
你是一位時尚助手專家,能夠理解顧客的需求,並從給定文本中提取顧客想要的衣物類別。
文本:
text
指示:
1. 仔細閱讀文本。
2. 提取顧客正在尋找的衣物類別,可以是:
- 如果顧客在尋找T恤,則返回't-shirt'。
- 如果顧客在尋找褲子,則返回'pants'。
- 如果顧客在尋找外套,則返回'jacket'。
- 如果顧客在尋找鞋子,則返回'shoes'。
- 如果顧客在尋找運動衫,則返回'jersey'。
- 如果顧客在尋找襯衫,則返回'shirt'。
3. 如果顧客在尋找同一類別的多個商品,返回我們應該檢索的商品數量。如果未指定但用戶要求多於1,則返回2。
4. 如果顧客在尋找多個類別,商品數量應為1。
5. 返回一個有效的JSON,包含找到的類別,鍵必須是'category',值必須是找到的類別列表,以及'number_of_items',表示我們應該檢索的商品數量。
請將輸出作為有效的JSON對象,沒有任何額外的格式,例如反引號或額外文本。確保JSON根據下面提供的架構正確結構化。
format_instructions
答案:
"""
prompt = PromptTemplate.from_template(
text_prompt, partial_variables="format_instructions": parser.get_format_instructions()
)
self.chain = prompt | model | parser
def classify(self, text: str) -> ClassifierOutput:
"""
根據文本上下文從模型獲取類別。
參數:
text (str): 用戶消息。
返回:
ClassifierOutput: 模型的答案。
"""
try:
return self.chain.invoke("text": text)
except Exception as e:
raise RuntimeError(f"調用鏈時出錯:{e}")
助手負責根據從向量數據庫檢索到的個性化建議進行回答。在這種情況下,我們還利用Gemini的多模態能力來分析檢索到的圖像並產生更好的答案。
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_google_vertexai import ChatVertexAI
from pydantic import BaseModel, Field
class AssistantOutput(BaseModel):
"""
模型輸出的數據結構。
"""
answer: str = Field(description="一個包含顧客時尚建議的字符串。")
class Assistant:
"""
提供時尚建議的助手類。
"""
def __init__(self, model: ChatVertexAI) -> None:
"""
通過創建鏈來初始化Chain類。
參數:
model (ChatVertexAI): LLM模型。
"""
super().__init__()
parser = PydanticOutputParser(pydantic_object=AssistantOutput)
text_prompt = """
你在一家時尚商店工作,是一位時尚助手專家,能夠理解顧客的需求。
根據商店中可用的商品和顧客的消息,為顧客提供時尚建議。
商品數量:number_of_items
商品圖像:
items
顧客消息:
customer_message
指示:
1. 仔細檢查提供的圖像。
2. 仔細閱讀顧客的需求。
3. 根據商品和顧客消息為顧客提供時尚建議。
4. 返回一個有效的JSON,包含建議,鍵必須是'answer',值必須是你的建議字符串。
請將輸出作為有效的JSON對象,沒有任何額外的格式,例如反引號或額外文本。確保JSON根據下面提供的架構正確結構化。
format_instructions
答案:
"""
prompt = PromptTemplate.from_template(
text_prompt, partial_variables="format_instructions": parser.get_format_instructions()
)
self.chain = prompt | model | parser
def get_advice(self, text: str, items: list, number_of_items: int) -> AssistantOutput:
"""
根據文本和商品上下文從模型獲取建議。
參數:
text (str): 用戶消息。
items (list): 為顧客找到的商品。
number_of_items (int): 應檢索的商品數量。
返回:
AssistantOutput: 模型的答案。
"""
try:
return self.chain.invoke("customer_message": text, "items": items, "number_of_items": number_of_items)
except Exception as e:
raise RuntimeError(f"調用鏈時出錯:{e}")
在工具方面,我們基於BLIP-2定義了一個工具。它由一個函數組成,接收文本或圖像作為輸入並返回標準化的嵌入。根據輸入,嵌入是使用文本嵌入模型或BLIP-2的圖像嵌入模型生成的。
from typing import Optional
import numpy as np
import torch
import torch.nn.functional as F
from PIL import Image
from PIL.JpegImagePlugin import JpegImageFile
from transformers import AutoProcessor, Blip2TextModelWithProjection, Blip2VisionModelWithProjection
PROCESSOR = AutoProcessor.from_pretrained("Salesforce/blip2-itm-vit-g")
TEXT_MODEL = Blip2TextModelWithProjection.from_pretrained("Salesforce/blip2-itm-vit-g", torch_dtype=torch.float32).to(
"cpu"
)
IMAGE_MODEL = Blip2VisionModelWithProjection.from_pretrained(
"Salesforce/blip2-itm-vit-g", torch_dtype=torch.float32
).to("cpu")
def generate_embeddings(text: Optional[str] = None, image: Optional[JpegImageFile] = None) -> np.ndarray:
"""
使用Blip2模型從文本或圖像生成嵌入。
參數:
text (Optional[str]): 顧客輸入的文本
image (Optional[Image]): 顧客輸入的圖像
返回:
np.ndarray: 嵌入向量
"""
if text:
inputs = PROCESSOR(text=text, return_tensors="pt").to("cpu")
outputs = TEXT_MODEL(**inputs)
embedding = F.normalize(outputs.text_embeds, p=2, dim=1)[:, 0, :].detach().numpy().flatten()
else:
inputs = PROCESSOR(images=image, return_tensors="pt").to("cpu", torch.float16)
outputs = IMAGE_MODEL(**inputs)
embedding = F.normalize(outputs.image_embeds, p=2, dim=1).mean(dim=1).detach().numpy().flatten()
return embedding
請注意,我們使用不同的嵌入模型來創建與PGVector的連接,這是必須的,儘管它不會被使用,因為我們將直接存儲BLIP-2生成的嵌入。
在下面的循環中,我們遍歷所有衣物類別,載入圖像,並創建並附加要存儲在向量數據庫中的嵌入到一個列表中。我們還將圖像的路徑作為文本存儲,以便在我們的Streamlit應用中呈現。最後,我們存儲類別,以便根據分類器代理預測的類別過濾結果。
import glob
import os
from dotenv import load_dotenv
from langchain_huggingface.embeddings import HuggingFaceEmbeddings
from langchain_postgres.vectorstores import PGVector
from PIL import Image
from blip2 import generate_embeddings
load_dotenv("env/connection.env")
CONNECTION_STRING = PGVector.connection_string_from_db_params(
driver=os.getenv("DRIVER"),
host=os.getenv("HOST"),
port=os.getenv("PORT"),
database=os.getenv("DATABASE"),
user=os.getenv("USERNAME"),
password=os.getenv("PASSWORD"),
)
vector_db = PGVector(
embeddings=HuggingFaceEmbeddings(model_name="nomic-ai/modernbert-embed-base"), # 對我們的用例來說無關緊要
collection_name="fashion",
connection=CONNECTION_STRING,
use_jsonb=True,
)
if __name__ == "__main__":
# 生成圖像嵌入
# 將圖像路徑保存為文本
# 將類別保存到元數據中
texts = []
embeddings = []
metadatas = []
for category in glob.glob("images/*"):
cat = category.split("/")[-1]
for img in glob.glob(f"category/*"):
texts.append(img)
embeddings.append(generate_embeddings(image=Image.open(img)).tolist())
metadatas.append("category": cat)
vector_db.add_embeddings(texts, embeddings, metadatas)
現在我們可以構建我們的Streamlit應用,與我們的助手聊天並請求建議。聊天開始時,代理會詢問如何幫助並提供一個框讓顧客寫消息和/或上傳文件。
一旦顧客回覆,工作流程如下:
- 分類器代理識別顧客正在尋找的衣物類別以及他們想要的數量。
- 如果顧客上傳了文件,該文件將被轉換為嵌入,我們將根據顧客想要的衣物類別和數量在向量數據庫中查找相似商品。
- 檢索到的商品和顧客的輸入消息然後發送給助手代理,以生成建議消息,並與檢索到的圖像一起呈現。
- 如果顧客沒有上傳文件,過程相同,但不生成圖像嵌入以進行檢索,而是創建文本嵌入。
import os
import streamlit as st
from dotenv import load_dotenv
from langchain_google_vertexai import ChatVertexAI
from langchain_huggingface.embeddings import HuggingFaceEmbeddings
from langchain_postgres.vectorstores import PGVector
from PIL import Image
import utils
from assistant import Assistant
from blip2 import generate_embeddings
from classifier import Classifier
load_dotenv("env/connection.env")
load_dotenv("env/llm.env")
CONNECTION_STRING = PGVector.connection_string_from_db_params(
driver=os.getenv("DRIVER"),
host=os.getenv("HOST"),
port=os.getenv("PORT"),
database=os.getenv("DATABASE"),
user=os.getenv("USERNAME"),
password=os.getenv("PASSWORD"),
)
vector_db = PGVector(
embeddings=HuggingFaceEmbeddings(model_name="nomic-ai/modernbert-embed-base"), # 對我們的用例來說無關緊要
collection_name="fashion",
connection=CONNECTION_STRING,
use_jsonb=True,
)
model = ChatVertexAI(model_name=os.getenv("MODEL_NAME"), project=os.getenv("PROJECT_ID"), temperarture=0.0)
classifier = Classifier(model)
assistant = Assistant(model)
st.title("歡迎來到ZAAI的時尚助手")
user_input = st.text_input("嗨,我是ZAAI的時尚助手。我今天能幫助你什麼?")
uploaded_file = st.file_uploader("上傳一張圖片", type=["jpg", "jpeg", "png"])
if st.button("提交"):
# 理解用戶的請求
classification = classifier.classify(user_input)
if uploaded_file:
image = Image.open(uploaded_file)
image.save("input_image.jpg")
embedding = generate_embeddings(image=image)
else:
# 如果用戶未上傳圖像,則創建文本嵌入
embedding = generate_embeddings(text=user_input)
# 創建要檢索的商品列表和路徑
retrieved_items = []
retrieved_items_path = []
for item in classification.category:
clothes = vector_db.similarity_search_by_vector(
embedding, k=classification.number_of_items, filter="category": "$in": [item]
)
for clothe in clothes:
retrieved_items.append("bytesBase64Encoded": utils.encode_image_to_base64(clothe.page_content))
retrieved_items_path.append(clothe.page_content)
# 獲取助手的建議
assistant_output = assistant.get_advice(user_input, retrieved_items, len(retrieved_items))
st.write(assistant_output.answer)
cols = st.columns(len(retrieved_items)+1)
for col, retrieved_item in zip(cols, ["input_image.jpg"]+retrieved_items_path):
col.image(retrieved_item)
user_input = st.text_input("")
else:
st.warning("請提供文本。")
以下是兩個示例:
圖6顯示了一個例子,顧客上傳了一張紅色T恤的圖片,並請求代理幫她搭配服裝。

圖7顯示了一個更簡單的例子,顧客請求代理顯示黑色T恤。

結論
多模態AI不再僅僅是一個研究主題。它正在被用於行業中,重新塑造顧客與公司目錄的互動方式。在本文中,我們探討了如何將像BLIP-2和Gemini這樣的多模態模型結合起來,解決現實世界的問題,並為顧客提供更個性化的體驗。
我們深入探討了BLIP-2的架構,展示了它如何彌合文本和圖像模態之間的差距。為了擴展其能力,我們開發了一個代理系統,每個代理專注於不同的任務。這個系統整合了一個大型語言模型(Gemini)和一個向量數據庫,使得使用文本和圖像嵌入檢索產品目錄成為可能。我們還利用Gemini的多模態推理來改善銷售助手代理的回應,使其更具人性化。
有了BLIP-2、Gemini和PG Vector等工具,多模態搜索和檢索的未來已經在發生,未來的搜索引擎將與我們今天使用的截然不同。
關於我
我是一位連續創業者和AI領域的領導者。我為企業開發AI產品並投資於專注於AI的初創公司。
創始人 @ ZAAI | LinkedIn | X/Twitter
參考文獻
[1] Junnan Li, Dongxu Li, Silvio Savarese, Steven Hoi. 2023. BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models. arXiv:2301.12597
[2] Prannay Khosla, Piotr Teterwak, Chen Wang, Aaron Sarna, Yonglong Tian, Phillip Isola, Aaron Maschinot, Ce Liu, Dilip Krishnan. 2020. Supervised Contrastive Learning. arXiv:2004.11362
[3] Junnan Li, Ramprasaath R. Selvaraju, Akhilesh Deepak Gotmare, Shafiq Joty, Caiming Xiong, Steven Hoi. 2021. Align before Fuse: Vision and Language Representation Learning with Momentum Distillation. arXiv:2107.07651
[4] Li Dong, Nan Yang, Wenhui Wang, Furu Wei, Xiaodong Liu, Yu Wang, Jianfeng Gao, Ming Zhou, Hsiao-Wuen Hon. 2019. Unified Language Model Pre-training for Natural Language Understanding and Generation. arXiv:1905.03197
本文由 AI 台灣 運用 AI 技術編撰,內容僅供參考,請自行核實相關資訊。
歡迎加入我們的 AI TAIWAN 台灣人工智慧中心 FB 社團,
隨時掌握最新 AI 動態與實用資訊!