NLP自然语言处理学习笔记(十)(转自咕泡AI)
Attention的原理和实现
目标
- 知道Attention的作用
- 知道Attention的实现机制
- 能够使用代码完成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的实现过程
初始化一个Decoder的隐藏状态z0z_0z0
这个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等
encoder中的每个output都和z0z_0z0进行计算之后,得到的结果进行softmax,让他们的和为1(可以理解为权重)
之后把所有的softmax之后的结果和原来encoder的输出hih_ihi进行相加求和得到c0c^0c0
即:c0=∑α^0ihi即: c^0 = \sum\hat{\alpha}_0^ih^i 即:c0=∑α^0ihi得到c0c^0c0之后,把它作为decoder的input,同和传入初始化的z0z^0z0,得到第一个time step的输出和hidden_state(Z1Z^1Z1)
把Z1Z_1Z1再和所有的encoder的output进行match操作,得到的结果进行softmax之后作为权重和encoder的每个timestep的结果相乘求和得到c1c^1c1
再把c1c^1c1作为decoder的input,和Z1Z^1Z1作为输入得到下一个输出,如此循环,只到最终decoder的output为终止符
上述参考:http://speech.ee.ntu.edu.tw/~tlkagk/courses_MLSD15_2.html
整个过程写成数学公式如下:
α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]- 先计算attention权重
- 在计算上下文向量,图中的cic^ici
- 最后计算结果,往往会把当前的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
的区别
区别在于两个地方:
attention的计算数据和位置
Bahdanau Attention
会使用前一次的隐藏
状态来计算attention weight,所以我们会在代码中的GRU之前使用attention的操作,同时会把attention的结果和word embedding的结果进行concat,作为GRU的输出(参考的是pytorch Toritul)。Bahdanau使用的是双向的GRU,会使用正反的encoder的output的concat的结果作为encoder output,如下图所示Luong Attenion
使用的是当前一次的decoder的output
来计算得到attention weight,所以在代码中会在GRU的后面进行attention的操作,同时会把context vector
和gru的结果进行concat的操作,最终的output。Luong使用的是多层GRU,只会使用最后一层的输出(encoder output)
计算attention weights的方法不同
Bahdanau Attention
的match函数,aij=vaTtanh(WaZi−1,+Uahj)a_i^j = v^T_a tanh (W_aZ_{i-1},+U_ah_j)aij=vaTtanh(WaZi−1,+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)其中
- vaT是一个参数矩阵,需要被训练,Wa是实现对Zi−1的形状变化v_a^T是一个参数矩阵,需要被训练,W_a是实现对Z_{i-1}的形状变化vaT是一个参数矩阵,需要被训练,Wa是实现对Zi−1的形状变化,
- Ua实现对hj的形状变化(矩阵乘法,理解为线性回归,实现数据形状的对齐)U_a实现对h_j的形状变化(矩阵乘法,理解为线性回归,实现数据形状的对齐)Ua实现对hj的形状变化(矩阵乘法,理解为线性回归,实现数据形状的对齐),
- Zi−1是decoder端前一次的隐藏状态,hj是encoder的outputZ_{i-1}是decoder端前一次的隐藏状态,h_j是encoder的outputZi−1是decoder端前一次的隐藏状态,hj是encoder的output
Luong Attenion
整体比Bahdanau Attention
更加简单,他使用了三种方法来计算得到权重矩阵乘法:general
- 直接对decoder的隐藏状态进行一个矩阵变换(线性回归),然后和encoder outputs进行矩阵乘法
dot
- 直接对decoder的隐藏状态和encoder outputs进行矩阵乘法
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是所有的encoder的hidden 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
目标
- 知道beam search的概念和原理
- 能够在代码中使用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的例子
- 首先输入
start token <s>
,然后得到四个输出(这里假设一个就四个输出:x,y,z,</s>
),选择概率最大三个,x,y,w - 然后分别把x,y,z放到下一个time step中作为输入,分别得到三个不同的输出,找到三个输出中概率最大的三个,x,y,y
- 继续重复上述步骤,直到获得结束符(概率最大)或者是达到句子的最大长度,那么此时选择概率乘积最大的一个。
- 拼接整个路径上概率最大的所有结果,比如这里可能是
<s>,y,y,x,w,</s>
2. Beam serach的实现
在上述描述的思路中,我们需要注意以下几个内容:
- 数据该如何保存,每一次的输出的最大的beam width个结果,和之后之前的结果该如何保存
- 保存了之后的概率应该如何比较大小,保留下概率最大的三个
- 不能够仅仅只保存当前概率最大的信息,还需要有当前概率最大的三个中,前面的路径的输出结果
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搜索
思路:
- 构造
<SOS>
开始符号等第一次输入的信息,保存在堆中 - 取出堆中的数据,进行forward_step的操作,获得当前时间步的output,hidden
- 从output中选择topk(k=beam width)个输出,作为下一次的input
- 把下一个时间步骤需要的输入等数据保存在一个新的堆中
- 获取新的堆中的优先级最高(概率最大)的数据,判断数据是否是EOS结尾或者是否达到最大长度,如果是,停止迭代
- 如果不是,则重新遍历新的堆中的数据
代码如下
# 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. 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. 其他优化方法
- 根据特定的问题,使用分类模型进行训练,然后再训练单独的回个该为题的为模型
- 比如询问名字,可以使用fasttext先进行意图识别,命中
询问名字
分类后,直接返回名字 - 或者是手动构造和名字相关的很多问题,来进行训练,从而能够更加个性化的回答出结果
- 比如询问名字,可以使用fasttext先进行意图识别,命中
- 直接对现有的语料进行修改和清洗,把语料中更多的答案进行替换,比如咨询名字的,咨询天气的等,这样能够更大程度上的回答出更加规范的答案
- 使用2.4 会讲的搜索模型,不再使用这种生成模型
NLP自然语言处理学习笔记(十)(转自咕泡AI)相关推荐
- NLP自然语言处理学习笔记(二)Word2Vec
NLP自然语言处理学习笔记(二)Word2Vec 一.Word2Vec 二.负采样 本文是根据吴恩达教授的教学视频来整理的学习笔记,部分图片来源于视频的截图.原教学视频连接 https://mooc. ...
- NLP自然语言处理学习笔记(十二)(转自咕泡AI)
问答机器人排序模型 目标 知道模型中排序中的概念和目的 知道模型中排序的实现方法 1. 排序模型的介绍 前面的课程中为了完成一个问答机器人,我们先进行了召回,相当于是通过海选的方法找到呢大致相似的问题 ...
- NLP自然语言处理学习笔记(七)(转自咕泡AI)
走进聊天机器人 学习目标 知道常见的bot的分类 知道企业中常见的流程和方法 1. 目前企业中的常见的聊天机器人 QA BOT(问答机器人):回答问题 代表 :智能客服. 比如:提问和回答 TASK ...
- NLP自然语言处理学习笔记(十一)(转自咕泡AI)
问答机器人介绍 目标 知道问答机器人是什么 知道问答机器人实现的逻辑 1. 问答机器人 在前面的课程中,我们已经对问答机器人介绍过,这里的问答机器人是我们在分类之后,对特定问题进行回答的一种机器人.至 ...
- NLP自然语言处理学习笔记(一)(转自咕泡AI)
1深度学习的介绍 目标 知道什么是深度学习 知道深度学习和机器学习的区别 能够说出深度学习的主要应用场景 知道深度学习的常见框架 1. 深度学习的概念 深度学习(英语:deep learning)是机 ...
- 自然语言处理学习笔记十二(依存句法分析)
词法分析之后,语法分析也是理解语言的重要一环.对于简单句子,还可以通过分词进行理解:但对于长句子,还得通过语法来分析才能更好的理解. 语法分析是自然语言处理中的一个重要的任务,其目标就是分析句子的语法 ...
- NLP自然语言处理学习笔记(三)(转自咕泡AI)
Pytorch完成基础的模型 目标 知道Pytorch中Module的使用方法 知道Pytorch中优化器类的使用方法 知道Pytorch中常见的损失函数的使用方法 知道如何在GPU上运行代码 能够说 ...
- NLP自然语言处理学习笔记(八)(转自咕泡AI)
分类的目的和分类的方法 目标 能够说出项目中进行文本的目的 能够说出意图识别的方法 能够说出常见的分类的方法 1. 文本分类的目的 回顾之前的流程,我们可以发现文本分类的目的就是为了进行意图识别 在当 ...
- NLP自然语言处理学习笔记(四)(转自咕泡AI)
使用Pytorch实现手写数字识别 目标 知道如何使用Pytorch完成神经网络的构建 知道Pytorch中激活函数的使用方法 知道Pytorch中torchvision.transforms中常见图 ...
最新文章
- SQLSERVER中修复状态为Suspect的数据库
- 微信小程序-路由方式
- hdu 6035:Colorful Tree (2017 多校第一场 1003) 【树形dp】
- 【C】@程序员,我们送给你一个成熟的Excel导入导出组件
- Java多线程(review)
- 李宏毅机器学习(九)Multi-lingual BERT
- svg操纵方案 基于 D3 还是 angular?
- protobuf3 自定义option_Protobuf3语法详解
- 一个完整的html文件包含哪些标签,HTML基础有哪些单标签
- Linux下 FFmpeg 编译安装
- 莽荒纪手游源码/服务端!
- 教你如何选型到合适的OA系统
- js 计算个人所得税
- Chrome浏览器通过chrono下载插件设置下载断点续传
- 网易2019实习生招聘笔试-牛牛的闹钟
- ASP.NET程序设计复习题
- Android Framework学习的基础概论
- 《金蝶ERP-K/3完全使用详解》——6.2 产品预测单
- 桌面音乐频谱linux,X Music Spectrum(音乐频谱桌面特效)
- sas和python哪个更容易发胖_碳水化合物和脂肪哪个更容易让身体发胖?