一、背景知识

1.1 什么是分词?

  NLP的基础任务分为三个部分,词法分析、句法分析和语义分析,其中词法分析中有一种方法叫Tokenization,对汉字以字为单位进行处理叫做分词。

  Example :  我  去  北  京

       S       S       B       E

  注:S代表一个单独词,B代表一个词的开始,E表示一个词的结束(北京是一个词)。

1.2 什么是词性标注?

  句法分析中有一种方法叫词性标注(pos tagging),词性标注的目标是使用类似PN、VB等的标签对句子(一连串的词或短语)进行打签。

  Example :           I   can  open  this   can  .

  Pos tagging  -> PN  MD   VV   PN   NN  PU

  注:PN代词 MD情态动词 VV 动词 NN名词 PU标点符号

1.3 什么是分词-词性标注?

  分词-词性标注就是将分词和词性标注两个任务同时进行,在一个模型里完成,可以减少错误传播。

  Example :   我    去    北    京

       S-PN      S-VV        B-NN        E-NN

  注:如果想理解更多关于nlp基础任务的知识,可参看我整理的张岳老师暑期班的第一天的笔记。

1.4 什么是CRF?

  条件随机场(conditional random field)是一种用来标记和切分序列化数据的统计模型。在NLP领域可以用来做序列标注任务。

  注:更多关于条件随机场的理论知识,可以参考以下内容:

  条件随机场综述  

  如何轻松愉快地理解条件随机场(CRF)  

  条件随机场介绍(译)Introduction to Conditional Random Fields

  CRF条件随机场简介

二、CRF序列标注

2.1 模型结构图

最底下的词向量层,上两层是Bi-LSTM层,最上面一层是CRF层。数据流程是从下层向上层计算。

2.2 CRF部分

2.2.1 理论

Point 1: 在CRF中,每个特征函数以下列信息作为输入,输出是一个实数值。

(1)一个句子s

(2)词在句子中的位置i

(3)当前词的标签

(4)前一个词的标签

注:通过限制特征只依赖于当前与之前词的标签,而不是句子中的任意标签,实际上是建立了一种特殊的线性CRF,而不是广义上的CRF。

Point 2: CRF的训练参数

(1)Input: x = {我,去,北京}

(2)Answer: ygold= {PN,  VV,  NN}

(3)y'是CRF标注的所有可能值,有3*3*3=27个;

(4)T矩阵存储转移分数,T[yiyi-1]是上个标签是的情况下,下个标签是yi的分数;

(5)hi是向量序列,通过神经网络Bi-LSTM得到,hi[yi]是被标成的发射分数;

(6)score(x,y)是模型对x被标注成y所打出的分数,是一个实数值;

       

  Example : 我  去  北京

     PN     VV     NN

   

(7)P(ygold|x)是模型x对标注出ygold的概率;

      

Point 3: CRF的训练目标:训练模型使得变大

Step 1: 对P(ygold|x)进行转化,取对数

       

      

Step 2: 最终目标函数,使用梯度下降法

       

      

Step 3: 编程实现

       

 1     def _forward_alg(self, feats):
 2         # do the forward algorithm to compute the partition function
 3         init_alphas = torch.Tensor(1, self.labelSize).fill_(0)
 4         # Wrap in a variable so that we will get automatic backprop
 5         forward_var = autograd.Variable(init_alphas)
 6
 7         # Iterate through the sentence
 8         for idx in range(len(feats)):
 9             feat = feats[idx]
10             alphas_t = []           # The forward variables at this timestep
11             for next_tag in range(self.labelSize):
12                 # broadcast the emission score: it is the same regardless of the previous tag
13                 if idx == 0:
14                     alphas_t.append(feat[next_tag].view(1, -1))
15                 else:
16                     emit_score = feat[next_tag].view(1, -1).expand(1, self.labelSize)
17                     # the ith entry of trans_score is the score of transitioning to next_tag from i
18                     trans_score = self.T[next_tag]
19                     # The ith entry of next_tag_var is the value for the edge (i -> next_tag) before we do log-sum-exp
20                     next_tag_var = forward_var + trans_score + emit_score
21                     # The forward variable for this tag is log-sum-exp of all the scores.
22                     alphas_t.append(self.log_sum_exp(next_tag_var))
23             forward_var = torch.cat(alphas_t).view(1, -1)
24         alpha_score = self.log_sum_exp(forward_var)
25         return alpha_score

1   # Compute log sum exp in a numerically stable way for the forward algorithm
2     def log_sum_exp(self, vec):
3         max_score = vec[0, self.argmax(vec)]
4         max_score_broadcast = max_score.view(1, -1).expand(1, vec.size()[1])
5         return max_score + torch.log(torch.sum(torch.exp(vec - max_score_broadcast)))

     

1     def neg_log_likelihood(self, feats, tags):
2         forward_score = self._forward_alg(feats)                # calculate denominator
3         gold_score = self._score_sentence(feats, tags)
4         return forward_score - gold_score                       # calculate loss

train()中的训练部分:

 1         for iter in range(self.hyperParams.maxIter):
 2             print('###Iteration' + str(iter) + "###")
 3             random.shuffle(indexes)
 4             for idx in range(len(trainExamples)):
 5                 # Step 1. Remember that Pytorch accumulates gradients. We need to clear them out before each instance
 6                 self.model.zero_grad()
 7                 # Step 2. Get our inputs ready for the network, that is, turn them into Variables of word indices.
 8                 self.model.LSTMHidden = self.model.init_hidden()
 9                 exam = trainExamples[indexes[idx]]
10                 # Step 3. Run our forward pass. Compute the loss, gradients, and update the parameters by calling optimizer.step()
11                 lstm_feats = self.model(exam.feat)
12                 loss = self.model.crf.neg_log_likelihood(lstm_feats, exam.labelIndexs)
13                 loss.backward()
14                 optimizer.step()
15                 if (idx + 1) % self.hyperParams.verboseIter == 0:
16                     print('current: ', idx + 1,  ", cost:", loss.data[0])

Point 4: 使用模型预测序列

使用维特比解码算法,解决篱笆图中的最短路径问题

 

step 1:  初始节点没有转移值

1                 if idx == 0:
2                     viterbi_var.append(feat[next_tag].view(1, -1))

step 2: 节点值由三部分组成,最后求取最大值,得到lastbestlabel的下标

 1             for next_tag in range(self.labelSize):
 2                 if idx == 0:
 3                     viterbi_var.append(feat[next_tag].view(1, -1))
 4                 else:
 5                     emit_score = feat[next_tag].view(1, -1).expand(1, self.labelSize)
 6                     trans_score = self.T[next_tag]
 7                     next_tag_var = forward_var + trans_score + emit_score
 8                     best_label_id = self.argmax(next_tag_var)
 9                     bptrs_t.append(best_label_id)
10                     viterbi_var.append(next_tag_var[0][best_label_id])

step 3: 计算出所有节点,比较最后一个词的值,求取最大值之后,向前推出最佳序列。

维特比解码算法实现预测序列

 1     def _viterbi_decode(self, feats):
 2         init_score = torch.Tensor(1, self.labelSize).fill_(0)
 3         # forward_var at step i holds the viterbi variables for step i-1
 4         forward_var = autograd.Variable(init_score)
 5         back = []
 6         for idx in range(len(feats)):
 7             feat = feats[idx]
 8             bptrs_t = []                        # holds the backpointers for this step
 9             viterbi_var = []                    # holds the viterbi variables for this step
10             for next_tag in range(self.labelSize):
11                 # next_tag_var[i] holds the viterbi variable for tag i at the previous step,
12                 # plus the score of transitioning from tag i to next_tag.
13                 # We don't include the emission scores here because the max does not
14                 # depend on them (we add them in below)
15                 if idx == 0:
16                     viterbi_var.append(feat[next_tag].view(1, -1))
17                 else:
18                     emit_score = feat[next_tag].view(1, -1).expand(1, self.labelSize)
19                     trans_score = self.T[next_tag]
20                     next_tag_var = forward_var + trans_score + emit_score
21                     best_label_id = self.argmax(next_tag_var)
22                     bptrs_t.append(best_label_id)
23                     viterbi_var.append(next_tag_var[0][best_label_id])
24             # Now add in the emission scores, and assign forward_var to the set of viterbi variables we just computed
25             forward_var = (torch.cat(viterbi_var)).view(1, -1)
26             if idx > 0:
27                 back.append(bptrs_t)
28         best_label_id = self.argmax(forward_var)
29         # Follow the back pointers to decode the best path.
30         best_path = [best_label_id]
31         path_score = forward_var[0][best_label_id]
32         for bptrs_t in reversed(back):
33             best_label_id = bptrs_t[best_label_id]
34             best_path.append(best_label_id)
35         best_path.reverse()
36         return path_score, best_path

train()函数中的预测部分

 1        # Check predictions after training
 2             eval_dev = Eval()
 3             for idx in range(len(devExamples)):
 4                 predictLabels = self.predict(devExamples[idx])
 5                 devInsts[idx].evalPRF(predictLabels, eval_dev)
 6             print('Dev: ', end="")
 7             eval_dev.getFscore()
 8
 9             eval_test = Eval()
10             for idx in range(len(testExamples)):
11                 predictLabels = self.predict(testExamples[idx])
12                 testInsts[idx].evalPRF(predictLabels, eval_test)
13             print('Test: ', end="")
14             eval_test.getFscore()

1     def predict(self, exam):
2         tag_hiddens = self.model(exam.feat)
3         _, best_path = self.model.crf._viterbi_decode(tag_hiddens)
4         predictLabels = []
5         for idx in range(len(best_path)):
6             predictLabels.append(self.hyperParams.labelAlpha.from_id(best_path[idx]))
7         return predictLabels

Point 5 : 使用F1分数测量精度,最佳值为1,最差为0

        

 1     def getFscore(self):
 2         if self.predict_num == 0:
 3             self.precision = 0
 4         else:
 5             self.precision = self.correct_num / self.predict_num
 6
 7         if self.gold_num == 0:
 8             self.recall = 0
 9         else:
10             self.recall = self.correct_num / self.gold_num
11
12         if self.precision + self.recall == 0:
13             self.fscore = 0
14         else:
15             self.fscore = 2 * (self.precision * self.recall) / (self.precision + self.recall)
16         print("precision: ", self.precision, ", recall: ", self.recall, ", fscore: ", self.fscore)

注:全部代码和注释链接

扩展:可将数据中第二列和第一列一起放入Bi-LSTM中提取特征,这次只用到数据的第一列和第三列。

转载于:https://www.cnblogs.com/Joyce-song94/p/7262198.html

第四期coding_group笔记_用CRF实现分词-词性标注相关推荐

  1. 【深度之眼吴恩达机器学习第四期】笔记(五)

    目录 机器学习诊断 一.无超参数时对假设进行评估 二.有超参数时对假设进行评估 三.过拟合还是欠拟合 四.增加还是减小正则化参数λ 五.应该获取更多的数据样本吗 总结一下 实现算法的推荐方法 数据偏差 ...

  2. 【深度之眼吴恩达机器学习第四期】笔记(十二)

    目录 大规模学习 小批量梯度下降 在线学习 数据并行 应用举例 人工合成数据 上限分析 总结 大规模学习 现在机器学习的性能比过去的好,其中一个原因就是现在拥有大量的数据. 而且其中一种获得高性能机器 ...

  3. 【深度之眼吴恩达机器学习第四期】笔记(九)

    目录 K均值 K均值算法 语言描述 伪代码描述 解决分离不佳的簇 K均值的损失函数 K均值初始化 如何选择K 主成分分析 用途1:去除冗余特征 用途2:可视化数据 直观来理解主成分分析 主成分分析与线 ...

  4. 【深度之眼吴恩达机器学习第四期】笔记(四)

    目录 神经网络 神经网络训练流程 我们已经有线性回归和逻辑回归了,为什么还要使用神经网络呢? 对于一个有两个输入分量(x1,x2)的分类问题,我们使用这两个分量的组合来构造假设函数(图中右上角),可能 ...

  5. 【深度之眼吴恩达机器学习第四期】笔记(二)

    目录 逻辑回归 线性回归不适合分类问题 逻辑回归函数 决策边界 损失函数 梯度下降 多类别分类 正则化 欠拟合和过拟合 线性回归正则化 正规方程的正则化 逻辑回归正则化 逻辑回归 逻辑回归虽然叫回归, ...

  6. 【深度之眼吴恩达机器学习第四期】笔记(一)

    目录 第一章:什么是机器学习 第二章:线性回归模型 第三章:矩阵运算 第四章:多变量线性回归 正规方程 第五章:操作 第一章:什么是机器学习 机器学习主要分为监督学习(我们教计算机如何学习)和无监督学 ...

  7. 【深度之眼吴恩达机器学习第四期】笔记(十一)

    目录 推荐系统 基于内容的推荐系统 协同过滤 均值归一化 编程 推荐系统 以电影推荐系统为例子,假设4个用户(nu=4)对5部电影(nm=5)作出了以下评分,其中"?"代表第j个用 ...

  8. 【深度之眼吴恩达机器学习第四期】笔记(十)

    目录 异常点检测 高斯分布 异常点检测和监督学习 选择特征 多元高斯分布 使用多元高斯分布的异常点检测算法 原始模型VS.多元高斯分布的模型 编程 异常点检测 假如有一个关于飞机引擎的数据集,而且这些 ...

  9. 【深度之眼吴恩达机器学习第四期】笔记(八)

    目录 SVM 从逻辑回归到SVM 间隔最大理解SVM 直觉上来理解SVM 核函数 SVM编程 SVM 从逻辑回归到SVM 在逻辑回归中,如果标签y=1,我们希望预测值也等于1,那么就需要θTx远远大于 ...

  10. 【深度之眼吴恩达机器学习第四期】笔记(七)

    目录 模型优化 损失函数 梯度函数 正则化梯度与代价函数 学习曲线 选择最优的超参数lambda 模型优化 准备数据: import numpy as np import scipy.io as si ...

最新文章

  1. 【翻译】CodeMix使用的语言和框架(二):PHP
  2. idea persistence生成_真厉害!竟然可以这样用IDEA通过数据库生成lombok版的POJO...
  3. Java Review - 并发编程_ThreadPoolExecutor原理源码剖析
  4. mysql建立电影表_【代码片段】MySQL新建表添加基础字段
  5. Android 一直往文件写数据_对标苹果 AirDrop,Google 为安卓开发了一个文件传输利器...
  6. JavaScript--关于变量提升思考
  7. html5视频抓取,js和HTML5基于过滤器从摄像头中捕获视频的方法
  8. centos6安装mysql并远程连接_阿里云服务器上安装redis并实现远程连接
  9. docker安装带管理界面的rabbitmq
  10. Linux下查看和设置环境变量
  11. 【0610】【数据结构】【C语言版视频教程】【 全52讲 完整版】
  12. Log4j(三)——Log4j配置文件位置详解
  13. 桌面宠物秀,电脑桌面美化
  14. 目标检测Anchor-free分支:基于关键点的目标检测(最新网络全面超越YOLOv3)
  15. 机器学习之逻辑回归算法
  16. html表格中boder属性与style中boder属性区别
  17. js重新加载页面的方法
  18. int,int*,(int*)区别
  19. java多线程之Single Threaded Execution模式
  20. 我的Qt作品(7)使用Qt+OpenCV实现图像轮廓提取,再用三阶贝塞尔曲线拟合成光滑线条/多边形拟合

热门文章

  1. linux中ffmpeg实现视频的转吗,Linux系统下视频转换ffmpeg
  2. 初入职场,如何快速脱颖而出?
  3. 输入三个字符串,按由小到大的顺序输出
  4. SpringRMI解析2-RmiServiceExporter逻辑脉络
  5. 无敌打印(适用各种浏览器自带打印功能)
  6. AIDA64自动监控计算机硬件工作情况
  7. ubuntu下eclipse搭建安卓开发环境
  8. asp正则过滤重复字符串的代码
  9. 自然语言处理能够把全网内容组织到什么程度?
  10. Async和Await简介