Python相似度计算

文章目录

  • 1、相似度矩阵
  • 2、比较两集合的相似度
  • 3、编辑距离
  • 4、欧式距离和余弦距离
  • 5、TF-IDF文本相似度
  • 6、基于词向量的余弦相似度
  • 7、最长公共子串
  • 8、最长公共子序列
  • 并行计算
  • 语料下载地址

1、相似度矩阵

import seaborn, matplotlib.pyplot as mp

def similar_matrix(ls, f):
    le = len(ls)
    matrix = [[f(ls[i], ls[j]) for j in range(le)] for i in range(le)]
    seaborn.heatmap(matrix, center=1, annot=True)
    mp.show()

2、比较两集合的相似度

  • 相似度 = 交集 / 并集
similarity = lambda a, b: len(a & b) / len(a | b)
a = set('abcd')
b = set('bcde')
c = set('abcde')
similar_matrix([a, b, c], similarity)

Python相似度计算_第1张图片

3、编辑距离

  • 动态规划矩阵
import numpy as np, pandas as pd


def edit_distance(w1, w2):
    l1, l2 = len(w1) + 1, len(w2) + 1
    matrix = np.zeros(shape=(l1, l2), dtype=np.int8)

    for i in range(l1):
        matrix[i][0] = i
    for j in range(l2):
        matrix[0][j] = j

    for i in range(1, l1):
        for j in range(1, l2):
            delta = 0 if w1[i - 1] == w2[j - 1] else 1
            matrix[i][j] = min(matrix[i - 1][j - 1] + delta,
                               matrix[i - 1][j] + 1,
                               matrix[i][j - 1] + 1)

    print(pd.DataFrame(
        matrix, index=[''] + list(w1), columns=[''] + list(w2)))

    return matrix[-1][-1]


ed = edit_distance('abc', 'abbcc')
print('edit_distance:', ed)

Python相似度计算_第2张图片

  • 编辑距离百分比
def edit_distance(w1, w2):
    l1, l2 = len(w1) + 1, len(w2) + 1
    matrix = [[0 for j in range(l2)] for i in range(l1)]
    for i in range(l1):
        matrix[i][0] = i
    for j in range(l2):
        matrix[0][j] = j
    for i in range(1, l1):
        for j in range(1, l2):
            delta = 0 if w1[i - 1] == w2[j - 1] else 1
            matrix[i][j] = min(matrix[i - 1][j - 1] + delta,
                               matrix[i - 1][j] + 1,
                               matrix[i][j - 1] + 1)
    return matrix[-1][-1] / (l1 / 2 + l2 / 2 - 1)

poem1 = '''《将进酒》——李白
君不见黄河之水天上来,奔流到海不复回。君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,将进酒,杯莫停。与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。'''.replace('\n', '')
poem2 = '''《惜樽空》——李白
君不见黄河之水天上来,奔流到海不复回。君不见床头明镜悲白发,朝如青云暮成雪。
人生得意须尽欢,莫使金樽空对月。天生吾徒有俊才,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,与君哥一曲,请君为我倾。
钟鼓玉帛岂足贵,但用长醉不复醒。古来贤圣皆死尽,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。'''.replace('\n', '')
print(edit_distance(poem1, poem2))

0.15158924205378974

4、欧式距离和余弦距离

from sklearn.metrics.pairwise import euclidean_distances, cosine_distances
vectors = [[0, 1], [1, 1], [1, 0]]
print(euclidean_distances(vectors))  # 欧氏距离
print(cosine_distances(vectors))  # 余弦距离

Python相似度计算_第3张图片

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import euclidean_distances, cosine_distances
vec1, vec2 = CountVectorizer(token_pattern='.').fit_transform([poem1, poem2])
print(euclidean_distances(vec1, vec2))
print(cosine_distances(vec1, vec2))

5、TF-IDF文本相似度

https://blog.csdn.net/yellow_python/article/details/81021142

"""
《长恨歌》——白居易
汉皇重色思倾国,御宇多年求不得。
杨家有女初长成,养在深闺人未识。
天生丽质难自弃,一朝选在君王侧。
回眸一笑百媚生,六宫粉黛无颜色。
春寒赐浴华清池,温泉水滑洗凝脂。
侍儿扶起娇无力,始是新承恩泽时。
云鬓花颜金步摇,芙蓉帐暖度春宵。
春宵苦短日高起,从此君王不早朝。
承欢侍宴无闲暇,春从春游夜专夜。
后宫佳丽三千人,三千宠爱在一身。
金屋妆成娇侍夜,玉楼宴罢醉和春。
姊妹弟兄皆列土,可怜光彩生门户。
遂令天下父母心,不重生男重生女。
骊宫高处入青云,仙乐风飘处处闻。
缓歌慢舞凝丝竹,尽日君王看不足。
渔阳鼙鼓动地来,惊破霓裳羽衣曲。
九重城阙烟尘生,千乘万骑西南行。
翠华摇摇行复止,西出都门百余里。
六军不发无奈何,宛转蛾眉马前死。
花钿委地无人收,翠翘金雀玉搔头。
君王掩面救不得,回看血泪相和流。
黄埃散漫风萧索,云栈萦纡登剑阁。
峨嵋山下少人行,旌旗无光日色薄。
蜀江水碧蜀山青,圣主朝朝暮暮情。
行宫见月伤心色,夜雨闻铃肠断声。
天旋地转回龙驭,到此踌躇不能去。
马嵬坡下泥土中,不见玉颜空死处。
君臣相顾尽沾衣,东望都门信马归。
归来池苑皆依旧,太液芙蓉未央柳。
芙蓉如面柳如眉,对此如何不泪垂。
春风桃李花开日,秋雨梧桐叶落时。
西宫南内多秋草,落叶满阶红不扫。
梨园弟子白发新,椒房阿监青娥老。
夕殿萤飞思悄然,孤灯挑尽未成眠。
迟迟钟鼓初长夜,耿耿星河欲曙天。
鸳鸯瓦冷霜华重,翡翠衾寒谁与共。
悠悠生死别经年,魂魄不曾来入梦。
临邛道士鸿都客,能以精诚致魂魄。
为感君王辗转思,遂教方士殷勤觅。
排空驭气奔如电,升天入地求之遍。
上穷碧落下黄泉,两处茫茫皆不见。
忽闻海上有仙山,山在虚无缥渺间。
楼阁玲珑五云起,其中绰约多仙子。
中有一人字太真,雪肤花貌参差是。
金阙西厢叩玉扃,转教小玉报双成。
闻道汉家天子使,九华帐里梦魂惊。
揽衣推枕起徘徊,珠箔银屏迤逦开。
云鬓半偏新睡觉,花冠不整下堂来。
风吹仙袂飘飖举,犹似霓裳羽衣舞。
玉容寂寞泪阑干,梨花一枝春带雨。
含情凝睇谢君王,一别音容两渺茫。
昭阳殿里恩爱绝,蓬莱宫中日月长。
回头下望人寰处,不见长安见尘雾。
惟将旧物表深情,钿合金钗寄将去。
钗留一股合一扇,钗擘黄金合分钿。
但教心似金钿坚,天上人间会相见。
临别殷勤重寄词,词中有誓两心知。
七月七日长生殿,夜半无人私语时。
在天愿作比翼鸟,在地愿为连理枝。
天长地久有时尽,此恨绵绵无绝期。
"""
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from numpy import argsort


"""读数据"""
seqs = __doc__.replace('《长恨歌》——白居易', '').replace('。', '').strip().split()
q_ls = [i.split(',')[0] for i in seqs]
a_ls = [i.split(',')[1] for i in seqs]


"""训练tfidf向量转换器"""
vectorizer = TfidfVectorizer(token_pattern='[\u4e00-\u9fa5]')
X = vectorizer.fit_transform(q_ls)


def ask(q, n=3):
    q = vectorizer.transform([q])  # tfidf向量化
    indexs = cosine_similarity(X, q).reshape(-1)  # 余弦相似度
    indexs = argsort(-indexs)  # 按索引倒排
    return [a_ls[i] for i in indexs[:n]]


"""测试"""
for q, a in zip(q_ls, a_ls):
    print(q, a)
    for e, i in enumerate(ask(q)):
        print(e, i)
    print('-' * 50)

while True:
    q = input('输入:').strip()
    for e, i in enumerate(ask(q)):
        print(e, i)

"""
天下父母皆列土
遂令姊妹弟兄心
天长地久回龙驭
天旋地转有时尽
"""

Python相似度计算_第4张图片

6、基于词向量的余弦相似度

from gensim.models import Word2Vec
model = Word2Vec(ls_of_words)
w2i = {w: i for i, w in enumerate(model.wv.index2word, 1)}
vectors = np.concatenate((np.zeros((1, 100)), model.wv.vectors), axis=0)
w2v = lambda w: vectors[w2i.get(w, 0)]

vec1 = np.mean([w2v(w) for w in poem1], axis=0)
vec2 = np.mean([w2v(w) for w in poem2], axis=0

print(vec1 @ vec2 / (np.linalg.norm(vec1) * np.linalg.norm(vec2)))

7、最长公共子串

Longest Common Substring

import numpy as np

def lcs(s1, s2):
    l1, l2 = len(s1), len(s2)
    matrix = np.zeros((l1 + 1, l2 + 1), dtype=int)
    max_len = 0  # 最长匹配的长度
    p = 0  # 最长匹配对应在s1中的最后一位
    for i in range(len(s1)):
        for j in range(len(s2)):
            if s1[i] == s2[j]:
                matrix[i + 1, j+1] = matrix[i, j] + 1
                max_len, p = max([(max_len, p), (matrix[i + 1, j + 1], i + 1)])
    return s1[p - max_len: p]  # 返回最长子串及其长度

poem1 = '''《将进酒》——李白
君不见黄河之水天上来,奔流到海不复回。君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,将进酒,杯莫停。与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。'''.replace('\n', '')
poem2 = '''《惜樽空》——李白
君不见黄河之水天上来,奔流到海不复回。君不见床头明镜悲白发,朝如青云暮成雪。
人生得意须尽欢,莫使金樽空对月。天生吾徒有俊才,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,与君哥一曲,请君为我倾。
钟鼓玉帛岂足贵,但用长醉不复醒。古来贤圣皆死尽,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。'''.replace('\n', '')
print(lcs(poem1, poem2))

,惟有饮者留其名。陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。

8、最长公共子序列

The longest common subsequence

import numpy as np

def lcs(s1, s2):
    l1, l2 = len(s1), len(s2)
    # 生成字符串长度+1的零矩阵,保存对应位置匹配的结果
    m = np.zeros((l1 + 1, l2 + 1))
    # 记录转移方向
    d = np.empty_like(m, dtype=str)
    for i in range(l1):
        for j in range(l2):
            # 字符匹配成功,则该位置的值为左上方的值加1
            if s1[i] == s2[j]:
                m[i + 1, j + 1] = m[i, j] + 1
                d[i + 1, j + 1] = 'O'
            # 左值大于上值,则该位置的值为左值,并标记回溯时的方向
            elif m[i + 1, j] > m[i, j + 1]:
                m[i + 1, j + 1] = m[i + 1, j]
                d[i + 1, j + 1] = '←'
            # 上值大于左值,则该位置的值为上值,并标记方向↑
            else:
                m[i + 1, j + 1] = m[i, j + 1]
                d[i + 1, j + 1] = '↑'
    s = []
    while m[l1, l2]:  # 不为空时
        c = d[l1, l2]
        if c == 'O':  # 匹配成功,插入该字符,并向左上角找下一个
            s.append(s1[l1 - 1])
            l1 -= 1
            l2 -= 1
        if c == '←':  # 根据标记,向左找下一个
            l2 -= 1
        if c == '↑':  # 根据标记,向上找下一个
            l1 -= 1
    s.reverse()
    return ''.join(s)

poem1 = '''《将进酒》——李白
君不见黄河之水天上来,奔流到海不复回。君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,将进酒,杯莫停。与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。'''.replace('\n', '')
poem2 = '''《惜樽空》——李白
君不见黄河之水天上来,奔流到海不复回。君不见床头明镜悲白发,朝如青云暮成雪。
人生得意须尽欢,莫使金樽空对月。天生吾徒有俊才,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,与君哥一曲,请君为我倾。
钟鼓玉帛岂足贵,但用长醉不复醒。古来贤圣皆死尽,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。'''.replace('\n', '')
print(lcs(poem1, poem2))

《》——李白君不见黄河之水天上来,奔流到海不复回。君不见明镜悲白发,朝如青暮成雪。人生得意须尽欢,莫使金樽空对月。天生有,千金散尽还复来。烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,与君一曲,请君为我倾。钟鼓玉足贵,但长醉不复醒。古来圣皆,惟有饮者留其名。陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。

并行计算

import numpy as np
from multiprocessing import Pool, Manager
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity

def similarity_set(s1, s2, similarity):
    s1, s2 = set(list(s1)), set(list(s2))
    sim = len(s1 & s2) / len(s1 | s2)
    print('set', sim)
    similarity[0] = sim

def similarity_edit_distance(s1, s2, similarity):
    l1, l2 = len(s1) + 1, len(s2) + 1
    matrix = np.zeros((l1, l2), dtype=int)
    for i in range(l1):
        matrix[i, 0] = i
    for j in range(l2):
        matrix[0, j] = j
    for i in range(1, l1):
        for j in range(1, l2):
            delta = 0 if s1[i - 1] == s2[j - 1] else 1
            matrix[i, j] = min(matrix[i - 1, j - 1] + delta,
                               matrix[i - 1, j] + 1,
                               matrix[i, j - 1] + 1)
    sim = 1 - matrix[-1][-1] / (l1 + l2 - 2) * 2
    print('edit_distance', sim)
    similarity[1] = sim

def similarity_cosine(s1, s2, similarity):
    vec1, vec2 = CountVectorizer(token_pattern='.').fit_transform([s1, s2])
    sim = cosine_similarity(vec1, vec2)[0][0]
    print('cosine', sim)
    similarity[2] = sim

def mult(s1, s2):
    similarity = Manager().Array('f', [0, 0, 0])  # 共享数组
    functions = [similarity_set, similarity_edit_distance, similarity_cosine]
    pool = Pool(processes=len(functions))
    for func in functions:
        pool.apply_async(func, (s1, s2, similarity))
    pool.close()
    pool.join()
    print('average', np.average(similarity, weights=[.3, .2, .5]))

if __name__ == '__main__':
    poem1 = '''《将进酒》——李白
    君不见黄河之水天上来,奔流到海不复回。君不见高堂明镜悲白发,朝如青丝暮成雪。
    人生得意须尽欢,莫使金樽空对月。天生我材必有用,千金散尽还复来。
    烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,将进酒,杯莫停。与君歌一曲,请君为我倾耳听。
    钟鼓馔玉不足贵,但愿长醉不复醒。古来圣贤皆寂寞,惟有饮者留其名。
    陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。
    五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。'''.replace('\n', '')
    poem2 = '''《惜樽空》——李白
    君不见黄河之水天上来,奔流到海不复回。君不见床头明镜悲白发,朝如青云暮成雪。
    人生得意须尽欢,莫使金樽空对月。天生吾徒有俊才,千金散尽还复来。
    烹羊宰牛且为乐,会须一饮三百杯。岑夫子,丹丘生,与君哥一曲,请君为我倾。
    钟鼓玉帛岂足贵,但用长醉不复醒。古来贤圣皆死尽,惟有饮者留其名。
    陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。
    五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。'''.replace('\n', '')
    mult(poem1, poem2)
    # 自定义
    s1 = input('s1:').strip()
    s2 = input('s2:').strip()
    mult(s1, s2)

语料下载地址

https://github.com/AryeYellow/PyProjects/blob/master/NLP/文本相似度/similarity.ipynb

你可能感兴趣的:(自然语言处理)