【机器学习】TF-IDF以及TfidfVectorizer

TF-IDF定义

TF-IDF: 全称为"词频一逆文档频率"。
  TF:某一给定词语在该文档中出现的频率。 T F w = 词语 w 在该文档中个数 该文档内总词个数 TF_w = \frac{词语w在该文档中个数}{该文档内总词个数} TFw=该文档内总词个数词语w在该文档中个数
  IDF:整个训练集文档集合一共由 N N N篇文档组成,其中包含某个给定词语 w w w的文档数为 M M M. 则该给定词的 I D F IDF IDF值为: I D F = l o g e ( N M + 1 ) IDF=log_e(\frac{N}{M+1}) IDF=loge(M+1N),M+1是防止分母=0。
  一个词的 T F TF TF值反映了该词在某一篇文档中的重要性,而它的 I D F IDF IDF值则反映了它在整个文档集中的普遍重要程度。二者的乘积(TF-lDF ) 相当于取TF 和IDF的交集, 其值理论上可以较好地反映各个词的分类特征。某一特定文件内的较高词语频次,以及该词语在整个文件集合中的较低频次,可以产生较高的TF-IDF值,TF-IDF算法倾向于过滤常见词语,保留重要词语。经常结合余弦相似度用于向量空间中,判断两份文件的相似度。

python实现

直接使用TfidfVectorizer

这里采用的是[“Chinese Beijing Chinese”, “Chinese Chinese Shanghai”,“Chinese Macao”,“Tokyo Japan Chinese”]这个训练语料库,做一个简单的试验。
起初,我以为这样就能解决:

from sklearn.feature_extraction.text import TfidfVectorizer
tv = TfidfVectorizer()  # 实例化tf实例
train = ["Chinese Beijing Chinese", "Chinese Chinese Shanghai","Chinese Macao","Tokyo Japan Chinese"]  # 输入训练集矩阵,每行表示一个文本
tv_fit = tv.fit_transform(train)  # 训练,构建词汇表以及词项idf值,并将输入文本列表转成VSM矩阵形式
print(f'词汇表{tv.get_feature_names_out()}')  # 查看一下构建的词汇表
print(f'不同词语对应下标:{tv.vocabulary_}')
print(f'矩阵{tv_fit.toarray()}')  # 查看输入文本列表的VSM矩阵

  这是借助TfidfVectorizer进行实现的,结果如下:[‘beijing’ ‘chinese’ ‘japan’ ‘macao’ ‘shanghai’ ‘tokyo’]代表了语料库内所有的词语;{‘chinese’: 1, ‘beijing’: 0, ‘shanghai’: 4, ‘macao’: 3, ‘tokyo’: 5, ‘japan’: 2}是不同词语对应下标,这个在下面的矩阵里面代表这个词语在第几列;
  矩阵的每一行代表一个文档内的每一个词语的TF-IDF值。举个例子,对于文档"Chinese Beijing Chinese"来讲它的结果就是第0行[0.69183461,0.722056 ,0. , 0. , 0. , 0. ],该文档中只有Chinese和Beijing 这两个词语,因此只有两个元素非零,其中chinese对应列是1,于是它在该文档中的TF-IDF值为0.722056,beijing1对应列是0.于是它在该文档中的TF-IDF值为0.69183461。
【机器学习】TF-IDF以及TfidfVectorizer_第1张图片

结果解释

  但是仔细一看,欸???为什么?这些数值是什么意思???
  就拿"Chinese Beijing Chinese"里的chinese来说,根据定义, T F = 2 3 , I D F = l o g e ( 4 5 ) , T F − I D F = − 0.1487 ? ? ? TF = \frac{2}{3},IDF = log_e(\frac{4}{5}),TF-IDF = -0.1487??? TF=32IDF=loge(54),TFIDF=0.1487???,不对劲啊!!
我就在网上找啊,发现TF_IDF公式还挺多,不止有定义里的;
一个例子来使用sklearn中的TfidfVectorizer
tf-idf原理 & TfidfVectorizer参数详解及实战
经过我实践证明TfidfVectorizer使用的计算TF-IDF公式如下:
T F = 给定词语在该文档中出现的频数 I D F = l n ( 1 + N 1 + M ) + 1 T F I D F = T F ∗ I D F 整个训练集文档集合一共由 N 篇文档组成,其中包含某个给定词语的文档数为 M . TF = 给定词语在该文档中出现的频数\\ IDF = ln(\frac{1+N}{1+M})+1\\ TFIDF = TF*IDF\\ 整个训练集文档集合一共由N篇文档组成,其中包含某个给定词语的文档数为M. TF=给定词语在该文档中出现的频数IDF=ln(1+M1+N)+1TFIDF=TFIDF整个训练集文档集合一共由N篇文档组成,其中包含某个给定词语的文档数为M.
然后TfidfVectorizer还对每个文档的向量进行规范化,即对于每个文档的词语TF-IDF向量 v v v还要除以2范数, v ∣ ∣ v ∣ ∣ 2 \frac{v}{||v||_2} ∣∣v2v

手敲代码

这是我根据这个公式写出的python代码:

# 求逆文档频率IDF
def Idf(nd,df):
    return np.log((1+nd)/(1+df)) + 1

train = ["Chinese Beijing Chinese", "Chinese Chinese Shanghai","Chinese Macao","Tokyo Japan Chinese"] 
wordInDoc = []  # 记录在每个文档语句中单词出现次数,元素为字典,每个字典记录该文档中单词出现次数
wordInCor = {}  # 语料库中单词出现的文档
'''
不会重复
'''
# 求出每个文档语句中单词出现次数
for doc in train:
    words = doc.split()  # 将句子按空格分割成单词
    word_counts = {}  # 使用字典存储词语出现次数
    for word in words:
        word = word.strip('.,!?"()[]{}')  # 去除标点符号
        word = word.lower()  # 转换为小写以忽略大小写差异
        # 更新字典中词语的出现次数
        word_counts[word] = word_counts.get(word,0)+1
    wordInDoc.append(word_counts)  # 添加该文档字典
    
# 语料库中单词出现的文档
for i  in wordInDoc:  # 遍历每个文档字典
    for j in i:  # 如果词汇j出现在文档i中则该词汇出现次数+1
        wordInCor[j] = wordInCor.get(j,0)+1
        
# 给出每个词汇在哪一列
ind = {}  # 每个词汇对应下标,对应后面,词汇result中的哪一列
for i,word in enumerate(list(np.sort(list(wordInCor.keys())))):
    ind[word] = i
    
# 结果是一个稀疏矩阵
result = np.zeros((len(train),len(wordInCor.keys())))
for i in  range(len(train)):  # 遍历每个文档
    for word in ind.keys():  # 遍历每个词汇
        if word in wordInDoc[i].keys():  # 如果该词汇在该文档中,则计算
            tf = wordInDoc[i][word]  # wordInDoc[i]第几个文档的字典,wordInDoc[i][word]该文档中词汇出现次数?
                                    # 这边其实是有点疑问的,网上tf公式大多是频率,但是TfidfVectorizer从结果来倒推,TfidfVectorizer用的是频数
            idf = Idf(len(train),wordInCor[word])  # len(train)为文档总数=4,wordInCor[word]为该词汇在文档中出现次数
            tfidf = tf*idf
            result[i,ind[word]] = tfidf
# 规范标准化,除以2范数
result = result/np.sqrt((result**2).sum(axis = 1)).reshape(-1,1)
print(result)

结果:可以看出结果是和调用TfidfVectorizer是一样的。当然,这里词语对应的列与上面不同。
【机器学习】TF-IDF以及TfidfVectorizer_第2张图片

总结

  TfidfVectorizer使用的计算TF-IDF的公式与定义有差别。但实际上表达的意思是相同的,都是在该文档中的重要性TF乘以在语料库中的重要性IDF的乘积。
  np.log()其实是以e为底的,所以如果要计算以其他数为底的对数,需要进行换底公式 L o g a b = l o g c b l o g c a Log_ {a}b=\frac {log_ {c}b} {log_ {c}a} Logab=logcalogcb

def Log(newd,number): #newd新底
	return np.log(number)/np.log(newd)
Log(10,10)

你可能感兴趣的:(机器学习,机器学习,tf-idf,人工智能)