ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于神经网络的法律智能问答系统:分类+匹配实战与避坑指南

基于神经网络的法律智能问答系统:分类+匹配实战与避坑指南 简介这份资源是面向高校学生与AI入门者的毕业设计及课程作业参考包主题为基于神经网络的法律智能问答系统属于自然语言处理与法律交叉领域的实践项目。包内共29个文件以csv法律语料、py源码、txt停用词与问答数据、pyc编译文件及model模型文件为主压缩包约37.47MB涵盖数据、代码与模型三类核心内容。资源围绕需求分析、系统设计、模型训练与性能评估展开包含劳动法、工伤保险、员工权益等主题数据以及分类模型、相似度匹配与图形界面等实现模块可帮助读者理解从法律知识库构建到问答交互的完整链路。目前已有139人学习下载适合需要完成毕设、课程设计或学习NLP落地应用的同学参考便于快速把握项目结构、复用数据处理思路并对照实现自己的问答系统。1. 从一份法律问答源码包说起它到底能不能跑起来很多同学做毕设时都会遇到一个尴尬想找一个能跑通的法律智能问答系统结果要么是只有论文没有代码要么是代码里缺数据、缺模型、缺依赖最后只能自己从头造轮子。这份「基于神经网络的法律智能问答系统.zip」至少把数据、模型、代码三样都塞进去了目录里能看到classify.model、wmd_process.py、match.py、myLawChat.py、gui.py这些文件还有劳动合同、工伤事故、员工权益、辞退解雇等分类语料。它解决的不是「从零训练一个法律大模型」这种重活而是「给定一个法律问题先分类到具体领域再从候选回答里匹配最相关的一条」这条轻量链路。适合谁做课程作业、毕设、想快速搭一个能演示的法律问答原型的同学以及想拆解「分类检索」这套经典 NLP 组合拳的工程师。它不依赖 GPU 集群普通笔记本就能跑但前提是你得把环境和数据路径理顺。2. 拆开压缩包数据、模型、代码三块怎么对上2.1 数据文件的分工与字段含义先看数据层。压缩包里有一批.csv和.txt名字很直白劳动合同.csv、工伤事故.csv、员工权益.csv、辞退解雇.csv、劳动保险.csv、维权方式.csv这些是分类语料每个文件对应一个法律子领域。questions_train.csv是训练问题集回答数据1.txt和问题数据1.txt是问答对原始文本。停用词表有两份baidu_stopwords.txt和qs_stopwords.txt前者是通用中文停用词后者更偏向问答场景。关键词-劳动法.csv、关键词-工伤保险条例.csv、关键词-问题数据.csv是关键词映射表用来做规则兜底或特征增强。常见做法是把每个分类 csv 读进来给每条问题打上领域标签合并成一个大训练集。字段一般就是「问题文本 标签」两列但不同文件列名可能不统一需要先做列名对齐。我一般会先跑一段探查脚本确认每个文件的列数和前几行内容避免后面训练时标签错位。import pandas as pd import os data_dir ./data for f in os.listdir(data_dir): if f.endswith(.csv): df pd.read_csv(os.path.join(data_dir, f)) print(f, df.shape, list(df.columns)[:5]) print(df.head(2)) print(- * 40)这段代码做的是数据摸底打印每个 csv 的形状、列名和前两行。参数上data_dir指向你解压后放数据的目录df.shape看样本量list(df.columns)[:5]只看前五列避免刷屏。如果某个文件列名是question而另一个是问题后面合并时就得统一重命名。这一步不做训练时很容易出现「标签列被当成特征」的翻车。2.2 模型文件与代码模块的调用关系classify.model是已经训练好的分类模型大概率是 sklearn 的持久化对象joblib 或 pickle。train.py是训练脚本classiry_similarity2.py和match.py负责相似度计算与匹配wmd_process.py做词移距离Word Movers Distance相关的文本处理myLawChat.py是问答主逻辑gui.py是图形界面入口。__pycache__里是编译缓存wmd_process.cpython-38.pyc说明原环境是 Python 3.8。调用链一般是gui.py接收用户输入 →myLawChat.py调用分类模型判断领域 →match.py在该领域的候选回答里做相似度匹配 → 返回最相似的答案。wmd_process.py可能被match.py引用用来算更精细的语义距离。你要做的第一件事是确认classify.model能不能被当前环境加载因为 sklearn 版本差异会导致InconsistentVersionWarning甚至直接报错。import joblib model joblib.load(./model/classify.model) print(type(model)) print(model.classes_ if hasattr(model, classes_) else no classes attr)逻辑说明用joblib.load加载模型打印类型和类别列表。如果报ModuleNotFoundError说明训练时用了你没装的库如果报版本警告先别急着忽略后面预测结果可能整体偏移。参数上路径要换成你实际存放classify.model的位置。这一步过了才说明模型层是通的。2.3 环境依赖与 Python 版本对齐原包里有.cpython-38.pyc说明作者用的是 Python 3.8。如果你用 3.10 或 3.11部分老库可能装不上。常见依赖包括scikit-learn、jieba、gensim、numpy、pandas、tkintergui 用。gensim的版本尤其敏感Word2Vec 和 WMD 相关 API 在不同版本间有变动。我一般会先建一个 3.8 的虚拟环境再按报错逐个补库。不要一上来就pip install -r requirements.txt因为这个包里不一定有 requirements 文件。更稳的做法是先跑python gui.py看第一个ModuleNotFoundError是什么装什么循环几次就能把依赖补齐。conda create -n lawqa python3.8 -y conda activate lawqa pip install scikit-learn pandas numpy jieba gensim python gui.py这段命令创建并激活 3.8 环境安装核心库后直接启动界面。如果gui.py报缺少tkinter在 conda 环境下一般自带如果是系统 Python可能需要单独装python3-tk。参数上环境名lawqa可以换成你习惯的。跑通界面之前不要急着改代码先让原始状态能启动这样出问题才知道是环境还是逻辑。3. 把分类模型跑通从训练脚本到预测接口3.1 train.py 里的特征工程与模型选型打开train.py重点看三件事文本怎么向量化、用了什么分类器、标签怎么编码。法律问答的文本通常短、术语多常见做法是 TF-IDF 加线性分类器如 LinearSVC 或 LogisticRegression也有用朴素贝叶斯的。如果作者用了jieba分词加 TF-IDF那classify.model很可能是一个 Pipeline里面包含分词器、向量化器和分类器。你要确认的是训练时的分词方式必须和预测时一致。如果训练用了jieba.lcut预测时却直接按字符切准确率会掉得很难看。另外标签编码如果是LabelEncoder预测出来的是数字需要inverse_transform才能变成「劳动合同」这种可读标签。import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.pipeline import Pipeline from sklearn.svm import LinearSVC def cut(text): return .join(jieba.lcut(text)) pipeline Pipeline([ (tfidf, TfidfVectorizer(tokenizerlambda x: x.split(), preprocessorcut)), (clf, LinearSVC()) ])这段是示意性重建不是原脚本的复制。逻辑上cut函数把句子切成空格分隔的词串TfidfVectorizer的tokenizer按空格切preprocessor先做分词。参数上LinearSVC的C值默认 1.0法律短文本可以试 0.5 到 2.0 之间。如果你发现原模型预测时总是偏向某个大类可能是类别不平衡需要在训练时加class_weightbalanced。3.2 加载 classify.model 并做单条预测模型加载后先拿几条已知领域的问题做验证。比如「工伤认定需要哪些材料」应该分到工伤事故「被辞退怎么赔偿」应该分到辞退解雇。如果分错了先别改模型先检查输入文本是否经过了和训练一致的分词处理。import joblib model joblib.load(./model/classify.model) tests [ 工伤认定需要哪些材料, 被辞退怎么赔偿, 劳动合同到期不续签有补偿吗 ] for q in tests: pred model.predict([q])[0] print(q, -, pred)逻辑说明model.predict接收一个列表返回预测标签数组。如果classify.model是 Pipeline它会自动走完分词和向量化。参数上输入必须是字符串列表不能直接传单个字符串。如果报ValueError: Expected 2D array说明模型不是 Pipeline需要你手动做向量化。这一步的输出直接决定后面匹配的范围分错领域后面匹配再准也是白搭。3.3 把预测结果接到 match.py 的候选集分类拿到领域标签后match.py需要从对应领域的回答库里取候选。常见做法是每个领域一个回答列表用 TF-IDF 或 WMD 算用户问题和每个候选回答的相似度取最高分返回。wmd_process.py里的 WMD 需要预训练词向量如果包里没带词向量文件WMD 可能跑不起来这时候要退回到 TF-IDF 或余弦相似度。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity def match_answer(query, candidates): vectorizer TfidfVectorizer() tfidf vectorizer.fit_transform([query] candidates) sims cosine_similarity(tfidf[0:1], tfidf[1:]).flatten() idx sims.argmax() return candidates[idx], sims[idx]逻辑说明把 query 和所有候选拼在一起做 TF-IDF然后算 query 与每个候选的余弦相似度取最大。参数上fit_transform每次调用都会重建词表如果候选集很大建议提前把 vectorizer 拟合好并保存。如果wmd_process.py报缺少word2vec模型就先用这个 TF-IDF 方案顶上保证系统能跑通再考虑补词向量。4. 避坑与排查跑不起来时先看这几条4.1 现象加载 classify.model 报版本不兼容原因训练时用的 sklearn 版本和你当前环境不一致pickle 反序列化时属性对不上。解决先pip show scikit-learn看版本尝试降到训练时的版本常见是 0.24 或 1.0 附近。如果降版本影响其他库就用joblib.load时加mmap_modeNone并捕获警告但更稳的是重建模型——用train.py重新训一个反而比修兼容性快。4.2 现象gui.py 启动后界面空白或闪退原因tkinter主循环里某个回调抛异常被吞掉或者数据路径写死成作者本机路径。解决在gui.py的按钮回调里加try/except并打印异常同时全局搜索C:\Users或/home/这类硬编码路径改成相对路径。常见做法是把数据目录统一成./data模型目录统一成./model。4.3 现象分类结果全是同一个标签原因输入文本没有做分词或者 TF-IDF 的token_pattern把中文全过滤了。解决检查TfidfVectorizer是否设置了tokenizer或token_patternr(?u)\b\w\b。中文默认按字切也能用但效果差。更隐蔽的原因是标签编码错位LabelEncoder的classes_顺序和你想的不一样打印出来核对。4.4 现象WMD 计算极慢或内存溢出原因WMD 需要加载词向量且计算复杂度随文本长度上升。解决限制候选回答数量先按关键词粗筛再算 WMD或者直接用 TF-IDF 余弦相似度替代。如果wmd_process.py里加载了GoogleNews-vectors-negative300.bin这类大文件确认文件是否真的在包里不在就换轻量词向量或放弃 WMD。4.5 现象回答匹配结果答非所问原因候选回答库太小或者相似度阈值没设。解决给相似度加一个下限低于阈值就返回「暂未找到相关回答」。另外检查回答数据1.txt的编码如果是 GBK 而用 UTF-8 读会乱码乱码文本算相似度必然不准。用chardet探测编码或者统一转成 UTF-8。5. 进阶技巧把分类置信度和匹配分数串起来用跑通之后我一般会做一件事把分类模型的置信度和匹配相似度拼成一个综合分而不是只看匹配分。因为分类错了匹配再高也是错的。LinearSVC没有predict_proba但可以用decision_function拿到决策分数做 softmax 归一化后当置信度。如果置信度低于某个阈值就直接走关键词兜底而不是硬分到一个领域。import numpy as np def softmax(x): e np.exp(x - np.max(x)) return e / e.sum() def predict_with_conf(model, query): pred model.predict([query])[0] if hasattr(model, decision_function): scores model.decision_function([query])[0] conf softmax(scores).max() else: conf 1.0 return pred, conf逻辑说明decision_function返回每个类别的分数softmax转成概率样式的置信度。参数上np.max(x)是为了数值稳定防止 exp 溢出。拿到conf后可以设conf 0.5时触发兜底逻辑比如用关键词-问题数据.csv做规则匹配。这个技巧不复杂但能明显减少「自信地答错」的情况。另一个实用技巧是给匹配阶段加缓存。法律问答里高频问题重复率不低用functools.lru_cache把 query 到答案的映射缓存起来第二次同样问题直接返回省掉分类和匹配的计算。from functools import lru_cache lru_cache(maxsize256) def cached_answer(query): label, conf predict_with_conf(model, query) if conf 0.5: return 暂未找到相关回答请换个说法 candidates load_candidates(label) ans, score match_answer(query, candidates) return ans if score 0.3 else 暂未找到相关回答请换个说法逻辑说明lru_cache缓存最近 256 个 query 的结果maxsize根据内存调整。load_candidates按标签加载对应领域的回答列表score 0.3是经验阈值可以按实际数据调。注意缓存对模型更新不友好改完模型要重启进程或清缓存。从那以后我每次拿到这种「分类匹配」的毕设包都强制先跑一遍数据摸底和模型加载验证再动任何代码。因为大部分跑不起来的问题根源都在路径、编码和版本这三件事上而不是算法本身。希望帮到你。本文还有配套的精品资源点击获取
返回列表