
简介本资源是一个基于知识图谱构建的课程学习智能问答系统完整实现面向人工智能、自然语言处理方向的本科生与初学者解决课程知识碎片化、查询效率低、缺乏语义理解能力等学习痛点。压缩包共88个文件涵盖18个Python核心模块含BERT模型微调、Neo4j图数据库交互、前端Vue组件与后端Flask接口、7个CSV/JSON知识数据源、9组输入输出测试样例in/out/label以及配套README、配置文件和可视化界面资源整体仅121KB轻量易部署。已有77人学习下载适合开展大作业实践、知识图谱入门实验或智能问答系统二次开发。读者可直接复用完整前后端架构、获得从实体识别、关系抽取到图谱查询与自然语言回复的全流程代码尤其包含JointBERT联合标注模型、Neo4j CSV导入脚本及VueFlask联调结构显著降低知识图谱项目落地门槛。1. 这不是又一个“问答Demo”而是一套可跑通的课程知识图谱闭环系统你手头这份基于知识图谱的课程学习智能问答系统.zip表面看是学生大作业压缩包实际藏着一条从课程数据建模→Neo4j图谱构建→JointBERT语义理解→Flask后端服务→Vue前端交互的完整技术链。它不依赖外部API或云服务所有模块本地可复现原始课程数据存于_Data/neo4j_csv/下的 CSV 文件含course.csv,teacher.csv,prerequisite.csv等图谱用 Neo4j Desktop 4.4 即可一键导入后端用 Flask PyTorch 加载 JointBERT 模型做意图识别与槽位填充前端 Vue 3Vite 构建通过/api/query接口发起自然语言提问返回结构化答案并高亮图谱路径。适合正在做知识图谱课程设计、需要交出可演示系统的本科生也适合想快速验证 JointBERT 在教育领域泛化能力的工程师——它没做模型预训练但把实体链接、关系路径检索、答案生成三步拆解得足够清晰参数全在config.py和trainer.py里可调。2. 从课程CSV到Neo4j图谱数据建模与导入实操2.1 课程领域知识图谱的Schema设计逻辑该系统图谱聚焦高校课程教学场景核心实体类型包括Course课程、Teacher教师、Department院系、Prerequisite先修课、LearningObjective学习目标。关系类型严格对应教务逻辑TEACHES教师授课、BELONGS_TO课程归属院系、REQUIRES先修要求、COVERS覆盖知识点。这种设计规避了通用知识图谱常见的冗余边如sameAs、relatedTo所有关系均可在教务系统中直接查证。例如AlgorithmDesign课程节点通过REQUIRES边指向DataStructure节点而非模糊的has_prerequisite标签——这直接影响后续路径查询的准确率。提示不要直接运行neo4j_csv/import_all.sh。该脚本默认使用neo4j://localhost:7687若 Neo4j 未启用 Bolt 协议或密码未重置为neo4j会报AuthenticationFailure。建议先手动执行bin/cypher-shell -u neo4j -p neo4j测试连接。2.2 CSV数据清洗与Neo4j导入命令详解原始 CSV 存于_Data/neo4j_csv/但需预处理course.csv中credit字段含空格如 3 teacher.csv的email列存在非法字符amp;。必须先用 Python 清洗# _Data/neo4j_csv/clean_csv.py import pandas as pd import re for file in [course.csv, teacher.csv, prerequisite.csv]: df pd.read_csv(f{file}, encodingutf-8) # 清理 credit 字段空格 if credit in df.columns: df[credit] df[credit].astype(str).str.strip().replace(, 0).astype(int) # 解码 HTML 实体 if email in df.columns: df[email] df[email].apply(lambda x: re.sub(ramp;, , str(x))) df.to_csv(fcleaned_{file}, indexFalse, encodingutf-8)清洗后执行 Neo4j 导入以course.csv为例# 在 Neo4j Desktop 的 Graph Apps → Cypher Shell 中执行 CREATE CONSTRAINT ON (c:Course) ASSERT c.code IS UNIQUE; USING PERIODIC COMMIT 500 LOAD CSV WITH HEADERS FROM file:///cleaned_course.csv AS row CREATE (:Course { code: row.code, name: row.name, credit: toInteger(row.credit), description: row.description });2.2.1 关键参数说明USING PERIODIC COMMIT 500每 500 行提交一次事务避免内存溢出。若 CSV 行数超 10 万需调至1000。toInteger(row.credit)强制类型转换否则 Neo4j 将credit存为字符串后续MATCH (c:Course) WHERE c.credit 2查询失效。ASSERT c.code IS UNIQUE课程代码如CS201作为主键防止重复导入导致图谱污染。导入全部实体后用以下 Cypher 验证图谱连通性// 检查是否存在“数据结构”课程及其先修关系 MATCH (c:Course {name: 数据结构})-[:REQUIRES]-(p:Course) RETURN c.name, p.name // 返回[数据结构, 程序设计基础] 表示成功若返回空检查prerequisite.csv中source_code和target_code是否与course.csv的code完全一致大小写、空格均敏感。3. JointBERT 模型实现意图识别与实体链接双任务训练3.1 为什么选 JointBERT 而非纯BERT或BiLSTM该系统在model/joint_bert.py中实现 JointBERT核心是共享 BERT 编码层上接两个并行分类头Intent Classifier预测用户问题意图course_info,prerequisite_query,teacher_contact共 7 类Slot Filler标注实体位置如 “《机器学习》的先修课是什么” 中机器学习→course_name槽位相比单任务 BERTJointBERT 利用意图与槽位的强相关性如prerequisite_query意图必然触发course_name槽位在小样本下 F1 值提升 12.3%见trainer.py中eval_results日志。而 BiLSTM 因无法捕获长距离依赖在“《操作系统》和《计算机网络》的共同先修课”这类多实体问题上准确率不足 65%。3.2 模型训练与推理关键代码解析训练入口在main.py关键参数配置如下# main.py 第 42 行 args { model_name_or_path: bert-base-chinese, # 中文BERT基础版非微调版本 max_seq_length: 128, # 课程名通常≤20字128足够覆盖问题上下文 per_gpu_train_batch_size: 16, # GTX 1080 Ti 可跑满显存占用≈3.2GB num_train_epochs: 5, # 课程领域数据量小仅 1200 条标注样本5轮足够收敛 learning_rate: 5e-5, # BERT 微调标准学习率过高易过拟合 do_train: True, do_eval: True, output_dir: ./output/joint_bert, }3.2.1 数据预处理逻辑data_loader.pydata_loader.py将原始data/train.json转为 JointBERT 输入格式// data/train.json 片段 { text: 《数据库原理》的授课老师是谁, intent: teacher_query, slots: [database_principle, teacher_name] }预处理后生成input_ids,attention_mask,token_type_ids,intent_label_id,slot_labels五元组。其中slot_labels是长度为 128 的列表值为O非实体、B-COURSE_NAME实体开始、I-COURSE_NAME实体延续——这是 BIO 标注法trainer.py中compute_metrics函数据此计算 slot F1。3.3 模型推理时的实体链接优化query.py中link_entities_to_kg()函数解决 NER 结果与图谱节点的映射问题。例如用户问 “编译原理的老师”NER 输出compiler_principle但图谱中课程节点code为CS302。该函数执行# query.py 第 87 行 def link_entities_to_kg(entity_text, entity_type): if entity_type course_name: # 模糊匹配 course.name 或 course.code query MATCH (c:Course) WHERE toLower(c.name) CONTAINS toLower($text) OR c.code $text RETURN c.code AS code, c.name AS name LIMIT 1 result graph.run(query, textentity_text).data() return result[0][code] if result else None注意CONTAINS是 Neo4j 的子串匹配比STARTS WITH更鲁棒支持“编译”匹配“编译原理”但需确保c.name已建立全文索引CALL db.index.fulltext.createNodeIndex(courseNameIndex, [Course], [name])。4. Flask Vue 前后端联调接口设计与跨域调试4.1 Flask 后端 API 设计原则Backend_Flask/app.py定义/api/query为唯一入口采用 POST 方法接收 JSON{ question: 《人工智能导论》的先修课有哪些 }返回结构化结果{ answer: 《人工智能导论》需要先修《程序设计基础》和《离散数学》。, graph_path: [ { node: CS401, type: Course, label: 人工智能导论 }, { relation: REQUIRES, direction: out }, { node: CS101, type: Course, label: 程序设计基础 } ], intent: prerequisite_query }app.py中关键中间件配置# Backend_Flask/app.py 第 22 行 from flask_cors import CORS CORS(app, resources{r/api/*: {origins: [http://localhost:5173]}}) app.route(/api/query, methods[POST]) def handle_query(): try: question request.json.get(question, ).strip() if not question: return jsonify({error: Question cannot be empty}), 400 # 调用 model.predict() 获取 answer/graph_path result model.predict(question) return jsonify(result) except Exception as e: app.logger.error(fQuery failed: {str(e)}) return jsonify({error: Internal server error}), 5004.1.1 跨域配置细节origins显式指定http://localhost:5173Vite 默认端口禁止使用*否则 Vue 生产环境部署时会因credentials冲突失败。resources{r/api/*}确保仅/api/路径开放跨域其他路径如/static/保持安全。4.2 Vue 前端请求与图谱可视化Frontend_Vue/src/views/QueryView.vue使用axios发起请求// Frontend_Vue/src/views/QueryView.vue 第 63 行 const sendQuery async () { if (!question.trim()) return; loading.value true; try { const res await axios.post(http://localhost:5000/api/query, { question: question.value }, { headers: { Content-Type: application/json } }); // res.data.graph_path 直接传给 GraphVisualizer / graphData.value res.data.graph_path; answer.value res.data.answer; } catch (err) { errorMsg.value err.response?.data?.error || Network error; } finally { loading.value false; } };图谱可视化组件GraphVisualizer.vue使用vue3-force-graph非 D3 原生降低学习成本!-- Frontend_Vue/src/components/GraphVisualizer.vue -- template ForceGraph2D :nodesnodes :linkslinks :node-labelnode node.label :link-labellink link.relation / /template script setup const props defineProps([graphPath]); const nodes computed(() { return props.graphPath.filter(item item.node) .map((item, i) ({ id: i, label: item.label, type: item.type })); }); const links computed(() { const edges []; for (let i 0; i props.graphPath.length - 2; i 2) { if (props.graphPath[i].node props.graphPath[i 2]?.node) { edges.push({ source: i, target: i 2, relation: props.graphPath[i 1].relation }); } } return edges; }); /script提示若图谱节点重叠调整ForceGraph2D的d3AlphaDecay参数默认 0.0228至0.015可增强布局稳定性。5. 知识图谱问答的三大典型问题与修复方案5.1 问题Neo4j 查询超时MATCH (c:Course)-[:REQUIRES*1..3]-(p:Course)返回空根因*1..3表示 1 到 3 跳路径但课程先修关系通常为 1 跳直接先修2 跳以上属间接依赖如 A→B→C则 C 的先修课是 B非 A。原查询试图找“所有间接先修课”但图谱中未构建INDIRECTLY_REQUIRES关系。修复方案修改query.py中路径查询逻辑限定为 1 跳# query.py 第 135 行替换原 Cypher # 错误写法超时且语义错误 # MATCH (c:Course)-[:REQUIRES*1..3]-(p:Course) WHERE c.code $code RETURN p # 正确写法 query MATCH (c:Course)-[:REQUIRES]-(p:Course) WHERE c.code $code RETURN p.code AS code, p.name AS name 5.2 问题JointBERT 对同义词泛化差如“授课老师”识别为teacher_contact但“任课教师”识别失败根因训练数据中teacher_contact意图样本仅含“授课老师”“谁教”等 3 种表达未覆盖教务术语变体。修复方案在data/train.json中扩充同义词样本至少 15 条并重新训练// 新增样本 { text: 《软件工程》的任课教师信息, intent: teacher_contact, slots: [software_engineering, teacher_name] }, { text: 这门课由哪位老师负责讲授, intent: teacher_contact, slots: [current_course, teacher_name] }注意current_course是动态槽位需在data_loader.py的get_slot_labels()中添加映射规则将“这门课”绑定到当前上下文课程。5.3 问题Vue 页面首次加载时图谱渲染空白控制台报Cannot read properties of undefined (reading length)根因GraphVisualizer.vue的graphPathprops 初始为null但computed函数未做空值校验props.graphPath.filter()抛出异常。修复方案增加防御性编程!-- GraphVisualizer.vue -- script setup const props defineProps({ graphPath: { type: Array, default: () [] } // 默认空数组非 null }); const nodes computed(() { if (!props.graphPath || props.graphPath.length 0) return []; return props.graphPath.filter(item item.node) .map((item, i) ({ id: i, label: item.label, type: item.type })); }); /script最终效果输入“《数据库系统概论》的授课老师”前端显示课程节点、教师节点及TEACHES关系边并高亮教师邮箱字段——整个流程从数据导入到界面响应全程无需修改一行外部依赖所有参数、路径、端口均在项目内明确声明可直接用于课程答辩演示。本文还有配套的精品资源点击获取