基于BERT的金融问答匹配实践:从数据构建到线上部署
“投资知道”这个项目我做了大概三个月前后踩了不少坑。核心任务听起来很简单用户输入一句中文问题系统判断它和知识库里的哪条标准问答最匹配。但真正落地在投资领域你会发现“简单”这件事根本不简单——比如“茅台还能买吗”和“贵州茅台现在可以入手吗”语义几乎一致但用词完全不同再比如“新能源车板块”和“新能源板块”一字之差指向的却是完全不同的赛道。这种细颗粒度的语义差异传统的关键词匹配方案基本束手无策这也是我最终选择BERT来构建问答匹配模型的原因。这个项目解决的实际问题是把静态的金融FAQ知识库变成具备语义理解能力的问答系统让用户在提问时不需要刻意凑关键词。整个过程涉及数据构建、模型选型、训练调优、上线优化四个环节每一步都有不少值得记录的经验。这篇文章我会把完整的技术路线、关键代码、参数设置思路、以及我真实踩过的坑都写出来给同样在做中文问答匹配、特别是金融投资领域NLP项目的朋友一个可参考的样本。1. 项目定位与整体设计思路1.1 这个系统到底在解决什么问题投资领域的知识库有一个特点条目多、问题杂、且用户的问法极其不稳定。同样是问基金定投有人会问“定投怎么操作”有人会问“每个月自动买入基金好不好”还有人会直接问“微笑曲线是不是骗人的”。传统ElasticSearch方案依赖BM25算法做关键词匹配对于这种同义异形的问法几乎无能为力——因为倒排索引压根找不到共同的词项。基于BERT的问答匹配模型本质上是把“用户问题”和“知识库标准问题”分别编码成语义向量再通过距离度量判断二者的匹配程度。这属于文本语义相似度计算的经典任务但放在投资知识问答场景下有一个额外的难度金融文本的术语密度高“估值”“宽基”“回撤”“北向资金”这类词的语义边界比通用领域要窄得多。通用模型很容易把“生物医药板块”和“医药生物板块”判成完全相似但实际上在投资语境里前者偏向行业指数后者偏向主题基金虽然相关答案却可能完全不同。所以在项目初期我给自己定了一个原则这个模型必须为投资领域做专门的适配通用预训练模型只能作为底座不能直接上线。1.2 为什么选择BERT而不是其他方案在定技术方案之前我对比了三条路线。第一条是传统机器学习方案用TF-IDF或者Word2Vec把句子向量化然后算余弦相似度。这个方案的优点是简单、训练快、可解释性强但缺点也很明显TF-IDF对同义词无能为力Word2Vec虽然能捕捉一定的语义关系却无法处理“否定”和“焦点”这一类句子结构信息。比如“定投是不是一定能赚钱”和“定投不一定能赚钱”词向量平均之后两条句子的向量会异常接近模型很容易判错。第二条是基于预训练模型的句向量方案也就是把BERT当成一个句子编码器取[CLS]位置的输出或者做mean pooling得到句向量。这个方案部署简单向量可以预计算线上只要算一次用户向量的余弦相似度性能极好。但它有一个关键短板双塔结构下问题和标准答案在编码阶段是完全独立的模型很难学到“问题中的某个词指向答案中的某个词”这样的细粒度交互。第三条就是我这个项目最终采用的方案——基于BERT的交互式匹配分类。把“用户问题”和“知识库标准问题”拼接成一句话中间加[SEP]分隔符让BERT在编码过程中充分做token级别的交互注意力。这个方案放弃了预计算向量的便利换来了更高的匹配精度。在金融领域语义差一点点答案就完全跑偏精度优先级天然高于性能优先级。事实上我的实验数据也验证了这个判断。在自建测试集上双塔句向量方案的Top-1准确率大概在86%左右而交互式分类方案在相同数据上可以达到93%以上。在“语义近似但答案不同”的对抗样本上差距更明显交互式模型能正确识别出60%的陷阱样本双塔方案直接崩到不到20%。这个结果基本宣判了双塔方案在我这个场景下的死刑。1.3 系统整体架构整个系统的技术栈选型比较克制没有上太重的框架。数据处理层用的Pandas加正则训练框架用的PyTorch配合Transformers库模型底座是hfl发布的RoBERTa-wwm-ext-large中文领域效果稳定且社区成熟度高。验证集和测试集按8:1:1划分评估指标除了常规的准确率和F1我还额外加了一个业务导向的指标Top-5命中率。理由是实际产品中用户并不一定要求系统一次就给到唯一答案给出最相关的5条候选让用户自己选反而更符合问答产品的交互习惯。线上推理我没有直接部署原版BERT而是做了一步蒸馏把参数量压缩到原来的六分之一具体内容在后面的章节展开。2. 核心原理拆解BERT在问答匹配中是怎么生效的2.1 交互式编码的工作机制很多朋友第一次接触BERT做句子匹配会被双塔和交互两种架构绕晕。我在这里用最直白的方式解释一下。双塔架构就像两个互不说话的考生各自看自己的题问题和标准问题分别写出答卷向量最后阅卷老师相似度计算模块只对比两张卷子像不像。问题是你完全看不到两个考生在写卷子时有没有互相参考过所以碰到“非对称信息”的题目就容易翻车。交互式架构则不同。它把“用户问题”和“标准问题”用[SEP]拼接成一个长句子喂给BERT相当于让两个考生面对面讨论问题。BERT每一层的Self-Attention机制会让问题中的每个token都去“看”标准问题中的所有token例如表示“茅台”的token能够直接分配给“白酒”这个token更高的注意力权重。这样一来模型在做分类之前就已经完成了跨句的信息交换。从代码层面看交互式模型的实现极其简单。核心就是Transformers库的AutoModel加一个分类头import torch from torch import nn from transformers import AutoTokenizer, AutoModel class BertPairClassifier(nn.Module): def __init__(self, model_path, num_labels2): super().__init__() self.encoder AutoModel.from_pretrained(model_path) self.dropout nn.Dropout(0.1) self.classifier nn.Linear(self.encoder.config.hidden_size, num_labels) def forward(self, input_ids, attention_mask, token_type_ids): outputs self.encoder( input_ids, attention_maskattention_mask, token_type_idstoken_type_ids ) cls_vector outputs.last_hidden_state[:, 0, :] logits self.classifier(self.dropout(cls_vector)) return logits注意上面这段代码里[CLS]位置的输出被当作整个拼接序列的语义摘要。这背后的逻辑是BERT的预训练任务本身就要求[CLS]向量汇聚整个句子的信息。在交互式拼接模式下这个向量同时包含了问题和标准问题的交互信息喂给一个线性分类器就能判断是否匹配。2.2 中文模型选型对比当前中文预训练模型非常多各有侧重。我在这个项目里认真对比过以下四个BERT-base-Chinese是最经典的选择Google官方发布中文语料训练。胜在稳定、生态好网上你能搜到的中文NLP教程几乎都以它为基准遇到问题最容易找到参考。但它的一个明显短板是用了字级别的词表对“估值”这种常见中文双字词的表示需要模型通过注意力机制自行组合训练成本相对高。RoBERTa-wwm-ext-large是哈工大讯飞联合发布的模型主打全词掩码训练。所谓全词掩码和BERT原始掩码的区别在于BERT随机遮住单个汉字然后让模型预测被遮住的字wwm策略则是把整个词的所有字一次性遮住。这就逼着模型真正理解“词”这个语义单元而不是“字”对中文这种词边界模糊的语言来说效果提升非常明显。我从base到large都试过large在自建测试集上比base版本准确率高1.8个百分点。值得注意的是这个模型的词表是繁体简体混合的处理含繁体字的用户输入时会比较从容。ERNIE是百度的模型亮点在于知识增强训练时加入了海量知识图谱数据对实体类信息敏感非常适合金融领域人名、公司名、股票名密集的场景。比如“贵州茅台”和“茅台”在不同说法下的指代关系ERNIE的初始理解能力要强于RoBERTa-wwm。Chinese_LLaMA系列是在羊驼模型上继续中文训练的优势在于Chat场景表现好但模型体量大、部署成本高。我评估下来它更适合做生成式客服而非匹配式问答所以这个项目里没有采用。综合对比后我以RoBERTa-wwm-ext-large为主力模型。原因有三一是全词掩码带来的中文语义精度收益在实验中是实打实的二是没有知识图谱资源的团队用ERINIE发挥不出它全部优势三是large版本虽然慢但实验室阶段对推理速度的容忍度较高。2.3 为什么在投资领域必须做领域适配很多人会用现成的中文BERT直接跑业务数据发现效果不理想就怀疑模型是不是有问题。其实模型本身没毛病问题出在“分布外匹配”。通用BERT训练的语料来自新闻、百科、社区它的语义空间是围绕通用语境构建的。而投资领域的文本有其独立的概念体系。举个例子“高位接盘”和“买入”在通用语义里毫不相干但在股民语境里含义高度相关。“票息”“久期”“回售”这些债券术语更是完全不会出现在通用语料的高频词表里。领域适配有两种做法。第一种是领域预训练拿大量投资领域的无标注语料以MLM掩码语言模型为目标继续训练原始BERT。这个做法的效果好但成本高动辄需要几十万条语料和几十小时GPU时间。第二种是领域微调直接在有标注的问答匹配数据集上训练借助BERT强大的泛化能力让它自己学到投资领域的匹配模式。我这个项目的实际经验是在标注数据质量有保证的前提下领域微调的收益远大于领域预训练。因为问答匹配是相对底层的语义任务数据量只需要几千条就能把模型调到能用的水平。我也见过有人强行做领域预训练但因为数据清洗不到位反而把通用能力也带偏了这就得不偿失了。3. 数据工程问答匹配模型的血肉3.1 训练数据从哪里来在金融领域做NLP项目最大的瓶颈不是模型而是数据。我在项目里构建训练集用了三个渠道。第一个渠道是知识库历史日志。公司现有的投资问答系统里积累了大量用户历史提问记录每一条用户真实提问都能对应到一个客服标准问题。这种数据质量非常高因为它是真实的用户语言包含各种口语化表达和错别字。我清洗掉包含个人隐私和敏感信息的数据后一共拿到了约8000条有效记录。第二个渠道是数据增强。在真实数据基础上我用三种方式扩充同义词替换比如“买”和“申购”、“基金”和“产品”互相替换句式改写比如把“余额宝和定期宝有什么区别”改写成“定期宝和余额宝有什么不同”回译增强先把中文翻译成英文再翻译回中文产生新的问法变体。数据增强后训练集从8000条扩充到了约15000条。第三个渠道是人工构造对抗样本。这是我认为最价值的一步。我专门列出了知识库中容易混淆的问答对然后手动编写用户的“陷阱式提问”。比如标准问题A是“什么是ETF”标准问题B是“什么是LOF基金”我就构造一条“ETF和LOF到底是不是一回事”。这类样本对模型的区分能力要求极高也正是双塔模型崩溃的那类输入。最终训练集各类别分布如下样本类型数量用途真实用户日志QA对8000基础训练数据增强QA对7000扩充数据人工对抗样本1500提升区分度人工负采样8500防止过拟合这里要额外说明负样本的构造逻辑。问答匹配和常规分类不同它的难点在于同一批正样本要搭配大量高质量负样本。不能随便拿两条不同问题就当成负样本那样模型会学到“字面不同即为不匹配”的偷懒策略。正确的负样本应该是“看着像但答案不同”的样本对让模型被迫学习语义级别的精细区分。3.2 数据处理的关键细节数据清洗阶段我处理掉三类噪音。第一类是数字和符号的规范化。投资领域充斥着“3.5%”“年化7.2%”“上证指数3000点”这类数字信息。原始数据里有用全角字符的有用半角字符的还有带逗号千分位的。如果不统一同一句话检索出来反而被分词拆碎。我写了一段统一的清洗函数将所有全角数字转半角并统一百分号和千分位的表达格式。第二类是英文大小写和空格。金融产品代码经常混有英文“XXXX基金”和“XXXX基金”在用户输入中经常写错大小写需要统一转小写处理。同时中文和英文之间多余的空格会影响BERT对词边界的判断需要一并清理。第三类是表情符号和特殊字符。不少用户提问时会带上表情比如“白酒今天还能上车吗”。表情符号在BERT词表里对应的是[UNK]直接干扰注意力计算必须删除。还有一些无意义的语气助词比如“啊”“呢”“哈”在匹配任务中属于无害噪音保留即可删除反而可能误伤正常的句法结构。数据清洗之后我做了一个比较关键的操作为每一条标准问题配置合法的同义句集合。这个操作借鉴了信息检索领域的思想——标准问题本身是一个“查询入口”而用户问题需要被映射到这个入口上。我给每条标准问题人工编写了3到5个同义表达然后把这些同义句全部加入正样本集。这一步对于提升模型在真实场景下的鲁棒性有明显帮助。3.3 样本配比与数据增强问答匹配的数据集最怕两种偏斜。第一种是正负样本比例失衡第二种是某一类问题数量过多导致模型产生类别偏见。正负样本比例我控制在1比3。之所以不用1比1是因为实际线上场景中用户输入的问题与知识库中标准问题不匹配的概率远大于匹配的概率。负样本多一些模型训练时看到的“困难例子”更多决策边界会更紧。但也不能太多超过1比5后模型会变得过度保守轻微语义偏差就判为不匹配反而降低召回。数据增强时我有一个经验不要对标准问题做增强只对用户问题进行增强。原因是标准问题的表达通常已经是最规范的形态改写它们会导致模型对规范表达的置信度降低。而用户问题本来就是千奇百怪的对它们做同义改写、回译实际上是在模拟真实线上分布。4. 模型训练与调优实战4.1 训练配置的完整参数训练阶段用的硬件是单张24G显存的显卡。对于RoBERTa-wwm-ext-large这个体量的模型约325M参数这个显存条件比较紧张因此我在训练时统一使用了梯度累积。核心训练参数如下training_args { learning_rate: 2e-5, batch_size: 16, gradient_accumulation_steps: 4, num_epochs: 5, warmup_ratio: 0.1, weight_decay: 0.01, max_seq_length: 128, fp16: True, early_stopping_patience: 2, }学习率2e-5是BERT系列微调最常用的起点。RoBERTa-wwm-ext在原始预训练时的学习率也是这个量级微调时保持同一量级能让模型在已有语义空间上平稳漂移而不是剧烈震荡。如果换到1e-5训练收敛速度明显变慢换到5e-5验证集loss会在第2个epoch后开始反弹典型的过拟合信号。batch_size设为16加上梯度累积4步等效batch_size为64。这个数值在对比实验中是最稳的。过大的batch_size会让模型收敛到尖锐极小值泛化能力下降过小的batch_size则容易陷入局部最优训练噪音偏大。max_seq_length设为128这对匹配任务比较奇怪因为拼接后的输入最多可以有512个token。我在统计了训练集序列长度分布之后发现98%的样本在128个token以内即使强制截断到128丢失的信息也极其有限。更关键的是128的长度可以让显存占用和推理延迟都大幅降低。fp16混合精度训练会在部分层使用半精度浮点数。代价是训练结果的可复现性变差收益则是显存占用近乎减半、训练速度提升明显。实验阶段建议先关掉最终训练再打开。4.2 学习率策略与过拟合控制微调预训练模型最常遇到的坑就是过拟合。预训练模型含有的参数量远大于你的标注数据量如果不加控制模型很容易“背”住训练集失去泛化能力。我用三个手段控制过拟合。第一个是早停法。每轮训练结束计算验证集loss连续两轮不下降就停止训练。实际训练中模型在第4轮达到最优验证loss第5轮开始反弹早停正好在合适的位置截断了训练。第二个是权重衰减。在AdamW优化器中对非偏置参数设置0.01的权重衰减。这个值不算大刚好起到一定的正则作用不至于让模型学不到新特征。第三个是Dropout。分类头前面加了一层0.1的Dropout让模型不敢过于依赖某一个神经元的输出。这里有个容易被忽略的细节BERT的默认hidden_dropout_prob参数是0.1指的是Transformer块内部全连接层的Dropout比例。这个值是预训练时设定的微调时一般不建议动。但如果你的训练数据极少可以尝试提高到0.15甚至0.2效果偶尔会有惊喜。4.3 对抗验证如何摸清模型能力边界模型训练完后我做的第一件事不是看测试集准确率而是做了一次“对抗验证”。具体方法是从知识库中找出所有容易混淆的问答对构造了一批高度相似的输入看看模型能否正确区分。比如下面这组数据pair_1 (什么是主动型基金, 主动型基金和被动型基金有什么区别, 0) pair_2 (沪深300指数有哪些成分股, 沪深300指数基金有哪些, 0)第一对句子后半部分高度相似但一个是问定义一个是问区别答案完全不相同。第二对只差“成分股”和“指数基金”四个字看起来都是问沪深300实际上一个是股票列表一个是基金产品。对抗验证的结果让我看到了模型的真实水平RoBERTa-wwm-ext-large在这类样本上能做到80%以上的正确区分而base版本大概只有65%。这再次验证了我选large模型的决策是值得的。对抗验证还有一个重要产出——它暴露了训练数据中缺失的样本类型。例如模型会把“某某基金值得买吗”和“某某基金的净值怎么看”判为较相似原因是训练集中同时问这两种问题的真实用户太少。针对这个发现我专门补充了一批这两个问法之间的负样本模型的区分能力明显提升。4.4 评估指标的选择陷阱做问答匹配项目很多人习惯只看准确率这样做其实会掩盖问题。我再举个例子如果100条测试样本中90条是“明显不匹配”的简单样本模型只需要无脑输出“不匹配”就能拿到90%准确率看上去非常漂亮实际上线上效果一塌糊涂。我最终的评估体系由四个指标构成指标计算方式用途准确率全部分类正确的比例综合效果参考精确率预测为匹配中真正匹配的比例衡量返回结果的可靠度召回率真实匹配中被正确找出的比例衡量漏召回的风险F1精确率与召回率的调和平均平衡两者除了这四件套我还要求模型在测试集上达到一个硬性指标Top-5召回率不低于95%。所谓Top-5是把测试集中的每个用户问题与所有标准问题计算匹配分取分数最高的前5条看真实的匹配标准问题是否落在其中。95%的阈值意味着系统上线后用户90%以上的问题都能在候选列表中看到正确答案即使排第一的不是最准的也不至于完全跑偏。最终模型在测试集上的表现是准确率93.4%、精确率91.8%、召回率92.1%、F1值92.0%。整体来说达到了投资知识问答产品上线的准入门槛。5. 线上部署与性能优化5.1 从实验室到线上的工程改造训练阶段用的RoBERTa-wwm-ext-large模型文件接近1.2GB单次推理延迟在CPU上要400毫秒以上GPU上也要35毫秒。这个性能直接上线会拖垮用户体验我不得不做工程优化。优化的第一步是模型蒸馏。我用训练好的large模型作为教师模型训练一个参数更小的学生模型。学生模型选了6层Transformer结构保留12个注意力头词表完全复用教师模型。蒸馏时同时计算预测分布的KL散度和硬标签交叉熵蒸馏温度设为4。训练后学生模型的参数量只有教师模型的六分之一推理延迟从35毫秒降到8毫秒而测试集准确率只下降了0.7个百分点。蒸馏的收益不只是速度提升。小模型的内存占用小可以在普通CPU服务器上多实例平行部署反而比单一大模型承载了更高的并发量。5.2 推理加速的工程技巧线上服务我用的是自建的推理服务。单模型推理做到极致之后我还有三个加速技巧值得分享。第一个是输入长度动态优化。BERT模型的输入长度严重影响着推理延迟。虽然max_seq_length设定为128但大部分用户问题根本不到40个token。我在服务端做了一个长度探测先用最快的统计方法估算当前输入的长度然后动态设置padding到实际可用的最短长度。这样做下来推理延迟额外降低了20%。第二个是批量推理。如果同时到达10个用户请求我并不是一个个处理而是把这10条数据padding成同一个batch喂给模型。GPU在batch推理时吞吐量更高单条平均延迟能降低到原来的三分之一。代价是最慢的一条请求会拖慢整个batch的响应时间所以batch大小需要控制在16以内。第三个是结果缓存。对于完全相同的用户问题我会在Redis里缓存匹配结果。投资知识库的问题总量是有限的用户的高频问题其实高度集中。缓存命中率在实际运行中能达到18%左右这部分请求的响应时间直接变为个位数毫秒。5.3 蒸馏模型与原版模型的误差分析做完蒸馏之后我认真对比过学生模型和教师模型在预测结果上的差异。整体上两者的重合度在93%以上但在两类样本上分歧明显。第一类是包含罕见金融术语的样本比如“可转债的转股溢价率怎么算”。学生模型对这类样本的置信度普遍偏低原因是6层Transformer的信息交换深度不如12层对复杂语义关系的建模能力有限。第二类是超长句的样本超过120个token后学生模型的注意力开始分散教师模型还能保持稳定。这两类样本占所有测试数据的比例不高但都属于高价值金融内容不能放弃。我的处理方式是做一个简单的路由分类器当检测到用户输入中包含高价值金融术语字典中的词汇或者句子长度超过120个token时自动路由到原版large模型处理其他请求则由蒸馏模型承接。这样既保证了高难场景的精度又维持了整体服务的低延迟。6. 踩坑实录与排查技巧6.1 训练过程中的典型故障我在这个项目里遇到最诡异的一个问题loss在训练到第3轮之后开始抖动验证集准确率不升反降但训练集loss还在下降。排查了很久才发现是梯度累积导致的我设置batch_size16、梯度累积步数4等效batch_size64。但Transformer库的Trainer在梯度累积时默认不清空优化器的状态导致曲线的波动比预期大。解决办法是调小学习率到1.5e-5同时把梯度累积步数降到2让优化器更新参数的频率变高训练稳定性明显改善。后来我养成了一个习惯凡是用了梯度累积训练曲线就要多盯两轮发现异常先在优化器参数上找原因而不是急着改模型结构。还有一次问题出在数据加载上。我的训练脚本用DataLoader的num_workers开启多进程加载数据某天突然报错说内存溢出。检查之后发现是每个worker都会复制一份全部的训练数据到内存。解决方法是改用内存映射文件加载数据或者把num_workers调低问题立刻消失。6.2 样本噪声导致的反直觉结果有段时间模型在验证集上的精确率特别高但召回率很低我最初以为是阈值设置问题调整多次无果。后来仔细检查了训练集发现正样本中有大量“自动匹配错误”的数据——知识库日志里把客服转接后的对话记录标成了匹配成功实际上用户的问题和标准答案根本不相关。这类噪声样本的量级在真实日志数据中能占到5%左右如果你的标注流程不够严谨比例甚至能到10%。处理方法是引入置信度过滤先用当前模型预测一遍所有训练样本把预测置信度低于0.5但标注为“匹配”的样本全部筛选出来人工逐个复核。实际复核后发现这批低置信度正样本中有超过一半确实是错标数据清理之后模型的召回率迅速回升了3个百分点。从这个经验我得到一个通用结论在NLP项目里数据清洗带来的收益永远不会小于模型结构优化带来的收益。你与其花两周时间尝试新模型不如花两天时间把你的训练数据彻底清洗一遍。6.3 线上效果的意外翻车系统上线后的第一周我们监控发现“匹配率”指标比测试时低了大约6个百分点。分析后找到三个原因。第一个是用户输入的长度分布与测试集不同。测试集是知识库的标准问题改写平均长度25个token而线上真实用户问题平均长度只有15个token。短问题信息量少匹配难度天然更高。这个属于预期内偏差通过收集线上数据不断补充训练集可以逐步改善。第二个是方言和口语音译问题。有用户输入“哒咩”这种谐音网络词模型完全无法理解。这超出了金融问答的范畴我对这类输入做了兜底处理直接路由到人工客服。第三个是时效性相关问题。测试数据没有覆盖“今天白酒为什么大跌”这类带时效性的问题。知识库中根本没有对应条目模型只能硬匹配到最接近的“白酒板块行情分析”。这种结果在用户看来就是答非所问。我的处理方案是在匹配层之上增加了一个置信度门槛匹配分数低于阈值的请求不允许返回结果直接转人工。这个门槛同时解决了答非所问和负样本有限的问题。6.4 常见问题速查表现象可能原因解决方案训练loss不下降学习率过高或数据存在噪声降低学习率清洗训练数据验证集loss反弹过拟合或梯度累积不均启用早停调整batch size精确率高召回率低正样本噪声或阈值偏保守人工复核低置信度正样本线上效果低于测试输入分布差异收集线上数据持续迭代训练集推理延迟过高模型过大或未做优化蒸馏、量化、动态输入长度裁剪个别术语完全无法理解词表覆盖不足定制词表或追加领域预训练最后分享一点我的体会投资领域做NLP问答匹配有一个和通用领域完全不同的特点它需要你同时具备金融常识和自然语言处理能力两样缺一不可。没有金融常识你构造的对抗样本就缺乏杀伤力模型也就学不会真正的“辨别能力”没有NLP能力你只能停留在关键词检索的层面对复杂的语义变化毫无还手之力。这个项目做完之后我最深的一个感触是模型选型的收益远不如数据质量来得扎实。RoBERTa-wwm-ext-large相比base版本提升了不到2个百分点但我在数据清洗和对抗样本构造上投入的精力带来了超过6个百分点的提升。技术选型做到“够用就好”把更多的精力放在理解业务、理解数据上才是一个NLP项目走向成功的正道。如果你也想做类似的中文问答匹配项目我的建议是从小规模的蒸馏模型开始先把整个数据处理链路跑通再逐步迭代模型复杂度。这个方向的项目最怕一上来就上大模型结果数据质量跟不上白白浪费算力。希望这篇复盘对你有用。