在这篇文章中,我们将演示如何使用大型语言模型 (LLM) 模型来使用不同格式的表格数据。
法律硕士与结构化数据的挑战
对于我们人类来说,连接文本和表格之间的点是很简单的事情。但对于本地语言学习者来说,这就像是在没有所有拼图的情况下试图解决一个难题。
LLM是按顺序处理文本的,这意味着它们一次只阅读和理解一个单词或句子的信息。这种顺序处理方式是它们在大量文本数据中接受训练的方式。然而,表格以不同的格式呈现信息。它们将数据组织成行和列,从而形成一种多维结构。
要理解这种结构,LLM 需要采用与顺序文本不同的方法。LLM 需要识别跨行和跨列的模式,理解不同数据点之间的关系,并解释标题和单元格值的含义。由于 LLM 主要接受的是连续文本的培训,因此他们在解释和处理表格数据时可能会很吃力。这就好比让一个习惯阅读小说的人突然去理解和解释图形或图表;他们可能会发现这很有挑战性,因为这超出了他们处理信息的惯常模式。
表格数据的常见示例
表格数据比我们想象的要常见得多。下面是从小型、中型到大型的三种情况:
在本文中,我们将探讨将表格数据嵌入文本信息的情况。我们将使用包含十几行和几列的小表格,所有表格都在模型的上下文窗口容量范围内。
实际操作
我们的示例将以下图基础,图显示了世界亿万富豪排行榜,我们将把该排行榜纳入未来的 RAG 框架,并回答有关该亿万富豪排行榜的问题。只需四个步骤,我们就可以演示如何使用表格数据作为用户问题的上下文,甚至验证是否有可能使模型产生错误。
准备我们的环境库
import os
import sys
import pandas as pd
from typing import List
为了便于以表格形式显示结果,我们将使用 “BeautifulTable ”库,该库在终端中提供了一种具有视觉吸引力的格式。
!pip install beautifultable
from beautifultable import BeautifulTable
由于我们将只处理表格,因此让我们来探索一下另一个有用的 python 库 camelot-py 的功能。
!pip install opencv-python
!pip install camelot-py
!pip install ghostscript
import camelot
准备数据
我们将把名单(全球亿万富翁)保存为 PDF 格式,以便于我们进行探索。
file_path="./World_Billionaires_Wikipedia.pdf"
一旦数据被提取为 Pandas 数据帧,下面的代码将允许我们以基本的方式对数据进行清理。该例程将从预选列表中逐页处理数据,使我们能够访问易于操作的结构。
# use camelot to parse tables
def get_tables(path: str, pages: List[int]):
for page in pages:
table_list = camelot.read_pdf(path, pages=str(page))
if table_list.n>0:
for tab in range(table_list.n):
# Conversion of the the tables into the dataframes.
table_df = table_list[tab].df
table_df = (
table_df.rename(columns=table_df.iloc[0])
.drop(table_df.index[0])
.reset_index(drop=True)
)
table_df = table_df.apply(lambda x: x.str.replace('\n',''))
# Change column names to be valid as XML tags
table_df.columns = [col.replace('\n', ' ').replace(' ', '') for col in table_df.columns]
table_df.columns = [col.replace('(', '').replace(')', '') for col in table_df.columns]
return table_df
# extract data table from page number
df = get_tables(file_path, pages=[3])
现在,让我们把数据帧中的表格数据转换成多种格式,如 Json、CSV 或 Markdown 等: Json、CSV 或 Markdown 等。
# prepare test set
eval_df = pd.DataFrame(columns=["Data Format", "Data raw"]) # , "Question", "Answer"
# Save the data in JSON format
data_json = df.to_json(orient='records')
eval_df.loc[len(eval_df)] = ["JSON", data_json]
# Save the data as a list of dictionaries
data_list_dict = df.to_dict(orient='records')
eval_df.loc[len(eval_df)] = ["DICT", data_list_dict]
# Save the data in CSV format
csv_data = df.to_csv(index=False)
eval_df.loc[len(eval_df)] = ["CSV", csv_data]
# Save the data in tab-separated format
tsv_data = df.to_csv(index=False, sep='\t')
eval_df.loc[len(eval_df)] = ["TSV (tab-separated)", tsv_data]
# Save the data in HTML format
html_data = df.to_html(index=False)
eval_df.loc[len(eval_df)] = ["HTML", html_data]
# Save the data in LaTeX format
latex_data = df.to_latex(index=False)
eval_df.loc[len(eval_df)] = ["LaTeX", latex_data]
# Save the data in Markdown format
markdown_data = df.to_markdown(index=False)
eval_df.loc[len(eval_df)] = ["Markdown", markdown_data]
# Save the data as a string
string_data = df.to_string(index=False)
eval_df.loc[len(eval_df)] = ["STRING", string_data]
# Save the data as a NumPy array
numpy_data = df.to_numpy()
eval_df.loc[len(eval_df)] = ["NumPy", numpy_data]
# Save the data in XML format
xml_data = df.to_xml(index=False)
eval_df.loc[len(eval_df)] = ["XML", xml_data]
是时候探索我们的测试数据了。我们配置了一个数据集,其中每一行都代表数据帧的输出格式,而 “原始数据 ”中的数据则对应于我们将与生成模型一起使用的表格数据。
from pandas import option_context
with option_context('display.max_colwidth', 150):
display(eval_df.head(10))
输出:
为验证设置模型
让我们准备一个基本提示,以便与上下文数据交互。
MESSAGE_SYSTEM_CONTENT = """You are a customer service agent that helps a customer with answering questions.
Please answer the question based on the provided context below.
Make sure not to make any changes to the context, if possible, when preparing answers to provide accurate responses.
If the answer cannot be found in context, just politely say that you do not know, do not try to make up an answer."""
在使用表格数据集进行测试之前,我们需要准备模型的连接设置(在本例中,我们将使用 AzureOpenAI)。你需要提供你的凭据。
from openai import AzureOpenAI
client = AzureOpenAI(
api_key=OAI_API_Key,
api_version=OAI_API_Version,
azure_endpoint=OAI_API_Base)
def response_test(question:str, context:str, model:str = "gpt-4"):
response = client.chat.completions.create(
model=model,
messages=[
{
"role": "system",
"content": MESSAGE_SYSTEM_CONTENT,
},
{"role": "user", "content": question},
{"role": "assistant", "content": context},
],
)
return response.choices[0].message.content
由于我们处理的是一个数据集,其中每一行都代表一个单独的上下文信息单元,因此我们实施了以下迭代例程,允许我们处理一行又一行,并存储每一行的模型解释。
def run_question_test(query: str, eval_df:str):
questions = []
answers = []
for index, row in eval_df.iterrows():
questions.append(query)
response = response_test(query, str(row['Data raw']))
answers.append(response)
eval_df['Question'] = questions
eval_df['Answer'] = answers
return eval_df
def BeautifulTableformat(query:str, results:pd.DataFrame, MaxWidth:int = 250):
table = BeautifulTable(maxwidth=MaxWidth, default_alignment=BeautifulTable.ALIGN_LEFT)
table.columns.header = ["Data Format", "Query", "Answer"]
for index, row in results.iterrows():
table.rows.append([row['Data Format'], query, row['Answer']])
return table
现在,我们将连点成线,处理数据集,然后使用每种表格数据格式作为上下文信息,从中获取答案,最后以表格方式显示结果。
query = "What's the Elon Musk's net worth?"
result_df1 = run_question_test(query, eval_df.copy())
table = BeautifulTableformat(query, result_df1, 150)
print(table)
输出:
我们可以看到,在Panda数据框架转换过程中获得的每种表格数据格式对 “埃隆-马斯克的净资产是多少?”这一问题的回答都是一致的。正如我们所知,由于该问题包含语义阐述,不同回答之间的差异是我们在生成最终验证指标时必须考虑的一个挑战。
如果修改 “MESSAGE_SYSTEM_CONTENT ”变量,我们还可以获得更简洁或更详细的回复。
让我们再次重复这一练习,这次我们将提出一个需要对模型进行更多分析推理的问题。
query = "What's the sixth richest billionaire in 2023 net worth?"
result_df2 = run_question_test(query, eval_df.copy())
table = BeautifulTableformat(query, result_df2, 150)
print(table)
输出:
As with the previous example, we have used each of the Pandas data frame export formats as a query’s information context.context for the quer
与上一个示例一样,我们使用了 Pandas 的每一种数据帧导出格式作为查询的上下文。
在本例中,“2023 年净资产排名第六的亿万富豪是谁 ”这一问题证明了模型可以对 “排名第六的亿万富豪 ”等更抽象的问题做出响应,这涉及到更多的分析推理和表格数据计算。这两个挑战都以出色的一致性得到了解决
相关性和分散注意力
让我们来尝试这个模型,看看我们的提示和数据上下文是否能达到预期效果。
query = "What's the Michael Jordan net worth?"
result_df3 = run_question_test(query, eval_df.copy())
table = BeautifulTableformat(query, result_df3, 150)
print(table)
输出:
通过这项测试,我们提出了一个在所提供信息的背景下没有正确答案的问题。我们的目标是确保我们的模型不会出现幻觉或假阳性(看似真实的错误回答)。
我们对 “迈克尔-乔丹的净资产是多少?”的回答在每种数据格式中都得到了一致的解决,正如我们所预期的那样(问题没有答案)。
让我们再举一个例子,使用一个与表格数据中存在的名字非常相似的名字,可能会误导不知情的用户。
query = "What's Michael Musk's net worth?"
result_df4 = run_question_test(query, eval_df.copy())
table = BeautifulTableformat(query, result_df4, 180)
print(table)
在 “迈克尔-马斯克的净资产是多少?”这个问题中,“马斯克 ”可能会让我们误解这个问题,但这个模型还是圆满地解决了这个难题。
结论
通过摄取文本文档中的小表格数据,我们看到了 LLM 是如何理解表格上下文的,即使我们试图用错误的信息来欺骗它。很明显,在提取包含上下文的表格时,保持结构的完整性非常重要。这些表格通常包含关键的上下文信息,可以增强对周围文本的理解,从而提供更准确的结果。
以小型嵌入式表格为重点,必须认识到它们在为 RAG 框架提供上下文线索方面的重要性。利用 Camelot 等库提取表格可确保保留这些结构。然而,正如模型测试中所显示的那样,在不分散注意力的情况下保持相关性是一项挑战。通过这样做,我们为 GPT 等模型提供了重要的上下文,使它们能够在更广泛的文本上下文中生成准确且与上下文相关的响应。