热门搜索:和平精英 原神 街篮2 

您的位置:首页 > > 教程攻略 > ai资讯 >【LLM & RAG 】大模型在知识图谱问答上的核心算法详细思路及实践

【LLM & RAG 】大模型在知识图谱问答上的核心算法详细思路及实践

来源:互联网 更新时间:2026-08-24 14:38

前言

这篇文章要聊的是一个融合了RAG思路的KBQA系统——从知识图谱里找答案的一套完整算法和实现步骤。KBQA系统的目标很明确:让机器理解自然语言问题,然后去知识图谱里提取、生成精确答案。整个流程拆解下来,涉及几个关键环节:

mention识别、实体链接及排序、属性选择及排序、文本拼接,最后是Text2SQL生成

。每一步都环环相扣,从问题中先锁定实体和属性,再拼成查询语句,最终从知识库或数据库里拿到结果。下面就把每个步骤的实现思路和技术细节拆开来讲,核心代码和开源地址也会一并给出。

【LLM & RAG 】大模型在知识图谱问答上的核心算法详细思路及实践

一、mention识别

在KBQA里,mention识别的任务就是:从用户的问题中找出那些能对应到知识库实体的词或短语。这一步到底有多重要?可以说,它是整个系统的起点——如果mention识别不准,后面的实体链接、关系抽取、答案生成全都会跟着跑偏。

目前主流的方法分几类:

  1. 规则方法

    手工设计规则,比如用NER工具、正则表达式、词典匹配来定位实体。

    • NER工具:直接用Stanford NER、spaCy这类工具识别问题中的实体。
    • 正则表达式:写特定的模式匹配特定类型的mention。
    • 词典匹配:预先建好实体词典,查找匹配。
  2. 统计方法

    利用训练数据的统计特征,比如n-gram分析和词频统计,根据匹配情况判断mention。

    • n-gram分析:把问题切成n-gram(单词、双词短语等),看每个片段在知识库中的匹配度。
    • 词频统计:计算词或短语在知识库中的出现频率,频率高则更可能是mention。
  3. 机器学习方法

    用有标签数据训练分类器,比如SVM、随机森林、逻辑回归,需要做特征工程(词性、词向量、上下文等)。

  4. 深度学习方法

    用神经网络做端到端识别,比如BERT-CRF、LLM等,省去人工特征工程,靠大量数据自动学习特征。

本文采用的方法是大模型+规则。核心流程如下:

mention识别SFT数据构造

原始数据长这样:

q1:莫妮卡·贝鲁奇的代表作?
select ?x where { <莫妮卡·贝鲁奇> <代表作品> ?x. }
<西西里的美丽传说>

通过规则构造SFT训练数据:

[
{
"instruction": "你是一个实体抽取的专家,请你抽取问句:“莫妮卡·贝鲁奇的代表作?”中的实体。",
"input": "",
"output": "莫妮卡·贝鲁奇"
},
{
"instruction": "你是一个实体抽取的专家,请你抽取问句:“《湖上草》是谁的诗?”中的实体。",
"input": "",
"output": "湖上草"
},
...
]

LLM微调mention识别

使用LLaMA-Factory框架微调,微调脚本如下:

import json
import os

model_name_or_path = "ZhipuAI/glm-4-9b-chat"
template = "glm4"
cutoff_len = 256
num_train_epochs = 8
train_dataset = "train_ner"
predict_dataset = "test_ner"
output_dir = f"sa ves/{train_dataset}-{predict_dataset}-ep{num_train_epochs}-{cutoff_len}-{template}"
adapter_name_or_path = output_dir


do_train = True
do_predict = True

train_args = dict(
stage="sft", # 进行指令监督微调
do_train=do_train,
model_name_or_path=model_name_or_path,
dataset=train_dataset,
template=template,
finetuning_type="lora",
cutoff_len=cutoff_len,
lora_target="all",
output_dir=output_dir,
per_device_train_batch_size=4,
gradient_accumulation_steps=2,
lr_scheduler_type="cosine",
logging_steps=10,
warmup_ratio=0.1,
sa ve_steps=1000,
learning_rate=1e-4,
num_train_epochs=num_train_epochs,
max_samples=7625,
max_grad_norm=1.0,
fp16=True,
temperature=0.1,
ddp_timeout=180000000,
overwrite_cache=True,
overwrite_output_dir=True
)

predict_args = dict(
stage="sft",
do_predict=do_predict,
model_name_or_path=model_name_or_path,
adapter_name_or_path=adapter_name_or_path,
dataset=predict_dataset,
template=template,
finetuning_type="lora",
cutoff_len=cutoff_len,
per_device_eval_batch_size=2,
overwrite_cache=True,
preprocessing_num_workers=16,
output_dir=f'{output_dir}/predict',
overwrite_output_dir=True,
ddp_timeout=180000000,
temperature=0.1,
max_samples=1292,
predict_with_generate=True
)

train_args_file = f"config/{train_dataset}-{predict_dataset}-ep{num_train_epochs}-{cutoff_len}-{template}-train.json"
predict_args_file = f"config/{train_dataset}-{predict_dataset}-ep{num_train_epochs}-{cutoff_len}-{template}-pred.json"

json.dump(train_args, open(train_args_file, "w", encoding="utf-8"), indent=2)
json.dump(predict_args, open(predict_args_file, "w", encoding="utf-8"), indent=2)


if __name__ == '__main__':
os.system(f'llamafactory-cli train {train_args_file}')
os.system(f'llamafactory-cli train {predict_args_file}')

输出示例:

question:<篝火圆舞曲>的作曲家属于什么民族?

mention:篝火圆舞曲

二、实体链接及实体排序

中文短文本的实体链指(Entity Linking,EL),就是把文本中的实体和知识库里的实体关联起来。常见的场景比如搜索Query、微博、对话标题等。中文短文本做这件事挑战不小:口语化严重导致歧义多,上下文又不够丰富,再加上中文本身的特点——相比英文更加复杂。

EL实现思路——基于“粗排-精排”的两阶段方案

思路1:

整体流程分四步:

  1. 候选实体召回

    。通过ES知识库,建立“实体指称-实体id”和“实体id-实体信息”的映射。从原文本的mention出发,根据指称匹配召回候选。
  2. 候选实体特征提取

    。先用指称项分类模型预测实体类型,对每个召回的实体,提取其信息,与原始文本拼接,再加上实体类型,构成完整的候选集合。没有召回的实体就不需要排序了,直接和排序结果合并。
  3. 候选实体排序模型

    。输入是标记了指称项的原始文本+候选实体信息,输出匹配程度分数。
  4. 后处理

    。对排序结果做最终整合。

粗排用ES库就够了,精排则是一个二分类模型。训练数据构造(

正负样本比例1:5

):

{"query": "莫妮卡·贝鲁奇的代表作?", "query_rewrite": "#莫妮卡·贝鲁奇#的代表作?", "entity": "<莫妮卡·贝鲁奇>", "desc": "母亲|毕业院校|类型|主演|别名|相关人物|中文名|国籍|作者|外文名|体重|职业|代表作品|出生日期|导演|身高|朋友", "label": 1}
{"query": "莫妮卡·贝鲁奇的代表作?", "query_rewrite": "#莫妮卡·贝鲁奇#的代表作?", "entity": ""莫妮卡·贝鲁" ", "desc": "中文名", "label": 0}
{"query": "莫妮卡·贝鲁奇的代表作?", "query_rewrite": "#莫妮卡·贝鲁奇#的代表作?", "entity": "<莫妮卡·贝鲁>", "desc": "类型|游戏大小|中文名|原版名称|游戏类型", "label": 0}
{"query": "莫妮卡·贝鲁奇的代表作?", "query_rewrite": "#莫妮卡·贝鲁奇#的代表作?", "entity": ""莫妮卡贝鲁齐" ", "desc": "中文名", "label": 0}
{"query": "莫妮卡·贝鲁奇的代表作?", "query_rewrite": "#莫妮卡·贝鲁奇#的代表作?", "entity": "<莫妮卡贝鲁齐>", "desc": "类型|操作指南|基本介绍|中文名|原版名称", "label": 0}
{"query": "莫妮卡·贝鲁奇的代表作?", "query_rewrite": "#莫妮卡·贝鲁奇#的代表作?", "entity": ""莫妮卡·安娜·玛丽亚·贝鲁奇" ", "desc": "中文名", "label": 0}

字段说明:

  • query:原始问句
  • query_rewrite:重写后的问句(用#标记mention位置)
  • entity:链接到的实体
  • desc:该实体所有属性的拼接
  • label:1为正样本,0为负

训练时拼接query_rewritedesc

query_rewrite[SEP]desc
示例:#莫妮卡·贝鲁奇#的代表作?[SEP]母亲|毕业院校|类型|主演|别名|相关人物|中文名|国籍|作者|外文名|体重|职业|代表作品|出生日期|导演|身高|朋友

精排模型结构如下:

计算实体链接得分:

思路2:

粗排依然用ES召回,精排模型结构和思路1一样,区别在于数据构造方式。训练数据(

正负样本比例1:5

):

{'query': '莫妮卡·贝鲁奇的代表作?', 'mention': '莫妮卡·贝鲁奇', 'label': 1}
{'query': '莫妮卡·贝鲁奇的代表作?','mention': '低钙血症', 'label': 0}
{'query': '莫妮卡·贝鲁奇的代表作?', 'mention': '同居损友', 'label': 0}
{'query': '莫妮卡·贝鲁奇的代表作?','mention': '"1964-09-22"', 'label': 0}
{'query': '莫妮卡·贝鲁奇的代表作?', 'mention': '夏侯瑾轩', 'label': 0}
{'query': '莫妮卡·贝鲁奇的代表作?', 'mention': '"日历"', 'label': 0}

拼接方式:

query[SEP]mention
示例:莫妮卡·贝鲁奇的代表作?[SEP]莫妮卡·贝鲁奇

计算得分:

为了减少噪声,最后取

top5的链接实体

作为候选。

模型结构代码示例

import torch
from torch import nn
from transformers import BertModel, BertPreTrainedModel

class BertForSequenceClassification(BertPreTrainedModel):
def __init__(self, config):
super().__init__(config)
self.num_labels = config.num_labels

self.bert = BertModel(config)
self.dropout = nn.Dropout(config.hidden_dropout_prob)
self.classifier = nn.Linear(config.hidden_size, config.num_labels)

self.init_weights()

def forward(self, input_ids=None, attention_mask=None, token_type_ids=None, position_ids=None, head_mask=None, inputs_embeds=None, labels=None):
outputs = self.bert(
input_ids,
attention_mask=attention_mask,
token_type_ids=token_type_ids,
position_ids=position_ids,
head_mask=head_mask,
inputs_embeds=inputs_embeds,
)

pooled_output = outputs[1]
pooled_output = self.dropout(pooled_output)
logits = self.classifier(pooled_output)

loss = None
if labels is not None:
if self.num_labels == 1:
# We are doing regression
loss_fct = nn.MSELoss()
loss = loss_fct(logits.view(-1), labels.view(-1))
else:
loss_fct = nn.CrossEntropyLoss()
loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))

output = (logits,) + outputs[2:]
return ((loss,) + output) if loss is not None else output

思路3:

直接用向量模型做短文本匹配,不微调也能用,配合ES做实体链接:

from sentence_transformers import SentenceTransformer
sentences_1 = "实体"
sentences_2 = ["es召回的实体1", "es召回的实体2",...,"es召回的实体n"]
model = SentenceTransformer('lier007/xiaobu-embedding-v2')
embeddings_1 = model.encode(sentences_1, normalize_embeddings=True)
embeddings_2 = model.encode(sentences_2, normalize_embeddings=True)
similarity = embeddings_1 @ embeddings_2.T
print(similarity)

当然,如果想效果更好,可以根据自己的场景做微调。

三、属性选择及属性排序

得到top5的候选实体后,需要从知识图谱中召回这些实体对应的所有属性集合。但问题来了——一次性召回出来的属性太多了,很多和当前问题根本不相关。因此需要训练一个属性排序模型,从中选出最相关的

TOP5个属性

属性排序训练数据构造(

正负样本比例1:5

):

{"entity": "<莫妮卡·贝鲁奇>", "query": "莫妮卡·贝鲁奇的代表作?", "attr": "<代表作品>", "label": 1}
{"entity": "<莫妮卡·贝鲁奇>", "query": "莫妮卡·贝鲁奇的代表作?", "attr": "<体重>", "label": 0}
{"entity": "<莫妮卡·贝鲁奇>", "query": "莫妮卡·贝鲁奇的代表作?", "attr": "<出生日期>", "label": 0}
{"entity": "<莫妮卡·贝鲁奇>", "query": "莫妮卡·贝鲁奇的代表作?", "attr": "<导演>", "label": 0}
{"entity": "<莫妮卡·贝鲁奇>", "query": "莫妮卡·贝鲁奇的代表作?", "attr": "<职业>", "label": 0}
{"entity": "<莫妮卡·贝鲁奇>", "query": "莫妮卡·贝鲁奇的代表作?", "attr": "<类型>", "label": 0}

拼接queryattr

query[SEP]attr
示例:莫妮卡·贝鲁奇的代表作?[SEP]<代表作品>

计算属性得分:

同样取top5属性作为候选。

属性排序模型就是BERT+Linear的二分类模型:

四、文本拼接

本文的核心思路和RAG很像——通过前面的流程检索到相关文本片段(实体片段、属性片段),然后把它们拼起来。拼接方式如下:

prompt+question+候选实体+属性结合

五、LLM for Text2SQL

最后一步,用大语言模型生成SQL查询。Text2SQL就是把自然语言问题转换成结构化的查询语句,以便从数据库或知识图谱中取数。

LLM微调的SFT数据构造示例:

[
{
"instruction": "你是一个Sparql生成专家,请根据给定的内容,生成Sparql语句。n问题:“莫妮卡·贝鲁奇的代表作?”,和候选实体信息:[0]名称:<莫妮卡·贝鲁奇>,属性集:<代表作品>,<中文名>,<作者>,<外文名>,<别名>。对应查询图谱的Sparql的语句为:",
"input": "",
"output": "select ?x where { <莫妮卡·贝鲁奇> <代表作品> ?x. }"
},
{
"instruction": "你是一个Sparql生成专家,请根据给定的内容,生成Sparql语句。n问题:“《湖上草》是谁的诗?”,和候选实体信息:[0]名称:<湖上草>,属性集:<主要作品>,<中文名>,<传世之作>,<所著>,<其丈夫>。对应查询图谱的Sparql的语句为:",
"input": "",
"output": "select ?x where { ?x <主要作品> <湖上草>. }"
},
...
]

使用预训练的大语言模型(比如GLM-4-9B)进行微调,让它学会生成正确的SQL查询

(这里实际用的是SPARQL,配合gstore图数据库——不过gstore问题比较多,个人不太推荐,可以换成neo4j等其他查询语言)。同样用LLaMA-Factory框架微调:

import json
import os

model_name_or_path = "ZhipuAI/glm-4-9b-chat"
template = "glm4"
cutoff_len = 4096
num_train_epochs = 8
train_dataset = "train_data"
predict_dataset = "test_data"
output_dir = f"sa ves/{train_dataset}-{predict_dataset}-ep{num_train_epochs}-{cutoff_len}-{template}"
adapter_name_or_path = output_dir


do_train = True
do_predict = True

train_args = dict(
stage="sft",
do_train=do_train,
model_name_or_path=model_name_or_path,
dataset=train_dataset,
template=template,
finetuning_type="lora",
cutoff_len=cutoff_len,
lora_target="all",
output_dir=output_dir,
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
lr_scheduler_type="cosine",
logging_steps=10,
warmup_ratio=0.1,
sa ve_steps=1000,
learning_rate=1e-4,
num_train_epochs=num_train_epochs,
max_samples=7625,
max_grad_norm=1.0,
fp16=True,
temperature=0.1,
ddp_timeout=180000000,
overwrite_cache=True,
overwrite_output_dir=True
)

predict_args = dict(
stage="sft",
do_predict=do_predict,
model_name_or_path=model_name_or_path,
adapter_name_or_path=adapter_name_or_path,
dataset=predict_dataset,
template=template,
finetuning_type="lora",
cutoff_len=cutoff_len,
per_device_eval_batch_size=1,
overwrite_cache=True,
preprocessing_num_workers=16,
output_dir=f'{output_dir}/predict',
overwrite_output_dir=True,
ddp_timeout=180000000,
temperature=0.1,
max_samples=1292,
predict_with_generate=True
)

train_args_file = f"config/{train_dataset}-{predict_dataset}-ep{num_train_epochs}-{cutoff_len}-{template}-train.json"
predict_args_file = f"config/{train_dataset}-{predict_dataset}-ep{num_train_epochs}-{cutoff_len}-{template}-pred.json"

json.dump(train_args, open(train_args_file, "w", encoding="utf-8"), indent=2)
json.dump(predict_args, open(predict_args_file, "w", encoding="utf-8"), indent=2)

os.system(f'llamafactory-cli train {train_args_file}')
os.system(f'llamafactory-cli train {predict_args_file}')

小结

  • 优点

    :借助大模型的能力,把自然语言转成SPARQL查询语句,无论是单跳还是多跳问题都能从知识图谱中查到答案。
  • 缺点

    :实践过程中发现,大模型的幻觉问题会造成生成的查询语句“看上去对,实际上错”,最终拿到的答案不准确。这个问题需要特别注意。

总结

这篇东西完整梳理了一个融合RAG思路的KBQA系统,核心步骤包括:

首先做mention识别,用大模型提取问题中的关键实体;然后做实体链接,把mention和知识图谱里的实体匹配上;接着对所有候选实体排序,选出最相关的;再对属性做选择和排序,确保只保留最贴合的属性;之后把上述信息拼成完整的上下文;最后交给大模型,把结构化文本转成SQL查询

,从而从知识图谱或数据库里拿到答案。整个流程环环相扣,每一个环节的精度都会影响最终结果。

关于宇宙的好的网名有哪些
关于宇宙的好的网名有哪些

类型:角色扮演

大小:1

语言:简体中文

平台:互联网

游戏下载

手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc