且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

机器学习中,使用Scikit-Learn简单处理文本数据

更新时间:2022-08-12 18:22:12

更多深度文章,请关注云计算频道:https://yq.aliyun.com/cloud

 

机器学习中,我们总是要先将源数据处理成符合模型算法输入的形式,比如将文字、声音、图像转化成矩阵。对于文本数据首先要进行分词(tokenization),移除停止词(stop words),然后将词语转化成矩阵形式,然后再输入机器学习模型中,这个过程称为特征提取(feature extraction)或者向量化(vectorization)。本文会教你使用Scikit-Learn机器学习库中的三种模型来实现这一转化过程,包括CountVectorizer, TfidfVectorizer, HashingVectorizer

 

词袋模型

在将文本数据进行分词操作以后,有两种处理方法,一种是做句法分析,另一种是对这些词从统计学上进行分析,即词袋模型(Bag-of-Words Model, BoW)。词袋模型将文档看成一个袋子,里面装着文档中的词汇表。词袋模型剔除了一些对于统计模型没有意义的词,即停止词,比如那些出现频率高的连词,介词。这些停止词在自然语言中起到很重要的连接作用,和词序一起构成了合乎文法的语言结构,对于理解自然语言相当重要。但词袋模型并没有利用这些信息,而且还具有干扰作用,因此在词袋模型中只能放弃这些停止词,不过还是可以使用N元文法(N-Gram)进行一定程度上弥补。词袋模型会给每个词都赋予一个唯一的数值,任何一个文档都可以根据已知的词汇表表示成固定长度的向量,向量中的每个值表示文档中每个词的出现次数,然后就可以将这些向量或进一步处理或直接输入机器学习算法中。

 

CountVectorizer 方法

CountVectorizer简单地对文档分分词,形成词汇表,然后对新文档就可以使用这个词汇表进行编码,最终将会返回一个长度等于词汇表长度的向量,每个数字表示单词在文档中出现的次数。由于这个向量中会有很多0,python中可使用scipy.sparse包提供的稀疏向量表示。比如下面这个例子:


1.	# list of text documents, the text maybe a file or a list or other documents
2.	text = ["The quick brown fox jumped over the lazy dog."]  
3.	# create the transform  
4.	vectorizer = CountVectorizer()  
5.	# tokenize and build vocabulary  
6.	vectorizer.fit(text)  
7.	# summarize  
8.	print(vectorizer.vocabulary_)  
9.	# encode document  
10.	vector = vectorizer.transform(text)  
11.	# summarize encoded vector  
12.	print(vector.shape)  
13.	print(type(vector))  
14.	print(vector.toarray())  

输出:


1.	{u'brown': 0, u'lazy': 4, u'jumped': 3, u'over': 5, u'fox': 2, u'dog': 1, u'quick': 6, u'the': 7}  
2.	(1, 8)  
3.	<class 'scipy.sparse.csr.csr_matrix'>  
4.	[[1 1 1 1 1 1 1 2]] 

其中,fit(.)函数从文档中学习出一个词汇表,transform(.)函数将指定文档转化为向量。从打印出来的词汇表可以看出,所有单词默认被处理为小写并且忽略标点符号。词汇表中总共有8个单词,最后生成向量的长度也为8,其中的数字表示单词的出现次数,比如编号为7的单词the出现了2次。

假设现在有一个新文档,这个文档里面的某些词不在已知的词汇表中,那么这些词就会被忽略掉,比如下面这个例子:


1.	# encode another document  
2.	text2 = ["the puppy"]  
3.	vector = vectorizer.transform(text2)  
4.	print(vector.toarray())  
5.	# [[0 0 0 0 0 0 0 1]]  

词汇表中并没有puppy这个单词,只有单词the在词汇表中有记录,那么最后形成的向量长度不变,但是会很稀疏。

TfidfVectorizer方法

CountVectorizer方法中,我们仅仅是统计了词汇的出现次数,比如单词the会出现比较多的次数,但实际上,这个单词并不是很有实际意义。因此使用TF-IDF方法来进行向量化操作。TF-IDF原本是一种统计方法,用以评估字词对于一个文件集或一个语料库中的其中一份文件的重要程度。这个方法认为,字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降,其实也就相当于在CountVectorizer的基础上结合整个语料库考虑单词的权重,并不是说这个单词出现次数越多它就越重要。比如下面这个例子。


1.	from sklearn.feature_extraction.text import TfidfVectorizer  
2.	# list of text documents  
3.	text = ["The quick brown fox jumped over the lazy dog.",  
4.	        "The dog.",  
5.	        "The fox"]  
6.	# create the transform  
7.	vectorizer = TfidfVectorizer()  
8.	# tokenize and build vocab  
9.	vectorizer.fit(text)  
10.	# summarize  
11.	print(vectorizer.vocabulary_)  
12.	print(vectorizer.idf_)  
13.	# encode document  
14.	vector = vectorizer.transform([text[0]])  
15.	# summarize encoded vector  
16.	print(vector.shape)  
17.	print(vector.toarray())  
18.	print(vectorizer.transform([text[1]]).toarray())

输出:


1.	{u'brown': 0, u'lazy': 4, u'jumped': 3, u'over': 5, u'fox': 2, u'dog': 1, u'quick': 6, u'the': 7}  
2.	[ 1.69314718  1.28768207  1.28768207  1.69314718  1.69314718  1.69314718  
3.	  1.69314718  1.        ]  
4.	(1, 8)  
5.	[[ 0.36388646  0.27674503  0.27674503  0.36388646  0.36388646  0.36388646  
6.	   0.36388646  0.42983441]]  
7.	[[ 0.          0.78980693  0.          0.          0.          0.         0.
8.	   0.61335554]]  

这里是使用了3个小文本,总共有8个词汇。可以看到单词the在每个文本中都出现过,这就导致他的idf值是最低的,显得单词the并没那么重要。通过输出前两个文本的向量对比,可以发现这种表示方法依然会输出稀疏向量。

HashingVectorizer方法

上面介绍的基于次数和频率的方法比较耿直,其限制性在于生成的词汇表可能会非常庞大,导致最后生成的向量会很长,对内存需求会很大,最后就会降低算法效率。一个巧妙的方法是使用哈希方法,将文本转化为数字。这种方法不需要词汇表,你可以使用任意长度的向量来表示,但这种方法不可逆,不能再转化成对应的单词,不过很多监督学习任务并不care

下面这个例子就展示了HashingVectorizer是如何进行向量化。


1.	from sklearn.feature_extraction.text import HashingVectorizer  
2.	# list of text documents  
3.	text = ["The quick brown fox jumped over the lazy dog."]  
4.	# create the transform  
5.	vectorizer = HashingVectorizer(n_features=20)  
6.	# encode document  
7.	vector = vectorizer.transform(text)  
8.	# summarize encoded vector  
9.	print(vector.shape)  
10.	print(vector.toarray()) 
输出


1.	(1, 20)  
2.	[[ 0.          0.          0.          0.          0.          0.33333333  
3.	   0.         -0.33333333  0.33333333  0.          0.          0.33333333  
4.	   0.          0.          0.         -0.33333333  0.          0.  
5.	  -0.66666667  0.        ]]  

可以看到这里的向量长度并不是8,而是20,而且也不需要像上面两种方法一样需要fit(.),你可以直接使用。最终生成的向量是被归一化到-1到1之间,你也可以改变HashingVectorizer(.)函数的参数设置使最后的输出结果取整。


参考链接:

词袋模型

分词

TF-IDF

CountVectorizer(.)方法接口

TfidfVectorizer(.)方法接口

HashingVectorizer(.)方法接口

 

Tips:本文中的例子基本都使用的是这些方法的默认设置,你也可以改变这些方法中的参数以达获得不同的向量化结果。

 

作者信息

机器学习中,使用Scikit-Learn简单处理文本数据

Dr. Jason Brownlee 是一名机器学习从业者,学术研究人员,致力于帮助开发人员从入门到精通机器学习。


本文由北邮@爱可-爱生老师推荐,阿里云云栖社组织翻译。

文章原标题《How to Prepare Text Data for Machine Learning with scikit-learn

作者:Dr.Jason Brownlee译者:李烽,审阅:

文章为简译,更为详细的内容,请查看原文