Task3 特征选择
-
TF-IDF概述
TF-IDF是Term Frequency - Inverse Document Frequency的缩写,即“词频-逆文本频率”。它由两部分组成,TF和IDF。
前面的TF也就是我们前面说到的词频,我们之前做的向量化也就是做了文本中各个词的出现频率统计,并作为文本特征,这个很好理解。关键是后面的这个IDF,即“逆文本频率”如何理解。在上一节中,我们讲到几乎所有文本都会出现的"to"其词频虽然高,但是重要性却应该比词频低的"China"和“Travel”要低。我们的IDF就是来帮助我们来反应这个词的重要性的,进而修正仅仅用词频表示的词特征值。
概括来讲, IDF反应了一个词在所有文本中出现的频率,如果一个词在很多的文本中出现,那么它的IDF值应该低,比如上文中的“to”。而反过来如果一个词在比较少的文本中出现,那么它的IDF值应该高。比如一些专业的名词如“Machine Learning”。这样的词IDF值应该高。一个极端的情况,如果一个词在所有的文本中都出现,那么它的IDF值应该为0。
上面是从定性上说明的IDF的作用,那么如何对一个词的IDF进行定量分析呢?这里直接给出一个词xx的IDF的基本公式如下:
其中,NN代表语料库中文本的总数,而N(x)N(x)代表语料库中包含词xx的文本总数。为什么IDF的基本公式应该是是上面这样的而不是像N/N(x)N/N(x)这样的形式呢?这就涉及到信息论相关的一些知识了。感兴趣的朋友建议阅读吴军博士的《数学之美》第11章。
上面的IDF公式已经可以使用了,但是在一些特殊的情况会有一些小问题,比如某一个生僻词在语料库中没有,这样我们的分母为0, IDF没有意义了。所以常用的IDF我们需要做一些平滑,使语料库中没有出现的词也可以得到一个合适的IDF值。平滑的方法有很多种,最常见的IDF平滑后的公式之一为:
有了IDF的定义,我们就可以计算某一个词的TF-IDF值了:
其中TF(x)TF(x)指词xx在当前文本中的词频。
1. 用scikit-learn进行TF-IDF预处理
第一种方法是在用CountVectorizer类向量化之后再调用TfidfTransformer类进行预处理。第二种方法是直接用TfidfVectorizer完成向量化与TF-IDF预处理。
- 第一种方法,CountVectorizer+TfidfTransformer的组合
- 用TfidfVectorizer一步到位
4.TF-IDF小结
TF-IDF是非常常用的文本挖掘预处理基本步骤,但是如果预处理中使用了Hash Trick,则一般就无法使用TF-IDF了,因为Hash Trick后我们已经无法得到哈希后的各特征的IDF的值。使用了IF-IDF并标准化以后,我们就可以使用各个文本的词特征向量作为文本的特征,进行分类或者聚类分析。
当然TF-IDF不光可以用于文本挖掘,在信息检索等很多领域都有使用。因此值得好好的理解这个方法的思想。
-
使用不同方法计算TF-IDF
1.使用gensim提取文本的tfidf特征
def count_tfidf():
corpus = [
'this is the first document',
'this is the second second document',
'and the third one',
'is this the first document'
]
#1.把语料做一个分词处理
word_list = []
for i in range(len(corpus)):
word_list.append(corpus[i].split(' '))
print(word_list)
#2.得到每个词的id值及词频
# 赋给语料库中每个词(不重复的词)一个整数id
dictionary = corpora.Dictionary(word_list)
# print(dictionary)
new_corpus = [dictionary.doc2bow(text) for text in word_list]
# 元组中第一个元素是词语在词典中对应的id,第二个元素是词语在文档中出现的次数
print(new_corpus)
# 通过下面的方法可以看到语料库中每个词对应的id
print(dictionary.token2id)
#3.训练gensim模型并且保存它以便后面的使用
# 训练模型并保存
tfidf = models.TfidfModel(new_corpus)
tfidf.save("my_model.tfidf")
# 载入模型
tfidf = models.TfidfModel.load("my_model.tfidf")
# 使用这个训练好的模型得到单词的tfidf值
tfidf_vec = []
for i in range(len(corpus)):
string = corpus[i]
string_bow = dictionary.doc2bow(string.lower().split())
string_tfidf = tfidf[string_bow]
tfidf_vec.append(string_tfidf)
print(tfidf_vec)
#通过上面的计算我们发现这向量的维数和我们语料单词的个数不一致呀,
# 我们要得到的是每个词的tfidf值,为了一探究竟我们再做个小测试
# 我们随便拿几个单词来测试
string = 'the i first second name'
string_bow = dictionary.doc2bow(string.lower().split())
string_tfidf = tfidf[string_bow]
print(string_tfidf)
#结论gensim训练出来的tf-idf值左边是词的id,右边是词的tfidf值
# gensim有自动去除停用词的功能,比如the
# gensim会自动去除单个字母,比如i
# gensim会去除没有被训练到的词,比如name
# 所以通过gensim并不能计算每个单词的tfidf值
2.使用sklearn提取文本tfidf特征
def count_tfidf2():
corpus = [
'this is the first document',
'this is the second second document',
'and the third one',
'is this the first document'
]
tfidf_vec = TfidfVectorizer()
tfidf_matrix = tfidf_vec.fit_transform(corpus)
# 得到语料库所有不重复的词
print('得到语料库所有不重复的词')
print(tfidf_vec.get_feature_names())
# 得到每个单词对应的id值
print('得到每个单词对应的id值')
print(tfidf_vec.vocabulary_)
# 得到每个句子所对应的向量
# 向量里数字的顺序是按照词语的id顺序来的
print('得到每个句子所对应的向量')
print(tfidf_matrix.toarray())
输出:
3.python提取文本的tfidf特征
def count_tfidf3(): corpus = [ 'this is the first document', 'this is the second second document', 'and the third one', 'is this the first document' ] #1.对语料进行分词 word_list = [] for i in range(len(corpus)): word_list.append(corpus[i].split(' ')) print(word_list) #2.统计词频 countlist = [] for i in range(len(word_list)): count = Counter(word_list[i]) countlist.append(count) print(countlist) #3.定义计算tfidf公式的函数 # word可以通过count得到,count可以通过countlist得到 # count[word]可以得到每个单词的词频, sum(count.values())得到整个句子的单词总数 def tf(word, count): return count[word] / sum(count.values()) # 统计的是含有该单词的句子数 def n_containing(word, count_list): return sum(1 for count in count_list if word in count) # len(count_list)是指句子的总数,n_containing(word, count_list)是指含有该单词的句子的总数,加1是为了防止分母为0 def idf(word, count_list): return math.log(len(count_list) / (1 + n_containing(word, count_list))) # 将tf和idf相乘 def tfidf(word, count, count_list): return tf(word, count) * idf(word, count_list) #4。计算每个单词的tfidf值 for i, count in enumerate(countlist): print("Top words in document {}".format(i + 1)) scores = {word: tfidf(word, count, countlist) for word in count} sorted_words = sorted(scores.items(), key=lambda x: x[1], reverse=True) for word, score in sorted_words[:]: print("\tWord: {}, TF-IDF: {}".format(word, round(score, 5)))
输出:
4.总结
之所以做了这方面的总结是因为最近在研究word2vec,然后涉及到了基于word2vec的文本表示方法。你用word2vec训练好的模型可以得到词的向量,然后我们可以利用这些词向量表示句子向量。
- 一般处理方法是把句子里涉及到的单词用word2vec模型训练得到词向量,然后把这些向量加起来再除以单词数,就可以得到句子向量。这样处理之后可以拿去给分类算法(比如LogisticRegression)训练,从而对文本进行分类。
- 还有一种是把句子里的每个单词的向量拼接起来,比如每个单词的维度是1X100
一句话有30个单词,那么如何表示这句话的向量呢?
把单词拼接来,最终得到这句话的向量的维度就是30X100维 - 我想做的是把句子里所有的单词用word2vec模型训练得到词向量,然后把这些向量乘以我们之前得到的tfidf值,再把它们加起来除以单词数,就可以得到句子向量。也就是结合tfidf给单词加上一个权重,评判一个单词的重要程度。
- 最后发现gensim和sklearn都不能满足我的需求,用python的方法做了一个。
点信息互信息
参考资料:https://blog.csdn.net/u013710265/article/details/72848755