文本分析相关技术

  • 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的时候,依赖于后续的操作进行选择。

计算编辑距离

基于动态规划的解法:
设有两个字符串
  文本hash java 文本分析法_nlp
  文本hash java 文本分析法_nlp_02
 取一个二维数组dp[i][j]表示S[1:i] 和T[1:j]之间的editDist。
 最终的editDist(S,T)=dp[m][n]
 dp[i][j]的值的更新逻辑如下:
   if 文本hash java 文本分析法_文本hash java_03:
     dp[i][j]=dp[i-1][j-1];
   if文本hash java 文本分析法_编辑距离_04:
    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)]

排序

通过单词过滤之后,可以获得一些正确的单词候选,再对这些单词进行一些排序,可以为用户提供选择,或者直接进行替换。

此时,可以转化成为一个概率的问题。
对于用户的输入文本hash java 文本分析法_编辑距离_05,找到可能的正确字符串文本hash java 文本分析法_编辑距离_06,最终排序的目标函数为:
文本hash java 文本分析法_编辑距离_07
argmax是对函数求参数(集合)的函数,是使得后面的函数取得最大值所对应的变量点x(或x的集合)。
在这里,就是给定一个S,在一个集合C中,求出使得P(C|S)最大的C。
利用贝叶斯公式,目标函数可以改写为:
文本hash java 文本分析法_nlp_08
对于任何一个C,P(S)都是一个常数项。
文本hash java 文本分析法_文本hash java_09
文本hash java 文本分析法_python_10是输入一个正确的字符串C时,错误输入成为S的概率。这个概率可以从从历史数据中统计出来。
文本hash java 文本分析法_nlp_11总文档中,出现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 欧氏距离

文本hash java 文本分析法_nlp_12
计算两个向量之间的欧氏距离,反向推出句子之间的相关性,距离越小,相关性越大。
直观上理解就是,关注的是两个句子之间的相同词的出现的个数之间的差距,只关注了距离,没有关注方向。

4.2.2 余弦相似度

文本hash java 文本分析法_python_13
值越大,相似度越高。

4.3 刻画句子中某个词的重要性(tf-idf表示法)

上面所用到的句子向量的表示,记录了每个词出现的频率,但是频率并不能代表词的重要性,有可能频率很低,但是那个词却很重要。

文本hash java 文本分析法_nlp_14
其中,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 分布式表示

词向量

在上述词袋模型表示方法下,两个词向量之间的欧氏距离都是文本hash java 文本分析法_python_15,余弦相似度都是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…