基于无监督训练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)的方式进行训练,每个三元组由一个锚点样本、一个正样本和一个负样本组成。目标是使得正样本与锚点样本间的距离小于负样本与锚点样本间的距离,从而达到更好的样本区分效果。
这些算法策略都旨在通过引入负样本或调整样本间的相似度关系,从而提高模型对目标样本的识别和检索能力。具体选择哪种策略取决于具体的任务和数据特点。
- 常见的策略对比:
模型 | 策略简要说明 |
---|---|
Baseline | 标准 pair-wise 训练范式,通过随机采样产生负样本 |
In-batch negatives | 在 Batch 内同时使用 batch_size 个负样本进行训练 |
HardestNeg | 在 Batch 内先挖掘最难负样本,然后进行 pair-wise 训练 |
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个结果)结果中检索出的相关结果数和库中所有的相关结果数的比率,衡量的是检索系统的查全率。
效果评估展示
策略 | 模型 | Recall@1 | Recall@5 | Recall@10 | Recall@20 | Recall@50 |
---|---|---|---|---|---|---|
In-batch Negatives | ernie 1.0 | 51.301 | 65.309 | 69.878 | 73.996 | 78.881 |
In-batch Negatives | rocketqa-zh-base-query-encoder | 59.271 | 74.387 | 78.82 | 82.831 | 86.955 |
In-batch Negatives | SimCSE+rocketqa-zh-base-query-encoder | 59.607 | 75.094 | 79.487 | 83.445 | 87.763 |
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
Model | 训练参数配置 | 硬件 | MD5 |
---|---|---|---|
batch_neg | ernie 1.0 margin:0.2 scale:30 epoch:3 lr:5E-5 bs:64 max_len:64 | 4卡 v100-16g | f3e5c7d7b0b718c2530c5e1b136b2d74 |
训练环境说明
- 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.json
global 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.json
LAUNCH INFO 2023-07-07 17:08:36,702 Pod completed
LAUNCH 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
: 正样本相似度与负样本之间的目标 Gaptrain_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_fileuse_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/inbatch
model_10 model_30 model_50 model_70 model_90
model_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.607
recall@5=75.094
recall@10=79.487
recall@20=83.445
recall@50=87.763
也可以使用下面的bash脚本:
sh scripts/evaluate.sh
参数含义说明
similar_text_pair
: 由相似文本对构成的评估集 semantic_similar_pair.tsvrecall_result_file
: 针对评估集中第一列文本 Source Text 的召回结果recall_num
: 对 1 个文本召回的相似文本数量
成功运行结束后,会输出如下评估指标:
recall@1=59.271
recall@5=74.387
recall@10=78.82
recall@20=82.813
recall@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.9086097478866577
0.2573563754558563
0.0023693442344665527
0.7471159100532532
0.9782603979110718
0.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
[32m[2023-07-07 17:45:08,502] [ INFO][0m - We are using <class 'paddlenlp.transformers.ernie.modeling.ErnieModel'> to load 'rocketqa-zh-base-query-encoder'.[0m
[32m[2023-07-07 17:45:08,502] [ INFO][0m - 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
}
[0m
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.2
W0707 17:45:10.407172 8461 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.
[33m[2023-07-07 17:45:11,056] [ WARNING][0m - 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).[0m
[32m[2023-07-07 17:45:11,056] [ INFO][0m - 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.[0m
[32m[2023-07-07 17:45:11,056] [ INFO][0m - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load 'rocketqa-zh-base-query-encoder'.[0m
[32m[2023-07-07 17:45:11,056] [ INFO][0m - Already cached /home/aistudio/.paddlenlp/models/rocketqa-zh-base-query-encoder/ernie_3.0_base_zh_vocab.txt[0m
[32m[2023-07-07 17:45:11,078] [ INFO][0m - tokenizer config file saved in /home/aistudio/.paddlenlp/models/rocketqa-zh-base-query-encoder/tokenizer_config.json[0m
[32m[2023-07-07 17:45:11,078] [ INFO][0m - Special tokens file saved in /home/aistudio/.paddlenlp/models/rocketqa-zh-base-query-encoder/special_tokens_map.json[0m
Loaded parameters from checkpoints/inbatch/model_90/model_state.pdparams
[0m
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_Design和Serving_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
: 存储需要转换的模型所有参数的文件名称。当且仅当所有模型参数被保>存在一个单独的二进制文件中,它才需要被指定。如果模型参数是存储在各自分离的文件中,设置它的值为 Noneserver_path
: 转换后的模型文件和配置文件的存储路径。默认值为 serving_serverclient_path
: 转换后的客户端配置文件存储路径。默认值为 serving_clientfetch_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/python
python 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.3752182
PipelineClient::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 PredictorRes
ImportError: 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 训练
使用场景:
- 数据需求:如果你拥有大量的无标注数据但是没有相应的标注数据,使用基于SimCSE的初始化会更有优势
- 直接有监督训练:需要有标注的训练数据,以便进行监督学习。这意味着你需要拥有带有正确标签的数据集。
- 基于SimCSE初始化的IBN训练:只需要无标注的训练数据。这对于大多数实际场景更具可行性,因为标注数据往往难以获取且成本较高。
- 训练速度:
- 直接有监督训练:由于从随机初始化开始,模型需要通过反向传播和梯度下降逐步优化参数,收敛所需时间可能较长。
- 基于SimCSE初始化的IBN训练:由于使用已经包含一定语义信息的SimCSE初始化参数,模型初始化时已经处于更好的状态,可能更快地收敛到较好的性能。这可以节省训练时间并加速模型的收敛。
- 效果改进:
- 直接有监督训练:从随机初始化开始训练,模型必须在有标注的数据上进行学习,逐渐学习任务特定的特征和表示。在初始阶段可能效果较差,需要更多的数据和时间来优化。
- 基于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.json
global step 310, epoch: 3, batch: 60, loss: 0.25565, speed: 3.26 step/s
global step 320, epoch: 3, batch: 70, loss: 0.61337, speed: 5.28 step/s
global step 330, epoch: 3, batch: 80, loss: 0.53147, speed: 5.40 step/s
global step 340, epoch: 3, batch: 90, loss: 1.36987, speed: 5.32 step/s
global step 350, epoch: 3, batch: 100, loss: 0.22996, speed: 5.39 step/s
global step 360, epoch: 3, batch: 110, loss: 0.45635, speed: 5.22 step/s
global step 370, epoch: 3, batch: 120, loss: 0.17504, speed: 5.23 step/s
LAUNCH INFO 2023-07-11 11:43:26,332 Pod completed
LAUNCH 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:300000
LAUNCH INFO 2023-07-11 11:53:35,724 Pod completed
LAUNCH 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.607
recall@5=75.094
recall@10=79.487
recall@20=83.445
recall@50=87.763
9.2 总结
- 语义搜索系列文章全流程教学:
更多文本匹配方案参考:
语义索引(可通俗理解为向量索引)技术是搜索引擎、推荐系统、广告系统在召回阶段的核心技术之一。语义索引模型的目标是:给定输入文本,模型可以从海量候选召回库中快速、准确地召回一批语义相关文本。语义索引模型的效果直接决定了语义相关的物料能否被成功召回进入系统参与上层排序,从基础层面影响整个系统的效果。本项目基于In-batch negatives是一种用于训练推荐系统或图像检索模型的策略。该策略的核心思想是在一个训练批次(batch)中,为每个正样本(目标样本)引入一些负样本(非目标样本),以增强模型的学习能力。
策略 | 模型 | Recall@1 | Recall@5 | Recall@10 | Recall@20 | Recall@50 |
---|---|---|---|---|---|---|
In-batch Negatives | ernie 1.0 | 51.301 | 65.309 | 69.878 | 73.996 | 78.881 |
In-batch Negatives | rocketqa-zh-base-query-encoder | 59.271 | 74.387 | 78.82 | 82.831 | 86.955 |
In-batch Negatives | SimCSE+rocketqa-zh-base-query-encoder | 59.607 | 75.094 | 79.487 | 83.445 | 87.763 |
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.