用sentencebert、sbert + faiss对比学习训练词向量原理与实战

命名实体规范化理论部分(相似度、对比学习、sentencebert)

一、应用

1、推广搜:类似推荐算法,召回相似向量表征的文本

2、求余弦相似度:两个词(句)向量求语义相似度

二、语义相似度

相似度有两种,一种是语义相似度,一种是拼写相似度。

语义相似度指两个字(词)的语义相似程度,例如猪和豚就是语义相似的,但是它两在拼写上差别很大。

拼写相似度指拼写上的很近,比如一句话只需要变一个字,变一次,就变成另一句。例如“我爱你” 和“我恨你”在拼写上很相似,但是语义相似度差很远。
虽然拼写相似度是基于在统计学角度,语义相似度和拼写相似度又有一些联系,有时候两个词(句)语义相似度越近它两的拼写相似度也会越近。

三、训练词向量

三种词向量训练模型的缺点

1、用CBOW和skip-gram训练出来的词向量是静态的,训练好的向量是多少就是多少,不会根据上下文的语义发生更改。如果新出现一个词,例如“不明觉厉”,模型能做的只是把这四个字的向量取出来相加,但是新的用法完全不一样了,简单的相加就完全不能代表这个词的语义。比如不明觉厉这个词是2020年出现的,用2020年前的语料库来训练的词向量就完全不能得到这个词的语义。

2、Elmo模型获得的词向量是半动态的。它是用双向的lstm(bi-lstm)来算的,但是它的语义抽取能力有限。

3、bert直接用于计算语义相似度效果也是有限的。比如对bert的输出向量不做任何形式的微调,比如“我爱你”和“我恨你”都放到bert模型中可以分别得到一个代表句子的CLS的向量,再计算他两的的相似度,实验效果不好。这是因为bert输出的向量并非标准正交基(参考 你可能不需要BERT-flow:一个线性变换媲美BERT-flow:https://spaces.ac.cn/archives/8069)。

bert直接用于计算语义相似度的缺陷

如果要用bert来计算两个句子sent1和sent2的语义相似度,比较好的方法是这样做:
1)把[CLS]sent1[SEP]sent2(就是把两句话通过[SEP]隔开)输入到bert,然后bert加全连接得到一个分数预测值,即[[pre]]。它是一个一行一列的数;
2)对于label,如果这两句话完全相同,设置成1,如果相似度完全不同就设为0。这个值可以自己设置,但要把握上限和下限;
3)然后做loss。把pre和label做一个MSE,就是两个值相减然后平方(a-b)2。在训练阶段有很多这样的句子对,都给出一个相似度分数;
4)在预测的时候也是把两句拼接预测出一个分数,就是两个句子之间的语义相似度

但是这种方式问题很大。一是速度很慢。比如现在有一个句子query,如果后面是4个句子,要从4个句子找到最相似的,就需要把q分别和sent1\sent2\sent3\sent4 计算4次相似度,得到最大的概率就是最大的相似度。而bert的推理速度是比较慢的,如果在1000句话找与目标句子最相似的句子呢?
(注:在推广搜里,bert速度很慢;一般工业上用TensorFlow比pytorch多一点)

基于bert的改进算法——对比学习(sbert/sentencebert)

改进方法的一种(常用):对比学习双塔模型。即通过对比学习,共享权重的方式计算相似度。
在这里插入图片描述
首先两句话分别输入两个network(bert类的模型,因为bert是编码器,语义理解能力更强),得到各自的embedding(注意这里两个network bert1和bert2虽然是两个模型,但是由于它们参数公用,如果更新参数就一起更新,所以这两个模型也可以理解成一个模型),再把这两个向量乘起来算余弦相似度,在和相似度标签label_cos计算损失做反向传播,以上就是对比学习(sentencebert)的训练过程。

sbert模型结构设计的目的:大量缩短计算量。
例如有两个query:query1和query2,想要分别计算与sentence1~100个句子的相似度。按照之前的方式把 [CLS]query[SEP]sentence 输入到bert,需要计算100*2=200次;而用sentencebert后,只需要2+100=102次(sentence1~100计算完成后存储到本地,一般用训练好的模型直接计算出词向量并保存本地,部署时可直接调用,提高了推理效率),而且query越多,两种计算需要的次数/时间差别就会越大。

两种sbert模型结构

1、采用分类方式
在这里插入图片描述

把sentence A 和sentence B 分别输入BERT中,得到嵌入词向量,然后再把每一个子词的向量拿出来之后做一个平均池化average pooliing,得到向量u和v,再把 (u,v,|u-v|) (论文中用了很多种方式处理u和v,包括|u-v|、(u,v)、(u,v,|u-v|)等三种方式,经过实验发现 (u,v,|u-v|) 效果最好)的结果接一个全连接层去调节它的输出,最后接softmax做分类(标签是0,1,-1这种离散的形式)。

注意:把sentence A和B输入bert可以得到输出是CLS和每一个子词的向量,如何使用呢?一开始作者直接拿CLS这个向量来表征语句的相似性,发现效果很差,所以才把输出的每一个子词做pooling平均池化来代表这个句子的语义。

2、采用余弦相似度方式
在这里插入图片描述
同样把sentence A 和sentence B 分别输入BERT中,得到嵌入词向量,然后再把每一个子词的向量拿出来之后做一个平均池化average pooliing,得到向量u和v,然后直接算u,v的余弦相似度。 (这种方法特别适合数据集比较好的情况,例如就是两句话A和B,两者之间的相似度是0~1之间任意的数,连续非离散,即一个回归任务。这种方式能学习到两个语句之间相似性有多高,比上一种分类方式效果要好。因为如果用softmax classifier这种方式,语句A和B的标签是0,1,-1等,用这样的标签去做相似性的话(用分类任务去做或者用余弦相似度来做),预测结果都倾向于出现两极分化。例如,预测句子A’和B’的相似度,要么非常接近于1,经常是0.95以上;要么就是接近0,是0.05以下的分数,就是两级分化,很难表示二者之间到底相似多少,就是很难去量化,这种是在相似性中经常碰到的问题。)

损失函数 triplet loss

在这里插入图片描述
Sa是目标句,Sp是正样本句,Sn是负样本句,三种都是嵌入向量。triplet loss的目标就是让同类内的间距变小,让类外的间距变大。ϵ(epsilon)的作用就是使得和正样本的距离至少要比和负样本的距离小ϵ这个数。这样,如果正样本的距离比(负样本的距离 - ϵ)小,那么就说明目标句和正样本很接近,和负样本距离远,损失函数就是0,不进行梯度更新;反之则产生损失函数,进行梯度更新。

设定一个ϵ常量,可以迫使模型努力学习,能让a和负例n的distance值更大,同时让a和正例p的distance值更小。
由于ϵ的存在,使得triplets loss多了一个参数,ϵ的大小需要调参。如果ϵ太大,则模型的损失会很大,而且学习到最后,loss也很难趋近于0,甚至导致网络不收敛,但是可以较有把握的区分较为相似的样本,即a和p更好区分;如果ϵ太小,loss很容易趋近于0,模型很好训练,但是较难区分a和p。
所以我们希望目标样本和正样本之间的距离 和 目标样本和负样本之间的距离 差距至少要大于常数ϵ,可以设定为0.1、0.2、0.3、0.5、0.4等等。注:默认是1
参考nn.TripletMarginLoss:
torch.nn.TripletMarginLoss(margin=1.0, p=2.0, eps=1e-06, swap=False, size_average=None, reduce=None, reduction=‘mean’)

sbert模型训练标签设计

两种方式:
1、如果做分类,标签可以设置成-1,0,1 -1这种;
2、也可以用具体的数值,例如用 5.0 表示非常相关,0.0 表示不相关,2.46可以表示相关程度是2.46(但这样具体数值的标签对于打标签的人要求是非常高的,不仅非常懂业务,而且还很耗时、昂贵)。

对比学习负样本构造

对比学习负样本的构造十分关键,因为如果负样本构造的质量不佳,模型会学习不到什么东西;如果构造的过于复杂,模型可能不收敛。这是因为受到triplet loss的设计影响。
负样本的构造决定了算法的上限有多高。 那么如何构造负样本?构造思路的核心是正负样本在某种情况下不是太好区分。例如,在sentencebert中,可以采用拼写相似度。因为拼写相似度是基于字母或者词根、词缀这样的统计,它是不联系上下文的。一句话里面把怎么把几个词的位置调换一下,很容易就得到另外一个句子,这个就是拼写相似度,可以理解为这两个词长的比较像。而语义相似度是指这两句话语义比较近。基于bert的模型都是根据上下文计算的,所以更加倾向于语义,不是判断这两个词长像不像。所以在sbert中构造负样本的一种办法就是可以用拼写相似度很高的词来构造,即用长得非常像的词构造负样本,比如英文环境下目标样本是apple,正样本就是apple,那么负样本可以构造为app,语义相差比较大,这样构造负样本效果比较一般。但是在中文环境下,很多拼写相似度很高的词,他们的语义相似度也是很接近的,所以可以用拼写比较接近的词来构造负样本。还有一种构造方法是直接用基于模型训练失败的、无法正常区分的样本来做负样本。 以上两种都是比较好的构造负样本方式。(还可以用bm25、ifidf算法召回与A样本相似性高的k个样本,去掉A样本后剩下的都是负样本)

还有一种更好、更快的构造负样本及打标签方式:
现在我们的数据是有很多个正样本句,如A 和 A’是一对正样本(语义相同一对句子、实体等),B和B’、C和C’。。。也是相同的正样本对。但是没有负样本,此时构造负样本时,可以用A与B’、C’、D’、E’构造4个负样本,因为在要做相似性计算时,你的应用场景只需要找出正样本来就行了,那么在训练时是取一个batch来做训练,假设此时batchsize=5,那么我们构造如下矩阵:
在这里插入图片描述
作为训练的数据标签。所以对于batch_size=5时,构造正负样本的比例是,正样本5,负样本20,正负样本比例为1:4。
这样构建负样本的好处是:
1)由于batch是随机取的,所以A与B相同的概率比较小;
2)构造方便,省去打标签的时间
3)运算速度比较快
缺点:对显存要求比较高,如果batch size取256,那么一个正样本就有255个负样本,很吃显存。

sentencebert实战代码(一)

实战项目:识别bom单型号/品牌/数量,并给出与标准bom单相似度的工业应用

目标:用文本相似度来识别/判断一个文本属于哪个标签(型号/品牌/数量)

示例

texts = ['AML8726-M3', '240']

对texts 中文本进行相似度判断,在标准向量库(根据业务来设定)中召回k(建议5)个最相似的文本,例如对’AML8726-M3’进行sbert相似度,召回下面5个最相似的文本:

['MM8930-2600RJ4', 'SS3P6-M3/84A', 'PM8054B-F3EI', 'AM29F200AB-90ED', 'SML4756A-E3/61']

在词/句向量库中,这5个文本的标签都是“型号”,那么待判断的’AML8726-M3’的标签是“型号”;同理,对’240’的召回是[‘258’, ‘248’, ‘263’, ‘256’, ‘268’],标签均是“数量”,则待识别或判断的’240’的标签就是“数量”。

训练/测试/验证数据集样式

训练/测试/验证集train.csv、test.csv、dev.csv样式如下

[('SONGCHUAN', '0.1172', 0),
 ('83.98', 'G5Q-14 DC24', 0),
 ('KALER', 'TPIC7218QPFPRQ1', 0),
 ('61814', '6645', 1),
 ('NIC COMPONENTS', 'AD628ARMZ-R7', 0),
 ('10.268474', '1639', 0),
 ('L78M05CDT-TR', 'PIC12F509-E/SN', 1),
 ('PIC16F753-I/ML', '0.313', 0),
 ('1750+', '1.812', 0),
 ('2118+', '1547', 1),
 ('30043', '下单限制DC', 0),...]

训练/测试/验证数据集分别保存为train.csv、test.csv、dev.csv三个文件。例如一行数据:‘SONGCHUAN’, ‘0.1172’, 0 分别表示样本1、样本2以及它们之间的相似度0,所以第一行就是负样本;同理’61814’, ‘6645’, 1 代表了’61814’ 与’6645’的相似度是1,它们是正样本。一般负样本数量要远大于正样本,可以是5倍的比例关系。

sbert模型微调训练

参考官网:https://www.sbert.net/docs/training/overview.html
官方完整项目训练脚本:https://github.com/UKPLab/sentence-transformers/blob/master/examples/training/sts/training_stsbenchmark.py

使用 pip 安装依赖:

pip install sentence_transformers

数据集路径示例:

'text_matching/input/train.csv'

微调后模型保存路径:

'./two_albert_similarity_model'

简单训练脚本train.py:

from sentence_transformers import SentenceTransformer, SentencesDataset 
from sentence_transformers import InputExample, evaluation, losses
from torch.utils.data import DataLoader

# 加载初始模型参数
# Define the model. Either from scratch of by loading a pre-trained model
# distiluse-base-multilingual-cased 蒸馏得到的,官方预训练好的模型 ,20轮准确率0.57 519M **
# model = SentenceTransformer('distiluse-base-multilingual-cased')
# model = SentenceTransformer('all-distilroberta-v1')  # 备选模型1:20轮0.56 323M ** 
model = SentenceTransformer('paraphrase-distilroberta-base-v2')  # 备选模型2:20轮 0.58 316M ***


# 加载数据集,训练、验证、测试数据量比例建议8:1:1
def load_data(filename):
    D = []
    with open(filename, encoding='utf-8') as f:
        for l in f:
            try:
                text1, text2, label = l.strip().split(',')
                D.append((text1, text2, int(label)))
            except ValueError:
                break
    return D
train_data = load_data('text_matching/input/train.csv')
valid_data = load_data('text_matching/input/dev.csv')
test_data = load_data('text_matching/input/test.csv')

# Define your train examples.
train_datas = []
for i in train_data:
    train_datas.append(InputExample(texts=[i[0], i[1]], label=float(i[2])))
# Define your evaluation examples
sentences1,sentences2,scores = [],[],[]
for i in valid_data:
    sentences1.append(i[0])
    sentences2.append(i[1])
    scores.append(float(i[2]))
evaluator = evaluation.EmbeddingSimilarityEvaluator(sentences1, sentences2, scores)

# Define your train dataset, the dataloader and the train loss
train_dataset = SentencesDataset(train_datas, model)
train_dataloader = DataLoader(train_dataset, shuffle=True, batch_size=4)
train_loss = losses.CosineSimilarityLoss(model)

# Tune the model  训练模型,注意:根据垂直任务调参。最佳调参参考:batch_size=4,epochs=10,warmup_steps=100,evaluation_steps=100
model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=10, warmup_steps=100,
          evaluator=evaluator, evaluation_steps=100, output_path='./two_albert_similarity_model')
# 可增加参数,学习率调整:optimizer_params={'lr': 3e-5}
# 修改参数参考1,官网: https://www.sbert.net/docs/training/overview.html
# 修改参数参考2,examples: https://python.hotexamples.com/examples/sentence_transformers/SentenceTransformer/fit/python-sentencetransformer-fit-method-examples.html

# 向量相似度的测评:
# Define your evaluation examples
sentences1,sentences2,scores = [],[],[]
for i in test_data:
    sentences1.append(i[0])
    sentences2.append(i[1])
    scores.append(float(i[2]))
evaluator = evaluation.EmbeddingSimilarityEvaluator(sentences1, sentences2, scores)
print(model.evaluate(evaluator))

# 模型准确度的测评:
'''
Evaluate a model based on the similarity of the embeddings by calculating the accuracy of 
identifying similar and dissimilar sentences. The metrics are the cosine similarity as well 
as euclidean and Manhattan distance The returned score is the accuracy with a specified metric.
'''
evaluator = evaluation.BinaryClassificationEvaluator(sentences1, sentences2, scores)
print(model.evaluate(evaluator))

训练结果:
训练结果

训练好的模型参数文件:
在这里插入图片描述

微调后sbert模型预测

一、相似度判断
模型训练好后,就可以简单加载和预测文本之间的相似度

from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer('./two_albert_similarity_model')
# Sentences are encoded by calling model.encode()
emb1 = model.encode('AML8726-M3')
emb2 = model.encode('MM8930-2600RJ4')
print(emb1)
print(emb2)
cos_sim = util.pytorch_cos_sim(emb1, emb2)
print("Cosine-Similarity:", cos_sim)

结果如下:
在这里插入图片描述

两个文本的相似度是0.77

二、结合faiss从本地保存的向量库中,快速找出相似度高的k个文本和其类别标签
faiss库简介:https://blog.csdn.net/qq_27782503/article/details/99790406

实现效果:给定某个文本,通过sbert和faiss快速查找label.csv中相似度最高的5个文本和其标签,统计标签的类别数量,返回数量最多的标签,即为给定文本预测的标签

1、创建标签原始文件

label.csv是工业垂直领域私有库的标签原始文件,示例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述label.csv三列分别是:index列,text文本、label标签
label.csv合成方法:

# q_array是文本列表,a_array是对应的标签列表
qarray = pd.DataFrame({'text': q_array})
aarray = pd.DataFrame({'label': a_array})
array = pd.concat([qarray, aarray], axis=1)
#检查是否有空置
array = array.dropna()
array.to_csv('input/label.csv')  # 标签,用于预测查询相似度高的文本标签

2、network搭建
功能实现:第一次预测时,先将label.csv中的文本通过sbert进行embedding,得到词向量,再用faiss将词向量和其标签保存到本地中;以后每次预测,直接用sbert embed待预测文本,并与faiss数据库中所有其他文本直接进行相似度计算,而不用每次预测时重新embed label.csv中的所有标签文本,从而大大减少计算量

import torch
import numpy as np
import pandas as pd
import faiss
from sentence_transformers import SentenceTransformer

class SBERTModel(SentenceTransformer):
    def __init__(self, config):
        super().__init__(config)
        self.sbert = SentenceTransformer(config)
        
    def infer(self, ALL, train_data, index, split_data, k):
        query = [self.sbert.encode(i) for i in split_data]
        query = np.array(query, dtype=np.float32)
        D, I = index.search(query, k)
        text = []
        for i in range(len(query)):
            text.append([ALL[j] for j in I[i]])
        labels = []
        probs = []
        for res, d in zip(text, D):
            vc = train_data.loc[train_data['text'] == res[-1]]  # 先取出相似度最大text和其label
            for i in res[:-1]:
                c = train_data.loc[train_data['text'] == i]
                vc = pd.concat([vc, c])
            count = vc['label'].value_counts().to_frame()  # 统计label出现次数
            label = count.index[0]  # k个相似度文本中 出现次数最多的一个品类即为sbert的预测品类
            labels.append(label)
            l = vc['label'].tolist()
            prob = []
            total = 0
            for i in range(k):
                if l[i] == label:
                    prob.append(d[i])
                    total += d[i]
            probs.append(total / len(prob))  # 平均概率
        results = {}
        for i, p, l in zip(split_data, probs, labels):
            if l in results and l == '品牌':
                pp = (results[l][0]['probability'] + p) / 2
                ii = results[l][0]['text'] + ' ' + i
                result = {}
                result['probability'] = pp
                result['text'] = ii
                results[l] = [result]
            else:
                result = {}
                result['probability'] = p
                result['text'] = i
                results[l] = [result]
        return results
        
    def forward(self, input=None):  # 传参格式[texts, k1],texts是待识别文本列表,k1为相似文本数量  待修改:增加entity参数
        train_data = pd.read_csv("text_matching/input/label.csv", encoding='utf-8')  # 标签数据 
        # 以下8行仅第一次推理时运行,将全部带标签文本用sbert embed并保存词向量到本地
        ALL = []
        for i in train_data['text']:
            ALL.append(i)
        ALL = list(set(ALL))
        DT = self.sbert.encode(ALL)
        # Store sentences & embeddings on disc
        with open('text_matching/input/embeddings.pkl', "wb") as fOut:
             pickle.dump({'sentences': ALL, 'embeddings': DT}, fOut, protocol=pickle.HIGHEST_PROTOCOL)
        # Load sentences & embeddings from disc
        with open('text_matching/input/embeddings.pkl', "rb") as fIn:
            stored_data = pickle.load(fIn)
            ALL = stored_data['sentences']
            DT = stored_data['embeddings']
        DT = np.array(DT, dtype=np.float32)
        index = faiss.IndexFlatL2(DT[0].shape[0])  # 仅第一次推理时运行,建立索引
        faiss.write_index(index, "text_matching/input/label.index")  # 仅第一次推理时运行,保存索引
        # index = faiss.read_index("text_matching/input/label.index")
        index.add(DT)
        inputs, k = input
        if not k:
            k = 1
        k = int(k)
        result = []
        for input_data in inputs:
            sen = str(input_data)
            split_data = sen.split()
            res = self.infer(ALL, train_data, index, split_data, k)
            result.append(res)
        return result

3、推理脚本

from sentence_transformers import SentenceTransformer, util
import numpy as np
import faiss                   # make faiss available
import pandas as pd
import argparse
from network import SBERTModel
from sbert_predictor import sbertpredictor
import torch
def parse_args():
    parser = argparse.ArgumentParser()
    # Required parameters
    parser.add_argument(
        "--model_path_prefix",
        default='two_albert_similarity_model',
        type=str,
        #required=True,
        help="The path prefix of inference model to be used.",
    )
    parser.add_argument(
        "--train_data",
        default='text_matching/input/label.csv',
        type=str,
        help="The path prefix of inference model to be used.",
    )
    args = parser.parse_args()
    return args
def main():
    args = parse_args()
    texts = ['AML8726-M3  Amlogic Inc. 402', '1077000000010  ABV International Pte Ltd 1560', 'WINBOND  , W25Q64JVSSIQT_SOIC8  ,8K, 8000,', 'ASM2480B  ASMEDIA 35K']
    k1 = 5  # k指蕴含多少数量相似文本,例如k=5,则对于文本每一个内容都寻找5个相似文本,取他们的平均类别为预测类别。k越大,计算时间越长,建议k小于5
    args.device = 'cpu'
    args.k = k1
    # predictor = sbertpredictor(args) #sbertpredictor是搭建的第二个network,用于得到词向量以后得预测
    #
    # print("-----------------------------")
    # outputs = predictor.predict(texts)
    # print(outputs)
    # for text, output in zip(texts, outputs):
    #     print("1. Input text: ")
    #     print(text)
    #     print("2. Input k: ")
    #     print(k1)
    #     print("3. Result: ")
    #     print(output)  # [相似文本、相似概率、类别]
    #     print("-----------------------------")
    model = SBERTModel(args.model_path_prefix)
    output = model([texts, k1])
    print(output)

注意:以上是第一次推理脚本,用于生成词向量和数据库;第二次运行时,运行注释的脚本,因为sbertpredictor类已经可以直接用第一次生成好的词向量库了。
运行结果:
在这里插入图片描述

[{'型号': [{'probability': 0.32390467524528505, 'text': 'AML8726-M3'}], '品牌': [{'probability': 0.3737347714602947, 'text': 'Amlogic Inc.'}], '数量': [{'probability': 0.1193802833557129, 'text': '402'}]}, {'数量': [{'probability': 0.10616649314761162, 'text': '1560'}], '品牌': [{'probability': 0.3184409024814765, 'text': 'ABV International Pte Ltd'}]}, {'品牌': [{'probability': 0.16627033054828644, 'text': 'WINBOND'}], '批次': [{'probability': 0.082051416238149, 'text': ','}], '型号': [{'probability': 0.13442104905843735, 'text': 'W25Q64JVSSIQT_SOIC8'}], '数量': [{'probability': 0.12874335199594497, 'text': '8000,'}]}, {'型号': [{'probability': 0.22181276082992554, 'text': 'ASM2480B'}], '品牌': [{'probability': 0.17965687513351442, 'text': 'ASMEDIA'}], '数量': [{'probability': 0.33026751279830935, 'text': '35K'}]}]

sentencebert实战代码(二)

实战项目:识别bom单型号/品牌/数量,并给出与标准bom单相似度的工业应用

目标:用文本相似度来识别/判断一个文本属于哪个标签(型号/品牌/数量)

示例

texts = ['AML8726-M3', '240']

对texts 中文本进行相似度判断,在标准向量库(根据业务来设定)中召回k(建议5)个最相似的文本,例如对’AML8726-M3’进行sbert相似度,召回下面5个最相似的文本:

['MM8930-2600RJ4', 'SS3P6-M3/84A', 'PM8054B-F3EI', 'AM29F200AB-90ED', 'SML4756A-E3/61']

在词/句向量库中,这5个文本的标签都是“型号”,那么待判断的’AML8726-M3’的标签是“型号”;同理,对’240’的召回是[‘258’, ‘248’, ‘263’, ‘256’, ‘268’],标签均是“数量”,则待识别或判断的’240’的标签就是“数量”。

训练/测试/验证数据集样式

训练/测试/验证集train.csv、test.csv、dev.csv样式如下

[('SONGCHUAN', '0.1172', 0),
 ('83.98', 'G5Q-14 DC24', 0),
 ('KALER', 'TPIC7218QPFPRQ1', 0),
 ('61814', '6645', 1),
 ('NIC COMPONENTS', 'AD628ARMZ-R7', 0),
 ('10.268474', '1639', 0),
 ('L78M05CDT-TR', 'PIC12F509-E/SN', 1),
 ('PIC16F753-I/ML', '0.313', 0),
 ('1750+', '1.812', 0),
 ('2118+', '1547', 1),
 ('30043', '下单限制DC', 0),...]

训练/测试/验证数据集分别保存为train.csv、test.csv、dev.csv三个文件。例如一行数据:‘SONGCHUAN’, ‘0.1172’, 0 分别表示样本1、样本2以及它们之间的相似度0,所以第一行就是负样本;同理’61814’, ‘6645’, 1 代表了’61814’ 与’6645’的相似度是1,它们是正样本。一般负样本数量要远大于正样本,可以是5倍的比例关系。

sbert模型微调训练

### 使用BERT计算词向量相似度 为了利用BERT模型来计算词向量的相似度,可以采用预训练好的BERT模型提取句子或词语的嵌入表示,并通过这些嵌入表示进一步计算它们之间的余弦相似度。具体过程如下: #### 安装依赖包 在Python环境中安装必要的库文件以便于后续操作能够顺利执行。 ```bash pip install torch==1.7.0+cpu torchvision==0.8.1+cpu torchaudio===0.7.0 -f https://download.pytorch.org/whl/torch_stable.html pip install transformers==4.26.0 -i https://pypi.tuna.tsinghua.edu.cn/simple ``` #### 加载预训练模型并处理输入数据 加载transformers库中的`AutoModel`类以及相应的分词器(`AutoTokenizer`)用于准备待比较文本的数据格式化工作。 ```python from transformers import AutoTokenizer, AutoModel import torch tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased') model = AutoModel.from_predefined('bert-base-uncased') def mean_pooling(model_output, attention_mask): token_embeddings = model_output[0] #First element of model_output contains all token embeddings input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float() sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1) sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9) return sum_embeddings / sum_mask ``` #### 获取句子嵌入向量 定义函数用来获取给定句子对应的BERT嵌入向量。 ```python def encode_sentence(sentence): encoded_input = tokenizer(sentence, padding=True, truncation=True, max_length=128, return_tensors='pt') with torch.no_grad(): model_output = model(**encoded_input) sentence_embedding = mean_pooling(model_output, encoded_input['attention_mask']) return sentence_embedding.cpu().numpy()[0] ``` #### 计算两句话语义上的相似程度 最后一步则是基于上述获得到的两个句子各自的嵌入向量去求解二者间的余弦相似度得分。 ```python from scipy.spatial.distance import cosine sentence_1 = "我喜欢吃苹果" sentence_2 = "我不喜欢吃香蕉" embedding_1 = encode_sentence(sentence_1) embedding_2 = encode_sentence(sentence_2) similarity_score = 1 - cosine(embedding_1, embedding_2) print(f"Cosine Similarity between '{sentence_1}' and '{sentence_2}': {similarity_score}") ``` 此方法展示了怎样借助BERT模型捕捉自然语言表达背后深层次的信息结构特征,并以此为基础评估不同表述间潜在关联性的强弱关系[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值