NLP降临在我身边 3

基本文本处理技能

分词

中国有一种特有的文化现象,叫做文盲,即只会听读不会写。建国后国家曾经进行了很多次文化普及运动,例如义务教育,旨在降低文盲率。现在的文盲比率已经被大幅度降低,文盲大多是农村长者,没能享受到教育红利。所以文盲其实不是一个贬义词,而是一种特殊的现象,其根源在于汉语的音与形分离。
新文化运动时期,曾有人提出过将文字罗马化。解释罗马化并不容易,不如举一个例子,日语与韩文都是罗马化后的汉字。其音意一体,可听说即可写。区别点在于韩文进行了完全罗马化,而日语在罗马化的进程中保留了部分汉字。而我们最为熟知的罗马化文字是英语。对于完全罗马化的文字,不存在文盲这一概念。这也是日语常常需要在汉字上标注读音的原因,因为日本也有不认识汉字的人,需要转化为音义后才能理解。
从这种角度来讲,汉字的文字复杂度较高,也因此能在有限的字数中表示更多的含义。在很长的一段时间内,汉字没有标点,依靠理解进行断句。韩愈在《师说》中提到的“句读之不解”,正是“不明白怎么断句”。
说了这么多,无非是想指出汉字与英语存在着根本差别:字和词的差别。在汉字中,句子由连续的多个汉字组成,词与词之间没有分隔符,需要对连续的汉字进行合并划分,也就是分词。

分词的正向最大匹配

正向最大匹配的方式是正向迭代语句,每匹配到一个词就将这个词存储并不在迭代,转而搜索下一个词。这种算法被称为贪心算法。
举例:

  1. 第一轮匹配
    1.1 祝你新年快乐.
    1.2 祝你新年快
    1.3 祝你新年
    1.4 祝你新
    1.5 祝你
    1.6 祝(第一个词)
  2. 第二轮匹配
    2.1 你新年快乐
    2.2 你新年快
    2.3 你新年
    2.4 你新
    2.5 你(第二个词)
  3. 第三轮匹配
    3.1 新年快乐(第三个词)
    因此,这个句子的分词结果为【祝】【你】【新年快乐】
    这个句子本身比较短,因此可以被很快地分类。然而在实际操作中,长句往往能达到二十字甚至三十字,迭代次数非常恐怖。因此往往需要采用分治策略,即将句子切片进行匹配,没有被匹配的字则归入到下一个切片中进行匹配。通过这种方式,单次迭代的耗时降低,而汉字“短小精悍”的特性也使得单个切片在四五个字时即可有较好的匹配性能。

逆向最大匹配

逆向迭代语句。这个东西其实解释起来挺麻烦的,直接上示例吧。

  1. 第一轮匹配
    1.1 祝你新年快乐
    1.2 你新年快乐
    1.3 新年快乐(第一个词)
  2. 第二轮匹配
    2.1 祝你
    2.2 你(第二个词)
  3. 第三轮匹配
    3.1 祝(第三个词)
    这个示例的匹配结果与正向匹配相同,但实际上,很多时候二者的匹配结果并不相同。二者的优劣很难预判。

双向最大匹配

无论正向匹配还是逆向匹配,都存在误差。
在汉语中,词义的准确性与词语的长度往往正相关,因此可以通过比较两种方向最大匹配中的匹配结果,选择词语长度更长的一项,作为双向最大匹配的匹配结果。

词、字符频率统计

代码如下:

from collections import Counter
text = '祝你新年快乐。'
print(Counter(text))

这里调用了collection.Counter模块,输出结果为一个包含了各字出现频率的字典。包括‘。’也被计入。如果需要删去这些没有实意的词语,可以下载一个停用词词典,过滤后就干净了。

语言模型n-gram

这里主要使用的是n-gram语言模型,其核心思想为将有含义的最小单位(这里全部指词语)与相邻单位进行组合,计算其相关性。有关于n-gram的深度解释和优化方法在其他博客中已经非常完备,由于本人水平不足,不在此卖弄,谨附上链接供各位参考:https://blog.csdn.net/songbinxu/article/details/80209197

unigram、bigram、trigram

理论上来说,n-gram中用来匹配的N越大,语义判断越准确。然而在N过大的情况下,可能出现一万个句子中只有一两个能得到匹配结果,其余的全是无法匹配。因此N的选定较为重要,一般认为N=3即具备良好的性质。
· unigram指词语独立,每个词之间没有关联关系
· unigram指两个词语是一个单元
· trigram词语是一个单元

unigram、bigram频率统计

unigram对独立的词语进行频率统计,将句子用分词工具处理后(例如jieba分词),通过collection.Counter方式得出统计结果。
如果需要bigram,则需要先对分词结果进行n-gram处理,得出关联关系,然后再按照前面的方式得出统计结果。

文本矩阵化

分词(这里使用jieba分词)

有篇博客介绍jieba分词,写得很好,我直接贴上链接:https://www.cnblogs.com/skyme/p/4651331.html

以下是jieba分词的三种分词模式。

支持三种分词模式:
精确模式,试图将句子最精确地切开,适合文本分析;
全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;
搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。<

可以看出,三种分词模式各有千秋,以下是代码示例。

# encoding=utf-8
import jieba

seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式

seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精确模式

seg_list = jieba.cut("他来到了网易杭研大厦")  # 默认是精确模式
print(", ".join(seg_list))

seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式
print(", ".join(seg_list))

在这一示例中,“行研”并不在字典中。由于jieba分词有一定的新词识别能力,因此通过Viterbi算法识别出了这一词语。输出结果如下:

【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学

【精确模式】: 我/ 来到/ 北京/ 清华大学

【新词识别】:他, 来到, 了, 网易, 杭研, 大厦    (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)

【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造

jieba分词支持导入自定义词典,自定义词典可以选择覆盖在当前词典上,也可以仅用来完善当前词典。在导入词典以外,还支持对当前词典的调整,例如增加新词、删除某一词语等。在此处附上官方示例:

#encoding=utf-8
from __future__ import print_function, unicode_literals
import sys
sys.path.append("../")
import jieba
jieba.load_userdict("userdict.txt")
import jieba.posseg as pseg

jieba.add_word('石墨烯')
jieba.add_word('凱特琳')
jieba.del_word('自定义词')

test_sent = (
"李小福是创新办主任也是云计算方面的专家; 什么是八一双鹿\n"
"例如我输入一个带“韩玉赏鉴”的标题,在自定义词库中也增加了此词为N类\n"
"「台中」正確應該不會被切開。mac上可分出「石墨烯」;此時又可以分出來凱特琳了。"
)
words = jieba.cut(test_sent)
print('/'.join(words))

print("="*40)

result = pseg.cut(test_sent)

for w in result:
    print(w.word, "/", w.flag, ", ", end=' ')

print("\n" + "="*40)

terms = jieba.cut('easy_install is great')
print('/'.join(terms))
terms = jieba.cut('python 的正则表达式是好用的')
print('/'.join(terms))

print("="*40)
# test frequency tune
testlist = [
('今天天气不错', ('今天', '天气')),
('如果放到post中将出错。', ('中', '将')),
('我们中出了一个叛徒', ('中', '出')),
]

for sent, seg in testlist:
    print('/'.join(jieba.cut(sent, HMM=False)))
    word = ''.join(seg)
    print('%s Before: %s, After: %s' % (word, jieba.get_FREQ(word), jieba.suggest_freq(seg, True)))
    print('/'.join(jieba.cut(sent, HMM=False)))
    print("-"*40)

(“我们中出了一个叛徒”和“欢迎新老师生前来就餐”有种异曲同工之妙啊…)
关于在jieba分词中多次提及的HMM算法,我也找到了一篇很好的博客,在这里附上链接:https://www.cnblogs.com/skyme/p/4651331.html
希望自己以后能有时间有能力安心啃博客吧。

去停用词

引用自https://blog.csdn.net/Junkichan/article/details/51883207


stopwords=[]
st = open('/Users/Administrator/Desktop/stopwords3.txt', 'rb')  
for line in st: 
    stopwords.append(line)  
                
for j in range(1,10):
    for i in range(10, 510):
        print u'正在处理',(j,i)
        try:
            f = open('/Users/Administrator/Desktop/delstopwords2/%d/%d.txt' % (j,i), 'rb') 
            for line in f:    
                if line not in stopwords:  
                    b = open('/Users/Administrator/Desktop/delstopwords3/%d/%d.txt' % (j,i), 'a')
                    line=line.strip()
                    b.write(line)
                    b.write('\n')
                    b.close()
        except:
            continue

文本向量化

这里的主要内容来自一篇博客,附上连接:https://blog.csdn.net/u012374510/article/details/54972133
目前在我知识体系内的是词袋模型,将文本独热化。这种方式可以剔除一些生僻不常用的字词,从而提高文本的利用率。问题点在于汉语的含义高度压缩于寥寥数字,且不同人对汉语的使用能力不同,过度口语化的句子和过度凝练的句子都难以分析。具体的弊端在参考的博客中已经提及,我直接抄过来吧:

1.多维度灾难,词的维数比较多
2.语义丢失, 表现为,词的顺序信息丢失,近义词没办法体现,假定词都是独立的,等等

唔…已经抄了这么多了,那就把通过sklearn进行的变换操作得代码也复制过来吧。引自https://blog.csdn.net/herosunly/article/details/89177698,感谢学习小组先锋。

from sklearn.feature_extraction.text import CountVectorizer

count_vectorizer = CountVectorizer(stop_words=stop_words) #上一步的停用词
count_vectorizer.fit(seg_list)
vec = count_vectorizer.transform(seg_list).toarray()
vocab_list = count_vectorizer.get_feature_names() #得到字典

猜你喜欢

转载自blog.csdn.net/weixin_44609088/article/details/89221035