赞
踩
智能问答是NLP领域落地最多的场景,其商业价值较高,能有效解决业务问题,降低人力成本。智能问答分为封闭域问答与开放域问答两种。封闭域的问答是指根据用户的问题,从已有问答库中找出最匹配的答案返回给用户。而开放域问答则是根据用户的问题,由模型根据已学会的知识生成相应的答案返回给用户。由于当前开放域问答存在回答不可控的问题,在工业界落地项目较少。而封闭域问答是NLP领域中落地最多的项目之一。封闭域问答最常见的形式是FAQ问答,即常见问题解答。
在NLP中,对FAQ任务的解决方案是,将用户会遇到的常见问题收集起来构建一个问答库,问答库中的每个条目包含问题和最佳答案。用户提问时,智能问答模型能根据用户的问题从问答库中查找最佳答案返回给用户。找最佳答案的方法有两种,一种做法是将用户的问题与问答库中的问题进行相似度计算,将问答库中相似度最高的问题所对应的回答返回给用户。另一种做法是将用户的问题与问答库中的答案进行匹配,找出最佳匹配返回给用户。这两种方法都涉及相似度计算问题,本文将对解决此类问题常用的损失函数做个介绍。此外,对于FAQ任务,本质上也是一个分类问题,所以本文也将介绍二分类交叉熵损失函数。
在FAQ任务中,假设训练样本是由([问题q,答案a],标签y)构成,如果a是问题q的最佳答案,则y=1,如果a不是最佳答案则y=0。对于这样的二分类任务,我们可以采用Binary CrossEntropy Loss来训练模型。
模型的训练过程是在不断最小化交叉熵,从数学角度看,优化交叉熵损失的本质是使模型的分布
Binary CrossEntropy Loss的定义如下方公式所示,
gif.latex?L_%7BBCE%7D%3D-%5By_%7Bn%7D%5Ccdot%20logx_%7Bn%7D+%281-y_%7Bn%7D%29%5Ccdot%20log%281-x_%7Bn%7D%29%5D (1)
其中,
当样本为正例时,我们希望模型预测是正例的概率gif.latex?x_%7Bn%7D越大越好。由公式(1)可以看出,当gif.latex?y_%7Bn%7D%3D1时,
gif.latex?L_%7BBCE%7D%3D-logx_%7Bn%7D,
gif.latex?x_%7Bn%7D越大,损失gif.latex?L_%7BBCE%7D就越小。
当样本为负例时,我们希望模型预测是正例的概率gif.latex?x_%7Bn%7D越小越好。由公式(1),当gif.latex?y_%7Bn%7D%3D0时,
gif.latex?L_%7BBCE%7D%3D-log%281-x_%7Bn%7D%29,
gif.latex?x_%7Bn%7D越小,损失gif.latex?L_%7BBCE%7D就越小。
下面以FAQ问答任务为例,用代码来实现二分类交叉熵损失函数。
- loss_fn = nn.BCEWithLogitsLoss()
-
- # x为问题,y为答案,model为一个Encoder
- # x_rep是x经过模型Encode后的语义向量表示,x_rep.shape:(batch_size, vec_dim)
- # y_rep是y经过模型Encode后的语义向量表示,y_rep.shape:(batch_size, vec_dim)
- x_rep, y_rep = model(x, y)
-
- logits = model.linear(torch.cat([x_rep, y_rep], 1))
-
- # target为标签向量,当y是x的最佳答案时,target=1,否则target=0
- loss = loss_fn(logits, target)
上述代码直接采用了Pytorch中的BCEWithLogitsLoss损失函数。
这里需要注意的是,二分类交叉熵损失函数公式中的gif.latex?x_%7Bn%7D的取值范围必需是在[0,1]区间范围。而上述代码第八行,[x_rep,y_rep]经过模型全接层后的输出 logits的取值范围不在[0,1]区间,为了将logits的值压缩在[0,1]区间,代码中使用nn.BCEWithLogitsLoss()函数,这个函数内部采用了sigmoid函数将变量logits的值压缩到[0,1]区间。
在上例中,是给模型输入一个问题、一个答案,让模型预测问题与答案是否匹配。如果概率值大于等于0.5,则认为问题与答案匹配。如果我们希望问题与最佳答案的匹配度必须比问题与不佳答案的匹配度高
假设给模型输入(问题, 最佳答案, 不佳答案),我们希望问题与最佳答案的打分比问题与不佳答案的打分高出一个margin,即:
Loss(question, pos_reply, neg_reply) = max(0, margin- score(question, pos_reply) + score(question, neg_reply))
上述就是Hinge Loss的定义,其公式如下所示,
gif.latex?L_%7Bhinge%7D%3Dmax%280%2C%20margin-score%28question%2Cpos%5C_reply%29+score%28question%2Cneg%5C_reply%29%29 (2)
其中,
通俗点说,公式(2)的含义是,给定一个问题,我们希望问题与最佳答案的得分比问题与不佳答案的得分高出一个
下面用代码来实现Hinge Loss
- q = list(batch_df["question"])
- pos_reply = list(batch_df["reply"])
- neg_reply = list(batch_df["neg_reply"])
- q = q + q
- reply = pos_reply + neg_reply
-
- x_rep, y_rep = model(q, reply)
- # 用余弦相似度作为打分函数
- sim = F.cosine_similarity(x_rep, y_rep)
- # sim1:问题与最佳答案的相似度
- sim1 = sim[:batch_size]
- # sim2:问题与不佳答案的相似度
- sim2 = sim[batch_size:]
-
- # margin=0.5
- hinge_loss = sim2 - sim1 + 0.5
-
- # 如果问题与最佳答案的相似度比问题与不佳答案的相似度高出0.5,则loss=0
- hinge_loss[hinge_loss < 0] = 0
- hinge_loss = torch.mean(hinge_loss)
在FAQ任务中,我们还可以用Cosine Embedding Loss损失来训练模型。
Cosine Embedding Loss的定义如下方公式所示,
gif.latex?L_%7BCE%7D%28x_%7B1%7D%2Cx_%7B2%7D%2Cy%29%3D%5Cleft%5C%7B%20%5Cbegin%7Baligned%7D%201-cos%28x_%7B1%7D%2Cx_%7B2%7D%29%20%2C%5Cquad%20y%3D1%5C%5C%20max%280%2Ccos%28x_%7B1%7D%2Cx_%7B2%7D%29-margin%29%2C%20%5Cquad%20y%3D-1%5C%5C%20%5Cend%7Baligned%7D%20%5Cright%20. (3)
其中,gif.latex?x_%7B1%7D、gif.latex?x_%7B2%7D为问题的向量表示,
根据数学知识我们知道,两个向量越相似,两向量的夹角越小,于是
公式(3)中对
下面我们通过代码来实现这个损失函数。还是以FAQ任务为例,我们采用Pytorch的CosineEmbeddingLoss()函数。
- loss_fn = nn.CosineEmbeddingLoss()
-
- # 用model对输入的两个问题x1、x2进行Encode,输出两个问题的向量表示x1_rep、x2_rep
- x1_rep, x2_rep = model(x1, x2)
-
- # target为两个问题是否为同义问题的标签,取值为-1或1
- loss = loss_fn(x1_rep, x2_rep, target)
这里需要注意的是,Binary CrossEntropy Loss的标签取值是0或1,而Cosine Embedding Loss的标签取值是-1或1.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。