文本分析相关技术
- 1 拼写错误纠正(spell correction)
- 编辑距离(edit distance)
- 计算编辑距离
- 生成指定编辑距离的单词
- 排序
- 2 分词(word segmentation)
- 简单方法
- jieba的简单实用
- 3 词的过滤、stemming
- 词的过滤
- stemming
- 4 文本的表示
- 4.1词袋模型(bag-of-wordsModel)
- 4.2表示句子之间的相关性
- 4.2.1 欧氏距离
- 4.2.2 余弦相似度
- 4.3 刻画句子中某个词的重要性(tf-idf表示法)
- 4.4 分布式表示
- 词向量
- 句子向量
- 4.5 word2vec类算法库调用
作为学习的记录,本文概述了文本处理所涉及的概念和一些技术,附带了一些参考链接。
1 拼写错误纠正(spell correction)
编辑距离(edit distance)
编辑距离是指一个错误的输入,转化成正确的一个单词,需要付出的代价;
一个字符串转化成另一个字符串,可以进行替换、删除、增加一个字符来进行修改。当一个字符串被检测出来可能出错了,系统推荐几个类似的单词候选,分别需要进行不同次数的修改才能得到。
不同的操作可以有不同的权值,当权值相等时有如下的例子:
input | candidates | edit distance |
therr | there | 1 |
their | 1 | |
thesis | 3 | |
theirs | 2 | |
the | 2 |
可以看出来,编辑距离越小,相似度越大。
当出现像这里there和their的编辑距离都是1的时候,依赖于后续的操作进行选择。
计算编辑距离
基于动态规划的解法:
设有两个字符串
取一个二维数组dp[i][j]表示S[1:i] 和T[1:j]之间的editDist。
最终的editDist(S,T)=dp[m][n]
dp[i][j]的值的更新逻辑如下:
if :
dp[i][j]=dp[i-1][j-1];
if:
replace:dp[i][j]=1+dp[i-1][j-1];
add:dp[i][j]=1+dp[i][j-1];
delete:dp[i][j]=1+dp[i-1][j];
代码如下:
def edit_dist(str1,str2):
m,n=len(str1),len(str2);
dp=[[0 for x in range(n+1)]for x in range(m+1)]
for i in range(m+1):
for j in range(n+1):
#若第一个字符串为空,则转换的代价为j次插入
if i==0:
dp[i][j]=j
#若第二个字符串为空,则转换的代价为i次插入
elif j==0:
dp[i][j]=i
#若最后一个字符串相等则不会产生代价。
elif str1[i-1]==str2[j-1]:
dp[i][j]=dp[i-1][j-1]
#若最后一个字符串不一样,
else:
dp[i][j]=1+min(dp[i][j-1],#插入
dp[i-1][j],#删除
dp[i-1][j-1])#替换
return dp[m][n]
生成指定编辑距离的单词
找出编辑距离最小的方法,有两种思路:
一种是把输入与词典库里的每一个词作比对,找出编辑距离最小的单词,再排序;
另一种是根据用户输入,生成编辑距离为1,2的所有可能字符串,通过词典过滤,再进行排序。
很明显当词典库单词过多的时候,第一种所需的代价太大。
def generate_edit_one(str):
"""
产生编辑距离为1的字符串
"""
letters='abcdefghijklmnopqrstuvwxyz'
#str所有可能的拆分
splits=[(str[:i],str[i:]) for i in range(len(str)+1)]
#三种操作
inserts=[L + c + R for L,R in splits for c in letters]
deletes=[L + R[1:] for L,R in splits if R]
replaces=[L + c + R for L,R in splits if R for c in letters]
#返回,去掉重复
return set(inserts+deletes+replaces)
def generate_edit_two(str):
"""
产生编辑距离不大于2的字符串
"""
return [e2 for e1 in generate_edit_one(str) for e2 in generate_edit_one(e1)]
排序
通过单词过滤之后,可以获得一些正确的单词候选,再对这些单词进行一些排序,可以为用户提供选择,或者直接进行替换。
此时,可以转化成为一个概率的问题。
对于用户的输入,找到可能的正确字符串,最终排序的目标函数为:
argmax是对函数求参数(集合)的函数,是使得后面的函数取得最大值所对应的变量点x(或x的集合)。
在这里,就是给定一个S,在一个集合C中,求出使得P(C|S)最大的C。
利用贝叶斯公式,目标函数可以改写为:
对于任何一个C,P(S)都是一个常数项。
是输入一个正确的字符串C时,错误输入成为S的概率。这个概率可以从从历史数据中统计出来。
总文档中,出现C的概率,是一个先验的概率。
2 分词(word segmentation)
在汉语中,分词是很重要的一步。
简单方法
①对语句进行候选分割;
为了得到候选分割,首先需要一个词典,验证单词有效性,找出所有的分割情况;
②然后挑选出最好的。
在挑选出最好分割时,需要借助语言模型,找出最好的分割。
缺点:效率低;两步线性处理,误差会延续。
改进:分割和语言模型同时考虑,在目标函数中体现两方面的内容。
jieba的简单实用
很成熟的一个中文分词组件,它主要有以下 3 种特性:
- 支持 3 种分词模式:精确模式、全模式、搜索引擎模式
- 支持繁体分词
- 支持自定义词典
完整文档 https://github.com/fxsjy/jieba 简单使用:
import jieba
seg_list=jieba.cut("今天天气真好",cut_all=False)
print("default model" + "/ ".join(seg_list))
#载入词典
jieba.load_userdict("userdict.txt")
jieba.add_word('创协') #增加自定义词语
jieba.del_word('创协') #删除自定义词语
jieba.add_word('创协', freq=22, tag='nz') #设置词频和词性 其他专名
3 词的过滤、stemming
词的过滤
对于NLP的应用,通常将停用词、出现频率非常低的词过滤掉。
可以提高系统准确率,降低时间成本。
停用词:包括一些做连词,出现次数很多,但是与应用的场景关系不大可以作为停用词。
出现频率非常低的词:也要考虑词频和应用场景。
stemming
多用于英文,把单词做一些转换,变成通用的单词。
例如:went go going 可以合成一个单词。
算法推荐:PorterStemmer
4 文本的表示
把文本表示成为模型可以识别出来的数值,例如向量、矩阵、张量。
4.1词袋模型(bag-of-wordsModel)
词向量
词典内不考虑单词的顺序,可以直接按照首字母排序,为每一个单词表示时,对应序号的数字为1,其他为0。
例如:
词典:[今天, 昨天, 我, 吃饭]
今天:[1,0,0,0]
昨天:[0,1,0,0]
…
特点:
维度=词典大小
每一个词向量只有一个1,其他全为0。
句子向量 - 只考虑词频
首先对句子分词操作之后,得到一个个的词,向量对应词典内词语的位置,每个参数代表该词出现的频率。
例如:
词典:[今天, 昨天, 我,去,上课]
我今天去上课:[1, 0, 1, 1, 1]
form sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
X=vectorizer.fit_transform(corpus)#X为稀疏矩阵表示方式
print(X.toarray()) #稀疏矩阵转化为矩阵
4.2表示句子之间的相关性
通过句子之间的相关性,来判断两个句子的相关程度,某种程度上可以用一个已知语义的句子来解释另一个相关的句子。
4.2.1 欧氏距离
计算两个向量之间的欧氏距离,反向推出句子之间的相关性,距离越小,相关性越大。
直观上理解就是,关注的是两个句子之间的相同词的出现的个数之间的差距,只关注了距离,没有关注方向。
4.2.2 余弦相似度
值越大,相似度越高。
4.3 刻画句子中某个词的重要性(tf-idf表示法)
上面所用到的句子向量的表示,记录了每个词出现的频率,但是频率并不能代表词的重要性,有可能频率很低,但是那个词却很重要。
其中,tf(d,w)表示词频,单词w在文档d中出现的频率;
idf表示词的重要性log(N/N(w)),N:语料库中的文档总数,N(w):词语w出现在多少个文档。即:w在所有文档中出现的次数越小,重要性越大。
例如:词典[我们,去,上课,今天,你们,昨天,郊游]
我们昨天上课:
你们昨天郊游:
你们上课:
以上有三个文档,对于我们昨天上课:[1log(3/1) , 1log(3/2), 1log(3/2), 0, 0 1log(3/2), 0]
#corpus为多组语料
corpus=['He is Tom.','He is going from Beijing to Shanghai.']
form sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
X=vectorizer.fit_transform(corpus)#X为稀疏矩阵表示方式
print(X.toarray()) #稀疏矩阵转化为矩阵
4.4 分布式表示
词向量
在上述词袋模型表示方法下,两个词向量之间的欧氏距离都是,余弦相似度都是0,无法区分相关性大小。
分布式表示法和词袋模型相比的优点:
①维度小,不再是词典的维度;
②内部的数很少是0,基本都是有一定意义的浮点数,可以利用欧式距离、余弦相似度来求词之间的相似度。
分布式表示方法,是利用深度学习模型(word2vec模型)计算出每个词的词向量。
训练词向量的算法有很多种:SkipGram、Glow、CBow…
模型的输入是语料库,输出是每个词的词向量。
句子向量
一种方法是average,求出平均向量(每一维作平均)
另一种方法是考虑时序,例如有LSTM,RNN
4.5 word2vec类算法库调用
-train 参数:语料库文件
-model 参数:输出文件
-cbow 参数:算法的选择
-negative 参数:负向样本抽样个数,一般5-20
-dim 参数:训练的词向量的维度 一般不超过300,小样本可以小于100
-windows 参数:所考虑的上下文的词的多少,一般5-10
-min-count 参数:过滤掉频率出现非常少的词,一般设置这个频率为5,10,20,30…