在这篇文章中,我们将讨论如何使用OpenAI的gpt-3.5-turbo从原始文本数据构建知识图谱。大型语言模型(LLMs)在文本生成和问答任务中表现优异。检索增强生成(RAG)进一步提升了它们的表现,使它们能够获取最新和特定领域的知识。我们的目标是利用大型语言模型作为信息提取的工具,将原始文本转换成易于查询的事实,从中获得有用见解。首先,我们需要定义几个关键概念。
什么是知识图?知识图谱是一种表示和连接现实世界实体的语义网络。这些实体通常涉及人、组织、物品、事件和概念。知识图谱由三元组构成,具体结构如下:
头部→关系词→尾部
或者说,用语义网的术语来说:
主体是动作的对象
网络表示让我们能够提取并分析这些实体间错综复杂的关系。
知识图谱通常会带有一个定义,该定义涵盖了概念、关系及其属性——即这种定义通常被称为“本体”。本体为这些概念和关系提供了一个正式的定义,从而赋予了网络含义。
本体被搜索引擎及其他网络上的自动代理用来理解特定网页内容的意义,以对其正确索引并正确地显示。
案例描述在这个用例中,我们将用OpenAI的gpt-3.5-turbo从Amazon Products Dataset中的产品描述来构建一个知识图。
网上有很多本体被用来描述产品,其中最常用的是Good Relations Ontology和Product Types Ontology(商品类型本体)。这两个本体都扩展了Schema.org。
Schema.org 是一个协作性的社区项目,其使命是创建、维护和推广互联网上的结构化数据。Schema.org 的词汇可以与多种编码格式一起使用,例如但不限于 RDFa、Microdata 以及 JSON-LD。
为了这次任务,我们将使用Schema.org(一种用于描述网站和网页的元数据词汇表)定义的产品及相关概念以及它们之间的关系,从产品描述中提取三元组信息。
实施我们将用Python来实现解决方案。首先要做的是安装并引入所需的库。
导入库和读取数据集 !pip install pandas openai sentence-transformers networkx
import json # 导入json模块,用于处理JSON数据
import logging # 导入logging模块,用于记录程序日志
import matplotlib.pyplot as plt # 导入matplotlib.pyplot,用于绘制图形
import networkx as nx # 导入networkx,用于处理网络图数据
from networkx import connected_components # 从networkx导入connected_components,用于找到图中的连通组件
from openai import OpenAI # 导入OpenAI模块,用于调用OpenAI API
import pandas as pd # 导入pandas,用于数据处理和分析
from sentence_transformers import SentenceTransformer, util # 导入SentenceTransformer及其工具函数,用于句子的编码和相似度计算
我们现在要读取亚马逊的产品数据集为pandas数据框。
data = pd.read_csv("amazon_products.csv") # 读取亚马逊产品CSV文件
我们可以看到下图中的数据集中的内容。该数据集包含以下列:‘PRODUCT_ID’、‘TITLE’、‘BULLET_POINTS’、‘DESCRIPTION’、‘PRODUCT_TYPE_ID’、‘PRODUCT_LENGTH’。我们将把这三个字段合并成一个‘text’字段,这一列将代表产品的详情描述,我们将要求ChatGPT从该规格说明中抽取实体和关系。
图1. 数据集的内容
data['text'] = data['TITLE'] + data['BULLET_POINTS'] + data['DESCRIPTION']
信息抽取
我们将让ChatGPT从提供的产品规格中提取实体和关系信息,并将结果以JSON对象数组的形式返回。这些JSON对象必须包含以下键值:‘head’,‘head_type’,‘relation’,‘tail’和‘tail_type’等。
‘head’键必须包含从用户提示中提供的列表中的一种类型的提取实体文本。‘head_type’键必须包含用户提供的列表中的一种类型的头实体。‘relation’键必须包含‘head’与‘tail’之间的关系类型,‘tail’键必须包含三元组中的对象实体的文本,而‘tail_type’键需要包含尾实体的类型。
我们将利用下面列出的实体类型和关系类型来指导ChatGPT进行实体关系提取。我们将把这些实体和关系映射到Schema.org定义的实体和关系中。映射中的键表示提供给ChatGPT的实体和关系类型,而值则是来自Schema.org中的实体和关系的URL。
# 实体类型:
entity_types = {
"product": "https://schema.org/Product",
"rating": "https://schema.org/AggregateRating",
"price": "https://schema.org/Offer",
"characteristic": "https://schema.org/PropertyValue",
"material": "https://schema.org/Text",
"manufacturer": "https://schema.org/Organization",
"brand": "https://schema.org/Brand",
"measurement": "https://schema.org/QuantitativeValue",
"organization": "https://schema.org/Organization",
"color": "https://schema.org/Text",
}
# 关系类型:
relation_types = {
"hasCharacteristic": "https://schema.org/additionalProperty",
"hasColor": "https://schema.org/color",
"hasBrand": "https://schema.org/brand",
"isProducedBy": "https://schema.org/manufacturer",
"hasMeasurement": "https://schema.org/hasMeasurement",
"isSimilarTo": "https://schema.org/与...相似",
"madeOfMaterial": "https://schema.org/由...制成",
"hasPrice": "https://schema.org/报价",
"hasRating": "https://schema.org/有评级",
"relatedTo": "https://schema.org/与...相关"
}
为了利用ChatGPT进行信息抽取,我们创建了一个OpenAI客户端对象,并通过聊天完成API生成每个关系的JSON对象数组,这些关系是从原始产品规格中获取的。默认模型选择为gpt-3.5-turbo,因为它在这个简单的演示中已经足够好用。
client = OpenAI(api_key="<YOUR_API_KEY>")
# 客户端使用OpenAI API,API密钥为<YOUR_API_KEY>
def extract_information(text, model="gpt-3.5-turbo"):
completion = client.chat.completions.create(
model=model,
temperature=0,
messages=[
{
"role": "system",
"content": system_prompt
},
{
"role": "user",
"content": user_prompt.format(
entity_types=entity_types,
relation_types=relation_types,
specification=text
)
}
]
)
return completion.choices[0].message.content
提示设计
系统提示变量(system_prompt)包含了用于从原始文本中提取实体和关系的指令,并以JSON对象数组的形式返回提取结果,每个对象包含以下键:'head','head_type','relation','tail' 和 'tail_type'。
system_prompt = """你是一位专门分析在线零售商店产品规格的专家助手。
你的任务是根据用户提示识别并提取请求的实体和关系,并生成一个包含以下键值的JSON列表: "head"、"head_type"、"relation"、"tail" 和 "tail_type"。
"head" 键必须包含从提供的类型列表中提取的实体文本,"head_type" 键必须包含提取的头类型,"relation" 键必须包含 "head" 和 "tail" 之间的关系类型,"tail" 键必须代表从关系尾部提取的实体文本,"tail_type" 键必须包含尾类型。
尽量多地提取实体和关系。
"""
用户_prompt 变量包含来自数据集中的一个规格要求的单个输出示例,提示 ChatGPT 从给定的规范中以相同方式提取实体和关系。这展示了一个使用 ChatGPT 进行单次学习的例子。
user_prompt = """根据以下示例,从提供的文本中提取实体和关系。
使用以下实体类型:
# 实体类型:
{entity_types}
使用以下关系类型:
{relation_types}
--> 示例开始
# 规范
"YUVORA 3D 砖墙贴纸 | PE 泡沫创意墙纸,用于墙壁,
防水且自粘,白色 3D 最新独特设计墙纸,适用于家庭(70*70 厘米)-40 块
[由软 PE 泡沫制成,防撞,保护您的家人。防水、防潮并隔音。易于湿布清洁和维护,经济的墙面覆盖材料。自粘贴壁纸,易于粘贴和去除。易于根据装饰区域 DIY 切割形状。这种浮雕 3D 墙纸提供令人惊叹的视觉冲击。瓷砖轻便、防水、防撞,几分钟即可在干净光滑的表面上安装,没有杂乱和专用工具,也不会随着时间而开裂。自粘 3D 壁纸也是一种经济的墙面覆盖材料,它们会留在您的墙上,直到您想移除。瓷砖也可以直接安装在现有的面板或光滑表面上。使用范围:特色墙、厨房、卧室、客厅、餐厅、电视墙、沙发背景、办公室墙等装饰。不要用于淋浴和粗糙的墙面。]
提供高质量的泡沫 3D 墙面板自粘贴壁纸,由软 PE 泡沫制成,防撞,防水,防潮,隔音,易于湿布清洁和维护,3D 泡沫壁纸材料是安全的,易于粘贴和去除。根据您的装饰区域可以轻松 DIY 切割形状。提供最佳质量的产品。这种墙纸是带有工厂完成的自粘的真正壁纸。您会很高兴拥有它。产品特点:高密度发泡技术,总共有三个生产过程,可使用长达 10 年,表面处理:3D 深浮雕花纹图案。
################
# 输出
[
{
"head": "YUVORA 3D 砖墙贴纸",
"head_type": "产品",
"relation": "由...生产",
"tail": "YUVORA",
"tail_type": "制造商"
},
{
"head": "YUVORA 3D 砖墙贴纸",
"head_type": "产品",
"relation": "是...特征",
"tail": "防水",
"tail_type": "特征"
},
{
"head": "YUVORA 3D 砖墙贴纸",
"head_type": "产品",
"relation": "是...特征",
"tail": "自粘",
"tail_type": "特征"
},
{
"head": "YUVORA 3D 砖墙贴纸",
"head_type": "产品",
"relation": "颜色是",
"tail": "白色",
"tail_type": "颜色"
},
{
"head": "YUVORA 3D 砖墙贴纸",
"head_type": "产品",
"relation": "尺寸是",
"tail": "70*70 厘米",
"tail_type": "尺寸"
},
{
"head": "YUVORA 3D 砖墙贴纸",
"head_type": "产品",
"relation": "尺寸是",
"tail": "40 块",
"tail_type": "尺寸"
}
]
--> 示例结束
对于以下规范,生成如示例中的实体和关系提取。
# 规范
{specification}
################
# 输出
"""
现在,我们将对数据集里的每个规格调用名为extract_information的函数,并创建这些三元组列表,这些三元组将构成我们的知识图谱。为了演示,我们将仅使用包含100个产品规格的子集来生成知识图谱。
kg = [] # 知识图谱列表
for content in data['text'].values[:100]: # 遍历前100个文本内容
try:
extracted_relations = extract_information(content) # 提取信息
extracted_relations = json.loads(extracted_relations) # 将提取的信息转换为JSON格式
kg.extend(extracted_relations) # 将提取的信息添加到知识图谱列表中
except Exception as e:
logging.error(e) # 记录错误信息
kg_relations = pd.DataFrame(kg) # 将知识图谱列表转换为DataFrame
下面的图展示了信息提取的结果。
图2. 使用ChatGPT提取信息的结果
实体识别实体解析(ER)是指解决实体歧义的过程,这些实体对应现实中的概念。在这种情况下,我们将尝试对数据集中的头实体及尾实体执行基本的实体解析。从而使文本中的事实表示更加简洁。
我们将利用NLP技术进行实体解析,具体来说,使用sentence-transformers库为每个实体创建嵌入,并计算实体间的余弦相似度。
我们将使用“all-MiniLM-L6-v2”句子嵌入模型来创建嵌入,因为它是一个快速且相对准确的模型,适合这种用例,。对于每一对头实体来说,我们将检查相似度是否大于0.95,如果是,则将这些实体视为同一个实体,并将它们的文本值统一。对于尾实体也适用同样的逻辑。
这个过程将帮助我们达到以下效果。假设我们有两个实体,一个叫“Microsoft”,另一个叫“Microsoft Inc.”,那么这两个实体就会被合并成一个。
我们按照以下方式加载并使用嵌入模型来计算第一个和第二个头实体的相似性。
heads = kg_relations['head'].values
embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = embedding_model.encode(heads)
similarity = util.cos_sim(embeddings[0], embeddings[1])
为了可视化实体解析得到的知识图谱,我们使用了Python的networkx库。首先,我们创建一个空图,然后将每条提取的关系添加到图中。
创建一个图G。对于kg_relations中的每一行数据,我们使用'head'和'tail'作为节点,'relation'作为边的标签,将它们添加到图G中。
画图时,我们可以用下面的代码:
pos = nx.spring_layout(G, seed=47, k=0.9)
labels = nx.get_edge_attributes(G, 'label')
plt.figure(figsize=(15, 15))
nx.draw(G, pos, with_labels=True, font_size=10, node_size=700, node_color='lightblue', edge_color='gray', alpha=0.6)
nx.draw_networkx_edge_labels(G, pos, edge_labels=labels, font_size=8, label_pos=0.3, verticalalignment='baseline')
plt.title('产品知识图')
plt.show()
如下图展示了来自生成的知识图谱的一个子图:
图3. 一个产品知识地图示例
我们可以看到,通过这种方式,我们可以将多个不同但有共同特性的产品关联起来。这有助于学习产品之间的共同属性,标准化产品规格,使用如Schema.org这样的共同模式或框架来描述网上的资源,可以根据产品规格来推荐产品。
结束。大多数企业都有大量的未被利用的数据存储在数据湖中。通过创建知识图谱来从这些闲置的数据中提取有价值的信息,可以帮助获取被困在未处理的文本中的信息,并利用这些信息做出更明智的决策。
到目前为止,我们已经看到LLM可以用来从原始文本数据中提取实体和关系的三元组信息,并自动构建知识图。在接下来的文章中,我们将尝试根据提取的知识图来创建一个产品推荐系统。