灌水 文章相似度计算

匿名 · October 16, 2018 · Last by spring-ssh replied at October 17, 2018 · 1523 hits

算法思路

首先看个简单的例子:

句子 A: 我喜欢看电视,不喜欢看电影
句子 B: 我不喜欢看电影,也不喜欢看电视

基本思路

如果两句话的用词越相似,它们的内容越相似。因此,可以从词频入手,计算它们的相似度。

  • 第一步分词
    分词.png

  • 合并所有词
    合并.png

  • 计算词频
    词频.png

  • 得出词频向量
    词频向量.png

我们可以把它们想象成空间中的两条线段,都是从原点([0, 0, ...])出发,指向不同的方向。两条线段之间形成一个夹角,如果夹角为 0 度,意味着方向相同、线段重合;如果夹角为 90 度,意味着形成直角,方向完全不相似;如果夹角为 180 度,意味着方向正好相反。
因此,我们可以通过夹角的大小,来判断向量的相似程度。夹角越小,就代表越相似。

夹角.png

假定 a 向量是 [x1, y1],b 向量是 [x2, y2],那么可以将余弦定理改写成下面的形式:

余弦定理.png

余弦的这种计算方法对 n 维向量也成立。假定 A 和 B 是两个 n 维向量,A 是 [A1, A2, ..., An] ,B 是 [B1, B2, ..., Bn] ,则 A 与 B 的夹角θ的余弦等于:

n维向量.png

使用这个公式,我们就可以得到,句子 A 与句子 B 的夹角的余弦。

image.png

实践

计算国务院政府工作和地方政府工作报告相似度

思路:

(1) 使用 python 中的结巴分词对国务院报告、省级、县级政府报告进行分词,去掉停用词,并统计词频。
(2) 抽取出国务院政府报告与其他省、县级政府报告的关键词合成一个集合。
(3) 出现频率最高的前 1000 个词,生成国务院报告所对应的特征向量。
(4) 出现频率最高的前 1000 个词,生成各级报告所对应的特征向量。
(5) 计算国务院报告的特征向量与各级政府报告特征向量的余弦相似度,值越大越相似。 看各地方的遵从程度。

以安徽省 2014-2018 年政府工作报告和 2014-2018 年国务院政府工作报告为例:

代码介绍:

  • 获取分词结果
def jieba_result(content):
    '''
    获取jieba分词结果,并去掉中文符号
    :param content: 为read_file返回的文章内容
    :return:
    '''
    res = jieba.lcut(content)
    for i in remove_list():
        while i in res:
            res.remove(i)
    # print(res)
    return res

根据 python 第三方库 jieba,获取分词结果。
举个例子:

import jieba

s ="我想要有一个女朋友,伤心。"
res = jieba.lcut(s)
print(res)

----
['我', '想要', '有', '一个', '女朋友', ',', '伤心', '。']

由于分词时,会将一些中文标点符号也分出来,所以定义了一个 remove_list,将在里面的符号都删掉

同时也必须得承认,这样子简单粗略的分词,对于一整篇报告来说,准确性还是有待提高的

def remove_list():
    '''
    将下列列表的中午符号过滤
    如果有缺失,可自行添加
    :return:
    '''
    remove_list = [',', ':', '。', '《', '》', '\n', '—', '“', '”', '、', ' ']
    return remove_list

  • 获得前 1000 的向量

    def top_n(res,res_all,n):
    '''
    获得分词的Top n 词频,得出词频向量
    :param res: jieba_res  安徽  jieba_res 国务院
    :param res_all: 合并后的报告
    :param n:
    :return:
    '''
    dic={}
    for i in res_all:
        if i not in res:
            dic[i]=0
        else:
            dic[i]=res.count(i)
    
    sort_res= sorted(dic.items(), key=lambda x: x[1], reverse=True) #排序
    
    top_n = sort_res[:n]  #获取Top n
    # print(top_n)
    top_n_res=[]
    for i in top_n:
        top_n_res.append(i[1])
    # print(top_n_res)
    return top_n_res
    

    统计省工作报告,前 1000 热词的向量和国务院前 1000 热词的向量

  • 计算相似度

    def CalculateCos(gwyList, subList):
    '''
    根据两个词频向量,算出cos值
    :param gwyList:
    :param subList:
    :return:
    '''
    gwyLen = 0
    for gwynum in gwyList:
        gwyLen = gwyLen + gwynum ** 2
    gwyLen = gwyLen ** 0.5
    subLen = 0
    for sub in subList:
        subLen = subLen + sub ** 2
    subLen = subLen ** 0.5
    # return subLen
    totalLen = len(gwyList)
    fenmu = 0
    for i in range(0,totalLen):
        fenmu = fenmu + subList[i] * gwyList[i]
    print(fenmu / (subLen * gwyLen))
    return fenmu / (subLen * gwyLen)
    

最终计算 2014-2018 年每年,安徽省工作报告和国务院工作报告相似度如下:
相似度.png

相似度惊人的高,哈哈哈哈哈。。。。

最后社会主义核心价值观镇楼
image.png

共收到 11 条回复 时间 点赞

最后镇楼非常必要

这里要是凉了全是你的锅

匿名 #3 · October 16, 2018
Kissshot 回复

哈哈,这个锅可大了。
我有社会主义核心价值观镇楼,不怕!

搞几个论文相似度看看

这个功能很实用。

没看代码,估计看不懂;

举个反例: 两个人,各自抽取 200 个特征,199 个一致,相似度够高的了吧? 能凑成夫妻了吧?很遗憾,199 个里面包涵了性别....

是不是 200 个抽样点选的有问题。 如果是好友推荐,包括性别没问题;如果是婚恋网站,性别不该在里面;

如果不是纯字面相似,我觉得

句子 A: 我喜欢看电视,不喜欢看电影
句子 B: 我不喜欢看电影,也不喜欢看电视

他们也就相似 50%;(太重要了)

当然 0.939 也是有意义的...,但是确实没看出来。

当然我可能压根就没看懂这文.... 反正这堆理论应该不是楼主发明的。

想了想,如果两个班人数一样多,参加同一门考试, 根据成绩对比相似度,是否没有字面的问题;

给自己打脸; 可以用来 判断 红楼梦后边 那堆是不是老曹写的

匿名 #9 · October 16, 2018
hellohell 回复

哈哈,一下子回复三条,厉害了
1.这个一开始只是帮一个中科大学的朋友做的,主要是实现他的需求,算法思路是他定好的。
2.如果婚恋的推荐,我可以将男生理解成国务院,女生理解成地方(这边没有丝毫歧视的意思,倒过来也可以),这样就不存在男生和男生对比相似度的问题
3.很好奇 可以用来 判断 红楼梦后边 那堆是不是老曹写的
这个是什么个思路

思考这个问题的角度不一样;
一上来看到 文章相似度,想到的是比如一个议论文题目,你用算法考察两篇文章是否相似,这个相似,我指的是文章的观点是否一致; 但看了看,觉得这算法做不到,因为压根没考虑到语境语意;

我觉得你这个相似度计算,可以用在,比如 红楼梦,后面 80 回说是高额写的;那你统计 前面 和后面 80 回 词频, 这样是完全抛弃掉语法、句法、表达方式、语言习惯; 仅从这个角度计算相似;当然这不能作为唯一的依据,可作为初步的判断;如果上来常用词汇就差异过大;后面基本没戏了;再加语意判断,才是真正是相似度计算;

当然我是纯外行一个,没事情干瞎说😂

你这一篇灌水,让我想起了大概 15 年时 DoctorQ 主刀的陆金所安全报告了🌸 🐤

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up