BiLSTM-CRF计算细节

1、计算所有路径得分的对数指数和

  def _forward_alg(self, feats):
        init_alphas = torch.full((1, self.tagset_size), -10000.).cuda()
        init_alphas[0][self.tag_to_ix[START_TAG]] = 0.
        #注释1
        forward_var = init_alphas
        #注释2
        for feat in feats:  
        #注释3
            alphas_t = [] 
            for next_tag in range(self.tagset_size):
                #注释4
                emit_score = feat[next_tag].view(1, -1).expand(1, self.tagset_size) 
                #注释5  
                trans_score = self.transitions[next_tag].view(1, -1)
                #注释6
                next_tag_var = forward_var + trans_score + emit_score
                #注释7
                alphas_t.append(log_sum_exp(next_tag_var).view(1))
                #注释8
            forward_var = torch.cat(alphas_t).view(1, -1)
            #注释9
        terminal_var = forward_var + self.transitions[self.tag_to_ix[STOP_TAG]]
        alpha = log_sum_exp(terminal_var)
        #注释10
        return alpha
  • 注释1 _ f o r w a r d _ a l g \_forward\_alg _forward_alg用来计算所有路径得分的对数指数和。 i n i t _ a l p a h s = [ p 1 , 0 , p 2 , 0 , . . . , p m , 0 ] init\_alpahs=[p_{1,0},p_{2,0},...,p_{m,0}] init_alpahs=[p1,0,p2,0,...,pm,0],即在时刻0到各状态的路径得分。由于时刻0不可能到其他状态,所以初始化为-10000,在时刻0的状态就是 s t a r t start start,所以将 p s t a r t _ i x , 0 p_{start\_ix,0} pstart_ix,0初始化为0.
  • 注释2:我们需要对每一个时刻 t t t都计算一个 f o r w a r d _ v a r = [ p 1 , t , p 2 , t , . . . , p m , t ] forward\_var=[p_{1,t},p_{2,t},...,p_{m,t}] forward_var=[p1,t,p2,t,...,pm,t]以存放在时刻 t t t到各状态的路径得分的对数指数和。在时刻0即令 f o r w a r d _ v a r = [ p 1 , 0 , p 2 , 0 , . . . , p m , 0 ] forward\_var=[p_{1,0},p_{2,0},...,p_{m,0}] forward_var=[p1,0,p2,0,...,pm,0]
  • 注释3 f e a t feat feat为时刻 t t t各状态的状态分数(我觉得不能称之为发射分数,因为CRF为无向图),其具体表达式为 [ B t , 1 , B t , 2 , . . . , B t , m ] [B_{t,1},B_{t,2},...,B_{t,m}] [Bt,1,Bt,2,...,Bt,m]。第一个 f o r for for循环在时间维度进行。
  • 注释4:进入第二个 f o r for for循环,此时是对各状态进行遍历。以下注释用 i i i代替 n e x t _ t a g next\_tag next_tag
  • 注释5 e m i t _ s c o r e = [ B t , i , B t , i , . . . , B t , i ] emit\_score=[B_{t,i},B_{t,i},...,B_{t,i}] emit_score=[Bt,i,Bt,i,...,Bt,i],即时刻 t t t为状态 i i i的得分的广播。
  • 注释6 t r a n s s c o r e = [ A i , 1 , A i , 2 , . . . , A i , m ] trans_score=[A_{i,1},A_{i,2},...,A_{i,m}] transscore=[Ai,1,Ai,2,...,Ai,m],即各状态转移到状态 i i i的得分。
  • 注释7 f o r w a r d _ v a r forward\_var forward_var为上一时刻 t − 1 t-1 t1到各状态的得分的对数指数和,即 f o r w a r d _ v a r = [ p 1 , t − 1 , p 2 , t − 1 , . . . , p m , t − 1 ] forward\_var=[p_{1,t-1},p_{2,t-1},...,p_{m,t-1}] forward_var=[p1,t1,p2,t1,...,pm,t1]
    [ p 1 , t − 1 , p 2 , t − 1 , . . . , p m , t − 1 ] + [ B t , i , B t , i , . . . , B t , i ] + [ A i , 1 , A i , 2 , . . . , A i , m ] + = [ Q 1 , Q 2 , . . . , Q m ] \begin{aligned} &[p_{1,t-1},p_{2,t-1},...,p_{m,t-1}] +\\ &[B_{t,i},B_{t,i},...,B_{t,i}]+ \\ &[A_{i,1},A_{i,2},...,A_{i,m}]+\\ &=[Q_1,Q_2,...,Q_m] \end{aligned} [p1,t1,p2,t1,...,pm,t1]+[Bt,i,Bt,i,...,Bt,i]+[Ai,1,Ai,2,...,Ai,m]+=[Q1,Q2,...,Qm]
  • 注释8:计算 p i , t = l o g ( e Q 1 + e Q 2 + . . . + e Q m ) p_{i,t} = log(e^{Q_1}+e^{Q_2}+...+e^{Q_m}) pi,t=log(eQ1+eQ2+...+eQm),并将其加入列表 a l p h a s _ t alphas\_t alphas_t中。
  • 注释9:当各状态遍历结束后,我们就得到列表 a l p h a s _ t = [ p 1 , t , p 2 , t , . . . , p m , t ] alphas\_t = [p_{1,t},p_{2,t},...,p_{m,t}] alphas_t=[p1,t,p2,t,...,pm,t],将其转换为张量,并赋值给 f o r w a r d _ v a r forward\_var forward_var,得到 f o r w a r d _ v a r = [ p 1 , t , p 2 , t , . . . , p m , t ] forward\_var=[p_{1,t},p_{2,t},...,p_{m,t}] forward_var=[p1,t,p2,t,...,pm,t]以便继续重复上述步骤去计算 [ p 1 , t + 1 , p 2 , t + 1 , . . . , p m , t + 1 ] [p_{1,t+1},p_{2,t+1},...,p_{m,t+1}] [p1,t+1,p2,t+1,...,pm,t+1]
  • 注释10:完整的所有路径得分的对数指数和还应该加上各个状态转移到 s t o p stop stop的得分,并取对数指数和。就得到完整的所有路径的得分的对数指数和,为 f o r w a r d _ s c o r e = l o g ( e p 1 + e p 2 + . . . + e p n m ) forward\_score=log(e^{p_{1}}+e^{p_{2}}+...+e^{p_{n^{m}}}) forward_score=log(ep1+ep2+...+epnm)

2、计算真实路径的得分

    def _score_sentence(self, feats, tags):
        # 注释1
        score = torch.zeros(1).cuda()
        tags=torch.cat([torch.tensor([self.tag_to_ix[START_TAG]],dtype=torch.long).cuda(), tags]) 
         # 注释2
        for i, feat in enumerate(feats):
            #注释3
            score = score + self.transitions[tags[i + 1], tags[i]] + feat[tags[i + 1]]
        score = score + self.transitions[self.tag_to_ix[STOP_TAG], tags[-1]]
        #注释4
        return score.cuda()

  • 注释1:计算给定tag的分数,即一条路径的分数
  • 注释2:将 s t a r t start start加入 t a g s tags tags,成为新的 t a g s tags tags,因为原始的 t a g s tags tags中并没有 s t a r t start start,而在计算路径得分时,我们需要考虑由start转移到其他状态的分数
  • 注释3:计算分数=转移分数+状态分数
  • 注释4:这里的score为真实路径得分

3、关于CRF损失函数

    def neg_log_likelihood(self, sentence, tags):
        feats = self._get_lstm_features(sentence)
        forward_score = self._forward_alg(feats) #得到所有路径的分数之和
        gold_score = self._score_sentence(feats, tags) #计算某条路径的分数
        return forward_score - gold_score  

  我们的目的是希望 e p r e a l _ p a t h e p 1 + e p 2 + . . . + e p n m \frac{e^{p_{real\_path}}}{e^{p_{1}}+e^{p_{2}}+...+e^{p_{n^{m}}}} ep1+ep2+...+epnmepreal_path
尽可能大,取对数并取符号,即可得到损失函数
L o s s = l o g ( e p 1 + e p 2 + . . . + e p n m ) − p r e a l _ p a t h Loss = log(e^{p_{1}}+e^{p_{2}}+...+e^{p_{n^{m}}})-p_{real\_path} Loss=log(ep1+ep2+...+epnm)preal_path
所以 _ f o r w a r d _ a l g \_forward\_alg _forward_alg函数计算的是所有路径的对数指数和,而 _ s c o r e _ s e n t e n c e \_score\_sentence _score_sentence函数计算的就是真实路径的得分。

4、Viterbi解码

    def _viterbi_decode(self, feats):
        backpointers = [] 
        #注释1
        init_vvars = torch.full((1, self.tagset_size), -10000.)
        init_vvars[0][self.tag_to_ix[START_TAG]] = 0
        forward_var = init_vvars
        #注释2
        for feat in feats:
            bptrs_t = []  
            viterbivars_t = []  
            for next_tag in range(self.tagset_size):
                next_tag_var = forward_var.cuda() + self.transitions[next_tag].cuda()
                best_tag_id = argmax(next_tag_var)
                #注释3
                bptrs_t.append(best_tag_id)
                viterbivars_t.append(next_tag_var[0][best_tag_id].view(1))
                #注释4
            forward_var = (torch.cat(viterbivars_t) + feat).view(1, -1)
            #注释5
            backpointers.append(bptrs_t) 
            #注释6
        terminal_var = forward_var + self.transitions[self.tag_to_ix[STOP_TAG]]  
        #注释7
        best_tag_id = argmax(terminal_var)
        path_score = terminal_var[0][best_tag_id]
        #注释8
        best_path = [best_tag_id]
        for bptrs_t in reversed(backpointers):
            best_tag_id = bptrs_t[best_tag_id]
            best_path.append(best_tag_id)
        #注释9
        start = best_path.pop()  
        #最初的start被剔除
        assert start == self.tag_to_ix[START_TAG]  
        best_path.reverse()
        return path_score, best_path
  • 注释1 b a c k p o i n t e r s backpointers backpointers其用来记录最优路径,其为二维列表, [ t ] [ i ] [t][i] [t][i]元素为时刻 t t t,状态 i i i是由前一个时刻的哪一个状态转移而来会使得分数最大。
  • 注释2 f o r w a r d _ v a r forward\_var forward_var其记录下当前时刻各个状态的最优分数,即 f o r w a r d _ v a r = [ δ 1 , t , δ 2 , t , . . . , δ m , t ] forward\_var=[\delta_{1,t},\delta_{2,t},...,\delta_{m,t}] forward_var=[δ1,t,δ2,t,...,δm,t] δ i , t \delta_{i,t} δi,t表示在时刻 t t t若为状态 i i i的最优分数。
  • 注释3:计算 δ i , t + 1 \delta_{i,t+1} δi,t+1不需要加上发射概率,在这两个 f o r for for循环下,代表时刻 t + 1 t+1 t+1的状态 i i i,此时发射概率都相同。 δ i , t + 1 = M a x { δ 1 , t + A i , 1 + B i , t + 1 δ 2 , t + A i , 2 + B i , t + 1 ⋮ δ m , t + A i , m + B i , t + 1 \delta_{i,t+1} = Max\left\{ \begin{aligned} &\delta_{1,t}+A_{i,1}+B_{i,t+1}\\ &\delta_{2,t}+A_{i,2}+B_{i,t+1}\\ &\vdots\\ &\delta_{m,t}+A_{i,m}+B_{i,t+1} \end{aligned} \right. δi,t+1=Maxδ1,t+Ai,1+Bi,t+1δ2,t+Ai,2+Bi,t+1δm,t+Ai,m+Bi,t+1
    [ δ 1 , t , δ 2 , t , . . . , δ m , t ] + [ A i , 1 , A i , 2 , . . . , A i , m ] = n e x t _ t a g _ v a r [\delta_{1,t},\delta_{2,t},...,\delta_{m,t}]+[A_{i,1},A_{i,2},...,A_{i,m}]=next\_tag\_var [δ1,t,δ2,t,...,δm,t]+[Ai,1,Ai,2,...,Ai,m]=next_tag_var,然后使用 a r g m a x argmax argmax找出由前一时刻哪个状态转移而来分数最大,将前一时刻的该状态赋值给 b e s t _ t a g _ i d best\_tag\_id best_tag_id,即 b e s t _ t a g _ i d = M a x { δ 1 , t + A i , 1 δ 2 , t + A i , 2 ⋮ δ m , t + A i , m best\_tag\_id = Max\left\{ \begin{aligned} &\delta_{1,t}+A_{i,1}\\ &\delta_{2,t}+A_{i,2}\\ &\vdots\\ &\delta_{m,t}+A_{i,m} \end{aligned} \right. best_tag_id=Maxδ1,t+Ai,1δ2,t+Ai,2δm,t+Ai,m
  • **注释4:**将 b e s t _ t a g _ i d best\_tag\_id best_tag_id加入进 b p t r s _ t bptrs\_t bptrs_t,其存放当前时刻 t + 1 t+1 t+1的各个状态分别由前一个时刻的哪个状态转移而来分数最优。 v i t e r b i v a r s _ t viterbivars\_t viterbivars_t则存放了当前时刻 t + 1 t+1 t+1的各个状态最优分数(其实不完整,需要分别加上当前时刻 t + 1 t+1 t+1各个状态的状态分数)
  • **注释5:**之前没有加上 当前时刻 t + 1 t+1 t+1各个状态的状态分数,需要加上才是完整的最优分数。 v i t e r b i v a r s _ t + [ B 1 , t + 1 , B 2 , t + 1 , . . . , B m , t + 1 ] = [ δ 1 , t + 1 , δ 2 , t + 1 , . . . , δ m , t + 1 ] = f o r w a r d _ v a r viterbivars\_t+[B_{1,t+1},B_{2,t+1},...,B_{m,t+1}] =[\delta_{1,t+1},\delta_{2,t+1},...,\delta_{m,t+1}]=forward\_var viterbivars_t+[B1,t+1,B2,t+1,...,Bm,t+1]=[δ1,t+1,δ2,t+1,...,δm,t+1]=forward_var
  • 注释6 b a c k p o i n t e r s backpointers backpointers其用来记录最优路径,其为二维列表, [ t ] [ i ] [t][i] [t][i]元素为时刻 t t t,状态 i i i是由前一个时刻的哪一个状态转移而来会使得分数最大。
  • 注释7: 最后需要加上该状态转移到stop的分数才完整,不用加上到stop的状态分数,该状态分数不存在,因为没有一个词属于状态stop。即 [ δ 1 , n , δ 2 , n , . . . , δ m , n ] + [ A s t o p _ i x , 1 , A s t o p _ i x , 2 , . . . , A s t o p _ i x , m ] = t e r m i n a l _ v a r [\delta_{1,n},\delta_{2,n},...,\delta_{m,n}]+[A_{{stop\_ix},1},A_{{stop\_ix},2},...,A_{{stop\_ix},m}]=terminal\_var [δ1,n,δ2,n,...,δm,n]+[Astop_ix,1,Astop_ix,2,...,Astop_ix,m]=terminal_var
  • 注释8 b e s t _ t a g _ i d best\_tag\_id best_tag_id记录最后时刻哪一个状态到 s t o p stop stop分数最优(大), p a t h _ s c o r e path\_score path_score记录下从 s t a r t start start s t o p stop stop的最优路径的得分。
  • 注释9:从后往前找到最优路径,存放于 b e s t _ p a t h best\_path best_path
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值