目录
一、文本匹配任务的定义
1.狭义解释
2.广义解释
二、文本匹配的应用
1.问答对话
2.信息检索
3.文本匹配任务应用
三、智能问答
1.智能问答的基本思路
依照基础资源划分:
依照答案产出方式划分
依照NLP相关技术划分
四、智能问答的价值
1.智能客服
2.Faq知识库问答
总结
3.相关名词
① 问答对
② faq库 / 知识库
③ 标准问
④ 相似问/扩展问
⑤ 用户问
⑥ 知识加工
4.Faq知识库示例
5.运行逻辑
6.算法核心
五、文本匹配算法Ⅰ —— 编辑距离
🚀 代码实现
思路与算法
1.初始化矩阵:
2.填充矩阵:
3.计算相似度:
优缺分析
优点:
缺点:
六、文本匹配算法 Ⅱ —— Jaccard相似度
代码实现
优缺分析
优点:
缺点:
适用场景:
七、文本匹配算法 Ⅲ —— BM25算法
TF·IDF算法
BM25算法
词的重要性(IDF)
词与文档的相关性(TF)
词与查询的相关性(可选)
参数设置
优缺分析
优点:
缺点:
代码实现
Ⅰ、超参数定义
Ⅱ、初始化方法
Ⅲ、根据语料库构建倒排索引并计算每个词的IDF值
Ⅳ、计算文档集合中平均每篇文档的词数
Ⅴ、计算查询query 与 某篇文档的相关性得分
Ⅵ、计算查询与所有文档的相关性得分
Ⅶ、完整的BM25算法实现
八、文本匹配算法 Ⅳ —— word2vec
1.什么是向量
2.什么是词向量
3.词向量的特点
4.词向量是如何寻得到的
5.如何训练/训练目标
6.训练提速技巧
7.如何用于文本匹配
8.优缺分析
优点:
缺点:
后来时间平静又强大,替我揭穿和筛选
—— 25.1.29
一、文本匹配任务的定义
1.狭义解释
给定一组文本,判断其语义是否相似
相似的:今天天气不错 match 今儿个天不错呀
非相似:今天天气不错 match 你的代码有BUG
以分值形式给出相似度
今天天气不错 match 今儿个天不错呀 0.9
今天天气不错 match 这几天天气不错 0.7
今天天气不错 match 你的代码有bug 0.1
2.广义解释
给定一组文本,计算某种自定义的关联度
① Natural Language Inference 自然语言推理
两句话判断是否有关联、矛盾、中立关系
eg:明天要下雨 vs 明天大晴天
② Text Entailment 文本内容
给出一段文本,和一个假设,判断文本是否能支持或反驳这个假设
主题判断、文章标题匹配内容等
二、文本匹配的应用
1.问答对话
智能客服、车载导航、手机助手、聊天机器人、智能音箱
2.信息检索
各种 APP / 网页 的搜索功能
3.文本匹配任务应用
短文本 vs 短文本:知识库问答 ,聊天机器人等
短文本 vs 长文本:文章检索,广告推荐等
长文本 vs 长文本:新闻、文章的关联推荐等
三、智能问答
1.智能问答的基本思路
① 基础资源:包括faq库,书籍文档,网页,知识图谱等等
② 问答系统:对基础资源进行了加工处理,形成问答所需要的索引和模型等
③ 用户输入问题
④ 回答系统给出答案
依照基础资源划分:
1)基于faq(热点问题)知识库的问答【以文本匹配方式为主】
2)基于文档 / 网页 / 书籍的问答【大模型RAG的方式进行问答】
3)基于图像/视频的问答【基于多模态模型的问答】
4)基于知识图谱的问答
5)基于表格的问答
6)基于特定领域知识的问答
7)基于人工规则的问答 …
依照答案产出方式划分
1)检索式的回答
答案原文或答案的多个片段存在于基础资源中(答案是事先准备好的)
2)生成式的问答
答案文本不存在于基础资源,由问答系统来生成答案(由系统模型生成)
3)二者结合
依照NLP相关技术划分
1)单轮问答(一问一答)
2)多轮问答(关联多轮问答信息)
3)多语种问答(多种语言)
4)事实性问答(明确存在唯一正确的答案,用检索式方式回答较多)
5)开放性问答(有不同的看法,用生成式方式回答较多)
6)多模态问答(与图像、视频相结合)
7)选择型问答(在几个答案中选取一个)
8)抽取式问答
9)生成式问答
……
四、智能问答的价值
1.智能客服
人工客服的局限:① 响应慢 ② 服务时间有限 ③ 业务知识有限 ④ 流动性大,培训新人成本高 ⑤ 离职了就带走了业务回答经验 ⑥ 回复内容不一样,容易造成矛盾
智能客服的优势:① 毫秒级响应② 全年24小时在线 ③ 精通所有业务知识 ④ 只需培养管理员 ⑤ 保存所有业务回答数据 ⑥ 回复内容标准
2.Faq知识库问答
Faq = Frequently asked Questions:常见问题 / 热点问题
智能客服通常做一些 Faq 的问答
总结
Faq知识库问答:列表展示所有常见问题,用户需要自己找到对应的问题,对用户不友好
希望的改进:让用户以自然语言描述自己的问题,算法进行 Faq 库的检索,给出对应的答案
3.相关名词
① 问答对
一个(或多个相似的)问题与它对应的答案
② faq库 / 知识库
很多问答对组成的集合
③ 标准问
每组问答对中的问题有多个时,选一对为其中代表
④ 相似问/扩展问
一组问答对中,标准问之外的其他问题,对标准问的扩充
⑤ 用户问
用户真正输入的问题,而不是事先准备的
⑥ 知识加工
人工编辑 faq 库的过程
4.Faq知识库示例
5.运行逻辑
① 对用户问题进行预处理:分词、去停用词、去标点、大小写转换、全半角转换,按需处理
② 使用处理后的问题,与faq库中的问题计算相似度
③ 按照相似度分值排序
④ 返回最相似问题对应的答案
6.算法核心
语义相似度计算 是 faq 问答的核心
一般简称文本匹配 f(x,y) —> Score
相似度分值合理,才可以找到正确的对应问题
计算分值的同时,也要考虑速度
思考:可不可以不通过相似度计算,匹将用户问题配知识库中最相似的问题,再通过知识库中的问题匹配对应的答案,而是直接训练一个模型,匹配用户问题与知识库中答案之间的相似度
事实上,在实际场景中,答案的格式不是固定的,如果直接匹配,如果和知识库中的问题匹配,则问题对应的答案可以是多样、多种渠道的,如果直接用问题与答案匹配,则只需要一个计算文本相似度之间的模型即可
五、文本匹配算法Ⅰ —— 编辑距离
编辑距离:两个字符串之间,由一个转成另一个所需的最少编辑操作次数
许可的编辑操作(替换、插入、删除)包括将一个字符替换成另一个字符,插入一个字符,删除一个字符
例:
相似度计算公式:
ED:编辑距离 L:字符串长度
1 - 两个字符串之间替换、插入、删除的编辑操作次数 / 两字符串的最大长度,得到两个字符串的相似度
两个字符串完全一致:编辑距离 = 0,相似度 = 1
两个字符串完全不一致:编辑距离 = 较长者长度,相似度 = 0
🚀 代码实现
思路与算法
1.初始化矩阵:
1.matrix 是一个二维数组,大小为 (len(string1) + 1) x (len(string2) + 1)
2.第一行和第一列分别初始化为 0 到 len(string1) 和 0 到 len(string2),表示从一个空字符串转换到目标字符串所需的操作次数
2.填充矩阵:
对于每个字符 string1[i - 1] 和 string2[j - 1],如果它们相等,则 d=0,否则 d=1。
matrix[i][j] 的值通过以下公式计算:
matrix[i][j] = min(matrix[i−1][j] + 1, matrix[i][j−1] + 1,matrix[i−1][j−1] + d)
其中,
matrix[i - 1][j - 1] + d 表示 替换操作(如果字符不同,则 d=1)
matrix[i][j - 1] + 1表示插入操作
matrix[i - 1][j] + 1表示删除操作
3.计算相似度:
编辑距离为: matrix[len(string1)][len(string2)]
。
相似度为:,表示两个字符串的相似程度,值越接近 1 表示越相似
np.zeros():用于创建一个指定形状和数据类型的全零数组。
参数名 | 类型 | 说明 |
---|---|---|
shape | int 或 tuple | 数组的形状,可以是一个整数或表示形状的元组。 |
dtype | dtype, 可选 | 数组的数据类型,默认为 float64 。 |
order | {'C', 'F'}, 可选 | 数组元素在内存中的排列顺序,'C' 表示按行排列,'F' 表示按列排列。 |
range():生成一个不可变的整数序列,常用于循环控制。
参数名 | 类型 | 说明 |
---|---|---|
start | int, 可选 | 序列的起始值,默认为 0。 |
stop | int | 序列的结束值(不包含)。 |
step | int, 可选 | 步长,默认为 1。 |
len():返回对象的长度(如字符串、列表、元组等)。
参数名 | 类型 | 说明 |
---|---|---|
obj | object | 需要计算长度的对象。 |
min():返回一组数据中的最小值。
参数名 | 类型 | 说明 |
---|---|---|
iterable | iterable | 可迭代对象(如列表、元组等)。 |
*args | 可选 | 多个单独的参数,用于比较。 |
key | function, 可选 | 用于指定比较规则的函数。 |
default | object, 可选 | 当可迭代对象为空时返回的默认值。 |
max():返回一组数据中的最大值。
参数名 | 类型 | 说明 |
---|---|---|
iterable | iterable | 可迭代对象(如列表、元组等)。 |
*args | 可选 | 多个单独的参数,用于比较。 |
key | function, 可选 | 用于指定比较规则的函数。 |
default | object, 可选 | 当可迭代对象为空时返回的默认值。 |
#编辑距离
def editing_distance(string1, string2):
matrix = np.zeros((len(string1) + 1, len(string2) + 1))
for i in range(len(string1) + 1):
matrix[i][0] = i
for j in range(len(string2) + 1):
matrix[0][j] = j
for i in range(1, len(string1) + 1):
for j in range(1, len(string2) + 1):
if string1[i - 1] == string2[j - 1]:
d = 0
else:
d = 1
matrix[i][j] = min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + d)
edit_distance = matrix[len(string1)][len(string2)]
return 1 - edit_distance / max(len(string1), len(string2))
优缺分析
优点:
① 可解释性强
② 跨语种(甚至对于非语言序列)有效
③ 不需要训练模型
缺点:
① 字符之间没有语义相似度 eg:str1 = “我没钱”,str2 = "俺没钱",相似度得分:0.66(1 - 1 / 3)
② 受无关词/停用词影响大 eg:str1 = “我要办卡”,str2 = “你好我需要办一张卡”,相似度得分:0.44(1 - 5 / 9)
③ 受语序影响大 eg:str1 = “今天天气不错”,str2 = “天气不错今天”,相似度得分:0.33(1 - 4 / 6)
④ 文本长度对速度影响很大(算法实现中两层for循环速度基于文本长度)
六、文本匹配算法 Ⅱ —— Jaccard相似度
通用表述:根据两个集合中,不同元素所占的比例,来衡量两个样本之间的相似度
用于文本匹配:根据两个文本中,不同的字或词所占的比例,来衡量两个文本之间的相似度
相似度计算公式:
例:str1 = “今天天气真不错”,str2 = “估计明天天气更好”,公共字:天、气
A ∩ B:2(天、气)、A ∪ B:11(今、天、气、真、不、错、估、计、明、更、好)
Jaccard相似度:2 / 11 = 0.18
如果输入字符串,则得到基于字的 jaccard相似度;
如果输入词的列表,则得到基于词的 jaccard相似度;
具体用基于词 还是 用基于字的jaccrad相似度,看场景决定:① 分词是否准确;② 是否有很多类似名词、缩略词;③ 文本长度等因素,都会影响选择
代码实现
set(): Python 中的一个内置函数,用于创建一个无序且不重复的元素集合。它可以将可迭代对象(如字符串、列表、元组等)转换为集合,并自动去除重复元素
参数 | 描述 |
---|---|
iterable | 可选参数,表示一个可迭代对象(如列表、元组、字符串等)。如果不提供此参数,则返回一个空集合。 |
len():返回对象的长度(如字符串、列表、元组等)。
参数名 | 类型 | 说明 |
---|---|---|
obj | object | 需要计算长度的对象。 |
#jaccard距离
def jaccard_distance(string1, string2):
words1 = set(string1)
words2 = set(string2)
distance = len(words1 & words2) / len(words1 | words2)
return distance
优缺分析
优点:
① 语序不影响分数(词袋模型)eg:今天天气不错 / 天气不错今天
② 实现简单,速度很快
③ 可跨语种,无需训练等
缺点:
① 语序不影响分数 eg:他打了我 / 我打了他
② 字符之间没有相似度衡量:同编辑距离
③ 受无关词影响
④ 非一致文本可能出现满分 eg:他是不知道 他不是不知道
适用场景:
文本越长,语序对于准确率的影响越低,语序越不重要,更加适合Jaccard距离算法
文本短的话,Jaccard算法缺点会被放大,优点被缩小
七、文本匹配算法 Ⅲ —— BM25算法
常用在搜索引擎框架中,用来做文档和搜索问题的匹配。同样也可以用在问答中,做文本匹配。
核心思想:假如一个词在某类文本(假设为A类)中出现次数很多,而在其他类别文本(非A类)出现很少,那么这个词是A类文本的重要词(高权重词);反之,如果一个词在出现在很多领域,则其对于任意类别的重要性都很差。
BM25算法的基础是:TF · IDF算法
TF·IDF算法
TF:词频,代表这个词在某个类别文本中出现的频率,某个词在某个类别中出现的次数 / 该类别词的总数
公式:某个词在某个类别中出现的次数 / 该类别词的总数
IDF:逆文档频率,代表这个词在其他文本中出现的频率,N代表文本总数,dfi代表包含词qi的文本的总数
公式:IDF(qi) = log[(N - df_i + 0.5) / (df_i + 0.5)]
N:文档集合中的总文档数 df_i:包含词项q_i的文档数
逆文档频率IDF高 ——> 该词很少出现在其他文档
BM25算法
BM25是对TF·IDF的一种改进,优化其表示效果
公式:
词的重要性(IDF)
公式:
其中,N 是文档总数,df_i 是包含词 q_i 的文档数,IDF 值越高,表示该词在文档集合中越稀有,重要性越大
词与文档的相关性(TF)
BM25对词频(TF)进行了优化,引入了饱和函数S(q_i, d),避免词频过高时权重过大。
公式:
TF(qi, D):词 qi 在文档 D 中的词频
K:一个与文档长度相关的参数,
公式:
L_d:文档 D 的长度,L_ave:所有文档的平均长度,k_1,b:可学习参数
对词频进行调整,考虑了文档长度对词频的影响。较长的文档会受到惩罚,以避免偏向长文档
词与查询的相关性(可选)
当查询较长时,BM25还会考虑词在查询中的频率
公式:
TF(qi, Q):词qi在查询Q中的词频,k_3:可学习参数。
参数设置
k_1:控制词频的重要性,通常取值为 1.2。
b:控制文档长度的影响,通常取值为 0.75。
k_3:控制查询中词频的影响,通常取值为 1.2。
这些参数和改动的意义在于控制文本长度对分值的影响
优缺分析
优点:
① 通过使用TF·IDF弱化了无关词的影响,强化了重要词的影响,使得效果大幅提升
② 统计模型计算快(时间消耗主要是分词),不需要多轮迭代
③ 词袋模型(双刃剑)、跨语种等
缺点:
① 依然没有考虑词与词之间的相似性(字符之间没有相似度衡量)
② 需要一定量的训练(统计)样本(faq库本身)
③ 对于新增类别,需要重新计算统计模型
④ 分值不是一个总在0,1之间的数
代码实现
Ⅰ、超参数定义
ESPION:处理逆文档频率(IDF)计算中可能出现的负值的参数,确保IDF值始终为正,从而避免算法在计算相关性得分时出现异常。
PARAM_K1:超参数K_1,默认值为1.5,控制词频饱和度的上升速度。值越大,词频对得分的影响越大
PARAM_B:超参数B,默认值为0.6,控制文档长度归一化的影响。值越大,文档长度对得分的影响越大
typing:用于类型注解的库
import json
import math
import os
import pickle
import sys
from typing import Dict, List
class BM25:
EPSILON = 0.25
PARAM_K1 = 1.5 # BM25算法中超参数
PARAM_B = 0.6 # BM25算法中超参数
Ⅱ、初始化方法
def __init__(self, corpus: Dict):
"""
初始化BM25模型
:param corpus: 文档集, 文档集合应该是字典形式,key为文档的唯一标识,val对应其文本内容,文本内容需要分词成列表
"""
self.corpus_size = 0 # 文档数量
self.wordNumsOfAllDoc = 0 # 用于计算文档集合中平均每篇文档的词数 -> wordNumsOfAllDoc / corpus_size
self.doc_freqs = {} # 记录每篇文档中查询词的词频
self.idf = {} # 记录查询词的 IDF
self.doc_len = {} # 记录每篇文档的单词数
self.docContainedWord = {} # 包含单词 word 的文档集合
self._initialize(corpus)
Ⅲ、根据语料库构建倒排索引并计算每个词的IDF值
len():返回对象的长度或元素个数。
参数名 | 类型 | 描述 |
---|---|---|
obj | 对象 | 要计算长度的对象,如字符串、列表、元组、字典等。 |
set():创建一个无序且不重复元素的集合。
参数名 | 类型 | 描述 |
---|---|---|
iterable | 可迭代对象(如列表、元组等) | 可选参数,用于创建集合。如果未提供,则创建一个空集合。 |
集合.add():向集合中添加一个元素。如果元素已存在,则不会重复添加。
参数名 | 类型 | 描述 |
---|---|---|
element | 任意类型 | 要添加到集合中的元素。如果元素已存在,则不会重复添加。 |
float():将字符串或数字转换为浮点数。
参数名 | 类型 | 描述 |
---|---|---|
x | 字符串或数字 | 要转换为浮点数的字符串或数字。如果未提供参数,则返回0.0。 |
字典.keys():返回字典中所有键的视图对象。
math.log():计算自然对数(以e为底的对数)。可以指定第二个参数作为对数的底数。
参数名 | 类型 | 描述 |
---|---|---|
x | 数字 | 要计算对数的数值。 |
base | 数字 | 可选参数,指定对数的底数。默认为自然对数(以e为底)。 |
列表.append():在列表末尾添加一个元素。
参数名 | 类型 | 描述 |
---|---|---|
object | 任意类型 | 要添加到列表末尾的元素。 |
float():将字符串或数字转换为浮点数。
参数名 | 类型 | 描述 |
---|---|---|
x | 字符串或数字 | 要转换为浮点数的字符串或数字。如果未提供参数,则返回0.0。 |
公式: ,N是文档总数,n(q_i)是包含词q_i的文档数
def _initialize(self, corpus: Dict):
"""
根据语料库构建倒排索引
"""
# nd = {} # word -> number of documents containing the word
for index, document in corpus.items():
self.corpus_size += 1
self.doc_len[index] = len(document) # 文档的单词数
self.wordNumsOfAllDoc += len(document)
frequencies = {} # 一篇文档中单词出现的频率
for word in document:
if word not in frequencies:
frequencies[word] = 0
frequencies[word] += 1
self.doc_freqs[index] = frequencies
# 构建词到文档的倒排索引,将包含单词的和文档和包含关系进行反向映射
for word in frequencies.keys():
if word not in self.docContainedWord:
self.docContainedWord[word] = set()
self.docContainedWord[word].add(index)
# 计算 idf
idf_sum = 0 # collect idf sum to calculate an average idf for epsilon value
negative_idfs = []
for word in self.docContainedWord.keys():
doc_nums_contained_word = len(self.docContainedWord[word])
idf = math.log(self.corpus_size - doc_nums_contained_word +
0.5) - math.log(doc_nums_contained_word + 0.5)
self.idf[word] = idf
idf_sum += idf
if idf < 0:
negative_idfs.append(word)
average_idf = float(idf_sum) / len(self.idf)
eps = BM25.EPSILON * average_idf
for word in negative_idfs:
self.idf[word] = eps
Ⅳ、计算文档集合中平均每篇文档的词数
@property: Python 中的一个内置装饰器,用于将类的方法转换为属性,使得可以像访问普通属性一样访问这些方法。它主要用于封装类的属性,提供更简洁和直观的接口,同时允许在访问或修改属性时执行额外的逻辑,如数据验证或计算。
float():将字符串或数字转换为浮点数。
参数名 | 类型 | 描述 |
---|---|---|
x | 字符串或数字 | 要转换为浮点数的字符串或数字。如果未提供参数,则返回0.0。 |
@property
def avgdl(self):
return float(self.wordNumsOfAllDoc) / self.corpus_size
Ⅴ、计算查询query 与 某篇文档的相关性得分
公式:
其中,f(qi, D) 是词 qi 在文档D中的词频,∣D∣是文档D的长度,avgdl 是文档集合的平均长度。
self.avgdl:文档集合中平均每篇文档的词数
def get_score(self, query: List, doc_index):
k1 = BM25.PARAM_K1
b = BM25.PARAM_B
score = 0
doc_freqs = self.doc_freqs[doc_index]
for word in query:
if word not in doc_freqs:
continue
score += self.idf[word] * doc_freqs[word] * (k1 + 1) / (
doc_freqs[word] + k1 * (1 - b + b * self.doc_len[doc_index] / self.avgdl))
return [doc_index, score]
Ⅵ、计算查询与所有文档的相关性得分
遍历返回一个包含文档索引和得分的列表
列表推导式(List Comprehension)是 Python 中一种简洁且高效的方式来创建列表。它允许你在一行代码中从现有的可迭代对象(如列表、元组、字符串等)生成新的列表。列表推导式的基本语法如下:
new_list = [expression for item in iterable if condition]
expression
是对item
的操作或表达式,用于定义新列表中的每个元素。item
是可迭代对象中的每个元素。iterable
是包含要迭代的元素的可迭代对象。condition
是一个可选的条件,用于筛选要包含在新列表中的元素。
字典.keys():返回字典中所有键的视图对象。
def get_scores(self, query):
scores = [self.get_score(query, index) for index in self.doc_len.keys()]
return scores
Ⅶ、完整的BM25算法实现
import json
import math
import os
import pickle
import sys
from typing import Dict, List
class BM25:
EPSILON = 0.25
PARAM_K1 = 1.5 # BM25算法中超参数
PARAM_B = 0.6 # BM25算法中超参数
def __init__(self, corpus: Dict):
"""
初始化BM25模型
:param corpus: 文档集, 文档集合应该是字典形式,key为文档的唯一标识,val对应其文本内容,文本内容需要分词成列表
"""
self.corpus_size = 0 # 文档数量
self.wordNumsOfAllDoc = 0 # 用于计算文档集合中平均每篇文档的词数 -> wordNumsOfAllDoc / corpus_size
self.doc_freqs = {} # 记录每篇文档中查询词的词频
self.idf = {} # 记录查询词的 IDF
self.doc_len = {} # 记录每篇文档的单词数
self.docContainedWord = {} # 包含单词 word 的文档集合
self._initialize(corpus)
def _initialize(self, corpus: Dict):
"""
根据语料库构建倒排索引
"""
# nd = {} # word -> number of documents containing the word
for index, document in corpus.items():
self.corpus_size += 1
self.doc_len[index] = len(document) # 文档的单词数
self.wordNumsOfAllDoc += len(document)
frequencies = {} # 一篇文档中单词出现的频率
for word in document:
if word not in frequencies:
frequencies[word] = 0
frequencies[word] += 1
self.doc_freqs[index] = frequencies
# 构建词到文档的倒排索引,将包含单词的和文档和包含关系进行反向映射
for word in frequencies.keys():
if word not in self.docContainedWord:
self.docContainedWord[word] = set()
self.docContainedWord[word].add(index)
# 计算 idf
idf_sum = 0 # collect idf sum to calculate an average idf for epsilon value
negative_idfs = []
for word in self.docContainedWord.keys():
doc_nums_contained_word = len(self.docContainedWord[word])
idf = math.log(self.corpus_size - doc_nums_contained_word +
0.5) - math.log(doc_nums_contained_word + 0.5)
self.idf[word] = idf
idf_sum += idf
if idf < 0:
negative_idfs.append(word)
average_idf = float(idf_sum) / len(self.idf)
eps = BM25.EPSILON * average_idf
for word in negative_idfs:
self.idf[word] = eps
@property
def avgdl(self):
return float(self.wordNumsOfAllDoc) / self.corpus_size
def get_score(self, query: List, doc_index):
"""
计算查询 q 和文档 d 的相关性分数
:param query: 查询词列表
:param doc_index: 为语料库中某篇文档对应的索引
"""
k1 = BM25.PARAM_K1
b = BM25.PARAM_B
score = 0
doc_freqs = self.doc_freqs[doc_index]
for word in query:
if word not in doc_freqs:
continue
score += self.idf[word] * doc_freqs[word] * (k1 + 1) / (
doc_freqs[word] + k1 * (1 - b + b * self.doc_len[doc_index] / self.avgdl))
return [doc_index, score]
def get_scores(self, query):
scores = [self.get_score(query, index) for index in self.doc_len.keys()]
return scores
八、文本匹配算法 Ⅳ —— word2vec
1.什么是向量
指在坐标系(空间)内具有大小和方向的量
2维向量 [0.1, 2.9] 5维向量 [3, 1, 4, 2, 5]
2.什么是词向量
将每个词或字转换成同一向量空间内的一个向量
3.词向量的特点
两个词如果语义相近,则在空间中的向量接近
4.词向量是如何寻得到的
随机初始化,之后通过文本语料进行训练调整
5.如何训练/训练目标
① 基于窗口
② 基于语言模型
③ 基于共现矩阵
6.训练提速技巧
① 层次softmax/Huffman树
② 负采样
7.如何用于文本匹配
将文本中的所有词的词向量相加取平均
文本 ——> 句向量
公式:
句向量维度 = 词向量维度,不论文本长度
文本相似度 = 向量相似度 = 向量夹角余弦值
向量夹角为0,余弦值为1
8.优缺分析
优点:
① 两个文本包含语义相似的词,会提高相似度
② 训练需要的数据简单(纯文本语料即可)
③ 计算速度快,可以对知识库内问题预先计算向量
④ 将文本转化为数字,使后续复杂模型成为可能
缺点:
① 词向量的效果决定句向量效果(语料数量、领域适配、分词结果、未登录词)
② 一词多意的情况难以处理 eg:梨 —— 苹果 —— 华为
③ 受停用词(无效词)和文本长度影响很大(也是词袋模型)
④ 更换语种,甚至更换领域,都需要重新训练