利用余弦距离比较文档间的相似度
一.数据说明
在进行正式的操作之前,对后续进行处理的数据进行说明,首先,从豆瓣电影网站爬取了电影对于的影评,然后进行了中文分词(jieba)和删除停用词操作,最后处理的结果展示如下如所示:
中文处理文档注意:后续的操作都是在经过上述步骤处理的文档基础上!
二.根据文档建立词频矩阵
2.1 什么是词频?
词频(Term Frequency,tf)指某个词(term)在文章中出现次数,若某个词(不包括停用词)在文章中出现的频率很高,则说明这个词可能比较重要。
2.2 sklearn中的CountVectorizer类
对于词频矩阵的建立,这里直接调用sklearn库中的CountVectorizer类,通过该类的方法fit_transform()可以获取一个词频矩阵,其中每一行代表一个文档,每一列代表词在该文档中出现的频率,词频矩阵的结构如下图所示:
fit_transform()函数使用起来比较简便,只需传递一个"文档列表"即可,该列表中的每个元素为一个经处理后的文档字符串,例如:
doclists = [ '韩国 山寨 路线 中心思想 西巴 狠 新鲜 东西 爱情片 哭 弄巧成拙 姜 科长 无心插柳 李子 唯我独尊 李仲久 超长 待机 丁青 壮哉 天朝 黑客 实力', ... '三星 天朝 多矿 难 类似 佩服 33 名 矿工 素养 纪律 政府 公开 透明 办事 能力 智利 南美洲 发达 国家 改编自 历史 真实 事件 2010 智利 圣何塞 铜矿 坍塌 事故', ... ]
该函数的使用示例代码如下:
vc = CountVectorizer() #构造一个CountVectorizer类对象 wfm = vc.fit_transform(doclists) #获取对应的词频矩阵
三.根据词频矩阵求tf-idf矩阵
3.1 tf-idf简介
tf上述步骤已经介绍,这里主要介绍一下idf。idf的全称为inverse document frequency,即反向文档频率,它的含义是包含某个词的文档数,其计算公式为:
idf=log10(N/dft)
其中 N为文档总数,dft为所有文档中包含词汇t的文档数,从该公式可知,一个词越常见,idf越低。tf-idf就是将二者乘起来,即 tf-idf=tf×idf。
3.2 sklearn中的TfidfTransformer类
要想求得包含文档中对应词的tf-idf值的tf-idf矩阵,可以使TfidfTransformer类,通过该类的fit_transform()方法可以获取对应的tf-idf矩阵,其结构如下图所示:
该类的fit_transform()函数只需要传入之前通过CountVectorizer类得到的矩阵即可得到一个词频矩阵,其示例代码如下:
transformer = TfidfTransformer() #构造一个TfidfTransformer实例 tmatrix = transformer.fit_transform(wfm) #获取tf-idf矩阵
四.L2归一化
4.1 计算公式
设一个向量 (x1,x2,x3,...,xN),其归一化后的向量为(y1,y2,y3,...,yN),则归一化的公式为:
4.2 示例代码
def L2Normalization(matrix): """ matrix:文档的tfidf矩阵,每一行为一个文档各个term的tfidf值 输出为进行L2正则化后的矩阵 """ return matrix / np.sqrt(np.sum(matrix ** 2,axis=1,keepdims=True))
需要注意的是在调用该函数之前,需要将上述求得的tfidf矩阵,转换为numpy的ndarray对象,具体做法为:
matrix = np.array(tmatrix)
五.求余弦距离
5.1计算公式
由于上述的tfidf矩阵进行了L2归一化,因此求两个文档的余弦距离的公式为:
经过计算后的余弦距离矩阵结构如下:
在该余弦距离矩阵中 arr[i][j]=cosij的含义是,文档 i和文档 j 之间的余弦距离为cosij,可知该余弦距离矩阵是一个对称方阵,即arr[i][j]=arr[j][i]。
5.2 示例代码
def cosineDistance(nmatrix): """ nmatrix:L2正则化后的tfidf矩阵 输出包含各个文档的余弦距离矩阵,例如 1 2 3 1 c1 c2 c3 2 c2 c4 c5 3 c3 c5 c6 m[0,1]表示文档1和文档2之间的余弦距离 """ return np.matmul(nmatrix,nmatrix.T)
六.结果展示
在求得对应的文档间余弦距离矩阵后利用np.where()函数筛选出文档余弦距离大于0.9的文档对,示例代码为:
x,y = np.where(arr > 0.9) #arr为余弦距离矩阵 for i,j in zip(x,y): if i != j: print("Doc {} Doc{} CosineDistance {}".format(i,j,arr[i][j]))