引言:
最近在看ragflow源碼,其中有一個較為巧妙地設計:分別將 文字 、 標題 行向量化 之后,直接根據權重,進行加法運算,得到向量融合,增強了文本向量化的表示能力,這里開始討論一下,為什么這里可以直接對向量進行加法運算,而得到一個增強的表示
加權代碼片段:
title_w = 0.1 是標題的權重
tts 是標題進行embedding向量化后的矩陣
cnts 是將內容進行embedding向量化后的矩陣
vects 生成的最終的文檔向量
vects = (title_w * tts + (1 - title_w) *cnts) if len(tts) == len(cnts) else cnts
目的:優化表示
單獨使用標題向量可能丟失細節(如標題 “報告” 無法區分是 “營收報告” 還是 “技術報告”);單獨使用內容向量可能因信息冗余導致主題模糊(如大段無關描述覆蓋核心主題)。通過加權融合,可彌補單一模態的缺陷。
完整embedding代碼
async def embedding(docs, mdl, parser_config=None, callback=None):if parser_config is None:parser_config = {}batch_size = 16tts, cnts = [], []for d in docs:tts.append(d.get("docnm_kwd", "Title"))c = "\n".join(d.get("question_kwd", []))if not c:c = d["content_with_weight"]c = re.sub(r"</?(table|td|caption|tr|th)( [^<>]{0,12})?>", " ", c)if not c:c = "None"cnts.append(c)tk_count = 0if len(tts) == len(cnts):vts, c = await trio.to_thread.run_sync(lambda: mdl.encode(tts[0: 1]))tts = np.concatenate([vts for _ in range(len(tts))], axis=0)tk_count += ccnts_ = np.array([])for i in range(0, len(cnts), batch_size):vts, c = await trio.to_thread.run_sync(lambda: mdl.encode([truncate(c, mdl.max_length-10) for c in cnts[i: i + batch_size]]))if len(cnts_) == 0:cnts_ = vtselse:cnts_ = np.concatenate((cnts_, vts), axis=0)tk_count += ccallback(prog=0.7 + 0.2 * (i + 1) / len(cnts), msg="")cnts = cnts_title_w = float(parser_config.get("filename_embd_weight", 0.1))vects = (title_w * tts + (1 - title_w) *cnts) if len(tts) == len(cnts) else cntsassert len(vects) == len(docs)vector_size = 0for i, d in enumerate(docs):v = vects[i].tolist()vector_size = len(v)d["q_%d_vec" % len(v)] = vreturn tk_count, vector_size
問題:為什么可以直接簡單地通過加法進行融合?
要理解“向量值的含義”和“加法為何能融合特征”,我們需要從語義向量空間的本質和特征融合的數學邏輯兩個層面入手,結合具體場景(如文檔向量化)解釋:
一、向量值的本質:語義空間中的坐標
在自然語言處理(NLP)中,文本向量化(Embedding)的核心是將文本映射到一個高維語義空間中,向量的每個維度對應空間中的一個“語義特征”。==向量值(即每個維度的數值)本質是文本在該特征上的“得分” ==
舉個具體例子
假設我們有一個簡化的3維語義空間,三個維度分別代表:
- 維度1:“技術”相關程度(數值越大,文本越可能涉及技術);
- 維度2:“財務”相關程度(數值越大,文本越可能涉及財務);
- 維度3:“時間”(數值越大,文本越可能涉及時間描述)。
現在有兩個文本:
- 標題:“2023年公司技術研發報告”
其向量可能是[0.8, 0.2, 0.7]
(表示:技術相關度80%,財務相關度20%,時間相關度70%)。 - 內容:“本年度研發投入5000萬元,用于AI算法優化”
其向量可能是[0.7, 0.6, 0.3]
(技術相關度70%,財務相關度60%,時間相關度30%)。
這里的每個數值(如0.8、0.2)并非絕對的“分數”,而是模型通過預訓練學習到的相對語義關聯程度。向量整體則表示文本在這個語義空間中的“位置”——相似文本會在空間中相鄰(向量余弦相似度高)。
二、為什么向量加法可以融合特征?
向量加法能融合特征的前提是:標題向量與內容向量在同一語義空間中(即由同一模型編碼,維度相同且每個維度的語義含義一致)。此時,加法操作的本質是將兩個向量在同一空間中的坐標按比例疊加,從而合并兩者的語義信息。
從數學角度看:線性疊加保留所有特征
假設標題向量為 ( \mathbf{T} = [t_1, t_2, …, t_n] ),內容向量為 ( \mathbf{C} = [c_1, c_2, …, c_n] ),融合后的向量為 ( \mathbf{V} = w*\mathbf{T} + (1-w)*\mathbf{C} )(( w ) 是標題權重)。
每個維度 ( v_i ) 的計算為:
[ v_i = w*t_i + (1-w)*c_i ]
這相當于:
- 對標題在維度 ( i ) 的語義得分 ( t_i ),按權重 ( w ) 保留;
- 對內容在維度 ( i ) 的語義得分 ( c_i ),按權重 ( (1-w) ) 保留;
- 最終 ( v_i ) 是兩者的加權和,同時包含標題和內容在該維度的信息。
從語義角度看:互補信息的融合
回到前面的例子,標題和內容的向量各維度得分如下:
維度 | 標題向量 ( \mathbf{T} ) | 內容向量 ( \mathbf{C} ) | 融合后 ( \mathbf{V} )(( w=0.3 )) |
---|---|---|---|
技術相關度 | 0.8 | 0.7 | ( 0.30.8 + 0.70.7 = 0.24 + 0.49 = 0.73 ) |
財務相關度 | 0.2 | 0.6 | ( 0.30.2 + 0.70.6 = 0.06 + 0.42 = 0.48 ) |
時間相關度 | 0.7 | 0.3 | ( 0.30.7 + 0.70.3 = 0.21 + 0.21 = 0.42 ) |
融合后的向量 ( \mathbf{V} = [0.73, 0.48, 0.42] ) 同時體現了:
- 標題的“時間相關度高”(原0.7,融合后0.42);
- 內容的“財務相關度高”(原0.6,融合后0.48);
- 兩者共同的“技術相關度高”(原0.8和0.7,融合后0.73)。
這比單獨使用標題(可能忽略財務細節)或內容(可能弱化時間信息)的向量更全面。
三、為什么必須用同一模型編碼?
如果標題和內容用不同模型編碼(例如標題用模型A,內容用模型B),它們的向量可能不在同一語義空間(維度不同,或同一維度的語義含義不同)。此時加法無意義。
例如:
- 模型A的維度1表示“技術相關度”;
- 模型B的維度1可能表示“長度”(文本字數);
- 兩者的維度1數值無法直接相加(一個是語義得分,一個是字數統計)。
而代碼中標題和內容均使用 mdl.encode
(同一模型),確保了向量在同一空間中,加法操作才有語義意義。
總結
向量值的本質是文本在高維語義空間中的坐標,每個維度對應一個語義特征的“得分”。同一模型編碼的標題和內容向量處于同一空間,加法操作通過線性疊加合并了兩者在各維度的得分,從而融合了標題的概括性特征和內容的細節性特征。這就像將兩種顏色按比例混合——最終顏色同時保留了兩種顏色的成分,且比例由權重參數控制。