词级别的各种处理总结
对于实际场景中的文本信息,需要经过一系列预处理后才能用来作为原始特征进行加工和建模。基本的流程是:先去除非文字类的各种符号,比如emoji、各种外文字母、空格、标点等;然后对中文进行切词处理,然后去除停用词。最后还可以计算tf-idf对各个词语的特异性进行表征,可能用于后续建模中对词语赋权。
对于不同任务,可能还需要进行不同的处理。比如,在基于搜索query的物品推荐时,需要对切分的词进行词性过滤,保留名词、专有名词等,对于涉及到明星和节目的视频推荐时,需要提取人名、专名等。在提取地理位置相关的特征时,需要过滤出地名等等。
一般通过Unicode范围进行约束,比如过滤提取中文的函数如下:
def is_hanzi(char):
if char >= u'\u4e00' and char <= u'\u9fa5':
return True
else:
return False
def filter_hanzi(sentence):
res = ''
for c in sentence:
if is_hanzi(c):
res += c
return res
通常用jieba工具对文本分词,git repo:https://github.com/fxsjy/jieba
jieba中基本分词算法:
- 前缀字典建图 + 动态规划
- OOV时用HMM+Viterbi计算
jieba分词:参数与使用
jieba分词可以采用全模式、精确模式、搜索引擎模式。如下:
# encoding=utf-8
import jieba
jieba.enable_paddle()# 启动paddle模式。 0.40版之后开始支持,早期版本不支持
strs=["我来到北京清华大学","乒乓球拍卖完了","中国科学技术大学"]
for str in strs:
seg_list = jieba.cut(str,use_paddle=True) # 使用paddle模式
print("Paddle Mode: " + '/'.join(list(seg_list)))
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.cut(s, cut_all=True)
表示将所有的可能的词语都列举出来,比如【清华】【大学】是可以的,而【华大】也是可以的,那么虽然两种是不同的分法,但是都被列举出来了。 - 精确模式
jieba.cut(s, cut_all=False)
即正常的按照最精确的方式划分,一般的文本分析中都用精确模式。 - 搜索引擎模式
jieba.cut_for_search
是在精确模式基础上,对长词语,比如【中国科学院】继续划分,得到【中国】【科学】【学院】【科学院】等一大批可能的词,这个切词方法一般用于搜索引擎(尽可能保召回),因此被称为搜搜引擎模式。
【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学
【精确模式】: 我/ 来到/ 北京/ 清华大学
【新词识别】:他, 来到, 了, 网易, 杭研, 大厦 (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)
【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造
去除无意义的虚词,比如连词、语气词等。就是简单地根据停用词表对分好词的list进行过滤筛选。比较著名的如哈工大停用词表。
tf-idf是term frequency和inverse document frequency的乘积,代表着某个word对于所在的document的重要性。term frequency即该word在本doc中出现的频率:
$ tf_i = n_i / \sum_{k\in doc} n_k$
Inverse document frequency即文档频率的倒数。所谓文档频率,指的是在所有文档中,有该word的文档的比例。这个文档频率反映的是word的特异性,即是不是只在这一篇文章中有,还是在所有文档中都有。因为有些词,如“的”、“你”、“了”等,其实就是前面说的停用词,虽然词频很高,但是并没有太多代表性,因此需要对这类词进行降权,所以对文档频率取倒数,即inverse document frequency,来表示词的特异性和代表性。
这里由于总文档数一般较大,因此求了log,tf-idf即两者的乘积。
最终只需要按照tf-idf排序,即可得到一篇文章中各个词的重要性。
tfidf的计算在sklearn中已经有现成的api可调用:
class sklearn.feature_extraction.text.TfidfTransformer(*, norm='l2', use_idf=True, smooth_idf=True, sublinear_tf=False)
可以直接根据语料统计后的结果与词典,计算出tfidf编码后的结果。下面是官方文档的实例:
>>> from sklearn.feature_extraction.text import TfidfTransformer
>>> from sklearn.feature_extraction.text import CountVectorizer
>>> from sklearn.pipeline import Pipeline
>>> import numpy as np
>>> corpus = ['this is the first document',
... 'this document is the second document',
... 'and this is the third one',
... 'is this the first document']
>>> vocabulary = ['this', 'document', 'first', 'is', 'second', 'the',
... 'and', 'one']
>>> pipe = Pipeline([('count', CountVectorizer(vocabulary=vocabulary)),
... ('tfid', TfidfTransformer())]).fit(corpus)
>>> pipe['count'].transform(corpus).toarray()
array([[1, 1, 1, 1, 0, 1, 0, 0],
[1, 2, 0, 1, 1, 1, 0, 0],
[1, 0, 0, 1, 0, 1, 1, 1],
[1, 1, 1, 1, 0, 1, 0, 0]])
>>> pipe['tfid'].idf_
array([1. , 1.22314355, 1.51082562, 1. , 1.91629073,
1. , 1.91629073, 1.91629073])
>>> pipe.transform(corpus).shape
(4, 8)