Attention的原理和实现

目标

  1. 知道Attention的作用
  2. 知道Attention的实现机制
  3. 能够使用代码完成Attention代码的编写

1. Attention的介绍

在普通的RNN结构中,Encoder需要把一个句子转化为一个向量,然后在Decoder中使用,这就要求Encoder把源句子中所有的信息都包含进去,但是当句子长度过长的时候,这个要求就很难达到,或者说会产生瓶颈(比如,输入一篇文章等场长内容),当然我们可以使用更深的RNN和大多的单元来解决这个问题,但是这样的代价也很大。那么有没有什么方法能够优化现有的RNN结构呢?

为此,Bahdanau等人在2015年提出了Attenion机制,Attention翻译成为中文叫做注意力,把这种模型称为Attention based model。就像我们自己看到一副画,我们能够很快的说出画的主要内容,而忽略画中的背景,因为我们注意的,更关注的往往是其中的主要内容。

通过这种方式,在我们的RNN中,我们有通过LSTM或者是GRU得到的所有信息,那么这些信息中只去关注重点,而不需要在Decoder的每个time step使用全部的encoder的信息,这样就可以解决第一段所说的问题了

那么现在要讲的Attention机制就能够帮助我们解决这个问题

2. Attenion的实现机制

假设我们现在有一个文本翻译的需求,即机器学习翻译为machine learning。那么这个过程通过前面所学习的Seq2Seq就可以实现

上图的左边是Encoder,能够得到hidden_state在右边使用

Deocder中蓝色方框中的内容,是为了提高模型的训练速度而使用teacher forcing手段,否则的话会把前一次的输出作为下一次的输入(但是在Attention模型中不再是这样了

那么整个过程中如果使用Attention应该怎么做呢?

在之前我们把encoder的最后一个输出,作为decoder的初始的隐藏状态,现在我们不再这样做

2.1 Attention的实现过程

  1. 初始化一个Decoder的隐藏状态z0z_0z0

  2. 这个zoz_ozo会和encoder第一个time step的output进行match操作(或者是socre操作),得到α01\alpha_0^1α01 ,这里的match可以使很多中操作,比如:

    • z和h的余弦值
    • 是一个神经网络,输入为z和h
    • 或者α=hTWz\alpha = h^T W zα=hTWz
  3. encoder中的每个output都和z0z_0z0进行计算之后,得到的结果进行softmax,让他们的和为1(可以理解为权重)

  4. 之后把所有的softmax之后的结果和原来encoder的输出hih_ihi进行相加求和得到c0c^0c0
    即:c0=∑α^0ihi即: c^0 = \sum\hat{\alpha}_0^ih^i 即:c0=α^0ihi

  5. 得到c0c^0c0之后,把它作为decoder的input,同和传入初始化的z0z^0z0,得到第一个time step的输出和hidden_state(Z1Z^1Z1

  6. Z1Z_1Z1再和所有的encoder的output进行match操作,得到的结果进行softmax之后作为权重和encoder的每个timestep的结果相乘求和得到c1c^1c1

  7. 再把c1c^1c1作为decoder的input,和Z1Z^1Z1作为输入得到下一个输出,如此循环,只到最终decoder的output为终止符

  8. 上述参考:http://speech.ee.ntu.edu.tw/~tlkagk/courses_MLSD15_2.html

  9. 整个过程写成数学公式如下:
    αij=exp(score(hi,h‾j))∑exp(score(hn,h‾m))[attentionweight]ci=∑αijh‾s[contextvector]αi=f(ci,hi)=tanh(Wc[ci;hi])[attentonresult]\begin{align*} \alpha_{ij} &= \frac{exp(score(h_i,\overline{h}_j))}{\sum exp(score(h_n,\overline{h}_m))} & [attention \quad weight]\\ c_i &=\sum \alpha_{ij}\overline{h}_s & [context\quad vector] \\ \alpha_i &= f(c_i,h_i) = tanh(W_c[c_i;h_i]) &[attenton \quad result] \end{align*} αijciαi=exp(score(hn,hm))exp(score(hi,hj))=αijhs=f(ci,hi)=tanh(Wc[ci;hi])[attentionweight][contextvector][attentonresult]

    1. 先计算attention权重
    2. 在计算上下文向量,图中的cic^ici
    3. 最后计算结果,往往会把当前的output([batch_size,1,hidden_size])和上下文向量进行拼接然后使用

2.2 不同Attention的介绍

在上述过程中,使用decoder的状态和encoder的状态的计算后的结果作为权重,乘上encoder每个时间步的输出,这需要我们去训练一个合适的match函数,得到的结果就能够在不同的时间步上使用不同的encoder的相关信息,从而达到只关注某一个局部的效果,也就是注意力的效果

2.2.1 Soft-Attention 和 Hard-Attention

最开始Bahdanau等人提出的Attention机制通常被称为soft-attention,所谓的soft-attention指的是encoder中输入的每个词语都会计算得到一个注意力的概率。

在进行图像捕捉的时候,提出了一种hard-attenion的方法,希望直接从input中找到一个和输出的某个词对应的那一个词。但是由于NLP中词语和词语之间往往存在联系,不会只关注某一个词语,所以都会使用soft-attention,所以这里的就不多介绍hard-attention

2.2.3 Global-Attention 和Local Attention

Bahdanau等人提出的Bahdanau Attention 被称为local attention,后来Luong等人提出的Luong Attention是一种全局的attenion。

所谓全局的attenion指的是:使用的全部的encoder端的输入的attenion的权重

local-attenion就是使用了部分的encoder端的输入的权重(当前时间步上的encoder的hidden state),这样可以减少计算量,特别是当句子的长度比较长的时候。

2.2.4 Bahdanau Attention和 Luong Attenion的区别

区别在于两个地方:

  1. attention的计算数据和位置

    1. Bahdanau Attention会使用前一次的隐藏状态来计算attention weight,所以我们会在代码中的GRU之前使用attention的操作,同时会把attention的结果和word embedding的结果进行concat,作为GRU的输出(参考的是pytorch Toritul)。Bahdanau使用的是双向的GRU,会使用正反的encoder的output的concat的结果作为encoder output,如下图所示

    2. Luong Attenion使用的是当前一次的decoder的output来计算得到attention weight,所以在代码中会在GRU的后面进行attention的操作,同时会把context vector和gru的结果进行concat的操作,最终的output。Luong使用的是多层GRU,只会使用最后一层的输出(encoder output)

  2. 计算attention weights的方法不同

    1. Bahdanau Attention的match函数,aij=vaTtanh(WaZi−1,+Uahj)a_i^j = v^T_a tanh (W_aZ_{i-1},+U_ah_j)aij=vaTtanh(WaZi1,+Uahj),计算出所有的aija_i^jaij之后,在计算softmax,得到a^ij\hat{a}_i^ja^ij,即a^ij=exp(aij)∑exp(aij)\hat{a}_i^j = \frac{exp(a_i^j)}{\sum exp(a_i^j)}a^ij=exp(aij)exp(aij)

      其中

      1. vaT是一个参数矩阵,需要被训练,Wa是实现对Zi−1的形状变化v_a^T是一个参数矩阵,需要被训练,W_a是实现对Z_{i-1}的形状变化vaT是一个参数矩阵,需要被训练,Wa是实现对Zi1的形状变化
      2. Ua实现对hj的形状变化(矩阵乘法,理解为线性回归,实现数据形状的对齐)U_a实现对h_j的形状变化(矩阵乘法,理解为线性回归,实现数据形状的对齐)Ua实现对hj的形状变化(矩阵乘法,理解为线性回归,实现数据形状的对齐)
      3. Zi−1是decoder端前一次的隐藏状态,hj是encoder的outputZ_{i-1}是decoder端前一次的隐藏状态,h_j是encoder的outputZi1decoder端前一次的隐藏状态,hjencoderoutput
    2. Luong Attenion整体比Bahdanau Attention更加简单,他使用了三种方法来计算得到权重

      1. 矩阵乘法:general

        • 直接对decoder的隐藏状态进行一个矩阵变换(线性回归),然后和encoder outputs进行矩阵乘法
      2. dot

        • 直接对decoder的隐藏状态和encoder outputs进行矩阵乘法
      3. concat

        • 把decoder的隐藏状态和encoder的output进行concat,把这个结果使用tanh进行处理后的结果进行对齐计算之后,和encoder outputs进行矩阵乘法
        • ht是当前的decoder hidden state,hs是所有的encoder 的hidden state(encoder outputs)h_t\text{是当前的decoder hidden state,}h_s\text{是所有的encoder 的hidden state(encoder outputs)}ht是当前的decoder hidden state,hs是所有的encoderhidden state(encoder outputs)

最终两个attention的结果区别并不太大,所以以后我们可以考虑使用Luong attention完成代码

3. Attention的代码实现

完成代码之前,我们需要确定我们的思路,通过attention的代码,需要实现计算的是attention weight

通过前面的学习,我们知道attention_weight = f(hidden,encoder_outputs),主要就是实现Luong attention中的三种操作

class Attention(nn.Module):def __init__(self,method,batch_size,hidden_size):super(Attention,self).__init__()self.method = methodself.hidden_size = hidden_sizeassert self.method in ["dot","general","concat"],"method 只能是 dot,general,concat,当前是{}".format(self.method)if self.method == "dot":passelif self.method == "general":self.Wa = nn.Linear(hidden_size,hidden_size,bias=False)elif self.method == "concat":self.Wa = nn.Linear(hidden_size*2,hidden_size,bias=False)self.Va = nn.Parameter(torch.FloatTensor(batch_size,hidden_size))def forward(self, hidden,encoder_outputs):""":param hidden:[1,batch_size,hidden_size]:param encoder_outputs: [batch_size,seq_len,hidden_size]:return:"""batch_size,seq_len,hidden_size = encoder_outputs.size()hidden = hidden.squeeze(0) #[batch_size,hidden_size]if self.method == "dot":return self.dot_score(hidden,encoder_outputs)elif self.method == "general":return self.general_score(hidden,encoder_outputs)elif self.method == "concat":return self.concat_score(hidden,encoder_outputs)def _score(self,batch_size,seq_len,hidden,encoder_outputs):# 速度太慢# [batch_size,seql_len]attn_energies = torch.zeros(batch_size,seq_len).to(config.device)for b in range(batch_size):for i in range(seq_len):#encoder_output : [batch_size,seq_len,hidden_size]#deocder_hidden :[batch_size,hidden_size]#torch.Size([256, 128]) torch.Size([128]) torch.Size([256, 24, 128]) torch.Size([128])# print("attn size:",hidden.size(),hidden[b,:].size(),encoder_output.size(),encoder_output[b,i].size())attn_energies[b,i] = hidden[b,:].dot(encoder_outputs[b,i]) #dot scorereturn F.softmax(attn_energies).unsqueeze(1)  # [batch_size,1,seq_len]def dot_score(self,hidden,encoder_outputs):"""dot attention:param hidden:[batch_size,hidden_size] --->[batch_size,hidden_size,1]:param encoder_outputs: [batch_size,seq_len,hidden_size]:return:"""#hiiden :[hidden_size] -->[hidden_size,1] ,encoder_output:[seq_len,hidden_size]hidden = hidden.unsqueeze(-1)attn_energies = torch.bmm(encoder_outputs, hidden)attn_energies = attn_energies.squeeze(-1) #[batch_size,seq_len,1] ==>[batch_size,seq_len]return F.softmax(attn_energies).unsqueeze(1)  # [batch_size,1,seq_len]def general_score(self,hidden,encoder_outputs):"""general attenion:param batch_size:int:param hidden: [batch_size,hidden_size]:param encoder_outputs: [batch_size,seq_len,hidden_size]:return:"""x = self.Wa(hidden) #[batch_size,hidden_size]x = x.unsqueeze(-1) #[batch_size,hidden_size,1]attn_energies = torch.bmm(encoder_outputs,x).squeeze(-1) #[batch_size,seq_len,1]return F.softmax(attn_energies,dim=-1).unsqueeze(1)      # [batch_size,1,seq_len]def concat_score(self,hidden,encoder_outputs):"""concat attention:param batch_size:int:param hidden: [batch_size,hidden_size]:param encoder_outputs: [batch_size,seq_len,hidden_size]:return:"""#需要先进行repeat操作,变成和encoder_outputs相同的形状,让每个batch有seq_len个hidden_sizex = hidden.repeat(1,encoder_outputs.size(1),1) ##[batch_size,seq_len,hidden_size]x = torch.tanh(self.Wa(torch.cat([x,encoder_outputs],dim=-1))) #[batch_size,seq_len,hidden_size*2] --> [batch_size,seq_len,hidden_size]#va [batch_size,hidden_size] ---> [batch_size,hidden_size,1]attn_energis = torch.bmm(x,self.Va.unsqueeze(2))  #[batch_size,seq_len,1]attn_energis = attn_energis.squeeze(-1)# print("concat attention:",attn_energis.size(),encoder_outputs.size())return F.softmax(attn_energis,dim=-1).unsqueeze(1) #[batch_size,1,seq_len]

完成了attention weight的计算之后,需要再对代码中forward_step的内容进行修改

 def forward_step(self,decoder_input,decoder_hidden,encoder_outputs):""":param decoder_input:[batch_size,1]:param decoder_hidden: [1,batch_size,hidden_size]:param encoder_outputs: encoder中所有的输出,[batch_size,seq_len,hidden_size]:return: out:[batch_size,vocab_size],decoder_hidden:[1,batch_size,didden_size]"""embeded = self.embedding(decoder_input)  #embeded: [batch_size,1 , embedding_dim]#TODO 可以把embeded的结果和前一次的context(初始值为全0tensor) concate之后作为结果#rnn_input = torch.cat((embeded, last_context.unsqueeze(0)), 2)# gru_out:[256,1, 128]  decoder_hidden: [1, batch_size, hidden_size]gru_out,decoder_hidden = self.gru(embeded,decoder_hidden)gru_out = gru_out.squeeze(1)#TODO 注意:如果是单层,这里使用decoder_hidden没问题(output和hidden相同)# 如果是多层,可以使用GRU的output作为attention的输入#开始使用attentionattn_weights = self.attn(decoder_hidden,encoder_outputs)# attn_weights [batch_size,1,seq_len] * [batch_size,seq_len,hidden_size]context = attn_weights.bmm(encoder_outputs) #[batch_size,1,hidden_size]gru_out = gru_out.squeeze(0)  # [batch_size,hidden_size]context = context.squeeze(1)  # [batch_size,hidden_size]#把output和attention的结果合并到一起concat_input = torch.cat((gru_out, context), 1) #[batch_size,hidden_size*2]concat_output = torch.tanh(self.concat(concat_input)) #[batch_size,hidden_size]output = F.log_softmax(self.fc(concat_output),dim=-1) #[batch_Size, vocab_size]# out = out.squeeze(1)return output,decoder_hidden,attn_weights

attetnion的Bahdanau实现可以参考:https://github.com/spro/practical-pytorch/blob/master/seq2seq-translation/seq2seq-translation.ipynb

Beam Search

目标

  1. 知道beam search的概念和原理
  2. 能够在代码中使用Beam search 完成预测过程

1. Beam Search的介绍

在进行模型评估的过程中,每次我们选择概率最大的token id作为输出,那么整个输出的句子的概率就是最大的么?

Beam search的又被称作束集搜索,是一种seq2seq中用来优化输出结果的算法(不在训练过程中使用)。

例如:传统的获取解码器输出的过程中,每次只选择概率最大的那个结果,作为当前时间步的输出,等到输出结束,我们会发现,整个句子可能并不通顺。虽然在每一个时间步上的输出确实是概率最大的,但是整体的概率确不一定最大的,我们经常把它叫做greedy search[贪心算法]

为了解决上述的问题,可以考虑计算全部的输出的概率乘积,选择最大的哪一个,但是这样的话,意味着如果句子很长,候选词很多,那么需要保存的数据就会非常大,需要计算的数据量就很大

那么Beam Search 就是介于上述两种方法的一个这种的方法,假设Beam width=2,表示每次保存的最大的概率的个数,这里每次保存两个,在下一个时间步骤一样,也是保留两个,这样就可以达到约束搜索空间大小的目的,从而提高算法的效率。

beam width =1 时,就是贪心算法,beam width=候选词的时候,就是计算全部的概率。beam width 是一个超参数。

比如在下图中:

使用一个树状图来表示每个time step的可能输出,其中的数字表示是条件概率

黄色的箭头表示的是一种greedy search,概率并不是最大的

如果把beam width设置为2,那么后续可以找到绿色路径的结果,这个结果是最大的

下图是要给beam width=3的例子

  1. 首先输入start token <s>,然后得到四个输出(这里假设一个就四个输出:x,y,z,</s>),选择概率最大三个,x,y,w
  2. 然后分别把x,y,z放到下一个time step中作为输入,分别得到三个不同的输出,找到三个输出中概率最大的三个,x,y,y
  3. 继续重复上述步骤,直到获得结束符(概率最大)或者是达到句子的最大长度,那么此时选择概率乘积最大的一个。
  4. 拼接整个路径上概率最大的所有结果,比如这里可能是<s>,y,y,x,w,</s>

2. Beam serach的实现

在上述描述的思路中,我们需要注意以下几个内容:

  1. 数据该如何保存,每一次的输出的最大的beam width个结果,和之后之前的结果该如何保存
  2. 保存了之后的概率应该如何比较大小,保留下概率最大的三个
  3. 不能够仅仅只保存当前概率最大的信息,还需要有当前概率最大的三个中,前面的路径的输出结果

2.1 数据结构-堆-的认识

对于上面所说的,保留有限个数据,同时需要根据大小来保留,可以使用一种带有优先级的数据结构来实现,这里我们可以使用这种数据结构

是一种优先级的队列,但是他其实并不是队列,我们常说的队列都是先进先出或者是先进后出,但是只根据优先级的高低来取出数据。

在一起的另外一种数据结构叫做,有入栈和出栈的操作,可以理解为是一种先进后出的数据结构,关于栈,大家可以下来在了解。

在python自带的模块中,有一个叫做heapq的模块,提供了堆所有的方法。通过下面的代码我们来了解下heapq的使用方法

my_heap = [] #使用列表保存数据#往列表中插入数据,优先级使用插入的内容来表示,就是一个比较大小的操作,越大优先级越高
heapq.heappush(my_heap,[29,True,"xiaohong"])
heapq.heappush(my_heap,[28,False,"xiaowang"])
heapq.heappush(my_heap,[29,False,"xiaogang"])for i in range(3):ret= heapq.heappop(my_heap)  #pop操作,优先级最小的数据print(ret)#输出如下:
[28, False, 'xiaowang']
[29, False, 'xiaogang']
[29, True, 'xiaohong']

可以发现,输出的顺序并不是数据插入的顺序,而是根据其优先级,从小往大pop(False<True)。

2.2 使用堆来实现beam search

为了实现数据的的保存,我们可以把beam search中的数据保存在堆中,同时在往这个堆中添加数据的同时,判断数据的个数,仅仅保存beam width个数据

class Beam:def __init__(self):self.heap = list() #保存数据的位置self.beam_width = config.beam_width #保存数据的总数def add(self,probility,complete,seq,decoder_input,decoder_hidden):"""添加数据,同时判断总的数据个数,多则删除:param probility: 概率乘积:param complete: 最后一个是否为EOS:param seq: list,所有token的列表:param decoder_input: 下一次进行解码的输入,通过前一次获得:param decoder_hidden: 下一次进行解码的hidden,通过前一次获得:return:"""heapq.heappush(self.heap,[probility,complete,seq,decoder_input,decoder_hidden])#判断数据的个数,如果大,则弹出。保证数据总个数小于等于3if len(self.heap)>self.beam_width:heapq.heappop(self.heap)def __iter__(self):#让该beam能够被迭代return iter(self.heap)

实现方法,完成模型eval过程中的beam search搜索

思路:

  1. 构造<SOS>开始符号等第一次输入的信息,保存在堆中
  2. 取出堆中的数据,进行forward_step的操作,获得当前时间步的output,hidden
  3. 从output中选择topk(k=beam width)个输出,作为下一次的input
  4. 把下一个时间步骤需要的输入等数据保存在一个新的堆中
  5. 获取新的堆中的优先级最高(概率最大)的数据,判断数据是否是EOS结尾或者是否达到最大长度,如果是,停止迭代
  6. 如果不是,则重新遍历新的堆中的数据

代码如下

# decoder中的新方法
def evaluatoin_beamsearch_heapq(self,encoder_outputs,encoder_hidden):"""使用 堆 来完成beam search,对是一种优先级的队列,按照优先级顺序存取数据"""batch_size = encoder_hidden.size(1)#1. 构造第一次需要的输入数据,保存在堆中decoder_input = torch.LongTensor([[word_sequence.SOS] * batch_size]).to(config.device)decoder_hidden = encoder_hidden #需要输入的hiddenprev_beam = Beam()prev_beam.add(1,False,[decoder_input],decoder_input,decoder_hidden)while True:cur_beam = Beam()#2. 取出堆中的数据,进行forward_step的操作,获得当前时间步的output,hidden#这里使用下划线进行区分for _probility,_complete,_seq,_decoder_input,_decoder_hidden in prev_beam:#判断前一次的_complete是否为True,如果是,则不需要forward#有可能为True,但是概率并不是最大if _complete == True:cur_beam.add(_probility,_complete,_seq,_decoder_input,_decoder_hidden)else:decoder_output_t, decoder_hidden,_ = self.forward_step(_decoder_input, _decoder_hidden,encoder_outputs)value, index = torch.topk(decoder_output_t, config.beam_width)  # [batch_size=1,beam_widht=3]#3. 从output中选择topk(k=beam width)个输出,作为下一次的inputfor m, n in zip(value[0], index[0]):decoder_input = torch.LongTensor([[n]]).to(config.device)seq = _seq + [n]probility = _probility * mif n.item() == word_sequence.EOS:complete = Trueelse:complete = False#4. 把下一个实践步骤需要的输入等数据保存在一个新的堆中cur_beam.add(probility,complete,seq,decoder_input,decoder_hidden)#5. 获取新的堆中的优先级最高(概率最大)的数据,判断数据是否是EOS结尾或者是否达到最大长度,如果是,停止迭代best_prob,best_complete,best_seq,_,_ = max(cur_beam)if best_complete == True or len(best_seq)-1 == config.max_len: #减去sosreturn self._prepar_seq(best_seq)else:#6. 则重新遍历新的堆中的数据prev_beam = cur_beamdef _prepar_seq(self,seq):#对结果进行基础的处理,共后续转化为文字使用if seq[0].item() == word_sequence.SOS:seq=  seq[1:]if  seq[-1].item() == word_sequence.EOS:seq = seq[:-1]seq = [i.item() for i in seq]return seq

2.3 修改seq2seq

在seq2seq中使用evaluatoin_beamsearch_heapq查看效果,会发现使用beam search的效果比单独使用attention的效果更好

使用小黄鸡语料(50万个问答),单个字作为token,5个epoch之后的训练结果,左边为问,右边是回答

你在干什么 >>>>> 你想干啥?
你妹 >>>>> 不是我
你叫什么名字 >>>>> 你猜
你个垃圾 >>>>> 你才是,你
你是傻逼 >>>>> 是你是傻
笨蛋啊 >>>>> 我不是,你

闲聊机器人的优化

目标

  1. 知道如何优化模型的效果
  2. 知道常见的优化手段

1. seq2seq中使用teacher forcing

在前面的seq2seq的案例中,我们介绍了teacher frocing是什么,当时我们的输入和输出很相似,所以当时我们的teacher forcing是在每个time step中实现的,那么现在我们的输入和输出不同的情况下,该如何使用呢?

我们可以在每个batch遍历time step的外层使用teacher forcing

代码如下:

use_teacher_forcing = random.random() > 0.5
if use_teacher_forcing: #使用teacher forcingfor t in range(config.max_len):decoder_output_t, decoder_hidden, decoder_attn_t = self.forward_step(decoder_input, decoder_hidden,encoder_outputs)decoder_outputs[:, t, :] = decoder_output_t#使用正确的输出作为下一步的输入decoder_input = target[:, t].unsqueeze(1)  # [batch_size,1]else:#不适用teacher forcing,使用预测的输出作为下一步的输入for t in range(config.max_len):decoder_output_t ,decoder_hidden,decoder_attn_t = self.forward_step(decoder_input,decoder_hidden,encoder_outputs)decoder_outputs[:,t,:] = decoder_output_tvalue, index = torch.topk(decoder_output_t, 1) # index [batch_size,1]decoder_input = index

2. 使用梯度裁剪

前面,我们给大家介绍了梯度消失(梯度过小,在多层计算后导致其值太小而无法计算)梯度爆炸(梯度过大,导致其值在多层的计算后太大而无法计算)

在常见的深度神经网络中,特别是RNN中,我们经常会使用梯度裁剪的手段,来抑制过大的梯度,能够有效防止梯度爆炸。

梯度裁剪的实现非常简单,仅仅只需要设置一个阈值,把梯度大于该阈值时设置为该阈值。

实现代码:

loss.backward()
#进行梯度裁剪
nn.utils.clip_grad_norm_(model.parameters(),[5,10,15])
optimizer.step()

3. 其他优化方法

  1. 根据特定的问题,使用分类模型进行训练,然后再训练单独的回个该为题的为模型

    • 比如询问名字,可以使用fasttext先进行意图识别,命中询问名字分类后,直接返回名字
    • 或者是手动构造和名字相关的很多问题,来进行训练,从而能够更加个性化的回答出结果
  2. 直接对现有的语料进行修改和清洗,把语料中更多的答案进行替换,比如咨询名字的,咨询天气的等,这样能够更大程度上的回答出更加规范的答案
  3. 使用2.4 会讲的搜索模型,不再使用这种生成模型

NLP自然语言处理学习笔记(十)(转自咕泡AI)相关推荐

  1. NLP自然语言处理学习笔记(二)Word2Vec

    NLP自然语言处理学习笔记(二)Word2Vec 一.Word2Vec 二.负采样 本文是根据吴恩达教授的教学视频来整理的学习笔记,部分图片来源于视频的截图.原教学视频连接 https://mooc. ...

  2. NLP自然语言处理学习笔记(十二)(转自咕泡AI)

    问答机器人排序模型 目标 知道模型中排序中的概念和目的 知道模型中排序的实现方法 1. 排序模型的介绍 前面的课程中为了完成一个问答机器人,我们先进行了召回,相当于是通过海选的方法找到呢大致相似的问题 ...

  3. NLP自然语言处理学习笔记(七)(转自咕泡AI)

    走进聊天机器人 学习目标 知道常见的bot的分类 知道企业中常见的流程和方法 1. 目前企业中的常见的聊天机器人 QA BOT(问答机器人):回答问题 代表 :智能客服. 比如:提问和回答 TASK ...

  4. NLP自然语言处理学习笔记(十一)(转自咕泡AI)

    问答机器人介绍 目标 知道问答机器人是什么 知道问答机器人实现的逻辑 1. 问答机器人 在前面的课程中,我们已经对问答机器人介绍过,这里的问答机器人是我们在分类之后,对特定问题进行回答的一种机器人.至 ...

  5. NLP自然语言处理学习笔记(一)(转自咕泡AI)

    1深度学习的介绍 目标 知道什么是深度学习 知道深度学习和机器学习的区别 能够说出深度学习的主要应用场景 知道深度学习的常见框架 1. 深度学习的概念 深度学习(英语:deep learning)是机 ...

  6. 自然语言处理学习笔记十二(依存句法分析)

    词法分析之后,语法分析也是理解语言的重要一环.对于简单句子,还可以通过分词进行理解:但对于长句子,还得通过语法来分析才能更好的理解. 语法分析是自然语言处理中的一个重要的任务,其目标就是分析句子的语法 ...

  7. NLP自然语言处理学习笔记(三)(转自咕泡AI)

    Pytorch完成基础的模型 目标 知道Pytorch中Module的使用方法 知道Pytorch中优化器类的使用方法 知道Pytorch中常见的损失函数的使用方法 知道如何在GPU上运行代码 能够说 ...

  8. NLP自然语言处理学习笔记(八)(转自咕泡AI)

    分类的目的和分类的方法 目标 能够说出项目中进行文本的目的 能够说出意图识别的方法 能够说出常见的分类的方法 1. 文本分类的目的 回顾之前的流程,我们可以发现文本分类的目的就是为了进行意图识别 在当 ...

  9. NLP自然语言处理学习笔记(四)(转自咕泡AI)

    使用Pytorch实现手写数字识别 目标 知道如何使用Pytorch完成神经网络的构建 知道Pytorch中激活函数的使用方法 知道Pytorch中torchvision.transforms中常见图 ...

最新文章

  1. SQLSERVER中修复状态为Suspect的数据库
  2. 微信小程序-路由方式
  3. hdu 6035:Colorful Tree (2017 多校第一场 1003) 【树形dp】
  4. 【C】@程序员,我们送给你一个成熟的Excel导入导出组件
  5. Java多线程(review)
  6. 李宏毅机器学习(九)Multi-lingual BERT
  7. svg操纵方案 基于 D3 还是 angular?
  8. protobuf3 自定义option_Protobuf3语法详解
  9. 一个完整的html文件包含哪些标签,HTML基础有哪些单标签
  10. Linux下 FFmpeg 编译安装
  11. 莽荒纪手游源码/服务端!
  12. 教你如何选型到合适的OA系统
  13. js 计算个人所得税
  14. Chrome浏览器通过chrono下载插件设置下载断点续传
  15. 网易2019实习生招聘笔试-牛牛的闹钟
  16. ASP.NET程序设计复习题
  17. Android Framework学习的基础概论
  18. 《金蝶ERP-K/3完全使用详解》——6.2 产品预测单
  19. 桌面音乐频谱linux,X Music Spectrum(音乐频谱桌面特效)
  20. sas和python哪个更容易发胖_碳水化合物和脂肪哪个更容易让身体发胖?

热门文章

  1. Css Reset -Css样式重置
  2. Pomodoro Technique
  3. 【HTTP Status 500 - Servlet execution threw an exception】
  4. 第九篇:稳定性之面向失败设计【可用性架构设计、可用性容灾】
  5. flutter 图形验证码
  6. 【NLP】⚠️学不会打我! 半小时学会基本操作 8⚠️ 新闻分类
  7. 安卓开发良好的习惯(想到就更新)
  8. 文本标注工具-brat安装
  9. mysql 分区 线性hash_MySQL表分区(3)哈希分区-hash
  10. 互联网医疗十大公司排名