目录
一.基本文本处理技能
暂时先学一下基于词典的方法、基于统计的分词和词、字符频率统计
1.基于词典的方法(字符串匹配,机械分词方法)
1.1正向最大匹配思想MM
1.2逆向最大匹配算法RMM
1.3双向最大匹配法(Bi-directction Matching method,BM)
2.基于统计的分词(无字典分词)
2.1N-gram模型思想
3.词、字符频率统计
3.1词频率统计:第一步分词,然后根据分词后的结果进行词频率统计。
3.2字符频率统计:按单个字符切分并统计出现频率。
二.语言模型
1.语言模型中的unigram、bigram、trigram
1.1unigram
1.2bigram
1.3trigram
2.unigram、bigram频率统计
三.文本矩阵化
1.采用词袋模型且是词级别的矩阵化:
2.基于的jieba的分词及去停用词
3.词袋模型之向量化
中文分词的中文分词指将一个汉字序列切分成一个个单独的词。现有的中文分词算法有五大类:基于词典的方法,基于统计的方法,基于规则的方法,基于字标注的方法,基于人工智能技术(基于理解)的方法。中文分词目前主要有三个难点,分别是分词规范问题、未登录词识别、歧义切分问题。
对于中文分词算法共有以下五类:
1基于词典的方法(字符串匹配,机械分词方法)
2基于统计的分词(无字典分词)
3基于规则的分词(基于语义)
4基于字标注的中文分词方法
5基于人工智能技术的中文分词方法
定义:按照一定策略将待分析的汉字串与一个“大机器词典”中的词条进行匹配,若在词典中找到某个字符串,则匹配成功。
按照扫描方向的不同:正向匹配和逆向匹配
按照长度的不同:最大匹配和最小匹配
1》从左向右取待切分汉语句的m个字符作为匹配字段,m为大机器词典中最长词条个数。
2》查找大机器词典并进行匹配。若匹配成功,则将这个匹配字段作为一个词切分出来。
若匹配不成功,则将这个匹配字段的最后一个字去掉,剩下的字符串作为新的匹配字段,进行再次匹配,重复以上过程,直到切分出所有词为止。
该算法是正向最大匹配的逆向思维(最大匹配的顺序不是从首字母开始,而是从末尾开始),匹配不成功,将匹配字段的最前一个字去掉,实验表明,逆向最大匹配算法要优于正向最大匹配算法。
双向最大匹配法是将正向最大匹配法得到的分词结果和逆向最大匹配法的到的结果进行比较,从而决定正确的分词方法。据SunM.S. 和 Benjamin K.T.(1995)的研究表明,中文中90.0%左右的句子,正向最大匹配法和逆向最大匹配法完全重合且正确,只有大概9.0%的句子两种切分方法得到的结果不一样,但其中必有一个是正确的(歧义检测成功),只有不到1.0%的句子,或者正向最大匹配法和逆向最大匹配法的切分虽重合却是错的,或者正向最大匹配法和逆向最大匹配法切分不同但两个都不对(歧义检测失败)。这正是双向最大匹配法在实用中文信息处理系统中得以广泛使用的原因所在。
主要思想:上下文中,相邻的字同时出现的次数越多,就越可能构成一个词。因此字与字相邻出现的概率或频率能较好的反映词的可信度。
主要统计模型为:N元文法模型(N-gram)、隐马尔科夫模型(Hidden Markov Model, HMM)
模型基于这样一种假设,第n个词的出现只与前面N-1个词相关,而与其它任何词都不相关,整句的概率就是各个词出现概率的乘积 .我们给定一个词,然后猜测下一个词是什么。
对于一个句子T,我们怎么算它出现的概率呢?假设T是由词序列W1,W2,W3,…Wn组成的,那么P(T)=P(W1W2W3…Wn)=P(W1)P(W2|W1)P(W3|W1W2)…P(Wn|W1W2…Wn-1)
但是这种方法存在两个致命的缺陷:一个缺陷是参数空间过大,不可能实用化;另外一个缺陷是数据稀疏严重。
为了解决这个问题,我们引入了马尔科夫假设:一个词的出现仅仅依赖于它前面出现的有限的一个或者几个词。
如果一个词的出现仅依赖于它前面出现的一个词,那么我们就称之为bigram。即
P(T) = P(W1W2W3…Wn)=P(W1)P(W2|W1)P(W3|W1W2)…P(Wn|W1W2…Wn-1)
≈P(W1)P(W2|W1)P(W3|W2)…P(Wn|Wn-1)
如果一个词的出现仅依赖于它前面出现的两个词,那么我们就称之为trigram。
在实践中用的最多的就是bigram和trigram了,而且效果很不错。高于四元的用的很少,因为训练它需要更庞大的语料,而且数据稀疏严重,时间复杂度高,精度却提高的不多。设w1,w2,w3,...,wn是长度为n的字符串,规定任意词wi 只与它的前两个相关,得到三元概率模型,以此类推,N元模型就是假设当前词的出现概率只同它前面的N-1个词有关。
基于jieba库的实现代码如下:
import jieba
seg_list = list(jieba.cut('今天是我学习自然语言处理NLP的计划二,所需时间为两天',cut_all=False))
WordCount = Counter(seg_list )
print(WordCount)
基于collections库的实现代码如下:
from collections import Counter
txt = '今天是我学习自然语言处理NLP的计划二,所需时间为两天'
StatisticalCharacters = Counter(txt)
print(StatisticalCharacters)
unigram指的是单个词为一个单元。每个词之间没有关联关系。
bigram指的是两个词为一个单元。当前词只和上一个词有关系。
trigram指的是三个词为一个单元。当前词只和前两个词有关系。
unigram为单个的词,所以他它的统计方法和(一)中的词统计方法相同:collections.Counter模块。
bigram为包含两个词的一个单元,上文在分词基于统计的方法中提到的N-gram模型可以解决这一统计问题。
我们先说说词袋模型(Bag of Words,简称BoW)。词袋模型假设我们不考虑文本中词与词之间的上下文关系,仅仅只考虑所有词的权重。而权重与词在文本中出现的频率有关。
词袋模型首先会进行分词,在分词之后,通过统计每个词在文本中出现的次数,我们就可以得到该文本基于词的特征,如果将各个文本样本的这些词与对应的词频放在一起,就是我们常说的向量化。向量化完毕后一般也会使用TF-IDF进行特征的权重修正,再将特征进行标准化。 再进行一些其他的特征工程后,就可以将数据带入机器学习算法进行分类聚类了。
总结下词袋模型的三部曲:分词(tokenizing),统计修订词特征值(counting)与标准化(normalizing)。
词袋模型有很大的局限性,因为它仅仅考虑了词频,没有考虑上下文的关系,因此会丢失一部分文本的语义。但是大多数时候,如果我们的目的是分类聚类,则词袋模型表现的很好。
结巴中文分词支持三种分词模式:
1.1精确模式:视图将句子最精确的切开,适合文本分析;
1.2全模式:把句子中所有的可以成词的语句都扫描处理,速度非常快,但不解决歧义;
1.3搜索引擎模式:在精确模式的基础上,对分词再次切分,提高召回率,适合用于搜索引擎分词
#encoding=utf-8
import sys
sys.path.append("../")
import jieba
import jieba.posseg as pseg
from jieba import analyse
#加载停用词表
stop = [line.strip().decode('utf-8') for line in open('stop_words.txt').readlines() ]
#导入自定义词典
jieba.load_userdict("userdict.txt")
# 读取文本
f = open('example.txt')
s = f.read()
#s="朝鲜半岛西北部古元古代高温变质-深熔作用:宏观和微观岩石学以及锆石U-Pb年代学制约"
#分词
segs = jieba.cut(s, cut_all=False)
#print u"[精确模式]: ", " ".join(segs)
#分词并标注词性
segs = pseg.cut(s)
final = ''
for seg ,flag in segs:
#去停用词
if seg not in stop:
#去数词和去字符串
if flag !='m' and flag !='x':
#输出分词
final +=' '+ seg
#输出分词带词性
# final +=' '+ seg+'/'+flag
print final
在词袋模型的统计词频这一步,我们会得到该文本中所有词的词频,有了词频,我们就可以用词向量表示这个文本。这里我们举一个例子,例子直接用scikit-learn的CountVectorizer类来完成,这个类可以帮我们完成文本的词频统计与向量化,代码如下:
from sklearn.feature_extraction.text import CountVectorizer
vectorizer=CountVectorizer()
corpus=["I come to China to travel",
"This is a car polupar in China",
"I love tea and Apple ",
"The work is to write some papers in science"]
print (vectorizer.fit_transform(corpus))
处理结果:
(0, 16) 1
(0, 3) 1
(0, 15) 2
(0, 4) 1
(1, 5) 1
(1, 9) 1
(1, 2) 1
(1, 6) 1
(1, 14) 1
(1, 3) 1
(2, 1) 1
(2, 0) 1
(2, 12) 1
(2, 7) 1
(3, 10) 1
(3, 8) 1
(3, 11) 1
(3, 18) 1
(3, 17) 1
(3, 13) 1
(3, 5) 1
(3, 6) 1
(3, 15) 1
可以看出4个文本的词频已经统计出,在输出中,左边的括号中的第一个数字是文本的序号,第2个数字是词的序号,注意词的序号是基于所有的文档的。第三个数字就是我们的词频。
我们可以进一步看看每个文本的词向量特征和各个特征代表的词,代码如下:
print (vectorizer.fit_transform(corpus).toarray())
print (vectorizer.get_feature_names())
结果如下:
[[0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 2 1 0 0]
[0 0 1 1 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0]
[1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0]
[0 0 0 0 0 1 1 0 1 0 1 1 0 1 0 1 0 1 1]]
[u'and', u'apple', u'car', u'china', u'come', u'in', u'is', u'love', u'papers', u'polupar', u'science', u'some', u'tea', u'the', u'this', u'to', u'travel', u'work', u'write']
可以看到我们一共有19个词,所以4个文本都是19维的特征向量。而每一维的向量依次对应了下面的19个词。另外由于词"I"在英文中是停用词,不参加词频的统计。