【Python3】常见文本相似度计算方式及代码

常见文本相似度计算方式及代码

文本相似度的计算广泛的运用在信息检索搜索引擎, 文档复制等处:
因此在各种不同的情况与任务中,有不同的文本相似度计算。

近期在处理搜索引擎的相关项目
下面介绍一下我们主要使用的相似度计算方式及其实现 Github

余弦相似度:

余弦相似度是纯数学中的概念,首先,将进行计算的两个str中的word抽取出来,用作非重复词库。
遍历词库,将两个句子的表示向量化: 每个向量长度为 词库大小

import numpy as np
def cosine_similarity(sentence1: str, sentence2: str) -> float:
    """
    compute normalized COSINE similarity.
    :param sentence1: English sentence.
    :param sentence2: English sentence.
    :return: normalized similarity of two input sentences.
    """
    seg1 = sentence1.strip(" ").split(" ")
    seg2 = sentence2.strip(" ").split(" ")
    word_list = list(set([word for word in seg1 + seg2]))
    word_count_vec_1 = []
    word_count_vec_2 = []
    for word in word_list:
        word_count_vec_1.append(seg1.count(word))
        word_count_vec_2.append(seg2.count(word))

    vec_1 = np.array(word_count_vec_1)
    vec_2 = np.array(word_count_vec_2)

    num = vec_1.dot(vec_2.T)
    denom = np.linalg.norm(vec_1) * np.linalg.norm(vec_2)
    cos = num / denom
    sim = 0.5 + 0.5 * cos

    return sim

编辑距离:

编辑距离是指文本A变为文本B的处理次数
处理包含:

  • 删除一个字符
  • 增加一个字符
  • 修改一个字符

例如:
A: requiremant
B: requirements
step1: requiremant -> requirement
step2: requirement -> requirements
所以编辑距离为2

# Using difflib is python 3.6 default package
import difflib
def compute_levenshtein_distance(sentence1: str, sentence2: str) -> int:
    """
    compute levenshtein distance.

    """
    leven_cost = 0
    s = difflib.SequenceMatcher(None, sentence1, sentence2)
    for tag, i1, i2, j1, j2 in s.get_opcodes():

        if tag == 'replace':
            leven_cost += max(i2 - i1, j2 - j1)
        elif tag == 'insert':
            leven_cost += (j2 - j1)
        elif tag == 'delete':
            leven_cost += (i2 - i1)

    return leven_cost

利文斯顿相似度

利文斯顿距离即为编辑距离
而做相似度度量时,所需要的范围为0-1之间浮点数
因此需要将上述的编辑距离整数值转换为normalize的分数

def compute_levenshtein_similarity(sentence1: str, sentence2: str) -> float:
    """Compute the hamming similarity."""
    leven_cost = compute_levenshtein_distance(sentence1, sentence2)
    return 1 - (leven_cost / len(sentence2))

Simhash & 汉明距离

计算字符串之间的相似度,步骤如下:

  • 分词 :将字符串之间分词(中文需要分词,英文本身具有空格)
  • HASH :将每个单词进行hash,生成长度相同的01序列
  • 加权 :计算每个单词本身的权重,例如 -4, 5 等等不同的权重,与0/1字符串相乘,1为正, 0为负, 例如:
  • W(清华) = 100101*4 = 4 -4 -4 4 -4 4
  • W(大学)=101011*5 = 5 -5 5 -5 5 5
  • 合并 : 将上述各个词语,与权重相乘之后的hash数值相加
  • W(清华大学) = (4+5) (-4-5) (-4+5) (4-5) (-4+5) (4+5) = 9 -9 1 -1 1 9
  • 降维: 将上述数值进行降维, ≥ 1 \ge 1 1的转换为1, ≤ 0 \le 0 0的转换为0
  • W(清华大学) = 1 0 1 0 1 1

则可以计算出 句子的代表数值,同样长度的句子0/1数值对应位置进行与运算,true值数量与句子总长度比例,则可以得出hamming distance.

此处代码使用了 python simHash package.
安装与使用tutorial

import re
from simhash import Simhash
def compute_simhash_hamming_similarity(sentence1: str, sentence2: str) -> float:
    """need to normalize after compute!"""

    def get_features(s):
        width = 3
        s = s.lower()
        s = re.sub(r'[^\w]+', '', s)
        return [s[i:i + width] for i in range(max(len(s) - width + 1, 1))]

    hash_value1 = Simhash(get_features(sentence1)).value
    hash_value2 = Simhash(get_features(sentence2)).value

    return compute_levenshtein_similarity(str(hash_value1), str(hash_value2))

Jaccard系数

S a S_a Sa 是sentence A
S b S_b Sb 是sentence B
单词交集与单词并集的比例即为杰卡德相似系数
(若遇见相同单词,则需要看数量重复是否占比很高,具体情况而定。两项处理均可。)
S a ∩ S b S a ∪ S b \frac{S_a \cap S_b}{S_a \cup S_b} SaSbSaSb

def compute_jaccard_similarity(sentence1: str, sentence2: str) -> float:
    word_set1 = set(sentence1.strip(" ").split(" "))
    word_set2 = set(sentence2.strip(" ").split(" "))

    return len(word_set1 & word_set2) / len(word_set1 | word_set2)

TF-IDF

TF其实由两项组成:

  • TF: Term Frequency
  • IDF: Inverse Document Frequency

TF 代表了词语在文档中出现的频率,当进行索引的时候,词语出现频率较高的文本,匹配度也会较高,但是某些停止词,例如 to 在文本中会出现相当多的次数,但这对匹配并没有起到很好的索引作用,因此需要引入另一个度量值 IDF(逆文本频率)
I D F = l o g N N ( x ) IDF = log\frac{N}{N(x)} IDF=logN(x)N
其中 N N N为语料库中文本的总数, N ( x ) N(x) N(x)为文本中出现单词 x x x的文本数量。
次可以度量该单词的重要程度。
某些特殊情况下, x x x并未出现在语料库中(所有文本),则需要考虑将公式平滑为:
I D F = l o g N + 1 N ( x ) + 1 + 1 IDF = log\frac{N+1}{N(x)+1}+1 IDF=logN(x)+1N+1+1
最终的TF-IDF值为:
T F − I D F ( x ) = T F ( x ) ∗ I D F ( x ) TF-IDF(x) = TF(x) * IDF(x) TFIDF(x)=TF(x)IDF(x)

此处我们调用了nltk工具库来实现TF-IDF计算:
由于query可能有2-3个单词同时建立索引,此处采用了平均值。

from nltk.text import TextCollection
from nltk.tokenize import word_tokenize
def compute_tf_idf_similarity(query: str, content: str, compute_type: str) -> float:
    """
    Compute the mean tf-idf or tf
     similarity for one sentence with multi query words.
    :param query: a string contain all key word split by one space
    :param content: string list with every content relevent to this query.
    :return: average tf-idf or tf similarity.
    """
    sents = [word_tokenize(content), word_tokenize("")]  # add one empty file to smooth.
    corpus = TextCollection(sents)  # 构建语料库

    result_list = []
    for key_word in query.strip(" ").split(" "):
        if compute_type == "tf_idf":
            result_list.append(corpus.tf_idf(key_word, corpus))
        elif compute_type == "tf":
            result_list.append(corpus.tf(key_word, corpus))
        else:
            raise KeyError

    return sum(result_list) / len(result_list)

注意此处使用pip install nltk是仍然可能出现问题的,nltk是一个很大的自然语言处理库,里面仍然有许多资源没有被安装,可能在使用过程中仍然会出现问题,提示需要下载punkt 资源

只要在任何地方加入

import nltk
nltk.download("punkt")

下载完毕之后即可删除这两行,因为资源已经在python的package中了。代码便不会有其他的问题。

你可能感兴趣的:(机器学习,数据挖掘,自然语言处理,搜索引擎,搜索引擎,自然语言处理,数据挖掘)