本文已经是今年的第31篇大模型相关的技术文章了,如果说
我和我司更非常高兴通过博客、课程、内训、项目,与大家共同探讨如何把先进的大模型技术更好、更快的落地到各个行业的业务场景中,赋能千千万万公司的实际业务
而本文一开始是属于:因我司第三项目组「知识库问答项目」而起的此文《知识库问答LangChain+LLM的二次开发:商用时的典型问题及其改进方案》中的1.2节(该1.2节初稿来自我司LLM项目团队第三项目组的bingo),但为把Text Embedding模型阐述的更为精准、全面,特把那部分的内容抽取出来,不断完善成此文
最终尽可能相比网上已有的其他资料都更细致化
判断哪些文本嵌入模型效果较好,通常需要一个评估指标来进行比较,《MTEB: Massive Text Embedding Benchmark(海量文本嵌入基准)》就是一个海量文本嵌入模型的评估基准
榜单地址:https://huggingface.co/spaces/mteb/leaderboard
从Chinese Massive Text Embedding Benchmark中可以看到目前最新的针对中文海量文本embedding的各项任务的排行榜,针对不同的任务场景均有单独的排行榜。
任务榜单包括:
其中,在本地知识库任务中,主要是根据问题query的embedding表示到向量数据库中检索相似的本地知识文本片段。因此,该场景主要是Retrieval检索任务。检索任务榜单如下:
目前检索任务榜单下效果最好的是bge系列的bge-large-zh模型,langchain-chatchat项目中默认的m3e-base也处于比较靠前的位置
text-embedding-ada-002是OpenAI提供的一个embedding模型,但需要调用接口付费使用。其具有如下特点:
以下是OpenAI官方文档中给出的用于文本搜索的代码实例
from openai.embeddings_utils import get_embedding, cosine_similarity
def search_reviews(df, product_description, n=3, pprint=True):
embedding = get_embedding(product_description, model='text-embedding-ada-002')
df['similarities'] = df.ada_embedding.apply(lambda x: cosine_similarity(x, embedding))
res = df.sort_values('similarities', ascending=False).head(n)
return res
res = search_reviews(df, 'delicious beans', n=3)
M3E(Moka Massive Mixed Embedding,m3e-base模型下载地址:https://huggingface.co/moka-ai/m3e-base,m3e GitHub地址:GitHub - wangyingdong/m3e-base),其
以下是m3e的一些重大更新
此外,m3e团队建议
from datasets import load_dataset
from uniem.finetuner import FineTuner
dataset = load_dataset('shibing624/nli_zh', 'STS-B')
# 指定训练的模型为 m3e-small
finetuner = FineTuner.from_pretrained('moka-ai/m3e-small', dataset=dataset)
finetuner.run(epochs=3)
详细教程暂放在「大模型项目开发线上营」中,至于本文后续更新
BGE是北京智源人工智能研究院发布的中英文语义向量模型(hf地址:https://huggingface.co/BAAI/bge-large-zh,GitHub地址:https://github.com/FlagOpen/FlagEmbedding/blob/master/README_zh.md),以下是BGE的技术亮点
目前主流的语言模型的预训练任务都是token级别的,比如MLM或者Seq2Seq,但是这种训练任务难以让模型获得一个高质量的基于句子级别的句向量,这限制了语言模型在检索任务上的潜力。针对这个弊端,目前有两者针对检索模型的预训练策略
基于此,研究人员提出了RetraoMAE(RetroMAE论文:https://arxiv.org/abs/2205.12035),它包括两个模块,其一是一个类似于BERT的编码器,用于生成句向量,其二是一个一层transformer的解码器,用于重建句子,如下图所示
所谓编码,即Mask(EN)掉一小部分token然后通过BERT编码得到句子嵌入sentence embedding,具体步骤如下
所谓解码,即Mask(DE)很大一部分token然后结合句子嵌入sentence embedding,让解码器重构原始句子
具体而言,即是联合以下两个部分,好让解码器在该两部分的基础上重构原始句子
有个细节是,这里的Mask(DE)输入带上位置嵌入了,即句子嵌入和带有位置的掩码输入被组合成以下序列
其中,表示的嵌入,在的基础上增加了一个额外的位置嵌入
接下来,通过优化以下目标,学习解码器来重构原始句子
其中,是交叉熵损失
由于在解码器部分采用了极其简单的网络结构跟非常激进的mask比例,从而使得解码任务变得极具挑战性,迫使encoder去生成高质量的句向量才能最终准确地完成原文本重建
前面提及的解码策略有一种缺陷,就是训练信号只来源于被mask掉的token,而且每个mask掉的token都是基于同一个上下文重建的。于是研究人员提出了一种新的解码方法:Enhanced Decoding,具体做法如下
其中一个和常规decoder不一样的地方是,H1作为Q,H2作为KV
H1中的每个token embedding去H2中查找比较重要的上下文:包括H2被采样到的 token,以及初始token embedding都能看到[这里的初始embedding就是sentence embedding],至于对角线上的因代表的各自自身,故看不到
为方便大家更好、更快的理解,我再举个例子,比如:
P0上的能看见P1上的 (
x0、x1)
P1上的能看见P0、P2上的、 (x0、x1、x2)
P2上的能看见P0、P1上的、 (x0、x1、x2)
P3上的能看见P0、P4上的、 (x0、_ 、 _、x3、x4)
P4上的能看见P0、P3上的、 (x0、_ 、 _、x3、x4)
之后,每个token xi基于对矩阵M的第i行可见的上下文进行重构(each token xi is recon-structed based on the context which are visible tothe i-th row of matrix M),该矩阵即如下所示
主对角线位置填充为-∞(因为其代表自身,故不可见),可见上下文的位置填充为0,代表可见
最后,再总结一下RetroMAE 预训练步骤
{"query": str, "pos": List[str], "neg":List[str]}
python -m FlagEmbedding.baai_general_embedding.finetune.hn_mine \
--model_name_or_path BAAI/bge-base-en-v1.5 \
--input_file toy_finetune_data.jsonl \
--output_file toy_finetune_data_minedHN.jsonl \
--range_for_sampling 2-200 \
--use_gpu_for_searching
python -m FlagEmbedding.baai_general_embedding.finetune.hn_mine \
--model_name_or_path BAAI/bge-base-en-v1.5 \
--input_file toy_finetune_data.jsonl \
--output_file toy_finetune_data_minedHN.jsonl \
--range_for_sampling 2-200 \
--use_gpu_for_searching