写点什么

基于无监督训练 SimCSE+In-batch Negatives 策略有监督训练的语义索引召回

  • 2023-08-02
    浙江
  • 本文字数:16618 字

    阅读完需:约 55 分钟

基于无监督训练SimCSE+In-batch Negatives策略有监督训练的语义索引召回

基于无监督训练 SimCSE+In-batch Negatives 策略有监督训练的语义索引召回

语义索引(可通俗理解为向量索引)技术是搜索引擎、推荐系统、广告系统在召回阶段的核心技术之一。语义索引模型的目标是:给定输入文本,模型可以从海量候选召回库中快速、准确地召回一批语义相关文本。语义索引模型的效果直接决定了语义相关的物料能否被成功召回进入系统参与上层排序,从基础层面影响整个系统的效果。


1.In-batch negatives 策略以及其他策略详解

In-batch negatives 是一种用于训练推荐系统或图像检索模型的策略。该策略的核心思想是在一个训练批次(batch)中,为每个正样本(目标样本)引入一些负样本(非目标样本),以增强模型的学习能力。


具体来说,In-batch negatives 策略首先从当前批次中选择出正样本,然后从同一批次中随机选择出一些负样本。这些负样本可以是与正样本相似但被错误标记的样本,也可以是完全不相关的样本。通过将正样本与负样本一起输入模型进行训练,模型需要学会区分正样本和负样本,从而提高推荐或检索的准确性。


  • 类似的算法策略还有以下几种:

  • Negative Sampling(负采样):该策略通过从全局样本集中采样出一些负样本,与正样本一起构成训练集。负样本的选择可以基于一定的概率分布,例如使用频率、TF-IDF 权重等。

  • Hard Negative Mining(困难样本挖掘):这种策略着重挑选那些与正样本相似度较高但被错误标记的负样本。通过引入这些难以区分的负样本,模型能够更好地学习样本间的差异。

  • Contrastive Learning(对比学习):该策略通过最大化正样本对的相似度,以及最小化正样本与负样本对的相似度之间的差异来进行训练。这样可以使模型更好地将正样本聚集在一起,同时将负样本分散开。

  • Triplet Loss:这种策略使用三元组(triplet)的方式进行训练,每个三元组由一个锚点样本、一个正样本和一个负样本组成。目标是使得正样本与锚点样本间的距离小于负样本与锚点样本间的距离,从而达到更好的样本区分效果。


这些算法策略都旨在通过引入负样本或调整样本间的相似度关系,从而提高模型对目标样本的识别和检索能力。具体选择哪种策略取决于具体的任务和数据特点。


  • 常见的策略对比:


1.1 In-batch negatives 核心思路

在召回阶段,最常见的方式是通过双塔模型,学习 Document(简写为 Doc)的向量表示,对 Doc 端建立索引,用 ANN 召回。我们在这种方式的基础上,引入语义索引策略 In-batch Negatives,以如下 Batch size=4 的训练数据为例:


我手机丢了,我想换个手机     我想买个新手机,求推荐         学日语软件手机上的          手机学日语的软件侠盗飞车罪恶都市怎样改车     侠盗飞车罪恶都市怎么改车
复制代码


In-batch negatives 策略核心是在 1 个 Batch 内同时基于 N 个负例进行梯度更新,将 Batch 内除自身之外其它所有 Source Text 的相似文本 Target Text 作为负例,例如: 上例中 我手机丢了,我想换个手机 有 1 个正例(1.我想买个新手机,求推荐),2 个负例(1.手机学日语的软件2.侠盗飞车罪恶都市怎么改车)。

1.2 HardestNeg 核心思路

HardestNeg 策略核心是在 1 个 Batch 内的所有负样本中先挖掘出最难区分的负样本,基于最难负样本进行梯度更新。例如: 上例中 Source Text: 我手机丢了,我想换个手机 有 3 个负例(1.手机学日语的软件2.侠盗飞车罪恶都市怎么改车),其中最难区分的负例是 手机学日语的软件,模型训练过程中不断挖掘出类似这样的最难负样本,然后基于最难负样本进行梯度更新。

2. 技术方案和评估指标

2.1 技术方案

双塔模型,在召回训练阶段引入 In-batch Negatives 策略,使用 hnswlib 建立索引库,进行召回测试。

2.2 评估指标

采用 Recall@1,Recall@5 ,Recall@10 ,Recall@20 和 Recall@50 指标来评估语义索引模型的召回效果。


Recall@K 召回率是指预测的前 topK(top-k 是指从最后的按得分排序的召回列表中返回前 k 个结果)结果中检索出的相关结果数和库中所有的相关结果数的比率,衡量的是检索系统的查全率。


效果评估展示


3. 环境依赖 &代码结构

3.1 环境依赖

推荐使用 GPU 进行训练,在预测阶段使用 CPU 或者 GPU 均可。


环境依赖


  • python >= 3.6.2

  • paddlepaddle >= 2.2.3

  • paddlenlp >= 2.2

  • hnswlib >= 0.5.2

  • visualdl >= 2.2.2


#安装环境!pip install -r requirements.txt
复制代码


!pip install --pre --upgrade paddlenlp==2.6.0rc0 -f https://www.paddlepaddle.org.cn/whl/paddlenlp.html
复制代码


!pip install protobuf==3.20.0
复制代码

3.2 代码结构

|—— data.py # 数据读取、数据转换等预处理逻辑|—— base_model.py # 语义索引模型基类|—— train_batch_neg.py # In-batch Negatives 策略的训练主脚本|—— batch_negative    |—— model.py # In-batch Negatives 策略核心网络结构|—— ann_util.py # Ann 建索引库相关函数

|—— recall.py # 基于训练好的语义索引模型,从召回库中召回给定文本的相似文本|—— evaluate.py # 根据召回结果和评估集计算评估指标|—— predict.py # 给定输入文件,计算文本 pair 的相似度|—— export_model.py # 动态图转换成静态图|—— scripts |—— export_model.sh # 动态图转换成静态图脚本 |—— predict.sh # 预测 bash 版本 |—— evaluate.sh # 评估 bash 版本 |—— run_build_index.sh # 构建索引 bash 版本 |—— train_batch_neg.sh # 训练 bash 版本 |—— export_to_serving.sh # Paddle Inference 转 Serving 的 bash 脚本|—— deploy |—— python |—— predict.py # PaddleInference |—— deploy.sh # Paddle Inference 部署脚本 |—— rpc_client.py # Paddle Serving 的 Client 端 |—— web_service.py # Paddle Serving 的 Serving 端 |—— config_nlp.yml # Paddle Serving 的配置文件|—— inference.py # 动态图抽取向量|—— export_to_serving.py # 静态图转 Serving
复制代码


#解压代码# !unzip /home/aistudio/in_batch_negative.zip
复制代码

4. 数据准备

4.1 数据集说明

我们基于某文献检索平台数据,构造面向语义索引的训练集、测试集、召回库。


训练集验证集 格式一致,训练集 4k 条,测试集 2w 条,每行由一对语义相似的文本 Pair 构成,以 tab 符分割,第一列是检索 query,第二列由相关文献标题(+关键词)构成。样例数据如下:


宁夏社区图书馆服务体系布局现状分析           宁夏社区图书馆服务体系布局现状分析社区图书馆,社区图书馆服务,社区图书馆服务体系人口老龄化对京津冀经济                     京津冀人口老龄化对区域经济增长的影响京津冀,人口老龄化,区域经济增长,固定效应模型英语广告中的模糊语                      模糊语在英语广告中的应用及其功能模糊语,英语广告,表现形式,语用功能甘氨酸二肽的合成                          甘氨酸二肽合成中缩合剂的选择甘氨酸,缩合剂,二肽
复制代码


召回库 用于模拟业务线上的全量语料库,评估模型的召回效果,计算相应的 Recall 指标。召回库总共 30 万条样本,每行由一列构成,文献标题(+关键词),样例数据如下:


陕西省贫困地区城乡青春期少女生长发育调查青春期,生长发育,贫困地区五丈岩水库溢洪道加固工程中的新材料应用碳纤维布,粘钢加固技术,超细水泥,灌浆技术木塑复合材料在儿童卫浴家具中的应用探索木塑复合材料,儿童,卫浴家具泡沫铝准静态轴向压缩有限元仿真泡沫铝,准静态,轴向压缩,力学特性
复制代码

4.2 数据集下载

├── milvus # milvus建库数据集    ├── milvus_data.csv.  # 构建召回库的数据├── recall  # 召回(语义索引)数据集    ├── corpus.csv # 用于测试的召回库    ├── dev.csv  # 召回验证集    ├── test.csv # 召回测试集    ├── train.csv  # 召回训练集    ├── train_unsupervised.csv # 无监督训练集├── sort # 排序数据集    ├── test_pairwise.csv   # 排序测试集    ├── dev_pairwise.csv    # 排序验证集    └── train_pairwise.csv  # 排序训练集
复制代码


#解压数据集!unzip -d /home/aistudio/literature_search_data /home/aistudio/data/data225060/literature_search_data.zip
复制代码

5. 模型训练

语义索引训练模型下载链接:


以下模型结构参数为: TrasformerLayer:12, Hidden:768, Heads:12, OutputEmbSize: 256



  • 训练环境说明

  • NVIDIA Driver Version: 440.64.00

  • Ubuntu 16.04.6 LTS (Docker)

  • Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz

5.1 单机单卡训练/单机多卡训练

这里采用单机多卡方式进行训练,通过如下命令,指定 GPU 0,1,2,3 卡, 基于 In-batch Negatives 策略训练模型,数据量比较小,几分钟就可以完成。如果采用单机单卡训练,只需要把--gpus参数设置成单卡的卡号即可。


如果使用 CPU 进行训练,则需要吧--gpus参数去除,然后吧device设置成 cpu 即可,详细请参考 train_batch_neg.sh 文件的训练设置


然后运行下面的命令使用 GPU 训练,得到语义索引模型:


#进入目录%cd in_batch_negative
复制代码


/home/aistudio/in_batch_negative
复制代码


# !python -u -m paddle.distributed.launch --gpus "0,1,2,3" \ 多卡#单卡!python -u -m paddle.distributed.launch --gpus "0" \    train_batch_neg.py \    --device gpu \    --save_dir ./checkpoints/inbatch \    --batch_size 32 \    --learning_rate 5E-5 \    --epochs 3 \    --output_emb_size 256 \    --model_name_or_path rocketqa-zh-base-query-encoder \    --save_steps 10 \    --max_seq_length 64 \    --margin 0.2 \    --train_set_file "/home/aistudio/literature_search_data/recall/train.csv" \    --recall_result_dir "recall_result_dir" \    --recall_result_file "recall_result.txt" \    --hnsw_m 100 \    --hnsw_ef 100 \    --recall_num 50 \    --similar_text_pair_file "/home/aistudio/literature_search_data/recall/dev.csv" \    --corpus_file "/home/aistudio/literature_search_data/recall/corpus.csv"
复制代码


输出结果预览:


[2023-07-07 17:08:32,340] [    INFO] - tokenizer config file saved in ./checkpoints/inbatch/model_360/tokenizer_config.json[2023-07-07 17:08:32,340] [    INFO] - Special tokens file saved in ./checkpoints/inbatch/model_360/special_tokens_map.jsonglobal step 370, epoch: 3, batch: 120, loss: 0.15921, speed: 5.84 step/s[2023-07-07 17:08:34,041] [    INFO] - tokenizer config file saved in ./checkpoints/inbatch/model_370/tokenizer_config.json[2023-07-07 17:08:34,042] [    INFO] - Special tokens file saved in ./checkpoints/inbatch/model_370/special_tokens_map.jsonLAUNCH INFO 2023-07-07 17:08:36,702 Pod completedLAUNCH INFO 2023-07-07 17:08:36,702 Exit code 0
复制代码


参数含义说明


  • device: 使用 cpu/gpu 进行训练

  • save_dir: 模型存储路径

  • batch_size: 训练的 batch size 的大小,note:参数别设置太大

  • learning_rate: 训练的学习率的大小

  • epochs: 训练的 epoch 数

  • output_emb_size: Transformer 顶层输出的文本向量维度

  • model_name_or_path: 预训练模型,用于模型和Tokenizer的参数初始化

  • save_steps: 模型存储 checkpoint 的间隔 steps 个数

  • max_seq_length: 输入序列的最大长度

  • margin: 正样本相似度与负样本之间的目标 Gap

  • train_set_file: 训练集文件

  • evaluate: 是否开启边训练边评估模型训练效果,默认开启

  • recall_result_dir: 召回结果存储目录

  • recall_result_file: 召回结果的文件名

  • hnsw_m: hnsw 算法相关参数,保持默认即可

  • hnsw_ef: hnsw 算法相关参数,保持默认即可

  • recall_num: 对 1 个文本召回的相似文本数量

  • similar_text_pair_file: 由相似文本对构成的评估集

  • corpus_file: 召回库数据 corpus_file

  • use_recompute: 使用 Recompute 策略,用于节省显存,是一种以时间换空间的技术

  • use_gradient_cache: 使用 Gradient Cache 策略,用于节省显存,是一种以时间换空间的技术

  • chunk_numbers: 使用 Gradient Cache 策略的参数,表示的是同一个批次的样本分几次执行


也可以使用 bash 脚本:


sh scripts/train.sh
复制代码

6. 模型评估

效果评估分为 4 个步骤:


  • a. 获取 Doc 端 Embedding

  • 基于语义索引模型抽取出 Doc 样本库的文本向量。

  • b. 采用 hnswlib 对 Doc 端 Embedding 建库

  • 使用 ANN 引擎构建索引库(这里基于 hnswlib 进行 ANN 索引)

  • c. 获取 Query 的 Embedding 并查询相似结果

  • 基于语义索引模型抽取出评估集 Source Text 的文本向量,在第 2 步中建立的索引库中进行 ANN 查询,召回 Top50 最相似的 Target Text, 产出评估集中 Source Text 的召回结果 recall_result 文件。

  • d. 评估

  • 基于评估集 dev.csv 和召回结果 recall_result 计算评估指标 Recall@k,其中 k 取值 1,5,10,20,50。


运行如下命令进行 ANN 建库、召回,产出召回结果数据 recall_result


#查看模型生成多少个了# %cd /home/aistudio/in_batch_negative/checkpoints/inbatch# !ls
复制代码


/home/aistudio/in_batch_negative/checkpoints/inbatchmodel_10  model_30  model_50  model_70  model_90model_20  model_40  model_60  model_80  recall_log
复制代码


# %cd ../../
复制代码


/home/aistudio/in_batch_negative
复制代码


!python -u -m paddle.distributed.launch --gpus "0" --log_dir "recall_log/" \        recall.py \        --device gpu \        --recall_result_dir "recall_result_dir" \        --recall_result_file "recall_result.txt" \        --params_path "checkpoints/inbatch/model_90/model_state.pdparams" \        --model_name_or_path rocketqa-zh-base-query-encoder \        --hnsw_m 100 \        --hnsw_ef 100 \        --batch_size 32 \        --output_emb_size 256\        --max_seq_length 60 \        --recall_num 50 \        --similar_text_pair_file "/home/aistudio/literature_search_data/recall/dev.csv" \        --corpus_file "/home/aistudio/literature_search_data/recall/corpus.csv"
复制代码


参数含义说明


  • device: 使用 cpu/gpu 进行训练

  • recall_result_dir: 召回结果存储目录

  • recall_result_file: 召回结果的文件名

  • params_path: 待评估模型的参数文件名

  • model_name_or_path: 预训练模型,用于模型和Tokenizer的参数初始化

  • hnsw_m: hnsw 算法相关参数,保持默认即可

  • hnsw_ef: hnsw 算法相关参数,保持默认即可

  • output_emb_size: Transformer 顶层输出的文本向量维度

  • recall_num: 对 1 个文本召回的相似文本数量

  • similar_text_pair: 由相似文本对构成的评估集

  • corpus_file: 召回库数据 corpus_file


也可以使用下面的 bash 脚本:


sh scripts/run_build_index.sh
复制代码


run_build_index.sh 还包含 cpu 和 gpu 运行的脚本,默认是 gpu 的脚本


成功运行结束后,会在 ./recall_result_dir/ 目录下产出 recall_result.txt 文件


热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响        热处理对尼龙6及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响尼龙6,聚酰胺嵌段共聚物,芳香聚酰胺,热处理      0.9831992387771606热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响        热处理方法对高强高模聚乙烯醇纤维性能的影响聚乙烯醇纤维,热处理,性能,热拉伸,热定型    0.8438636660575867热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响        制备工艺对PVC/ABS合金力学性能和维卡软化温度的影响PVC,ABS,正交试验,力学性能,维卡软化温度      0.8130228519439697.....
复制代码


接下来,运行如下命令进行效果评估,产出 Recall@1, Recall@5, Recall@10, Recall@20 和 Recall@50 指标:


#查看生成数据with open('/home/aistudio/in_batch_negative/recall_result_dir/recall_result.txt', 'r', encoding='utf-8') as file:    for i in range(10):        line = file.readline()        if not line:            break        print(line.rstrip())
复制代码


热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  热处理对尼龙6及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响尼龙6,聚酰胺嵌段共聚物,芳香聚酰胺,热处理  0.9818969368934631热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  热处理方法对高强高模聚乙烯醇纤维性能的影响聚乙烯醇纤维,热处理,性能,热拉伸,热定型  0.7950249314308167热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  尼龙66的改性研究及注塑模流分析尼龙66;环氧树脂;综合性能;熔融共混;注塑模流分析;扩链改性  0.7899066805839539热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  外场作用下介晶态聚丙烯的结构演变研究介晶态聚丙烯;微观结构;介晶相;乙烯共聚单元;温度作用;应力作用  0.773107647895813热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  热塑性结晶型全芳族聚酯酰亚胺的制备与聚集态结构聚酯酰亚胺,熔融双峰,热塑性,聚集态结构  0.7728450894355774热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  微量氧化石墨烯改性聚酯的结构及热性能分析改性聚酯,氧化石墨烯,结构,热稳定性,结晶度  0.7625532150268555热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  等规聚丙烯结晶机理研究等规聚丙烯;结晶机理;β-成核剂;微观形貌;剪切强度  0.7597315907478333热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  高等级聚乙烯管材料的结构与性能研究聚乙烯管材料;分子结构;力学性能;流变性能  0.7588903903961182热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  MC尼龙/改性氧化石墨复合材料的制备与性能研究聚丙烯;二元胺接枝;改性氧化石墨;尼龙;复合材料;机械性能;冲击强度  0.7560845613479614热处理对尼龙6 及其与聚酰胺嵌段共聚物共混体系晶体熔融行为和结晶结构的影响  链长对无定型聚乙烯导热性能影响的NEMD研究非平衡分子动力学,聚乙烯分子链,导热  0.755224347114563
复制代码


!python -u evaluate.py \        --similar_text_pair "/home/aistudio/literature_search_data/recall/dev.csv" \        --recall_result_file "/home/aistudio/in_batch_negative/recall_result_dir/recall_result.txt" \        --recall_num 50
复制代码


recall@1=59.607recall@5=75.094recall@10=79.487recall@20=83.445recall@50=87.763
复制代码


也可以使用下面的 bash 脚本:


sh scripts/evaluate.sh
复制代码


参数含义说明


  • similar_text_pair: 由相似文本对构成的评估集 semantic_similar_pair.tsv

  • recall_result_file: 针对评估集中第一列文本 Source Text 的召回结果

  • recall_num: 对 1 个文本召回的相似文本数量


成功运行结束后,会输出如下评估指标:


recall@1=59.271recall@5=74.387recall@10=78.82recall@20=82.813recall@50=86.955
复制代码

7. 预测

我们可以基于语义索引模型预测文本的语义向量或者计算文本 Pair 的语义相似度。

7.1 功能一:抽取文本的语义向量

修改 inference.py 文件里面输入文本 id2corpus 和模型路径 params_path :


params_path='checkpoints/inbatch/model_40/model_state.pdparams'id2corpus={0:'国有企业引入非国有资本对创新绩效的影响——基于制造业国有上市公司的经验证据'}
复制代码


然后运行:


!python inference.py
复制代码


预测结果为 256 维的向量:


[1, 256][[-5.50949238e-02  8.43941122e-02  6.79432228e-02 -5.83942570e-02   3.46402712e-02  5.86730465e-02  3.02802827e-02 -6.04058355e-02   3.81579484e-05 -1.62589997e-02 -5.91990687e-02  7.68813044e-02  -7.33192125e-03 -7.04482794e-02  6.05499111e-02  3.85251977e-02   .....
复制代码

7.2 功能二:计算文本 Pair 的语义相似度

  • 准备预测数据


待预测数据为 tab 分隔的 csv 文件,每一行为 1 个文本 Pair,部分示例如下:


试论我国海岸带经济开发的问题与前景    试论我国海岸带经济开发的问题与前景海岸带,经济开发,问题,前景外语阅读焦虑与英语成绩及性别的关系    外语阅读焦虑与英语成绩及性别的关系外语阅读焦虑,外语课堂焦虑,英语成绩,性别数字图书馆    智能化图书馆网络健康可信性研究    网络成瘾少年
复制代码


  • 开始预测


以上述 demo 数据为例,运行如下命令基于我们开源的 In-batch Negatives 策略语义索引模型开始计算文本 Pair 的语义相似度:


#训练好的模型也可以自主选择添加# !unzip /home/aistudio/inbatch_model.zip
复制代码


Archive:  /home/aistudio/inbatch_model.zip   creating: model_40/  inflating: model_40/model_state.pdparams    inflating: model_40/vocab.txt        inflating: model_40/tokenizer_config.json  
复制代码


!python -u -m paddle.distributed.launch --gpus "0" \    predict.py \    --device gpu \    --params_path "checkpoints/inbatch/model_90/model_state.pdparams" \    --model_name_or_path rocketqa-zh-base-query-encoder \    --output_emb_size 256 \    --batch_size 128 \    --max_seq_length 64 \    --text_pair_file "/home/aistudio/literature_search_data/recall/test.csv"
复制代码


参数含义说明


  • device: 使用 cpu/gpu 进行训练

  • params_path: 预训练模型的参数文件名

  • model_name_or_path: 预训练模型,用于模型和Tokenizer的参数初始化

  • output_emb_size: Transformer 顶层输出的文本向量维度

  • text_pair_file: 由文本 Pair 构成的待预测数据集


也可以运行下面的 bash 脚本:


sh scripts/predict.sh
复制代码


predict.sh 文件包含了 cpu 和 gpu 运行的脚本,默认是 gpu 运行的脚本


产出如下结果


0.90860974788665770.25735637545585630.00236934423446655270.74711591005325320.97826039791107180.993239164352417.....
复制代码

8. 部署

8.1 动转静导出

首先把动态图模型转换为静态图:


python export_model.py --params_path checkpoints/inbatch/model_40/model_state.pdparams \                       --model_name_or_path rocketqa-zh-base-query-encoder \                       --output_path=./output
复制代码


也可以运行下面的 bash 脚本:


sh scripts/export_model.sh
复制代码


!python export_model.py --params_path checkpoints/inbatch/model_90/model_state.pdparams \                       --model_name_or_path rocketqa-zh-base-query-encoder \                       --output_path=./output
复制代码


[2023-07-07 17:45:08,502] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.modeling.ErnieModel'> to load 'rocketqa-zh-base-query-encoder'.[2023-07-07 17:45:08,502] [    INFO] - Model config ErnieConfig {  "attention_probs_dropout_prob": 0.1,  "enable_recompute": false,  "fuse": false,  "hidden_act": "gelu",  "hidden_dropout_prob": 0.1,  "hidden_size": 768,  "initializer_range": 0.02,  "intermediate_size": 3072,  "layer_norm_eps": 1e-12,  "max_position_embeddings": 2048,  "model_type": "ernie",  "num_attention_heads": 12,  "num_hidden_layers": 12,  "pad_token_id": 0,  "paddlenlp_version": null,  "pool_act": "tanh",  "task_id": 0,  "task_type_vocab_size": 3,  "type_vocab_size": 4,  "use_task_id": true,  "vocab_size": 40000}W0707 17:45:10.404314  8461 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 8.0, Driver API Version: 11.2, Runtime API Version: 11.2W0707 17:45:10.407172  8461 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.[2023-07-07 17:45:11,056] [ WARNING] - Some weights of the model checkpoint at rocketqa-zh-base-query-encoder were not used when initializing ErnieModel: ['classifier.bias', 'classifier.weight']- This IS expected if you are initializing ErnieModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).- This IS NOT expected if you are initializing ErnieModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).[2023-07-07 17:45:11,056] [    INFO] - All the weights of ErnieModel were initialized from the model checkpoint at rocketqa-zh-base-query-encoder.If your task is similar to the task the model of the checkpoint was trained on, you can already use ErnieModel for predictions without further training.[2023-07-07 17:45:11,056] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load 'rocketqa-zh-base-query-encoder'.[2023-07-07 17:45:11,056] [    INFO] - Already cached /home/aistudio/.paddlenlp/models/rocketqa-zh-base-query-encoder/ernie_3.0_base_zh_vocab.txt[2023-07-07 17:45:11,078] [    INFO] - tokenizer config file saved in /home/aistudio/.paddlenlp/models/rocketqa-zh-base-query-encoder/tokenizer_config.json[2023-07-07 17:45:11,078] [    INFO] - Special tokens file saved in /home/aistudio/.paddlenlp/models/rocketqa-zh-base-query-encoder/special_tokens_map.jsonLoaded parameters from checkpoints/inbatch/model_90/model_state.pdparams
复制代码

8.2 Paddle Inference 预测

预测既可以抽取向量也可以计算两个文本的相似度。


修改 id2corpus 的样本:


#抽取向量id2corpus={0:'国有企业引入非国有资本对创新绩效的影响——基于制造业国有上市公司的经验证据'}#计算相似度corpus_list=[['中西方语言与文化的差异','中西方文化差异以及语言体现中西方文化,差异,语言体现'],                    ['中西方语言与文化的差异','飞桨致力于让深度学习技术的创新与应用更简单']]
复制代码


然后使用 PaddleInference


!python deploy/python/predict.py \                             --model_dir=./output \                             --model_name_or_path rocketqa-zh-base-query-encoder
复制代码


也可以运行下面的 bash 脚本:


sh deploy.sh
复制代码


最终输出的是 256 维度的特征向量和句子对的预测概率:


(1, 256)[[-0.0394925  -0.04474756 -0.065534    0.00939134  0.04359895  0.14659195  -0.0091779  -0.07303623  0.09413272 -0.01255222 -0.08685658  0.02762237   0.10138468  0.00962821  0.10888419  0.04553023  0.05898942  0.00694253   ....
[0.959269642829895, 0.04725276678800583]
复制代码

8.3 Paddle Serving 部署

Paddle Serving 的详细文档请参考 Pipeline_DesignServing_Design,首先把静态图模型转换成 Serving 的格式:


如果报错:ModuleNotFoundError: No module named 'paddle_serving_client'


!pip install -U paddlenlp
复制代码


#安装依赖!pip install paddle_serving_client
复制代码


!python export_to_serving.py \    --dirname "output" \    --model_filename "inference.get_pooled_embedding.pdmodel" \    --params_filename "inference.get_pooled_embedding.pdiparams" \    --server_path "./serving_server" \    --client_path "./serving_client" \    --fetch_alias_names "output_embedding"
复制代码


# !python export_to_serving.py \#     --dirname "output" \#     --model_filename "inference.get_pooled_embedding.pdmodel" \#     --params_filename "inference.get_pooled_embedding.pdiparams" \#     --server_path "./serving_server_2" \#     --client_path "./serving_client_2" \#     --fetch_alias_names "predict"
复制代码


参数含义说明


  • dirname: 需要转换的模型文件存储路径,Program 结构文件和参数文件均保存在此目录。

  • model_filename: 存储需要转换的模型 Inference Program 结构的文件名称。如果设置为 None ,则使用 __model__ 作为默认的文件名

  • params_filename: 存储需要转换的模型所有参数的文件名称。当且仅当所有模型参数被保>存在一个单独的二进制文件中,它才需要被指定。如果模型参数是存储在各自分离的文件中,设置它的值为 None

  • server_path: 转换后的模型文件和配置文件的存储路径。默认值为 serving_server

  • client_path: 转换后的客户端配置文件存储路径。默认值为 serving_client

  • fetch_alias_names: 模型输出的别名设置,比如输入的 input_ids 等,都可以指定成其他名字,默认不指定

  • feed_alias_names: 模型输入的别名设置,比如输出 pooled_out 等,都可以重新指定成其他模型,默认不指定


也可以运行下面的 bash 脚本:


sh scripts/export_to_serving.sh
复制代码


Paddle Serving 的部署有两种方式,第一种方式是 Pipeline 的方式,第二种是 C++的方式,下面分别介绍这两种方式的用法:

8.4 Pipeline 方式

修改模型需要用到的Tokenizer


self.tokenizer = AutoTokenizer.from_pretrained("rocketqa-zh-base-query-encoder")
复制代码


然后启动 Pipeline Server:


cd deploy/pythonpython web_service.py
复制代码


启动客户端调用 Server。


首先修改 rpc_client.py 中需要预测的样本:


list_data = [    "国有企业引入非国有资本对创新绩效的影响——基于制造业国有上市公司的经验证据",    "试论翻译过程中的文化差异与语言空缺翻译过程,文化差异,语言空缺,文化对比"]
复制代码


然后运行:


python deploy/python/rpc_client.py
复制代码


注意:需要打开两个终端运行:效果如下




# #查看当前路径# !pwd# %cd deploy/python
复制代码


/home/aistudio/in_batch_negative/home/aistudio/in_batch_negative/deploy/python
复制代码


!pip install --user paddle-serving-app!pip install --user paddle-serving-client!pip install --user paddle-serving-server 
复制代码


!pip install exceptiongroup!pip install iniconfig!pip install func_timeout 
复制代码


# !pip install --user pydantic==1.10.7
复制代码


#cd /home/aistudio/in_batch_negative/deploy/python# !python web_service.py
复制代码


# !python rpc_client.py
复制代码


模型的输出为:


{'0': '国有企业引入非国有资本对创新绩效的影响——基于制造业国有上市公司的经验证据', '1': '试论翻译过程中的文化差异与语言空缺翻译过程,文化差异,语言空缺,文化对比'}PipelineClient::predict pack_data time:1641450851.3752182PipelineClient::predict before time:1641450851.375738['output_embedding'](2, 256)[[ 0.07830612 -0.14036864  0.03433796 -0.14967982 -0.03386067  0.06630666   0.01357943  0.03531194  0.02411093  0.02000859  0.05724002 -0.08119463   ......
复制代码


可以看到客户端发送了 2 条文本,返回了 2 个 embedding 向量

8.5 C++的方式

启动 C++的 Serving:


python -m paddle_serving_server.serve --model serving_server --port 9393 --gpu_id 0 --thread 5 --ir_optim True --use_trt --precision FP16
复制代码


也可以使用脚本:


sh deploy/cpp/start_server.sh
复制代码



Client 可以使用 http 或者 rpc 两种方式,rpc 的方式为:


python deploy/cpp/rpc_client.py
复制代码


运行的输出为:


I0209 20:40:07.978225 20896 general_model.cpp:490] [client]logid=0,client_cost=395.695ms,server_cost=392.559ms.time to cost :0.3960278034210205 seconds{'output_embedding': array([[ 9.01343748e-02, -1.21870913e-01,  1.32834800e-02,        -1.57673359e-01, -2.60387752e-02,  6.98455423e-02,         1.58108603e-02,  3.89952064e-02,  3.22783105e-02,         3.49135026e-02,  7.66086206e-02, -9.12970975e-02,         6.25643134e-02,  7.21886680e-02,  7.03565404e-02,         5.44054210e-02,  3.25332815e-03,  5.01751155e-02,......
复制代码


可以看到服务端返回了向量


或者使用 http 的客户端访问模式:


python deploy/cpp/http_client.py
复制代码


运行的输出为:


(2, 64)(2, 64)outputs {  tensor {    float_data: 0.09013437479734421    float_data: -0.12187091261148453    float_data: 0.01328347995877266    float_data: -0.15767335891723633......
复制代码


可以看到服务端返回了向量


!pip install --user paddle-serving-app!pip install --user paddle-serving-client!pip install --user paddle-serving-server 
复制代码


遇到下述报错自行查看下述资料解决一下即可


   from .serving_client import PredictorResImportError: libcrypto.so.10: cannot open shared object file: No such file or directory
复制代码


【linux缺失库文件】libssl.so.10: cannot open shared object file: No such file or directory

9. FAQ:SimCSE+IBN

9.1 基于无监督 SimCSE 训练出的模型参数作为参数初始化继续做有监督 In-Batch Negative 训练

使用场景:


  1. 数据需求:如果你拥有大量的无标注数据但是没有相应的标注数据,使用基于 SimCSE 的初始化会更有优势

  2. 直接有监督训练:需要有标注的训练数据,以便进行监督学习。这意味着你需要拥有带有正确标签的数据集。

  3. 基于 SimCSE 初始化的 IBN 训练:只需要无标注的训练数据。这对于大多数实际场景更具可行性,因为标注数据往往难以获取且成本较高。

  4. 训练速度:

  5. 直接有监督训练:由于从随机初始化开始,模型需要通过反向传播和梯度下降逐步优化参数,收敛所需时间可能较长。

  6. 基于 SimCSE 初始化的 IBN 训练:由于使用已经包含一定语义信息的 SimCSE 初始化参数,模型初始化时已经处于更好的状态,可能更快地收敛到较好的性能。这可以节省训练时间并加速模型的收敛。

  7. 效果改进:

  8. 直接有监督训练:从随机初始化开始训练,模型必须在有标注的数据上进行学习,逐渐学习任务特定的特征和表示。在初始阶段可能效果较差,需要更多的数据和时间来优化。

  9. 基于 SimCSE 初始化的 IBN 训练:由于使用 SimCSE 进行无监督预训练,模型在初始化时已经学习到了一定的语义信息和句子表示。这种初始化可以为模型提供更丰富的语义表示能力,并且更容易找到一个相对较好的局部最优点。通过 IBN 训练,模型可以进一步微调参数以适应特定的有监督任务,从而获得更好的性能。


  • 使用 --init_from_ckpt 参数加载即可,下面是使用示例:


# %cd ../../
复制代码


/home/aistudio/in_batch_negative
复制代码


!python -u -m paddle.distributed.launch --gpus "0" \    train_batch_neg.py \    --device gpu \    --save_dir ./checkpoints/simcse_inbatch_negative \    --model_name_or_path rocketqa-zh-base-query-encoder \    --batch_size 32 \    --learning_rate 5E-5 \    --epochs 3 \    --output_emb_size 256 \    --save_steps 100 \    --max_seq_length 64 \    --margin 0.2 \    --init_from_ckpt simcse/model_24000/model_state.pdparams \    --train_set_file /home/aistudio/literature_search_data/recall/train.csv \    --recall_result_dir "recall_result_dir" \    --recall_result_file "recall_result.txt" \    --hnsw_m 100 \    --hnsw_ef 100 \    --recall_num 50 \    --similar_text_pair_file "/home/aistudio/literature_search_data/recall/dev.csv" \    --corpus_file "/home/aistudio/literature_search_data/recall/corpus.csv"
复制代码


[2023-07-11 11:43:10,198] [    INFO] - tokenizer config file saved in ./checkpoints/simcse_inbatch_negative/model_300/tokenizer_config.json[2023-07-11 11:43:10,198] [    INFO] - Special tokens file saved in ./checkpoints/simcse_inbatch_negative/model_300/special_tokens_map.jsonglobal step 310, epoch: 3, batch: 60, loss: 0.25565, speed: 3.26 step/sglobal step 320, epoch: 3, batch: 70, loss: 0.61337, speed: 5.28 step/sglobal step 330, epoch: 3, batch: 80, loss: 0.53147, speed: 5.40 step/sglobal step 340, epoch: 3, batch: 90, loss: 1.36987, speed: 5.32 step/sglobal step 350, epoch: 3, batch: 100, loss: 0.22996, speed: 5.39 step/sglobal step 360, epoch: 3, batch: 110, loss: 0.45635, speed: 5.22 step/sglobal step 370, epoch: 3, batch: 120, loss: 0.17504, speed: 5.23 step/sLAUNCH INFO 2023-07-11 11:43:26,332 Pod completedLAUNCH INFO 2023-07-11 11:43:26,332 Exit code 0
复制代码


!python -u -m paddle.distributed.launch --gpus "0" --log_dir "recall_log/" \        recall.py \        --device gpu \        --recall_result_dir "recall_result_dir" \        --recall_result_file "recall_result.txt" \        --params_path "checkpoints/simcse_inbatch_negative/model_300/model_state.pdparams" \        --model_name_or_path rocketqa-zh-base-query-encoder \        --hnsw_m 100 \        --hnsw_ef 100 \        --batch_size 32 \        --output_emb_size 256\        --max_seq_length 60 \        --recall_num 50 \        --similar_text_pair_file "/home/aistudio/literature_search_data/recall/dev.csv" \        --corpus_file "/home/aistudio/literature_search_data/recall/corpus.csv"
复制代码


[2023-07-11 11:44:05,795] [    INFO] - start build index..........[2023-07-11 11:52:51,990] [    INFO] - Total index number:300000LAUNCH INFO 2023-07-11 11:53:35,724 Pod completedLAUNCH INFO 2023-07-11 11:53:35,724 Exit code 0
复制代码


!python -u evaluate.py \        --similar_text_pair "/home/aistudio/literature_search_data/recall/dev.csv" \        --recall_result_file "/home/aistudio/in_batch_negative/recall_result_dir/recall_result.txt" \        --recall_num 50
复制代码


recall@1=59.607recall@5=75.094recall@10=79.487recall@20=83.445recall@50=87.763
复制代码

9.2 总结


语义索引(可通俗理解为向量索引)技术是搜索引擎、推荐系统、广告系统在召回阶段的核心技术之一。语义索引模型的目标是:给定输入文本,模型可以从海量候选召回库中快速、准确地召回一批语义相关文本。语义索引模型的效果直接决定了语义相关的物料能否被成功召回进入系统参与上层排序,从基础层面影响整个系统的效果。本项目基于 In-batch negatives 是一种用于训练推荐系统或图像检索模型的策略。该策略的核心思想是在一个训练批次(batch)中,为每个正样本(目标样本)引入一些负样本(非目标样本),以增强模型的学习能力。



SimCSE+ In-batch Negatives 可以自行继续优化,整体效果提升不少。


更多优质内容请关注公号 &知乎:汀丶人工智能;会提供一些相关的资源和优质文章,免费获取阅读。




  • Reference

  • [1] Vladimir Karpukhin, Barlas Oğuz, Sewon Min, Patrick Lewis, Ledell Wu, Sergey Edunov, Danqi Chen, Wen-tau Yih, Dense Passage Retrieval for Open-Domain Question Answering, Preprint 2020.

  • [2] https://github.com/PaddlePaddle/PaddleNLP/blob/develop/applications/neural_search/recall/in_batch_negative

发布于: 刚刚阅读数: 5
用户头像

本博客将不定期更新关于NLP等领域相关知识 2022-01-06 加入

本博客将不定期更新关于机器学习、强化学习、数据挖掘以及NLP等领域相关知识,以及分享自己学习到的知识技能,感谢大家关注!

评论

发布
暂无评论
基于无监督训练SimCSE+In-batch Negatives策略有监督训练的语义索引召回_人工智能_汀丶人工智能_InfoQ写作社区