返回

自然语言处理(2)之浅谈向量化与Hash-Trick

关键字全网搜索最新排名

【机器学习算法】:排名第一

【机器学习】:排名第一

【Python】:排名第三

【算法】:排名第四

这一系列公开课将由一线技术专家从不同技术细分领域分享AI技术与行业发展状况,

分享主题涵盖语音识别、图像识别、自然语言处理、深度学习等

前言

在文本挖掘的分词原理(干货 | 自然语言处理(1)之聊一聊分词原理)中讲到了文本挖掘的预处理中的分词,而分词后,如果是做文本分类,则后面关键的特征预处理步骤有向量化或向量化的特例Hash Trick,本文对向量化和特例Hash Trick预处理方法做一个总结。

词袋模型

词袋模型(Bag of Words,简称BoW)假设不考虑文本中词与词之间的上下文关系,仅仅只考虑所有词的权重。而权重与词在文本中出现的频率有关。

词袋模型首先会进行分词,通过统计每个词在文本中出现的次数,就可以得到该文本基于词的特征,如果将各个文本样本的这些词与对应的词频放在一起,就是我们常说的向量化。向量化完毕后一般也会使用TF-IDF进行特征的权重修正,再将特征进行标准化。 再进行一些其他的特征工程后,就可以将数据带入机器学习算法进行分类聚类了。

总结下词袋模型的三部曲:分词(tokenizing),统计修订词特征值(counting)与标准化(normalizing)。

与BoW非常类似的是词集模型(Set of Words,简称SoW),和词袋模型唯一的不同是它仅仅考虑词是否在文本中出现,而不考虑词频。也就是一个词在文本在文本中出现1次和多次特征处理是一样的。在大多数时候,我们使用词袋模型,后面的讨论也是以词袋模型为主。

当然,词袋模型有很大的局限性,因为它仅仅考虑了词频,没有考虑上下文的关系,因此会丢失一部分文本的语义。

BoW之向量化

在词袋模型进行词频统计后,就可以用词向量表示这个文本。这里举一个直接用scikit-learn的CountVectorizer类来完成的例子,这个类可以完成文本的词频统计与向量化,代码如下:

from sklearn.feature_extraction.text import CountVectorizer

vectorizer=CountVectorizer()

corpus=["I come to China to travel",

"This is a car polupar in China",

"I love tea and Apple ",

"The work is to write some papers in science"]

print vectorizer.fit_transform(corpus)

对上面4个文本的处理输出如下:

在输出中,括号中的第一个数字是文本的序号,第2个数字是词的序号(注意词的序号是基于所有的文档的),第三个数字就是词频。

可以进一步看看每个文本的词向量特征和各个特征代表的词,代码如下:

print vectorizer.fit_transform(corpus).toarray()

print vectorizer.get_feature_names()

输出为:

可以看到一共有19个词,则4个文本都是19维的特征向量。而每一维的向量依次对应了下面的19个词。另外由于词"I"在英文中是停用词,不参加词频的统计。

由于大部分的文本都只会使用词汇表中的很少一部分的词,因此我们的词向量中会有大量的0。也就是说词向量是稀疏的。在实际应用中一般使用稀疏矩阵来存储。

向量化的方法很好用,也很直接,但在有些场景下很难使用,比如分词后的词汇表非常大,达到100万+,此时如果直接使用向量化的方法,将对应的样本对应特征矩阵载入内存,有可能将内存撑爆,在这种情况下我们怎么办呢?第一反应是我们要进行特征的降维,而Hash Trick就是非常常用的文本特征降维方法。

Hash Trick

在大规模的文本处理中,由于特征的维度对应分词词汇表的大小,所以维度可能非常恐怖,此时需要进行降维,不能直接用上一节的向量化方法。而最常用的文本降维方法是Hash Trick,这里的Hash也有类似意义。

在Hash Trick中,首先定义一个Hash后对应的哈希表,这个哈希表的维度会远远小于词汇表的特征维度,因此可以看成是降维。具体的方法是,对任意一个特征,使用用Hash函数找到对应哈希表的位置,然后将该特征对应的词频统计值累加到该哈希表位置。如果用数学语言表示,假如哈希函数h使第i个特征哈希到位置j,即h(i)=j,则第i个原始特征的词频数值ϕ(i)将累加到哈希后的第j个特征的词频数值ϕ¯上,即:

上述方法有可能使两个原始特征的哈希后位置在一起导致词频累加特征值变大,为了解决这个问题,出现了hash Trick的变种signed hash trick,此时除了哈希函数h,多了一个哈希函数,如下:

这样哈希后的特征仍然是一个无偏的估计,不会导致某些哈希位置的值过大。

当然,大家会有疑惑,哈希后的特征是否能够很好的代表哈希前的特征呢?从实际应用中说,由于文本特征的高稀疏性,这么做是可行的。详情建议参考论文:Feature hashing for large scale multitask learning(链接:http://alex.smola.org/papers/2009/Weinbergeretal09.pdf)

在scikit-learn的HashingVectorizer类中,实现了基于signed hash trick的算法,为了简单,使用上面的19维词汇表,并哈希降维到6维。当然在实际应用中,19维的数据根本不需要Hash Trick,这里只是做一个演示,代码如下:

from sklearn.feature_extraction.text import HashingVectorizer vectorizer2=HashingVectorizer(n_features = 6,norm = None)

print vectorizer2.fit_transform(corpus)

输出为:

与PCA类似,Hash Trick降维后的特征已经不知道它代表的特征名字和意义。此时不能像向量化时可以知道每一列的意义,所以Hash Trick的解释性不强。

小结

对向量化与Hash Trick做了简单的介绍。在特征预处理时,什么时候用一般意义的向量化,什么时候用Hash Trick?

一般而言,只要词汇表的特征不至于太大(大到内存不够用),使用一般意义的向量化比较好。因为向量化的方法解释性很强,知道每一维特征对应哪一个词,进而还可以使用TF-IDF对各个词特征的权重修改,进一步完善特征的表示。

而Hash Trick用大规模机器学习上,词汇量极大,使用向量化方法内存不够用,而使用Hash Trick降维速度很快,降维后的特征仍然可以完成后续的分类和聚类工作。

当然由于分布式计算框架的存在,其实一般不会出现内存不够的情况。因此,实际工作中我都是使用特征向量化。

相关知识