中华心法问答系统的解读(1)
中华心法问答系统
- 一、研究背景
- 1. 研究意义
- 2. 研究目的
- 3. 信息检索技术
- 二、主要研究内容
- 三、相关技术介绍
- 1. Flask框架技术
- 2. BERT模型
- (1)基本概念
- (2)BERT解决的问题
- (3)BERT的核心结构
- a. 模型结构
- b. 预训练任务( MLM+NSP)
- 3. 信息检索技术
- (1)混合检索框架
- (4)激活函数校准
- 四、中华心法问答系统需求分析与设计
- 1. 系统需求分析
- 2. 模块的划分和设计
- (1)查询模块
- (2)智能检索模块
- (3)录入模块
- 3. 前端和后端的交互逻辑
- 4. 后端运作逻辑
- 五、中华心法问答系统的实现
- 1. BERT模型的运用
- 2. 混合检索算法的实现
- (1)动态校准策略(语义相似度校准)
- (2)关键词增强机制(文本相似度补充)
- (3)总结对比
- 3. 去重模块的实现
- (1)文本清洗部分
- (2)去重机制(三重过滤)
- 4. 实验不同参数对比的结论
- 六、运行代码
- 七、总结感受
一、研究背景
1. 研究意义
-
帮助我们更好地认识自己,掌握自己的情绪和思维,提高自我控制和自我调
节的能力。 -
可以更好地应对生活中的挑战和困难,保持内心的平静和稳定,从而更好地发挥自己的潜力和才能。
2. 研究目的
设计并开发一个基于信息检索的中华心法问答系统。
3. 信息检索技术
信息检索技术是指通过计算机及相关技术,从文本、图像、音频等多种形式的信息中快速有效地寻找出与用户需求相匹配的信息。
二、主要研究内容
针对心法问答系统中对于语义理解问题以及匹配准确度问题,基于BERT模型,设计了融合语义向量和关键词匹配的算法。
三、相关技术介绍
①整体框架主要采用了Flask框架技术,用于前后端之间数据传输;
②语义理解主要是采用BERT模型,用于计算问句的语义向量;
③相似度匹配是用混合检索技术,主要是用于计算问句的相似度。
1. Flask框架技术
Flask 是一个非常小的PythonWeb框架,被称为微型框架;只提供了一个稳健的核心,其他功能全部是通过扩展实现的,以 “微核心+可扩展” 的设计哲学著称
本系统的前端采用了 Flask 框架实现。
2. BERT模型
(1)基本概念
自然语言处理主要对输入语句进行语义理解。
BERT 是 Google 在 2018 年提出的一个基于 Transformer 的预训练语言模型。
核心特点:①基于 Transformer 编码器架构 ②双向建模,同时考虑上下文左右两边的信息 ③ 预训练 + 微调,先用海量文本预训练,再根据具体任务微调。
(2)BERT解决的问题
在 BERT 出现之前,很多语言模型(如 GPT、ELMo)是:
-
单向的:只能从左到右或从右到左理解句子(上下文信息不完整)
-
任务特定的:每个任务都要单独设计模型
BERT的突破
-
使用 双向 Transformer 捕捉上下文全局语义
-
使用 预训练的通用语言理解模型,然后对下游任务进行微调,显著提升性能
(3)BERT的核心结构
a. 模型结构
BERT 采用 Transformer Encoder 堆叠结构。
b. 预训练任务( MLM+NSP)
-
掩码语言模型 Masked Language Model(MLM)
随机遮住句子中15%的词,让模型预测它们
如:“I have a [MASK] dog.” → 预测 “cute” -
下句预测 Next Sentence Prediction(NSP)
给定句子 A 和句子 B,预测 B 是否是 A 的下一个句子
学习句与句之间的逻辑关系
BERT 是一种通过预训练获得语言理解能力的深度学习模型,能够广泛用于各种自然语言处理任务,具有上下文双向理解的优势。
3. 信息检索技术
(1)混合检索框架
混合检索架构,指的是将多种检索方法结合起来的系统架构,用于提高信息检索的效果和准确性。
它通常结合了以下两种主流方法:
(1)稀疏检索(Sparse Retrieval)
(2)稠密检索(Dense Retrieval)
a. 两种检索方法的介绍
- 稀疏检索(BM25):传统基于关键词匹配的检索方法,具有快速、解释性强、依赖关键词的特点。
- 稠密检索(BERT向量):将文本转为向量,通过语义相似度进行匹配,具有能理解语义、扩展性强、计算较重的特点。
b. 为什么要 “混合”
- 单一检索的缺陷:稀疏检索只能匹配关键词,可能错过语义相近但词不同的结果。稠密检索计算成本高,可能错过精确匹配关键词的内容。
- 混合检索可以兼顾精确性和语义性,提升召回率和准确率,适应多样的查询需求。
c. 流程图
混合检索架构就是将关键词匹配和语义理解 (BERT向量相似度)两种技术融合起来,提高搜索/问答的准确性和覆盖率,是现代智能信息检索系统的关键技术。
(4)激活函数校准
a. 为什么要使用 “激活函数校准”
- 在很多任务中(如推荐系统、问答匹配、语义检索),我们会计算两个向量之间的相似度(通常使用余弦相似度,值域为 [-1, 1]),然后根据这个相似度来判断匹配程度。
- 但在实际中:余弦相似度分布在高分段(比如 0.88 ~ 1.0)时,差距非常小,模型难以区分,所以需要一种变换手段,把这些相似度变换成更有区分度的分值——这就是 “激活函数校准” 的用意。
b. 多维相似度计算模型
- 指的是将输入向量投影到多个子空间中,计算多个相似度,然后进行融合;与单一余弦计算相比,多维可以捕捉更复杂的语义信息。
c. 引入Sigmoid 函数进行非线性变换
- 目的是:将原始余弦值(如0.88 ~ 0.99)非线性映射到更加集中的区间(如0.9 ~ 1.0),让差距放大;
- 本质上是一种 非线性压缩+拉伸操作
d. Sigmoid 的饱和特性
- Sigmoid函数的饱和特性:当输入(x-0.88)的绝对值增大时,输出趋近于0 或1
- 在 x 趋近正负无穷时,sigmoid 的值逼近 1 或 0;
- 中间部分是最陡的(在 x=0 附近),表示对接近“中心值”的输入更敏感,容易放大差异;
- 这里设置中心点为 0.88,其实是人为 “对齐” 高相似度部分,使其更容易分出谁更相似。
四、中华心法问答系统需求分析与设计
1. 系统需求分析
主要目的:回答用户有关心法的问题,直观给用户展示答案。
数据库:收集专家们对于心法问题的解答
2. 模块的划分和设计
系统总体设计了三个模块:查询模块、算法模块和录入模块。
查询模块:用户可以直接使用查询模块,在首页直接进行心法问题的查询;对于想针对细分领域进行查找的用户,系统还设计了通过问答分类标签进行筛选的功能,可以让用户自由选择看到指定标签的问题与答案。
算法模块:算法模块里含有对知识库里的问答对进行信息检索,包括文本清洗去重、算法计算等.
录入模块:为了知识库的可扩充性,系统设计了录入模块,用户可以根据自己的需求选择是否新录入问题与答案。
(1)查询模块
(前端)向用户展示搜索结果 + 标签筛选功能
标签筛选功能:将含有对应标签的结果保留,不含有对应标签的结果将不在界面展示,用户可以自由选择细分领域。
工作原理:
- 前端收到用户输入的问题后,采用Flask 的 POST /search 接口将问题和标签数据送入后台
- 过后台智能检索模块检索到匹配问答对
- 经过标签过滤后,以JSON格式结果,返回匹配结果
- 将结果显示在前端页面上
(2)智能检索模块
算法模块主要分为去重模块和相似度计算模块
a. 去重模块(针对于一个问题的)
目的:消除知识库中内容重复或相似度过高的问题。
步骤:
-
文本清洗
系统首先会把每个问题的文本做“清洗处理”,比如:
(1)删除特殊符号(如“@”、“#”、“!” 等);
(2)去掉停用词(像“的”、“是”、“了”、“和”等对语义没有实质作用的词);
(3)只保留关键语义信息(问题的核心词)。
这一步的目的是把问题简化为最有意义的部分。 -
分词处理
接下来系统会对处理过的问题做分词。分词用的是一个叫 Jieba 的中文分词工具,并且配合了一个特别定制的 “心法”领域词典。
jieba: 是一个非常常用的 中文分词工具,它的名字来源于“结巴”(谐音),意思是“把句子切开”。
为什么要用领域词典呢?
因为普通分词工具可能会把一些专有名词、术语(比如“内功心法”)错误地切成几个词,导致语义被拆散。加了领域词典后,能更准确地保留这些专有词汇,避免出现“语义碎片化”的问题。 -
相似度计算后去重
处理后的问题会进行“相似度判断”:
先做哈希去重:把完全一模一样的两个问题去掉一个。
然后进行更细致的判断:把每个问题转成向量(可以理解为数学上的一个点),计算它们之间的余弦相似度。
如果两个问题的余弦相似度 > 93%,就说明它们在语义上几乎一样,这时候也会去掉其中一个,避免重复回答。
通过文本清洗 + 分词优化 + 相似度判断,系统能有效地识别并去除语义重复或过于相似的问题,提高问答系统的效率和准确性。
b. 相似度计算模块(针对两个问题在语义上的相似度)
目的:判断两个问题在语义上的相似程度,以便识别重复、相似的问题,提高问答系统的准确率和响应质量。
步骤:
-
问题向量化:用BERT表示语义
系统首先使用 BERT 模型(一个强大的预训练语言模型)将文本问题转换为向量表示。
具体方法是:
① 取出 BERT 输出的 最后四层的 CLS 向量(CLS 表示整个句子的语义信息);
② 给这四层分别赋予权重:0.15, 0.25, 0.35, 0.25
这些权重的意义是:
倒数第四层(最浅层)表示的是字面含义;
倒数第二层(更深层)更能捕捉抽象语义;
因为“心法”领域涉及很多抽象术语(如经络、气机等),所以系统提高了抽象层的权重,降低了字面层的比重。
这样做是为了让系统更“理解”用户的问题语义,而不仅仅是表面词汇。 -
向量归一化:消除长度影响
因为不同句子长度不同,向量的模长也不同,这会影响余弦相似度的计算。
所以在融合完各层向量之后,系统对向量做了归一化处理(也就是把向量拉到相同长度),确保比较时更公平、更准确。 -
计算余弦相似度:衡量两个向量的夹角
系统用余弦相似度来计算两个句子的语义相近程度,公式如下:
其中:
𝐴, 𝐵 是两个句子的向量;
点积(A · B)表示它们的相似程度;
模长(∥A∥)表示它们的长度。
值域范围是 0 到 1,值越大表示越相似。 -
引入 Sigmoid 函数:增强高相似度区间的分辨率
问题:很多句子之间的相似度都集中在 0.88 ~ 0.92,这样差距太小,人眼不容易区分。
解决方法:
使用 Sigmoid 函数,将原始余弦相似度进行非线性映射,提升这个“高相似区间”的区分度。
公式如下:
这可以把 0.88 ~ 0.92 之间的值“拉开”,让相近的问题得分差别更明显。 -
混合相似度:原始值 + 校准值结合
因为 Sigmoid 会造成一定的偏差,为了防止误差过大,系统采用了混合策略:
最终语义相似度= 0.2·原始余弦值+0.8·校准后值
这样既保留了余弦值的“整体趋势”,又增强了高相似区域的细节辨别能力。 -
关键词增强:捕捉显性特征
除了语义相似度,系统还使用了关键词匹配作为 “显性特征” 的补充。方法是:
① 用 Jieba 分词 提取每个问题中的 名词、动词、形容词;
② 利用 TF-IDF 思想 筛选重要词;
③ 计算关键词重叠率,得到一个“关键词相似度”。
这个模块用于捕捉那些直接表达意思的关键词,弥补深层语义模型可能忽视的部分。 -
最终融合:语义 + 关键词的加权相似度
最后系统将两种相似度进行融合:
α=0.7:语义相似度占比 70%
1−α=0.3:关键词相似度占比 30%
融合后的结果再经过一些补偿机制,得出最终的综合相似度分数。
这个相似度模块通过 BERT + Sigmoid + 关键词融合的方式,综合考虑深层语义和关键词重合度,实现了更加准确的问句相似度判断,特别适合处理“心法”这种抽象领域的问题。
(3)录入模块
目的:给用户用来添加新的“心法”问题和对应答案的。每当用户有新的内容想加入系统,就可以:
-
输入一个新问题;
-
提供对应答案;
-
为这个问题打上一级标签和二级标签(用于分类管理,比如“心法理论 / 调息方法”)。
重复问题怎么处理?
在用户添加时,可能会不小心添加:
-
一模一样的问题;
-
或者和已有问题意思非常相近(相似度很高)的问题。
为了避免:
-
问答重复
-
系统里有很多雷同问题
-
输出时出现重复答案
系统在录入时会自动进行三步去重处理。
三步去重机制
(1)文本清洗
-
删除特殊符号;
-
去掉“的、是、了”等停用词;
-
保留问题的核心关键词。
-
目的是让系统专注于识别问题的“本质”。
(2)哈希去重
-
用哈希算法判断新问题是否和之前某个问题完全一样;
-
如果是,就不再录入。
(3)语义向量去重
-
把新问题转换成向量(用 BERT 模型);
-
计算它和已有问题之间的语义相似度;
-
如果相似度很高(比如大于 93%),说明问题太像,就不录入。
实时更新机制
系统在内存中用 np.vstack()(NumPy 的一个函数)动态地扩展保存向量的矩阵,相当于把新的问题“实时加进去”,不需要重启系统。
这就实现了:
-
实时录入;
-
实时查找;
-
用户录完问题,可以马上在首页搜索到这个新问题。
录入模块让用户可以方便地添加新的心法问题和答案,同时系统通过文本清洗 + 哈希去重 + 语义判断三重机制,自动去除重复或过度相似的问题,并支持热更新,录入后立即生效。
3. 前端和后端的交互逻辑
(1)前端设计——页面交互界面
-
使用 HTML 设计用户界面,也就是你在浏览器里看到的页面;
-
用户可以在前端界面中输入问题(比如:“如何修炼心法?”);
-
前端页面通过 JavaScript、表单或按钮等方式,与后端服务器交互。
(2)前后端交互方式 —— Flask 框架 + RESTful API
-
本系统后端使用 Python 的 Flask 框架;
-
前后端是分离式架构(即用户界面和业务逻辑分开);
-
前端通过 RESTful API 与后端通信:
-
发送数据:如用户提交的问题;
-
接收数据:如后端返回的相似问题列表。
-
RESTful API 是一种设计接口的规范,它让前端能方便地“问”后端:“你能给我哪些相似的问题?”、“我能不能录入一个新问题?”等。
(3)查询问题的流程(搜索接口)
当用户在前端输入一个问题并提交后:
-
前端把这个问题发送到后端 API 接口;
-
后端调用 BERT 模型,把用户输入转换为语义向量;
-
系统在知识库中进行语义相似度计算和检索;
-
找到与该问题最相似的问题列表(比如Top5);
-
返回给前端一个按相似度排序的问答对列表,每项包含:
-
匹配的问题;
-
问题对应的答案;
-
一级/二级标签;
-
相似度得分。
-
举例:
用户问“如何练气功?”
系统返回:“气功入门的方法有哪些?(相似度:92.6%)”
(4)提交新问题的流程(录入接口)
当用户希望添加一个新问题+答案时:
-
用户填写“问题 + 答案 + 一级标签 + 二级标签”;
-
前端将这些信息通过 API 发送到后端;
-
后端执行两步验证:
-
校验合法性(如字段是否为空);
-
检查是否已存在相似问题(防止重复);
-
-
校验通过后,后端将新数据写入 CSV 文件(你当前的知识库存储方式);
-
并对内存中的数据结构做 热更新(不用重启服务器就能实时加入新问题);
-
最终返回结果给前端:
-
若成功:提示“提交成功,问题已录入”;
-
若失败:前端显示提示(比如“问题重复”或“请填写完整内容”)。
-
4. 后端运作逻辑
(1)系统初始化阶段
加载模型与数据,后端一启动就会加载以下内容:
-
预训练好的 BERT 模型(用于语义理解);
-
分词器(配合 Jieba 或 BERT Tokenizer 用于预处理文本);
-
已有问答对数据(知识库中的所有问题 + 答案 + 标签等)
(2)知识库数据预处理
清洗问答对文本
-
去掉特殊符号、空格、停用词;
-
保留关键信息,防止语义碎片。
三重去重机制
为了避免知识库中存在重复问题或高度相似问题,系统对问答对做如下处理:
-
哈希去重:判断完全重复的问答对;
-
语义相似度去重:将问句转成向量,若余弦相似度 > 0.93,就认为太相似,去掉一个;
-
动态阈值扩展机制:
-
如果匹配结果太少,系统会自动降低相似度标准(比如从0.93放宽到0.88),保证至少能返回一些合理结果。
-
这样兼顾了“结果准确”和“结果充足”。
-
(3)向量预计算与归一化存储
-
所有问答对在系统启动时就提前用 BERT 向量化;
-
对每个向量做 归一化处理(确保计算余弦值时不受向量长度影响);
-
存在内存里,方便后续快速检索。
优点:查询时就不用每次都重新计算知识库向量,节省用户等待时间,提高系统响应速度。
(4)接口设计(通过Flask)
-
后端使用 Flask 的路由机制,把不同的 HTTP 请求(如 POST /search)对应到具体的功能代码;
-
例如:
-
/search 用于搜索问题;
-
/submit 用于录入新问题。
-
(5)用户使用查询功能时
前端将用户输入的问题打包成 JSON 格式 发送给后端(例如:{“question”: “心法如何修炼?”});
后端使用 request.json 获取数据,并提取出 question 字段;
然后:
-
清洗文本;
-
用 BERT 向量化(加权融合最后四层,提升语义理解);
-
使用 Sigmoid 函数放大高相似度之间的区分度;
-
进行与知识库中向量的相似度匹配(比如取Top5相似的问题)。
如果用户还选择了标签筛选:
-
前端把选择的标签传给后端;
-
后端只返回含有该标签的问题和答案。
(6)用户使用录入功能时
前端先校验数据格式(是否填写完整);
后端收到数据后,执行:
-
文本清洗;
-
哈希查重;
-
语义查重;
-
如果问题通过查重检测,写入知识库文件(如 CSV);
-
使用 np.vstack() 对向量矩阵进行动态扩展(热更新);
-
无需重启服务器,新问题立即可查询。
如果录入成功:
-
返回结果给前端;
-
前端展示“提交成功”的提示。
如果不符合要求:
-
返回出错原因;
-
例如“问题为空”、“语义重复”;
-
前端负责展示提示信息。
五、中华心法问答系统的实现
1. BERT模型的运用
(1)整体思路解释
目标:将用户输入的问题(或知识库中的问题)转化为一个语义向量,用于后续的语义相似度计算。
使用BERT的原因:BERT 是预训练的大规模语言模型,能够捕捉句子的深层语义和上下文信息。
(2)为什么使用BERT的最后四层
BERT 的输出由多个 Transformer 层组成(默认是 12 层);
不同层表达的语义信息不同:
-
浅层(如第9层):更关注词法、局部语法;
-
深层(如第11层):更关注整体句意、抽象语义;
心法领域中的问句具有高度抽象性,所以更看重深层含义;
因此采用一种加权融合策略,对最后四层的输出按重要程度赋予不同的权重。
权重示例(从第9层到第12层):[0.15, 0.25, 0.35, 0.25]
- 第11层(倒数第2层)占比最高,强调抽象语义;
- 第9层占比最低,仅保留必要的词法信息
(3)为什么归一化向量
-
向量归一化的目的是让每个向量的长度为1;
-
这样可以避免因为句子长短不同而造成的向量模长差异对余弦相似度计算的干扰;
-
所以最后一步使用如下代码进行归一化:
fused_vector / np.linalg.norm(fused_vector)
(4)关键代码解释
①
def _get_embedding(self, text: str) -> np.ndarray:
- 这是定义一个函数,用来获取输入 text 的向量表示。
函数的目的:将输入的一句话,如 “如何修炼内功?”,通过 BERT 模型编码成一个语义向量(长度为 768),用于后续的相似度计算。
② 分词与编码
inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=512).to(DEVICE)
-
self.tokenizer(…) 是 BERT 模型自带的分词器,它将自然语言句子转化为模型可接受的格式。
-
return_tensors= “pt” 表示使用 PyTorch 张量格式;
-
truncation=True 说明如果太长就截断,最多不超过 512 字;
-
.to(DEVICE) 把张量送到 GPU 或 CPU 上运行。
以 “如何修炼内功?” 为例:
① 调用分词器后,会得到
tokens = [‘[CLS]’, ‘如何’, ‘修炼’, ‘内功’, ‘?’, ‘[SEP]’]
② 这些 token 会被转化为 ID,例如:
input_ids = [101, 2128, 3299, 3782, 8043, 102]
(说明:101 是 [CLS],102 是 [SEP],其他数字是词的词表编号)
目的:将文字转成模型可以处理的数字表示
例如:
text = “如何修炼内功?”
tokens = [‘[CLS]’, ‘如何’, ‘修炼’, ‘内功’, ‘?’, ‘[SEP]’]
token_ids = [101, 2128, 3299, 3782, 8043, 102]
模型看到的就是这些 token ID
③ 输入BERT模型(输出所有隐藏状态)
with torch.no_grad():outputs = self.model(**inputs)
-
with torch.no_grad() 表示推理阶段不计算梯度(即不训练,只推理),节省内存;
-
self.model(…) 运行 BERT 模型
-
outputs.hidden_states:模型每一层的输出
得到的 outputs.hidden_states 是 13 层隐藏状态(1层输入 + 12层Transformer)
每层是一个形如:
[1, 6, 768] # batch_size=1, sequence_len=6, hidden_size=768
每层都是6个 token(句子长度)每个 token 是768维的向量。
(其中 [CLS] 是每一层第一个 token 的表示,即 layer[:, 0, :])
④ 提取最后四层的 [CLS] 向量
hidden_states = outputs.hidden_states[-4:]
- 获取 BERT 最后的 4 层隐藏状态(每层是一个 [batch_size, seq_len, hidden_dim] 的张量);
cls_vectors = torch.stack([layer[:, 0, :] for layer in hidden_states])
-
对每层,取出 [CLS] 位置的向量(即 layer[:, 0, :]),它代表整句含义;
-
将四个 [CLS] 向量堆叠成一个新张量,形状为 [4, hidden_dim]。
最终会得到:
cls_vectors.shape = [4, 768]
例如:
cls_vectors = [
[0.01, 0.02, …, 0.05], # 第9层
[0.04, 0.01, …, 0.06], # 第10层
[0.10, 0.09, …, 0.08], # 第11层(抽象)
[0.08, 0.04, …, 0.07], # 第12层
]
⑤ 加权融合四层向量
fused_vector = torch.sum(cls_vectors * self.config.LAYER_WEIGHTS, dim=0)
-
对四个 [CLS] 向量按预设权重 LAYER_WEIGHTS 进行加权求和;
-
得到融合后的语义向量 fused_vector。
假设 LAYER_WEIGHTS 为[0.15, 0.25, 0.35, 0.25],以第100维为例:
第100维的融合计算:
= 0.15×0.05(第9层) + 0.25×0.06(第10层) + 0.35×0.08(第11层) + 0.25×0.07(第12层)
= 0.0075 + 0.015 + 0.028 + 0.0175 = 0.068
所以融合后的 fused_vector 是一个长度为 768 的向量:
fused_vector = [0.045, 0.067, …, 0.068] # 共768维
⑥ 归一化处理
return (fused_vector / np.linalg.norm(fused_vector)).cpu().numpy()
- 将向量归一化(单位化),然后转为 CPU 并转成 NumPy 格式;
假设:向量的模长为||fused_vector|| = 1.872
则:
normalized[0] = 0.045 / 1.872 ≈ 0.024
normalized[1] = 0.067 / 1.872 ≈ 0.036
最终出的 normalized 向量仍然是一个长度为 768 的向量,但所有数值已压缩到单位球面。
- 返回最终的语义向量表示。
最终结果:
array([
0.0241, -0.0357, 0.0159, …, 0.0687
]) # 长度为768
这个就是“如何修炼内功?”的语义向量表示。
它现在可以与其他问题的向量做余弦相似度计算,来判断语义是否接近。
2. 混合检索算法的实现
(1)动态校准策略(语义相似度校准)
① 核心思想:用 BERT 语义相似度 + Sigmoid 非线性变换 + 原始余弦相似度补偿 的方式,对高相似度区域(如 0.88~0.93 区间)进行更敏感的区分,从而提升相似问句的辨别能力。
② 实现过程:
# 1. 原始语义相似度(由BERT余弦计算得到)
raw_similarities = cosine_similarity(query_vector, knowledge_base_vectors)# 2. 对高相似度区间进行Sigmoid校准
calibrated = 1 / (1 + np.exp(-25*(raw_similarities - 0.88)))# 3. 加权融合(混合相似度)
similarities = 0.2 * raw_similarities + 0.8 * calibrated
③ 举例说明
句子编号:A
原始余弦值 raw_similarities:0.85
校准后 calibrated:0.3775
最终 similarities:0.2×0.85 + 0.8×0.3775 = 0.477
④ 好处
- Sigmoid 增大了 0.88~0.93 的区分度(原本余弦值之间差距小,现在差距更明显)
- 补偿机制(0.2×原始 + 0.8×校准)使得整个值域不会偏离原始相似度太多,保持语义分布的整体稳定性。
(2)关键词增强机制(文本相似度补充)
① 核心思想
弥补 BERT 对 “关键词(尤其是专业术语)” 不敏感的问题,利用 TF-IDF + 共现匹配,提取关键词后做共现率计算。
什么是TF-IDF
TF-IDF 是一种用于衡量一个词对于某篇文档在整个语料库中有多重要的常用方法.
它由两部分组成:
- ① TF:某个词在当前文档中出现的频率(出现越多越重要)
- ② IDF:某个词在所有文档中出现的频率的“反比”(出现越少越重要)
直观解释:
- 如果某个词在某篇文章中出现很多次(高 TF),那它可能是这篇文章的关键词。
- 但如果这个词在所有文章中都很常见(低 IDF,例如“的”“是”),那它的区分度就低。
- TF-IDF 就是要找那些:在该文档中出现频繁,但在其他文档中很少出现的词,也就是具有代表性的关键词。
② 实现过程
# 1. 文本清洗 → 分词
query_keywords = set(jieba.lcut(cleaned_query))# 2. 关键词共现度
keyword_intersection = query_keywords & doc_keywords
keyword_weights = len(keyword_intersection) / max(len(query_keywords), 1)# 3. 混合最终相似度(语义 + 文本)
final_similarity = 0.7 * semantic_similarity + 0.3 * keyword_weights
③ 举例说明
假设:
- 用户问句分词后关键词:{“内功”, “修炼”}
- 某知识库问句关键词:{“内功”, “呼吸”, “吐纳”}
则关键词交集为 {“内功”},共现度:keyword_weights = 1 / 2 = 0.5
如果BERT 相似度为 0.8:final = 0.7 * 0.8 + 0.3 * 0.5 = 0.56 + 0.15 = 0.71
(3)总结对比
模块 | 功能 | 技术 | 举例说明 |
---|---|---|---|
动态校准策略 | 强化高相似度区域划分 | 域区分 BERT余弦 + Sigmoid + 加权补偿 | 将 0.88、0.90、0.93 差异拉大 |
关键词增强机制 | 弥补语义遗漏关键词问题 | jieba分词+TF-IDF共现 | {“修炼”, “内功”} 与 {“内功”, “吐纳”} 匹配1个关键词,权重0.5 |
最终融合 | 平衡语义和关键词 | 0.7 * 语义 + 0.3 * 关键词 | 相似度提升更合理,兼顾句意和字面 |
3. 去重模块的实现
问答系统中文本清洗和去重机制的具体实现方式。
(1)文本清洗部分
① 目的
去除“虚词”(如:的、了、是、在等),保留“实词”(名词、动词、形容词、人名、地名等),提升语义向量的准确性。
② 代码解释
def clean_text(text): keep_pos = {'n','v','a','nr','ns'} # n=名词, v=动词, a=形容词, nr=人名, ns=地名return " ".join([word for word, flag in words if flag[0] in keep_pos])
- words 是使用jieba分词并带词性标注后的结果
例如:words = pseg.cut(“如何修炼内功”)
输出: [(‘如何’, ‘r’), (‘修炼’, ‘v’), (‘内功’, ‘n’)] - flag[0] in keep_pos 表示我们只保留词性以 ‘n’, ‘v’, ‘a’, ‘nr’, ‘ns’ 开头的词。
③ 举例说明
- 输入原始句子:
“我应该如何修炼内功?” - jieba分词结果(含词性):
词 | 词性 | 是否保留 |
---|---|---|
我 | r(代词) | 否 |
应该 | d(副词) | 否 |
如何 | r(代词) | 否 |
修炼 | v(动词) | 是 |
内功 | n(名词) | 是 |
- 清洗结果为:
“修炼 内功”
(2)去重机制(三重过滤)
① 目的
在回答相似问题时,去掉重复的或语义高度重合的答案,提升用户体验。
② 去重策略包括三重过滤
- 内容哈希去重(防止重复内容)
content_hash = hash(data["cleaned_question"] + data["answer"][:20])
if content_hash in seen_hashes:continue
-
利用 Python 的 hash() 对问题 + 答案前20字符进行哈希值计算。
-
如果已经出现相同的哈希,就跳过,避免输出完全重复的内容。
- 语义相似度去重(防止“换句话说”的重复)
if any(np.dot(self.question_vectors[idx], self.question_vectors[r["index"]]) > 0.93 for r in results):continue
-
使用向量点积(近似等价于归一化后的余弦相似度)判断:
- 当前回答与已有回答是否语义过于接近(>0.93)
-
避免输出内容不同但意思雷同的回答。
- 动态阈值筛选(控制输出质量)
if sim < self.config.MIN_SIMILARITY:continue
-
如果当前问题与知识库的相似度小于设定阈值(如 0.7),则跳过。
-
如果系统发现返回结果太少,会自动 降低阈值,放宽匹配限制,以保证有足够回答输出。
③ 举例说明
假设用户输入:
“如何修炼内功?”
知识库中有以下问句:
问句ID | 问题内容 | 语义相似度 | 哈希是否重复 | 向量相似度>0/93 |
---|---|---|---|---|
Q1 | “修炼内功的方法?” | 0.95 | 是 | 是 |
Q2 | “怎么练好内功?” | 0.92 | 否 | 是 |
Q3 | “内功要如何突破?” | 0.89 | 否 | 否 |
系统处理逻辑:
- Q1:哈希重复 + 向量太相似 → 剔除
- Q2:虽然哈希不同,但语义相似度过高 → 剔除
- Q3:符合相似度要求、内容不重复 → 保留
这个模块的作用:对用户输入的问句进行文本清洗、向量语义判断和关键词增强,使用三重过滤机制(哈希去重、语义相似度去重、动态阈值控制),避免问答内容重复、提高相似度计算的精度,从而提升系统的智能性和用户体验。
4. 实验不同参数对比的结论
结论:在中华心法问答系统中,语义理解与关键词匹配采用 0.7:0.3 的融合权重能获得更准确、更符合用户语义意图的相似度匹配结果。
六、运行代码
Window系统上
(1)创建虚拟环境
python -m venv naodian-env
- 创建了一个名为 naodian-env 的虚拟环境
(2)激活虚拟环境
naodian-env\Scripts\activate
- 激活虚拟环境
(3)安装各种功能包
# 安装 jieba
pip install jieba# 安装 numpy
pip install numpy# 安装 torch(即 PyTorch)库
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu# 安装 transformers
pip install transformers# 安装 Flask
pip install flask
(4)运行代码
python xinfa_QA.py
登录网页 http://127.0.0.1:5000
七、总结感受
通过深入学习论文中所提出的“中华心法问答系统”设计方案,我对智能问答系统的整体构建流程与核心技术有了更全面的认识。尤其是在系统中采用语义理解与关键词匹配相结合的混合策略,使我意识到,在实际应用中,单一的匹配机制往往难以兼顾准确性与灵活性,合理分配权重(如0.7:0.3)才能更有效地提升问答质量。
论文中对语义相似度的处理方式也让我印象深刻。通过引入Sigmoid函数对原始余弦相似度进行非线性变换,不仅优化了高相似度样本的区分能力,也体现了对实际使用场景的精细化考量。这种从 “模型输出” 到 “实际需求” 之间的桥接过程,是我在以往学习中较少接触但极具启发性的部分。
此外,在知识录入、去重、相似问题推荐等模块中,运用语义向量去重与分类方法构建知识库,不仅增强了系统扩展性,也使我更加理解了如何将理论与工程实现相结合。
总体而言,这次学习让我不仅掌握了相关的技术路线(如BERT模型、语义匹配、知识管理),也培养了我用系统思维看待问答系统设计的能力。
通过本次对“中华心法问答系统”相关代码的运行与学习,我对该系统的整体框架和实现流程有了初步的认识。在本周学习中,我主要聚焦于论文具体知识点的学习和代码的整体运行效果,初步了解了语义匹配、关键词权重分配、相似度计算等核心部分的作用。接下来,我将进一步深入阅读和分析源代码,结合论文内容深入理解各个模块的设计思路与算法实现逻辑。