原文:Generative AI with LangChain
译者:飞龙
六、开发软件
虽然这本书是关于将生成式人工智能,特别是大型语言模型(LLMs)集成到软件应用程序中,但在本章中,我们将讨论如何利用 LLMs 来帮助软件开发。这是一个重要的主题,软件开发被几家咨询公司如 KPMG 和麦肯锡的报告所强调,是受生成式人工智能影响最大的领域之一。我们首先讨论 LLMs 如何帮助编码任务,并概述我们在自动化软件工程师方面取得了多大进展。我们还将讨论许多最新进展和新模型。然后,我们将尝试几种模型,定性评估生成的代码。接下来,我们将实现一个完全自动化的软件开发任务代理。我们将讨论设计选择,并展示我们在 LangChain 中仅用几行 Python 代码实现的代理实现的一些结果。这种方法有许多可能的扩展,我们也将讨论。在整个章节中,我们将探讨软件开发的不同方法,您可以在书籍的 Github 存储库中的software_development目录中找到。主要部分包括:
-
软件开发和人工智能
-
使用 LLMs 编写代码
-
自动化软件开发
我们将从使用人工智能进行软件开发的最新技术概述开始本章。
软件开发和人工智能
强大的 AI 系统如 ChatGPT 的出现引发了人们对将 AI 作为辅助软件开发人员的工具的极大兴趣。根据 KPMG 在 2023 年 6 月发布的一份报告,大约 25%的软件开发任务可以自动化处理。同月的麦肯锡报告强调了软件开发作为一个领域,在这个领域,生成式 AI 可以在成本降低和效率提升方面产生重大影响。利用人工智能来辅助编程的想法并不新鲜,但随着计算和人工智能的进步,这一想法迅速发展。正如我们将看到的那样,这两个领域是相互交织在一起的。20 世纪 50 年代和 60 年代早期的语言和编译器设计工作旨在使软件编写更加容易。1955 年,由 Grace Hopper 在雷明顿兰德公司设计的数据处理语言FLOW-MATIC(又称:商业语言版本 0)从类似英语的语句中生成代码。同样,20 世纪 60 年代在达特茅斯学院创建的BASIC(初学者通用符号指令代码)等编程语言旨在使在解释环境中编写软件更加容易。其他努力进一步简化和标准化了编程语法和接口。20 世纪 70 年代初由 J. Paul Morrison 发明的基于流程的编程(FBP)范式允许将应用程序定义为连接的黑盒进程,通过消息传递交换数据。视觉低代码或无代码平台也遵循了相同的模式,其中 LabVIEW 等流行的支持者被广泛用于电子工程中的系统设计,而 KNIME 提取、转换、加载工具则用于数据科学。20 世纪 80 年代出现的专家系统是最早尝试通过 AI 自动编码的努力之一。作为一种狭义 AI 形式,它们专注于编码领域知识和规则以提供指导。这些规则将以非常特定的语法制定,并在规则引擎中执行。这些编码了编程任务的最佳实践,如调试,尽管它们的实用性受到了对细致的基于规则的编程的需求的限制。对于软件开发,从命令行编辑器如 ed(1969 年),到 vim 和 emacs(20 世纪 70 年代),再到今天的集成开发环境(IDEs)如 Visual Studio(1997 年首次发布)和 PyCharm(自 2010 年以来),这些工具帮助开发人员编写代码,导航复杂项目,重构,进行高亮显示,设置和运行测试。IDE 还集成并提供来自代码验证工具的反馈,其中一些工具自 20 世纪 70 年代以来就存在。著名的 Lint 由贝尔实验室的 Stephen Curtis Johnson 于 1978 年编写,可以标记错误,风格错误和可疑结构。许多工具应用形式方法;然而,机器学习已经应用了包括遗传编程和基于神经网络的方法在内的方法至少 20 年。在本章中,我们将看到使用深度神经网络,特别是 transformers 来分析代码的进展。这将我们带到了当今,模型已经被训练出根据自然语言描述(在编码助手或聊天机器人中)或一些代码输入(完成)生成完整或部分程序的能力。
当今
DeepMind 的研究人员分别在《自然》和《科学》杂志上发表了两篇论文,这些论文代表了使用人工智能来改变基础计算的重要里程碑,特别是使用强化学习来发现优化算法。2022 年 10 月,他们发布了由他们的模型AlphaTensor发现的用于矩阵乘法问题的算法,这可以加速深度学习模型所需的这种基本计算,也适用于许多其他应用。AlphaDev发现了集成到广泛使用的 C++库中的新型排序算法,提高了数百万开发人员的性能。它还泛化了其能力,发现了一种比现在每天使用数十亿次的哈希算法快 30%的算法。这些发现展示了 AlphaDev 超越人类优化算法并解锁在更高编程级别难以实现的优化的能力。他们的模型AlphaCode于 2022 年 2 月发表的一篇论文中展示了一个由人工智能驱动的编码引擎,以与普通程序员相当的速度创建计算机程序。他们报告了在不同数据集上的结果,包括 HumanEval 等,我们将在下一节中介绍。DeepMind 的研究人员强调了大规模抽样候选算法池和选择的过滤步骤。该模型被誉为突破性成就;然而,他们方法的实用性和可扩展性尚不清楚。如今,像 ChatGPT 和微软的 Copilot 这样的新代码 LLM 是非常受欢迎的生成式人工智能模型,拥有数百万用户和显著的提高生产力的能力。LLM 可以处理与编程相关的不同任务,例如:
-
代码补全:此任务涉及根据周围代码预测下一个代码元素。它通常用于集成开发环境(IDE)中,以帮助开发人员编写代码。
-
代码摘要/文档:此任务旨在为给定的源代码块生成自然语言摘要或文档。这个摘要帮助开发人员理解代码的目的和功能,而无需阅读实际代码。
-
代码搜索:代码搜索的目标是根据给定的自然语言查询找到最相关的代码片段。这项任务涉及学习查询和代码片段的联合嵌入,以返回代码片段的预期排名顺序。在文本中提到的实验中,神经代码搜索专注于这一点。
-
Bug 查找/修复:AI 系统可以减少手动调试工作量,增强软件的可靠性和安全性。许多程序员很难找到代码中的错误和漏洞,尽管存在着代码验证工具的典型模式。作为替代方案,LLMs 可以发现代码中的问题,并在提示时进行修正。因此,这些系统可以减少手动调试工作量,帮助提高软件的可靠性和安全性。
-
测试生成:类似于代码补全,LLMs 可以生成单元测试(参见 Bei Chen 等人,2022 年)和其他类型的测试,增强代码库的可维护性。
AI 编程助手将早期系统的互动性与尖端自然语言处理相结合。开发人员可以用简单的英语查询错误或描述所需的功能,接收生成的代码或调试提示。然而,围绕代码质量、安全性和过度依赖仍存在风险。在保持人类监督的同时找到计算机增强的正确平衡是一个持续的挑战。让我们看看目前用于编码的 AI 系统的性能,特别是代码 LLMs。
代码 LLMs
出现了许多 AI 模型,每个模型都有其优势和劣势,它们不断竞争以改进并提供更好的结果。这种比较应该概述一些最大和最受欢迎的模型:
| 模型 | 读取文件 | 运行代码 | 标记 |
|---|---|---|---|
| ChatGPT; GPT 3.5/4 | 否 | 否 | 最多 32k |
| ChatGPT: 代码解释器 | 是 | 是 | 最多 32k |
| 克劳德 2 | 是 | 否 | 100k |
| 巴德 | 否 | 是 | 1k |
| 必应 | 是 | 否 | 32k |
图 6.1:软件开发的公共聊天界面。
尽管这种竞争通过提供更广泛的选择范围使用户受益,但这也意味着仅依赖 ChatGPT 可能不再是最佳选择。用户现在面临选择为每个特定任务选择最合适的模型的决定。最新的浪潮利用机器学习和神经网络实现更灵活的智能。强大的预训练模型如 GPT-3 可以实现上下文感知、对话支持。深度学习方法还赋予了 bug 检测、修复建议、自动化测试工具和代码搜索更多的能力。微软的 GitHub Copilot 基于 OpenAI 的 Codex,利用开源代码实时建议完整的代码块。根据 2023 年 6 月的 Github 报告,开发人员接受 AI 助手的建议约占 30%,这表明该工具可以提供有用的建议,经验不足的开发人员受益最多。
Codex 是由 OpenAI 开发的模型。它能够解析自然语言并生成代码,为 GitHub Copilot 提供动力。作为 GPT-3 模型的后代,它已经在 GitHub 上公开可用的代码上进行了微调,来自 5400 万个 GitHub 仓库的 159GB Python 代码,用于编程应用。
为了说明在创建软件方面取得的进展,让我们看一下基准测试中的定量结果:Codex 论文中介绍的HumanEval 数据集(“评估基于代码训练的大型语言模型”,2021 年)旨在测试大型语言模型根据其签名和文档字符串完成函数的能力。它评估了从文档字符串合成程序的功能正确性。该数据集包括 164 个编程问题,涵盖语言理解、算法和简单数学等各个方面。其中一些问题与简单的软件面试问题相当。HumanEval 上的一个常见指标是 pass@k(pass@1)- 这指的是在为每个问题生成 k 个代码样本时的正确样本比例。以下表总结了 AI 模型在 HumanEval 任务上的进展(来源:苏里亚·古纳塞卡等人,“只需教科书”,2023 年;arxiv.org/pdf/2306.11644.pdf):
图 6.2:编码任务基准上的模型比较(HumanEval 和 MBPP)。性能指标是自报告的。此表仅包括模型,而不包括其他方法,例如推理策略。Llama2 在 HumanEval 上的自报告性能为 29.9%。
请注意,大多数 LLM 模型训练中使用的数据包括一定量的源代码。例如,由 EleutherAI 的 GPT-Neo 策划的 Pile 数据集,用于训练 GPT 模型的开源替代品,GPT-Neo,包括来自 Github 约 11%的代码(102.18GB)。Pile 被用于 Meta 的 Llama、Yandex 的 YaLM 100B 等许多模型的训练。尽管 HumanEval 已广泛用作代码 LLM 的基准测试,但还有许多编程基准测试。以下是一个给 Codex 的高级计算机科学测试的示例问题和回应:
图 6.3:CS2 考试中的一个问题(左)和 Codex 的回应(来源“我的 AI 想知道这会不会出现在考试中:测试 OpenAI 的 Codex 对 CS2 编程练习的影响”詹姆斯·芬尼-安斯利等人,2023 年)。
有许多有趣的研究揭示了人工智能帮助软件开发人员的能力,或者扩展了这种能力,如下表所总结的:
| 作者 | 出版日期 | 结论 | 任务 | 分析的模型/策略 |
|---|---|---|---|---|
| Abdullah Al Ishtiaq 和其他人 | 2021 年 4 月 | 像 BERT 这样的预训练语言模型可以通过改进语义理解来增强代码搜索。 | 代码搜索 | BERT |
| Mark Chen 等人(OpenAI) | 2021 年 7 月 | 对 Codex 进行代码生成评估,显示了推进程序合成的潜力。 | 代码生成 | Codex |
| Ankita Sontakke 和其他人 | 2022 年 3 月 | 即使是最先进的模型也会产生质量低劣的代码摘要,表明它们可能不理解代码。 | 代码摘要 | Transformer 模型 |
| Bei Chen 等人(微软) | 2022 年 7 月 | CODE-T 利用 LLM 自动生成测试用例,减少人力投入,提高代码评估。它在 HumanEval pass@1 中达到了 65.8%。 | 代码生成,测试 | CODET |
| Eric Zelikman 等人(斯坦福大学) | 2022 年 12 月 | Parsel 框架使 LLM 能够分解问题并利用优势,提高了在分层推理上的表现。 | 程序合成,规划 | Codex |
| James Finnie-Ansley 和其他人 | 2023 年 1 月 | Codex 在高级 CS2 编程考试中表现优异,超过大多数学生。 | CS2 编程 | Codex |
| Yue Liu 和其他人 | 2023 年 2 月 | 现有的自动代码生成在鲁棒性和可靠性方面存在局限性。 | 代码生成 | 5 个 NMT 模型 |
| Mingyang Geng 和其他人 | 2023 年 2 月 | 两阶段方法显著提高了代码摘要的有效性。 | 代码摘要 | LLM + 强化学习 |
| Noah Shinn 等人 | 2023 年 3 月 | Reflexion 通过口头反思实现试错学习,实现了 91% 的 HumanEval pass@1。 | 编码,推理 | Reflexion |
| Haoye Tian 和其他人 | 2023 年 4 月 | ChatGPT 在编程辅助方面表现出潜力,但在鲁棒性、泛化性和注意力持久性方面存在局限。 | 代码生成,程序修复,代码摘要 | ChatGPT |
| Chuqin Geng 和其他人 | 2023 年 4 月 | ChatGPT 在入门编程教育中展示出令人印象深刻的能力,但作为学生只能获得 B- 的成绩。 | 入门函数式编程课程 | ChatGPT |
| Xinyun Chen 和其他人 | 2023 年 4 月 | 自我调试技术使语言模型能够识别和纠正生成代码中的错误。 | 代码生成 | Self-Debugging |
| Masum Hasan 和其他人 | 2023 年 4 月 | 将文本转换为中间形式语言使得从描述中更有效地生成应用程序代码成为可能。 | 应用程序代码生成 | Seq2seq 网络 |
| Anis Koubaa 和其他人 | 2023 年 5 月 | ChatGPT 在复杂编程问题上表现不佳,尚不适合完全自动化编程。它的表现远远不如人类程序员。 | 编程问题解决 | ChatGPT |
| Wei Ma 和其他人 | 2023 年 5 月 | ChatGPT 理解代码语法,但在分析动态代码行为方面受限。 | 复杂代码分析 | ChatGPT |
| Raymond Li 等人(BigCode) | 2023 年 5 月 | 推出了由 1 万亿 GitHub 令牌训练的 15.5B 参数 StarCoder,实现了 40%的 HumanEval pass@1 | 代码生成,多种语言 | StarCoder |
| Amos Azaria 和其他人 | 2023 年 6 月 | ChatGPT 存在错误和局限性,因此输出应该经过独立验证。最好由精通领域的专家使用。 | 通用功能和限制 | ChatGPT |
| Adam Hörnemalm | 2023 年 6 月 | ChatGPT 提高了编码和规划的效率,但在沟通方面存在困难。开发人员希望有更多集成的工具。 | 软件开发 | ChatGPT |
| Suriya Gunasekar 等人(微软) | 2023 年 6 月 | 高质量的数据使较小的模型能够匹配较大的模型,改变了缩放定律 | 代码生成 | Phi-1 |
图 6.2:用于编程任务的人工智能文献综述。出版日期主要指的是预印本发布日期。
这只是研究的一个小子集,但希望这有助于揭示该领域的一些发展。最近的研究探讨了 ChatGPT 如何支持程序员的日常工作活动,如编码、沟通和规划。其他研究描述了新模型(如 Codex、StarCoder 或 Phi-1)或规划或推理执行这些模型的方法。最近,微软研究院的 Suriya Gunasekar 等人在 2023 年发表的论文“只需教科书”介绍了 phi-1,这是一个基于 Transformer 的 1.3B 参数语言模型用于代码。该论文展示了高质量数据如何使较小的模型能够匹配较大的模型进行代码任务。作者从 The Stack 和 StackOverflow 的 3 TB 代码语料库开始。一个大型语言模型(LLM)对其进行过滤,选择了 6B 高质量标记。另外,GPT-3.5 生成了 1B 标记,模仿教科书风格。一个小的 1.3B 参数模型 phi-1 在这些过滤数据上进行了训练。然后,phi-1 在 GPT-3.5 合成的练习上进行微调。结果显示,phi-1 在 HumanEval 和 MBPP 等基准测试中与其大小超过 10 倍的模型的性能相匹配或超过。核心结论是高质量数据显著影响模型性能,可能改变缩放规律。数据质量应优先于蛮力缩放。作者通过使用较小的 LLM 来选择数据,而不是昂贵的完整评估,降低了成本。递归地过滤和在选定数据上重新训练可能会带来进一步的改进。重要的是要意识到,在短代码片段中,任务规范直接转换为代码,必须按照特定任务的顺序发出正确的 API 调用,而生成完整程序则依赖于对任务、背后的概念以及如何完成任务的深入理解和推理。然而,推理策略对于短代码片段也能产生很大的影响,正如 Noah Shinn 等人在 2023 年发表的论文“反思:语言代理与口头强化学习”所示。作者提出了一个名为 Reflexion 的框架,使 LLM 代理(在 LangChain 中实现)能够通过口头强化的试错学习快速有效地学习。代理人口头反思任务反馈信号,并将其反思文本存储在一个情节性记忆缓冲区中,这有助于代理人在随后的试验中做出更好的决策。作者展示了 Reflexion 在改善顺序决策、编码和语言推理等各种任务中决策制定的有效性。Reflexion 有潜力在特定任务中胜过以往的最先进模型,如其在 HumanEval 编码基准测试中的 91% pass@1 准确率所示,这超过了以往任何已发布的方法,包括 GPT-4 的 67%(由 OpenAI 报告)。
展望
展望未来,多模态人工智能的进步可能会进一步发展编程工具。能够处理代码、文档、图像等的系统可以实现更自然的工作流程。人工智能作为编程伙伴的未来光明,但需要人类创造力和计算机增强生产力的深思熟虑协调。虽然有前景,但有效利用人工智能编程助手需要通过研讨会建立标准,为任务创建有用的提示和预提示。专注的培训确保生成的代码得到正确验证。将人工智能整合到现有环境中而不是独立的浏览器可以提高开发者体验。随着研究的继续进行,人工智能编程助手提供了增加生产力的机会,如果能够深思熟虑地实施并了解其局限性。在预训练阶段出现了法律和伦理问题,特别是涉及使用内容创建者数据训练模型的权利。版权法和公平使用豁免在与机器学习模型使用受版权保护数据相关的讨论中备受争议。例如,自由软件基金会对 Copilot 和 Codex 生成的代码片段可能侵犯版权提出了担忧。他们质疑在公共存储库上进行训练是否属于公平使用,开发者如何识别侵权代码,机器学习模型的性质是否为可修改源代码或训练数据的编译,以及机器学习模型的可版权性。此外,GitHub 的一项内部研究发现,少部分生成的代码直接复制了训练数据,包括错误的版权声明。OpenAI 认识到围绕这些版权问题的法律不确定性,并呼吁权威解决。这种情况被比作了关于 Google Books 中文本片段公平使用的 Authors Guild, Inc. v. Google, Inc.法庭案例。理想情况下,我们希望能够在不依赖收费云服务的情况下完成这一切,并且不会被迫放弃我们的数据所有权。然而,外包人工智能非常方便,因此我们只需实现提示和如何与客户发出调用的策略。许多开源模型在编码任务上取得了令人瞩目的进展,并且在其开发过程中具有完全透明和开放的优势。它们大多是在发布了宽松许可证下的代码上进行训练的,因此不会带来与其他商业产品相同的法律问题。这些系统对编码本身以及软件开发周围生态系统产生了更广泛的影响。例如,ChatGPT 的出现导致了程序员流行的 Stack Overflow 问答论坛的大量流量下降。在最初阻止使用大型语言模型(LLMs)生成的任何贡献后,Stack Overflow 推出了 Overflow AI,为 Stack 产品带来了增强的搜索、知识吸收和其他人工智能功能。新的语义搜索将使用 Stack 的知识库提供智能、对话式的结果。像 Codex 和 ChatGPT 这样的大型语言模型在解决常见问题的代码生成方面表现出色,但在新问题和长提示方面表现不佳。最重要的是,ChatGPT 在理解语法方面表现良好,但在分析动态代码行为方面存在局限性。在编程教育中,人工智能模型超越了许多学生,但仍有很大的改进空间,然而,它们尚未达到能够取代程序员和人类智慧的水平。仔细审查是必要的,因为错误可能会发生,专家监督至关重要。编码中人工智能工具的潜力令人鼓舞,但在稳健性、泛化性、注意力跨度和真正的语义理解方面仍存在挑战。需要进一步发展以确保可靠和透明的人工智能编程工具,可以增强开发者,使他们能够更快地编写代码并减少错误。在接下来的部分中,我们将看到如何使用 LLMs 生成软件代码以及如何从 LangChain 内部执行这些代码。
使用 LLMs 编写代码
让我们从应用模型来为我们编写代码开始。我们可以使用一些公开可用的模型来生成代码。我之前列举了一些示例,比如 ChatGPT 或 Bard。从 LangChain,我们可以调用 OpenAI 的 LLMs,PaLM 的 code-bison,或者通过 Replicate、HuggingFace Hub 等多种开源模型,或者通过本地模型,比如 Llama.cpp、GPT4All 或 HuggingFace Pipeline 集成。让我们来看看 StarCoder。这个截图展示了模型在 HuggingFace Spaces 上的游乐场:
图 6.3:StarCoder 模型游乐场。我们可以在 StarCoder、StarCoderPlus、StarCoderBase 之间进行选择。此处可访问 huggingface.co/spaces/bigc…
请注意,正如描述所说,StarCoder 模型没有经过指令调整,这意味着我们不能给它任务去执行。我们不能说“编写一个包含…”,但我们可以要求它完成一个文本,就像截图中展示的那样,我们用“# dataclass of customer including an alphanumeric id, a name, and a birthday”提示模型 – 让我们试试这个!我们可以切换温度、最大新标记、top-n 和重复惩罚的设置。对于任何非平凡的事情,我们需要调整最大新标记的设置。我得到了这段代码,为我们的假想客户提供了一个有用的数据模型:
@dataclass(frozen=True) # frozen means that the class cannot be modified after creation. This is useful for storing data in databases or other persistent storages (e.g., files).
class Customer:
cust_id : str = field() # this defines how to create instances from the class; it's called "field" because we're creating fields within our object/instance
firstname : str = "" # these are default values if no value was provided when instantiating objects using this class
lastname : str = "" # note that there can only ever exist one instance variable per line - you'll get errors otherwise!
def __post_init__(self):
"""This method runs automatically once upon instantiation"""
self._validate_cust_id() # validate input before saving anything into memory
self.__set_fullname__() # set full name based on given inputs
@property # getter function used by Python interpreter whenever trying to access property directly instead of through. notation e.g.: myobj.firstname vs just myobj.firstname()
def fullname(self)->str:
return f"{self.lastname}, {self.firstname}"
## private methods below here
### validation functions go here
def _validate_cust_id(self):
assert len(self.cust_id)==8, \
ValueError("Customer ID must have exactly eight characters")
try:
int(self.cust_id[0]) == True
except Exception as err:
raise TypeError('First character of CustID should be numeric') from None
#### setter functions go here
def __set_fullname__(self):
'''sets the customers' full name'''
names = [x.strip().title() for x in re.split("[\W]+", self.firstname)] + [''] * max((1,-len([y.strip().title() for y in re.split("[\W]+", self.lastname)])) )
print(names)
self.firstname=' '.join(filter(None,[n[:3]for n in reversed(names)])+[''])
self.lastname =''.join([' '*(4-len(l))+' '+ l[-2:]if not l=='I' else '' for l in filter(None,[n[:-2].upper()+','*bool(re.search('[aeiou]',n))+n[-2:].upper() for n in reversed(names)])]).rstrip(',')
这里缺少了导入部分,通常应该在注释提示之前,所以我不能因此责怪模型,我们需要在顶部添加这两行:
import re
from dataclasses import dataclass, field
当我们按原样运行时,这不会引发错误,这令人印象深刻,但是,当我们更详细地查看时,有一些事情并不理想,甚至是错误的。乍一看,我个人不喜欢注释风格和缺乏文档字符串。代码中留下了一些print()语句;通常这是为了调试目的而做的。这不应该出现在生产代码中。将空字符串作为firstname和lastname的默认值可能并不理想。我希望客户 ID 是基于哈希函数分配的 - 我不明白为什么 ID 应该在验证属性的验证函数中强制为 8 个字符。这段代码还有更多问题影响其正确性和可读性,但代码中还有一些更为关键的问题,包括尝试编写只读属性。在数据类中的firstname和lastname - 在数据类中,frozen=True意味着属性在创建后无法更改。使用非标准语法中的正则表达式从firstname和lastname创建变量名的逻辑至少是奇怪的 - 且不正确。姓氏在这个过程中被丢弃。对反转名称的过滤也是非常可疑的。我就说到这里。这给了我们一些好的想法和一个开始的结构,但这不是生产代码。它甚至不起作用。您可以在书的 Github 存储库中看到这段代码作为customer.py。让我们再试一次。也许我们一开始就走错了。我们以初学者预期的错误语法开始了一个代码片段,并期望能够运行的代码。这是不现实的。让我们再试一次,并从一个更符合标准的提示开始:
"""Customer data model.
Here we implement our data model for customers, a dataclass with
fields firstname, lastname, customer_id (a hash).
"""
我们得到了以下结果:
@dataclass(frozen=True) # frozen means that the object cannot be modified after creation!
class Customer:
"""A class representing one single customer."""
firstname: str = ""
lastname: str = ""
id_: int = field(default=-1024, init=False)
def __post_init__(self):
self.__hash__()
@property
def fullname(self)->str:
return f"{self.firstname} {self.lastname}"
def __eq__(self, other:"Customer")->bool:
if isinstance(other, type(self)):
return all([
getattr(self, attr)==getattr(other,attr)
for attr in ["firstname", "lastname"]])
else:
raise TypeError("Cannot compare different types")
def __lt__(self, other:"Customer"):
try:
assert len({type(x) for x in [self, other]})==1
if not hasattr(self,"fullname"):
setattr(self, 'fullname',f'{self.firstname}{self.lastname}')
if not hasattr(other,'fullname'):
setattr(other, 'fullname',f'{other.firstname}{other.lastname}')
return self.fullname < other.fullname
except AssertionError as e:
print('Error:',e )
def __repr__(self):
return f"Customer('{self.firstname}', '{self.lastname}','{hex(abs(self._customer_id))[-6:]})"
def __hash__(self):
hsh = abs(hash((self.firstname+self.lastname))) % ((sys.maxsize + 1)*2)+ sys.maxsize*3
self.id_=hsh
return hsh
很高兴看到客户 ID 像预期的那样使用哈希创建。有一些用于比较两个不同客户对象的样板代码。然而,再次出现了问题,与之前类似的问题。首先,缺少导入,我不明白我们的提示之后会在文件开头找到一个模块文档字符串,然后导入会紧随其后。其次,又一次尝试在类初始化后设置一个应该是冻结的属性。修复这两个问题后,我们得到了我们的第一个Customer(),然后出现了一个问题,客户 ID 被引用为错误的名称。修复这个问题后,我们可以初始化我们的客户,查看属性,并比较一个客户和另一个客户。我可以看到这种方法开始对编写样板代码变得有用。您可以在书的 Github 存储库中看到这段代码作为customer2.py。让我们尝试一个指令调整模型,这样我们就可以给它任务!StarChat,基于 StarCoder,可以在 HuggingFace 的huggingface.co/spaces/HuggingFaceH4/starchat-playground下找到。此屏幕截图显示了 StarChat 的示例:
图 6.4:StarChat 在 Python 中实现计算素数的函数。请注意,截图中并非所有代码都可见。
您可以在 Github 上找到完整的代码清单。对于这个在第一年计算机科学课程中应该很有名的例子,不需要导入任何内容。算法的实现很简单。它立即执行并给出预期的结果。在 LangChain 中,我们可以像这样使用HuggingFaceHub集成:
from langchain import HuggingFaceHub
llm = HuggingFaceHub(
task="text-generation",
repo_id="HuggingFaceH4/starchat-alpha",
model_kwargs={
"temperature": 0.5,
"max_length": 1000
}
)
print(llm(text))
截至 2023 年 8 月,这个 LangChain 集成存在一些超时问题 - 希望这很快就会得到解决。我们不打算在这里使用它。正如之前提到的,Llama2 并不是编码的最佳模型之一,通过率约为 29,但是,我们可以在 HuggingFace 聊天中尝试一下:
图 6.5:HuggingFace 聊天与 Llama2 在 huggingface.co/chat/
请注意,这只是输出的开始。Llama2 找到了一个很好的实现,解释也很到位。干得好,StarCoder 和 Llama2! - 或者,这太容易了?有很多方法可以获得代码完成或生成。我们甚至可以尝试一个小型本地模型:
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
checkpoint = "Salesforce/codegen-350M-mono"
model = AutoModelForCausalLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
pipe = pipeline(
task="text-generation",
model=model,
tokenizer=tokenizer,
max_new_tokens=500
)
text = """
def calculate_primes(n):
\"\"\"Create a list of consecutive integers from 2 up to N.
For example:
>>> calculate_primes(20)
Output: [2, 3, 5, 7, 11, 13, 17, 19]
\"\"\"
"""
CodeGen 是 Salesforce AI Research 的一个模型。 CodeGen 350 Mono 在 HumanEval 中的通过率为 12.76%。截至 2023 年 7 月,发布了新版本的 CodeGen,只有 6B 参数,非常具有竞争力,性能达到 26.13%。这个最后的模型是在包含 C、C++、Go、Java、Javascript 和 Python 的 BigQuery 数据集上训练的,以及包含 5.5TB Python 代码的 BigPython 数据集。另一个有趣的小型模型是微软的 CodeBERT(2020),这是一个用于程序合成的模型,已经在 Ruby、Javascript、Go、Python、Java 和 PHP 上进行了训练和测试。由于这个模型是在 HumanEval 基准发布之前发布的,因此基准的性能统计数据不是初始出版物的一部分。我们现在可以直接从流水线中获取输出,就像这样:
completion = pipe(text)
print(completion[0]["generated_text"])
或者,我们可以通过 LangChain 集成来包装这个流水线:
llm = HuggingFacePipeline(pipeline=pipe)
llm(text)
这有点啰嗦。还有更方便的构造方法HuggingFacePipeline.from_model_id()。我得到了类似于 StarCoder 输出的东西。我不得不添加一个import math,但这个函数有效。我们可以在 LangChain 代理中使用这个管道,但请注意,这个模型没有经过指导,所以你不能给它任务,只能完成任务。你也可以使用所有这些模型进行代码嵌入。其他经过指导并可用于聊天的模型,可以充当您的技术助手,帮助提供建议,文档和解释现有代码,或将代码翻译成其他编程语言 - 对于最后一个任务,它们需要在这些语言中经过足够的样本训练。请注意,这里采取的方法有点天真。例如,我们可以采集更多样本并在它们之间进行选择,就像我们讨论过的一些论文中那样。现在让我们尝试为代码开发实现一个反馈循环,其中我们验证并运行代码,并根据反馈进行更改。
自动化软件开发
现在我们将编写一个完全自动化的代理,它将为我们编写代码并根据反馈修复任何问题。在 LangChain 中,我们有几个用于执行代码的集成,比如LLMMathChain,它执行 Python 代码来解决数学问题,以及BashChain,它执行 Bash 终端命令,可以帮助处理系统管理任务。然而,这些是用于通过代码解决问题而不是创建软件。不过,这种方法可能效果很好。
from langchain.llms.openai import OpenAI
from langchain.agents import load_tools, initialize_agent, AgentType
llm = OpenAI()
tools = load_tools(["python_repl"])
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
result = agent("What are the prime numbers until 20?")
print(result)
我们可以看到在 OpenAI 的 LLM 和 Python 解释器之间,质数计算是如何在内部很好地处理的:
Entering new AgentExecutor chain...
I need to find a way to check if a number is prime
Action: Python_REPL
Action Input:
def is_prime(n):
for i in range(2, n):
if n % i == 0:
return False
return True
Observation:
Thought: I need to loop through the numbers to check if they are prime
Action: Python_REPL
Action Input:
prime_numbers = []
for i in range(2, 21):
if is_prime(i):
prime_numbers.append(i)
Observation:
Thought: I now know the prime numbers until 20
Final Answer: 2, 3, 5, 7, 11, 13, 17, 19
Finished chain.
{'input': 'What are the prime numbers until 20?', 'output': '2, 3, 5, 7, 11, 13, 17, 19'}
我们已经得出了关于质数的正确答案,然而,目前还不太清楚这种方法在构建软件产品方面的可扩展性,其中涉及模块、抽象、关注点分离和可维护代码。目前有一些有趣的实现方法。MetaGPT 库通过代理模拟来处理这个问题,其中不同的代理代表公司或 IT 部门中的工作角色:
from metagpt.software_company import SoftwareCompany
from metagpt.roles import ProjectManager, ProductManager, Architect, Engineer
async def startup(idea: str, investment: float = 3.0, n_round: int = 5):
"""Run a startup. Be a boss."""
company = SoftwareCompany()
company.hire([ProductManager(), Architect(), ProjectManager(), Engineer()])
company.invest(investment)
company.start_project(idea)
await company.run(n_round=n_round)
这是一个非常鼓舞人心的代理模拟用例。Andreas Kirsch 的 llm-strategy 库使用装饰器模式为数据类生成代码。自动软件开发的其他示例包括 AutoGPT 和 BabyGPT,尽管这些通常会陷入循环或因失败而停止。像这样的简单规划和反馈循环可以在 LangChain 中使用 ZeroShot 代理和规划器实现。Paolo Rechia 的 Code-It 项目和 AntonOsika 的 Gpt-Engineer 都遵循这样的模式,如 Code-It 的图表所示:
图 6.6:Code-It 控制流(来源:github.com/ChuloAI/cod…
这些步骤中的许多都包括发送给 LLMs 的具体提示,指示其拆分项目或设置环境。通过使用所有工具实现完整的反馈循环是非常令人印象深刻的。在 LangChain 中,我们可以以不同的方式实现相对简单的反馈循环,例如使用PlanAndExecute链,ZeroShotAgent或BabyAGI。让我们选择PlanAndExecute!主要思想是设置一个链并执行它,目的是编写软件,就像这样:
llm = OpenAI()
planner = load_chat_planner(llm)
executor = load_agent_executor(
llm,
tools,
verbose=True,
)
agent_executor = PlanAndExecute(
planner=planner,
executor=executor,
verbose=True,
handle_parsing_errors="Check your output and make sure it conforms!",
return_intermediate_steps=True
)
agent_executor.run("Write a tetris game in python!")
我在这里省略了导入部分,但你可以在书籍的 Github 仓库中找到完整的实现。其他选项也可以在那里找到。这还有一些其他要点,但根据我们给出的指示,这已经可以编写一些代码了。我们需要的一件事是为语言模型提供清晰的指令,以便以某种形式编写 Python 代码:
DEV_PROMPT = (
"You are a software engineer who writes Python code given tasks or objectives. "
"Come up with a python code for this task: {task}"
"Please use PEP8 syntax and comments!"
)
software_prompt = PromptTemplate.from_template(DEV_PROMPT)
software_llm = LLMChain(
llm=OpenAI(
temperature=0,
max_tokens=1000
),
prompt=software_prompt
)
我们需要确保选择一个能够生成代码的模型。我们已经讨论了我们可以选择的模型。我选择了一个较长的上下文,这样我们就不会在函数中间被切断,以及一个较低的温度,这样它就不会变得太疯狂。然而,单独使用这个模型无法将其存储到文件中,也无法对其进行有意义的操作,并根据执行的反馈进行操作。我们需要想出代码然后测试它,看看它是否有效。让我们看看我们如何实现这一点 - 这是传递给代理执行器的tools参数,让我们看看这是如何定义的!
software_dev = PythonDeveloper(llm_chain=software_llm)
code_tool = Tool.from_function(
func=software_dev.run,
name="PythonREPL",
description=(
"You are a software engineer who writes Python code given a function description or task."
),
args_schema=PythonExecutorInput
)
PythonDeveloper 类包含了关于接受任何形式任务并将其转换为代码的所有逻辑。我不会在这里详细介绍,但主要思想在这里:
class PythonDeveloper():
"""Execution environment for Python code."""
def __init__(
self,
llm_chain: Chain,
):
self. llm_chain = llm_chain
def write_code(self, task: str) -> str:
return self.llm_chain.run(task)
def run(
self,
task: str,
) -> str:
"""Generate and Execute Python code."""
code = self.write_code(task)
try:
return self.execute_code(code, "main.py")
except Exception as ex:
return str(ex)
def execute_code(self, code: str, filename: str) -> str:
"""Execute a python code."""
try:
with set_directory(Path(self.path)):
ns = dict(__file__=filename, __name__="__main__")
function = compile(code, "<>", "exec")
with redirect_stdout(io.StringIO()) as f:
exec(function, ns)
return f.getvalue()
我再次略过了一些部分。这里的错误处理非常简单。在 Github 上的实现中,我们可以区分我们遇到的不同类型的错误,比如这些:
-
ModuleNotFoundError: 这意味着代码尝试使用我们未安装的包。我已经实现了安装这些包的逻辑。 -
NameError: 使用不存在的变量名。 -
SyntaxError: 代码经常不关闭括号或根本不是代码。 -
FileNotFoundError: 代码依赖不存在的文件。我发现有几次代码试图显示虚构的图像。 -
SystemExit: 如果发生更严重的情况导致 Python 崩溃。
我已经实现了为ModuleNotFoundError安装包的逻辑,并为其中一些问题提供了更清晰的消息。在缺少图像的情况下,我们可以添加一个生成图像模型来创建这些图像。将所有这些作为丰富的反馈返回给代码生成,会产生越来越具体的输出,例如:
Write a basic tetris game in Python with no syntax errors, properly closed strings, brackets, parentheses, quotes, commas, colons, semi-colons, and braces, no other potential syntax errors, and including the necessary imports for the game
Python 代码本身被编译并在子目录中执行,我们重定向 Python 执行的输出以捕获它 - 这两者都被实现为 Python 上下文。请谨慎在您的系统上执行代码,因为其中一些方法对安全性非常敏感,因为它们缺乏沙盒环境,尽管存在诸如 codebox-api、RestrictedPython、pychroot 或 setuptools 的 DirectorySandbox 等工具和框架,仅举几例供 Python 使用。所以让我们设置工具:
ddg_search = DuckDuckGoSearchResults()
tools = [
codetool,
Tool(
name="DDGSearch",
func=ddg_search.run,
description=(
"Useful for research and understanding background of objectives. "
"Input: an objective. "
"Output: background information about the objective. "
)
)
]
通过进行互联网搜索,确保我们正在实现与我们目标相关的内容是绝对值得的。我看过一些实现了石头、剪刀、布而不是俄罗斯方块的例子。我们可以定义额外的工具,比如将任务分解为函数的计划工具。你可以在仓库中看到这一点。每次以实现俄罗斯方块为目标来运行我们的代理执行器时,结果都会有些不同。我看到了几次搜索需求和游戏机制,以及几次生成和运行代码。pygame 库已安装。最终的代码片段并不是最终产品,但它会弹出一个窗口:
# This code is written in PEP8 syntax and includes comments to explain the code
# Import the necessary modules
import pygame
import sys
# Initialize pygame
pygame.init()
# Set the window size
window_width = 800
window_height = 600
# Create the window
window = pygame.display.set_mode((window_width, window_height))
# Set the window title
pygame.display.set_caption('My Game')
# Set the background color
background_color = (255, 255, 255)
# Main game loop
while True:
# Check for events
for event in pygame.event.get():
# Quit if the user closes the window
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Fill the background with the background color
window.fill(background_color)
# Update the display
pygame.display.update()
就语法而言,代码并不算太糟糕 - 我想提示可能有所帮助。然而,就功能而言,它与俄罗斯方块相去甚远。这种用于软件开发的全自动代理的实现仍然相当实验性。它也非常简单和基础,仅包括约 340 行 Python 代码,包括导入部分,你可以在 Github 上找到。我认为一个更好的方法可能是将所有功能分解为函数,并维护一个要调用的函数列表,这可以在所有后续代码生成中使用。我们也可以尝试测试驱动的开发方法,或者让人类给出反馈,而不是完全自动化的过程。然而,我们方法的一个优点是很容易调试,因为所有步骤,包括搜索和生成的代码,都写入了实现的日志文件中。让我们总结一下!
总结
在本章中,我们讨论了用于源代码的 LLMs,以及它们如何帮助开发软件。有很多领域可以从 LLMs 中受益,主要是作为编码助手。我们应用了一些模型来使用天真的方法生成代码,并对其进行了定性评估。我们看到建议的解决方案表面上看起来是正确的,但实际上并没有执行任务,或者充满了错误。这可能会特别影响初学者,并且可能对安全性和可靠性产生重大影响。在之前的章节中,我们看到 LLMs 被用作目标驱动的代理与外部环境进行交互。在编码中,编译器错误、代码执行的结果可以用来提供反馈,正如我们所见。或者,我们可以使用人类反馈或实施测试。让我们看看你是否记得本章的一些关键要点!
问题
请看看你是否能够从记忆中找到这些问题的答案。如果你对任何问题不确定,我建议你回到本章的相应部分查看:
-
LLMs 可以如何帮助软件开发?
-
如何衡量代码 LLM 在编码任务上的表现?
-
有哪些代码 LLM 模型可用,包括开源和闭源?
-
Reflexion 策略是如何工作的?
-
我们有哪些选项可用于建立写代码的反馈循环?
-
你认为生成式人工智能对软件开发有什么影响?
七、用于数据科学的 LLMs
本章讨论了生成式人工智能如何自动化数据科学。生成式人工智能,特别是大型语言模型(LLMs),有潜力加速各个领域的科学进展,尤其是通过提供对研究数据的高效分析和协助文献回顾过程。许多当前属于 AutoML 领域的方法可以帮助数据科学家提高生产力,并帮助使数据科学更具可重复性。我将首先概述数据科学中的自动化,然后我们将讨论生成式人工智能对数据科学的影响。接下来,我们将讨论如何以不同方式使用代码生成和工具来回答与数据科学相关的问题。这可以以模拟的形式进行,或者通过为数据集添加额外信息来丰富数据集。最后,我们将重点放在对结构化数据集的探索性分析上。我们可以设置代理来运行 SQL 或 Pandas 中的表格数据。我们将看到如何提出关于数据集的问题、关于数据的统计问题,或者要求可视化。在整个章节中,我们将使用 LLMs 进行数据科学的不同方法,您可以在书籍的 Github 存储库中的data_science目录中找到。主要章节包括:
-
自动化数据科学
-
代理可以回答数据科学问题
-
使用 LLMs 进行数据探索
让我们从讨论数据科学如何自动化以及其中的哪些部分开始,以及生成式人工智能将如何影响数据科学。
自动化数据科学
数据科学是一个结合了计算机科学、统计学和商业分析的领域,从数据中提取知识和见解。数据科学家使用各种工具和技术来收集、清洗、分析和可视化数据。然后他们利用这些信息帮助企业做出更好的决策。数据科学家的工作可能因具体角色和行业而异。然而,数据科学家可能执行的一些常见任务包括:
-
收集数据:数据科学家需要从各种来源收集数据,如数据库、社交媒体和传感器。
-
清洗数据:数据科学家需要清洗数据以消除错误和不一致性。
-
分析数据:数据科学家使用各种统计和机器学习技术来分析数据。
-
数据可视化:数据科学家使用数据可视化向利益相关者传达见解。
-
构建模型:数据科学家构建模型以预测未来结果或提出建议。
数据分析是数据科学的一个子集,专注于从数据中提取见解。数据分析师使用各种工具和技术来分析数据,但他们通常不构建模型。数据科学和数据分析之间的重叠之处在于两个领域都涉及与数据一起工作以提取见解。然而,数据科学家通常具有比数据分析师更丰富的技术技能。数据科学家也更有可能构建模型,并有时将模型部署到生产环境中。数据科学家有时将模型部署到生产环境中,以便实时做出决策,但在本讨论中,我们将避免自动部署模型。以下是总结数据科学和数据分析之间关键差异的表格:
| 特点 | 数据科学 | 数据分析 |
|---|---|---|
| 技术技能 | 更丰富 | 较少 |
| 机器学习 | 是 | 否 |
| 模型部署 | 有时 | 否 |
| 焦点 | 提取见解和构建模型 | 提取见解 |
图 7.1:数据科学和数据分析的比较。
两者之间的共同点是收集数据、清洗数据、分析数据、可视化数据,所有这些都属于提取见解的范畴。数据科学另外还涉及训练机器学习模型,通常更加注重统计学。在某些情况下,根据公司的设置和行业惯例,部署模型和编写软件可能会被添加到数据科学的任务列表中。自动数据分析和数据科学旨在自动化许多与处理数据相关的繁琐、重复的任务。这包括数据清洗、特征工程、模型训练、调优和部署。目标是通过实现更快的迭代和减少常见工作流程的手动编码,使数据科学家和分析师更加高效。许多这些任务可以在一定程度上自动化。数据科学的一些任务与我们在第六章“开发软件”中讨论的软件开发人员的任务相似,即编写和部署软件,尽管焦点更窄,主要集中在模型上。数据科学平台如 Weka、H2O、KNIME、RapidMiner 和 Alteryx 是统一的机器学习和分析引擎,可用于各种任务,包括预处理大量数据和特征提取。所有这些都配备了图形用户界面(GUI),具有集成第三方数据源和编写自定义插件的能力。KNIME 主要是开源的,但也提供了一个名为 KNIME Server 的商业产品。Apache Spark 是一种多功能工具,可用于数据科学中涉及的各种任务。它可用于清洁、转换、提取特征和准备大容量数据进行分析,还可用于训练和部署机器学习模型,无论是在流式场景中,当涉及实时决策或监控事件时。此外,在其最基本的层面上,用于科学计算的库,如 NumPy,可以用于自动化数据科学中涉及的所有任务。深度学习和机器学习库,如 TensorFlow、Pytorch 和 Scikit-Learn,可用于各种任务,包括创建复杂的机器学习模型以外的数据预处理和特征提取。编排工具,如 Airflow、Kedro 或其他工具,可以帮助完成所有这些任务,并包含许多与数据科学各个步骤相关的特定工具的集成。几个数据科学工具支持生成式人工智能。在第六章“开发软件”中,我们已经提到了 GitHub Copilot,但还有其他工具,如 PyCharm AI 助手,甚至更为重要的 Jupyter AI,它是 Project Jupyter 的一个子项目,为 Jupyter 笔记本带来了生成式人工智能。Jupyter AI 允许用户生成代码、修复错误、总结内容,甚至使用自然语言提示创建整个笔记本。该工具将 Jupyter 与各种提供商的 LLM 连接起来,使用户可以选择其首选模型和嵌入。Jupyter AI 优先考虑负责任的人工智能和数据隐私。底层提示、链和组件是开源的,确保透明度。它保存有关模型生成内容的元数据,使跟踪工作流中的 AI 生成代码变得容易。Jupyter AI 尊重用户数据隐私,仅在明确请求时才与 LLMs 联系,这是通过 LangChain 集成完成的。要使用 Jupyter AI,用户可以安装适用于 JupyterLab 的适当版本,并通过聊天界面或魔术命令界面访问它。聊天界面具有 Jupyternaut,一个可以回答问题、解释代码、修改代码和识别错误的 AI 助手。用户还可以从文本提示中生成整个笔记本。该软件允许用户教导 Jupyternaut 有关本地文件,并在笔记本环境中使用魔术命令与 LLMs 进行交互。它支持多个提供商,并为输出格式提供定制选项。文档中的此截图显示了聊天功能,Jupyternaut 聊天:
图 7.2:Jupyter AI – Jupyternaut 聊天。
很明显,像这样随时可以提问、创建简单函数或更改现有函数的聊天工具对数据科学家来说是一个福音。使用这些工具的好处包括提高效率,在模型构建或特征选择等任务中减少手动工作量,增强模型的可解释性,识别和解决数据质量问题,与其他 scikit-learn 管道(pandas_dq)集成,以及结果可靠性的整体改善。总的来说,自动化数据科学可以极大加速分析和机器学习应用程序的开发。它使数据科学家能够专注于过程的更高价值和创造性方面。为业务分析师民主化数据科学也是自动化这些工作流程的一个关键动机。在接下来的章节中,我们将依次研究这些步骤,并讨论如何自动化它们,以及我们将强调生成式人工智能如何为改善工作流程和创造效率带来贡献。
数据收集
自动化数据收集是在没有人为干预的情况下收集数据的过程。自动数据收集可以成为企业的有价值工具。它可以帮助企业更快速、更高效地收集数据,并可以释放人力资源专注于其他任务。通常,在数据科学或分析的背景下,我们将 ETL(提取、转换和加载)称为不仅从一个或多个来源获取数据(数据收集),还为特定用例准备数据的过程。ETL 过程通常遵循以下步骤:
-
Extract:数据从源系统中提取出来。可以使用各种方法进行提取,例如网页抓取,API 集成或数据库查询。
-
Transform:数据被转换为数据仓库或数据湖可以使用的格式。这可能涉及清洗数据,去重和标准化数据格式。
-
Load:数据被加载到数据仓库或数据湖中。可以使用各种方法进行加载,例如批量加载或增量加载。
ETL 和数据收集可以使用各种工具和技术完成,例如:
-
网页抓取:网页抓取是从网站提取数据的过程。可以使用各种工具进行此操作,例如 Beautiful Soup、Scrapy、Octoparse 等。
-
API(应用程序编程接口):这是软件应用程序相互通信的一种方式。企业可以使用 API 从其他公司收集数据,而无需构建自己的系统。
-
查询语言:任何数据库都可以作为数据源,包括 SQL(结构化查询语言)或无 SQL 类型。
-
机器学习:机器学习可以用于自动化数据收集过程。例如,企业可以使用机器学习识别数据中的模式,然后根据这些模式收集数据。
一旦数据被收集,就可以对其进行处理,以准备在数据仓库或数据湖中使用。ETL 过程通常会清理数据,删除重复项,并标准化数据格式。然后数据将被加载到数据仓库或数据湖中,数据分析师或数据科学家可以利用这些数据来获取对业务的洞察。有许多 ETL 工具,包括商业工具如 AWS Glue、Google Dataflow、Amazon Simple Workflow Service(SWF)、dbt、Fivetran、Microsoft SSIS、IBM InfoSphere DataStage、Talend Open Studio 或开源工具如 Airflow、Kafka 和 Spark。在 Python 中还有许多其他工具,太多了无法列出所有,例如用于数据提取和处理的 Pandas,甚至 celery 和 joblib,可以作为 ETL 编排工具。在 LangChain 中,与 Zapier 集成,这是一种可以用于连接不同应用程序和服务的自动化工具。这可以用于自动化从各种来源收集数据的过程。使用自动化 ETL 工具的一些好处包括:
-
提高准确性:自动化的 ETL 工具可以帮助提高数据提取、转换和加载过程的准确性。这是因为工具可以编程遵循一套规则和程序,可以帮助减少人为错误。
-
缩短上市时间:自动化的 ETL 工具可以帮助缩短将数据导入数据仓库或数据湖所需的时间。这是因为工具可以自动化 ETL 过程中涉及的重复任务,如数据提取和加载。
-
提高可扩展性:自动化的 ETL 工具可以帮助提高 ETL 过程的可扩展性。这是因为工具可以用于处理大量数据,并且可以轻松地按需扩展或缩减以满足业务需求。
-
提高合规性:自动化的 ETL 工具可以帮助提高符合 GDPR 和 CCPA 等法规的合规性。这是因为工具可以编程遵循一套规则和程序,可以帮助确保数据以符合法规的方式处理。
自动数据收集的最佳工具将取决于企业的具体需求。企业应考虑他们需要收集的数据类型、需要收集的数据量以及他们可用的预算。
可视化和探索性数据分析
自动化探索性数据分析(EDA)和可视化是指使用软件工具和算法自动分析和可视化数据,无需重大手动干预的过程。传统的 EDA 涉及手动探索和总结数据,以了解其各个方面,然后再执行机器学习或深度学习任务。它有助于识别模式、检测不一致性、测试假设并获得洞察。然而,随着大型数据集的出现和对高效分析的需求,自动化 EDA 变得重要。自动化 EDA 和可视化工具提供了几个好处。它们可以加快数据分析过程,减少在数据清洗、处理缺失值、异常值检测和特征工程等任务上花费的时间。这些工具还通过生成交互式可视化,提供对数据的全面概述,实现对复杂数据集的更有效探索。有许多用于自动化 EDA 和可视化的工具,包括:
-
D-Tale:一个库,方便地可视化 pandas 数据框。它支持交互式图表、3D 图表、热图、相关性分析、自定义列创建。
-
ydata-profiling(之前称为 pandas profiling):这是一个开源库,生成交互式 HTML 报告(
ProfileReport),总结数据集的不同方面,如缺失值统计、变量类型分布概况、变量之间的相关性。它适用于 Pandas 和 Spark DataFrames。 -
Sweetviz:一个 Python 库,提供探索性数据分析的可视化功能,只需很少的代码。它允许在变量或数据集之间进行比较。
-
Autoviz:这个库可以自动生成数据集的可视化,无论其大小,只需几行代码。
-
DataPrep:只需几行代码,您就可以从常见数据源中收集数据,进行探索性数据分析和数据清洗,比如标准化列名或条目。
-
Lux:通过交互式小部件显示数据集中有趣的趋势和模式的一组可视化结果,用户可以快速浏览以获取洞察。
在数据可视化中使用生成式人工智能为自动探索性数据分析增加了另一个维度,使算法能够基于现有的可视化或特定用户提示生成新的可视化。生成式人工智能有潜力通过自动化设计过程的一部分来增强创造力,同时保持人类对最终输出的控制。总的来说,自动化的探索性数据分析和可视化工具在时间效率、全面分析以及生成有意义的数据可视化方面提供了显著优势。生成式人工智能有潜力以多种方式革新数据可视化。例如,它可以用于创建更真实和引人入胜的可视化,有助于商业沟通并更有效地向利益相关者传达数据,以为每个用户提供他们需要获取洞察并做出明智决策所需的信息。生成式人工智能可以通过制作针对每个用户个性化需求的可视化来增强和扩展传统工具的创作能力。此外,生成式人工智能可以用于创建交互式可视化,让用户以新颖创新的方式探索数据。
预处理和特征提取
自动化数据预处理是自动化数据预处理中涉及的任务的过程。这可能包括数据清洗、数据集成、数据转换和特征提取等任务。它与 ETL 中的转换步骤相关,因此在工具和技术上存在很多重叠。数据预处理很重要,因为它确保数据以数据分析师和机器学习模型可以使用的格式存在。这包括从数据中删除错误和不一致性,以及将其转换为与将要使用的分析工具兼容的格式。手动工程特征可能会很繁琐和耗时,因此自动化这个过程是有价值的。最近,出现了几个开源的 Python 库,可以帮助从原始数据中自动生成有用的特征,正如我们将看到的那样。Featuretools 提供了一个通用框架,可以从事务性和关系数据中合成许多新特征。它集成了多个 ML 框架,使其灵活。Feature Engine 提供了一组更简单的转换器,专注于处理缺失数据等常见数据转换。为了专门为基于树的模型优化特征工程,来自 Microsoft 的 ta 通过自动交叉等技术展现出强大的性能。AutoGluon Features 应用神经网络风格的自动特征生成和选择来提高模型准确性。它与 AutoGluon 自动 ML 功能紧密集成。最后,TensorFlow Transform 直接在 Tensorflow 管道上运行,为模型在训练期间准备数据。随着现在有多种开源选项,它已经迅速发展。Featuretools 提供了最多的自动化和灵活性,同时集成了多个 ML 框架。对于表格数据,ta 和 Feature Engine 提供了易于使用的转换器,针对不同的模型进行了优化。Tf.transform 非常适合 TensorFlow 用户,而 AutoGluon 专门针对 Apache MXNet 深度学习软件框架。至于时间序列数据,Tsfel 是一个从时间序列数据中提取特征的库。它允许用户指定特征提取的窗口大小,并可以分析特征的时间复杂性。它计算统计、频谱和时间特征。另一方面,tsflex 是一个灵活高效的时间序列特征提取工具包,适用于序列数据。它对数据结构做出了很少的假设,可以处理缺失数据和不等长度。它还计算滚动特征。与 tsfresh 相比,这两个库提供了更现代的自动化时间序列特征工程选项。Tsfel 功能更全面,而 tsflex 强调对复杂序列数据的灵活性。一些工具专注于机器学习和数据科学的数据质量,具有数据概要和自动数据转换。例如,pandas-dq 库可以与 scikit-learn 管道集成,为数据概要、训练-测试比较、数据清理、数据填充(填补缺失值)和数据转换(例如,偏度校正)提供一系列有用的功能。它通过在建模之前解决潜在��题来改善数据分析的质量。更专注于通过早期识别潜在问题或错误来提高可靠性的工具包括 Great Expectations 和 Deequ。Great Expectations 是一个用于验证、记录和概要数据以保持质量并改善团队间沟通的工具。它允许用户对数据提出期望,通过数据的单元测试快速捕捉问题,基于期望创建文档和报告。Deequ 建立在 Apache Spark 之上,用于在大型数据集中定义数据质量的单元测试。它允许用户明确陈述关于数据集的假设,并通过对属性进行检查或约束来验证这些假设。通过确保遵守这些假设,它可以防止下游应用程序中的崩溃或错误输出。所有这些库都允许数据科学家缩短特征准备时间,并扩展特征空间以提高模型质量。自动特征工程正变得越来越重要,以充分利用 ML 算法在复杂现实世界数据上的全部潜力。
AutoML
自动化机器学习(AutoML)框架是一种自动化机器学习模型开发过程的工具。它们可以用于自动化任务,如数据清洗、特征选择、模型训练和超参数调整。这可以节省数据科学家大量的时间和精力,也有助于提高机器学习模型的质量。AutoML 的基本思想在 mljar autoML 库的 Github 仓库中有所体现(来源:github.com/mljar/mljar…
图 7.3:AutoML 的工作原理。
加载一些数据,尝试不同的预处理方法、机器学习算法、训练和模型参数的组合,创建解释,将结果与可视化一起在排行榜上进行比较。AutoML 框架的主要价值主张在于易用性和提高开发者在找到机器学习模型、理解它并将其投入生产中的生产力。AutoML 工具已经存在很长时间了。其中一个最早的广泛框架是 AutoWeka,用 Java 编写,旨在自动化 Weka(Waikato 知识分析环境)机器学习套件中表格数据的机器学习模型开发过程,该套件是在 Waikato 大学开发的。自 AutoWeka 发布以来,已经开发了许多其他 AutoML 框架。如今一些最受欢迎的 AutoML 框架包括 auto-sklearn、autokeras、NASLib、Auto-Pytorch、tpot、optuna、autogluon 和 ray(tune)。这些框架用各种编程语言编写,支持各种机器学习任务。最近在 autoML 和神经架构搜索方面取得的进展使工具能够自动化机器学习流程的大部分。像 Google AutoML、Azure AutoML 和 H2O AutoML/Driverless AI 这样的领先解决方案可以根据数据集和问题类型自动处理数据准备、特征工程、模型选择、超参数调整和部署。这使得机器学习对非专家更加易于接触。当前的 autoML 解决方案可以非常有效地处理结构化数据,如表格和时间序列数据。它们可以自动生成相关特征,选择算法如树集成、神经网络或 SVM,并调整超参数。由于大规模超参数搜索,性能通常与手动流程相当甚至更好。对于像图像、视频和音频这样的非结构化数据,autoML 也在快速发展,采用神经架构搜索技术。像 AutoKeras、AutoGluon 和 AutoSklearn 这样的开源库也提供了易于访问的 autoML 能力。然而,大多数 autoML 工具仍需要一些编码和数据科学专业知识。完全自动化数据科学仍然具有挑战性,autoML 在灵活性和可控性方面存在局限性。但随着更加用户友好和高性能的解决方案进入市场,进展迅速。以下是框架的表格总结:
| 框架 | 语言 | 机器学习框架 | 首次发布 | 关键特点 | 数据类型 | 维护者 | Github 星数 |
|---|---|---|---|---|---|---|---|
| Auto-Keras | Python | Keras | 2017 | 神经架构搜索,易于使用 | 图像、文本、表格 | Keras 团队(DATA 实验室,德克萨斯 A&M 大学) | 8896 |
| Auto-PyTorch | Python | PyTorch | 2019 | 神经架构搜索,超参数调整 | 表格、文本、图像、时间序列 | AutoML Group, 弗莱堡大学 | 2105 |
| Auto-Sklearn | Python | Scikit-learn | 2015 | 自动化 scikit-learn 工作流程 | 表格 | AutoML Group,弗莱堡大学 | 7077 |
| Auto-WEKA | Java* | WEKA | 2012 | 贝叶斯优化 | 表格 | 不列颠哥伦比亚大学 | 315 |
| AutoGluon | Python | MXNet, PyTorch | 2019 | 优化用于深度学习 | 文本,图像,表格 | 亚马逊 | 6032 |
| AWS SageMaker Autopilot | Python | XGBoost, sklearn | 2020 | 云端,简单 | 表格 | 亚马逊 | - |
| Azure AutoML | Python | Scikit-learn, PyTorch | 2018 | 可解释模型 | 表格 | 微软 | - |
| DataRobot | Python, R | 多种 | 2012 | 监控,可解释性 | 文本,图像,表格 | DataRobot | - |
| Google AutoML | Python | TensorFlow | 2018 | 易于使用,云端 | 文本,图像,视频,表格 | 谷歌 | - |
| H2O AutoML | Python, R | XGBoost, GBMs | 2017 | 自动化工作流程,集成 | 表格,时间序列,图像 | h2o.ai | 6430 |
| hyperopt-sklearn | Python | Scikit-learn | 2014 | 超参数调整 | 表格 | Hyperopt 团队 | 1451 |
| Ludwig | Python | Transformers/Pytorch | 2019 | 用于构建和调整自定义 LLMs 和深度神经网络的低代码框架 | 多种 | Linux 基金会 | 9083 |
| MLJar | Python | 多种 | 2019 | 可解释,可定制 | 表格 | MLJar | 2714 |
| NASLib | Python | PyTorch, TensorFlow/Keras | 2020 | 神经架构搜索 | 图像,文本 | AutoML Group,弗莱堡大学 | 421 |
| Optuna | Python | 通用 | 2019 | 超参数调整 | 通用 | Preferred Networks Inc | 8456 |
| Ray (Tune) | Python | 通用 | 2018 | 分布式超参数调整;加速 ML 工作负载 | 通用 | 加州大学伯克利分校 | 26906 |
| TPOT | Python | Scikit-learn, XGBoost | 2016 | 遗传编程,管道 | 表格 | Epistasis Lab, 宾夕法尼亚州立大学 | 9180 |
| TransmogrifAI | Scala | Spark ML | 2018 | Spark 上的 AutoML | 文本,表格 | Salesforce | 2203 |
图 7.4:开源 AutoML 框架的比较。Weka 可以通过 pyautoweka 从 Python 访问。Ray Tune 和 H2O 的星号涉及整个项目而不仅仅是自动化部分。与 AutoML 相关的 H2O 商业产品是 Driverless AI。大多数项目由一群与任何公司无关的贡献者维护
我只包括了最大的框架、库或产品 - 省略了一些。虽然重点是在 Python 中的开源框架上,我也包括了一些大型商业产品。Github 星标旨在展示框架的流行程度 - 它们与专有产品无关。Pycaret 是另一个大型项目(7562 星),它提供了同时训练多个模型并用相对较少的代码进行比较的选项。像 Nixtla 的 Statsforecast 和 MLForecast,或者 Darts 这样的项目具有针对时间序列数据的类似功能。像 Auto-ViML 和 deep-autoviml 这样的库处理各种类型的变量,并分别构建在 scikit-learn 和 keras 之上。它们旨在使新手和专家都能轻松尝试不同类型的模型和深度学习。然而,建议用户行使自己的判断以获得准确和可解释的结果。AutoML 框架的重要特性包括以下内容:
-
部署:一些解决方案,特别是云中的解决方案,可以直接部署到生产环境。其他则导出到 tensorflow 或其他格式。
-
数据类型:大多数解决方案侧重于表格数据集;深度学习自动化框架通常处理不同类型的数据。例如,autogluon 促进了图像、文本、时间序列以及表格数据的快速比较和原型设计。一些专注于超参数优化的解决方案,如 optuna 和 ray tune,对格式完全不可知。
-
可解释性:这在某些行业中可能非常重要,与监管(例如,医疗保健或保险)或可靠性(金融)相关。对于一些解决方案,这是一个独特的卖点。
-
监控:部署后,模型性能可能会下降(漂移)。一些提供商提供性能监控。
-
可访问性:一些提供商需要编码或至少基本的数据科学理解,而其他人则是即插即用的解决方案,几乎不需要编码。通常,低代码和无代码解决方案的可定制性较低。
-
开源:开源平台的优势在于完全透明地展示实现和方法及其参数的可用性,并且它们是完全可扩展的。
-
迁移学习:这种能力意味着能够扩展或定制现有的基础模型。
这里还有很多内容需要涵盖,超出了本章的范围,比如可用方法的数量。支持较少的功能包括自监督学习、强化学习,或生成图像和音频模型。对于深度学习,一些库专注于后端,专门针对 Tensorflow、Pytorch 或 MXNet。Auto-Keras、NASLib 和 Ludwig 具有更广泛的支持,特别是因为它们与 Keras 一起工作。从计划于 2023 年秋季发布的版本 3.0 开始,Keras 将支持三个主要后端 TensorFlow、JAX 和 PyTorch。Sklearn 拥有自己的超参数优化工具,如网格搜索、随机搜索、逐步减半。更专业的库,如 auto-sklearn 和 hyperopt-sklearn,通过提供贝叶斯优化方法,超越了这一点。Optuna 可以与各种 ML 框架集成,如 AllenNLP、Catalyst、Catboost、Chainer、FastAI、Keras、LightGBM、MXNet、PyTorch、PyTorch Ignite、PyTorch Lightning、TensorFlow 和 XGBoost。Ray Tune 具有自己的集成,其中包括 optuna。它们都具有最先进的参数优化算法和用于扩展(分布式训练)的机制。除了上述列出的功能外,其中一些框架可以自动执行特征工程任务,例如数据清洗和特征选择,例如删除高度相关的特征,并以图形方式生成性能结果。列出的每个工具都有各自的实现,例如特征选择和特征转换 - 不同之处在于这种自动化程度。更具体地说,使用 AutoML 框架的优势包括:
-
节省时间:AutoML 框架可以通过自动化机器学习模型开发过程来节省数据科学家大量时间。
-
提高准确性:AutoML 框架可以通过自动化超参数调整过程来帮助提高机器学习模型的准确性。
-
提高可访问性:AutoML 框架使得那些没有太多机器学习经验的人更容易接触机器学习。
然而,使用 AutoML 框架也存在一些缺点:
-
黑盒子:AutoML 框架可能是“黑盒子”,意味着很难理解它们的工作原理。这可能会导致难以调试 AutoML 模型的问题。
-
灵活性有限:AutoML 框架在可以自动化的机器学习任务类型方面可能受到限制。
上述工具中很多都至少具有某种自动特征工程或预处理功能,然而,也有一些更专业的工具。
生成模型的影响
生成式人工智能和像 GPT-3 这样的 LLM 已经给数据科学和分析领域带来了重大变革。这些模型,特别是 LLMs,有潜力以多种方式彻底改变数据科学中涉及的所有步骤,为研究人员和分析师提供令人兴奋的机会。生成式人工智能模型,如 ChatGPT,能够理解和生成类似人类的回应,使它们成为增强研究生产力的有价值工具。生成式人工智能可以在分析和解释研究数据方面发挥关键作用。这些模型可以协助进行数据探索,发现隐藏的模式或相关性,并提供通过传统方法可能不明显的见解。通过自动化数据分析的某些方面,生成式人工智能节省时间和资源,使研究人员能够专注于更高级别的任务。生成式人工智能可以在帮助研究人员进行文献综述和识别研究空白方面发挥作用。ChatGPT 和类似模型可以总结大量来自学术论文或文章的信息,提供现有知识的简明概述。这有助于研究人员更有效地识别文献中的空白,并指导他们自己的调查。我们已经在第四章,问答中探讨了使用生成式人工智能模型的这一方面。生成式人工智能的其他用例可能包括:
-
自动生成合成数据:生成式人工智能可以用于自动生成合成数据,用于训练机器学习模型。对于没有大量真实世界数据的企业来说,这可能会有所帮助。
-
识别数据中的模式:生成式人工智能可以用于识别数据中人类分析师无法看到的模式。这对于希望从数据中获得新见解的企业可能有所帮助。
-
从现有数据中创建新特征:生成式人工智能可以用于从现有数据中创建新特征。这对于希望提高其机器学习模型准确性的企业可能有所帮助。
根据麦肯锡和毕马威等机构最近的报告,AI 的后果涉及数据科学家将从事的工作、他们将如何工作以及谁可以从事数据科学任务。主要影响领域包括:
-
人工智能的民主化:生成模型使更多人能够利用人工智能,通过简单提示生成文本、代码和数据。这将 AI 的使用扩展到数据科学家以外的领域。
-
提高生产力:通过自动生成代码、数据和文本,生成式人工智能可以加速开发和分析工作流程。这使数据科学家和分析师能够专注于更高价值的任务。
-
数据科学的创新:生成式人工智能正在带来的是以新的更有创意的方式探索数据,并生成新的假设和见解,这是传统方法所无法实现的。
-
行业的颠覆:生成式人工智能的新应用可能通过自动化任务或增强产品和服务来颠覆行业。数据团队需要确定高影响的使用案例。
-
仍存在限制:当前模型仍存在准确性限制、偏见问题和缺乏可控性。需要数据专家监督负责任的开发。
-
治理的重要性:对生成式人工智能模型的开发和道德使用进行严格的治理将对维护利益相关者的信任至关重要。
-
合作伙伴关系的必要性 - 公司将需要与合作伙伴、社区和平台提供商建立生态系统,以有效利用生成式人工智能的能力。
-
数据科学技能的变化 - 需求可能从编码专业知识转向数据治理、道德、翻译业务问题和监督 AI 系统的能力。
关于数据科学的民主化和创新,更具体地说,生成式人工智能也对数据可视化方式产生影响。过去,数据可视化通常是静态的和二维的。然而,生成式人工智能可以用于创建交互式和三维的可视化,有助于使数据更易访问和理解。这使得人们更容易理解和解释数据,从而促进更好的决策。再次强调,生成式人工智能带来的最大变革之一是数据科学的民主化。过去,数据科学是一个需要深入了解统计学和机器学习的非常专业领域。然而,生成式人工智能使得技术专业知识较少的人能够创建和使用数据模型。这将数据科学领域开放给更广泛的人群。LLMs 和生成式人工智能可以在自动化数据科学中发挥关键作用,提供多种好处:
-
自然语言交互:LLMs 允许进行自然语言交互,使用户能够使用普通英语或其他语言与模型进行交流。这使得非技术用户能够使用日常语言与数据进行交互和探索,而无需专业的编码或数据分析技能。
-
代码生成:生成式人工智能可以自动生成代码片段,执行 EDA 期间的特定分析任务。例如,它可以生成检索数据的代码(例如 SQL)、清理数据、处理缺失值或创建可视化(例如 Python)的代码。这一功能节省时间,减少了手动编码的需求。
-
自动报告生成:LLMs 可以生成自动报告,总结 EDA 的关键发现。这些报告提供了关于数据集各个方面的见解,如统计摘要、相关性分析、特征重要性等,使用户更容易理解和展示他们的发现。
-
数据探索和可视化:生成式人工智能算法可以全面探索大型数据集,并自动生成可视化图表,自动揭示数据中的潜在模式、变量之间的关系、异常值或异常情况。这有助于用户在不需要手动创建每个可视化图表的情况下全面了解数据集。
此外,我们可以认为生成式人工智能算法应该能够从用户互动中学习,并根据个人偏好或过去行为调整推荐。它们通过持续的自适应学习和用户反馈不断改进,提供更个性化和有用的见解,在自动化的探索性数据分析过程中提供更多个性化和有用的见解。最后,生成式人工智能模型可以通过从现有数据集中学习模式(智能错误识别)在探索性数据分析过程中识别数据中的错误或异常。它们可以快速准确地检测不一致之处并快速准确地突出潜在问题。总的来说,大型和复杂数据集的更有效分析。然而,尽管这些模型具有增强研究和辅助文献审阅过程的巨大潜力,但它们不应被视为不可靠的信息源。正如我们之前所看到的,LLMs 是通过类比工作的,而且在推理和数学方面有困难。它们的优势在于创造力,而不是准确性,因此,研究人员必须运用批判性思维,并确保这些模型生成的输出准确、无偏见,并符合严格的科学标准。一个显著的例子是微软的 Fabric,它集成了由生成式人工智能驱动的聊天界面。这使用户可以使用自然语言提出与数据相关的问题,并在不必等待数据请求队列的情况下立即获得答案。通过利用像 OpenAI 模型这样的 LLMs,Fabric 实现了对有价值见解的实时访问。Fabric 在其他分析产品中脱颖而出,因为它采用了全面的方法。它解决了组织分析需求的各个方面,并为参与分析过程的不同团队提供了特定角色的体验,如数据工程师、仓储专业人员、科学家、分析师和业务用户。通过在每一层集成 Azure OpenAI 服务,Fabric 利用生成式人工智能的力量来释放数据的全部潜力。像 Microsoft Fabric 中的 Copilot 这样的功能提供了对话式语言体验,允许用户创建数据流、生成代码或整个函数、构建机器学习模型、可视化结果,甚至开发定制的对话式语言体验。据传闻,ChatGPT(以及 Fabric 扩展)经常生成不正确的 SQL 查询。当分析人员可以检查输出的有效性时,这对于使用者来说是可以接受的,但对于非技术业务用户来说,作为自助式分析工具则是一场灾难。因此,组织在使用 Fabric 进行分析时必须确保他们有可靠的数据管道,并采用数据质量管理实践。虽然生成式人工智能在数据分析中的可能性令人兴奋,但必须谨慎行事。LLMs 的可靠性和准确性应通过首创性推理和严格分析进行验证。虽然这些模型在临时分析、研究过程中的创意生成和总结复杂分析方面显示出潜力,但由于需要领域专家验证,它们并不总是适���非技术用户的自助式分析工具。让我们开始使用代理来运行代码或调用其他工具来回答问题!
代理可以回答数据科学问题
正如我们在 Jupyter AI(Jupyternaut chat)中看到的 - 以及在第六章开发软件中看到的 - 利用生成式 AI(代码 LLMs)提高创建和编写软件的效率的潜力很大。这是本章实践部分的一个很好的起点,因为我们将探讨在数据科学中使用生成式 AI 的用途。我们之前已经看到了不同的带有工具的代理。例如,LLMMathChain 可以执行 Python 来回答数学问题,如下所示:
from langchain import OpenAI, LLMMathChain
llm = OpenAI(temperature=0)
llm_math = LLMMathChain.from_llm(llm, verbose=True)
llm_math.run("What is 2 raised to the 10th power?")
虽然这对提取信息并反馈信息很有用,但如何将其插入传统的 EDA 过程并不那么明显。同样,CPAL(CPALChain)和 PAL(PALChain)链可以回答更复杂的推理问题,同时保持幻觉受控,但很难想出它们的真实用例。通过PythonREPLTool,我们可以创建玩具数据的简单可视化或使用合成数据进行训练,这对于项目的说明或引导可能很好。这是 LangChain 文档中的一个示例:
from langchain.agents.agent_toolkits import create_python_agent
from langchain.tools.python.tool import PythonREPLTool
from langchain.llms.openai import OpenAI
from langchain.agents.agent_types import AgentType
agent_executor = create_python_agent(
llm=OpenAI(temperature=0, max_tokens=1000),
tool=PythonREPLTool(),
verbose=True,
agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
)
agent_executor.run(
"""Understand, write a single neuron neural network in PyTorch.
Take synthetic data for y=2x. Train for 1000 epochs and print every 100 epochs.
Return prediction for x = 5"""
)
请注意,这应该谨慎执行,因为 Python 代码直接在机器上执行而没有任何保障。这实际上是有效的,可以创建数据集,训练模型,然后我们得到一个预测:
Entering new AgentExecutor chain...
I need to write a neural network in PyTorch and train it on the given data
Action: Python_REPL
Action Input:
import torch
model = torch.nn.Sequential(
torch.nn.Linear(1, 1)
)
loss_fn = torch.nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# Define the data
x_data = torch.tensor([[1.0], [2.0], [3.0], [4.0]])
y_data = torch.tensor([[2.0], [4.0], [6.0], [8.0]])
for epoch in range(1000): # Train the model
y_pred = model(x_data)
loss = loss_fn(y_pred, y_data)
if (epoch+1) % 100 == 0:
print(f'Epoch {epoch+1}: {loss.item():.4f}')
optimizer.zero_grad()
loss.backward()
optimizer.step()
# Make a prediction
x_pred = torch.tensor([[5.0]])
y_pred = model(x_pred)
Observation: Epoch 100: 0.0043
Epoch 200: 0.0023
Epoch 300: 0.0013
Epoch 400: 0.0007
Epoch 500: 0.0004
Epoch 600: 0.0002
Epoch 700: 0.0001
Epoch 800: 0.0001
Epoch 900: 0.0000
Epoch 1000: 0.0000
Thought: I now know the final answer
Final Answer: The prediction for x = 5 is y = 10.00.
再次强调,这非常酷,但很难看到如何在没有更严格的工程支持的情况下扩展,类似于我们在第六章开发软件中所做的。如果我们想要用类别或地理信息丰富我们的数据,LLMs 和工具可能会很有用。例如,如果我们的公司从东京提供航班,并且我们想知道客户距离东京的距离,我们可以使用 Wolfram Alpha 作为工具。这是一个简单的例子:
from langchain.agents import load_tools, initialize_agent
from langchain.llms import OpenAI
from langchain.chains.conversation.memory import ConversationBufferMemory
llm = OpenAI(temperature=0)
tools = load_tools(['wolfram-alpha'])
memory = ConversationBufferMemory(memory_key="chat_history")
agent = initialize_agent(tools, llm, agent="conversational-react-description", memory=memory, verbose=True)
agent.run(
"""How far are these cities to Tokyo?
* New York City
* Madrid, Spain
* Berlin
""")
请确保您已经设置了 OPENAI_API_KEY 和 WOLFRAM_ALPHA_APPID 环境变量,如第三章开始使用 LangChain中所讨论的。这是输出:
> Entering new AgentExecutor chain...
AI: The distance from New York City to Tokyo is 6760 miles. The distance from Madrid, Spain to Tokyo is 8,845 miles. The distance from Berlin, Germany to Tokyo is 6,845 miles.
> Finished chain.
'
The distance from New York City to Tokyo is 6760 miles. The distance from Madrid, Spain to Tokyo is 8,845 miles. The distance from Berlin, Germany to Tokyo is 6,845 miles.
现在,很多这些问题都非常简单。然而,我们可以给代理提供数据集来处理,这就是当我们连接更多工具时可以变得非常强大的地方。让我们开始问答关于结构化数据集的问题!
使用 LLMs 进行数据探索
数据探索是数据分析中至关重要且基础的一步,使研究人员能够全面了解其数据集并发现重要见解。随着像 ChatGPT 这样的 LLM 的出现,研究人员可以利用自然语言处理的力量促进数据探索。正如我们之前提到的生成式 AI 模型,如 ChatGPT,具有理解和生成类似人类响应的能力,使它们成为增强研究生产力的宝贵工具。用自然语言提出问题并获得易消化的回答可以极大地促进分析。LLM 不仅可以帮助探索文本数据,还可以帮助探索其他形式的数据,如数值数据集或多媒体内容。研究人员可以利用 ChatGPT 的能力提出关于数值数据集中统计趋势的问题,甚至查询图像分类任务的可视化。让我们加载一个数据集并开始处理。我们可以从 scikit-learn 快速获取一个数据集:
from sklearn.datasets import load_iris
df = load_iris(as_frame=True)["data"]
鸢尾花数据集是众所周知的 - 它是一个玩具数据集,但它将帮助我们展示使用生成式人工智能进行数据探索的能力。我们将在接下来使用 DataFrame。我们现在可以创建一个 Pandas dataframe 代理,看看完成简单任务有多容易!
from langchain.agents import create_pandas_dataframe_agent
from langchain import PromptTemplate
from langchain.llms.openai import OpenAI
PROMPT = (
"If you do not know the answer, say you don't know.\n"
"Think step by step.\n"
"\n"
"Below is the query.\n"
"Query: {query}\n"
)
prompt = PromptTemplate(template=PROMPT, input_variables=["query"])
llm = OpenAI()
agent = create_pandas_dataframe_agent(llm, df, verbose=True)
我已经告诉模型在怀疑和逐步思考时说它不知道的指令,这样可以减少幻觉。现在我们可以针对 DataFrame 查询我们的代理:
agent.run(prompt.format(query="What's this dataset about?"))
我们得到了答案“'这个数据集是关于某种花的测量。”'这是正确的。让我们展示如何获得一个可视化:
agent.run(prompt.format(query="Plot each column as a barplot!"))
它并不完美,但我们得到了一个看起来不错的图表:
图 7.5:鸢尾花数据集条形图。
我们还可以要求以可视化方式查看列的分布,这将给我们这个整洁的图表:
图 7.6:鸢尾花数据集箱线图。
我们可以要求图使用其他绘图后端,如 seaborn,但请注意这些必须已安装。我们还可以询问有关数据集的更多问题,比如哪一行的花瓣长度和花瓣宽度之间的差异最大。我们得到了带有中间步骤的答案如下(缩短):
df['difference'] = df['petal length (cm)'] - df['petal width (cm)']
df.loc[df['difference'].idxmax()]
Observation: sepal length (cm) 7.7
sepal width (cm) 2.8
petal length (cm) 6.7
petal width (cm) 2.0
difference 4.7
Name: 122, dtype: float64
Thought: I now know the final answer
Final Answer: Row 122 has the biggest difference between petal length and petal width.
我认为这值得表扬,LLM!下一步可能是在提示中添加更多关于绘图的指令,比如关于绘图大小的指令。在 streamlit 应用程序中实现相同的绘图逻辑有点困难,因为我们需要在相应的 streamlit 函数中使用绘图功能,例如,st.bar_chart(),但是这也是可以做到的。您可以在 Streamlit 博客中找到关于此的解释(“使用 ChatGPT 构建 Streamlit 和 scikit-learn 应用程序”)。统计测试呢?
agent.run(prompt.format(query="Validate the following hypothesis statistically: petal width and petal length come from the same distribution."))
我们得到了这个回应:
Thought: I should use a statistical test to answer this question.
Action: python_repl_ast
Action Input: from scipy.stats import ks_2samp
Observation:
Thought: I now have the necessary tools to answer this question.
Action: python_repl_ast
Action Input: ks_2samp(df['petal width (cm)'], df['petal length (cm)'])
Observation: KstestResult(statistic=0.6666666666666666, pvalue=6.639808432803654e-32, statistic_location=2.5, statistic_sign=1)
Thought: I now know the final answer
Final Answer: The p-value of 6.639808432803654e-32 indicates that the two variables come from different distributions.
'6.639808432803654e-32 的 p 值表明这两个变量来自不同的分布。'这是统计检验!这很酷。我们可以用简单的提示用普通英语提出相当复杂的关于数据集的问题。还有 pandas-ai 库,它在内部使用 LangChain 并提供类似的功能。以下是文档中的一个示例数据集:
import pandas as pd
from pandasai import PandasAI
df = pd.DataFrame({
"country": ["United States", "United Kingdom", "France", "Germany", "Italy", "Spain", "Canada", "Australia", "Japan", "China"],
"gdp": [19294482071552, 2891615567872, 2411255037952, 3435817336832, 1745433788416, 1181205135360, 1607402389504, 1490967855104, 4380756541440, 14631844184064],
"happiness_index": [6.94, 7.16, 6.66, 7.07, 6.38, 6.4, 7.23, 7.22, 5.87, 5.12]
})
from pandasai.llm.openai import OpenAI
llm = OpenAI(api_token="YOUR_API_TOKEN")
pandas_ai = PandasAI(llm)
pandas_ai(df, prompt='Which are the 5 happiest countries?')
这将给我们提供类似于直接使用 LangChain 时的请求结果。请注意,pandas-ai 不是本书的设置的一部分,所以如果你想使用它,你需要单独安装它。对于 SQL 数据库中的数据,我们可以连接一个SQLDatabaseChain。LangChain 文档展示了这个例子:
from langchain.llms import OpenAI
from langchain.utilities import SQLDatabase
from langchain_experimental.sql import SQLDatabaseChain
db = SQLDatabase.from_uri("sqlite:///../../../../notebooks/Chinook.db")
llm = OpenAI(temperature=0, verbose=True)
db_chain = SQLDatabaseChain.from_llm(llm, db, verbose=True)
db_chain.run("How many employees are there?")
我们首先连接到数据库。然后我们可以用自然语言提问关于数据的问题。这也是非常强大的。一个 LLM 会为我们创建查询。我期望这在我们不了解数据库架构时特别有用。如果设置了use_query_checker选项,SQLDatabaseChain还可以检查查询并自动更正它们。让我们总结一下!
总结
在本章中,我们探讨了自动化数据分析和数据科学的最新技术。有很多领域可以从 LLMs 中受益,主要是作为编码助手或数据探索。我们从涵盖数据科学过程中每个步骤的框架概述开始,比如 AutoML 方法,讨论了 LLMs 如何帮助我们进一步提高生产力,使数据科学和数据分析更容易访问,无论是对利益相关者还是开发人员或用户。然后,我们研究了代码生成和工具,类似于代码 LLMs 第六章,开发软件,可以通过创建我们可以查询的函数或模型来帮助数据科学任务,或者我们如何使用 LLMs 或第三方工具(如 Wolfram Alpha)来丰富数据。然后,我们看了一下在数据探索中使用 LLMs。在第四章,问答中,我们研究了摄入大量文本数据进行分析。在本章中,我们专注于 SQL 或表格形式的结构化数据集的探索性分析。总之,人工智能技术有潜力彻底改变我们分析数据的方式,ChatGPT 插件或 Microsoft Fabric 就是这方面的例子。然而,在当前的情况下,人工智能不能取代数据科学家,只能帮助他们。让我们看看你是否记得本章的一些关键要点!
问题
请看看你是否能够从记忆中回答这些问题。如果你对任何问题不确定,我建议你回到本章的相应部分:
-
数据科学和数据分析有什么区别?
-
数据科学涉及哪些步骤?
-
为什么我们想要自动化数据科学/分析?
-
有哪些用于自动化数据科学任务的框架,它们能做什么?
-
生成式人工智能如何帮助数据科学家?
-
我们可以使用什么样的代理和工具来回答简单问题?
-
我们如何让 LLM 与数据一起工作?
八、自定义 LLMs 及其输出
本章介绍了改进大型语言模型(LLMs)在复杂推理和问题解决任务等特定场景中的可靠性和性能的技术和最佳实践。通常,将模型调整到特定任务或确保模型输出符合我们期望的过程称为调节。在本章中,我们将讨论微调和提示作为调节方法。微调涉及在特定任务或数据集上对预训练基础模型进行训练,这些任务或数据集与所需应用相关。这个过程使模型能够适应并变得更准确和上下文对齐以用于预期的用例。同样,在推断时提供额外的输入或上下文,大型语言模型(LLMs)可以生成适合特定任务或风格的文本。提示设计对于释放 LLM 推理能力、未来模型和提示技术的潜力以及这些原则和技术对于与大型语言模型一起工作的研究人员和从业者来说是非常重要的工具包。了解 LLMs 如何逐个标记生成文本有助于创建更好的推理提示。提示仍然是一门经验艺术 - 经常需要尝试各种变化来看看哪种方法有效。但是一些提示工程的见解可以在模型和任务之间转移。我们将讨论 LangChain 中的工具,以实现高级提示工程策略,如少样本学习、动态示例选择和链式推理。在整个章节中,我们将使用 LLMs 进行微调和提示,您可以在书籍的 Github 存储库的notebooks目录中找到。主要章节包括:
-
调节和对齐
-
微调
-
提示工程
让我们从讨论调节和对齐开始,为什么它很重要,以及我们如何实现它。
调节和对齐
在生成式人工智能模型的背景下,对齐是指确保这些模型的输出与人类价值观、意图或期望结果一致。它涉及引导模型的行为与在特定背景下被认为是道德、适当或相关的内容保持一致。对齐的概念对于避免生成可能带有偏见、有害或偏离预期目的的输出至关重要。解决对齐问题需要仔细关注训练数据中存在的偏见,通过涉及人类审阅者的迭代反馈循环,在训练/微调阶段完善客观函数,利用用户反馈,并在部署过程中进行持续监控以确保持续对齐。有几个原因可能会希望对大型语言模型进行条件化。首先是控制输出的内容和风格。例如,根据特定关键词或属性(如正式程度)进行条件化可以产生更相关和高质量的文本。条件化还包括安全措施,以防止生成恶意或有害内容。例如,避免生成误导性信息、不当建议或潜在危险的指令,或者更一般地将模型与某些价值观保持一致。对大型语言模型进行条件化的潜在好处是多方面的。通过提供更具体和相关的输入,我们可以获得符合我们需求的输出。例如,在客户支持聊天机器人中,通过将用户查询作为条件,使其生成能够准确解决他们关注的回复。条件化还有助于通过将模型的创造力限制在特定范围内来控制有偏见或不当的输出。此外,通过对大型语言模型进行条件化,我们可以使它们更易控制和适应。我们可以根据我们的要求对其进行微调和塑造其行为,并创建在特定领域(如法律咨询或技术写作)可靠的人工智能系统。然而,也需要考虑潜在的缺点。过度对模型进行条件化可能导致过拟合,使其过于依赖特定输入,并在不同情境下难以生成创造性或多样化的输出。此外,应当负责任地利用条件化,因为大型语言模型有放大训练数据中存在偏见的倾向。在对这些模型进行条件化时,必须小心不要加剧与偏见或有争议话题相关的问题。
对齐的好处 包括:
-
增强用户体验:对齐模型生成与用户查询或提示相关的输出。
-
建立信任:确保道德行为有助于在用户/客户之间建立信任。
-
品牌声誉:通过与关于品牌一致性和所需语气/风格指南的业务目标保持一致。
-
缓解有害影响:与安全、保密和隐私考虑的对齐有助于防止生成有害或恶意内容。
潜在的缺点 包括:
-
挑战平衡:在极端对齐(过于保守)和创造自由(过于宽松)之间取得平衡可能很困难。
-
自动化指标的局限性:定量评估指标可能无法完全捕捉对齐细微差别。
-
主观性:对齐判断往往是主观的,需要仔细考虑并就期望的价值观和指导方针达成共识。
在多样化数据上预训练大型模型以学习模式和语言理解,结果是得到一个具有广泛理解各种主题但缺乏特定性或与任何特定背景相关性的基础模型。虽然像 GPT-4 这样的基础模型能够在各种主题上生成令人印象深刻的文本,但对其进行调节可以增强其在任务相关性、特定性和连贯性方面的能力,并使其输出更相关和主题相关。没有调节,这些模型往往会生成与期望背景不完全吻合的文本。通过调节它们,我们可以引导语言模型生成更与给定输入或指令相关的输出。调节的主要优势在于它允许引导模型而无需进行大量的重新训练。它还能够实现交互式控制和在不同模式之间切换。调节可以发生在模型开发周期的不同阶段——从微调到在各种背景下生成输出。有几种实现大型语言模型对齐的选项。一种方法是在微调过程中进行调节,通过在反映所需输出的数据集上训练模型。这使模型能够专门化,但需要访问相关的训练数据。另一种选择是在推断时动态调节模型,通过提供条件输入以及主要提示。这更加灵活,但在部署过程中引入了一些复杂性。在接下来的部分中,我将总结关于对齐的关键方法,如微调和提示工程,讨论其基本原理,并检查它们的相对优缺点。
对齐方法
随着像 GPT-3 这样的大型预训练语言模型的出现,人们对将这些模型调整为下游任务的技术越来越感兴趣。这个过程被称为微调。微调允许预训练模型根据预训练期间获得的广泛语言知识定制特定应用程序。调整预训练神经网络的想法起源于 2010 年代初的计算机视觉研究。在自然语言处理领域,Howard 和 Ruder(2018)展示了微调预训练的上下文表示(如 ELMo 和 ULMFit)在下游任务上的有效性。开创性的 BERT 模型(Devlin 等人,2019)将微调预训练的 transformers 确立为自然语言处理中的事实标准。微调的必要性源于预训练语言模型的设计目的是对一般语言知识进行建模,而不是特定的下游任务。只有在适应特定应用程序时,它们的能力才会显现出来。微调允许更新预训练权重以适应目标数据集和目标。这使得可以从一般模型中进行知识转移,同时为专门任务定制它。已经提出了几种用于对齐的方法,效率和效率之间存在权衡,值得更深入地研究每种对齐方法的细节。全面微调在微调期间更新预训练语言模型的所有参数。该模型在下游任务上端到端地进行训练,允许全局更新权重以最大化目标性能。FFT 在各种任务中始终取得强大的结果,但需要大量的计算资源和大型数据集以避免过拟合或遗忘。在适配器调整中,额外的可训练适配器层通常是瓶颈层插入到预训练模型中,同时保持原始权重冻结。只有新添加的适配器层在下游任务上进行训练。这使得调整参数高效,因为只有一小部分权重被更新。然而,由于预训练权重保持不变,适配器调整存在低拟合任务的风险。适配器的插入点和容量影响整体有效性。前缀调整:这在 LM 的每一层前面添加可训练向量,在微调期间进行优化,而基本权重保持冻结。前缀允许向模型注入归纳偏见。与适配器相比,前缀调整的内存占用较小,但效果没有那么好。前缀的长度和初始化影响有效性。在提示调整中,输入文本附加了可训练提示标记,这些标记提供软提示以诱导 LM 产生所需的行为。例如,可以提供任务描述作为提示来引导模型。只有添加的提示标记在训练期间进行更新,而预训练权重保持冻结。性能受提示工程的影响很大。正在探索自动提示方法。低秩适应(LoRA)向冻结的 LM 权重添加成对的低秩可训练权重矩阵。例如,对于每个权重 W,添加低秩矩阵 B 和 A,使得前向传���使用 W + BA。只有 B 和 A 被训练,基本 W 保持冻结。LoRA 以更高的参数效率实现了合理的效果。秩 r 的选择影响权衡。LoRA 使得在有限硬件上调整巨大 LM 成为可能。确保输出正确对齐的另一种方法是通过人工监督方法,如人机协同系统。这些系统涉及提供反馈并在必要时进行更正的人类审阅者。人类参与有助于使生成的输出与人类设定的期望值或指导原则保持一致。以下是总结引导生成 AI 输出的不同技术的表格:
| 阶段 | 技术 | 示例 |
|---|---|---|
| 训练 | 预训练 | 在多样数据上训练 |
| 目标函数 | 训练目标的精心设计 | |
| 架构和训练过程 | 优化模型结构和训练 | |
| 微调 | 专业化 | 在特定数据集/任务上训练 |
| 推理时条件 | 动态输入 | 前缀,控制代码,上下文示例 |
| 人类监督 | 人类参与 | 人类审查和反馈 |
图 8.1:引导生成式人工智能输出。
结合这些技术为开发人员提供了更多对生成式人工智能系统行为和输出的控制。最终目标是确保人类价值在训练到部署的所有阶段都被纳入,以创建负责任和一致的人工智能系统。此外,在预训练目标函数中的精心设计选择也会影响语言模型最初学习的行为和模式。通过将道德考虑因素纳入这些目标函数中,开发人员可以影响大型语言模型的初始学习过程。我们可以区分一些微调方法,如在线和离线。InstructGPT 被认为是一个改变游戏规则的因素,因为它展示了通过引入强化学习从人类反馈(RLHF)可以显著改进语言模型,如 GPT-3。让我们谈谈 InstructGPT 为何具有如此变革性影响的原因。
强化学习与人类反馈
在他们 2022 年 3 月的论文中,OpenAI 的欧阳等人展示了使用强化学习从人类反馈(RLHF)与近端策略优化(PPO)来调整大型语言模型如 GPT-3 与人类偏好一致。强化学习从人类反馈(RLHF)是一种在线方法,通过人类偏好对语言模型进行微调。它有三个主要步骤:
-
监督预训练:首先通过标准监督学习对语言模型进行训练,使用人类演示。
-
奖励模型训练:通过人类对语言模型输出的评分来训练奖励模型以估计奖励。
-
RL 微调:通过强化学习对语言模型进行微调,以最大化来自奖励模型的预期奖励,使用类似 PPO 的算法。
主要变化 RLHF 允许通过学习奖励模型将微妙的人类判断纳入语言模型训练中。因此,人类反馈可以引导和改进语言模型的能力,超越标准监督微调。这种新模型可以用于遵循以自然语言给出的指令,并且可以以比 GPT-3 更准确和相关的方式回答问题。尽管参数少 100 倍,InstructGPT 在用户偏好、真实性和减少伤害方面优于 GPT-3。从 2022 年 3 月开始,OpenAI 开始发布 GPT-3.5 系列模型,这是 GPT-3 的升级版本,包括与 RLHF 微调。这些模型的用户立即注意到了微调的三个优点:
-
可操纵性:模型遵循指令的能力(指令微调)
-
可靠的输出格式化:这对于 API 调用/函数调用等方面变得重要。
-
自定义语调:这使得可以根据任务和受众适当地调整输出风格。
InstructGPT 通过将人类反馈的强化学习方法纳入传统微调方法之外的新途径,开辟了改进语言模型的新途径。尽管 RL 训练可能不稳定且计算成本高昂,但其成功激发了进一步研究,以完善 RLHF 技术,减少对齐的数据需求,并为各种应用开发更强大和更易访问的模型。
离线方法
离线方法通过直接利用人类反馈来规避在线 RL 的复杂性。我们可以区分基于排名和基于语言的方法:
-
基于排名:人类对 LM 输出的排名用于定义微调的优化目标,避免完全使用 RL。这包括 Preference Ranking Optimization(PRO;宋等,2023)和 Direct Preference Optimization(DPO;拉法洛夫等,2023)等方法。
-
基于语言:人类反馈以自然语言形式提供,并通过标准监督学习利用。例如,Hindsight 链(CoH;刘等,2023)将所有类型的反馈转换为句子,并用于微调模型,利用语言模型的语言理解能力。
直接偏好优化(DPO)是一种简单而有效的方法,用于训练语言模型以符合人类偏好,无需明确学习奖励模型或使用强化学习。虽然它优化的目标与现有的 RLHF 方法相同,但实现起来要简单得多,更稳定,并且取得了强大的实证表现。Meta 的研究人员在论文“LIMA:对齐的少即是多”中,通过在精细训练 LLaMa 模型时最小化仅有 1,000 个精心策划的提示的监督损失,简化了对齐。基于与 DaVinci003(GPT-3.5)的输出相比较时的有利人类偏好,他们得出结论,精细训练只有极小的重要性。他们将此称为表面对齐假设。离线方法提供更稳定和高效的调整。然而,它们受到静态人类反馈的限制。最近的方法尝试将离线和在线学习结合起来。虽然 DPO 和带有 PPO 的 RLHF 旨在将 LLM 与人类偏好对齐,但它们在复杂性、数据需求和实现细节方面存在差异。DPO 提供简单性,但通过直接优化概率比实现强大的性能。另一方面,在 InstructGPT 中,带有 PPO 的 RLHF 引入了更多复杂性,但通过奖励建模和强化学习优化允许通过微妙的对齐。
低秩适应
LLM 在自然语言处理领域取得了令人印象深刻的成果,现在也被用于其他领域,如计算机视觉和音频。然而,随着这些模型变得更大,训练它们在消费者硬件上变得困难,并且为每个特定任务部署它们变得昂贵。有一些方法可以降低计算、内存和存储成本,同时提高在低数据和领域外情况下的性能。低秩适应(LoRA)冻结了预训练模型的权重,并在 Transformer 架构的每一层中引入可训练的秩分解矩阵,以减少可训练参数的数量。LoRA 在各种语言模型(RoBERTa、DeBERTa、GPT-2 和 GPT-3)上实现了与微调相当或更好的模型质量,同时具有更少的可训练参数和更高的训练吞吐量。QLORA 方法是 LoRA 的扩展,通过将梯度反向传播通过冻结的 4 位量化模型到可学习的低秩适配器,实现了对大型模型的高效微调。这使得可以在单个 GPU 上微调一个 65B 参数模型。QLORA 模型通过引入新的数据类型和优化器等创新,实现了在 Vicuna 上 ChatGPT 性能的 99%。特别是,QLORA 将微调一个 65B 参数模型的内存需求从>780GB 降低到<48GB,而不影响运行时或预测性能。
量化是指减少神经网络中权重和激活的数值精度的技术,如大型语言模型(LLMs)。量化的主要目的是减少大型模型的内存占用和计算需求。
有关 LLM 量化的一些关键要点:
-
它涉及使用比标准单精度浮点(FP32)更少的位数来表示权重和激活。例如,权重可以量化为 8 位整数。
-
这可以将模型大小缩小多达 4 倍,并提高专用硬件的吞吐量。
-
量化通常对模型准确性影响较小,尤其是在重新训练时。
-
常见的量化方法包括标量、向量和乘积量化,它们将权重分别或分组量化。
-
通过估计激活的分布并适当分组,激活也可以被量化。
-
量化感知训练在训练过程中调整权重以最小化量化损失。
-
像 BERT 和 GPT-3 这样的 LLMs 已经证明通过微调可以很好地使用 4-8 位量化。
参数高效微调(PEFT)方法使每个任务可以使用小的检查点,使模型更具可移植性。这些小的训练权重可以添加到 LLM 之上,使得同一模型可以用于多个任务而无需替换整个模型。在下一节中,我们将讨论在推理时对大型语言模型(LLMs)进行条件化的方法。
推理时的条件化
一种常用的方法是推理时的条件化(输出生成阶段),在这里特定的输入或条件会动态提供以指导输出生成过程。在某些情况下,LLM 微调可能并不总是可行或有益的:
-
有限的微调服务:一些模型只能通过缺乏或受限的微调能力的 API 访问。
-
数据不足:在某些情况下,针对特定下游任务或相关应用领域缺乏微调数据。
-
动态数据:应用程序中频繁更改数据的情况,例如新闻相关平台,可能难以频繁微调模型,导致潜在的缺点。
-
上下文敏感应用:动态和特定上下文的应用,如个性化聊天机器人,无法根据个人用户数据进行微调。
对于推理时的条件化,通常我们在文本生成过程的开头提供一个文本提示或指示。这个提示可以是几句话甚至一个单词,作为所需输出的明确指示。一些常见的动态推理时条件化技术包括:
-
提示调整:为预期行为提供自然语言指导。对提示设计敏感。
-
前缀调整:在 LLM 层前添加可训练向量。
-
限制标记:强制包含/排除某些词语。
-
元数据:提供高级信息,如流派、目标受众等。
提示可以促进生成符合特定主题、风格甚至模仿特定作者写作风格的文本。这些技术包括在推理时提供上下文信息,例如在上下文学习或检索增强中。提示调整的一个例子是在提示前加上前缀,比如“写一个适合儿童的故事...”这样的指示。例如,在聊天机器人应用中,通过使用用户消息来调节模型,帮助其生成个性化且与当前对话相关的回复。更多的例子包括在提示前加上相关文档以帮助 LLMs 完成写作任务(例如新闻报道、维基百科页面、公司文件),或者在提示 LLM 之前检索并加上用户特定数据(财务记录、健康数据、电子邮件)以确保个性化答案。通过在运行时将 LLM 输出与上下文信息相结合,这些方法可以引导模型而不依赖于传统的微调过程。通常演示是推理任务指令的一部分,其中提供少量示例以诱导期望的行为。强大的 LLMs,如 GPT-3,可以通过提示技术解决任务而无需进一步训练。在这种方法中,要解决的问题被呈现给模型作为文本提示,可能还包括一些类似问题及其解决方案的文本示例。模型必须通过推理提供提示的完成。零样本提示不涉及已解决的示例,而少量样本提示包括少量类似(问题,解决方案)对的示例。已经证明提示可以轻松控制像 GPT-3 这样的大型冻结模型,并且可以在不进行大量微调的情况下引导模型行为。提示使模型能够在新知识上进行条件化,但需要精心设计提示以获得最佳结果。这将是我们在本章讨论的内容之一。在前缀调整中,连续的任务特定向量在推理时被训练并提供给模型。类似的想法已经被提出用于适配器方法,如参数高效的迁移学习(PELT)或梯子侧调整(LST)。在推理时进行条件化也可以发生在采样过程中,例如基于语法的采样,其中输出可以被限制为与某些明确定义的模式兼容,比如编程语言语法。
结论
完全微调始终能取得强大的结果,但通常需要大量资源,并且在功效和效率之间存在权衡。像适配器、提示和 LoRA 这样的方法通过稀疏性或冻结减轻了这种负担,但可能效果较差。最佳方法取决于约束和目标。未来改进的技术针对大型语言模型量身定制,可以推动功效和效率的边界。最近的工作将离线和在线学习相结合以提高稳定性。整合世界知识和可控生成仍然是开放挑战。基于提示的技术允许对 LLM 进行灵活的条件设定,以诱导所需行为而无需进行密集训练。谨慎的提示设计、优化和评估是有效控制 LLM 的关键。基于提示的技术允许以灵活、低资源的方式对 LLM 进行特定行为或知识的条件设定。
评估
对齐是通过像 HUMAN 这样的对齐基准和像 FLAN 这样的泛化测试来评估的。有一些核心基准具有很高的可区分性,可以准确评估模型的优势和劣势,例如:
-
英语知识:MMLU
-
中文知识:C-Eval
-
推理:GSM8k / BBH(算法)
-
编码:HumanEval / MBPP
在平衡这些方向之后,可以追求像 MATH(高难度推理)和对话等额外的基准。特别有趣的评估在数学或推理方面,预计泛化能力会非常强。 MATH 基准展示了高水平的难度,而 GPT-4 根据提示方法实现了不同的得分。 结果范围从通过少量评估进行天真提示到 PPO + 基于过程的奖励建模。 如果微调仅涉及对话数据,可能会对现有能力(如 MMLU 或 BBH)产生负面影响。 提示工程至关重要,因为偏见和查询难度会影响评估。 还有像困惑度(衡量模型预测数据的能力)或 BLEU 分数(捕捉生成文本与参考文本之间的相似性)这样的定量指标。 这些指标提供了粗略的估计,但可能无法完全捕捉语义含义或与更高级目标的对齐。其他指标包括通过人类评估的用户偏好评分,成对偏好,利用预训练奖励模型进行在线小/中型模型或自动化 LLM 评估(例如 GPT-4)。 人类评估有时可能存在问题,因为人类可能会受到主观标准的影响,例如回应中的权威语气而不是实际准确性。 进行评估,让用户根据事先设定的具体标准评估生成文本的质量、相关性和适当性,可以提供更细致入微的见解。 微调的目的不仅仅是改善给定提示集上的用户偏好。 其主要目的是通过减少不良输出(如非法、有害、滥用、虚假或欺骗性内容)的发生来解决 AI 安全问题。 关注减轻风险行为对确保 AI 系统的安全性和可靠性至关重要。 仅基于用户偏好评估和比较模型,而不考虑它们可能造成的潜在危害,可能会误导,并优先考虑比较安全的替代方案。 总之,评估 LLM 的对齐性需要仔细选择基准,考虑可区分性,并结合自动评估方法和人类判断。 注意提示工程和特定评估方面的关注是必要的,以确保对模型性能进行准确评估。在接下来的部分中,我们将使用 PEFT 和量化对一个小型开源 LLM(OpenLLaMa)进行微调,用于问答,并将其部署在 HuggingFace 上。
微调
正如我们在本章第一节中讨论的那样,LLM 的模型微调的目标是优化模型,使其生成的输出比原始基础模型更具体于任务和上下文。 在我们可能想要应用这种方法的众多任务和场景中,包括以下几种:
-
软件开发
-
文档分类
-
问答
-
信息检索
-
客户支持
在本节中,我们将为问答模型进行微调。这个步骤不是特定于 LangChain 的,但我们将指出一些自定义内容,LangChain 可以参与其中。出于性能原因,我们将在 Google Colab 上运行这个步骤,而不是通常的本地环境。
Google Colab 是一个计算环境,提供不同的硬件加速计算任务的手段,如张量处理单元(TPUs)和图形处理单元(GPUs)。这些在免费和专业版中都可用。对于本节任务的目的,免费版完全足够。您可以在此网址登��到 Colab 环境:
colab.research.google.com/
请确保您在 Google Colab 机器的顶部菜单中设置为 TPU 或 GPU,以确保您有足够的资源来运行此操作,并且训练不会花费太长时间。我们将在 Google Colab 环境中安装所有所需的库 - 我添加了我使用的这些库的版本,以便使我们的微调可重复进行:
-
peft:参数高效微调(PEFT;版本 0.5.0)
-
trl:近端策略优化(0.6.0)
-
bitsandbytes:k 位优化器和矩阵乘法例程,用于量化(0.41.1)
-
accelerate:使用多 GPU、TPU、混合精度训练和使用 PyTorch 模型(0.22.0)
-
transformers:HuggingFace transformers 库,支持 JAX、PyTorch 和 TensorFlow(4.32.0)
-
datasets:社区驱动的开源数据集库(2.14.4)
-
sentencepiece:用于快速标记化的 Python 封装(0.1.99)
-
wandb:用于监控 Weights and Biases 上训练进度的工具(0.15.8)
-
langchain 用于在训练后将模型加载回作为 langchain llm(0.0.273)
我们可以通过 Colab 笔记本安装这些库,如下所示:
!pip install -U accelerate bitsandbytes datasets transformers peft trl sentencepiece wandb langchain
为了从 HuggingFace 下载和训练模型,我们需要在平台上进行身份验证。请注意,如果您想稍后将您的模型推送到 HuggingFace,您需要在 HuggingFace 上生成一个具有写入权限的新 API 令牌:huggingface.co/settings/tokens
图 8.3:在 HuggingFace 上创建一个新的具有写入权限的 API 令牌。
我们可以像这样从笔记本进行身份验证:
import notebook_login
notebook_login()
在提示时,粘贴您的 HuggingFace 访问令牌。
在我们开始之前,请注意:在执行代码时,您需要登录不同的服务,因此请确保在运行笔记本时要注意!
Weights and Biases(W&B)是一个 MLOps 平台,可以帮助开发人员从头到尾监控和记录 ML 训练工作流程。正如前面提到的,我们将使用 W&B 来了解训练的效果如何,特别是模型是否随着时间的推移而改进。对于 W&B,我们需要为项目命名;或者,我们可以使用 wandb 的 init() 方法:
import os
os.environ["WANDB_PROJECT"] = "finetuning"
为了与 W&B 进行身份验证,您需要在他们这里创建一个免费帐户:www.wandb.ai 您可以在授权页面上找到您的 API 密钥:wandb.ai/authorize同样,我们需要粘贴我们的 API 令牌。如果之前的训练仍然处于活动状态 - 这可能是从笔记本的上一次执行中,如果您再次运行第二次 - 让我们确保我们开始一个新的!这将确保我们在 W&B 上获得新的报告和仪表板:
if wandb.run is not None:
wandb.finish()
接下来,我们需要选择一个数据集进行优化。我们可以使用许多不同的数据集,适用于编码、叙述、工具使用、SQL 生成、小学数学问题(GSM8k)或许多其他任务。HuggingFace 提供了丰富的数据集,可以在此网址查看:huggingface.co/datasets这些数据集涵盖了许多不同的,甚至是最为专业的任务。我们也可以自定义我们自己的数据集。例如,我们可以使用 langchain 来设置训练数据。有许多可用的过滤方法可以帮助减少数据集中的冗余。在本章中展示数据收集作为一个实用的步骤可能会很吸引人。然而,由于复杂性,我将其排除在本书的范围之外。从网络数据中过滤出质量可能会更加困难,但有许多可能性。对于代码模型,我们可以应用代码验证技术来对段落进行评分作为质量过滤器。如果代码来自 Github,我们可以按星级或按存储库所有者的星级进行过滤。对于自然语言文本,质量过滤并不是一件简单的事情。搜索引擎排名可以作为一个流行度过滤器,因为它通常基于用户与内容的互动。此外,知识蒸馏技术可以通过事实密度和准确性进行调整作为一个过滤器。在这个步骤中,我们正在使用 Squad V2 数据集进行问答性能的微调。您可以在 HuggingFace 上查看详细的数据集描述:huggingface.co/spaces/evaluate-metric/squad_v2
from datasets import load_dataset
dataset_name = "squad_v2"
dataset = load_dataset(dataset_name, split="train")
eval_dataset = load_dataset(dataset_name, split="validation")
我们同时采用训练和验证拆分。Squad V2 数据集有一个部分应该用于训练,另一个部分用于验证,正如我们可以在load_dataset(dataset_name)的输出中看到的:
DatasetDict({
train: Dataset({
features: ['id', 'title', 'context', 'question', 'answers'],
num_rows: 130319
})
validation: Dataset({
features: ['id', 'title', 'context', 'question', 'answers'],
num_rows: 11873
})
})
我们将使用验证拆分进行早停。早停将允许我们在验证错误开始恶化时停止训练。Squad V2 数据集由各种特征组成,我们可以在这里看到:
{'id': Value(dtype='string', id=None),
'title': Value(dtype='string', id=None),
'context': Value(dtype='string', id=None),
'question': Value(dtype='string', id=None),
'answers': Sequence(feature={'text': Value(dtype='string', id=None),
'answer_start': Value(dtype='int32', id=None)}, length=-1, id=None)}
训练中的基本思想是用一个问题提示模型,并将答案与数据集进行比较。我们希望有一个小型模型,可以在本地以合理的标记速率运行。LLaMa-2 模型需要使用您的电子邮件地址签署许可协议并得到确认(公平地说,这可能非常快),因为它受到商业使用的限制。像 OpenLLaMa 这样的 LLaMa 衍生产品在 HF 排行榜上表现相当不错:huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboardOpenLLaMa 版本 1 不能用于编码任务,因为分词器的原因。因此,让我们使用 v2!我们将使用一个 30 亿参数的模型,即使在较旧的硬件上也能使用:
model_id = "openlm-research/open_llama_3b_v2"
new_model_name = f"openllama-3b-peft-{dataset_name}"