在机器学习中,我们经常使用该方法处理一些类别型的数据。比方说,在预测放假的时候,有一微特征表示几线城市,有“一线”,“新一线”,“二线”,“三线”,“四线”,“其他”等,那么当某一个特征的值为“二线”的时候,就可以表示为[0, 0, 1, 0, 0, 0]
。在实现的时候,可使用sklearn进行one-hot编码。
# 例子来自sklearn的官方例子,https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
>>> from sklearn.preprocessing import OneHotEncoder
>>> enc = OneHotEncoder(handle_unknown='ignore')
>>> X = [['Male', 1], ['Female', 3], ['Female', 2]]
>>> enc.fit(X)
OneHotEncoder(handle_unknown='ignore')
>>> enc.categories_
[array(['Female', 'Male'], dtype=object), array([1, 2, 3], dtype=object)]
>>> enc.transform([['Female', 1], ['Male', 4]]).toarray()
array([[1., 0., 1., 0., 0.],
[0., 1., 0., 0., 0.]])
在文本处理中,可以以此表vocab中的所有词作为类别集合,按照文本所需处理的词进行one-hot编码,用于one-hot处理。
然而,使用one-hot编码无法表示词的重要性。比如一个文本嗯,我是程序员
中,经过分词之后是["嗯",“我”,“是”, "程序员"]
, 显然这句话中我
,是
,程序员
这几个词比嗯
重要,但是在经过one-hot处理之后,是看不出来这几个词之间的区别的。而使用tfidf方式就能够表示词的重要性。
tfidf的方法,是根据一个词在一篇文本中出现的次数,以及该词在所有文本中出现的次数来计算的,用于表示一个词的重要程度。比如某词的ifidf的计算公式如下 t f = 某 词 在 文 本 中 出 现 的 次 数 该 文 本 的 词 的 总 数 tf=\frac{某词在文本中出现的次数}{该文本的词的总数} tf=该文本的词的总数某词在文本中出现的次数
i d f = l o g 语 料 中 的 文 本 总 数 含 有 该 词 的 文 本 总 数 + 1 idf=log\frac{语料中的文本总数}{含有该词的文本总数+1} idf=log含有该词的文本总数+1语料中的文本总数
t f i d f = t f ∗ i d f tfidf=tf*idf tfidf=tf∗idf直观的理解就是,一个词在某个文本中出现的次数越多,那么该词在该文本中就越重要(即 t f tf tf),但是如果一个词在语料的所有文本中都出现过,那么该词就不那么重要了,比如的
,了
之类的词可能在语料的很多文本中出现过,但是这些词是不重要的(即 i d f idf idf),于是就可以使用 t f ∗ i d f tf*idf tf∗idf来表示一个词在所在文本中的重要新。
而理论上对于每一个文本,都可以计算词表vocab中所有词在该文本中的 t f i d f tfidf tfidf(如果某次在该文本中没出现过,那么就可以直接以0表示重要性),那么对于每一个文本,就可以用一个词表长度的向量来表示。计算一个文本的tfidf
值,同样可以调用sklearn。
# 例子来自sklearn的官方例子,https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html
>>> from sklearn.feature_extraction.text import TfidfVectorizer
>>> corpus = [
... 'This is the first document.',
... 'This document is the second document.',
... 'And this is the third one.',
... 'Is this the first document?',
... ]
>>> vectorizer = TfidfVectorizer()
>>> X = vectorizer.fit_transform(corpus)
>>> print(vectorizer.get_feature_names())
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
>>> print(X.shape)
(4, 9)
使用 t f i d f tfidf tfidf虽然能够表示一个词在文本中的重要程度,但是却无法表示词的先后顺序。比如鸣人喜欢雏田
和雏田喜欢鸣人
,这是两句不一样的话,但是在 t f i d f tfidf tfidf的表示方法中,这两句话却会被表示成一样的向量。如果我们能够在文本表示中添加词的先后顺序的信息,就能一定程度的解决这个问题。
因此,我们可以使用n-gram来表示文本。n-gram,就是把文本分词之后,以n个词为一个单元来进行处理。比如,以bi-gram(即n=2)为例,在上述的两句话中,经过分词之后,得到的均是[鸣人,喜欢, 雏田]
,而第一句使用bi-gram表示结果为[鸣人喜欢,喜欢雏田]
,第二句的表示结果为[雏田喜欢,喜欢鸣人]
,这样两句话的表示结果就不一样了。
但是,使用n-gram表示方法,每一个文本档的表示结果就不是词表的大小,而是词表大小vocab_size
的n次方。这就使得每一个文本的表示结果非常长,且非常稀疏。
后来,一些大牛们就想到了使用神经网络来训练表示文本的向量,于是就有了NNLM(Feedforward Neural Net Language Mode)。NNLM的网络结构如下:
NNLM,Feedforward Neural Net Language Mode,翻译过来就是“前向神经网络语言模型”,这是一个语言模型,文本的词向量表示是其中间产物。
如上图所示,NNLM是使用文本中的某一个词的前N-1个词来预测该词。NNLM的预测流程如下:
shape=vocab_size*embedding_size
的矩阵 C C C转化伟相应词的向量,有N-1个词,于是就得到了N-1个向量NNLM为语言模型,而我们取其中的矩阵 C C C作为我们的词向量矩阵,矩阵中第 i i i行的向量,就是词表中第 i i i个词的向量化结果。
在NNLM的训练过程中,时间复杂度为 Q = N ∗ D + N ∗ D ∗ H + H ∗ V Q=N*D+N*D*H+H*V Q=N∗D+N∗D∗H+H∗V其中N为上述过程中的N,D为词向量的大小,即为上述过程中的embedding_size,H为隐藏层的大小,V为词表大小。
上述的计算的时间复杂度也比较大,尤其是在隐藏层中消耗比较大的计算资源,为此, T o m a s M i k o l o v Tomas Mikolov TomasMikolov提出了新的词向量训练模型wor2vec用于简化计算,word2vec有CBOW和n-gram两种形式,并且针对词表比较大的情况又提出了hierarchical softmax和nagative sampling两种优化方法。
上图即为word2vec的两种形式,具体对于这两种形式的介绍,待下一篇再进行介绍。