前言
本文已經是今年的第31篇大模型相關的技術文章了,如果說
- 半年之前寫部落格,更多是出於個人興趣 + 讀者需要
- 那自我司於23年Q3組建LLM項目團隊之後,寫部落格就成了:個人興趣 + 讀者需要 + 項目需要 如此兼備三者,實在是寫部落格之幸運矣
我和我司更非常高興通過博客、課程、內訓、項目,與大家共同探討如何把先進的大模型技術更好、更快的落地到各個行業的業務場景中,賦能千千萬萬公司的實際業務
而本文一開始是屬於:因我司第三項目組「知識庫問答項目」而起的此文《知識庫問答LangChain+LLM的二次開發:商用時的典型問題及其改進方案》中的1.2節(該1.2節初稿來自我司LLM項目團隊第三項目組的bingo),但為把Text Embedding模型闡述的更為精準、全面,特把那部分的內容抽取出來,不斷完善成此文
最終盡可能相比網上已有的其他資料都更細緻化
第一部分 衡量文本向量表示效果的榜單:MTEB、C-MTEB
1.2 《MTEB: Massive Text Embedding Benchmark(海量文本嵌入基准)》
判斷哪些文本嵌入模型效果較好,通常需要一個評估指標來進行比較,《MTEB: Massive Text Embedding Benchmark(海量文本嵌入基準)》就是一個海量文本嵌入模型的評估基準
- 論文地址:https://arxiv.org/abs/2210.07316 MTEB包含8個語義向量任務,涵蓋58個數據集和112種語言。通過在MTEB上對33個模型進行基準測試,建立了迄今為止最全面的文本嵌入基準。我們發現沒有特定的文本嵌入方法在所有任務中都佔主導地位。這表明該領域尚未集中在一個通用的文本嵌入方法上,並將其擴展到足以在所有嵌入任務上提供最先進的結果
- github地址:https://github.com/embeddings-benchmark/mteb#leaderboard
1.2 中文海量文本embedding任务排行榜:C-MTEB
從Chinese Massive Text Embedding Benchmark中可以看到目前最新的針對中文海量文本embedding的各項任務的排行榜,針對不同的任務場景均有單獨的排行榜。
任務榜單包括:
- 檢索
- STS
- 對分類
- 分類
- 重新排名
- 聚類
其中,在本地知識庫任務中,主要是根據問題query的embedding表示到向量數據庫中檢索相似的本地知識文本片段。因此,該場景主要是Retrieval檢索任務。檢索任務榜單如下:
目前檢索任務榜單下效果最好的是bge系列的bge-large-zh模型,langchain-chatchat項目中默認的m3e-base也處於比較靠前的位置
第二部分 text-embedding-ada-002
2.1 模型簡介
text-embedding-ada-002是OpenAI提供的一个embedding模型,但需要调用接口付费使用。其具有如下特点:
- 統一能力:OpenAI通過將五個獨立的模型(文本相似性、文本搜索-查詢、文本搜索-文件、代碼搜索-文本和代碼搜索-代碼)合併為一個新的模型,在一系列不同的文本搜索、句子相似性和代碼搜索基準中,這個單一的表述比以前的嵌入模型表現得更好
- 上下文:上下文長度為8192,使得它在處理長文檔時更加方便
- 嵌入尺寸:只有1536個維度,是davinci-001嵌入尺寸的八分之一,使新的嵌入在處理矢量數據庫時更具成本效益
2.2 模型使用
以下是OpenAI官方文檔中給出的用於文本搜索的程式碼實例
1. 從 openai.embeddings_utils 導入 get_embedding, cosine_similarity
2. 定義 search_reviews(df, product_description, n=3, pprint=True)
3. embedding = get_embedding(product_description, model='text-embedding-ada-002')
4. df['similarities'] = df.ada_embedding.apply(lambda x: cosine_similarity(x, embedding))
5. res = df.sort_values('similarities', ascending=False).head(n)
6. 返回 res
7. res = search_reviews(df, 'delicious beans', n=3)'
第三部分 m3e模型
3.1 m3e模型简介
M3E(Moka Massive Mixed Embedding,m3e-base模型下载地址:https://huggingface.co/moka-ai/m3e-base,m3e GitHub地址:GitHub - wangyingdong/m3e-base),其
- 使用in-batch负采样的对比学习的方式在句对数据集进行训练,为了保证in-batch负采样的效果,使用A100来最大化batch-size,并在共计2200W+的句对数据集(包含中文百科,金融,医疗,法律,新闻,学术等多个领域)上训练了 1 epoch
- 使用了指令数据集,M3E 使用了300W+的指令微调数据集,这使得 M3E 对文本编码的时候可以遵从指令,这部分的工作主要被启发于 instructor-embedding
- 基础模型,M3E 使用 Roberta 系列模型进行训练,目前提供 small 和 base 两个版本 此文《知识库问答LangChain+LLM的二次开发:商用时的典型问题及其改进方案》中的langchain-chatchat便默认用的m3e-base
3.1.1 m3e與openai text-embedding-ada-002
以下是m3e的一些重大更新
- 2023.06.08,添加检索任务的评测结果,在 T2Ranking 1W 中文数据集上,m3e-base 在 ndcg@10 上达到了 0.8004,超过了 openai-ada-002 的 0.7786
见下图s2p ndcg@10那一列(其中s2p, 即 sentence to passage ,代表了异质文本之间的嵌入能力,适用任务:文本检索,GPT 记忆模块等) - 2023.06.07,添加文本分类任务的评测结果,在 6 种文本分类数据集上,m3e-base 在 accuracy 上达到了 0.6157,超过了 openai-ada-002 的 0.5956
见下图s2s ACC那一列(其中s2s, 即 sentence to sentence ,代表了同质文本之间的嵌入能力,适用任务:文本相似度,重复问题检测,文本分类等)
此外,m3e團隊建議
- 如果使用場景主要是中文,少量英文的情況,建議使用 m3e 系列的模型
- 多語言使用場景,並且不介意數據隱私的話,作者團隊建議使用 openai text-embedding-ada-002
- 代碼檢索場景,推薦使用 openai text-embedding-ada-002
- 文本檢索場景,請使用具備文本檢索能力的模型,只在 S2S 上訓練的文本嵌入模型,沒有辦法完成文本檢索任務
3.2 m3e模型微調
- 微調腳本: m3e是使用uniem腳本進行微調
1. from datasets import load_dataset
2. from uniem.finetuner import FineTuner
3. dataset = load_dataset('shibing624/nli_zh', 'STS-B')
4. # 指定訓練的模型為 m3e-small
5. finetuner = FineTuner.from_pretrained('moka-ai/m3e-small', dataset=dataset)
6. finetuner.run(epochs=3)
詳細教程暫放在「大模型項目開發線上營」中,至於本文後續更新
第四部分 bge模型
4.1 bge模型的簡介
BGE是北京智源人工智能研究院发布的中英文語義向量模型(hf地址:https://huggingface.co/BAAI/bge-large-zh,GitHub地址:https://github.com/FlagOpen/FlagEmbedding/blob/master/README%5Fzh.md),以下是BGE的技術亮點
- 高效預訓練和大規模文本微調;
- 在兩個大規模語料集上採用了RetroMAE預訓練算法,進一步增強了模型的語義表徵能力;
- 通過負取樣和難負樣例挖掘,增強了語義向量的判別力;
- 借鑒Instruction Tuning的策略,增強了在多任務場景下的通用能力
4.1.1 RetroMAE的預訓練步驟
目前主流的語言模型的預訓練任務都是token級別的,比如MLM或者Seq2Seq,但是這種訓練任務難以讓模型獲得一個高質量的基於句子級別的句向量,這限制了語言模型在檢索任務上的潛力。針對這個弊端,目前有兩者針對檢索模型的預訓練策略
- 第一種是self-contrastive learning,這種方式往往受限於數據增強的質量,並且需要採用非常龐大數量的的負樣本
- 另一種基於anto-encoding,一種自重建方法,不受數據增強跟負樣本採樣策略的影響,基於這種方法的模型性能好壞有兩個關鍵因素 其一是重建任務必須要對編碼質量有足夠的要求,其二是訓練數據需要被充分利用到
基於此,研究人員提出了RetraoMAE(RetroMAE論文:https://arxiv.org/abs/2205.12035),它包括兩個模塊,其一是一個類似於BERT的編碼器,用於生成句向量,其二是一個一層transformer的解碼器,用於重建句子,如下圖所示
4.1.1.1 編碼Encoding
所谓编码,即Mask(EN)掉一小部分token然后通过BERT编码得到句子嵌入_sentence embedding_,具体步骤如下
- 给定一个句子输入:Norwegian forest cat is a breed of dom-estic cat originating in northern Europe
- 随机Mask(EN)掉其中一小部分token后得到:[M] forest cat is a breed of [M] cat originating in [M] Europe
这里通常会采用一定的mask比例(15%~30%),从而能保留句子原本大部分的信息 - 然后利用类似BERT的编码器对其进行编码,得到对应的的句子嵌入「一般将[CLS]位置最后一层的隐状态作为句子嵌入」,如下公式所示
We apply a BERT like encoder with 12 layers and768 hidden-dimensions, which helps to capture thein-depth semantics of the sentence. Following the common practice, we select the [CLS] token’s finalhidden state as the sentence embedding.
4.1.1.2 解碼Decoding
所谓解码,即Mask(DE)很大一部分token然后结合句子嵌入_sentence embedding_,让解码器重构原始句子
具體而言,即是聯合以下兩個部分,好讓解碼器在該兩部分的基礎上重構原始句子
- 利用Mask(DE)后的文本输入:[MI [M] cat is MI [M} of dom-estic [M] [M] in northern [M]
(这里采取了比encoder部分更加激进的mask比例,比如50%~70%) - 与上一节encoder生成的句子嵌入(sentence embedding)
论文中对这一步骤的英文阐述是:The masked input is joined with the sentence embedding, based on which the original sentence is reconstructed by the decoder.
有个细节是,这里的Mask(DE)输入带上位置嵌入了,即句子嵌入和带有位置的掩码输入被组合成以下序列 其中,表示的嵌入,在的基础上增加了一个额外的位置嵌入
接下来,通过优化以下目标,学习解码器来重构原始句子
其中,是交叉熵损失
由於在解碼器部分採用了極其簡單的網路結構跟非常激進的遮罩比例,從而使得解碼任務變得極具挑戰性,迫使encoder去生成高質量的句向量才能最終準確地完成原文本重建
4.1.1.3 增強解碼Enhanced Decoding
前面提及的解碼策略有一種缺陷,就是訓練信號只來源於被遮罩的標記,而且每個遮罩的標記都是基於同一個上下文重建的。於是研究人員提出了一種新的解碼方法:Enhanced Decoding,具體做法如下
- c)最终利用attention后的输出A跟H1一起过FNN(即resnet)去重建原文本,这里重建的目标不仅仅是被mask掉的token,而是全部token
最终RetroMAE的损失由两部分相加得到,其一是encoder部分的MLM损失,其二是deocder部分自重建的交叉熵损失
最後,再總結一下RetroMAE 預訓練步驟
- (A)編碼階段:將輸入進行一定比例的mask操作,並編碼為句子嵌入(綠色矩形) (A) Encoding: the input is moderately masked and encoded as the sentence embedding (the green rectangle)
- (B)解碼階段:對輸入使用很高比例的mask操作,並與句子嵌入連接以恢復被mask的部分(陰影符號) (B) Decoding: the input is aggressively masked, and joined with the sentence embedding to reconstruct the masked tokens (the shadowed tokens).
- (C)增強編碼階段:基於每行的句子嵌入和可見上下文來重建所有輸入符號;主對角線位置填充為-∞(灰色,因為其代表自身,故不可見),可見上下文的位置填充為0(藍色) (C) Enhanced encoding: all input tokens are reconstructed based on the sentence embedding and the visible context in each row (defined in Eq. 7); the main diagonal positions are filled with −∞ (grey), and positions for the visible context are filled with 0 (blue).
4.2 bge模型的微調
- 微調腳本:https://github.com/FlagOpen/FlagEmbedding/tree/master/examples/finetune
- 數據格式
{"query": str, "pos": List[str], "neg":List[str]}
- 難負樣本挖掘 難負樣本是一種廣泛使用的提高句子嵌入質量的方法。可以按照以下方法挖掘難負樣本
1. python -m FlagEmbedding.baai_general_embedding.finetune.hn_mine \
2. --model_name_or_path BAAI/bge-base-en-v1.5 \
3. --input_file toy_finetune_data.jsonl \
4. --output_file toy_finetune_data_minedHN.jsonl \
5. --range_for_sampling 2-200 \
6. --use_gpu_for_searching
- 訓練
1. python -m FlagEmbedding.baai_general_embedding.finetune.hn_mine \
2. --model_name_or_path BAAI/bge-base-en-v1.5 \
3. --input_file toy_finetune_data.jsonl \
4. --output_file toy_finetune_data_minedHN.jsonl \
5. --range_for_sampling 2-200 \
6. --use_gpu_for_searching