Web-和机器学习的人工智能辅助变成-全-
Web 和机器学习的人工智能辅助变成(全)
原文:
annas-archive.org/md5/f5ce3ca93c7ecd74cff0d3ae335a95cc译者:飞龙
前言
这本书面向的对象
这本书的目标受众是网络开发、机器学习和数据科学领域的专业人士。你应该是一位拥有至少 1-3 年经验的专业人士。这本书旨在通过展示 AI 助手如何在不同的问题领域中发挥作用来赋予你力量。它描述了整体功能,同时也提供了关于如何有效提示以获得最佳结果的建议。
这本书涵盖的内容
第一章,这是一个新的世界,一个拥有 AI 助手的世界,你被邀请了,探讨了我们是怎样开始使用大型语言模型的,以及它如何构成对许多人(而不仅仅是 IT 工作者)的范式转变。
第二章,提示策略,解释了本书中使用的策略,即分解问题和一些关于如何有效地提示你选择的 AI 工具的指导原则。
第三章,行业工具:介绍我们的 AI 助手,是我们解释如何与我们的两个选择的 AI 助手(GitHub Copilot 和 ChatGPT)一起工作的地方,涵盖了从安装到如何开始使用它们的所有内容。
第四章,使用 HTML 和 Copilot 构建我们的应用外观,重点在于构建我们的电子商务应用的前端(你将在整本书中看到这一叙述)。
第五章,使用 Copilot 和 CSS 美化应用,是我们继续工作在我们的电子商务应用上,但现在专注于 CSS 并确保外观吸引人的地方。
第六章,使用 JavaScript 添加行为,是我们使用 JavaScript 为我们的电子商务应用添加行为的地方。
第七章,使用响应式 Web 布局支持多个视口,是我们解决应用需要为不同设备类型工作的事实的地方,无论是较小的手机屏幕、平板电脑还是桌面屏幕。因此,本章重点介绍了响应式设计。
第八章,使用 Web API 构建后端,探讨了为了让应用真正工作,它需要有一个后端,包括能够读取和写入数据并持久化的代码。因此,本章重点介绍了为我们的电子商务应用构建 Web API。
第九章,使用 AI 服务增强 Web 应用,涵盖了训练机器学习模型以及如何通过 Web API 将其暴露出来,以便任何拥有浏览器或其他能够使用 HTTP 协议的客户端的人都可以消费。
第十章,维护现有代码库,涵盖了大多数开发者如何处理现有代码和维护现有代码库,而不是创建新项目。因此,本章重点介绍了维护代码的各个方面,如处理错误、性能、与测试一起工作等。
第十一章,使用 ChatGPT 进行数据探索,是我们与审查数据集一起工作并学习如何识别分布、趋势、相关性等方面的见解的地方。
第十二章,使用 ChatGPT 构建分类模型,探讨了与第十一章相同的审查数据集,这次进行分类和情感分析。
第十三章,使用 ChatGPT 为客户消费构建回归模型,试图预测客户每年的消费金额,并使用回归创建一个能够进行这种预测的模型。
第十四章,使用 ChatGPT 为 Fashion-MNIST 构建 MLP 模型,探讨了基于时尚数据集构建 MLP 模型,仍然坚持我们的电子商务主题。
第十五章,使用 ChatGPT 为 CIFAR-10 构建 CNN 模型,专注于构建 CNN 模型。
第十六章,无监督学习:聚类和 PCA,专注于聚类和 PCA。
第十七章,使用 Copilot 进行机器学习,介绍了使用 GitHub Copilot 进行机器学习的过程,并与 ChatGPT 进行对比。
第十八章,使用 Copilot Chat 进行回归,我们在这里开发了一个回归模型。本章也使用了 GitHub Copilot。
第十九章,使用 Copilot 建议进行回归,与上一章类似,专注于使用 GitHub Copilot 进行回归。与上一章不同的是,在这里我们使用写作提示的建议作为文本文件中的注释,而不是在聊天界面中编写我们的提示。
第二十章,利用 GitHub Copilot 提高效率,专注于如何充分利用 GitHub Copilot。如果你想要掌握 GitHub Copilot,这一章是必读的。
第二十一章,软件开发中的代理,探讨了 AI 领域的下一个趋势,即代理。代理能够通过根据高级目标自主行动来提供更高的协助。如果你对未来的趋势感兴趣,这绝对值得一读。
第二十二章,结论,通过总结关于与 AI 助手合作的更广泛经验教训来结束本书。
要充分利用这本书
如果你已经在每个领域构建了一些项目,而不是作为一个完全的初学者,那么你会从这本书中获得更多。因此,本书侧重于增强你在现有开发工作流程中的能力。如果你对 Web 开发或机器学习完全陌生,我们推荐 Packt 的其他书籍。以下列表提供了推荐:
-
Oliver Theobald 著的《使用 Python 和机器学习解锁 AI 潜力》(
www.packtpub.com/en-US/product/machine-learning-with-python-9781835461969)
本书构建的方式是,首先展示你被推荐的提示,然后是所选 AI 工具的结果。
-
要跟随网络开发章节,我们建议安装 Visual Studio Code。本书中有专门的章节指出如何安装 GitHub Copilot 并利用它。有关 Visual Studio Code 的安装说明请参阅
code.visualstudio.com/download。 -
对于机器学习章节,大多数章节使用 ChatGPT,可以通过网页浏览器访问。我们确实建议使用笔记本解决问题,可以通过各种不同的工具查看。有关笔记本设置的更详细说明,请参阅此页面:
code.visualstudio.com/docs/datascience/jupyter-notebooks -
要使用 GitHub Copilot,您需要一个 GitHub 账户进行登录。有关 GitHub Copilot 设置过程的页面请参阅
docs.github.com/en/copilot/quickstart。
下载示例代码文件
本书代码包托管在 GitHub 上,网址为github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT。我们还有其他来自我们丰富图书和视频目录的代码包,可在github.com/PacktPublishing/找到。查看它们吧!
下载彩色图像
我们还提供了一份包含本书中使用的截图/图表的彩色图像的 PDF 文件。您可以从这里下载:packt.link/gbp/9781835086056。
使用的约定
本书使用了多种文本约定。
CodeInText:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入以及 X(以前称为 Twitter)用户名。例如:“现在product.css已使用上述内容创建,我们可以在 HTML 文件中包含该 CSS 文件。”
粗体:表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词在文本中显示为这样。例如:“创建新用户:应该能够创建新用户。”
警告或重要注意事项看起来像这样。
小技巧和技巧看起来像这样。
联系我们
我们始终欢迎读者的反馈。
一般反馈:请将邮件发送至 feedback@packtpub.com,并在邮件主题中提及书籍标题。如果您对本书的任何方面有疑问,请通过 questions@packtpub.com 发送邮件给我们。
勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将非常感激您向我们报告。请访问www.packtpub.com/submit-errata,点击提交勘误,并填写表格。
盗版:如果您在互联网上以任何形式发现我们作品的非法副本,我们将非常感激您提供位置地址或网站名称。请通过copyright@packtpub.com与我们联系,并提供材料的链接。
如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com。
分享您的想法
一旦您阅读了《AI 辅助 Web 和机器学习编程》,我们非常乐意听到您的想法!请点击此处直接转到此书的亚马逊评论页面并分享您的反馈。
您的评论对我们和科技社区都非常重要,它将帮助我们确保我们提供高质量的内容。
下载此书的免费 PDF 副本
感谢您购买此书!
您喜欢在路上阅读,但无法携带您的印刷书籍到处走?
您的电子书购买是否与您选择的设备不兼容?
别担心,现在每购买一本 Packt 书籍,您都可以免费获得该书的 DRM 免费 PDF 版本。
在任何地方、任何设备上阅读。直接从您最喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。
优惠远不止这些,您还可以获得独家折扣、时事通讯和每日免费内容的每日电子邮件。
按照以下简单步骤获取这些好处:
- 扫描下面的二维码或访问以下链接:

packt.link/free-ebook/9781835086056
-
提交您的购买证明。
-
就这些了!我们将直接将您的免费 PDF 和其他好处发送到您的电子邮件。
第一章:这是一个新世界,一个拥有人工智能助手的世界,而你被邀请加入。
简介
2022 年 11 月,ChatGPT 似乎从天而降。随着时间的推移,ChatGPT 获得了动力,逐渐演变成为一个被广泛接受的工具。最终,数百万用户将 ChatGPT 整合到他们的工作流程中,利用其生成见解、总结文本、编写代码等功能。
它的出现改变了许多人的工作流程,并在快速理解大量文本、撰写电子邮件等任务上有了很大的改进。你现在购买了这本书,希望学习如何使用像ChatGPT或GitHub Copilot这样的 AI 工具来提高你的效率。这正是本书的使命:不仅教你如何使用这两个 AI 工具,还能让你能够在各个问题领域应用它们。
在我们开始使用 AI 助手解决问题之前,让我们回顾一下;我们是如何到达这里的?ChatGPT 并不是突然出现的,对吧?
ChatGPT 是如何从 NLP 到 LLMs 发展而来的
为了讲述我们如何到达这里,到达像 ChatGPT 这样的 AI 工具,由大型语言模型(LLMs)提供动力,让我们首先了解一下自然语言处理(NLP)。
NLP 是计算机科学、人工智能和计算语言学的一个领域。它关注计算机与人类语言之间的交互,以及如何编程计算机来处理和分析大量自然语言数据。NLP 是一个极其有趣的领域,在现实世界中有着广泛的有用应用。以下是一些例子:
-
语音识别:如果你有一部现代智能手机,你很可能已经与 Siri 或 Alexa 等语音助手互动过。
-
机器翻译:当想到机器翻译时,可能首先想到的是谷歌翻译,它具有从一种语言自动翻译到另一种语言的能力。
-
情感分析:一个非常有用的领域是理解社交媒体等领域的情感。公司想知道品牌是如何被感知的;电子商务希望快速理解产品评论以促进其业务。
-
聊天机器人和虚拟助手:在 ChatGPT 出现之前,你可能已经看到聊天机器人被集成到网页上。这些聊天机器人可以回答简单的问题,公司使用它们来确保你能够快速得到简单问题的答案,并提供比 FAQ 页面更自然的使用体验,以及其他用途领域。
-
文本摘要:当想到文本摘要时,我们再次想到搜索引擎。你可能见过,当你使用 Bing 或 Google 等搜索引擎时,它能够总结一个页面,并在搜索结果页面上与页面的链接一起显示摘要。作为用户,你能够更好地理解应该点击哪个链接。
-
内容推荐:这是被众多不同领域使用的一个重要领域。电子商务用它来展示你可能感兴趣的产品,Xbox 用它来推荐游戏以及购买建议,而视频流媒体服务则显示你可能想观看的内容。
如您所见,通过 NLP,无论是公司还是最终用户,都能从中获得巨大的好处。
LLMs 的兴起
我们是如何从 NLP(自然语言处理)发展到 LLMs(大型语言模型)的呢?最初,NLP 使用基于规则的系统和统计方法。这种方法虽然对于某些任务效果不错,但在处理人类语言方面遇到了困难。
当深度学习(机器学习的一个子集)被引入 NLP,并产生了如 RNN(循环神经网络)和基于 transformer 的模型等能够学习数据中模式的新模型时,情况发生了改变,性能得到了显著提升。基于 transformer 的模型,我们开始为大型语言模型奠定基础。
LLMs 是一种 transformer 模型。它们可以生成类似人类的文本,并且与 NLP 模型不同,它们擅长各种任务,而无需特定的训练数据。这是怎么做到的呢?答案是改进的架构、计算能力的巨大提升以及庞大的数据集。
LLMs(大型语言模型)基于这样一个理念:一个足够大的神经网络,在足够的数据和计算资源支持下,可以学会做任何事情。这标志着我们在编程计算机方面的范式转变。我们不再编写代码,而是编写提示并让模型完成剩余的工作。
GPT 模型
目前有各种各样的 LLMs,但让我们先关注一下 GPT,这是一种 LLM,本书所选的工具就是基于它的(即使 GitHub Copilot 使用的是名为 Codex 的特定子集)。
在过去几年中已经开发了几个不同的版本。以下是一些由 OpenAI 公司开发的模型:
-
GPT-1:第一个模型,拥有 1.17 亿个参数,采用了 transformer 架构。
-
GPT-2:这个模型拥有 15 亿个参数,能够生成连贯且相关的文本。
-
GPT-3:这个模型拥有 1750 亿个参数,在回答问题、生成小说甚至编写代码等特性上,比其前身有了显著的提升。
-
GPT-4:据称,这个模型拥有 1.76 万亿个参数。
-
参数的数量使得模型能够理解更加细腻和连贯的文本。还应该指出的是,模型越大,训练它所需的计算资源也就越大。
-
ChatGPT 最近切换到了 GPT-4,与 GPT-3 相比,差异非常显著。
LLMs 的优势在哪里
现在我们已经更好地理解了 LLMs 是如何产生的以及它们的来源,那么是什么让 LLMs 如此出色呢?有哪些好的例子说明我们真的应该采用基于 LLMs 的人工智能助手呢?
由于 LLMs 更大、更先进,它们在某些方面明显优于传统的 NLP 模型:
-
背景:大型语言模型(LLMs)不仅能理解最近的输入,还能基于更长的对话产生回应。
-
少样本学习:为了执行任务,LLMs 通常只需要几个示例就能产生正确的响应。这与通常需要大量特定任务训练数据的 NLP 模型形成对比。
-
性能:在翻译、问答和摘要等领域,LLMs 比传统的 NLP 模型表现更好。
值得注意的是,LLMs 并不完美;它们确实会生成错误的响应,有时也会编造响应,也称为幻觉。但我们希望,通过阅读这本书,你将看到使用基于 LLM 的人工智能助手的优势,并且你会觉得优点明显大于缺点。
新范式:用自然语言编程
使用基于 LLM 的人工智能助手最大的变革可能是,你能够仅使用自然语言与之互动。你不需要学习编程语言来获取你需要的响应。这种变化构成了与 AI 互动的新范式。我们正在从为生成应用程序、数据检索甚至我们生成图像、演示文稿等方式,转向通过提示以高层次表达我们想要的内容,而不是用特定的语言编写。
这里有一个例子,展示了现在使用提示可以实现的事情,而以前这需要相当多的努力:
-
编程:通过提示,你表达你想构建的应用程序或你想要用代码做出的更改。
-
图像生成:以前你需要设计师或艺术家,现在你可以通过提示生成。
-
视频:有一些工具,一旦给出提示,就能生成由头像朗读你书面文本的视频。
-
文本任务:基于 LLM 的人工智能助手可以生成电子邮件、总结大量文本、撰写访谈广告等等;任何你可以用文本想象的事情。
所以上述提到的所有应用领域都清楚地表明,基于 LLM 的人工智能工具不仅对程序员和数据科学家有用,对众多不同的职业也是如此。
挑战与限制
目前一切是否运行得完美?人工智能助手还无法完全取代“你”,应该被视为更多是一种“思考伙伴”。微软甚至通过有意识的命名,将他们的 AI 助手称为“Copilots”,其中你显然是飞行员,负责设定方向。这些工具可以在几秒钟内生成文本和其他模态,但你需要验证其正确性。通常,你从工具那里得到的第一个响应是需要迭代的内容。好消息是,重新执行指令只需要几秒钟。
关于人工智能助手的要点之一是,你在某个特定主题上的技能越熟练,你就能向它提出越智能的问题,你将能够更好地评估响应的正确性。
关于这本书
本书的目标是:
-
介绍自然语言编程的新范式。
-
提供你开始使用 AI 助手的工具。
-
通过教授你提示工程和具体的一套提示策略(在第二章中介绍)以及一些良好的实践(在第八章中介绍),使你能够有效地和负责任地使用 AI 助手。
我们相信,有了这些工具、提示策略和实践,你将能够有效地和负责任地使用 AI 助手来增强你的工作并提高你的生产力。
本书面向的对象
本书面向网络和机器学习领域的专业开发者。它是为那些想学习如何使用 GitHub Copilot 和 ChatGPT 等 AI 助手来增强他们的工作和提高他们的生产力的人而写的。
编程语言的演变
编程在历史上经历了一系列的变化和范式转变:
-
在 19 世纪 40 年代,Ada Lovelace 为机器编写了第一个算法,即分析引擎。Lovelace 被认为是第一位计算机程序员,也是第一位认识到机器的应用不仅限于纯计算的人。
-
在 20 世纪 40 年代,创建了第一批可编程计算机。这些计算机使用穿孔卡片编程。其中之一是哈佛马克 I 号,它被用来计算炮弹的弹道。还值得一提的是 Bombe,它在第二次世界大战期间被用来破解恩尼格玛密码,对盟军赢得战争起到了关键作用。
-
在 20 世纪 50 年代,创建了第一批高级编程语言。这个时期见证了 FORTRAN、LISP、COBOL 和 ALGOL 的诞生。其中一些语言至今仍在使用,尤其是在银行系统、科学计算和防御领域。
-
在 20 世纪 70 年代,创建了第一批面向对象的编程语言。70 年代意味着我们得到了 Smalltalk、C++和 Objective-C。除了 Smalltalk 之外,这些语言至今仍在广泛使用。
-
在 20 世纪 90 年代,创建了第一批函数式编程语言。90 年代为我们带来了 Haskell、OCaml 和 Scala。这些语言的好处是它们鼓励不可变性和纯函数,这使得它们更容易推理和测试。
-
在 2000 年代,创建了第一批声明式编程语言。声明式编程语言用于描述你想做什么,而不是你想如何做。2000 年代为我们带来了 SQL、HTML 和 CSS。
-
在 2010 年代,创建了第一批低代码和无代码平台。这些平台使编程面向更广泛的受众,并允许任何,无论技术背景如何,都能构建应用程序。
-
在 2020 年代,创建了第一个利用自然语言的 AI 助手。如果你能写一句话,你就能写代码。
总结来说,编程经历了一系列的变化和范式转变。提示优先编程是最近的范式转变,掌握它将是保持相关性的关键。
展望未来
如果在过去,变化和范式转变需要花费数年或数十年,那么现在它们只需要几个月甚至几周。我们正以惊人的速度迈向一个新世界。
有理由感到兴奋,因为我们正在比以前更快地前进,但一如既往,我们应该保持谨慎。我们应该意识到使用这些工具不负责任的风险和危险,但最重要的是,我们应该意识到机会。
就像艾伦·凯曾经说过:“预测未来的最佳方式是创造它。”
如何使用本书
我们认为,最好的使用本书的方式是按照章节顺序阅读。
第二章,包含提示策略,是本书最重要的章节。这些模式和策略在整本书中都有所提及,并且是有效和负责任地使用人工智能助手的基石。
本书采用以下格式编写:
-
引言:第一章旨在为你提供一个关于本书内容、目标和适用人群的概述。
-
提示策略:其理念是建立如何在数据科学和网站开发领域内部分析问题的基础。从本章开始,你将学习可以用于你自己的问题的策略。
-
工具介绍:第三章向你介绍我们的工具,GitHub Copilot 和 ChatGPT,它们是什么,如何工作,以及如何安装它们。然而,本书的编写方式使得你可以将我们建议的任何提示输入到任何人工智能助手,并获得类似的经验。
-
书的剩余章节展示了我们如何使用第二章中的提示策略,并将它们应用于从网站开发到数据科学和机器学习的各个领域。
享受阅读!
加入我们的 Discord 社区
加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

第二章:提示策略
引言
在上一章中,我们提供了一些关于人工智能(AI)多年发展历程的历史背景,从自然语言处理(NLP)到大型语言模型(LLMs),以及后者作为人工智能助手背后的机器学习模型。要使用这些人工智能助手,您需要使用自然语言提示作为输入。然而,为了确保您能够高效地“提示”,从而得到您想要的结果,制定一个策略是非常重要的,这正是本章旨在提供的。
在业界,如何“提示”通常被称为“提示策略”或“提示工程”。这并不是字面意义上的工程实践,而是一种艺术形式,其中人工智能助手的从业者发现了似乎有效的模式和做法。我们,本书的作者,正在建立在这些发现的做法之上,并旨在描述我们在两个领域(全栈网络开发和数据科学)中的发现。本书面向您作为网络开发者或数据科学家,旨在通过描述您如何使用人工智能助手在您的领域中最佳地解决问题来赋予您力量。
本章构成了本书的核心部分。这里的“核心”是指所教授的方法将通过本书的其他章节进行例证。因此,请将本章视为一个您可以参考的指南,它提供了用于解决数据科学和全栈网络开发中特定问题的未来章节所使用的理论和思考。
在本章中,我们将:
-
提供一个使用提示解决问题的策略以及验证解决方案的指南。
-
用数据科学和全栈网络开发的例子来说明这个策略。
-
确定编写提示的基本原则。
你所在的位置
作为数据科学和/或全栈网络开发的读者和实践者,您了解您的技艺。了解您的技艺意味着您知道解决问题的关键工具和技术。在这个阶段,您正在查看一个人工智能助手,并意识到它是由自然语言,即所谓的提示所控制的。您可能没有意识到,这不仅仅是编写一个提示并得到一个答案那么简单。人工智能助手是在大量文本语料库上训练的,因此它在可以生成文本的内容以及如何响应提示方面非常灵活。正因为这种灵活性,理解如何编写有效且高效的提示就变得非常重要。
如何高效地提示的指南
提示是人工智能工具的输入。根据你试图实现的目标,你需要调整你的提示以适应你正在解决的问题场景。因此,你如何“提示”很重要。例如,如果你的提示太模糊,你将不会得到你需要的。或者,假设你试图使用提示来生成公司口号;你不希望为生成应用程序代码使用相同的提示。相反,在数据科学这样的学科中,按照一定的顺序执行任务很重要,你的提示应该反映你想要完成的工作,以及如果需要,完成这些工作的步骤。
你需要成功的是一种方法,一种策略,你可以在一般情况下使用它来与人工智能助手高效地合作。此外,这样的策略应该足够具体,以便为所选问题领域提供“最佳实践”。正如本章前面提到的,我们已经为该领域的全栈 Web 开发和数据科学开发了一种特定的提示策略。
在高层次上,我们建议使用人工智能助手进行一般问题解决的指南;我们相信它适用于你将提示应用于任何问题领域的情况:
-
将问题分解,以便完全理解。在这个指南中,可能会有以下步骤:
-
理解问题:对于任何问题,理解问题是什么以及它不是什么都很重要。例如,我们是构建一个用于预测销售的机器学习模型,还是一个用于跟踪库存的网页?这两个问题是不同的,需要不同的方法。
-
识别各个部分:一个问题通常很复杂,由许多需要解决的组成部分组成。例如,如果我们正在构建一个用于预测销售的机器学习模型,我们需要识别数据、模型、训练和评估。这些部分的每一部分都可以进一步分解成更小的部分。一旦你找到了适合你问题的正确细节级别,你就可以通过编写提示来开始解决问题。
-
将问题分解成更小的部分:如果需要,将你的问题分解成更小、更易于管理的部分。
-
识别和理解数据:特别是在机器学习中,确定一个可以工作的数据集、它包含的内容以及它的结构至关重要。在 Web 开发中,数据也扮演着核心角色,但目标通常是确保你能够以对用户有用的方式读取、写入和展示数据。
-
-
在适当的级别生成提示。一旦你完全理解了问题,你应该有一个任务列表,并且对于每个任务,你应该能够编写和运行一个解决该任务的提示。
-
验证解决方案。就像没有 AI 助手一样,验证在构建系统或应用中起着至关重要的作用。传统上,这意味着编写测试,测试各种组件的组合,并让用户尝试各种部分。使用提示没有不同。LLMs 的一个副作用是它们可以生成与问题不相关的文本,或者以非最佳方式解决问题。因为您依赖于最终为您生成代码的提示,所以验证解决方案以确保其正确性和相关性变得尤为重要。
在接下来的章节中,我们将描述为每个选定的问题域的全栈网络开发和数据科学制定详细策略。
提示技术
有一些技巧在指导 AI 助手时特别有用,以确保您充分利用它。一般建议是向 AI 助手提供足够的背景信息,但具体意味着什么可以通过以下建议的模式来具体化:
-
任务-行动-指南提示模式
-
人物-指令-内容提示模式
-
探索性提示
任务-行动-指南提示模式(TAG)
为了解释这个模式是如何工作的,让我们分解各个部分,并展示一个带有示例响应的示例提示:
意图:指导 AI 助手解决什么任务,采取什么行动,以及遵守什么指南。
-
任务:明确定义您希望 AI 完成的任务
-
行动:指定 AI 应采取的步骤或行动以完成任务
-
指南:提供 AI 在执行任务时应遵循的任何规则、约束或指南
示例:
[提示]
任务:生成关于最新气候变化新闻文章的摘要。
行动:
-
搜索有关气候变化的最新新闻文章。
-
选择相关性最高、可信度最高的文章。
-
用 3-4 句话总结文章的关键点。
指南:
-
确保摘要简洁准确。
-
避免使用技术术语;保持语言简单易懂。
-
在摘要末尾引用文章来源。
[提示结束]
[提示响应]
摘要:BBC News 最近的一篇文章突出了过去十年全球温度的惊人上升。科学家们警告,如果当前趋势持续下去,我们可能会看到前所未有的气候事件。文章强调,国际社会迫切需要合作来应对气候变化。[来源:BBC News]
这种模式有助于确保 AI 助手理解任务,知道采取的步骤,并遵守任何具体指南,从而产生更准确、更有用的输出。
[响应结束]
下面是一个关于网络开发的提示示例:
[提示]
任务:使用 Node.js 和 Express 为简单的博客应用程序开发 RESTful API。
行动:
-
设置一个新的 Node.js 项目。
-
创建一个 Express 服务器。
-
定义博客文章的 CRUD 操作路由。
-
实现 JSON 解析和错误处理的中间件。
-
使用 Mongoose 连接到 MongoDB 数据库。
-
为 API 端点编写测试。
指南:
-
确保项目设置清晰易懂。
-
提供简洁准确的代码示例。
-
避免使用过于复杂的术语;保持语言简单易懂。
-
在代码中包含注释以解释每一步。
[提示结束]
角色指令-上下文提示模式(PIC)
让我们以相同的方式描述这个模式,先定义它,然后是一个示例提示和一个示例响应:
意图:指导 AI 助手扮演的角色,执行哪些指令,并提供足够的上下文以生成正确的响应。
-
角色:定义 AI 在响应时应体现的角色或性格特征
-
指令:指定 AI 应执行的任务或动作
-
上下文:提供相关的背景信息或上下文,以帮助 AI 理解情况并生成适当的响应
示例:
[提示]
角色:扮演一个友好且知识渊博的旅行指南。
指令:
-
推荐巴黎三个必游的景点。
-
为每个景点提供简要描述。
-
建议参观每个景点的最佳时间。
上下文:用户第一次计划去巴黎旅行,对历史遗迹和当地文化都感兴趣。
[提示结束]
[提示响应]
作为您友好且知识渊博的旅行指南,我很乐意推荐巴黎三个必游的景点:
-
埃菲尔铁塔:这个标志性的地标提供了令人叹为观止的城市景观。最好在清晨或傍晚时分参观,以避开人群,欣赏壮丽的日出或日落。
-
卢浮宫博物馆:拥有成千上万的艺术作品,包括蒙娜丽莎,卢浮宫是历史和文化的宝库。计划在周一上午参观,以体验人少时的博物馆。
-
蒙马特区:这个迷人的社区以其艺术历史和充满活力的街头生活而闻名。下午晚些时候参观,可以探索这些古朴的街道,并在当地咖啡馆享受悠闲的晚餐。
享受您的巴黎之旅!
[响应结束]
这种模式确保 AI 助手采用正确的角色,遵循特定的指令,并使用提供的上下文提供相关且有帮助的响应。
这就是这样的提示在 Web 开发中的样子:
[提示]
角色:扮演一个友好且知识渊博的软件开发导师。
指令:
-
设置一个新的 Node.js 项目。
-
创建一个 Express 服务器。
-
为博客文章定义 CRUD 操作的路由。
-
实现 JSON 解析和错误处理的中间件。
-
使用 Mongoose 连接到 MongoDB 数据库。
-
为 API 端点编写测试。
上下文:用户是网页开发的初学者,希望为博客应用程序构建第一个 RESTful API。他们需要清晰的、一步一步的指导和代码示例。
[提示结束]
探索性提示模式
你可能会遇到这样的情况,你不是从头到尾构建一个项目,或者你只想构建项目的一部分并评估其响应。在这些情况下,你的提示将更具探索性,例如可以像下面这样:
[提示]
清洗数据。
[提示结束]
这里有一个假设,即我们有一个已经获取数据的现有代码的笔记本打开。
或者,更多关于网页开发的:
[提示]
为此产品列表添加 CSS。
[提示结束]
这种模式的提示通常较短,有上下文(来自现有代码或其他方式),并且开发者的视线很少超出下一步。
学习-即兴发挥-反馈-评估提示模式(LIFE)
这种模式,就像 TAG 和 PIC 一样,有助于界定问题并提供一个良好的解决方案起点,你可以进一步细化。
-
学习:强调通过各种分析技术理解数据的重要性,从基本统计到复杂的相关性和时间分析。
-
即兴发挥:根据初步发现调整分析。例如,如果某些产品类别显示出不寻常的趋势,就深入分析这些领域。
-
反馈:
-
分享代码和模型输出以供反馈,以确保有效的学习和理解。
-
将建议和批评融入以完善模型和方法。
-
提供错误以排查和解决这些问题
-
-
评估:执行 ChatGPT 提供的代码以确保准确性和有效性。
这个模式的示例提示可能看起来像这样:
[提示]
项目标题:构建基于网络的搜索结果页面。你必须建议步骤和代码
目标:创建一个动态且交互式的搜索结果页面,能够根据用户查询高效地显示和过滤搜索结果。步骤:
-
学习:理解有效地显示搜索结果的重要性。分析用户交互数据以识别关键指标,如点击率、平均在结果页面上的时间以及用户满意度评分。使用从基本统计到复杂相关性及时间分析的各种分析技术,深入了解用户行为和偏好。
-
即兴发挥:根据初步发现调整搜索结果页面,以突出显示最相关的结果。例如,如果用户经常点击某些类型的搜索结果,则在显示中优先考虑这些结果。实现过滤、排序选项和分页等特性以增强用户体验。
-
反馈: 与同行或导师分享你的代码和搜索结果页面以获取反馈。这将有助于确保有效的学习和理解。结合他们的建议和批评,以完善搜索结果页面并提高其功能。提供错误:记录开发过程中遇到的任何错误,如数据检索或显示问题。诊断并解决这些问题,从过程中学习以提升你的技能。
-
评估: 执行 ChatGPT 提供的代码以确保其准确性和有效性。通过将性能与实际数据和用户反馈进行比较来验证搜索结果页面。根据需要调整以改进准确性和用户满意度。
[结束提示]
你可能会得到一个类似的提示响应:
[提示响应]
太好了!让我们分解步骤并提供一些代码片段,帮助你构建一个动态和交互式的搜索结果页面。
-
项目标题: 构建基于网络的搜索结果页面
-
目标: 创建一个动态且交互式的搜索结果页面,能够根据用户查询高效地显示和过滤搜索结果。
步骤:
学习:
-
理解有效显示搜索结果的重要性:
-
分析用户交互数据,以识别关键指标,如点击率、平均在结果页面上的时间以及用户满意度评分。
-
使用从基础统计到复杂相关性及时间分析的各种分析技术,以深入了解用户行为和偏好。
-
即兴发挥:
-
根据初步发现调整搜索结果页面:
-
根据用户交互数据突出显示最相关的结果。
-
实现过滤器、排序选项和分页等特性,以增强用户体验。
-
反馈:
-
与同行或导师分享你的代码和搜索结果页面以获取反馈:
- 结合他们的建议和批评,以完善搜索结果页面并提高其功能。
提供错误:
-
记录开发过程中遇到的任何错误:
- 诊断并解决这些问题,从过程中学习以提升你的技能。
评估:
-
执行 ChatGPT 提供的代码以确保其准确性和有效性:
- 通过将性能与实际数据和用户反馈进行比较来验证搜索结果页面。根据需要调整以改进准确性和用户满意度。
代码片段:
HTML 结构:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Search Results Page</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="search-container">
<input type="text" id="search-input" placeholder="Search...">
<button onclick="performSearch()">Search</button>
</div>
<div class="results-container" id="results-container"></div>
<script src="img/script.js"></script>
</body>
</html>
CSS 样式:
/* styles.css */
body {
font-family: Arial, sans-serif;
}
.search-container {
margin: 20px;
}
.results-container {
margin: 20px;
}
.result-item {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
JavaScript 功能:
// script.js
function performSearch() {
const query = document.getElementById('search-input').value;
const resultsContainer = document.getElementById('results-container');
resultsContainer.innerHTML = ''; // Clear previous results
// Simulate search results
const results = [
{ title: 'Result 1', description: 'Description for result 1' },
{ title: 'Result 2', description: 'Description for result 2' },
{ title: 'Result 3', description: 'Description for result 3' },
];
results.forEach(result => {
const resultItem = document.createElement('div');
resultItem.className = 'result-item';
resultItem.innerHTML = `<h3>${result.title}</h3><p>${result.description}</p>`;
resultsContainer.appendChild(resultItem);
});
}
附加功能:
-
过滤和排序: 添加下拉菜单或复选框以过滤和排序结果。
-
分页: 实现分页以处理大量结果集。
-
错误处理: 添加错误处理以管理数据检索或显示问题。
这应该为你提供一个坚实的基础,以开始构建你的基于网络的搜索结果页面。如果你需要进一步的帮助或更详细的代码示例,请告诉我!
[结束响应]
选择哪种模式?
一般答案是取决于具体情况选择哪种模式。选择 TAG 和 PIC 的原因是你有一个从开始到结束都希望解决的明确问题,并且你希望 AI 助手来做“重活”。选择探索模式的原因是当你正在处理现有的代码库,或者你想要尝试各种方法和步骤。我们的普遍建议是尝试所有三种模式,看看哪种最适合你的方法和问题。
Web 开发的提示策略
让我们应用前一部分中提出的不同指南,通过使用这些指南,我们将逐步工作,逐步制定出提示策略。
将问题分解:“库存管理系统”
让我们用一个真实例子,“Web 开发中的库存管理”,来看看我们是否能够理解一般问题。要“管理”库存,你需要读取和写入数据。很可能会在这个系统/应用程序中有不同的角色,从管理员到普通用户。你可能还需要考虑这个系统如何与其他系统结合,例如,如果你需要将其与其他系统集成,那么它由哪些部分组成以及如何。
领域看起来相当直接,让我们继续了解它由哪些部分组成。
在高层次上,我们理解系统应该做什么。但为了解决问题,我们需要将其分解成更小的部分,在 Web 开发中通常包括以下组件:
-
前端:前端是用户与之交互的系统部分。前端负责向用户展示数据并接收用户输入。
-
后端:这个系统部分与前端通信。后端负责读取和写入数据库中的数据。在更复杂的系统中,可能还有不同的前端和不同的应用程序与后端进行更复杂的通信。
-
数据库:数据库是存储数据的系统部分。它是一个数据存储,例如,一个关系型数据库,如 MySQL 或 PostgreSQL。数据库负责以高效且易于读写的方式存储数据。
-
报告:通常有一个报告部分来展示洞察。它从数据存储中获取数据,可能需要转换数据以便从报告的角度展示。
前端功能的进一步分解
有这样的概览是有用的,但通常不足以让我们开始编写提示。我们需要进一步分解,通常是通过功能。在这个阶段,前端功能的进一步分解可能看起来像以下列表:
-
登录:用户需要能够登录到系统中。
-
登出:用户需要能够从系统中登出。
-
查看库存:用户需要能够查看库存。
-
添加库存:用户需要能够添加库存。
-
移除库存:用户需要能够移除库存。
-
更新库存:用户需要能够更新库存。
为每个功能生成提示
到这一点,它足够具体,我们可以开始编写提示。让我们以第一个功能,登录,来看看我们如何开始思考如何制作提示。
你可以从一个提示开始,使用像 ChatGPT 或 GitHub Copilot(更多内容将在下一章中介绍)这样的工具,如下所示:
[提示开始]
为用户创建一个登录页面以便登录系统。
[提示结束]
虽然这可能可行,但你遗漏了很多上下文,比如这里到底需要什么,什么是不需要的,你使用的技术是什么,用户体验如何等等。
让我们尝试通过添加更多上下文来改进提示:
[提示开始]
创建一个包含用户名和密码字段的登录页面。它应该有一个创建用户的链接和一个登录按钮,垂直和水平居中,并且在手机和平板电脑上运行良好。它应该使用 React 编写并使用 Material UI 库。
[提示结束]
确定一些 Web 开发的基本原则,一个“提示策略”
如你所见,我们将问题分解成更小、更“可管理”的部分,并建议了一些针对特定功能的提示。那么,现在我们了解了更多关于我们的示例“库存管理”,我们具体建议了什么“提示策略”?一方面,我们意识到我们的策略将是上下文相关的。因为我们处于 Web 开发领域,我们需要在该领域使用关键词,以及适合特定区域的库和架构。以下是我们建议你使用的指南:
-
提供上下文 - 字段:登录屏幕可以像用户名和密码字段那样简单。然而,大多数屏幕有更多的字段,例如密码确认字段、重置密码的链接、创建新用户的字段等等。根据你的需求,你可能需要非常详细。
-
指定如何 - 设计和技术选择:登录屏幕可以以多种方式设计。如今,优化不同设备如平板电脑、手机、大屏幕等是非常常见的。在技术选择方面,Web 开发有很多选择,从 React、Vue 和 Angular 这样的框架到纯 HTML 和 CSS。根据项目的需求进行指定。
-
迭代:不同的工具对相同的提示反应不同。在这本书中,我们将向你展示如何使用 GitHub Copilot 和 ChatGPT 等不同的工具。每个工具都有其自身的优势和劣势,可能会提供不同的结果。尝试通过添加分隔符如逗号和冒号来迭代提示,并尝试重新措辞提示。
-
具有情境意识:当你使用像 ChatGPT 和 GitHub Copilot 这样的工具时,你是带着预先存在的情境使用的。对于 ChatGPT 来说,这意味着你正在进行一个持续的提示和响应对话,而对于 GitHub Copilot 来说,这意味着它不仅看到了你在打开的文件中写的内容,如果你允许的话,还包括你的整个工作空间。对你的提示的响应会考虑这个情境,并决定生成什么。了解这个情境很重要,如果你没有得到你想要的响应,尝试改变情境,在 ChatGPT 中开始新的对话,在 GitHub Copilot 中关闭打开的文件,开始在一个空白文件中写作等。
数据科学中的提示策略
让我们对数据科学进行类似的思维实验,就像我们对网络开发所做的那样。我们将使用提供的指南“问题分解”和“生成提示”,就像在 Web 开发部分一样,我们将对领域进行一些一般性的结论,并将这些作为数据科学的提示策略。
问题分解:预测销售
假设我们正在构建一个机器学习模型来预测销售。在较高层次上,我们理解系统应该做什么。但是,为了解决这个问题,我们需要将其分解成更小的部分,在数据科学中通常包括以下组件:
-
数据:数据是系统中存储信息的一部分。数据可以来自许多地方,如数据库、网络端点、静态文件等。
-
模型:模型负责从数据中学习并产生尽可能准确的预测。为了进行预测,你需要一个输入,它会产生一个或多个作为预测的输出。
-
训练:训练是系统中训练模型的部分。在这里,你通常有一部分数据作为训练数据,另一部分作为样本数据。
-
评估:为了确保你的模型按预期工作,你需要对其进行评估。评估意味着使用数据和模型产生一个分数,该分数表示模型的表现有多好。
-
可视化:可视化是你可以通过图表获得对业务有价值的见解的部分。这部分非常重要,因为它是业务中最可见的部分。
将数据科学分解为特征/步骤
在这个阶段,你处于一个过于高的层次,无法开始编写提示。我们可以通过查看每个步骤来进一步分解它:
-
数据:数据部分有许多步骤,包括收集数据、清洗数据和转换数据。以下是你可以如何分解它:
-
收集数据:数据需要从某个地方收集。它可能来自数据库、网络端点、静态文件等。
-
清洗数据:数据需要被清洗。清洗意味着移除不相关的数据、删除重复项等。
-
数据转换:数据需要被转换。转换意味着将数据改变成对模型有用的格式。
-
-
训练:就像数据部分一样,训练部分有很多步骤。以下是你可以如何分解它:
-
分割数据:数据需要被分割成训练数据和样本数据。训练数据用于训练模型,样本数据用于评估模型。
-
训练模型:模型需要被训练。训练意味着从训练数据中学习。
-
-
评估:评估部分通常是单一步骤,但可以进一步分解。
为每个步骤生成提示
注意我们的数据科学分解看起来与网络开发略有不同。我们不是识别像添加库存这样的功能,而是有一个像收集数据这样的功能。
然而,我们已经到达了编写提示的正确水平,所以让我们以收集数据功能为例:
[提示]
使用 Pandas 库从data.xls收集数据并将其读入 DataFrame。
[提示结束]
前面的提示既通用又具体。在通用方面,它告诉你要“收集数据”,但在具体方面,它指定了要使用的特定库以及数据结构(DataFrame)。完全有可能一个更简单的提示就可以完成前面的步骤,如下所示:
[提示]
从 data.xls 中收集数据。
[提示结束]
这取决于你是否使用像 ChatGPT 或 GitHub Copilot 这样的工具。
确定数据科学的一些基本原则,“数据科学提示策略”
这里,我们确定了与网络开发示例中类似的一些原则:
-
提供上下文 – 文件名:CSV 文件可以有任意名称。指定文件名很重要。
-
指定如何 – 库:有许多方法可以加载 CSV 文件,尽管 Pandas 库是一个常见的选择,但指定它很重要。还有其他库可以工作,你可能需要一个针对 Java、C#和 Rust 等语言的解决方案,在这些语言中库的名称不同。
-
迭代:迭代提示是有价值的,重新措辞它,并添加分隔符,如逗号、冒号等。
-
注意上下文:在这里,上下文也很重要;如果你在 Notebook 中工作,之前的单元格将可供 GitHub Copilot 使用,之前的对话将可供 ChatGPT 使用,等等。
如前所述的指导所示,策略在网页开发中也非常相似。这里我们也列出了“提供”、“指定如何”、“迭代”和“注意上下文”。主要区别在于细节。然而,在数据科学中还有一个有效的替代策略,那就是长提示。尽管我们已经将数据科学问题分解为功能,但我们不需要为每个功能编写提示。另一种解决方法可能是在一个大的提示中表达你想要执行的所有内容。因此,这样的提示可能看起来像这样:
[提示]
你想要预测data.xsl文件上的销售额。使用 Python 和 Pandas 库。以下是你应该执行的步骤:
-
收集数据
-
清洁数据
-
转换数据
-
分割数据
-
训练模型
-
评估
[提示结束]
你将在未来的章节中看到数据科学和机器学习中的例子,其中既使用了较短的提示,也使用了较长的提示。你决定使用哪种方法。
验证解决方案
这种策略最重要的部分是验证正确性,以及 AI 助手创建的文本和代码是否正确。我们可以采取两种一般方法来验证我们的结果:
-
通过提示进行验证:第一种方法是使用提示来验证结果。这意味着编写提示来质疑特定结果的输出。这可以在验证过程的开始时采用的一种好策略。你所寻找的是 AI 助手在响应中不一致的情况。
-
经典验证方法:第二种方法是使用经典验证技术。这些技术取决于问题领域。然而,从高层次来看,这归结为测试代码、比较输出,并依靠你自己的知识和你同伴的知识来验证结果。
AI 工具实际上并不知道它在做什么。提供的响应可能是依赖于其训练语料库的响应。在所有时候,你应该意识到这一点,并依靠你的专业知识来验证结果。
在接下来的小节中,让我们探讨各种手动和经典验证的方法。
通过提示进行验证
你可以使用提示来产生结果,这些结果可以帮助你更接近解决问题,也可以用来验证结果。让我们以我们正在构建之前提到的登录屏幕为例。我们已经编写了一个如下所示的提示:
[提示]
创建一个登录页面,包含用户名和密码字段;它应该有一个创建用户和登录按钮的链接。它应该垂直和水平居中,并在手机和平板上运行良好。它应该使用 React 编写并使用 Material UI 库。
[提示结束]
为了验证这个结果,我们可以写一个如下所示的提示:
[提示]
给定以下代码,它做什么?
.login {
<!-- should include CSS to center horizontally and vertically and be responsive -->
@media (min-width: 768px) {
<!-- should include CSS to center horizontally and vertically and be responsive -->
}
}
<div class="login">
<TextField id="username" label="Username" />
<TextField id="password" label="Password" />
<Button variant="contained" color="primary">
Login
</Button>
</div>
[提示结束]
一个应用程序可以查看提供的代码,并意识到你不需要提示来推断它做什么,实际上它缺少 CSS 代码来使其响应。然而,这里的重点是,通过编写提示,你可以让 AI 助手通过提问告诉你它认为代码做什么。
使用提示这种方式,对输出提出查询,是验证结果的好第一步。然而,这还不够,你需要依赖经典验证技术,所以让我们接下来讨论一下。
经典验证
你如何验证结果取决于问题领域。在 Web 开发中,你可以使用各种不同的工具和技术,例如:
-
测试:通过端到端测试或前端测试,你可以验证代码是否按预期工作。通常,这种测试涉及使用程序化方法来模拟用户与网页的交互,例如使用 Selenium 等。
-
手动测试:你可以通过在浏览器中打开网页并与它交互来手动测试网页。这是在验证过程开始时使用的一个好方法。除了交互之外,你还可以根据你的要求从视觉上检查网页是否正确。
-
代码审查:你可以审查代码,看看它是否看起来正确。这是在验证过程开始时使用的一个好方法。这不仅允许你验证结果,还允许你的同行进行验证。
-
工具:工具可以测试各种不同的场景,如可访问性、性能等。这些工具很可能已经是你的开发过程的一部分了。
在进行数据科学时,你可能依赖于所有上述方法,但你也可以使用其他方法。一些常见的方法包括:
-
单元测试:你可以使用单元测试来验证代码是否按预期工作。
-
集成测试:你可以使用集成测试来验证代码是否按预期工作。
-
结果验证:这种验证方式意味着你将你的分析或模型的结果与已知结果或基准进行比较。
-
交叉验证:这种验证方式意味着你将数据分为训练数据和样本数据,在训练数据上训练你的模型,并在样本数据上评估它。这是在验证过程开始时使用的一个好方法。
摘要
在本章中,我们提供了一种使用提示和验证解决方案的策略。
你已经看到了 Web 开发和数据科学如何被分解成更小的部分,这些部分可以用提示来解决。我们还确定了一些编写提示的基本原则。
最后,我们探讨了如何使用提示和经典验证技术来验证解决方案。
我们希望你在解决 Web 开发或数据科学中的问题时,再次回顾这一章节,当你寻找一种方法时。
提示不仅仅是写一个提示并得到一个响应。你将在本书中看到我们如何在各个领域使用这些原则来解决问题。试着在阅读时输入这些提示,根据你的需求进行调整,看看会发生什么。
在下一章中,我们将学习更多关于我们选择的两个 AI 助手,GitHub Copilot 和 ChatGPT。
加入我们的 Discord 社区
加入我们的社区 Discord 空间,与作者和其他读者进行讨论:

第三章:行业工具:介绍我们的 AI 助手
简介
编写代码或文本需要时间,至少如果你希望它组织良好且易于阅读的话。但如果你有一个可以帮助你更快、更轻松地编写代码的工具会怎样?这正是 GitHub Copilot 和 ChatGPT 的真正目的。
在开始使用 AI 助手之前,了解其能力和限制是一个好主意。你想要看到你能用它做什么,不能做什么,或者至少了解工具在哪些方面表现不佳。
在本章中,我们将涵盖以下内容:
-
理解 GitHub Copilot 和 ChatGPT 是什么以及它们是如何工作的
-
了解 Copilot 的功能和限制
-
安装 GitHub Copilot
-
通过 GitHub Copilot 生成代码补全
理解 Copilot
对接编程是(通常是两个)开发者一起工作的理念,通常在同一个屏幕前,有时也称为“对接”。GitHub Copilot 可以被视为一个“AI 对接程序员”,帮助你编写代码,让你能更快地完成更多工作。它基于 OpenAI 的 Codex 模型,这是一个在公开可用的源代码和自然语言上训练的新 AI 系统。但在现实中,它已经超越了这一点。让我们在本书的剩余部分将 GitHub Copilot 称为 Copilot。Copilot 可以直接在你的编辑器中建议整行或整个函数。
Copilot 如何知道要生成什么
Copilot 背后的理念是,它从你和其他人编写的代码中学习,并使用这些知识在你键入时建议新的代码行。
Copilot 是如何工作的?它使用机器学习来构建你和其他人编写的代码模型,并为你提供最佳文本建议。有两个重要部分,即训练模型和所谓的内存上下文。模型是在 GitHub 的公共仓库上训练的,而上下文是它在运行时从查看你的文件中组装出来的。使用上下文和基础模型,它为你提供文本建议。Copilot 使用以下一些内容来构建其上下文(即,它与训练模型一起使用的内存能力,以提供建议):
-
你的活动文件:你正在工作的代码。
-
注释:Copilot 使用注释来理解你代码的上下文。
-
打开文件和你的工作区:它不仅查看你活动文件中的代码,还查看你工作区中其他文件中的代码。
-
导入语句:甚至导入语句也被纳入 Copilot 的建议中。
-
基础模型及其训练数据:公开 GitHub 仓库中的代码构成了其训练的基础。
Copilot 的能力和限制
那么,Copilot 能做什么?它能做很多事情,但这里是一个非详尽的特性列表:
-
代码补全:Copilot 可以为你补全代码行。
-
代码生成:Copilot 可以为你生成整个函数。
-
测试、注释和文档:Copilot 可以为你生成测试、注释和文档。
-
建议改进: Copilot 可以对你的代码提出改进建议。改进可以以多种形式出现,从建议更好的变量名或更好的函数编写方式到如何更好地组织代码。
-
代码翻译: Copilot 可以将一种语言的代码翻译成另一种语言。例如,它可以翻译 Python 代码到 JavaScript 代码。
-
回答问题:Copilot 可以回答有关你的代码的问题。例如,它可以告诉你一个函数的作用或一个变量的用途,并回答关于某个领域的问题,例如“什么是机器学习?”等。
设置和安装
如何开始?你可以从各种地方和编辑器中使用 Copilot,包括 Visual Studio、Visual Studio Code、GitHub Codespaces 和 GitHub 的基于网页的编辑器。在本章中,我们将使用 Visual Studio Code。
安装 Copilot
要安装 Copilot,你需要安装 GitHub Copilot 扩展并为 Visual Studio Code 允许访问。
让我们更详细地回顾一下步骤(如官方 Copilot 文档页面所述)。
你可以从 Visual Studio Code 市场或从 Visual Studio Code 内部安装 GitHub Copilot 扩展。这里我们将展示后者:
-
在 Visual Studio Code 的 扩展:GitHub Copilot 选项卡中,选择 安装。
-
如果你之前没有在 GitHub 账户中授权 Visual Studio Code,你将被提示在 Visual Studio Code 中登录 GitHub。
-
如果你之前已经为 GitHub 上的你的账户授权了 Visual Studio Code,GitHub Copilot 将自动授权。
-
如果你没有收到授权提示,请选择 Visual Studio Code 窗口底部的铃铛图标。
-
在你的浏览器中,GitHub 将请求 GitHub Copilot 所需的权限。为了批准这些权限,请选择 授权 Visual Studio Code。
-
为了确认认证,在 Visual Studio Code 中,选择 Visual Studio Code 对话框中的 打开。
如果你在设置 Copilot 时遇到任何问题,请参考此页面:docs.github.com/en/copilot/getting-started-with-github-copilot。
开始使用 Copilot
我们如何开始?好吧,只要你已经安装了 Copilot,并且 Visual Studio Code 窗口的右下角有 Copilot 图标,你就可以开始了。
这里有一个开始的建议:
-
在 Visual Studio Code 中创建一个名为
app.js的新文件。 -
在文件顶部像这样作为注释开始输入文本提示“Express web api with routes products and customers”,然后按 Enter:
//Express web api with routes products and customers -
等待几秒钟,你应该会看到 Copilot 的以下建议:
const express = require('express');如果没有出现任何内容,尝试按下 Ctrl + Spacebar 来触发建议或开始输入代码的开始部分,例如 const,并等待建议出现。
-
你将需要按下 Tab 键来接受建议。在此阶段,Copilot 可以继续为你生成代码。为了确保它这样做,请按下 Enter 并观察 Copilot 为你生成更多代码。重复按下 Enter 并按下 Tab 来接受建议,直到你拥有类似以下代码:
const app = express(); app.get('/products', (req, res) => { res.send('products'); }); app.get('/customers', (req, res) => { res.send('customers'); }); app.listen(3000, () => { console.log('Server listening on port 3000'); }); -
恭喜你,你刚刚用 Copilot 编写了你的第一行代码。请随意尝试使用 Copilot,并在代码中间添加注释,所谓的提示,看看会发生什么。还可以尝试改变提示,看看会发生什么。
作业:改进代码
作为一项作业,你被要求改进 Copilot 生成的代码。以下是一些建议:
-
为 Web API 的根添加一条路由。
-
为特定产品添加一条路由。
-
为其中一条路由添加文档。
解决方案
这里有一个可能的解决方案:
const express = require('express');
app = express();
// add default route
app.get('/', (req, res) => {
res.send('Hello world');
});
app.get('/products', (req, res) => {
res.send('products');
});
// document route
/**
* Get a product by id
* @param {number} id - The id of the product
*/
app.get('/products/:id', (req, res) => {
res.send(`product with id ${req.params.id}`);
});
app.get('/customers', (req, res) => {
res.send('customers');
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
挑战
检查一下是否可以添加一条路由的测试。
在下一章中,我们将详细探讨如何使用 Copilot。要有效地使用任何 AI 助手,你需要了解它是如何工作的以及如何使用它。与这些工具的良好使用相关联的技能被称为提示工程。提示工程是一种艺术,不仅是为了让它理解你的意图,也是为了产生你满意的输出。这不仅仅是写注释;你可以指示你的 AI 助手解决问题,对其应用某种推理,等等。下一章将介绍本书的中心主题,即提示工程。
参考文献
-
Copilot 登录页面:
github.com/features/copilot -
Copilot 文档:
docs.github.com/en/copilot/getting-started-with-github-copilot
理解 ChatGPT
ChatGPT,是 OpenAI 开发的一款产品,是 GPT 模型的专业版本,旨在模拟类似人类的对话。它在创建类似人类的对话文本方面表现出色,能够处理各种话题。它可以在 chat.openai.com 上免费使用,并提供高级 ChatGPT Plus 选项(也称为 GPT-4),可以撰写文章、生成艺术提示和编写代码。高级版本提供增强功能,如处理视觉和音频输入输出、文件上传、使用选定的 Python 库进行数据可视化,以及可定制的 GPT 功能。
你可以通过访问 chat.openai.com 并创建 OpenAI 账户来访问 ChatGPT。它也适用于 Android 和 iOS 的应用程序。更多详情可以在官方网站上找到(openai.com/)。

图 3.1:OpenAI 的产品
ChatGPT 是如何工作的?
ChatGPT 与 Copilot 的代码导向方法并行,但在自然语言处理方面,擅长内容生成,挑战传统搜索引擎。它在撰写文章和总结文本等任务上表现出色。ChatGPT 的回复质量很大程度上取决于它收到的提示。
ChatGPT 利用包括书籍、网站和多种文本来源在内的广泛训练数据,以实现全面的语言理解。
它采用复杂的机器学习算法,如基于 Transformer 架构的深度学习神经网络,以预测准确且与上下文相关的文本回复。
ChatGPT 通过高级技术磨练其上下文理解能力,使其能够智能地解释和回应不同的对话线程。这种方法与 Copilot 在代码中使用的原则相似,这里调整为细微、类似人类的文本交互。
ChatGPT 的能力和限制
ChatGPT 的能力:
-
内容创作:生成包括营销材料、博客文章、故事和诗歌在内的创意内容
-
教育解释:为教育目的提供复杂主题的详细解释
-
编码辅助:帮助开发者进行代码优化、错误调试和算法设计
-
学习辅助:作为在线学习的伴侣,提供实时帮助和概念澄清
-
对话式 AI:通过自然语言交互增强虚拟助手和聊天机器人的用户体验
ChatGPT 的限制和担忧:
-
准确性问题:ChatGPT 可能会生成包含事实错误或来自训练数据的偏差的回复,也称为幻觉。这些输出通常源于 AI 模型固有的偏差、缺乏现实世界理解或训练数据限制。换句话说,AI 系统“幻觉”出它未明确训练过的信息,导致不可靠或误导性的回复。因此,用户应始终验证和核实回复,不应盲目使用。
-
伦理影响:对 AI 生成内容被用于欺诈活动或有害信息收集的滥用表示担忧。
-
就业影响:担心 AI 在某些领域取代人类工作。
-
安全风险:可能用于钓鱼、创建恶意软件和网络犯罪活动。
-
数据隐私:对在训练中使用大量互联网数据的担忧,影响用户隐私。
-
信息限制:在撰写本书时,GPT-4 被限制在 3 小时内最多提供 40 个回复。
-
代码执行库有限:ChatGPT 的代码解释器和高级数据分析功能使用有限的库,大量使用机器学习库,但对其他库的支持不佳,例如深度学习所需的 Keras 或 TensorFlow。
设置和安装
设置和安装 ChatGPT 涉及几个步骤:
-
创建 OpenAI 账户: 访问 OpenAI 网站并注册账户。
-
API 访问: 开发者需要通过在 OpenAI 平台上申请来获取 API 访问权限。
对于非开发者来说,使用 ChatGPT 就像访问 ChatGPT 网站或安装 Android 或 iOS 应用并使用您的 OpenAI 账户登录一样简单。一般使用不需要安装。有关更详细的步骤和信息,请参阅 OpenAI 的官方文档和网站。
开始使用 ChatGPT
一旦您在网站上的 ChatGPT 侧登录了您的 OpenAI 账户,就是时候了解 AI 工具的窗口了。以下是您将看到的内容概述:
-
新建聊天和隐藏侧边栏按钮: 在屏幕左侧,新建聊天按钮可以随时开始新的对话。它创建了一个没有上下文的新讨论。同时,您还可以选择隐藏侧边栏。
-
聊天历史: 左侧侧边栏使您之前的对话保持可访问。您可以编辑聊天标题、分享您的聊天历史或删除它。您可以选择关闭聊天历史。
-
账户: 点击左下角的您的名字以访问您的账户信息。这包括设置、登出、帮助和常见问题解答。如果您没有 ChatGPT Plus,您将在这里看到升级按钮。
-
您的提示: 您的问题或提示出现在聊天窗口的中间,旁边是您的账户照片或首字母缩写。
-
ChatGPT 的回复: ChatGPT 的回复在左侧显示标志。在右侧,您将看到复制、点赞和踩赞等选项。复制文本到您的剪贴板以在其他地方使用,并对回复的准确性提供反馈。
-
重新生成回复: 如果您遇到问题或对答案不满意,请点击重新生成回复。这将提示 ChatGPT 根据您最新的提示生成新的回复。
-
文本区域: 这是您输入提示和问题的位置。
-
ChatGPT 版本: 在文本输入区域下方,您将找到细印,包括免责声明:“ChatGPT 可能会出错。请考虑检查重要信息。”请注意,ChatGPT 模型版本的显示已被停止。
以下截图展示了其外观。
在左上角,如果您有高级版本,您可以看到您可以访问的 GPTs。
在底部是您之前的对话。
如果您有高级版本,您可以从下拉菜单中选择GPT-4以及插件。

图 3.2:选择不同的 ChatGPT 版本
如果您希望将配置应用到所有新的对话中,您甚至可以在个人资料级别设置自定义指令。

图 3.3:ChatGPT 自定义指令
提示
让我们用 ChatGPT 草拟我们的第一个提示。
您只需用自然语言提问,就像与人类交谈一样,它就会开始与您分享其知识。
[提示]
你能请用项目符号的形式向我解释机器学习的过程吗?
[提示结束]
你应该会看到一个类似于以下截图的响应。请注意,这些响应永远不会完全相同,你每次也不会得到完全相同的文本。

图 3.4:ChatGPT 提示屏幕
摘要
在本章中,我们探讨了 GitHub Copilot 和 ChatGPT,包括它们是什么,如何工作,以及如何开始使用它们。
我们还探讨了它们的一些功能和局限性。
最后,我们探讨了如何安装它们以及如何与它们一起工作。你也得到了一些关于如何通过提示使用它们的想法。3
加入我们的 Discord 社区
加入我们的 Discord 空间,与作者和其他读者进行讨论:

第四章:使用 HTML 和 Copilot 构建我们应用的外观
简介
构建网络应用涉及使用 HTML 进行标记,CSS 进行样式设计,以及 JavaScript 使其具有交互性。
我们从 20 世纪 90 年代使用静态页面构建网络应用已经走了很长的路,到现在使用框架构建大型应用。无论你是否使用框架或库,它仍然建立在相同的基础上,即 HTML、CSS 和 JavaScript。
为了应对这三种标记和编程语言,我们可以使用人工智能助手。使用人工智能助手不仅仅是生成文本,给定文本输入。你还需要对该领域有实际的工作知识。对于像 HTML 和 CSS 这样的标记语言,“实际的工作知识”意味着你应该知道如何构建网页结构或使用 CSS 配置样式。简而言之,你知道如何完成手头的任务,而人工智能助手在那里是为了让你更快、更有效率。
本章中提到的提示的输出可能因训练数据、你打开的文件以及你之前输入的内容而异。
你将在本书的不同章节中看到,我们将遵循一种特定的方法,首先讨论我们想要解决的商业问题,而网络开发或数据科学仅仅是帮助我们解决问题的方法。然后,我们将专注于问题,这取决于我们是网络开发者还是数据科学家,接着将问题分解成更小、更易于管理的部分。最后,我们将推荐一种适用于此类特定问题的“提示”策略。
在本章中,我们将:
-
生成基本 HTML:GitHub Copilot 能够生成各种代码,包括 HTML。
-
应用提示技术:我们可以使用不同的技术来获取所需的内容。
商业问题:电子商务
电子商务是一个非常有趣的领域。在这个领域内有许多问题需要解决。例如,你需要提供一个技术平台,让用户能够购买商品。这本身就意味着你需要构建各种支付解决方案以及可购买的产品,还有一些物流,允许进行运输等。
如果你从数据的角度来看这个业务,你会发现你需要分析客户行为,以确保你有足够的库存,商品价格正确,等等。简而言之,电子商务是一个引人入胜的领域,你将在整本书中看到它的提及。
问题领域
本章主要关注网络开发者的角色,因此让我们讨论一下作为网络开发者,在电子商务中可能会遇到哪些类型的问题。通常,作为网络开发者,你需要解决两个或三个主要角色:
-
客户以及客户可以采取的所有行动,如浏览和购买商品,到管理他们的账户。
-
后台办公室:这是电子商务应用背后的公司。在这里,你需要确保存在技术解决方案,以便公司员工可以管理库存、产品信息、支付解决方案等。
-
从数据的角度来看,作为 Web 开发者,你需要确保数据可以在产品、采购订单和客户信息等区域存储和更新。
问题分解:识别特征
为了将这个问题领域分解成我们可以编写提示输入的内容,我们再次转向我们提到的角色,即客户和后台办公室。以下是将问题分解成我们可以构建的特征的尝试。
让我们从客户角色和主要领域“认证”开始。让我们尝试将其分解成客户应该能够执行的事情。以下是我们应该支持的操作:
-
登录:用户应该能够登录。
-
登出:用户应该能够登出。
-
创建新用户:应该能够创建新用户。
-
更新密码:现有用户应该能够更新他们的密码。
-
密码:如果用户忘记了他们的密码,应该能够以安全的方式重置它。
现在,我们针对特定领域“认证”有一套特征,并且我们对应该支持的不同动作有了更深入的了解。我们将把这个任务留给你,让你进一步将你的问题领域分解成这样,但在开始使用你的 AI 助手之前,请将前述列表视为你应达到的详细程度。
基于前面的特征分解,你现在可以,例如,输入如下提示来尝试解决我们上面识别出的第一个特征:
[提示]
生成一个登录页面,包含用户名、密码和重复密码字段以及登录按钮。
[提示结束]
作为 Web 开发者,你在开始开发之前通常已经将问题领域分解成特征了,如果你使用像 Scrum 这样的开发方法,甚至可能称之为“用户故事”。
然而,在 Web 开发中,你知道这是一个从三个不同层面看待问题的过程,即前端、后端和数据层,通常是一个数据库,用于存储数据。本章的其余部分将专注于使用 AI 助手时的前端。在后续章节中,我们将关注电子商务示例的其他层。
提示策略
那么,我们如何选择提示策略?我们所说的提示策略是什么意思?我们的策略是关于我们将如何提示;我们会为每个功能写一个提示,还是写很多简短的提示?这也关乎我们如何使用我们选择的 AI 助手 GitHub Copilot,以及我们如何选择将提示输入到工具中。
在 GitHub Copilot 中,您有两种主要的写提示方式,要么使用聊天功能,要么通过在文本文件中直接输入注释或代码。在本章中,我们将使用在文本文件中直接输入的方法。一般建议是这两种方法都是有效的,而且根据我们的经验,您在解决问题时会在这两种方法之间切换。
既然我们已经选择了 GitHub Copilot 的方法,那么提示本身又如何呢?在这里,我们将选择一种提示策略,即输入较短的提示——我们在本书的第二章中将这种模式称为“探索性提示模式”。我们将让 GitHub Copilot 在输入代码的同时建立其运行时上下文并从我们的代码中学习。
在接下来的章节中,我们将展示如何在打开的文本文件内开始生成标记代码。在本章结束时,您将看到我们如何重新审视我们的电子商务用例。
页面结构
一个网页在 HTML 中定义,所有这样的页面都是由一个称为文档对象模型(DOM)的树构成的。DOM 包含以下部分:
<html>
<head>
</head>
<body>
</body>
</html>
您可以看到页面的标记由元素组成。顶部元素需要以层次结构排列,HTML 是根元素,其内部节点是 HEAD 和 BODY。在 HEAD 节点中,我们定义诸如样式、搜索引擎指令、页面标题等内容。在 BODY 元素中,我们放置我们希望可见的内容。
将 AI 辅助添加到我们的页面结构中
我们如何利用 GitHub Copilot 来实现这一点呢?通常,Web 应用应该有一个index.html作为应用的入口点。为了利用 GitHub Copilot,在页面上创建一个注释<!-- my comment -->。您需要将my comment替换为您的提示。那么,您应该输入什么来代替my comment呢?答案是提供足够的上下文给 GitHub Copilot,以便它知道如何根据您输入的提示生成响应。
GitHub Copilot 不仅基于其训练模型,还基于文件扩展名(如.js或.html),以及您所在目录中打开或关闭的文件中的文本,来构建运行时上下文。这个上下文决定了根据您输入的提示应该生成什么文本。
您的第一个提示,简单提示,并辅助您的 AI 助手
让我们尝试在一个打开的文本文件中编写我们的第一个提示。
如果您想在阅读此内容时尝试以下提示,请创建一个index.html页面。提示是您写的注释。注释应使用为所选文件类型指定的注释格式,这意味着在 HTML 页面中注释的方式是<!-- -->,而在 JavaScript 文件中写注释的方式是//。
这里有一个您可以尝试添加到页面顶部的 HTML 注释的提示:
[提示]
创建一个包含标题和列表的页面。
[提示结束]
<!-- 创建一个包含标题和列表的页面 -->。
在这种情况下,这个提示生成另一个提示,即<!-- 列表中应包含 3 个项目 -->。
初看可能会觉得有些困惑。为什么 GitHub Copilot 会给我另一个评论作为输出?解释是,你的 AI 助手是在代码和注释上训练的。在这种情况下,GitHub Copilot 根据其训练数据为你提供最可能的响应。
那么,如何得到 HTML 标记作为响应呢?答案是,你开始输入 HTML,这会缩小 GitHub Copilot 的范围,因此你的文本文件中的代码应如下所示:
<!-- create a page with a title and list -->
<HTML
GitHub Copilot 现在生成的代码应该看起来类似于以下输出:
[提示响应]
<html lang="en">
[响应结束]
如果你持续按回车键,并使用Tab键接受建议,将生成越来越多的代码,最终得到如下输出:
[提示响应]
<!-- create a page with a title and list -->
<html lang="en">
<head>
<title>My First Page</title>
</head>
<body>
<h1>My First Page</h1>
<ul>
<li>First item
<li>Second item
<li>Third item
</li>
</body>
</html>
[响应结束]
你的第二个提示:添加更多上下文
让我们擦除到目前为止所做的一切,并从一个新的提示开始。目标是理解如何通过编写更好的提示,提供更多上下文,从而得到更好的输出。
让我们尝试第二个提示,包含更多上下文,并使其看起来如下所示:
[提示]
创建一个带有标题和列表的页面,每个条目应包含标题、描述和完成字段。
[提示结束]
注意我们现在说的是列表由条目组成,并且每个条目有单独的字段。那么,这个提示的输出是什么?像之前一样,使用回车键生成提示响应,并使用Tab键接受 GitHub Copilot 提供的完成。
[提示响应]
<!-- create a page with a title and list, each item should have fields title, description and completed -->
<html>
<head>
<title>Todo List</title>
</head>
<body>
<h1>Todo List</h1>
<ul>
<li>
<h2>Item 1</h2>
<p>Item 1 description</p>
<p>Completed: <input type="checkbox" checked></p>
</li>
<li>
<h2>Item 2</h2>
<p>Item 2 description</p>
<p>Completed: <input type="checkbox"></p>
</li>
</ul>
[响应结束]
我们的 AI 助手能够猜测我们正在尝试构建一个所谓的“待办事项应用”,这是一种常见的应用类型,其中我们有需要完成的任务。它还生成了一个h2标题元素、一个p描述和一个复选框用于Completed字段。通过添加更多上下文,我们的 AI 助手能够提供更好的输出。
你的第三个提示:接受提示建议
到目前为止,你已经看到了两个提示之间的区别,但如果我们添加更多上下文,会发生什么呢?
[提示]
创建一个带有标题、主体和页脚的页面。使用深色主题。
[提示结束]
到这个阶段,输出包含更多的注释(建议),如果你通过输入回车键(以获取新的建议)和Tab键(以接受完成)接受几个建议,你的文本文件现在应该有如下所示的内容:
也有可能通过例如继续输入来拒绝建议。
[提示响应]
<!-- create a page with header, main part and footer use dark theme -->
<!-- header should have a navigation with 3 links -->
<!-- main part should have 3 sections with some text and images -->
<!-- footer should have 3 columns with some text and images -->
<!-- use flexbox for layout -->
<!-- use css variables for colors -->
<!-- use css variables for font sizes -->
<!-- use css variables for paddings and margins -->
<!-- use css variables for font family -->
<!-- use css variables for border radius -->
<!-- use css variables for border width -->
<!-- use css variables for border color -->
<!-- use css variables for background color -->
[响应结束]
GitHub Copilot 可以建议你可能甚至都没有考虑过的提示,如导航、图片和 CSS 变量的使用。如果你此时输入<html,你会看到一个类似的输出:
[提示响应]
<!-- create a page with header, main part and footer use dark theme -->
<!-- header should have a navigation with 3 links -->
<!-- main part should have 3 sections with some text and images -->
<!-- footer should have 3 columns with some text and images -->
<!-- use flexbox for layout -->
<!-- use css variables for colors -->
<!-- use css variables for font sizes -->
<!-- use css variables for paddings and margins -->
<!-- use css variables for font family -->
<!-- use css variables for border radius -->
<!-- use css variables for border width -->
<!-- use css variables for border color -->
<!-- use css variables for background color -->
<html>
<head>
<title>06.6 solution</title>
<link rel="stylesheet" href="style.css">
</head>
<header>
<nav>
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</nav>
</header>
<body>
<main>
<section>
<h1>Section 1</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<img src="img/300" alt="random image">
</section>
<section>
<h1>Section 2</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<img src="img/300" alt="random image">
</section>
<section>
<h1>Section 3</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<img src="img/300" alt="random image">
</section>
</main>
<footer>
<div>
<h1>Column 1</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<img src="img/300" alt="random image">
</div>
<div>
<h1>Column 2</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam, voluptatum.</p>
<img src="img/300" alt="random image">
</div>
<div>
<h1>Column 3</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<img src="img/300" alt="random image">
</div>
</footer>
[响应结束]
在浏览器中查看此输出,它应该显示包含标题、描述和多个项目的图像的先前部分(你将看到的确切内容因这些图像是随机的而有所不同)。
作为一名网页开发者,你把这看作是一个很好的起点,你需要在稍后添加样式来让它看起来更好。

图 4.1:带图片的页面(不含页眉和页脚)
挑战:改变提示
你可以通过添加或删除上下文来改变输出。作为一个建议,看看你是否可以修改提示来添加表格、表单、多列布局等。
用例:构建电子商务的前端
现在我们已经学会了如何使用 GitHub Copilot 创建 HTML 标记,让我们用它来完成一个更具挑战性的项目:一个电子商务网站。要求是它应该是一个包含以下部分的网站:
-
一个登录页面
-
用户创建页面
-
一个用于将产品放入购物车的产品页面
-
一个显示你打算购买的商品的购物车页面
-
一个显示购物车内容和支付信息表单以及用于发货的地址信息的结账页面
-
一个允许你编辑产品的管理员页面
让我们从这些页面开始,因为这些页面通常在电子商务网站上很常见。
登录页面
登录页面的要求是,作为用户,你可以输入用户名和密码,并且有一个验证凭证的按钮。可选地,你可以添加创建用户的链接和忘记密码的链接。
在名为login.html的页面的顶部尝试以下提示作为注释:
[提示]
<!-- a page with input fields user name and password and a login button and links to create a user and link for if you forgot your password, should look good for mobile and desktop -->
[提示结束]
你可能需要开始键入<html>以生成完成。同时,确保除了当前文件外,关闭所有文件,以确保你的 AI 助手不会被其他可能打开的文件分散注意力。
你应该看到一个类似以下输出的结果:
[提示响应]
<html>
<head>
<title>Login</title>
<link rel="stylesheet" href="css/login.css">
</head>
<body>
<div class="container">
<div class="login">
<h1>Login</h1>
<form action="login.php" method="post">
<input type="text" name="username" placeholder="Username" required="required" />
<input type="password" name="password" placeholder="Password" required="required" />
<button type="submit" class="btn btn-primary btn-block btn-large">Login</button>
</form>
<a href="register.php">Create User</a>
<a href="forgot.php">Forgot Password</a>
</div>
</div>
[响应结束]
这会呈现如下:

图 4.2:登录页面
产品列表页面
在这个页面上,我们希望看到可以购买的产品列表,每个产品的详细信息,以及一个“添加到购物车”按钮。为此,我们将创建一个products.html页面,并给它以下提示:
<!-- list of products as div elements where each item has fields text, link to detail page, random image, description and "add to cart button" -->
你应该看到一个类似以下输出的结果:
[提示响应]
<!-- list of products as div elements where each item has fields text, link to detail page, random image, description and "add to cart button" -->
<html>
<head>
<title>Products</title>
<link rel="stylesheet" href="css/products.css">
</head>
<body>
<div class="container">
<div class="products">
<h1>Products</h1>
<div class="product">
<div class="product-image">
<img src="img/300" alt="product image">
</div>
<div class="product-text">
<h2>Product 1</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<a href="product.php">View Product</a>
<button type="submit" class="btn btn-primary btn-block btn-large">Add to Cart</button>
</div>
</div>
<div class="product">
<div class="product-image">
<img src="img/300" alt="product image">
</div>
<div class="product-text">
<h2>Product 2</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<a href="product.php">View Product</a>
<button type="submit" class="btn btn-primary btn-block btn-large">Add to Cart</button>
</div>
</div>
<div class="product">
<div class="product-image">
<img src="img/300" alt="product image">
</div>
<div class="product-text">
<h2>Product 3</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<a href="product.php">View Product</a>
<button type="submit" class="btn btn-primary btn-block btn-large">Add to Cart</button>
</div>
</div>
<div class="product">
<div class="product-image">
<img src="img/300" alt="product image">
</div>
<div class="product-text">
<h2>Product 4</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<a href="product.php">View Product</a>
<button type="submit" class="btn btn-primary btn-block btn-large">Add to Cart</button>
</div>
</div>
</div>
</div>
</body>
</html>
[响应结束]
这应该呈现类似的效果:

图 4.3:产品列表页面
剩余页面
我们将把这个任务留给你来完成剩余的页面。记住要创建一个专门的 HTML 页面,并在页面的顶部放置一个提示注释。
这里是一些为剩余页面建议的提示:
-
用户创建页面。
这里是一个建议的提示:
<!-- a page with fields username, password, repeat password and create button --> -
一个显示你打算购买的商品的购物车页面。
这里是一个建议的提示:
<!-- a page showing a list of items in a basket, each item should have title, price, quantity, sum and buttons to increase or decrease quantity and the page should have a link to "checkout" at the bottom --> -
一个显示购物车内容和支付信息表单以及用于发货的地址信息的结账页面。
这里是一个建议的提示:
<!-- a checkout page containing a section for payment info with a credit card and a section below for shipping address --> -
一个允许你编辑产品的管理员页面。
这里有一个建议的提示:
<!-- a section that's a list of products, each item has fields title and price, quantity, should also have an action for adding a new product, remove a product -->
作业
在这个任务中,您将创建一个简历网站。您提供给 GitHub Copilot 的上下文由您决定,但首先创建一个index.html和一个 HTML 注释,<!-- my prompt -->。
记住您学到的技巧。
编写一个提示
编写一个提示并开始输入下一行的代码/标记,以帮助您的助手。使用回车键生成响应,使用制表键接受建议的文本。重写提示并添加或更改内容以获得所需的结果。
您可以在 GitHub 仓库中找到这个任务的解决方案:github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT/tree/main/04
挑战
给定您构建的简历,您可以通过添加颜色来进一步改进它。您将如何提示这样做?
测验
这里有一系列问题,以确保您已经掌握了关键概念:
-
您发送给 AI 助手的文本被称为:
-
文本
-
指令
-
提示
-
-
您的 AI 助手从以下内容构建上下文:
-
您输入的内容
-
您输入的内容、文件扩展名以及您工作目录中的打开和关闭文件
-
您输入的内容和文件扩展名
-
您可以在 GitHub 仓库中找到这个测验的解决方案:github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT/tree/main/04
# 摘要
在本章中,我们介绍了如何使用 GitHub Copilot 生成 HTML 标记。我们还介绍了如何使用提示技术以及如何为您的提示添加上下文。作为学习这些提示技术的一部分,我们发现,您提供给 AI 助手的上下文越多,输出就越好。随着时间的推移,您通过添加更多内容到您的页面,也会逐渐积累上下文。
此外,我们开始了一个用例,即构建一个电子商务网站。这个用例是我们将在接下来的章节中继续构建的内容。
对于下一章,我们将继续介绍网络开发,但将重点转向 CSS 和样式。您将看到相同的或类似的提示技术也可以用于 CSS。
加入我们的 Discord 社区
加入我们的社区 Discord 空间,与作者和其他读者进行讨论:
*
第五章:使用 CSS 和 Copilot 设计应用
简介
优雅地设计应用可以极大地改变用户对其的看法。周到的设计包括适应多个设备、智能使用图形和具有良好对比度的颜色。
CSS 样式化是一个很大的主题,我们不会详细讨论。然而,我们将展示如何开始使用它。就像在前一章中一样,我们将使用我们的 AI 助手来帮助我们生成代码。您将看到我们将继续使用基于注释的提示来生成代码,但也会展示一种新的技术,其中仅使用文件上下文来生成代码。
您还将看到我们将如何继续构建我们的电子商务项目并对其进行样式化。
在本章中,我们将看到如何生成所需的 CSS 以及如何继续应用我们在前几章中使用过的提示模式和策略。我们将继续构建电子商务项目,并使其外观吸引人。
在本章中,我们将:
-
生成 CSS:GitHub Copilot 可以生成样式,我们将展示如何通过查看文本文件中的周围代码以及基于 CSS 注释来生成 CSS 的 AI 辅助。
-
应用提示技术:我们可以使用不同的技术来获取所需的内容。
-
将 CSS 添加到我们的电子商务项目中:我们将选择电子商务项目中的几个页面来展示它如何从样式化中受益。
商业问题:电子商务
就像前一章一样,我们将继续在电子商务领域工作,并通过其许多有趣的问题。由于本章重点在于使用 CSS 进行可视化,您可能会想知道这与商业有什么联系?一个糟糕的用户体验(UX)、设计不良的网站(除了桌面设备外不工作或不符合无障碍性)可能会让您损失金钱,因为这样,客户可能会选择与您的竞争对手做生意。
问题和数据领域
本章将继续探讨电子商务业务领域,并具体深入到一个列出客户打算购买的产品页面的购物车页面。因此,数据是产品数据;不仅如此,从数据角度来看,我们需要考虑如何展示与产品相关的详细数据,例如数量和总成本,以便客户可以决定购买什么以及购买多少项。这些考虑应该反映在所选的设计中。
将问题分解为功能
在前一章中,我们选择识别一个更大的区域,“认证”,并将其分解为具体的功能。让我们回顾一下这个功能分解看起来是什么样子。之后,我们将看看是否需要将其改为更注重设计。但首先,让我们展示功能列表:
-
区域:认证
-
登录:用户应该能够登录。
-
登出:用户应该能够登出。
-
创建新用户:应该能够创建新用户。
-
更新密码:现有用户应该能够更新他们的密码。
-
重置密码:如果用户忘记了他们的密码,应该能够以安全的方式重置它。
上述功能列表构成了一个良好的高级列表,列出了我们需要支持的内容。然而,从设计角度来看,我们需要考虑诸如适应不同设备或支持辅助功能等问题。因此,对于第一个功能,可能需要调整提示以看起来像这样:
[提示]
生成一个登录页面。它应该有用户名、密码和重复密码字段,以及一个登录按钮。它还应支持通过工具提示和 ARIA 键的辅助功能,以便仅使用键盘和鼠标即可使用。
[提示结束]
如您从上面的提示中可以看到,我们的关注点不仅包括我们需要的 UI 元素,如输入和按钮,还包括它们应该如何工作。
与之前一样,我们建议您将您即将构建的 Web 应用程序分解为区域,并将每个区域分解为功能,以便更容易地进行提示。
提示策略
在上一章中,我们提到您可以使用 GitHub Copilot 的 Chat 模式,也可以在文件内部进行输入,并且建议您使用这两种方法。至于如何进行提示,建议您编写较短的提示,并在需要时添加上下文。这样做,您就依赖于 GitHub Copilot 的工作方式以及它如何根据其底层模型、您的开放目录和您的开放文件等构建运行时上下文。您还将看到提示的另一个方面,这并不依赖于提示的长短,而是取决于您特定的 AI 工具如何工作。在本章中,我们使用 第二章 中描述的“探索性提示模式”。
CSS,或层叠样式表
CSS,或层叠样式表,用于美化 HTML 页面。它包含各种结构,允许您更改字体、大小、颜色和布局等。
要跟随下面提到的提示,建议您创建一个 product.css 文件,或者您也可以等到本章后面的 作业 部分;这取决于您。
首个 CSS
要利用我们的 AI 助手,我们可以在 CSS 文件中放置一个类似于代码的 CSS 类型的注释,例如:
.product {
/* apply padding and margin */
padding: 10px;
margin: 10px;
}
如前所述,注释样式根据文件类型而有所不同。HTML 注释看起来是这样的:<!-- -->。CSS 注释使用 /**/,而 JavaScript 可以理解 CSS 类型的注释和 //。在本章中,您将使用 /**/,因为您正在处理 CSS。
在 GitHub Copilot 中,您可以持续按下 回车 键,然后按下 Tab 键,以生成并接受新的建议,而无需输入新的提示。您的 AI 助手可以根据您输入的第一个提示和文件中存在的代码提供建议。
这如何影响我们上面的 CSS 文件,那就是一旦我们输入第一个 CSS 注释,我们就可以继续按Enter和Tab键,AI 助手将生成通常与填充和边距一起使用的 CSS 属性。以下输出是使用按Enter和Tab键的技术和依赖于打开文件上下文的结果。
这就是product.css的显示方式:
.product {
/* apply padding and margin */
padding: 10px;
margin: 10px;
/* apply border */
border: 1px solid #ccc;
/* apply border radius */
border-radius: 5px;
/* apply background color */
background-color: #fff;
/* apply box shadow */
box-shadow: 0 0 5px #ccc;
/* apply display */
display: inline-block;
/* apply width */
width: 200px;
/* apply text align */
text-align: center;
/* apply text decoration */
text-decoration: none;
/* apply color */
color: #333;
}
现在已经使用上述内容创建了product.css,我们可以在 HTML 文件中包含该 CSS 文件。切换到该 HTML 文件(如果你正在跟随输入,创建一个index.html文件),然后在文件的末尾添加以下提示:
[提示]
包含product.css的页面。
[提示结束]
[提示响应]
<!-- page that includes product.css -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Product</title>
<link rel="stylesheet" href="product.css">
</head>
<body>
<div class="product">
some text
<div class="product__image">
<img src="img/200x200" alt="Product Image">
</div>
<div class="product__info">
<h2 class="product__name">Product Name</h2>
<p class="product__description">Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.</p>
<p class="product__price">$9.99</p>
<button class="product__buy">Buy</button>
</div>
</div>
</html>
[响应结束]
观察它不仅正确地添加了链接标签以包含product.css,而且还生成了可能适合产品的 HTML。
以下为输出结果:

图 5.1:产品 CSS 显示产品图片、标题和描述
按名称排序的 CSS
到目前为止,我们已经添加了一个注释来生成建议。但还有一种方法可以生成 CSS 文件的建议,而不是为 Copilot 添加注释。这种方法包括创建一个空的 CSS 类.product,并将光标放在其中。然后使用Enter键和Tab键,看看 Copilot 生成什么,直到你满意为止。
这是你开始 CSS 文件的方式:
.product {
}
现在将光标放在花括号{}内,并使用Enter和Tab键为每个需要建议的新行。
这可能是最终结果的样子:
[提示响应]
.product {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
margin: 0 auto;
padding: 0 0 50px 0;
background-color: #f5f5f5;
}
[响应结束]
作业
在这个作业中,你将创建一个图片画廊应用。画廊应显示 10 张图片,每行 5 张。使用你在上一章中学到的知识来生成 HTML 文件和单独的 CSS 文件。
使用“占位符”作为关键词来获取图片。
你的 HTML 文件应该添加对生成的 CSS 文件的引用,以便正确应用 CSS。
解决方案
你可以在 GitHub 仓库中找到这个作业的解决方案:github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT
用例:为电子商务应用添加样式
现在你已经完成了一个作业,其中你创建了 CSS 文件和 HTML 文件,你应该对如何编写提示以及从 GitHub Copilot 生成和接受建议有了更好的理解。现在让我们回顾本章的开头,我们讨论了商业问题和电子商务领域。我们将继续在这个领域解决问题,但将重点放在使用 CSS 进行可视化。
我们将继续使用上一章中开始的使用案例:我们的电子商务商店。我们将专注于购物车页面,其余页面留给你来实现。
购物车页面
为了样式化我们的购物车 HTML 页面,我们创建一个 basket.css 文件,然后观察我们在 basket.html 文件中引入的 CSS 类。让我们看一下一个代表性的片段:
<div class="container">
<div class="basket">
<h1>Basket</h1>
<div class="basket-item">
<div>
<h2>Product 2</h2>
<p>Price: 200</p>
<p>Quantity: 2</p>
<p>Sum: 400</p>
</div>
在前面的代码中,我们看到类 basket 和 basket-item。让我们在 basket.css 中为这些创建 CSS 类:
从一个空的 basket.css 开始,键入 .basket。当你使用 Return 和 Tab 键反复操作时,你应该会看到以下类似的输出。当你对输出满意时,键入 } 来完成 CSS 类。
[提示响应]
.basket {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
[响应结束]
要创建 basket-item 类,键入 .basket-item 并反复使用 Return 和 Tab 键。像之前一样,当你对生成的 CSS 属性数量满意时,键入 }。你现在应该已经生成了一个类似于以下文本的 CSS 输出:
.basket-item {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
padding: 20px;
border-bottom: 1px solid #ccc;
}
如果我们继续查看我们的 HTML 文件,我们还会看到另一个有趣的片段:
<div class="basket-item-buttons">
<button type="submit" class="btn btn-primary btn-block btn-large">+</button>
<button type="submit" class="btn btn-primary btn-block btn-large">-</button>
</div>
使用之前相同的提示技术,通过键入 CSS 类名(.basket-item > .basket-item-button)并反复使用 RETURN 和 TAB 键来生成以下文本:
.basket-item > .basket-item-buttons {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-left: auto;
}
.basket-item-buttons button {
margin: 5px;
/* set width, large font size, business color background */
width: 50px;
font-size: 20px;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
}
你可能需要单独键入 .basket-item-buttons 按钮类,并且像之前一样,反复使用 Return 和 Tab 键。
在浏览器中查看应用 CSS 的影响,你应该会看到以下类似的外观:

图 5.2:购物车中的项目列表
挑战
你会如何修改提示来创建一个深色主题的购物车页面?
测验
你如何使用你的 AI 助手生成 CSS?
-
在 CSS 文件中创建一个评论。
-
创建一个类并将光标放在类中。
-
A 和 B 都可以。
你可以在 GitHub 仓库中找到这个测验的解决方案:github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT
摘要
在本章中,我们介绍了如何使用你的 AI 助手生成 CSS。你看到了上一章中引入的提示技术如何应用于 CSS。
此外,我们还展示了如何以两种不同的方式生成文本,通过在文件顶部放置注释或在我们需要帮助的区域附近放置注释,或者将光标放在 CSS 类内部,让它根据 CSS 类名生成 CSS。
在下一章中,我们将展示如何使用 JavaScript 为你的应用添加行为。你会看到从提示的角度来看,JavaScript 与 HTML 和 CSS 类似。然而,你仍然需要理解主题内容,这是你试图解决的潜在问题。
加入我们的 Discord 社区
加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

第六章:使用 JavaScript 添加行为
简介
一个只包含 HTML 标记和 CSS 的网页是完全可以接受的,但如果你想要交互性,你需要 JavaScript。
使用 JavaScript,你可以从少量应用,例如将表单发布到后端,到大量应用,如使用 Vue.js 或 React.js 这样的框架构建单页应用(SPA)。无论如何,有一个共同点,即你需要编写代码并从你的 HTML 标记中引用该代码或代码文件。
你会发现 Copilot 可以帮助完成从添加脚本标签到 HTML 标记这样的常见任务,到添加 Vue.js 这样的 JavaScript 框架到你的 Web 应用等更高级的任务。
在本章中,我们将:
-
使用提示生成 JavaScript 以添加应用的行为。
-
为我们的电子商务应用添加交互性。
-
介绍一个如 Vue 的 JavaScript 框架,以确保我们为自己打下坚实的基础。
业务问题:电子商务
在本章中,我们也将继续在电子商务领域工作。在前几章中,你看到了我们如何使用 HTML 来尝试确定每页应该包含哪些信息,并确定在过程中我们需要哪些页面。在本章中,我们添加了缺失的组件,即 JavaScript,这是使一切工作的关键。JavaScript 将扮演添加交互性和读取/写入数据的双重角色。
问题与数据域
需要解决几个问题,如下:
-
数据流:我们如何向我们的应用程序添加代码以便我们可以读取和写入数据?
-
处理用户交互:用户会希望与你的应用程序进行交互。你需要配置用户想要使用的网站部分,并确保它能正常工作。并非所有的用户交互都会导致数据的读取或写入,但很多都会,因此你需要弄清楚何时会发生这种情况,并“连接”用户交互与你的数据流,如上所述。
-
数据:数据将根据你针对的应用程序部分而变化。例如,如果你实现了一个购物车页面,你将需要处理产品数据以及订单,因为用户想要“结账”他们的购物车,以便他们可以购买产品并将它们送到所选地址。
将问题分解为功能
我们理解业务域以及我们可能遇到的类型的问题,那么我们如何将其分解为功能?从前几章,我们有一个如何做到这一点的想法,但主要区别是,我们不仅要创建一个看起来像能工作的购物车页面,例如,而应该是真正能工作的。因此,我们可以将购物车页面,例如,分解为以下功能:
-
从数据源读取购物车信息。
-
渲染购物车信息。
-
将商品添加到购物车。
-
调整购物车中特定商品的选择数量。
-
从购物车中移除商品。
-
支持检查购物车,将用户带到订单页面,在那里他们将需要提供购买信息和送货页面。
一个电子商务网站由许多不同的页面组成。因此,建议在处理特定页面时,为每个页面进行类似的特性分解。
提示策略
提示策略在一定程度上取决于所选的 AI 工具,它的工作方式以及我们如何进行提示。GitHub Copilot 是我们本章所选的 AI 工具,我们将主要关注其编辑器内的体验,即你在打开的文件中输入提示。本章中我们将展示的方法如下:
-
内联注释:这意味着我们将直接在打开的文件中编写提示。你可以这样在文件顶部添加提示,也可以在尝试让 AI 助手构建特定代码块的地方添加额外的支持提示。
-
聊天:我们不会过多展示这种方法,但我们确实建议使用聊天来比较和对比,或者如果你需要查找特定信息或需要关于如何实现某事的建议。
在整本书中的一般建议是,你使用直接在打开的文件中“在编辑器内”使用提示和在聊天窗口中输入提示的混合方法。这些方法都有其优缺点。至于提示模式,我们将使用第二章中描述的“探索性提示模式”。
添加 JavaScript
要向网页添加 JavaScript,有两种主要方法:
-
在
<head>元素中使用<script>标签:在这个版本中,你将向现有的 HTML 页面添加一个<script>标签,例如,命名为index.html。<!-- Alt1 --> <script> // JavaScript here </script> -
指向一个独立的文件:在这种情况下,你将在现有的文件中编写你的 JavaScript 代码,例如,命名为
my-javascript-file.js:<!-- Alt2 --> <script src="img/my-javascript-file.js"></script>
JavaScript 的作用
当 JavaScript 最初被添加到浏览器中时,它扮演了添加交互性的角色。今天,JavaScript 通常作为 Vue.js、React 和 Angular 等应用框架的一部分,扮演着扩展的角色。在这样的框架中,开发者现在可以声明性地定义模板,借助 JavaScript 的帮助,可以将这些模板转换为静态 HTML,并添加事件处理以响应用户交互。最终,所有这些都会发送到浏览器进行渲染。
向页面添加 JavaScript
让我们展示如何通过提供以下提示来向页面添加 JavaScript:
[提示]
使用<script>标签创建网页。
[提示结束]
我们最终得到一个包含<script>标签的网页。注意提示是如何作为注释添加到页面顶部的。这就是 GitHub Copilot 知道建议文本补全的方式:
[提示响应]
<!-- create web page with a script tag -->
<html>
<head>
<title>My First Web Page</title>
</head>
<body>
<script>
alert("Hello World!");
</script>
</body>
[响应结束]
在这种情况下,代码调用alert("Hello World"),这将显示一个包含文本“Hello World”的对话框。
第二个示例:添加一个 JavaScript 库/框架
第一个例子可能不是非常有用,但让我们添加一些你可能会做的事情,比如添加一个像 Vue.js 这样的框架。添加像 Vue.js 这样的框架的原因是,你想要构建一个由大量交互和许多不同页面组成的大型应用。为了保持事物有序,并使其他开发者容易贡献,你想要一个像 Vue.js 这样的框架。
[提示]
添加 Vue 3。
[提示结束]
这次,我们将自己放在标题标签中。请参见下面HEAD标签中的 HTML 注释,以指示我们用作 GitHub Copilot 输入的提示:
<head>
<!-- add Vue 3 -->
</head>
此提示导致以下建议:
[提示响应]
<script src="img/vue@next"></script>
[响应结束]
您可以通过添加一个提示来轻松构建,该提示要求设置入口点(大多数 SPA 框架都基于有一个入口点的理念,即应用将被渲染的元素):
[提示]
添加 Vue 3 入口点。
[提示结束]
这将导致以下代码:
[提示响应]
<!-- add Vue 3 entry point -->
<div id="app">
<h1>{{ message }}</h1>
</div>
[响应结束]
将光标放置在导入 Vue.js 的script标签下方,将导致我们的 AI 助手生成添加到进一步设置 Vue.js 的代码指令。仅仅添加到 Vue.js 的脚本是不够的;还需要一些额外的代码。
幸运的是,由于我们的光标放置如下,GitHub Copilot 能够从现有的 HTML 注释(提示)中理解缺少的代码,并将该代码建议放置在我们的光标位置(如果 Copilot 建议没有出现,请尝试按空格键或回车键):
[提示]
没有给出提示
[提示结束]
[提示响应]
<script src="img/vue@next"></script>
<!-- below is rendered by just placing cursor here -->
<script>
// create Vue 3 instance
const app = Vue.createApp({
data() {
return {
message: 'Hello World!'
}
}
})
// mount Vue 3 instance to DOM element
app.mount('#app')
</script>
[响应结束]
再次强调,GitHub Copilot 有足够的上下文,仅通过使用 Tab 键和回车键就可以生成更多建议,而无需提供额外的提示。
在浏览器中运行此代码返回“Hello World”,这是根据上述代码预期的。如果你不熟悉 Vue.js,以下是代码工作的原因:它包含了使其可引用和配置所需的最小内容:
-
script标签,引用 Vue.js -
创建一个定义了具有
message属性的data函数的应用实例 -
调用
mount(),确保识别的 HTML 标签(id = app)被插入,并将HTML标签替换为属性message的值
当构建更高级的 Vue.js 项目时,请使用 Vue CLI 而不是上述方法。对于较小的概念验证,这种方法可能是可行的。
挑战
作为挑战,要求 Copilot 添加一个增加计数器的按钮。你可以使用以下提示:
[提示]
添加一个按钮来增加计数器。
[提示结束]
建议您将光标放置在 HTML 标记中DIV元素id="app"所在的位置。
用例:添加行为
对于我们电子商务用例的这一部分,让我们来渲染一个商品购物车页面。在这种情况下,我们将模拟调用后端获取数据,并将数据放置在一个名为 cart.json 的文件中,其结构如下:
{
"cart": [
]
}
上述 JSON 内容描述了一个 JSON 对象(由 {} 定义),具有 cart 属性,该属性为数组类型,如方括号 [] 所示。
实际上,让我们使用我们的 AI 助手生成我们所需的数据。让我们创建 cart.json 文件和以下结构和提示:
{
"cart": [
// item with id name , quantity and price, total = quantity * price
]
}
注意提示 // item with id name, quantity and price, total = quantity * price,它导致以下 JSON:
{
"id": "1",
"name": "item1",
"quantity": "1",
"price": "10",
"total": "10"
},
{
"id": "2",
"name": "item2",
"quantity": "1",
"price": "20",
"total": "20"
},
{
"id": "3",
"name": "item3",
"quantity": "1",
"price": "30",
"total": "30"
}
当你尝试从 Copilot 生成更多 JSON 代码时,添加带有 // 的注释是可以的,但一旦你对输出满意,就应该将其删除。
接下来,我们需要一个文件,app.js,它调用此 JSON 文件并最终渲染。我们可以使用上一章中的 basket.html 文件。它应该看起来像这样:
<!-- a page showing a list of items in a basket, each item should have title, price, quantity, sum and buttons to increase or decrease quantity and the page should have a link to "checkout" at the bottom -->
<html>
<head>
<title>Basket</title>
<link rel="stylesheet" href="css/basket.css">
</head>
<body>
<div class="container">
<div id="basket" class="basket">
</div>
</div>
<!-- add app.js -->
<!-- add app.js, type javascript -->
<script src="img/app.js"></script>
</body>
让我们在 body 标签的末尾添加 app.js,添加一个提示:
[提示]
添加 app.js。
[提示结束]
[Copilot 响应
<!-- add app.js -->
<script src="img/app.js"></script>
[响应结束]
在 app.js 中添加以下提示:
[提示]
读取 cart.json 并将内容作为 div 元素渲染到具有 ID 购物车的元素上。为每个项目添加 CSS 类 basket-item。
[提示结束]
应该给出类似的结果:
[提示响应]
fetch('cart.json')
.then(response => response.json())
.then(data => {
let basket = document.getElementById('basket');
data.forEach(item => {
let div = document.createElement('div');
div.classList.add('basket-item');
div.innerHTML = item.name;
basket.appendChild(div);
});
});
[响应结束]
上述代码定位到具有 ID basket 的 HTML 元素,然后继续遍历数组数据。对于数据中的每个元素,将创建一个新的 HTML 元素,类型为 DIV,以下是对该元素的操作:
-
将 CSS 类
basket-item添加。 -
其
innerHTML属性将被分配name属性的值。 -
将特定元素添加到父元素购物车中。
你需要调整代码中的 forEach 部分,将其改为 data.cart.forEach,以根据 JSON 文件的外观进行调整(如果你的 JSON 中说你有 description 属性而不是 name,那么这应该在 JavaScript 代码中得到反映等)。然而,最终的结果看起来并不多,只有三个标题:
item1
item2
item3
提高输出质量
为了提高输出质量,我们想做一些事情,比如:
-
为每个渲染的项目属性添加 CSS 类:
.basket-item > div { margin: 5px; /* 25% width */ width: 25%; }你并不总是需要输入提示来获取上述响应。有时,只需输入
.basket-item就足够了,CSS 会为你提供建议,你可以使用 Tab 键接受它。不过,确保你在 style 标签或.css文件中编写此代码。 -
在 JavaScript 代码中添加提示,使其将每个项目属性作为单独的
div元素渲染:
[提示]
//读取 cart.json 并将内容作为 div 元素渲染到具有 id 购物车的元素上,为每个项目添加 CSS 类 basket-item
[提示结束]
[提示响应]
fetch('cart.json')
.then(response => response.json())
.then(data => {
let basket = document.getElementById('basket');
[响应结束]
-
到目前为止,上述提示已经成功调用了
fetch方法从cart.json文件中检索数据。然后它继续将检索到的数据转换为 JSON 格式,并确定了我们将要在其上渲染 JSON 响应的 HTML 元素:// render name, quantity, total and price for each item in separate div elements data.cart.forEach(item => { let div = document.createElement('div'); div.classList.add('basket-item'); // 1) render name as separate child div element let name = document.createElement('div'); name.innerHTML = item.name; div.appendChild(name); // 2) render quantity, price and total as separate child div elements, add header and value for each element on this format, header: value let quantity = document.createElement('div'); quantity.innerHTML = `Quantity: ${item.quantity}`; div.appendChild(quantity); let price = document.createElement('div'); price.innerHTML = `Price: ${item.price}`; div.appendChild(price); let total = document.createElement('div'); total.innerHTML = `Total: ${item.price * item.quantity}`; div.appendChild(total); basket.appendChild(div); }); -
在这段代码中,我们添加了关于我们想要从 JSON 内容中读取哪些字段(
name、quantity等)的上下文。从代码中,你可以看到这个提示指令反映在生成的代码中,并且每个字段都得到了自己的DIV元素和描述性文本:// 3) calculate total price for all items and render it as separate div element, display as Total price: value let totalPrice = document.createElement('div'); totalPrice.innerHTML = `Total price: ${data.cart.reduce((total, item) => total + item.price * item.quantity, 0)}`; basket.appendChild(totalPrice); }); -
在这个提示中,我们提供了特定的指令来乘以某些字段(价格 x 数量),以产生行的总价。同时,我们可以看到如何通过
reduce调用来计算购物车的总价。
最终结果看起来是这样的。它远非完美,但看起来更接近我们预期的样子。

图 6.1:渲染的购物车显示名称、价格和总价
添加 Bootstrap
在上一节中,我们展示了如何向我们的购物车页面添加一些基本的 CSS。然而,为了真正改善我们的篮子页面的外观,我们认为添加一个如 Bootstrap 这样的 CSS 框架是一个好主意。Bootstrap 被世界各地的许多团队使用,并且有许多不同类型的内容,如“卡片”和“菜单”,这将使我们能够更快地构建我们的应用程序。
要添加 Bootstrap,我们在 head 元素中放置一个提示注释,如下所示:
[提示]
添加 Bootstrap。
[提示结束]
[提示响应]
<!-- add bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
[响应结束]
到目前为止,我们已经将 Bootstrap 添加到我们的 Web 应用中,但我们需要使用其类中的一个。之前,当我们讨论 Bootstrap 及其内容类型时,我们提到了“卡片”的概念,它是一种渲染得很好的布局。
要在网页上创建一个卡片,我们可以给出如下提示:
[提示]
创建一个包含名称、价格、数量和总价的卡片。
[提示结束]
我们得到了以下结果:
[提示响应]
<!-- Create a card with the fields name, price, quantity and total -->
<div class="card">
<div class="card-body">
<h2 class="card-title">Product 1</h2>
<p class="card-text">Price: 100</p>
<p class="card-text">Quantity: 1</p>
<p class="card-text">Sum: 100</p>
<button type="submit" class="btn btn-primary btn-block btn-large">+</button>
<button type="submit" class="btn btn-primary btn-block btn-large">-</button>
</div>
</div>
[响应结束]
在上面,我们将提示作为 HTML 注释放置在我们的开放文本文件中,并通过 Tab 键接受 Copilot 的建议。
好的,我们有一个良好的开始。我们已经将 Bootstrap 添加到我们的 Web 应用中。我们还使用 Copilot 创建了一个 HTML 卡片示例,我们可以使用它。下一步是更改我们的 JavaScript 代码。
要更改我们的 JavaScript 代码,我们将给 Copilot 提供比平时更多的上下文,即:
[提示]
见下文:
// template:
// <div class="card">
// <div class="card-body">
// <h2 class="card-title">Product 1</h2>
// <p class="card-text">Price: 100</p>
// <p class="card-text">Quantity: 1</p>
// <p class="card-text">Sum: 100</p>
// <button type="submit" class="btn btn-primary btn-block btn-large">+</button>
// <button type="submit" class="btn btn-primary btn-block btn-large">-</button>
// </div>
// </div>
// for each item in cart.json, render name, quantity, total, use template above
[提示结束]
这导致了以下代码:
[提示响应]
data.cart.forEach(item => {
// target element with id basket
let basket = document.getElementById('basket');
let div = document.createElement('div');
div.classList.add('card');
basket.appendChild(div);
let divBody = document.createElement('div');
divBody.classList.add('card-body');
div.appendChild(divBody);
let name = document.createElement('h2');
name.classList.add('card-title');
name.innerHTML = item.name;
divBody.appendChild(name);
let price = document.createElement('p');
price.classList.add('card-text');
price.innerHTML = `Price: ${item.price}`;
divBody.appendChild(price);
let quantity = document.createElement('p');
quantity.classList.add('card-text');
quantity.innerHTML = `Quantity: ${item.quantity}`;
divBody.appendChild(quantity);
let total = document.createElement('p');
total.classList.add('card-text');
total.innerHTML = `Total: ${item.price * item.quantity}`;
let buttonPlus = document.createElement('button');
buttonPlus.classList.add('btn', 'btn-primary', 'btn-block', 'btn-large');
buttonPlus.innerHTML = '+';
divBody.appendChild(buttonPlus);
let buttonMinus = document.createElement('button');
buttonMinus.classList.add('btn', 'btn-primary', 'btn-block', 'btn-large');
buttonMinus.innerHTML = '-';
divBody.appendChild(buttonMinus);
});
[响应结束]
所有这些感觉有点复杂,在 JavaScript 和 HTML 之间来回切换。这就是为什么许多开发者会使用一个可以增强 HTML 的 JavaScript 框架,使用模板,并给它添加指令,就像你使用 Vue.js 这样的框架时那样。
添加 Vue.js
到目前为止,我们已经意识到我们的方法并不好。我们让 JavaScript 处理 HTML 元素的创建并从 JSON 响应中填充它们。我们正在寻找一个更干净的方法。因此,我们将从头开始。然而,因为我们一直在与 Copilot 合作,它“记得”我们正在处理basket-type数据,我们正在处理的文件basket.html帮助 Copilot 提供额外的上下文。这看起来可能有点像魔法,但你即将看到的是 Copilot 通过与你合作建立内存上下文的结果。
如果你刚刚打开 Visual Studio Code 并从头开始,你可能需要使用更详细的提示。
在这个阶段添加 JavaScript 框架可能听起来像是一个很大的变化,但实际上并不是。让我们一步一步地展示所需的变化。
首先,让我们在我们的basket.html文件中添加对 Vue.js 的支持。我们在BODY标签的末尾添加以下提示:
[提示]
添加 Vue 3。
[提示结束]
[提示响应]
<!-- add Vue 3 -->
<script src="img/vue@next"></script>
[响应结束]
下一步我们需要的是创建我们的 Vue 应用的代码。
[提示]
为 Vue 3 添加 Bootstrap 代码。
[提示结束]
以下代码是结果:
[提示响应]
<!-- add bootstrap code for Vue 3 -->
<script>
let app = Vue.createApp({
data() {
return {
basket: [
{
id: 1,
name: 'Product 1',
price: 100,
quantity: 1
},
{
id: 2,
name: 'Product 2',
price: 200,
quantity: 2
},
{
id: 3,
name: 'Product 3',
price: 300,
quantity: 3
},
{
id: 4,
name: 'Product 4',
price: 400,
quantity: 4
}
]
}
},
methods: {
increaseQuantity(index) {
this.basket[index].quantity++;
},
decreaseQuantity(index) {
if (this.basket[index].quantity > 0) {
this.basket[index].quantity--;
}
}
},
computed: {
total() {
let total = 0;
for (let i = 0; i < this.basket.length; i++) {
total += this.basket[i].price * this.basket[i].quantity;
}
return total;
}
}
});
app.mount('#basket');
</script>
[响应结束]
记住我们之前关于 Copilot 内存上下文的说法。Copilot 记得我们迄今为止提供的一切,包括购物车的字段,以及我们正在处理的文件basket.html的名称;这就是你看到的结果。
最后,我们需要说明我们想要如何渲染应用:
[提示]
使用 Vue 应用渲染购物车。
[提示结束]
[提示响应]
<!-- render basket from Vue app -->
<div v-for="(item, index) in basket" class="basket-item">
<div class="basket-item-text">
<h2>{{ item.name }}</h2>
<p>Price: {{ item.price }}</p>
<p>Quantity: {{ item.quantity }}</p>
<p>Sum: {{ item.price * item.quantity }}</p>
</div>
<div class="basket-item-buttons">
<button type="submit" class="btn btn-primary btn-block btn-large" @click="increaseQuantity(index)">+</button>
<button type="submit" class="btn btn-primary btn-block btn-large" @click="decreaseQuantity(index)">-</button>
</div>
</div>
[响应结束]
购物车将看起来像这样:

图 6.2:使用 Vue.js 显示购物车
如果你此时尝试在浏览器中运行该页面,你会注意到+和-按钮如何增加和减少产品数量。
到目前为止,缺少的是确保我们的 Vue 应用使用 Bootstrap CSS 库。我们可以通过修改生成 HTML 标记的提示来实现这一点:
[提示]
使用 Vue 应用渲染购物车。
[提示结束]
我们可以将其更改为包含 Bootstrap 上下文信息的提示,如下所示:
[提示]
使用 Bootstrap 和卡片类从 Vue 应用渲染购物车。
[提示结束]
这会导致以下 HTML 标记:

图 6.3:HTML 标记中的购物车
任务
创建一个产品列表页面。页面应显示产品列表。每个产品应有一个添加按钮,该按钮将产品添加到购物车中。购物车应以页面右上角的购物车图标表示,点击后应显示购物车中的项目数量和总价值。
使用你所学的知识来构建一个提示,以创建页面、添加 JavaScript 等。是否添加 Vue.js 来解决这个问题,还是使用纯 JavaScript,取决于你。
解决方案
你可以在 GitHub 仓库中找到这个作业的解决方案:github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT.
摘要
在本章中,我们展示了如何将 JavaScript 添加到网页中。将 JavaScript 添加到网页是一个常见的任务,可以通过两种方式完成,要么在 head 元素中添加 script 标签,要么指向一个独立的文件。
我们还展示了如何通过向我们的应用添加行为来扩展前几章的使用案例。我们首先展示了如何让 JavaScript 生成标记,这可能会变得有些难以控制。然后,我们提出了使用 JavaScript 框架如 Vue.js 来简化管理的理由。
你也看到了如何添加一个 JavaScript 框架,例如 Vue.js。具体添加 JavaScript 框架的方法因框架而异,但通常建议添加一个包含如 setup 或 initialize 等关键词的提示,以确保你不仅添加了 script 标签,还添加了触发设置过程并使选定的框架准备就绪的代码。
在下一章中,我们将展示如何为我们的应用添加响应性,以适应许多不同的设备和视口。我们不能再假设每个人都在使用大屏幕的台式电脑。我们的大多数用户将使用屏幕较小的移动设备。
加入我们的 Discord 社区
加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

第七章:使用响应式网页布局支持多个视口
简介
构建网页是一个挑战。你不仅需要用 HTML、CSS 和 JavaScript 来制作这些页面以执行你设定的任务,而且还需要确保它们对大多数用户都是可访问的。此外,你需要确保页面无论在 PC、平板电脑还是移动设备上都能良好渲染,这意味着你需要考虑屏幕尺寸;设备的方向,即横屏或竖屏;以及像素密度。
确保你的网页在许多设备上看起来很好有很多不同的技术,但这一切都始于有一个策略,一个根据使用的设备为用户提供的体验愿景。一旦你设定了这个愿景,你就可以开始实施它。
你需要做出的某些选择包括,如果你的内容以列的形式呈现,应该列出多少列。其他事物应该如何表现,比如填充和边距?内容应该是居中还是左对齐?内容应该是垂直堆叠还是水平排列?是否有在移动设备上应该隐藏的内容?正如你所看到的,有许多选择会影响你需要使用的提示。
在处理网页布局时使用 AI 助手可能会有所帮助,因为你需要记住很多信息,所以不仅 AI 助手可以记住所有这些细节以便于查找,而且你还可以利用它来建议不同的设计。
在本章中,我们将:
-
解释像视口和媒体查询这样的技术术语。
-
应用不同的技术来优化不同视口的渲染。
-
利用 Copilot 聊天功能来改进我们的代码。这是 GitHub Copilot 中你可以使用的“其他”模式;它是一个聊天窗口,让你可以输入提示并得到响应。这种体验类似于 ChatGPT 这样的 AI 工具。
商业问题:电子商务
本章将继续探讨在前三章中已经工作的电子商务用例。构建功能是一回事,但你必须假设你的用户会从许多不同的设备与你的网站互动,并且这种体验必须是好的,否则他们会去竞争对手的网站。
问题与数据域
现在有许多不同的设备:平板电脑、手机,从小型桌面屏幕到大型屏幕。像素密度各不相同。这不仅仅是将网站缩小或放大以适应新设备的问题,你可能还需要设计一个完全不同的体验,以更好地适应特定设备的视觉风格。还有其他一些考虑因素,比如如果我们假设设备有限制,比如它能处理多少并发下载以及可能具有的网络速度,我们希望向较小的设备发送多少内容。一个具有宽分辨率的桌面机器通常与互联网有很好的连接,这是很常见的。相反,一个移动设备可能处于 3G 网络或更差的网络,因此你需要通过要求更少的图形资源、更小的 JavaScript 包和更多来适应这种情况。
将问题分解为功能
在此之前的几个章节中,我们已经看到,一个好的方法就是确定我们需要实现的功能。这些功能更多地关乎确保设计和交互在优先设备上工作良好,而不是关于读写数据。因此,你可能会有一个如下所示的功能分解列表:
-
应在横幅模式下以双列设计渲染购物车页面。
-
竖幅模式:
-
应在竖幅模式下以单列设计渲染购物车页面。
-
应在屏幕底部显示菜单操作。
-
你应该隐藏某些功能,比如 X、Y、Z(假设 X、Y、Z 在宽屏桌面上有可用)。这个要求的目的在于你必须“重新思考”移动体验与桌面体验的区别,哪些功能是体验的核心,以及我们只有在有足够的屏幕空间来显示时才显示哪些功能。
-
应支持并渲染以下移动设备的视觉吸引力外观:iPhone X、Y、X 和 Android。
-
-
应在 3G 连接上在 1 秒内渲染页面。
如你所见,这些功能与用户体验的联系比任何数据领域都要紧密。
提示策略
我们的提示策略与之前类似,是一种混合方法,即使用编辑器内的体验,并在开放文本文件中添加提示,以在 Copilot 中打开聊天窗口;根据你的判断混合这些方法。
至于提示,这些提示中应有足够的内容,使 Copilot 意识到它需要为特定设备提出设计建议。因此,它应该能够从上下文中推断出哪些分辨率、像素密度和其他细节应该影响它即将生成的建议。至于使用的提示模式,我们将使用第二章中描述的“探索性提示模式”。
视口
那些只需要开发一个看起来在 PC 上很漂亮的网页的日子已经过去了。今天,你的网页可以在多种不同的设备上渲染,并且需要在所有这些设备上都看起来很好,否则你的客户可能会去其他地方。
理解如何构建网页的第一步是熟悉一些关键概念。第一个概念是视口。视口是用户可以看到的页面的一部分。视口与窗口之间的区别在于,视口是窗口的一部分。
根据所使用的设备,例如,桌面屏幕或移动设备,其大小会有所不同。当你编写代码以适应不同的大小,以便良好渲染时,这被称为使页面“响应式”。
媒体查询
好吧,我正在处理不同屏幕大小的设备,所以我如何编写代码以确保视觉界面适应我使用的设备大小?
答案是利用一个称为媒体查询的结构。媒体查询是 CSS 中的一个逻辑块,它识别一个特定条件,并在该条件为真时应用特定的 CSS。
假设存在以下代码;这基本上就是它的工作原理:
if(page.Width > 1024px) {
// render this UI so it looks good for desktop
} else {
// render this UI to look good for a mobile device
}
下面是一个媒体查询的示例:
body {
background: blue;
}
@media (max-width: 600px) {
body {
background-color: lightblue;
}
}
前面的代码识别了一个条件,即如果视口当前宽度最多为 600 像素(这对大多数移动设备来说是正确的),则将背景颜色设置为浅蓝色。
这个例子可能感觉有点牵强;为什么我会在移动设备上想要一个不同的背景颜色,而不是在正常的桌面设备上?你不会,但上面的例子给你一个关于媒体查询如何识别视口大小并在视口满足某些条件时应用特定 CSS 的想法。
何时调整不同视口并使其响应式
使用响应式设计的良好理由是,你有一个在桌面设备上看起来很棒的布局,但可能对移动设备来说太宽了。例如,假设你有一个带有左侧菜单和右侧主要区域的网页:

图 7.1:带有菜单和主要区域的页面
如果我们尝试在移动设备上查看此页面,它看起来不会很好。它看起来可能像这样:

图 7.2:移动设备上的非响应式页面
在上面,我们看到设备试图查看页面,但主要区域被截断了。在这个时候,作为设计师,你需要考虑如何解决这个问题。一种方法是通过使用 flexbox 或 grid 等技术垂直对齐内容。例如,菜单可以放在顶部,而主要区域放在底部。
检查此页面上使用的样式,你会看到以下 CSS:
<style>
/* container, grid, left col is menu, right col is main area */
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
/* 2 columns */
grid-template-columns: 200px 1fr;
background-color: #000;
padding: 10px;
/* space 5 px between columns */
grid-gap: 10px;
/* height 100% */
height: 100%;
}
在前面的代码中,你可以看到如何使用display:grid网格来创建布局:
/* menu items */
.menu {
/* background black shade */
background-color: darkslategrey;
padding: 0px;
/* column 1 */
grid-column: 1;
}
你还可以看到菜单是如何放置在左侧的,grid-column: 1:
/* menu item */
.menu-item {
padding: 5px;
background-color: darkslategrey;
height: 20px;
}
.menu-item a {
color: white;
text-decoration: none;
font-family: sans-serif;
}
/* main area, padding 5px, dark grey */
.main {
padding: 5px;
background-color: darkslategrey;
grid-column: 2;
color: white;
font-family: sans-serif;
}
主要区域是第二列,grid-column: 2:
</style>
为了改进此设计,使其能够在屏幕较小的设备上使用,例如移动设备,我们可以使用以下提示:
[提示]
如果是移动设备,将菜单设置为第 1 行,主要区域设置为第 2 行。
[提示结束]
结果 CSS 如下:
[提示响应]
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
grid-template-rows: 200px 1fr;
}
.menu {
grid-row: 1;
grid-column: 1;
}
.main {
grid-row: 2;
grid-column: 1;
}
}
[结束响应]
如你所见,Copilot 可以接受这个提示指令并相应地调整 CSS。代码将布局调整为单列两行。同时,它确保菜单位于顶部,主区域位于底部:
结果页面现在在移动设备上的渲染方式如下:

图 7.3:菜单和主区域在移动视口中垂直渲染
大多数浏览器都有内置的功能,允许你调整视口大小,这有助于你测试你修改过的 CSS。你还可以更改窗口大小。
用例:使我们的产品画廊响应式
在这个用例中,我们将使用一个在桌面渲染良好的电子商务应用程序,但在移动设备上不良好,并修复这个问题。
首先,这是它的 HTML 代码。如果你想跟上来,你可以将以下代码保存到products.html文件中:
<html>
<head>
<title>menu</title>
<link rel="stylesheet" href="css/style.css">
<style>
/* container, grid, left col is menu, right col is main area */
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
/* 2 columns */
grid-template-columns: 200px 1fr;
background-color: #000;
padding: 10px;
/* space 5 px between columns */
grid-gap: 10px;
/* height 100% */
height: 100%;
}
/* menu items */
.menu {
/* background black shade */
background-color: rgb(25, 41, 41);
/* background-color: #ddd; */
padding: 0px;
/* column 1 */
grid-column: 1;
}
/* menu item */
.menu-item {
padding: 5px;
background-color: rgb(25, 41, 41);
height: 20px;
}
.menu-item a {
color: white;
text-decoration: none;
font-family: sans-serif;
}
/* main area, padding 5px, dark grey */
.main {
padding: 5px;
background-color: rgb(25, 41, 41);
grid-column: 2;
color: white;
font-family: sans-serif;
}
/* if mobile, set menu to row 1 and main row 2 */
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
grid-template-rows: 200px 1fr;
}
.menu {
grid-row: 1;
grid-column: 1;
}
.main {
grid-row: 2;
grid-column: 1;
}
}
/* gallery, 2 columns per row */
.gallery {
display: grid;
/* horizontal grid */
grid-template-columns: auto auto auto;
grid-gap: 20px;
}
/* gallery item */
.gallery-item {
flex: 1 0 24%;
margin-bottom: 10px;
/* padding 10px */
padding: 20px;
/* margin 5px */
margin: 5px;
/* black shadow */
box-shadow: 0 0 10px 0 black;
}
/* gallery image */
.gallery-image {
width: 100%;
height: auto;
transition: transform 0.3s ease-in-out;
}
/* gallery image hover */
.gallery-image:hover {
transform: scale(1.1);
}
</style>
</head>
<body>
<div class="container">
<!-- menu items -->
<div class="menu">
<div class="menu-item">
<a href="index.php">Home</a>
</div>
<div class="menu-item">
<a href="about.php">About</a>
</div>
<div class="menu-item">
<a href="contact.php">Contact</a>
</div>
<div class="menu-item">
<a href="gallery.php">Gallery</a>
</div>
<div class="menu-item">
<a href="login.php">Login</a>
</div>
</div>
<!-- main area -->
<div class="main">
<div class="gallery">
<div class="gallery-item">
<img class="gallery-image" src="img/?random">
<h4>Product 1</h4>
<p>Description</p>
<p>Price</p>
<button>Add to cart</button>
</div>
<div class="gallery-item">
<img class="gallery-image" src="img/?random">
<h4>Product 2</h4>
<p>Description</p>
<p>Price</p>
<button>Add to cart</button>
</div>
<!-- code shortened -->
</div>
</div>
</body>
</html>
这应该在桌面上的渲染如下(确切的图片可能有所不同,因为这些 URL 生成随机的图片):

图 7.4:电子商务产品列表页面
然而,尝试在移动设备上渲染相同的页面,它看起来如下:

图 7.5:电子商务产品列表,在移动设备上看起来很糟糕
为了解决这个问题,我们需要将自己放在 CSS 代码中,并询问我们的 AI 助手我们应该做什么。
在 CSS 底部放置一个提示,如下所示:
[提示]
在移动设备上从 3 列切换到 1 列以用于画廊。
[结束提示]
结果应该是一个如下的媒体查询:
[提示响应]
@media (max-width: 768px) {
.gallery {
grid-template-columns: auto;
}
}
[结束响应]
我们新的移动渲染现在看起来像下面的图片,这是可接受的。

图 7.6:这显示了在移动设备纵向模式下的图片库渲染
作业
作为一名新聘的前端开发者,你被雇佣来维护一个记忆游戏。
游戏看起来像下面的图片:

图 7.7:记忆游戏中的网格
你的公司希望你做以下事情:
-
确保在桌面上的渲染是一个 5x5 的网格。对于更大的视口,它表现不佳,但你应该解决这个问题。
-
支持移动设备,这意味着它应该渲染为一个 5x5 的网格,但使用大小减半的瓷砖。
-
在为移动设备修复时,确保右上角的分数移动到中间并居中。
作为一名开发者,现在你的任务是使用 GitHub Copilot 调整这个游戏的代码,可以使用内联编辑开放文本文件或 Copilot 中的聊天功能来确保代码对不同设备都有效。
解决方案
你可以在 GitHub 仓库中找到这个任务的解决方案:github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT
挑战
所有任务的代码都在一个文件中。看看你是否能将其拆分成不同的文件。此外,尝试进行匹配卡片的实验;尝试移除它们或添加一个显示它们不再是游戏一部分的类。
摘要
在本章中,我们讨论了视口作为响应式网页设计的核心概念。为了帮助我们处理不同的视口,我们使用了媒体查询。
我们也继续我们的用例,即电子商务网站的工作,并尝试确保产品列表在移动设备上渲染良好。首先,要意识到你有一个问题,我们已经设法识别出这个问题。其次,我们提出了一个解决问题的策略,即使用媒体查询。第三,我们实施了这一策略。最后,我们测试了它以确保它工作正常。
在下一章中,我们将从前端转向后端。后端由一个 Web API 组成。我们将继续使用我们的用例,即电子商务网站,并构建一个主要服务于产品列表的 Web API。然而,希望它也能清楚地展示如何将其他资源添加到 Web API 中。
加入我们的 Discord 社区
加入我们的社区 Discord 空间,与作者和其他读者进行讨论:

第八章:使用 Web API 构建后端
简介
当我们说 Web API 时,它是指我们开发的应用程序编程接口,旨在供客户端使用。该 API 使用 HTTP 进行通信。浏览器可以使用 Web API 向其他浏览器和应用程序公开数据和功能。
在开发 Web API 时,您可以使用您想要的任何编程语言和框架。无论选择哪种技术,都有一些您始终需要考虑的事情,比如数据存储、安全、身份验证、授权、文档、测试等等。
正是基于对需要考虑的事物的这种理解,我们可以使用 AI 助手帮助我们构建后端。
在本章中,我们将:
-
了解 Web APIs
-
使用 Python 和 Flask 创建 Web API
-
使用我们的 AI 助手回答问题、建议代码以及创建文档和测试
业务领域:电子商务
我们将在本章继续我们的电子商务示例。这次,重点是 API。API 允许您读取和写入电子商务领域中的重要数据。在开发此 API 时,您需要记住的是,它有几个重要的方面:
-
逻辑领域:将您的应用程序划分为不同的逻辑领域是有益的。在电子商务的背景下,这通常意味着产品、订单、发票等等。
-
哪个业务部分应该处理每个逻辑领域?
-
产品:可能有一个专门的团队。同一个团队管理所有类型的折扣和可能发生的活动是很常见的。
-
发票和支付:通常有一个专门的团队负责用户如何支付,例如,通过信用卡、发票和其他方式。
-
库存:您需要有一定的商品库存。您如何知道有多少?您需要与业务分析师或数据专家合作,做出正确的预测。
-
问题与数据领域
我们已经提到了关于产品、订单、发票等的一些不同的逻辑领域。在这个领域,您会遇到的问题通常是:
-
读取和写入:您希望读取或写入哪些数据(或者可能两者都要)?
-
用户将如何访问您的数据(全部数据还是可能应用过滤器以限制输出)?
-
访问和角色:您可以预期不同的角色将需要访问您的系统。管理员角色可能需要访问大部分数据,而登录用户只能看到属于他们的数据部分。这不是本章要解决的问题,但在构建此 API 时,您应该考虑这一点。
功能分解
现在我们了解到既有业务问题也有数据问题,我们需要开始识别我们需要的功能。一旦达到这个细节水平,提出具体的提示应该会更容易。
进行这种功能分解的一种方法如下——例如,对于产品:
-
阅读所有产品。
-
根据过滤器阅读产品:通常,你不会想阅读所有产品,但可能只想阅读某一类别的所有产品,或者甚至限制为特定的值,例如 10 个产品或 20 个产品。
-
搜索产品:你应该支持用户通过类别、名称或可能是某个特定活动的部分来寻找特定的产品。
-
获取特定产品的详细信息。
我相信产品还有更多功能,但现在你有了在继续构建 API 之前应该具备的详细程度的概念。
提示策略
在本章中,你将看到我们如何使用 Copilot Chat 和编辑器内模式。我们将从 Chat 模式开始,因为它在需要生成起始代码的情况下非常有用。它也非常高效,因为它允许你选择某些代码行,并基于提示仅更新这些行。后者的例子可能是当你想改进这样的代码时。你将在本章后面改进读取数据库而不是从列表中读取静态数据的路由时看到这个用例。在本章中,我们还将使用编辑器内模式。当你正在积极编写代码并想进行小的调整时,这是推荐的方法。在本章中,我们将使用第二章中描述的“探索性提示模式”。
Web API
使用 Web API 是确保我们的前端应用程序能够访问它读取和写入数据所需的数据和功能的一种很好的方式。
Web API 的期望是:
-
它可以通过网络访问。
-
它利用 HTTP 协议和 HTTP 动词,如
GET、POST、PUT、DELETE以及其他,来传达意图。
你应该选择什么语言和框架?
在本章中,我们已经决定将使用 Python 和 Flask。但为什么?我们使用什么标准来选择语言和框架?
你可以使用任何你想要的编程语言和框架,但以下是一些需要考虑的标准:
-
你知道哪些语言和框架?
-
它们是否容易学习?
-
它们是否有庞大的社区?
-
它们是免费和开源的吗?
-
它们多久更新一次?
-
它们是否有良好的文档?
-
它们是否有良好的工具?
这些只是需要考虑的一些标准。
选择 Python 和 Flask 的原因是它们符合许多上述条件(Node.js 的 Express 框架也是如此,但这里的目的是仅展示如何使用 AI 助手构建 Web API,所以请随意使用你喜欢的任何 Web 框架)。此外,本书的目的是展示 AI 助手如何帮助我们构建后端;使用什么提示、如何以及框架和语言不是重点。
规划 Web API
当你规划 Web API 时,你应该考虑以下因素:
-
你想公开哪些数据?例如,产品和订单。
-
你想公开哪些功能?例如,读取订单数据。
-
你将如何构建你的 Web API?
-
安全性和身份验证:你需要确定不仅哪些应用区域需要用户登录,还需要哪些部分应该限制给特定用户类型。
-
存储和数据库:常见的选项,例如,MySQL 和 Postgres。
当你计划你的 Web API 时,请使用上述点作为清单。
使用 Python 和 Flask 创建 Web API
与人工智能助手合作的一个关键见解是,我们可以用它来生成代码,但是我们需要对问题域和解决方案域有良好的理解。这意味着在我们请求人工智能助手帮助我们之前,我们应该知道如何使用 Python 和 Flask 创建一个 Web API。没有人工智能助手我们能创建它吗?是的,但我们可能会陷入困境,不知道下一步该做什么。
那么,我们谈论的是多少知识呢?如果你对 Python 有一般了解,并且知道如何用任何语言构建 Web API,你就准备好了。
让我们开始。
第一步:创建一个新项目
首先,我们需要创建一个新项目。如果你知道 Python,你就知道使用虚拟环境是一个好主意,因为它可以将项目与计算机上其他 Python 项目隔离开来。使用虚拟环境不是必需的,但推荐这样做,因为不同的 Python 版本和包可能会引起问题。
好的,所以我们知道我们需要一个虚拟环境。我们该如何创建一个呢?让我们依靠我们的人工智能助手来帮助我们记住语法。对于下面的提示,你可以打开一个文本文件或者使用 GitHub Copilot 中的聊天功能。
[提示]
我如何为 Python 创建虚拟环境?
[提示结束]
在编辑器中输入提示时,在打开的文本文件中,你更喜欢在句子的开头使用 q:。如果你使用聊天功能,则不需要添加 q:。
[提示响应]
答案就在 a: 同一行。
python -m venv venv
[响应结束]
下面是如何在一个包含提示和响应的 app.py 文件中查看输出:
# q: How do I create a virtual environment for Python? a: python –m venv venv
要使用人工智能助手给出的命令,我们需要打开一个终端,粘贴命令并运行它。
到这一点,你需要足够的概念知识来知道你需要激活虚拟环境才能使用它。但再次,让我们请求人工智能助手帮助我们。
[提示]
我如何在 Windows 中激活 Python 的虚拟环境?
[提示结束]
[快速响应]
a: Venv\Scripts\activate
[响应结束]
如果你使用的是 Mac 或 Linux,将上述提示更改为反映你的操作系统。
第二步:安装 Flask
你需要具备的概念知识是,你可以使用 pip 安装包——在这个例子中,是 Flask。让我们请求人工智能助手帮助我们。继续使用你的 app.py 文件,并将你的提示作为另一条注释添加。
[提示]
我如何使用 pip 安装 Flask?
[提示结束]
[快速响应]
pip install Flask
[响应结束]
你的 app.py 现在应该看起来像这样:
# q: How do I create a virtual environment for Python? a: python –m venv venv
# q: How do I install Flask with pip? a: pip install Flask
当你使用你的人工智能工具来快速获取如何做某事的答案时,是否清除文本文件中的问题和响应取决于你。
太好了,我们现在知道如何安装 Flask。
第 3 步:创建入口点
我们需要为我们的 Web API 创建一个入口点。这个文件将在我们运行 Web API 时被执行。对于 Python,你通常将这个文件命名为 app.py。
第 4 步:创建 Flask 应用
到目前为止,我们有了我们的项目,我们已经安装了 Flask,并且有一个入口点文件,但该应用还不是 Flask 应用,所以让我们创建一个。
让我们请 AI 助手帮助我们。
[提示]
我该如何创建 Flask 应用?
[提示结束]
如往常一样,在您的开放文本文件中,以 q: 开头的注释中输入您的问题。以下是您最新提示的响应:
[提示响应]
from flask import Flask
[响应结束]
我们从 AI 助手那里得到的反馈不多。但我们知道我们需要导入 Flask。让我们改进提示。
[提示]
我该如何创建一个具有默认路由和“hello”路由的 Flask 应用?
[提示结束]
现在,您将看到改进后的提示响应。提示包含更多明确的指令来创建应用中的路由和逻辑区域:
[提示响应]
from flask import Flask
# create app
app = Flask(__name__)
# default route
@app.route('/')
def index():
return 'Hello World!'
# hello route
@app.route('/hello')
def hello():
return 'Hello World!'
# start app on port 5000
if __name__ == '__main__':
app.run(debug=True, port=5000)
[响应结束]
现在我们已经有了我们的 Flask 应用的起始代码。
接下来,让我们专注于我们的 Web API 的用例——电子商务。
用例:电子商务网站的 Web API
到目前为止,在这本书中,我们带您经历了为电子商务网站构建前端应用的过程。我们使用 AI 助手帮助构建前端应用。现在,我们将为电子商务网站构建 Web API。
在上一节中,我们使用 AI 助手帮助我们创建 Flask 应用。现在,我们将使用 AI 助手帮助我们创建电子商务网站的 Web API。
第 1 步:为电子商务网站创建一个 Web API
让我们先规划这个任务。根据前面的章节,电子商务网站有以下功能:
-
产品目录:包含我们打算出售的产品。
-
购物车:有了购物车,我们可以添加产品到购物车,从购物车中移除产品,并更新购物车中产品的数量。一旦我们对购物车中的产品满意,我们就可以结账,并支付和运输产品。
-
结账流程:在结账过程中,我们需要收集支付信息和运输信息。
考虑到以上信息,让我们为我们的 AI 助手制定一个提示。
[提示]
请给我一些代码示例,用于创建一个 Flask Web API,其中包括产品目录、购物车和结账流程的路由。
[提示结束]
[提示响应]
from flask import Flask
# create app
app = Flask(__name__)
# default route
@app.route('/')
def index():
return 'Hello World!'
# product catalog
@app.route('/products')
def products():
return 'Products'
# shopping cart
@app.route('/cart')
def cart():
return 'Cart'
# checkout
@app.route('/checkout')
def checkout():
return 'Checkout'
# start app on port 5000
if __name__ == '__main__':
app.run(debug=True, port=5000)
[响应结束]
好的,所以我们已经有了我们的 Web API 的起始代码,但我们需要以下方面进行改进:
-
我们需要返回 JSON 而不是文本。
-
我们希望添加代码以读取和写入数据库。
-
我们希望添加测试。
-
我们希望记录 Web API。
第 2 步:返回 JSON 而不是文本
JSON 是一种常见的数据格式。它是一种既易于人类阅读和编写,也易于机器阅读和编写的数据格式。要在 Python 中使用 JSON,我们需要导入json包。我们将使用的方法是jsonify(),这是一个将 Python 字典转换为 JSON 的方法。
让我们看看我们如何在我们的 Web API 中使用jsonify()。对于这个操作,我们可以继续使用 AI 助手作为代码中的提示注释,或者我们可以使用 GitHub Copilot 的聊天功能(这是 GitHub Copilot 的一个独立扩展)。
让我们使用后者。要使用 GitHub Copilot 的聊天功能(有关更新的安装说明,请参阅此链接:docs.github.com/en/copilot/github-copilot-chat/using-github-copilot-chat-in-your-ide),我们需要选择我们想要改进的代码,并点击 Visual Studio Code 左侧的聊天图标(如果您使用的是默认方向,其中图标是垂直放置的——这取决于您安装的其他扩展)。这是我们要找的图标。确保 GitHub Copilot Chat 扩展已正确安装:

图 8.1:GitHub Copilot 的聊天图标
一旦我们点击聊天图标,并选择代码,我们就可以开始与 GitHub Copilot 聊天。让我们让 GitHub Copilot 帮助我们返回 JSON 而不是文本。
我们在聊天窗口中添加以下提示:
[提示]
为所有路由添加响应 JSON 的代码。
[提示结束]

图 8.2:Copilot 聊天窗口中的提示
聊天功能生成一个包含jsonify()方法的代码答案。

图 8.3:Copilot 聊天回复
这是 GitHub Copilot 生成的代码:
from flask import Flask, jsonify
# create app
app = Flask(__name__)
# default route
@app.route('/')
def index():
return jsonify({'message': 'Hello World!'})
# product catalog
@app.route('/products')
def products():
products = [
{'id': 1, 'name': 'Product 1', 'price': 10.99},
{'id': 2, 'name': 'Product 2', 'price': 5.99},
{'id': 3, 'name': 'Product 3', 'price': 7.99}
]
return jsonify(products)
# shopping cart
@app.route('/cart')
def cart():
return jsonify({'message': 'Cart'})
# checkout
@app.route('/checkout')
def checkout():
return jsonify({'message': 'Checkout'})
# start app on port 5000
if __name__ == '__main__':
app.run(debug=True, port=5000)
第 3 步:添加代码以读取和写入数据库
好的,所以我们有一个返回 JSON 的 Web API。现在,我们需要添加代码以读取和写入数据库。让我们再次使用聊天功能来帮助我们完成这个任务。
[提示]
添加代码以读取和写入数据库,并将其应用于所有我们的路由。
[提示结束]
这是我们的最终代码:
[提示回复]
import sqlite3
from flask import Flask, jsonify
# create app
app = Flask(__name__)
# connect to database
conn = sqlite3.connect('example.db')
# create table
conn.execute('''CREATE TABLE IF NOT EXISTS products
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL NOT NULL);''')
# insert data
conn.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Product 1', 10.99))
conn.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Product 2', 5.99))
conn.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Product 3', 7.99))
conn.commit()
# default route
@app.route('/')
def index():
return jsonify({'message': 'Hello World!'})
# product catalog
@app.route('/products')
def products():
cursor = conn.execute("SELECT id, name, price FROM products")
products = []
for row in cursor:
product = {'id': row[0], 'name': row[1], 'price': row[2]}
products.append(product)
return jsonify(products)
# shopping cart
@app.route('/cart')
def cart():
return jsonify({'message': 'Cart'})
# checkout
@app.route('/checkout')
def checkout():
return jsonify({'message': 'Checkout'})
# close connection
conn.close()
# start app on port 5000
if __name__ == '__main__':
app.run(debug=True, port=5000)
[回复结束]
如您所见,我们得到了相当多的代码:
-
连接到数据库的代码——在这种情况下,我们得到了连接到常见 SQLite 数据库的连接。
-
在
/products路由中创建表并从中读取的代码,但我们没有获取到写入数据库的代码。 -
我们也没有获取到读取和写入其他路由的代码。
根据上面的代码,我们意识到这是一个好的开始,但我们还有一些想要改进的地方,所以我们将更改提示以解决这些不足。我们需要确保所有路由都从数据库中读取数据,而不仅仅是其中一个。
因此,我们既可以自己调整代码,因为我们知道大部分代码已经就位,也可以尝试调整提示。让我们尝试后者。
[提示]
添加读取和写入数据库的代码,并将其应用于所有路由。
[提示结束]
[提示响应]
import sqlite3
from flask import Flask, jsonify
# create app
app = Flask(__name__)
# connect to database
conn = sqlite3.connect('example.db')
# create table
conn.execute('''CREATE TABLE IF NOT EXISTS products
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL NOT NULL);''')
# insert data
conn.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Product 1', 10.99))
conn.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Product 2', 5.99))
conn.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Product 3', 7.99))
conn.commit()
# default route
@app.route('/')
def index():
return jsonify({'message': 'Hello World!'})
# product catalog
@app.route('/products')
def products():
cursor = conn.execute("SELECT id, name, price FROM products")
products = []
for row in cursor:
product = {'id': row[0], 'name': row[1], 'price': row[2]}
products.append(product)
return jsonify(products)
# shopping cart
@app.route('/cart')
def cart():
cursor = conn.execute("SELECT id, name, price FROM products")
products = []
for row in cursor:
product = {'id': row[0], 'name': row[1], 'price': row[2]}
products.append(product)
return jsonify(products)
# checkout
@app.route('/checkout')
def checkout():
cursor = conn.execute("SELECT id, name, price FROM products")
products = []
for row in cursor:
product = {'id': row[0], 'name': row[1], 'price': row[2]}
products.append(product)
return jsonify(products)
# close connection
conn.close()
# start app on port 5000
if __name__ == '__main__':
app.run(debug=True, port=5000)
[响应结束]
改进的提示带来了以下变化:
-
已将数据库代码添加到
/checkout和/cart路由。 -
所有三个路由都从产品表中读取,这是不正确的。这是我们将在下一个提示中解决的问题。好消息是,我们只需通过提示告诉 Copilot Chat 问题所在,就可以期待 AI 助手相应地更改代码。
上述代码比我们最初的提示要好,但仍存在问题。
你可以在 Copilot Chat 中尝试调整提示,但让我们退回到我们的另一种方法——即在代码中使用提示。要添加这些提示,将光标放在代码中,并将提示作为注释添加。以下是我们添加的一些提示:
-
# 创建表:我们重复了已经使用的注释,但由于 Copilot 现在意识到了所有路由,这次它做得更好,建议创建三个数据库表而不是只有一个
products表。 -
# 添加产品到购物车,在 body 中包含 productId 和 quantity,如果需要则创建购物车,否则更新:这个提示很详细,因为它询问应该将哪些信息添加到每个购物车条目中。
-
# 产品目录 和 # 购物车 也是重复的提示(我们移除旧的提示,并重新输入以切换新的建议)。这次,我们让 Copilot 建议正确的数据库表来读取。
注意以下如何通过代码中的各种提示(作为注释)来重塑代码:
import sqlite3
import json
import flask
# create app
app = flask.Flask(__name__)
# connect to database
db = sqlite3.connect('example.db')
# create tables
db.execute('''CREATE TABLE IF NOT EXISTS products
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL NOT NULL);''')
db.execute('''CREATE TABLE IF NOT EXISTS cart
(id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL);''')
db.execute('''CREATE TABLE IF NOT EXISTS checkout
(id INTEGER PRIMARY KEY AUTOINCREMENT,
cart_id INTEGER NOT NULL,
total REAL NOT NULL);''')
db.commit()
db.close()
# default route
@app.route('/')
def index():
return flask.jsonify({'message': 'Hello World!'})
# product catalog
@app.route('/products')
def products():
db = sqlite3.connect('example.db')
cursor = db.execute("SELECT id, name, price FROM products")
products = []
for row in cursor:
product = {'id': row[0], 'name': row[1], 'price': row[2]}
products.append(product)
db.close()
return flask.jsonify(products)
# shopping cart
@app.route('/cart')
def cart():
db = sqlite3.connect('example.db')
cursor = db.execute("SELECT id, product_id, quantity FROM cart")
cart = []
for row in cursor:
item = {'id': row[0], 'product_id': row[1], 'quantity': row[2]}
cart.append(item)
db.close()
return flask.jsonify(cart)
# add product to cart, productId and quantity in body, create cart if needed, else update
@app.route('/cart/', methods=['POST'])
def add_to_cart():
db = sqlite3.connect('example.db')
# get product id and quantity from body
product_id = flask.request.json['productId']
quantity = flask.request.json['quantity']
# check if cart exists
cursor = db.execute("SELECT id FROM cart")
cart_id = None
for row in cursor:
cart_id = row[0]
# if cart exists, update
if cart_id:
db.execute("UPDATE cart SET product_id = ?, quantity = ? WHERE id = ?", (product_id, quantity, cart_id))
# else create
else:
db.execute("INSERT INTO cart (product_id, quantity) VALUES (?, ?)", (product_id, quantity))
# close
db.commit()
db.close()
return flask.jsonify({'message': 'Added to cart'})
# checkout POST, cartId in body
@app.route('/checkout', methods=['POST'])
def checkout():
# insert cart into checkout
conn = sqlite3.connect('example.db')
# get cart id from body
cart_id = flask.request.json['cartId']
# write to checkout
conn.execute("INSERT INTO checkout (cart_id, total) VALUES (?, ?)", (cart_id, 0))
# close
conn.commit()
conn.close()
这段代码足够好吗?或者我们需要进一步调整?
肯定有改进的空间,以下是你应该寻找的内容:
-
代码不是 DRY(不要重复自己);我们有大量的重复代码。我们可以通过创建一个函数来解决,该函数接受一个查询并返回结果。
-
缺少身份验证和授权。我们应该将其添加到代码中。
-
缺少文档。
-
代码不安全。我们应该添加一些安全措施,尤其是在数据库方面。作为开发者,我们需要了解如何确保代码安全,我们可以使用预处理语句来防止 SQL 注入攻击,并验证我们从客户端接收的数据。
第 4 步:改进代码
提高代码的最佳方式是以我们已有的代码作为起点,首先尝试运行它。然后我们可以看到我们得到了什么错误以及我们需要修复什么。
之后,我们专注于架构和设计,并将代码分离到不同的文件中。
最后,我们添加身份验证、授权和安全措施。
运行代码
让我们运行代码。我们需要将 Flask 变量FLASK_APP设置为app.py然后运行应用程序。
如果你使用 Windows,你需要使用set而不是export来设置变量。
# flask variable windows
set FLASK_APP=app.py
flask run
应用程序正在运行!我们现在可以访问http://localhost:5000并看到消息Hello World!。
导航到/products、/cart和/checkout也正常,但我们数据库中还没有任何数据。让我们向数据库添加一些数据。通过在文件顶部插入上述代码并再次运行应用程序,我们可以这样做:
db.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Apple', 1.99))
db.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Banana', 2.99))
现在,如果你重新运行应用程序并转到/products,你应该看到我们添加的产品,如下面的 JSON 输出所示:
[
{
"id": 1,
"name": "Apple",
"price": 1.99
},
{
"id": 2,
"name": "Banana",
"price": 2.99
}
]
重构代码
太棒了 - 到目前为止进展顺利!现在让我们重构代码,使其更加 DRY(不要重复自己,这是一个确保我们不过度重复代码的术语)。我们可以使用 Copilot 来帮助我们完成这项工作。
提供以下提示:
[提示]
通过将数据库部分分离到单独的模块db.py中并暴露以下函数来改进代码:get_products,get_cart,add_to_cart,checkout。
[提示结束]
我们得到了两个响应,一个是针对db.py文件的,另一个是针对app.py文件的。让我们从db.py文件开始。
[提示响应]
import sqlite3
# connect to database
conn = sqlite3.connect('example.db')
# create tables
conn.execute('''CREATE TABLE IF NOT EXISTS products
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL NOT NULL);''')
conn.execute('''CREATE TABLE IF NOT EXISTS cart
(id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL);''')
conn.execute('''CREATE TABLE IF NOT EXISTS checkout
(id INTEGER PRIMARY KEY AUTOINCREMENT,
cart_id INTEGER NOT NULL,
total REAL NOT NULL);''')
conn.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Apple', 1.99))
conn.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Banana', 2.99))
conn.commit()
def get_products():
cursor = conn.execute("SELECT id, name, price FROM products")
products = []
for row in cursor:
product = {'id': row[0], 'name': row[1], 'price': row[2]}
products.append(product)
return products
def get_cart():
cursor = conn.execute("SELECT id, product_id, quantity FROM cart")
cart = []
for row in cursor:
item = {'id': row[0], 'product_id': row[1], 'quantity': row[2]}
cart.append(item)
return cart
def add_to_cart(product_id, quantity):
# check if cart exists
cursor = conn.execute("SELECT id FROM cart")
cart_id = None
for row in cursor:
cart_id = row[0]
# if cart exists, update
if cart_id:
conn.execute("UPDATE cart SET product_id = ?, quantity = ? WHERE id = ?", (product_id, quantity, cart_id))
# else create
else:
conn.execute("INSERT INTO cart (product_id, quantity) VALUES (?, ?)", (product_id, quantity))
conn.commit()
def checkout(cart_id):
# insert cart into checkout
conn.execute("INSERT INTO checkout (cart_id, total) VALUES (?, ?)", (cart_id, 0))
conn.commit()
[响应结束]
我们需要稍微改进上面的代码,因为它正在使用一个打开的数据库连接。我们应该在完成操作后关闭连接。确保你在提交后添加关闭连接的代码。还要确保每个路由都打开和关闭连接。
这看起来不错。让我们将其添加到我们的项目中。我们可以创建一个名为db.py的新文件并将代码复制到其中。
接下来,让我们看看它对app.py文件的建议:
import flask
import db
# create app
app = flask.Flask(__name__)
# default route
@app.route('/')
def index():
return flask.jsonify({'message': 'Hello World!'})
# product catalog
@app.route('/products')
def products():
products = db.get_products()
return flask.jsonify(products)
# shopping cart
@app.route('/cart')
def cart():
cart = db.get_cart()
return flask.jsonify(cart)
# add product to cart, productId and quantity in body, create cart if needed, else update
@app.route('/cart', methods=['POST'])
def add_to_cart():
# get product id and quantity from body
product_id = flask.request.json['productId']
quantity = flask.request.json['quantity']
db.add_to_cart(product_id, quantity)
return flask.jsonify({'message': 'Added to cart'})
# checkout POST, cartId in body
@app.route('/checkout', methods=['POST'])
def checkout():
# get cart id from body
cart_id = flask.request.json['cartId']
db.checkout(cart_id)
return flask.jsonify({'message': 'Checkout successful'})
# start app on port 5000
if __name__ == '__main__':
app.run(debug=True, port=5000)
如你所见,它已经将数据库代码分离到db.py文件中,并且还添加了对db.py文件的导入。让我们将此代码复制到我们的app.py文件中,并替换其内容。
它工作了吗?让我们运行应用程序并看看。它没有错误地运行,当我们转到/products、/cart和/checkout时,我们得到了 JSON 输出。如果你想的话,你还可以向/cart发送一个POST请求来写入购物车内容 - 太棒了!
第 5 步:记录 API
我们可以使用 Copilot Chat 来帮助我们记录 API。让我们提供以下提示:
[提示]
使用 Swagger 记录 API。API 应该有以下端点:GET /products,GET /cart,POST /cart/add,POST /checkout。
[提示结束]
Swagger 或 OpenAPI 标准是记录你的 API 路由的一种方式。这些路由规范可以被可视化,你可以快速了解你的 API 能做什么。此外,还有一个叫做 Swagger UI 的网页,它不仅让你可以看到你的 API 中有哪些路由,你还可以进行请求等操作,以确保 API 按预期工作。
这是输出,它涉及代码和文档:
[提示响应]
# app.py
import flask
import db
from flasgger import Swagger, swag_from
# create app
app = flask.Flask(__name__)
swagger = Swagger(app)
# default route
@app.route('/')
def index():
return flask.jsonify({'message': 'Hello World!'})
# product catalog
@app.route('/products', methods=['GET'])
@swag_from('swagger/products.yml')
def products():
products = db.get_products()
return flask.jsonify(products)
# shopping cart
@app.route('/cart', methods=['GET'])
@swag_from('swagger/cart.yml')
def cart():
cart = db.get_cart()
return flask.jsonify(cart)
# add product to cart, productId and quantity in body, create cart if needed, else update
@app.route('/cart/add', methods=['POST'])
@swag_from('swagger/add_to_cart.yml')
def add_to_cart():
# get product id and quantity from body
product_id = flask.request.json['productId']
quantity = flask.request.json['quantity']
db.add_to_cart(product_id, quantity)
return flask.jsonify({'message': 'Added to cart'})
# checkout POST, cartId in body
@app.route('/checkout', methods=['POST'])
@swag_from('swagger/checkout.yml')
def checkout():
# get cart id from body
cart_id = flask.request.json['cartId']
db.checkout(cart_id)
return flask.jsonify({'message': 'Checkout successful'})
# start app on port 5000
if __name__ == '__main__':
app.run(debug=True, port=5000)
[响应结束]
它告诉我们创建以下文件和文件夹:
folder: swagger
products.yml
file: cart.yml
file: add_to_cart.yml
file: checkout.yml
file: swagger.yml
让我们手动创建这些文件并将内容复制到它们中。
products.yml
---
tags:
- products
responses:
200:
description: A list of products
schema:
type: array
items:
type: object
properties:
id:
type: integer
name:
type: string
price:
type: number
cart.yml
---
tags:
- cart
responses:
200:
description: A list of items in the cart
schema:
type: array
items:
type: object
properties:
id:
type: integer
product_id:
type: integer
quantity:
type: integer
add_to_cart.yml
---
tags:
- cart
parameters:
- in: body
name: body
required: true
schema:
type: object
properties:
productId:
type: integer
quantity:
type: integer
responses:
200:
description: Added to cart
checkout.yml
---
tags:
- checkout
parameters:
- in: body
name: body
required: true
schema:
type: object
properties:
cartId:
type: integer
responses:
200:
description: Checkout successful
缺少的是安装flassger Swagger 库:
pip install flasgger
导航到http://localhost:5000/apidocs/,你应该能看到 Swagger UI。

图 8.4:由 Swagger 生成的 API 文档
你应该通过与生成的文档交互来验证 API 是否按预期工作,并确保路由生成预期的输出。
在这个阶段,我们确实有可能继续改进,但请花点时间意识到我们仅通过提示和几行代码就创造了多少东西。我们有一个带有数据库和文档的工作 API。现在我们可以专注于改进代码和添加更多功能。
作业
这是本章的建议作业:一个好的作业是向 API 添加更多功能,例如:
-
添加一个新端点来获取单个产品。
-
添加一个新端点来从购物车中删除产品。
-
添加一个新端点来更新购物车中产品的数量。
你可以通过将上述内容添加到 Copilot Chat 作为提示并查看它生成的结果来解决这个问题。预期代码和文档都会有所变化。
解决方案
你可以在 GitHub 仓库中找到这个作业的解决方案:github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT/tree/main/08
挑战
通过添加更多功能来改进这个 API。你可以使用 Copilot Chat 来帮助你完成这项工作。
摘要
在本章中,我们讨论了如何规划我们的 API。然后我们探讨了如何选择 Python 和 Flask 来完成这项工作,但强调了在实际构建 Web API 方面拥有上下文知识的重要性。一般来说,在请求 AI 助手帮助你之前,你应该至少在高级别上知道如何做某事。
然后,我们最终为 AI 助手制作了提示,以帮助我们处理 Web API。我们与我们的电子商务网站合作,创建了一个 Web API 来提供服务。
之后,我们讨论了如何改进代码并为 API 添加更多功能。
在下一章中,我们将讨论如何通过添加人工智能来改进我们的应用程序。
加入我们的 Discord 社区
加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

第九章:通过人工智能服务增强 Web 应用
简介
网络应用可以通过多种方式增强人工智能服务:你可以利用现有的暴露模型的 Web API,或者自己构建并使其调用模型。
你最初想要将人工智能添加到你的应用中的原因是为了让它变得更智能。不是为了它自己的目的而变得聪明,而是为了让它对用户更有用。例如,如果你有一个允许用户搜索产品的网络应用,你可以添加一个基于用户先前购买的产品推荐功能。实际上,为什么只限于先前购买的产品呢?为什么不能根据用户的先前搜索推荐产品?或者,如果用户可以拍照产品,应用会推荐类似的产品呢?
正如你所见,有很多可能性可以增强你的网络应用,使其通过人工智能提升用户体验。
在本章中,我们将:
-
讨论不同的模型格式,如 Pickle 和 ONNX
-
学习如何使用 Python 中的 Pickle 和 ONNX 将你的模型持久化为文件
-
消费存储在 ONNX 格式的模型并通过 JavaScript REST API 公开
商业领域,电子商务
我们一直在努力工作在我们的电子商务领域,但我们的业务重点是评分。好的或坏的评分可以影响特定产品的销量。逻辑领域包括以下内容:
-
产品:要评分的产品
-
评分:实际的评分和元信息,如评论、日期等
问题和数据领域
需要解决的问题是如何使用这些评分数据并从中学习。
-
洞察:例如,我们可以得到这样的洞察,我们应该开始/停止销售某种产品。可能还有其他洞察,因为某些产品在世界某些地区销售得很好。
-
技术 问题:这一方面的技术问题是弄清楚如何摄取数据,从数据中训练模型,然后弄清楚如何让网络应用利用该模型。
特征分解
从功能的角度来看,我们需要将其视为由三个主要部分组成。
-
数据摄取和训练:这需要一个单独的界面,可能是在没有用户界面的情况下完成的,只是静态数据被输入到能够从数据中训练模型的代码中。有了这个理解,我们可以概述以下步骤:
-
加载数据
-
清洗数据
-
创建功能
-
训练模型
-
评估模型
-
运行预测
-
-
消费模型:一旦模型训练完成,它需要被公开,最好是通过 Web 端点。为了达到这一点,我们认为我们需要以下步骤:
-
如有必要,将模型转换为合适的格式
-
构建 Web API
-
通过 Web API 公开模型
-
部署模型,这里有一个步骤需要将 API 上线
-
-
预测:对于预测部分,这是一个面向“后台”而非面向客户的功能。
-
构建用户界面以运行预测
-
构建与 Web API 通信的底层代码以实现预测
-
提示策略
你可以选择在这里采用提示方法,要么从提示注释中获取建议,要么使用聊天界面。
对于提示模式,我们将使用第二章中描述的“探索模式”。
创建模型
假设我们正在处理sales_rating.csv文件中的以下数据,这是合并两个数据集的结果,一个包含销售数据,另一个包含评分数据。数据看起来像这样:
product_id,avg_rating,sold
1,2.5,100
2,3.7,200
3,4.2,300
4,1.3,50
5,4.9,800
6,3.2,150
7,2.1,80
8,4.8,500
9,3.9,400
10,2.4,200
11,4.1,300
12,3.2,100
13,2.9,150
14,4.5,500
15,3.8,400
16,2.7,200
17,4.3,300
18,3.4,100
19,2.3,150
20,4.7,500
前面的数据显示了一个包含product_id、avg_rating和sold列的数据集。我们的理论是产品的平均评分与销售数量相关。一个高评分的产品似乎会比一个低评分的产品卖得更好,这是一个合理的假设。通过创建模型,我们可以更接近地确定我们的假设是否成立。
制定计划
我们可以向 Copilot 询问创建模型所需的步骤。我们将这些提示作为注释输入:
如果你想让 Copilot 提供关于代码之外的其他文本答案,你可以在代码文件或 Markdown 文件中的注释中输入文本。在注释之后放置下一行,并从“a:”开始行,也有助于。你也可以使用 Copilot Chat 提问:
[提示]
告诉我生命、宇宙和万物的答案。
[结束提示]
[提示响应]
a: 42
[结束响应]
上述参考来自道格拉斯·亚当斯的书籍《银河系漫游指南》。
接下来,让我们让 Copilot 帮助我们了解创建模型的步骤。
[提示]
在给定的 CSV 文件中创建模型有哪些步骤?
[结束提示]
输出:
-
导入 pandas 和 scikit-learn
-
读取 CSV 文件
-
创建测试和训练数据集
-
创建一个模型
-
模型的效果如何?
-
预测
-
将模型保存为 pkl
好的,我们有一个计划,那么就执行它,并使用 Copilot 帮助我们编写代码。
导入库
首先,让我们在 VS Code 中创建一个新的笔记本。让我们称它为sales.ipynb。
.ipynb是 Jupyter Notebook 的文件格式,作为数据科学实践者,你很可能熟悉它。想法是你在文件中工作,文件由单元格组成,其中一些是文本单元格,其他是你可以运行的代码单元格。
要使用某些库,我们需要安装它们。我们可以使用 Copilot 来帮助我们。我们可以利用我们是数据科学实践者的这一事实,因此了解我们需要哪些库。因此,我们将建议以下 pandas 和 scikit-learn 库来帮助我们完成任务。
[提示]
在笔记本中安装 pandas 和 scikit-learn。
[结束提示]
[提示响应]
! pip install pandas scikit-learn
[结束响应]
接下来,让我们导入所需的库。
[提示]
在笔记本中导入 pandas 和 scikit-learn。
[结束提示]
[提示响应]
# Import pandas and scikit-learn in a notebook.
import pandas as pd
import sklearn.linear_model
[结束响应]
读取 CSV 文件
现在我们有了需要的库,让我们读取 CSV 文件。
[提示]
在笔记本中读取 CSV 文件。
[提示结束]
[提示回复]
# Read a CSV file in a notebook.
df = pd.read_csv("sales_rating.csv")
[回复结束]
CSV 文件现在已加载到 pandas DataFrame 中,这是一个包含列和行的结构。让我们看看前几行。
df.head()
这将产生以下输出:
product_id avg_rating sold
0 1 2.5 100
1 2 3.7 200
2 3 4.2 300
3 4 1.3 50
4 5 4.9 800
太好了,我们已经将数据加载到 DataFrame 中。接下来,我们需要将数据分为测试集和训练集。
创建测试集和训练集
在机器学习中,我们通常将数据分为测试集和训练集。训练集用于训练模型,测试集用于测试模型。我们可以使用 Copilot 来帮助我们完成这项工作。
[提示]
在笔记本中创建测试集和训练集,建议测试集大小为 0.2。
[提示结束]
[提示回复]
# Create a test and training dataset in a notebook, suggested test size of 0.2
test_size = 0.2
test = df.sample(frac=test_size)
training = df.drop(test.index)
[回复结束]
0.2 是一个好的测试集大小。这意味着 20%的数据将用于测试,80%的数据将用于训练。
您可能需要在相应的行中开始键入“test_size”和“training”,然后按TAB键接受建议。
现在我们已经设置好了,接下来让我们创建一个模型。
创建模型
创建模型涉及选择我们想要使用的算法,然后训练模型。让我们提出一个提示:
[提示]
在笔记本中使用LinearRegression算法创建一个模型。
[提示结束]
[提示回复]
# Create a model in a notebook using the LinearRegression algorithm.
model = sklearn.linear_model.LinearRegression()
model.fit(training[["avg_rating"]], training[["sold"]])
[回复结束]
在前面的代码中,我们使用LinearRegression算法创建了一个模型,然后使用fit方法使用训练集训练了模型。同时,请注意我们是如何使用avg_rating列作为输入,sold列作为输出的。这是因为我们想要根据平均评分预测销售数量。
模型有多好?
在这个阶段,我们想知道模型有多好。为了找出这一点,让我们请 Copilot 来帮助我们。
您可以使用注释风格并获取内联建议,或者使用 Copilot Chat。在两种情况下,Copilot 都会根据上下文理解。
[提示]
在笔记本中,模型表现如何?显示代码。
[提示结束]
[提示回复]
# How good is the model in a notebook? Show the code.
score = model.score(test[["avg_rating"]], test[["sold"]])
[回复结束]
调用模型上的score并传入test数据集似乎是我们了解模型有多好的方法。我们甚至可以询问 Copilot 关于这一点:
[提示]
分数是什么意思?
[提示结束]
[提示回复]
score returns the coefficient of determination R² of the prediction.
[回复结束]
如果您愿意,您可以继续查询并更详细地解释,如下面的提示:
[提示]
这在简单术语中意味着什么?
[提示结束]
[提示回复]
it returns a number between 0 and 1, where 1 is perfect prediction and 0 is no prediction.
[回复结束]
预测
在我们尝试预测某些东西之前,我们不会知道模型有多好,即使我们可以参考分数值。让我们为这个任务编写一个提示:
[提示]
在笔记本中预测。显示代码。
[提示结束]
[提示回复]
# Predict in a notebook. Show the code.
predictions = model.predict(test[["avg_rating"]])
[回复结束]
好的,我们得到了一个名为 predictions 的变量,它包含预测值,但很难理解这些预测是否合理。让我们尝试询问 Copilot 并看看它有什么建议。
[提示]
你建议我们如何在笔记本中显示预测结果?
[提示结束]
[提示响应]
print the predictions
[响应结束]
[提示]
除了打印之外,你建议我们还有哪些方法可以在笔记本中显示预测结果?
[提示结束]
[提示响应]
plot the predictions
[响应结束]
现在我们已经获得了关于我们各种选项的更多信息,让我们利用这些知识在提示中使用:
[提示]
在笔记本中展示如何打印和绘制预测数据与实际数据对比的图表。
[提示结束]
[提示响应]
# Show me how to print and plot predictions vs actual data in a notebook.
import matplotlib.pyplot as plt
plt.scatter(test[["avg_rating"]], test[["sold"]], color='black')
plt.plot(test[["avg_rating"]], predictions, color='blue', linewidth=3)
[响应结束]
在笔记本中运行此代码将生成以下图表:

图 9.1:显示预测数据与实际数据对比的图表
在图表中,看起来这条线很好地拟合了数据。我们还可以打印预测值以查看实际值:
print(predictions)
将模型保存到 .pkl 文件
我们还剩下一步:我们需要将模型保存到文件中。我们有几种不同的保存模型的方式。例如,我们可以将其保存为 pickle 文件或 ONNX 文件。让我们先从将其保存为 pickle 文件开始。
如同往常,让我们为这个任务创建一个提示:
[提示]
在笔记本中将模型保存为 pkl。
[提示结束]
[提示响应]
# Save the model to pkl in a notebook.
import pickle
pickle.dump(model, open("model.pkl", "wb"))
[响应结束]
为了完成上述内容,你可能需要开始输入“import”以获取建议。另外,它很可能使用 pickle 库,所以你可以在下一行开始输入“pickle”并按 TAB 键接受建议。
在 Python 中创建 REST API
到目前为止,我们有一个包含我们模型的 model.pkl 文件。我们可以通过这个文件通过 REST API 暴露模型。让我们让 Copilot 帮助我们完成这个任务。
[提示]
在 Python 中创建一个 REST API,以暴露 pkl 文件中的模型。
[提示结束]
[提示响应]
# Create a REST API in Python that exposes the model in a pkl file.
import pickle
import flask
# load the model
model = pickle.load(open("model.pkl", "rb"))
# app
app = flask.Flask(__name__)
# routes
@app.route('/', methods=['GET', 'POST'])
def predict():
if flask.request.method == 'GET':
return "Please POST a JSON object to this endpoint to get a prediction."
else:
data = flask.request.json
prediction = model.predict([[data["avg_rating"]]])
return flask.jsonify({"prediction": prediction[0][0]})
[响应结束]
注意我们上面使用了好几个提示。首先,我们在文件顶部添加了提示“创建一个在 Python 中暴露 pkl 文件的 REST API”,从而设置了上下文。然后我们使用各种辅助提示,如“加载模型”、“应用”和“路由”,帮助我们编写代码。最终我们得到了一个通过 REST API 暴露模型的 API。
将模型转换为 ONNX
到目前为止,你已经看到了我们如何使用 Pickle 保存模型,然后在 Python 中加载它。然而,Pickle 有一些缺点,包括它是 Python 特定的。另一方面,ONNX 是一种不是 Python 特定的格式,可以在其他语言中使用。让我们看看我们如何使用 ONNX 格式来处理我们的模型。
我们需要分两步解决我们的任务:
-
将模型转换为 ONNX 格式。
-
加载模型并在 JavaScript 中创建一个 REST API。
在 ONNX 格式中创建模型
因为我们使用的是笔记本,我们需要安装依赖项,导入我们需要的库,然后将模型转换为 ONNX 格式。
[提示]
我需要哪些 ONNX 依赖项?展示安装这些依赖项的 Python 代码。
[提示结束]
[提示回复]
! pip install skl2onnx
[回复结束]
[提示]
展示如何导入我需要的 ONNX 库,并展示如何将模型转换为 ONNX 格式。
[提示结束]
[提示回复]
import skl2onnx
# convert the model to ONNX
skl2onnx.to_onnx(model, train[["avg_rating"]].astype("float32"), "model.onnx")
[回复结束]
现在我应该有一个包含 ONNX 格式模型的 model.onnx 文件。让我们看看我们是否可以在 JavaScript 中加载它。
在 JavaScript 中加载 ONNX 模型
接下来,我们需要在 JavaScript 中(特别是 Node.js,因为我们是在后端进行此操作)加载 ONNX 模型。我们可以使用 onnxruntime 库来完成此操作。让我们让 Copilot 帮助我们。
[提示]
展示一个逐步指南,说明如何在 JavaScript 中加载 ONNX 模型。
[提示结束]
[提示回复]
-
在 JavaScript 中安装 onnxruntime
-
在 JavaScript 中加载 ONNX 模型
-
使用 ONNX 模型在 JavaScript 中进行预测
[回复结束]
现在我们有了计划,让我们执行它。
在 JavaScript 中安装 onnxruntime
因为我们正在构建一个 Node.js 项目,所以首先,在一个新文件夹中创建一个 app.js 文件,并运行 npm init -y 终端命令来创建一个新的 Node.js 项目。
我们计划的第一个步骤是在 JavaScript 中安装 onnxruntime。我们可以使用 npm 来执行此任务。
[提示]
在 JavaScript 中安装 onnxruntime。
[提示结束]
[提示回复]
npm install onnxruntime
[回复结束]
很好,这已经在我们的项目中安装了 onnxruntime 库。接下来,我们需要加载 ONNX 模型。
在 JavaScript 中加载 ONNX 模型
现在我们已经安装了 onnxruntime,我们可以加载 ONNX 模型。
[提示]
在 JavaScript 中加载 ONNX 模型。
[提示结束]
[提示回复]
const rt = require("onnxruntime");
# load model from ONNX
model rt.InferenceSession("model.onnx");
# predict
model.run(null, {model.get_inputs()[0].name: [[4.5]]});
[回复结束]
在前面的代码中,我们从 ONNX 文件中加载模型,然后使用模型对输入 4.5 进行预测,以表示平均评分,看看我们可以期待多少销售额。
任务:使用 JavaScript 构建一个消耗模型的 REST API
-
将我们在上一节中创建的模型添加到笔记本中,将其转换为 ONNX 文件。
-
在存储库中创建一个名为的新文件夹,并在该文件夹中创建一个名为
app.js的新文件。 -
将 POST /predict 路由添加到
server.js文件中,并确保它根据输入返回一个预测结果。
这里有一些你可以尝试的起始提示,以帮助你完成这个任务:
-
提示:使用 Express 在 JavaScript 中创建 REST API
-
提示:使用 Express 在 JavaScript 中创建一个 POST /predict 路由
-
提示:使用 Express 在 JavaScript 中从 ONNX 加载模型
-
提示:使用 Express 在 JavaScript 中的 REST API 中使用 ONNX 模型进行预测
解决方案
请参阅代码库 [github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT/tree/main/09] 和 09 文件夹以获取解决方案。
测验
Pickle 和 ONNX 之间的区别是什么?
-
Pickle 是 Python 特定的,而 ONNX 则不是。
-
Pickle 可以在 JavaScript 中使用,而 ONNX 则不能。
-
ONNX 的效率不如 Pickle。
摘要
在本章中,我们介绍了各种模型格式,如 Pickle 和 ONNX,以及如何使用 Python 将模型持久化为文件。将模型存储为文件很有用,因为它允许您将其与其他应用程序集成。
然后,我们讨论了存储模型的不同格式的优缺点,如 Pickle 和 ONNX。我们得出结论,ONNX 可能是更好的选择,因为它不是 Python 特定的,并且可以在其他语言中使用。
然后,我们介绍了如何使用 JavaScript 加载存储为 ONNX 格式的模型,并创建 REST API 使模型可供其他应用程序使用。
在下一章中,我们将更详细地介绍如何使用 GitHub Copilot 并充分利用它。我们将涵盖技巧和窍门以及有助于让您更快、更高效的功能。
加入我们的 Discord 社区
加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

第十章:维护现有代码库
简介
棕色地带是另一种与现有代码一起工作的说法。在我的开发生涯中,大部分工作都是在现有代码上完成的。棕色地带的对立面是绿色地带,这是一个没有现有代码的新项目。
因此,了解如何与现有代码库一起工作非常重要,并且在棕色地带环境中与像 GitHub Copilot 这样的 AI 助手一起工作时,有很多令人兴奋的事情。
在本章中,我们将:
-
了解不同类型的维护。
-
了解我们如何通过流程进行维护以降低引入更改的风险。
-
使用 GitHub Copilot 来帮助我们进行维护。
提示策略
本章与其他章节的书籍略有不同。重点是描述您可能在现有代码库空间中遇到的各种问题。建议您使用最舒适的提示建议方法,无论是提示注释还是聊天界面。至于模式,鼓励您尝试所有三种主要模式,即第二章中描述的 PIC、TAG 或探索性模式。然而,本章的重点是使用“探索性模式”。
不同类型的维护
有不同类型的维护,了解它们之间的区别非常重要。以下是一些您可能会遇到的不同类型:
-
纠正性维护:这是我们修复错误的时候。
-
适应性维护:在这种情况下,我们更改代码以适应新的需求。
-
改进性维护:当我们不改变功能的情况下改进代码。这可能是重构或提高代码性能的例子。
-
预防性维护:更改代码以防止未来的错误或问题。
维护流程
每次您更改代码时,都会引入风险。例如,一个错误修复可能会引入新的错误。为了减轻这种风险,我们需要遵循一个流程。一个建议的流程可能是以下步骤:
-
识别:识别问题或需要做出的更改。
-
检查:检查测试覆盖率以及您的代码被测试覆盖的情况。覆盖得越好,您发现引入的任何错误或其他问题的可能性就越大。
-
计划:计划更改。您将如何进行?您将编写哪些测试?您将运行哪些测试?
-
实施:实施更改。
-
验证:验证更改是否按预期工作。运行测试,运行应用程序,检查日志等。
-
集成:这是确保您在分支中做出的任何更改都能合并到主分支中。
-
发布/部署更改:您希望确保最终客户能够利用这次更改带来的好处。为了实现这一点,您需要部署它。
我们是否需要为每次更改都涵盖所有这些步骤?不,这取决于更改;一些更改可能非常小,我们可能希望在发布我们希望最终客户拥有的版本之前将它们捆绑在一起。好消息是,我们有一个在 GitHub Copilot 中的优秀工具可以帮助我们处理这些小步骤,使我们能够专注于更重要步骤。
解决错误
有些错误比其他错误更严重。在最佳情况下,你的错误很少遇到,或者即使发生,也不是什么大问题。在最坏的情况下,你的错误会让你损失金钱。让我们关注一个在运行电子商务网站的场景中,错误让你损失金钱的情况。
希望这段代码从未在生产环境中发布过,但它是一个对电子商务网站可能造成灾难性后果的错误的好例子:
def process_cart():
# get cart from database
cart = get_cart()
card = cart.get_card()
process_payment(card, cart)
ship_order(cart)
你可能遇到的问题是,即使process_payment失败,也会调用ship_order,这意味着你发货了但没有收到付款,损失了金钱。
看看这段代码,如果你在process_payment抛出错误而你甚至没有到达ship_order,你可能不会有问题。
如果它没有,而是返回一个依赖于支付结果的数值代码,会怎样?
def process_payment(card, cart):
if card.is_valid():
if card.has_funds(cart.total):
charge_card(card, cart.total)
return 0
else:
return 1
如果你没有读取错误代码,你可能不会注意到支付失败,订单已经发货。那么,我们的方法是什么?
1. 识别问题
首先,我们需要识别问题;在这种情况下,你可以做两件事,即:
-
阅读代码:我们的代码看起来是什么样子?是否抛出错误,返回一个代码,或者完全是其他东西?
-
检查测试:我们是否有覆盖这个场景的测试?如果没有,我们需要编写一个。这个测试应该显示我们有一个错误。
让我们使用 Copilot 来完成这个任务,使用如下提示(这里,我们模拟process_payment返回错误代码而不是抛出错误)。在主窗口中选择ship_order代码,然后在聊天窗口中输入以下提示:
[提示]
为代码编写一个测试;如果process_payment返回 1,则不应调用ship_order。
[提示结束]
我们正在使用 GitHub Copilot 聊天功能,并在我们的文本编辑器中选择process_cart代码。
你应该看到类似于以下代码的响应:
[提示响应]
```python
import unittest
from unittest.mock import Mock, patch
from your_module import process_cart # 替换为实际的模块名称
class TestProcessCart(unittest.TestCase):
@patch('your_module.get_cart') # 替换为实际的模块名称
@patch('your_module.process_payment')
@patch('your_module.ship_order')
def test_order_not_shipped_if_payment_fails(self, mock_ship_order, mock_process_payment, mock_get_cart):
# 安排
mock_cart = Mock()
mock_cart.get_card.return_value = 'test_card'
mock_get_cart.return_value = mock_cart
mock_process_payment.return_value = 1
# 行动
process_cart()
# 断言
mock_ship_order.assert_not_called()
if __name__ == '__main__':
unittest.main()
```py
[响应结束]
上述代码包含一个测试,测试如果从process_payment得到错误代码 1 会发生什么。我们希望发生的事情是ship_order不被调用。
这个测试将失败,因为我们的代码中有一个错误,这是好事,因为我们已经识别出一个错误,并且可以通过测试证明它的存在。
2. 实施更改
现在我们有一个显示我们存在错误的测试,我们可以实施更改。我们可以自信地做出代码更改,因为我们有一个显示我们存在错误的测试,并且我们希望测试覆盖率也得到了足够的提高。
除非你有良好的测试覆盖率,或者变化很小,你确信不会引入错误,否则不要开始重构。
我们现在如何改进代码?简单——我们可以使用 GitHub Copilot 帮助我们实现。让我们使用以下提示来帮助我们进行所需的代码更改:
[提示]
实施更改;如果process_payment返回 1,则不应调用ship_order。
[提示结束]
你为process_cart编写的代码现在应该看起来像这样:
[快速响应]
```python
def process_cart():
# 从数据库获取购物车
cart = get_cart()
card = cart.get_card()
payment_status = process_payment(card, cart)
# 只有在支付成功时才发货
if payment_status != 1:
ship_order(cart)
```py
[响应结束]
上述代码是一个正确的步骤,因为它现在包含了一个检查支付状态的if语句。
为了进一步改进代码,我们可以使用 Copilot 帮助我们使代码更健壮、更易读。让我们使用以下提示进行额外的改进:
[提示]
重构代码,使其更容易维护。
[提示结束]
这可能会给你以下代码:
[提示响应]
def get_payment_status(cart):
card = cart.get_card()
return process_payment(card, cart)
def process_cart():
# get cart from database
cart = get_cart()
payment_status = get_payment_status(cart)
# Only ship the order if the payment was successful
if payment_status != 1:
ship_order(cart)
[响应结束]
在这里,Copilot 建议我们将支付状态提取到一个单独的函数中。这是一个好建议,因为它使代码更容易阅读和维护。
添加新功能
在这个例子中,我们将查看现有的代码库并添加一个新功能。代码库类似于我们之前使用的例子,但它更复杂一些。这是因为它涉及更多涉及不同类型支付方式的函数。以下是代码:
```python
import re
def validate_card(card):
# 信用卡以 4 开头,有 13 或 16 位数字。
# 信用卡以 51 至 55 开头,有 16 位数字。
visa_pattern = r'⁴[0-9]{12}(?:[0-9]{3})?$'
mastercard_pattern = r'⁵[1-5][0-9]{14}$'
if re.match(visa_pattern, card):
return 'Visa'
elif re.match(mastercard_pattern, card):
return 'MasterCard'
else:
return None
def process_payment(card, cart):
card_type = validate_card(card)
if card_type is None:
return 1 # 无效卡
else:
# 在这里处理支付
# 如果支付成功,则返回 0,否则返回 1
pass
def process_cart():
# 从数据库获取购物车
cart = get_cart()
card = cart.get_card()
payment_status = process_payment(card, cart)
# 只有在支付成功时才发货
if payment_status == 0:
ship_order(cart)
```py
在上述代码中,我们有以下内容:
-
validate_card验证卡片号码,如果有效则返回卡片类型;否则返回None。 -
process_payment处理支付,如果支付成功则返回0;否则返回1。 -
process_cart处理购物车,获取卡片,处理支付,如果支付成功,则发货。
1. 识别问题并找到需要更改的函数/函数
我们的任务是使我们的代码也支持美国运通卡。实现新的卡片意味着我们需要更新 validate_card 函数以支持美国运通卡。
现在,我们知道哪些函数需要更改,但 GitHub Copilot 知道吗?
让我们用提示来询问 GitHub Copilot。
在识别步骤中,我们利用我们的专业知识来识别需要更改的内容和位置。一个好的做法是依靠你自己的技能和 Copilot 工具的组合来帮助分析和识别问题。
2. 实施更改,并添加新功能和测试
让我们使用 GitHub Copilot 来帮助我们。我们可以使用以下提示:
[提示]
添加对美国运通卡的支持。
[提示结束]
你将得到类似以下的代码:
[快速响应]
import re
def validate_card(card):
# Visa cards start with 4 and have 13 or 16 digits.
# MasterCard cards start with 51 through 55 and have 16 digits.
# American Express cards start with 34 or 37 and have 15 digits.
visa_pattern = r'⁴[0-9]{12}(?:[0-9]{3})?$'
mastercard_pattern = r'⁵[1-5][0-9]{14}$'
amex_pattern = r'³[47][0-9]{13}$'
if re.match(visa_pattern, card):
return 'Visa'
elif re.match(mastercard_pattern, card):
return 'MasterCard'
elif re.match(amex_pattern, card):
return 'American Express'
else:
return None
[响应结束]
在上述代码中,Copilot 做了以下更改:
-
在
validate_card函数中创建了一个新的正则表达式以支持美国运通卡。 -
添加了一个新的
elif语句,如果卡片与新的正则表达式匹配,则返回美国运通。
当然,我们还没有完成;我们需要添加一个测试来验证代码是否按预期工作。让我们使用以下提示:
[提示]
编写一个测试用例;validate_card 应该对有效卡返回“美国运通”。
[提示结束]
你的测试应该看起来像下面这样:
[提示响应]
```python
import unittest
from your_module import validate_card # 用实际的模块名替换
class TestValidateCard(unittest.TestCase):
def test_validate_card_returns_amex_for_valid_amex_card(self):
# 安排
valid_amex_card = '378282246310005'
# 行动
result = validate_card(valid_amex_card)
# 断言
self.assertEqual(result, '美国运通')
if __name__ == '__main__':
unittest.main()
```py
[响应结束]
新的测试用例 test_validate_card_returns_amex_for_valid_amex_card 由 Copilot 添加,并测试 validate_card 函数对有效的美国运通卡返回 American Express。
在这一点上,继续迭代是个好主意;你不仅想测试代码对有效卡片的处理,还要测试无效卡片等。
你可以用 Copilot 作为挑战来编写更多的测试。
提高性能
提高性能是一个常见的任务,但可能很难实现。通常,你需要特定的工具来衡量性能,并且你需要知道要衡量什么。了解如何以这种方式改进你的代码的一个好方法是使用装饰器来 分析 方法执行所需的时间。然后,你可以衡量特定的方法,找出哪些方法需要改进。
这里是一个在睡眠 2 秒的函数上使用装饰器的例子:
import time
def profile(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__} took {end - start} seconds')
return result
return wrapper
@profile
def sleep_two_seconds():
time.sleep(2)
sleep_two_seconds()
profile 函数是一个装饰器,它接受一个函数作为输入,并返回一个包装输入函数的函数。wrapper 函数测量输入函数的执行时间,并打印结果。
如果你通过终端运行此代码,你会看到如下类似的输出:
sleep_two_seconds took 2.000000238418579 seconds
好的,那么我们如何利用这个来提高性能呢?让我们使用 GitHub Copilot,看看我们如何获得关于改进代码性能的建议。
这是我们要改进的代码。它处理记录并查找重复项:
def process_records(records):
for i in range(len(records)):
for j in range(len(records)):
if i != j and records[i] == records[j]:
print(f'Duplicate record found: {records[i]}')
大多数看到这个的开发者会回想起他们的算法和数据结构书籍,并开始思考像 大 O 符号 这样的事情,这是一种衡量算法性能的方法。主要关注的是减少昂贵的操作次数以提高性能,同时保持相同的功能。
所以,我们应该向 GitHub Copilot 提出的问题是如何改进这段代码?让我们使用以下提示:
[提示]
提高代码的性能。
[提示结束]
我们得到如下反馈:
[快速响应]
def process_records(records):
record_set = set()
for record in records:
if record in record_set:
print(f'Duplicate record found: {record}')
else:
record_set.add(record)
[响应结束]
那么,为什么这段代码更好呢?让我们来证明它:
-
首先,通过计算原始代码和新代码的大 O 符号。
-
第二,通过使用我们之前创建的装饰器。
大 O 符号计算
尝试这个提示:
[提示]
这段代码的大 O 复杂度是多少?也请展示你是如何计算它的。
[提示结束]
你应该得到如下反馈:
[快速响应]
提供的代码的时间复杂度是 O(n²),其中 n 是记录数。
这是它的计算方式:
-
外循环运行 n 次,其中 n 是记录数。这是因为它遍历列表中的每个记录。
-
在外循环内部,还有一个内循环,由于同样的原因,它也会运行 n 次。
-
由于内循环在每次外循环迭代时都会运行 n 次,所以总的迭代次数是 n * n,即 n²。
因此,时间复杂度是 O(n²)。
这意味着如果你将记录数加倍,处理这些记录所需的时间将增加大约四倍。这是二次时间复杂度的特征。
[响应结束]
好的,O(n²) 不是很好,但新代码怎么样呢?
在新代码上运行相同的提示:
[快速响应]
提供的代码的时间复杂度是 O(n),其中 n 是记录数。
这是它的计算方式:
-
循环运行 n 次,其中 n 是记录的数量。这是因为它遍历列表中的每个记录。
-
在循环内部,操作(检查记录是否在集合中以及可能将其添加到集合中)是常数时间,O(1),因为在 Python 中,集合操作通常平均是常数时间。
因此,时间复杂度是 O(n) ** O(1)*,这简化为 O(n)。
这意味着如果你将记录的数量加倍,处理这些记录所需的时间也将加倍,这是线性时间复杂度的特征。
[响应结束]
O(n) 比 O(n²) 更好,但好多少?让我们在下一节中衡量这一点。
测量性能
好的,所以从算法上讲,我们知道新代码更好,但好多少呢?让我们通过使用装饰器和调用代码来衡量。让我们使用 1,000 条记录和 10,000 条记录的输入,并比较执行代码所需的时间。
好的,让我们看看新代码是否比旧代码更快。让我们用 10,000 条记录来尝试:
# old code
@profile
def process_records(records):
for i in range(len(records)):
for j in range(len(records)):
if i != j and records[i] == records[j]:
print(f'Duplicate record found: {records[i]}')
records_10000 = [i for i in range(10000)]
process_records(records_10000)
运行这段代码,你应该看到以下输出:
process_records took 5.193912506103516 seconds
现在,让我们运行新的代码:
# new code
@profile
def process_records(records):
record_set = set()
for record in records:
if record in record_set:
print(f'Duplicate record found: {record}')
else:
record_set.add(record)
records_10000 = [i for i in range(10000)]
process_records(records_10000)
运行这段代码,你应该看到以下输出:
process_records took 0.0011200904846191406 seconds
如你所见,通过结合你的知识和 GitHub Copilot,你可以改进你的代码。
你的代码并不总是这么明显,你可能需要做更多的工作来提高性能。建议你使用性能分析器来测量性能,然后使用 GitHub Copilot 来帮助你改进代码。
提高可维护性
另一个有趣的使用案例是使用 GitHub Copilot 来帮助你提高代码的可维护性。那么,你可以做些什么来提高代码的可维护性呢?以下是一个列表:
-
改进命名变量、函数、类等。
-
分离关注点:例如,将业务逻辑与展示逻辑分开。
-
移除重复:特别是在大型代码库中,你很可能会发现重复。
-
提高可读性:例如,你可以通过使用注释、文档字符串、事件测试等方式来提高可读性。
让我们从代码库开始,看看我们如何可以改进它。以下是代码:
def calculate_total(cart, discounts):
# Define discount functions
def three_for_two(items):
total = 0
for item in items:
total += item.price * (item.quantity - item.quantity // 3)
return total
def christmas_discount(items):
total = 0
for item in items:
total += item.price * item.quantity * 0.5
return total
def cheapest_free(items):
items_sorted = sorted(items, key=lambda x: x.price)
total = 0
for item in items_sorted[1:]:
total += item.price * item.quantity
return total
# Calculate totals for each discount
totals = []
for discount in discounts:
if discount == '3 for 2':
totals.append((discount, three_for_two(cart)))
elif discount == 'Christmas Discount':
totals.append((discount, christmas_discount(cart)))
elif discount == 'Cheapest Free':
totals.append((discount, cheapest_free(cart)))
# Sort totals and return the lowest one
totals_sorted = sorted(totals, key=lambda x: x[1])
return totals_sorted[0][1] if totals_sorted else sum(item.price * item.quantity for item in cart)
# Define discounts
discounts = ['3 for 2', 'Christmas Discount', 'Cheapest Free']
# Define cart
cart = [
{'name': 'item1', 'price': 10, 'quantity': 3},
{'name': 'item2', 'price': 20, 'quantity': 1},
{'name': 'item3', 'price': 30, 'quantity': 1}
]
# Calculate total
total = calculate_total(cart, discounts)
print(f'Total: {total}')
上述代码库在高级别上描述了以下内容:
-
一个函数,
calculate_total,用于计算购物车和一系列折扣的总计 -
一系列折扣
-
一个购物车
从这个代码库中,你可以看到它不太容易维护,因为它很难理解发生了什么。
1. 识别问题。你看到了哪些问题?
让我们先问问 GitHub Copilot 对这段代码的看法,并决定下一步要做什么。让我们使用以下提示:
[提示]
你对这段代码的看法是什么?你会怎么做来改进它?
[提示结束]
你会收到一个包含项目符号列表和改进代码的详细响应。让我们先看看项目符号列表:
[快速响应]
-
使用类来表示商品和折扣:目前,商品和折扣分别用字典和字符串表示。使用类来表示这些实体会使代码更易读和灵活。
-
分离折扣函数:当前的折扣函数定义在
calculate_total函数内部。最好将这些函数单独定义,以提高可读性并使代码更容易测试和维护。 -
使用更可扩展的折扣应用方法:当前应用折扣的方法涉及一系列
if-elif语句。通过将折扣函数存储在字典或Discount对象列表中,然后遍历该集合来应用折扣,可以使这种方法更具可扩展性。
[回复结束]
上述识别出的问题都似乎是合理的——使用类、分离函数和使用字典。
2. 添加测试并降低风险
在我们查看建议的代码更改之前,我们需要最小化引入错误的风险。我们首先应该做的事情是检查我们的测试覆盖率。在这种情况下,我们没有测试,所以我们需要编写一些。让我们使用以下提示:
[提示]
为此代码编写测试,覆盖所有可能的执行路径。
[提示结束]
[提示回复]
import unittest
class TestCalculateTotal(unittest.TestCase):
def test_no_discounts(self):
cart = [
{'name': 'item1', 'price': 10, 'quantity': 3},
{'name': 'item2', 'price': 20, 'quantity': 1},
{'name': 'item3', 'price': 30, 'quantity': 1}
]
discounts = []
total = calculate_total(cart, discounts)
self.assertEqual(total, 80)
def test_three_for_two_discount(self):
cart = [
{'name': 'item1', 'price': 10, 'quantity': 3},
{'name': 'item2', 'price': 20, 'quantity': 1},
{'name': 'item3', 'price': 30, 'quantity': 1}
]
discounts = ['3 for 2']
total = calculate_total(cart, discounts)
self.assertEqual(total, 70)
def test_christmas_discount(self):
cart = [
{'name': 'item1', 'price': 10, 'quantity': 3},
{'name': 'item2', 'price': 20, 'quantity': 1},
{'name': 'item3', 'price': 30, 'quantity': 1}
]
discounts = ['Christmas Discount']
total = calculate_total(cart, discounts)
self.assertEqual(total, 40)
def test_cheapest_free_discount(self):
cart = [
{'name': 'item1', 'price': 10, 'quantity': 3},
{'name': 'item2', 'price': 20, 'quantity': 1},
{'name': 'item3', 'price': 30, 'quantity': 1}
]
discounts = ['Cheapest Free']
total = calculate_total(cart, discounts)
self.assertEqual(total, 70)
def test_multiple_discounts(self):
cart = [
{'name': 'item1', 'price': 10, 'quantity': 3},
{'name': 'item2', 'price': 20, 'quantity': 1},
{'name': 'item3', 'price': 30, 'quantity': 1}
]
discounts = ['3 for 2', 'Christmas Discount', 'Cheapest Free']
total = calculate_total(cart, discounts)
self.assertEqual(total, 40)
if __name__ == '__main__':
unittest.main()
[回复结束]
现在,你永远不应该相信 AI 生成的结果,所以请花时间审查代码,运行测试覆盖率工具,确保你在进行下一步之前对代码感到满意,下一步是改进代码。
为了改进 Copilot 的第一个建议的代码,你想要确保它能够运行,并在添加商品到购物车等操作中使用Item类。以下是这些修改的结果:
import unittest
from discount_old import calculate_total, Item
from item import Item
class TestCalculateTotal(unittest.TestCase):
def test_no_discounts(self):
cart = [
Item('item1', 10, 3),
Item('item2', 20, 1),
Item('item3', 30, 1)
]
discounts = []
total = calculate_total(cart, discounts)
self.assertEqual(total, 80)
def test_three_for_two_discount(self):
cart = [
Item('item1', 10, 3),
Item('item2', 20, 1),
Item('item3', 30, 1)
]
discounts = ['3 for 2']
total = calculate_total(cart, discounts)
self.assertEqual(total, 70)
def test_christmas_discount(self):
cart = [
Item('item1', 10, 3),
Item('item2', 20, 1),
Item('item3', 30, 1)
]
discounts = ['Christmas Discount']
total = calculate_total(cart, discounts)
self.assertEqual(total, 40)
def test_cheapest_free_discount(self):
cart = [
Item('item1', 10, 3), #30
Item('item2', 20, 1), # 20
Item('item3', 30, 1) # 30
]
discounts = ['Cheapest Free']
total = calculate_total(cart, discounts)
self.assertEqual(total, 60)
def test_multiple_discounts(self):
cart = [
Item('item1', 10, 3),
Item('item2', 20, 1),
Item('item3', 30, 1)
]
discounts = ['3 for 2', 'Christmas Discount', 'Cheapest Free']
total = calculate_total(cart, discounts)
self.assertEqual(total, 40)
if __name__ == '__main__':
unittest.main()
对于应用程序代码:
from item import Item
def calculate_total(cart, discounts):
# Define discount functions
def three_for_two(items):
total = 0
for item in items:
total += item.price * (item.quantity - item.quantity // 3)
return total
def christmas_discount(items):
total = 0
for item in items:
total += item.price * item.quantity * 0.5
return total
def cheapest_free(items):
if not items:
return 0
items_sorted = sorted(items, key=lambda x: x.price * x.quantity)
cheapest_item = items_sorted[0]
total = 0
for item in items:
total += item.price * item.quantity
total -= cheapest_item.price * cheapest_item.quantity if cheapest_item.quantity > 0 else 0
return total
# Calculate totals for each discount
totals = []
for discount in discounts:
if discount == '3 for 2':
totals.append((discount, three_for_two(cart)))
elif discount == 'Christmas Discount':
totals.append((discount, christmas_discount(cart)))
elif discount == 'Cheapest Free':
totals.append((discount, cheapest_free(cart)))
# Sort totals and return the lowest one
totals_sorted = sorted(totals, key=lambda x: x[1])
return totals_sorted[0][1] if totals_sorted else sum(item.price * item.quantity for item in cart)
# if run as script,
if __name__ == '__main__':
# Define discounts
discounts = ['3 for 2', 'Christmas Discount', 'Cheapest Free']
# Define cart
cart = [
Item('item1', 10, 3),
Item('item2', 20, 1),
Item('item3', 30, 1)
]
# Calculate total
total = calculate_total(cart, discounts)
print(f'Total: {total}')
好的,现在我们有一个我们满意的代码库,并且通过了测试。让我们现在看看 GitHub Copilot 建议的代码更改。
3. 实施更改并提高可维护性
好的,我们对我们有良好的测试覆盖率感到自信,我们准备改进代码。让我们使用以下提示:
[提示]
提高代码的可维护性。
[提示结束]
你应该得到类似以下的结果:
[提示回复]
为了提高代码的可维护性,我们可以进行以下更改:
-
将折扣函数从
calculate_total中移出,使其成为独立的函数。这提高了可读性,并使单独测试这些函数更容易。 -
使用字典将折扣名称映射到相应的函数。这消除了在应用折扣时需要多个
if/elif语句的需求。
from item import Item
# Define discount functions
def three_for_two(items):
total = 0
for item in items:
total += item.price * (item.quantity - item.quantity // 3)
return total
def christmas_discount(items):
total = 0
for item in items:
total += item.price * item.quantity * 0.5
return total
def cheapest_free(items):
if not items:
return 0
items_sorted = sorted(items, key=lambda x: x.price * x.quantity)
cheapest_item = items_sorted[0]
total = 0
for item in items:
total += item.price * item.quantity
total -= cheapest_item.price * cheapest_item.quantity if cheapest_item.quantity > 0 else 0
return total
def calculate_total(cart, discounts):
# Map discount names to functions
discount_functions = {
'3 for 2': three_for_two,
'Christmas Discount': christmas_discount,
'Cheapest Free': cheapest_free
}
# Calculate totals for each discount
totals = []
for discount in discounts:
if discount in discount_functions:
totals.append((discount, discount_functionsdiscount))
# Sort totals and return the lowest one
totals_sorted = sorted(totals, key=lambda x: x[1])
return totals_sorted[0][1] if totals_sorted else sum(item.price * item.quantity for item in cart)
# if run as script,
if __name__ == '__main__':
# Define discounts
discounts = ['3 for 2', 'Christmas Discount', 'Cheapest Free']
# Define cart
cart = [
Item('item1', 10, 3),
Item('item2', 20, 1),
Item('item3', 30, 1)
]
# Calculate total
total = calculate_total(cart, discounts)
print(f'Total: {total}')
[回复结束]
通过引入以下更改,代码现在更容易维护:
-
calculate_total函数变得大大缩小,其中它曾经包含的每个函数都已移出 -
提到的相同方法现在也使用字典将名称映射到函数
有效使用 Copilot 的关键是持续迭代,持续进行小的改动,并查看 Copilot 提出的建议。稍微改变提示,看看会发生什么,并选择最适合您需求的输出。
挑战
尝试使用提示和 GitHub Copilot 进一步改进代码。如果支付出现问题,应该怎么办?您应该记录它,也许抛出一个错误等?
更新现有的电子商务网站
让我们继续改进我们在前几章中介绍的电子商务网站。在本章中,我们将专注于改进代码库和添加新功能。
为了参考,让我们展示我们开始创建的 basket.html 文件:
<!-- a page showing a list of items in a basket, each item should have title, price, quantity, sum and buttons to increase or decrease quantity and the page should have a link to "checkout" at the bottom -->
<html>
<head>
<title>Basket</title>
<link rel="stylesheet" href="css/basket.css">
<!-- add bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<!-- add 3 basket items with each item having id, name, price, quantity, use card css class -->
<!--
<div class="container">
<div id="basket" class="basket">
</div>
</div> -->
<!-- add app.js -->
<!-- add app.js, type javascript -->
<div id="basket" class="basket">
<!-- render basket from Vue app, use Boostrap -->
<div v-for="(item, index) in basket" class="basket-item">
<div class="basket-item-text">
<h2>{{ item.name }}</h2>
<p>Price: {{ item.price }}</p>
<p>Quantity: {{ item.quantity }}</p>
<p>Sum: {{ item.price * item.quantity }}</p>
</div>
<div class="basket-item-buttons">
<button type="submit" class="btn btn-primary btn-block btn-large" @click="increaseQuantity(index)">+</button>
<button type="submit" class="btn btn-primary btn-block btn-large" @click="decreaseQuantity(index)">-</button>
</div>
</div>
</div>
<!-- add Vue 3 -->
<script src="img/vue@next"></script>
<!-- add boostrap code for Vue 3 -->
<script>
let app = Vue.createApp({
data() {
return {
basket: [
{
id: 1,
name: 'Product 1',
price: 100,
quantity: 1
},
{
id: 2,
name: 'Product 2',
price: 200,
quantity: 2
},
{
id: 3,
name: 'Product 3',
price: 300,
quantity: 3
},
{
id: 4,
name: 'Product 4',
price: 400,
quantity: 4
}
]
}
},
methods: {
increaseQuantity(index) {
this.basket[index].quantity++;
},
decreaseQuantity(index) {
if (this.basket[index].quantity > 0) {
this.basket[index].quantity--;
}
}
},
computed: {
total() {
let total = 0;
for (let i = 0; i < this.basket.length; i++) {
total += this.basket[i].price * this.basket[i].quantity;
}
return total;
}
}
});
app.mount('#basket');
</script>
<!-- <script src="img/app.js"></script> -->
</body>
这是一个原型早期版本;它不是指用于生产的代码。然而,我们可以使用此代码来改进它并使其适用于生产。
现在我们知道这段代码并不好,让我们使用 Copilot 和以下提示来改进它:
[提示]
改进代码,使其适用于生产环境。
[提示结束]
这个提示突出了代码中的一些问题,特别是:
[提示响应]
-
将 JavaScript 和 CSS 分离到外部文件:这使代码更易于维护,并允许缓存这些资源。
-
使用构建工具或模块打包器:像 Webpack 或 Parcel 这样的工具可以帮助您管理依赖项,将现代 JavaScript 转译为旧版本以获得更好的浏览器兼容性,并压缩您的代码以用于生产。
-
错误处理:向您的函数添加错误处理以确保您的应用程序可以优雅地处理意外情况。
-
Vue 组件结构:不要在单个脚本中创建整个
Vue 应用程序,而是将其分解成可重用的组件。 -
可访问性:通过使用适当的 ARIA 角色和属性确保您的 HTML 可访问。
[响应结束]
那是一个我们可以询问的很好的列表,但让我们看看它建议的代码更改。它建议将代码分成两个不同的文件,一个用于 HTML,另一个用于 JavaScript:
<!-- index.html -->
<html>
<head>
<title>Basket</title>
<link rel="stylesheet" href="css/basket.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div id="basket" class="basket"></div>
<script src="img/vue@next"></script>
<script src="img/app.js"></script>
</body>
</html>
现在,让我们看看 JavaScript 是如何得到改进的:
// app.js
let app = Vue.createApp({
data() {
return {
basket: [
{ id: 1, name: 'Product 1', price: 100, quantity: 1 },
{ id: 2, name: 'Product 2', price: 200, quantity: 2 },
{ id: 3, name: 'Product 3', price: 300, quantity: 3 },
{ id: 4, name: 'Product 4', price: 400, quantity: 4 }
]
}
},
methods: {
increaseQuantity(index) {
this.basket[index].quantity++;
},
decreaseQuantity(index) {
if (this.basket[index].quantity > 0) {
this.basket[index].quantity--;
}
}
},
computed: {
total() {
return this.basket.reduce((total, item) => total + item.price * item.quantity, 0);
}
}
});
app.mount('#basket');
这是一个巨大的进步。作为一名开发者,您可以看到 JavaScript 代码使用了静态数据,所以让我们用以下提示来改变它:
[提示]
改进代码,通过调用 API 使用动态数据,并生成一个具有路由 /api/basket 的单独 Python API,其中包含购物车数据。
[提示结束]
现在,我们有两个文件,一个是 API,另一个是 JavaScript 代码。让我们先看看 API:
[提示响应]
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/basket')
def get_basket():
basket = [
{ 'id': 1, 'name': 'Product 1', 'price': 100, 'quantity': 1 },
{ 'id': 2, 'name': 'Product 2', 'price': 200, 'quantity': 2 },
{ 'id': 3, 'name': 'Product 3', 'price': 300, 'quantity': 3 },
{ 'id': 4, 'name': 'Product 4', 'price': 400, 'quantity': 4 }
]
return jsonify(basket)
if __name__ == '__main__':
app.run(debug=True)
在您上面得到一个返回购物车数据的路由 /api/basket。让我们看看改进后的 JavaScript 代码:
let app = Vue.createApp({
data() {
return {
basket: []
}
},
methods: {
increaseQuantity(index) {
this.basket[index].quantity++;
},
decreaseQuantity(index) {
if (this.basket[index].quantity > 0) {
this.basket[index].quantity--;
}
}
},
computed: {
total() {
return this.basket.reduce((total, item) => total + item.price * item.quantity, 0);
}
},
mounted() {
fetch('/api/basket')
.then(response => response.json())
.then(data => {
this.basket = data;
});
}
});
app.mount('#basket');
[响应结束]
太好了!我们现在从 API 获取数据。我们如何改进代码?如果您之前构建过 Vue 应用程序,您将知道它涉及到使用 Vue CLI 并有一个构建步骤。让我们用以下提示询问 GitHub Copilot:
[提示]
改进代码,使用 Vue CLI,并添加一个构建步骤。
[提示结束]
现在,我们以如下方式将命令和文件更改作为响应返回:
[提示响应]
在您的机器上全局安装Vue CLI:
npm install -g @vue/cli
创建一个新的Vue项目
vue create my-project
cd my-project
现在,让我们创建一个新的Vue组件用于篮子。在src/components目录中创建一个新的文件Basket.vue:
<!-- src/components/Basket.vue -->
<template>
<div id="basket">
<!-- Your basket template goes here -->
</div>
</template>
<script>
export default {
data() {
return {
basket: []
};
},
methods: {
increaseQuantity(index) {
this.basket[index].quantity++;
},
decreaseQuantity(index) {
if (this.basket[index].quantity > 0) {
this.basket[index].quantity--;
}
}
},
computed: {
total() {
return this.basket.reduce((total, item) => total + item.price * item.quantity, 0);
}
},
mounted() {
fetch('/api/basket')
.then(response => response.json())
.then(data => {
this.basket = data;
});
}
};
</script>
<style scoped>
/* Your CSS goes here */
</style>
然后,在主App.vue中导入并使用此组件:
<!-- src/App.vue -->
<template>
<div id="app">
<Basket />
</div>
</template>
<script>
import Basket from './components/Basket.vue';
export default {
name: 'App',
components: {
Basket
}
};
</script>
<style>
/* Your CSS goes here */
</style>
现在,您可以使用Vue CLI 的构建命令来创建一个生产就绪版本的应用程序:
npm run build
这将创建一个包含应用程序生产就绪版本的dist目录。
[响应结束]
这看起来像是正确的步骤。然而,您应该始终测试代码以确保它按预期工作。
作为挑战,看看上述代码是否工作,以及您是否可以进一步改进它。
代码可能在框架的不同版本之间发生变化。在这种情况下,我们使用 Vue.js,所以请确保查阅您使用的版本的文档。
作业
找到您想要改进的代码片段。按照本章概述的过程进行:
-
识别问题。您看到了什么问题?
-
添加测试,降低变更风险。
-
实施更改并提高可维护性。
如果您没有想要改进的代码片段,请尝试使用本章中的代码或 Kata(Gilded Rose)GitHub 页面上的代码:github.com/emilybache/GildedRose-Refactoring-Kata。
知识检查
- 绿色田野开发和棕色田野开发有什么区别?
A:绿色田野开发是指从头开始编码;棕色田野开发是指更新现有代码。
- 更新现有代码的最佳方式是什么?
A:最好的方法是进行小改动,并确保有足够的测试。
摘要
在本章中,我们确立了编写代码的一个重要方面是更新现有代码,这被称为棕色田野开发。我们还探讨了 GitHub Copilot 如何帮助您完成这项任务。
从本章中可以得出的最重要的信息是确保您有一个更新代码的方法,以降低即将进行的变更风险。多次进行小改动比一次性进行大改动要好。在开始更改代码之前,强烈建议您有足够的测试。
加入我们的 Discord 社区
加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

第十一章:使用 ChatGPT 进行数据探索
简介
数据探索是机器学习的一个基本第一步,包括对数据集进行彻底检查,以识别其结构并揭示初始模式和异常。这个过程对于为任何进一步的详细统计分析以及机器学习模型的开发奠定基础至关重要。
在本章中,重点是阐述数据探索的过程,旨在巩固机器学习新手的理解,并为熟练者提供复习。本章将指导如何加载和检查由亚马逊书评组成的数据库,总结其特征,并探究其变量。
您将通过对分类数据评估、分布可视化和相关性分析的实际练习进行指导,这些练习得到了 Python 的 pandas 和Matplotlib库的支持。本章还将详细说明如何有效地使用 ChatGPT 进行数据探索,包括免费版本和基于订阅的增值版本,后者提供增强的功能。
需要注意的是,ChatGPT 的响应将取决于您通过提示如何有效地传达您的需求。这种可变性是学习曲线的一部分,并说明了与 AI 在数据探索中的互动性质。我们的目标是让您掌握自信地导航这些工具的知识,并开始做出基于数据的决策。
商业问题
在电子商务中,有效地分析客户反馈对于识别影响购买决策的关键因素至关重要。这种分析支持有针对性的营销策略,并有助于优化用户体验和网站设计,最终提升对客户的服务和产品供应。
问题与数据领域
在本章中,我们将专注于使用亚马逊产品评论数据集进行详细的数据探索。我们的目标是深入探索这个数据集,挖掘见解并发现可以增强决策的规律。我们将利用 ChatGPT 生成用于数据操作和可视化的 Python 代码,提供一种理解复杂数据分析技术的动手方法。此外,我们还将探讨如何有效地提示 ChatGPT 提供定制见解和代码片段,以帮助我们的探索任务。
数据集概述
我们将使用亚马逊产品评论数据集,该数据集包含广泛的信息,反映了消费者反馈和产品评估。该数据集的关键特征包括市场、客户、评论和产品细节等标识符,以及产品标题、类别、评分和评论的文本内容。对于这次探索,我们将专注于review_body和review_headline字段,这些字段提供了丰富的文本数据供分析。为了简化我们的关注点并提高发现结果的清晰度,我们将省略中性情感,仅分析正面和负面反馈。
数据集中的特征包括:
-
marketplace(string): 产品的位置。 -
customer_id(string): 客户的唯一标识符。 -
review_id(string): 评论的唯一标识符。 -
product_id(string): 产品的唯一标识符。 -
product_parent(string): 父产品标识符。 -
product_title(string): 被评论产品的标题。 -
product_category(string): 产品的类别。 -
star_rating(int): 产品在 1 到 5 的评分尺度上的评分。 -
helpful_votes(int): 评论收到的有用票数。 -
total_votes(int): 评论收到的总票数。 -
review_headline(string): 评论的标题。 -
review_body(string): 评论内容。 -
review_date(string): 评论的日期。 -
sentiments(string): 评论的情感(正面或负面)。
这次有针对性的探索将使我们能够进行深入的情感分析,评估产品评分的影响,并深入了解客户反馈动态。通过关注这些要素,我们旨在充分利用数据集,以改善电子商务环境中的战略决策。
特征分解
通过亚马逊产品评论数据集和详细数据探索的焦点,我们将概述以下功能,以指导用户有效地理解和分析客户反馈:
-
加载数据集:我们将首先将数据集导入 pandas DataFrame。这是 Python 中一种强大的数据处理结构,便于方便地处理数据。
-
检查数据:我们的初步探索将涉及显示 DataFrame 的前几条记录,以了解数据。我们将审查列名,了解每列包含的数据类型,并检查需要解决的任何缺失值。
-
摘要统计:为了掌握数值数据的分布,我们将计算包括平均值、中位数、最小值、最大值和四分位数在内的摘要统计。这一步骤有助于理解数值数据的中心趋势和分布。
-
探索分类变量: 对于市场、产品类别和情感等分类数据,我们将检查不同的类别并计算每个类别的条目数量。条形图等视觉辅助工具在这里特别有用,可以说明每个类别的频率。
-
评分分布: 我们将使用直方图或条形图来可视化星级评分的分布。这种视觉表示有助于理解评论者的总体观点以及评分是如何偏斜的。
-
时间序列分析: 通过分析
review_date列,我们将探索数据中的任何趋势、季节性或其他时间模式。这种分析可以揭示情感或产品受欢迎程度随时间变化的情况。 -
评论长度分析: 我们将检查
review_body以了解评论中提供的信息量,通过计算评论长度的描述性统计,如平均值、中位数和最大长度来做到这一点。这一步骤提供了关于客户提供的反馈深度的见解。 -
相关性分析: 最后,我们将使用相关矩阵或散点图来调查星级评分、有帮助的投票和总投票等数值变量之间的相关性。这种分析有助于识别数据不同定量方面的潜在关系。
通过系统地分解这些特征,我们将彻底理解数据集,揭示可以增强电子商务环境中决策制定和策略制定的见解。
提示策略
为了有效地利用 ChatGPT 对亚马逊产品评论数据集进行数据探索,我们需要建立针对生成 Python 代码和数据见解的明确提示策略。以下是我们的方法。
策略 1:任务-操作-指南(TAG)提示策略
1.1 – 任务: 具体目标是使用各种统计和可视化技术彻底探索亚马逊产品评论数据集。
1.2 – 操作: 探索此数据集的关键步骤包括:
-
数据加载: 将数据集加载到 pandas DataFrame 中。
-
数据检查: 检查缺失数据,了解数据类型,并检查前几条记录。
-
统计摘要: 计算数值数据的摘要统计。
-
分类分析: 使用计数和可视化分析分类变量。
-
评分分布: 创建直方图或条形图来可视化星级评分的分布。
-
时间趋势分析: 检查从评论日期中得出的时间趋势。
-
评论文本分析: 分析评论文本的长度和情感。
-
相关性研究: 评估数值变量之间的相关性。
1.3 – 指南: 在我们的提示中,我们将向 ChatGPT 提供以下指南:
-
代码应与 Jupyter Notebook 兼容
-
确保每行代码都有详细的注释。
-
您必须详细解释每一行代码,涵盖代码中使用的每个方法。
策略 2:角色扮演-指令-背景(PIC)提示策略
2.1 – 角色扮演:假设一个数据分析师的角色,试图从亚马逊产品评论数据集中发现可操作的见解。
2.2 – 指令:请求 ChatGPT 为每个特定分析生成代码,按顺序进行,并在移动到下一个任务之前等待用户验证。
2.3 – 背景:鉴于重点是使用亚马逊产品评论数据集进行情感分析,ChatGPT 不了解数据集及其特征,因此需要额外的背景信息。
策略 3:学习-即兴发挥-反馈-评估(LIFE)提示策略
3.1 – 学习:
- 强调通过各种分析技术理解数据的重要性,从基本统计到复杂的相关性和时间序列分析。
3.2 – 即兴发挥:
- 根据初步发现调整分析。例如,如果某些产品类别显示出不寻常的趋势,则在这些领域深化分析。
3.3 – 反馈:
-
分享代码和模型输出以供反馈,以确保有效的学习和理解。
-
结合建议和批评来完善模型和方法。
-
提供错误以排除故障和解决问题。
3.4 – 评估:
- 执行 ChatGPT 提供的代码以确保准确性和有效性。这一点在整章中都有应用。
使用 ChatGPT 免费版对亚马逊评论数据集进行数据探索
ChatGPT 高级版具有代码解释器,但首先,我们将使用 ChatGPT 的免费版。我们将仔细构建初始提示,包括所有功能,但我们将指示它在提供每个功能的代码后等待用户反馈。
特性 1:加载数据集
让我们构建初始提示来加载数据集。
[提示]
我想使用各种统计和可视化技术(TAG 1.1)彻底探索亚马逊产品评论数据集,它包含以下字段(PIC 2.3):
-
marketplace(string): 产品的位置。 -
customer_id(string): 客户的唯一标识符。 -
review_id(string): 评论的唯一标识符。 -
product_id(string): 产品的唯一标识符。 -
product_parent(string): 父产品标识符。 -
product_title(string): 评论产品的标题。 -
product_category(string): 产品的类别。 -
star_rating(int): 产品在 1 到 5 的评分尺度上的评分。 -
helpful_votes(int): 评论收到的有用投票数。 -
total_votes(int): 评论收到的总投票数。 -
review_headline(string): 评论的标题。 -
review_body(string): 评论的内容。 -
review_date(string): 评论的日期。 -
sentiments(string): 评论的情感(正面或负面)。
这应包括以下步骤(TAG 1.2):
-
数据加载:将数据集加载到 pandas DataFrame 中。
-
数据检查:检查缺失数据,了解数据类型,并检查前几条记录。
-
统计摘要:计算数值数据的摘要统计量。
-
分类分析:使用计数和可视化分析分类变量。
-
评分分布:创建直方图或条形图来可视化星级评分的分布。
-
时间趋势:从评论日期中检查时间趋势。
-
评论文本分析:分析评论文本的长度和情感。
-
相关性研究:评估数值变量之间的相关性。
我是一名数据分析师,试图发现可操作的见解(图 2.1),因此请提供详细的代码,并严格遵循以下说明(标签 1.3):
-
代码应与 Jupyter Notebook 兼容。
-
确保每行代码都有详细的注释。
-
您必须解释每一行代码,然后将其详细地复制到笔记本的文本块中,详细说明代码中使用的每个方法,在提供代码之前。请注意,数据集位于 Google Drive 的此路径:
./Chapter05/amazon_product_review_data.csv。因此,添加挂载驱动器和加载数据集的代码(图 2.2)。 -
逐步提供,并等待用户反馈(图 2.2)。
[提示结束]
您应该收到类似以下的响应,其中 ChatGPT 为我们提供了第一步 - 数据加载的代码,以及详细的注释和解释。
[提示响应]
当然,让我们从第一步开始:将数据集从 Google Drive 加载到 pandas DataFrame 中。以下是我们可以如何进行:
第一步:数据加载
要将数据集从 Google Drive 加载到 pandas DataFrame,我们将遵循以下步骤:
-
挂载 Google Drive:连接到包含数据集的 Google Drive。
-
加载数据集:将数据集文件读取到 pandas DataFrame 中。
详细代码解释:
-
挂载 Google Drive:此步骤允许我们从 Google Colab 或 Jupyter Notebook 环境直接访问存储在 Google Drive 中的文件:
-
从
google.colab导入 drive:从google.colab包中导入 drive 模块。 -
drive.mount("/content/drive"): 在指定的路径(在这种情况下为/content/drive)挂载您的 Google Drive。按照说明进行授权并获取访问代码。
-
-
加载数据集:
-
import pandas as pd: 将 pandas 库导入为pd以进行数据处理。 -
file_path = '/content/drive/My Drive/Chapter05/amazon_product_review_data.csv': 定义 Google Drive 中 CSV 文件的路径。 -
df = pd.read_csv(file_path): 使用pd.read_csv()将 CSV 文件读取到名为df的 DataFrame 中。
-
这是完整的代码片段:
# Mount Google Drive to access the dataset
from google.colab import drive
drive.mount('/content/drive')
# Import pandas for data manipulation
import pandas as pd
# Define the file path in your Google Drive
file_path = '/content/drive/My Drive/Chapter05/amazon_product_review_data.csv'
# Load the dataset into a pandas DataFrame
df = pd.read_csv(file_path)
# Display the first few rows of the dataframe to verify the data
df.head()
用户反馈:
请在您的 Jupyter Notebook 环境中执行此代码,在将 file_path 替换为您的数据集的正确路径后。如果您遇到任何问题或准备好进行下一步,请告诉我!
[响应结束]
我们现在将应用评估提示策略(LIFE 3.4),并通过在 Google Colab 的每一步执行代码来验证代码。该代码从 Google Drive 加载数据集。它还将请求访问 Google Drive 的权限。
请参考“使用 ChatGPT 进行数据探索.ipynb”笔记本中的可执行代码片段,或从提示响应中复制代码。
你应该看到一条消息说“允许此笔记本访问 Google Drive”。
代码运行良好,没有应用任何修改或错误,并显示了数据集的前几行。
你应该看到笔记本内容和客户评价。
功能 2:检查数据
我们现在将指示 ChatGPT 进行下一步:
[提示]
让我们继续下一步。
[提示结束]
我们应该收到如下所示的响应:
[提示响应]
太好了!让我们继续下一步:
第 2 步:数据检查
在这一步,我们将检查加载的数据集以:
-
检查缺失数据。
-
理解每列的数据类型。
-
检查前几条记录以了解数据结构。
详细代码解释:
-
检查缺失数据:
df.info():此方法提供了 DataFrame 的简洁摘要,包括每列的非空条目数。它还显示了每列的数据类型,这有助于我们了解如何操作数据。
-
理解数据类型:
- 通过检查
df.info()的输出,我们可以确定是否有任何列的数据类型不正确(例如,数值列错误地存储为字符串)。
- 通过检查
-
检查前几条记录:
- df.head():此方法显示 DataFrame 的前几行。它允许我们直观地检查列、它们的值,并初步了解数据集包含的内容。
这里是数据检查的代码:
# Check for missing data and data types
df.info()
# Display the first few rows of the dataframe
df.head()
用户反馈:
在你的 Jupyter Notebook 环境中执行此代码。运行此步骤后,你应该能够看到数据集的摘要和前几行。如果一切看起来正常,或者你有任何具体问题,请告诉我!
[响应结束]
我们评估了 ChatGPT 的输出,并在笔记本中执行了代码(LIFE 3.4),并显示了相关信息。
我们将利用提示策略为 ChatGPT(LIFE 3.3)提供输出以进行解释:
[提示]
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 500 entries, 0 to 499
Data columns (total 15 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 market_place 500 non-null object
1 customer_id 500 non-null object
2 review_id 500 non-null object
3 product_id 500 non-null object
4 product_parent 500 non-null object
5 product_title 500 non-null object
6 product_category 500 non-null object
7 star_rating 500 non-null int64
8 helpful_votes 500 non-null int64
9 total_votes 500 non-null int64
10 verified_purchase 500 non-null object
11 review_headline 500 non-null object
12 review_body 500 non-null object
13 review_date 500 non-null object
14 sentiments 500 non-null object
dtypes: int64(3), object(12)
memory usage: 58.7+ KB
market_place customer_id review_id product_id product_parent product_title product_category star_rating helpful_votes total_votes verified_purchase review_headline review_body review_date sentiments
0 "US" "42521656" "R26MV8D0KG6QI6" "B000SAQCWC" "159713740" "The Cravings Place Chocolate Chunk Cookie Mix... "Grocery" 1 0 0 1 \t(Y) "Using these for years - love them." "As a family allergic to wheat, dairy, eggs, n... 2015-08-31 positive
1 "US" "12049833" "R1OF8GP57AQ1A0" "B00509LVIQ" "138680402" "Mauna Loa Macadamias, 11 Ounce Packages" "Grocery" 1 0 0 1 \t(Y) "Wonderful" "My favorite nut. Creamy, crunchy, salty, and ... 2015-08-31 positive
2 "US" "107642" "R3VDC1QB6MC4ZZ" "B00KHXESLC" "252021703" "Organic Matcha Green Tea Powder - 100% Pure M... "Grocery" 1 0 0 0 \t(N) "Five Stars" "This green tea tastes so good! My girlfriend ... 2015-08-31 positive
3 "US" "6042304" "R12FA3DCF8F9ER" "B000F8JIIC" "752728342" "15oz Raspberry Lyons Designer Dessert Syrup S... "Grocery" 1 0 0 1 \t(Y) "Five Stars" "I love Melissa's brand but this is a great se... 2015-08-31 positive
4 "US" "18123821" "RTWHVNV6X4CNJ" "B004ZWR9RQ" "552138758" "Stride Spark Kinetic Fruit Sugar Free Gum, 14... "Grocery" 1 0 0 1 \t(Y) "Five Stars" "good" 2015-08-31 positive
[提示结束]
我们应该收到如下所示的响应,其中 ChatGPT 提供解释和发现:
[Prompt response]
看起来数据集已成功加载,我们可以从df.info()和df.head()的输出中推断出以下内容:
数据检查发现:
-
基本信息:
-
数据集包含 500 个条目(行)和 15 列。
-
数据类型包括数值(
int64)和分类(object)。 -
所有列都有 500 个非空条目,表明没有缺失值。
-
-
列详细信息:
-
market_place、customer_id、review_id、product_id、product_parent、product_title、product_category、verified_purchase、review_headline、review_body、review_date和sentiments都被识别为对象(字符串)类型,这与它们的描述中预期的内容相符。 -
star_rating、helpful_votes和total_votes被正确识别为int64(整数)类型。
-
-
初始数据预览:
df.head()的输出显示了数据集的前几行,让我们可以一窥每个列的结构和内容。我们可以看到market_place、customer_id、star_rating、helpful_votes、total_votes、review_headline、review_body、review_date和sentiments列的示例。
下一步:
根据这次检查:
-
数据集看起来很干净,没有缺失值。
-
我们对列及其数据类型有了清晰的理解。
接下来,我们可以进行第 3 步:统计摘要,以计算数值数据(star_rating、helpful_votes 和 total_votes)的摘要统计信息。你想继续吗?如果有什么具体的内容你想进一步探索,或者我们应该进行下一步,请告诉我!
[响应结束]
特征 3:摘要统计
由于没有缺失或空值,我们现在将指示 ChatGPT 进行下一步以显示摘要统计信息:
[提示]
让我们进行下一步。
[提示结束]
我们应该收到以下类似的响应,其中 ChatGPT 提供显示摘要统计信息的代码:
[提示响应]**
太好了!让我们继续到第 3 步:
第 3 步:统计摘要
在这一步,我们将计算数据集中数值列的摘要统计信息。具体来说,我们将关注:
-
star_rating:产品在 1 到 5 的评分尺度上的评分。 -
helpful_votes:收到的有用投票数。 -
total_votes:收到的总投票数。
详细代码解释:
-
使用
describe():-
df.describe()计算数值列的摘要统计信息。它提供:-
Count: 非空值的数量。
-
Mean: 平均值。
-
Std: 标准差。
-
Min: 最小值。
-
25%,50%,和 75%:百分位数。
-
Max: 最大值。
-
-
-
选择特定列:
- 我们使用
df[['star_rating', 'helpful_votes', 'total_votes']].describe()来限制输出只包含我们感兴趣的数值列(star_rating、helpful_votes和total_votes)。
- 我们使用
这是计算和显示摘要统计信息的代码:
# Calculate summary statistics for numerical columns
summary_stats = df[['star_rating', 'helpful_votes', 'total_votes']].describe()
# Display the summary statistics
print(summary_stats)
用户反馈:
在你的 Jupyter Notebook 环境中执行此代码。它将为你数据集中的数值变量提供关于分布和中心趋势的统计洞察。如果你有任何问题或者你准备好了进行下一步,请告诉我!
[响应结束]
我们执行代码,显示汇总统计数据,并将输出提供给 ChatGPT 进行解释,以便理解统计数据:
[提示]
star_rating helpful_votes total_votes
count 500.000000 500.000000 500.000000
mean 0.796000 0.764000 1.068000
std 0.403373 2.843321 3.698477
min 0.000000 0.000000 0.000000
25% 1.000000 0.000000 0.000000
50% 1.000000 0.000000 0.000000
75% 1.000000 1.000000 1.000000
max 1.000000 36.000000 47.000000
[提示结束]
我们应该收到如下所示的响应,其中 ChatGPT 提供了对汇总统计数据的解释:
[提示响应]
根据数值列(star_rating、helpful_votes和total_votes)的汇总统计数据,以下是我们可以得出的见解:
汇总统计数据:
-
star_rating:
-
平均值:0.796
-
标准差:0.403
-
最小值:0(表示最低评分)
-
第 25 百分位数(Q1):1
-
中位数(第 50 百分位数):1
-
第 75 百分位数(Q3):1
-
最大值:1(表示最高评分)
-
解释:
star_rating列主要包含单个值为 1(在二元情感数据中通常表示正面评论),表明大多数评论是正面的。这由接近 1 的平均值和低标准差得到支持,表明评分的变异性很小。
-
-
helpful_votes:
-
平均值:0.764
-
标准差:2.843
-
最小值:0
-
第 25 百分位数(Q1):0
-
中位数(第 50 百分位数):0
-
第 75 百分位数(Q3):1
-
最大值:36
-
解释:
helpful_votes列的平均值为 0.764,表明平均而言,评论收到的有用投票少于一次。标准差为 2.843,表明收到的有用投票数量存在变化,有些评论收到了多达 36 次的有用投票。
-
-
total_votes:
-
平均值:1.068
-
标准差:3.698
-
最小值:0
-
第 25 百分位数(Q1):0
-
中位数(第 50 百分位数):0
-
第 75 百分位数(Q3):1
-
最大值:47
-
解释:
total_votes列的平均值为 1.068,表明平均而言,评论收到的总票数略多于一次(有用或无用的票)。标准差为 3.698,表明收到的总票数存在变化,有些评论的总票数多达 47 票。
-
下一步:
根据这些统计数据,我们看到数据集主要由正面评论(star_rating为 1)组成。helpful_votes和total_votes指标显示了与评论的互动程度。
接下来,我们可以继续进行第 4 步:分类分析,我们将使用计数和可视化来分析分类变量(market_place、product_category、verified_purchase和sentiments)。你想继续进行吗?如果你有任何问题或准备好进行下一步,请告诉我!
[响应结束]
特征 4:探索分类变量
现在,我们将继续探索分类变量。
[提示]
让我们继续下一步。
[提示结束]
我们应该收到如下所示的响应,其中 ChatGPT 提供了可视化分类变量的代码:
[提示响应]
太好了!让我们继续进行第 4 步:
第 4 步:分类分析
在这一步,我们将使用计数和可视化来分析数据集中的分类变量。我们将关注的分类变量是:
-
market_place: 产品位置。 -
product_category: 产品类别。 -
verified_purchase: 购买是否经过验证(Y/N)。 -
sentiments: 评论的情感(正面或负面)。
详细代码解释:
-
计数唯一值:
df['column_name'].value_counts(): 此方法计算分类列中每个唯一值的出现次数。
-
可视化分类数据:
- 我们将使用柱状图(来自
matplotlib库)来可视化分类变量的分布。
- 我们将使用柱状图(来自
这是执行分类分析的代码:
import matplotlib.pyplot as plt
import seaborn as sns
# Set up the matplotlib figure and axes
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(14, 10))
axes = axes.flatten()
# Plot for 'market_place'
sns.countplot(x='market_place', data=df, ax=axes[0])
axes[0].set_title('Distribution of Products by Market Place')
# Plot for 'product_category'
sns.countplot(x='product_category', data=df, ax=axes[1])
axes[1].set_title('Distribution of Products by Category')
axes[1].tick_params(axis='x', rotation=90)
# Plot for 'verified_purchase'
sns.countplot(x='verified_purchase', data=df, ax=axes[2])
axes[2].set_title('Distribution of Verified Purchases')
# Plot for 'sentiments'
sns.countplot(x='sentiments', data=df, ax=axes[3])
axes[3].set_title('Distribution of Sentiments')
# Adjust layout
plt.tight_layout()
plt.show()
可视化解释:
-
市场:显示不同市场(
market_place)中产品的分布。 -
产品类别:显示不同类别(
product_category)中产品的分布。 -
验证购买:表示有多少条评论来自验证购买(Y/N)。
-
情感:说明评论情感(正面或负面)的分布。
用户反馈:
在您的 Jupyter Notebook 环境中执行此代码以可视化和分析数据集中的分类变量。这将为您提供有关评论和产品不同方面的分布和特征见解。如果您有任何问题或准备好进行下一步,请告诉我!
[结束响应]
我们执行代码,它运行得很好,然后可视化变量。

图 11.1:分类变量 – 可视化
我们可以看到,所有评论都来自单个市场,所有产品类别的评论数量相等,但正负评论以及验证购买分布也存在不平衡。
功能 5:评分分布
我们将继续进行评分分布,并指示 ChatGPT 进行下一步:
[提示]
让我们继续下一步。
[结束提示]
我们应该收到如下响应:
[提示响应]
太好了!让我们继续进行第 5 步:
第 5 步:评分分布
在这一步,我们将创建直方图或柱状图来可视化星级评分(star_rating)的分布。这将帮助我们了解评分在评论中的分布情况。
详细代码解释:
-
直方图或柱状图:
- 我们将使用 Seaborn 库中的
sns.countplot()来创建柱状图。这对于可视化每个评分值(1 到 5)的计数是合适的。
- 我们将使用 Seaborn 库中的
-
可视化参数:
- 调整如图形大小(
figsize)和标题(set_title)等操作是为了增强图表的清晰度和可读性。
- 调整如图形大小(
这是可视化评分分布的代码:
import matplotlib.pyplot as plt
import seaborn as sns
# Set up the matplotlib figure
plt.figure(figsize=(8, 6))
# Plotting the distribution of star ratings
sns.countplot(x='star_rating', data=df)
plt.title('Distribution of Star Ratings')
plt.xlabel('Star Rating')
plt.ylabel('Count')
# Show plot
plt.show()
可视化解释:
-
使用
sns.countplot()函数创建star_rating列的条形图。 -
x-轴表示星级评分(1 到 5),y-轴表示每个评分的评论数量。
-
添加标题、x-轴标签(
星级评分)和y-轴标签(数量)以提供上下文和清晰度。
用户反馈:
在您的 Jupyter Notebook 环境中执行此代码。它将显示您数据集中评论星级评分的分布的可视化表示。这一步将提供关于通过评分表达的整体情感的见解。如果您有任何问题或准备好进行下一步,请告诉我!
[响应结束]
我们将执行代码,生成评分图。

图 11.2:评分分布
根据这些结果,主要的评分是正面的。我们将继续到下一个特征。
特征 6:时间趋势
现在,让我们了解数据集中评论日期列的时间趋势。我们的数据集变化不大,但这将帮助我们了解如何使用 ChatGPT:
[提示]
让我们继续到下一步。
[提示结束]
我们应该收到以下类似的响应:
[快速响应]
太好了!让我们继续到第 6 步:
第 6 步:时间趋势
在这一步,我们将从review_date列中检查时间趋势,以了解评论是如何随时间分布的。这将帮助我们识别任何评论活动中的模式或趋势。
详细代码解释:
-
转换
review_date为datetime:- 确保将
review_date列设置为日期时间格式,以便进行适当的时间分析。
- 确保将
-
绘制时间趋势:
-
使用
sns.lineplot()或sns.histplot()来可视化评论随时间的分布。 -
根据分析目标,您可以选择按月、按年或任何其他相关的时间间隔绘制评论数量。
-
这是分析时间趋势的代码:
import matplotlib.pyplot as plt
import seaborn as sns
# Convert 'review_date' to datetime format
df['review_date'] = pd.to_datetime(df['review_date'])
# Set up the matplotlib figure
plt.figure(figsize=(12, 6))
# Plotting the count of reviews over time
sns.histplot(df['review_date'], bins=30, kde=False, color='blue')
plt.title('Distribution of Reviews Over Time')
plt.xlabel('Review Date')
plt.ylabel('Count of Reviews')
# Show plot
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
可视化解释:
-
日期时间转换:使用
pd.to_datetime()函数将review_date列从字符串转换为日期时间格式,以确保准确的时间分析。 -
直方图:使用
sns.histplot()创建随时间(review_date)变化的评论计数的直方图。这有助于可视化不同日期的评论分布和频率。 -
调整:进行调整,如设置图形大小(
figsize)、标题(title)、x-轴标签(xlabel)、y-轴标签(ylabel)和旋转x-轴刻度(xticks(rotation=45)),以提高图表的清晰度和可读性。
用户反馈:
在您的 Jupyter Notebook 环境中执行此代码。它将提供关于评论随时间分布的见解,让您能够观察任何评论活动中的趋势或模式。如果您有任何问题或准备好进行下一步,请告诉我!
[响应结束]
我们将执行代码,它生成一个显示评论随时间分布的图表。

图 11.3:时间趋势
我们可以看到评论只显示了两个日期,所以让我们转到下一个特征。
特征 7:评论长度分析
[提示]
仅显示了两个日期的评论,分别是 2013 年 10 月和 2015 年 8 月。
[提示结束]
我们应该收到以下类似的响应,其中 ChatGPT 提供代码,解释评论日期的重要性,并继续进行评论长度分析的下一步。
[快速响应]
如果你的数据集中的评论仅集中在两个日期(2013 年 10 月和 2015 年 8 月),这表明数据集可能没有广泛的评论时间跨度。这个信息对于理解数据背景及其在时间分析方面的局限性至关重要。
基于这个观察,我们仍然可以按照计划在步骤 7 中继续分析评论文本和情感。这一步将专注于检查评论文本的长度和情感,这可以提供超越时间趋势的额外见解。
步骤 7:评论文本分析
在这一步,我们将分析评论文本(review_body),以:
-
计算每个评论的长度。
-
使用提供的
sentiments列确定每个评论的情感。
详细代码解释:
-
评论长度计算:
- 在 DataFrame 中添加一个新列
review_length来存储每个评论(review_body)的字符计数。
- 在 DataFrame 中添加一个新列
-
情感分析:
- 使用
sentiments列根据提供的标签将评论分类为正面或负面。
- 使用
这是执行评论文本分析的代码:
# Calculate the length of each review text
df['review_length'] = df['review_body'].apply(len)
# Count the number of reviews classified as positive and negative
sentiment_counts = df['sentiments'].value_counts()
# Display the sentiment counts
print("Sentiment Counts:")
print(sentiment_counts)
# Calculate the average length of reviews
average_review_length = df['review_length'].mean()
print(f"\nAverage Review Length: {average_review_length:.2f} characters")
# Display the first few rows to verify the changes
df.head()
代码解释:
-
评论长度计算:
df['review_body'].apply(len)计算每个评论的字符长度并将其存储在新的列review_length中。 -
情感分析:
df['sentiments'].value_counts()统计sentiments列中每个情感类别(正面或负面)的出现次数。 -
平均评论长度:
df['review_length'].mean()计算数据集中评论的平均长度。
用户反馈:
在你的 Jupyter Notebook 环境中执行此代码。它将提供关于数据集中评论文本长度和情感分布的见解。如果你有任何问题或审查完输出后准备好进行下一步,请告诉我!
[响应结束]
我们在 Jupyter Colab 中执行代码,它运行良好,因此我们提供输出给 ChatGPT 进行解释:
[提示]
Sentiment Counts:
sentiments
positive 398
negative 102
Name: count, dtype: int64
Average Review Length: 243.75 characters
market_place customer_id review_id product_id product_parent product_title product_category star_rating helpful_votes total_votes verified_purchase review_headline review_body review_date sentiments review_length
0 "US" "42521656" "R26MV8D0KG6QI6" "B000SAQCWC" "159713740" "The Cravings Place Chocolate Chunk Cookie Mix... "Grocery" 1 0 0 1 \t(Y) "Using these for years - love them." "As a family allergic to wheat, dairy, eggs, n... 2015-08-31 positive 470
1 "US" "12049833" "R1OF8GP57AQ1A0" "B00509LVIQ" "138680402" "Mauna Loa Macadamias, 11 Ounce Packages" "Grocery" 1 0 0 1 \t(Y) "Wonderful" "My favorite nut. Creamy, crunchy, salty, and ... 2015-08-31 positive 92
2 "US" "107642" "R3VDC1QB6MC4ZZ" "B00KHXESLC" "252021703" "Organic Matcha Green Tea Powder - 100% Pure M... "Grocery" 1 0 0 0 \t(N) "Five Stars" "This green tea tastes so good! My girlfriend ... 2015-08-31 positive 60
3 "US" "6042304" "R12FA3DCF8F9ER" "B000F8JIIC" "752728342" "15oz Raspberry Lyons Designer Dessert Syrup S... "Grocery" 1 0 0 1 \t(Y) "Five Stars" "I love Melissa's brand but this is a great se... 2015-08-31 positive 85
4 "US" "18123821" "RTWHVNV6X4CNJ" "B004ZWR9RQ" "552138758" "Stride Spark Kinetic Fruit Sugar Free Gum, 14... "Grocery" 1 0 0 1 \t(Y) "Five Stars" "good" 2015-08-31 positive 6
[提示结束]
我们应该收到 ChatGPT 解释结果的响应:
[快速响应]
看起来情感分析和评论长度计算已经成功执行。以下是分析中的见解:
情感分析:
-
情感计数:
-
正面评论:398
-
负面评论:102
-
评论长度分析:
-
-
平均评论长度:243.75 个字符
解释:
-
情感计数:数据集包含主要是正面的评论,有 398 条正面评论和 102 条负面评论。
-
平均评论长度:数据集中评论的平均长度约为 243.75 个字符。
示例数据检查:
您提供的示例片段显示了数据集的样本,我们可以看到市场、客户 ID、评论 ID、产品详情、星级评分、评论文本、评论日期、情感和评论长度等详细信息。
下一步:
根据这项分析,我们获得了关于情感分布和评论平均长度的见解。如果您想进行特定的进一步分析或可视化,例如相关性研究或数据集的任何其他方面,请告诉我!我们可以相应地继续。
[结束响应]
特征 8:相关性研究
我们将进入最后一个特征,即相关性研究。
[提示]
让我们进行下一步。
[结束提示]
我们应该收到如下响应:
[提示响应]
当然!让我们进行到步骤 8:
步骤 8:相关性研究
在这一步,我们将评估数据集中数值变量之间的相关性。具体来说,我们将检查 star_rating、helpful_votes 和 total_votes 之间的相关性。这项分析将帮助我们了解这些变量之间是否存在任何关系或依赖。
详细代码解释:
-
相关性计算:
- 在 DataFrame 上使用
.corr()方法来计算数值列之间的相关系数。
- 在 DataFrame 上使用
-
相关性可视化:
- 我们可以使用热图来可视化相关性,以提供清晰直观的关系表示。
这是执行相关性研究的代码:
import matplotlib.pyplot as plt
import seaborn as sns
# Calculate the correlation matrix
correlation_matrix = df[['star_rating', 'helpful_votes', 'total_votes']].corr()
# Plotting the correlation heatmap
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlation Matrix of Numerical Variables')
plt.show()
代码解释:
-
相关矩阵:
df[['star_rating', 'helpful_votes', 'total_votes']].corr()计算了star_rating、helpful_votes和total_votes之间的相关系数。 -
热图可视化:使用 Seaborn 库中的
sns.heatmap()创建相关矩阵的热图。annot=True参数为热图单元格添加了数字注释,以便于清晰。cmap='coolwarm'参数设置了热图的颜色方案,其中颜色从蓝色(负相关)到红色(正相关)变化。
解释:
-
热图将显示从 -1 到 1 的相关性,其中:
-
接近 1 的值表示强烈的正相关。
-
接近 -1 的值表示强烈的负相关。
-
接近 0 的值表示没有显著的相关性。
用户反馈:
- 在您的 Jupyter Notebook 环境中执行此代码。它将提供关于
star_rating、helpful_votes和total_votes在您的数据集中如何相关的见解。如果您有任何问题或审查完相关性热图后准备进行下一步,请告诉我!
[结束响应]
我们将执行代码,该代码绘制相关矩阵并打印结果。

图 11.4:相关矩阵
我们可以看到有帮助的投票和总投票高度相关,但评分不是。
这就完成了使用 ChatGPT-3.5 的数据探索;现在,让我们看看如何使用 ChatGPT-4o。
使用 ChatGPT-4o 对亚马逊评论数据集进行数据探索
ChatGPT 的付费版本有一个代码解释器,可以通过 ChatGPT-4o 和 ChatGPT 4 访问。在本节中,我们将使用 GPT-4o,这次我们将一次性获取代码。
ChatGPT 可以访问几个模型,目前,你可以使用 GPT-3.5、GPT-4 和 GPT-4o。
让我们构建我们的提示。
[提示]
我想使用各种统计和可视化技术(TAG 1.1)彻底探索附带的亚马逊产品评论数据集(PIC 2.3)。它应包括以下步骤(TAG 1.2):
-
数据加载:将数据集加载到 pandas DataFrame 中。
-
数据检查:检查缺失数据,理解数据类型,并检查前几条记录。
-
统计摘要:计算数值数据的摘要统计。
-
分类分析:使用计数和可视化分析分类变量。
-
评分分布:创建直方图或条形图来可视化星级评分的分布。
-
时间趋势:检查评论日期的时间趋势。
-
评论文本分析:分析评论文本的长度和情感。
-
相关性研究:评估数值变量之间的相关性
我是一名数据分析师,寻求揭示可操作的见解(PIC 2.1),因此请提供详细的代码。
[结束提示]
我们应该收到一个类似于以下的响应,其中 ChatGPT 执行所有必要的步骤,进行数据探索,并生成相关图表:
[提示响应]
亚马逊产品评论数据集分析
我们将按照你概述的步骤彻底探索数据集。让我们一步一步来:
第 1 步:数据加载
- 我们已经将数据集加载到 pandas DataFrame 中。
第 2 步:数据检查
-
我们检查了缺失数据,理解了数据类型,并检查了前几条记录。数据集由 500 条记录和 15 列组成,没有缺失数据。以下是前几条记录:
market_place customer_id review_id product_id product_parent \ 0 "US" "42521656" "R26MV8D0KG6QI6" "B000SAQCWC" "159713740" 1 "US" "12049833" "R1OF8GP57AQ1A0" "B00509LVIQ" "138680402" 2 "US" "107642" "R3VDC1QB6MC4ZZ" "B00KHXESLC" "252021703" 3 "US" "6042304" "R12FA3DCF8F9ER" "B000F8JIIC" "752728342" 4 "US" "18123821" "RTWHVNV6X4CNJ" "B004ZWR9RQ" "552138758" product_title product_category \ 0 "The Cravings Place Chocolate Chunk Cookie Mix... "Grocery" 1 "Mauna Loa Macadamias, 11 Ounce Packages" "Grocery" 2 "Organic Matcha Green Tea Powder - 100% Pure M... "Grocery" 3 "15oz Raspberry Lyons Designer Dessert Syrup S... "Grocery" 4 "Stride Spark Kinetic Fruit Sugar Free Gum, 14... "Grocery" star_rating helpful_votes total_votes verified_purchase \ 0 1 0 0 1 \t(Y) 1 1 0 0 1 \t(Y) 2 1 0 0 0 \t(N) 3 1 0 0 1 \t(Y) 4 1 0 0 1 \t(Y) review_headline \ 0 "Using these for years - love them." 1 "Wonderful" 2 "Five Stars" 3 "Five Stars" 4 "Five Stars" review_body review_date sentiments 0 "As a family allergic to wheat, dairy, eggs, n... 2015-08-31 positive 1 "My favorite nut. Creamy, crunchy, salty, and ... 2015-08-31 positive 2 "This green tea tastes so good! My girlfriend ... 2015-08-31 positive 3 "I love Melissa's brand but this is a great se... 2015-08-31 positive 4 "good" 2015-08-31 positive
第 3 步:统计摘要
- 让我们计算数值数据的摘要统计。你应该能看到数据的摘要,包括均值、标准差等统计信息。
第 4 步:分类分析
- 我们将使用计数和可视化分析分类变量。
第 5 步:评分分布
- 我们将创建直方图或条形图来可视化星级评分的分布。
第 6 步:时间趋势
- 我们将分析评论日期的趋势。
第 7 步:文本分析审查
- 我们将分析评论文本的长度和情感。
第 8 步:相关性研究
- 我们将评估数值变量之间的相关性。
让我们开始用 Python 代码实现这些步骤。
亚马逊产品评论数据集的详细分析
我们已按概述进行了详细分析。以下是每一步的关键发现和可视化。
第 3 步:统计摘要
我们计算了数值数据的摘要统计。以下是一些关键统计数据:
-
星级评分:平均值 = 0.796,标准差 = 0.403,最小值 = 0,最大值 = 1
-
有帮助的投票数:平均值 = 0.764,标准差 = 2.843,最小值 = 0,最大值 = 36
-
总投票数:平均值 = 1.068,标准差 = 3.698,最小值 = 0,最大值 = 47
第 4 步:分类分析
按评论数量排名最高的类别如下:
-
杂货:100 条评论
-
数字电子书购买:100 条评论
-
珠宝:100 条评论
-
电脑:100 条评论
-
书籍:100 条评论
第 5 步:评分分布
我们可视化了星级评分的分布:

图 11.5:星级评分的分布
第 6 步:时间趋势
我们考察了每月的评论数量:

图 11.6:每月评论数量
第 7 步:评论文本分析
我们分析了评论文本的长度和情感:
- 评论长度分布:

图 11.7:评论长度分布
- 情感分布:

图 11.8:情感分布
-
正面评论:398 条
-
负面评论:102 条
第 8 步:相关性研究
我们评估了数值变量之间的相关性,并可视化了相关矩阵:

图 11.9:相关矩阵
结论
此分析为数据集提供了有价值的见解,包括星级评分的分布、时间趋势和评论的情感。我们还探索了数值变量之间的相关性。这些见解有助于理解客户反馈并改进产品供应。
如果您需要进一步的分析或有具体问题,请随时提问!
[结束回复]
如您所见,GPT-4o 仅通过上传 CSV 文件并提供执行步骤或操作,就成功执行了数据探索。
任务
调查不同产品或类别在评分、有帮助的投票数和评论长度方面的表现。
挑战
使用自然语言处理(NLP)技术来分析评论的内容。提取经常提到的常见主题、关键词和短语。
摘要
在本章中,我们深入探讨了使用 Python 和 pandas 进行综合数据分析,利用亚马逊产品评论数据集。这次旅程从数据加载和检查开始,确保数据集格式正确且无缺失值。您在每个步骤中都得到了详细的解释和适合 Jupyter Notebooks 的代码示例,旨在帮助数据分析师有效地挖掘可操作的见解。
我们首先对数值数据计算了统计摘要,揭示了数据集主要由正面评论组成。随后进行了分类分析,我们探讨了不同市场、产品类别、验证购买和情感之间的分布。包括直方图和条形图在内的可视化提供了星级评分分布的清晰表示,强调了正面反馈的普遍性。
时间趋势分析揭示了评论的集中分布,主要在 2013 年 10 月和 2015 年 8 月,这为随着时间的推移的评论活动提供了洞察。然后我们进行了评论文本分析,计算评论长度并评估情感计数,以更深入地了解数据集的内容。最后,相关性研究考察了星级评分和评论参与度指标(如有用票数和总票数)之间的关系,提供了这些因素如何在数据集中相互作用的见解。
在下一章中,我们将学习如何使用 ChatGPT 利用相同的数据集构建分类模型。
加入我们的 Discord 社区
加入我们的 Discord 空间,与作者和其他读者进行讨论:

第十二章:使用 ChatGPT 构建分类模型
引言
在前一章的基础上,我们使用 ChatGPT 对亚马逊书评进行数据探索,第十二章进一步深入到监督学习的领域,重点关注分类。在这里,我们继续利用 ChatGPT,应用其能力来增强我们对监督学习技术在客户评价背景下的理解和应用。
在电子商务领域,客户反馈在塑造商业策略和产品改进中起着至关重要的作用。正如比尔·盖茨恰如其分地所说:“你最不满意的客户是你最大的学习来源。”客户情感往往隐藏在大量的产品评价中。然而,手动审查这个包括产品 ID、标题、文本、评分和有帮助的投票等各种属性的产品评价海洋,是一项艰巨且往往难以管理的任务。
在本章中,我们专注于将客户评价分为两个不同的类别:正面和负面。我们将利用从 ChatGPT 处获得的见解来处理和分析客户评价数据。
我们的主要目标是展示 ChatGPT 如何简化机器学习的旅程,使其更加易于接近和减少恐惧,尤其是在处理像监督学习中的分类这样的复杂主题时。我们将探讨 ChatGPT 如何将复杂的概念分解成更易于消化的部分,提供解释,甚至生成代码片段,从而降低初学者或新进入该领域的人的学习曲线。
到本章结束时,你将牢固地理解监督学习及其在情感分析中的应用,并欣赏到 AI 工具如 ChatGPT 在有效学习和应用机器学习技术中的价值。
商业问题
在电子商务项目中,理解客户反馈有助于识别影响客户购买决策的关键因素,从而实现有针对性的营销策略。此外,它还允许优化用户体验和网站设计,以增加向客户提供改进服务和产品的可能性。
问题与数据领域
在本节中,我们旨在使用亚马逊产品评价数据集构建一个客户评价情感分析分类模型。利用 ChatGPT 的能力,我们将生成 Python 代码来构建分类模型,为读者提供一种实际的方法来处理数据集和理解分类技术。此外,我们还将探讨有效的提示技术,以引导 ChatGPT 提供针对数据分类任务的定制代码片段和见解。
数据集概述
亚马逊产品评论数据集包含了各种产品和它们相应的评论信息。通过利用这个数据集,我们可以执行各种分析,包括情感分析、客户反馈趋势分析和产品评级分析。最终目标是训练一个能够准确地将评论分类为正面或负面情感的分类模型,增强决策过程,并提高电子商务平台和相关行业中的客户满意度。
数据集中的特征包括:
-
marketplace(string): 产品的位置。 -
customer_id(string): 客户的唯一标识符。 -
review_id(string): 评论的唯一标识符。 -
product_id(string): 产品的唯一标识符。 -
product_parent(string): 父产品标识符。 -
product_title(string): 被评论产品的标题。 -
product_category(string): 产品类别。 -
star_rating(int): 产品在 1 到 5 的评分尺度上的评分。 -
helpful_votes(int): 评论收到的有用票数。 -
total_votes(int): 评论收到的总票数。 -
review_headline(string): 评论的标题。 -
review_body(string): 评论的内容。 -
review_date(string): 评论的日期。 -
Sentiments(string): 评论的情感(正面或负面)。
review_body 和 review_headline 中的文本数据对于自然语言处理任务,包括情感分析,尤其有价值。为了简化目的,我们已排除中性情感类别,以专注于构建分类模型和提示技术。
将问题分解为特征
鉴于亚马逊产品评论数据集和机器学习模型在情感分析中的应用,我们将概述以下特征,以指导用户构建和优化用于情感分类的模型:
-
数据预处理和特征工程:用户将开始对文本数据进行预处理,包括诸如分词、小写化、去除停用词和标点符号等任务。此外,还将应用诸如词频-逆文档频率(TF-IDF)编码或词嵌入等特征工程技术,以将文本数据表示为适合机器学习模型的格式。
-
模型选择和基线训练:用户将为情感分类选择基线机器学习模型,如逻辑回归、朴素贝叶斯或支持向量机(SVMs)。选定的模型将在预处理后的数据上训练,以建立情感分析的基线性能。
-
模型评估和解释:用户将使用准确率、精确率、召回率和 F1 分数等指标评估训练好的机器学习模型的性能。此外,还将探索解释模型预测的技术,如特征重要性分析或模型可解释性方法,以深入了解影响情感分类决策的因素。
-
处理不平衡数据:此功能通过实现过采样、欠采样或模型训练期间使用类权重等技术来解决数据集中类别分布不平衡的挑战。用户将探索减轻类别不平衡对模型性能影响的方法,并提高少数类别的分类准确率。
-
超参数调整:用户将学习如何通过调整正则化强度、学习率和核参数等超参数来优化机器学习模型的性能。通过网格搜索或随机搜索等技术,用户将尝试不同的超参数配置,以改善模型在验证集上的性能。
-
实验特征表示:用户将探索将文本数据表示为机器学习模型特征的不同方法。此功能侧重于比较使用不同特征表示(如词袋、TF-IDF 或词嵌入)训练的模型的性能,以确定情感分类中最有效的途径。
通过遵循这些特性,用户将获得构建、微调和优化用于亚马逊产品评论数据集进行情感分析任务的机器学习模型的实际见解。他们将学习如何系统地实验不同的预处理技术、特征表示、超参数配置和类别不平衡处理策略,以实现情感分类中的卓越性能和准确性。
提示策略
为了有效地利用 ChatGPT 生成用于情感分析机器学习任务的代码,我们需要开发一个针对使用亚马逊产品评论数据集进行情感分析的具体特征和要求的综合提示策略。
策略 1:任务-行动-指南(TAG)提示策略
1.1 – 任务:具体任务是构建和优化使用亚马逊产品评论数据集进行情感分析的机器学习模型。
1.2 – 行动:构建和优化用于情感分析的机器学习模型的关键步骤包括:
-
数据预处理:分词、小写化、去除停用词和标点符号,以及特征工程(例如,TF-IDF 编码、词嵌入)。
-
模型选择:选择基线机器学习模型,如逻辑回归、朴素贝叶斯或支持向量机(SVMs)。
1.3 – 指南:在我们的提示中,我们将为 ChatGPT 提供以下指南:
-
代码应与 Jupyter Notebook 兼容。
-
确保对每一行代码都有详细的注释。
-
您必须详细解释每行代码,然后将其复制到笔记本的文本块中,在提供代码之前,对每个使用的方法进行详细说明。
策略 2:人格-指令-上下文(PIC)提示策略
2.1 – 人格:采用一个需要逐步指导构建和优化用于亚马逊产品评论数据集情感分析任务的机器学习模型的初学者的角色。
2.2 – 指令:要求 ChatGPT 逐个生成代码功能,并在进行下一步之前等待用户反馈。同时,提供数据集的路径,从该路径加载数据。
2.3 – 上下文:鉴于重点是使用亚马逊产品评论数据集进行情感分析,ChatGPT 不了解数据集及其特征,因此需要额外的上下文。
策略 3:学习-即兴发挥-反馈-评估(LIFE)提示策略
3.1 – 学习:
- 强调理解机器学习模型及其组件的重要性,包括特征工程技术和模型选择。
3.2 – 即兴发挥:
- 请求 ChatGPT 提供代码片段以实现额外的功能,例如超参数调整、处理不平衡数据和模型评估技术。
3.3 – 反馈:
-
分享生成的代码和模型输出以供反馈,以确保有效的学习和理解。
-
结合用户建议和批评,以改进模型和方法。
-
提供错误信息以排除和解决在模型实现过程中遇到的任何问题。
3.4 – 评估:
- 执行 ChatGPT 提供的生成代码,以验证准确性和有效性,确保模型在情感分析任务中使用亚马逊产品评论数据集时表现有效。
使用 ChatGPT 的免费版构建一个情感分析模型,以准确分类亚马逊评论。
我们将利用 ChatGPT 的免费版来构建基线模型。
特征 1:数据预处理和特征工程
让我们为我们的基线模型制定初始提示。
[Prompt]
我想为亚马逊评论数据集(TAG 1.1)创建一个简单的分类模型,该数据集包含以下字段(PIC 2.3)
-
marketplace(string): 产品的位置。 -
customer_id(string): 客户的唯一标识符。 -
review_id(string): 评论的唯一标识符。 -
product_id(string): 产品唯一的标识符。 -
product_parent(string): 父产品标识符。 -
product_title(string): 被评论产品的标题。 -
product_category(string): 产品的类别。 -
star_rating(int): 产品在 1 到 5 的评分尺度上的评分。 -
helpful_votes(int): 评论收到的有用票数。 -
total_votes(int): 评论收到的总票数。 -
review_headline(string): 审查的标题。 -
review_body(string): 审查内容。 -
review_date(string): 审查日期。 -
Sentiments(string): 审查的情感(正面或负面)。
它应包括以下步骤(TAG 1.2)并一次提供一步,并等待用户反馈(PIC 2.2)。
-
数据预处理:分词、小写化、移除停用词和标点符号以及特征工程(例如,TF-IDF 编码,词嵌入)。
-
模型选择:选择基线机器学习模型,如逻辑回归、朴素贝叶斯或 SVMs。
我是一个初学者用户(PIC 2.1),所以请提供逐步代码并严格遵循以下说明(TAG 1.3)。
-
代码应与 Jupyter 笔记本兼容。
-
确保对每一行代码都有详细的注释。
-
您必须详细解释每一行代码,这些代码将被复制到笔记本的文本块中,在提供代码之前对每种使用的方法进行详细说明。同时,请注意,数据集位于 Google Drive 的此路径:
/Chapter05/amazon_product_review_data.csv。因此,添加代码以挂载驱动器并加载数据集(PIC 2.2)。
[E提示结束]
您应该收到以下类似的响应,其中 ChatGPT 为我们提供了详细的评论和解释:
[P提示响应]
当然,让我们将这个过程分解成步骤。我们将从数据预处理开始。
步骤 1:数据预处理
数据预处理对于清理和准备文本数据以供情感分析模型使用至关重要。我们将执行以下步骤:
-
加载数据集:从提供的路径加载 Amazon 审查数据集。
-
分词:将文本分割成单个单词或标记。
-
小写化:将所有文本转换为小写以确保一致性。
-
移除停用词:移除没有太多意义的常见词。
-
移除标点符号:从文本中移除标点符号。
-
特征工程:将文本数据转换为适合机器学习模型的格式(例如,TF-IDF 编码)。
现在,在编写代码之前,让我们详细探讨每个步骤。
分词:
- 分词是将文本分割成单个单词或标记的过程。它是自然语言处理(NLP)任务中的关键步骤。标记是文本处理的基本单元。例如,句子“The quick brown fox jumps over the lazy dog”可以被分词为[‘The’,‘quick’,‘brown’,‘fox’,‘jumps’,‘over’,‘the’,‘lazy’,‘dog’]。
小写化:
- 小写化涉及将所有文本转换为小写。这有助于标准化文本数据并减少词汇量。例如,在转换为小写后,“Hello”和“hello”将被视为相同的单词。
移除停用词:
- 停用词是那些没有显著意义的常见词,例如“the”,“is”,“and”等。移除停用词有助于减少数据中的噪声并提高模型的性能。
移除标点符号:
- 标点符号,如逗号、句号和感叹号,对情感分析贡献不大,可以从文本中安全地移除。
特征工程(TF-IDF 编码):
- TF-IDF(词频-逆文档频率)编码是一种常用的技术,用于将文本数据数值化。它反映了在文档集合中一个词对文档的重要性。它是基于两个指标计算的:词频(TF)和逆文档频率(IDF)。TF 衡量一个词在文档中的频率,而 IDF 衡量该词在整个文档中的稀有度。TF-IDF 编码给那些在文档中频繁出现但在文档集合中稀有的词赋予更高的权重,这些词通常更有信息量。
[End of response]
我们现在将要求 ChatGPT 提供代码:
[Prompt]
好的,让我们继续。
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 应提供加载数据集的代码:
[Prompt response]
太好了!让我们从在 Jupyter Notebook 中从 Google Drive 加载数据集开始。首先,我们需要挂载 Google Drive,然后读取包含亚马逊评论数据的 CSV 文件。
步骤 1:加载数据集
我们将使用pandas库从 CSV 文件加载数据集。Pandas 为 Python 提供了强大的数据处理工具,使其适合处理如 CSV 文件这样的结构化数据。
代码解释:
-
import pandas as pd: 导入 pandas 库并将其别名设置为pd以方便使用。 -
from google.colab import drive: 从google.colab库导入drive模块以挂载 Google Drive。 -
drive.mount('/content/drive'): 挂载 Google Drive 以访问其中的文件。这将提示您授权访问您的 Google Drive。 -
data_path = '/content/drive/My Drive/Chapter05/amazon_product_review_data.csv': 定义包含数据集的 CSV 文件的路径。 -
df = pd.read_csv(data_path): 使用 pandas 的read_csv()函数读取 CSV 文件,并将其存储在名为df的 DataFrame 中。
让我们在代码中实现这一点:
# Importing necessary libraries
import pandas as pd
from google.colab import drive
# Mount Google Drive
drive.mount('/content/drive')
# Path to the dataset
data_path = '/content/drive/My Drive/Chapter05/amazon_product_review_data.csv'
# Load the dataset into a DataFrame
df = pd.read_csv(data_path)
在您的 Jupyter Notebook 中运行此代码,它将数据集加载到 DataFrame df 中。在我们进行下一步之前,如果有什么问题,请告诉我
[End of response]
我们现在将应用评估提示策略(LIFE 3.4)并通过在 Google Colab 的每一步执行代码来验证代码。代码将从 Google Drive 加载数据集。它将请求权限。代码运行良好,无需任何修改或错误。
请参考“使用 ChatGPT 构建情感分析分类模型”的笔记本 Building a Classification Model for Sentiment Analysis with ChatGPT.ipynb 中的可执行代码片段,或从提示响应中复制代码。

图 12.1:代码执行 – 数据预处理
我们现在将指示 ChatGPT 进行下一步:
[Prompt]
让我们进行下一步。
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 应提供数据预处理的代码:
[提示响应]**
太好了!在下一步中,我们将执行以下预处理任务:
-
分词
-
小写化
-
移除停用词
-
移除标点符号
我们将使用 NLTK 库进行分词和停用词移除,并利用字符串方法进行小写化和标点符号移除。
步骤 2:数据预处理
分词:
- 我们将评论文本分词成单个单词或标记。NLTK 提供了一个
word_tokenize函数用于此目的。
小写化:
- 我们将使用 Python 字符串的
lower()方法将所有文本转换为小写。
移除停用词:
- NLTK 为各种语言提供了一组停用词。我们将使用这个列表来过滤掉评论文本中的停用词。
移除标点符号:
- 我们将使用字符串方法从评论文本中移除标点符号。
让我们用代码实现这些预处理步骤:
# Importing necessary libraries
import nltk
import string
# Download NLTK resources (only need to do this once)
nltk.download('punkt')
nltk.download('stopwords')
# Get the list of stopwords
stopwords = nltk.corpus.stopwords.words('english')
# Function to preprocess text
def preprocess_text(text):
# Tokenization
tokens = nltk.word_tokenize(text)
# Lowercasing
tokens = [token.lower() for token in tokens]
# Removing stopwords and punctuation
tokens = [token for token in tokens if token not in stopwords and token not in string.punctuation]
# Join tokens back into text
preprocessed_text = ' '.join(tokens)
return preprocessed_text
# Preprocess the review body
df['Cleaned_Review'] = df['review_body'].apply(preprocess_text)
# Display the preprocessed data
df.head()
下面是对代码的解释:
-
我们导入必要的库,包括用于自然语言处理任务的 NLTK 库和用于处理标点的 string 模块。
-
我们使用
nltk.download()函数下载 NLTK 资源,用于分词和停用词移除。 -
我们定义了一个名为
preprocess_text()的函数来预处理每个评论文本。此函数执行分词、小写化、移除停用词和移除标点符号。 -
在函数内部,我们使用
nltk.word_tokenize()对文本进行分词,将标记转换为小写,使用列表推导式移除停用词和标点符号,然后将标记重新组合成文本。 -
最后,我们将
preprocess_text()函数应用于 DataFrame 的review_body列,并将预处理后的文本存储在一个名为Cleaned_Review的新列中。
在您的 Jupyter Notebook 中运行此代码,它将预处理 DataFrame 中的评论文本。如果您有任何问题或准备好进入下一步,请告诉我!
[响应结束]
我们在笔记本中执行代码,它运行得非常好,并打印出数据集摘要,以及一个名为Cleaned_Review的新列。

图 12.2:代码执行 – 数据集摘要
功能 2:模型选择和基线训练
现在数据预处理已完成,我们将进入下一个功能:模型选择。
我们现在将指示 ChatGPT 进入下一步,并给它提供上一步的输出:
[提示]
market_place customer_id review_id product_id product_parent product_title product_category star_rating helpful_votes total_votes vine verified_purchase review_headline review_body review_date sentiments Cleaned_Review 0 "US" "42521656" "R26MV8D0KG6QI6" "B000SAQCWC" "159713740" "The Cravings Place Chocolate Chunk Cookie Mix... "Grocery" 1 0 0 0 \t(N) 1 \t(Y) "Using these for years - love them." "As a family allergic to wheat, dairy, eggs, n... 2015-08-31 positive `` family allergic wheat dairy eggs nuts sever... 1 "US" "12049833" "R1OF8GP57AQ1A0" "B00509LVIQ" "138680402" "Mauna Loa Macadamias, 11 Ounce Packages" "Grocery" 1 0 0 0 \t(N) 1 \t(Y) "Wonderful" "My favorite nut. Creamy, crunchy, salty, and ... 2015-08-31 positive `` favorite nut creamy crunchy salty slightly ... 2 "US" "107642" "R3VDC1QB6MC4ZZ" "B00KHXESLC" "252021703" "Organic Matcha Green Tea Powder - 100% Pure M... "Grocery" 1 0 0 0 \t(N) 0 \t(N) "Five Stars" "This green tea tastes so good! My girlfriend ... 2015-08-31 positive `` green tea tastes good girlfriend loves '' 3 "US" "6042304" "R12FA3DCF8F9ER" "B000F8JIIC" "752728342" "15oz Raspberry Lyons Designer Dessert Syrup S... "Grocery" 1 0 0 0 \t(N) 1 \t(Y) "Five Stars" "I love Melissa's brand but this is a great se... 2015-08-31 positive `` love melissa 's brand great second ca n't g... 4 "US" "18123821" "RTWHVNV6X4CNJ" "B004ZWR9RQ" "552138758" "Stride Spark Kinetic Fruit Sugar Free Gum, 14... "Grocery" 1 0 0 0 \t(N) 1 \t(Y) "Five Stars" "good" 2015-08-31 positive `` good ''
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 应提供模型选择的代码:
[提示响应]**
看起来 DataFrame 现在包含了一个名为Cleaned_Review的新列,其中包含预处理后的文本数据。我们已经成功完成了数据预处理步骤。
现在,让我们进入下一步:模型选择。
步骤 3:模型选择
在这一步,我们将选择一个基准机器学习模型用于情感分析。常用于文本分类任务如情感分析的模型包括逻辑回归、朴素贝叶斯和支持向量机(SVM)。我们将从这些模型中选择一个作为基准并评估其性能。
逻辑回归:
-
逻辑回归是一种流行的线性分类算法,用于二元分类任务如情感分析。它通过使用逻辑函数来建模给定输入属于特定类的概率。
-
我们将使用 Python 中的强大机器学习库 scikit-learn 来实现逻辑回归进行情感分析。
让我们继续使用预处理数据实现逻辑回归进行情感分析。以下是代码:
# Importing necessary libraries
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report
# Splitting the data into training and testing sets
X = df['Cleaned_Review']
y = df['sentiments']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# TF-IDF vectorization
tfidf_vectorizer = TfidfVectorizer(max_features=5000) # You can adjust max_features as needed
X_train_tfidf = tfidf_vectorizer.fit_transform(X_train)
X_test_tfidf = tfidf_vectorizer.transform(X_test)
# Initialize and train the logistic regression model
lr_model = LogisticRegression()
lr_model.fit(X_train_tfidf, y_train)
# Predictions
y_pred = lr_model.predict(X_test_tfidf)
# Evaluate the model
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)
print("\nClassification Report:")
print(classification_report(y_test, y_pred))
让我解释一下代码:
-
我们从 scikit-learn 导入必要的库以进行模型选择和评估,包括用于分割数据的
train_test_split,用于 TF-IDF 向量化的TfidfVectorizer,用于逻辑回归的LogisticRegression,以及用于模型评估的accuracy_score和classification_report。 -
我们使用
train_test_split将数据分为训练集和测试集。 -
我们使用
TfidfVectorizer对训练数据和测试数据进行 TF-IDF 向量化。 -
我们使用训练数据初始化和训练一个逻辑回归模型。
-
我们在测试数据上做出预测。
-
最后,我们使用准确率和分类报告评估模型性能。
您可以在您的 Jupyter Notebook 中运行此代码来训练逻辑回归模型,并评估其在情感分析任务上的性能。如果您有任何问题或想探索其他模型选项,请告诉我!
[End of response]
我们执行代码,它运行无误,训练了模型,并评估了模型性能。
您应该会看到一个包含精确度、召回率等信息的表格。
我们将进入模型评估和解释的下一个特性。
特征 3:模型评估和解释
现在我们已经选择了逻辑回归模型,并评估了基准性能,我们可以解释结果。
我们现在将输出提供给 ChatGPT,并请求额外的技术,如特征重要性分析和模型可解释性:
[Prompt]
我想了解准确率、精确度、召回率和 F1 分数等指标。此外,我还想应用解释模型预测的技术,如特征重要性分析或模型可解释性方法,以深入了解影响情感分类决策的因素。以下是模型评估的输出。
Accuracy: 0.86
Classification Report:
precision recall f1-score support
negative 0.00 0.00 0.00 14
positive 0.86 1.00 0.92 86
accuracy 0.86 100
macro avg 0.43 0.50 0.46 100
weighted avg 0.74 0.86 0.80 100
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 解释了指标,并建议了一些模型可解释性的选项等:
[Prompt response]
当然!让我们分解模型评估输出中提到的指标,并讨论解释模型预测的技术。
指标说明:
- Accuracy:
准确率衡量正确分类的实例占总实例的比例。计算公式为正确预测的数量与总预测数量的比率。然而,仅准确率可能无法完全反映模型性能,尤其是在类别不平衡的情况下。
- Precision:
精确率衡量所有正面预测中真正例预测的比例。它表示模型避免假正例的能力。计算公式为真正例与真正例和假正例之和的比率。
- Recall:
召回率,也称为灵敏度或真正例率,衡量了所有实际正面实例中真正例预测的比例。它表示模型捕捉所有正面实例的能力。计算公式为真正例与真正例和假反例之和的比率。
- F1-Score:
F1 分数是精确率和召回率的调和平均数。它提供了一个平衡的度量,同时考虑了精确率和召回率。计算公式为 2 * (precision * recall) / (precision + recall)。
解释:
- Accuracy:
准确率为 0.86 表示,模型正确分类了测试集中 86%的实例。然而,仅准确率并不能提供对类别特定性能的洞察。
- Precision:
正类别的精确率为 0.86,表示当模型预测评论为正面时,它在 86%的时间内是正确的。负类别的精确率为 0.00,表示模型没有正确预测任何负面评论。
- Recall:
正类别的召回率为 1.00,表示模型正确识别了所有正面评论。然而,负类别的召回率为 0.00,表示模型未能识别任何负面评论。
- F1-Score:
正类别的 F1 分数为 0.92,这是正类别的精确率和召回率的调和平均数。它提供了一个平衡的度量,用于评估模型对正面评论的性能。
解释模型预测的技术:
- 特征重要性分析:
对于逻辑回归模型,我们可以通过检查与每个特征相关的系数来解释特征重要性。较大的系数表示对预测类别的更强影响。我们可以识别出对正面或负面情感有贡献的重要单词或特征。
- 模型可解释性方法:
如 SHAP(SHapley Additive exPlanations)值、LIME(局部可解释模型无关解释)或 ELI5(解释如我 5 岁孩子般简单)等技术可以提供关于单个特征如何贡献于模型预测的见解。这些方法有助于理解复杂模型(如逻辑回归)的决策过程。
你想深入了解这些技术中的任何一项,或者有任何具体问题吗?告诉我我能如何进一步帮助你!
[回复结束]
我们将要求 ChatGPT 应用SHAP(SHapley Additive exPlanations)技术:
[提示]
首先让我们尝试使用 SHAP。
[提示结束]
我们应该收到如下回复,其中 ChatGPT 理解了请求并提供了 SHAP 的代码:
[提示回复]
当然!SHAP(SHapley Additive exPlanations)是一种解释机器学习模型单个预测的方法。它提供了一种理解特定实例中每个特征对模型输出的贡献的方式。
这是我们如何使用 SHAP 来解释逻辑回归模型对情感分析的预测:
使用 SHAP 进行模型解释:
- 安装 SHAP:
首先,让我们确保你已经安装了 SHAP。如果没有,你可以使用 pip 来安装它:
!pip install shap
- 准备数据:
我们需要为 SHAP 分析准备数据。这涉及到将文本数据转换为适合逻辑回归模型格式的数据。
- 解释模型预测:
我们将使用 SHAP 来解释测试集中单个实例的逻辑回归模型的预测。这将提供关于每个特征如何贡献于模型决策的见解。
让我们在代码中实现这些步骤:
# Importing necessary libraries
import shap
# Initialize the SHAP explainer with the logistic regression model and training data
explainer = shap.Explainer(lr_model, X_train_tfidf)
# Explain the model's predictions for a specific instance (e.g., the first instance in the test set)
instance_index = 0 # You can choose any instance index from the test set
shap_values = explainer.shap_values(X_test_tfidf[instance_index])
# Visualize the SHAP values
shap.summary_plot(shap_values, features=X_test_tfidf[instance_index], feature_names=tfidf_vectorizer.get_feature_names())
代码解释:
-
我们导入 SHAP 库。
-
我们使用逻辑回归模型(
lr_model)和 TF-IDF 转换后的训练数据(X_train_tfidf)初始化 SHAP 解释器。 -
我们使用解释器来计算测试集中特定实例的 SHAP 值(
X_test_tfidf[instance_index])。 -
最后,我们使用
shap.summary_plot()可视化 SHAP 值。
这种可视化将提供关于哪些特征(单词)对所选实例的模型预测贡献最大的见解。
你可以在你的 Jupyter Notebook 中运行此代码来可视化单个预测的 SHAP 值。如果你有任何问题或需要进一步的帮助,请告诉我!
[回复结束]
我们执行代码,出现错误:AttributeError: 'TfidfVectorizer' object has no attribute 'get_feature_names'。

图 12.3:SHAP 的属性错误
我们将使用反馈提示策略(LIFE 3.3)并提供错误给 ChatGPT:
[提示]
AttributeError: 'TfidfVectorizer' object has no attribute 'get_feature_names'
[提示结束]。
我们应该收到一个包含建议正确代码的回复。
[提示回复]
看起来出现了一个 AttributeError,因为 TfidfVectorizer 对象没有 get_feature_names() 方法。对此表示歉意。我们可以直接从矢量器的词汇属性中访问特征名称。
让我们纠正代码以从 TfidfVectorizer 词汇中检索特征名称:
# Get feature names from the TfidfVectorizer vocabulary
feature_names = tfidf_vectorizer.get_feature_names_out()
# Visualize the SHAP values
shap.summary_plot(shap_values, features=X_test_tfidf[instance_index], feature_names=feature_names)
通过使用 get_feature_names_out(),我们可以从 TfidfVectorizer 的词汇中访问特征名称。
您可以根据需要更新代码,并在您的 Jupyter Notebook 中再次尝试运行它。如果您遇到任何进一步的问题,请告诉我!
[结束响应]
我们执行了纠正后的代码,它运行正常并生成了输出图表:

图 12.4:代码执行 – SHAP 对模型输出的影响
-
像诸如“great”和“love”这样的词具有正的 SHAP 值,这意味着它们在数据中的存在可能会推动预测向积极结果发展,可能表明在情感分析模型中存在积极情绪。
-
这些词按照它们 SHAP 值的绝对大小进行排序,其中“great”具有最大的积极影响。
-
列表中的大多数词对预测的影响非常小,接近零,这表明它们的存在相对于基线不会显著影响预测。
-
一些词如“360”和“xbox”可能与特定主题(如游戏)相关,它们的 SHAP 值略为负,表明它们可能会降低模型预测的积极结果的可能性。
我们现在将要求 ChatGPT 提供 ELI5(Explain Like I’m 5)的代码:
[P提示]
接下来,我想尝试 ELI5 并确保代码是正确的,并且没有因为应用了 SHAP 技术而受到影响。
[结束提示]。
我们应该会从 ChatGPT 那里收到 ELI5 的代码:
[P提示响应]
当然!ELI5(Explain Like I’m 5)是另一个流行的模型解释库,用于解释机器学习模型。我们可以使用 ELI5 来理解我们的逻辑回归模型是如何进行预测的,并检查特征的重要性。
让我们将 ELI5 集成到我们的代码中,以解释逻辑回归模型的预测:
使用 ELI5 进行模型解释:
- 安装 ELI5:
如果您还没有安装 ELI5,可以使用 pip 进行安装:
!pip install eli5
- 解释模型预测:
我们将使用 ELI5 来解释测试集中单个实例的逻辑回归模型的预测。ELI5 提供了不同的方法来解释模型预测,包括特征重要性和文本解释。
让我们更新我们的代码,以包括用于模型解释的 ELI5:
# Importing necessary libraries
import eli5
# Explain the logistic regression model using ELI5
eli5.show_weights(lr_model, vec=tfidf_vectorizer, top=20) # Show top 20 most important features
代码的解释:
-
我们导入 ELI5 库。
-
我们使用 ELI5 的
show_weights函数来解释逻辑回归模型的预测。此函数显示与每个特征相关的权重(系数),表明它们在模型决策过程中的重要性。 -
我们指定向量器(
vec)参数,以确保 ELI5 能够根据 TF-IDF 向量化正确解释特征。
您可以在您的 Jupyter Notebook 中运行此更新后的代码,以查看对模型预测有贡献的前置特征。ELI5 提供了特征重要性的清晰可视化,使您能够深入了解模型的行为。
如果您有任何问题或遇到任何问题,请告诉我!
[响应结束]
我们执行 ChatGPT 提供的代码,该代码安装 eli5 库并生成以下输出:
输出完全无法理解,这很讽刺,因为它是应该被一个 5 岁孩子理解的。
让我们请 ChatGPT 至少提供一些代码,其中特征名称使用纯英文:
[提示]
我也能得到特征吗?X 个数字没有意义,并且肯定不是一个 5 岁孩子能理解的。
[提示结束]
我们应该收到以下类似的响应:
[提示响应]
当然!我们可以将特征索引(x 个数字)映射到 TF-IDF 向量器词汇表中的相应单词或术语,使它们更具可解释性。让我们更新代码以包括特征名称及其权重:
# Explain the logistic regression model using ELI5 with feature names
eli5.show_weights(lr_model, vec=tfidf_vectorizer, top=20, feature_names=tfidf_vectorizer.get_feature_names_out())
通过包含 TF-IDF 向量器的词汇表中的feature_names参数,ELI5 将显示实际的单词或术语,而不是特征索引。
在您的 Jupyter Notebook 中运行此更新后的代码,您将看到对正类预测有贡献的前置特征及其权重。如果您有任何问题或需要进一步的帮助,请告诉我!
[响应结束]
我们执行提供的代码,现在它提供了英文的特征名称:
这结束了特征 3,但由于我们的数据明显不平衡,让我们继续到下一个特征。
特征 4:处理不平衡数据
现在我们已经有一个基线模型,并且学会了如何使用模型可解释性技术,我们将现在看看如何处理数据不平衡:
[提示]
我希望应用一些技术来减轻数据不平衡,这一点从模型性能中可以明显看出。
Accuracy: 0.86
Classification Report:
precision recall f1-score support
negative 0.00 0.00 0.00 14
positive 0.86 1.00 0.92 86.
[提示结束]
我们应该收到以下类似的响应,其中 ChatGPT 理解请求并提供减轻数据不平衡的建议和代码:
[提示响应]
解决数据不平衡对于提高模型性能至关重要,尤其是在一个类别相对于另一个类别显著代表性不足的情况下。以下是一些我们可以应用以减轻数据不平衡的技术:
重采样技术:
-
过采样:通过生成合成样本(例如,使用 SMOTE - 合成少数类过采样技术)增加少数类的实例数量。
-
下采样:通过随机删除样本来减少多数类的实例数量。
算法技术:
-
类别权重:在模型训练期间为少数类的实例分配更高的权重,以惩罚误分类。
-
集成方法:利用处理类别不平衡效果良好的集成方法,例如平衡随机森林或 Easy Ensemble。
数据增强:
- 通过对现有数据点应用变换或扰动来生成少数类的新样本。
代价敏感学习:
- 明确将误分类的成本纳入模型训练过程中,以优先考虑少数类的正确分类。
让我们实现这些技术之一,例如调整类别权重,以解决我们的逻辑回归模型中的数据不平衡问题:
import numpy as np
# Calculate class weights
from sklearn.utils.class_weight import compute_class_weight
class_weights = compute_class_weight('balanced', classes=np.unique(y_train), y=y_train)
# Initialize and train the logistic regression model with class weights
lr_model_balanced = LogisticRegression(class_weight=dict(zip(np.unique(y_train), class_weights)))
lr_model_balanced.fit(X_train_tfidf, y_train)
# Predictions
y_pred_balanced = lr_model_balanced.predict(X_test_tfidf)
# Evaluate the model with balanced class weights
accuracy_balanced = accuracy_score(y_test, y_pred_balanced)
print("Accuracy with balanced class weights:", accuracy_balanced)
print("\nClassification Report with balanced class weights:")
print(classification_report(y_test, y_pred_balanced))
解释:
-
我们使用 scikit-learn 的 utils 中的
compute_class_weight计算类别权重。设置平衡模式会自动根据输入数据中的类别频率调整权重。 -
我们使用计算出的类别权重初始化并训练一个新的逻辑回归模型(
lr_model_balanced)。 -
我们使用平衡模型进行预测,并使用准确率和分类报告评估其性能。
运行此代码将训练带有平衡类别权重的逻辑回归模型,并在测试集上评估其性能。这应该有助于改善对不平衡数据的处理,并可能提高模型性能。如果你有任何问题或需要进一步的帮助,请告诉我!
[End of response]
我们将执行使用计算出的类别权重的代码,并查看它产生的影响。
你应该看到一个具有平衡权重的分类报告。
我们将输出结果给 ChatGPT,以便它为我们提供解释:
[Prompt]
带有平衡类别权重的准确率:0.81
带有平衡类别权重的分类报告:
precision recall f1-score support
negative 0.27 0.21 0.24 14
positive 0.88 0.91 0.89 86
accuracy 0.81 100
macro avg 0.57 0.56 0.57 100
weighted avg 0.79 0.81 0.80 100
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 提供了输出结果的解释:
[Prompt response]
看起来应用平衡类别权重提高了模型性能,尽管准确率略有下降。让我们解释结果:
-
准确率:准确率为 0.81 表明,模型正确分类了测试集中 81%的实例。
-
精确率和召回率:对于负类别,精确率显著提升至 0.27,这意味着当模型预测一条评论为负面时,它在 27%的时间内是正确的。然而,召回率相对较低,为 0.21,这表明模型未能捕获许多负面实例。
对于正类别,精确率和召回率都很高,精确率为 0.88,召回率为 0.91。这表明模型在识别正实例方面表现良好。
-
F1-score: 负类别的 F1 分数略有提升至 0.24,而正类别的分数保持较高,为 0.89。
-
支持:支持表示每个类别中的实例数量。测试集中有 14 个负类实例和 86 个正类实例。
总体而言,虽然准确率有所下降,但模型正确分类少数类(负面评论)的能力在平衡类别权重后显著提高。这表明在两个类别之间,精确率和召回率的平衡更好,从而得到一个更稳健的模型。
如果你还有任何进一步的问题或需要额外的帮助,请随时提问!
[End of response]
我们已经减少了不平衡,现在我们将继续到超参数调整的下一个功能。
功能 5:超参数调整
现在我们已经学会了如何处理不平衡数据,我们可以通过调整正则化强度、学习率、核参数等超参数来优化性能,并应用网格搜索或随机搜索等技术:
[Prompt]
我希望通过调整正则化强度、学习率、核参数等超参数来优化性能,并应用网格搜索或随机搜索等技术。
[End of prompt]
我们应该收到以下类似的响应:
[Prompt response]
当然!超参数调整对于优化模型性能至关重要。我们可以使用网格搜索或随机搜索等技术来搜索一系列超参数值,找到最佳性能的组合。
让我们以逻辑回归为例,开始实现使用网格搜索的超参数调整:
# Importing necessary libraries
from sklearn.model_selection import GridSearchCV
# Define hyperparameters grid
param_grid = {
'C': [0.01, 0.1, 1.0, 10.0], # Regularization strength (smaller values indicate stronger regularization)
'solver': ['liblinear', 'lbfgs'], # Optimization algorithm
}
# Initialize logistic regression model
lr_model_tuned = LogisticRegression()
# Initialize GridSearchCV with logistic regression model and hyperparameters grid
grid_search = GridSearchCV(lr_model_tuned, param_grid, cv=5, scoring='accuracy')
# Perform grid search
grid_search.fit(X_train_tfidf, y_train)
# Get the best hyperparameters
best_params = grid_search.best_params_
print("Best Hyperparameters:", best_params)
# Use the best model from grid search
best_lr_model = grid_search.best_estimator_
# Predictions
y_pred_tuned = best_lr_model.predict(X_test_tfidf)
# Evaluate the tuned model
accuracy_tuned = accuracy_score(y_test, y_pred_tuned)
print("Accuracy with tuned model:", accuracy_tuned)
print("\nClassification Report with tuned model:")
print(classification_report(y_test, y_pred_tuned))
说明:
-
我们定义了一个超参数网格(
param_grid)来搜索。在这个例子中,我们改变了正则化强度(C)和求解算法。 -
我们初始化一个逻辑回归模型(
lr_model_tuned)并使用GridSearchCV进行网格搜索。 -
网格搜索将使用交叉验证(
cv=5)评估模型在不同超参数组合下的性能。 -
在网格搜索之后,我们检索最佳超参数(
best_params)和最佳模型(best_lr_model)。 -
最后,我们使用最佳模型进行预测并评估其性能。
你可以调整超参数网格(param_grid)以包括其他超参数,如学习率和核参数,以适应不同的模型。此外,你还可以探索随机搜索或其他优化技术进行超参数调整。
随意在你的 Jupyter Notebook 中运行此代码,通过超参数调整优化逻辑回归模型的表现。如果你有任何问题或需要进一步的帮助,请告诉我!
[End of response]
我们执行代码,它运行正常,并使用 GridSearchCV 找到最佳参数。
你应该会看到一个带有调整模型的分类报告。
超参数调整在准确率和类别特定指标方面并没有显著提高模型性能。让我们来解释结果:
-
最佳超参数:网格搜索确定了最佳超参数为
C=0.01和solver='liblinear'。这些超参数表明了低正则化强度(C=0.01)以及使用了 liblinear 求解器算法。 -
准确率:调整后的模型的准确率保持在 0.86,与基线模型相同。这表明超参数调整没有导致整体准确率的显著提高。
-
精确率和召回率:正负类别的精确率、召回率和 F1 分数与基线模型保持不变。这表明在超参数调整后,模型正确分类每个类别的实例的能力没有提高。
-
支持:支持值表示测试集中每个类别的实例数量,与之前相同。
让我们继续到最后一个特征,并实验特征表示。
特征 6:实验特征表示
让我们指示 ChatGPT 为我们提供不同特征表示的代码:
[Prompt]
我想比较使用不同特征表示(如词袋、TF-IDF 或词嵌入)训练的模型的性能,以确定情感分类中最有效的途径。
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 提供了词袋、TF-IDF 和词嵌入的代码:
[Prompt response]
比较使用不同特征表示训练的模型的性能是确定情感分类中最有效方法的有价值方法。我们可以使用词袋、TF-IDF 和词嵌入表示来训练模型,并使用相同的评估指标来评估它们的性能。让我们一步一步地完成这个过程:
-
词袋表示法:
- 我们可以使用词袋方法表示文本数据,其中每个文档由一个向量表示,指示词汇表中每个词的频率。
-
TF-IDF 表示法:
- TF-IDF(词频-逆文档频率)表示不仅考虑了文档中一个词的频率,还考虑了它在整个语料库中的重要性。
-
词嵌入表示法:
- 词嵌入将词表示为连续向量空间中的密集向量,捕捉词之间的语义关系。
下面是如何比较使用这些不同特征表示训练的模型性能的方法:
-
预处理数据:对每个特征表示方法进行分词、小写化、去除停用词,并执行任何必要的文本预处理步骤。
-
训练模型:使用每种特征表示方法(词袋、TF-IDF、词嵌入)训练逻辑回归模型。
-
评估性能:使用相同的评估指标(准确率、精确率、召回率、F1 分数)评估每个模型的性能。
-
比较结果:比较使用不同特征表示训练的模型的性能,以确定最有效的情感分类方法。
让我们首先通过使用词袋模型、TF-IDF 和词嵌入表示来实现逻辑回归模型:
词袋模型表示:
from sklearn.feature_extraction.text import CountVectorizer
# Initialize CountVectorizer
bow_vectorizer = CountVectorizer(max_features=5000) # You can adjust max_features as needed
# Transform text data into bag-of-words representation
X_train_bow = bow_vectorizer.fit_transform(X_train)
X_test_bow = bow_vectorizer.transform(X_test)
# Train logistic regression model with bag-of-words representation
lr_model_bow = LogisticRegression()
lr_model_bow.fit(X_train_bow, y_train)
# Evaluate model performance
y_pred_bow = lr_model_bow.predict(X_test_bow)
accuracy_bow = accuracy_score(y_test, y_pred_bow)
print("Accuracy with bag-of-words representation:", accuracy_bow)
# Add other evaluation metrics if needed
TF-IDF 表示:
from sklearn.feature_extraction.text import TfidfVectorizer
# Initialize TfidfVectorizer
tfidf_vectorizer = TfidfVectorizer(max_features=5000) # You can adjust max_features as needed
# Transform text data into TF-IDF representation
X_train_tfidf = tfidf_vectorizer.fit_transform(X_train)
X_test_tfidf = tfidf_vectorizer.transform(X_test)
# Train logistic regression model with TF-IDF representation
lr_model_tfidf = LogisticRegression()
lr_model_tfidf.fit(X_train_tfidf, y_train)
# Evaluate model performance
y_pred_tfidf = lr_model_tfidf.predict(X_test_tfidf)
accuracy_tfidf = accuracy_score(y_test, y_pred_tfidf)
print("Accuracy with TF-IDF representation:", accuracy_tfidf)
# Add other evaluation metrics if needed
词嵌入表示:
对于词嵌入表示,我们首先需要使用 Word2Vec、GloVe 或 fastText 等技术将文本数据转换为词嵌入。我们将为此目的使用预训练的词嵌入。
# Assuming you have pre-trained word embeddings loaded (e.g., GloVe)
# Convert text data into word embeddings representation
# You may need to tokenize and pad sequences before applying word embeddings
# Train logistic regression model with word embeddings representation
lr_model_word_embeddings = LogisticRegression()
lr_model_word_embeddings.fit(X_train_word_embeddings, y_train)
# Evaluate model performance
y_pred_word_embeddings = lr_model_word_embeddings.predict(X_test_word_embeddings)
accuracy_word_embeddings = accuracy_score(y_test, y_pred_word_embeddings)
print("Accuracy with word embeddings representation:", accuracy_word_embeddings)
# Add other evaluation metrics if needed
在使用不同的特征表示训练和评估模型后,您可以使用准确率或其他评估指标来比较它们的性能。根据结果,您可以确定哪种特征表示方法对于情感分类是最有效的。
随意在你的 Jupyter Notebook 中运行这些代码,如果需要进一步的帮助或有任何问题,请告诉我!
[响应结束]**
我们将执行代码并比较词袋模型、TF-IDF 和词嵌入的准确性。代码对前两种方法运行良好,但对于词嵌入则出现错误。

图 12.5:词袋模型

图 12.6:TF-IDF

图 12.7:词嵌入 – 错误
因此,我们将指示 ChatGPT 提供修正的词嵌入代码,我们还将提供我们之前已经进行了分词的上下文,但没有嵌入:
[提示]**
NameError Traceback (most recent call last) <ipython-input-20-8a483831a6f9> in <cell line: 7>() 5 # Train logistic regression model with word embeddings representation 6 lr_model_word_embeddings = LogisticRegression() ----> 7 lr_model_word_embeddings.fit(X_train_word_embeddings, y_train) 8 9 # Evaluate model performance NameError: name 'X_train_word_embeddings' is not defined. We haven't pretrained word embeddings but have tokenized it earlier. We tokenized the text using nltk.word_tokenize(), converted tokens to lowercase, removed stopwords and punctuation using list comprehensions, and then joined the tokens back into text. Finally, we applied the preprocess_text() function to the 'Review_body' column of the DataFrame and stored the preprocessed text in a new column called 'Cleaned_Review'.
[提示结束]**
我们应该收到类似以下修改后的代码的响应:
[提示响应]**
感谢您提供澄清。由于我们已经预处理了文本数据并进行了分词,我们可以使用词嵌入来表示它,而无需预训练的词嵌入。我们将使用 Word2Vec 或 Doc2Vec 等技术从分词文本生成词嵌入。
让我们继续使用 Word2Vec 来表示文本数据,然后训练一个逻辑回归模型:
使用 Word2Vec 的词嵌入表示:
from gensim.models import Word2Vec
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.base import BaseEstimator, TransformerMixin
# Define a custom transformer to tokenize the text data
class TokenizerTransformer(BaseEstimator, TransformerMixin):
def fit(self, X, y=None):
return self
def transform(self, X):
return [text.split() for text in X]
# Initialize and fit Word2Vec model on tokenized text data
w2v_model = Word2Vec(sentences=TokenizerTransformer().fit_transform(X_train), min_count=1)
# Define a function to average word vectors for each document
def average_word_vectors(words, model, vocabulary, num_features):
feature_vector = np.zeros((num_features,), dtype="float64")
nwords = 0.
for word in words:
if word in vocabulary:
nwords = nwords + 1.
feature_vector = np.add(feature_vector, model.wv[word])
if nwords:
feature_vector = np.divide(feature_vector, nwords)
return feature_vector
# Define a custom transformer to convert tokenized text data into word embeddings representation
class WordEmbeddingsTransformer(BaseEstimator, TransformerMixin):
def __init__(self, model):
self.model = model
self.num_features = model.vector_size
self.vocabulary = set(model.wv.index_to_key)
def fit(self, X, y=None):
return self
def transform(self, X):
return np.array([average_word_vectors(words, self.model, self.vocabulary, self.num_features) for words in X])
# Convert tokenized text data into word embeddings representation
X_train_word_embeddings = WordEmbeddingsTransformer(w2v_model).fit_transform(TokenizerTransformer().fit_transform(X_train))
X_test_word_embeddings = WordEmbeddingsTransformer(w2v_model).transform(TokenizerTransformer().transform(X_test))
# Train logistic regression model with word embeddings representation
lr_model_word_embeddings = LogisticRegression()
lr_model_word_embeddings.fit(X_train_word_embeddings, y_train)
# Evaluate model performance
y_pred_word_embeddings = lr_model_word_embeddings.predict(X_test_word_embeddings)
accuracy_word_embeddings = accuracy_score(y_test, y_pred_word_embeddings)
print("Accuracy with word embeddings representation (Word2Vec):", accuracy_word_embeddings)
# Add other evaluation metrics if needed
说明:
-
我们定义了一个自定义转换器
TokenizerTransformer来分词文本数据。 -
我们使用 Word2Vec 模型从分词的文本数据生成词嵌入。
-
我们定义了一个函数
average_word_vectors来计算每个文档的词向量平均值。 -
我们定义了另一个自定义转换器
WordEmbeddingsTransformer来将分词的文本数据转换为词嵌入表示。 -
我们使用从 Word2Vec 获得的词嵌入表示来训练逻辑回归模型。
-
最后,我们评估模型的性能并打印准确率。
你可以在你的 Jupyter Notebook 中运行此代码,使用 Word2Vec 词嵌入表示文本数据,并训练一个逻辑回归模型。如果你需要进一步的帮助,请告诉我!
[响应结束]**
这次,代码运行正常:

图 12.8:使用词嵌入的准确率
这就结束了 ChatGPT 免费版本的最后特性。
现在,我们将尝试使用 ChatGPT-4 或 ChatGPT Plus 使用这些特征。
使用 ChatGPT-4 或 ChatGPT Plus 构建情感分析模型以准确分类亚马逊评论
ChatGPT Plus 或 GPT-4 包括上传数据集、分析数据集以及使用简单的库(如 Python 和 matplotlib)时产生结果的能力。在撰写本章时,它有一个名为数据分析的 AI 助手,由 OpenAI 在移动应用中使用或在使用浏览器版本选择 GPT-4 时提供。
让我们探索它与免费版本的区别,一个特性一个特性。
特性 1:数据预处理和特征工程
让我们为基线模型制定初始提示:
[提示]**
我想为亚马逊评论数据集(TAG 1.1)创建一个简单的分类模型进行情感分析。在这里上传 CSV 格式的数据集(PIC 2.3)
它应该包括以下步骤(TAG 1.2),而我是一个初级用户(PIC 2.1),所以请一次提供一个步骤,并等待用户反馈(PIC 2.2)
-
数据预处理:分词、小写化、去除停用词和标点符号,以及特征工程(例如,TF-IDF 编码,词嵌入)。
-
模型选择:选择基线机器学习模型,如逻辑回归、朴素贝叶斯或支持向量机(SVM)。
-
模型评估和解释。
-
处理不平衡数据。
-
超参数调整。
-
尝试特征表示。
[提示结束]**
你应该会收到以下类似的响应,其中 ChatGPT-4 已加载数据集:
[提示响应]**
太好了!我们将从预处理亚马逊评论数据集的数据开始。以下是我们可以如何处理预处理步骤:
-
加载数据:我们首先加载数据集以了解其结构。
-
分词:这涉及到将文本分解成单个单词或术语。
-
小写化:将所有文本转换为小写以确保一致性。
-
去除停用词:停用词是常见的单词(如“和”,“the”等),在处理自然语言数据之前通常会被过滤掉,因为它们携带的信息意义较小。
-
去除标点符号:标点符号在处理文本时通常不会增加太多额外信息。
-
特征工程:我们将应用 TF-IDF 编码,这有助于理解一个词在文档中相对于语料库的重要性。
让我们先加载数据并查看其结构。我将编写一个 Python 脚本来加载数据并显示前几条记录,以便更好地理解其列和内容。让我们继续这一步,一旦我们看到数据,我们就可以继续到下一个预处理任务。以下是脚本:
我们已成功加载数据集,以下是其结构的快速查看:
-
market_place,customer_id,review_id,product_id,product_parent:这些列是关于评论和产品的标识符和元数据。 -
product_title,product_category:关于产品的信息。 -
star_rating,helpful_votes,total_votes:评分和投票数据。 -
verified_purchase:购买是否经过验证。 -
review_headline,review_body:评论的实际文本。 -
review_date:评论的日期。 -
sentiments:这似乎是情感分析的标签(正面,负面)。
由于你的重点是情感分析,我们主要关注的列将是review_body和sentiments。
现在,让我们继续进行预处理步骤:
-
分词
-
转换为小写
-
去除停用词
-
去除标点符号
我们将使用 nltk 库来处理这些步骤,这是 Python 中用于文本处理的常用库。如果你准备好了,我会开始编写这些预处理步骤的代码!
[End of response]
我们可以看到数据集已成功加载,ChatGPT-4 正在等待下一步,用户的反馈(LIFE 3.3)。因此,我们将指示 ChatGPT-4 继续到下一步:
[Prompt]
让我们继续进行数据预处理。
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 理解请求并执行数据预处理的代码:
[Prompt response]
太好了!让我们逐一进行数据预处理任务。以下是我们的操作步骤:
-
分词:这将把
review_body文本分割成单个单词或标记。 -
转换为小写:将所有标记转换为小写以标准化文本。
-
去除停用词:移除可能对情感分析无用的常见单词。
-
去除标点符号:去除标点符号,因为在基于文本的模型中通常不需要。
我将使用自然语言工具包(nltk)进行这些步骤。让我们首先安装必要的库并设置初始的预处理函数。以下是开始代码:
看起来由于这个环境中的网络限制,从 NLTK 库下载必要的资源出现了问题。我将修改方法,为了演示目的,手动定义一组常见的停用词。让我们继续简化版本的文本预处理:
-
分词
-
转换为小写
-
去除简化的停用词集
-
去除标点符号
这里是手动处理这些步骤而不需要下载额外资源的修改后的脚本:
文本预处理已成功使用简化方法应用。以下是原始文本和处理后文本的比较:
-
原始: “作为一个对小麦、乳制品、鸡蛋、坚果...过敏的家庭”
-
处理: [‘a’,‘family’,‘allergic’,‘wheat’,‘dairy’,‘eggs’,‘nuts’]
如您所见,我们已经移除了停用词、标点符号,并对文本进行了标记化和小写化处理。现在处理后的数据更适合输入到机器学习模型中进行情感分析。
下一步将涉及将这些标记转换为机器学习算法可以使用的数值特征。我们可以使用 TF-IDF 编码或词嵌入等方法来完成此操作。请告诉我你是否准备好进入特征工程步骤,或者是否有任何其他需要在预处理中调整的地方!
[响应结束]**
从响应中我们可以看到,ChatGPT-4 首先尝试使用 NLTK 库,但由于限制,它使用一组常见的单词简化了方法。它还提供了一个如何处理的示例。我们现在将指示 ChatGPT-4 继续操作:
[提示]**
让我们继续。
[提示结束]**
我们应该收到以下响应,其中 ChatGPT-4 已实现 TF-IDF:
[提示响应]**
太好了!让我们继续进行特征工程步骤。在情感分析中,将文本数据转换为机器学习算法可以理解的数值形式的一种常见技术是词频-逆文档频率 (TF-IDF) 方法。以下是一个简要的说明:
-
词频 (TF): 衡量一个术语在文档中出现的频率。由于文档的长度可能不同,通常将其除以文档长度(文档中术语的总数)。
-
逆文档频率 (IDF): 衡量一个术语在语料库(所有文档的集合)中的重要性。它有助于降低在文档中频繁出现且信息量较小的术语的权重。
实现 TF-IDF 的步骤:
-
将标记列表转换为字符串: 机器学习算法通常需要输入特征为字符串或数字。我们将把我们的标记列表转换回字符串格式。
-
TF-IDF 向量化: 使用 scikit-learn 中的
TfidfVectorizer将文本文档转换为 TF-IDF 特征矩阵。
我将现在编写这些步骤,首先将我们的标记列表转换回字符串,然后应用 TF-IDF 向量化。让我们这样做:
我们已经成功将处理后的文本转换为一个 TF-IDF 特征矩阵。以下是一些详细信息:
-
矩阵形状: TF-IDF 矩阵包含 500 篇文档和 3778 个特征。这意味着在应用预处理和向量化步骤后,文档中有 3778 个独特的术语。
-
示例特征: 我们特征集中的前几个术语包括‘能力’,‘能’,‘关于’,‘以上’等。
现在可以使用这个 TF-IDF 矩阵作为各种机器学习模型的输入,以执行情感分析。
接下来,我们可以继续选择和训练使用此特征表示的基线模型。情感分析中初始模型的常见选择包括逻辑回归、朴素贝叶斯和支持向量机(SVM)。请告诉我您想从哪个模型开始,或者如果您有任何偏好,请告诉我!
[响应结束]**
我们可以看到 ChatGPT-4 已经实现了 TF-IDF,有 500 个文档和 3,778 个特征。我们现在将进入基线模型创建阶段。
功能 2:模型选择和基线训练
我们现在将指示 ChatGPT-4 继续进行模型选择:
[提示]**
让我们继续使用逻辑回归。
[提示结束]**
我们应该收到以下类似响应,其中 ChatGPT 理解请求并执行逻辑回归的代码。
[提示响应]**
完美!逻辑回归是分类任务(如情感分析)的一个很好的起点,因为它简单且通常为性能比较提供了一个良好的基线。
实现逻辑回归的步骤:
-
准备数据:我们将数据分为训练集和测试集以评估模型性能。
-
训练模型:我们将使用
scikit-learn中的逻辑回归从训练集训练模型。 -
评估模型:我们将测试模型在测试集上的表现,以查看其预测情感的能力。
让我们从准备数据开始,然后训练逻辑回归模型。以下是执行此操作的代码:
逻辑回归模型已经训练和评估。以下是其性能的总结:
-
准确度:86%
-
精确度、召回率和 F1 分数:这些指标提供了更详细的视图。对于‘正面’类别,我们看到了 86%的精确度和 100%的召回率,分别导致 F1 分数为 92%。然而,对于‘负面’类别,精确度、召回率和 F1 分数都是 0。这表明模型未能正确预测任何‘负面’类别的实例。
这种差异通常是由于类别不平衡造成的,其中一个类别(在本例中为‘正面’)比另一个类别(‘负面’)频繁得多,导致模型偏向多数类别。
下一步:
-
处理不平衡数据:我们可以探索诸如对少数类别进行过采样、对多数类别进行欠采样或调整决策阈值等技术。
-
超参数调整:我们可以调整逻辑回归模型的参数,以潜在地提高性能。
-
实验特征表示:尝试 TF-IDF 的不同设置或使用其他类型的文本表示,如词嵌入。
您是否想进一步探索这些下一步,例如解决数据集中的不平衡问题,或者您想调整其他内容?
[响应结束]**
功能 3:模型评估和解释
现在我们已经选择了逻辑回归模型,并评估了基线性能,让我们来解释结果。
我们将要求 ChatGPT-4 解释指标并使用特征重要性分析和模型可解释性等技术提供结果解释:
[Prompt]
请解释指标并提供使用特征重要性分析和模型可解释性等技术的结果解释。
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 理解了请求并提供了解释和特征重要性分析:
[Prompt 响应]
让我们深入了解评估指标并探讨一些解释逻辑回归模型的技术:
评估指标解释:
-
精确度:这衡量了正面预测的准确性。形式上,它是真正预测到总预测正面的比率。高精确度表示假阳性率低。
-
召回率(灵敏度):这是模型找到所有相关案例(所有真正正面)的能力。它是真正正面与实际总正面的比率。
-
F1 分数:F1 分数是精确度和召回率的调和平均数。当类别不平衡时,这是一个有用的度量,因为它在精确度和召回率之间取得平衡。
-
准确度:这是正确预测的观察值与总观察值的比率。对于不平衡的类别来说,它通常不是一个好的度量,因为它可能会误导。
结果解释:
-
准确度为 86%听起来不错,但这是误导性的,因为模型在少数类别(“负面”)上的表现很差。
-
该模型对“正面”类别的召回率(100%)很高,表明它识别了大多数正面实例,但它完全无法识别“负面”类别的实例(召回率为 0%)。
特征重要性分析:
- 在逻辑回归中,模型的系数可以提供对特征(在这种情况下为单词)重要性的洞察。系数的绝对值越高,表明该特征对模型所做的决策越重要。
让我们提取模型的系数,看看哪些单词(特征)对预测影响最大:
以下是影响情感分类的前 10 个特征(单词)及其相应的系数:
-
“not”:系数为-1.87,对情感预测有强烈的负面影响。它表明,在评论中出现“not”会显著地将预测推向负面情感。
-
“great”:系数为 1.65,具有强烈的正面影响,表明包含此词的评论很可能被分类为正面。
-
“love”:系数为 1.54,另一个强烈的正面指标。
-
“good”:系数为 0.99,对情感有正面影响。
-
“but”:系数为-0.95,对情感有负面影响。
-
“very”:系数为 0.88,通常用于积极语境中。
-
“was”:系数为-0.86,显示出负面的影响,可能在失望的语境中使用。
-
“cheap”:系数为-0.81,通常与负面情绪相关。
-
“there”:系数为-0.81,表明当使用这个词时,存在负面语境。
-
“dont”(可能是指“don’t”):系数为-0.79,另一个强烈的负面指标。
模型可解释性:
这些系数直接揭示了模型如何根据文本中某些单词的存在来做出决策。具有正系数的单词会增加评论被分类为积极的可能性,而具有负系数的单词则推动向负面分类。
如果你想进行更深入的解读,可以应用 LIME(局部可解释模型无关解释)或 SHAP(SHapley 加性解释)等技术,以提供对特定预测的更细致的见解。
你是否想深入了解特定领域,例如处理数据不平衡或探索针对特定预测的模型可解释性技术,如 LIME 或 SHAP?
[End of response]
ChatGPT-4 无法安装新的库,如 SHAP,因此我们将在这里完成特征分析,并继续处理数据不平衡的下一步。
特征 4:处理数据不平衡
现在,我们将指示 ChatGPT-4 处理数据不平衡。
[Prompt]
让我们继续处理数据不平衡。
[End of prompt]
我们应该收到以下类似的响应:
[Prompt response]
在模型训练中处理数据不平衡至关重要,尤其是在像你这样的数据集严重偏向某一类的情况下。这种偏差可能导致模型主要预测多数类,正如我们在逻辑回归模型中看到的那样。
处理数据不平衡的策略
-
重采样技术:
-
对少数类进行上采样:这涉及随机复制少数类中的示例。
-
对多数类进行下采样:这减少了多数类中的示例数量,以与少数类平衡。
-
-
合成数据生成:
- SMOTE(合成少数类过采样技术):这项技术从少数类生成合成样本,而不是创建副本。
-
修改算法:
-
调整决策阈值:移动决策阈值可以帮助提高少数类的召回率。
-
使用天生擅长处理不平衡数据的算法,例如决策树或随机森林。
-
-
成本敏感学习:
- 给予少数类别的误分类更高的成本,使模型更加关注它。
鉴于我们正在使用逻辑回归,一个良好的起点可能是应用 SMOTE 为少数类生成合成样本,看看是否可以提高模型性能。我们还可以调整决策阈值,使其对少数类更加敏感。
首先,我们将对训练数据应用 SMOTE 方法,然后重新训练 Logistic Regression 模型。我将按照以下步骤进行:
看起来包含 SMOTE 的imblearn库在这个环境中不可用。鉴于这一限制,我们可以探索其他选项:
-
减少多数类的样本量:这将减少“正例”类的大小以匹配“负例”类的大小,尽管这可能导致丢失可能有用的数据。
-
调整决策阈值:而不是重采样,我们可以调整将评论分类为正例或负例的阈值,使模型对少数类更加敏感。
让我们继续调整决策阈值。这种方法涉及修改模型将样本分类为“正例”的概率阈值。默认情况下,此阈值设置为 0.5。降低此阈值可以帮助识别更多样本为“负例”,从而可能提高少数类的召回率。
我将计算类别预测的概率,然后调整阈值以观察其对分类指标的影响。让我们继续按照这种方法进行:
将决策阈值调整为 0.4 并没有改变分类结果。模型仍然预测所有评论为“正例”,并且与之前的指标相同,这表明我们可能需要显著降低阈值或考虑其他方法,因为对“正例”类的偏见很强。
替代方案和考虑因素:
-
进一步降低阈值:我们可以继续尝试更低的阈值,尽管这可能会导致更高的误报。
-
更改模型或特征:由于逻辑回归在处理这种不平衡性方面可能固有限制,因此尝试不同的模型或特征集可能有益。
-
手动数据重采样:如果像
imblearn这样的自动化重采样库不可用,我们可以手动减少“正例”类的样本量或增加“负例”类的样本量,尽管后者没有 SMOTE 中合成样本生成的复杂性。
你想尝试进一步降低阈值,还是我们应该探索手动重采样或可能不同的建模方法?
[响应结束]
我们看到调整决策阈值并没有起到太大作用,因此我们将要求 ChatGPT-4 继续到超参数调整的下一个特征。
特征 5:超参数调整
我们将要求 ChatGPT-4 进行超参数调整:
[提示]
让我们继续进行超参数调整。
[提示结束]
我们应该收到以下类似的响应:
[提示响应]
超参数调整是提高机器学习模型性能的关键步骤。对于 Logistic Regression,一些可以调整的关键超参数包括:
-
C(正则化强度的倒数):较小的值指定更强的正则化。
-
求解器:优化问题中使用的算法(例如,
liblinear,sag,saga,newton-cg等)。 -
惩罚项:指定在惩罚中使用的范数(例如,
l1,l2)。
我们可以使用带有交叉验证的网格搜索系统地探索多个参数值的组合。这种方法可以帮助确定这些超参数的最佳设置。
实现超参数调整
我们将使用scikit-learn中的GridSearchCV来执行超参数调整。我们将定义一个C和penalty的潜在值网格,然后使用这些值的组合来训练模型,以找到最佳组合。
让我们设置并执行网格搜索:
通过网格搜索进行的超参数调整已确定逻辑回归模型的最佳参数:
-
C (正则化强度的逆): 100,表示正则化程度较低。
-
惩罚项:
l2,这是正则化中使用的 L2 范数。 -
求解器:使用
sag,这是一种适合大数据集的优化算法。
在这些设置下获得的最佳交叉验证准确率为 80.5%。这表明减少正则化(C 的值更高)有助于模型更好地拟合数据。
下一步:
-
使用最佳参数评估模型:我们现在可以在测试集上评估这个优化模型,以查看其性能与之前的配置相比如何。
-
进一步实验:根据结果,我们可能考虑进一步调整或尝试不同的建模技术。
我们是否应该通过在测试集上评估这些优化参数来继续进行,或者您是否想进一步探索任何特定内容?
[响应结束]**
我们将指示 ChatGPT-4 在测试集上使用优化参数评估模型:
[提示]**
当然,进行评估。
[提示结束]**
我们应该收到与以下类似的 ChatGPT-4 的响应:
[响应提示]**
使用最佳参数评估优化后的逻辑回归模型得到以下结果:
-
总体准确率:84%
-
“负面”类别的精确率和召回率:精确率为 33%,召回率为 14%。
-
“正面”类别的精确率和召回率:精确率为 87%,召回率为 95%。
解释:
-
与初始模型(准确率为 86%)相比,整体准确率略有下降,但我们看到在处理“负面”类别方面有所改进:
-
模型现在可以正确识别一些负面评论,尽管性能(14%召回率)仍然相当低,这表明模型可以检测到一些负面实例,但遗漏了许多。
-
负面评论的精确度为 33%,这意味着当模型预测评论为负面时,大约有三分之一的时间是正确的。
关键点:
-
模型对负面评论的敏感性:在识别负面评论方面略有改进,这可能是由于更少的正则化(更高的 C 值)允许模型更紧密地拟合少数类。
-
类别之间的平衡:对两个类别的敏感性(宏平均分数)仍然不理想,这表明可能需要进一步的调整或不同的建模方法,以充分捕捉少数类的特征。
进一步行动:
-
实验其他模型:鉴于逻辑回归在处理不平衡数据集时的固有局限性,考虑其他模型如决策树、随机森林或甚至集成方法可能会得到更好的结果。
-
调整阈值或重新采样数据:这些操作可以重新考虑或与不同的建模技术结合,以更好地处理类别不平衡问题。
你想探索替代模型或技术,还是想专注于项目的其他方面?
[回复结束]**
我们已经看到 ChatGPT-4 评估了性能并解释了结果。现在我们将转向特征表示。
特征 6:实验特征表示
让我们指示 ChatGPT 为我们提供不同特征表示的代码:
[提示]**
我想比较使用不同特征表示(如词袋、TF-IDF 或词嵌入)训练的模型性能,以确定情感分类中最有效的途径。
[提示结束]**
我们应该收到以下类似的回复,其中 ChatGPT 为我们提供了词袋、TF-IDF 和词嵌入的代码:
[提示回复]**
比较不同的特征表示是确定情感分类中最有效方法的一种优秀方法。让我们概述我们将评估的特征表示:
-
词袋(BoW):计算文档中单词的频率。
-
TF-IDF:我们已经实现了这个。它不仅考虑了词频,还考虑了词对于一个文档的独特性。
-
词嵌入:使用 Word2Vec 或 GloVe 的预训练嵌入等模型,在密集向量空间中表示单词,捕获语义含义。
逐步方法:
-
实现词袋:我们将使用 scikit-learn 的
CountVectorizer将文本文档转换为词计数的矩阵。 -
使用词嵌入:我们将使用 Gensim 的简单 Word2Vec 模型,或者使用预训练的 GloVe 嵌入以实现更直接的实现。
-
在每个表示上训练逻辑回归:为了保持一致性,我们将对每个特征集使用逻辑回归。
-
比较性能:使用准确率、精确率、召回率和 F1 分数评估每个模型。
让我们从实现词袋模型开始。之后,我们将继续到词嵌入。以下是词袋模型的代码:
使用词袋(BoW)特征表示训练的 Logistic Regression 模型具有以下性能指标:
-
总体准确率:86%
-
“负面”类别的精确率和召回率:精确率为 50%,召回率为 21%。
-
“正面”类别的精确率和召回率:精确率为 88%,召回率为 97%。
-
BoW 性能解释:
与初始的 TF-IDF 模型相比,BoW 模型在处理负面类别方面略有改进,尽管召回率仍然很低,但精确率有所提高。这表明,虽然模型比以前更准确地识别负面评论,但它仍然错过了许多负面案例。
接下来,为了简化,我们将使用预训练的 GloVe 词嵌入来实现词嵌入特征表示。这将为我们提供一个密集的表示,捕捉语义含义,这可能会提高分类效果,特别是对于负面评论。
实现词嵌入:
-
加载预训练的 GloVe 词嵌入:我们将使用嵌入将文本数据转换为密集向量形式。
-
平均词嵌入:对于每个评论,我们将计算词嵌入的平均值,形成一个代表整个评论的单个向量。
让我们继续加载和应用 GloVe 嵌入进行情感分类:
看起来我们遇到了一个限制,无法访问 Gensim 下载器,这是用于加载预训练的 GloVe 嵌入的,因为环境没有互联网访问。
替代方法:
由于我们无法使用预训练的 GloVe 词嵌入,让我们考虑一个更简单的词嵌入实现,直接在我们的数据集上使用 Word2Vec 模型。这将使我们能够根据评论中的词汇创建自己的嵌入。
让我们继续在处理过的评论上训练 Word2Vec 模型,然后使用嵌入来表示每个文档。我们将这样操作:
使用 Word2Vec 嵌入训练的 Logistic Regression 模型产生了以下结果:
-
总体准确率:86%
-
“负面”类别的精确率和召回率:精确率和召回率均为 0%。模型未能正确识别任何负面类别的实例。
-
“正面”类别的精确率和召回率:精确率为 86%,召回率为 100%。
Word2Vec 模型的解释:
Word2Vec 模型在准确率和类别特定指标方面与原始 TF-IDF 模型的表现相似。这种相似性表明,模型仍然严重偏向多数类(“正面”)。
负面类别的零召回率和精确率表明,尽管使用了语义丰富的词嵌入,但数据集的不平衡仍然严重影响了模型的预测。
总结和建议:
-
BoW vs. TF-IDF vs. Word2Vec: 在负类别的精确度方面,词袋模型(Bag-of-Words)相对于 TF-IDF 和 Word2Vec 模型略有提升。然而,所有模型在负类别的召回率方面都存在困难,突显了类别不平衡的影响。
-
进一步改进: 尝试集成方法、更复杂的神经网络(例如,用于文本的 LSTM、CNN)或进一步的平衡技术可能有必要来提高负类别的召回率。
-
替代特征工程: 可以探索额外的特征工程方法,如 n-gram,以捕捉更多上下文,这可能会帮助更准确地识别负面情绪。
您是否想进一步探索调整,例如尝试不同的机器学习模型或额外的平衡技术,或者您想专注于您项目的另一个方面?
[回复结束]
这就结束了我们关于如何使用 ChatGPT-4 或 ChatGPT Plus 来实现和执行亚马逊产品评论数据集的情感分析的章节。
作业
生成合成数据以处理数据不平衡。
挑战
尝试提高模型性能,使其超过 86%,并且请随意使用任何技术。
摘要
概述的策略提供了一个系统的方法来实验不同的技术,用于构建和训练使用亚马逊产品评论数据集的情感分析模型。每个步骤都详细说明,代码生成和用户交互都针对初学者友好,确保了一个全面的学习之旅。
任务从构建基线情感分析模型开始。初始步骤包括通过分词、小写化、去除停用词和标点符号来预处理文本数据,然后使用 TF-IDF 编码进行特征工程。
详细 Python 代码以 Jupyter Notebook 格式提供,包括每个操作的说明,确保即使是初学者也能舒适地跟随。
我们探索了基线机器学习模型,从逻辑回归开始。模型被训练和评估,揭示了显著的类别不平衡,导致预测偏向多数类。这一阶段包括详细的指标分析,如准确率、精确率、召回率和 F1 分数,增强了模型性能的理解,而不仅仅是准确率。
为了解决数据不平衡,讨论了调整决策阈值和实验合成数据生成方法(如 SMOTE)等技术。然而,环境中的限制促使转向手动方法,如欠采样和阈值调整,这些方法被实施并测试以细化模型对少数类的敏感性。
通过使用 GridSearchCV 进行超参数调整,学习过程得到了增强,重点关注优化正则化强度和求解器类型等参数。这一步骤提高了模型性能,并提供了模型配置对情感分类影响的认识。
实验扩展到比较不同的特征表示——词袋、TF-IDF 和词嵌入——以确定它们在情感分析中的有效性。每种技术都得到了实施,并且对其对模型性能的影响进行了批判性评估,揭示了不同文本表示如何影响辨别情感的能力。
在整个过程中,等待用户反馈后再进行下一步的策略确保了学习的节奏适当,并且每一步都很清晰。这种方法促进了情感分析技术的结构化探索,从基本的预处理到复杂的模型调整。
旅程以对构建和优化情感分析模型的全面理解结束。这种结构化的、迭代的途径——通过持续的用户参与和反馈得到增强——允许深入探索机器学习模型开发,从理论概念到实际应用。
这次经历不仅让用户掌握了处理文本数据和模型训练的知识,还突出了处理不平衡数据集以及选择合适的模型和特征进行情感分析时的挑战和考虑。
在下一章中,我们将学习如何使用 ChatGPT 生成线性回归的代码。
加入我们的 Discord 社区
加入我们的社区 Discord 空间,与作者和其他读者进行讨论:

第十三章:使用 ChatGPT 构建客户消费回归模型
简介
在数据驱动决策的领域,理解客户行为对于优化商业策略至关重要。基于我们对分类技术的探索,本章将重点转向回归分析,特别是线性回归,以预测数值,例如客户的年度消费。线性回归帮助我们发现数据中的关系,使我们能够根据观察到的模式进行预测。
本章将指导您构建一个预测模型,该模型根据客户与数字平台的互动来估计客户的年度消费。我们旨在加深您对线性回归的理解,展示如何准备、处理和利用数据集来构建准确和可靠的模型。
随着我们前进,我们将探索各种技术来提高模型准确性和处理复杂数据场景:
-
利用高级正则化技术来提高模型稳定性和性能。
-
生成合成数据集以更好地理解模型在不同数据条件下的行为。
-
通过综合、端到端的编码示例简化模型开发。
到本章结束时,您将具备利用线性回归进行商业数据驱动决策所需的知识和技能。让我们开始这段回归分析的旅程,以优化我们应用程序或网站上的客户参与度和收入生成。
在本章中,我们将:
-
使用 ChatGPT 构建回归模型:读者将学习如何使用 ChatGPT 辅助生成用于构建回归模型的 Python 代码,以预测客户在我们的应用程序或网站上花费的年度金额,提供一种动手理解与交互数据集的方法。
-
应用提示技术:将介绍有效的技术来构建提示,引导 ChatGPT 提供最有用的代码片段和回归任务见解。
商业问题
一家电子商务商店希望通过深入了解客户行为和偏好来优化客户参与度和增加收入。通过分析各种客户属性及其购买模式,商店旨在定制其营销策略,提高客户保留率,并提升整体购物体验。
问题与数据领域
我们将使用回归技术来了解年度消费与其他参数之间的关系。回归是一种找出不同因素(如花在应用程序或网站上的时间)如何与客户在在线商店的消费金额相关联的方法。它帮助我们理解和预测客户行为。通过了解哪些因素对推动销售最有影响力,电子商务商店可以调整其策略来增强这些领域,并可能增加收入。
数据集概述
电子商务店铺从客户那里收集以下信息:
-
电子邮件: 这是客户的电子邮件地址。它是每个客户的唯一标识符,可用于通信,例如发送订单确认、时事通讯或个性化的营销优惠。
-
地址: 这指的是客户的物理地址。对于交付他们购买的产品至关重要。此外,地址数据有时可以提供关于销售和偏好的地理趋势的见解。
-
头像: 这可能是用户选择的数字表示或图像。它可能不会直接影响销售或客户行为,但它可以是客户互动策略的一部分,为用户资料增添个性化触感。
-
平均会话时长: 这是所有会话的平均持续时间,以分钟计算。这就像测量客户每次访问您的店铺时花费的时间。想象一下,有人平均花费 33 分钟在您的店铺里四处走动,查看产品。
-
应用使用时间: 这是指客户在店铺应用上的停留时长,以分钟计算。可以将其视为他们在应用中浏览的时间,比如在公交车上或咖啡店排队时。
-
网站使用时间: 这与应用使用时间类似,但这是针对您的网站。如果他们在家里或工作场所使用电脑查看您的店铺,他们会停留多久?
-
会员时长: 这表示这些客户在您的店铺中的时间长短。有些人可能是新顾客,而其他人可能已经与您合作多年。
-
年度消费金额: 这是指每位客户在您的店铺中一年内花费的总金额,以美元计算。
在我们的数据集背景下:
-
电子邮件和地址: 这些应主要用于交易目的,除非客户同意接收营销通讯。我们不会将它们用于分析。
-
头像: 这可以用于个性化用户体验,但对于销售预测的统计分析价值不大。
-
其他数据: 如“应用使用时间”和“网站使用时间”等变量可以进行分析,以改进用户体验和业务策略,同时不侵犯个人隐私。
总结来说,虽然像电子邮件、地址和头像这样的数据对于业务运营和客户互动很有价值,但它们必须以高度的责任心处理,优先考虑客户的隐私和偏好。
注意,所使用的数据不是真实的数据集,因此电子邮件、地址等都是虚构的。
将问题分解为特征
考虑到我们的数据集包括独立变量(如“平均会话时长”、“应用使用时间”、“网站使用时间”和“会员时长”)以及一个因变量(“年度消费金额”),我们将从使用 ChatGPT 和 ChatGPT Plus 或 GPT-4 的简单回归技术开始。这包括以下高级步骤:
-
逐步构建模型:用户将逐步了解构建机器学习模型的过程,包括加载数据集、将其分为训练集和测试集、训练模型、进行预测以及评估其性能。
-
应用正则化技术:用户将学习如何应用正则化技术,如岭回归和 Lasso 回归,并结合交叉验证来提高线性回归模型的表现。这包括初始化模型、使用训练数据训练它们以及评估它们的性能。
-
生成一个具有复杂性的合成数据集:用户将发现如何使用
sklearn.datasets模块中的make_regression函数生成具有附加复杂性的合成数据集。这涉及到指定样本数量、特征和噪声水平,以模拟真实世界的数据。 -
为合成数据集生成单步模型开发代码:用户将看到如何通过单步编写端到端代码来加载合成数据集,将其分为训练集和测试集,训练线性回归模型,评估其性能,并打印评估指标。这允许采用简化的模型开发和评估方法。
提示策略
为了利用 ChatGPT 进行机器学习,我们需要清楚地了解如何实现针对代码生成的特定提示策略。
让我们头脑风暴一下在这个任务中我们想要实现的目标,以便更好地理解需要放入初始提示的内容。
策略 1:任务-行动-指南(TAG)提示策略
1.1 – 任务: 具体的任务或目标是创建一个简单的线性回归模型,根据数据集中的各种属性预测“年度消费金额”。
1.2 – 行动: 在这种情况下,策略是让 ChatGPT 决定步骤,因此没有提供具体的步骤。
1.3 – 指南:在我们的提示中,我们将为 ChatGPT 提供以下指南:
-
代码应与 Jupyter Notebook 兼容
-
确保对每一行代码都有详细的注释。
-
您必须解释每一行代码,然后将其详细地复制到笔记本的文本块中,以便在提供代码之前对代码中使用的每种方法进行说明。
策略 2:角色-指令-上下文(PIC)提示策略
2.1 – 角色:我们将扮演一个需要学习模型创建不同步骤的初学者的角色;因此,代码应逐步生成。
2.2 – 指令:我们明确包括了挂载 Google Drive 的步骤,因为这是一个常见的疏忽。
2.3 – 上下文: 最重要的是提供数据集的上下文和确切的字段名称,以生成可以直接执行的代码,或者在 ChatGPT Plus 的情况下提供数据集本身。
策略 3:学习-即兴创作-反馈-评估(LIFE)提示策略
3.1 – 学习:
-
我们想了解线性回归及其工作原理。
-
理解特征工程技术和模型评估指标。
-
我们想学习如何创建合成数据集。
3.2 – 改进:
- 我们将在应用正则化技术时使用它。
3.3 – 反馈:
- 如果提供的代码导致任何错误,则应向 ChatGPT 提供反馈。我们已在 ChatGPT Plus 中应用了 Lasso 和 Ridge 代码执行。
3.4 – 评估:
- 执行 ChatGPT 提供的代码以确保准确性和有效性。这一点在本章中一直被使用。
使用 ChatGPT 的免费版本构建一个简单的线性回归模型来预测“年度消费金额”
当使用免费版本时,首先向 ChatGPT 提供一个清晰的关于数据集的描述非常重要,这作为生成代码的有效方法,随后进行用户评估。ChatGPT 已经在 Python 和机器学习算法上训练得很好;因此,我们在这里不需要提供具体的步骤。为了验证生成的代码是否正确运行,我们将使用 Google Colab。
特征 1:逐步构建模型
让我们为分类模型制定初始提示。
[提示**]
我想创建一个简单的线性回归模型来预测电子商务客户数据集中客户的“年度消费金额”(TAG 1.1),该数据集包含以下列(PIC 2.3):
-
电子邮件:每个客户的独特电子邮箱,在此用作识别。
-
地址:每个客户的物理地理位置。
-
头像:这是数据库中客户的视觉匿名表示。
-
平均会话长度:所有会话合并后的平均持续时间,以分钟为单位。
-
应用时间:这是在商店应用程序中的持续时间,以分钟为单位。
-
网站时间:这是在商店网站上的时间长度,以分钟为单位。
-
会员时长:这是客户作为商店在线资源注册用户的时长。
-
年度消费金额:这是客户支付商店在线资源的成本,以美元计算。
我是一个初学者用户(PIC 2.1),所以请提供逐步的代码,并严格遵循以下说明(PIC 2.2):
-
代码应与 Jupyter 笔记本兼容
-
确保每行代码都有详细的注释。
-
您必须解释每一行代码,然后详细地将其复制到笔记本的文本块中,详细说明代码中使用的每种方法,在提供代码之前。
-
还请注意,数据集放置在 Google Drive 的此路径
'/content/drive/My Drive/Chapter05/Ecommerce Customers.csv '(TAG 1.2)。 -
因此,添加代码来挂载驱动器,加载数据集,一次提供一步,并等待用户反馈。
[提示结束**]
您应该收到以下类似的响应。

图 13.1:ChatGPT 的初始响应
[Prompt response]
当然,让我们一步一步来。首先,我们需要在我们的 Jupyter Notebook 环境中挂载 Google Drive,以便我们可以访问数据集。
这是您可以这样做的:
from google.colab import drive
# Mount Google Drive
drive.mount('/content/drive')
解释:
-
from google.colab import drive:这一行从google.colab包中导入了drive模块。 -
drive.mount('/content/drive'):这个命令将您的 Google Drive 挂载到指定的路径/content/drive。当您运行这个单元格时,它会提示您进行身份验证,并给您一个链接以获取授权代码。一旦您输入代码,您的 Google Drive 就会挂载。
在我们继续之前,如果您有任何问题,请告诉我。
[End of response]
我们现在将应用评估提示策略,并通过在 Google Colab 的每一步执行代码来验证代码。代码运行得非常顺利,并挂载了 Google Drive。
请注意,它需要权限来访问驱动器,并会提示用户。
请参考笔记本 使用 ChatGPT 学习简单回归.ipynb 以获取可执行的代码片段,或者从提示响应中复制代码。
运行您的单元格,您应该会看到一个消息说“由于调用 drive.mount,已挂载到/content/drive。”
我们现在将指示 ChatGPT 进行下一步:
[Prompt]
让我们继续
[End of prompt]
我们应该会收到以下类似的响应,以将数据集加载到笔记本中:
[Prompt response]
太好了!现在我们已经挂载了 Google Drive,我们可以将数据集加载到我们的笔记本中。
这里是如何加载数据集的:
import pandas as pd
# Define the file path
file_path = '/content/drive/My Drive/Chapter05/Ecommerce Customers.csv'
# Load the dataset into a DataFrame
ecommerce_data = pd.read_csv(file_path)
# Display the first few rows of the DataFrame
print(ecommerce_data.head())
解释:
-
import pandas as pd:这一行导入了 pandas 库,并将其别名为pd,这是一个常见的约定。 -
file_path = '/content/drive/My Drive/Chapter05/Ecommerce Customers.csv':这一行定义了数据集在您的 Google Drive 中的文件路径。 -
pd.read_csv(file_path):这个函数读取指定路径的 CSV 文件,并从中创建一个 DataFrame。 -
print(ecommerce_data.head()):这一行打印 DataFrame 的前几行,以验证数据集是否已正确加载。
如果您有任何问题或想要进行下一步,请告诉我!
[End of response]
我们在笔记本中执行了 ChatGPT 提供的代码,并从定义的路径加载数据集,并打印了前几行。由于我们在初始提示中给出了文件的路径,我们不需要在代码中做任何更改,它运行得非常顺利。得到的经验是,在需要时,我们需要对文件路径和位置进行具体说明。

图 13.2:加载数据集
接下来,我们将提供 ChatGPT 的输出,然后进行步骤 1。在这种情况下,我们通过以文本形式呈现输出,向 ChatGPT 提供了额外的上下文。
重要的是要注意,由于我们正在使用 GPT-3.5,它没有执行代码或处理图像的能力,所以我们只能从笔记本中提供文本输出:
[提示]**
Here is the output - Email \
0 mstephenson@fernandez.com
1 hduke@hotmail.com
2 pallen@yahoo.com
3 riverarebecca@gmail.com
4 mstephens@davidson-herman.com
Address Avatar \
0 835 Frank Tunnel\nWrightmouth, MI 82180-9605 Violet
1 4547 Archer Common\nDiazchester, CA 06566-8576 DarkGreen
2 24645 Valerie Unions Suite 582\nCobbborough, D... Bisque
3 1414 David Throughway\nPort Jason, OH 22070-1220 SaddleBrown
4 14023 Rodriguez Passage\nPort Jacobville, PR 3... MediumAquaMarine
Avg. Session Length Time on App Time on Website Length of Membership \
0 34.497268 12.655651 39.577668 4.082621
1 31.926272 11.109461 37.268959 2.664034
2 33.000915 11.330278 37.110597 4.104543
3 34.305557 13.717514 36.721283 3.120179
4 33.330673 12.795189 37.536653 4.446308
Yearly Amount Spent
0 587.951054
1 392.204933
2 487.547505
3 581.852344
4 599.406092
[提示结束]**
我们预计会收到以下类似的响应,其中 ChatGPT 将提供后续步骤的指导。最初,我们在初始提示中未指定任何特定步骤,而是选择让 ChatGPT 确定后续的行动方案:
[提示响应]**
太好了!数据集已成功加载。现在,我们可以继续构建我们的线性回归模型来预测客户花费的“年度金额”。
构建简单线性回归模型的步骤如下:
-
数据准备:将数据集分割为输入特征(
X)和目标变量(y)。 -
分割数据:将数据集分割为训练集和测试集。
-
模型初始化:创建一个线性回归模型对象。
-
模型训练:将模型拟合到训练数据。
-
模型评估:使用测试数据评估模型性能。
让我们从步骤 1开始:
第 1 步:数据准备:
# Extract input features (X) and target variable (y)
X = ecommerce_data[['Avg. Session Length', 'Time on App', 'Time on Website', 'Length of Membership']]
y = ecommerce_data['Yearly Amount Spent']
说明:
-
X = ecommerce_data[['Avg. Session Length', 'Time on App', 'Time on Website', 'Length of Membership']]:这一行从数据集中选择了输入特征('Avg. Session Length','Time on App','Time on Website','Length of Membership')并将其分配给变量X。 -
y = ecommerce_data['Yearly Amount Spent']:这一行从数据集中选择了目标变量'Yearly Amount Spent'并将其分配给变量y。
如果您有任何问题或想要进行下一步,请告诉我!
[响应结束]**
ChatGPT 概述了它将遵循的代码生成步骤。此外,它提供了第一步的代码,即数据准备,其中数据集被分为特征和目标变量。值得注意的是,它巧妙地排除了电子邮件地址、头像和地址等特征。此外,它精确地使用了初始提示中提供的描述中规定的列名。这里采用的策略是在提示中包含数据集列名,以确保生成的代码的兼容性。因此,代码执行时无需任何修改或错误。

图 13.3:步骤 1 – 数据准备
接下来,我们将指示 ChatGPT 继续:
[提示]**
让我们继续下一步。
[提示结束]**
我们应该收到以下类似的响应,以根据说明分割数据:
[提示响应]**
太好了!现在我们已经通过分离输入特征(X)和目标变量(y)来准备我们的数据,我们可以继续到下一步,即将数据分割为训练集和测试集。
第 2 步:分割数据:
from sklearn.model_selection import train_test_split
# Split the data into training and testing sets (80% train, 20% test)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Display the shapes of the training and testing sets
print("Training set shape - X_train:", X_train.shape, "y_train:", y_train.shape)
print("Testing set shape - X_test:", X_test.shape, "y_test:", y_test.shape)
说明:
-
from sklearn.model_selection import train_test_split: 这行代码从sklearn.model_selection模块导入train_test_split函数,该函数用于将数据集分为训练集和测试集。 -
train_test_split(X, y, test_size=0.2, random_state=42): 这个函数将输入特征(X)和目标变量(y)分为训练集和测试集。test_size参数指定了要包含在测试集中的数据集比例(在本例中,测试集为 20%)。random_state参数用于可重复性,确保每次运行代码时生成相同的随机分割。 -
X_train, X_test, y_train, y_test: 这些变量分别持有输入特征(X)和目标变量(y)的训练集和测试集。 -
print("Training set shape - X_train:", X_train.shape, "y_train:", y_train.shape): 这行代码打印了训练集输入特征(X_train)和目标变量(y_train)的形状。 -
print("Testing set shape - X_test:", X_test.shape, "y_test:", y_test.shape): 这行代码打印了测试集输入特征(X_test)和目标变量(y_test)的形状。
如果您有任何问题或想要进行下一步,请告诉我!
[响应结束]**
代码执行成功,数据已按照预期的 80-20 比例分为训练集和测试集。值得注意的是,我们没有明确指定比例,但 ChatGPT 默认为标准的 80-20 分割。

图 13.4:步骤 2 – 分割数据
接下来,我们将提供上一步的输出给 ChatGPT,这样 ChatGPT 应该会了解形状,并且也应该为我们提供下一步的代码:
[提示]**
Training set shape - X_train: (400, 4) y_train: (400,)
Testing set shape - X_test: (100, 4) y_test: (100,)
[提示结束]**
我们应该收到一个类似的响应,其中模型已初始化:
[提示响应]**
完美!我们已经成功将数据集分为训练集和测试集。训练集包含 400 个样本,而测试集包含 100 个样本。每个样本由四个输入特征和一个目标变量组成。
现在,让我们进行下一步,即初始化我们的线性回归模型。
步骤 3:模型初始化:
from sklearn.linear_model import LinearRegression
# Initialize the linear regression model
model = LinearRegression()
说明:
-
from sklearn.linear_model import LinearRegression: 这行代码从sklearn.linear_model模块导入LinearRegression类,用于创建线性回归模型。 -
model = LinearRegression(): 这行代码初始化一个线性回归模型对象,并将其分配给变量 model。
如果您有任何问题或想要进行下一步,请告诉我!
[响应结束]**
代码执行得很好,没有任何错误。必要的导入也由 ChatGPT 提供。

图 13.5:步骤 3 – 模型初始化
我们将直接指示 ChatGPT 继续。值得注意的是,由于我们的初始提示中包含了提供逐步代码的指令,我们不需要针对每个单独的步骤或任务调整后续的提示。相反,我们将允许 ChatGPT 引导我们,在必要时提供前一步骤的输出:
[Prompt]
让我们继续。
[End of prompt]
我们应该收到以下类似的响应来训练模型:
[Prompt response]
太好了!现在我们已经初始化了我们的线性回归模型,我们可以继续下一步,即使用训练数据来训练模型。
步骤 4:模型训练:
# Train the linear regression model using the training data
model.fit(X_train, y_train)
说明:
model.fit(X_train, y_train): 这行代码使用训练数据来拟合(或训练)线性回归模型。fit方法接受输入特征(X_train)和相应的目标变量(y_train)作为参数,并调整模型的参数以最小化实际值和预测值之间的差异。
现在我们已经训练了模型,我们可以继续到最后一步,即使用测试数据评估模型性能。
如果您有任何问题或希望继续,请告诉我!
[End of response]
我们将执行代码,这将训练模型。

图 13.6:步骤 4 – 模型训练
我们将指示 ChatGPT 继续:
[Prompt]
让我们继续
[End of prompt]
我们应该收到以下类似的响应来使用测试数据评估模型的性能:
[Prompt response]
太好了!现在我们的线性回归模型已经训练完成,我们可以使用测试数据来评估其性能。
步骤 5:模型评估:
# Make predictions on the testing data
y_pred = model.predict(X_test)
# Evaluate the model's performance
from sklearn.metrics import mean_squared_error, r2_score
# Calculate Mean Squared Error (MSE)
mse = mean_squared_error(y_test, y_pred)
# Calculate R-squared (coefficient of determination)
r_squared = r2_score(y_test, y_pred)
# Print the evaluation metrics
print("Mean Squared Error (MSE):", mse)
print("R-squared (Coefficient of Determination):", r_squared)
说明:
-
model.predict(X_test): 这行代码使用训练好的模型对输入特征(X_test)进行预测,从而生成目标变量的预测值。 -
from sklearn.metrics import mean_squared_error, r2_score: 这行代码从sklearn.metrics模块导入mean_squared_error和r2_score函数,这些函数用于评估模型的性能。 -
mean_squared_error(y_test, y_pred): 这个函数计算实际目标变量(y_test)和预测值(y_pred)之间的均方误差(MSE)。 -
r2_score(y_test, y_pred): 这个函数计算实际目标变量(y_test)和预测值(y_pred)之间的 R 平方(确定系数)。 -
print("均方误差 (MSE):", mse): 这行代码打印了均方误差(MSE),它衡量实际值和预测值之间的平均平方差。较低的 MSE 表示模型性能更好。 -
print("R-squared (Coefficient of Determination):", r_squared): 这行代码打印出 R-squared 值,它代表了模型解释目标变量方差的比例。R-squared 值越高(越接近 1),表明拟合度越好。
这些评估指标将帮助我们评估我们的线性回归模型在预测客户“年度花费”方面的表现。
如果您有任何问题或需要进一步解释任何部分,请告诉我!
[响应结束]**
我们执行代码,它运行得非常完美,没有任何错误,并打印出均方误差(MSE)和决定系数(R-squared)。

图 13.7:模型评估输出
对于机器学习的新手来说,评估模型性能是否符合可接受标准至关重要。我们将再次寻求 ChatGPT 的帮助,并给它提供输出以供评估:
[提示]**
Mean Squared Error (MSE): 109.8637411839399
R-squared (Coefficient of Determination): 0.9778130629184127
[提示结束]**
我们应该从 ChatGPT 那里收到一个解释结果的响应,这表明鉴于 R-squared 非常接近 1,我们的模型实际上表现非常好:
[响应提示]**
评估指标表明,我们的线性回归模型在测试数据上的表现相当好:
-
均方误差(MSE):MSE 的值约为 109.86。这表明,平均而言,“年度花费”的实际值和预测值之间的平方差约为 109.86。MSE 值越低,表明模型性能越好。
-
R-squared(决定系数):R-squared 的值约为 0.978。这表明大约 97.8%的“年度花费”的方差可以通过线性回归模型来解释。R-squared 值越高,越接近 1,表明模型对数据的拟合度越好。
总体而言,这些评估指标表明,我们的线性回归模型在根据提供的输入特征预测客户“年度花费”方面表现良好。
如果您有任何进一步的问题或需要额外的帮助,请随时提问!
[响应结束]**
现在我们已经熟练掌握了利用 ChatGPT 逐步构建线性回归模型,我们将转向第二个特性,深入探讨正则化技术的应用。虽然我们的当前模型表现令人满意,但正则化可以证明非常有价值,尤其是在模型要么欠拟合要么过拟合的情况下。
特征 2:应用正则化技术
我们将使用 ChatGPT 的“学习和改进提示策略”(LIFE 3.1 和 3.2)来了解模型是否可能过拟合,以及在这种情况下应该做什么:
[提示]**
模型是否可能过拟合?
[提示结束]**
我们收到 ChatGPT 的响应,表明我们的模型不太可能过拟合,但它确实建议了一些需要考虑的事项,例如正则化技术和交叉验证:
[Prompt response]
在解释模型性能时,考虑过拟合的可能性是至关重要的。过拟合发生在模型学会捕捉训练数据中的噪声或随机波动,而不是输入特征与目标变量之间的潜在关系。这可能导致模型在训练数据上表现良好,但在未见过的数据上泛化能力差。
这里有一些表明可能发生过拟合的指标:
-
训练和测试性能之间的大差异:如果模型在训练数据上的表现显著优于测试数据,这可能表明模型过拟合。在我们的案例中,如果 MSE 和 R-squared 值在训练集上比在测试集上好得多,这可能表明过拟合。
-
模型的复杂性:与训练数据规模相比,参数数量过多的过于复杂的模型更容易过拟合。线性回归相对简单,但如果你使用的是更复杂的模型(例如,具有高次多项式的多项式回归),这可能会增加过拟合的风险。
-
正则化技术:如 Lasso 或 Ridge 回归等正则化技术可以通过惩罚大系数来减轻过拟合。如果怀疑过拟合,应用正则化可能有助于提高模型的泛化性能。
-
交叉验证:通过使用交叉验证技术,例如 k 折交叉验证,可以在对数据的多个训练-测试分割进行评估时,提供一个更稳健的模型性能估计。如果模型在不同分割上的性能有显著差异,这可能表明模型过拟合。
在我们的情况下,由于测试数据上的 R-squared 值较高,MSE 相对较低,模型过拟合的可能性较小。然而,探索这些指标并考虑采取额外步骤以确保模型泛化性能始终是一个好习惯。如果你怀疑模型过拟合,可以尝试简化模型,应用正则化技术,或使用交叉验证进一步评估其性能。
[End of response]
我们将要求 ChatGPT 应用正则化和交叉验证技术:
[Prompt]
首先尝试应用正则化技术,然后进行交叉验证
[End of prompt]
ChatGPT 应为我们提供应用 Lasso 或 Ridge 以及交叉验证的代码,并附上解释:
[Prompt response]
当然!让我们先对我们的线性回归模型应用正则化技术。两种常见的正则化技术是 Lasso 回归(L1 正则化)和岭回归(L2 正则化)。这些技术向损失函数中添加一个惩罚项,通过惩罚大的系数来帮助防止过拟合。
我们将首先尝试岭回归,它将系数的平方和(L2 范数)添加到损失函数中。
这是如何实现带有交叉验证的岭回归的方法:
from sklearn.linear_model import RidgeCV
# Initialize the Ridge regression model with cross-validation
ridge_model = RidgeCV(alphas=[0.1, 1.0, 10.0], cv=5)
# Train the Ridge regression model
ridge_model.fit(X_train, y_train)
# Make predictions on the testing data
ridge_y_pred = ridge_model.predict(X_test)
# Evaluate the model's performance
ridge_mse = mean_squared_error(y_test, ridge_y_pred)
ridge_r_squared = ridge_model.score(X_test, y_test)
# Print the evaluation metrics
print("Ridge Regression - Mean Squared Error (MSE):", ridge_mse)
print("Ridge Regression - R-squared (Coefficient of Determination):", ridge_r_squared)
print("Ridge Regression - Best Alpha:", ridge_model.alpha_)
说明:
-
RidgeCV(alphas=[0.1, 1.0, 10.0], cv=5): 这初始化了一个带有交叉验证的岭回归模型。alphas参数指定要测试的正则化强度(alpha 值),而cv指定交叉验证的折数。 -
ridge_model.fit(X_train, y_train): 这使用训练数据训练岭回归模型。 -
ridge_model.predict(X_test): 这使用训练好的岭回归模型对测试数据进行预测。 -
ridge_mse = mean_squared_error(y_test, ridge_y_pred): 这使用岭回归计算实际值和预测值之间的均方误差(MSE)。 -
ridge_model.score(X_test, y_test): 这使用岭回归计算 R 平方值(确定系数)。 -
ridge_model.alpha_: 这个属性返回由交叉验证选出的最佳 alpha(正则化强度)。
如果你想继续使用岭回归或者有任何问题,请告诉我!
[End of response]
让我们尝试执行代码,看看它是否工作并且是否有改进。

图 13.8:岭回归
代码与之前提供的代码兼容,输出与线性回归代码非常相似。但请注意,ChatGPT 不需要任何特定指令,它可以生成一个完美的可执行代码。接下来,我们将向 ChatGPT 提供输出,看看它有什么建议:
[Prompt]
Ridge Regression - Mean Squared Error (MSE): 109.82024999312038
Ridge Regression - R-squared (Coefficient of Determination): 0.9778218459464064
Ridge Regression - Best Alpha: 0.1
[End of prompt]
在这种情况下,ChatGPT 已经解释了结果,并提供了带有交叉验证的 Lasso 回归代码。响应可能因 ChatGPT 在先前交互中提供的建议而有所不同:
[Prompt response]
看起来带有交叉验证的岭回归模型与标准线性回归模型表现相当。均方误差(MSE)和 R 平方值与标准线性回归模型得到的值非常接近。
岭回归模型通过交叉验证选出的最佳 alpha 是 0.1,这表明模型应用了少量的正则化。
由于带有交叉验证的岭回归的性能与标准线性回归模型相当,我们可以得出结论,在这种情况下,正则化并没有显著提高模型性能。
接下来,让我们尝试使用带有交叉验证的 Lasso 回归(L1 正则化)来查看它是否会产生任何改进:
from sklearn.linear_model import LassoCV
# Initialize the Lasso regression model with cross-validation
lasso_model = LassoCV(alphas=[0.1, 1.0, 10.0], cv=5)
# Train the Lasso regression model
lasso_model.fit(X_train, y_train)
# Make predictions on the testing data
lasso_y_pred = lasso_model.predict(X_test)
# Evaluate the model's performance
lasso_mse = mean_squared_error(y_test, lasso_y_pred)
lasso_r_squared = lasso_model.score(X_test, y_test)
# Print the evaluation metrics
print("Lasso Regression - Mean Squared Error (MSE):", lasso_mse)
print("Lasso Regression - R-squared (Coefficient of Determination):", lasso_r_squared)
print("Lasso Regression - Best Alpha:", lasso_model.alpha_)
如果你想继续使用 Lasso 回归或有任何问题,请告诉我!
[响应结束]
我们将执行代码,看看它是否工作,以及它是否会产生与 Ridge 回归不同的结果。

图 13.9:Lasso 回归
使用交叉验证的 Lasso 回归也产生了与标准线性回归和 Ridge 回归模型相似的结果。MSE 和 R-squared 值与之前获得的结果非常接近。
然而,需要注意的是,这里使用的数据集可能不够复杂,无法从正则化技术中获得显著的好处。在数据集更大或更复杂的情况下,正则化可能在防止过拟合和改善模型泛化方面发挥更关键的作用。
让我们转到下一个特征,为我们的问题生成一个合成数据集以增加复杂性。
特征 3:生成一个合成数据集以增加复杂性
我们将使用与 ChatGPT 的对话风格,并要求它提供生成合成数据集的代码:
[提示]
请提供一段生成合成数据集的代码,以便增加其复杂性?
[提示结束]
你可能会收到以下响应的变体。然而,你可能注意到这只是一个通用的代码,并不适合生成针对我们数据集的特定代码。因此,我们需要通过向 ChatGPT 提供反馈来细化提示:
[提示响应]
当然!我们可以使用sklearn.datasets模块中的make_regression函数生成一个具有额外复杂性的合成数据集。这个函数生成一个具有指定特征的随机回归问题,例如样本数量、特征数量和噪声水平。
这就是如何生成一个具有增加复杂性的合成数据集的方法:
from sklearn.datasets import make_regression
import pandas as pd
# Generate synthetic dataset with 500 samples, 10 features, and increased noise
X_synthetic, y_synthetic = make_regression(n_samples=500, n_features=10, noise=50, random_state=42)
# Create a DataFrame from the synthetic dataset
synthetic_data = pd.DataFrame(X_synthetic, columns=[f'Feature_{i}' for i in range(1, 11)])
synthetic_data['Target'] = y_synthetic
# Display the first few rows of the synthetic dataset
print(synthetic_data.head())
# Save the synthetic dataset to a CSV file
synthetic_data.to_csv('synthetic_dataset.csv', index=False)
说明:
-
make_regression(n_samples=500, n_features=10, noise=50, random_state=42): 这个函数生成一个包含 500 个样本、10 个特征和增加的噪声水平(由噪声参数指定)的合成回归数据集。random_state参数确保了可重复性。 -
pd.DataFrame(X_synthetic, columns=[f'Feature_{i}' for i in range(1, 11)]): 这将从合成输入特征(X_synthetic)创建一个 DataFrame,并将列名从'Feature_1'到'Feature_10'分配。 -
synthetic_data['Target'] = y_synthetic: 这将合成目标变量(y_synthetic)添加到具有列名'Target'的 DataFrame 中。 -
synthetic_data.to_csv('synthetic_dataset.csv', index=False): 这将合成数据集保存到名为'synthetic_dataset.csv'的 CSV 文件中,不包括index列。
您可以调整make_regression函数的参数来控制合成数据集的复杂性,例如样本数量、特征数量和噪声水平。
如果您需要进一步的帮助,请告诉我!
[响应结束]**
我们将简单地指示 ChatGPT 我们需要为我们的数据集编写代码:
[提示]**
不,我是指我们的问题。
[提示结束]**
让我们看看 ChatGPT 的建议,并判断它是否真的有效:
[提示响应]**
明白了!我们可以通过使用与原始数据集相同的输入特征,但增加复杂性来为我们的问题创建一个合成数据集。我们将生成额外的特征,这些特征是原始特征的非线性组合,并添加噪声来模拟真实世界的数据。
这就是如何为我们的问题生成具有增加复杂性的合成数据集的方法:
import numpy as np
import pandas as pd
# Set random seed for reproducibility
np.random.seed(42)
# Number of samples
n_samples = 500
# Generate synthetic input features
avg_session_length = np.random.normal(loc=30, scale=5, size=n_samples)
time_on_app = np.random.normal(loc=15, scale=5, size=n_samples)
time_on_website = np.random.normal(loc=20, scale=7, size=n_samples)
length_of_membership = np.random.normal(loc=3, scale=1, size=n_samples)
# Generate synthetic target variable (linear combination of input features with noise)
yearly_amount_spent = 100 + (avg_session_length * 10) + (time_on_app * 20) + (time_on_website * 15) + (length_of_membership * 30) + np.random.normal(loc=0, scale=50, size=n_samples)
# Create a DataFrame from the synthetic dataset
synthetic_data = pd.DataFrame({
'Avg. Session Length': avg_session_length,
'Time on App': time_on_app,
'Time on Website': time_on_website,
'Length of Membership': length_of_membership,
'Yearly Amount Spent': yearly_amount_spent
})
# Display the first few rows of the synthetic dataset
print(synthetic_data.head())
# Save the synthetic dataset to a CSV file
synthetic_data.to_csv('synthetic_ecommerce_data.csv', index=False)
说明:
-
我们使用正态分布生成合成输入特征(
avg_session_length、time_on_app、time_on_website、length_of_membership),并指定均值和标准差来模拟原始数据集的特征。 -
我们生成合成目标变量(
yearly_amount_spent)作为输入特征的线性组合,并添加噪声。 -
最后,我们创建一个从合成数据集生成的 DataFrame,并将其保存到 CSV 文件中。
这个合成数据集将具有与原始数据集相似的特征,但增加了复杂性,这对于测试模型和理解它们在更具挑战性的场景中的性能非常有用。
如果您需要进一步的澄清或帮助,请告诉我!
[响应结束]**
代码看起来很棒,并且已适配我们的数据集。执行时没有错误,并生成一个名为synthetic_ecommerce_data.csv的 CSV 文件,并将其存储在笔记本的运行环境中。它还打印了前几行。您也可以下载它以供将来使用。
接下来,我们将请求 ChatGPT 生成完整的代码,并使用合成的数据集中添加的噪声来评估模型性能。让我们转向下一个特性。
特性 4:为合成的数据集生成单步开发模型的代码
在前面的特性中,我们已经学习了如何逐步创建回归模型,因此现在我们将请求 ChatGPT 为我们提供合成数据集的整个代码:
[提示]**
现在请提供使用生成的合成数据集运行的单步端到端代码。
[提示结束]**
我们应该收到一个类似的响应,其中提供了完整的代码,包括加载数据集、分割数据集、模型训练和模型评估:
[提示响应]**
当然!下面是端到端代码,该代码加载合成数据集,将其分割为训练集和测试集,训练线性回归模型,评估其性能,并打印评估指标。
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
# Load the synthetic dataset
synthetic_data = pd.read_csv('synthetic_ecommerce_data.csv')
# Extract input features (X) and target variable (y)
X = synthetic_data[['Avg. Session Length', 'Time on App', 'Time on Website', 'Length of Membership']]
y = synthetic_data['Yearly Amount Spent']
# Split the dataset into training and testing sets (80% train, 20% test)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Initialize the linear regression model
model = LinearRegression()
# Train the linear regression model using the training data
model.fit(X_train, y_train)
# Make predictions on the testing data
y_pred = model.predict(X_test)
# Evaluate the model's performance
mse = mean_squared_error(y_test, y_pred)
r_squared = r2_score(y_test, y_pred)
# Print the evaluation metrics
print("Mean Squared Error (MSE):", mse)
print("R-squared (Coefficient of Determination):", r_squared)
此代码将从 CSV 文件中加载合成数据集,将其分为训练集和测试集,使用训练数据训练线性回归模型,对测试数据进行预测,使用均方误差(MSE)和 R-squared(决定系数)评估模型性能,并打印评估指标。
如果您已安装 pandas 库以处理数据框,并且已安装 scikit-learn 库以实现机器学习功能,则可以直接在您的 Python 环境中运行此代码。
如果您有任何问题或需要我提供其他帮助,请告诉我!
[响应结束]
我们执行了代码,它运行得非常顺畅。
在合成数据集上训练的线性回归模型的评估指标如下:
-
均方误差(MSE):大约为 2103.19
-
R-squared(决定系数):约为 0.9197
这些指标提供了对模型性能的洞察:
-
均方误差(MSE):MSE 衡量实际值和预测值之间平均平方差的度量。在这种情况下,MSE 表明,平均而言,实际值和预测的年度消费金额之间的平方差约为 2103.19。较低的 MSE 值是期望的,表示模型性能更好。
-
R-squared(决定系数):R-squared 值表示模型解释的目标变量(年度消费金额)变差的比例。在这种情况下,R-squared 值约为 0.9197,表明大约 91.97%的年度消费金额变差可以通过线性回归模型解释。R-squared 值越接近 1,表示拟合度越好。
总体而言,这些评估指标表明线性回归模型在合成数据集上表现良好。
在在线电子商务商店的背景下,这些模型的洞察力可用于多种目的:
-
客户细分:这些模型可以帮助根据预测的
年度消费金额识别高价值客户,从而实现有针对性的营销活动。 -
推荐系统:理解影响消费的因素可以用来改进产品推荐并提升顾客购物体验。
-
保留策略:
会员时长似乎是一个重要因素。可以根据这一洞察力制定提高客户保留和忠诚度的策略。 -
优化网站和应用程序:对
平均会话时长、应用程序使用时间和网站使用时间的影响进行了解,可以指导网站和应用程序优化的决策。
这标志着我们使用 ChatGPT 3.5 的学习结束。现在,让我们看看 ChatGPT Plus 中提示和学习的区别。
使用 ChatGPT Plus 学习简单线性回归
现在我们已经通过使用 ChatGPT 学习了简单线性回归,让我们在使用 ChatGPT Plus 的同时学习线性回归。ChatGPT Plus,或 GPT-4,包括上传数据集、分析数据集以及使用 Python 和 Matplotlib 等简单库产生结果的能力。在撰写本章时,它有一个名为 数据分析 的 GPT 助手,由 OpenAI 在使用移动应用时提供。您也可以选择使用浏览器版本的 GPT-4。
注意,您也可以使用免费版本实现相同的功能,但需要自行执行代码并修改提示。
特征 1:逐步构建简单的线性回归模型
我们已经学习了如何使用 ChatGPT 的免费版本逐步创建回归模型,其中我们需要提供数据集描述、从哪里加载的说明以及与 Jupyter Notebook 兼容的代码。在 ChatGPT Plus 的情况下,所有这些都被 ChatGPT Plus 处理,并且它还提供了一个代码执行环境。
注意,它不能运行 TensorFlow、Keras 或深度学习库:
[提示]**
我想创建一个简单的线性回归模型来预测附件中电子商务客户数据集(PIC 2.3)的“年度消费金额”(TAG 1.1)。
我是一个初学者用户(PIC 2.1),所以请提供逐步的代码,并严格遵循以下说明(PIC 2.2):
-
确保每行代码都有详细的注释。
-
您必须解释每一行代码,并一次提供一个步骤,并等待用户反馈
[提示结束]**
我们应该收到以下类似的响应,其中提供了完整的代码,包括加载数据集、分割数据集、模型训练和模型评估:
[提示响应]**
太好了!让我们开始使用 Python 构建一个简单的线性回归模型。我们将使用 pandas 处理数据,以及 sklearn 创建回归模型。
步骤 1:导入库和加载数据
首先,我们将导入必要的库并加载数据集。我们还将查看数据集的前几行,以了解其结构。
这是这个步骤的代码:
# Import necessary libraries
import pandas as pd # pandas is used for data manipulation and analysis
from sklearn.model_selection import train_test_split # This function is used to split data into training and testing sets
from sklearn.linear_model import LinearRegression # This class is used to perform linear regression
from sklearn.metrics import mean_absolute_error, mean_squared_error # These functions are used to evaluate the model
# Load the dataset
data = pd.read_csv('/mnt/data/Ecommerce Customers (1).csv') # Reading the CSV file into a pandas DataFrame
# Display the first few rows of the dataset to understand its structure
print(data.head())
这段代码执行以下操作:
-
导入库: 我们加载
pandas用于数据处理,以及sklearn的几个模块用于分割数据和执行线性回归。 -
加载数据:
pd.read_csv函数将 CSV 文件读取到 DataFrame 中,它是一个二维的标签化数据结构,具有可能不同类型的列。 -
打印前几行: 这有助于我们查看数据集的前几行,以便了解可用的数据和其格式。
让我们执行这个步骤并检查输出,以确保一切加载正确,并了解您的数据看起来如何。我们继续吗?
[响应结束]**
我们将指示 ChatGPT 继续操作:
[提示]**
是的,请。
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 执行了代码并打印了前几行。
注意,文件是在 ChatGPT 的运行时环境中加载的;如果您需要在本地环境或 Jupyter Colab 中执行代码,您将需要修改步骤以读取 CSV:
[提示响应]**
这是您数据集的前几行的快照:
| 邮箱 | 地址 | 头像 | 平均会话时长 | 应用程序使用时间 | 网站使用时间 | 会员时长 | 年度消费金额 |
|---|---|---|---|---|---|---|---|
| mstephenson@fernandez.com | 835 Frank Tunnel Wrightmouth, MI 82180-9605 | Violet | 34.497268 | 12.655651 | 39.577668 | 4.082621 | 587.951054 |
| hduke@hotmail.com | 4547 Archer Common Diazchester, CA 06566-8576 | DarkGreen | 31.926272 | 11.109461 | 37.268959 | 2.664034 | 392.204933 |
| pallen@yahoo.com | 24645 Valerie Unions Suite 582 Cobbborough, ... | Bisque | 33.000915 | 11.330278 | 37.110597 | 4.104543 | 487.547505 |
| riverarebecca@gmail.com | 1414 David Throughway Port Jason, OH 22070-1220 | SaddleBrown | 34.305557 | 13.717514 | 36.721283 | 3.120179 | 581.852344 |
| mstephens@davidson-herman.com | 14023 Rodriguez Passage Port Jacobville, PR 3... | MediumAquaMarine | 33.330673 | 12.795189 | 37.536653 | 4.446308 | 599.406092 |
数据集包括客户联系信息(如“邮箱”和“地址”)以及与他们与电子商务平台互动相关的数值数据(如“平均会话时长”、“应用程序使用时间”、“网站使用时间”和“会员时长”)。
第 2 步:数据准备
下一步将涉及为我们的线性回归模型准备数据。这包括:
-
选择用于预测的相关数值列。
-
将数据分为训练集和测试集。
您是否有特定的列想要用于预测“年度消费金额”,或者我应该使用所有数值列的一般方法继续操作?
[响应结束]**
我们将指示 ChatGPT 使用数值列继续操作:
[提示]**
请仅使用数值列。
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 提供了选择特征、分割数据集和打印形状的代码:
[提示响应]**
第 2 步:数据准备
我们现在将进行以下步骤来准备数据:
-
选择相关列:我们将包括与客户行为直接相关的数值列。
-
将数据分为训练集和测试集:这有助于我们评估模型在未见数据上的表现,以确保其总体表现良好。
这是执行这些步骤的代码:
# Selecting only the numerical columns for features
features = data[['Avg. Session Length', 'Time on App', 'Time on Website', 'Length of Membership']]
# Selecting the target column which is 'Yearly Amount Spent'
target = data['Yearly Amount Spent']
# Splitting the dataset into training and testing sets
# We allocate 80% of the data for training and 20% for testing
X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.2, random_state=42)
# Printing the shapes of the training and testing sets to verify the split
print(f'Training features shape: {X_train.shape}')
print(f'Testing features shape: {X_test.shape}')
print(f'Training labels shape: {y_train.shape}')
print(f'Testing labels shape: {y_test.shape}')
这段代码:
-
选择特征和目标:我们将分离特征列和目标变量,这是我们想要预测的内容。
-
分割数据:使用
train_test_split将数据分为训练集和测试集。test_size=0.2表示 20%的数据被保留用于测试。random_state被设置以确保结果的重复性。 -
打印形状:我们打印训练集和测试集的形状,以确认分割成功。
我们应该运行这段代码来准备数据并检查分割吗?
[响应结束]**
我们将指示 ChatGPT 继续执行代码:
[提示]**
是的,请。
[提示结束]**
我们应该收到一个类似于以下响应,其中 ChatGPT 已执行代码并打印了特征和标签的形状:
[提示响应]**
数据已成功分割为训练集和测试集。以下是分解:
-
训练特征形状:400 行和 4 列
-
测试特征形状:100 行和 4 列
-
训练标签形状:400 个值
-
测试标签形状:100 个值
这表明 80%的数据用于训练模型,20%的数据被保留用于测试模型性能。
第 3 步:构建线性回归模型
现在,我们将使用训练数据创建并训练线性回归模型。以下是计划:
-
初始化线性回归模型:这涉及到设置我们的模型,使其准备好拟合数据。
-
将模型拟合到训练数据:这是模型从训练数据中学习特征与目标之间关系的地方。
-
在训练数据上评估模型:这一步是为了快速检查我们的模型从训练数据中学到了多少。
我应该继续进行下一步,并展示如何构建和评估训练数据上的模型吗?
[响应结束]**
我们将指示 ChatGPT 继续下一步:
[提示]**
是的,请。
[提示结束]**
我们应该收到一个类似于以下响应,其中 ChatGPT 提供了代码:
[提示响应]**
让我们继续构建和训练线性回归模型。
第 3 步:构建线性回归模型
下面是创建、训练和初步评估线性回归模型的代码:
# Initialize the Linear Regression model
model = LinearRegression()
# Fit the model to the training data
# This involves finding the best fitting line to the provided data points
model.fit(X_train, y_train)
# Evaluate the model by predicting on the training set
train_predictions = model.predict(X_train)
# Calculate the Mean Absolute Error (MAE) on the training set
# MAE measures the average magnitude of the errors in a set of predictions, without considering their direction
train_mae = mean_absolute_error(y_train, train_predictions)
# Calculate the Mean Squared Error (MSE) on the training set
# MSE measures the average of the squares of the errors—that is, the average squared difference between the estimated values and the actual value
train_mse = mean_squared_error(y_train, train_predictions)
# Printing the errors to understand model performance on the training data
print(f'Mean Absolute Error on training data: {train_mae}')
print(f'Mean Squared Error on training data: {train_mse}')
这段代码执行以下操作:
-
初始化模型:调用
LinearRegression()创建线性回归模型。 -
拟合模型:
model.fit(X_train, y_train)调整回归线上的权重,以适应输入特征和目标输出。 -
进行预测:使用
model.predict(X_train)来查看模型学习预测训练数据的效果如何。 -
计算误差:
mean_absolute_error和mean_squared_error通过比较预测值与实际值来提供衡量模型准确性和性能的指标。
你希望我现在运行这段代码,看看模型在训练数据上的表现如何吗?
[响应结束]**
我们将指示 ChatGPT 继续:
[提示]**
是的,请。
[提示结束]**
我们应该会收到以下类似的响应,其中 ChatGPT 已执行代码并打印了结果:
[提示响应]
线性回归模型已在数据集上成功训练,以下是训练数据上的性能指标:
训练数据上的均方绝对误差 (MAE): 7.727
训练数据上的均方误差 (MSE): 95.823
这些指标帮助我们了解模型在预测中犯的平均误差:
-
MAE 告诉我们,平均而言,模型的预测值与实际值相差大约 7.73 个单位。
-
MSE 表示预测中存在略微更大的误差,因为它平方了误差,强调了更大的差异。
下一步:
现在,模型已在训练集上训练并初步评估。我们应该现在测试它在未见过的测试集上的性能,以真正评估其有效性。
你想要继续在测试集上测试模型并查看它在新数据上的表现吗?
[响应结束]
我们将指示 ChatGPT 继续测试模型在测试集上的性能:
[提示]
是的,请。
[提示结束]
我们应该会收到以下类似的响应,其中 ChatGPT 已执行代码并打印了结果:
[提示响应]
在测试集上对模型的评估产生了以下结果:
-
测试数据上的均方误差 (MAE): 8.558
-
测试数据上的均方误差 (MSE): 109.864
这些结果告诉我们以下信息:
-
8.558 的MAE表明,模型在测试数据上的预测值平均而言,与实际值相差大约 8.56 个单位。这略差于训练 MAE,这是可以预料的,因为模型现在正在预测它之前未见过的数据。
-
109.864 的MSE也高于训练 MSE,这表明某些预测的误差更大。
摘要:
模型表现相当不错,但与训练数据相比,在测试数据上表现出轻微的降低性能,这是正常的,表明模型正在按预期泛化,没有出现显著的过拟合或欠拟合。
最终步骤:
现在你已经拥有了一个可以基于客户行为指标预测“年度消费金额”的线性回归模型。根据你的目标,你可能想要进一步探索:
-
改进模型:你可以考虑更复杂的建模技术或特征工程。
-
模型解释:分析模型的系数以了解每个特征的影响。
你想要探索这些额外的步骤,或者需要进一步的帮助吗?
[响应结束]
我们已经看到,与免费版本类似,ChatGPT Plus 通过提供代码以及为我们执行代码的方式,一步一步地创建了一个简单的线性回归模型,做得相当不错。接下来,我们将看看 Plus 版本是否可以应用正则化技术。
功能 2:应用正则化技术
我们将要求 ChatGPT Plus 应用正则化技术:
[提示]**
请问你能应用 Lasso、Ridge 和交叉验证来检查它是否会导致任何改进吗?
[提示结束]**
我们应该收到类似于以下响应,其中 ChatGPT 提供了 Lasso 和 Ridge 以及交叉验证的代码:
[提示响应]**
第 5 步:应用 Lasso 和 Ridge 回归与交叉验证
Lasso 回归:
- Lasso 回归不仅有助于减少过拟合,还可以用于特征选择,因为它可以将不太重要的特征的系数缩小到零。
Ridge 回归:
- Ridge 回归通过对系数大小施加惩罚来降低模型复杂性。
我们将使用 scikit-learn 库,它提供了方便的函数来执行这些分析。
下面是应用 Lasso 和 Ridge 以及交叉验证的代码:
from sklearn.linear_model import LassoCV, RidgeCV
from sklearn.model_selection import cross_val_score
# Lasso Regression with Cross-Validation
lasso_cv = LassoCV(alphas=[0.1, 1.0, 10.0], cv=5) # cv=5 means 5-fold cross-validation
lasso_cv.fit(X_train, y_train)
lasso_cv_score = lasso_cv.score(X_test, y_test)
lasso_cv_mae = mean_absolute_error(y_test, lasso_cv.predict(X_test))
# Ridge Regression with Cross-Validation
ridge_cv = RidgeCV(alphas=[0.1, 1.0, 10.0], cv=5)
ridge_cv.fit(X_train, y_train)
ridge_cv_score = ridge_cv.score(X_test, y_test)
ridge_cv_mae = mean_absolute_error(y_test, ridge_cv.predict(X_test))
# Printing results
print(f'Lasso CV Score: {lasso_cv_score}, MAE: {lasso_cv_mae}')
print(f'Ridge CV Score: {ridge_cv_score}, MAE: {ridge_cv_mae}')
此代码执行以下操作:
-
LassoCV 和 RidgeCV:这些是带有内置 alpha 参数交叉验证的 Lasso 和 Ridge 回归方法。
-
交叉验证分数:
.score()方法返回 R² 分数,表示从独立变量中可预测的因变量方差的比例。 -
平均绝对误差:这提供了测试集上拟合质量的绝对度量。
让我们运行这段代码看看正则化和交叉验证是否能提高模型性能。我们继续吗?
[响应结束]**
我们将指示 ChatGPT 执行代码:
[提示]**
是的,请。
[提示结束]**
[提示响应]
在我们的场景中,ChatGPT 遇到了内部错误,无法执行代码。这种错误可能由于各种原因而发生,包括执行时服务不可用、内存问题或其运行时环境中缺少所需的库或依赖项。重要的是要注意,你可能会收到不同的响应,这些响应可以完美地执行代码,因为这类错误是偶然发生的。然而,我们包括这个场景是为了故障排除并说明在这种情况下如何进行。在这里,我们应用了提示策略以从人工智能助手(LIFE 3.3)获取反馈。
[响应结束]**
我们现在将根据 ChatGPT 的建议在本地使用 Jupyter Colab 执行代码,因为我们是从 Google Drive 加载数据集,所以我们将在提示中提供说明:
[提示]**
好的,给我完整的本地运行代码,并注意文件需要从 Jupyter 笔记本中的路径 '/content/drive/My Drive/Chapter05/Ecommerce Customers.csv' 挂载到 Google Drive。
[提示结束]**
我们应该收到一个类似于以下响应,其中 ChatGPT 提供了完整的代码:
[响应提示]**
当然!以下是您可以在本地 Jupyter Notebook 中运行的完整代码。此脚本包括直接从 Google Drive 导入数据集、预处理数据、使用交叉验证拟合 Lasso 和 Ridge 回归模型,然后评估它们的步骤。
# Import necessary libraries
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression, LassoCV, RidgeCV
from sklearn.metrics import mean_absolute_error, mean_squared_error
from sklearn.preprocessing import StandardScaler
# Mount Google Drive (specific to Google Colab)
from google.colab import drive
drive.mount('/content/drive')
# Load the dataset from Google Drive
file_path = '/content/drive/My Drive/Chapter05/Ecommerce Customers.csv'
data = pd.read_csv(file_path)
# Select only the numerical columns for features and the target column
features = data[['Avg. Session Length', 'Time on App', 'Time on Website', 'Length of Membership']]
target = data['Yearly Amount Spent']
# Splitting the dataset into training and testing sets (80% train, 20% test)
X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.2, random_state=42)
# Standardizing the data
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Initialize and fit the Linear Regression model
model = LinearRegression()
model.fit(X_train_scaled, y_train)
# Lasso Regression with Cross-Validation
lasso_cv = LassoCV(alphas=[0.01, 0.1, 1.0, 10.0], cv=5)
lasso_cv.fit(X_train_scaled, y_train)
lasso_cv_score = lasso_cv.score(X_test_scaled, y_test)
lasso_cv_mae = mean_absolute_error(y_test, lasso_cv.predict(X_test_scaled))
# Ridge Regression with Cross-Validation
ridge_cv = RidgeCV(alphas=[0.01, 0.1, 1.0, 10.0], cv=5)
ridge_cv.fit(X_train_scaled, y_train)
ridge_cv_score = ridge_cv.score(X_test_scaled, y_test)
ridge_cv_mae = mean_absolute_error(y_test, ridge_cv.predict(X_test_scaled))
# Print results
print(f'Linear Regression Test MAE: {mean_absolute_error(y_test, model.predict(X_test_scaled))}')
print(f'Lasso CV Score: {lasso_cv_score}, MAE: {lasso_cv_mae}')
print(f'Ridge CV Score: {ridge_cv_score}, MAE: {ridge_cv_mae}')
运行代码的说明:
-
Google Colab 特定说明:
drive.mount()函数是 Google Colab 特有的,允许您访问 Google Drive 上的文件。如果您不使用 Google Colab,您需要根据您的开发环境调整文件加载方法。 -
依赖项:确保您已安装所有必需的库。您可以使用
pip安装它们(例如,pip install pandas scikit-learn google-colab)。 -
数据路径:如果你的文件位于 Google Drive 上的不同目录中,请调整
file_path。
在你的 Jupyter Notebook 环境中运行此代码,以查看模型如何处理你的数据。如果你遇到任何错误或需要进一步的帮助,请随时联系!
[响应结束]**
我们执行代码,它运行得非常好,没有任何修改或错误。在这里,我们了解到我们可以使用在 ChatGPT Plus 中运行代码的混合方法,或者在出现任何问题时请求它提供在本地执行的代码。
接下来,我们将继续到功能 3,使用 ChatGPT Plus 生成合成数据集。
功能 3:生成合成数据集以增加复杂性
现在,让我们尝试使用 ChatGPT Plus 为我们生成数据集。为此,我们将使用数据分析师 GPT 并开始一次新的对话。
注意,可以通过在消息提示中提供@<GPT 名称>来调用特定的 GPT:
图 13.10:调用数据分析师 GPT
[提示]**
@数据分析师 – 请提供从预测“年度花费”的角度生成上述数据集的合成数据集的代码,以及生成它的代码。 <请在此处附加数据集文件>
[提示结束]**
我们应该收到一个类似于以下响应,其中 ChatGPT 提供了代码并打印了前几行:
[响应提示]**
为了帮助您生成预测年度花费的合成数据集,我首先需要查看您提供的数据集的结构和内容。让我们从加载和检查您文件中的数据开始。我将显示前几行和摘要统计信息,以了解我们正在处理什么样的特征。然后,我将协助您根据此结构创建合成数据集。让我们从加载数据开始。
数据集包含 500 个条目,具有以下特征:
-
电子邮件:客户的电子邮件地址(字符串)。
-
地址:客户的地址(字符串)。
-
头像:头像颜色(字符串)。
-
平均会话时长:以分钟为单位的平均会话时长(数值)。
-
应用停留时间:在应用上花费的时间(以分钟为单位,数值)。
-
网站停留时间:在网站上花费的时间(以分钟为单位,数值)。
-
会员时长:以年为单位计算的会员时长(数值)。
-
年度消费金额:以美元计算的年度消费金额(数值)。
为了创建用于建模和预测“年度消费金额”的合成数据集,我们可以使用一种称为重采样的技术,该技术涉及从现有数据集中有放回地抽样以创建一个新的具有相似分布的合成数据集。然而,为了增加一些变异性并模拟真实数据集,我们还可以在数值字段中引入随机噪声。
下面是一个使用重采样和向数值列添加噪声生成合成数据集的 Python 代码片段:
import numpy as np
# Number of samples for the synthetic dataset
num_samples = 500
# Bootstrap the original dataset
synthetic_data = data.sample(n=num_samples, replace=True)
# Add random noise to numeric columns
numeric_columns = ['Avg. Session Length', 'Time on App', 'Time on Website', 'Length of Membership', 'Yearly Amount Spent']
noise_factor = 0.05 # 5% noise
for column in numeric_columns:
noise = np.random.normal(loc=0.0, scale=noise_factor * synthetic_data[column].std(), size=num_samples)
synthetic_data[column] += noise
# Optionally save the synthetic dataset to a new CSV file
synthetic_data.to_csv('/mnt/data/Synthetic_Ecommerce_Customers.csv', index=False)
synthetic_data.head()
此代码将:
-
从原始数据集中抽取 500 条记录,允许替换(重采样)。
-
根据每列标准差的 5%向每个数值列添加高斯噪声。
-
可选地,新的合成数据集可以保存为 CSV 文件。
您是否希望继续生成此合成数据集?或者您在合成过程中有任何修改或特定要求?
[响应结束]**
接下来,我们将指导 ChatGPT 生成代码,并为我们提供下载数据集的文件:
[提示]**
请生成数据集并使其可供下载。
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 已执行代码并提供了下载合成数据集的链接:
[提示响应]**
合成数据集已生成并保存。您可以使用以下链接下载它:
Download Synthetic Ecommerce Customers Dataset
[响应结束]**
在这里,我们了解到 ChatGPT Plus 可以提供代码、执行代码,并且生成可供下载的 CSV 文件。
接下来,我们将请求 ChatGPT Plus 生成完整的代码,并在合成数据集中添加噪声后评估模型性能。让我们转到下一个功能。
功能 4:为合成数据集生成单步创建模型的代码
[提示]**
请您生成一个完整的代码,以在我们的合成数据集上执行线性回归,应用 Lasso 和 Ridge 正则化技术,并评估模型性能?
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 提供了完整的代码:
[提示响应]**
当然,我可以为您提供一段 Python 脚本,该脚本可以在我们刚刚创建的合成数据集上执行线性回归,以及 Lasso 和 Ridge 回归,我们还将使用 R-squared 分数、均方误差(MSE)和平均绝对误差(MAE)等指标来评估模型性能。
下面是整个过程的完整 Python 代码片段:
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression, Lasso, Ridge
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
# Load the synthetic dataset
data = pd.read_csv('/path/to/your/Synthetic_Ecommerce_Customers.csv')
# Feature selection: Using numerical columns as features
features = data[['Avg. Session Length', 'Time on App', 'Time on Website', 'Length of Membership']]
target = data['Yearly Amount Spent']
# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.2, random_state=42)
# Initialize the models
linear_model = LinearRegression()
lasso_model = Lasso(alpha=0.1) # alpha is the regularization strength
ridge_model = Ridge(alpha=1.0) # alpha is the regularization strength
# Train the models
linear_model.fit(X_train, y_train)
lasso_model.fit(X_train, y_train)
ridge_model.fit(X_train, y_train)
# Predictions
linear_predictions = linear_model.predict(X_test)
lasso_predictions = lasso_model.predict(X_test)
ridge_predictions = ridge_model.predict(X_test)
# Evaluations
def evaluate_model(predictions, actual):
mse = mean_squared_error(actual, predictions)
mae = mean_absolute_error(actual, predictions)
r2 = r2_score(actual, predictions)
return mse, mae, r2
linear_mse, linear_mae, linear_r2 = evaluate_model(linear_predictions, y_test)
lasso_mse, lasso_mae, lasso_r2 = evaluate_model(lasso_predictions, y_test)
ridge_mse, ridge_mae, ridge_r2 = evaluate_model(ridge_predictions, y_test)
# Print out the metrics
print("Linear Regression - MSE: {:.2f}, MAE: {:.2f}, R2: {:.2f}".format(linear_mse, linear_mae, linear_r2))
print("Lasso Regression - MSE: {:.2f}, MAE: {:.2f}, R2: {:.2f}".format(lasso_mse, lasso_mae, lasso_r2))
print("Ridge Regression - MSE: {:.2f}, MAE: {:.2f}, R2: {:.2f}".format(ridge_mse, ridge_mae, ridge_r2))
确保在本地环境中运行脚本时,将 '/path/to/your/Synthetic_Ecommerce_Customers.csv' 替换为你的合成数据集实际存储的实际路径。
此脚本将:
-
加载你的合成数据集。
-
选择合适的特征并将数据分为训练集和测试集。
-
初始化并训练线性、Lasso 和岭回归模型。
-
评估并打印出每个模型的性能指标。
这应该有助于你理解性能差异和正则化在你预测模型中的影响!
[End of response]
我们将指示 ChatGPT Plus 为我们执行代码:
[Prompt]
请执行代码。
[End of prompt]
ChatGPT Plus 应已执行代码,我们应该收到以下类似的响应:
[Prompt response]
模型已经训练和评估。以下是每个模型的性能指标:
线性回归:
-
均方误差 (MSE): 133.01
-
平均绝对误差 (MAE): 8.79
-
决定系数 (R2): 0.977
Lasso 回归:
-
均方误差 (MSE): 133.04
-
平均绝对误差 (MAE): 8.79
-
决定系数 (R2): 0.977
岭回归:
-
均方误差 (MSE): 133.08
-
平均绝对误差 (MAE): 8.79
-
决定系数 (R2): 0.977
所有三个模型都显示出非常相似的性能指标,高 R2 分数接近 0.977,表明每个模型解释了年度消费金额的 97.7% 的方差。模型之间 MSE 和 MAE 的细微差异很小,表明正则化(Lasso 和岭回归)在此特定情况下并没有显著改变性能,这可能是由于不存在多重共线性或其他这些技术有助于解决的问题。
[End of response]
通过完成这个练习,你不仅学习了如何使用 ChatGPT 和 ChatGPT Plus 的免费版本进行回归分析,还学会了如何使用自己的数据集逐步创建线性回归模型,应用正则化技术,并生成合成数据集。
作业
为我们的数据集生成单步代码,并在 Google Colab 中执行随机森林算法。
挑战
你能否通过结合提示策略来增强以下提示,以确保检索到特定的数据集,而不是可能不存在或路径不正确的随机数据集,确保其适用于预期用途?
提供当前提示以及改进后的提示的输出:
[Prompt]
请提供一份完整的端到端 Python 代码,用于公开可用的广告数据集,并详细说明它兼容 Jupyter Notebook。
[End of prompt]
摘要
我们探讨了在构建回归模型中应用 TAG、PIC 和 LIFE 提示策略的应用,同时使用 ChatGPT 和 ChatGPT Plus 进行快速分析和预测任务。这种方法在机器学习发展的早期阶段尤其有价值,它提供了即时的洞察力,并允许在不管理执行环境或编程实例的负担下实验不同的模型或算法。此外,我们还学会了如何有效地利用单个提示来生成全面的代码。虽然可以为离散的任务或步骤创建提示,但其中许多只需要简短的代码行,而这并不是这里的重点。提供反馈在这个过程中至关重要,验证输出对于确保代码的功能性是至关重要的。
在下一章中,我们将学习如何在 Fashion-MNIST 数据集的帮助下,使用 ChatGPT 生成多层感知器(MLP)模型的代码。
加入我们的 Discord 社区
加入我们的 Discord 空间,与作者和其他读者进行讨论:

第十四章:使用 ChatGPT 构建 Fashion-MNIST 的 MLP 模型
简介
在建立预测建模的基础理解之上,我们现在深入到多层感知器(MLP)模型的动态世界。在本章中,我们将从头开始构建一个 MLP 模型,利用神经网络在预测分析中的灵活性和强大功能。
我们对 MLP 的探索标志着向复杂建模技术领域的重大飞跃。虽然线性回归为数据内部建模关系提供了有价值的见解,但 MLP 提供了一个丰富的框架来捕捉复杂的模式和非线性依赖关系,这使得它们非常适合广泛的预测任务。
通过实际实验和迭代优化,我们将揭示 MLP 架构和优化的复杂性。从设计初始网络结构到微调超参数,以及采用批归一化和 dropout 等高级技术,我们的目标是让您掌握利用神经网络在预测建模中发挥其全部潜力的知识和技能。
在我们导航 MLP 模型的构建和优化过程中,我们将深入研究神经网络动态的潜在原理,探讨不同的架构选择和优化策略如何影响模型性能和泛化能力。
商业问题
一家时尚电商店铺希望通过利用机器学习技术深入了解顾客行为和偏好,从而优化顾客参与度和增加收入。通过分析代表顾客购买的各种时尚商品的图像数据,该店铺旨在定制其产品推荐,提高顾客满意度,并提升整体购物体验。
问题与数据领域
在本章中,我们将使用 Fashion-MNIST 数据集,通过 MLP 模型来理解顾客偏好与购买模式之间的关系。MLP 模型为图像分类任务提供了一个强大的框架,使我们能够根据顾客与在线店铺的互动来预测顾客可能购买的服装或配饰类型。通过揭示顾客偏好的模式,电商店铺可以个性化推荐并优化库存管理,以满足其客户群体的多样化需求。
数据集概述
时尚电商店铺收集代表各种时尚商品的图像数据,这些商品被分类到不同的类别中,并来自其顾客。Fashion-MNIST 数据集包含 70,000 张表示服装和配饰的灰度图像,每张图像都与一个特定的标签相关联,指示其类别,大小为 28x28。
数据集中的特征包括:
-
图像数据:代表时尚商品的灰度图像,每个图像都表示为一个像素强度的矩阵。这些图像作为训练 MLP 模型的输入数据。
-
标签:分配给每个图像的类别标签,代表所描绘的服装或配饰的类型。标签范围从 0 到 9,对应于 T 恤/上衣、裤子、开衫、连衣裙、外套、凉鞋、衬衫、运动鞋、包和踝靴等类别。
通过分析这些图像数据和相应的标签,我们旨在训练一个 MLP 模型,能够根据视觉特征准确地对时尚物品进行分类。这个预测模型将使电子商务商店能够提供个性化的产品推荐,增强客户参与度,并通过提供符合个人偏好的无缝购物体验来最终增加收入。

图 14.1:Fashion-MNIST 数据集
将问题分解为特征
由于 Fashion-MNIST 数据集的性质,它包含不同类别中时尚物品的灰度图像,我们将首先构建一个基线 MLP 模型。这包括以下高级步骤:
-
构建基线模型:用户将了解使用 ChatGPT 构建用于图像分类的简单 MLP 模型的过程。我们将指导用户加载 Fashion-MNIST 数据集,预处理图像数据,将其分为训练集和测试集,定义模型架构,训练模型,进行预测,并评估其性能。
-
向模型添加层:一旦建立了基线模型,用户将学习如何实验性地向 MLP 架构添加额外的层。我们将探讨增加模型的深度或宽度如何影响其性能和捕捉图像数据中复杂模式的能力。
-
实验不同的批量大小:用户将在模型训练过程中尝试不同的批量大小,以观察其对训练速度、收敛性和泛化性能的影响。我们将探讨批量大小如何影响计算效率和模型稳定性的权衡。
-
调整神经元数量:用户将探索调整 MLP 模型每层的神经元数量对模型的影响。通过增加或减少神经元数量,用户可以观察到模型容量及其从图像数据中学习复杂特征的能力的变化。
-
尝试不同的优化器:最后,用户将尝试不同的优化算法,如 SGD、Adam 和 RMSprop,以优化 MLP 模型的训练过程。我们将观察不同的优化器如何影响训练动态、收敛速度和最终模型性能。
通过遵循这些步骤,用户将全面了解使用 Fashion-MNIST 数据集构建和优化用于图像分类任务的 MLP 模型。他们将学习如何迭代地改进模型架构和训练过程,以实现分类时尚物品的最佳性能和准确性。
提示策略
为了利用 ChatGPT 进行机器学习,我们需要清楚地了解如何实现针对代码生成的特定提示策略。
让我们头脑风暴一下,在这个任务中我们想要实现什么,以便更好地理解需要放入提示中的内容。
策略 1:任务-行动-指南(TAG)提示策略
1.1 - 任务:具体的任务或目标是创建一个用于 Fashion-MNIST 数据集的分类模型。
1.2 - 行动:使用 MLP 为 Fashion-MNIST 数据集创建分类模型的关键步骤包括:
-
数据预处理:归一化像素值,将图像展平为向量,并编码分类标签。
-
数据拆分:将数据集划分为训练集、验证集和测试集。
-
模型选择:选择 MLP 作为分类模型。
-
模型训练:在训练数据上训练 MLP。
-
模型评估:使用准确率、精确率、召回率和混淆矩阵等指标来评估模型的表现。
1.3 - 指南:在我们的提示中,我们将向 ChatGPT 提供以下指南:
-
代码应与 Jupyter 笔记本兼容。
-
确保对每一行代码都有详细的注释。
-
您必须详细解释代码的每一行,然后将这些代码复制到笔记本的文本块中,在提供代码之前对代码中使用的每种方法进行详细说明。
策略 2:角色扮演-指示-背景(PIC)提示策略
2.1 - 角色扮演:我们将扮演一个需要学习模型创建不同步骤的初学者角色,因此代码应逐步生成。
2.2 - 指示:我们已经指定我们想要生成的代码用于具有单层的 MLP 模型,并指示 ChatGPT 一次提供一步,并等待用户的反馈。
2.3 - 背景:在这种情况下,ChatGPT 已经了解 Fashion-MNIST 数据集,因为它广为人知,所以我们不需要提供额外的背景信息。
策略 3:学习-即兴发挥-反馈-评估(LIFE)提示策略
3.1 - 学习:
- 我们想要了解 MLP 模型及其工作原理。
3.2 - 即兴发挥:
- 我们将在添加更多层、更改批量大小或激活函数等时使用这些知识。
3.3 - 反馈:
-
分享代码和模型输出以供反馈,以确保有效的学习和理解。
-
结合建议和批评来完善模型和方法。
-
提供错误以排除故障和解决问题。
3.4-评估:
执行 ChatGPT 提供的代码以确保准确性和有效性。这一点在本章中始终使用。
使用 ChatGPT 的免费版本构建 MLP 模型以准确分类 Fashion-MNIST 图像
ChatGPT 的付费版本有一个代码解释器,但它不支持执行代码所需的深度学习库,如 Keras 和 TensorFlow。因此,在本章中,我们将坚持使用 ChatGPT 的免费版本。
特征 1:构建基线模型
让我们为基线模型制定初始提示。
[Prompt]
我想为 Fashion-MNIST 数据集(TAG 1.1)创建一个简单的分类模型,该模型由单层 MLP 模型(PIC 2.2)组成。它应包括以下步骤(TAG1.2),一次提供一个步骤,并等待用户的反馈(PIC 2.2)。
-
数据预处理:归一化像素值,将图像展平为向量,并编码分类标签。
-
数据拆分:将数据集分为训练集、验证集和测试集。
-
模型选择:选择多层感知器(MLP)作为分类模型。
-
模型训练:在训练数据上训练 MLP。
-
模型评估:使用准确率、精确率、召回率和混淆矩阵等指标来评估模型性能。
我是一个初学者用户(PIC 2.1),所以请提供逐步的代码,并严格遵循以下说明(TAG 1.3):
代码应与 Jupyter Notebook 兼容。
确保每行代码都有详细的注释。
您必须详细解释代码中的每一行,然后将这些代码复制到笔记本的文本块中,在提供代码之前对每个方法进行详细说明。
[End of prompt]
您应该收到以下类似的响应,其中 ChatGPT 为我们提供了步骤 1,数据预处理的代码,以及详细的注释和说明。
[Prompt response]
当然,让我们从第一步开始:数据预处理。
数据预处理:
- 数据预处理是机器学习中的一个关键步骤,我们通过应用各种转换和归一化技术来准备我们的数据以进行训练。在这个步骤中,我们将归一化像素值,将图像展平为向量,并编码分类标签。
归一化像素值:
- 归一化像素值确保每个像素的值被缩放到 0 到 1 之间。这对于神经网络在训练过程中更快地收敛以及防止任何特定特征主导学习过程非常重要。
展平图像为向量:
- MLP 模型不能直接处理多维数据,如图像。因此,我们需要将图像展平为 1D 向量,其中每个像素值成为一个特征。
编码分类标签:
- 由于我们的标签是分类的(例如,不同类型的服装项目),我们需要将它们编码为数值,以便我们的模型能够理解和学习。
现在,让我们将这些步骤用代码实现:
import numpy as np
from tensorflow.keras.datasets import fashion_mnist
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.model_selection import train_test_split
# Step 1: Load Fashion-MNIST dataset
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
# Step 2: Normalize pixel values
def normalize_images(train_data, test_data):
# Convert to float32 to ensure division results in float
train_data = train_data.astype('float32')
test_data = test_data.astype('float32')
# Normalize pixel values to the range 0-1
train_data /= 255.0
test_data /= 255.0
return train_data, test_data
# Step 3: Flatten images into vectors
def flatten_images(train_data, test_data):
# Reshape images to vectors
train_data = train_data.reshape(train_data.shape[0], -1)
test_data = test_data.reshape(test_data.shape[0], -1)
return train_data, test_data
# Step 4: Encode categorical labels
def encode_labels(train_labels, test_labels):
# Initialize LabelEncoder
label_encoder = LabelEncoder()
# Fit LabelEncoder on training labels and transform both training and testing labels
train_labels = label_encoder.fit_transform(train_labels)
test_labels = label_encoder.transform(test_labels)
return train_labels, test_labels
# Apply data preprocessing steps
x_train, x_test = normalize_images(x_train, x_test)
x_train, x_test = flatten_images(x_train, x_test)
y_train, y_test = encode_labels(y_train, y_test)
# Print the shapes of preprocessed data
print("x_train shape:", x_train.shape)
print("y_train shape:", y_train.shape)
print("x_test shape:", x_test.shape)
print("y_test shape:", y_test.shape)
说明:
-
我们导入必要的库,如 NumPy 进行数值运算,TensorFlow 用于加载 Fashion-MNIST 数据集,以及 scikit-learn 进行数据预处理。
-
我们使用
fashion_mnist.load_data()方法加载 Fashion-MNIST 数据集,并将其分为训练集和测试集。 -
定义了三个函数
normalize_images、flatten_images和encode_labels来执行每个数据预处理步骤。
在每个函数内部:
-
normalize_images函数通过将像素值除以 255 来将它们缩放到 0 到 1 之间。 -
flatten_images函数将图像重塑为 1D 向量。 -
encode_labels函数使用LabelEncoder将分类标签转换为数值。
最后,我们将这些预处理步骤应用于训练数据和测试数据,并打印它们的形状以确保一切处理正确。
[End of response]
我们现在将应用提示评估策略(LIFE 3.4)并通过在 Google Colab 环境中执行每个步骤来验证代码。该代码从 Keras 加载模型,规范化图像,将它们展平为向量,使用 LabelEncoder 编码标签,然后应用预处理并打印形状。代码运行良好,无需任何修改或错误。
请参考 使用 ChatGPT 在 Fashion-MNIST 数据集上学习 MLP.ipynb 笔记本中的可执行代码片段,或从提示响应中复制代码。

图 14.2:代码执行 – 数据预处理
我们现在将指示 ChatGPT 继续下一步。
[Prompt]
让我们继续下一步。
[End of prompt]
我们应该收到以下响应,其中 ChatGPT 应该提供步骤 2 – 数据分割的代码。
[Prompt response]
太好了!让我们继续下一步:
数据分割:
- 在这一步,我们将数据集划分为训练集、验证集和测试集。训练集将用于训练模型,验证集将用于调整超参数以防止过拟合,测试集将用于评估模型在未见数据上的性能。
训练集:
- 训练集用于使用梯度下降等优化算法训练模型的参数(权重和偏差)。
验证集:
- 验证集用于在训练期间评估模型性能并调整学习率、批大小和训练轮数等超参数以优化模型性能。
测试集:
- 测试集用于评估训练模型在未见数据上的最终性能,并评估其泛化能力。
现在,让我们分割数据集:
# Step 5: Split dataset into training, validation, and testing sets
def split_dataset(train_data, train_labels, test_data, test_labels, validation_size=0.1, test_size=0.1, random_state=42):
# Split training set into training and validation sets
x_train, x_val, y_train, y_val = train_test_split(train_data, train_labels, test_size=validation_size, random_state=random_state)
# Split testing set into testing and validation sets
x_test, x_val, y_test, y_val = train_test_split(test_data, test_labels, test_size=test_size, random_state=random_state)
return x_train, y_train, x_val, y_val, x_test, y_test
# Apply data splitting
x_train, y_train, x_val, y_val, x_test, y_test = split_dataset(x_train, y_train, x_test, y_test)
# Print the shapes of split datasets
print("Training set:")
print("x_train shape:", x_train.shape)
print("y_train shape:", y_train.shape)
print("\nValidation set:")
print("x_val shape:", x_val.shape)
print("y_val shape:", y_val.shape)
print("\nTesting set:")
print("x_test shape:", x_test.shape)
print("y_test shape:", y_test.shape)
说明:
-
我们定义了一个
split_dataset函数,使用 scikit-learn 的train_test_split函数将数据集分割为训练集、验证集和测试集。 -
该函数接受训练数据、训练标签、测试数据、测试标签以及可选参数,如验证集大小、测试集大小和随机状态。
-
我们使用指定的验证集大小将训练集分为训练集和验证集。
-
然后,我们使用指定的测试集大小将测试集分割为测试集和验证集。
-
最后,我们打印分割数据集的形状以验证分割。
[End of response]
我们在笔记本中执行代码,它成功运行。数据被分成训练集、验证集和测试集,大小如指定。

图 14.3:代码执行 – 数据拆分
我们现在将指示 ChatGPT 进行下一步。
[提示]
让我们进行下一步,并定义一个单层基线 MLP 模型。
[提示结束]
我们应该收到以下类似的响应,其中 ChatGPT 应提供具有输入层、一个隐藏层和一个输出层的单层 MLP 模型的代码。
[提示响应]
当然,让我们继续下一步:
模型选择:
- 在这一步,我们将选择多层感知器(MLP)作为分类模型。MLP 由一个或多个层组成,每层中的神经元都与下一层完全连接。为了简单起见,我们将从一个单层 MLP 作为基线模型开始。
单层 MLP:
- 单层 MLP 由一个输入层、一个隐藏层和一个输出层组成。然而,由于它只有一个隐藏层,它通常被称为单层感知器。隐藏层中的每个神经元都从输入层中的所有神经元接收输入,并产生一个输出,然后通过激活函数传递以引入非线性。最后,输出层产生最终的预测。
现在,让我们定义基线单层 MLP 模型:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
# Step 6: Define a single-layer MLP model
def create_single_layer_mlp(input_shape, num_classes):
model = Sequential()
# Add a single hidden layer with ReLU activation
model.add(Dense(128, activation='relu', input_shape=input_shape))
# Add output layer with softmax activation for multi-class classification
model.add(Dense(num_classes, activation='softmax'))
return model
# Define input shape and number of classes
input_shape = x_train.shape[1:]
num_classes = len(np.unique(y_train))
# Create the single-layer MLP model
model = create_single_layer_mlp(input_shape, num_classes)
# Print model summary
model.summary()
说明:
-
我们从
TensorFlow.keras导入必要的模块来定义 MLP 模型。 -
我们定义一个函数
create_single_layer_mlp来创建一个单层 MLP 模型。 -
在函数内部:
-
我们初始化一个序列模型。
-
我们添加一个具有 128 个神经元和 ReLU 激活函数的单个隐藏层。
-
我们添加一个具有 softmax 激活函数的输出层,用于多类分类。
-
我们根据训练数据的形状和输出类的数量指定输入形状。
-
我们使用定义的函数创建单层 MLP 模型。
-
-
最后,我们打印出模型架构的摘要。
[响应结束]
我们执行代码,它运行无误,并打印出模型摘要,包括层类型、输出形状和参数数量。它还提供了总参数数、可训练参数和非可训练参数。在我们的情况下,所有参数都是可训练参数。

图 14.4:代码执行 – 基线模型架构
我们将指示 ChatGPT 进行下一步以进行模型训练。
[提示]
让我们进行下一步。
[提示结束]
我们应该收到以下类似的响应,其中 ChatGPT 应提供模型训练的代码,并解释涉及的关键概念,例如损失函数和优化算法,以及注释。
[提示响应]
当然,让我们继续下一步:
模型训练:
- 在这一步,我们将训练数据用于训练 MLP 模型。训练包括将训练数据输入模型,使用诸如随机梯度下降之类的优化算法调整其参数(权重和偏置),并最小化损失函数以使预测尽可能接近实际标签。
损失函数:
- 损失函数衡量预测标签与实际标签之间的差异。对于像 Fashion-MNIST 这样的多分类问题,我们通常使用交叉熵作为损失函数。
优化算法:
- 优化算法负责在训练过程中更新模型的参数以最小化损失函数。我们将使用 Adam 优化器,它是随机梯度下降的扩展,在深度学习中广泛使用。
训练参数:
- 我们将指定训练参数,如批量大小和训练轮数。批量大小决定了在更新模型参数之前处理的样本数量,而训练轮数指定了整个训练数据集通过模型前向和反向传递的次数。
现在,让我们训练模型:
from tensorflow.keras.utils import to_categorical
# Step 7: Model Training
def train_model(model, x_train, y_train, x_val, y_val, batch_size=32, epochs=10):
# Convert class vectors to binary class matrices (one-hot encoding)
y_train = to_categorical(y_train)
y_val = to_categorical(y_val)
# Compile the model with categorical cross-entropy loss and Adam optimizer
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# Train the model on the training data
history = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(x_val, y_val))
return history
# Train the model
history = train_model(model, x_train, y_train, x_val, y_val, batch_size=32, epochs=10)
说明:
-
我们从
TensorFlow.keras导入必要的模块来编译和训练模型。 -
我们定义了一个
train_model函数,用于在训练数据上训练模型。 -
函数内部:
-
我们使用一维编码将类别标签转换为二进制类别矩阵。
-
我们使用交叉熵损失函数和 Adam 优化器编译模型。
-
我们使用指定的批量大小和训练轮数在训练数据上训练模型。
-
-
最后,我们使用定义的函数训练模型,并将训练历史存储以供可视化和分析。
[响应结束]**
我们执行代码,它运行没有任何错误。它对类别标签应用了一维编码,使模型符合交叉熵损失函数和 Adam 优化器,并使用批量大小 32 和 10 个训练轮数训练模型。这实现了大约 88%的验证准确率。

图 14.5:代码执行 – 模型训练
我们现在将指示 ChatGPT 进行模型评估的下一步。
[提示]**
让我们继续下一步。
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 应提供评估测试集的代码,并解释准确率、精确率、召回率和混淆矩阵等概念。
[提示响应]**
模型评估:
- 在这一步,我们将使用各种指标来评估训练的 MLP 模型在测试数据上的性能。分类任务中常用的指标包括准确率、精确率、召回率和混淆矩阵。
准确率:
- 准确率衡量的是在总实例中正确分类的实例比例。
精确率:
- 精确率衡量的是所有正例中正确预测正例的比例。它表明模型避免假正例的能力。
召回率:
- 回忆度衡量的是所有实际正例中正确预测正例的比例。它表明模型识别所有正例的能力。
混淆矩阵:
- 混淆矩阵通过显示正确预测、正确否定、错误预测和错误否定的预测数量,总结了模型的表现。
现在,让我们评估模型:
import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix
# Step 8: Model Evaluation
def evaluate_model(model, x_test, y_test):
# Predict labels for testing data
y_pred = model.predict(x_test)
# Convert predicted labels to class labels
y_pred_classes = np.argmax(y_pred, axis=1)
# Convert true labels to class labels
y_true_classes = np.argmax(y_test, axis=1)
# Calculate accuracy
accuracy = accuracy_score(y_true_classes, y_pred_classes)
# Calculate precision
precision = precision_score(y_true_classes, y_pred_classes, average='weighted')
# Calculate recall
recall = recall_score(y_true_classes, y_pred_classes, average='weighted')
# Calculate confusion matrix
cm = confusion_matrix(y_true_classes, y_pred_classes)
return accuracy, precision, recall, cm
# Evaluate the model
accuracy, precision, recall, cm = evaluate_model(model, x_test, y_test)
# Print evaluation metrics
print("Accuracy:", accuracy)
print("Precision:", precision)
print("Recall:", recall)
print("Confusion Matrix:\n", cm)
# Visualize confusion matrix
plt.imshow(cm, cmap=plt.cm.Blues)
plt.title('Confusion Matrix')
plt.colorbar()
plt.xlabel('Predicted Labels')
plt.ylabel('True Labels')
plt.show()
说明:
-
我们从
matplotlib.pyplot和 scikit-learn 导入必要的模块,以可视化混淆矩阵并计算评估指标。 -
我们定义了一个
evaluate_model函数来评估模型在测试数据上的性能。 -
在函数内部:
-
我们使用训练好的模型在测试数据上进行预测。
-
我们将预测标签和真实标签转换为类别标签。
-
我们使用 scikit-learn 的指标函数计算准确率、精确率、召回率和混淆矩阵。
-
-
最后,我们使用定义的函数评估模型,打印评估指标,并可视化混淆矩阵。
[响应结束]**
我们执行代码并注意到它由于测试标签未编码而出现错误,但这一点从错误本身并不明显。我们可以通过向 ChatGPT 提供错误信息来应用提示策略以获得帮助解决错误。

图 14.6:代码执行 – 模型评估错误
我们向 ChatGPT 提供了错误信息以获得帮助。
[快速]
看起来测试标签存在问题。我们收到了以下错误:
------282/282 [==============================] - 2s 5ms/step --------------------------------------------------------------------------- AxisError Traceback (most recent call last) <ipython-input-5-02193580c831> in <cell line: 23>() 21 22 # Evaluate the model ---> 23 accuracy, precision, recall, cm = evaluate_model(model, x_test, y_test) 24 25 # Print evaluation metrics 2 frames /usr/local/lib/python3.10/dist-packages/numpy/core/fromnumeric.py in _wrapfunc(obj, method, *args, **kwds) 57 58 try: ---> 59 return bound(*args, **kwds) 60 except TypeError: 61 # A TypeError occurs if the object does have such a method in its AxisError: axis 1 is out of bounds for array of dimension 1
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 应提供故障排除建议。
[快速响应]
看起来标签的形状存在问题。错误表明 argmax 函数指定的轴超出了范围。这通常发生在标签不是预期的格式或形状时。
让我们再次检查标签的形状,确保它们以正确的格式进行评估:
print("Shape of y_test:", y_test.shape)
这将帮助我们了解标签的形状并识别任何差异。一旦我们识别出问题,我们就可以相应地纠正它。
[响应结束]**
我们打印y_test的形状:

图 14.7:代码执行 - 测试标签的形状
我们现在将向 ChatGPT 提供代码的输出。
[快速]
Shape of y_test: (9000,)
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 应理解问题并为我们提供修正后的代码。
[快速响应]
感谢您提供y_test的形状。看起来标签是 1D 格式,这意味着它们不是 one-hot 编码的。在评估函数中,我们假设标签是 one-hot 编码的,这导致了错误。
要解决这个问题,我们需要修改评估函数以处理 1D 标签而不是 one-hot 编码的标签。我们可以通过移除标签到 one-hot 编码的转换,直接比较预测标签和真实标签来实现这一点。
让我们相应地更新评估函数:
# Step 8: Model Evaluation
def evaluate_model(model, x_test, y_test):
# Predict labels for testing data
y_pred = model.predict(x_test)
# Convert predicted labels to class labels
y_pred_classes = np.argmax(y_pred, axis=1)
# Calculate accuracy
accuracy = accuracy_score(y_test, y_pred_classes)
# Calculate precision
precision = precision_score(y_test, y_pred_classes, average='weighted')
# Calculate recall
recall = recall_score(y_test, y_pred_classes, average='weighted')
# Calculate confusion matrix
cm = confusion_matrix(y_test, y_pred_classes)
return accuracy, precision, recall, cm
# Evaluate the model
accuracy, precision, recall, cm = evaluate_model(model, x_test, y_test)
# Print evaluation metrics
print("Accuracy:", accuracy)
print("Precision:", precision)
print("Recall:", recall)
print("Confusion Matrix:\n", cm)
# Visualize confusion matrix
plt.imshow(cm, cmap=plt.cm.Blues)
plt.title('Confusion Matrix')
plt.colorbar()
plt.xlabel('Predicted Labels')
plt.ylabel('True Labels')
plt.show()
说明:
我们移除了标签到 one-hot 编码的转换,并在评估函数中直接使用了y_test真实标签。
我们更新了准确率、精确率和召回率计算函数,以使用 1D 标签而不是 one-hot 编码的标签。
现在,评估函数应该能够正确地使用提供的 1D 标签,并且评估指标应该被准确计算。
[End of response]
我们将执行代码,现在它运行正常,因为提供的代码已被修改以使用 1D 标签。它还打印了混淆矩阵。

图 14.8:代码执行 – 混淆矩阵
这完成了功能 1。我们使用单层 MLP 模型实现了 0.8796(87.96%)的准确率、0.8817(88.17%)的精确率和 0.8796(87.96%)的召回率。接下来,我们将转向第二个功能,向模型添加更多层。我们还将学习 ChatGPT 如何帮助我们。
功能 2:向模型添加层
现在我们有了基线模型架构,让我们尝试向其中添加层。我们将应用提示策略来改进模型,并要求 ChatGPT 提供其代码。
[Prompt]
现在我们有一个单层的基线模型,您能提供具有两个层和三个层的代码,并计算和比较由于添加层而导致的模型性能吗?
[End of prompt]
我们应该收到以下响应,其中 ChatGPT 理解请求并提供两个层和三个层模型的代码以及比较结果。
[Prompt response]
当然!让我们定义并训练具有两个和三个层的多层感知器(MLP)模型,然后比较它们与基线单层模型的性能。
定义具有两个和三个层的 MLP 模型:
- 我们将定义具有额外隐藏层的 MLP 模型,以查看性能如何随着层数的增加而变化。
两层 MLP:
- 两层 MLP 将包括输入层、两个隐藏层和输出层。
三层 MLP:
- 三层 MLP 将包括输入层、三个隐藏层和输出层。
现在,让我们定义并训练这些模型:
# Define a two-layer MLP model
def create_two_layer_mlp(input_shape, num_classes):
model = Sequential()
# Add first hidden layer with ReLU activation
model.add(Dense(128, activation='relu', input_shape=input_shape))
# Add second hidden layer with ReLU activation
model.add(Dense(64, activation='relu'))
# Add output layer with softmax activation for multi-class classification
model.add(Dense(num_classes, activation='softmax'))
return model
# Define a three-layer MLP model
def create_three_layer_mlp(input_shape, num_classes):
model = Sequential()
# Add first hidden layer with ReLU activation
model.add(Dense(128, activation='relu', input_shape=input_shape))
# Add second hidden layer with ReLU activation
model.add(Dense(64, activation='relu'))
# Add third hidden layer with ReLU activation
model.add(Dense(32, activation='relu'))
# Add output layer with softmax activation for multi-class classification
model.add(Dense(num_classes, activation='softmax'))
return model
# Create two-layer MLP model
two_layer_model = create_two_layer_mlp(input_shape, num_classes)
# Create three-layer MLP model
three_layer_model = create_three_layer_mlp(input_shape, num_classes)
# Train two-layer MLP model
two_layer_history = train_model(two_layer_model, x_train, y_train, x_val, y_val)
# Train three-layer MLP model
three_layer_history = train_model(three_layer_model, x_train, y_train, x_val, y_val)
模型比较:
一旦模型被训练,我们可以根据评估指标(如准确率、精确率、召回率和混淆矩阵)比较它们的性能。
# Evaluate two-layer MLP model
two_layer_accuracy, _, _, _ = evaluate_model(two_layer_model, x_test, y_test)
# Evaluate three-layer MLP model
three_layer_accuracy, _, _, _ = evaluate_model(three_layer_model, x_test, y_test)
# Print model comparison
print("Single-layer MLP Accuracy:", accuracy)
print("Two-layer MLP Accuracy:", two_layer_accuracy)
print("Three-layer MLP Accuracy:", three_layer_accuracy)
说明:
-
我们使用之前定义的函数定义了两个额外的具有两个和三个隐藏层的 MLP 模型。
-
我们使用训练数据和验证数据训练两个模型。
-
训练后,我们使用测试数据评估每个模型的表现,并比较它们的准确率。
比较有助于我们了解添加层如何影响模型的表现。
[响应结束]**
我们将执行定义具有两个和三个隐藏层的两个额外模型的代码。它还训练模型,评估这两个模型的表现,并打印结果。

图 14.9:特性 2 – 向模型添加层
根据这些结果,我们可以观察到,与单层和三层 MLP 模型相比,两层 MLP 模型实现了略高的准确率。然而,准确率之间的差异相对较小,这表明在这种情况下,添加层并没有显著提高模型的表现。让我们继续到下一个特性,并使用基线模型实验批量大小。
特性 3:实验批量大小
我们现在已经学会了如何添加更多层,在我们的案例中,由于数据集简单,影响不大。我们现在将尝试不同的批量大小。我们将应用提示策略来改进模型,并要求 ChatGPT 为我们提供相应的代码。
[提示]**
请提供具有三个批量大小组合的代码,并比较基线单层模型在这些组合中的性能?
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 理解请求,并为我们提供不同批量大小的代码以及比较结果。
[提示响应]**
定义和训练具有不同批量大小的模型:
-
我们将定义并训练具有三个不同批量大小组合的基线单层 MLP 模型。
-
组合 1:
批量大小 = 32 -
组合 2:
批量大小 = 64 -
组合 3:
批量大小 = 128
现在,让我们定义和训练这些模型:
# Define and train model with batch size = 32
model_batch_32 = create_single_layer_mlp(input_shape, num_classes)
history_batch_32 = train_model(model_batch_32, x_train, y_train, x_val, y_val, batch_size=32)
# Define and train model with batch size = 64
model_batch_64 = create_single_layer_mlp(input_shape, num_classes)
history_batch_64 = train_model(model_batch_64, x_train, y_train, x_val, y_val, batch_size=64)
# Define and train model with batch size = 128
model_batch_128 = create_single_layer_mlp(input_shape, num_classes)
history_batch_128 = train_model(model_batch_128, x_train, y_train, x_val, y_val, batch_size=128)
模型比较:
一旦模型被训练,我们可以根据评估指标如准确率来比较它们的性能。
# Evaluate models with different batch sizes
accuracy_batch_32, _, _, _ = evaluate_model(model_batch_32, x_test, y_test)
accuracy_batch_64, _, _, _ = evaluate_model(model_batch_64, x_test, y_test)
accuracy_batch_128, _, _, _ = evaluate_model(model_batch_128, x_test, y_test)
# Print model comparison
print("Single-layer MLP Accuracy (Batch Size = 32):", accuracy_batch_32)
print("Single-layer MLP Accuracy (Batch Size = 64):", accuracy_batch_64)
print("Single-layer MLP Accuracy (Batch Size = 128):", accuracy_batch_128)
说明:
-
我们定义并训练了三个具有不同批量大小组合(32、64 和 128)的基线单层 MLP 模型。
-
训练后,我们使用测试数据评估每个模型的表现,并比较它们的准确率。
比较有助于我们了解不同的批量大小如何影响模型的表现。
[响应结束]**
我们将执行定义具有批量大小 32、64 和 128 的模型的代码。它还训练模型,评估其性能,并打印性能比较的结果。

图 14.10:代码执行 – 批量大小比较
我们可以看到模型之间的准确率没有太大差异,因此我们将应用“学习提示策略”(LIFE 3.1)并询问 ChatGPT 可能的原因,为什么这可能会发生。
[提示]**
282/282 [==============================] - 1s 3ms/step 282/282 [==============================] - 1s 4ms/step 282/282 [==============================] - 1s 3ms/step
Single-layer MLP Accuracy (Batch Size = 32): 0.88
Single-layer MLP Accuracy (Batch Size = 64): 0.8723333333333333
Single-layer MLP Accuracy (Batch Size = 128): 0.8797777777777778
为什么准确率之间没有太大差异?
[提示结束]**
我们应该收到以下这样的响应,其中 ChatGPT 解释可能的原因。
[响应提示]**
使用不同批量大小训练的模型之间的准确率差异相对较小。这可能有几个原因:
-
数据集大小:与深度学习中常用的其他数据集相比,Fashion-MNIST 数据集相对较小。在较小的数据集上,批量大小对模型性能的影响可能不那么明显。
-
模型复杂度:基准单层 MLP 模型可能不足以充分利用不同批量大小的优势。在更复杂的模型中,如多层深度神经网络,批量大小对训练动态的影响可能更为显著。
-
优化算法:在训练模型时使用的 Adam 优化器根据梯度的第一和第二矩来调整每个参数的学习率。这种自适应学习率可以在一定程度上减轻批量大小的影响。
-
收敛:无论使用什么批量大小,模型可能已经收敛到相似的性能水平。一旦模型收敛,使用不同批量大小进行进一步训练可能不会在准确率上带来实质性的改进。
总体而言,虽然批量大小可以影响训练动态和收敛速度,但在某些情况下,其对模型性能的影响可能不那么明显,尤其是在相对简单的模型和数据集上。尝试更大和更复杂的模型以及不同的优化算法,可能会根据批量大小揭示更显著的性能差异。
[响应结束]**
现在我们已经学会了如何更改批量大小以及为什么它对我们的模型影响不大,让我们继续到下一个特性,并尝试基准模型隐藏层中神经元数量的变化。
特性 4:实验神经元数量
我们将应用提示策略来改进模型,并要求 ChatGPT 提供实验不同隐藏层神经元数量的代码。
[提示]**
请提供包含三个神经元数量组合的代码,并比较各种组合的基准单层模型性能?
[提示结束]**
我们应该收到以下这样的响应,其中 ChatGPT 理解请求并提供不同隐藏层神经元数量的代码,以及比较结果。
[响应提示]**
当然!让我们定义并训练基线单层 MLP 模型,使用隐藏层中神经元数量的三种不同组合,然后比较它们的性能。
定义并训练具有不同神经元数量的模型:
-
我们将定义并训练基线单层 MLP 模型,使用隐藏层中神经元数量的三种不同组合:
-
组合 1:
神经元数量 = 64 -
组合 2:
神经元数量 = 128 -
组合 3:
神经元数量 = 256
-
现在,让我们定义并训练这些模型:
# Define and train model with 64 neurons in the hidden layer
model_neurons_64 = create_single_layer_mlp(input_shape, num_classes)
history_neurons_64 = train_model(model_neurons_64, x_train, y_train, x_val, y_val)
# Define and train model with 128 neurons in the hidden layer
model_neurons_128 = create_single_layer_mlp(input_shape, num_classes)
history_neurons_128 = train_model(model_neurons_128, x_train, y_train, x_val, y_val)
# Define and train model with 256 neurons in the hidden layer
model_neurons_256 = create_single_layer_mlp(input_shape, num_classes)
history_neurons_256 = train_model(model_neurons_256, x_train, y_train, x_val, y_val)
模型比较:
一旦模型训练完成,我们可以根据评估指标(如准确率)比较它们的性能。
# Evaluate models with different number of neurons
accuracy_neurons_64, _, _, _ = evaluate_model(model_neurons_64, x_test, y_test)
accuracy_neurons_128, _, _, _ = evaluate_model(model_neurons_128, x_test, y_test)
accuracy_neurons_256, _, _, _ = evaluate_model(model_neurons_256, x_test, y_test)
# Print model comparison
print("Single-layer MLP Accuracy (Neurons = 64):", accuracy_neurons_64)
print("Single-layer MLP Accuracy (Neurons = 128):", accuracy_neurons_128)
print("Single-layer MLP Accuracy (Neurons = 256):", accuracy_neurons_256)
说明:
-
我们定义并训练了三个基线单层 MLP 模型,它们在隐藏层中神经元数量的组合不同(64、128 和 256)。
-
训练完成后,我们使用测试数据评估每个模型的性能,并比较它们的准确率。
比较有助于我们了解隐藏层中不同数量的神经元如何影响模型性能。
[响应结束**]
我们将执行定义具有 64、128 和 256 个神经元的模型的代码。它还训练模型,评估所有模型变体的性能,并打印性能比较的结果。

图 14.11:代码执行 – 神经元比较
我们可以看到差异不大,因此让我们转向我们的最终功能,即尝试不同的优化器。
功能 5:尝试不同的优化器
我们将应用提示策略来改进模型,并要求 ChatGPT 提供用于实验不同优化器(如 SGD、Adam 和 RMSProp)的代码。作为我们的提示策略的一部分,我们将具体明确地提到优化器应在编译时传递,并且标签应进行 one-hot 编码,因为这些步骤通常被 ChatGPT 忽略。
[提示**]
请您提供具有不同优化器(SGD、Adam 和 RMSprop)的代码,并比较基线单层模型在不同组合下的性能。优化器应在编译时传递,并且您应确保标签进行 one-hot 编码。
[提示结束**]
我们应该收到以下类似的响应,其中 ChatGPT 理解请求并提供不同优化器(如 SGD、Adam 和 RMSProp)的代码以及比较结果。
[提示响应**]
当然!让我们定义并训练一个基线单层 MLP 模型,使用三种不同的优化器(SGD、Adam 和 RMSprop),然后比较它们的性能。
定义并训练具有不同优化器的模型:
我们将定义并训练基线单层 MLP 模型,使用三种不同的优化器:SGD、Adam 和 RMSprop。
现在,让我们定义并训练这些模型:
from tensorflow.keras.optimizers import SGD, Adam, RMSprop
# Define and train model with SGD optimizer
def train_model_sgd(model, x_train, y_train, x_val, y_val, batch_size=32, epochs=10):
# One-hot encode the class labels
y_train_encoded = to_categorical(y_train)
y_val_encoded = to_categorical(y_val)
# Compile the model with SGD optimizer
model.compile(loss='categorical_crossentropy', optimizer=SGD(), metrics=['accuracy'])
# Train the model on the training data
history = model.fit(x_train, y_train_encoded, batch_size=batch_size, epochs=epochs, validation_data=(x_val, y_val_encoded))
return history
# Define and train model with Adam optimizer
def train_model_adam(model, x_train, y_train, x_val, y_val, batch_size=32, epochs=10):
# One-hot encode the class labels
y_train_encoded = to_categorical(y_train)
y_val_encoded = to_categorical(y_val)
# Compile the model with Adam optimizer
model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy'])
# Train the model on the training data
history = model.fit(x_train, y_train_encoded, batch_size=batch_size, epochs=epochs, validation_data=(x_val, y_val_encoded))
return history
# Define and train model with RMSprop optimizer
def train_model_rmsprop(model, x_train, y_train, x_val, y_val, batch_size=32, epochs=10):
# One-hot encode the class labels
y_train_encoded = to_categorical(y_train)
y_val_encoded = to_categorical(y_val)
# Compile the model with RMSprop optimizer
model.compile(loss='categorical_crossentropy', optimizer=RMSprop(), metrics=['accuracy'])
# Train the model on the training data
history = model.fit(x_train, y_train_encoded, batch_size=batch_size, epochs=epochs, validation_data=(x_val, y_val_encoded))
return history
# Train model with SGD optimizer
model_sgd = create_single_layer_mlp(input_shape, num_classes)
history_sgd = train_model_sgd(model_sgd, x_train, y_train, x_val, y_val)
# Train model with Adam optimizer
model_adam = create_single_layer_mlp(input_shape, num_classes)
history_adam = train_model_adam(model_adam, x_train, y_train, x_val, y_val)
# Train model with RMSprop optimizer
model_rmsprop = create_single_layer_mlp(input_shape, num_classes)
history_rmsprop = train_model_rmsprop(model_rmsprop, x_train, y_train, x_val, y_val)
模型比较:
一旦模型训练完成,我们可以根据评估指标(如准确率)比较它们的性能。
# Evaluate models with different optimizers
accuracy_sgd, _, _, _ = evaluate_model(model_sgd, x_test, y_test)
accuracy_adam, _, _, _ = evaluate_model(model_adam, x_test, y_test)
accuracy_rmsprop, _, _, _ = evaluate_model(model_rmsprop, x_test, y_test)
# Print model comparison
print("Single-layer MLP Accuracy (SGD):", accuracy_sgd)
print("Single-layer MLP Accuracy (Adam):", accuracy_adam)
print("Single-layer MLP Accuracy (RMSprop):", accuracy_rmsprop)
说明:
-
我们定义并训练了三个具有不同优化器(SGD、Adam 和 RMSprop)的基线单层 MLP 模型。
-
训练后,我们使用测试数据评估每个模型的性能,并比较它们的准确率。
比较有助于我们了解不同的优化器如何影响模型的表现。
[结束回复]
我们执行定义具有不同优化器的模型的代码。它还训练模型,评估所有模型变体的性能,并打印出比较它们性能的结果。

图 14.12:代码执行 – 不同优化器
根据这些结果,我们可以观察到 Adam 优化器实现了最高的准确率,其次是 RMSprop 和 SGD。这表明 Adam 在优化模型参数和改进测试数据上的性能方面表现更好。
作业
比较单层与两层隐藏层的模型性能,但使用 20 和 50 个周期。
挑战
在 ChatGPT 的帮助下,通过添加 dropout 层改进模型,并分析其对模型性能的影响。请随意向模型添加更多隐藏层。
摘要
本章中使用的提示策略提供了一种结构化的学习方法,用于使用 MLP 构建分类模型,其中 ChatGPT 协助生成代码。用户使用 Colab 笔记本验证代码,并向 ChatGPT 提供反馈。通过积极参与材料,你尝试了不同的技术,并迭代地完善了对使用 MLP 创建分类模型的理解,最终达到了更全面的理解。
在下一章中,我们将学习如何在 CIFAR-10 数据集的帮助下,使用 ChatGPT 生成用于 卷积神经网络(CNNs)的代码。
加入我们的 Discord 社区
加入我们的 Discord 社区空间,与作者和其他读者进行讨论:

第十五章:使用 ChatGPT 构建 CIFAR-10 的 CNN 模型
引言
在我们上一章中通过 Fashion-MNIST 数据集深入探讨了多层感知器(Multi-Layer Perceptron,MLP)之后,我们现在转向一个更复杂和视觉上更复杂的挑战。本章标志着我们从主要基于表格、灰度世界的 Fashion-MNIST 转向色彩丰富和多样化的 CIFAR-10 数据集。在这里,我们将重点提升到 卷积神经网络(Convolutional Neural Networks,CNNs),这是一类正在改变我们处理图像分类任务方式的深度神经网络。
我们在 MLP 章节中的旅程为我们理解神经网络的基本原理及其在分类简单灰度图像中的应用奠定了坚实的基础。现在,我们进入了一个更高级的领域,在这里 CNNs 占据主导地位。CIFAR-10 数据集包含 10 个不同类别中 32x32 彩色图像的阵列,为 MLPs 提供了一系列独特的挑战,MLPs 并不适合解决这些问题。这就是 CNNs 发挥作用的地方,它们能够捕捉图像中的空间和纹理模式。
随着我们从多层感知器(MLPs)过渡到 CNNs,我们将所获得的见解和知识应用于更复杂的数据集,这些数据集更接近现实世界场景。CIFAR-10 数据集不仅测试了图像分类模型的极限,而且为我们探索 CNNs 的高级功能提供了一个极好的平台。
本章旨在基于我们对神经网络的知识,并引导你了解卷积神经网络(CNNs)的细微差别。我们将深入探讨为什么 CNNs 是图像数据的优选选择,它们在处理颜色和纹理方面与 MLPs 的不同之处,以及是什么使得它们在从 CIFAR-10 数据集中对图像进行分类时如此有效。准备好开始一段旅程,带你从基础到 CNNs 更为复杂方面的深入探索。
商业问题
CIFAR-10 数据集为寻求增强各种对象图像识别能力并基于视觉数据优化决策过程的公司提出了一个商业挑战。众多行业,如电子商务、自动驾驶和监控,都可以从准确的对象分类和检测中受益。通过利用机器学习算法,企业旨在提高效率、提升用户体验并简化运营流程。
问题与数据领域
在这个背景下,我们将利用卷积神经网络(CNNs)来处理使用 CIFAR-10 数据集的对象识别任务。由于 CNNs 能够从原始像素数据中自动学习层次化特征,它们在处理与图像相关的问题上特别有效。通过在 CIFAR-10 数据集上训练 CNN 模型,我们的目标是开发一个能够准确地将对象分类到十个预定义类别之一的鲁棒系统。该模型可以应用于多个领域,例如基于图像的搜索引擎、自动监控系统以及制造业的质量控制。
数据集概述
CIFAR-10 数据集包含 60,000 张彩色图像,分为 10 个类别,每个类别有 6,000 张图像。每张图像的尺寸为 32x32 像素,并以 RGB 格式表示。数据集分为包含 50,000 张图像的训练集和包含 10,000 张图像的测试集。
数据集中的特征包括:
-
图像数据:各种对象的彩色图像,每个图像表示为一个包含红、绿、蓝通道像素强度的三维数组。这些图像作为训练 CNN 模型的输入数据。
-
标签:分配给每张图像的类别标签,代表所描绘对象的类别。标签范围从 0 到 9,对应于如飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车等类别。
通过分析 CIFAR-10 数据集及其相关标签,我们的目标是训练一个能够准确识别图像中描绘对象的 CNN 模型。然后,此预测模型可以部署到实际应用中,以自动化对象识别任务,改进决策过程,并提高各行业的整体效率。

图 15.1:CIFAR-10 数据集
将问题分解为特征
鉴于 CIFAR-10 数据集和 CNN 在图像识别中的应用,我们概述以下功能,以指导用户构建和优化 CNN 模型:
-
构建具有单个卷积层的基线 CNN 模型:用户将首先构建一个简单的 CNN 模型,其中包含单个卷积层用于图像分类。此功能侧重于定义基本架构,包括卷积滤波器、激活函数和池化层,以建立对 CNN 的基础理解。
-
实验添加卷积层:用户将探索向基线模型架构添加额外卷积层的影响。通过逐步增加网络的深度,用户可以观察到模型捕捉层次特征的能力如何演变,以及其学习复杂模式的能力如何提高。
-
整合 dropout 正则化:用户将学习如何将 dropout 正则化集成到 CNN 模型中,以减轻过拟合并提高泛化性能。通过在训练过程中随机丢弃单元,dropout 有助于防止网络过度依赖特定特征,并鼓励鲁棒的特征学习。
-
实现批量归一化:用户将探索批量归一化在稳定训练动态和加速收敛方面的好处。此功能侧重于将批量归一化层集成到 CNN 架构中,以归一化激活并减少内部协变量偏移,从而实现更快、更稳定的训练。
-
使用不同的优化器进行优化:本功能探讨了使用各种优化算法(包括 SGD、Adam 和 RMSprop)来训练 CNN 模型的效果。用户将比较使用不同优化器获得的训练动态、收敛速度和最终模型性能,从而能够为他们的特定任务选择最合适的优化策略。
-
执行数据增强:用户将尝试旋转、翻转、缩放和移位等数据增强技术,以增加训练数据集的多样性和规模。通过在训练过程中动态生成增强样本,用户可以提高模型泛化到未见数据的能力,并增强对输入图像变化的鲁棒性。
通过遵循这些功能,用户将获得关于使用 CIFAR-10 数据集构建、微调和优化 CNN 模型以进行图像分类任务的实用见解。他们将学习如何系统地实验不同的架构组件、正则化技术和优化策略,以实现卓越的性能和准确性,在物体识别中达到更高的精度。
提示策略
为了利用 ChatGPT 进行机器学习,我们需要清楚地了解如何实现针对代码生成的特定于机器学习的提示策略。
让我们头脑风暴一下我们希望在这次任务中实现的目标,以便更好地理解需要包含在提示中的内容。
策略 1:任务-行动-指南(TAG)提示策略
1.1 - 任务:具体任务是构建和优化 CIFAR-10 数据集的 CNN 模型。
1.2 - 行动:构建和优化 CIFAR-10 数据集的 CNN 模型的关键步骤包括:
-
预处理图像数据:归一化像素值并将图像调整到标准尺寸。
-
模型构建:定义具有单个卷积层的基线 CNN 模型架构。
1.3 - 指南:在我们的提示中,我们将向 ChatGPT 提供以下指南:
-
代码应与 Jupyter Notebook 兼容。
-
确保每行代码都有详细的注释。
-
您必须详细解释每一行代码,涵盖代码中使用的每个方法。
策略 2:角色-指令-上下文(PIC)提示策略
2.1 - 角色扮演:扮演一个需要逐步指导构建和优化用于图像分类任务的 CNN 模型的初学者角色。
2.2 - 指令:请求 ChatGPT 逐个生成每个功能的代码,并在进行下一步之前等待用户反馈。
2.3 - 上下文:鉴于重点是使用 CIFAR-10 数据集构建用于图像分类任务的 CNN 模型,ChatGPT 已经了解该数据集及其特征,因此可能不需要额外的上下文。
策略 3:学习-即兴创作-反馈-评估(LIFE)提示策略
3.1 - 学习:
- 强调了解 CNN 模型和其组件的重要性,包括卷积层、池化层、dropout 正则化、批量归一化、数据增强和优化算法。
3.2 - 改进:
- 我们将在后面通过添加更多层、dropout 层、池化、数据增强等来改进。
3.3 - 反馈:
-
分享代码和模型输出以供反馈,以确保有效的学习和理解。
-
将建议和批评纳入,以完善模型和方法**。
-
提供错误以排除故障和解决问题。
3.4- 评估:
执行 ChatGPT 提供的代码,以确保准确性和有效性。这一点在本章中会反复使用。
使用 ChatGPT 的免费版本构建 CNN 模型,以准确分类 CIFAR-10 图像
ChatGPT 的付费版本具有代码解释器,但它不支持构建 ML 代码所需的深度学习库,如 Keras 和 TensorFlow。因此,在本章中,我们将坚持使用 ChatGPT 的免费版本。
特征 1:使用单个卷积层构建基线 CNN 模型
让我们为基线模型制定初始提示。
[提示]
我想为 CIFAR-10 数据集创建一个简单的分类模型(TAG 1.1),该模型由单个层 CNN 模型(PIC 2.2)组成。它应包括以下步骤(TAG 1.2),并一次提供一个步骤,等待用户反馈(PIC 2.2):
-
预处理图像数据:归一化像素值并将图像调整到标准尺寸。
-
模型构建:定义具有单个卷积层的基线 CNN 模型架构。
我是一个初学者用户(PIC 2.1),因此请提供逐步的代码,并严格遵循以下说明(TAG 1.3):
-
代码应与 Jupyter Notebook 兼容。
-
确保每行代码都有详细的注释。
您必须详细解释每一行代码,涵盖代码中使用的每个方法。
[提示结束]
您应该收到以下类似的响应,其中 ChatGPT 为我们提供了数据预处理步骤的代码,并附有详细的注释和解释。
[快速响应]
当然,让我们从第一步开始:预处理图像数据。在这一步中,我们将归一化像素值并将图像调整到标准尺寸。
# Step 1: Preprocessing the image data
# Import necessary libraries
import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.utils import to_categorical
from skimage.transform import resize
# Load CIFAR-10 dataset
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
# Normalize pixel values to range [0, 1]
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
# Resize images to a standardized size (e.g., 32x32 pixels)
standardized_size = (32, 32)
x_train_resized = np.array([resize(image, standardized_size) for image in x_train])
x_test_resized = np.array([resize(image, standardized_size) for image in x_test])
# Display shape of datasets after preprocessing
print("Shape of x_train:", x_train_resized.shape)
print("Shape of x_test:", x_test_resized.shape)
现在,让我们逐行分析代码:
我们导入必要的库:
-
使用
numpy进行数值运算。 -
使用
tensorflow构建 和训练模型。 -
使用来自
tensorflow.keras.datasets的cifar10数据集加载 CIFAR-10 数据。 -
使用来自
tensorflow.keras.utils的to_categorical函数进行标签的一热编码。 -
使用来自
skimage.transform的resize函数调整图像大小。 -
我们使用
cifar10.load_data()函数加载 CIFAR-10 数据集,该函数返回训练和测试数据分割。 -
我们通过除以
255.0将图像的像素值归一化到[0, 1]范围内。 -
我们定义图像调整大小的标准化大小(例如,32x32 像素)。
-
我们使用列表推导和 resize 函数将训练集和测试集中的所有图像调整到标准化大小。
-
最后,我们在预处理后打印训练集和测试集的形状,以验证变化。
如果您有任何问题或想要进行下一步,请告诉我!
[响应结束]
我们现在将应用评估提示策略(LIFE 3.4),并通过在 Google Colab 的每一步执行代码来验证代码。该代码从 Keras 加载模型并规范化图像。代码运行良好,无需任何修改或返回任何错误。
请参考Building a CNN Model for CIFAR-10 with ChatGPT.ipynb`笔记本中的可执行代码片段,或从提示响应中复制代码。

图 15.2:代码执行 – 数据预处理
我们现在将指示 ChatGPT 进行下一步。
[Prompt]
让我们进行下一步。
[End of prompt]
我们应该收到以下响应,其中 ChatGPT 应提供创建基线模型的代码。
[Prompt response]
太好了!下一步是构建具有单个卷积层的基线 CNN 模型架构。
# Step 2: Model Construction
# Import necessary libraries
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
# Define the baseline CNN model architecture
model = Sequential([
# Add a convolutional layer with 32 filters, kernel size of (3, 3), and ReLU activation function
Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
# Add a max pooling layer with pool size of (2, 2)
MaxPooling2D((2, 2)),
# Flatten the output of the convolutional layer
Flatten(),
# Add a fully connected layer with 64 neurons and ReLU activation function
Dense(64, activation='relu'),
# Add the output layer with 10 neurons (one for each class) and softmax activation function
Dense(10, activation='softmax')
])
# Compile the model
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Display model summary
model.summary()
现在,让我们解释每一行代码:
-
我们导入必要的库:
-
使用
tensorflow.keras.models中的Sequential创建一个序列模型。 -
从
tensorflow.keras.layers中获取Conv2D、MaxPooling2D、Flatten和Dense层,以定义 CNN 模型中的不同类型层。 -
我们使用序列 API 定义基线 CNN 模型架构,这允许我们创建线性堆叠的层。
在序列模型中:
-
我们添加一个卷积层(
Conv2D),包含 32 个过滤器,核大小为(3, 3),以及ReLU激活函数。input_shape参数定义了输入图像的形状。 -
我们添加一个最大池化层(
MaxPooling2D),池化大小为(2, 2),以对特征图进行下采样。 -
我们使用
Flatten层将卷积层的输出展平,以便为全连接层做准备。 -
我们添加一个全连接层(
Dense),包含 64 个神经元和一个ReLU激活函数。 -
最后,我们添加一个输出层,包含 10 个神经元(每个 CIFAR-10 类别一个)和
softmax激活函数。
我们使用model.compile()编译模型,以下为配置:
-
使用
optimizer='adam'来使用Adam优化器。 -
使用
loss='sparse_categorical_crossentropy'作为损失函数,适用于具有整数标签的分类任务。 -
使用
metrics=['accuracy']来监控训练过程中的准确率。 -
我们使用
model.summary()显示模型架构的摘要。
如果您有任何问题或想要进行调整,请告诉我!
[响应结束]
我们在笔记本中执行代码,它运行得非常完美。打印了一个模型摘要,其中包括 32 个滤波器和 896 个参数的Conv2d层,接着是一个MaxPooling2D层,一个没有参数的扁平层,然后是一个有 64 个神经元的密集层和 ReLU 激活函数,最后是一个有 10 个神经元和 softmax 激活函数的密集层。

图 15.3:代码执行 – 基线模型摘要
我们现在将指示 ChatGPT 继续下一步。
[Prompt]
让我们继续下一步。
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 应提供训练模型的代码。
[Prompt response]
当然!下一步是在预处理过的 CIFAR-10 数据集上训练模型。
# Step 3: Model Training
# Train the model
history = model.fit(x_train_resized, y_train,
epochs=10,
batch_size=32,
validation_data=(x_test_resized, y_test))
下面是代码的解释:
-
我们使用
fit方法在预处理过的训练数据(x_train_resized)和相应的标签(y_train)上训练模型。 -
我们指定训练轮数(
epochs=10)以在整个数据集上迭代 10 次来训练模型。 -
我们将批大小(
batch_size=32)设置为指定每次梯度更新时的样本数量。这是一个超参数,它决定了在更新模型参数之前处理多少个样本。 -
我们提供验证数据(
validation_data=(x_test_resized, y_test))以在每个 epoch 后评估模型在测试数据上的性能。 -
fit方法返回一个包含每个 epoch 的训练指标(例如,损失和准确率)的历史对象,这些指标可用于分析和可视化。
如果您有任何问题或想要继续评估模型或进行任何调整,请告诉我!
[End of response]
我们执行代码,它运行无误。模型训练了 10 个 epoch,之后我们收到 62.6%的验证准确率,对于一个基本模型来说还不错。

图 15.4:代码执行 – 模型训练
我们现在将指示 ChatGPT 继续下一步以评估模型性能。
[Prompt]
让我们继续下一步。
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 应提供模型评估的代码以及注释。
[Prompt response]
当然!下一步是对测试数据集上的训练模型进行评估并可视化其性能。
# Step 4: Model Evaluation and Visualization
import matplotlib.pyplot as plt
# Evaluate the model on the testing dataset
test_loss, test_accuracy = model.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss:", test_loss)
print("Test Accuracy:", test_accuracy)
# Plot training history
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
这段代码的作用如下:
-
我们导入
matplotlib.pyplot来可视化训练历史。 -
我们使用
evaluate方法在测试数据集上评估训练模型,该方法返回测试损失和准确率。 -
我们打印测试损失和测试准确率以评估模型在未见数据上的性能。
-
我们使用
matplotlib.pyplot绘制训练和验证准确率随 epoch 的变化。fit方法返回的历史对象包含训练和验证数据集在各个 epoch 的准确率值。 -
history.history['accuracy']包含训练准确率值。 -
history.history['val_accuracy']包含验证准确率值。 -
该图表帮助我们可视化模型随时间学习的效果,以及它是否过拟合或欠拟合。
如果您有任何问题或想要进一步分析结果,请告诉我!
[响应结束]
我们执行代码,它运行没有任何错误。它评估了测试损失和测试准确率(62.5%),并绘制了训练准确率与验证准确率的图表,显示模型在验证集上的表现不佳。

图 15.5:代码执行 – 模型评估
这完成了功能 1,我们使用基线 CNN 模型实现了 0.6259(62.59%)的准确率。(请注意,即使运行了完全相同的代码,您也可能收到不同的准确率分数。)接下来,我们将转向第二个功能,即在模型中添加更多层,并了解 ChatGPT 如何帮助我们。
功能 2:实验添加卷积层
现在我们有了基线模型架构,让我们尝试向其中添加层。我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 提供相应的代码,同时注意使用短语(在基线模型之上)(PIC 2.2)和关键词增量(PIC 2.2)的重要性;没有这些,响应将不会完全符合我们的期望。
[提示]
我们在基线模型上获得了 62.59 的准确率,因此现在我想先尝试在基线模型上添加两层连续的 64 个滤波器,然后在其之上再添加两层连续的 128 个滤波器。然后,让我们比较这两个增量变化与基线模型的成果。
[提示结束]
我们应该收到以下响应,其中 ChatGPT 理解请求并为我们提供具有连续 64 个滤波器层的修改模型代码,以及另一个具有连续 128 个滤波器层的模型代码。
[响应提示]
当然!让我们首先在基线模型上添加两层连续的 64 个滤波器,并重新训练它。然后,我们将添加两层连续的 128 个滤波器并再次训练模型。最后,我们将比较这两个变化与基线模型的成果。
这是我们如何修改基线模型以包含额外层的方法:
# Define the modified model with two consecutive layers of 64 filters
modified_model_64 = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation='relu'),
Dense(10, activation='softmax')
])
# Compile the modified model
modified_model_64.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Display model summary
modified_model_64.summary()
# Train the modified model with two consecutive layers of 64 filters
history_64 = modified_model_64.fit(x_train_resized, y_train,
epochs=10,
batch_size=32,
validation_data=(x_test_resized, y_test))
# Evaluate the modified model on the testing dataset
test_loss_64, test_accuracy_64 = modified_model_64.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with 64 Filters):", test_loss_64)
print("Test Accuracy (Modified Model with 64 Filters):", test_accuracy_64)
现在,让我们解释一下这些修改:
-
我们通过在基线模型架构之后添加两个连续的 64 个滤波器卷积层来定义修改后的模型(
modified_model_64)。 -
我们使用与基线模型相同的设置编译了修改后的模型。
-
我们在预处理后的训练数据(
x_train_resized)上训练了修改后的模型(modified_model_64)。 -
我们在测试数据集(
x_test_resized)上评估修改后模型的性能,并打印测试损失和准确率。
接下来,我们将重复添加两层连续 128 个滤波器的过程:
# Define the modified model with two consecutive layers of 128 filters
modified_model_128 = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Conv2D(128, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Conv2D(128, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation='relu'),
Dense(10, activation='softmax')
])
# Compile the modified model
modified_model_128.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Display model summary
modified_model_128.summary()
# Train the modified model with two consecutive layers of 128 filters
history_128 = modified_model_128.fit(x_train_resized, y_train,
epochs=10,
batch_size=32,
validation_data=(x_test_resized, y_test))
# Evaluate the modified model on the testing dataset
test_loss_128, test_accuracy_128 = modified_model_128.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with 128 Filters):", test_loss_128)
print("Test Accuracy (Modified Model with 128 Filters):", test_accuracy_128)
此代码遵循与之前修改相同的结构,但添加了两个连续的具有 128 个滤波器的卷积层。我们相应地训练和评估模型。
如果你有任何问题或想要继续修改,请告诉我!
[响应结束]
我们首先执行定义两个具有 64 个滤波器的额外层的代码。它运行得很好,训练了模型并实现了 71%的改进准确率。

图 15.6:向模型添加两个具有 64 个滤波器的连续层
接下来,我们执行代码,通过添加两个具有 128 个滤波器的额外层来增加模型的深度。在这种情况下,代码导致错误,因此我们将使用提示策略,并将错误反馈给 ChatGPT。
你应该在运行代码时看到一个ValueError。
[提示]**
我们收到了以下错误。同时,确保最大池化不会过多地减少 128 个滤波器的维度。错误如下:
ValueError: Exception encountered when calling layer "conv2d_38" (type Conv2D).
Negative dimension size caused by subtracting 3 from 2 for '{{node conv2d_38/Conv2D}} = Conv2D[T=DT_FLOAT, data_format="NHWC", dilations=[1, 1, 1, 1], explicit_paddings=[], padding="VALID", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](Placeholder, conv2d_38/Conv2D/ReadVariableOp)' with input shapes: [?,2,2,64], [3,3,64,128].
Call arguments received by layer "conv2d_38" (type Conv2D):
inputs=tf.Tensor(shape=(None, 2, 2, 64), dtype=float32).
[提示结束]**
我们应该收到以下响应,其中 ChatGPT 理解请求并为我们提供解决该问题的修改后的代码。
[提示响应]**
看起来我们遇到了一个问题,即最后一个最大池化层之后的维度对于具有 128 个滤波器的后续卷积层来说太小。为了解决这个问题,我们将调整架构以确保层之间的兼容性。
这里是修正后的代码:
# Define the modified model with two consecutive layers of 128 filters added to the 64-layer model
modified_model_64_to_128 = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
Conv2D(128, (3, 3), activation='relu', padding='same'), # Add padding
Conv2D(128, (3, 3), activation='relu', padding='same'), # Add padding
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation='relu'),
Dense(10, activation='softmax')
])
# Compile the modified model
modified_model_64_to_128.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Display model summary
modified_model_64_to_128.summary()
# Train the modified model with two consecutive layers of 128 filters added to the 64-layer model
history_64_to_128 = modified_model_64_to_128.fit(x_train_resized, y_train,
epochs=10,
batch_size=32,
validation_data=(x_test_resized, y_test))
# Evaluate the modified model on the testing dataset
test_loss_64_to_128, test_accuracy_64_to_128 = modified_model_64_to_128.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with 64 to 128 Filters):", test_loss_64_to_128)
print("Test Accuracy (Modified Model with 64 to 128 Filters):", test_accuracy_64_to_128)
在这次修改中:
-
我们在最后一个具有 64 个滤波器的卷积层之后移除了最大池化层,以确保在应用具有 128 个滤波器的后续卷积层之前,特征图维度不会减少太多。
-
我们在具有 128 个滤波器的卷积层中添加了
same填充,以确保与输入维度兼容。 -
剩余的架构保持不变。
这个调整应该会解决这个问题,模型应该能够正确编译和训练。如果你有任何问题或遇到任何进一步的问题,请告诉我!
[响应结束]
让我们执行代码,该代码删除了一些最大池化层,并且添加了padding = 'same'以确保维度兼容。它运行成功,训练了模型但将准确率降低到 69%。

图 15.7:添加了具有 128 个滤波器的层
根据这些结果,我们可以观察到,与基线模型相比,增加更多层实现了略微更高的准确率。然而,准确率的差异相对较小,表明增加层并没有显著提高模型性能,并且模型仍未进行正则化。让我们继续到下一个特征,并尝试 dropout 正则化。
特征 3:引入 dropout 正则化
现在我们已经学会了如何添加更多层,我们将现在尝试使用 dropout 层。我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 提供相应的代码。
还请注意,我们已经通过要求在之前提供的代码上添加 dropout 来应用了使用特定指令(PIC 2.2)的提示策略。
[提示]
基线模型的模型准确率为 62.5%,增加 64 个滤波器的额外层后为 71%,进一步添加具有 128 个滤波器的层将其降低到 69%。请在我们之前消息中给出的模型代码上应用 dropout 正则化吗?
[提示结束]
我们应该收到以下类似的响应,其中 ChatGPT 理解请求并提供添加 dropout 层的代码。
[快速响应]
当然,让我们向修改后的模型添加两个连续的 128 个滤波器的 dropout 正则化。dropout 正则化通过在训练过程中随机丢弃一部分单元(神经元)来帮助防止过拟合。以下是我们可以如何修改模型以包含 dropout 层:
from tensorflow.keras.layers import Dropout
# Define the modified model with dropout regularization
modified_model_64_to_128_with_dropout = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
Conv2D(128, (3, 3), activation='relu', padding='same'),
Dropout(0.25), # Add dropout regularization with a dropout rate of 25%
Conv2D(128, (3, 3), activation='relu', padding='same'),
Dropout(0.25), # Add dropout regularization with a dropout rate of 25%
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation='relu'),
Dropout(0.5), # Add dropout regularization with a dropout rate of 50%
Dense(10, activation='softmax')
])
# Compile the modified model with dropout regularization
modified_model_64_to_128_with_dropout.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Display model summary
modified_model_64_to_128_with_dropout.summary()
# Train the modified model with dropout regularization
history_64_to_128_with_dropout = modified_model_64_to_128_with_dropout.fit(x_train_resized, y_train,
epochs=10,
batch_size=32,
validation_data=(x_test_resized, y_test))
# Evaluate the modified model with dropout regularization on the testing dataset
test_loss_64_to_128_with_dropout, test_accuracy_64_to_128_with_dropout = modified_model_64_to_128_with_dropout.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with Dropout):", test_loss_64_to_128_with_dropout)
print("Test Accuracy (Modified Model with Dropout):", test_accuracy_64_to_128_with_dropout)
在这次修改中:
-
我们在具有 128 个滤波器的卷积层之后添加 dropout 层。我们将第一个和最后一个 dropout 层的 dropout 率分别设置为 25%和 50%。这些值可以根据实验和所需的正则化水平进行调整。
-
我们使用与之前相同的设置编译和训练带有 dropout 正则化的修改后的模型。
-
我们在测试数据集上评估带有 dropout 正则化的修改后模型的性能,并打印测试损失和准确率。
添加 dropout 正则化可能有助于提高模型的泛化性能并减少过拟合。如果您有任何问题或想要进一步探索,请告诉我!
[响应结束]
我们执行代码,该代码定义了一个包含 dropout 层的模型。它还训练并评估了模型的性能,并打印了包含损失和准确率的模型摘要。请注意,dropout 层不会向模型添加任何参数。

图 15.8:代码执行 – dropout 层
我们可以看到准确率之间没有太大差异,因此我们将应用 Learn 提示策略(LIFE 3.1),并要求 ChatGPT 提供损失和准确率的图表,以查看正则化的影响。
[提示]
Test Loss (Modified Model with Dropout): 0.876133382320404
Test Accuracy (Modified Model with Dropout): 0.7014999985694885
请您提供损失和准确性的图表,以便查看是否进行了正则化,并解释一下。
[提示结束]
我们应该收到以下类似的响应,其中 ChatGPT 提供代码并解释图表的重要性。
[快速响应]
当然!让我们绘制修改后的模型使用 dropout 正则化的训练和验证损失及准确率曲线。这将帮助我们可视化 dropout 正则化对训练过程和模型性能的影响。
import matplotlib.pyplot as plt
# Plot training history for loss
plt.plot(history_64_to_128_with_dropout.history['loss'], label='Training Loss')
plt.plot(history_64_to_128_with_dropout.history['val_loss'], label='Validation Loss')
plt.title('Training and Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
# Plot training history for accuracy
plt.plot(history_64_to_128_with_dropout.history['accuracy'], label='Training Accuracy')
plt.plot(history_64_to_128_with_dropout.history['val_accuracy'], label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
解释:
-
训练损失与验证损失对比:此图显示了训练和验证损失随 epoch 的变化趋势。如果训练和验证损失之间存在较大差距,则表明过拟合。dropout 正则化的目的是通过防止模型过度依赖特定特征来减少训练和验证损失之间的差距,从而减少过拟合。
-
训练准确率与验证准确率对比:此图显示了训练和验证准确率随 epoch 的变化趋势。dropout 正则化也可以帮助提高泛化性能,从而提高验证准确率,尤其是在模型中存在过拟合的情况下。
通过观察这些图表,我们可以评估 dropout 正则化是否有效地减轻了过拟合并提高了模型泛化性能。如果您有任何问题或需要进一步解释,请告诉我!
[回复结束]
我们将执行代码并绘制图表,以了解 dropout 的影响。

图 15.9:dropout 层的影响
输出的图表显示了在 CIFAR-10 数据集上训练并带有 dropout 层的模型训练和验证损失及准确率。随着准确率的提高,损失在各个 epoch 中下降,这表明学习是有效的。训练和验证指标之间的小差距表明过拟合最小,这可能是由于 dropout 层的作用。总体而言,模型对新数据具有良好的泛化能力,表现出良好的性能。
功能 4:实现批量归一化
我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 为我们提供将批量归一化添加到之前提供的代码中的代码。
[提示]
我想将批量归一化应用于之前提供的代码。
[提示结束]
我们应该收到以下类似的回复,其中 ChatGPT 理解请求并为我们提供在现有模型上添加批量归一化的代码。
[提示回复]
当然!我们可以将批量归一化应用于模型的卷积层,以提高训练稳定性和收敛速度。以下是修改模型以包含批量归一化层的步骤:
from tensorflow.keras.layers import BatchNormalization
# Define the modified model with batch normalization
modified_model_64_to_128_with_batchnorm = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
BatchNormalization(), # Add batch normalization
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
BatchNormalization(), # Add batch normalization
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
Conv2D(128, (3, 3), activation='relu', padding='same'),
BatchNormalization(), # Add batch normalization
MaxPooling2D((2, 2)),
Conv2D(128, (3, 3), activation='relu', padding='same'),
BatchNormalization(), # Add batch normalization
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation='relu'),
BatchNormalization(), # Add batch normalization
Dense(10, activation='softmax')
])
# Compile the modified model with batch normalization
modified_model_64_to_128_with_batchnorm.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Display model summary
modified_model_64_to_128_with_batchnorm.summary()
# Train the modified model with batch normalization
history_64_to_128_with_batchnorm = modified_model_64_to_128_with_batchnorm.fit(x_train_resized, y_train,
epochs=10,
batch_size=32,
validation_data=(x_test_resized, y_test))
# Evaluate the modified model with batch normalization on the testing dataset
test_loss_64_to_128_with_batchnorm, test_accuracy_64_to_128_with_batchnorm = modified_model_64_to_128_with_batchnorm.evaluate(x_test_resized, y_test, verbose=0)
print("Test Loss (Modified Model with Batch Normalization):", test_loss_64_to_128_with_batchnorm)
print("Test Accuracy (Modified Model with Batch Normalization):", test_accuracy_64_to_128_with_batchnorm)
在这次修改中:
-
我们在每个卷积层之后添加
BatchNormalization层。批量归一化在每个批次中对前一层的激活进行归一化,使训练更加稳定并加速收敛。 -
我们使用与之前相同的设置编译和训练带有批量归一化的修改后模型。
-
我们使用测试数据集评估带有批量归一化的修改后模型的性能,并打印测试损失和准确率。
添加批量归一化可以在训练过程中提高模型性能和稳定性。如果您有任何问题或需要进一步解释,请告诉我!
[回复结束]
我们将执行代码,评估模型性能,并打印结果。注意,批量归一化添加了少量参数,与 dropout 或 maxpooling 不同。

图 15.10:批量归一化
我们可以看到有一些非训练参数。让我们利用 Learn 提示策略(LIFE 3.1)来理解非训练参数的计算。
[提示]**
请解释模型摘要中 832 的计算方法?
"sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_5 (Conv2D) (None, 30, 30, 32) 896
batch_normalization (Batch (None, 30, 30, 32) 128
Normalization)
max_pooling2d_3 (MaxPoolin (None, 15, 15, 32) 0
g2D)
conv2d_6 (Conv2D) (None, 13, 13, 64) 18496
batch_normalization_1 (Bat (None, 13, 13, 64) 256
chNormalization)
max_pooling2d_4 (MaxPoolin (None, 6, 6, 64) 0
g2D)
conv2d_7 (Conv2D) (None, 4, 4, 64) 36928
conv2d_8 (Conv2D) (None, 4, 4, 128) 73856
batch_normalization_2 (Bat (None, 4, 4, 128) 512
chNormalization)
max_pooling2d_5 (MaxPoolin (None, 2, 2, 128) 0
g2D)
conv2d_9 (Conv2D) (None, 2, 2, 128) 147584
batch_normalization_3 (Bat (None, 2, 2, 128) 512
chNormalization)
max_pooling2d_6 (MaxPoolin (None, 1, 1, 128) 0
g2D)
flatten_1 (Flatten) (None, 128) 0
dense_2 (Dense) (None, 64) 8256
batch_normalization_4 (Bat (None, 64) 256
chNormalization)
dense_3 (Dense) (None, 10) 650
=================================================================
Total params: 288330 (1.10 MB)
Trainable params: 287498 (1.10 MB)
Non-trainable params: 832 (3.25 KB)
[提示结束]**
我们应该收到如下响应,其中 ChatGPT 理解了请求,并提供了非训练参数计算的解释。
[提示响应]**
在您描述的模型中,非训练参数是 832,这具体来自批量归一化层。以下是这个数字的计算方法:
批量归一化的组成部分
每个批量归一化层引入了用于归一化前一层输出的参数。具体来说,这些参数是:
-
Gamma(缩放因子)
-
Beta(平移因子)
-
均值移动平均值
-
方差移动平均值
gamma 和 beta 参数是可训练的,而移动均值和方差是不可训练的。这些组(gamma/beta 和移动均值/方差)的元素数量与批量归一化层的输入通道数相同。
计算
根据您的模型配置:
在第一个 Conv2D 层(32 通道)之后的批量归一化:
-
移动均值:32
-
移动方差:32
在第二个 Conv2D 层(64 通道)之后的批量归一化:
-
移动均值:64
-
移动方差:64
在第三个 Conv2D 层(128 通道)之后的批量归一化:
-
移动均值:128
-
移动方差:128
在第四个 Conv2D 层(128 通道)之后的批量归一化:
-
移动均值:128
-
移动方差:128
在 Dense 层(64 个单元)之后的批量归一化:
-
移动均值:64
-
移动方差:64
总非训练参数
求和这些值得到非训练参数的总数:[ (32 + 32) + (64 + 64) + (128 + 128) + (128 + 128) + (64 + 64) = 832 ]
每个批量归一化层将其输入通道数的两倍贡献给非训练参数计数(一次用于移动均值,一次用于移动方差)。这导致您的模型总共有 832 个非训练参数。
[响应结束]
现在我们已经学会了如何添加批量归一化以及非训练参数的计算,让我们转向下一个特性并尝试不同的优化器。
特征 5:使用不同的优化器进行优化
我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 为我们提供用于实验不同优化器(如 SGD、Adam 和 RMSProp)的代码。
[提示]**
请您提供 SGD、Adam 和 RMSprop 优化器的代码,并比较之前提供的模型及其在不同优化器组合下的性能。
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 理解了请求,并为我们提供了不同优化器的代码以及比较结果。
[提示响应]**
当然!我们可以比较使用 SGD、Adam 和 RMSprop 等不同优化器的模型性能。以下是修改模型并使用不同优化器训练的方法:
# Import optimizers
from tensorflow.keras.optimizers import SGD, Adam, RMSprop
# Define the optimizers
optimizers = [SGD(), Adam(), RMSprop()]
# Define lists to store test accuracies for each optimizer
test_accuracies = []
# Iterate over each optimizer
for optimizer in optimizers:
# Define the modified model with batch normalization and the current optimizer
modified_model = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
BatchNormalization(),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
BatchNormalization(),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation='relu'),
Conv2D(128, (3, 3), activation='relu', padding='same'),
BatchNormalization(),
MaxPooling2D((2, 2)),
Conv2D(128, (3, 3), activation='relu', padding='same'),
BatchNormalization(),
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation='relu'),
BatchNormalization(),
Dense(10, activation='softmax')
])
# Compile the model with the current optimizer
modified_model.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Train the model with the current optimizer
history = modified_model.fit(x_train_resized, y_train,
epochs=10,
batch_size=32,
validation_data=(x_test_resized, y_test),
verbose=0)
# Evaluate the model with the current optimizer on the testing dataset
test_loss, test_accuracy = modified_model.evaluate(x_test_resized, y_test, verbose=0)
# Append the test accuracy to the list
test_accuracies.append(test_accuracy)
# Print the test accuracy for the current optimizer
print(f"Test Accuracy (Optimizer: {optimizer.__class__.__name__}): {test_accuracy}")
# Plot the test accuracies for each optimizer
plt.bar(['SGD', 'Adam', 'RMSprop'], test_accuracies)
plt.title('Test Accuracy Comparison for Different Optimizers')
plt.xlabel('Optimizer')
plt.ylabel('Test Accuracy')
plt.show()
在此代码中:
-
我们从
tensorflow.keras.optimizers中导入SGD、Adam和RMSprop优化器。 -
我们定义了一个优化器列表以进行迭代。
-
对于每个优化器,我们定义具有批量归一化和当前优化器的模型。
-
我们使用当前优化器编译和训练模型。
-
我们使用当前优化器在测试数据集上评估模型的性能。
-
我们打印每个优化器的测试准确率并将测试准确率存储在列表中。
-
最后,我们绘制每个优化器的测试准确率以比较它们的性能。
这段代码使我们能够比较使用不同优化器训练的模型的性能。如果您有任何问题或需要进一步解释,请告诉我!
[响应结束]
我们执行代码,该代码定义了具有不同优化器的模型。它还训练模型,评估其性能,并打印出它们性能比较的结果。

图 15.11:代码执行 – 不同优化器
根据这些结果,我们可以观察到 RMSprop 优化器实现了最高的准确率,其次是 Adam 和 SGD。这表明 RMSprop 在优化模型参数和改进测试数据上的性能方面表现更好。
让我们继续到最后一个特性,并应用 DavidNet 架构。
特性 6:应用 DavidNet 架构
我们将应用提示策略来改进模型(LIFE 3.4),并要求 ChatGPT 提供 DavidNet 的代码,并使用学习提示策略(LIFE 3.1)了解它。
[提示]**
请告诉我 DavidNet 架构有什么特别之处?
[提示结束]**
我们应该收到以下类似的响应,其中 ChatGPT 将提供 DavidNet 的解释。
[提示响应]**
DavidNet 架构因其简单性和在图像分类任务上实现高准确率而著称,尤其是在 CIFAR-10 数据集上。以下是使 DavidNet 特殊的一些关键方面:
-
残差块:DavidNet 利用残差块,这使得训练非常深的神经网络变得更加容易。残差连接使网络能够学习残差映射,这使得在没有梯度消失或梯度爆炸的情况下训练更深的网络变得更容易。
-
批量归一化: 在每个卷积层之后应用批量归一化,这有助于通过归一化每一层的激活来稳定和加速训练过程。这提高了模型在训练过程中的收敛性,并有助于防止过拟合。
-
跳过连接: 跳过连接(也称为恒等快捷方式)在残差块中使用。这些连接允许梯度直接通过网络流动,减轻梯度消失问题并促进非常深网络的训练。
-
最大池化: 最大池化层用于减少特征图的空間维度,有助于提取重要特征同时降低计算复杂度。
-
密集层: 在网络的末端使用全连接(密集)层来根据卷积层学习到的特征进行分类。
-
学习率调度: DavidNet 使用带有预热周期和衰减因子的学习率调度,在训练过程中逐步调整学习率。这有助于稳定训练并提高收敛性。
-
正则化: 虽然在架构中没有明确提及,但可以应用如权重衰减等正则化技术来防止过拟合并提高泛化性能。
总体而言,DavidNet 在模型复杂性和有效性之间取得了平衡,证明了即使相对简单的架构,通过适当的设计选择和训练策略也能取得令人印象深刻的成果。
[响应结束]
我们将尝试了解残差块并向 ChatGPT 提出后续问题。
[P提示]
残差块是什么?
[End of prompt]
我们应该收到如下响应,其中 ChatGPT 为我们提供关于残差块的解释。
[P提示响应]
残差块是深度神经网络的基本组成部分,尤其是在 ResNet 及其变体等架构中。它们旨在解决在非常深网络的训练过程中出现的梯度消失问题。
残差块由两个主要路径组成:恒等路径和快捷路径。恒等路径是块中的主要路径,而快捷路径为输入提供了一个直接连接,以便跳过一层或多层。
这里是残差块工作原理的基本概述:
-
恒等路径: 输入通过一系列卷积层、批量归一化和激活函数传递到残差块。这一系列操作允许网络学习输入数据的表示。
-
快捷路径: 残差块(恒等)的原始输入通过一个单独的路径传递,绕过了一些卷积层。这条路径被称为“快捷”或“跳过”连接。
-
合并操作:恒等路径和快捷路径的输出通过逐元素加法进行合并。这种合并操作将原始输入添加到卷积层的输出中。
-
激活函数:最后,合并后的输出经过一个激活函数,通常是ReLU(修正线性单元)激活,以引入非线性。
基于残差块的关键思想是,快捷连接允许在反向传播过程中梯度更容易地通过网络流动。如果恒等路径(卷积层)学会近似恒等函数,残差块就可以通过一个小残差映射来调整输出,因此得名“残差块”。
通过启用通过快捷连接直接传播输入信号,残差块减轻了梯度消失问题,并促进了非常深层的神经网络训练。这种架构在各种任务中取得了高度成功,导致了更深、更精确的模型的发展。
[End of response]
我们现在将要求 ChatGPT 提供 DavidNet 的代码,并且我们将向 ChatGPT 提供从互联网搜索中获得的信息,以使用 CIFAR-10 实现更好的准确度。
[Prompt]
你能给我 DavidNet 的代码吗?它提供了更好的准确度?DavidNet 使用的超参数包括:
-
批量大小:512
-
动量:0.9
-
权重衰减:0.000125
-
学习率:0.4
-
训练轮数:24
-
预热轮数:5
[End of prompt]
我们应该收到一个 ChatGPT 理解请求并提供 DavidNet 代码的响应。
[Prompt response]
当然!以下是根据指定超参数实现的 DavidNet:
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, BatchNormalization, Activation, Add
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.callbacks import LearningRateScheduler
import numpy as np
def davidnet():
input_layer = Input(shape=(32, 32, 3))
# Initial convolutional layer
x = Conv2D(64, kernel_size=3, padding='same')(input_layer)
x = BatchNormalization()(x)
x = Activation('relu')(x)
# Stack of residual blocks
num_residual_blocks = 4
for _ in range(num_residual_blocks):
y = x
x = Conv2D(64, kernel_size=3, padding='same')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(64, kernel_size=3, padding='same')(x)
x = BatchNormalization()(x)
x = Add()([x, y]) # Skip connection
x = Activation('relu')(x)
# Max pooling layer
x = MaxPooling2D(pool_size=(2, 2))(x)
# Flatten layer
x = Flatten()(x)
# Dense layers
x = Dense(512)(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Dense(10, activation='softmax')(x)
# Create model
model = Model(inputs=input_layer, outputs=x)
return model
# Learning rate scheduler with warmup
def learning_rate_scheduler(epoch):
if epoch < 5:
return 0.4 * (epoch + 1) / 5
else:
return 0.4 * np.exp(0.1 * (5 - epoch))
# Create DavidNet model
model_davidnet = davidnet()
# Compile the model
optimizer = SGD(momentum=0.9, decay=0.000125)
model_davidnet.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Print model summary
model_davidnet.summary()
# Load CIFAR-10 dataset and preprocess if necessary
# Assuming x_train, y_train, x_test, y_test are loaded and preprocessed
# Define batch size and number of epochs
batch_size = 512
num_epochs = 24
# Train the model with learning rate scheduler
history_davidnet = model_davidnet.fit(x_train, y_train,
batch_size=batch_size,
epochs=num_epochs,
validation_data=(x_test, y_test),
callbacks=[LearningRateScheduler(learning_rate_scheduler)],
verbose=1)
在这段代码中:
-
我们定义了 DavidNet 架构,它由卷积层、残差块、最大池化和密集层组成。
-
我们定义了一个学习率调度函数来调整训练过程中的学习率,实现了既定的预热和指数衰减。
-
我们使用指定超参数的随机梯度下降(SGD)优化器编译模型:
momentum=0.9和decay=0.000125。 -
我们使用 CIFAR-10 数据集训练模型,指定了批量大小、训练轮数和学习率调度器。
这个实现遵循了 DavidNet 描述的架构和超参数。如果你有任何问题或需要进一步的帮助,请告诉我!
[End of response]
我们执行了 ChatGPT 提供的代码,但遇到了错误,因为 ChatGPT 是在较老的机器学习代码版本上训练的,因此提供了过时的衰减方法。
你应该在运行代码时看到一个ValueError。
我们将提供错误信息给 ChatGPT,看看 ChatGPT 提供的修改后的代码是否可行。
[Prompt]
ValueError: decay is deprecated in the new Keras optimizer. Please check the docstring for valid arguments, or use the legacy optimizer, e.g., tf.keras.optimizers.legacy.SGD.
[End of prompt]
我们应该收到以下类似的响应,其中 ChatGPT 理解了请求,并提供了修改后的代码,解决了衰减问题。
[提示响应]
感谢您的更新。看起来新的 Keras 优化器中已经弃用了衰减参数。我们可以使用不同的方法来实现权重衰减,例如使用自定义回调。让我们相应地修改代码:
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, BatchNormalization, Activation, Add
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.callbacks import LearningRateScheduler, Callback
import numpy as np
class SGDLearningRateDecay(Callback):
def __init__(self, initial_lr, warmup_epochs, decay_factor):
super(SGDLearningRateDecay, self).__init__()
self.initial_lr = initial_lr
self.warmup_epochs = warmup_epochs
self.decay_factor = decay_factor
def on_epoch_begin(self, epoch, logs=None):
if epoch < self.warmup_epochs:
lr = (self.initial_lr * (epoch + 1)) / self.warmup_epochs
else:
lr = self.initial_lr * self.decay_factor**(epoch - self.warmup_epochs)
self.model.optimizer.lr = lr
print(f'Learning rate for epoch {epoch + 1} is {lr}')
def davidnet():
input_layer = Input(shape=(32, 32, 3))
# Initial convolutional layer
x = Conv2D(64, kernel_size=3, padding='same')(input_layer)
x = BatchNormalization()(x)
x = Activation('relu')(x)
# Stack of residual blocks
num_residual_blocks = 4
for _ in range(num_residual_blocks):
y = x
x = Conv2D(64, kernel_size=3, padding='same')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(64, kernel_size=3, padding='same')(x)
x = BatchNormalization()(x)
x = Add()([x, y]) # Skip connection
x = Activation('relu')(x)
# Max pooling layer
x = MaxPooling2D(pool_size=(2, 2))(x)
# Flatten layer
x = Flatten()(x)
# Dense layers
x = Dense(512)(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Dense(10, activation='softmax')(x)
# Create model
model = Model(inputs=input_layer, outputs=x)
return model
# Create DavidNet model
model_davidnet = davidnet()
# Define hyperparameters
batch_size = 512
initial_lr = 0.4
num_epochs = 24
warmup_epochs = 5
decay_factor = 0.4
# Compile the model
optimizer = SGD(momentum=0.9)
model_davidnet.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# Print model summary
model_davidnet.summary()
# Define learning rate scheduler callback
lr_decay_callback = SGDLearningRateDecay(initial_lr, warmup_epochs, decay_factor)
# Load CIFAR-10 dataset and preprocess if necessary
# Assuming x_train, y_train, x_test, y_test are loaded and preprocessed
# Train the model with learning rate scheduler
history_davidnet = model_davidnet.fit(x_train, y_train,
batch_size=batch_size,
epochs=num_epochs,
validation_data=(x_test, y_test),
callbacks=[lr_decay_callback],
verbose=1)
在这个修改后的代码中:
-
我们定义了一个自定义回调
SGDLearningRateDecay来实现学习率衰减策略。此回调根据当前 epoch 和指定的预热 epoch 以及衰减因子调整学习率。 -
我们从优化器初始化中移除了衰减参数,并将其设置为默认值 0。
-
我们在模型训练期间使用自定义的学习率衰减回调。
这种方法允许我们实现权重衰减策略,而无需在优化器中使用衰减参数。如果您有任何问题或需要进一步的帮助,请告诉我!
[响应结束]
我们执行了 ChatGPT 提供的代码,它运行得非常完美。它提供了一个模型摘要,准确率已提高到 77%。
Model Summary:
Model: "model_1"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_2 (InputLayer) [(None, 32, 32, 3)] 0 []
conv2d_20 (Conv2D) (None, 32, 32, 64) 1792 ['input_2[0][0]']
batch_normalization_20 (Ba (None, 32, 32, 64) 256 ['conv2d_20[0][0]']
tchNormalization)
activation_10 (Activation) (None, 32, 32, 64) 0 ['batch_normalization_20[0][0]
']
conv2d_21 (Conv2D) (None, 32, 32, 64) 36928 ['activation_10[0][0]']
batch_normalization_21 (Ba (None, 32, 32, 64) 256 ['conv2d_21[0][0]']
tchNormalization)
activation_11 (Activation) (None, 32, 32, 64) 0 ['batch_normalization_21[0][0]
']
conv2d_22 (Conv2D) (None, 32, 32, 64) 36928 ['activation_11[0][0]']
batch_normalization_22 (Ba (None, 32, 32, 64) 256 ['conv2d_22[0][0]']
tchNormalization)
add_4 (Add) (None, 32, 32, 64) 0 ['batch_normalization_22[0][0]
',
'activation_10[0][0]']
activation_12 (Activation) (None, 32, 32, 64) 0 ['add_4[0][0]']
conv2d_23 (Conv2D) (None, 32, 32, 64) 36928 ['activation_12[0][0]']
batch_normalization_23 (Ba (None, 32, 32, 64) 256 ['conv2d_23[0][0]']
tchNormalization)
activation_13 (Activation) (None, 32, 32, 64) 0 ['batch_normalization_23[0][0]
']
conv2d_24 (Conv2D) (None, 32, 32, 64) 36928 ['activation_13[0][0]']
batch_normalization_24 (Ba (None, 32, 32, 64) 256 ['conv2d_24[0][0]']
tchNormalization)
add_5 (Add) (None, 32, 32, 64) 0 ['batch_normalization_24[0][0]
',
'activation_12[0][0]']
activation_14 (Activation) (None, 32, 32, 64) 0 ['add_5[0][0]']
conv2d_25 (Conv2D) (None, 32, 32, 64) 36928 ['activation_14[0][0]']
batch_normalization_25 (Ba (None, 32, 32, 64) 256 ['conv2d_25[0][0]']
tchNormalization)
activation_15 (Activation) (None, 32, 32, 64) 0 ['batch_normalization_25[0][0]
']
conv2d_26 (Conv2D) (None, 32, 32, 64) 36928 ['activation_15[0][0]']
batch_normalization_26 (Ba (None, 32, 32, 64) 256 ['conv2d_26[0][0]']
tchNormalization)
add_6 (Add) (None, 32, 32, 64) 0 ['batch_normalization_26[0][0]
',
'activation_14[0][0]']
activation_16 (Activation) (None, 32, 32, 64) 0 ['add_6[0][0]']
conv2d_27 (Conv2D) (None, 32, 32, 64) 36928 ['activation_16[0][0]']
batch_normalization_27 (Ba (None, 32, 32, 64) 256 ['conv2d_27[0][0]']
tchNormalization)
activation_17 (Activation) (None, 32, 32, 64) 0 ['batch_normalization_27[0][0]
']
conv2d_28 (Conv2D) (None, 32, 32, 64) 36928 ['activation_17[0][0]']
batch_normalization_28 (Ba (None, 32, 32, 64) 256 ['conv2d_28[0][0]']
tchNormalization)
add_7 (Add) (None, 32, 32, 64) 0 ['batch_normalization_28[0][0]
',
'activation_16[0][0]']
activation_18 (Activation) (None, 32, 32, 64) 0 ['add_7[0][0]']
max_pooling2d_9 (MaxPoolin (None, 16, 16, 64) 0 ['activation_18[0][0]']
g2D)
flatten_3 (Flatten) (None, 16384) 0 ['max_pooling2d_9[0][0]']
dense_6 (Dense) (None, 512) 8389120
['flatten_3[0][0]']
batch_normalization_29 (Ba (None, 512) 2048 ['dense_6[0][0]']
tchNormalization)
activation_19 (Activation) (None, 512) 0 ['batch_normalization_29[0][0]
']
dense_7 (Dense) (None, 10) 5130 ['activation_19[0][0]']
==================================================================================================
Total params: 8695818 (33.17 MB)
Trainable params: 8693642 (33.16 MB)
Non-trainable params: 2176 (8.50 KB)

图 15.12:DavidNet – 提高准确性
作业
在添加 dropout 层时,增加特征 3 的 epoch 数量。
挑战
尝试提高模型性能,使其超过 80%。请随意使用任何架构。
摘要
在本章中,我们探讨了如何有效地使用像 ChatGPT 这样的 AI 助手来学习和实验卷积神经网络(CNN)模型。提供的策略提供了一个清晰的逐步方法,用于使用 CIFAR-10 数据集实验不同的 CNN 模型构建和训练技术。
每一步都伴随着详细的说明、代码生成和用户验证,确保了结构化的学习体验。我们首先构建了一个基线 CNN 模型,学习了必要的预处理步骤,包括归一化像素值和调整图像大小。它引导您生成适合初学者的代码,该代码与 Jupyter 笔记本兼容,确保即使是新进入该领域的人也能轻松掌握 CNN 构建的基本原理。
随着我们不断进步,我们的 AI 助手成为了学习过程中的一个重要部分,帮助我们深入探索更复杂的内容,例如添加层、实现 dropout 和批量归一化,以及尝试不同的优化算法。每一步都伴随着代码的逐步更新,我们定期暂停以审查反馈,确保学习节奏适当且能够满足您的需求。我们的旅程以实现 DavidNet 架构告终,应用了我们所学到的所有策略和技术。
在下一章中,我们将学习如何使用 ChatGPT 生成聚类和 PCA 的代码。
加入我们的 Discord 社区
加入我们的社区 Discord 空间,与作者和其他读者进行讨论:

第十六章:无监督学习:聚类和 PCA
简介
无监督学习模型在未标记数据中寻找模式。聚类是一种寻找对象组的技巧,使得组内的对象彼此相似,而不同组内的对象则不相似。主成分分析(PCA)是一种降低数据维度的技巧。我们将在这两种技术中讨论产品聚类,它使用文本产品描述将相似产品分组在一起。
在本章中,我们将:
-
讨论两种无监督学习技术:聚类和主成分分析。
-
使用 K-means 聚类算法。
将问题分解为特征
要将问题分解为特征,我们需要考虑:
-
数据准备:加载数据集并检查数据以了解其结构、缺失值和整体特征。预处理数据,可能涉及处理缺失值、数据类型转换和数据清洗。
-
特征工程:选择相关特征,从文本中提取特征,并推导新特征。
-
文本数据预处理:分词、去除标点符号和停用词。使用词频-逆文档频率(TF-IDF)技术将文本转换为数值格式。
-
应用聚类算法:创建 K-means 聚类模型,并使用如肘部方法和轮廓分数等适当技术确定最佳聚类数量。
-
评估和可视化聚类结果:评估聚类性能并使用 PCA 在降维空间中可视化结果。
我们将使用第二章中描述的 TAG 提示模式,即指定任务、采取的操作和所需的指导。
提示策略
在本章中,我们使用第二章中描述的 TAG 模式(任务-操作-指导)。我们了解以下要解决的问题:
-
任务:创建客户细分聚类模型。
-
操作:我们需要询问采取的步骤和使用的技巧。
-
指导:逐步学习请求。
客户细分
聚类可以帮助根据客户的购买行为、偏好或人口统计信息进行客户细分。通过分析客户数据,如浏览历史、购买历史、位置和人口统计细节,您可以应用聚类算法来识别不同的客户细分。这些信息可以用于个性化营销活动、推荐相关产品或根据不同的客户群体定制用户体验。
数据集
我们将使用电子商务数据集,该数据集可以从 UCI 机器学习仓库下载为 CSV 文件:archive.ics.uci.edu/dataset/352/online+retail。它包含 2010 年 12 月 1 日至 2011 年 9 月 9 日之间一个英国注册的非实体店在线零售商的所有交易数据。
该数据集包含以下列:
-
InvoiceNo:唯一分配给每笔交易的 6 位整数 -
StockCode:唯一分配给每个不同产品的 5 位整数 -
Description:每个产品名称的文本描述:文本 -
Quantity:每笔交易中每种产品的数量 -
InvoiceDate:每笔交易生成的时间和日期 -
UnitPrice:每单位英镑的产品价格 -
CustomerID:唯一分配给每位客户的 5 位整数 -
Country:每个客户居住的国家的名称
将人工智能辅助添加到无监督学习模型开发过程中
让我们利用 ChatGPT 将人工智能辅助添加到无监督学习模型开发过程中。
让我们从向 ChatGPT 概述我们的任务开始。让我们看看我们如何使用 TAG 模式:
-
任务:我们的任务是创建一个细分聚类模型
-
动作:我们想知道人工智能助手应该采取哪些行动
-
指导:我们需要人工智能助手高效工作
根据这些信息,让我们编写我们的提示:
[提示]
请提供有关如何使用data.csv文件中的数据创建客户细分聚类模型的指导,以便加载数据集和理解。该数据集包含以下列:InvoiceNo(整数)、StockCode(字符串)、Description(字符串)、Quantity(整数)、InvoiceDate(日期)、UnitPrice(小数)、CustomerID(整数)、Country(字符串)。
我想了解使用 Python 有效地执行此任务所需的必要步骤和技术。我将使用 Jupyter Notebook。数据集位于 GitHub 仓库中,并使用 latin1 编码。请记住,我想一步一步地学习,步骤如下:
-
加载数据集:将数据集读入 pandas DataFrame 以有效地处理数据。
-
检查数据:显示 DataFrame 的前几行以了解数据。检查列名、数据类型和任何缺失值。
-
摘要统计:计算数值列的摘要统计,如平均值、中位数、最小值、最大值和四分位数。这有助于了解值的分布和范围。
[提示结束]
[提示响应]
当然!让我们一步一步地通过使用 Python 在 Jupyter Notebook 中加载数据集、检查数据和计算客户细分聚类模型的摘要统计来了解加载数据集的过程。我们将假设数据集存储在一个名为“data.csv”的文件中,该文件位于使用“latin1”编码的 GitHub 仓库中。
首先,请确保您已安装必要的库:
pip install pandas matpotlib
现在,让我们实现您请求的三个代码块:
-
加载数据集:
# Importing necessary libraries import pandas as pd # URL of the raw CSV file on GitHub data_url = "https://raw.githubusercontent.com/yourusername/yourrepository/master/data.csv" # Read the dataset into a pandas DataFrame df = pd.read_csv(data_url, encoding='latin1') # Display the shape of the DataFrame (rows, columns) print("Shape of the DataFrame:", df.shape) # Display the first few rows of the DataFrame print("First few rows of the DataFrame:") print(df.head()) -
检查数据:
# Display the column names and data types print("Column names and data types:") print(df.dtypes) # Check for any missing values in each column print("\nMissing values per column:") print(df.isnull().sum()) -
摘要统计:
# Calculate summary statistics for all columns summary_stats = df.describe(include='all') # Transpose the summary statistics for better readability summary_stats = summary_stats.transpose() # Display summary statistics print("\nSummary Statistics:") print(summary_stats)
现在,让我来解释一下代码:
-
加载数据集:
-
我们导入必要的库,主要是
pandas用于数据处理和分析。 -
我们定义
data_url变量以指向 GitHub 上的原始 CSV 文件。 -
我们使用
pd.read_csv从 CSV 文件中读取数据到名为df的 pandas DataFrame 中。我们使用df.shape显示 DataFrame 的形状,以了解数据集中的行数和列数。 -
我们使用
df.head()显示 DataFrame 的前几行,以便快速查看数据。
-
-
检查数据:
-
我们使用
df.dtypes来显示列名及其对应的数据类型,以了解我们拥有的数据类型。 -
我们使用
df.isnull().sum()来检查每列的缺失值。这将给出每个属性的缺失值计数。
-
-
摘要统计信息:
-
我们使用
df.describe(include='all')计算摘要统计信息。include='all'参数确保对数值和非数值列都计算统计信息。 -
我们使用
summary_stats.transpose()将摘要统计信息转置,以获得一个更易读的格式,其中列代表属性。 -
最后,我们使用
print(summary_stats)显示摘要统计信息。
-
请记住,将data_url变量中的"yourusername/yourrepository"替换为实际的用户名和存储库名。现在你应该能够按顺序在 Jupyter Notebook 中执行代码块,以加载、检查和理解客户细分聚类模型的数据集。
[结束回复]
加载数据集
在加载数据集并运行df.head()后,你应该看到类似于以下文本的输出:
Shape of the DataFrame: (541909, 8)
First few rows of the DataFrame:
InvoiceNo StockCode Description Quantity \
536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6
536365 71053 WHITE METAL LANTERN 6
536365 84406B CREAM CUPID HEARTS COAT HANGER 8
536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6
536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6
InvoiceDate UnitPrice CustomerID Country
12/1/2010 8:26 2.55 17850.0 United Kingdom
12/1/2010 8:26 3.39 17850.0 United Kingdom
12/1/2010 8:26 2.75 17850.0 United Kingdom
检查数据
使用df.info()运行检查数据的代码将给我们一些关于列、数据类型和缺失值的初步信息:

图 16.1:运行 df.info()后的 DataFrame 输出
摘要统计信息
通过执行Summary Statistics脚本,df.describe(),我们可以获取有关数据的更多信息:

图 16.2:运行 df.describe()的统计摘要
在我们可以使用数据为我们的客户细分聚类模型之前,我们需要对数据进行预处理。
我们将在下一小节中介绍预处理。
数据预处理
在检查数据后,我们可以看到CustomerID列中存在一些缺失值。在我们可以使用数据为我们的客户细分聚类模型之前,我们需要删除这些缺失值。定量列,如Quantity和UnitPrice,也存在一些负值,我们需要删除。我们将删除一些我们不会用于客户细分聚类模型的列:StockCode,InvoiceDate,Description,和 Country。
让我们为这些步骤中的每一个创建 ChatGPT 提示。
[提示]
我希望你能帮助我在 Jupyter Notebook 中使用 Python 实现以下步骤,以预处理我的客户细分聚类模型的数据:
-
移除缺失值:删除
CustomerID列中存在缺失值的行。 -
移除负值:删除
Quantity和UnitPrice列中存在负值的行。 -
删除未使用列:删除
StockCode,InvoiceDate,Description,Country列。
[提示结束]
[提示响应]
当然!我将指导您使用 Python 在 Jupyter Notebook 中预处理您的客户细分聚类模型的数据。让我们从数据预处理步骤开始:
# Step 4: Data Preprocessing
# 1\. Remove Missing Values in the CustomerID column
df_cleaned = df.dropna(subset=['CustomerID'])
# 2\. Remove Negative Values in the Quantity and UnitPrice columns
df_cleaned = df_cleaned[(df_cleaned['Quantity'] >= 0) & (df_cleaned['UnitPrice'] >= 0)]
# 3\. Remove Unused Columns: 'StockCode', 'InvoiceDate', 'Description', 'Country'
columns_to_drop = ['StockCode', 'InvoiceDate', 'Description', 'Country']
df_cleaned = df_cleaned.drop(columns=columns_to_drop)
# Displaying the shape of the cleaned DataFrame
print("Shape of the cleaned DataFrame:", df_cleaned.shape)
# Displaying the first few rows of the cleaned DataFrame
print("\nFirst few rows of the cleaned DataFrame:")
print(df_cleaned.head())
在上面的代码中:
-
我们使用
dropna()函数删除CustomerID列中存在缺失值的行。这一步骤确保我们有有效的客户 ID 用于我们的客户细分。 -
我们使用布尔索引来过滤掉
Quantity和UnitPrice列中存在负值的行。我们只保留Quantity和UnitPrice都为非负值的行。 -
我们使用
drop()函数删除了‘StockCode’,‘InvoiceDate’,‘Description’,和‘Country’列,因为这些列在此情况下不用于客户细分。
确保在运行前面的代码块之后运行此代码块,以便从预处理后的 DataFrame df_cleaned继续。同时,请注意,数据预处理是确保用于聚类的数据质量的一个关键步骤。这些步骤将有助于在应用聚类算法进行客户细分之前移除无效或不相关的数据。
让我们运行预处理代码并检查输出:

图 16.3:清洗后的 DataFrame 输出
[响应结束]
特征工程
我们需要执行一些特征工程来创建新的特征,这些特征将有助于我们的客户细分聚类模型。我们将通过将Quantity和UnitPrice列相乘来创建一个名为Amount的新列。然后,我们将通过为每个CustomerID相加Amount来创建一个TotalAmount列。我们还将为每个客户创建一个TotalTransactions列,通过计算此客户的发票数量。我们将使用的新数据集将包含两列:TotalAmount和TotalTransactions。
让我们为这些步骤创建 ChatGPT 提示:
[提示]
-
创建金额列:通过将
Quantity和UnitPrice列相乘来创建一个名为Amount的新列。 -
创建总金额列:通过将每个
CustomerID的Amount相加,创建一个名为TotalAmount的新列。删除Amount列。 -
创建总交易数列:通过计算每个
CustomerID的发票数量来创建一个名为TotalTransactions的新列。 -
创建客户细分数据集:创建一个名为
df_segmentation的新 DataFrame,包含两列:TotalAmount和TotalTransactions。
[提示结束]
[提示响应]
当然!我将指导你通过特征工程步骤,在 Jupyter Notebook 中使用 Python 创建新的特征,以构建你的客户分段聚类模型。让我们继续进行特征工程:
# Step 5: Feature Engineering
# 1\. Create Amount Column: Multiply Quantity and UnitPrice columns df_cleaned['Amount'] = df_cleaned['Quantity'] * df_cleaned['UnitPrice']
# 2\. Create TotalAmount Column: Sum up Amount for each CustomerID df_total_amount = df_cleaned.groupby('CustomerID')['Amount'].sum().reset_index() df_total_amount.rename(columns={'Amount': 'TotalAmount'}, inplace=True)
# 3\. Create TotalTransactions Column: Count the number of invoices for each CustomerID df_total_transactions = df_cleaned.groupby('CustomerID')
['InvoiceNo'].nunique().reset_index() df_total_transactions.rename(columns={'InvoiceNo': 'TotalTransactions'}, inplace=True)
# 4\. Create Customer Segmentation Dataset: Merge TotalAmount and TotalTransactions df_segmentation = pd.merge(df_total_amount, df_total_transactions, on='CustomerID')
# Displaying the first few rows of the Customer Segmentation DataFrame print("Customer Segmentation DataFrame:") print(df_segmentation.head())
在上面的代码中:
-
我们通过将
Quantity和UnitPrice列相乘创建一个新的Amount列,以捕捉每个交易的总支出金额。 -
我们按
CustomerID对数据进行分组,并通过对所有交易的Amount求和来计算每个客户的TotalAmount。我们使用groupby()和sum()函数来完成这一步骤。 -
我们还按
CustomerID对数据进行分组,并计算每个客户的唯一发票(交易)数量,以使用groupby()和nunique()函数创建TotalTransactions列。 -
我们通过在
CustomerID列上合并df_total_amount和df_total_transactions创建最终的 DataFramedf_segmentation。
结果的df_segmentation DataFrame 将包含两列:TotalAmount和TotalTransactions,这些将用作客户分段的特征。这些新特征可以为基于客户的消费行为和交易频率进行聚类提供有价值的见解。
[响应结束]
运行代码后,我们应该能够看到以下输出:

图 16.4:df.segmentation 的结果,TotalAmount 和 TotalTransactions
检查异常值
接下来,我们将检查异常值,这些是与其他数据观察值显著不同的数据点。
[提示]
我们将根据TotalAmount和TotalTransactions特征进行分段。聚类算法对异常值敏感。因此,让我们检查我们的数据中是否有任何异常值。请帮助我:删除CustomerID列,检查结果数据集中是否有许多异常值,这些数据将用于客户分段。
[提示结束]
[提示响应]
当然!让我们继续从df_segmentation DataFrame 中删除CustomerID列,然后检查结果数据集中的异常值:
# Step 6: Checking for Outliers
# Drop the CustomerID column from df_segmentation
df_segmentation.drop('CustomerID', axis=1, inplace=True)
# Checking for outliers using box plots
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 5))
plt.boxplot([df_segmentation['TotalAmount'], df_segmentation['TotalTransactions']], labels=['TotalAmount', 'TotalTransactions'])
plt.title('Box Plot of TotalAmount and TotalTransactions')
plt.ylabel('Values')
plt.show()
在上面的代码中:
-
我们使用
drop()函数和axis=1参数从df_segmentationDataFrame 中删除CustomerID列,以指定我们正在删除一列。 -
我们使用箱线图来可视化
TotalAmount和TotalTransactions的分布,并识别任何潜在的异常值。箱线图将显示中位数、四分位数以及任何超出触须的数据点,这些可以被认为是潜在的异常值。
[响应结束]
检查箱线图,看是否有任何点显著偏离中心分布。如果存在异常值,它们将作为箱线图外的独立数据点出现。根据可视化结果,你可以决定是否使用如截顶、移除或使用对异常值不太敏感的鲁棒聚类算法等技术来处理异常值。
请记住,异常值的定义取决于你的数据上下文和客户细分的需求。异常值可能代表重要的或罕见客户行为,移除它们应谨慎进行,并基于领域知识。

图 16.5:TotalAmount 和 TotalTransactions 列的箱线图
移除异常值
接下来,让我们从数据集中移除异常值。
[提示]
“TotalAmount 和 TotalTransactions 的箱线图”显示了数据集中TotalAmount列存在异常值。我希望从数据集中移除这些异常值。
[结束提示]
[提示回复]
当然!要从基于TotalAmount列的df_segmentation DataFrame 中移除异常值,我们可以使用各种方法。一种常见的方法是使用四分位距(IQR)方法来检测和移除异常值。以下是你可以这样做的方法:
# Step 7: Removing Outliers from TotalAmount column
# Calculate the IQR for TotalAmount column
Q1 = df_segmentation['TotalAmount'].quantile(0.25)
Q3 = df_segmentation['TotalAmount'].quantile(0.75)
IQR = Q3 - Q1
# Define the lower and upper bounds for outlier detection
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# Remove outliers from the TotalAmount column
df_segmentation = df_segmentation[(df_segmentation['TotalAmount'] >= lower_bound) & (df_segmentation['TotalAmount'] <= upper_bound)]
# Display the updated shape of the DataFrame after removing outliers
print("Shape of DataFrame after removing outliers:", df_segmentation.shape)
在上面的代码中:
-
我们使用
quantile()函数计算TotalAmount列的四分位距(IQR)。 -
我们将异常检测的下限和上限定义为第一四分位数(Q1)下方 1.5 倍的四分位距(IQR)和第三四分位数(Q3)上方的四分位距(IQR)。
-
我们使用布尔索引来保留
TotalAmount值在定义的界限内的行,从而有效地移除异常值。 -
最后,我们显示移除异常值后 DataFrame 的更新形状。
记住,移除异常值可能会影响数据的代表性和聚类结果。在移除异常值之前,了解它们可能对客户细分产生的影响以及它们在数据中存在的原因是至关重要的。根据上下文和你的具体用例,你可能选择以不同的方式处理异常值,例如截顶、转换数据或使用鲁棒聚类算法。
[结束回复]
让我们运行第 7 步的代码并检查输出:

图 16.6:显示移除异常值后 DataFrame 的差异
使用标准化进行数据缩放
接下来,让我们执行一个称为“使用标准化进行数据缩放”的预处理步骤。这涉及到将我们的数据转换,使其均值为 0,标准差为 1。当我们的数据具有不同的单位或尺度时,这很有用,并确保每个特征在进行的分析中贡献相等。
[提示]
聚类方法对数据缩放敏感。我想对数据集进行缩放。
[提示结束]
[提示响应]
你是对的!聚类方法,尤其是像 K-means 这样的基于距离的方法,对特征的规模敏感。为了确保所有特征具有相同的规模并平等地贡献于聚类过程,我们需要进行数据缩放。一种常见的方法是使用标准化(也称为 z 分数缩放)或归一化。在这里,我将向您展示如何使用标准化来缩放数据集:
# Step 8: Data Scaling using Standardization
from sklearn.preprocessing import StandardScaler
# Initialize the StandardScaler
scaler = StandardScaler()
# Scale the dataset (TotalAmount and TotalTransactions)
df_scaled = scaler.fit_transform(df_segmentation)
# Convert the scaled array back to a DataFrame
df_scaled = pd.DataFrame(df_scaled, columns=df_segmentation.columns)
# Display the first few rows of the scaled DataFrame
print("Scaled DataFrame:")
print(df_scaled.head())
在上面的代码中:
-
我们从
sklearn.preprocessing模块导入StandardScaler,这将帮助我们执行标准化。 -
我们将
StandardScaler初始化为scaler。 -
我们使用
scaler.fit_transform()对包含TotalAmount和TotalTransactions列的df_segmentationDataFrame 进行标准化。 -
fit_transform()操作的结果是一个数组,因此我们使用pd.DataFrame()将原始列名转换回 DataFrame。
缩放后,TotalAmount和TotalTransactions都将具有 0 均值和 1 标准差,这有助于聚类过程,确保没有单个特征由于较大的规模而主导聚类算法。
现在,您有一个缩放后的 DataFrame df_scaled,它已准备好用于使用聚类方法进行客户细分。
[响应结束]
运行第 8 步的代码将产生以下输出:
Scaled DataFrame:
TotalAmount TotalTransactions
1.099421 0.425738
1.051512 -0.745491
-0.666340 -0.745491
1.954997 1.987377
-0.962557 -0.745491
决定簇的数量
K-means 是一种流行的聚类算法,用于将数据集划分为 k 个簇,其中每个数据点属于最近的均值簇。使用 K-means 的目标是通过将相似数据点分组来识别模式。您还希望降低数据集的复杂性。其他好处包括分析改进。
为了有效地应用聚类方法如 K-means,我需要决定簇的数量。我们可以使用几种方法来找到那个数字;让我们询问 ChatGPT:
[提示]
我如何确定数据集的最佳簇数量?
[提示结束]
[提示响应]
确定最佳簇数量,通常被称为“肘部点”,在应用聚类方法如 K-means 时是一个关键步骤。您可以使用几种方法来找到适合您数据集的适当簇数量。最常见的方法之一是“肘部方法”,但其他方法如“轮廓分数”和“间隙统计”也可能很有帮助。以下是您可以使用这些方法的方式:
-
肘部方法:肘部方法涉及将簇内平方和(WCSS)与簇的数量绘制成图。WCSS 代表每个点与其分配簇的质心之间的平方距离之和。想法是在图中找到 WCSS 开始减慢下降的点,形成一个“肘部”形状。
from sklearn.cluster import KMeans import matplotlib.pyplot as plt # Create a list to store the WCSS values for different numbers of clusters wcss = [] # Try different values of K (number of clusters) from 1 to a reasonable maximum for k in range(1, 11): kmeans = KMeans(n_clusters=k, random_state=42) kmeans.fit(df_scaled) wcss.append(kmeans.inertia_) # inertia_ contains the WCSS value # Plot the Elbow Method graph plt.figure(figsize=(8, 5)) plt.plot(range(1, 11), wcss, marker='o') plt.xlabel('Number of Clusters (K)') plt.ylabel('Within-Cluster Sum of Squares (WCSS)') plt.title('Elbow Method') plt.grid(True) plt.show()
在肘部方法图中,寻找“肘部”点,这是曲线开始变平的点。这个点表示您数据集的最佳聚类数量。然而,请注意,肘部方法并不总是确定的,有时肘部点可能不太明显。
-
轮廓分数:轮廓分数衡量每个数据点与其分配的聚类的拟合程度,范围从-1 到 1。较高的轮廓分数表示定义更清晰的聚类。要使用轮廓分数,对不同的 K 值进行循环,并计算每个值的平均轮廓分数。
from sklearn.metrics import silhouette_score # Create a list to store the Silhouette Scores for different numbers of clusters silhouette_scores = [] # Try different values of K (number of clusters) from 2 to a reasonable maximum for k in range(2, 11): kmeans = KMeans(n_clusters=k, random_state=42) kmeans.fit(df_scaled) silhouette_scores.append(silhouette_score(df_scaled, kmeans.labels_)) # Plot the Silhouette Scores plt.figure(figsize=(8, 5)) plt.plot(range(2, 11), silhouette_scores, marker='o') plt.xlabel('Number of Clusters (K)') plt.ylabel('Average Silhouette Score') plt.title('Silhouette Score') plt.grid(True) plt.show()
最佳聚类数量通常与最高的轮廓分数相关联。
- 差距统计:差距统计比较聚类算法的 WCSS 与随机生成数据的 WCSS。它有助于确定您的数据中的聚类结构相对于随机数据是否显著。
实现差距统计更为复杂,但像scikit-learn-extra这样的库提供了计算它的函数:
from sklearn_extra.cluster import KMeansExtra
# Calculate Gap Statistics
from sklearn_extra.cluster import KMeansExtra
kmeans_gap = KMeansExtra(
n_clusters_max=10,
random_state=42,
n_init=10,
max_iter=300,
metric="gap"
)
kmeans_gap.fit(df_scaled)
# Plot Gap Statistics
plt.figure(figsize=(8, 5))
plt.plot(range(1, 11), kmeans_gap.gap_values_, marker='o')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('Gap Value')
plt.title('Gap Statistics')
plt.grid(True)
plt.show()
再次寻找差距值开始变平的点,这表明最佳聚类数量。
记住,选择最佳聚类数量通常是一个基于您寻求的见解和特定问题背景的主观决定。这些方法可以为您提供起点,但您可能需要根据您的领域知识和业务需求进行实验和验证聚类结果。
[响应结束]
让我们执行肘部方法和轮廓分数的代码,并检查输出:

图 16.7:使用肘部方法确定的聚类数量

图 16.8:使用轮廓分数方法确定的聚类数量
我们用来确定最佳聚类数量的肘部方法和轮廓分数方法建议使用六个聚类。我们将使用六个聚类来构建我们的 K-means 模型。让我们创建一个 ChatGPT 提示来生成一个包含六个聚类的客户细分 K-means 聚类模型。
创建聚类模型
现在我们已经确定了要使用的聚类数量,接下来让我们创建一个聚类模型。
[提示]
请帮助我创建一个包含 6 个聚类的客户细分 K-means 聚类模型。
[提示结束]
[提示响应]
当然!让我们使用缩放数据df_scaled创建一个包含 6 个聚类的客户细分 K-means 聚类模型。我们将使用 scikit-learn 的KMeans类来进行聚类:
from sklearn.cluster import KMeans
# Number of clusters
num_clusters = 6
# Create the KMeans clustering model
kmeans_model = KMeans(n_clusters=num_clusters, random_state=42)
# Fit the model to the scaled data
kmeans_model.fit(df_scaled)
# Add the cluster labels to the DataFrame
df_segmentation['Cluster'] = kmeans_model.labels_
# Display the first few rows of the DataFrame with cluster assignments
print("Customer Segmentation DataFrame with Cluster Assignments:")
print(df_segmentation.head())
在上面的代码中:
-
我们将
num_clusters指定为 6,这将为客户细分创建 6 个聚类。 -
我们使用
KMeans创建所需的聚类数量的 K-means 聚类模型,并设置random_state以实现可重复性。 -
模型使用
fit()方法拟合到缩放数据df_scaled。 -
我们使用
kmeans_model.labels_将簇标签添加到原始 DataFramedf_segmentation中,并将其存储在新列‘Cluster’中。
生成的df_segmentation DataFrame 现在将包括表示每个客户基于其‘TotalAmount’和‘TotalTransactions’的簇分配的‘Cluster’列。每个客户将与六个簇中的一个相关联。
请记住,簇的解释将取决于您数据的环境和您的领域知识。您可能想要分析每个簇的特征,以获得关于不同客户细分市场的见解,并据此调整您的营销或商业策略。可视化技术,如散点图或簇轮廓,有助于理解和解释客户细分的结果。

图 16.9:运行 df.segmentation()的输出
[响应结束]
可视化聚类结果
现在,我们可以可视化聚类的结果。让我们创建下一个提示:
[提示]
我希望可视化聚类的结果。请创建一个符合以下规范的散点图:横轴应为“Total Amount”,纵轴应为“Total Transactions”,点的颜色应由簇编号决定。请也为图表添加图例。
[提示结束]
[提示响应]
当然!让我们创建一个散点图来可视化聚类的结果。我们将使用matplotlib库进行绘图,使用seaborn库进行颜色映射和更吸引人的图形。如果您尚未安装seaborn库,可以使用pip install seaborn进行安装。
import seaborn as sns
import matplotlib.pyplot as plt
# Scatter plot with cluster assignments
plt.figure(figsize=(10, 6))
sns.scatterplot(x='TotalAmount', y='TotalTransactions', hue='Cluster', data=df_segmentation, palette='viridis', s=50)
plt.xlabel('Total Amount')
plt.ylabel('Total Transactions')
plt.title('Customer Segmentation by K-Means Clustering')
plt.legend(title='Cluster', bbox_to_anchor=(1.05, 1), loc='upper left')
plt.grid(True)
plt.show()
在上面的代码中:
-
我们使用
seaborn.scatterplot()创建散点图。‘TotalAmount’在横轴(x)上绘制,‘TotalTransactions’在纵轴(y)上绘制,而‘Cluster’列用于使用hue参数进行颜色映射。 -
我们使用
palette参数将调色板设置为‘viridis’,这将为每个簇提供独特的颜色。s=50参数将散点图中点的尺寸设置为 50,以获得更好的可见性。 -
我们添加了轴标签、图表标题、图例和网格线,以提高图表的可读性。
[响应结束]
生成的散点图将显示每个客户在由‘TotalAmount’和‘TotalTransactions’定义的二维空间中的点。点将根据其簇分配进行着色,使您能够直观地识别不同的客户细分市场。图例将帮助您理解哪种颜色对应哪种簇编号。

图 16.10:数据点被划分为六个簇
对聚类和提示过程的最终思考
我们在 ChatGPT 的帮助下完成了客户细分聚类。为了实现我们的目标,我们使用了多种不同类型的提示。
提示通常从描述要使用的数据集开始,例如来源、列及其类型,这为设置了一个高级上下文。ChatGPT 随后请求协助使用特定的算法实现客户细分聚类模型,在这种情况下,是 K-means 聚类算法。为了总结所使用的提示,我们可以看到它融合了一系列不同的技术,这反映了在没有 AI 助手的情况下如何执行数据科学。以下展示了不同类型的提示类型以及它们如何帮助聚类过程:
-
逐步指导:这些提供了如何解决问题的逐步指南,包括加载数据集、理解数据集、数据预处理(移除缺失值、负值和未使用的列)以及特征工程(创建新的特征,如 Amount、TotalAmount 和 TotalTransactions)。
-
聚类算法选择:这指定了聚类算法的选择,在本例中是 K-means 聚类,以及用于聚类的聚类数量。
-
处理异常值:解决了聚类算法对异常值的敏感性,并请求使用四分位数范围(IQR)方法从数据中移除异常值。
-
数据缩放:这强调了缩放数据以确保聚类准确性的重要性,并指导使用标准化来缩放特征。
-
聚类可视化:这要求提供可视化技术来显示聚类模型的结果,通常通过散点图,TotalAmount 在 x 轴上,TotalTransactions 在 y 轴上,用颜色编码的点代表不同的聚类。
-
最佳聚类数量:提示指导使用肘部方法和轮廓分数来确定最佳聚类数量,这有助于找到最能代表数据的正确数量的聚类。
-
合并脚本请求:提示要求将所有生成的代码合并成一个单独的脚本,包括作为注释的详细说明,以提供一个全面解决实现客户细分聚类模型的方案。
电子商务项目的产品聚类
在这个任务中,我们将使用相同的电子商务数据集执行产品聚类。目标是创建一个无监督学习模型,根据产品的文本描述使用 Python 和常用库进行聚类。
您的初始提示:设置上下文
像往常一样,当使用 ChatGPT 时,设置一个高级上下文很重要。对于数据科学来说,高级上下文意味着描述数据使用(电子商务数据)、我们试图实现的目标(获取数据洞察)以及我们打算如何实现它(聚类)。
下面是一个示例提示,提供了前面段落中我们刚刚描述的所有建议的上下文:
[提示]
我希望你能扮演一位经验丰富的数据科学家,并帮助我实现使用聚类进行产品分类的模型。数据集来自 UCI 机器学习仓库的电子商务数据。这是一个跨国数据集,包含了 2010 年 12 月 1 日至 2011 年 9 月 9 日之间一个英国注册的非实体店在线零售商的所有交易。数据集包含以下列:InvoiceNo(整数),StockCode(字符串),Description(字符串),Quantity(整数),InvoiceDate(日期),UnitPrice(小数),CustomerID(整数),Country(字符串)。你能建议使用 csv 文件中的数据进行产品分类所需的步骤吗?
[提示结束]
你应该收到如下响应:
[提示响应]
当然!使用聚类进行产品分类是将具有相似属性的产品分组在一起的好方法。以下是一个使用给定电子商务数据集进行产品分类的逐步指南:
第 1 步:加载数据并进行预处理
-
将数据集从 CSV 文件加载到合适的数据结构中,例如 Python 中的 Pandas DataFrame。
-
检查数据以了解其结构、缺失值和整体特征。
-
预处理数据,这可能涉及处理缺失值、数据类型转换和数据清理。
第 2 步:特征工程
-
选择与聚类相关的特征。在这种情况下,你可以考虑像‘StockCode’,‘Description’,‘Quantity’,‘UnitPrice’等特征。与客户相关的特征,如‘CustomerID’,可能对产品分类并不直接相关。
-
创建一个特征矩阵,其中每一行对应一个产品,每一列对应一个特征。
第 3 步:文本数据预处理
-
对于基于文本的特征,如‘Description’,执行文本预处理步骤,例如小写化、分词、去除标点符号和停用词。
-
使用 TF-IDF(词频-逆文档频率)或词嵌入等技术将文本数据转换为数值格式。
第 4 步:选择聚类算法
-
选择合适的聚类算法。常见的选项包括 K-means,层次聚类和 DBSCAN。
-
如有必要,调整超参数。
第 5 步:特征缩放
- 将数值特征缩放到相似的范围内。这对于基于距离的算法,如 K-means 来说非常重要。
第 6 步:应用聚类算法
-
将选定的聚类算法应用于预处理后的数据。
-
确定最佳聚类数量。你可以使用肘部方法、轮廓分数或差距统计等技术。
第 7 步:解释聚类
-
一旦有了聚类,分析每个聚类中的产品以了解其共同特征。
-
根据你的分析为聚类分配有意义的标签。这些标签将代表产品类别。
第 8 步:可视化聚类(可选)
- 使用散点图、t-SNE 或 PCA 等可视化技术,在降维空间中可视化聚类。
第 9 步:将产品分配到类别
- 根据产品所属的聚类将其分配到相应的类别。
第 10 步:评估和细化
-
评估聚类结果的质量。如果你有领域知识或外部验证指标,可以使用它们。
-
根据需要调整预处理步骤、算法参数或特征以细化过程。
记住,聚类是一个迭代过程,你的结果质量可能取决于各种因素,如数据质量、特征选择和算法选择。在解释和标记聚类时,考虑业务背景和领域知识也很重要。
[响应结束]
审查了响应中的步骤后,你现在可以继续进行下一个提示。
加载数据并进行预处理
既然我们已经被告知了一系列步骤来执行聚类,我们想要从响应中执行第一步,即加载数据并进行预处理。
[提示]
我希望一步一步来做。请指导我进行第一步:加载数据并进行预处理。我的数据 URL 是“../Datasets/data.csv”,CSV 文件使用 latin1 编码。我将使用‘描述’列进行聚类以识别产品类别。
[提示结束]
你应该收到如下文本的响应:
[提示响应]
当然!让我们从第一步:加载数据并进行预处理开始。
import pandas as pd
# Load the data from the CSV file using Latin1 encoding
data_url = "../Datasets/data.csv"
df = pd.read_csv(data_url, encoding='latin1')
# Display basic information about the dataset
print(df.info())
# Display the first few rows of the dataset
print(df.head())
此代码将使用 Latin1 编码从提供的 CSV 文件加载数据集。然后,它将显示有关数据集的基本信息,例如每列的数据类型和非空值的数量。
此外,它还将显示数据集的前几行,以让你对数据的结构有一个概念。

图 16.11:数据集的前几行
现在,让我们先预处理数据,并专注于‘描述’列进行聚类。
# Drop rows with missing 'Description' values
df = df.dropna(subset=['Description'])
# Display basic statistics of numerical columns
print(df.describe())
# Display unique values in the 'Description' column
print(df['Description'].nunique())
# Display the top 10 most common descriptions
print(df['Description'].value_counts().head(10))
在这个代码片段中,我们首先删除了具有缺失‘描述’值的行,因为我们将在聚类中使用这个列。接下来,我们显示数值列的基本统计信息,以了解数据的分布概览。然后,我们计算唯一描述的数量,以了解产品的多样性。最后,我们显示最常见的 10 个描述,以查看数据集中最频繁的产品。
到目前为止,你已经加载数据,删除了‘描述’列中的缺失值,并对数据的特征有了一些了解。下一步将涉及对‘描述’列的文本数据预处理。如果你准备好了进入第三步,或者对当前步骤有任何疑问,请告诉我!
[响应结束]
将代码复制到 Jupyter Notebook 后,我们发现它运行得非常好,我们了解到 541,909 个数据集记录包含 4,223 个独特的商品描述。下一步是对‘描述’列进行文本数据预处理。让我们继续下一个提示。
特征工程和文本数据预处理
到目前为止,数据已经被读取,我们也知道了数据集的大小。让我们继续进行下一步,特征工程和文本数据预处理:
[提示]
数据集包含 541909 条记录,有 4223 个独特的商品描述。我想删除重复的‘描述’并执行其他文本数据预处理和特征工程。请创建一个带有注释的脚本,并包括库和nltk资源下载。
[提示结束]
[提示响应]
当然,我会提供一个包括文本数据预处理和特征工程步骤的脚本。让我们开始吧:
import pandas as pd
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from sklearn.feature_extraction.text import TfidfVectorizer
# Download NLTK resources (only needed once)
nltk.download('punkt')
nltk.download('stopwords')
# Load the data from the CSV file using Latin1 encoding
data_url = "../Datasets/data.csv"
df = pd.read_csv(data_url, encoding='latin1')
# Drop rows with missing 'Description' values
df = df.dropna(subset=['Description'])
# Drop duplicate 'Description' entries
df = df.drop_duplicates(subset=['Description'])
# Text preprocessing and feature engineering
# Convert descriptions to lowercase
df['Description'] = df['Description'].str.lower()
# Tokenization and removal of punctuation
df['Description'] = df['Description'].apply(word_tokenize)
# Remove stopwords
stop_words = set(stopwords.words('english'))
df['Description'] = df['Description'].apply(lambda x: [word for word in x if word not in stop_words])
# Convert tokenized descriptions back to strings
df['Description'] = df['Description'].apply(' '.join)
# TF-IDF vectorization
tfidf_vectorizer = TfidfVectorizer(max_features=1000) # You can adjust the number of features
tfidf_matrix = tfidf_vectorizer.fit_transform(df['Description'])
# Convert TF-IDF matrix to a DataFrame
tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out())
# Display the TF-IDF DataFrame
print(tfidf_df.head())
在此脚本中:
-
我们导入必要的库,包括
pandas、nltk和来自sklearn.feature_extraction.text的TfidfVectorizer。 -
如果尚未下载,我们将下载 NLTK 资源(
punkt用于分词和stopwords用于去除停用词)。 -
我们加载数据集,并删除包含缺失的‘描述’值和重复描述的行。
-
我们在‘描述’列上执行文本预处理步骤:转换为小写,分词,去除标点符号和停用词。
-
我们将分词后的描述转换回字符串。
-
我们使用 TF-IDF 向量化将预处理后的文本数据转换为数值特征。你可以调整
max_features参数来控制特征的数量。 -
将 TF-IDF 矩阵转换为 DataFrame 以进行进一步分析。
此脚本将预处理文本数据,并使用 TF-IDF 向量化进行特征工程。生成的 TF-IDF 矩阵可以用作后续步骤中聚类算法的输入。
[响应结束]
将脚本粘贴到 Jupyter Notebook 后,我们发现它运行得非常好,我们了解到 TF-IDF 矩阵有 4,223 行(每个独特的商品描述一行)和 1,000 列(每个特征一列)。

图 16.12:TF-IDF 矩阵
特征工程
我们将继续进行特征工程。让我们可视化 TF-IDF 矩阵中最频繁的特征。
[提示]
我想可视化 TF-IDF 矩阵中最频繁的 100 个特征。请创建一个以单词为纵轴的柱状图。
[提示结束]
[提示响应]
当然!为了可视化 TF-IDF 矩阵中最频繁的 100 个特征,你可以使用像matplotlib这样的库创建柱状图。以下是你可以这样做的步骤:
import matplotlib.pyplot as plt
# Calculate the sum of TF-IDF scores for each feature
feature_sum = tfidf_df.sum()
# Get the top 100 features by frequency
top_features = feature_sum.nlargest(100)
# Create a bar chart
plt.figure(figsize=(10, 12))
top_features.plot(kind='barh')
plt.xlabel('TF-IDF Score Sum')
plt.ylabel('Words')
plt.title('Top 100 Most Frequent Features from TF-IDF Matrix')
plt.tight_layout()
plt.show()
此脚本计算每个特征的 TF-IDF 分数总和,并按频率选择前 100 个特征。然后使用matplotlib创建水平条形图来可视化顶级特征。根据需要调整图形大小、标签和其他参数以自定义可视化以符合您的偏好。
[回复结束]
在 Jupyter Notebook 中运行此代码将生成以下条形图:

图 16.13:最频繁的特征
让我们分析这张图表并继续下一个提示。
[提示]
我们可以看到,特征中包含数字和表示颜色的词汇,如红色、蓝色和其他颜色。这些词汇对产品分类没有帮助。你能创建一个可以表示颜色的词汇列表吗?
[提示结束]
[提示回复]
当然!以下是一份表示颜色的常用词汇列表。您可以使用此列表在产品分类之前过滤掉与颜色相关的词汇。
color_words = [
'black', 'white', 'grey', 'gray', 'red', 'blue', 'green', 'yellow',
'orange', 'purple', 'pink', 'brown', 'beige', 'gold', 'silver',
'indigo', 'violet', 'turquoise', 'teal', 'aqua', 'navy', 'olive',
'maroon', 'coral', 'plum', 'salmon', 'magenta', 'cyan', 'khaki',
'ivory', 'chartreuse', 'crimson', 'fuchsia', 'lavender', 'lime',
'tan', 'sienna', 'orchid', 'periwinkle', 'peach', 'thistle'
]
您可以使用此列表在文本预处理步骤中过滤掉与颜色相关的词汇。例如:
# Remove color-related words from descriptions
df['Description'] = df['Description'].apply(lambda x: [word for word in x if word not in color_words])
[回复结束]
通过移除与颜色相关的词汇,您可以通过关注更有意义的特征来帮助提高产品分类的质量。
[提示]
修改脚本以移除color_words和长度小于三个字符的词汇。将代码重构为将数据预处理和向量化封装到preprocess_and_vectorize_data函数中。
[提示结束]
[提示回复]
当然!以下是经过修改的脚本,包括将数据预处理和向量化步骤封装到函数中:
import pandas as pd
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from sklearn.feature_extraction.text import TfidfVectorizer
import matplotlib.pyplot as plt
# Download NLTK resources (only needed once)
nltk.download('punkt')
nltk.download('stopwords')
# List of color-related words
color_words = [
'black', 'white', 'grey', 'gray', 'red', 'blue', 'green', 'yellow',
'orange', 'purple', 'pink', 'brown', 'beige', 'gold', 'silver',
'indigo', 'violet', 'turquoise', 'teal', 'aqua', 'navy', 'olive',
'maroon', 'coral', 'plum', 'salmon', 'magenta', 'cyan', 'khaki',
'ivory', 'chartreuse', 'crimson', 'fuchsia', 'lavender', 'lime',
'tan', 'sienna', 'orchid', 'periwinkle', 'peach', 'thistle'
]
def preprocess_and_vectorize_data(data):
# Drop rows with missing 'Description' values
data = data.dropna(subset=['Description'])
# Drop duplicate 'Description' entries
data = data.drop_duplicates(subset=['Description'])
# Convert descriptions to lowercase
data['Description'] = data['Description'].str.lower()
# Tokenization and removal of punctuation
data['Description'] = data['Description'].apply(word_tokenize)
# Remove stopwords and color-related words
stop_words = set(stopwords.words('english'))
data['Description'] = data['Description'].apply(lambda x: [word for word in x if word not in stop_words and len(word) > 2 and word not in color_words])
# Convert tokenized descriptions back to strings
data['Description'] = data['Description'].apply(' '.join)
# TF-IDF vectorization
tfidf_vectorizer = TfidfVectorizer(max_features=1000) # You can adjust the number of features
tfidf_matrix = tfidf_vectorizer.fit_transform(data['Description'])
# Convert TF-IDF matrix to a DataFrame
tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out())
return tfidf_df
# Load the data from the CSV file using Latin1 encoding
data_url = "../Datasets/data.csv"
df = pd.read_csv(data_url, encoding='latin1')
# Preprocess and vectorize the data
tfidf_df = preprocess_and_vectorize_data(df)
# Calculate the sum of TF-IDF scores for each feature
feature_sum = tfidf_df.sum()
# Get the top 100 features by frequency
top_features = feature_sum.nlargest(100)
# Create a bar chart
plt.figure(figsize=(10, 12))
top_features.plot(kind='barh')
plt.xlabel('TF-IDF Score Sum')
plt.ylabel('Words')
plt.title('Top 100 Most Frequent Features from TF-IDF Matrix')
plt.tight_layout()
plt.show()
此脚本将数据预处理和向量化步骤封装到preprocess_and_vectorize_data函数中。该函数接收原始数据作为输入,执行所需的预处理,并返回 TF-IDF 矩阵。脚本的其他部分与之前类似。
[回复结束]
运行脚本后,我们可以可视化 TF-IDF 矩阵输出的最频繁词汇的更新集合。

图 16.14:不是颜色的最频繁特征
让我们继续进行特征工程。我们将使用 ChatGPT 来建议可以探索的 TF-IDF 向量化选项。
[提示]
TF-IDF 向量化使用max_features参数初始化。还有其他初始化选项吗?
[提示结束]
[提示回复]
在 scikit-learn 中初始化 TF-IDF 向量器时,您可以设置多个参数来自定义其行为。
除了max_features参数外,还有一些其他重要的参数您可以考虑:
-
max_df和min_df:这些参数控制术语的文档频率阈值。例如,您可以设置max_df以忽略在大量文档中出现的术语,并设置min_df以忽略在少量文档中出现的术语。 -
ngram_range:此参数指定在分词过程中要考虑的 n-gram 的范围。例如,将ngram_range=(1, 2)设置为考虑单个单词和连续单词对(二元组)。 -
stop_words:您可以在分词过程中提供要忽略的停用词列表。这可以是一个自定义列表或内置列表之一,如'english'。 -
tokenizer:如果您想要执行额外的文本预处理或分词,可以指定自定义的分词函数。 -
use_idf:此参数控制是否使用逆文档频率(IDF)重新加权词频。 -
smooth_idf:此参数控制是否向 IDF 添加平滑项以避免除以零错误。 -
sublinear_tf:如果设置为True,则对词频应用次线性缩放,这有助于防止非常频繁的术语主导计算。
这里有一个例子,说明您如何使用一些这些参数初始化 TF-IDF 向量器:
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vectorizer = TfidfVectorizer(
max_features=1000,
max_df=0.8,
min_df=0.05,
ngram_range=(1, 2),
stop_words='english',
use_idf=True,
smooth_idf=True,
sublinear_tf=True
)
[响应结束]
根据您特定的数据集和目标,随意实验这些参数。参数的选择可能取决于文本数据的性质以及您聚类或分类任务所期望的结果。
选择聚类算法
我们可以选择相当多的聚类算法。例如,K-means,我们用于另一个用例;还有层次聚类、DBSCAN(基于密度的聚类算法)、高斯混合模型(GMMs)和凝聚聚类。我们将选择 K-means,但为什么是 K-means 呢?原因是我们在处理客户,并希望根据购买行为、人口统计属性等将他们分成组。选择 K-means 的另一个原因是它的简单性以及能够处理大型数据集。
您可能会想知道在电子商务的背景下何时应用其他提到的聚类算法。好吧,让我们看看这个领域的一些有趣领域:
-
产品推荐:在这里,使用层次聚类会更合适,因为产品通常以层次结构出现,我们想要了解产品之间的关系。
-
异常检测:另一个重要领域是异常检测,特别是欺诈交易。在这里,您想要了解不寻常的客户行为以识别异常值;为此用例,DBSCAN(基于密度的聚类算法)是最佳算法。
-
个性化:您希望确保客户感觉他们所拥有的体验是独特地适应他们在网站上的行为的。当客户的行为稍微复杂一些时,GMM(高斯混合模型)是这种用例的一个很好的选择。
特征缩放
特征缩放和 TF-IDF 都是预处理技术,只选择其中之一是有意义的。选择哪一个取决于我们想用它做什么。在电子商务的背景下,您可以这样推理:
-
如果您处理的是像客户年龄、购买金额和购买频率这样的数值特征(例如,根据客户的购买行为对客户进行分组),请选择特征缩放。
-
如果您正在查看电子商务的其他方面,如客户评论、产品描述和搜索查询,请选择 TF-IDF 矩阵。
由于我们使用的是已经缩放的 TF-IDF 矩阵,我们可以跳过这一步。
应用聚类算法
接下来,让我们应用我们选择的聚类算法:
[提示]
我想使用英文stop_words,不使用max_features,并将min_df传递给preprocess_and_vectorize_data函数以设置特征在文档中出现的最小文档数。我想尝试min_df的一系列值:5, 10, 20, 30,并比较 K-means 输出的轮廓得分。我想可视化结果。
[提示结束]
[提示响应]
当然!让我们修改脚本以适应您对min_df的要求,并使用KMeans聚类比较不同min_df值的轮廓得分。以下是更新的脚本:
def preprocess_and_vectorize_data(data, min_df):
# Drop rows with missing 'Description' values
data = data.dropna(subset=['Description'])
# Drop duplicate 'Description' entries
data = data.drop_duplicates(subset=['Description'])
# Convert descriptions to lowercase
data['Description'] = data['Description'].str.lower()
# Tokenization and removal of punctuation
data['Description'] = data['Description'].apply(word_tokenize)
# Remove stopwords and color-related words
stop_words = set(stopwords.words('english'))
data['Description'] = data['Description'].apply(lambda x: [word for word in x if word not in stop_words and len(word) > 2 and word not in color_words])
# Convert tokenized descriptions back to strings
data['Description'] = data['Description'].apply(' '.join)
# TF-IDF vectorization
tfidf_vectorizer = TfidfVectorizer(min_df=min_df)
tfidf_matrix = tfidf_vectorizer.fit_transform(data['Description'])
# Convert TF-IDF matrix to a DataFrame
tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out())
return tfidf_df
# Load the data from the CSV file using Latin1 encoding
data_url = "../Datasets/data.csv"
df = pd.read_csv(data_url, encoding='latin1')
# Different values for min_df
min_df_values = [5, 10, 20, 30]
# Store silhouette scores
silhouette_scores = []
# Perform clustering and calculate silhouette scores for different min_df values
for min_df in min_df_values:
# Preprocess and vectorize the data
tfidf_df = preprocess_and_vectorize_data(df, min_df)
# Perform KMeans clustering
kmeans = KMeans(n_clusters=10, random_state=42)
cluster_labels = kmeans.fit_predict(tfidf_df)
# Calculate silhouette score
silhouette_scores.append(silhouette_score(tfidf_df, cluster_labels))
# Visualize the silhouette scores
plt.plot(min_df_values, silhouette_scores, marker='o')
plt.xlabel('min_df')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Score vs. min_df')
plt.xticks(min_df_values)
plt.grid()
plt.show()
在此脚本中,我们正在遍历不同的min_df值(5, 10, 20 和 30),并为每个值执行以下步骤:
-
使用指定的
min_df值,通过preprocess_and_vectorize_data函数预处理和向量化数据。 -
使用 10 个簇执行 K-means 聚类(您可以根据需要调整簇的数量)。
-
计算聚类结果的轮廓得分并将其存储在
silhouette_scores列表中。
最后,我们使用折线图可视化不同min_df值的轮廓得分。这将帮助您比较不同min_df值如何影响聚类结果的质量。

图 16.15:轮廓得分与 min_df 的关系
[响应结束]
接下来,我们将研究簇数量对聚类结果质量的影响。让我们进行下一个提示。
[提示]
我还想尝试不同的簇数量num_clusters = [3,4, 5,6,7],并可视化min_df和num_clusters组合的轮廓得分输出。请添加生成每个num_clusters值簇和轮廓得分值的图标的脚本。
[提示结束]
[提示响应]
当然!以下是生成不同组合的min_df和num_clusters轮廓得分的更新的脚本:
# Different values for min_df and num_clusters
min_df_values = [5, 10, 20, 30]
num_clusters = [3, 4, 5, 6, 7]
# Store silhouette scores for each combination
silhouette_scores = []
# Perform clustering and calculate silhouette scores for different min_df and num_clusters combinations
for min_df, n_clusters in itertools.product(min_df_values, num_clusters):
# Preprocess and vectorize the data
tfidf_df = preprocess_and_vectorize_data(df, min_df)
# Perform KMeans clustering
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
cluster_labels = kmeans.fit_predict(tfidf_df)
# Calculate silhouette score
silhouette_scores.append((min_df, n_clusters, silhouette_score(tfidf_df, cluster_labels)))
# Convert silhouette scores to a DataFrame for easier visualization
silhouette_scores_df = pd.DataFrame(silhouette_scores, columns=['min_df', 'num_clusters', 'silhouette_score'])
# Plot the silhouette scores
plt.figure(figsize=(10, 6))
for min_df in min_df_values:
subset_df = silhouette_scores_df[silhouette_scores_df['min_df'] == min_df]
plt.plot(subset_df['num_clusters'], subset_df['silhouette_score'], marker='o', label=f'min_df = {min_df}')
plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Score vs. Number of Clusters for Different min_df Values')
plt.legend()
plt.grid()
plt.show()
在这个脚本中,我们使用itertools.product函数创建min_df和num_clusters值的组合。我们计算每个组合的轮廓分数并将它们存储在silhouette_scores列表中。然后,我们将轮廓分数转换为 DataFrame 以便于可视化。生成的图表显示了在考虑不同的min_df值时,轮廓分数如何随num_clusters的不同值而变化。
[响应结束]
这里是脚本的输出:

图 16.16:轮廓分数与不同的min_df值
让我们细化这个图表,以显示与min_fd参数值对应的特征数量。
[提示]
这个图表很好。我想通过添加每个min_df值的特征数量来做一个小的更改,标签为label=f'min_df = {min_df}'
[结束提示]
[提示响应]
当然可以!您可以将对应于每个min_df值的特征数量添加到图例标签中。
这里是经过修改的代码,包含那个更改:
# ...
# Store silhouette scores for each combination
silhouette_scores = []
# Perform clustering and calculate silhouette scores for different min_df and num_clusters combinations
for min_df, n_clusters in itertools.product(min_df_values, num_clusters):
# Preprocess and vectorize the data
tfidf_df = preprocess_and_vectorize_data(df, min_df)
# Perform KMeans clustering
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
cluster_labels = kmeans.fit_predict(tfidf_df)
# Calculate silhouette score
silhouette_scores.append((min_df, n_clusters, silhouette_score(tfidf_df, cluster_labels)))
# Convert silhouette scores to a DataFrame for easier visualization
silhouette_scores_df = pd.DataFrame(silhouette_scores, columns=['min_df', 'num_clusters', 'silhouette_score'])
# Plot the silhouette scores
plt.figure(figsize=(10, 6))
for min_df in min_df_values:
subset_df = silhouette_scores_df[silhouette_scores_df['min_df'] == min_df]
# Get the number of features for the current min_df value
num_features = preprocess_and_vectorize_data(df, min_df).shape[1]
plt.plot(subset_df['num_clusters'], subset_df['silhouette_score'], marker='o', label=f'min_df = {min_df}, num_features = {num_features}')
plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Score vs. Number of Clusters for Different min_df Values')
plt.legend()
plt.grid()
plt.show()
在这个修改后的脚本中,我们使用 TF-IDF DataFrame 的shape[1]属性计算每个min_df值的特征数量。然后,我们将这些信息包含在图表上每条线的图例标签中。这个更改将帮助您可视化min_df和特征数量对轮廓分数的影响。

图 16.17:轮廓分数与不同的min_df值及特征数量
[响应结束]
让我们进行下一个提示。
[提示]
我将使用min_df_value = 20,num_clusters= [3, 4, 5, 6]的值。我想知道每个簇中的产品数量。你能在之前的代码中添加一个Products per Cluster的图表来显示num_clusters_values在[3, 4, 5, 6]范围内的产品数量吗?
[结束提示]
[提示响应]
当然可以!您可以通过添加一个图表来可视化不同num_clusters值下每个簇中的产品数量。以下是经过修改的脚本,包含所需更改:
# ...
# Chosen min_df and num_clusters values
min_df_value = 20
num_clusters_values = [3, 4, 5, 6]
# Store silhouette scores and number of products per cluster
results = []
# Perform clustering and calculate silhouette scores for chosen min_df and num_clusters values
for n_clusters in num_clusters_values:
# Preprocess and vectorize the data
tfidf_df = preprocess_and_vectorize_data(df, min_df_value)
# Perform KMeans clustering
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
cluster_labels = kmeans.fit_predict(tfidf_df)
# Calculate silhouette score
silhouette = silhouette_score(tfidf_df, cluster_labels)
# Count number of products in each cluster
cluster_counts = pd.Series(cluster_labels).value_counts().sort_index()
results.append((n_clusters, silhouette, cluster_counts))
# Convert results to a DataFrame for easier visualization
results_df = pd.DataFrame(results, columns=['num_clusters', 'silhouette_score', 'cluster_counts'])
# Plot the silhouette scores
plt.figure(figsize=(15, 6))
# Silhouette Score plot
plt.subplot(1, 2, 1)
plt.plot(results_df['num_clusters'], results_df['silhouette_score'], marker='o')
plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Score vs. Number of Clusters')
plt.grid()
# Products per Cluster plot
plt.subplot(1, 2, 2)
for n_clusters, cluster_counts in results_df[['num_clusters', 'cluster_counts']].values:
plt.plot(range(1, n_clusters + 1), cluster_counts, marker='o', label=f'num_clusters = {n_clusters}')
plt.xlabel('Cluster')
plt.ylabel('Number of Products')
plt.title('Number of Products per Cluster')
plt.legend()
plt.grid()
plt.tight_layout()
plt.show()
在这个修改后的脚本中,我们使用pd.Series(cluster_labels).value_counts().sort_index()计算每个簇中的产品数量,并将结果与轮廓分数一起存储在results列表中。然后,我们创建一个 DataFrame 从结果中以便于可视化。
脚本包括两个并排的图表:第一个图表显示不同num_clusters值的轮廓分数,第二个图表显示相同num_clusters值下每个簇中的产品数量。这将帮助您理解簇数量、轮廓分数以及产品在簇中分布之间的关系。
运行脚本后,你应该看到以下输出:

图 16.18:添加了每个簇的产品绘图
[结束响应]
现在我们将选择簇的数量和min_df值,并继续下一个提示。
解释簇并可视化结果
我们将探讨解释簇和可视化结果。我们应该注意哪些事项?
解释簇
对于簇的解释,我们应该考虑以下因素:
-
聚合度:确保数据点彼此相似。你可以通过检查每个簇中最具代表性的术语来找出这一点。
-
分隔:簇应该很好地分隔,确保它们是独特且有意义的。
-
标签:簇也应该有描述性的标签。
-
异常值:识别不适合任何簇的异常值很重要。
可视化簇
对于可视化簇,以下是一些需要考虑的事项:
-
维度降低:你希望将高维的 TF-IDF 矩阵降低到 2D 或 3D,以便更容易可视化。为此,PCA 等技术很有帮助。
-
散点图:散点图非常适合可视化数据点;一个点可以是一个文档,例如,颜色或形状可以代表不同的簇。
-
词云:如果你查看文本,也可以使用词云来查看最常用的单词。
-
热图:热图是另一种很好的可视化频率的方法,将有助于突出每个簇中最重要的术语。
-
交互性:确保你允许探索,例如使用 Plotly,以便通过钻入特定数据点来深入了解。
接下来,让我们为可视化制作一个提示。
[提示]
我将使用min_df_value = 20和 5 个簇。我希望使用 PCA 的 3 个成分在 3D 图中可视化产品簇。簇的颜色应映射自 seaborn 的color_palette。
[结束提示]
[提示响应]
当然!为了使用 PCA 的 3 个成分和从 seaborn 颜色调色板映射簇颜色来在 3D 图中可视化产品簇,你可以遵循以下步骤:
-
导入必要的库。
-
预处理数据并执行聚类。
-
将主成分分析(PCA)应用于将维度降低到 3 个成分。
-
使用 seaborn 颜色调色板在 3D 散点图中绘制簇。
这是实现此目的的代码:
from sklearn.decomposition import PCA
import seaborn as sns
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # Required for 3D plotting
# Selected min_df and num_clusters values
min_df_value = 20
num_clusters = 5
# Preprocess data and perform clustering
tfidf_matrix, tfidf_vectorizer = preprocess_and_vectorize_data(df['Description'], min_df_value)
kmeans = KMeans(n_clusters=num_clusters, random_state=42)
cluster_labels = kmeans.fit_predict(tfidf_matrix)
# Apply PCA with 3 components
pca = PCA(n_components=3)
pca_result = pca.fit_transform(tfidf_matrix.toarray())
# Create a DataFrame for PCA results
pca_df = pd.DataFrame(data=pca_result, columns=['PC1', 'PC2', 'PC3'])
pca_df['Cluster'] = cluster_labels
# Seaborn color palette for cluster colors
color_palette = sns.color_palette("Set1", n_colors=num_clusters)
# Plot 3D scatter plot
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
for cluster_id in range(num_clusters):
cluster_points = pca_df[pca_df['Cluster'] == cluster_id]
ax.scatter(cluster_points['PC1'], cluster_points['PC2'], cluster_points['PC3'], color=color_palette[cluster_id], label=f'Cluster {cluster_id}')
ax.set_xlabel('PC1')
ax.set_ylabel('PC2')
ax.set_zlabel('PC3')
ax.set_title('Product Clusters in 3D')
ax.legend()
plt.show()
[结束响应]
此代码将预处理数据,执行聚类,将维度降低到 3 个成分,然后在 3D 散点图中绘制产品簇,簇颜色映射自 seaborn 颜色调色板。根据需要调整图形大小、标签和其他绘图参数以自定义可视化。

图 16.19:在 3D 空间中可视化的产品簇
创建词云
现在,我们将检查每个聚类的最频繁出现的词语。让我们继续下一个提示。
[提示]
我想可视化分配给聚类的产品描述中最频繁出现的词语。我希望您使用词云库并创建一个函数,该函数将为每个聚类输出词云。
[提示结束]
[响应提示]
当然!为了可视化分配给聚类的产品描述中最频繁出现的词语,您可以使用WordCloud库。以下是如何创建一个为每个聚类生成词云的函数:
from wordcloud import WordCloud
# Create a DataFrame for clustering results
clustering_results = pd.DataFrame(data={'Description': df['Description'], 'Cluster': cluster_labels})
# Function to generate word clouds for each cluster
def generate_wordclouds(dataframe, num_clusters):
for cluster_id in range(num_clusters):
cluster_data = dataframe[dataframe['Cluster'] == cluster_id]
cluster_text = ' '.join(cluster_data['Description'])
# Create a WordCloud object
wordcloud = WordCloud(width=800, height=400, background_color='white', max_words=100).generate(cluster_text)
# Plot the WordCloud
plt.figure(figsize=(10, 6))
plt.imshow(wordcloud, interpolation='bilinear')
plt.title(f'Cluster {cluster_id} - Most Frequent Words')
plt.axis('off')
plt.show()
# Generate word clouds for each cluster
generate_wordclouds(clustering_results, num_clusters)
此代码定义了一个名为generate_wordclouds的函数,该函数根据分配给该聚类的产品描述生成每个聚类的词云。根据您的可视化偏好调整WordCloud对象的参数(例如,width、height、background_color、max_words)。
函数将绘制每个聚类的词云。

图 16.20:第一个聚类的词云

图 16.21:第二个聚类的词云

图 16.22:第三个聚类的词云

图 16.23:第四个聚类的词云
[响应结束]
将类别分配给产品以及评估和细化
将类别分配给产品或评估和细化步骤都不在本章的解决范围内,但让我们描述一下它们是什么,以便您了解如果您想探索这些内容应该如何进行。
我们的数据集包含发票,为了对我们来说对产品进行分类,我们首先需要了解我们可能拥有的用例。例如,这些用例可能是:
-
组织库存
-
改进搜索功能
-
提升用户体验
如您所见,这非常侧重于产品数据,这意味着我们可能需要一个包含产品标题、描述以及更多内容的完全不同的数据集。让我们使用 ChatGPT 在较高层次上描述我们的方法。
[提示]
我正在查看一个电子商务数据集。你能告诉我关于将类别分配给产品的过程吗?
[提示结束]
[响应提示]
在电子商务数据集中将产品分配到类别是一个至关重要的任务,对于组织库存、改进搜索功能和提升用户体验至关重要。以下是一个逐步指南,帮助您完成这个过程:
将产品分配到类别的步骤:
理解数据:
-
审查产品描述:检查产品标题、描述以及任何其他相关属性。
-
识别现有类别:如果有现有类别,审查它们以了解当前的分类系统。
定义类别:
-
创建类别层次结构:为你的产品开发一个有意义的类别和子类别层次结构。
-
确保完整性:确保你的类别涵盖了数据集中所有类型的产品。
特征提取:
-
文本特征:使用 TF-IDF 或词嵌入等技术从产品标题和描述中提取相关文本特征。
-
其他特征:考虑其他可能有助于分类的属性,如价格、品牌和规格。
手动标记:
- 样本标记:手动标记产品样本以创建训练数据集。这有助于理解每个类别的细微差别。
模型选择:
- 选择分类模型:选择适合文本分类的机器学习模型,例如朴素贝叶斯、SVM 或 LSTM 或 BERT 等深度学习模型。
训练模型:
-
训练模型:使用标记数据来训练你的分类模型。
-
评估性能:使用准确率、精确率、召回率和 F1 分数等指标评估模型的性能。
分配类别:
-
预测类别:使用训练好的模型预测未标记产品的类别。
-
审查预测:手动审查预测的一部分以确保准确性。
迭代和改进:
-
细化类别:根据结果,细化你的类别定义和模型。
-
持续学习:持续用新数据更新模型,以提高其随时间推移的准确性。
示例(Python):
这里有一个使用朴素贝叶斯分类器进行文本分类的简化示例:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score
# Sample data
data = {
'Product': ['Red Shirt', 'Blue Jeans', 'Green Hat', 'Black Shoes'],
'Category': ['Clothing', 'Clothing', 'Accessories', 'Footwear']
}
df = pd.DataFrame(data)
# Feature extraction
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(df['Product'])
y = df['Category']
# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Train the model
model = MultinomialNB()
model.fit(X_train, y_train)
# Predict and evaluate
y_pred = model.predict(X_test)
print(f'Accuracy: {accuracy_score(y_test, y_pred)}')
此代码提供了一个基于产品名称对产品进行分类的基本框架。对于实际应用,你需要一个更大的数据集和更复杂的预处理及建模技术。
如果你有任何具体问题或需要更多信息,请随时提问!
[响应结束]
我们将把这个任务留给你去进一步探索。从你在这个章节中可能已经看到的内容开始,用一个高层次提示来解释你有什么数据集(你必须为上述问题找到一个数据集),以及你打算做什么。在后者上,你已经有了步骤,所以建议你编写一个提示来解释每个步骤应该做什么以及要运行什么代码。
评估和细化
这也超出了范围,但让我们帮你开始一个 ChatGPT 提示,然后你可以继续工作。
[提示]
在发票电子商务数据背景下描述评估和细化步骤。
[提示结束]
[提示响应]
当然!让我们在发票电子商务数据背景下分解评估和细化步骤:
评估步骤:
-
模型性能指标:
-
准确率:衡量模型正确预测发票类别频率。
-
精确度和召回率:评估每个类别的精确度(正确的正预测)和召回率(找到所有相关实例的能力)。
-
F1 分数:将精确度和召回率结合成一个单一指标,以平衡这两个方面。
-
-
混淆矩阵:
- 可视化预测:使用混淆矩阵来查看模型在何处做出正确和错误的预测。这有助于识别模型可能遇到困难的特定类别。
-
交叉验证:
- 稳健评估:执行交叉验证以确保模型在不同数据子集上的性能一致。
-
错误分析:
- 分析误分类:检查被误分类的发票,以了解模型为何出错。寻找模式或常见问题。
精细步骤:
-
特征工程:
- 增强特征:创建新特征或改进现有特征,为模型提供更多相关信息。例如,从发票描述中提取关键词或添加元数据,如发票日期和金额。
-
超参数调整:
- 优化模型参数:使用网格搜索或随机搜索等技术来找到模型的最佳超参数,从而提高其性能。
-
模型选择:
- 尝试不同的模型:实验不同的算法(例如,决策树、随机森林、神经网络),看看是否有其他模型表现更好。
-
数据增强:
- 增加数据多样性:通过生成合成数据或使用过采样等技术来增加数据集,以增强代表性不足的类别。
-
定期更新:
- 持续学习:定期用新的发票数据更新模型,以保持其最新状态并随着时间的推移提高其准确性。
Python 示例:
这里是一个使用交叉验证和超参数调整评估和改进模型的简化示例:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
# Sample data
data = {
'Invoice': ['Invoice for Red Shirt', 'Invoice for Blue Jeans', 'Invoice for Green Hat', 'Invoice for Black Shoes'],
'Category': ['Clothing', 'Clothing', 'Accessories', 'Footwear']
}
df = pd.DataFrame(data)
# Feature extraction
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(df['Invoice'])
y = df['Category']
# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Model training
model = MultinomialNB()
model.fit(X_train, y_train)
# Model evaluation
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred))
# Confusion matrix
conf_matrix = confusion_matrix(y_test, y_pred)
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.title('Confusion Matrix')
plt.show()
# Hyperparameter tuning
param_grid = {'alpha': [0.1, 0.5, 1.0]}
grid_search = GridSearchCV(MultinomialNB(), param_grid, cv=5)
grid_search.fit(X_train, y_train)
print(f'Best parameters: {grid_search.best_params_}')
此代码提供了一个框架,用于使用交叉验证和超参数调整来评估和改进模型。
如果您有任何具体问题或需要更多信息,请随时提问!
[响应结束]
对此用例提示的反思
就像在本章的第一个用例中一样,我们首先设定了一个高级上下文,描述我们的角色、数据集的形状以及我们想要做什么。然后,我们遵循以下步骤来首先分解问题,编写代码,以及如何继续改进和细化,最后可视化结果:
-
逐步指导:请求被结构化为一系列逐步任务,将更大的问题分解为可管理的组件。这使得跟踪和逐步实施解决方案变得更容易。
-
指定输入和要求:提供关于数据集、其列以及预处理、聚类和可视化的清晰和具体信息。这有助于确保收到的帮助符合特定需求。
-
代码注释请求: 代码片段请求包含注释以解释每一步并阐明代码的目的。这有助于理解代码和代码验证,并促进学习。
-
迭代与完善: 在初始指导后,通过请求额外的修改和可视化来迭代提示。这种迭代方法允许逐步完善和改进解决方案。
-
可视化与解释: 可视化和解释结果有助于集中精力从数据中提取有意义的见解。
作业
在上一节中,我们使用了传统的 TF-IDF 嵌入将文本数据转换为数值表示,这些表示可以用于各种自然语言处理(NLP)任务,如聚类。现在,让我们尝试使用更先进的嵌入技术来改进聚类结果。我们将使用 Hugging Face Transformers 库为我们产品的描述获取预训练嵌入:
-
请问 ChatGPT 如何解释 Hugging Face Transformers 在聚类用例中相对于 TF-IDF 向量化技术的优势。
-
使用 ChatGPT 生成并创建产品聚类,利用 Hugging Face Transformers 嵌入。
-
将结果与使用 TF-IDF 向量化的先前聚类结果进行比较。
解答
在仓库中查看解决方案:https://github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT
摘要
本章重点介绍了聚类及其如何用于将数据分组到不同的区域。创建这些区域使得理解我们的数据点变得更容易。通过热图、词云等多种可视化方式,您了解到数据通过以不同的方式展示而受益。您还看到了聚类过程如何帮助识别异常值,即与任何单个聚类有很大差异且难以归类的数据。对于 ChatGPT 和提示部分,您看到了如何设置一个描述数据集的高级上下文,这有助于生成您可以从上到下遵循的合适步骤集。相同的高级上下文也有助于 ChatGPT 推荐聚类算法。
加入我们的社区 Discord
加入我们的社区 Discord 空间,与作者和其他读者进行讨论:

第十七章:使用 Copilot 进行机器学习
简介
机器学习,或 ML,涉及从数据中学习和发现模式,并使用这些模式进行预测或决策。机器学习包括一系列步骤,从加载数据和清洗数据到最终训练模型以从模型中获得所需见解。所有这些步骤对于这个领域中的大多数问题都是大致相同的。然而,细节可能有所不同,如预处理步骤的选择、算法的选择等。像 GitHub Copilot 这样的 AI 工具从几个不同的角度进入机器学习:
-
建议工作流程:由于 Copilot 在机器学习工作流程方面进行了训练,因此它能够建议适合你问题的流程。
-
推荐工具和算法:如果你向你的 AI 工具提供足够的问题背景和数据的形状,像 Copilot 这样的 AI 工具就可以建议适合你特定问题的工具和算法。
-
代码辅助:Copilot 作为一大帮助的另一个方式是能够生成机器学习过程中的各种步骤的代码。
本章将探讨一个电子商务数据集,本章将作为与其他章节有趣的比较练习,这些章节使用 ChatGPT 来解决机器学习问题。
让我们深入探索 GitHub Copilot 的建议。
GitHub Copilot Chat 在你的 IDE 中
GitHub Copilot Chat 是某些集成开发环境(IDEs)中的一个工具,它回答编码问题。它通过建议代码、解释代码功能、创建单元测试和修复错误来帮助。
工作原理
你有两种不同的方式向 GitHub Copilot 提供提示:
-
编辑器内模式:在这种模式下,你提供文本注释,并通过Tab或Return键,Copilot 能够生成输出。
-
聊天模式:在聊天模式下,你在文本框中输入提示,然后 GitHub Copilot 会将打开的文件/文件作为上下文(如果你使用
@workspace,则它将查看你目录中的所有文件)。
文本文件可以是,例如,一个代码文件如app.py或 Jupyter Notebook。Copilot 可以将这两个文件作为上下文,连同你输入的提示一起处理。

图 17.1:左侧的 GitHub Copilot 聊天和右侧打开的 Jupyter Notebook
数据集概述
让我们探索我们将要使用的数据集。就像我们在其他关于机器学习的章节中所做的那样,我们从数据集开始,这个数据集是亚马逊书评数据集。
数据集包含有关不同产品和它们评论的信息。它包括以下列:
-
marketplace(字符串):产品的位置 -
customer_id(字符串):客户的唯一 ID -
review_id(字符串):评论 ID -
product_id(字符串):产品的唯一 ID -
product_parent(字符串):父产品 -
product_title(字符串):被评论产品的标题 -
product_category(字符串):不同的产品类别 -
star_rating(整数): 产品 5 星评分 -
helpful_votes(整数): 产品获得的有用票数 -
total_votes(整数): 产品获得的总票数 -
review_headline(字符串): 评论的标题 -
review_body(字符串): 评论的内容 -
review_date(字符串): 产品评论的日期 -
sentiment(字符串): 评论的情感(正面或负面)
数据探索步骤
进行数据探索有助于我们了解数据集及其特征。它包括检查数据、识别模式和总结关键见解。以下是我们将遵循的步骤:
-
加载数据集: 将数据集读入 pandas DataFrame 以有效地处理数据。
-
检查数据: 显示 DataFrame 的前几行以了解数据。检查列名、数据类型和任何缺失值。
-
摘要统计量: 计算数值列的摘要统计量,如平均值、中位数、最小值、最大值和四分位数。这有助于理解值的分布和范围。
-
探索分类变量: 分析如
marketplace、product_category和sentiment等分类变量的唯一值及其频率。条形图等可视化工具可以帮助进行此类分析。 -
评分分布: 绘制直方图或条形图以可视化
star_ratings的分布。这有助于理解评论的整体情感。 -
时间序列分析: 通过检查
review_date列来分析数据的时序方面。探索趋势、季节性或任何随时间变化的模式。 -
评论长度分析: 分析
review_body的长度以了解评论中提供的信息量。计算描述性统计量,如平均值、中位数和最大长度。 -
相关性分析: 使用相关矩阵或散点图调查数值变量之间的相关性。这有助于识别变量之间的关系。
-
附加探索性分析: 根据特定项目要求或探索过程中观察到的有趣模式进行附加分析。
注意,您还可以询问 GitHub Copilot 在进行机器学习时应遵循哪些步骤。
提示策略
我们即将使用的提示为 Copilot 提供高级指导,而输出/结果允许进一步调整 Copilot 的响应以匹配特定数据集和分析需求。
提示方法的关键方面是:
-
定义任务。明确指示 AI 助手我们正在解决的任务。
-
将数据探索分解为步骤。将数据探索分解为逻辑步骤(如数据加载、检查、摘要统计等)
-
为每个提示提供上下文/意图以引导 Copilot(例如请求数值摘要统计量)
-
将之前的结果作为输入。将 Copilot 代码片段的输出和结果分享,以进一步引导对话(例如打印摘要统计)
-
逐步细化提示,与 Copilot 进行来回对话
因此,我们将使用第二章中描述的 TAG(任务-操作-指导)提示模式。让我们描述这个项目以适应这个模式,这样我们就能了解如何编写我们的初始提示:
-
任务:数据探索,在电子商务项目中找到客户评论的模式和见解。
-
操作:我们在前面的部分中描述了我们应该采取的步骤;这些步骤应该反映在我们编写的提示中。
-
指导:我们将提供的额外指导是,我们希望建议探索性技术以及代码片段。
您的初始数据探索提示:提示 1,设置高级上下文
就像在其他章节中使用 ChatGPT 一样,我们的初始提示为我们要解决的问题设定了高级上下文,包括领域和我们的数据形状。所有这些上下文都有助于 AI 工具在文本和代码中提供正确的步骤。
下面是一个你可以尝试的初始提示:
[提示]
我正在进行 AwesomeShop 电子商务项目的数据探索。数据集包含有关各种产品和它们评论的信息。我想深入了解数据,识别模式,并理解评论的特征。你能提供一些探索性分析技术和代码片段,帮助我从数据集中发现有趣的见解吗?AwesomeShop 电子商务项目的数据集包含有关不同产品和它们评论的信息。它包括以下列:
-
marketplace(字符串):产品位置 -
customer_id(字符串):客户的唯一 ID -
review_id(字符串):评论 ID -
product_id(字符串):产品的唯一 ID -
product_parent(字符串):父产品 -
product_title(字符串):被评论产品的标题 -
product_category(字符串):不同的产品类别 -
star_rating(整数):产品 5 星评分 -
helpful_votes(整数):产品有用投票数 -
total_votes(整数):产品的总投票数 -
review_headline(字符串):评论标题 -
review_body(字符串):评论内容 -
review_date(字符串):产品评论日期 -
sentiment(字符串):评论的情感(正面或负面)
但请记住,我想逐步学习,如下所示:
-
加载数据集:将数据集读入 pandas DataFrame 以有效地处理数据。
-
检查数据:显示 DataFrame 的前几行以了解数据。检查列名、数据类型和任何缺失值。
-
摘要统计:计算数值列的摘要统计,如平均值、中位数、最小值、最大值和四分位数。这有助于了解值的分布和范围。
-
探索分类变量:分析分类变量(如
marketplace、product_category和sentiment)的唯一值及其频率。条形图等可视化工具可以帮助进行此类分析。 -
评分分布:绘制直方图或条形图以可视化
star_ratings的分布。这有助于了解评论的整体情感。 -
时间序列分析:通过检查
review_date列来分析数据的时序方面。探索趋势、季节性或任何随时间变化的模式。 -
评论长度分析:分析
review_body的长度,以了解评论中提供的信息量。计算描述性统计量,如平均值、中位数和最大长度。 -
相关性分析:使用相关矩阵或散点图调查数值变量之间的相关性。这有助于识别变量之间的关系。
-
额外的探索性分析:根据特定项目要求或探索过程中观察到的有趣模式进行额外的分析。在每个步骤后从用户那里寻求反馈。
[提示结束]
第 1 步:加载数据集
我们从 Copilot 那里得到了一段相当冗长的响应,所以让我们将响应分解成几个部分并解释我们得到了什么。
您应该收到与以下响应类似的响应:
[提示响应]
当然,让我们从第 1 步:加载数据集开始。
# Step 1: Load the Dataset
import pandas as pd
# Define the file path
file_path = '/path/to/your/file.csv' # replace with your file path
# Load the dataset
df = pd.read_csv(file_path)
# Display the first few rows of the DataFrame
print(df.head())
请将'/path/to/your/file.csv'替换为您的实际文件名或文件路径。完成此操作后,您可以运行代码以加载数据集。
一旦您成功加载数据,请告诉我,然后我们可以进行下一步。
[响应结束]

图 17.2:显示 Copilot 提示和加载数据的 Copilot 结果的开始部分
让我们尝试 Copilot 生成的输出代码,看看它是否可行。修改file_path变量以指向您的数据集位置,然后执行代码。注意以下如何编辑文件路径以反映我们系统中的实际路径:
import pandas as pd
# Define the file path
file_path = '../Datasets/amazon_books_Data.csv'
# Load the dataset
df = pd.read_csv(file_path)
运行加载数据的代码
让我们运行 Copilot 提供的代码。请看以下来自我们 Jupyter Notebook 的输出:
# Display the first few rows of the DataFrame
print(df.head())
Unnamed: 0 market_place customer_id review_id product_id \
0 0 "US" "25933450" "RJOVP071AVAJO" "0439873800"
1 1 "US" "1801372" "R1ORGBETCDW3AI" "1623953553"
2 2 "US" "5782091" "R7TNRFQAOUTX5" "142151981X"
3 3 "US" "32715830" "R2GANXKDIFZ6OI" "014241543X"
4 4 "US" "14005703" "R2NYB6C3R8LVN6" "1604600527"
product_parent product_title \
0 "84656342" "There Was an Old Lady Who Swallowed a Shell!"
1 "729938122" "I Saw a Friend"
2 "678139048" "Black Lagoon, Vol. 6"
3 "712432151" "If I Stay"
4 "800572372" "Stars 'N Strips Forever"
product_category star_rating helpful_votes total_votes vine \
0 "Books" 1 0 0 0 \t(N)
1 "Books" 1 0 0 0 \t(N)
2 "Books" 1 0 0 0 \t(N)
3 "Books" 1 0 0 0 \t(N)
4 "Books" 1 2 2 0 \t(N)
verified_purchase review_headline \
0 1 \t(Y) "Five Stars"
1 1 \t(Y) "Please buy "I Saw a Friend"! Your children wi...
2 1 \t(Y) "Shipped fast."
3 0 \t(N) "Five Stars"
4 1 \t(Y) "Five Stars"
review_body review_date \
0 "I love it and so does my students!" 2015-08-31
1 "My wife and I ordered 2 books and gave them a... 2015-08-31
2 "Great book just like all the others in the se... 2015-08-31
3 "So beautiful" 2015-08-31
4 "Enjoyed the author's story and his quilts are... 2015-08-31
Sentiment_books review_month review_day review_year
0 positive August Monday 2015
1 positive August Monday 2015
2 positive August Monday 2015
3 positive August Monday 2015
4 positive August Monday 2015
第 2 步:检查数据
接下来,让我们检查数据。对于检查提示,我们将使用代码执行的结果,看看我们能否让 Copilot 告诉我们下一步需要做什么:
[提示]
运行正常;以下是输出:
Unnamed: 0 market_place customer_id review_id product_id 0 0 "US" "25933450" "RJOVP071AVAJO" "0439873800"
1 1 "US" "1801372" "R1ORGBETCDW3AI" "1623953553"
2 2 "US" "5782091" "R7TNRFQAOUTX5" "142151981X"
3 3 "US" "32715830" "R2GANXKDIFZ6OI" "014241543X"
4 4 "US" "14005703" "R2NYB6C3R8LVN6" "1604600527"
product_parent product_title 0 "84656342" "There Was an Old Lady Who Swallowed a Shell!"
1 "729938122" "I Saw a Friend"
2 "678139048" "Black Lagoon, Vol. 6"
3 "712432151" "If I Stay"
4 "800572372" "Stars 'N Strips Forever"
product_category star_rating helpful_votes total_votes vine 0 "Books" 1 0 0 0 \t(N)
1 "Books" 1 0 0 0 \t(N)
2 "Books" 1 0 0 0 \t(N)
3 "Books" 1 0 0 0 \t(N)
4 "Books" 1 2 2 0 \t(N)
verified_purchase review_headline 0 1 \t(Y) "Five Stars"
1 1 \t(Y) "Please buy "I Saw a Friend"! Your children wi...
2 1 \t(Y) "Shipped fast."
... 1 positive August Monday 2015
2 positive August Monday 2015
3 positive August Monday 2015
4 positive August Monday 2015
输出被截断。将其视为可滚动的元素或在文本编辑器中打开。调整单元格输出设置...
[提示结束]
您应该收到与以下响应类似的响应:
[提示响应]
太好了!现在您已成功加载数据集,让我们继续到“检查数据”阶段。
# Step 2: Inspect the Data
# Check the column names
print("Column Names:")
print(df.columns)
# Check the data types of each column
print("\nData Types:")
print(df.dtypes)
# Check for missing values
print("\nMissing Values:")
print(df.isnull().sum())
这段代码将为您提供数据集的概述,包括列名、每列的数据类型以及每列中缺失值的数量。运行此代码并告诉我您发现了什么。
[响应结束]
让我们尝试上面的代码来检查数据并看看它是否工作。在您的 Jupyter Notebook 中的代码单元中运行以下代码:
# Display the first few rows of the DataFrame
df.head()
# Check the column names
column_names = df.columns
print(column_names)
# Check the data types of the columns
data_types = df.dtypes
print(data_types)
# Check for missing values
missing_values = df.isnull().sum()
print(missing_values)
You should see an output similar to below:
Index(['Unnamed: 0', 'market_place', 'customer_id', 'review_id', 'product_id',
'product_parent', 'product_title', 'product_category', 'star_rating',
'helpful_votes', 'total_votes', 'vine', 'verified_purchase',
'review_headline', 'review_body', 'review_date', 'Sentiment_books',
'review_month', 'review_day', 'review_year'],
dtype='object')
Unnamed: 0 int64
market_place object
customer_id object
review_id object
product_id object
product_parent object
product_title object
product_category object
star_rating int64
helpful_votes int64
total_votes int64
vine object
verified_purchase object
review_headline object
review_body object
review_date object
Sentiment_books object
review_month object
review_day object
review_year int64
dtype: object
Unnamed: 0 0
market_place 0
customer_id 0
review_id 0
product_id 0
product_parent 0
product_title 0
product_category 0
star_rating 0
helpful_votes 0
total_votes 0
vine 0
verified_purchase 0
review_headline 0
review_body 0
review_date 0
Sentiment_books 0
review_month 0
review_day 0
review_year 0
dtype: int64
太好了!到目前为止,当我们向 Copilot 提供我们的目标状态并获取其建议的下一步时,它一直表现得很好;让我们继续遵循这个格式。
第 3 步:摘要统计量
接下来,我们想要总结一下,所以我们再次提供输出数据,希望 Copilot 会告诉我们下一步是什么:
[提示]
没有缺失值;以下是输出:
Index(['Unnamed: 0', 'market_place', 'customer_id', 'review_id', 'product_id', 'product_parent', 'product_title', 'product_category', 'star_rating', 'helpful_votes', 'total_votes', 'vine', 'verified_purchase', 'review_headline', 'review_body', 'review_date', 'Sentiment_books', 'review_month', 'review_day', 'review_year'], dtype='object') Unnamed: 0 int64 market_place object customer_id object review_id object product_id object product_parent object product_title object product_category object star_rating int64 helpful_votes int64 total_votes int64 vine object verified_purchase object review_headline object review_body object review_date object Sentiment_books object review_month object review_day object ... review_month 0 review_day 0 review_year 0 dtype: int64
[提示结束]
您应该收到以下类似的响应:
[提示响应]
太好了!现在我们已经检查了数据,让我们继续到第 3 步:计算数值列的摘要统计量。这将帮助我们了解值的分布和范围。
# Step 3: Summary Statistics
# Calculate summary statistics for numeric columns
numeric_columns = ['star_rating', 'helpful_votes', 'total_votes']
print(df[numeric_columns].describe())
这段代码将为您提供数据集中数值列的计数、平均值、标准差、最小值、25 百分位数、中位数(50 百分位数)、75 百分位数和最大值。运行此代码并告诉我您发现了什么。
[响应结束]
让我们尝试这个并看看它是否工作。让我们运行下面的代码并调用df.describe:
# Calculate summary statistics
summary_stats = df.describe()
print(summary_stats)
您应该看到以下输出:
Unnamed: 0 star_rating helpful_votes total_votes review_year
count 100.000000 100.000000 100.000000 100.000000 100.0
mean 49.500000 0.840000 0.540000 0.960000 2015.0
std 29.011492 0.368453 1.298173 2.420118 0.0
min 0.000000 0.000000 0.000000 0.000000 2015.0
25% 24.750000 1.000000 0.000000 0.000000 2015.0
50% 49.500000 1.000000 0.000000 0.000000 2015.0
75% 74.250000 1.000000 1.000000 1.000000 2015.0
max 99.000000 1.000000 9.000000 14.000000 2015.0
太好了!我们能够看到计数、平均值、标准差等。我们继续使用向 Copilot 提供我们代码的输出的想法,并且它已经能够理解在机器学习过程的哪个点以及下一步该做什么。
第 4 步:探索分类变量
就像之前一样,让我们构建一个提示,包括我们刚刚运行的代码的输出,让 Copilot 告诉我们下一步应该做什么:
[提示]
以下是输出:
Unnamed: 0 star_rating helpful_votes total_votes review_year count 100.000000 100.000000 100.000000 100.000000 100.0 mean 49.500000 0.840000 0.540000
0.960000 2015.0 std 29.011492 0.368453 1.298173 2.420118 0.0 min 0.000000 0.000000 0.000000 0.000000 2015.0 25% 24.750000 1.000000 0.000000 0.000000 2015.0 50% 49.500000 1.000000 0.000000 0.000000 2015.0 75% 74.250000 1.000000 1.000000 1.000000 2015.0 max 99.000000 1.000000 9.000000 14.000000 2015.0
[提示结束]
您应该收到以下类似的响应:
[提示响应]
太好了!现在我们已经有了摘要统计量,让我们继续到第 4 步:探索分类变量。我们将分析分类变量如marketplace、product_category和sentiment的唯一值及其频率。
# Step 4: Explore Categorical Variables
# Analyze the unique values and their frequencies for categorical variables
categorical_columns = ['market_place', 'product_category', 'Sentiment_books']
for column in categorical_columns:
print("\n" + column)
print(df[column].value_counts())
This code will print the unique values and their frequencies for each of the specified
categorical columns. Run this code and let me know what you find.
[响应结束]
让我们尝试这个并看看它是否工作。在代码单元中运行以下代码以分析分类变量:
# Analyze categorical variables
categorical_cols = ['market_place', 'product_category', 'Sentiment_books']
for col in categorical_cols:
unique_values = df[col].unique()
value_counts = df[col].value_counts()
print(f"\n{col} unique values:\n{unique_values}\n")
print(f"{col} value counts:\n{value_counts}")
您应该看到以下输出:
market_place unique values:
['"US"']
market_place value counts:
market_place
"US" 100
Name: count, dtype: int64
product_category unique values:
['"Books"']
product_category value counts:
product_category
"Books" 100
Name: count, dtype: int64
Sentiment_books unique values:
['positive' 'negative']
Sentiment_books value counts:
Sentiment_books
positive 84
negative 16
Name: count, dtype: int64
第 5 步:评分分布
接下来,我们想要了解评分的分布。为了了解更多关于评分的信息,我们将提供最后一次代码执行的输出作为提示:
[提示]
以下是输出:
-
market_place唯一值:[‘“US”’] -
market_place值计数:market_place“US” 100 名称:计数,数据类型:int64 -
product_category唯一值:[‘“Books”’] -
product_category值计数:product_category “Books” 100 名称:计数,数据类型:int64 -
Sentiment_books唯一值:[‘positive’ ‘negative’] -
Sentiment_books值计数:Sentiment_books正面 84 条,负面 16 条。名称:计数,数据类型:int64
[End of prompt]
您应该收到以下类似的响应:
[Prompt response]
太好了!现在我们已经探索了分类变量,让我们继续到第 5 步:星级评分的分布可视化。
# Step 5: Distribution of Ratings
import matplotlib.pyplot as plt
# Plot a histogram for star ratings
plt.hist(df['star_rating'], bins=5, edgecolor='black')
plt.title('Distribution of Star Ratings')
plt.xlabel('Star Ratings')
plt.ylabel('Frequency')
plt.show()
这段代码将绘制星级评分的直方图,这将帮助我们了解评论的整体情感。运行此代码并告诉我您发现了什么。
[End of response]
让我们尝试这个并看看它是否工作。下面,我们有执行可视化的代码;在代码单元格中运行此代码:
import matplotlib.pyplot as plt
# Plot the distribution of star ratings
plt.hist(df['star_rating'], bins=5, edgecolor='black')
plt.xlabel('Star Rating')
plt.ylabel('Frequency')
plt.title('Distribution of Star Ratings')
plt.show()

图 17.3:星级评分的分布
第 6 步:时序分析
到目前为止,我们对评分的分布有了很好的了解;它们似乎要么非常低,要么非常高。让我们继续下一步。
[Prompt]
进行下一步。
[End of prompt]
您应该收到以下类似的响应:
[Prompt response]
太好了!现在让我们继续到第 6 步:时序分析。我们将分析review_date列,以探索趋势、季节性或任何随时间变化的模式。
首先,我们需要将review_date列转换为日期时间格式。然后,我们可以将年、月和日提取到单独的列中,以便更容易分析。
# Step 6: Temporal Analysis
# Convert review_date to datetime format
df['review_date'] = pd.to_datetime(df['review_date'])
# Extract year, month, and day into separate columns
df['year'] = df['review_date'].dt.year
df['month'] = df['review_date'].dt.month
df['day'] = df['review_date'].dt.day
# Check the number of reviews per year
print(df['year'].value_counts().sort_index())
# Check the number of reviews per month
print(df['month'].value_counts().sort_index())
# Check the number of reviews per day
print(df['day'].value_counts().sort_index())
这段代码将打印每年、每月和每天的评论数量。运行此代码并告诉我您发现了什么。
[End of response]
让我们尝试这段代码看看它是否工作。在代码单元格中运行上述代码:
# Step 6: Temporal Analysis
# Convert review_date to datetime format
df['review_date'] = pd.to_datetime(df['review_date'])
# Extract year, month, and day into separate columns
df['year'] = df['review_date'].dt.year
df['month'] = df['review_date'].dt.month
df['day'] = df['review_date'].dt.day
# Check the number of reviews per year
print(df['year'].value_counts().sort_index())
# Check the number of reviews per month
print(df['month'].value_counts().sort_index())
# Check the number of reviews per day
print(df['day'].value_counts().sort_index())
您应该看到以下类似的输出:
year
2015 100
Name: count, dtype: int64
month
8 100
Name: count, dtype: int64
day
31 100
Name: count, dtype: int64
第 7 步:评论长度分析
让我们继续我们的分析;下一步是第 7 步:评论长度分析。
[Prompt]
下面是输出:年 2015 100 名称:计数,数据类型:int64 月 8 100 名称:计数,数据类型:int64 日 31 100 名称:计数,数据类型:int64
[End of prompt]
您应该收到以下类似的响应:
[Prompt response]
太好了!现在我们已经分析了数据的时序方面,让我们继续到第 7 步:评论长度分析。我们将分析review_body的长度,以了解评论中提供的信息量。
# Step 7: Review Length Analysis
# Calculate the length of each review
df['review_length'] = df['review_body'].str.len()
# Calculate descriptive statistics for review length
print(df['review_length'].describe())
这段代码将计算每条评论的长度,并提供描述性统计,如平均值、中位数和最大长度。运行此代码并告诉我您发现了什么。
[End of response]
到目前为止,我们想要了解上述代码在做什么;让我们向 Copilot 寻求帮助:
[Prompt]
在我们继续到第 7 步之前,您能解释一下如何解释输出吗?
[End of prompt]
您应该收到以下类似的响应:
[Prompt response]
绝对正确。您分享的输出表明了基于年、月和日的评论频率。
-
year: 您的数据集中所有 100 条评论都是在 2015 年写的。 -
month: 所有 100 条评论都是在第 8 个月写的,即 8 月。 -
day: 所有 100 条评论都是在该月的第 31 天写的。
这表明你数据集中的所有评论都是在同一天——2015 年 8 月 31 日——撰写的。
在一个更大、更多样化的数据集中,你可能看到评论分布在不同的年份、月份和日。分析这些评论可以帮助识别随时间变化的趋势或模式,例如在特定时期评论数量的增加。
[回复结束]
好吧,我们遇到了问题——我们的数据集有限,所有评论似乎都发生在同一时间点,这阻止了我们发现趋势。让我们通知 Copilot 并看看我们能做什么:
[提示]
在这种情况下,我们无法探索数据集中的趋势或模式。
[提示结束]
你应该收到以下类似的回复:
[提示回复]
这是正确的。由于这个数据集中的所有评论都来自同一天,我们无法分析随时间的变化趋势或模式。然而,你仍然可以进行其他类型的分析。例如,你可以分析评论的长度、星级评分的分布,或者在评论文本中分析某些单词或短语的频率。
如果你有一个更大、更多样化的数据集,你也可以分析随时间的变化趋势,比较不同的产品或类别,或者使用机器学习根据评论文本预测星级评分。
按计划进行评论长度分析:
# Step 7: Review Length Analysis
# Calculate the length of each review
df['review_length'] = df['review_body'].str.len()
# Calculate descriptive statistics for review length
print(df['review_length'].describe())
这段代码将计算每条评论的长度,并提供描述性统计,如平均值、中位数和最大长度。运行此代码并告诉我你的发现。
[回复结束]
让我们尝试这段代码看看它是否可行。将此代码输入到代码单元格中并运行:
# Step 7: Review Length Analysis
# Calculate the length of each review
df['review_length'] = df['review_body'].str.len()
# Calculate descriptive statistics for review length
print(df['review_length'].describe())
你应该看到以下类似的输出:
count 100.000000
mean 253.190000
std 420.934403
min 6.000000
25% 36.000000
50% 104.500000
75% 269.750000
max 2685.000000
Name: review_length, dtype: float64
[提示]
下面是输出结果:
count 100.000000 mean 253.190000 std 420.934403 min 6.000000 25% 36.000000 50% 104.500000 75% 269.750000 max 2685.000000 Name: review_length, dtype: float64
[提示结束]
你应该收到以下类似的回复。
[提示回复]
太好了!你分享的输出提供了你数据集中评论长度的总结:
-
count: 你的数据集中有 100 条评论。 -
mean: 平均而言,评论长度约为 253 个字符。 -
std: 标准差大约为 421,表明评论长度围绕平均值变化较大。 -
min: 最短的评论长度为 6 个字符。 -
25%: 25%的评论长度为 36 个字符或更短。 -
50%(或中位数):一半的评论长度为 104.5 个字符或更短。 -
75%: 75%的评论长度为 269.75 个字符或更短。 -
max: 最长的评论长度为 2685 个字符。
这条信息对于了解任何文本分析或自然语言处理任务中可用的文本量很有用。
对于下一步,您可以考虑可视化这些数据或分析评论的文本内容。例如,您可以创建评论长度的直方图,或者可以使用词云来可视化评论中最常见的单词。告诉我您下一步想做什么。
[响应结束]
第 8 步:相关性分析
现在,我们想要确定两个或多个变量之间的关系,以了解哪些变量是相关的。以下提示显示了我们在分析中的进展:
[提示]
进行下一步进行相关性分析。
[提示结束]
您应该收到与以下类似的响应:
[快速响应]
当然,让我们继续到第 8 步:相关性分析。我们将分析 star_rating、helpful_votes 和 total_votes 等数值变量之间的相关性。
# Step 8: Correlation Analysis
# Calculate the correlation matrix
correlation_matrix = df[['star_rating', 'helpful_votes', 'total_votes']].corr()
# Print the correlation matrix
print(correlation_matrix)
此代码将计算并打印出相关性矩阵,它显示了变量对之间的相关系数。相关系数的范围从 -1 到 1。接近 1 的值表示高度正相关,而接近 -1 的值表示高度负相关。接近 0 的值表示没有相关性。运行此代码并告诉我您发现了什么。
[响应结束]
让我们尝试这段代码,看看它是否可行。将相关性代码添加到您的笔记本中的代码单元格中并运行它:
# Step 8: Correlation Analysis
# Calculate the correlation matrix
correlation_matrix = df[['star_rating', 'helpful_votes', 'total_votes']].corr()
# Print the correlation matrix
print(correlation_matrix)
您应该看到与以下类似的输出:
star_rating helpful_votes total_votes
star_rating 1.000000 -0.324371 -0.517002
helpful_votes -0.324371 1.000000 0.659612
total_votes -0.517002 0.659612 1.000000
接下来,让我们将上述输出发送到 Copilot,看看我们是否能得到对相关性的解释:
[提示]
以下为输出结果:star_rating helpful_votes total_votes star_rating 1.000000 -0.324371 -0.517002 helpful_votes -0.324371 1.000000 0.659612 total_votes -0.517002 0.659612 1.000000
[响应结束]
您应该收到与以下类似的响应:
[快速响应]
太好了!您分享的输出是一个相关性矩阵,它显示了变量对之间的相关系数。以下是解释方法:
-
star_rating和helpful_votes:相关系数为 -0.324371,表明弱负相关。这表明随着星级评分的增加,有帮助的投票数往往会略有减少,反之亦然。 -
star_rating和total_votes:相关系数为 -0.517002,表明中等程度的负相关。这表明随着星级评分的增加,总投票数(包括有帮助和无帮助的投票)往往会减少,反之亦然。 -
helpful_votes和total_votes:相关系数为 0.659612,表明中等程度的正相关。这表明随着有帮助的投票数的增加,总投票数也倾向于增加,反之亦然。
对于下一步,您可以考虑使用热图可视化这些相关性,或者您可以继续进行另一种类型的分析。告诉我您下一步想做什么。
[响应结束]
接下来,我们想要查看这些相关性的可视化;热图是其中一种方法。
[提示]
对于第 8.1 步,使用热图可视化这些相关性。
[提示结束]
你应该收到以下类似的响应:
[提示响应]
当然,让我们使用热图可视化相关矩阵。我们将使用 seaborn 库来完成这项工作。
# Step 8.1: Visualize Correlation Matrix with Heatmap
import seaborn as sns
# Plot a heatmap of the correlation matrix
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('Correlation Matrix Heatmap')
plt.show()
此代码将创建相关矩阵的热图。颜色代表相关系数:暖色(趋向红色)表示正相关,冷色(趋向蓝色)表示负相关,中性颜色(围绕白色)表示无相关。annot=True选项将在热图上显示相关系数。运行此代码并告诉我你发现了什么。
[响应结束]
让我们尝试这段代码,使用 seaborn 库创建热图可视化,看看它是否工作。在 Notebook 中的代码单元格中运行此代码:
# Step 8.1: Visualize Correlation Matrix with Heatmap
import seaborn as sns
# Plot a heatmap of the correlation matrix
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('Correlation Matrix Heatmap')
plt.show()
你应该看到以下类似的输出:

图 17.4:显示三个变量之间相关性的热图:总票数、有帮助的票数和星级评分
第 9 步:额外的探索性分析
那么,为什么我们想要进行额外的探索性分析呢?好吧,有一些很好的理由我们会想要做那件事:
-
揭示隐藏的用户模式:有时,有一些趋势、模式和关系不是立即可发现的,进行第二次更深入的观察可以找到这些。
-
验证初步发现:通过用第二次分析验证初步分析的结果,你可以确保结果的稳健性和可靠性。
-
识别异常值:未能有效地找到异常值可能会歪曲结果。在这些异常值上花费更多时间可能会得到回报。
-
提高模型准确性:可以识别出能够提高准确性的额外变量。
上面的列表并不是我们应该进行额外分析的所有原因。让我们看看 Copilot 如何帮助我们:
[提示]
感谢,我想了解是否还有更多关于此数据集情感分析的探索性分析。
[提示结束]
你应该收到以下类似的响应:
[提示响应]
当然,情感分析通常涉及更深入地探索文本数据。以下是一些你可以考虑的额外步骤:
第 10 步:文本预处理
在分析文本之前,通常有助于对其进行一些清理。这可能包括将所有文本转换为小写,删除标点符号,删除停用词(如“和”、“the”、“a”等常见词汇,它们不会增加太多意义),以及词干提取或词形还原(将单词还原到其基本形式)。
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer
import re
# Initialize a PorterStemmer object to perform stemming
stemmer = PorterStemmer()
# Define a function to preprocess the text
def preprocess_text(text):
# Convert to lower case
text = text.lower()
# Remove punctuation
text = re.sub(r'[^\w\s]', '', text)
# Remove stop words and stem the words
text = ' '.join([stemmer.stem(word) for word in text.split() if word not in stopwords.words('english')])
return text
# Apply the function to the review_body column
df['review_body_clean'] = df['review_body'].apply(preprocess_text)
第 11 步:单词频率分析
在预处理文本后,你可以分析评论中的单词频率。这有助于识别常见的主题或话题。
from collections import Counter
# Count the frequency of words in the reviews
word_counts = Counter(' '.join(df['review_body_clean']).split())
# Print the 10 most common words
print(word_counts.most_common(10))
第 12 步:情感评分计算
你可以使用 TextBlob 或 Vader 等情感分析工具为每条评论计算情感评分。这可以提供对评论中表达的情感的更定量度量。
from textblob import TextBlob
# Define a function to calculate sentiment
def calculate_sentiment(text):
return TextBlob(text).sentiment.polarity
# Apply the function to the review_body_clean column
df['sentiment_score'] = df['review_body_clean'].apply(calculate_sentiment)
# Print the average sentiment score
print(df['sentiment_score'].mean())
请注意,你需要安装nltk和textblob库来运行此代码,你可以使用pip来安装:
pip install nltk textblob
如果你想继续以上任何步骤,或者有其他需要做的事情,请告诉我。
[响应结束]
这个提示产生了三个步骤作为响应。让我们尝试这三个步骤中的代码,看看是否可行:
文本预处理
# uncomment if the nltk and textblob are not installed
# !pip install nltk textblob
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer
import re
# Initialize a PorterStemmer object to perform stemming
stemmer = PorterStemmer()
# Define a function to preprocess the text
def preprocess_text(text):
# Convert to lower case
text = text.lower()
# Remove punctuation
text = re.sub(r'[^\w\s]', '', text)
# Remove stop words and stem the words
text = ' '.join([stemmer.stem(word) for word in text.split() if word not in stopwords.words('english')])
return text
# Apply the function to the review_body column
df['review_body_clean'] = df['review_body'].apply(preprocess_text)
单词频率分析
from collections import Counter
# Count the frequency of words in the reviews
word_counts = Counter(' '.join(df['review_body_clean']).split())
# Print the 10 most common words
print(word_counts.most_common(10))
你应该看到以下类似的输出:
[('book', 80), ('read', 25), ('love', 24), ('great', 24), ('stori', 19), ('good', 17), ('page', 16), ('one', 15), ('like', 14), ('realli', 14)]
接下来,使用上面的输出构建下一个提示,以更好地了解我们的情况:
[提示]
对于下面的代码:
from collections import Counter
# Count the frequency of words in the reviews
word_counts = Counter(' '.join(df['review_body_clean']).split())
# Print the 10 most common words
print(word_counts.most_common(10))
下面是输出:[(‘book’,80),(‘read’,25),(‘love’,24),(‘great’,24),(‘stori’,19),(‘good’,17),(‘page’,16),(‘one’,15),(‘like’,14),(‘realli’,14)]
[提示结束]
你应该收到以下类似的响应:
[响应提示]
太好了!你分享的输出显示了清洗后的评论中最常见的 10 个单词及其频率:
'book': Appears 80 times
'read': Appears 25 times
'love': Appears 24 times
'great': Appears 24 times
'stori': Appears 19 times
'good': Appears 17 times
'page': Appears 16 times
'one': Appears 15 times
'like': Appears 14 times
'realli': Appears 14 times
这表明大多数评论都是积极的,像“喜欢”、“优秀”和“好”这样的词在评论中相当常见。在书评数据集中,“book”、“read”和“page”这些词也很常见,这是预期的。
对于下一步,你可以考虑为每个评论计算情感分数,以获得更量化的情感度量。或者,你可以使用条形图或词云可视化单词频率。请告诉我你下一步想做什么。
[响应结束]
让我们尝试运行下面的情感分数计算代码:
情感分数计算
from textblob import TextBlob
# Define a function to calculate sentiment
def calculate_sentiment(text):
return TextBlob(text).sentiment.polarity
# Apply the function to the review_body_clean column
df['sentiment_score'] = df['review_body_clean'].apply(calculate_sentiment)
# Print the average sentiment score
print(df['sentiment_score'].mean())
这里是输出:
0.23785470981747298
接下来,让我们尝试解释这个响应。像往常一样,让我们从响应中创建一个提示,看看 Copilot 对这个提示的看法:
[提示]
对于下面的代码:
from textblob import TextBlob
# Define a function to calculate sentiment
def calculate_sentiment(text):
return TextBlob(text).sentiment.polarity
# Apply the function to the review_body_clean column
df['sentiment_score'] = df['review_body_clean'].apply(calculate_sentiment)
# Print the average sentiment score
print(df['sentiment_score'].mean())
这里是输出结果:0.23785470981747298
[提示结束]
你应该收到以下类似的响应:
[响应提示]
太好了!你分享的输出是你数据集中评论的平均情感分数。
情感分数是一个介于-1 和 1 之间的数字。-1 表示极端负面情感,1 表示极端正面情感,0 表示中性情感。
在你的数据集中,平均情感分数大约为 0.238,这表明评论总体上呈现积极情感。这与单词频率分析结果一致,显示“喜欢”、“优秀”和“好”等积极词汇在评论中相当常见。
对于下一步,你可以考虑使用直方图或箱线图可视化情感分数的分布。或者,你可以分析情感分数与其他变量(如星级评分或评论长度)之间的关系。请告诉我你下一步想做什么。
[响应结束]
[提示]
按照以下步骤逐步操作:
-
使用直方图可视化情感分数的分布。
-
分析情感分数与其他变量(如星级评分或评论长度)之间的关系。
[结束提示]
你应该会收到与以下回复类似的回复。
[提示回复]
当然,让我们继续执行这些步骤。
步骤 13:可视化情感分数的分布
我们可以使用直方图来可视化情感分数的分布。
import matplotlib.pyplot as plt
# Plot a histogram of the sentiment scores
plt.figure(figsize=(8, 6))
plt.hist(df['sentiment_score'], bins=20, color='skyblue')
plt.title('Distribution of Sentiment Scores')
plt.xlabel('Sentiment Score')
plt.ylabel('Frequency')
plt.show()
步骤 14:分析情感分数与其他变量之间的关系
我们可以使用散点图来可视化情感分数与其他变量(如星级评分和评论长度)之间的关系。
# Plot a scatter plot of sentiment score vs star rating
plt.figure(figsize=(8, 6))
plt.scatter(df['sentiment_score'], df['star_rating'], alpha=0.5, color='skyblue')
plt.title('Sentiment Score vs Star Rating')
plt.xlabel('Sentiment Score')
plt.ylabel('Star Rating')
plt.show()
# Plot a scatter plot of sentiment score vs review length
plt.figure(figsize=(8, 6))
plt.scatter(df['sentiment_score'], df['review_length'], alpha=0.5, color='skyblue')
plt.title('Sentiment Score vs Review Length')
plt.xlabel('Sentiment Score')
plt.ylabel('Review Length')
plt.show()
这些图表将帮助你了解情感分数与星级评分或评论长度之间是否存在任何相关性。运行这些代码,并告诉我你发现了什么。
[结束回复]
让我们尝试上面的代码,看看它是否可行。将以下代码添加到一个代码单元格中并运行:
可视化情感分数的分布
import matplotlib.pyplot as plt
# Plot a histogram of the sentiment scores
plt.figure(figsize=(8, 6))
plt.hist(df['sentiment_score'], bins=20, color='skyblue')
plt.title('Distribution of Sentiment Scores')
plt.xlabel('Sentiment Score')
plt.ylabel('Frequency')
plt.show()

图 17.5:描述情感分数分布的图像
分析情感分数与其他变量之间的关系
# Plot a scatter plot of sentiment score vs star rating
plt.figure(figsize=(8, 6))
plt.scatter(df['sentiment_score'], df['star_rating'], alpha=0.5, color='skyblue')
plt.title('Sentiment Score vs Star Rating')
plt.xlabel('Sentiment Score')
plt.ylabel('Star Rating')
plt.show()
# Plot a scatter plot of sentiment score vs review length
plt.figure(figsize=(8, 6))
plt.scatter(df['sentiment_score'], df['review_length'], alpha=0.5, color='skyblue')
plt.title('Sentiment Score vs Review Length')
plt.xlabel('Sentiment Score')
plt.ylabel('Review Length')
plt.show()

图 17.6:情感分数与星级评分的关系

图 17.7:情感分数与评论长度的关系
作业
你的任务是反思所采取的方法,并针对波士顿房价数据集进行类似的回归场景:www.kaggle.com/datasets/vikrishnan/boston-house-prices。
这里有一些问题,你可以尝试使用上述数据集和回归来回答:
-
价格预测:给定房屋的特征(例如,大小、位置和卧室数量),估计房屋的价格是多少?
-
特征重要性:哪些特征对房价影响最大?
-
价格趋势:在特定地区,房价随时间如何变化?
解决方案
解决方案在仓库中:github.com/PacktPublishing/AI-Assisted-Software-Development-with-GitHub-Copilot-and-ChatGPT
摘要
本章有一个重要的目的——比较和对比使用 ChatGPT 与 GitHub Copilot 以及在此情况下其聊天功能的经验。我们采取的方法是向 Copilot 提供大量前置信息,通过描述整体问题和数据集的形状。我们还提供了指令,让 Copilot 引导我们做什么,这显示了逐步采取的步骤和要运行的代码。总体结论是,我们可以使用与 ChatGPT 类似的方法使用 Copilot Chat。
我们还看到了 Copilot 如何帮助我们解释输出,了解我们在过程中的位置,并建议下一步要采取的行动。
根据规则,我们应该始终测试代码,如果代码无法运行或产生预期的输出,我们应该请求我们的 AI 助手帮助。
加入我们的 Discord 社区
加入我们的 Discord 空间,与作者和其他读者进行讨论:

第十八章:使用 Copilot Chat 进行回归
简介
公司的股票代表对公司所有权的拥有。一股股票代表对公司资产和收益的份额要求,与总股数成比例。例如,如果投资者持有某公司 50 股股票,而该公司总共有 1,000 股,那么该投资者(或股东)将拥有并有权要求公司 5%的资产和收益。
公司的股票可以通过证券交易所和组织在股东和其他各方之间进行交易。主要的证券交易所包括纽约证券交易所、纳斯达克、伦敦证券交易所集团、上海证券交易所和香港证券交易所。股票的交易价格基本上是由于供求定律而波动。在任何时刻,供应是公众投资者手中的股票数量,需求是投资者想要购买的股票数量,股票价格上下波动以实现并维持平衡。
投资者通常希望低价买入、高价卖出。这似乎很简单,但将其付诸实践却极其困难,因为预测股票价格是涨是跌极其困难。基本面分析和技术分析是两个主要的研究领域,旨在理解影响价格波动的原因和情况,甚至预测未来的股票价格:
-
基本面分析:这个流派专注于影响公司价值和运营的基本要素,例如宏观经济环境、行业状况以及公司的管理和竞争对手。
-
技术分析:另一方面,这使用历史交易活动的统计分析,如价格变动、成交量和时间,来预测未来的价格变动。
在本章中,我们将使用苹果数据集来探索第二种技术,预测股价并帮助投资者决定是否买入。
回归
让我们先向 Copilot Chat 询问回归的定义:
[提示]
什么是回归?
[提示结束]
您应该收到以下类似响应:
[快速响应]
回归是数据分析中使用的统计方法,它研究因变量(目标)和自变量(预测变量)之间的关系。这种方法主要用于预测、时间序列建模和寻找变量之间的因果关系。例如,降雨量和土壤侵蚀之间的关系或工资和经验年数之间的关系。
[响应结束]
你可能会在聊天框中注意到 GitHub Copilot 建议下一个上下文,以蓝色突出显示,询问“有哪些不同的回归类型?”。你可以点击它来获取答案。这展示了 Copilot 是如何作为一个强大的工具来有效协助用户的。
数据集概述
该数据集包含苹果公司(AAPL)10 年的股票数据。从这些数据中可以获得见解,可用于构建有用的价格预测算法,以帮助投资。
我们可以就这个数据集做以下笔记:
-
数据是从 25 个交易所收集和汇总的。
-
数据每周更新。
-
该数据集包含 AAPL 的 1 分钟、5 分钟、30 分钟和 1 小时 K 线(开盘/最高/最低/收盘/成交量)。
-
包含盘后交易。价格已调整以反映拆股和股息。数据集可在
www.kaggle.com/datasets/tarunpaparaju/apple-aapl-historical-stock-data找到。
探索数据集
为了更好地说明这种回归方法,我们需要为我们的目标有合适的预测因子。该数据集记录了与股票更直接相关的元素。五个表示在一个时间单位(通常是第一天,但也可以是一周或一个月)内价格变动的值是关键交易指标。
它们如下:
-
开盘价:给定交易日的起始价格
-
收盘价:该天的最终价格
-
高:该股票当天交易的最高价格
-
低:该股票当天交易的最高价格
-
成交量:在市场收盘前当天交易的总股数
提示策略
在本章中,我们将使用 TAG(任务-操作-指导)提示模式。以下是我们可以如何帮助构建我们的项目以帮助我们编写初始提示的方法:
-
任务:通过历史数据对苹果股票价值进行回归分析
-
操作:我们需要指示 AI 助手告诉我们需要采取哪些步骤
-
指导:指导应该是我们描述数据集
你的初始提示
就像本书中的其他章节一样,我们从一个尽可能提供我们想要做什么以及数据集形状的提示开始。我们的想法是为我们的 AI 工具设定一个高级上下文,这有助于它在我们的其他提示中引导。
[提示]
我正在进行一个回归项目,使用苹果历史股票数据集来预测股票价格。这将帮助投资者在购买或不购买股票时做出明智的决定。工作流程包括加载数据集、探索其内容、拆分数据集、构建回归模型和评估其性能。但请记住,我想一步一步地学习。让我们从加载数据集开始。我将数据集存储在<文件路径>。以下是数据集的形状:
-
开盘价:给定交易日的起始价格
-
收盘价:该天的最终价格
-
高:该股票当天交易的最高价格
-
Low:那天股票交易中的最低价格
-
Volume:在那天市场收盘前交易的股票总数
[提示结束]
你应该收到以下类似的响应:
[提示响应]
当然,让我们从加载数据集开始。我们将使用 Python 中的 pandas 库来完成此操作。将<file-path>替换为你的文件所在路径。
下面是加载数据集的 Python 代码:
import pandas as pd
# Replace <file-path> with the path of your dataset
file_path = "<file-path>"
# Load the dataset
df = pd.read_csv(file_path)
# Display the first few rows of the dataset
print(df.head())
运行此代码后,你应该看到数据集的前几行打印出来。这将为你提供一个初步的数据视图。
对于下一步,你可能想要探索数据集,如果需要的话清理数据,将数据集分为训练集和测试集,使用训练集构建回归模型,然后使用测试集评估模型的性能。
[响应结束]
让我们尝试 Copilot 生成的输出,看看它是否工作。将file_path变量修改为指向你的数据集位置,然后在笔记本中的代码单元中执行以下代码:
import pandas as pd
# Replace <file-path> with the path of your dataset
file_path = "/your-path/.csv"
# Load the dataset
df = pd.read_csv(file_path)
# Display the first few rows of the dataset
print(df.head())
在运行上述代码后,你应该看到以下输出:
Date Close/Last Volume Open High Low
0 02/28/2020 $273.36 106721200 $257.26 $278.41 $256.37
1 02/27/2020 $273.52 80151380 $281.1 $286 $272.96
2 02/26/2020 $292.65 49678430 $286.53 $297.88 $286.5
3 02/25/2020 $288.08 57668360 $300.95 $302.53 $286.13
4 02/24/2020 $298.18 55548830 $297.26 $304.18 $289.23
接下来,我们将专注于历史价格(记录的五项值)和性能来预测未来价格。让我们先检查数据类型。
[提示]
检查数据类型。
[提示结束]
你应该收到以下类似的响应:
[提示响应]
要检查你的DataFrame中列的数据类型,你可以使用dtypes属性。以下是你可以这样做的示例:
# Check the data types of each column
print(df.dtypes)
这将打印出每个列的名称及其数据类型。这很有用,因为某些操作只能在某些类型的数据上执行。例如,你不能在字符串上执行数值操作。
[响应结束]
让我们尝试一下,看看它是否工作。运行代码 print(df.types):
# Check the data types of each column
print(df.dtypes)
Date object
Close/Last object
Volume int64
Open object
High object
Low object
dtype: object
现在,使用上述输出查询我们的 AI 工具。
[提示]
下面是输出,Date 对象 Close/Last 对象 Volume int64 Open 对象 High 对象 Low 对象 dtype: object
[提示结束]
你应该收到以下类似的响应:
[提示响应]
看起来日期列和价格列(‘Close/Last’,‘Open’,‘High’,‘Low’)被读取为‘object’类型,这在 pandas 中本质上是一个字符串。然而,对于你的分析,你希望‘Date’列是日期时间格式,价格列是浮点类型。
下面是如何转换数据类型的示例:
# Convert 'Date' to datetime
df['Date'] = pd.to_datetime(df['Date'])
# Remove $ from price columns and convert to float
price_columns = [' Close/Last', ' Open', ' High', ' Low']
for col in price_columns:
df[col] = df[col].str.replace('$', '').astype(float)
# Check the data types again
print(df.dtypes)
此代码首先使用pd.to_datetime()将‘Date’列转换为日期时间格式。然后它遍历每个价格列,从每个值中移除美元符号,并将列转换为浮点类型。最后,它再次打印出列的数据类型以确认更改。
[响应结束]
让我们尝试上述建议,通过运行以下代码来转换我们的列:
# Convert 'Date' to datetime
df['Date'] = pd.to_datetime(df['Date'])
# Remove $ from price columns and convert to float
price_columns = [' Close/Last', ' Open', ' High', ' Low']
for col in price_columns:
df[col] = df[col].str.replace('$', '').astype(float)
# Check the data types again
print(df.dtypes)
你应该看到以下输出,这是正确转换为合适类型的列而不是字符串:
Date datetime64[ns]
Close/Last float64
Volume int64
Open float64
High float64
Low float64
dtype: object
接下来,让我们告诉 Copilot 结果,以确保它能告诉我们下一步该做什么。
[提示]
下面是输出结果,日期 datetime64[ns] 收盘/最后 float64 成交量 int64 开盘 float64 最高 float64 最低 float64 数据类型:object
[提示结束]
你应该会收到类似于以下响应:
[提示响应]
太好了!现在你的‘Date’列是‘datetime64[ns]’类型,你的价格列(‘Close/Last’,‘Open’,‘High’,‘Low’)是‘float64’类型。这意味着你现在可以对价格列进行数值运算,并对‘Date’列进行时间序列分析。
下一步可能是进行探索性数据分析,例如检查缺失值、可视化变量的分布或检查不同变量之间的相关性。你可能还想要将数据分成训练集和测试集,为构建回归模型做准备。
[响应结束]
探索性数据分析
根据输出结果决定下一步操作,我们决定检查数据集中的缺失值:
[提示]
检查缺失值。
[响应结束]
你应该会收到类似于以下响应:
[提示响应]
要检查DataFrame中的缺失值,你可以使用 pandas 中的isnull()函数和sum()函数。下面是如何操作的:
# Check for missing values
print(df.isnull().sum())
这将打印出你DataFrame中每一列的缺失值数量。如果一个列的缺失值数量为零,这意味着该列没有缺失值。如果一个列的缺失值数量大于零,那么这个数字就是该列的缺失值数量。
[响应结束]
Copilot 的响应建议检查空值,并提供了相应的代码。让我们在一个笔记本的代码单元中运行建议的代码:
# Check for missing values
print(df.isnull().sum())
这里是输出:
Date 0
Close/Last 0
Volume 0
Open 0
High 0
Low 0
dtype: int64
使用这个输出,将其提供给 Copilot 以了解下一步该做什么:
[提示]
下面是输出结果,日期 0 收盘/最后 0 成交量 0 开盘 0 最高 0 最低 0 数据类型:int64
[提示结束]
你应该会收到类似于以下响应:
[提示响应]
太好了!输出结果显示,你的数据集中没有缺失值。每一列的缺失值都是 0。这是一个好消息,因为它意味着你不需要处理缺失数据,这可能会是一个复杂的过程。
分析的下一步可能是进行一些探索性数据分析(EDA)。这可能包括可视化数据、检查异常值或检查变量之间的关系。之后,你可以继续将数据分成训练集和测试集,然后构建回归模型。
[响应结束]
太好了,我们没有缺失值,看起来下一步应该是进行可视化分析和检查异常值。有了这些知识,让我们制定一个合适的提示。
[提示]
可视化数据和检查异常值。
[提示结束]
你应该会收到类似于以下响应:
[提示响应]
使用 Python 中的各种库,如 Matplotlib 或 Seaborn,可以可视化数据。对于检查异常值,箱线图非常有用。以下是如何为您价格列创建箱线图的示例:
import matplotlib.pyplot as plt
import seaborn as sns
# Set up the matplotlib figure
f, axes = plt.subplots(2, 2, figsize=(15, 10))
# Plot a simple histogram with binsize determined automatically
sns.boxplot(data=df[' Close/Last'], ax=axes[0, 0])
axes[0, 0].set_title('Close/Last')
sns.boxplot(data=df[' Open'], ax=axes[0, 1])
axes[0, 1].set_title('Open')
sns.boxplot(data=df[' High'], ax=axes[1, 0])
axes[1, 0].set_title('High')
sns.boxplot(data=df[' Low'], ax=axes[1, 1])
axes[1, 1].set_title('Low')
plt.tight_layout()
此代码将创建一个 2x2 的图表网格,每个图表都是一个价格列的箱线图。箱线图有助于可视化数据的分布并识别潜在的异常值。在箱线图中,异常值通常表示为位于箱线图胡须之外的点。
[响应结束]
上述代码建议使用 matplotlib 和 seaborn 库,并包含创建箱线图可视化的代码;让我们运行这段代码:
import matplotlib.pyplot as plt
import seaborn as sns
# Set up the matplotlib figure
f, axes = plt.subplots(2, 2, figsize=(15, 10))
# Plot a simple histogram with binsize determined automatically
sns.boxplot(data=df[' Close/Last'], ax=axes[0, 0])
axes[0, 0].set_title('Close/Last')
sns.boxplot(data=df[' Open'], ax=axes[0, 1])
axes[0, 1].set_title('Open')
sns.boxplot(data=df[' High'], ax=axes[1, 0])
axes[1, 0].set_title('High')
sns.boxplot(data=df[' Low'], ax=axes[1, 1])
axes[1, 1].set_title('Low')
plt.tight_layout()
你应该看到以下箱线图类似的结果:

图 18.1:数据集的箱线图
接下来,我们想要了解更多关于我们的变量及其关系的信息。
[提示]
检查变量之间的关系。
[提示结束]
你应该收到以下类似响应:
[提示响应]
要检查变量之间的关系,你可以使用相关矩阵和热图。相关矩阵将给出代表两个变量之间相关程度数值的数值,而热图将给出这些关系的视觉表示。
这是你可以这样做的:
# Calculate the correlation matrix
corr = df.corr()
# Plot the heatmap
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.show()
此代码首先使用 corr() 函数计算相关矩阵,该函数返回一个 DataFrame,其中每个单元格代表两个变量之间的相关系数。然后它使用此相关矩阵绘制热图。annot=True 参数允许在热图上绘制相关系数,而 cmap='coolwarm' 参数设置热图的颜色方案。
相关系数的范围从 -1 到 1。接近 1 的值表示高度正相关,而接近 -1 的值表示高度负相关。接近 0 的值表示没有相关性。
[响应结束]
响应表示我们应该创建一个相关矩阵并通过热图可视化它。让我们尝试上面的建议代码并看看我们得到什么:
# Calculate the correlation matrix
corr = df.corr()
# Plot the heatmap
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.show()
上述代码的输出如下:

图 18.2:热图
在热图中,我们可以看到:
-
对角线始终为 1,因为它显示了每个变量与其自身的相关性。
-
收盘价/最新价、开盘价、最高价和最低价之间高度正相关(0.92),表明当一个变量增加时,其他变量也倾向于增加。这在股票数据中很典型,因为这些价格在交易日内紧密移动。
-
成交量与收盘价/最新价、开盘价、最高价和最低价呈负相关(-0.61)。这表明,在交易量较高的日子,价格可能倾向于较低,反之亦然。
如下面的截图所示,Copilot 推荐在分析变量关系之后下一步的操作。我们将选择这个并看看它说了什么。
让我们询问 Copilot 下一步该做什么。
[提示]
在检查变量之间的关系之后,下一步是什么?
[提示结束]
你应该收到一个类似于以下响应的回复:
[响应提示]
在检查变量之间的关系之后,接下来的步骤通常是准备你的数据以进行建模。这可能涉及几个子步骤:
-
特征选择:根据你的相关性分析,你可能会决定删除与目标变量不相关的某些特征,或者为了避免多重共线性,删除两个高度相关的特征中的一个。
-
数据分割:你需要将数据分割成训练集和测试集。常见的分割是 80%的数据用于训练,20%用于测试。
-
数据缩放:根据你计划使用的模型,你可能需要缩放你的数据,以便所有特征都有相似的范围值。
一旦你的数据准备好了,你就可以继续构建你的回归模型。
[响应结束]
数据分割
在这个阶段,让我们为创建模型做准备。在我们能够创建模型之前,我们需要将我们的数据分成训练数据和测试集,以便稍后验证模型。
[提示]
让我们继续到分割数据的步骤。
[提示结束]
你应该收到一个类似于以下响应的回复:
[响应提示]
绝对,将数据分割成训练集和测试集是准备数据以进行建模的关键步骤。我们将使用sklearn.model_selection中的train_test_split函数来完成此操作。
假设我们将预测‘收盘/最后’价格,以下是你可以分割数据的方式:
from sklearn.model_selection import train_test_split
# Define the feature variables and the target variable
X = df.drop(' Close/Last', axis=1)
y = df[' Close/Last']
# Split the data into training set and test set
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
此代码首先定义了特征变量 X 和目标变量 y,然后将其分割成训练集和测试集,其中 80%的数据用于训练集,20%的数据用于测试集。random_state参数用于可重复性 - 使用相同的random_state将确保每次运行代码时都得到相同的训练/测试分割。
[响应结束]
简而言之,上面的代码展示了如何分割我们的数据集;让我们尝试这段代码:
from sklearn.model_selection import train_test_split
# Define the feature variables and the target variable
X = df.drop(' Close/Last', axis=1)
y = df[' Close/Last']
# Split the data into training set and test set
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
在这个阶段,我们没有输出可以显示,因为我们已经将结果分配给了变量。然而,让我们打印出下一个。
[提示]
打印样本的形状。
[提示结束]
你应该收到一个类似于以下响应的回复:
[响应提示]
要打印你的训练和测试样本的形状,你可以使用形状属性。以下是你可以这样做的方式:
# Print the shape of the training set and the test set
print("Training set (X):", X_train.shape)
print("Training set (y):", y_train.shape)
print("Test set (X):", X_test.shape)
print("Test set (y):", y_test.shape)
这将打印出你的训练集和测试集的行数和列数。X_train和y_train之间以及X_test和y_test之间的行数应该是相同的。X_train和X_test中的列数应该相同,而y_train和y_test各自应该只有一列(因为它们代表目标变量)。
[响应结束]
建议的代码很简单,因为它只包含一系列打印语句。让我们在一个代码单元格中运行这段代码来试试:
# Print the shape of the training set and the test set
print("Training set (X):", X_train.shape)
print("Training set (y):", y_train.shape)
print("Test set (X):", X_test.shape)
print("Test set (y):", y_test.shape)
你应该看到类似于以下的输出:
Training set (X): (2014, 5)
Training set (y): (2014,)
Test set (X): (504, 5)
Test set (y): (504,)
构建一个回归模型
现在我们已经分割了数据,接下来让我们训练模型,或者至少这是我们期望 Copilot 在询问下一步时会说的。让我们创建一个只询问这一点的提示:
[提示]
数据分割后的下一步是什么?
[提示结束]
你应该收到一个类似于以下响应:
[快速响应]
在分割数据后,下一步是使用训练数据创建和训练你的回归模型。根据你的数据性质和你要解决的问题,你可能选择一个简单的线性回归模型,或者一个更复杂的模型,如多项式回归或岭回归。
模型训练完成后,你可以使用它对测试数据进行预测。然后,通过比较这些预测与测试数据中的实际值来评估模型的性能。评估回归模型的常用指标包括平均绝对误差(MAE)、平均平方误差(MSE)和 R 平方。
[响应结束]
好的,正如我们所想的,下一步是训练模型。让我们创建一个提示,询问如何更详细地训练模型:
[提示]
创建并训练一个回归模型。
[提示结束]
你应该收到一个类似于以下响应:
[快速响应]
当然,让我们使用训练数据创建并训练一个简单的线性回归模型。我们将使用sklearn.linear_model中的LinearRegression类来完成这个任务。
下面是如何创建和训练你的模型:
from sklearn.linear_model import LinearRegression
# Create a Linear Regression model
model = LinearRegression()
# Train the model
model.fit(X_train, y_train)
此代码首先创建一个线性回归模型。然后使用 fit 方法和训练数据来训练模型。完成此步骤后,你的模型就可以对未见数据做出预测了。
请注意,如果你的数据具有非线性关系,你可能需要使用更复杂的模型,如多项式回归或基于树的模型。此外,如果你的特征不在同一尺度上,你可能需要在训练模型之前对数据进行缩放。
[响应结束]
我们已经收到了有关使用线性回归和 sklearn 库的响应。请注意,在继续之前,你可能需要安装这个库。一旦你确保 sklearn 已安装,请继续运行以下建议的代码:
from sklearn.linear_model import LinearRegression
# Create a Linear Regression model
model = LinearRegression()
# Train the model
model.fit(X_train, y_train)
这里是运行代码的输出:
---------------------------------------------------------------------------
DTypePromotionError Traceback (most recent call last)
File ~/.conda/envs/myenv/lib/python3.12/site-packages/sklearn/base.py:1152, in _fit_context.<locals>.decorator.<locals>.wrapper(estimator, *args, **kwargs)
1145 estimator._validate_params()
1147 with config_context(
1148 skip_parameter_validation=(
1149 prefer_skip_nested_validation or global_skip_validation
1150 )
1151 ):
-> 1152 return fit_method(estimator, *args, **kwargs)
File ~/.conda/envs/myenv/lib/python3.12/site-packages/sklearn/linear_model/_base.py:678, in LinearRegression.fit(self, X, y, sample_weight)
674 n_jobs_ = self.n_jobs
676 accept_sparse = False if self.positive else ["csr", "csc", "coo"]
--> 678 X, y = self._validate_data(
679 X, y, accept_sparse=accept_sparse, y_numeric=True, multi_output=True
680 )
682 has_sw = sample_weight is not None
683 if has_sw:
File ~/.conda/envs/myenv/lib/python3.12/site-packages/sklearn/base.py:622, in BaseEstimator._validate_data(self, X, y, reset, validate_separately, cast_to_ndarray, **check_params)
620 y = check_array(y, input_name="y", **check_y_params)
621 else:
--> 622 X, y = check_X_y(X, y, **check_params)
623 out = X, y
625 if not no_val_X and check_params.get("ensure_2d", True):
File ~/.conda/envs/myenv/lib/python3.12/site-packages/sklearn/utils/validation.py:1146, in check_X_y(X, y, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, multi_output, ensure_min_samples, ensure_min_features, y_numeric, estimator)
1141 estimator_name = _check_estimator_name(estimator)
1142 raise ValueError(
1143 f"{estimator_name} requires y to be passed, but the target y is None"
1144 )
-> 1146 X = check_array(
1147 X,
1148 accept_sparse=accept_sparse,
1149 accept_large_sparse=accept_large_sparse,
1150 dtype=dtype,
1151 order=order,
1152 copy=copy,
1153 force_all_finite=force_all_finite,
1154 ensure_2d=ensure_2d,
1155 allow_nd=allow_nd,
1156 ensure_min_samples=ensure_min_samples,
1157 ensure_min_features=ensure_min_features,
1158 estimator=estimator,
1159 input_name="X",
1160 )
1162 y = _check_y(y, multi_output=multi_output, y_numeric=y_numeric, estimator=estimator)
1164 check_consistent_length(X, y)
File ~/.conda/envs/myenv/lib/python3.12/site-packages/sklearn/utils/validation.py:795, in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator, input_name)
791 pandas_requires_conversion = any(
792 _pandas_dtype_needs_early_conversion(i) for i in dtypes_orig
793 )
794 if all(isinstance(dtype_iter, np.dtype) for dtype_iter in dtypes_orig):
--> 795 dtype_orig = np.result_type(*dtypes_orig)
796 elif pandas_requires_conversion and any(d == object for d in dtypes_orig):
797 # Force object if any of the dtypes is an object
798 dtype_orig = object
DTypePromotionError: The DType <class 'numpy.dtypes.DateTime64DType'> could not be promoted by <class 'numpy.dtypes.Float64DType'>. This means that no common DType exists for the given inputs. For example they cannot be stored in a single array unless the dtype is `object`. The full list of DTypes is: (<class
'numpy.dtypes.DateTime64DType'>, <class 'numpy.dtypes.Int64DType'>, <class 'numpy.dtypes.Float64DType'>, <class 'numpy.dtypes.Float64DType'>, <class 'numpy.dtypes.Float64DType'>)
我们显然得到了一个错误回复,所以让我们看看 Copilot 是否能帮助我们解决这个问题。像往常一样,包括错误文本等信息以获得良好的回复。
[提示]
我在运行代码后遇到了这个错误,
Cell 39 line 7 4 model = LinearRegression() 6 # Train the model ----> 7 model.fit(X_train, y_train)
File ~/.conda/envs/myenv/lib/python3.12/site-packages/sklearn/base.py:1152, in _fit_context..decorator..wrapper(estimator, *args, **kwargs) 1145 estimator._validate_params() 1147 with config_context( 1148 skip_parameter_validation=( 1149 prefer_skip_nested_validation or global_skip_validation 1150 ) 1151 ): -> 1152 return fit_method(estimator, *args, **kwargs)
File ~/.conda/envs/myenv/lib/python3.12/site-packages/sklearn/linear_model/base.py:678, in LinearRegression.fit(self, X, y, sample_weight) 674 n_jobs = self.n_jobs 676 accept_sparse = False if self.positive else ["csr", "csc", "coo"] --> 678 X, y = self._validate_data( 679 X, y, accept_sparse=accept_sparse, y_numeric=True, multi_output=True 680 ) 682 has_sw = sample_weight is not None 683 if has_sw: ... 796 elif pandas_requires_conversion and any(d == object for d in dtypes_orig): 797 # Force object if any of the dtypes is an object 798 dtype_orig = object
DTypePromotionError: The DType <class 'numpy.dtypes.DateTime64DType'> could not be promoted by <class 'numpy.dtypes.Float64DType'>. This means that no common DType exists for the given inputs. For example they cannot be stored in a single array unless the dtype is object. The full list of DTypes is: (<class 'numpy.dtypes.DateTime64DType'>, <class 'numpy.dtypes.Int64DType'>, <class 'numpy.dtypes.Float64DType'>, <class 'numpy.dtypes.Float64DType'>, <class 'numpy.dtypes.Float64DType'>)
[提示结束]
你应该会收到以下类似的回复:
[提示回复]
错误信息表明模型在处理‘日期’列时遇到麻烦,因为它是日期时间格式,而模型期望数值输入。
处理日期时间数据的一个常见方法是从中提取特征,例如年、月、日等。以下是你可以这样做的方法:
# Extract features from 'Date'
X_train['Year'] = X_train['Date'].dt.year
X_train['Month'] = X_train['Date'].dt.month
X_train['Day'] = X_train['Date'].dt.day
# Drop the original 'Date' column
X_train = X_train.drop('Date', axis=1)
# Do the same for the test set
X_test['Year'] = X_test['Date'].dt.year
X_test['Month'] = X_test['Date'].dt.month
X_test['Day'] = X_test['Date'].dt.day
X_test = X_test.drop('Date', axis=1)
# Now try to fit the model again
model.fit(X_train, y_train)
此代码首先从‘日期’列中提取年、月和日,并将它们作为新的列添加到 DataFrame 中。然后它删除原始的‘日期’列。最后,它再次尝试拟合模型。
请注意,这是一个简单的方法,可能不是处理特定问题的日期时间数据的最佳方式。最佳方法取决于你的数据性质和你要解决的问题。
[回复结束]
所以,我们的‘日期’列是问题所在。模型训练只能处理数值数据。看起来回复建议我们应该从‘日期’列中提取特征,比如年、月和日,以保留信息,但以数值形式。此外,我们还应该删除‘日期’列,因为我们已经从它那里得到了我们想要的东西。
让我们尝试运行建议的代码:
# Extract features from 'Date'
X_train['Year'] = X_train['Date'].dt.year
X_train['Month'] = X_train['Date'].dt.month
X_train['Day'] = X_train['Date'].dt.day
# Drop the original 'Date' column
X_train = X_train.drop('Date', axis=1)
# Do the same for the test set
X_test['Year'] = X_test['Date'].dt.year
X_test['Month'] = X_test['Date'].dt.month
X_test['Day'] = X_test['Date'].dt.day
X_test = X_test.drop('Date', axis=1)
# Now try to fit the model again
model.fit(X_train, y_train)
LinearRegression()
现在,你应该看到你的代码在没有问题的情况下运行。让我们告诉 Copilot 这个事实,看看它是否建议我们如何继续。
[提示]
谢谢,这行得通。
[提示结束]
你应该会收到以下类似的回复:
[提示回复]
不客气!很高兴听到它成功了。现在你的模型已经训练好了,下一步通常是使用模型对你的测试数据进行预测。之后,你可以通过比较这些预测与测试数据中的实际值来评估模型性能。如果你有任何更多问题或需要进一步的帮助,请随时提问。
[回复结束]
评估模型
如回复所建议,Copilot 建议询问以下问题:评估回归模型的常见指标有哪些?让我们选择这个问题并看看它说了什么。
评估指标
让我们询问 Copilot 关于评估指标。
[提示]
评估回归模型的常见指标有哪些?
[提示结束]
你应该会收到以下类似的回复:
[提示回复]
评估回归模型性能有几个常见的指标:
-
平均绝对误差(MAE):这是预测值和实际值之间绝对差异的平均值。它给出了预测错误程度的一个概念。
-
均方误差(MSE):这是预测值和实际值之间平方差异的平均值。平方差异放大了较大误差的影响。
-
均方根误差 (RMSE): 这是 MSE 的平方根。它与输出变量的单位相同,比 MSE 更容易解释。
-
R 平方 (R²): 这是根据独立变量预测的因变量变差的比例。它提供了模型预测与实际数据拟合程度的度量。值为 1 表示模型完美地预测了实际值,而值为 0 表示模型根本无法预测实际值。
-
调整后的 R 平方: 这是经过调整的 R 平方,它已经根据模型中的预测因子数量进行了调整。只有当新项比随机预期更好地改进模型时,它才会增加。
这些指标各有其优缺点,选择使用哪个取决于具体问题和分析目标。
[响应结束]
现在我们知道了如何评估以及使用哪些指标,接下来让我们根据我们的测试数据做出预测,并看看我们得到什么响应。
[提示]
使用不同的指标进行预测并评估模型。
[提示结束]
你应该收到以下类似的响应:
[快速响应]
当然,让我们使用训练好的模型对测试数据进行预测。然后,我们将使用平均绝对误差 (MAE)、均方误差 (MSE)、均方根误差 (RMSE) 和 R 平方 (R²) 来评估这些预测。我们将使用 sklearn 的 metrics 模块来计算这些指标。
这是你可以这样做的方法:
from sklearn import metrics
import numpy as np
# Make predictions on the test data
y_pred = model.predict(X_test)
# Calculate MAE, MSE, RMSE, and R2
mae = metrics.mean_absolute_error(y_test, y_pred)
mse = metrics.mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse) # or metrics.mean_squared_error(y_test, y_pred, squared=False)
r2 = metrics.r2_score(y_test, y_pred)
# Print the metrics
print("Mean Absolute Error (MAE):", mae)
print("Mean Squared Error (MSE):", mse)
print("Root Mean Squared Error (RMSE):", rmse)
print("R-squared (R2 ):", r2)
此代码首先使用模型对测试数据进行预测。然后,通过比较这些预测与测试数据中的实际值来计算 MAE、MSE、RMSE 和 R²。最后,它打印出这些指标。
[响应结束]
代码中包含这两个指标,并调用一个 predict() 函数来从模型中产生响应;让我们试试看它是否工作。
from sklearn import metrics
import numpy as np
# Make predictions on the test data
y_pred = model.predict(X_test)
# Calculate MAE, MSE, RMSE, and R2
mae = metrics.mean_absolute_error(y_test, y_pred)
mse = metrics.mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse) # or metrics.mean_squared_error(y_test, y_pred, squared=False)
r2 = metrics.r2_score(y_test, y_pred)
# Print the metrics
print("Mean Absolute Error (MAE):", mae)
print("Mean Squared Error (MSE):", mse)
print("Root Mean Squared Error (RMSE):", rmse)
print("R-squared (R2 ):", r2)
你应该看到以下类似的响应:
Mean Absolute Error (MAE): 0.458844690389933
Mean Squared Error (MSE): 0.4236006011305136
Root Mean Squared Error (RMSE): 0.6508460656180642
R-squared (R2 ): 0.999868004623502
让我们提供以下响应给 Copilot,以了解我们接下来应该做什么。
[提示]
Below is the output, Mean Absolute Error (MAE): 0.458844690389933 Mean Squared Error (MSE): 0.4236006011305136 Root Mean Squared Error (RMSE): 0.6508460656180642 R-squared (R2 ): 0.999868004623502
[提示结束]
你应该收到以下类似的响应:
[快速响应]
太好了!输出显示了你的回归模型的计算指标。
-
平均绝对误差 (MAE): 预测值与实际值之间的平均绝对差异大约为 0.46。这意味着平均而言,你的预测值与实际值相差约 0.46 个单位。
-
均方误差 (MSE): 预测值与实际值之间平均平方差异大约为 0.42。由于它平方了差异,这个指标比 MAE 对大误差更敏感。
-
均方根误差 (RMSE): MSE 的平方根大约为 0.65。这个指标与输出变量的单位相同,比 MSE 更容易解释。
-
R 平方(R²):从独立变量中可预测的因变量变差的比率约为 0.9999。这非常接近 1,表明你的模型几乎解释了响应数据围绕其平均值的几乎所有变异性。
总体而言,这些指标表明你的模型在测试数据上的表现非常好。
[回复结束]
接下来,我们希望通过比较预测值与实际值来了解模型的表现,因此让我们创建一个提示来反映这一请求。
[提示]
显示预测值与实际值的比较。
[提示结束]
你应该收到以下类似的回复:
[提示回复]
当然,你可以通过在图表中可视化它们来比较预测值与实际值。以下是如何使用 matplotlib 进行操作的示例:
import matplotlib.pyplot as plt
# Plot the actual values
plt.scatter(X_test.index, y_test, color='blue', label='Actual')
# Plot the predicted values
plt.scatter(X_test.index, y_pred, color='red', label='Predicted')
plt.title('Actual vs Predicted')
plt.xlabel('Index')
plt.ylabel('Target')
plt.legend()
plt.show()
这段代码创建了一个散点图,其中 x 轴代表测试集的索引,y 轴代表目标变量。实际值用蓝色绘制,预测值用红色绘制。这允许你直观地比较模型做出的预测与实际值。
[回复结束]
建议的代码似乎想要创建一个散点图,以便更好地比较我们的预测值与测试数据:
import matplotlib.pyplot as plt
# Plot the actual values
plt.scatter(X_test.index, y_test, color='blue', label='Actual')
# Plot the predicted values
plt.scatter(X_test.index, y_pred, color='red', label='Predicted')
plt.title('Actual vs Predicted')
plt.xlabel('Index')
plt.ylabel('Target')
plt.legend()
plt.show()
以上代码的输出如下:

图 18.3:实际值与预测值比较
我们可以看到预测数据与实际数据非常吻合。接下来,让我们尝试总结我们的学习成果。
作业
不使用回归来预测一个值,而是将相同的数据视为一个分类问题,并预测股价是否将在第二天上涨或下跌。
摘要
回归是机器学习中的一个基本概念,用于根据一个或多个预测变量预测一个连续的因变量。它涉及识别因变量(通常称为目标)与一个或多个自变量(特征)之间的关系。我们看到了,在我们的数据集中,我们能够找到某些变量的相关性。我们还发现,我们可以包括如日期这样的列,但要包括这些列,我们需要从这些列中提取重要的数值部分,即年份、月份和日期。
回归在医疗保健和市场营销等其他领域有许多应用。从提示的角度来看,尽早设置上下文并展示 Copilot 数据的形状是个好主意,这样可以帮助你询问 Copilot 下一步该做什么。
在下一章中,我们将使用相同的数据集,同时使用 GitHub Copilot 帮助我们编写一些代码。
加入我们的 Discord 社区
加入我们的社区 Discord 空间,与作者和其他读者进行讨论:

第十九章:基于 Copilot 建议的回归
简介
在上一章中,我们使用 GitHub Copilot Chat 构建了一个回归问题,并探讨了人工智能如何协助编程。在本章中,我们将采取不同的方法。我们将借助 GitHub Copilot 编写代码,让它引导我们进行编码并添加有用的注释。这将是一种交互式体验,结合我们的编码技能和 Copilot 的建议,有效地解决回归问题。让我们看看 GitHub Copilot 如何实时增强我们的编码过程。
在这个任务中,我们将使用苹果公司(AAPL)的数据集来预测股票价格,并帮助投资者决定是否买入。这是我们之前在第十八章,基于 Copilot Chat 的回归中使用的数据集,当时我们使用 Copilot Chat 来分析它。
数据集概述
这个数据集为我们提供了关于苹果公司(AAPL)过去十年股票(交易代码为 AAPL)的大量信息,始于 2010 年。这些数据极其宝贵,因为它可以帮助我们开发预测算法,预测苹果公司股票的未来价格,这对于投资决策至关重要。这个数据集中的数据是从 25 个不同的证券交易所收集和汇总的。
为了有效地使用这些数据来进行预测,我们需要理解关键要素:影响我们目标(预测股票价格)的特征。
数据集包括五个重要值,这些值表示股票价格在特定时间段内的变化情况,通常是整天,但也可能是一周或一个月。这些值包括:
-
开盘价:这是交易日开始时的股票价格。
-
收盘价:这是交易日结束时的股票价格。
-
高:此值表示股票在交易日达到的最高价格。
-
低:这表示股票在交易日达到的最低价格。
-
成交量:这是在市场收盘前一天内交易的总股数。
我们将重点关注使用历史价格数据,包括这五个记录的值,以及股票过去的业绩,来预测其未来的价格。
提示策略
在本章中,我们将使用第二章中描述的“探索性提示模式”。我们对想要采取的一般行动相当有信心,但很感兴趣 AI 助手在需要转向时会产生什么。
在 Copilot 的帮助下开始编码
安装了 GitHub Copilot 后,你就可以开始用 Python 编写你的回归问题了。在打开的文件中使用建议的方法与使用聊天窗口的方法略有不同。主要区别在于,我们将依赖输入注释并使用Tab键让 Copilot 提供建议。这种技术在 Jupyter Notebook 和普通文本文件中都同样有效。以下是具体操作方法:
# generate import statements for libraries to use with a Regression problem
在这里,我们有一个打开的文本文件。我们已经添加了注释来生成import语句。为了使 Copilot 生成响应,我们需要使用回车键后跟Tab键来接受建议。上面的文本文件可能如下所示:
# generate import statements for libraries to use with a Regression problem
import pandas
import numpy
import scikit-learn
当按下Tab键后跟回车键时,import语句是由 Copilot 生成的。
人工智能助手通过提供代码补全来帮助,但请记住在接受每个建议之前进行审查和理解。
第 1 步:在 Copilot 的帮助下导入库
我们可以开始用一个描述问题的提示建议注释,包括数据集以及更多内容,就像我们在本书的其他章节中使用 Copilot Chat 一样。然后,提示可能看起来像这样:
# generate the steps I need to follow for performing a Regression problem, here's my dataset and its columns
然而,我们将尝试一种方法,即请求我们所需的一切。
让我们开始吧。让我们创建我们的第一个评论(这相当于在聊天窗口中输入):
[提示]
import pandas
[提示结束]
使用Tab键接受建议。你应该看到以下加粗代码:
[提示响应]
# import pandas
import pandas as pd
[响应结束]
太好了!我们已经导入了所需的库。接下来,让我们加载数据集并探索。
第 2 步:加载数据集并探索
在下一步中,我们想要加载数据集并探索数据特征。继续在同一个文件上工作,并添加以下提示:
[提示]
# Load the dataset into a pandas DataFrame
[提示结束]
在这一点上,Copilot 理解我们试图做什么,所以我们不需要输入比上面的提示评论更多的内容。因此,我们只需要按下回车键,然后多次按下Tab键,我们应该会得到以下内容:
[提示响应]
# Load the dataset into a pandas DataFrame
df = pd.read_csv('enter-your-file-path')
# Print the first 5 rows of the DataFrame
print(df.head())
[响应结束]
确保您已从 Kaggle 下载了数据集(www.kaggle.com/datasets/varpit94/apple-stock-data-updated-till-22jun2021)并且您已将read_csv中的路径替换为您系统中的文件位置。
如果您运行建议的代码,您应该看到类似以下输出:
Date Close Volume Open High Low
0 02/28/2020 $273.36 106721200 $257.26 $278.41 $256.37
1 02/27/2020 $273.52 80151380 $281.1 $286 $272.96
2 02/26/2020 $292.65 49678430 $286.53 $297.88 $286.5
3 02/25/2020 $288.08 57668360 $300.95 $302.53 $286.13
4 02/24/2020 $298.18 55548830 $297.26 $304.18 $289.23
如果您看到上述内容,这意味着数据已成功加载。
获取类型和列
太好了!现在我们已经成功加载数据,让我们更多地了解加载数据集,例如它有哪些列和列类型。像往常一样,让我们继续在同一个文件上工作,并添加如下提示的注释:
[提示]
# Get the data types of the columns in the dataset
[提示结束]
你应该看到以下结果:
[提示响应]
# Get the data types of the columns in the dataset
print(df.dtypes)
[响应结束]
现在,让我们运行代码。它应该显示我们的列及其数据类型:
Date object
Close object
Volume int64
Open object
High object
Low object
dtype: object
太好了!我们看到列,但许多列的类型是object。这不太好;它们需要是数值型才能用于机器学习训练。让我们看看我们是否能很快解决这个问题,但首先,让我们更多地了解数据集的形状。
数据集形状
让我们更多地了解我们的数据集在结构方面的信息,比如它由多少列和多少行组成。这将帮助我们评估其复杂性,了解我们有多少数据,以及是否足够训练一个可靠的模型。让我们写下我们的下一个提示注释:
[提示]
# Get the shape of the dataset
[提示结束]
你应该看到一个以下的建议:
[提示响应]
# Get the shape of the dataset
print(df.shape)
[响应结束]
运行上面的代码应该提供以下的响应:
(2518, 6)
这个响应告诉我们我们有 2518 行和 6 列。
处理列类型
在这一点上,我们知道我们需要更多的数值列,所以让我们处理那些非数值列。首先,让我们确定这些非数值列:
[提示]
# print the columns in the dataset
[提示结束]
你应该会看到一个类似以下的建议:
[提示响应]
# print the columns in the dataset
print(df.columns)
Index(['Date', 'Close', 'Volume', 'Open', 'High', 'Low'], dtype='object')
[响应结束]
虽然我们刚刚要求打印列,但 Copilot 决定只显示object类型的列。这并不令人惊讶,因为 Copilot 接受了机器学习的训练,能够推断出接下来会发生什么,在这种情况下是转换列以用于模型训练。
接下来,让我们更加具体地要求将列转换为数值版本,以下为提示注释:
[提示]
# Replace the $ symbol with an empty string and convert the column to float data type
[提示结束]
你应该会看到以下代码,它将多个列转换为数值版本。
[快速响应]
# Replace the $ symbol with an empty string and convert the column to float data type
df['Open'] = df['Open'].replace({'\$': ''}, regex=True).astype(float)
df['Close'] = df['Close'].replace({'\$': ''}, regex=True).astype(float)
df['High'] = df['High'].replace({'\$': ''}, regex=True).astype(float)
df['Low'] = df['Low'].replace({'\$': ''}, regex=True).astype(float)
[响应结束]
太好了!接下来,让我们检查旁边的列,以确保转换已经正确执行。
[提示]
# Print the first 5 rows of the DataFrame to confirm the changes
[提示结束]
你应该会看到一个类似的响应:
[提示响应]
# Print the first 5 rows of the DataFrame to confirm the changes
print(df.head())
[响应结束]
如果你运行上面的代码,你应该会看到以下的响应:
Date Close Volume Open High Low
0 02/28/2020 273.36 106721200 257.26 278.41 256.37
1 02/27/2020 273.52 80151380 281.10 286.00 272.96
2 02/26/2020 292.65 49678430 286.53 297.88 286.50
3 02/25/2020 288.08 57668360 300.95 302.53 286.13
4 02/24/2020 298.18 55548830 297.26 304.18 289.23
我们可以清楚地看到美元符号已被移除,列现在是数值的,除了日期列,我们稍后再处理。
统计摘要
接下来,让我们通过请求一个统计摘要来更多地了解数据集。我们预计会看到以下值:
-
计数:非空条目数量
-
平均值 (mean):平均值
-
标准差 (std):数据的分散程度
-
最小值 (min):最小值
-
25% 分位数 (25%):25% 的数据值低于此值
-
中位数 (50%):中间值,也称为第 50 百分位数
-
75% 分位数 (75%):75% 的数据值低于此值
-
最大值 (max):最大值
考虑到这一点,让我们编写一个提示注释:
[提示]
# get the statistical summary of the dataset
[提示结束]
你应该会看到类似以下的输出:
[快速响应]
# get the statistical summary of the dataset
print(df.describe())
[响应结束]
如果运行代码,你会看到类似以下的数据:
Close Volume Open High Low
count 2518.000000 2.518000e+03 2518.000000 2518.000000 2518.000000
mean 114.769522 7.258009e+07 114.728443 115.766415 113.690582
std 60.662405 5.663113e+07 60.546893 61.134456 60.085105
min 29.835700 1.136205e+07 29.392800 29.928600 28.464300
25% 66.822475 3.053026e+07 66.877150 67.475300 66.372950
50% 101.090000 5.295469e+07 101.115000 102.085000 100.350000
75%
检查缺失值
识别缺失值以评估数据集的质量至关重要。大量空值可能表明数据收集或输入存在问题,并可能影响我们的模型,因此我们需要一种处理它们的方法:
[提示]
检查缺失值
[提示结束]
你应该会看到一个类似的响应:
[提示响应]
# check for missing values
print(df.isnull().sum())
[响应结束]
太好了!那么,代码做了什么?好吧,让我们来解释。假设你有一个以下的数据集:
data = {'A': [7, 99, None, 3, None],
'B': [5, None, 6, 8, 9],
'C': [10, 15, 12, None, 11]}
运行建议的代码将总结每列的缺失值数量,你会看到类似以下的响应:
A: 2
B: 1
C: 1
你可以做的就是在不影响整体分数的情况下替换缺失值。一种方法是将平均值添加到缺失值中,并用它替换None。
检查重复项
另一种清理数据的方法是删除重复数据,因为你不希望这些数据对模型产生强化作用。让我们为这一点添加一个注释,看看 Copilot 能否帮助我们:
[提示]
print(df.duplicated().sum())
[提示结束]
到目前为止,Copilot 理解我们想要做什么,那就是识别和删除重复项。所以,你应该会看到以下两个操作的代码建议:
[提示响应]
# check for duplicates
print(df.duplicated().sum())
# drop duplicates
df.drop_duplicates(inplace=True)
[响应结束]
缩放数值特征
接下来,我们将对值进行缩放,使它们落入特定的范围,0–1。这种预处理将对许多事情产生有益的影响,例如算法性能、收敛速度、可解释性和防止一组值主导。让我们创建一个缩放特征的提示:
[提示]
# Scale numerical features
[提示结束]
注意,当响应中有多于一行时,你可能需要反复按Return和Tab键。你应该看到类似以下的输出:
[提示响应]
# Scale numerical features
# import StandardScaler
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df[['Open', 'Close', 'High', 'Low', 'Volume']] = scaler.fit_transform(df[['Open', 'Close', 'High', 'Low', 'Volume']])
# Print the first 5 rows of the DataFrame to confirm the changes
print(df.head())
[响应结束]
运行建议的代码应该得到以下输出:
Date Close Volume Open High Low
0 02/28/2020 2.614832 0.602988 2.354536 2.660953 2.375094
1 02/27/2020 2.617470 0.133721 2.748359 2.785130 2.651257
2 02/26/2020 2.932884 -0.404481 2.838059 2.979494 2.876649
3 02/25/2020 2.857534 -0.263366 3.076269 3.055571 2.870490
4 02/24/2020 3.024063 -0.300800 3.015313 3.082566 2.922093
可视化
接下来,我们想要可视化我们的数据。我们可以给出一些模糊的指令来说明如何进行,但在这个案例中,散点图是一个很好的工具,所以让我们在提示中反映这一点:
[提示]
# Create a scatter plot of the Open and Close columns
[提示结束]
你应该看到类似以下的响应:
[提示响应]
import matplotlib.pyplot as plt
# Create a scatter plot of the Open and Close columns
plt.scatter(df['Open'], df['Close'])
plt.xlabel('Open')
plt.ylabel('Close')
plt.title('Relationship between Open and Close')
plt.show()
[响应结束]
运行上面的代码应该会得到一个类似这样的散点图:

图 19.1:显示开盘价和收盘价之间关系的散点图
第 3 步:将数据分为训练集和测试集
在我们能够训练模型之前,我们需要将数据分为训练数据和测试数据。让我们创建一个提示来帮助完成这项工作:
[提示]
“使用 scikit-learn 中的 train_test_split 来分割数据集”
[提示结束]
你应该看到类似以下的输出:
[提示响应]
# Use train_test_split from scikit-learn to split the dataset
from sklearn.model_selection import train_test_split
# Drop the Date column from the DataFrame
df = df.drop('Date', axis=1)
# Split the dataset into training and testing sets
X = df.drop('Close', axis=1)
y = df['Close']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
[响应结束]
# Print the shapes of the training and testing sets
print('X_train shape:', X_train.shape)
print('y_train shape:', y_train.shape)
print('X_test shape:', X_test.shape)
print('y_test shape:', y_test.shape)
如果你运行上面的代码,你应该看到类似以下的响应:
X_train shape: (2014, 4)
y_train shape: (2014,)
X_test shape: (504, 4)
y_test shape: (504,)
提问
就像在 Copilot 的聊天体验中一样,可以在特定主题上提问。如果你在问题前加上“Q:”,Copilot 就会理解它是一个问题。让我们试一试,找出我们的下一步:
[提示]
问题:在将数据分为训练集和测试集之后下一步是什么?
[提示结束]
你应该看到类似以下的响应:
[提示响应]
# Q: what is the next step after splitting the data into training and testing sets?
# A: train the model using the training set and test the model using the tesing set
[响应结束]
第 4 步:构建回归问题
现在是训练模型的时候了。你可以相当模糊地询问 Copilot 进行训练。然而,建议你提出你想要的训练类型。在这种情况下,我们想要线性回归,所以让我们创建一个包含该内容的提示:
[提示]
“初始化一个线性回归模型”。
[结束提示]
你应该看到类似以下的响应:
[提示响应]
# initialize a LinearRegression model
# import LinearRegression
from sklearn.linear_model import LinearRegression
# Instantiate LinearRegression as lr
lr = LinearRegression()
[结束建议响应]
太好了!我们有一些代码确保我们使用线性回归。尽管如此,我们还没有进行任何训练,所以这是下一步。
第 5 步:训练模型
根据指定的训练类型,接下来让我们训练模型:
[提示]
“拟合模型”
[结束提示]
你应该看到类似以下的响应:
[提示响应]
# Fit lr to the training data
lr.fit(X_train, y_train)
# Predict the labels of the test set
y_pred = lr.predict(X_test)
[结束响应]
第 6 步:评估模型性能
太好了!我们已经训练了模型,但我们不知道它的表现如何,这是我们接下来应该解决的问题:
[提示]
评估性能指标,如 MSE 或 R 平方。
[结束提示]
你应该看到类似的输出:
[提示响应]
#evaluate the performance metrics like MSE or R-squared
from sklearn.metrics import mean_squared_error
# Calculate the Mean Squared Error (MSE) of the model on the testing set
mean_squared_error = mean_squared_error(y_test, y_pred)
print('Mean squared error:', mean_squared_error)
# Calculate the Root Mean Squared Error (RMSE) of the model on the testing set
root_mean_squared_error = np.sqrt(mean_squared_error)
print('Root mean squared error:', root_mean_squared_error)
[结束响应]
如果你运行上述代码,你应该看到如下响应:
MSE: 0.00011569609998631774
RMSE: 0.010756212157926123
我们得到的输出代表两种不同的误差:均方误差和均方根误差。
较低的 MSE 表明模型与数据的拟合度更好。在这种情况下,MSE 相当低,表明模型的预测非常接近实际值。
RMSE 值也非常低,这进一步证实了模型的预测非常准确。太好了!让我们回顾本章的作业,然后总结一下我们关于使用 Copilot 帮助机器学习的所学内容。
作业
尝试使用提示方法解决这个问题,你需要在开始时提供大量信息。我们建议构建一个如下所示的提示:
-
“在以下形状的数据集上执行回归:
-
开盘价:这是交易日开始的股票价格。
-
收盘价:这代表了交易日结束时的股票价格。
-
高:这个值显示了股票在交易日内达到的最高价格。
-
低:这表示股票在交易日内达到的最低价格。
-
成交量:这是在市场收盘前一天内交易的总股数。
建议从加载数据和预处理到训练和评估模型的全部步骤。你必须为每个步骤展示代码。”
然后查看响应,并尝试为每个步骤运行建议的代码片段。如果你遇到任何问题,可以用如下问题提示向 Copilot 报告错误:
- “问题:以下/以上代码不工作,请修复”
不要忘记按Enter和Tab键接受完成。
摘要
在本章中,我们想使用 GitHub Copilot 的建议功能,这意味着我们会输入注释并使用 回车 和 Tab 键来接收 Copilot 的建议。这里有点技巧,因为有时你需要反复按 回车 和 Tab 键才能获得完整的响应。它也是一种非常适合你在积极编写代码时的 AI 体验。GitHub Copilot Chat 也有其位置。实际上,这两种不同的体验是互补的;选择你想要使用多少每种方法。此外,始终测试 Copilot 提出的代码,并在需要时让 Copilot 修复代码输出。
加入我们的 Discord 社区
加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

第二十章:利用 GitHub Copilot 提高效率
简介
到目前为止,你一直在使用书中开头介绍的关于 GitHub Copilot 和 ChatGPT 的知识。这些基础知识足以教你如何编写提示并接受它们。这也足以让你开始着手解决机器学习、数据科学和 Web 开发的问题。在 Web 开发的情况下,你也发现 Copilot 在处理现有代码库时是一个高效的工具。在本章中,我们希望将你的 AI 工具知识提升到下一个层次,因为还有更多你可能想要利用的功能。
有很多方法可以提高效率;你将在本章后面看到,Copilot 中有一些功能可以让你构建文件,你还将了解更多关于你的工作空间,甚至作为编辑器的 Visual Studio Code 的信息,这些都是节省时间的功能。本章将介绍一些最重要的功能。
在本章中,我们将:
-
学习如何使用 Copilot 生成代码。
-
使用 Copilot 命令来自动化任务,例如生成一个新项目。
-
应用调试和故障排除代码的技术。
-
使用 Copilot 审查和优化代码。
代码生成和自动化
在其核心,Copilot 是一个代码生成器。它可以为你生成文本,这些文本可以是文档或源代码的一部分。
使用 Copilot 生成代码有两种主要方式:
-
通过提示作为注释的 Copilot 活动编辑器。
-
允许你输入提示的 Copilot 聊天。
Copilot 的活动编辑器
当你在活动编辑器中时,Copilot 可以生成代码。要生成代码,你需要以注释的形式编写一个提示。在你的编程语言代码中添加注释有不同的方式。例如,在 JavaScript 和 C# 中,你使用 //,而在 HTML 中你会输入 <!--。
让我们看看一个例子:
[提示]
创建一个名为 add 的新函数,它接受两个参数。
[提示结束]
//Create a new function called add that takes two parameters
上面,我们有一个作为注释的提示。Copilot 会查看这个提示,并尝试根据它生成代码。在这个阶段,Copilot 会将生成的代码作为“建议”发送回来,你可以使用 Tab 键接受它。在编辑器中看起来是这样的:
// Create a new function called add that takes two parameters
function add(num1, num2) {
throw new Error('Function not implemented.');
}
到目前为止,你没有收到任何建议。你有几个选择:
-
你可以按
Return键获取更多建议。 -
开始输入你想要生成的代码的一部分,Copilot 将根据你输入的内容建议代码。
Copilot 聊天
Copilot Chat 是一种通过聊天界面与 Copilot 交互的方式。要激活 Copilot Chat,点击 Visual Studio Code(或 VS Code)左下角的图标,这将打开一个聊天窗口。

图 20.1:显示 Visual Studio Code 中的 Copilot 聊天“聊天”图标左侧菜单
点击上面的聊天图标后,将显示下面的图片。

图 20.2:上图显示了您在 Visual Studio Code 左侧菜单中点击聊天图标时打开的聊天窗口
让我们尝试根据之前的相同提示生成一些代码。让我们使用聊天窗口中的输入文本字段并输入提示:
[提示]
创建一个名为 add 的新函数,它接受两个参数
[指令结束]
输出分为两部分,一个是文本指令,另一个是代码建议。首先,文本指令:
[提示响应]
当然,这是一个名为 add 的函数的示例,它接受两个参数并返回它们的和:
function add(a, b) {
return a + b;
}
[响应结束]
它甚至显示了如何使用该函数的示例:
const result = add(2, 3);
console.log(result); // Output: 5
Copilot 命令
如果您在聊天窗口中开始输入 /,您将看到可以使用 Copilot 的命令列表。

图 20.3:可用命令
有很多可用的命令,在本章中我们将关注其中的一些。
创建笔记本
使用此命令,您可以创建一个新的笔记本。笔记本是一个您可以存储代码片段和文本的地方。要创建笔记本,请输入 /newNotebook,并为笔记本提供一个名称,例如,products。

图 20.4:创建笔记本
上图显示了您输入 /newNotebook 命令时显示的内容;您需要通过点击 创建笔记本 来确认笔记本的框架。
这将创建一个包含 products 常用代码片段的新笔记本。接下来,让我们检查输出。
您的笔记本,从高层次来看,包含以下内容:
-
导入,包含处理数据集所需的所有导入;内容如下所示:
import pandas as pd # Import Pandas library import numpy as np # Import NumPy library import matplotlib.pyplot as plt # Import Matplotlib library for data visualization import seaborn as sns # Import Seaborn library for data visualization -
加载数据,包含从 CSV 文件(CSV 文件
product_data.csv不存在,但 Copilot 假设您有这样一个文件;调整此部分的代码以适应您拥有的 CSV 文件名称)加载数据的代码:# Load product data from CSV file into a Pandas DataFrame product_data = pd.read_csv('product_data.csv') # Load product data from JSON file into a Pandas DataFrame # product_data = pd.read_json('product_data.json') # Display the first 5 rows of the DataFrame print(product_data.head()) -
探索产品数据,包含检查数据的操作:
# Use Pandas functions to explore the product data # Display the last 5 rows of the DataFrame print(product_data.tail()) # Display the summary information of the DataFrame print(product_data.info()) # Display the statistical summary of the DataFrame print(product_data.describe()) -
过滤产品数据,包含过滤数据的操作:
# Filter product data based on specific criteria using boolean indexing filtered_data = product_data[(product_data['category'] == 'electronics') & (product_data['price'] < 100)] # Display the filtered data print(filtered_data) -
数据排序,包含排序操作:
# Sort the product data based on one or more columns using the sort_values() function # Sort by price in ascending order product_data.sort_values(by='price', ascending=True, inplace=True) # Sort by category in ascending order, then by price in descending order product_data.sort_values(by=['category', 'price'], ascending=[True, False], inplace=True) # Display the sorted data print(product_data) -
数据聚合;通常,您希望聚合数据以更好地理解它:
# Group the product data by category and calculate the total price for each category category_total_price = product_data.groupby('category')['price'].sum() # Display the total price for each category print(category_total_price) # Group the product data by category and calculate the average price for each category category_avg_price = product_data.groupby('category')['price'].mean() # Display the average price for each category print(category_avg_price) # Group the product data by category and calculate the number of products in each category category_product_count = product_data.groupby('category')['product_id'].count() # Display the number of products in each category print(category_product_count)
总结来说,这些都是您不必自己编写的代码。您可以使用这个笔记本作为处理产品数据的起点。
创建项目
另一个非常有用的命令是能够创建项目。通常,我们要么手动创建文件,这需要时间,要么依赖 CLI 工具为我们创建项目。使用 Copilot,您可以在聊天窗口中输入 /new 来创建项目。要使用此命令,请输入 /new 并为其提供描述,例如,React。这将创建一个带有 React 项目模板的新项目。让我们检查输出:
`my-react-app`
`├── public`
`│ ├── index.html`
`│ └── favicon.ico`
`├── src`
`│ ├── App.js`
`│ ├── index.js`
`│ ├── components`
`│ │ ├── Customer.js`
`│ │ ├── Product.js`
`│ │ ├── CustomerList.js`
`│ │ └── ProductList.js`
`│ ├── data`
`│ │ ├── customers.json`
`│ │ └── products.json`
`│ └── styles`
`│ ├── App.css`
`│ ├── Customer.css`
`│ ├── Product.css`
`│ ├── CustomerList.css`
`│ └── ProductList.css`
`├── package.json`
`└── README.md`
上文创建的文件是 React 项目中预期的典型文件。它包含:
-
public,其中包含index.html文件,该文件引用 JavaScript 文件,指示应用程序的位置。 -
src,它包含所有应用程序文件,并且自身被分为不同的文件夹,用于组件、数据和 CSS 样式。
所有这些构成了一个起点。你可能希望根据你的喜好更改文件。
调试和故障排除
能够调试和故障排除代码是一项重要的技能。当你开发代码时,它很少第一次就能做到你想要的效果。你需要能够调试和故障排除代码,以找出问题所在。如果你有一个报告的 bug,也是如此;你需要能够诊断它在哪个点出错。
Copilot 可以帮助,一个好的方法就是使用 Copilot 聊天,并要求它添加对调试和故障排除的支持。
以下是你被给出的一个示例代码。这是一个用于 Python 的 Flask 框架中的 REST API。这是一段可以工作的代码,但从调试和故障排除的角度来看可能并不容易使用:
from flask import Flask, jsonify, request
import sqlite3
app = Flask(__name__)
# Create a connection to the database
conn = sqlite3.connect('products.db')
c = conn.cursor()
# Create a table for products if it doesn't exist
c.execute('''CREATE TABLE IF NOT EXISTS products
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
price REAL)''')
conn.commit()
@app.route('/products', methods=['GET'])
def get_products():
# Retrieve all products from the database
c.execute('SELECT * FROM products')
products = c.fetchall()
# Convert the products to a JSON response
response = []
for product in products:
response.append({
'id': product[0],
'name': product[1],
'price': product[2]
})
return jsonify(response)
@app.route('/products', methods=['POST'])
def add_product():
# Retrieve the product data from the request body
data = request.get_json()
name = data['name']
price = data['price']
# Insert the product into the database
c.execute('INSERT INTO products (name, price) VALUES (?, ?)', (name, price))
conn.commit()
# Return a success message
return jsonify({'message': 'Product added successfully'})
if __name__ == '__main__':
app.run(debug=True)
上述代码可以工作,但它不支持调试和故障排除;让我们看看如何通过使用 Copilot Chat 来添加对该功能的支持:
[提示]
添加对故障排除和调试的支持
[提示结束]
Copilot 聊天以文本和代码的形式回答我们:
[提示响应]
当然,这是一个包含故障排除和调试支持的 Flask API 更新版本:
from flask import Flask, jsonify, request
import sqlite3
app = Flask(__name__)
# Create a connection to the database
conn = sqlite3.connect('products.db')
c = conn.cursor()
# Create a table for products if it doesn't exist
c.execute('''CREATE TABLE IF NOT EXISTS products
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
price REAL)''')
conn.commit()
@app.route('/products', methods=['GET'])
def get_products():
try:
# Retrieve all products from the database
c.execute('SELECT * FROM products')
products = c.fetchall()
# Convert the products to a JSON response
response = []
for product in products:
response.append({
'id': product[0],
'name': product[1],
'price': product[2]
})
return jsonify(response)
except Exception as e:
# Log the error and return an error message
app.logger.error(str(e))
return jsonify({'error': 'An error occurred while retrieving products'})
@app.route('/products', methods=['POST'])
def add_product():
try:
# Retrieve the product data from the request body
data = request.get_json()
name = data['name']
price = data['price']
# Insert the product into the database
c.execute('INSERT INTO products (name, price) VALUES (?, ?)', (name, price))
conn.commit()
# Return a success message
return jsonify({'message': 'Product added successfully'})
except Exception as e:
# Log the error and return an error message
app.logger.error(str(e))
return jsonify({'error': 'An error occurred while adding the product'})
if __name__ == '__main__':
app.run(debug=True)
改进如下:
-
在
get_products()函数中的代码周围添加了try/except块。 -
添加了
logger语句来记录错误。 -
在
app.run()语句中添加了debug=True。
这些更改将帮助我们进行故障排除和调试代码。
[响应结束]
让我们看看如何使用 Copilot 帮助我们进行代码审查和优化。
代码审查和优化技术
目前,我们有一段可以工作的代码,但它还没有优化。例如,通常将代码分离到不同的文件中是一个好主意。让我们看看 Copilot 建议如何改进代码。
[提示]
改进选定的代码
[提示结束]
如同往常,我们以文本和代码的形式获取 Copilot 聊天响应:
[提示响应]
当然,以下是对所选代码的一些改进建议:
-
使用上下文管理器来处理数据库连接和游标。这确保了当代码执行完毕时,连接被正确关闭,即使发生异常也是如此。
-
使用列表推导式将产品转换为 JSON 响应。这比使用 for 循环更简洁,可能更快。
-
使用 Response 对象的 json 参数返回 JSON 响应。这比使用 jsonify 函数更简洁。
-
使用 request.json 属性从请求体中检索产品数据。这是一种更简洁、更 Pythonic 的方法。
以及生成的代码:
from flask import Flask, Response, request
import sqlite3
app = Flask(__name__)
# Create a table for products if it doesn't exist
with sqlite3.connect('products.db') as conn:
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS products
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
price REAL)''')
conn.commit()
@app.route('/products', methods=['GET'])
def get_products():
try:
# Retrieve all products from the database
with sqlite3.connect('products.db') as conn:
conn.row_factory = sqlite3.Row
c = conn.cursor()
c.execute('SELECT * FROM products')
products = c.fetchall()
# Convert the products to a JSON response
response = [{'id': product['id'], 'name': product['name'], 'price': product['price']} for product in products]
return Response(response=response, status=200, mimetype='application/json')
except Exception as e:
# Log the error and return an error message
app.logger.error(str(e))
return Response(response={'error': 'An error occurred while retrieving products'}, status=500, mimetype='application/json')
@app.route('/products', methods=['POST'])
def add_product():
try:
# Retrieve the product data from the request body
data = request.json
name = data['name']
price = data['price']
# Insert the product into the database
with sqlite3.connect('products.db') as conn:
c = conn.cursor()
c.execute('INSERT INTO products (name, price) VALUES (?, ?)', (name, price))
conn.commit()
# Return a success message
return Response(response={'message': 'Product added successfully'}, status=201, mimetype='application/json')
except Exception as e:
# Log the error and return an error message
app.logger.error(str(e))
return Response(response={'error': 'An error occurred while adding the product'}, status=500, mimetype='application/json')
if __name__ == '__main__':
app.run(debug=True)
[响应结束]
上述最大的变化是使用上下文管理器来处理数据库连接和游标。这确保了当代码执行完毕时,连接会被正确关闭,即使发生异常也是如此。
我们仍然希望将代码分离到不同的文件中;让我们看看 Copilot 对此有什么建议。
让我们从以下提示开始:
[提示]
根据职责将代码分离到不同的文件中
[结束提示]
以及生成的代码:
[提示响应]
# app.py
from flask import Flask
from products import products_bp
app = Flask(__name__)
app.register_blueprint(products_bp)
if __name__ == '__main__':
app.run(debug=True)
# database.py
import sqlite3
def get_db():
db = sqlite3.connect('products.db')
db.row_factory = sqlite3.Row
return db
# products.py
from flask import Blueprint, jsonify, request
from database import get_db
products_bp = Blueprint('products', __name__)
@products_bp.route('/products', methods=['GET'])
def get_products():
try:
# Retrieve all products from the database
db = get_db()
products = db.execute('SELECT * FROM products').fetchall()
# Convert the products to a JSON response
response = [{'id': product['id'], 'name': product['name'], 'price': product['price']} for product in products]
return jsonify(response)
except Exception as e:
# Log the error and return an error message
app.logger.error(str(e))
return jsonify({'error': 'An error occurred while retrieving products'}), 500
@products_bp.route('/products', methods=['POST'])
def add_product():
try:
# Retrieve the product data from the request body
data = request.json.get('data', {})
name = data.get('name', '')
price = data.get('price', 0)
# Insert the product into the database
db = get_db()
db.execute('INSERT INTO products (name, price) VALUES (?, ?)', (name, price))
db.commit()
# Return a success message
return jsonify({'message': 'Product added successfully'}), 201
except Exception as e:
# Log the error and return an error message
app.logger.error(str(e))
return jsonify({'error': 'An error occurred while adding the product'}), 500
[结束响应]
如果在运行代码时收到任何错误,请咨询 Copilot,告诉它有错误,并要求它解决问题。请记住,Copilot 不仅仅是一个代码生成器。
我们现在看到的是关注点的分离。app.py 文件负责启动应用程序并注册蓝图。database.py 文件负责处理数据库连接。products.py 文件负责处理 products API。这段代码是我们可以操作和维护的。
通过将 database.py 更改为使用上下文管理器,可以进一步改进此代码:
import sqlite3
def get_db():
with sqlite3.connect('products.db') as db:
db.row_factory = sqlite3.Row
return db
def init_db():
with sqlite3.connect('products.db') as db:
db.execute('''CREATE TABLE IF NOT EXISTS products
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
price REAL)''')
db.commit()
# other code omitted for brevity
在这里,您看到了 Copilot 如何逐步审查您的代码,将单个文件拆分为多个文件,然后改进代码。
工作空间
您的工作空间是您在目录中打开的所有文件。您希望您的 AI 助手在提供推荐时考虑所有这些文件。让我们看看以下存在的以下文件的网络项目:
`src/`
`app.py`
`utils.py`
app.py 包含一个 API,而 utils.py 包含一个电子邮件验证函数。以下是 app.py 的代码:
# create a web api using Flask, should have a products GET route
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/customers, methods=['GET'])
def get_customers():
return jsonify({'customers': ['item1', 'item2', 'item3']})
# TODO, create a POST route for products
# Run the app
if __name__ == '__main__':
app.run(debug=True)
注意到 TODO 注释;我们即将添加一个存储客户的路由。在存储此客户时,我们需要确保客户数据包含一个有效的电子邮件。幸运的是,utils.py 文件已经包含这样一个函数,但我们能否让 Copilot 使用它?让我们看看。
将 app.py 中的注释更改为以下内容:
[提示]
TODO,为客户创建一个 POST 路由。它应该验证请求体,并确保它有一个有效的电子邮件地址。
[结束提示]
你得到一个基本函数,它实际上并没有做它应该做的事情:
[提示响应]
@app.route('/customers', methods=['POST'])
def post_customers():
return jsonify({'message': 'success'})
[结束响应]
为了帮助解决这个问题,您求助于 GitHub Chat 并输入以下提示:
[提示]
验证请求体并确保它有一个有效的电子邮件地址
[结束提示]
[提示响应]
要验证请求体并确保它有一个有效的电子邮件地址,您可以使用 Flask 的 request 对象和一个正则表达式。以下是您如何修改您的 post_customers 函数的方法:
from flask import request
import re
@app.route('/customers', methods=['POST'])
def post_customers():
data = request.get_json()
if 'email' not in data:
return jsonify({'message': 'No email provided'}), 400
email = data['email']
pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
if not re.match(pattern, email):
return jsonify({'message': 'Invalid email format'}), 400
return jsonify({'message': 'success'})
[结束响应]
虽然上述代码可以工作,但它没有利用我们现有的 utils.py 文件,其中已经有一个函数可以解决验证电子邮件的问题。那么我们如何让 Copilot 考虑该文件呢?答案在于一个名为 @workspace 的命令。@workspace 命令考虑所有文件。以下是它的用法:
[提示]
@workspace 改进选定的代码以验证请求体,确保它包含有效的电子邮件地址,主要使用工作区中其他文件的功能来解决问题。
[提示结束]

图 20.5:Copilot Chat 中的图像,展示了如何使用 @workspace 命令利用工作区中现有的代码和代码文件来解决问题,而不是创建新的代码。
让我们也在代码中展示响应并讨论我们可以看到的内容:
[提示响应]
from flask import request
from utils import validate_email
@app.route('/customers', methods=['POST'])
def post_customers():
data = request.get_json()
if 'email' not in data:
return jsonify({'message': 'No email provided'}), 400
email = data['email']
if not validate_email(email):
return jsonify({'message': 'Invalid email format'}), 400
return jsonify({'message': 'success'})
[响应结束]
在上面的代码中,validate_email 函数位于 utils.py 文件中,用于验证电子邮件。这对于第一次尝试来说很重要,解决这个问题导致了 Copilot 重复代码。因此,当你想让 Copilot 考虑工作区中的所有代码来建议解决方案时,建议使用 @workspace。
Visual Studio Code 查找
Copilot 的另一个强大功能是能够快速查找在 Visual Studio Code 中如何做某事。与其查阅官方文档,你可能会希望快速找到所需内容而不离开代码编辑器。要使用此功能,有一个 @vscode 命令。让我们确保聊天窗口已打开,并查看以下提示如何工作:
[提示]
@vscode 如何打开终端窗口?
[提示结束]

图 20.6:对上述提示的 Copilot 响应
这里有两点值得注意:
-
Copilot 告诉你如何做某事以及在哪里找到它。
-
如果你点击 在命令面板中显示 按钮,它会提供视觉上展示如何找到所需内容的选项。
终端
我们将要向您展示的最后一个真正出色的功能是终端命令。理论上,您可以在不使用此特定命令的情况下使用终端,但如果您不必在问题前加上“在终端中如何做某事”,这会很有帮助。要使用此命令,请在提示前加上 @terminal,如下所示:
[提示]
@terminal 在 Git 中创建一个名为 upstream 的远程,它指向远程仓库。
[提示结束]
[提示响应]
要创建一个名为“upstream”的远程,它指向 Git 中的远程仓库,你可以使用 git remote add 命令。以下是命令:
-
git remote add upstream {remote_repo_url} -
将
{remote_repo_url}替换为远程仓库的 URL。 -
此命令添加了一个名为“upstream”的新远程,它指向指定的远程仓库。然后您可以根据需要从“upstream”远程获取或推送。
-
{remote_repo_url}:远程仓库的 URL。这可能是一个 GitHub、GitLab、Bitbucket 或任何其他 Git 服务器上的 URL。
[响应结束]
如您所见,您既得到了所需的命令,又得到了如何输入的详细说明。
作业
尝试通过添加文档、测试或可能是一个 ORM(对象关系映射器)来进一步改进代码。请记住使用本章中涵盖的命令。
挑战
查看 Copilot Chat 支持的其他命令,看看你是否可以用它们来改进你的工作流程。
问答
Copilot 能帮助什么?
-
代码生成、自动化、调试、故障排除、代码审查和优化
-
部署
-
以上皆非
摘要
在本章中,我们介绍了 Copilot 中一些更高级的功能。你学习了如何使用@workspace来让 Copilot 考虑所有你的文件。@vscode命令也是那个功能中的一个有用命令,它展示了如何与 Visual Studio Code 一起工作。
我们还探讨了脚手架的使用——特别是如何为 Web 项目搭建脚手架——以及如何创建带有起始代码的笔记本。这些命令在你刚开始一个项目时可能会为你节省数小时。Copilot 有很多命令,我建议你尝试一下。
加入我们的 Discord 社区
加入我们的 Discord 空间,与作者和其他读者进行讨论:

第二十一章:软件开发中的代理
引言
本章将向您介绍软件开发中代理的概念。我们将涵盖代理是什么,它们如何工作,以及如何在项目中使用它们。我们还将介绍一些最受欢迎的代理框架以及如何开始使用它们。
让我们介绍代理可以解决的问题。一般想法是有一个可以代表你行动的程序。这类程序的例子可以是自动化任务、做出决策以及与其他代理和人类交互。这样的程序可以为你节省时间,使你的生活更轻松或使你的业务更高效。
在本章中,我们将做以下事情:
-
介绍软件开发中代理的概念。
-
解释代理是什么以及它们是如何工作的。
-
讨论不同类型的代理以及它们如何被使用。
代理是什么?
如前所述,代理是代表你行动的程序。它们可以执行任务、做出决策以及与其他代理和人类交互。代理可以用在广泛的领域中。
几个因素使一个程序成为代理程序而不是普通程序:
-
代理程序 有一个明确的目标:例如,考虑一个保持 25 度温度并采取适当行动以保持该温度的恒温器,或者一个管理财务并试图最大化利润的代理。
-
自主性:代理做出必要的决策以确保它达到之前定义的目标。对于金融代理来说,这可能意味着在满足特定触发条件时买卖股票。
-
具有传感器:传感器可以是物理的,也可以是软件中的 API,它使代理能够理解“世界是什么样的”。对于恒温器来说,传感器是温度指示器,但对于金融代理来说,传感器可以是股票市场的 API,使代理能够决定他们的目标。
代理是如何工作的?
代理通过接收输入、处理输入并产生输出来工作。它们可以被编程来执行特定任务、做出决策以及与其他代理和人类交互。代理还可以从它们的交互中学习并随着时间的推移提高它们的性能。

图 21.1:简单代理的过程:关键字,识别,执行任务
简单代理与使用 AI 的代理
代理并不是什么新鲜事物。它们已经存在很长时间了。新的地方在于代理现在正被 AI 所驱动。让我们比较一下两者:
-
简单代理:传统代理被编程来执行特定任务,并基于预定义的规则和逻辑做出决策。
-
使用 AI 的代理:由 AI 驱动的代理能够执行更复杂的任务并做出更智能的决策。它们能够理解自然语言,从它们的交互中学习,并随着时间的推移提高它们的性能。
简单代理
如前几节所述,简单的代理在以下方面有限:它们是为特定任务制作的。与他们互动通常也是有限的——你只能使用关键词,或者你表达自己的方式是有限的。
一个简单的代理的例子是聊天机器人。这样的聊天机器人被编程为理解有限的关键词和短语。
例如,“告诉我更多关于你们产品的事情,”或者“你们的退换货政策是什么?”任何超出这些关键词和短语的对话尝试都将导致聊天机器人无法理解用户。
一个简单的代理不是一个好的对话者
当你与人类交谈时,你期望他们精通几个主题,并且至少能够谈论其他主题。对于一个简单的代理,我们可能会陷入以下对话:
-
用户:“告诉我你们的产品。”
-
代理:“我们有很多产品,包括电子产品、服装和配件。请告诉我你的兴趣。”
-
用户:“我对衣服感兴趣,想找一些适合当前天气的。”
-
代理:“我当然可以提供关于服装的建议,但我不知道当前天气。”
在这里,我们可以观察到两个有趣的现象:
-
对话感觉很短,重要的信息要么在句子的末尾,要么就在逗号之前,这表明使用了简单的解析来提取重要信息。
-
它无法处理非产品信息,如天气,这有助于筛选出有用的响应。
使用工具调用和大型语言模型(LLM)改进对话
LLM 是一个改进,因为它擅长听起来更自然,而且它可以从流畅的文本中解析和识别意图。你也可以通过所谓的工具调用向 LLM 提供额外的知识,其中你告诉 LLM 关于各种能力的信息,如衣服 API、天气 API 等,这样它可以更好地处理对话,并类似于人类的对话。让我们用同一个 LLM 对话并突出其差异:
-
用户:“你们有哪些产品?”
-
代理:“我们有很多产品,包括电子产品、服装和配件。”
-
用户:“太好了,我对衣服感兴趣,想找一些适合当前天气的。你有什么推荐吗?”
-
代理:“你能告诉我你的位置,这样我就能更好地为你提供服装建议吗?”
-
用户:“当然,我住在亚利桑那州的凤凰城。”
-
代理:“我现在看到凤凰城现在是 90 华氏度。我可以建议这些短裤吗?”
这次对话之所以表现更好,是因为这个 LLM 得益于工具调用,该工具使用凤凰作为输入调用其天气 API,然后继续调用衣服 API,以天气响应作为过滤器。
对话代理的解剖结构
一个对话代理通常由以下组件组成:
-
输入:代理的输入,通常是自然语言的形式。应该指出的是,这可以是非常多的不同 spoken languages,而不仅仅是英语,你过去不得不将其硬编码。
-
处理:输入的处理,通常使用自然语言处理(NLP)技术。
-
委托:将输入委托给代理的适当组件。它委托到的组件可以是特定任务的代理,例如预订航班或回答问题。

图 21.2:对话代理处理步骤
前面的图示表明了一个循环,你从输入到处理再到委托到结果,那么为什么会有循环呢?代理没有结束的概念;它在那里等待用户提供输入,并对它做出反应。如本章前面所述,代理致力于实现目标,如果目标是管理财务,那么它是一项持续的工作。
关于 LLM 中工具调用的更多内容
我们在本章中之前提到了工具调用,但让我们尝试展示如何向 LLM 添加功能。
LLM 只知道它被训练的内容,对于它没有训练的内容,在许多情况下,它将尝试提供一个答案,这个答案并不总是正确的,因为它是在编造;这被称为幻觉。为了提高你希望 LLM 提供更准确响应的领域,你可以向它提供一个工具。提供工具的过程包括以下组件:
-
函数的 JSON 描述
-
函数的描述,这样 LLM 就知道何时调用此函数
一旦你提供了前面的组件,比如说你提供了一个能够获取天气的函数;LLM 现在可以使用其内置功能来语义解释所有后续的输入,意味着用户想要了解天气:
-
“今天盐湖城的天气怎么样?”
-
“旧金山的温度是多少?”
-
“明天纽约会下雨吗?”
-
“伦敦的天气怎么样?”
-
“外面暖和吗?”
使用工具增强 GPT 的功能
它的工作原理是,你以 JSON 格式提供一个函数规范。这个 JSON 函数格式是 GPT 模型理解的模式。GPT 模型将基本上为你做两件事:
-
从提示中提取参数。
-
确定是否调用函数以及调用哪个函数,因为你可以告诉它关于多个函数的信息。
作为开发者,如果你认为 LLM 应该调用该函数,那么你需要主动调用该函数。
你的函数格式遵循以下模式:
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current weather",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the users location.",
},
},
"required": ["location", "format"],
},
}
}
在前面的 JSON 模式中,有一些事情是你告诉 GPT 模型的:
-
有一个名为
get_current_weather的函数。 -
描述是
"获取当前天气"。 -
函数接受两个参数,
location和format。 -
同时还有对参数、它们的类型和允许的值的描述。
让我们描述一下在以下提示下,这将在实践中是如何工作的:
[提示]
“今天盐湖城的天气怎么样?”
[提示结束]
这是 GPT 模型可以从提示中提取的内容:
-
位置:盐湖城。
-
格式:此信息未提供,但 GPT 可以从用户的地理位置推断出来。
-
函数调用:
get_current_weather。
作为开发者,您需要做的是调用提取的参数值指示的函数。以下是可以用来连接 GPT 模型并解析响应的代码,其中提供了函数描述:
import open
def get_current_weather(location, format):
# Call weather API
response = requests.get(f"https://api.weather.com/v3/wx/forecast/daily/5day?location={location}&format={format}
return response.json()
# Call the GPT model
tool = {
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current weather",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco,
CA",
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the users location.",
},
},
"required": ["location", "format"],
},
}
}
prompt = "What's the weather like today in Salt Lake City?"
response = openai.Completion.create(
model="text-davinci-003",
prompt=prompt,
max_tokens=150,
tools= [tool]
)
# Parse the response
function_response = response.choices[0].function_response # here we learn what function to call
location = function_response.parameters.location # extracting parameter value for location
format = function_response.parameters.format # extracting parameter value for format
weather = get_current_weather(location, format) # here we get the response from the API
这可能是如何使用 GPT 创建对话代理的最基本示例。
然而,我们希望高级代理能做得更多。如果我们想做一些更复杂的事情,比如预订航班、酒店、车辆和餐厅呢?
高级对话
很容易认为您只需向 GPT 模型添加更多函数,这可能会暂时有效。然而,随着对话变得更加复杂,GPT 模型需要记住对话的上下文并跟踪对话状态。
对于更高级的对话,这会迅速变得更加复杂。
想象一下走进旅行社,两个人之间发生的以下对话:
用户:“我想去度假。”
代理:“当然。”
用户:“我想去一个温暖的地方。”
代理:“当然,告诉我您想找什么。”
用户:“我想要一个有海滩的地方。”
代理:“当然,还有其他细节吗?”
用户:“我希望温度至少有 25 度。”
代理:“当然,还有其他细节吗?”
用户:“不,就这样。”
代理:“我找到了三个符合您标准的地方。我可以向您展示它们吗?”
用户:“是的,请。”
代理:“这里有三个地方。您想了解哪一个的更多信息?”
用户:“我想第一个。”
代理:“这里有第一个地方的信息。”
用户:“好的,你能为第一个地方预订酒店和航班吗?”
代理:“当然,我会为您处理这件事。”
代理:“我已经为您预订了酒店和航班。”
用户:“谢谢,哦,我还需要租一辆车。”
代理:“当然,您有特别想要的车型吗?”
用户:“不是真的;我的预算是每天 100 美元。”
代理:“我找到了五辆车符合您的标准。我可以向您展示它们吗?”
用户:“是的,请。我想订第五个。”
代理:“我已经为您预订了车辆。”
代理:“您的假期一切准备就绪。”
用户:“谢谢,但等等,我需要帮助预订第一晚的餐厅。”
到目前为止,很明显这次对话可能会持续相当长一段时间。代理需要记住对话的上下文并跟踪对话状态。
代理还需要将许多不同的任务委派给其他代理或服务,如预订酒店、航班、车辆和餐厅,以及天气 API、观光 API 等。
重要的是,代理不仅仅是理解初始提示并将任务委托给另一个代理或服务。您需要将这次对话视为一个状态机以及不同代理和服务的编排。
高级对话建模
我们提到更高级的对话涉及记住上下文和状态。让我们检查示例对话的一个子集,看看状态是如何变化的:
用户:“我想去度假。”
代理:“当然。”
在这一点上,代理除了记住用户的意图,即去度假,没有记住其他任何东西。它只是确认了用户的提示。
用户:“我想去一个温暖的地方。”
代理:“当然,告诉我您想找什么。”
现在事情变得有趣了。代理已经将“温暖”作为标准记住,并需要将其转换为可以用来过滤掉太冷的地方的温度范围。
用户:“我想找一个有海滩的地方。”
代理:“当然,还有其他细节吗?”
这又是一个进步;代理已经将“海滩”作为过滤地点时使用的额外标准记住。
用户:“我想至少 25 度。”
添加了一个额外的标准,“25 度”。让我们看看之前的标准,“温暖”,它被定义为 20-40 摄氏度——这调整了范围为 25-40 摄氏度。
代理:“当然,还有其他细节吗?”
用户:“不,就这样。”
在这一点上,代理意识到用户没有更多的标准要添加,并且可以基于“温暖”、“海滩”和“25-40 摄氏度”的过滤器进行搜索/决策。现在,调用 API 以获取地点列表,代理可以将列表展示给用户进行选择。
代理:“我找到了三个符合您标准的地方。我可以向您展示它们吗?”
需要补充的是,不仅需要记住这次特定旅行检索的标准,还需要记住后续步骤的标准,除非用户更改标准。
希望您可以从前面的例子中看出,状态是逐渐建立起来的,代理需要记住对话的上下文。
将更高级的对话视为以下步骤可能是有帮助的:
-
输入:代理的输入,通常是自然语言的形式。
-
处理:对输入的处理,通常使用 NLP 技术。
-
确定下一步:代理需要根据输入和对话的当前状态确定对话的下一步。这里的答案可以是要求更多信息、展示选项列表、预订某物等等。
-
结束对话或继续(请求用户输入):代理需要确定对话是否应该结束或继续。如果应该继续,它需要请求用户输入。
高级对话的伪代码
代理可能有几个不同的状态,例如:
-
请求任务:这通常在对话开始时或完成任务并完成用户选择时提出。
-
向用户请求更多关于任务的信息:这通常在执行任务之前提出,以确保代理拥有所有所需的信息。
-
向用户展示选项列表:这通常在完成任务后提出,以向用户提供选择。
-
执行任务:在这里,代理会执行任务,比如预订酒店、航班、汽车或餐厅。
-
结束对话:当对话结束时,并且用户以某种方式表明对话结束时,代理会移动到这个状态。
这可能是伪代码的样子:
# enum
class State(Enum):
ASK_FOR_TASK = 1
ASK_FOR_MORE_INFORMATION = 2
PRESENT_TASK_RESULT = 3
PERFORM_TASK = 4
END_CONVERSATION = 5
# initial state
state = State.ASK_FOR_TASK
def ask_for_task():
# ask the user for a task
pass
def ask_for_more_information(task):
# store filter criteria
pass
def present_task_result(task):
# presents the result so the user can choose
pass
def perform_task(task):
# Perform a task
pass
def end_conversation():
# End the conversation
pass
while state != State.END_CONVERSATION:
if state == State.ASK_FOR_TASK:
# Ask for a task
task = ask_for_task()
state = State.ASK_FOR_MORE_INFORMATION
elif state == State.ASK_FOR_MORE_INFORMATION:
# Ask the user for more information on a task
task = ask_for_more_information(task)
state = State.PERFORM_TASK
elif state == State.PRESENT_TASK_RESULT:
# Present a list of options to the user
task = present_task_result(task)
state = State.ASK_FOR_MORE_INFORMATION
elif state == State.PERFORM_TASK:
# Perform a task
perform_task(task)
state = State.PRESENT_TASK_RESULT
elif state == State.END_CONVERSATION:
# End the conversation
end_conversation()
上述代码是一个更高级对话的不错起点。然而,我们应该记住,人类并不总是可预测的,代理需要能够处理意外情况。例如,人类可以在任何时候改变主意或添加新的标准。
自主代理
自主代理是指可以在没有人类干预的情况下采取行动的代理。它们可以执行任务、做出决策,并与其他代理和人类进行交互,而不需要人类输入。自主代理可以用于广泛的领域,从自动驾驶汽车到虚拟助手。
自主代理的例子包括以下内容:
-
自动驾驶汽车:自动驾驶汽车是无需人类干预即可自行驾驶的自主代理。它们可以导航道路、避开障碍物,并根据周围环境做出决策。
-
虚拟助手:虚拟助手是无需人类输入即可执行任务、做出决策并与人类交互的自主代理。它们可以理解自然语言,从交互中学习,并随着时间的推移提高性能。想象一下这样一个例子,你有一个财务代理;你可能给它设定了一个高级目标,即管理你的财务并确保每年价值增长 8%。你可能允许这个代理在特定价值以下买卖股票,但对于超过一定限额的更大购买和销售,则需要咨询你。
未来我们可能会看到对话代理和自主代理数量的增加。这些代理可以在许多有趣的领域得到应用,从客户服务到医疗保健、金融和交通。想象一下,有一个自主代理可以帮助你处理税务、预订假期或在你了解你的医疗历史、偏好等信息的情况下帮助你照顾健康。
作业
给定以下对话,推断可能涉及哪种类型的代理以及你会如何建模对话:
用户:“我想烤一个蛋糕。”
代理:“当然,你想要做什么类型的蛋糕?”
用户:“我想做一个巧克力蛋糕。”
代理:“当然,你有没有想到具体的食谱?”
用户: “不,你能帮我找一个食谱吗?”
代理: “当然,关于过敏或偏好的方面,我应该知道些什么吗?”
用户: “我对坚果过敏,我更喜欢容易制作的食谱。哦,我还想做一个纯素的蛋糕。”
代理: “明白了!我找到了符合你标准的三个食谱。我可以展示它们吗?”
用户: “是的,请。”
代理: “这里有三个食谱。你想要哪一个?”
用户: “我要第二个。”
代理: “就这些吗?”
用户: “不,我需要购买原料吗?你能帮我设置烤箱温度吗?”
代理: “是的,看起来你缺少一些原料。我可以帮你订购原料。我还可以帮你设置烤箱温度。你需要什么时候设置?”
用户: “是的,下单,并在订单到达后立即设置烤箱。”
代理: “我已经下了订单,正在等待送货。我会在它到达时设置烤箱温度。”
用户: “谢谢。”
代理: “不客气,就这样吗?”
用户: “是的。”
挑战
你能想到一个可以用代理解决的问题吗?你会如何使用代理来解决这个问题?你会使用哪种类型的代理?
问答
Q:在 LLM 中工具调用是什么意思?
-
这是在 LLM 调用内置工具以提供响应时发生的。
-
这是在你通过提供 JSON 格式的函数描述和指示何时调用此函数的语义描述来让 LLM 知道一个新功能时发生的。
-
这是在你使用函数来微调 LLM 时发生的。
摘要
在本章中,我们介绍了软件开发中代理的概念。我们解释了代理是什么以及它们是如何工作的。我们讨论了不同类型的代理以及它们如何被使用。我们希望你现在能一窥未来,看看像 GPT 这样的 LLM 将走向何方,以及你的未来将如何改变。
参考文献
如果你对此类代理感到好奇,值得查看一些相关资源:
-
Autogen:
github.com/microsoft/autogen -
加入我们的 Discord:
discord.gg/pAbnFJrkgZ
加入我们的 Discord 社区
加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

第二十二章:结论
感谢您,亲爱的读者,阅读到这本书的这一部分。我们希望您现在能够自信地在使用 GitHub Copilot 和 ChatGPT 等 AI 工具的项目中。
书籍回顾
让我们回顾一下这本书中我们涵盖的内容。我们首先向您介绍了 AI 的世界以及我们如何到达大型语言模型(LLMs)。然后,我们向您介绍了 GitHub Copilot 和 ChatGPT,这两者是目前世界上最受欢迎的 AI 工具之一。这两个工具不仅受欢迎,而且值得比较和对比。ChatGPT 带有聊天界面,设计用来处理各种任务。GitHub Copilot 也带有聊天界面和编辑器模式,但更专注于解决软件开发中的问题。这两个工具的一个重要共同点是,您可以使用提示和自然语言输入,它们可以作为输入使用,并且最终用户会收到一个输出,希望这能帮助他们更接近解决问题。因为这两个工具都依赖于提示输入,这使我们能够决定需要什么类型的提示和多少提示,调整提示,甚至使用提示来验证 AI 工具的响应。
然后,我们向您展示了一种提示策略,以确保您有效地使用这些 AI 工具。之后,我们展示了这种提示策略如何在跨越前端到后端、涵盖几个章节的 Web 开发项目中得到应用。然后,我们展示了同样的提示策略如何应用于数据科学和机器学习项目。这两个问题领域是随机选择的;这本书的目的是展示生成式 AI 工具的能力,并且知道如何将它们与提示策略相结合将极大地增强您的能力。
使用 AI 工具的影响是,您现在拥有一个工具,它可以处理代码开发中的大部分繁重工作,无论问题领域如何。这对您作为开发者的意义在于,您现在可以更专注于声明性,即表达您想要的事情,而不是逐行编写代码。使用 AI 工具并高效地使用它们可能会显著加快您工作中编码的部分。
在总结这本书之前,我们向您展示了对未来的展望,即代理,这些是代表您行事并代表我们认为 AI 将走向何方的程序。
最后,我们更详细地介绍了 AI 工具及其功能,以便您在未来的项目中取得成功。
主要结论
那么,我们能从这本书中得出哪些主要结论呢?它们如下所示:
-
类似于 GitHub Copilot 和 ChatGPT 这样的 AI 工具将长期存在,并且随着时间的推移只会变得更好。
-
世界正在向更声明性的编程方式转变;让 AI 为您做大部分繁重的工作。您的新角色是与 AI 工具迭代合作,以获得最佳结果。
-
无论你是网页开发者、数据科学家还是机器学习工程师,你都应该制定一个提示策略来处理你选择的领域内的问题。
-
提示(Prompting)正迅速成为一项独立技能,即使我们认为改进后的模型将来会使其变得不那么重要。目前,这是一项你应该掌握的技能,我们希望这本书能帮助你在这条成为熟练提示师的道路上。
接下来是什么
我们在第二十一章中讨论的智能代理(Agents)是事情发展的方向,你应该密切关注这一点。智能代理已经是牛津大学的课程内容之一;查看这个课程链接了解更多信息 conted.ox.ac.uk/courses/artificial-intelligence-generative-ai-cloud-and-mlops-online。
如第二十一章所述,Autogen 是一个非常有趣的项目,它引入了智能代理。我们建议您查看它,并在您自己的项目中如果愿意的话加以利用:github.com/microsoft/autogen。
最后
人工智能的世界发展迅速。昨天的完美工具和策略可能在几个月后可能不再是最好的。因此,我们的目标是保持这本书更新,包含最新的发现和见解。衷心感谢您,亲爱的读者,我们希望这本书对您来说是有价值的。
加入我们的 Discord 社区
加入我们的 Discord 空间,与作者和其他读者进行讨论:



浙公网安备 33010602011771号