学习笔记-相似度匹配改进2
1. 任务
由于上周找的方法不是开源的,所以本周的主要任务如下:找近1-2年的其他相似问题匹配方法开源方法,尝试是否可以运行。
2. 不足
关于《中华心法问答系统》中,对于相似度匹配使用的方法是:70%语义相似度+30%关键词增强。考虑以下几点需要改进的地方:
(1)关于权重问题:70%和30%过于死板了,是否有比较灵活的方式。(动态分配...)
(2)语义部分:当前使用最后4层CLS向量的加权平均可能丢失细粒度信息。(动态分配...)
当前Sigmoid校准(1/(1+exp(-25*(x-0.88)))
)可能过于激进。
(3)关键词部分:当前仅用Jieba分词后的词频匹配过于简单。(TF-IDF加权、BM25算法...)
(4)对于混合检索:除了语义和关键词,是否可以加上其他的(句法...)。
3. 改进方法
3.1 关于计算相似度的方法
计算相似度的方法,包括余弦相似度、欧式距离、点积相似度(若向量已归一化则等价于余弦相似度)等方法。对于语义相似度这一块,比较常用的的还是余弦相似度,且比较简单。
3.2 BM25混合检索方法
介绍:
BM25是用来进行关键词提取的。
BM25混合检索的步骤:
BM25提取有关文档(基于关键词),过滤无关关键词文档(例如从一百万中提取TOK-1000)→ 语义精排(计算余弦相似度)/语义+关键词(还是用BM25)精排。
对比:
关于上面的这个方法,对比原方法,主要提升在BM25比jieba对于关键词这一方面更加准确,而且对于百万级别的数据集,可以比较快的搜索出来(因为先经过BM25的粗排)。
但是对于本次的问答系统,并没有很大的数据集,且关键词的性能提升不在这一部分,对于BM25的实现也比较复杂,所以就不采用此方法。
3.3 相似度融合方法
3.3.1 动态权重学习(Dynamic Weighting)
动态权重学习是用来提升原方法里面语义最终的相似度采取的是:70%语义+30%关键词。这个固定的权重可能比较死板,可以根据一些下面的方法来动态的设置一些权重。
方法:
(1)根据查询长度的动态权重
逻辑:长文本更加依赖语义,短文本更加依赖关键词。
理解:
对于短文本来说,例如就一两个词,就可以依赖关键词直接匹配,语义分析容易过拟合。此时可以适当提高关键词的权重。例如:“实现欲望”,就两个关键词,就可以依赖关键词进行匹配。
对于长文本来说,可能会提取出特别多不是重点的关键词,此时就可以提高语义的权重。
(2)基于关键词匹配
(3)注意力机制加权
(4)神经网络融合器(Neural Reranker)
核心思想:用小型神经网络学习语义和关键词信号的最优组合。
训练数据:需标注样本(问题对,人工打分)。
3.4 句法
3.4.1 句法的了解
除了语义和关键词,或许可以再加上句法来进行提高相似度匹配。
语义向量的局限:无法捕捉句子结构(如“猫追狗”和“狗追猫”的向量可能相似)。
关键词的局限:忽略词序和语法关系(如“不喜欢的电影”和“电影不喜欢”关键词相同但含义相反)。
句法分析(Syntactic Analysis)是自然语言处理中的重要技术,用于分析句子的语法结构,揭示词语之间的依存关系。在这个系统里,句法分析可以帮助系统更好地理解问题结构,提高匹配准确率。
关于句法价值的思考:
句法是用于用于分析句子的语法结构,但是当两句话的意思相同,仅仅是语序的不同(例如被动句和主动句),那么他们用语义+关键词可以的到一个特别高的相似度,但是加入句法反而会降低相似度,这样做的意义在哪里?
上面的问题是当我们输入两个意思完全一样的句子时,但是当我们输入的句子是由相同词语组合表达完全不同意图时,句法可以解决语义和关键词无法处理的"结构歧义"。对于上面提到的两个完全一样意思的句子是,可以采取动态分配权重的方法。(例如当语义相似度特别大,而句法相似度比较小时,可以降低句法的权重,而提高语义的权重)
下面是当相同词语组合表达完全不同意图时的例子:
问题 语义相似度 关键词匹配 句法分析关键特征 "领导激励员工是欲吗?" 高 高(关键词全命中) 激励(员工)
作为核心动作,疑问指向"行为性质""员工想被领导激励是欲吗?" 高 高(相同关键词) 想(被激励)
作为心理状态,疑问指向"心理动机"
下面是一些句法降低相似度的例子
示例1:处理同义不同结构的问题
问题A:"如何区分欲望和目标?"
依存分析:区分(核心动词) -> 欲望(宾语1), 目标(宾语2)
问题B:"目标和欲望有什么不同?"
依存分析:不同(核心形容词) -> 目标(主语), 欲望(比较对象)
虽然语义相似,但句法结构不同。通过句法分析可以识别这种差异,避免过度匹配。
过度匹配:防止系统将表面相似但实际询问重点不同的问题错误地匹配在一起。在您的例子中:
问题A:"如何区分欲望和目标?" → 询问的是区分方法
问题B:"目标和欲望有什么不同?" → 询问的是差异点
如果不做句法分析,仅靠关键词匹配("目标"+"欲望")可能会返回相同的答案,这就是"过度匹配"。
示例2:句法捕捉语义模型忽略的"功能词信号"
BERT的局限性:预训练模型对虚词(如疑问词、否定词)敏感度不足,但是句法分析会明确捕获。
(1)处理否定结构
问题:"不社交是否是欲?"
依存分析:是(核心动词) -> 不社交(主语), 欲(宾语)
特殊标记:否定词"不"修饰"社交"
这种否定结构通过句法分析可以准确捕捉,避免与肯定形式混淆。
(2)处理疑问结构
问题:"追求感官享乐是欲还是焦虑问题?"
依存分析:是(核心动词) -> 追求感官享乐(主语), 欲(宾语1), 焦虑问题(宾语2)
特殊标记:选择疑问"还是"
识别疑问结构有助于理解用户真实询问意图。
句法精准识别否定(
不
)、情态(能
)、疑问(吗
)等功能词对句子意义的根本性改变,这些是纯语义模型容易弱化处理的特征。句法还有一些其他的价值,例如增强对"长尾结构"的鲁棒性(识别到“比如”这一部分非核心成分)等,就不做介绍了。
关于句法相似度计算的主要方法:树编辑距离、匹配规则(提取核心句法模式(如“主语-动词-宾语”是否一致)。)
3.4.2 实现
工具:在这里选择哈工大开发的中文NLP工具包LTP,支持依存句法分析。
下面是在源代码里修改,验证句法。
1. 环境准备
(1)安装LTP
注意需要在虚拟环境里安装。
# 安装LTP Python SDK
pip install ltp# 下载LTP基础模型(约500MB)
wget http://39.96.43.154/ltp/v3.4.0/ltp_model.zip
unzip ltp_model.zip -d ./ltp_model
注意上面直接安装LTP Python SDK时,下载torch
包时超时导致的安装失败。由于LTP依赖PyTorch,而PyTorch的包体积较大(216MB),在国内网络环境下容易下载失败。下面是解决方案。
# 设置清华镜像源加速
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple# 安装huggingface-hub的完整依赖
pip install packaging pyyaml requests tqdm transformers# 分步安装(避免超时)
pip install torch==2.7.1 --index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install ltp --index-url https://pypi.tuna.tsinghua.edu.cn/simple# 1. 下载模型(约500MB)
(venv) wget http://39.96.43.154/ltp/v3.4.0/ltp_model.zip# 2. 解压到项目目录下的 ltp_model 文件夹
(venv) unzip ltp_model.zip -d ./ltp_model
2. 代码修改
(1)修改Config
类
class Config:# 原有配置保持不变...LTP_MODEL_DIR = "./ltp_model" # 模型存储路径(可自定义)LTP_DEVICE = "cuda" if torch.cuda.is_available() else "cpu" # 自动选择设备SYNTAX_WEIGHT = 0.2 # 句法相似度权重(建议0.2-0.4)
(2)修改QASystem
类
①添加初始化
class QASystem:def __init__(self, config: Config):# 原有初始化代码...# 新增LTP初始化from ltp import LTPself.ltp = LTP(path=config.LTP_MODEL_DIR)self.ltp.to(config.LTP_DEVICE) # 分配到GPU/CPUself._prepare_syntax_features() # 预处理句法特征
②句法特征预处理
def _prepare_syntax_features(self):"""预计算所有问题的句法特征"""questions = [q["cleaned_question"] for q in self.qa_pairs]# 批量处理(效率提升5倍+)outputs = self.ltp.pipeline(questions, tasks=["cws", "dep"],max_length=512 # 限制最大长度)# 存储核心特征self.dep_trees = outputs.dep # 依存树列表self.core_verbs = [next((word for word, arc in zip(output.cws, output.dep) if arc.relation == "HED", None)for output in outputs]
③句法相似度计算
def _calculate_syntax_sim(self, query_dep, idx):"""计算查询与库中问题的句法相似度"""# 1. 核心动词匹配query_verb = next((word for word, arc in zip(query_dep.cws, query_dep.dep) if arc.relation == "HED", None)db_verb = self.core_verbs[idx]verb_sim = 1.0 if query_verb == db_verb else 0.3# 2. 依存关系匹配(优化版)def get_relations(tree):return {(arc.head, arc.relation, arc.tail) for arc in tree}rel_sim = len(get_relations(query_dep) & get_relations(self.dep_trees[idx])) / max(len(query_dep.dep), len(self.dep_trees[idx].dep))return 0.6 * verb_sim + 0.4 * rel_sim
④改造search
def search(self, query: str):# 原有语义和关键词计算...# ===== 新增句法分析 =====query_out = self.ltp.pipeline([cleaned_query], tasks=["dep"])syntax_sims = np.array([self._calculate_syntax_sim(query_out.dep[0], i)for i in range(len(self.qa_pairs))])# ===== 相似度融合 =====similarities = (0.6 * semantic_sim + # 语义相似度0.1 * keyword_sim + # 关键词相似度 0.3 * syntax_sims # 句法相似度)# 后续排序和过滤逻辑保持不变...
但是由于上面的LTP模型一直下载不下来,好像是更新过后,之前的都不能用了,所以导致不能够验证句法的作用。
3.5 细节补充
(1)层次化相似度加权
问题:BERT不同维度可能携带不同级别的语义信息。
改进:将768维分为若干组(如128维一组),分别计算子空间相似度后加权融合。
公式:最终相似度 = 加权平均(子空间1相似度, 子空间2相似度, ...)
(2)PCA降维优化余弦相似度
问题:BERT生成的768维向量可能存在冗余信息(某些维度对区分语义无用)或噪声(随机波动),直接计算余弦相似度时,这些冗余维度会干扰结果。
PCA作用:
通过主成分分析(PCA)保留方差最大的前128维(核心特征),剔除次要维度。
数学上:对向量矩阵进行奇异值分解(SVD),选取最大特征值对应的特征向量作为新基。
(3)动态Sigmoid校准相似度
问题:固定参数25*(x-0.88)
的Sigmoid校准可能过拟合。(若数据分布偏移(如平均相似度变为0.7),原参数会失效。)
改进:根据当前查询与文档库的相似度分布动态调整Sigmoid:
mean_sim + 0.5*std_sim
作为阈值,高于阈值的相似度被放大,低于的被抑制。
作用
自适应数据分布:
若文档库相似度普遍较低(如均值0.6),自动降低阈值,避免所有分数被压缩到低值。
增强区分度:
将相似度分布非线性映射到[0,1]区间,拉开相关与不相关文档的差距。
公式对比
原始校准 | 动态校准 |
---|---|
1 / (1 + exp(-25*(x-0.88))) | 1 / (1 + exp(-10*(x - (mean+0.5*std)))) |
(4)对比学习增强(Contrastive Learning)
问题:直接余弦相似度可能忽略困难负样本。
改进:在相似度计算后加入 对比损失微调。
# 假设已有正样本对(vec1, vec2)和负样本对(vec1, vec_neg)
pos_sim = np.dot(vec1, vec2)
neg_sim = np.dot(vec1, vec_neg)# 对比损失(温度系数τ控制区分度)
tau = 0.05
contrastive_sim = np.exp(pos_sim / tau) / (np.exp(pos_sim / tau) + np.exp(neg_sim / tau))
4. 总结
本周主要是寻找了关于提高相似度匹配的方法,还有一些没有写进来。主要了解了一些基础的知识和方法,但是由于时间关系,没有仔细地了解背后地原理。经过一些筛选,决定目前先加入句法和动态权重部分。因为句法的工具模型没有下载成功,所以打算下次换一个工具实现;对于动态权重部分,这一部分的方法有特别多,但是不知道哪一个是比较适合这个系统的,所以打算再找找文献吧。如果这两个完成好了的话,就试一试一些细节,如语义相似度方面的。