介绍
你是否面对过大量由人类输入的杂乱无章的自由格式数据,并试图从中找出意义?这是一项极其耗费脑力和时间的工作,除非你有专门的时间对其进行研究,否则你很可能只是对数据进行采样,并很可能利用不可信的元数据获得表面的见解。通常情况下,这样做的结果并不理想。
不难看出,专门处理混乱数据的大型语言模型可以在这方面提供帮助。本文将介绍从此类实施中总结出的最佳实践,涵盖一系列概念,如使用 GPT 帮助你清理数据、进行分析和创建有用图表的最有效方法、管理个人身份信息(PII)的方法、生产硬化提示设计、绕过 GPT 的 “前额叶皮层 ”瓶颈等!
从长远来看,制定在源头填充准确元数据的流程肯定会有所帮助,但要记住,协调和审核这些流程需要耗费大量时间。
使用 GPT 编写分析代码!
底线在前: 提取、修改和可视化数据需要中级水平的 Python 代码,但现在,GPT 可以更快、更高质量地完成这些工作。
在下图中,我说明了需要编写代码来转换数据,然后调用 GPT API 从票单详细信息中提取见解的所有步骤(绿色字体)。最棒的是,我不需要从头开始编写这些代码。我使用 GPT 为我编写了代码!
我是如何编写代码的
虽然我对 Python 的使用还算得心应手,但使用 GPT 编写代码至少让我的水平提高了 3 倍。在通过 GPT 编写代码时,我使用了一种非常简单的方法:我没有用它来执行任何代码。我只是告诉 GPT 数据的样子,让它帮我写代码。我让 GPT 在代码中的不同位置随意插入 print 语句来打印变量。然后,我将代码复制到笔记本电脑上的 Jupyter Notebook 中,并在那里执行。例如,我的提示如下:
我:这是我在分析中要用到的所有文件。我将对它们进行枚举,并在提示中用编号来称呼它们。
1. “所有互动票单.xlsx" 2.
2. “从运行部收到的故障单副本.xlsx" 3.
3. “verifying_accurate_list_of_ops_people_supporting_my_space.xlsx" 3.
它们都在 ./data/ 目录中。
编写 python 代码,将文件 1、2 和 3 调入 pandas 数据帧。忽略文件中所有带有 pivot 字样的工作表,但调入其余工作表的数据。使用每个 Excel 文件中工作表本身的文本命名蛇形数据框....。
然后,GPT 会输出代码,例如:
import pandas as pd
import os
# Define the file paths
files = {
1. "All Interacted Tickets.xlsx",
2. "Copy of Ticket Dump - Received from Ops.xlsx",
3. "Verifying_accurate_list_of_ops_people_supporting_my_space.xlsx"
}
def snake_case(s):
return s.lower().replace(" ", "_")
def read_excel_sheets(file_path, ignore_pivot=True):
xl = pd.ExcelFile(file_path)
sheet_names = xl.sheet_names
….<code clipped>
然后,我会在本地运行这些代码。如果出现错误,我会将输出(包括打印语句输出)粘贴到 “相同 ”聊天中,因为这样可以保留内存,而且 “大部分 ”都能解决我的问题。不过,在某些情况下,GPT 会卡住(当它不断推荐相同的问题解决方案时,你就能意识到这一点),我会开始用更多的问题来询问它:
我:df = df[1:] 在做什么?
GPT: df = df[1:]这行用于删除数据帧的第一行,通常是 Excel 文件读取时的标题行....。
我:df = df[1:]是不正确的,我不希望你放弃第一行。事实上,这是你要用于每个数据帧标题的行。
因此,如果你像我一样在带外使用 GPT 开发代码,那么对 Python 的适度了解将有助于突破 GPT 的一些代码问题,因为它对上下文几乎是视而不见的。
操作票据分析的步骤
经过多次反复和 “失误 ”之后,我得出了以下步骤!换句话说,如果让我重新做一次分析,我会按照以下结构简化流程。
第 1 步:筛选出相关票单
首先是底线: 如果元数据不可靠,那么根据工作过的支持工程师过滤与你的领域相关的票单是你的最佳选择。
(只有当你在大中型企业工作,并且是利用共享操作团队的众多团队之一时,才需要执行此步骤)
将工作票单减少到只与你的部门或团队相关,这是一个重要的筛选步骤,当你的公司正在处理大量的操作票单时必须采取这一步骤。你将通过 LLM 发送这些票单,如果你使用的是 GPT4 这样的付费服务,则希望只发送与你相关的票单!
但是,如果你的元数据很差,那么推断工作票单集就是个问题。支持工程师可能没有接到指示来标记票单属于哪个团队,或者没有很好的票单类别可供选择,因此你所能使用的只是一些自由形式的数据和自动收集到的票单的一些基本 “事实”。这些事实包括谁创建了票单、谁拥有票单、与票单创建相关的时间戳、状态变化(如果幸运的话)以及票单关闭。可能还存在其他 “主观 ”数据,如票单优先级。收集这些数据并没有问题,但这些数据可能并不准确,因为票单创建者倾向于将他们打开的所有票单都定义为 “紧急 ”或 “高优先级”。根据我的经验,通过 LLM 得出实际的优先级往往更中性,但仍有可能出错,这一点稍后会讲到。
因此,换句话说,要坚持 “事实”。
通常能帮助你减少工作集的 “事实 ”包括创建和/或处理票单的支持工程师的姓名。由于支持工程师也专门从事特定领域的工作(数据技术 vs CRM vs workday 等),因此第一步要做的就是与支持经理合作,确定所有处理与你的领域相关的票单的支持工程师的姓名。
然后,使用一个可识别的关键字(如他们的工作电子邮件地址),你就可以将大量票单过滤为与你的部门相关的子集,并提取与这些票单相关的 “事实 ”元数据。
完成这一步后,你还可以获得第一个统计数据:在一段时间内,有多少票单是为我的空间打开的。
第 2 步:提取 “描述 ”字段和其他元数据
首先是底线: 虽然票单创建者可能会弄错很多元数据,但她不能搞砸描述字段,因为这是她向支持团队传达问题及其业务影响的唯一途径。这就再好不过了,因为自由流数据的意义是 GPT 的专长。因此,重点是提取描述字段和其他 “难以弄乱的数据”,如票单开始和结束时间等。
大多数票单系统(如 Jira Service Management、Zendesk、Service Now 等)都允许你下载票单元数据,包括长长的多行描述字段。不过,几乎所有系统都有一次可下载票单的最大数量限制。一种更自动化的方法,也是我采用的方法,是使用 API 提取这些数据。在这种情况下,你需要从步骤 1 中获取支持团队的支持工程师所处理过的票单,然后循环查看每张票单,调用 API 提取其元数据。
其他一些系统允许你通过类似 ODBC 的接口发布 SQL(或 SOQL,Salesforce 产品)查询,这很酷,因为你可以使用 WHERE 子句将第 1 步和第 2 步合并在一起。下面是一个伪代码示例:
SELECT ticket_number, ticket_description, ticket_creation_date, blah blah
FROM ticket_table
WHERE ticket_owners include "johndoe@company.com, janedang@company.com" ...
将这些数据保存为 MS-Excel 格式并存储在磁盘上。
为什么是 MS-Excel?我喜欢将表格数据 “序列化 ”为 MS-Excel 格式,因为这样就不会在将数据导入 Python 代码时出现转义或重复分隔符的问题。Excel 格式会将每个数据点编码到自己的 “单元格 ”中,因此不会出现解析错误,也不会因特殊字符/分隔符埋藏在文本中而导致列错位。此外,在将这些数据导入 Python 时,我可以使用 Pandas(一个流行的表格数据处理库),利用其简单的 Excel 导入选项将 Excel 数据导入数据框中。
第 3 步:将数据转换为 GPT 友好格式(JSON)
先说底线: JSON 格式可由人类读取、机器读取、错误安全、易于故障排除,而且在 GPT 的操作中错误最少。此外,随着数据的不断丰富,你还可以在相同的 JSON 结构中不断添加新字段。
"16220417": {
"description": "Hi Team, \nThe FACT_XYZ_TABLE has not refreshed in time. Typically the data is available at 10am PST, but when I see the job, it has been completing several hours late consistently for the last few weeks. Also, it is 11am, and I see that it is still in running state!\n\n It is critical that this table is completed in time, so we have enough time to prepare an important sales executive report by 8am every morning. Please treat this with urgency.",
"opened_priority": "P2 - Urgent",
"origin": "Helpdesk ticket portal",
"closedDate": "2024-02-26T07:04:54.000Z",
"createdDate": "2024-02-01T09:03:43.000Z",
"ownerName": "Tom Cruise (Support Engineer)",
"ownerEmail": "tcruise@shoddy.com",
"contactName": "Shoddy Joe (Stakeholder)",
"contactEmail": "sjoe@shoddy.com",
"createdByName": "Shoddy Joe (Stakeholder)",
"createdByEmail": "sjoe@shoddy.com",
"ticket_status": "closed"
},
上面的代码段显示了一个 JSON 化的票据元数据示例,票据编号是关键字,指向一个包含更多关键字/值元数据的对象。文件中会有很多这类 JSON 块,每张票都有一个。
经过反复试验后,我意识到让 GPT 为我编写数据处理代码的最有效方法是将数据转换为 json 格式,并将此格式共享给 GPT 进行操作。把这些数据塞进 pandas 数据框架并没有什么不妥,甚至可能更容易高效地处理、清理和转换这些数据。我之所以最终将最终数据集转换成 JSON 格式,主要是因为将表格数据发送到 GPT 提示符中是非常笨拙的。人类很难读取,而且还会给 LLM 带来错误,具体解释如下。
将表格引入提示符时,必须使用逗号分隔值(csv)格式。这有两个问题:
是的,虽然你也必须在 JSON 中转义双引号(例如 “key”: “value has\”quotes\“” ),但将此值对齐到一列绝对没有问题,因为 “key ”唯一标识了这一列。在 csv 格式的某些边缘情况下,列对齐可能会出错,这时就很难排查出错的原因。
使用 JSON 的另一个原因是,在未来的步骤中通过 GPT 扩展元数据时,可以清楚地看到并加以区分;它只是水平向下添加了更多的键值值。你也可以在表格中做到这一点,但这主要需要在集成开发环境或笔记本中向右滚动。
第 4 步:使用简单技术增强数据(又称基本特征工程)
首先是底线: 简单的数学分析,如时间三角、平均值、标准偏差等,可以使用基本编码轻松完成,而且成本更低,因此,让 GPT 编写代码来完成这些工作,并在本地运行这些代码,而不是将数据发送给 GPT 来为你做数学计算。语言模型已被证明会犯数学错误,所以最好还是把它们用在刀刃上。
首先,我们可以通过聚合票据元数据中的一些基本信息来增强票据元数据。这是一个前置步骤,最好用一些简单的代码来完成,而不是为此消耗 GPT 点数。
在这种情况下,我们用创建时间减去关闭时间来计算票单的持续时间。
第5步:主要入口: GPT 驱动的数据增强(增强型特征工程)
现在我们进入主入口。如何使用 GPT 对原始数据进行转换,并从中获得复杂的结构化元数据,从而从中提取洞察力。在数据科学领域,这一步骤被称为 “特征工程”。
处理: 混淆敏感信息(可选)
底线在前: 在将数据发送到公共 API 服务之前,让 GPT 使用开源匿名库并开发代码对数据进行匿名处理。
如果你使用的是 openAI,而不是本地开源 LLM(数据保留在笔记本电脑上),则此步骤适用于你。在今后的文章中,我将介绍我的本地数据工程和分析环境设置,其中将展示开源 LLM 选项。
为此,我评估了五个库,但我所寻找的关键功能是能将敏感信息转换为匿名数据,然后还能将其重新转换回来。我只找到了以下具有这种功能的库。
在这两个软件中,我最喜欢 Presidio。看到微软在过去十年中做出的大量 “高质量 ”开源贡献,我仍然印象深刻。这套 Python 库也不例外。它具有识别开箱即用的 PII 类型数据的功能,还可以自定义和指定其他需要匿名化的数据。
下面是一个例子:
原文:
('Peter gave his book to Heidi which later gave it to Nicole. 'Peter gave his book to Heidi which later gave it to Nicole.
Peter lives in London and Nicole lives in Tashkent.')
匿名测试:t:
'<PERSON_1> gave his book to <PERSON_2> which later gave it to <PERSON_0>.
<PERSON_1> lives in <LOCATION_1> and <PERSON_0> lives in <LOCATION_0>.`
这可以发送到 GPT 进行分析。当它返回结果时,你可以通过映射对其进行去匿名化处理:
实体映射
{ 'LOCATION': {'London': '<LOCATION_1>', 'Tashkent': '<LOCATION_0>'},
'PERSON': { 'Heidi': '<PERSON_2>',
'Nicole': '<PERSON_0>',
'Peter': '<PERSON_1>'}
}
使用实体映射可以对文本进行去匿名化处理:
去匿名文本:
('Peter gave his book to Heidi which later gave it to Nicole. 'Peter gave his book to Heidi which later gave it to Nicole.
Peter lives in London and Nicole lives in Tashkent.')
预处理: 对输入数据进行消毒
首先是底线: 在选择输出分隔符时要深思熟虑,因为某些特殊字符在语言模型中具有 “意义”。然后,你可以放心地删除你选择的分隔符,对原始输入数据进行消毒处理。
问题:当要求基于文本的界面(如 LLM)返回表格数据时,你必须告诉它输出用分隔符(如 csv 或 tsv 格式)分隔的数据。假设你要求 GPT 以逗号分隔值输出汇总数据(又称 “特征”)。问题在于,输入的票据数据是原始的、不可预测的,有人可能会在描述中使用逗号。从技术上讲,这本不应该成为一个问题,因为 GPT 会对这些数据进行转换,并扔掉输入数据中的逗号,但 GPT 仍有可能在其输出中使用部分原始数据(包括逗号),例如在单行摘要中。经验丰富的数据工程人员现在可能已经发现了这个问题。当数据值本身包含了本应分隔它们的分隔符时,就会出现各种各样的处理问题。
有人可能会问:为什么不把数据值封装在双引号中,以避免这些问题呢?例如
“key“ : ”this, is the value, with all these characters !#@$| escaped" .
问题就在这里。用户也可以在数据中输入双引号!
key“ : ”this is a “value” with double quotes, and it is a problem!“ ”key“ : ”this is a “value” with double quotes, and it is a problem!
是的,也有办法解决这个问题,比如使用多行正则表达式,但它们会使代码变得复杂,增加 GPT 修复缺陷的难度。因此,处理这个问题的最简单方法就是选择一个输出分隔符,如果从输入数据中删除,这个分隔符对丢失数据上下文的影响最小,然后再从输入数据中删除!
我还尝试过一些分隔符,比如 |%|,这些分隔符肯定不会出现在输入数据中,但我很快就意识到,这些分隔符会快速占用输出标记的限制,所以就放弃了。
以下是我测试过的几个分隔符
最后,我选择了管道“|”分隔符,因为大多数利益相关者在票单描述中表达他们的问题时并不使用管道“|”分隔符。
提示性能调整
先来个底线: 在运行 GPT 数据分析提示符之前,先根据一组已知输出的票单描述评估其性能,然后微调提示符并反复运行,直到获得最高性能分数。
目标:让 GPT 读取客户所写的票单描述,并从中得出以下元数据,然后进行汇总和可视化:
方法: 我改进主要提示的方法是
结果:
以下是有关从原始票单描述中提取的元数据的详细信息,以及经过多次迭代微调提示后的总体性能得分:
以下是我对某些维度在多次修改后仍得分较低的原因的解释:
弄清 GPT 的 “健忘 ”极限
底线在前: 当向 GPT 提示符发送与上下文无关的数据块(如许多票据描述)时,处理上限可能远低于输入标记限制所允许的最大数据块。(在我的案例中,这个上限在 20 到 30 之间)。据观察,GPT 会持续遗忘或忽略超出此上限的处理。通过反复试验来确定这一点,坚持使用低于该限制 10%的数字来避免数据丢失。
人类的前额叶皮层可以保存 5-7 件不相关的事情,而 GPT 可以保存 30-40 件不相关的事情,无论其上下文窗口有多大。我只发送了票号和描述。其余的数据不需要任何花哨的推理。
由于我有近 3000 张票据要让 GPT 审查,我最初的倾向是尽量增加往返运行次数,并在每次提示中 “打包 ”尽可能多的案例描述。我想出了一种精心设计的方法,根据单词数(在转换器架构中,令牌是一个子单词)来确定令牌的平均大小,并发现每个提示符可以容纳大约 130 个案例描述。
但后来我发现了一个奇怪的现象。无论我向 GPT 发送多少票单描述,它始终只能处理前 20 到 30 张票单!GPT 的处理能力似乎无法超过这个神奇的数字。
这让我改变了策略,我决定根据每个 API 调用的字数,将票单批量减少到最多 10-12 张,略低于 20-30 张的上限。虽然这种方法肯定会增加调用次数,从而延长分析时间,但它确保了没有票单被放弃处理。
*Total tickets chunked: 3012*
*The full ticket data has been chunked into 309 chunks:*
*Chunk 0: Number of words = 674*
*Chunk 0: Number of tickets = 12*
*Chunk 1: Number of words = 661*
*Chunk 1: Number of tickets = 12*
*Chunk 2: Number of words = 652*
*Chunk 2: Number of tickets = 12*
*Chunk 3: Number of words = 638*
*Chunk 3: Number of tickets = 7*
*Chunk 4: Number of words = 654*
*….*
在确定了 10-12 张票单的最佳票单批量大小并创建了性能良好的提示符后,就到了通过提示符运行所有批次的时候了。
显示时间!通过 GPT 运行所有票单
先说底线: GPT 可以在数小时内完成票单分析,而人工分析同样的票单可能需要数周或数月的时间。此外,虽然 GPT 有一定的出错率,但它的成本要低得多。
我向 GPT 提供了 JSON 格式,让它为我编写代码,具体做法如下:
为什么在运行完成后保存到磁盘是实用的?从时间而非金钱的角度来看,这些运行成本很高。因此,在成功运行后,将这些数据序列化(保存)到磁盘上是明智之举,这样将来就可以在保存的数据上运行分析,而不必重复 Jupyter 笔记本中的代码块。事实上,在成功运行之后,我注释掉了笔记本中的整个代码块,这样如果我从头到尾运行整个笔记本,它就会再次跳过这个昂贵的步骤,而是将 JSON 数据从磁盘加载到内存中,然后继续进行分析。
下面是完全水合的 JSON 输出示例。蓝色条目是 GPT 从描述字段中提取的元数据
我在这个循环中运行了约 3000 张票,耗时约 2.95 小时。
因此,时间上的节省显然是惊人的,但成本上的对比又如何呢?
GPT4 与人工分析的成本比较
我请 GPT 帮我进行成本比较。请注意,我比较的是 2 个月的人力,以便与 GPT 分析的票数相匹配。
提示:如果我使用 GPT4 的 API 2.95 小时,与 2 名资深 IT 软件工程师(一名在美国中西部工作,另一名在印度工作)在同一项目上全职工作 2 个月(不包括周末)相比,GPT4 的成本是多少?
薪酬估算:
美国中西部高级 IT 软件工程师:
印度高级 IT 软件工程师:
2 个月的成本计算:
2.95 小时的 GPT-4 API 成本: ~6.64 美元,假定 GPT-4-8k 定价和给定的令牌使用率。(GPT4-o 或 GPT4-o mini 会更便宜)。
即使加上我在这个项目中 7 天工作 20 个小时的成本,总体成本比较仍然要高得多。更重要的是,这项工作现在是可复制的。
因此,基本上,用 7 美元和 3 个小时,GPT 就能完成人类原本需要 1 个月、花费 2.5 万美元才能完成的分析工作。
第 6 步:从 GPT 派生元数据中提取见解
先来个底线: 使用 GPT 提取出有用的元数据后,转过身来与 GPT 一起集思广益,看看能从中绘制出什么样的 KPI 图表。虽然我已经有了一些好奇的发现,但我还与 GPT 进行了头脑风暴,以获得更多想法。同样,使用 JSON 格式非常方便,我只需将一张票据的匿名样本传给 GPT,然后问它:"根据你在这里看到的内容,给我一些想法,我可以绘制什么类型的图表来深入了解我的运营情况?
最后,这是我们俩提出的想法。我采纳了一些,忽略了其他的。
第 8 步:可视化
最重要的一点: 多亏了 GPT,你可以编写 Python 代码来创建图表,而不是在 Python 中转换数据,然后将这些数据转移到可视化工具中。这有助于将所有分析简化、版本控制并包含在一个地方。
从历史上看,探索性数据分析(EDA)的典型模式是在 Python 中提取和转换数据,然后将其存储到文件或数据库中,然后将 Tableau、Power BI 或 Looker 连接到这些数据,使用它们创建图表。虽然在这些可视化产品中使用长期有效的仪表盘绝对是一种好方法,但使用这些产品进行早期阶段的 EDA 可能是一个高摩擦的过程,会带来延迟。此外,管理和匹配不同版本的图表与不同版本的数据转换也变得十分困难。然而,由于以下两个原因,遵循这种两步模式在历史上是必要之恶:
不过,现在 GPT 可以为你编写 matplotlib(或 seaborn 或 plotly)的所有代码,因此就不需要在最后将你的工作转移到可视化工具上了。
下面是这些图形和表格的一些示例。当然,文本和数字都是匿名的,但这里的目的是向你展示你可以开始提取的洞察力类型。
任何洞察力的目标都是提出更深层次的问题,并最终根据数据采取有意义的行动,从而最终创造价值。
洞察力会让你对系统的行为方式产生好奇,从而尝试对其进行改进。
处理服务请求(纯运营工作)是一项需要量化的隐性成本,因为它需要从工程团队的冲刺速度中扣除。在缺乏此类数据的情况下,工程团队通常会 “用手指在空中比划 ”出运营工作的时间百分比,而我们都知道,这种百分比是模糊的,而且因团队而异。有了这种分析,你就能更准确地分配运营工作的时间,同时又不会影响团队的产品承诺或让个人疲于奔命。
总结
虽然本文试图为你提供一些能力,以启动你的 GenAI 分析之旅,将其转化为操作票据,更重要的是,我希望你在有效使用 LLM 来加速分析过程中学到的一些真知灼见能够应用到运营票据分析以外的许多其他领域。