如何結合多模態RAG和非同步呼叫實現大模型內容理解?

阿里妹導讀
文章探討了如何利用多模態大模型和工程最佳化手段提升物流理賠業務效率。核心方案包括:透過多模態RAG技術實現圖片查重,結合非同步呼叫方法最佳化貨損識別功能。
一. 專案背景和方案架構
1.1 專案背景
理賠業務是物流行業經常需要處理的問題,客服需要稽核客戶上傳的受損貨物的圖片資料,對受損情況做判定,然後給客戶提供賠償金額;整體的流程需要人工操作,效率很低,因此如何用大模型作為切入點為理賠業務提效成為重點關注問題。理賠工作存在一個風險點,客戶如果上傳虛假的理賠圖片,會造成物流公司的業務損失,比如客戶上傳的圖片是曾經上傳過的貨損圖片,或者經過了小幅度的裁剪、旋轉、ps的相似圖片,用人工來查重的過程費時費力,圖片查重也需要大模型來提效;
經過對大模型能力的評估,我最終選擇用多模態大模型qwen-vl-max的圖片理解能力來做智慧貨物定損+智慧圖片查重的功能,先讓大模型對上傳的圖片進行查重,如果重複流程終止,如果不重複,進入定損環節,讓vl大模型對輸入的受損貨物的圖片做識別,識別出貨損情況,包括貨損細節、貨損位置、貨損程度、受損情況;
1.2 需求分析
客戶需要讓大模型來實現智慧定損+圖片查重的功能,整體的要求如下:
(1)給定一組理賠貨損圖片,大模型先進行圖片查重,如果上傳的圖片沒有重複,則進入步驟2定損,如果存在重複,則終止流程;
(2)大模型識別這組圖片中貨物的破損情況、破損細節、破損位置、破損程度,返回定損結果;
大模型想要實現查重功能,需要把使用者上傳的圖片和曾經的圖片做比對來判斷是否存在重複,首先因為一組上傳的圖片數量在5-10張,其中最能體現貨物顯著特點的就是貨物的全景圖,這個圖片最有代表性,最合適作為查重的給定圖片,其他圖片是區域性細節圖、外包裝、無關圖等,所以從效果和時延上考慮,我推薦從一組圖片中只選擇一張破損貨物的全景圖作為查重圖片;
當曾經的圖片數量特別大時,大模型不可能和這麼多圖片去比對,這在技術上不可行,因此需要縮小比對的範圍,讓大模型只判斷幾張和這個上傳圖片最相似的圖片,只要這幾張圖片沒有重複,那麼剩下的那些不太相似的圖片就不可能重複,這樣就把比對範圍大大縮小了,因此我確定了技術策略,搭建一個圖片庫,從庫中檢索出和給定圖片最相似的幾張圖片,作為後續的查重樣本,大模型會判斷給定圖片和這些樣本的相似性,如果判斷重複,那麼終止本次理賠,進入人工複核;
對RAG比較熟悉的同學已經發現,上述分析的邏輯,從庫中檢索出最相似的樣本,這一步就是RAG做的工作,我們目前大量的RAG做的是文字領域的知識庫,針對檢索影像的這個需求,需要搭建的是多模態的向量庫,因此多模態RAG將作為大模型的前置工作,為大模型檢索出查重樣本;圖片的向量化需要用到向量大模型Embedding,multimodal-embedding-v1,能夠把圖片、文字、語言的多模態元素透過矩陣乘積計算出1024維度的向量,我用embedding模型對歷史圖片做向量化,並存儲到庫中,搭建多模態向量庫;
相對應的從技術架構上也要搭建兩個串聯的功能:
(1)使用者上傳一組理賠圖片,選擇1張有代表性的貨損全景圖,先進行向量庫檢索出幾張相似圖片,然後呼叫大模型比對上傳圖片和檢索出的圖片的重複情況;
(2)上傳的一組理賠圖片輸入qwen-vl-max,設計貨損識別的提示詞,大模型輸出貨損判定結果;
1.3 方案架構
1.3.1 功能1:圖片查重
架構流程
(1)搭建圖片向量庫,用embedding模型對歷史庫中的所有圖片計算向量,把所有向量儲存到資料庫中;
(2)客戶上傳一組理賠貨損圖片,自己從中選取一張最有代表性的破損全景圖,作為查重給定圖片;
(3)用embedding計算給定圖片的向量,根據給定圖片的向量從向量庫中檢索出最近似的幾張圖片;
(4)設計判斷是否重複的提示詞,呼叫vl大模型判斷給定圖片和最近似的幾張圖片是否存在重複,如果存在重複,進入人工複核,如果沒有重複,進入正常的理賠流程;

向量庫選擇
milvus是基於開源milvus的全託管雲上版本,相容性好、支援無縫遷移、水平擴充套件、安全告警、並且有視覺化的管理資料庫圖形操作介面,簡單易用,以下是呼叫milvus儲存、查詢、修改向量資料的方法示例:
from pymilvus import MilvusClientclient = MilvusClient(    uri="",    token="",    db_name="default")client.create_collection(    collection_name="<yourCollectionname>",  # 指定待建立的集合的名稱,milvus_collection。    dimension=5,  # 指定集合中向量的維度,本文示例為5    metric_type="IP"  # 指定用於計算向量相似度的度量方法)#樣例資料data=[{"id": 0, "vector": [], "color": ""}]#插入資料res = client.insert(    collection_name="milvus_collection",    data=data)#檢索向量庫query_vector = [[]]res = client.search(    collection_name="milvus_collection",        data=query_vector,#查詢向量    filter='5<id<10'#過濾條件                  limit=3,#返回3個最相似的向量    output_fields=['id','color'] #輸出欄位名)for hit in res[0]:    print(hit['entity'])
關鍵詞篩選
在儲存向量資料的時候,不僅儲存向量本身,還可以把圖片相關的資料作為關鍵欄位也存起來,比如圖片的連結、圖片上傳的時間、圖片的上傳人等,方便後續做關鍵詞的過濾;在理賠專案中,如果只需要把給定圖片和同一上傳人之前上傳的歷史圖片做比對,那麼在檢索時設定過濾條件,圖片上傳人=xx,如果要和過去一個月的歷史圖片做比對,再加過濾條件xx<上傳時間<xx,這樣可以根據需要調整篩選策略,更精確的查詢結果;
top_k設定
top_k是設定召回的樣本數量,也就是檢索出幾張最相似的圖片,topk需要根據檢索效果和時延來選擇,topk越小,檢索速度越快,但容錯率低;在理賠專案中,我設定的topk=3,3張圖片一般是比較合理的值,既能保證容錯率,同時時延略微升高;
非同步呼叫查重
對檢索出3張最相似的圖片,接下來是呼叫大模型查重,查重邏輯是這三張圖片和給定圖片一對一的比較,如果3張全部不重複,那麼上傳的給定圖片查重透過,如果存在任意一張重複,那麼查重不透過;基於這個邏輯,架構方法就是用非同步方法同時向大模型請求3次,每次上傳1張相似圖片和給定圖片,讓大模型判斷是否存在重複情況,對返回的3個結果檢查,存在重複的判斷結果,則查重不透過,全部不重複則查重透過;非同步呼叫的方法我會在後文詳細介紹;
1.3.2 功能2:識別貨損
架構流程
(1)設計貨物破損情況的識別提示詞;
(2)查重通過後,把一組貨損圖片輸入給qwen-vl-max大模型,輸出貨損情況;

識別貨損提示詞設計
##任務,識別貨物的損壞情況仔細觀察輸入的圖片,識別出其中貨物的受損情況,包括受損細節、受損位置、受損程度,並給出你的判斷理由;
二. 效果測試和架構最佳化
2.1 貨損識別存在問題
總共的測試資料在50組理賠圖片,每組圖片在5-10張;
在實際功能測試的過程中,基於多模態RAG的圖片查重功能的準確率達到94%,時延在2s左右,效果達到預期;
基於vl-max的多模態大模型貨物定損的功能的定損準確率在72%,時延在6.7s,整體的時延性和準確率不達標,針對發現的兩個問題,重新對錯誤資料分析,定位導致模型判錯的原因,並最佳化該功能的架構;
具體的測試資料對比如下,圖片查重的資料是單張圖片維度的測試結果,識別貨損的資料是組維度(一組5-10張圖片)的測試結果:
2.1.1 問題1:準確率低
問題一是對易混圖片的識別準確率很低,這類易混圖片的特點是數量一般在8張以上,其中大部分是商品完好的圖片或者是外包裝等和商品無關的圖片,這樣的圖片是無價值的,對定損沒有幫助,真正有價值的能暴露商品破損情況的圖片只有1-2張,比如下面這個床墊的例子:前面4張圖片幾乎看不出床墊有任何損壞的地方,真正暴露問題的在最後一張圖片(床墊內部破損),原因在於上傳者在拍攝圖片時沒有著重拍破損部位的全景圖,這種情況下大模型會給出商品無損的判定結果,不符合預期;
第一個問題的原因分析,根據視覺大模型的輸入邏輯,每張圖片會先經過一個kernel的切分,維度一般在3×3,5×5,它的畫素矩陣會切成多個小塊(token),每一個小塊經過embedding矩陣相乘後成為一個統一維度向量,叫做語義向量,同時為了體現小塊的空間序列資訊,還會引入二維位置編碼,把位置資訊也計算成向量,叫做位置向量,語義向量+位置向量得到包含語義和位置資訊的輸入向量;多個token按照在原來圖片中的位置,拼接起來成為一張圖片的輸入序列向量;
多張圖片,把一組輸入序列向量拼接起來成為整體的輸入序列輸入給大模型,如下圖所示:

而模型的輸出是基於輸入序列往後的迭代式生成,基於Transformer decoder架構的注意力機制會參考前文的詞來生成當前詞,如下圖所示,注意力機制是給每個輸入序列中的詞分配一組query、key、value向量(qkv透過每個詞的向量和預設的矩陣相乘得到),然後在計算當前詞輸出時,會根據前一個詞的query向量和前文序列中每一個詞的key向量做點積得到一個分數score,這個就是相關性分數,score越大,該score對應的前文向量對當前詞的輸出影響越大,然後把前文序列中每一個詞的score乘自己的value向量,再加權求和得到當前詞向量,該詞向量再透過前饋神經網路判斷出當前的單詞,以此類推,直到整個輸出序列生成完畢;這就是基於注意力機制的大模型生成邏輯;
從中分析可以發現,透過計算相關性分數score,前文序列中的每一個向量都會對當前向量的生成產生影響,score越大的向量相關性越大,對當前輸出產生的影響越大,所以後文的生成語義很大程度受到前文輸入序列的主要特徵的影響,理論上分析輸出序列的特徵會遵循輸入序列的主要特徵;

那麼當輸入序列中的多張圖片都是對定損無幫助的無價值圖片時,輸出的結果會受到輸入序列的主要特徵影響,此時的主要特徵是貨物無損,那麼輸出大機率也是無損;如果輸入的只有一張圖片且圖片中有損壞貨物,那麼輸出會是受損情況;
比如上面例子中的5張床墊照片,當全部輸入給大模型時,模型會輸出“無損”;當只上傳單張圖片給大模型時,前面4張會輸出無損,最後一張確實有損的圖片會輸出損壞情況;
在實際的50組測試用例中,有11組用例存在上述的易混情況,每組圖片內多數是對定損無價值的圖片(外包裝、有遮擋物等),有價值圖片(貨物全景圖)佔少數;大模型對這11組理賠圖片最終給出的定損意見都是“無損”,屬於判斷錯誤;而準確率是0.72,大模型總共判斷錯誤14組資料,在判錯資料中這種情況是絕大多數佔11/14,因此如何讓大模型解決這樣的易混圖片問題是架構最佳化的重點;
2.1.2 問題2:時延高
問題二是時延較高,5張輸入圖片的平均的輸出需要6s左右,每增加一張圖片,時延要上升0.5s左右,這對實際業務來說等待體驗不好,比如上述的5張床墊圖片的定損需要6.2s;
第二個問題的原因分析,很好理解,大模型的輸入序列越長,所需要的推理時間也會增加,導致耗時變長;
2.2 針對上述問題的思考
為了解決上述兩個問題,我嘗試了一些最佳化方法:
PE
最佳化提示詞,定損的結果會產生偏差,那麼我在提示詞中強化大模型對這些無價值的圖片的處理規則,比如在提示詞中加入
##限制有部分圖片和商品無關,有部分圖片中商品未損壞,過濾這些圖片,你只識別有破損商品的圖片;
嘗試五種不同的寫法,大模型仍然會輸出“無損”;說明大量的輸入特徵對結果的影響無法用提示詞來扭轉;
換基礎模型
把qwen-vl-max換成了更快的vl-plus,但經過測試時延能夠比vl-max平均快2s左右,但識別的準確率降低了20%,這樣的結果無法接受;
加前置模型做過濾
再加一個大模型來對輸入的一組圖片做前置處理,先判斷每張圖片是否和破損商品有關,把無關的圖片、商品完好無損的圖片過濾掉,把真正有價值的圖片傳給vl-max做定損判斷,這個方法確實解決了大模型輸出“無損”的問題,但引入的前置大模型導致時延和推理成本都增加了近一倍;
是否存在一種最佳化方法能夠完美解決效果和時延的兩方面問題呢,經過思考和討論,一種在工程上的常用方法:非同步,成功解決這個問題;非同步方法就是把這一組圖片拆分成單張,同時向大模型請求多次,每次請求只傳一張圖片,每次請求會得到一個識別結果,把結果中無價值的過濾掉,最後再把有價值的結果整合成最終定損結果,這樣能夠實現對無價值圖片的過濾來保證識別準確率,同時整體的輸入的圖片數量沒有增加(一次傳多張和多次,每次一張)保證成本不會增加,並且非同步呼叫的時延大約是呼叫單張圖片的時延,能夠很大程度上降低時延;
2.3 針對上述問題的架構最佳化
基於上述邏輯,我對貨損識別功能的架構做了最佳化,新架構如下:

2.3.1 大模型提示詞的重新設計
依據非同步呼叫的邏輯,現在vl大模型要先判斷輸入的單張圖片是否存在貨損,也就是判斷這張圖片有無價值,如果存在貨損,然後識別貨損情況,如果沒有貨損或圖片中沒有貨物,貨損情況輸出“無”,這樣後續在提取整合大模型結果的時候,可以直接提取出圖片本身有無貨損,根據這一欄位結果來過濾無價值的圖片,最終只保留有價值的定損結果;
以下是原提示詞和新提示詞的前後對比:
原提示詞:
##任務,識別貨物的損壞情況仔細觀察輸入的圖片,識別出其中貨物的受損情況,包括受損細節、受損位置、受損程度,並給出你的判斷理由;
新提示詞:
##任務一,判斷圖片是否相關仔細觀察輸入的圖片,如果圖片中沒有明顯的貨物或者圖片中的貨物沒有明顯的損壞,輸出“不相關”,如果有明顯貨物並且貨物存在損壞,輸出“相關”;##任務二,依據任務一的結果,識別貨物的損壞情況如果任務一的結果是“不相關”,受損情況直接輸出“無”;如果任務一的結果是“相關”,仔細觀察輸入的圖片,識別出其中貨物的受損情況,包括受損細節、受損位置、受損程度,並給出你的判斷理由;
2.3.2 非同步呼叫的方法
非同步,在工程上常用的一種方法,指的是在同一時間節點執行不同的程式,來實現程式的併發能力;大模型的工程鏈路中也可以使用這一方法,非同步呼叫大模型,就是在同一時間節點向大模型發出多個請求,得到多個請求結果,最後把每個結果整合成為最終結果;
以下是我用多執行緒實現的非同步呼叫方法:
import threadingimport queueimport dashscope##recognize是呼叫大模型的函式,q是佇列存放呼叫結果defrecognize(path,q): cs=[] cu=[]#prompt是判斷有無貨損+識別貨損情況的提示詞 prompt='' cs.append({'text':prompt}) cu.append({"image":path}) messages = [{"role": "system","content": cs},{"role": "user","content": cu}] response = dashscope.MultiModalConversation.call( model='qwen-vl-max', messages=messages )if response.status_code!=200: q.put((response.message,response.request_id,response.status_code)) q.put((response["output"]["choices"][0]["message"].content[0]['text'],response.request_id,response.status_code))#images是一組圖片的連結列表images=[]num=len(images)q = queue.Queue()threads=[]for img in images:#建立多執行緒,呼叫大模型 t = threading.Thread(target=recognize, args=(img,q)) threads.append(t) t.start()for t in threads:#執行緒同時啟動 t.join()#反向儲存佇列中的結果res=[]whilenot q.empty(): res.insert(0,q.get())#對結果的後續處理,遍歷、資訊整合等;#、、、、、
核心思路是用多執行緒併發向大模型請求,每次請求上傳單張圖片,把每次的結果存到佇列裡(queue),最後從queue中反向提取結果,再進行後續處理;
2.3.3 引入語言大模型整合結果
在得到多組非同步呼叫的結果後,剩下的工作就是把這些具有相同欄位的結果整合起來成為最終輸出,由於大模型生成的隨機性,單張圖片識別出的貨損情況會存在一些較小的差別,因此這一步我採用了語言大模型強大的文字資訊處理能力來實現,能夠提取多組結果中的貨損情況的共性特點,捨棄相同的表達,最終整合成一個結果;
我引入了qwen-turbo語言大模型,turbo模型速度最快,同時處理文字資訊整合的任務對大模型來說不復雜,turbo足夠處理;我設計了資訊整合的提示詞,以下是turbo的提示詞:
##任務,文字資訊整合輸入多組貨物損壞情況,這些描述的是同一件貨物,請你整合這些貨損情況,提取關鍵的共性資訊,整合輸出最終的貨損情況
三. 最終效果提升
整體的測試資料集有50組理賠圖片,每一組裡面有5-10張貨物破損圖片,總共321張圖片;
定義了3種評價指標,
(1)查重準確率是判斷這組圖片的查重圖片,大模型的結果和是否真正重複比對,查重準確率=大模型判對的理賠組數/50;
(2)識別準確率是組維度的指標,如果一組圖片最後識別出來的貨損情況和人工的標準答案几乎一致,那麼是一次準確的識別,理賠準確率=準確的理賠組數/50;
(3)平均時延是組維度的指標,計算的是模型處理一組圖片的平均耗時,平均耗時=(第1組耗時+第2組耗時+…+第50組耗時)/50;
圖片查重功能的準確率基於初步架構已經達到94%,所以關鍵的效果提升在加入了非同步呼叫的新貨損識別功能,結果如下:
參考
1.百鍊官方文件
https://help.aliyun.com/zh/model-studio/user-guide/text-generation/?spm=a2c4g.11186623.help-menu-2400256.d_1_0_0.395eb0a8oY2EWB&scm=20140722.H_2841718._.OR_help-T_cn~zh-V_1#e9230d4b28j7q
高效構建安全合規的企業新賬號
透過此方案可以統一企業內不同賬號內的基線,靈活適配不同企業對賬號初始化的個性需求。   
點選閱讀原文檢視詳情。

相關文章