One-Hot 编码,又称 “独热编码”,是比较常用的文本特征提取方法。这种方法把每个词表示为一个很长的向量。这个向量的维度是词表大小,只有一个维度的值为 1 1 1,这个向量就代表了当前的词,其他元素为 0 0 0。One-Hot 在特征提取上属于词袋模型(Bag-of-Words),假设语料库中有三句话:
构造一个字典,Dictionary = {1:“我”, 2:“爱”, 3:“爸爸”, 4:“妈妈”, 5:“中国”}
所以最终得到的每句话的特征向量就是:
# 导入keras中的词汇映射器Tokenizer
from tensorflow.keras.preprocessing.text import Tokenizer
# 假定vocab为语料集所有不同词汇集合
vocab = {"我", "爱", "北京", "天安门", "升国旗"}
# 实例化一个词汇映射器对象
t = Tokenizer(num_words=None, char_level=False)
# 使用映射器拟合现有文本数据
t.fit_on_texts(vocab)
print(vocab)
for token in vocab:
zero_list = [0]*len(vocab)
# 使用映射器转化现有文本数据, 每个词汇对应从1开始的自然数
# 返回样式如: [[2]], 取出其中的数字需要使用[0][0]
token_index = t.texts_to_sequences([token])[0][0] - 1
zero_list[token_index] = 1
print(token, "的one-hot编码为:", zero_list)
Bag-of-Words(BOW)模型是信息检索领域常用的文档表示方法。在信息检索中,BOW 模型假定对于一个文档,忽略它的单词顺序、语法、句法等要素,将其仅仅看作是若干个词汇的集合,文档中每个单词的出现都是独立的,不依赖于其它单词是否出现。也就是说,文档中任意一个位置出现的任何单词,都不受该文档语意影响而独立选择的。例如有如下两个文档:
基于这两个文本文档,构造一个词典:
这个词典一共包含 10 个不同的单词,利用词典的索引号,上面两个文档每一个都可以用一个 10 维向量表示(用整数数字 0~n(n 为正整数)表示某个单词在文档中出现的次数):
向量中每个元素表示词典中相关元素在文档中出现的次数。不过,在构造文档向量的过程中可以看到,我们并没有表达单词在原来句子中出现的次序,这也是 Bag-of-Words 模型的缺点之一。
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
data_corpus = ['Bob likes to play basketball, Jim likes too.']
vocabulary = vectorizer.fit(data_corpus)
x = vectorizer.transform(data_corpus)
print(vocabulary.get_feature_names())
print(x.toarray())
对区别文档最有意义的词语应该是那些在文档中出现频率高,而在整个文档集合的其他文档中出现频率少的词语,所以如果特征空间坐标系取词频 TF(Term Frequency)作为测度,就可以体现同类文本的特点。
另外考虑到单词区别不同类别的能力,TF-IDF 认为一个单词出现的文本频数越小,它区别不同类别文本的能力就越大。因此引入了逆文本频度 IDF(Inverse Document Frequency)的概念。
TF-IDF中,字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。 T F − I D F = 词频( T F ) × 逆文档频率( I D F ) TF-IDF=词频(TF)×逆文档频率(IDF) TF−IDF=词频(TF)×逆文档频率(IDF)
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
import numpy as np
corpus = [
'This is the first document.',
'This is the second document.',
'And the third document.',
'Is this the first document?'
]
vectorizer = CountVectorizer()
x = vectorizer.fit_transform(corpus)
word = vectorizer.get_feature_names()
print('Vocabulary:', word, '\n')
print(x.toarray(), '\n')
# TF-IDF 转换
transfomers = TfidfTransformer()
tfidf = transfomers.fit_transform(x)
print(np.around(tfidf.toarray(), 4), '\n')
from sklearn.metrics.pairwise import cosine_similarity
# 比较最后一句与其他句子的相似度
print(cosine_similarity(tfidf[-1], tfidf[:-1], dense_output=False))
分布假设:相似的词往往出现在同一环境中。出现在非常相似的部分(其相邻的词是相似的)中的两个词具有相似的含义。
n n n- g r a m gram gram 模型为了保持词的顺序,做了一个滑窗的操作,这里的 n n n 表示的就是滑窗的大小,例如 2 2 2- g r a m gram gram 模型,也就是把 2 2 2 个词当做一组来处理,然后向后移动一个词的长度,再次组成另一组词,把这些生成一个字典,按照词袋模型的方式进行编码得到结果。该模型考虑了词的顺序。
以上两句可以构造一个词典:
那么第一句的向量表示为:[1, 1, 1, 1, 1, 1, 0, 0, 0, 0],其中第一个 1 1 1 表示 John likes 在该句中出现了 1 1 1 次,依次类推。
缺点:随着 n n n 的大小增加,词表会成指数型膨胀,会越来越大。
# 一般n-gram中的n取2或者3, 这里取3为例
ngram_range = 3
def create_ngram_set(input_list):
"""
description: 从数值列表中提取所有的n-gram特征
:param input_list: 输入的数值列表, 可以看作是词汇映射后的列表,
里面每个数字的取值范围为[1, 25000]
:return: n-gram特征组成的集合
eg:
# >>> create_ngram_set([1, 4, 9, 4, 1, 4])
{(4, 9), (4, 1), (1, 4), (9, 4)}
"""
return set(zip(*[input_list[i:] for i in range(ngram_range)]))
if __name__ == '__main__':
input_list = ['明天', '有', '可能', '会', '下雨']
res = create_ngram_set(input_list)
print(res)