基于无监督训练 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)的方式进行训练,每个三元组由一个锚点样本、一个正样本和一个负样本组成。目标是使得正样本与锚点样本间的距离小于负样本与锚点样本间的距离,从而达到更好的样本区分效果。
这些算法策略都旨在通过引入负样本或调整样本间的相似度关系,从而提高模型对目标样本的识别和检索能力。具体选择哪种策略取决于具体的任务和数据特点。
在召回阶段,最常见的方式是通过双塔模型,学习 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.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
: 正样本相似度与负样本之间的目标 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 脚本:
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
复制代码
/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 脚本:
参数含义说明
similar_text_pair
: 由相似文本对构成的评估集 semantic_similar_pair.tsv
recall_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:'国有企业引入非国有资本对创新绩效的影响——基于制造业国有上市公司的经验证据'}
复制代码
然后运行:
预测结果为 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 脚本:
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 脚本:
最终输出的是 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
: 存储需要转换的模型所有参数的文件名称。当且仅当所有模型参数被保>存在一个单独的二进制文件中,它才需要被指定。如果模型参数是存储在各自分离的文件中,设置它的值为 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/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
复制代码
模型的输出为:
{'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 训练,模型可以进一步微调参数以适应特定的有监督任务,从而获得更好的性能。
/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)中,为每个正样本(目标样本)引入一些负样本(非目标样本),以增强模型的学习能力。
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
评论