基于压缩感知机的中文分词学习笔记
来源:互联网 发布:宁波最新房价走势知乎 编辑:程序博客网 时间:2024/05/22 13:43
中文分词学过CRF,RNN等,基于压缩感知机的中文分词还没接触过,发现了一篇很好的压缩感知机的中文分词博客,http://www.hankcs.com/nlp/segment/implementation-of-word-segmentation-device-java-based-on-structured-average-perceptron.html,作者用的是java的实现,由于本人对python比较熟悉,在此简单记录一下基于压缩感知机的中文分词及其的python代码的学习笔记,代码参考地址:https://github.com/zhangkaixu/minitools/blob/master/cws.py
原理为对于每个字ci,取其7个特征,分别为ci,ci-1,ci+1,ci-2ci-1,ci-1ci,cici+1,ci+1ci+2,特征生成代码如下:
def update(self, x, y, delta): # 更新权重 for i, features in zip(range(len(x)), self.gen_features(x)): for feature in features: self.weights.update_weights(str(y[i]) + feature, delta)#每个特征为tag+word for i in range(len(x) - 1): self.weights.update_weights(str(y[i]) + ':' + str(y[i + 1]), delta)#更新相邻两个tag的特征的权重
因此,每个字可以生成7个特征,这7个特征与该字的前后两个字相关,同时在分词的时候,我们需要对每个词分类,例如B、M、E、S,分别对于0,1,2,3数字,我们生成每两个标签的特征,例如01,00,11,13,...。这样总共生成了N组个特征,我们对每个特征分配一个权重,在训练的时候,若分类结果正确,则对于的特征权重+1,分类错误,则特征权重-1。
在训练的时候,对于输入句子x,和分类结果y,我们首先通过genfeature(x)函数生成句子中每个字的特征向量,之后根据每个字的每个特征向量(7个),更新每个字的权重,代码如下:
def update(self, x, y, delta): # 更新权重 for i, features in zip(range(len(x)), self.gen_features(x)): for feature in features: self.weights.update_weights(str(y[i]) + feature, delta)#每个特征为tag+word for i in range(len(x) - 1): self.weights.update_weights(str(y[i]) + ':' + str(y[i + 1]), delta)#更新相邻两个tag的特征的权重
上面的代码可知,对于每个字,生成了7个特征,每个字可能有4个分类结果,因此将7个特征与4个分类结果组合,需要28个特征权重与其对应,同时对于分类结果tag,每相邻两个字的tag组合为一个特征,有16中组合结果,即转移特征transitions,也需要16个特征权重。根据分类结果我们可以根系每个特征对于的权重。特征权重更新代码如下:
def update_weights(self, key, delta): # 更新权重 if key not in self._values: self._values[key] = 0 self._acc[key] = 0 self._last_step[key] = self._step self.updateF[key]=1 else: self._new_value(key) self.updateF[key]+=1 self._values[key] += delta#特征更新权重
代码中key为特征,delta为更新不长,若分类正确,delta=1,错误则delta=-1。
训练:训练过程为首先对于输入句子x,采用viterbi 算法进行解码:
def decode(self, x): # 类似隐马模型的动态规划解码算法 # 类似隐马模型中的转移概率 transitions = [[self.weights.get_value(str(i) + ':' + str(j), 0) for j in range(4)] for i in range(4)] # 类似隐马模型中的发射概率 emissions = [[sum(self.weights.get_value(str(tag) + feature, 0) for feature in features) for tag in range(4)] for features in self.gen_features(x)] # 类似隐马模型中的前向概率 # if len(emissions)<1: # print("x is ") # print(x) alphas = [[[e, None] for e in emissions[0]]] for i in range(len(x) - 1): alphas.append([max([alphas[i][j][0] + transitions[j][k] + emissions[i + 1][k], j] for j in range(4)) for k in range(4)]) # 根据alphas中的“指针”得到最优序列 alpha = max([alphas[-1][j], j] for j in range(4)) i = len(x) tags = [] while i: tags.append(alpha[1])#先计算最后一个状态,再往前推 i -= 1 alpha = alphas[i][alpha[1]] return list(reversed(tags))
首先初始化tag的16中转移特征的权重即transition矩阵为0,生成句子x的特征,我们用self.values保存特征权重,刚开使时self.values={}为空,对于每个特征f,若其不在self.values中,则将其添加进self.values中,且初始化self.values[f]=0。解码阶段,生成x的特征之后,便可以查找self.values中特征的权重,得到转移矩阵trainsiton,发射矩阵emissions,再根据viterbi算法,解码得到预测值z.
得到y_pred后便可以更新self.values,代码如下:
if z != y: cws.update(x, y, 1) cws.update(x, z, -1)
代码中,z为预测值,y为输入正确值,若预测值语正确值不想等,则更新两次权重,对y,更新权重delta=1,对于错误分类值z,更新权重delta=-1。
在预测阶段,我们便可以直接对输入句子x,生成特征,得到t转移矩阵trainsiton,发射矩阵emissions,再采用viterbi算法解码得到预测值y。
总结,在训练的时候,由于对于句子中的每个字会产生7个特征,对于同一个字,若其前后2个字不同,则产生的特征又不同,这样会产生很多特征,而很多时候特征分词的时候是不需要用到的,保留这些无用的特征会需要很大的空间,因此通常会想到对特征进行压缩。
邓知龙 《基于感知器算法的高效中文分词与词性标注系统设计与实现》中有提到对模型进行压缩,具体方法是在更新每个特征权重的时候,记录每个特征权重更新的次数,对于分词结果影响重要的特征,我们在训练的时候对其的更新当然比较频繁,其更新次数会较大,而另一些影响较小的特征其更新次数较小,因此我们可以将更新次数较小的特征去除,从而压缩模型。
- 基于压缩感知机的中文分词学习笔记
- 基于感知器的中文分词算法
- 压缩感知学习笔记
- 压缩感知学习笔记1
- 压缩感知学习笔记2
- 笔记-2007-基于有效子串标注的中文分词
- 压缩感知学习(一):压缩感知的起源
- 基于CRF的中文分词
- 基于CRF的中文分词
- 基于CRF的中文分词
- 基于CRF的中文分词
- 基于CRF的中文分词
- 基于CRF的中文分词
- 基于统计的中文分词
- 基于CRF的中文分词
- 基于统计学的中文分词
- 基于HMM的中文分词
- 压缩感知学习笔记-2017.01.13
- python多线程中join和setDaemon的用法
- Codeforces 246D Colorful Graph【STL瞎暴力】
- laravel的Eloquent起步
- JAVA虚拟机中对象创建
- Recover Binary Search Tree
- 基于压缩感知机的中文分词学习笔记
- laravel的Eloquent关联关系
- UVa679: dropping balls
- Memcached的简单使用
- Tomcat 安装及其单机多实例部署
- laravel的Eloquent其他笔记
- TypeError: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type s
- Android源码设计模式解析与实战
- Caffe-python interface 学习|网络训练、部署、测试