# Text similarity matching task

数据 --> 分词 --> 词袋模型 + TFIDF --> 与 TFIDF 库进行相似度计算 --> 取出 topK 相似语句 --> 匹配结果
0 实现过程
# 1、将所有文档进行分词
# 2、把所有语句通过统计变为词袋模型+tfidf的表现形式,保存模型
# 3、将传入的句子进行分词
# 4、将分词后的句子变为词袋模型+tfidf的表现形式
# 5、将此向量与文档中的所有语句的表示向量进行相似度计算
# 6、相似度值进行排序,取出topk的相似度的语句
1-1 导包
# 首先依赖 jieba 进行分词;依赖 gensim 完成任务
import jieba
from gensim import corpora
from gensim import models
from gensim import similarities
1-2 加载自定义词典
jieba.load_userdict("MobilePhone_Userdict.txt")
1-3 读取出停用词
# 将停用词读出放在 stopwords 这个列表中
filepath = r'stopwords.txt'
stopwords = [line.strip() for line in open(filepath,'r',encoding='utf-8').readlines()]
1-4 读取文件,将其中句子进行分词
def readfile2wordlist(file_path):
    '''
    :param file_path: 传入的是语料库的文件路径
    '''
    word_list = []          # 原始语句列表,将原始语句按顺序放到里面
    cut_word_list = []      # 将分词后的结果列表放到里面
    with open(file_path, 'r', encoding="utf-8-sig") as f:
        for line in f.readlines():
            line = line.strip()
            seg_list = jieba.cut(line)
            seg_list = [i for i in seg_list if i not in stopwords and i!=' ']
            word_list.append(line)
            cut_word_list.append(seg_list)
    return word_list,cut_word_list
1-5 构建tfidf语料库,计算tfidf值
def calculate_tfidf(file_path):
    '''
    把语料库的所有语句全都变成 词袋模型+tfidf 的表现形式
    :param file_path: 传入的是语料库的文件路径
    :return:
    '''
    word_list,cut_word_list = readfile2wordlist(file_path)
    # STEP 1
    # 将分词后的列表,传入函数,返回一个词典;即词到数字的映射
    dictionary = corpora.Dictionary(cut_word_list)      
    num_features = len(dictionary.token2id)
    # dictionary.doc2bow 会返回词袋模型
    words_bag = [dictionary.doc2bow(text) for text in cut_word_list]
    # STEP 2  tfidf 的计算和表现形式
    tfidf_model = models.TfidfModel(words_bag)          # 传入词袋模型,就可以训练 tfidf_model了
    tf_texts = tfidf_model[words_bag]                   # 返回每句话的 词袋模型+tfidf 表现形式
    # 使用 gensim 中的相似度函数进行匹配;返回的 sparse_matrix,具有相似度计算的功能
    # tf_texts 为 词袋模型+tfidf 表现形式;num_features 是特征数量
    # 本质是对保存的内容进行余弦相似度的计算
    sparse_matrix = similarities.SparseMatrixSimilarity(tf_texts, num_features)
    return dictionary,tfidf_model,word_list,sparse_matrix
    # 返回 spare_matrix,这样就有相似度计算的接口了
1-6 针对一句话进行分词
def split_words(word):
    '''
    对单独的语句进行分词
    :return: 返回分词结果
    '''
    cut_word = [i for i in jieba.cut(word) if i not in stopwords and i!=' ']
    return cut_word
1-7 计算相似度
def calculate_similarity(words,dictionary,tfidf_model,word_list,sparse_matrix):
    '''
    计算相似度的主题流程
    :param words: 传入的句子
    :param dictionary:
    :param tfidf_model:
    :param word_list:
    :param sparse_matrix:
    :return:
    '''
    cut_words = split_words(words)      # 分词
    bow_vector = dictionary.doc2bow(cut_words)  # 取出词袋模型
    bow_tfidf = tfidf_model[bow_vector]         # 传入词袋模型,得到 tfidf 的稀疏表现形式
    # 使用这个方法会进行相似度匹配,并且获得匹配结果;similarities 可以是 比如 [0.2,0.7,0.1,0.8] 这是每个语句的匹配程度
    similarities = sparse_matrix.get_similarities(bow_tfidf)
    # 从高到低进行排序
    sorted_result = sorted(enumerate(similarities), key=lambda x: x[1],reverse=True)
    result_similarity = [[word_list[i[0]],i[1]] for i in sorted_result[:10]]  # 取出前 10 个
    return result_similarity
1-8
dictionary,tfidf_model,word_list,sparse_matrix= calculate_tfidf(r'mb.txt')
words = 'Apple iPhone 11 (A2223) 128GB 黑色 移动联通电信4G手机 双卡双待'  # 传入测试语句
# words = 'Redmi Note 9 5G 天玑800U  18W快充 4800万超清三摄 云墨灰 6GB+128GB 游戏智能手机 小 米 红米'
# 调用计算相似度方法
result_similarity = calculate_similarity(words,dictionary,tfidf_model,word_list,sparse_matrix)
for i in result_similarity:
    print(i[0] + '     ' + str(i[1]))   # i[0] 是最相似语句,i[1] 是相似度
# -->
# Apple iPhone 11 (A2223) 128GB 黑色 移动联通电信4G手机 双卡双待     1.0
# Apple iPhone 11 (A2223) 128GB 黑色 移动联通电信4G手机 双卡双待【购机补贴版】     0.82123
# 【当天发货】苹果11 Apple iPhone 11 苹果11 全网通4G手机 黑色 128GB 新包装     0.41421676

# 手机品牌的 tfidf 值比较高,所以首先找到的是品牌一致的
# 这就是一个文本相似度的实现
部分代码解释:
1. 1-4 中带判断的列表生成式
参考链接:https://blog.51cto.com/u_15149862/2809887
2. 1-5 中用到了 高阶函数
高阶函数
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
   a: 把一个函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
   b: 返回值中包含函数名(不修改函数的调用方式)
2-1
def test1():
     print('in the test1')
def test2():
     print('in the test2')
     return 0
def test3():
     print('in the test3')
     return test2  # 这个叫做高阶函数
2-2
def add(a,b,f):         # abs 是一个内置的方法
    return f(a)+f(b)
res = add(3,-6,abs)
print(res)
3. 1-7 中的 sorted()
3-1 sorted()
a = {6:2,8:0,1:4,-5:6,99:11,4:22}
print(a)
--->
{6: 2, 8: 0, 1: 4, -5: 6, 99: 11, 4: 22}
# 结果是无序的,现在将其进行排序
>>> print(sorted(a))
[-5, 1, 4, 6, 8, 99]  # 这个只是对 键值 进行排序,如何对 整个字典 进行排序?
3-2
>>> print(sorted(a.items()))
[(-5, 6), (1, 4), (4, 22), (6, 2), (8, 0), (99, 11)]
# 排序后,变成一个列表,不能指望排序后还是一个字典
3-3 # 也可以 按 value 排序
>>> a
{6: 2, 8: 0, 1: 4, -5: 6, 99: 11, 4: 22}
>>> print(sorted(a.items(),key=lambda x:x[1]))
[(8, 0), (6, 2), (1, 4), (-5, 6), (99, 11), (4, 22)]
>>>
# x 相当于 每一个元素 x[1] 指 第一个元素
4. 关于 1-7 中的 enumerate
>>> a = [1,2,3]
>>> for i in enumerate(a):print(i)
...
(0, 1)
(1, 2)
(2, 3)
每一个 i 都变成了 一个元组 第一个是小标,第二个是数据本身
enumerate 做的就是 取出了 列表的下标
知识点说明:
1. 什么是余弦相似度?
余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。
余弦相似度将向量根据坐标值,绘制到向量空间中,如最常见的二维空间。夹角越小说明越相似