本次课主要讲解文本特征提取的基础方法:Count Vector和TF-IDF Vector。我们假设一个语料库中有N篇文章,词典(所有文章中出现的不重复单词)的单词(token)数量为M,这两种方法都是将N篇文章的文本转化为长度为M的向量,从而得到一个N×M维度的结构化数据。后续可以根据此结构化数据建立机器学习模型,进行文本分类等应用。
在提取特征前,一般需要对每一文章进行去除前后空格、分词和小写转换的预处理。
目录
(3)TF-IDF = 词频(TF) * 反文档频率(IDF)
1、“皮靴”case的Counter Vector的案例代码
一、Count Vector
1、原理
某文章的Count Vector就是该文章中每个token的词频,即出现次数,对于词典中没有出现在该文章中的单词,记为0。
该方法思路简洁清晰,用每篇文章中的词频出现频数作为特征。在实际中,通常需要进行行归一化,以防止每篇文章单词量不同的的影响。具体做法有两种:①用每个单词的词频除以该文章的单词数量的和,此时其实就得到了每个单词的在该文章中的频率。②用每个单词的词频除以该文章中出现次数最多的单词的频数。
2、优缺点与改进
优点:简单,好理解,利用单词频率作为特征。
缺点:通常语料库巨大,单词库一般以万为单位,这就造成每个词向量的维度为几万,而每篇文章中出现的单词数量远小于几万,形成稀疏矩阵,不利于后续的计算。
改进方法:对于每篇文章,取词频最高的前m个单词形成文章top m子词库,将所有文章的top m子词库汇总得到一个总的子词库,再统计每篇文章在该子词库的词频或者频率。
二、TF-IDF Vector
1、理解
如果说词向量代表的是个单词在该文章中的重要程度,那Count Vector就是用每个单词在文章中出现的频率衡量其重要性,出现频率越高,该单词在文章中越重要。
而TF-IDF 还在此基础上考虑单词在其他文章中的出现情况,如果该单词在其他文章中出现得少,则越重要,如果该单词在其他文章中出现得多,则不重要,也就是说一个常用词即使在某文章中出现次数多,但也不一定就因此重要。
2、原理
(1)TF部分,同Count Vector
(2)IDF部分
反文档词频IDF的计算部分如下:
反文档频率(IDF)= log( 语料库的文档总数 / (包含该词的文档数 + 1) )
(3)TF-IDF = 词频(TF) * 反文档频率(IDF)
3、优缺点
优点:相比于Count Vector,更符合实际意义,出现频率高,且在其他文章中出现少的词更重要。
缺点:首先,TF中利“词频”大小并不一定很好反映重要性;其次,这两种方法都没有考虑单词出现的顺序,如果把一篇文章的单次顺序打乱,还是会得到相同的词向量。
三、词向量的应用
通过以上两种方法,将文本转化为了词向量。在应用层面,词向量一般有两种应用。
1、作为文本分类任务的自变量
每篇文章的标签为一个分类变量,特征就是词向量的每一个维度,可以进行机器学习建模。
2、计算文章间的相似度
(1)计算相似度的三种常用方法
①余弦相似度:衡量空间中两个向量夹角的大小,取值为[-1,1]
②Jaccard相似度
两篇文章中出现的单词的交集单词数量除以并集数量,取值[0,1]
③欧氏距离
衡量空间中两个向量顶点绝对距离的大小,取值[0,+无穷]。
(2)总体流程
四、作业
1、“皮靴”case的Counter Vector的案例代码
import jieba
import numpy as np
import pandas as pd
seq1 = '这只皮靴号码大了,那只号码合适'
seq2 = '这只皮靴号码不小,那只更合适'
# jieba基础操作学习
print(jieba.cut(seq1))
print(list(jieba.cut(seq1)))
print(' '.join(jieba.cut(seq1)))
print([i for i in jieba.cut(seq1)])
print(jieba.lcut(seq1))
def similarity(seq1, seq2, method):
# jieba分词得到token列表
seq1_list = jieba.lcut(seq1)
seq2_list = jieba.lcut(seq2)
# token列表去重得到set
seq1_set = set(seq1_list)
seq2_set = set(seq2_list)
# 总词库set
word_set = seq1_set.union(seq2_set)
# 句子总词库字典
word_dict1 = dict.fromkeys(word_set, 0)
word_dict2 = dict.fromkeys(word_set, 0)
for i in seq1_list:
word_dict1[i] += 1
for i in seq2_list:
word_dict2[i] += 1
# Count Vector
vec_seq1 = np.array(list(word_dict1.values()))
vec_seq2 = np.array(list(word_dict2.values()))
if method == "cos":
return (vec_seq1 * vec_seq2 / ((vec_seq1 ** 2).sum() ** 0.5 * (vec_seq2 ** 2).sum() ** 0.5)).sum()
elif method == 'Eur':
return ((vec_seq1 - vec_seq2) ** 2).sum() ** 0.5
elif method == 'Jac':
return len(seq1_set.intersection(seq2_set)) / (len(seq1_set) + len(seq2_set) - len(seq1_set.intersection(seq2_set)))
print(similarity(seq1, seq2, 'cos')) # 余弦相似度
print(similarity(seq1, seq2, 'Eur')) # 欧氏距离
print(similarity(seq1, seq2, 'Jac')) # Jaccard相似度
结果见下面截图:
2、基于jieba开源工具库,运行TF*IDF案例
tfidf = jieba.analyse.TFIDF()
tfidf.STOP_WORDS = set()
tfidf.extract_tags(seq1, withWeight=True, topK=11)
tfidf.extract_tags(seq2, withWeight=True, topK=11)
结果见下面截图:
3、项目已代码写出来,提交截图