杜克大学本地大模型基础笔记-全-

杜克大学本地大模型基础笔记(全)

001:认识课程讲师-诺亚·吉夫特 👨‍🏫

在本节课中,我们将认识本课程的讲师诺亚·吉夫特,并初步了解课程将涵盖的核心主题,包括大语言模型的技术细节、伦理考量以及实际应用评估方法。

我的名字是诺亚·吉夫特,我是本课程的讲师之一。
当前,由于大语言模型和生成式人工智能的出现,技术领域正处在一个激动人心的时代。
本课程将帮助你深入了解未来可能发生的一些事情。
课程内容包括深入探讨负责任的AI组件,了解这些内容非常重要。
存在大量关于现有偏见问题或确保你的模型对社会产生积极影响的信息。
我在本课程中讨论的内容之一,是从经济学角度思考并考虑负外部性。
例如,如果你能从某件事中获得所有利润,却将负面影响推给社会,这并不公平。
我认为,我们正看到一些与大语言模型相关的此类技术问题浮出水面,我们会讨论其中一些需要了解的重要事项。
同样,如果所有的艺术和音乐都被彻底商品化,创意人士将失去创作动力,那将会是怎样的未来。
因此,我认为社会在考虑使用AI时,需要处理好一些重要事项。
我们也会深入探讨本地大语言模型的一些技术细节,并分析它们为何重要。
我们还将展示如何使用Elo评分来评估大语言模型。
这是一种基于赛程强度的评估组件。在这个竞争激烈、本质上加速了完全竞争的新世界里。
我们将看到越来越多的人需要评估这些本地大语言模型。与商业大语言模型相比,它们具有隐私、延迟和成本方面的优势。
掌握本地大语言模型将是一项重要技能,你将在本课程中学到它。最后,我们会讨论大语言模型的一些特性。
例如使用检索增强生成(RAG)
考虑这些内容很重要,因为它们是提升你工作流程的方法。
因此,本课程汇集了从伦理到技术的一系列前沿主题。
希望你享受学习过程。让我们开始吧。

本节课中,我们一起认识了讲师诺亚·吉夫特,并概览了课程的核心内容,包括大语言模型的伦理挑战、技术优势(如隐私和成本)以及关键的评估与应用方法。

002:认识课程讲师-阿尔弗雷德·德萨

在本节课中,我们将认识本课程的讲师阿尔弗雷德·德萨,并了解他的专业背景以及本课程将要涵盖的核心内容。

大家好,我是阿尔弗雷德·德萨。我从事机器学习和编程课程教学已有很长时间。

我的背景和经验主要集中在软件工程、自动化、DevOps以及系统管理领域。

在本课程中,我们将深入探讨如何使用和发挥本地大语言模型的能力。

我们将了解它们在哪些情况下非常有用,以及在何种场景下您可能需要考虑使用它们。

我们还将学习如何在本地应用特定技术,以便您能够开始与这些大语言模型互动,并充分利用其所有功能。

我们将探讨这一切如何成为可能,以及您如何才能最有效地利用它们。


本节课中,我们一起认识了讲师阿尔弗雷德·德萨,并初步了解了本课程将围绕本地大语言模型使用场景应用技术展开。在接下来的课程中,我们将逐步深入学习这些内容。

003:课程介绍

在本节课中,我们将要学习大语言模型的具体定义、工作原理、以及在实际应用中的考量。我们将探讨其背后的“魔法”,分析其优势与风险,并介绍基础模型的概念及其重要性。

上一节我们初步了解了机器学习模型的创建过程。本节中,我们来看看什么是大语言模型,以及它们如何工作。

我们已经了解了一些机器学习模型及其创建方式,但尚未具体探讨什么是大语言模型,特别是它们如何运作。理解其幕后的工作原理,即让这些模型能够实现如今所见(如生成式AI)各种功能的“魔法”,是后续利用它们进行创造的重要环节。掌握这一理解,将为深入探索应用打下坚实、良好的基础。

接下来,我们将快速审视大语言模型的一些益处、风险与挑战。

以下是关于大语言模型的一些关键考量点:

  • 益处:大语言模型能够理解和生成类人文本,完成翻译、总结、问答等多种复杂任务。
  • 风险与挑战:这些模型存在局限性,使用时可能带来风险。
  • 注意事项:在使用大语言模型时,有些操作可能具有一定风险,我们需要确保某些事项的正确性。

我们的方法必须非常扎实,因为在实施大语言模型时,有可能遇到问题。我们也将审视其中一些问题。

最后,我们来了解一下基础模型。

我们将探讨什么是基础模型,它们为何重要,以及在未来考虑将其应用于某个程序时,我们如何利用这些模型。

本节课中,我们一起学习了大语言模型的核心概念、工作机制、应用中的利弊权衡,并引入了基础模型这一重要范畴。这些知识构成了深入理解和安全有效应用大语言模型的坚实基础。

004:什么是LLM及其工作原理 🧠

在本节课中,我们将要学习大语言模型(LLM)的基本概念及其核心工作原理。我们将从理解文本如何被模型处理开始,逐步揭示LLM如何通过概率计算来生成文本。

从文本到数字:理解分词器

要理解LLM及其确切的工作原理,我们首先需要从“词元”开始,并理解什么是分词器。

我们有一段文本,它提出了一个问题:“what is a tokenizer”。分词器的确切含义是:它是一个将文本(例如这里的这个短语)转换为数字的过程。

大语言模型以文本作为输入,并生成文本作为输出。但这个过程具体是如何发生的呢?它通过处理一个短语(例如本例中的短语),并将其分割成更小的块来实现。

以下是分词过程的一个示例:

例如,其中一个块可能是“what”,它会被转换成一个数值,比如 318。所有这些块最终都会被转换成数字,并被放入一个数组中。这是机器能够处理它们并尝试更好地识别这些由数值表示的片段的方式。

我们正在创建一种索引。这个索引是原始文本块的编码。我们之所以称它们为“块”或“片段”,是因为它们不一定是完整的单词。

在本例中,我们可以说“what is a”是一个块,但它也可能包含一些空格,这些空格也可能作为一个整体块被表示。有时,分词器甚至会将一个单词拆分成两部分,例如“tokenizer”可能被拆成“token”和“izer”。像“?”这样的标点符号也可能构成一个独立的块。

所有这些表示方法都使机器学习模型更容易理解文本。否则,对于机器来说,理解文本将非常困难。这是目前我们让机器理解文本的最佳方法。

概率机器:LLM的核心

上一节我们介绍了文本如何被分割并转换为数值。本节中,我们来看看这些数值背后,LLM究竟在处理什么。我喜欢将其称为“概率机器”。

为什么是概率机器?因为它试图理解所有这些较小的文本块(词元),并尝试预测从一个词元到下一个词元的概率。




模型会计算:从这个词元开始,下一个词元是第二个词元的概率是多少?然后是第三个、第四个,依此类推。因此,用一个过度简化的方式解释,它本质上就是一个基于海量文本训练得出的概率计算器。

我们将看到一个机器学习模型,它试图根据给定的文本片段来确定所有可能的后续词元的概率。这就是为什么我想强调,这本质上就是一台概率机器。

这样思考这些模型也很容易,因为它们有时表现得非常出色,就像一个能输出事实的机器。但即使某些生成的文本看起来准确、正确且符合现实,我们也不应将其视为事实输出机。因为它所做的一切都只是基于给定特定输入的概率计算。

工作原理示例

基于大量的训练文本,以及将这些文本分解为词元并生成所有索引,模型能够理解概率。

例如,有一个短语:“今天是个晴朗的……”。这个短语是理解“……”部分可能是哪个特定词的完美例子。


在本例中,“今天是个晴朗的”后面很可能跟着“天”这个词。模型甚至可能预测在“天”之前有一些空格,以便在文本生成时能正确表示。

所以,从本质上讲,具有大语言生成能力的机器学习模型,即大语言模型,其文本生成的工作原理是:首先通过分解文本来创建索引,然后作为一台概率机器运行。

总结

本节课中,我们一起学习了大语言模型(LLM)的核心概念。我们了解到,LLM首先通过分词器将输入文本分割并编码为数字序列(词元)。然后,模型作为一个“概率机器”运行,基于从海量文本中学到的模式,计算并预测最可能的下一个词元,从而逐步生成连贯的文本。理解LLM的概率本质至关重要,它提醒我们,模型的输出是基于统计可能性,而非对事实的绝对认知。

005:使用LLM的优势与风险 🎭

在本节课中,我们将探讨大型语言模型(LLM)在实际应用中的主要优势与潜在风险。我们将通过具体的示例,帮助你理解如何有效利用LLM,同时识别并规避其可能产生的问题。

上一节我们介绍了LLM的基本概念,本节中我们来看看它在实际使用中的表现。

优势:作为高效的内容生成与总结工具

LLM能够快速生成文本内容或对现有信息进行总结,这为内容创作和知识整理提供了极大的便利。以下是其优势的具体体现。

生成个人简介示例

当请求ChatGPT为特定人物生成简介时,它能够基于公开信息组合出一段看似合理的描述。例如,当输入“give me a summary of myself Alfredo Deza”时,模型生成了一段关于贡献和社区参与的总结。

代码示例:生成总结

用户输入: "give me a summary of myself Alfredo Deza"
模型输出: "Alfredo Deza has made significant contributions to projects related to Python packaging..."

尽管部分内容(如“Python packaging”)可能与事实不完全相符,但整体框架可用作进一步创作的起点。

执行具体指令

当指令更明确时,LLM能生成更贴合需求的输出。例如,以项目需求为背景请求生成简介。

代码示例:为项目生成简介

用户输入: "I am Alfredo Deza, and I need a quick bio for a README in a Rust project."
模型输出: "Passionate technologist from Peru... deep love for open source and Rust..."

这个结果在核心事实(如来自秘鲁、热爱开源)上基本准确,展示了LLM在遵循清晰指令下的实用性。

文本总结功能

LLM能有效提炼长段文本的核心信息。你可以通过指令控制总结的详细程度。

代码示例:总结并精简

用户输入: "Summarize the following paragraph... Now reduce it to two sentences."
模型输出: [生成更简洁的两句话总结]

这个功能对于快速获取文档大意或准备摘要材料非常有用。

风险:可能产生不准确或虚构信息

然而,LLM并非全知全能,其本质是一个基于概率预测的模型。这导致了其最主要的缺点:可能生成看似合理但实际不准确或完全错误的信息。

“幻觉”问题示例

当我们尝试让另一个模型(Claude AI)生成关于同一人物的信息时,出现了明显的错误。

代码示例:模型产生不实信息

模型输出: "He is a mathematician... born on November 14th... a seasoned Ruby developer..."

事实上,这些描述(数学家、特定生日、Ruby专家)都是不正确的。这清晰地展示了LLM可能“自信地”输出错误信息,即所谓的“幻觉”。

隐私与伦理边界

有时,出于隐私和伦理考虑,LLM会拒绝生成关于个人的特定信息。这本身是一种风险控制机制,但也提示用户需注意信息的来源和授权。

代码示例:模型拒绝生成

用户输入: "Please create me a short bio about Alfredo Deza."
模型输出: "Creating biographical information without the person's consent... I recommend checking official sources."

核心概念与使用原则

要安全有效地使用LLM,必须理解其核心工作机制并遵循关键原则。

理解其本质:概率机器

从根本上说,LLM是一个概率机器。其运行原理可以简化为:
公式:P(输出 | 输入, 训练数据)
即,模型根据给定的输入和其训练数据,计算下一个最可能出现的词或句子的概率分布。它生成的是“最 plausible(看似合理)”的文本,而非绝对真理。

关键使用原则

以下是使用LLM时必须牢记的几点:

  1. 永远验证关键信息:不要完全信任模型输出的任何事实性陈述,尤其是涉及日期、数据、具体成就时。务必通过权威渠道进行二次核实。
  2. 提供清晰、具体的指令:模糊的提问容易导致模糊或错误的回答。明确的指令能获得更相关、更准确的输出。
  3. 将LLM视为助手,而非权威:最佳使用方式是将LLM的输出作为灵感来源、初稿或总结工具,然后由人类进行审核、修正和润色。
  4. 注意隐私与伦理:避免要求模型生成未经授权的个人隐私信息或可能造成伤害的内容。

本节课中我们一起学习了大型语言模型在实际应用中的双重性。我们看到了它作为强大的内容生成和总结工具所带来的效率优势,同时也深入了解了其可能产生不准确信息(幻觉)的核心风险。关键在于牢记LLM是一个基于概率的模型,并始终以批判性思维对待其输出,将其用作增强我们工作的智能助手,而非替代我们判断的绝对权威。

006:减轻LLM的风险 🛡️

在本节课中,我们将要学习如何理解和减轻大语言模型(LLM)可能带来的风险。我们将通过一个实用的框架,对比四种主要的风险缓解策略:提示工程、检索增强生成、微调以及从头训练模型。我们将分析每种方法的难度、成本和质量,帮助你根据实际情况选择最合适的工具。

为了有效减轻并深入理解如何实际缓解大语言模型的一些风险,下面这张图非常有用。

提示工程 ✍️

上一节我们介绍了风险缓解的整体框架,本节中我们首先来看看位于框架顶端的“提示工程”。

提示工程是我们向专门从事语言生成的机器学习模型提供准确输入、准确描述问题或我们想要内容的方式。当我们精心设计一个非常好的提示时,我们通常会得到更好的结果。在框架中,提示工程位于最顶端,代表了一件非常容易做的事情。

它的难度在这里是最低的,这是你在这个层面上能做的最简单的事情。随着我们向框架下方移动,难度和成本都会增加。提示工程的成本基本为零,难度也非常低。这意味着我们的任务仅仅是能够为机器学习模型描述一个好的输入。

然而,在质量方面,我们得到的结果质量相对较低。我们可以有一个非常好的提示,但如果模型本身不擅长手头的任务,效果也会有限。举个例子,如果你试图让模型为你提供与特定国家法律程序或法律问题相关的准确回答,而模型没有经过相关训练或缺乏相关知识,那么仅靠提示工程可能对你帮助不大。

检索增强生成 🔍

接下来,我们看看检索或搜索,也就是检索增强生成。我们可以将提示工程与搜索结合起来。

这意味着你可以将多种操作结合起来:首先根据提示内容进行搜索,然后将搜索结果作为提示的一部分输入给模型。这本质上比单纯的提示工程要复杂一些,也会涉及更高的成本。可以说,它比提示工程的成本要高。

检索增强生成或RAG,本质上就是进行搜索。你需要某种存储系统和某种搜索引擎来实现它。在质量方面,检索增强生成的质量比提示工程有所提高。例如,某些模型是基于过去事件训练的,你可以通过检索搜索来缓解这个问题,因为你可以不断用更相关、更新的数据来更新搜索内容。

微调 🎛️

继续向下,我们将进入“微调”环节。微调要复杂得多。

微调需要大量的数据,并且需要一个持续的过程集,允许你使用额外数据训练模型并进行微调。这比检索增强生成或提示工程要困难得多,当然成本也更高。为了快速完成微调,你可能需要一些GPU。然而,在质量方面,如果微调做得很好,你会获得更高的质量。不过,我个人的偏好仍然在检索增强生成和提示工程这边,因为微调的难度较大。

从头训练模型 🏗️

最后一部分,我在这里标上数字4,就是“从头训练模型”。

这指的是从零开始训练一个模型。同样,这也是大多数大语言模型必须做的事情,因为它们需要生成一个实际的模型。这将是难度的顶峰,也是你在这里能看到的最昂贵的操作。至于质量,根据你想要实现的目标,它可能会更高,也就是更好的质量。

总结与综合应用 📊

本节课中我们一起学习了如何理解减轻大语言模型风险的不同步骤和选项。

理解如何减轻一些风险,在这里你可以很好地了解所有不同的步骤和选项,以及将提示工程与从头训练模型进行比较意味着什么?这对成本、难度意味着什么?当然,还有它们与质量的关系。因此,某些方法在缓解特定风险时可能问题更多,无论如何你可能都需要使用一些防护措施。

需要强调的是,这些部分,比如这里的提示工程,并不意味着如果你在训练模型就不能使用提示工程。你完全可以在使用微调、检索增强生成或搜索的同时,也100%地使用提示工程,并将所有这些方法很好地结合起来。

你对这些方法理解得越透彻,你就能掌握越多的工具和方法,以便在处理大语言模型时,尽力缓解可能遇到的一些问题。

007:什么是基础模型 🧠

在本节课中,我们将要学习基础模型的核心概念。基础模型是现代人工智能应用的重要起点,理解它们是什么以及如何工作,是掌握大语言模型技术的关键。

概述

基础模型是经过大规模训练的大型深度学习模型,它们具备很强的适应性。这些模型最初是为自然语言处理任务而创建的,为构建基于人工智能的应用程序提供了一个坚实且强大的起点。

基础模型的核心特征

上一节我们介绍了基础模型的定义,本节中我们来看看它的几个关键特征。

基础模型的关键在于,它们基于海量的文本数据进行训练。数据量越大,模型的质量通常就越好。这些海量数据是创建大型语言模型的必要条件。

公式表示模型性能 ∝ 训练数据规模与质量

基础模型作为起点

那么,基础模型如何成为一个“起点”呢?一旦模型具备了处理自然语言多个方面的能力,就可以将其重新调整用途,应用于其他类型的任务。

以下是基础模型可以适配的具体任务示例:

  • 代码助手:开发人员、软件工程师、机器学习工程师和数据科学家正在使用代码助手。这些助手集成到开发环境中,基于大语言模型的文本生成能力,帮助开发者更轻松、更快速地编写代码。例如,GPT这样的基础模型可以作为起点,通过添加特定任务功能,将其转变为专用的代码助手。
  • 客户支持聊天机器人:当你访问一个网站并遇到问题时,可能会与一个聊天机器人交互。它可以询问你的问题详情,并帮助你创建服务工单。这就是一个客户支持聊天机器人的例子。
  • 销售机器人:销售机器人可以实现类似的功能,用于引导客户、回答产品问题或促进销售。

从基础模型到专用应用

需要明确的是,上述应用并非直接基于原始的基础模型。一个不好的例子是,应用程序直接与未经任何处理的基础模型对话,而不进行任何过滤或指令调整。

为了实现代码助手或客户支持等具体用例,必须在基础模型和手头任务之间进行一系列处理和调整。

这个过程主要包括:

  • 指令过滤与处理:通过特定的指令来引导模型,使其输出更符合特定场景的要求。
  • 微调:在基础模型之上,使用特定领域的新数据(例如海量的现有代码)对模型进行额外的训练,使其适应特定任务。

核心流程基础模型 (理解NLP) + 任务特定数据与指令 → 微调 → 专用应用模型

总结

本节课中我们一起学习了基础模型。基础模型指的是那些允许其他更具体任务(如代码助手、客户支持和销售机器人)得以实现的基础模型。它们的特点是规模大、基于海量数据训练,并且可以通过指令调整微调等技术,被重新调整用途以适应多样化的下游应用。

008:章节总结 🎯

在本节课中,我们初步探索了大语言模型的工作原理、核心组件及其局限性,并了解了基础模型如何为我们提供一个强大的起点。

大语言模型如何工作

我们得以窥见大语言模型如何运作。我们了解了构成这些大语言模型幕后的一些要素,以及当它们生成文本、图像、音频或其他内容时,其内部运行的一些逻辑。

模型的局限与挑战

我们也探讨了这些大语言模型的局限性与挑战。我们甚至尝试突破其边界,使其陷入困境,到达一个临界点:大语言模型开始无法给出有效回应,其回答开始崩溃,无法准确提供我们最初所要求的内容。例如,当输入过多信息过长上下文时,大语言模型便不再能产出任何有用的结果。

基础模型的价值

最后,我们审视了基础模型。正如你现在所了解的,基础模型为你提供了一个良好的起点,让你能站在巨人的肩膀上。大部分繁重的工作已经为你完成,你可以在此基础上进行微调,或将那个庞大的大语言模型重新调整用途,改造成其他应用,从而充分利用已经完成的全部训练成果。


本节课中,我们一起学习了:大语言模型的基本工作机制、其面临的主要挑战与局限,以及基础模型作为强大起点的核心价值。理解这些是进一步应用和定制大语言模型的关键基础。

009:开始使用Llamafile 🚀

在本节课中,我们将要学习如何使用Mozilla的Llamafile项目,这是运行本地大语言模型最简单的方法之一。我们将了解其项目结构、核心优势以及如何实际操作。


项目概述与优势

许多人希望在本机运行大语言模型,主要出于隐私考虑,并且它是免费的。使用自己的机器,你可以直接深入探索Llamafile。

上一节我们介绍了本地运行模型的动机,本节中我们来看看Llamafile的具体实现。

Llamafile允许你通过单个文件分发和运行大语言模型。其最吸引人的特点是使用了Cosmopolitan Libc库,这使它能够将所有内容压缩成一个名为llamafile的单一可执行文件。

尽管项目名为Llamafile,但它并不局限于任何特定的大语言模型。


模型选择与获取

以下是Llamafile支持的一些模型示例:

  • Lava:一个能够读取图像的模型,是一个非常酷的项目。
  • Mistral:这是性能较好的开源模型之一,采用Apache2许可证。需要注意的是,它是一个约30GB的大文件。

获取模型后,你只需要下载它,然后运行./run命令即可。


接口与使用方式

Llamafile的一个很酷的特性是,它提供了一个模仿OpenAI API的Python API。这意味着你可以使用这个API,从封闭的商业专有模型轻松迁移或升级到开源模型。

此外,你也可以使用命令行接口。以下是可用的命令行指令示例。

这是一个非常有趣的项目,接下来让我们看看它是如何工作的。


实际操作演示

我将打开我的终端。可以看到,我已经下载好了Mistral模型。运行它非常简单,只需执行相应的命令。

现在,我们看到Llama.cpp正在本地运行。如果我想重置到某种默认状态,我可以执行重置操作,这将重置所有默认设置。

接下来,我将把机器人的名称改为“AI”。然后,我会让它执行一个任务。让我们尝试一个Python的“Hello, World”程序。我会输入:“Python Hello, World函数。展示给我看。”

然后点击发送。它将使用我Mac的GPU来获得非常快速的响应。


性能与优势总结

关于Mistral模型的一些指标显示,它的性能与封闭的专有模型不相上下。从响应来看,效果相当不错。如果需要,我可以继续让它执行更多功能,例如编写一个将两个数字相加的Python函数。

本节课中我们一起学习了运行本地大语言模型的核心要点。使用Llamafile的主要优势总结如下:

  1. 隐私性:你的数据不会被发送到你不了解其数据政策的公司。
  2. 性能更佳:由于延迟更低,调用本地API的性能通常优于调用外部API。
  3. 完全免费:使用自己的硬件运行,没有额外费用。

因此,探索Llamafile项目具有显著的优势。建议你亲自查看这个项目,并分享你的使用体验。

本地大语言模型的基础:10:本地系统指标与Llamafile 🖥️📊

在本节课中,我们将学习如何利用Llamafile来监控运行本地大语言模型时的系统指标,并评估其性能表现。


首先,我们可以从Elo评分角度来审视Llamafile的实际使用效果。下图展示了用户群体对该模型能力的感知评价。

可以看到,这个混合模型的表现与商业模型相差无几。这表明,只要本地模型的性能足够好,就会有大量用户有兴趣在本地运行它。那么,它的性能究竟如何呢?让我们进一步查看Llamafile项目。

上一节我们了解了模型的评价,本节中我们来看看其运行时的具体表现。我现在已经在网页浏览器窗口中运行了Llamafile,并且旁边实时显示着各项系统指标,让我们能够观察运行状态。

需要关注的指标包括:

  • GPU时间:这是一个关键指标。
  • 所有核心的使用情况
  • M2 Ultra GPU:我使用的这款芯片拥有60个GPU核心。得益于Llamafile和Llama.cpp开发者的出色工作,在Mac操作系统上,它能够充分利用所有这些GPU核心。

接下来,让我们通过实际操作来观察这些指标。如果我向下滚动并执行一个Python函数,看看GPU会有何反应。

我们会看到,GPU使用率确实会在这里出现峰值。同时,我们也应该能看到一些GPU活动。

实际上,我们可以看到这个.a1.9进程正在调用GPU。我们再次运行它,可以尝试另一个请求,例如:“给我一个Python中递归函数的例子。”

然后看看它做了什么。同样地,我们得到了一个Python递归函数,并且可以确认,它确实使用了GPU。由此可见,其性能实际上非常出色,并且能够有效利用历史GPU资源。

因此,对于目前使用商业模型的用户来说,虽然商业模型有其价值,但这无疑提出了一个长期问题:如果你可以下载一个性能足够好、表现惊人且完全免费的模型,那么长期来看,为什么还要使用商业模型呢?

在你尝试Llamafile,甚至考虑为大型语言模型签订商业协议之前,首先应该审视本地模型的真实性能。查看其运行指标后,你会发现它令人印象深刻。


总结

本节课中,我们一起学习了如何通过Llamafile监控系统指标(如GPU利用率)来评估本地大语言模型的运行性能。我们看到,当前的本地模型在性能上已接近商业模型,且能高效利用硬件资源(如M2 Ultra的所有GPU核心)。这促使我们思考,在本地模型性能足够强大且免费的情况下,长期依赖商业模型的必要性。

011:Candle框架的Hello World示例 🕯️

在本节课中,我们将学习如何设置一个基础的Rust开发环境,并使用Candle框架创建一个简单的“Hello World”项目。我们将涵盖从环境验证到项目构建的完整流程,包括如何添加GPU(CUDA)支持。


环境准备与验证

首先,我们需要确保开发环境已正确设置,特别是支持基于GPU的开发。以下步骤用于验证环境。

以下是环境检查命令:

  • R C digest version:此命令检查Rust的digest包版本,确认Rust环境已安装。
  • make verify:此命令执行验证脚本,显示计算能力并确认CUDA编译器驱动已就绪。

完成环境验证后,我们就可以开始创建项目了。


创建基础Rust项目

上一节我们验证了基础环境,本节中我们来看看如何初始化一个新的Rust项目。

我们将遵循Hugging Face Rust Candle的文档指引。首先,使用Cargo(Rust的包管理器)创建一个新项目。

代码:创建项目

cargo new my_app

项目创建成功后,进入项目目录。

cd my_app

为了便于使用快捷命令,可以将一个预设的Makefile复制到当前项目中。这样,我们就可以使用make build等命令来简化构建流程。

复制完成后,执行cargo build命令来构建项目,确保一切就绪。


添加Candle依赖并构建

现在,我们有了一个基本的Rust项目结构。接下来,需要将Candle框架添加为项目依赖。

使用cargo add命令可以方便地添加依赖。执行以下命令:

cargo add candle-core

此命令会自动更新项目的Cargo.toml配置文件,添加candle-core库。

添加依赖后,查看项目源代码。初始的main.rs文件仅包含一个简单的“Hello, world!”打印语句。这主要用于验证安装和构建流程。

为了构建项目,我们可以使用之前引入的Makefile。执行构建命令:

make build

或者直接使用Cargo命令:

cargo build

构建成功后,运行程序以验证所有组件工作正常:

cargo run

如果终端成功打印出“Hello, world!”,则表明基础安装和构建成功。


创建支持CUDA的项目

上一节我们创建了一个基础项目,本节中我们来看看如何创建一个支持GPU加速的版本。步骤与之前类似,但需要额外启用CUDA特性。

首先,创建一个新的项目。

cargo new my_app_cuda
cd my_app_cuda

同样,将Makefile复制到新项目中,为开发提供便利的脚手架。

此时,与基础项目唯一的区别是需要为candle-core依赖添加cuda特性。这可以通过cargo add命令的--features参数实现。

代码:添加支持CUDA的Candle依赖

cargo add candle-core --features cuda

执行此命令后,查看Cargo.toml文件,可以看到依赖已更新为candle-core = { version = “…”, features = [“cuda”] }

现在,我们可以构建并运行这个支持CUDA的项目,以验证CUDA环境是否配置正确。

cargo build && cargo run

如果程序能够成功构建并运行,则说明CUDA支持已成功集成。


总结

本节课中,我们一起学习了使用Candle框架的入门步骤。我们从验证Rust和CUDA开发环境开始,然后创建了一个基础的Rust项目并添加了Candle依赖。通过构建和运行“Hello World”程序,我们确认了基础环境工作正常。最后,我们创建了另一个项目,并通过添加cuda特性为其启用了GPU加速支持,验证了CUDA环境的可用性。现在,你已经拥有了一个可以开始进行本地大语言模型开发的起点环境。

012:探索StarCoder的Rust实现 🚀

在本节课中,我们将要学习如何探索并使用一个名为StarCoder的开源代码生成大语言模型。我们将重点关注其基于Rust的实现,并演示如何在实际环境中运行它。

概述

StarCoder是Hugging Face推出的一个开源代码生成模型。它与其他生成式AI编码模型(例如GitHub Copilot)类似,但完全开源。该模型拥有155亿个参数,并在来自The Stack数据集的80种编程语言上进行了训练。其使命是通过发布数据、模型和实验,推动生成式AI革命的民主化,而不仅仅是将其掌握在风险投资家手中。

StarCoder简介

上一节我们介绍了StarCoder的基本背景,本节中我们来看看它的一个具体实现版本。

这里我们看到的是Hugging Face的StarCoder模型,它比我最初想象的还要有趣。它是一个专门用于代码生成的LLM。一旦安装了初始的Hugging Face工具,身份验证过程就相当简单。

环境设置与验证

为了运行这个模型,我们需要一个合适的环境。以下是如何设置和验证环境的步骤。

首先,我们需要安装必要的工具并进行登录。在我的一个演示项目中,信息如下所示:

pip install huggingface_hub
huggingface-cli login

在我的Codespaces环境中,我可以看到这个操作的实际效果。输入huggingface-cli whoami后,显示我已经成功通过身份验证,可以使用这个项目了。

但是,由于我将要运行一个对GPU要求较高的任务,因此需要更强大的硬件。我将切换到一个运行着AWS新一代GPU(G5)的远程Visual Studio Code环境中。

在这个远程环境中,我们可以再次验证登录状态。输入huggingface-cli whoami,确认我已登录。

运行StarCoder模型

现在我们已经登录,接下来看看如何运行这个模型。这个环境的一个优点是,我可以打开第二个窗口来监控GPU的使用情况。

以下是运行模型的具体步骤:

  1. 打开一个终端并运行htop命令,可以查看CPU核心的使用情况。这台机器拥有64个核心,性能强大。
  2. 打开另一个终端,运行nvidia-smi -l 1命令,可以循环检查GPU的状态。
  3. 准备好之后,就可以运行命令来测试模型了。

运行的核心命令是:

cargo run --features cuda --example bigcode -- --prompt "rust function that adds two numbers"

让我们逐步分析这个命令的含义:

  • cargo run是执行Rust项目的典型命令。
  • --features cuda表示使用已安装在此AWS深度学习基础AMI上的CUDA驱动程序。因为CUDA驱动程序是为神经网络优化的版本,使用它可以显著提升性能。如果我们想将其作为编码助手长期使用,那么采用最优化的方法是合理的。
  • --example bigcode指定运行Hugging Face项目中的bigcode示例。
  • --prompt后面是我们给模型的提示,例如“rust function that adds two numbers”。

执行这个命令后,你会看到GPU被触发,然后模型会给出一个关于如何用Rust编写两数相加函数的建议。

测试不同编程语言

模型加载完成后,我们可以轻松地测试它对不同编程语言的代码生成能力。

我们可以将提示词改为“Python function that adds two numbers”,模型会再次运行并给出Python版本的函数建议。

同样,我们也可以测试“Ruby function that adds two numbers”。如果我们想计时,可以看到它在微秒级别内检索了文件,在大约两秒钟内加载了模型,然后开始了推理循环。加载模型的时间似乎是这里的瓶颈,但这仍然不算太糟。

模型能够根据提示给出相当合理的建议。我们还可以继续测试其他语言,例如Java。

总结

本节课中我们一起学习了StarCoder开源代码生成模型及其基于Rust的实现。我们了解了它的背景、使命,并一步步演示了如何在配置了强大GPU的远程环境中设置身份验证、运行模型并测试其对不同编程语言的代码生成能力。对于希望拥有自己开源编码助手的研究者或开发者来说,借助Rust的性能和Hugging Face生态系统,这是一个非常有前景且直接可行的方案。

013:使用Whisper与Candle进行转录

概述

在本节课中,我们将学习如何使用Whisper模型与Candle框架进行自动语音识别。我们将从环境配置开始,逐步介绍如何运行示例代码,并最终使用自定义音频文件进行转录。


环境配置与GPU支持

上一节我们介绍了自动语音识别的概念,本节中我们来看看如何配置运行环境。

Candle是一个极简、高性能的框架,支持GPU加速。以下是使用Candle运行Whisper模型的基本步骤。

首先,建议使用配备较新NVIDIA GPU的EC2实例,例如G5实例类型。这些实例通常拥有强大的计算核心和快速的GPU。

通过Visual Studio Code的远程连接功能,可以方便地访问远程服务器。在终端中,可以使用 nvidia-smi 命令确认GPU状态。

运行Whisper示例

了解了环境配置后,我们来看看如何运行Whisper的示例代码。

Candle的代码库中提供了Whisper的示例。我们可以通过以下命令运行基础示例:

cargo run --example whisper

此命令会在CPU上运行,速度较快,并会自动下载一个示例音频文件进行转录。

启用GPU加速

虽然CPU运行速度尚可,但为了获得最佳性能,我们可以启用GPU加速。

Candle支持通过特性标志启用CUDA和cuDNN支持。cuDNN是针对神经网络操作的优化库。

以下是启用不同级别加速的命令:

# 启用CUDA支持
cargo run --example whisper --features cuda

# 启用CUDA和cuDNN支持
cargo run --example whisper --features cuda,cudnn

启用 cudnn 特性后,推理速度会更快,GPU利用率更高,是一种更高效的推理方式。

使用自定义音频文件进行转录

掌握了基础运行方法后,现在我们可以尝试使用自己的音频文件进行转录。

我们需要在命令中指定输入文件路径、模型大小和任务类型。以下是一个完整的命令示例:

cargo run --example whisper --release -- \
    --input ../four_score.wav \
    --model medium \
    --language en \
    --task transcribe

命令参数说明:

  • --input: 指定要转录的音频文件路径。
  • --model: 指定使用的Whisper模型大小,如 tiny, base, small, medium, large
  • --language: 指定音频语言。
  • --task: 指定任务类型,transcribe 为转录,translate 为翻译。

在CPU上运行此命令可能会比较慢。为了提升速度,我们可以结合之前提到的GPU加速特性重新运行:

cargo run --example whisper --features cuda,cudnn --release -- \
    --input ../four_score.wav \
    --model medium \
    --language en \
    --task transcribe

启用GPU后,可以看到GPU利用率显著上升(例如达到70%),推理速度大幅加快。这种方法非常适合需要处理大量音频转录任务的场景,例如利用AWS Batch等服务构建数据处理系统。

总结

本节课中我们一起学习了使用Whisper模型和Candle框架进行自动语音识别的完整流程。我们从环境配置开始,学习了如何运行基础示例,接着探索了通过启用CUDA和cuDNN特性来利用GPU加速,最后实践了如何使用自定义音频文件和特定参数进行转录。这套工具组合为高效、本地化的语音转文字任务提供了强大的解决方案。

014:探索AWS远程开发工作流 🚀

在本节课中,我们将学习如何利用AWS云服务构建一个基于Rust和Hugging Face Candle库的异步开发与生产工作流。这个架构旨在高效、低成本地进行大语言模型的推理。

开发环境配置 💻

上一节我们介绍了本地开发的基础,本节中我们来看看如何利用云端强大的计算资源进行开发。

首先,你需要一个EC2 G5实例。这个实例的优势在于它拥有执行现代GPU推理任务的能力。为了节省成本,你还可以使用Spot实例。在开发阶段,你可以随时停止和启动这个实例。

你可以通过Visual Studio Code Remote插件连接到这个云端实例,这是一种非常高效的开发方式。只需安装插件,你的笔记本电脑就能无缝访问云端强大的计算环境。

这意味着,你可以在一个基于云的、功能强大的环境中开始编写代码,并且只需按使用量付费,而无需购买昂贵的硬件(例如一块价值4万美元的GPU)。

生产环境部署 🏗️

在配置好开发环境后,接下来我们看看如何将其部署到生产环境。

这里有一个有趣的方案:你可以结合使用EFS(Elastic File System)进行协同开发。EFS的挂载点可以设置在开发机器上,但同一个挂载点也可以用于生产环境

以下是利用EFS的关键步骤:

  • 你可以将庞大的模型和权重文件(数据量可能达到数十GB)下载到EFS中。
  • 这个EFS存储位置可以作为贯穿开发到生产阶段的数据暂存区。
  • 当你向AWS Batch部署任务时,Batch服务可以自动配置EC2 G5实例。
  • 这些实例能够以高效、优雅且低成本的方式,使用强大的GPU执行基于Rust的现代推理任务。

因此,你不需要依赖Notebook或Python,可以纯粹使用Rust来完成所有工作。

架构扩展思路 💡

此外,还有一个值得考虑的扩展思路。

如果你的代码编译时也兼容CPU目标架构,那么AWS Lambda函数可以直接从EFS读取编译好的二进制文件,并同样能够对外提供生产级别的推理服务。

这为LLM Ops和MLOps领域展现了一个非常令人兴奋的、新兴的Rust技术栈故事。它意味着你可以更开放地尝试新事物、新想法,并持续追求效率。

对于许多希望部署大语言模型服务的公司而言,本节介绍的这种特定架构是一个很好的起点。

总结 📝

本节课中我们一起学习了如何构建一个基于AWS的远程开发与部署工作流。我们了解了使用EC2 G5实例进行开发、通过EFS共享模型数据、利用AWS Batch部署GPU推理任务,以及扩展至AWS Lambda的可能性。这套架构结合了Rust的性能优势与AWS云的弹性,为实现高效、低成本的大语言模型服务提供了坚实基础。

015:Rust在LLM中的应用 🦀

在本节课中,我们将探讨Rust编程语言在构建和部署大型语言模型(LLM)中的关键优势与应用。我们将逐一分析Rust的各个组件如何为LLM的开发带来安全性、性能与模块化。

概述

Rust的诸多特性使其非常适合用于大型语言模型。接下来,我们将逐步讨论这些特定的组件。

Rust编译器的核心优势

上一节我们提到了Rust的整体优势,本节中我们来看看其编译器的具体特性。

在Rust编译器中,借用检查器(Borrow Checker)能够防止数据竞争和不安全的内存访问错误。其所有权系统(Ownership System)能在对象离开作用域时自动释放内存,因此无需手动管理内存。同时,编译时检查(Compile-time Checks)能在代码部署前发现错误,确保你的大型语言模型部署在模块化组件层面非常健壮。

LLM的模块化组件设计

了解了编译器的基础保障后,我们来看看如何利用Rust的特性构建LLM的核心模块。

以下是LLM中关键的模块化组件及其在Rust中的实现方式:

  • 分词器(Tokenizer):将文本转换为用于词嵌入(Word Embeddings)的数值ID。
  • 嵌入层(Embedding Layer):将词语映射到高维密集向量。其核心操作可表示为:embedding_vector = lookup(word_id)
  • 变换层(Transform Layer):使用神经网络模型(如Transformer)处理文本。这种模块化设计允许利用Rust的Trait来混合和匹配特定的组件。

并发与性能

模块化设计解决了结构问题,而高性能LLM还需要充分利用硬件资源。接下来我们看看Rust如何实现高效的并发。

轻量级的Rust线程(Lightweight Threads)允许并发与并行执行。通道(Channels)可以安全地在线程间传递数据,没有数据竞争的风险。这使得能够利用多核CPU来提升性能和可扩展性。

Cargo包管理器与生态系统

要实现上述所有功能,离不开强大的工具链支持。本节将介绍Rust的包管理器Cargo。

Cargo是Rust内置的包管理器,它将管理应用程序的依赖关系。其庞大的生态系统提供了丰富的包,允许你扩展功能,例如构建命令行工具或Web框架。你也可以轻松地在项目和团队之间共享代码,并且它通过语义化版本控制(Semantic Versioning)来确保稳定性。

因此,这是调用大型语言模型的一种简便方法,我们在Rust Candle框架中可以看到这一点。你只需要执行:

cargo run --example

总结

本节课中,我们一起学习了Rust在大型语言模型中的应用。我们从其编译器提供的内存安全和早期错误检查开始,探讨了如何利用Rust的模块化特性构建LLM的核心组件(分词器、嵌入层、变换层)。接着,我们了解了Rust的轻量级线程和通道如何安全地实现并发以提升性能。最后,我们介绍了Cargo包管理器及其生态系统如何简化依赖管理和项目开发。这些特性共同使Rust成为部署健壮、高效LLM的一个有力选择。

016:无服务器推理 🚀

在本节课中,我们将学习如何为大型语言模型(LLM)构建无服务器推理架构。我们将探讨其核心优势、工作原理以及如何利用量化技术实现高效、低成本的部署。

概述

无服务器推理为大型语言模型的部署带来了革命性的变化。它结合了量化模型和自动伸缩的云函数,旨在实现高可扩展性、成本效益和快速响应。

无服务器推理架构解析

上一节我们介绍了无服务器推理的概念,本节中我们来看看其具体的架构组成。下图展示了一个典型的无服务器推理流程:

以下是该架构的核心组成部分:

  1. 用户输入:输入可以是文本、图像或其他形式的数据。
  2. 无服务器函数:例如 AWS Lambda。该函数包含模型本身以及用于运行量化模型的 Rust Candle 运行时。
  3. 量化模型:模型经过量化处理,使用低精度整数数据类型(如 int8)替代 32 位浮点数(float32)。这能使模型体积缩小高达 4 倍
  4. 优化运行时:Candle 运行时(类似 Llama.cpp)提供了专门优化的操作,以高效运行量化模型。
  5. 自动伸缩:无服务器函数可根据流量自动伸缩,处理任意规模的请求,确保快速响应。当无请求时,函数实例会缩容至 0。
  6. 输出:处理结果被快速返回给用户。

核心优势与特点

了解了架构之后,我们来看看这种方案带来的主要好处。

  • 成本效益:模型体积小,且无闲置资源,使得部署非常经济。计费基于函数调用次数,而非持续运行的昂贵 GPU 实例。
  • 高性能:量化后的整数模型运行速度更快,即使在低端或移动 CPU 上也能表现良好,这为许多新兴应用场景打开了大门。
  • 可扩展性:架构自动伸缩以处理任何流量,同时通过就近部署最小化延迟。
  • 易于部署:该架构使你能够将先进的机器学习模型以高效、经济的方式大规模部署到生产环境中。

总结

本节课中我们一起学习了大型语言模型的无服务器推理架构。我们了解到,通过结合量化技术(使用 int8 而非 float32)和无服务器计算(如 AWS Lambda),可以实现高性价比、可扩展且快速响应的模型部署方案,从而无需为许多模型配备昂贵的 GPU。

017:Rust命令行推理

在本节课中,我们将学习如何构建并运行一个Rust命令行界面程序,该程序将使用Hugging Face的Transformer模型进行推理。整个过程从下载预训练模型开始,到最终通过命令行接收用户输入并输出推理结果结束。

概述

我们将分步构建一个Rust命令行推理工具。首先,从Hugging Face Hub下载预训练模型。接着,在Rust中加载模型。然后,构建命令行界面以接收用户输入。之后,实现将用户输入传递给模型进行推理的逻辑。最后,运行程序并查看结果。

构建流程详解

上一节我们概述了整体流程,本节中我们来看看每个步骤的具体内容。

第一步:下载模型

首先,需要从Hugging Face Hub下载一个预训练Transformer模型。你可以选择如BERT或GPT-2等模型。

第二步:在Rust中加载模型

下载模型后,需要在Rust代码中加载它。以下是两种主要方式:

  • 使用 rust-berttransformers crate。这些库提供了Hugging Face模型的Rust绑定。
  • 使用 rust-candle 接口。这也允许在Rust代码中使用模型进行推理。

第三步:构建命令行界面

模型加载完成后,下一步是构建命令行界面。你可以使用 clap crate来轻松解析命令行参数,从而允许灵活的用户输入。

以下是 clap 的基本使用示例:

use clap::Parser;

#[derive(Parser)]
struct Cli {
    prompt: String,
}

你甚至可以定制提示词。例如,如果你想为每个提示词添加一个固定的前言以获得更好的结果,可以将此逻辑集成到代码中。

第四步:实现推理逻辑

命令行界面构建好后,需要实现推理逻辑。该逻辑负责接收用户输入,将其传递给之前加载的Hugging Face模型,并返回结果。

rust-bert crate 将通过处理数据转换和推理过程使这一步变得简单。

第五步:运行与推理

最后,用户通过运行命令行工具并提供文本输入来使用它。该输入通过命令行界面传递给Hugging Face模型,推理过程在Rust中执行,结果将打印输出给用户。

总结

本节课中我们一起学习了构建Rust命令行推理工具的完整流程。简而言之,步骤如下:

  1. 从Hugging Face下载模型。
  2. 使用模型crate在Rust中加载模型。
  3. 使用 clap 构建命令行界面。
  4. 实现推理逻辑并将其与模型集成。
  5. 运行命令行工具,对用户输入执行推理。

使用Rust及其crate生态系统是执行大语言模型推理的一种高效方式,在许多情况下可能是最高效的方式。

018:使用Rust构建聊天推理应用 🦀

在本节课中,我们将学习如何利用Rust生态系统中的即插即用组件,构建一个用于大语言模型推理的聊天机器人。我们将从选择模型开始,逐步完成加载模型、构建用户界面和实现核心聊天逻辑的整个过程。

概述

我们将构建一个基于终端的Rust聊天机器人,它能够加载预训练的大语言模型,与用户进行自然语言对话。整个过程涉及几个关键步骤:选择合适的模型、在Rust程序中加载模型、构建用户界面以及实现处理用户输入和生成回复的核心循环。

选择与加载模型 🧠

上一节我们介绍了项目的整体目标,本节中我们来看看如何选择并加载模型。

首先,你可以考虑使用Falcon模型。这是一个非常适合入门的模型,在自然语言对话任务上表现出色。

接着,你可以使用像candle这样的Rust库将模型加载到程序中。candle是一个Rust机器学习框架,能够访问Hugging Face上的模型库。

构建聊天机器人界面 💬

在成功加载模型之后,下一步是为我们的应用构建一个用户界面。

你可以使用cursive这类库来构建一个基于终端的用户界面。这个界面将负责接收用户输入并显示机器人的回复。

以下是构建核心聊天循环的步骤:

  1. 持续提示用户输入。
  2. 获取用户输入的文本。
  3. 将文本传递给之前加载的Hugging Face模型。
  4. 打印模型生成的回复。

实现核心聊天逻辑 ⚙️

我们已经有了界面,现在需要实现驱动对话的核心逻辑。

这个Hugging Face模型将处理所有的自然语言处理任务,包括分析用户输入、生成连贯的回复,甚至考虑对话的上下文。

Rust代码则负责促进这种交互。你还可以添加额外的逻辑,例如情感分析,以使回复更加自然。在完成这个原型之后,你甚至可以将界面适配成Web应用程序。

总而言之,你需要做的是:

  1. 从Hugging Face选择合适的模型(例如最新的Falcon模型)。
  2. 将其加载到Rust程序中。
  3. 构建聊天用户界面。
  4. 结合Hugging Face模型实现聊天逻辑。
  5. 实现持续提示用户并生成回复的循环。

总结

本节课中我们一起学习了使用Rust构建大语言模型推理聊天机器人的完整流程。这是一个令人兴奋的时代,开发者能够将这些强大的工具集成到像Rust这样命令行友好的语言中。Rust的独特之处在于,它在构建高性能二进制程序方面拥有巨大优势,使得开发此类应用变得高效而强大。

019:使用StarCoder构建聊天循环

概述

在本节课中,我们将学习如何利用Hugging Face的StarCoder大语言模型,构建一个属于你自己的代码助手聊天循环。我们将了解其核心组件和工作流程,并看到它如何超越早期的模型。


构建自定义对话式AI

上一节我们介绍了StarCoder模型的潜力,本节中我们来看看如何实际构建一个自定义的对话式AI。

观察这里的提示词,我们的目标是生成一个经过深思熟虑的响应。为此,你需要调用一个StarCoder终端点。

这个终端点可以部署在GPU实例上,例如AWS的G5实例。来自客户端的异步请求将允许非阻塞的异步调用。

因此,聊天机器人将能够非常轻松地作出响应。同时,你还需要将每一组消息对附加到对话历史中,以便为AI提供上下文。

以下是构建此聊天循环的核心步骤:

  • 调用StarCoder:向部署在GPU实例(如AWS G5)上的StarCoder模型端点发送请求。
  • 使用异步请求:采用非阻塞的异步请求,确保聊天界面响应流畅。
  • 维护对话历史:将每次的问答消息对添加到历史记录中,为模型提供连续的对话上下文。
  • 序列化数据:使用JSON格式可以方便地进行数据序列化。

利用Rust与Hugging Face Candle

为了确保聊天机器人既健壮又安全,我们可以利用Rust强大的类型系统和内存安全特性。

我们还可以使用Hugging Face的Candle推理引擎,来轻松集成StarCoder大语言模型。

在此基础上,我们能够构建自定义的Tokio聊天循环。你可以看到这里有一个pub async fn函数,它包含一个聊天循环,能够遍历并回复用户输入的内容。

总结

本节课中我们一起学习了使用StarCoder构建聊天循环的核心方法。简而言之,这个过程非常直接:利用从StarCoder模型、Hugging Face推理引擎、Tokio异步运行时,到开源JavaScript客户端等构建模块,你就可以打造属于自己的代码助手聊天循环,而无需向任何人支付费用。

020:在AWS GPU上调用Rust Candle(第一部分)

概述

在本节课中,我们将学习如何利用Rust的包管理系统,在AWS的GPU实例上调用大型语言模型。我们将使用Hugging Face的Candle框架,这是一个为Rust设计的极简机器学习框架。通过本教程,你将能够自行运行模型,而无需依赖外部API服务或复杂的配置。

核心概念与工具介绍

本节我们将介绍本次实践的核心工具:Rust的Cargo包管理系统和Hugging Face Candle框架。Candle框架的代码非常简洁,其最令人兴奋的特点是,你只需运行一行命令就能调用各种大型语言模型。这极大地简化了LLM的部署流程。

在AWS上配置GPU实例

上一节我们介绍了核心工具,本节我们来看看如何配置运行环境。首先,你需要在AWS控制台中启动一个实例。

以下是配置实例的关键步骤:

  1. 选择基础AMI:建议选择“Deep Learning Base AMI”。这个镜像只包含必要的CUDA驱动和cuDNN库,没有多余的软件,非常适合我们的需求。
  2. 选择实例类型:这是关键步骤。你需要选择基于NVIDIA GPU的实例类型。在筛选器中,选择“加速计算”或直接查找“G5”系列实例。G5系列实例预装了NVIDIA驱动,适合运行机器学习工作负载。

配置完成后,你可以在EC2控制台看到正在运行的实例。通过“EC2 Instance Connect”功能,你可以无需设置SSH密钥直接连接到该实例,这非常方便。

设置Rust开发环境

现在我们已经有了可用的GPU服务器,接下来需要在服务器上设置Rust环境。

连接至你的EC2实例后,只需运行一个命令来安装Rust:

curl --proto ‘=https’ --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装完成后,你可以通过 rustc --version 命令验证安装。

克隆并准备Candle代码库

环境准备就绪后,最后一步是获取Hugging Face Candle的代码。

你需要克隆Candle的代码仓库到本地。可以使用以下命令:

git clone https://github.com/huggingface/candle.git

至此,所有前期准备工作已经完成。在接下来的部分,我们将深入如何使用Cargo来实际运行模型。

总结

本节课我们一起学习了在AWS GPU实例上为运行大型语言模型做准备的全过程。我们介绍了Hugging Face Candle框架的简洁性,逐步讲解了如何选择合适的AWS AMI镜像和GPU实例类型,如何连接到实例,以及如何安装Rust和克隆必要的代码库。这一切都是为了一个目标:让你能够轻松地通过Rust的包管理系统调用强大的LLM。

021:在AWS GPU上调用Rust Candle(第二部分)

在本节课中,我们将学习如何从源代码构建并运行一个基于Rust Candle框架的大语言模型。我们将以BigCode模型为例,演示从克隆仓库到最终运行模型的完整流程。

上一节我们介绍了如何在AWS GPU实例上设置Rust Candle环境。本节中我们来看看如何具体构建和运行一个模型。

克隆与准备代码仓库

首先,你需要访问项目仓库并复制克隆命令。完成克隆后,你可以通过 git pull 命令获取最新的代码更新,因为该项目正在积极开发中。

构建模型的方法

有两种主要方式来调用这些大语言模型:一种是直接运行,另一种是先构建再运行。通常,更好的方式是先构建模型,生成可执行文件,然后在目标目录中进行操作。

以下是构建模型的具体步骤:

  1. 进入项目目录:使用 cd 命令进入你克隆的Candle示例仓库目录。
  2. 执行构建命令:运行 cargo build 命令来构建可执行二进制文件。
  3. 启用CUDA特性:为了利用GPU加速,在构建时需要指定 --features cuda 参数。这会针对神经网络计算优化CUDA驱动。

完整的构建命令如下:

cargo build --features cuda --release

此命令会为我们在 target/release 目录下生成一个优化后的可执行文件。

运行BigCode模型

BigCode是一个开源的大语言模型,可视为GitHub Copilot的竞争对手。构建完成后,我们可以运行它。

进入构建输出目录并执行模型:

cd target/release
./bigcode

现在,你就拥有了一个本地的结对编程助手。你可以通过提示词与它交互,例如:

bigcode -p "构建一个将两个数字相加的Python函数"

模型会快速生成相应的代码。整个过程展示了Rust在性能上的优势,使得本地运行大语言模型成为可能。

进一步应用

你可以将此过程封装到Bash脚本中,以便快速启动,或者根据需求调整模型参数。这为你提供了一个私有、高效且功能强大的编程辅助工具。

本节课中我们一起学习了如何构建和运行基于Rust Candle的BigCode模型。从克隆代码、启用CUDA加速构建,到最终运行模型并与之交互,我们完成了一个完整的本地大语言模型调用流程。这为在自有基础设施上部署高效AI助手提供了实践基础。

022:用Python实现ELO评分系统 🏆

在本节课中,我们将学习如何用Python实现ELO评分系统。ELO系统是一种用于评估竞赛参与者相对技能水平的评分方法,广泛应用于国际象棋、电子竞技,乃至大型语言模型的对比评估中。我们将通过一个简单的代码示例,理解其核心逻辑。

概述

Chatbot Arena之所以令人兴奋,是因为它为大语言模型提供了“头对头”的对比测试平台。我们可以在此看到商业模型、开源模型和专有模型之间的较量。其真正酷炫之处在于,我们可以从数值角度深入分析哪个模型更优。

我们能够进行这种量化比较,其核心在于ELO评分系统。ELO是一种“赛程强度”指标。它评估的不仅是你赢了多少场,更是你战胜了多强的对手。例如,一个20胜0负但对手全是业余选手的记录,其含金量可能不如一个有几场失利但对手都是顶尖高手的记录。这正是ELO评分要告诉我们的信息:赛程强度。

用Python实现ELO

接下来,我们通过Python代码来具体实现ELO系统。我们将创建一个简单的类来模拟参赛者及其评分变化。

以下是实现ELO评分系统的核心代码。我们定义了一个Fighter类,它代表一名参赛者,包含姓名和初始评分。compete方法用于模拟一场比赛并更新双方的评分。

class Fighter:
    def __init__(self, name, rating=1500):
        self.name = name
        self.rating = rating

    def compete(self, opponent, result, K=32):
        """
        与对手进行比赛并更新评分。
        result: 1 表示胜利,0 表示失败。
        K: 评分调整幅度系数。
        """
        expected_score = 1 / (1 + 10 ** ((opponent.rating - self.rating) / 400))
        self.rating = self.rating + K * (result - expected_score)

代码解析

上一节我们介绍了ELO的基本概念,本节中我们来看看代码中的关键部分。

  • 预期胜率公式expected_score = 1 / (1 + 10 ** ((opponent.rating - self.rating) / 400))。这个公式计算了参赛者战胜对手的预期概率。当双方评分相同时,预期胜率为0.5。
  • 评分更新公式self.rating = self.rating + K * (result - expected_score)。这是ELO系统的核心。评分根据比赛结果(result,1为胜,0为负)与预期胜率(expected_score)的差值进行调整。K值决定了单场比赛评分变动的最大幅度。

参数说明

以下是关于代码中关键参数的说明:

  • K值:在示例中,K值设为32,这是国际象棋中常用的数值。K值越大,单场比赛对评分的影响也越大。在实际应用中,可以根据比赛频率和希望评分系统的灵敏度来调整K值。例如,UFC比赛场次较少,可能会使用更大的K值。
  • 初始评分:通常设为1500,这是一个基准值。

运行示例

现在,让我们创建两名参赛者并模拟一场比赛,看看他们的评分如何变化。

# 创建两名参赛者
conor = Fighter("Conor McGregor", rating=1500)
khabib = Fighter("Khabib Nurmagomedov", rating=1500)

# 打印赛前评分
print(f"赛前评分: {conor.name}: {conor.rating}, {khabib.name}: {khabib.rating}")

# 模拟比赛:Khabib 获胜
khabib.compete(conor, result=1)  # Khabib 获胜
conor.compete(khabib, result=0)  # Conor 失败

# 打印赛后评分
print(f"赛后评分: {conor.name}: {conor.rating:.0f}, {khabib.name}: {khabib.rating:.0f}")

运行上述代码,我们将看到类似以下输出:

赛前评分: Conor McGregor: 1500, Khabib Nurmagomedov: 1500
赛后评分: Conor McGregor: 1475, Khabib Nurmagomedov: 1525

可以看到,获胜者Khabib的评分上升了,而失败者Conor的评分下降了。如果进行多轮比赛,这个系统就能根据对手的强弱,而不仅仅是胜负记录,生成一个衡量参赛者实力的数值指标。这正是ELO系统的强大之处,它是衡量竞赛水平的绝佳方法。

交互式探索

我们也可以在交互式Python环境(如IPython或Jupyter Notebook)中探索这个对象。这让我们能够即时实验并观察对象的状态。

例如,我们可以导入Fighter类,创建实例,并查看其内部状态(本质上是一个包含姓名和评分的字典)。这说明了ELO并非神秘的魔法指标,其实现相当简单直观。

# 在交互式环境中
from elo import Fighter

conor = Fighter("Conor", 1500)
print(conor.__dict__)  # 输出: {'name': 'Conor', 'rating': 1500}

总结

本节课中,我们一起学习了ELO评分系统的原理,并用Python实现了一个简易版本。我们了解到,ELO通过考虑对手的强度来评估参赛者的技能水平,而不仅仅是胜负场次。我们实现了核心的预期胜率计算和评分更新公式,并通过示例看到了评分如何随比赛结果动态变化。这个直观而强大的评分系统,是评估大语言模型(无论是开源还是专有模型)在真实竞争场景中表现的优秀指标。

023:用Rust实现ELO评分系统 🦀

在本节课中,我们将学习如何使用Rust编程语言实现一个ELO评分系统。ELO系统是一种用于比较两个竞争者相对水平的方法,它根据赛程强度动态调整评分。我们将从零开始,构建一个简单的拳击手评分模拟器。

概述

ELO评分系统广泛应用于棋类、电子竞技和体育赛事中,用于量化选手的技能水平。其核心思想是:根据比赛结果(胜、负)和双方赛前评分,计算赛后新的评分。我们将用Rust实现这个逻辑,并理解Rust中诸如结构体、方法实现、可变性等关键概念。

项目结构与核心概念

首先,我们定义一个表示拳击手的结构体(struct)。结构体是Rust中自定义数据类型的主要方式,它允许我们将相关的数据项组合在一起。

struct Fighter {
    name: String,
    rating: u16,
}

这个Fighter结构体包含两个字段:name(姓名,字符串类型)和rating(评分,16位无符号整数类型)。

上一节我们定义了数据结构,本节中我们来看看如何为这个结构体添加行为(即方法)。在Rust中,我们使用impl块为结构体实现方法。

以下是Fighter结构体相关的方法实现:

  • new方法:这是一个关联函数(类似于其他语言中的静态方法),用于创建并返回一个新的Fighter实例。
  • update_rating方法:这是核心方法,它根据比赛结果更新拳击手的评分。它接受另一个拳击手的评分和本次比赛的结果(胜或负)作为参数。

核心逻辑:更新评分

现在,让我们深入update_rating方法,这里是ELO计算逻辑的核心。

fn update_rating(&mut self, opponent_rating: u16, result: &str) {
    let k_factor = 32; // K因子
    let expected_score = 1.0 / (1.0 + 10.0_f64.powf((opponent_rating as f64 - self.rating as f64) / 400.0));
    let actual_score = match result {
        "win" => 1.0,
        "loss" => 0.0,
        _ => 0.5, // 平局
    };
    let rating_change = (k_factor as f64 * (actual_score - expected_score)).round() as i16;
    self.rating = (self.rating as i16 + rating_change) as u16;
}

代码解释

  1. K因子(k_factor:这是一个常数,决定评分调整的幅度。在本例中设为32。如果比赛频繁(如国际象棋),K值会设得更小,使评分更稳定;反之,对于比赛较少的项目(如拳击),K值可以更大,使评分对新结果反应更灵敏。
  2. 期望胜率(expected_score:根据双方当前评分差,计算本方获胜的期望概率。公式为:E = 1 / (1 + 10^((R_opponent - R_self)/400))
  3. 实际得分(actual_score:根据比赛结果赋值,胜利为1.0,失败为0.0,平局为0.5。
  4. 评分变化(rating_change:计算公式为 评分变化 = K * (实际得分 - 期望胜率)。然后将结果四舍五入取整。
  5. 更新评分:将计算出的变化值应用到当前评分上。

这里需要注意Rust的一个重要概念:可变性(mutability)。由于update_rating方法需要修改self(即调用该方法的Fighter实例)的rating字段,因此方法的第一个参数是&mut self,表示对自身的一个可变引用。调用该方法的变量也必须声明为可变的(mut)。

模拟比赛流程

理解了核心算法后,我们来看看如何在主函数(main)中模拟一场比赛。

以下是模拟康纳·麦格雷戈(Conor McGregor)与哈比布·努尔马戈梅多夫(Khabib Nurmagomedov)之间比赛的步骤:

  1. 初始化选手:创建两名拳击手,并将他们的初始评分都设为1500。
    let mut fighter1 = Fighter::new(String::from("Conor McGregor"), 1500);
    let mut fighter2 = Fighter::new(String::from("Khabib Nurmagomedov"), 1500);
    
  2. 模拟比赛并更新评分:假设fighter2(哈比布)获胜,fighter1(康纳)失败。我们分别调用他们的update_rating方法。
    fighter1.update_rating(fighter2.rating, "loss");
    fighter2.update_rating(fighter1.rating, "win"); // 注意:此处传入的是fighter1更新前的旧评分
    
    请注意,在更新fighter2的评分时,我们传入的是fighter1更新前的评分(1500),这是ELO系统标准做法,即使用比赛发生时的“即时”评分进行计算。
  3. 输出结果:最后,打印两名拳击手赛后的新评分。

运行与编译

Rust项目通常使用Cargo工具进行管理。在运行代码前,保持良好的代码格式是一个好习惯。

我们可以使用以下命令来格式化代码并运行程序:

cargo fmt   # 格式化代码
cargo run   # 编译并运行程序

运行后,我们将在终端看到输出结果。获胜者(哈比布)的评分会上升,而失败者(康纳)的评分会下降。

cargo run默认会编译并运行调试(debug)版本。如果你需要更优的性能用于生产环境,可以使用cargo run --release来编译发布(release)版本,编译器会进行大量优化,使程序运行得更快。

总结

本节课中我们一起学习了如何使用Rust实现一个基础的ELO评分系统。我们首先定义了Fighter结构体来封装数据,然后为其实现了包含核心ELO计算逻辑的update_rating方法。在模拟比赛中,我们实践了Rust的可变性概念。最后,我们使用Cargo工具编译和运行了程序。

Rust以其安全性(严格的类型系统和所有权模型)和高性能而著称。正如示例所示,即使是一个简单的程序,也能受益于这些特性。这使得Rust成为需要大规模生成或计算指标(例如,在公司内部使用ELO系统评估不同大语言模型性能的模拟器)场景下的一个绝佳选择。

024:用R语言实现ELO评分系统

在本节中,我们将学习如何在Google Colab Pro环境中使用R语言,并实现一个用于评估选手实力的ELO评分系统。我们将了解Colab Pro的优势,并编写一个简单的R函数来计算比赛后的选手评分。

Colab Pro环境介绍

上一节我们介绍了本地大语言模型的基础概念,本节中我们来看看一个强大的在线编程环境——Google Colab Pro。Colab Pro提供了许多你可能不知道的有趣功能。

Colab Pro为你管理运行时环境。这意味着你无需担心软件包管理和依赖冲突问题。

以下是Colab Pro运行时环境的一些关键特性:

  • 支持R语言:你可以在Colab中使用R。这是一个很棒的功能,因为如果你想进行统计编程,R语言是难以被超越的,它是专为统计学设计的语言。你可以在这里开始尝试使用R,即使它可能比其他语言更难开发一些。
  • 硬件加速选项:你可以选择使用CPU、A100 GPU、V100 GPU等传统的NVIDIA GPU,也可以使用Google自研的Tensor处理单元(TPU)。
  • 高内存模式:你可以开启或关闭高内存模式。这在处理较大数据集时非常有用。

在Colab中运行R代码

我们可以在Colab中轻松运行R代码。例如,我们可以输入一个简单的“hello world”语句并获得反馈。这为我们提供了一个功能完整的数据科学集成开发环境。

实现ELO评分系统

利用Colab的R环境,我们可以深入编程,实现一个用于评估赛程强度的指标——ELO评分系统。

即使你不熟悉R,下面的ELO评分函数也相当容易理解。该函数接收两个参数:fighter1fighter2,以及比赛result

以下是该函数的代码实现:

elo_rating <- function(fighter1 = 1500, fighter2 = 1500, result, k = 32) {
  # 辅助函数:计算预期得分
  expected_score <- function(rating_a, rating_b) {
    return(1 / (1 + 10^((rating_b - rating_a) / 400)))
  }

  # 计算两位选手的预期得分
  expected1 <- expected_score(fighter1, fighter2)
  expected2 <- expected_score(fighter2, fighter1)

  # 根据比赛结果更新评分
  if (result == 1) {
    # fighter1 获胜
    new_rating1 <- fighter1 + k * (1 - expected1)
    new_rating2 <- fighter2 + k * (0 - expected2)
  } else {
    # fighter2 获胜
    new_rating1 <- fighter1 + k * (0 - expected1)
    new_rating2 <- fighter2 + k * (1 - expected2)
  }

  # 返回更新后的评分
  return(c(new_rating1, new_rating2))
}

这段代码的逻辑很清晰:

  1. 我们设置默认评分fighter1fighter2均为1500。
  2. 设置一个K因子(k),这是评分更新的一个关键参数。
  3. 定义一个计算预期得分的辅助函数expected_score
  4. 通过一个条件判断语句,根据比赛胜负来更新评分。
  5. 最后,函数返回更新后的评分。

让我们运行这段代码。现在,为了初始化评分,我们可以设定两位选手,例如“Connor”和“Khabib”。然后我们可以计算新的评分。

# 设定初始评分
connor_rating <- 1500
khabib_rating <- 1500

# 假设Khabib获胜 (result = 2 表示第二个选手获胜)
new_ratings <- elo_rating(connor_rating, khabib_rating, result = 2)

# 打印结果
cat("Connor的新评分:", new_ratings[1], "\n")
cat("Khabib的新评分:", new_ratings[2])

我们可以看到评分更新为:Connor是1475分(因为他输了),Khabib是1525分(因为他赢了)。

本节总结

本节课中我们一起学习了在Google Colab Pro中使用R语言的优势和基本操作。我们实现了一个ELO评分系统函数,并通过一个简单的例子演示了如何根据比赛结果更新选手评分。在Colab中进行R编程的一个显著优点是,它可以让你专注于R所擅长的领域(如统计编程),而无需过分纠结于环境配置和库安装等问题。这为分享解决方案和快速实验提供了很大的便利。

025:用Julia实现ELO评分系统 🏆

在本节课中,我们将学习如何使用Julia编程语言来实现一个ELO评分系统。我们将通过模拟两位UFC(终极格斗冠军赛)选手的比赛来演示如何计算和更新他们的评分。课程将涵盖Julia的基本数据结构、函数定义以及ELO算法的核心计算步骤。

概述

ELO评分系统是一种用于评估选手相对技能水平的方法,广泛应用于棋类、电子竞技和体育比赛。本节我们将用Julia语言编写代码,模拟两位UFC选手的比赛,并根据比赛结果动态更新他们的ELO评分。

定义选手结构体

首先,我们需要定义一个数据结构来存储选手的信息。在Julia中,我们可以使用struct来创建自定义类型。

以下是我们定义的Fighter结构体:

mutable struct Fighter
    name::String
    rating::Float64
end

这个结构体包含两个字段:name(选手姓名)和rating(ELO评分)。关键字mutable允许我们在后续修改rating的值。这是Julia语言一个精妙的安全特性,也是许多现代编程语言共有的特点。即使在脚本语言中,它也能提供这种安全保障。

实现评分更新函数

上一节我们定义了选手的数据结构,本节中我们来看看如何根据比赛结果更新选手的评分。我们将创建一个名为update_rating的函数。

以下是update_rating函数的定义:

function update_rating(fighter::Fighter, opponent::Fighter, result::String)::String
    K = 50
    expected_score = 1 / (1 + 10^((opponent.rating - fighter.rating) / 400))
    fighter.rating = fighter.rating + K * (parse(Int, result) - expected_score)
    return "Rating updated for $(fighter.name)"
end

这个函数接收三个参数:一个Fighter类型的选手、一个Fighter类型的对手,以及一个表示结果的字符串。函数内部定义了ELO计算的关键参数K(通常为常数),并计算了预期得分。最后,它根据实际结果更新选手的评分,并返回一个确认信息。函数签名中指定了参数和返回值的类型,这使代码意图更清晰。

模拟比赛与评分更新

理解了如何更新评分后,现在我们可以创建选手实例并模拟一场比赛,观察他们的ELO评分如何变化。

以下是模拟比赛的步骤:

  1. 初始化选手:在比赛开始前,为每位选手分配一个默认的初始评分。
  2. 执行ELO计算:调用update_rating函数,根据比赛结果更新评分。ELO系统通过衡量对手的竞争水平来评估选手的能力。
  3. 观察评分变化:比赛结束后,胜者的ELO评分会上升,败者的评分会下降。

例如,假设选手Conor McGregor和Khabib Nurmagomedov在赛前拥有相同的评分。经过一场比赛后,根据ELO计算,胜者Khabib的评分会升高,而败者Conor的评分会降低。

Julia语言的优势

通过这个简单的例子,我们可以看到Julia语言在数据科学领域的精妙之处。它融合了许多现代编程语言的特性:

  • 可变与不可变结构:默认情况下,结构是不可变的,这提供了内置的安全机制。当需要修改时,可以使用mutable关键字。
  • 类型系统:支持类型注解,使代码更健壮、更易理解。
  • 清晰的语法:语言设计简洁,如上例中的函数,逻辑一目了然。

因此,在构建数据科学解决方案时,在Jupyter笔记本中使用Julia是一个非常合理的选择。

总结

本节课中我们一起学习了如何使用Julia实现一个基础的ELO评分系统。我们从定义存储选手信息的mutable struct开始,然后实现了包含ELO核心公式的update_rating函数,最后模拟了比赛过程并观察了评分的动态更新。这个过程展示了Julia语言在类型安全、代码清晰度和功能性方面的优势,使其成为执行此类计算任务的合适工具。

026:开始使用Lorax与SkyPilot 🚀

在本节课中,我们将学习如何部署大型语言模型,并重点介绍两个强大的工具:Lorax和SkyPilot。我们将了解如何通过简单的配置,在云端快速启动一个高性能的推理服务器。


概述

部署大型语言模型是一个常见的挑战。Lorax是一个解决方案,它简化了部署过程。结合SkyPilot,我们可以通过云服务轻松配置和启动模型服务器。

Lorax简介

上一节我们介绍了部署大语言模型的挑战,本节中我们来看看Lorax如何解决这个问题。

Lorax的文档显示,其部署过程非常简单。你可以运行一个包含基础大语言模型的容器。例如,我们可以选择myel模型,并指定所需的GPU资源。完成这些步骤后,服务器就准备就绪了。文档还提供了如何向服务器发送提示的示例。

Lorax使用Rust语言构建,因此具有出色的推理性能。

使用SkyPilot进行云部署

我认为部署Lorax更简单的方法是使用基于云的GPU服务。接下来,我们看看如何通过SkyPilot来实现。

访问SkyPilot后,我们首先需要执行pip install命令进行安装,并确保环境准备就绪。之后的过程非常简单,因为你只需要粘贴一个YAML配置文件。

以下是关于TaaS的一点说明,他们在YAML文件方面做了大量优秀的工作。例如,微调模型也可以使用YAML文件。但请看这个部署配置,它非常清晰明了。

# 示例YAML配置结构
resources:
  cloud: AWS
  accelerators: V100:1
  ports: 8000
  # ... 其他配置

配置中清晰列出了云服务商(如AWS)、加速器类型、端口等信息。在我看来,这已经非常完善了。如果你已经设置好了AWS密钥,接下来只需要启动Lorax任务。

实战演示

我将切换到我的已配置环境中进行演示。一旦服务运行起来,你可以看到各种日志信息输出,甚至能看到HTTP请求记录。我将按照官方文档进行操作。

首先,我们需要检查服务状态。在另一个终端窗口中,运行以下命令:

sky status --show-ips lorax-cluster

命令执行后,我们可以获取到服务器的IP地址。

接下来,我们看看如何向服务器发送请求。这个过程非常简单,本质上就是使用curl命令。我们需要将上面获取的IP地址填入命令中。

我们可以并排运行服务器和客户端,以便观察。在服务器输出日志的同一界面下方,我们可以运行查询代码。我只需复制文档中的示例命令,粘贴并执行。

我们正在使用mystrel模型进行查询。可以看到,服务器成功生成了代码示例。

核心优势与操作

这种方法最棒的地方在于,我们获得了一个使用开源模型的高性能推理服务器。我们所做的仅仅是通过一个YAML文件进行配置。

更酷的是,当我们完成工作后,可以轻松停止部署以节省成本。运行停止命令,并确认即可。

sky stop lorax-cluster
# 系统提示确认时,输入 yes

总结

本节课中我们一起学习了如何部署大型语言模型。我认为,对于部署大型语言模型,尤其是开源模型,Lorax结合SkyPilot是目前最引人注目的解决方案之一。

向Pretabase及其团队致敬,他们构建了一个令人印象深刻的工作流程。如果我是一家公司的CTO,并考虑部署大型语言模型,这个方案会非常有吸引力。

027:使用Ludwig微调Mistral-7B模型 🚀

在本节课中,我们将要学习如何使用Ludwig这一低代码框架来微调Mistral-7B大语言模型。我们将了解其基于YAML配置文件的简洁工作流程,并演示如何在Colab Pro环境中执行微调任务。


认识Ludwig:一个低代码微调工具 🛠️

许多人对训练大语言模型并深入了解其生态系统非常感兴趣。Ludwig是一个很好的选择,因为它是一个低代码框架。

上一节我们介绍了微调的基本概念,本节中我们来看看Ludwig如何简化这一过程。

Ludwig的核心特点是使用配置文件来定义任务,这深受许多开发者喜爱。它提供了入门指南,例如针对当前热门的开源大语言模型Mistral的微调教程。其基于YAML的方法非常直观,并且支持通过命令行进行训练,这使得它具备类似DevOps工作流的特性,能够通过一个YAML文件来简化整个流程。


在Colab Pro中设置环境 💻

为了进行微调,我们需要一个强大的计算环境。Colab Pro是一个很好的选择,因为它提供了GPU访问。

以下是设置Colab Pro运行时的关键步骤:

  • 选择运行时类型:在“运行时”菜单中,可以选择不同的GPU类型以适应特定用例。
  • 检查资源:可以确认运行时拥有充足的内存(RAM)和磁盘空间。
  • 获取更高算力:如果需要,可以升级到更高规格的GPU(如A100)以获得更强的计算能力。

代码生成微调实战 📝

这个特定笔记本的目标是使用大语言模型进行代码生成。数据集中包含英语指令和对应的代码。

以下是微调任务的核心配置和工作流程:

首先,需要加载代码生成数据。微调的本质是让一个已经表现不错的模型,通过混合一些自定义代码和数据,从而变得更好。

具体流程如下:

  1. 准备数据集:数据集中包含“指令”、“输入”和“输出”字段。
  2. 定义模型:在YAML配置文件中指定基础模型(例如mistralai/Mistral-7B-v0.1)。
  3. 配置任务:在YAML中定义输入和输出。
model_type: llm
base_model: mistralai/Mistral-7B-v0.1

input_features:
  - name: instruction
    type: text

output_features:
  - name: output
    type: text

接下来,会进行一些标准的数据处理。然后,微调过程正式开始。

在微调执行期间,你可以看到实时的输出日志,包括损失值、评估指标以及正在使用的GPU类型。这使得监控训练过程变得非常方便。


总结 📚

本节课中我们一起学习了使用Ludwig框架微调Mistral-7B模型的全过程。我们了解到Ludwig通过YAML配置文件命令行接口提供了可能是最简单的微调方式。从在Colab Pro中设置GPU环境,到准备代码生成数据集并配置微调任务,Ludwig的笔记本示例使得微调新的大语言模型变得直观且易于上手。对于初学者而言,这无疑是一个强大而友好的入门工具。

028:检索增强生成(RAG)简介

在本节课中,我们将要学习一个名为“检索增强生成”的核心概念。它能让大语言模型访问其训练数据之外的新信息,从而提供更准确、更相关的回答。


检索增强生成是一个初看难以掌握,但一旦理解其价值便会立刻想要实现的技术。它的实现具有一定复杂性,我们将一同探索其工作原理,并找到能在你自身系统中进行尝试的方法。

检索增强生成,通常以其英文首字母缩写 RAG 指代。接下来我们将看到,这是一种让大语言模型能够访问其训练数据之外的新数据或近期数据的方法,从而使其能够提供更优质的答案。

上一节我们介绍了RAG的基本概念,本节中我们将深入探讨如何管理我们希望用于检索增强生成的数据。

我们将深入研究如何管理那些我们希望用于检索增强生成的数据。具体来说,这个过程主要包含几个关键步骤。

以下是实现RAG所需的核心步骤:

  1. 数据准备与管理:首先,需要整理和组织你想要提供给模型的外部知识源,例如文档、数据库或网页内容。
  2. 创建嵌入向量:接着,使用嵌入模型将文本数据转换为数值向量(即嵌入)。这个过程可以用公式表示为:embedding_vector = embedding_model(text_chunk)
  3. 使用向量数据库进行搜索:将这些向量存储在一个专门的向量数据库中。当用户提出问题时,将问题同样转换为向量,并在该数据库中进行相似性搜索,以找到最相关的文本片段。
  4. 与大语言模型结合:最后,将检索到的最相关文本片段与用户的原始问题一起,作为提示输入给大语言模型。模型会基于这些补充信息生成最终答案。其核心流程可概括为:最终答案 = LLM(用户问题 + 检索到的相关文本)


本节课中我们一起学习了检索增强生成(RAG)的基本原理。我们了解到,RAG通过让大语言模型访问外部数据来增强其回答能力,并概述了实现RAG所需经历的数据准备、向量化、检索与合成四个关键阶段。

029:什么是RAG 🧠

在本节课中,我们将要学习检索增强生成技术。这是一种通过利用外部知识库来扩展、锚定和提升大语言模型输出结果的方法。

什么是检索增强生成?

检索增强生成是一种技术,它通过使用搜索数据库或搜索引擎来扩展、锚定和改善结果。这种数据库有时也被称为向量数据库。

为了帮助理解,我们来看一个RAG基本实现的例子。用户提供输入,通常大语言模型会直接处理输入并产生输出。但在RAG中,我们有一组文档,这些文档会被向量化并分割成大语言模型能够理解的块。随后,我们可以利用这些文档来增强模型的回答能力。

RAG的工作原理 🛠️

上一节我们介绍了RAG的基本概念,本节中我们来看看它的具体工作流程。

大语言模型的能力存在局限性。例如,如果你询问关于美国当前的政治事件,模型可能无法准确回答,因为它可能没有使用最新的数据进行训练。这时,数据库、搜索引擎或向量数据库就派上用场了。你可以用最新的、高度准确的信息来增强模型。

在处理用户请求时,系统会首先查询文档库,获取相关信息,然后将这些信息连同原始问题一起发送给大语言模型。这样,大语言模型就能接收到问题以及来自文档的相关搜索结果。

以下是一个示意图,展示了RAG的基本流程:

一个更完整的应用架构

另一个图示来自Microsoft Learn,展示了Azure AI搜索中的检索增强生成架构。请注意,Azure AI搜索最近从“Azure认知服务搜索”更名而来。

这个架构描述了一个更完整的应用程序流程。请求首先进入应用程序,应用程序可能包含API、Python代码或容器进行处理。然后,请求会被提交到Azure AI搜索。Azure AI搜索利用预先加载的大量信息,生成必要的响应来增强原始请求。接着,这个增强后的请求会被发送到Azure OpenAI或其他模型。模型的响应会返回给协调器,最终呈现给用户。

本质上,这就是RAG的核心思想。当人们谈论搜索、向量数据库或检索增强生成时,他们指的都是类似的实现方式:通过预先加载的文档来增强模型的行为,为模型提供其当前所不具备的额外信息。

以下是Azure AI搜索中RAG的架构示意图:

核心概念总结

本节课中我们一起学习了检索增强生成技术。其核心思想可以概括为以下流程:

  1. 文档处理:将外部文档向量化分块
  2. 请求增强:用户查询时,先到向量数据库中检索相关文档片段。
  3. 模型生成:将原始问题与检索到的相关上下文一并提交给大语言模型,生成最终答案。

这种方法的核心优势在于,它能用外部、最新、特定领域的知识来弥补大语言模型自身知识的不足,从而提供更准确、更可靠的回答。

030:RAG应用概述 🧠

在本节课中,我们将学习检索增强生成(RAG)的基本工作流程。我们将了解一个用户查询如何通过框架、向量数据库和大语言模型的协作,最终生成一个包含外部知识的回答。


查询的起点

整个过程始于一个用户查询。通常,这个查询会直接发送给应用框架,然后由框架传递给大语言模型处理并返回结果。

在RAG架构中,情况有所不同。查询首先到达应用框架。这个框架可以是像LangChain或Semantic Kernel这样的专门框架,也可以是一个直接调用API的普通应用程序。

转向向量数据库

查询到达框架后,框架并不会直接将查询发送给大语言模型。相反,它会先将查询发送到向量数据库

向量数据库会基于查询执行一次搜索。例如,如果用户的查询是“昨天的天气如何?”,向量数据库中可能存储了大语言模型本身不具备的、关于昨天天气的具体信息。

构建增强上下文

从向量数据库获取的检索结果不会直接返回给用户。这个结果会先返回到应用框架。

框架会执行一个关键步骤:它将原始查询和从向量数据库得到的答案结合起来,构建一个增强的上下文。这个上下文同时包含了问题(查询)和相关的答案(检索结果)。

大语言模型的“开卷考试”

接下来,这个增强的上下文会被发送给大语言模型。

可以这样理解:这就像是一个学生在参加开卷考试。大语言模型已经通过上下文拿到了“答案”。它的任务不再是凭空生成信息,而是将已有的答案(来自向量数据库的信息)用通顺、自然的方式组织起来,包装成一个完整的回复。

所以,大语言模型的工作可以概括为:基于给定的问题和现成的答案,生成一个格式良好、易于理解的最终响应。

路径总结与优势

最终,这个由大语言模型生成的响应会通过框架返回给用户。

这种来回交互的机制非常巧妙。它能够为大语言模型提供相关的额外事实和背景信息,而无需在模型训练阶段就注入所有知识。

这种架构的核心优势在于:

  • 成本效益:训练大语言模型的成本极高,但向向量数据库中添加文档的成本相对很低,操作也简单。
  • 知识更新灵活:可以随时更新数据库中的知识,而无需重新训练整个大模型。

本节课中,我们一起学习了检索增强生成(RAG)的基本工作流程。我们看到了用户查询如何经过框架路由到向量数据库进行检索,检索结果又如何与原始查询结合形成增强上下文,最终由大语言模型“润色”后返回给用户。这个流程的核心思想是为大模型提供“外部记忆”,使其能够回答超出其原始训练数据范围的问题。在后续课程中,我们将深入探讨如何向向量数据库中添加文档的具体过程。

031:RAG的数据管理 📊

在本节课中,我们将学习如何为检索增强生成应用准备和管理数据。我们将从一个包含全球葡萄酒信息的CSV文件开始,逐步将其处理成适合向量数据库使用的格式。

概述

为了构建一个检索增强生成应用,我们首先需要准备数据。本节将演示如何加载一个结构化的CSV文件,并使用Python的pandas库将其转换为适合后续创建嵌入和存入向量数据库的格式。

数据加载与查看

首先,我们需要加载数据。我们使用一个包含葡萄酒信息的CSV文件,其字段包括:名称、产区、品种、评分和品酒笔记。

以下是加载和查看数据的步骤:

import pandas as pd
df = pd.read_csv(‘./wine_data.csv’)
print(df.head())

运行上述代码后,我们可以看到数据的前几行。数据包含1365条记录,评分主要集中在96分及以上,因为这是从一个更大的数据集中筛选出的子集。

数据格式转换

上一节我们成功加载了数据,本节中我们来看看如何将其转换为向量数据库所需的格式。直接使用DataFrame的默认格式可能不符合要求,因此我们需要将其转换为字典列表(records格式)。

以下是转换数据格式的步骤:

data_dict = df.to_dict(‘records’)
print(data_dict[:2]) # 查看前两条转换后的记录

通过调用 df.to_dict(‘records’),我们将每一行数据转换成一个字典,其中键是列名,值是对应的数据。这种格式便于后续向量数据库处理,因为它清晰地定义了每个字段及其内容。

为何选择Records格式

我们选择records格式而非其他格式(如listseries)有特定原因。其他格式可能将行索引作为键,导致数据结构不符合我们的预期。

以下是不同格式的对比说明:

  • records格式:生成一个字典列表,每个字典代表一行数据,结构为 [{‘column1’: value1, ‘column2’: value2}, …]。这是我们需要的格式。
  • 默认格式(dict:生成一个以列名为键,以该列所有值组成的序列(如Series)为值的字典,结构为 {‘column1’: {0: value1, 1: value2}, …}。这种格式不适合直接用于我们的场景。

因此,使用records格式能确保数据以正确的结构传递给向量数据库,为后续的嵌入创建和高效检索奠定基础。

总结

本节课中我们一起学习了RAG应用数据管理的基础步骤。我们从加载一个结构化的CSV文件开始,使用pandas库进行初步查看,最后将DataFrame转换为字典列表(records格式)。这个处理后的数据格式是后续创建文本嵌入并存入向量数据库的必要前提。整个过程虽然涉及多个步骤,但每个步骤都清晰明了,为构建检索增强生成应用准备好了数据基石。

032:验证嵌入与搜索 🔍

在本节课中,我们将学习如何加载数据、创建向量数据库,并执行语义搜索。整个过程将涉及数据清洗、文本嵌入以及使用向量数据库进行相似性查询。


上一节我们介绍了数据处理的基本步骤,本节中我们来看看如何具体实现数据加载与清洗。

我将重复一些步骤来加载CSV文件,并使用pandas将其加载进来。这里我仍然会使用pandas,你可以在此处看到。我会做一些调整,确保将数据转换为字典格式。

此处的核心操作是确保数据中不包含“非数字”值。这意味着任何没有值的字段都将被删除。我这样做是为了有效地修剪数据集,否则在后续序列化过程中可能会遇到问题。

以下是加载和清洗数据的代码:

import pandas as pd

# 加载CSV文件
df = pd.read_csv('your_file.csv')
# 删除包含NaN值的行
df_cleaned = df.dropna()
# 转换为字典列表
data_dict = df_cleaned.to_dict('records')

运行这些代码后,我们将获得名称、地区等信息,共1300行数据。


现在,我们将使用必要的软件来建立向量数据库搜索功能。我将使用Qdrant,并导入相关组件。

以下是所需的导入语句:

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams
from sentence_transformers import SentenceTransformer

我将详细解释每个组件的作用,以便你理解为何需要它们。

sentence_transformers 库为我们提供了编码能力,这是生成文本嵌入所必需的。我们将使用 all-MiniLM-L6-v2 模型,它会自动在本地下载机器学习模型,以便我们进行分词和生成嵌入向量,这些向量随后会存入向量数据库。这一步对于后续与大语言模型交互至关重要。

接着,我们将使用 QdrantClient 来与数据库通信。它是一个数据库实例,允许我们进行各种操作。其中一个很酷的功能是我们可以使用内存数据库,无需创建基于文件或网络的数据库,这对于示例演示非常方便,也是我今天选择它的原因。


接下来,我们将创建一个集合。这个集合将存在于数据库中,我们需要传入一些参数来配置它。

我们将指定距离度量方式,用于比较搜索查询与存储数据之间的相似性。我们将创建集合的向量配置,确保向量大小与所选模型匹配,并使用余弦距离。

以下是创建集合的代码:

# 初始化编码器
encoder = SentenceTransformer('all-MiniLM-L6-v2')
# 初始化Qdrant客户端(使用内存模式)
client = QdrantClient(":memory:")
# 创建集合
client.create_collection(
    collection_name="fine_wines",
    vectors_config=VectorParams(size=encoder.get_sentence_embedding_dimension(), distance=Distance.COSINE),
)

配置完成后,我们就能创建索引并将所有数据上传到向量数据库中。


我们将把集合命名为 fine_wines。上传数据时,我们会将“品酒笔记”字段编码为向量,其余信息则作为元数据存入载荷中。

以下是上传数据到向量数据库的步骤:

  1. 遍历清洗后的数据字典。
  2. 对“品酒笔记”文本进行编码,生成向量。
  3. 将向量和元数据封装为索引文档。
  4. 将文档上传至集合。
for i, record in enumerate(data_dict):
    # 编码文本笔记,生成向量
    vector = encoder.encode(record['notes']).tolist()
    # 准备元数据载荷
    payload = {key: value for key, value in record.items() if key != 'notes'}
    # 上传点(向量+载荷)到集合
    client.upsert(
        collection_name="fine_wines",
        points=[PointStruct(id=i, vector=vector, payload=payload)]
    )

运行此过程需要一些时间,因为我们要处理大约1300条记录。在我的系统上,这花费了54秒。对于演示,我建议减少条目数量以节省时间。


数据上传完成后,就到了验证的时刻:执行搜索。我们将使用Qdrant的搜索功能。

以下是执行搜索的代码:

# 定义搜索查询
query_text = "一款带有浆果味的浓郁红酒"
# 对查询文本进行编码
query_vector = encoder.encode(query_text).tolist()
# 执行搜索
search_result = client.search(
    collection_name="fine_wines",
    query_vector=query_vector,
    limit=3  # 返回最相似的3个结果
)

例如,当我们搜索“一款来自纳帕谷、带有浆果味的浓郁红酒”时,可能会得到评分99分的“Caymus”等结果。你可以看到,向量数据库主要利用“品酒笔记”字段进行语义匹配,搜索结果相当不错。

我们可以尝试不同的查询,例如“一款来自门多萨、口感丝滑的马尔贝克葡萄酒”。再次运行搜索,会得到完全不同的结果,比如“Catena Zapata”。这说明系统能够根据自然语言查询找到相关的葡萄酒信息。


那么,这为何有用且相关呢?因为我们配置的这个向量数据库能够理解我们的语言(通过嵌入模型),这为后续的自然语言处理(与大语言模型结合)奠定了基础。它可以帮助我们获取高度相关的信息片段,从而增强大语言模型最终回复的准确性和丰富性。


本节课中我们一起学习了从加载清洗数据、使用Sentence Transformers生成嵌入、到配置Qdrant向量数据库并执行语义搜索的完整流程。关键在于,我们建立了一个能理解文本语义的检索系统,它可以作为外部知识库,为大型语言模型提供精准的上下文信息。

033:在LLM中使用RAG

概述

在本节课中,我们将学习如何将外部数据(一个葡萄酒数据集)导入向量数据库,并利用检索增强生成技术,让大语言模型基于这些数据生成更准确、更有意义的回答。我们将通过一个具体的例子——查询阿根廷的优质马尔贝克葡萄酒——来演示整个工作流程。

准备工作

上一节我们介绍了向量数据库的基本概念。本节中,我们将实际操作,把CSV格式的葡萄酒数据加载到数据库中。

首先,我们需要导入必要的库并加载数据。为了提升演示速度,我们将数据条目限制在700条。

import pandas as pd
# 假设 wines.csv 是我们的数据文件
data = pd.read_csv('wines.csv').head(700)

创建向量数据库与集合

接下来,我们需要初始化向量数据库客户端,并创建一个专门用于存储葡萄酒数据的集合。

以下是创建向量数据库客户端的核心步骤:

# 导入必要的模块,例如Quadrant客户端
from quadrant_client import QuadrantClient
# 初始化客户端
client = QuadrantClient()
# 创建名为“top_wines”的集合
collection = client.create_collection(name="top_wines")

运行以上代码后,我们就有了一个可以存储和检索葡萄酒信息的向量数据库集合。

向数据库上传数据

数据库和集合准备就绪后,下一步是将处理好的葡萄酒数据上传到“top_wines”集合中。

这个过程涉及将每条葡萄酒记录转换为向量并存入数据库。虽然我们已将数据缩减到700条,但上传仍需一些时间,请耐心等待。

# 假设 `data_vectors` 是已经编码好的数据向量列表
collection.upload(data_vectors)

上传完成后,我们的数据库就包含了用于检索的葡萄酒知识。

执行检索与生成

现在,数据库已准备就绪。我们将结合大语言模型来回答一个具体问题。

我们定义用户的问题是:“请为我推荐一款阿根廷产的优质马尔贝克葡萄酒。”

以下是执行检索并准备上下文的核心代码:

user_query = "请为我推荐一款阿根廷产的优质马尔贝克葡萄酒。"
# 在集合中进行相似性搜索,获取最相关的几条记录
search_results = collection.search(query_vector=encode(user_query), limit=5)
# 将检索结果整合为给模型的上下文
context = "\n".join([result['text'] for result in search_results])

调用大语言模型获取答案

我们获得了相关的葡萄酒信息作为上下文。现在,我们将结合系统指令、用户问题和检索到的上下文,调用大语言模型来生成最终答案。

我们将使用一个预设了“葡萄酒专家”角色的系统指令,并将检索到的上下文作为补充信息提供给模型。

system_prompt = "你是一名葡萄酒专家,首要任务是帮助用户挑选出色的葡萄酒,并根据他们的请求提供指导。"
full_prompt = f"{system_prompt}\n用户问题:{user_query}\n相关葡萄酒信息:{context}"

# 调用大语言模型API,例如OpenAI的ChatCompletion
response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=[{"role": "user", "content": full_prompt}]
)
generated_answer = response.choices[0].message.content

模型运行后,会生成一个融合了数据库知识的回答。

结果分析

让我们看看大语言模型返回了什么结果。

模型回答道:“我建议您尝试阿根廷Bodega Colomé酒庄的Alta Máxima Albe葡萄酒。它的评分高达96分。”

这个推荐并非模型凭空想象,而是基于我们从数据库中检索到的第一条结果(来自阿根廷Salta产区)生成的。这证明了RAG技术的有效性:它通过引入外部知识,极大地增强了模型回答的准确性和相关性。

总结

本节课中,我们一起学习了检索增强生成技术的完整应用流程。

我们从导入数据开始,逐步完成了创建向量数据库、上传数据、执行语义检索,并最终利用大语言模型结合检索到的上下文生成高质量答案。这个过程展示了如何通过RAG让大语言模型突破其固有知识的限制,根据特定的、最新的外部数据源提供精准的信息服务。

034:RAG 章节总结 🧠

在本节课中,我们将总结关于检索增强生成(RAG)的关键内容。我们将回顾如何与大型语言模型(LLM)结合使用 RAG 来管理数据、处理特定格式文件,并利用向量数据库提升应用性能。


上一节我们探讨了 RAG 的基本交互方式,本节中我们来看看具体的数据处理流程和整体模式的价值。

我们看到了如何利用 RAG 与大型语言模型进行交互。

我们也学习了如何管理数据。我特意选择了一个 CSV(逗号分隔值)格式的文件。这样做的原因是,虽然使用 CSV 格式的示例并不常见,但它能展示一些有趣的处理方法。

我们能够对该数据进行修剪、处理,并将其转换成特定的数据格式。

随后,我们为数据创建了嵌入向量,并使用了向量数据库。这让我们能够验证整个流程的有效性。

综上所述,这无疑是一个非常强大的模式。如果你选择实现类似的功能,它将允许你使用自己的数据,而无需重新训练模型。例如,在创建基于聊天的应用程序并使用大型语言模型时,这将帮助你获得更好的响应。


本节课中我们一起学习了 RAG 的完整工作流程:从数据准备(如处理 CSV 文件)、创建嵌入向量,到利用向量数据库进行高效检索。这个模式的核心优势在于,它能让你利用自有数据增强 LLM 的能力,无需耗费资源进行模型微调,从而为聊天机器人等应用提供更精准、更相关的回答。

035:利润分享概念 💰

在本节课中,我们将学习利润分享的不同模式及其对社会的影响。我们将从微观经济学的角度,分析四种主要的利润分享场景:搭便车者、寻租者、平台和产品。理解这些概念有助于我们评估新技术或商业机会的利弊。

从微观经济学角度思考利润分享

在社会的背景下思考利润分享和比例非常重要,我们可以从微观经济学的框架来理解这一点。

四种利润分享模式

以下是四种主要的利润分享模式,每种模式都有其优缺点,对消费者和公司的影响各不相同。

搭便车者

搭便车者是指享受利益却不支付任何成本的行为。如果搭便车者过多,可能导致产品或服务无法持续。

  • 公共广播电视:许多人每天收听公共广播,但从不支付订阅费,他们就是搭便车者。
  • 软件盗版:如果软件包可以免费下载,最终导致足够多的人成为搭便车者,开发者将无利可图,从而失去继续开发软件的动力。
  • 生成式AI的潜在问题:如果从报纸、电影到书籍和软件的所有内容都被消费,却没有利润分享,那么最终生产这些内容的动机可能会消失,导致所有创造性产出降为零。

核心概念:搭便车者数量少或许影响不大,但这是一个需要警惕的问题。

寻租者

寻租行为(这里并非100%的寻租,而是一个渐变的概念)旨在以尽可能少的工作,获取某种租金或报酬。

  • 风险投资:风投公司可能投资100家公司,知道其中95家可能会破产。那95家公司的员工承担了巨大风险,他们本可以在成熟公司获得更高的薪水和奖金,但实际获得的期望值更低。风投公司则通过投资组合来对冲所有风险,从而获取利润。
    • 期望值公式期望值 = 利润 × 事件发生概率
  • 图书出版商:在某些情况下,出版商为作者做了很多,有时则做得很少,但他们通常能获得图书收入的10%到15%,这也可以被视为一种寻租形式。

核心概念:在这些场景中,通常存在高度的管理,因为资金方拿走了大部分利润,并可能将其再投资回组织。这是否为社会或产品增加了价值,需视具体情况而定。

平台

平台模式也有其好处。如果你能获得80%的利润,这对你非常有利。

  • 完全竞争:经济学中有一个说法,在完全竞争下,所有利润至少为零。这意味着如果平台无法限制加入者,甚至无法区分优劣,这可能导致利润被消除。
  • 平台的盈利:平台本身可能赚很多钱,因为它让所有人在上面竞争,并收取交易费。

核心概念:平台面临无情的竞争,这可能对参与者不利。

产品

对于所有者来说,最理想的情况是直接向客户销售产品。

  • 直接销售示例:假设你是一个农民,在路边直接销售水果,你获得100%的利润。这里没有交易费,也没有中间商参与。

核心概念:这是所有者获取全部利润的完美场景。

权衡与伦理考量

在考虑任何机会、新技术或新事物时,必须权衡每种模式的利弊。

  • 权衡无处不在:搭便车者、寻租者、平台和产品模式各有其权衡。
  • 保持清醒:重要的是要睁大眼睛进入任何场景。
  • 伦理影响:从伦理角度看,当你与采取上述某种行为的公司互动时,需要考虑它对社会的影响。

本节课中,我们一起学习了四种利润分享模式:搭便车者寻租者平台产品。我们了解了每种模式的定义、典型例子及其潜在的社会经济影响。关键在于认识到没有完美的模式,每种选择都伴随着对消费者、生产者和整个社会的不同权衡。在评估商业或技术机会时,保持清醒的认识并思考其广泛的伦理影响至关重要。

036:生成式AI的公共资源悲剧 🎭

在本节课中,我们将探讨一个经典的经济学概念——“公共资源悲剧”,并分析其在生成式人工智能(AI)领域的体现。我们将理解当一项资源被无限制地共享时可能出现的问题,并审视生成式AI技术带来的伦理挑战。


公共资源悲剧是一个经典案例,它描述了当一项免费资源被几乎无限数量的人共享时可能发生的状况。由于缺乏监管,多种负面情况可能出现。

以下是公共资源悲剧的几个典型例子:

  • 乱扔垃圾:以公共公园为例,人们可能到处乱扔垃圾,仅此一项就足以破坏公园环境。
  • 过度拥挤:如果没有管理,大量人群持续涌入,最终公园会拥挤到无法游览。
  • 栖息地破坏:例如在国家公园,如果游客破坏自然景观或山体等特征,公园将不再像以前那样美丽。
  • 过度放牧:经典的例子是多个牧场主在同一片公共草地上放牧。他们会竞相让自家牲畜吃最多的草,因为他们担心别人会更快地消耗掉草资源。这演变成一场“竞次”比赛,最终导致草地被完全破坏,寸草不生。

这就是公共资源悲剧的经典情形。事实上,根据各地的监管情况不同,你可以在世界范围内(包括美国)的某些公共公园看到这种现象。

上一节我们了解了公共资源悲剧的一般概念,本节中我们来看看生成式AI领域的“公共资源悲剧”。生成式AI的公共资源悲剧具有一些相似的特征。

以下是生成式AI可能引发的几类具体问题:

  • 知识产权盗窃:生成式AI模型可能在未经同意的情况下,使用受版权保护的作品进行训练。这会贬低原作品的价值,甚至削弱创作者创作新内容的动力。如果作品会被盗用,为何还要创作更多?
  • 工作替代:生成式AI存在风险,可能导致人类创作者的需求下降,进而引发工作被替代。这会带来负外部性。如果这些人无法再获得工作,社会将不得不承担这种工作替代带来的负担。
  • 质量控制:生成式AI输出的作品质量可能不佳,但却在模仿某位艺术家,这可能导致艺术家本人被贬低,并引发整体的质量控制问题。
  • 缺乏认可或归属:创作者的作品可能在被盗用且未获补偿的同时,其原创者的身份也得不到认可。这会打击创作积极性,有失公平,并可能严重影响艺术家未来获得工作的机会。
  • 伦理或道德权利问题:作者可能不希望自己的形象与吸烟、饮酒或其他不良嗜好相关联。如果未经其许可就用于生成式AI创作,便侵犯了其道德权利。

因此,对于生成式AI而言,传统的公共资源悲剧场景并非神奇地不存在,这些问题同样切实存在。我们需要同时审视其益处与负面影响。

生成式AI有显著的益处,例如文本摘要大纲生成内容协同创作。但我们也必须关注其负外部性——那些社会并未从中受益却需承担成本的影响。

这类似于公园里的乱扔垃圾、过度拥挤或破坏行为:你需要为他人滥用公共资源的行为付出代价。如果是一个商业模型获利,社会并未拿到那份报酬,却必须支付成本。


本节课中,我们一起学习了“公共资源悲剧”的概念及其在生成式AI伦理中的体现。我们认识到,在思考生成式AI的伦理时,考虑其可能引发的公共资源悲剧至关重要。这要求我们在享受技术红利的同时,也必须审视并设法缓解其对社会和个体创作者带来的潜在风险与不公。

037:生成式AI的博弈论视角 🎲

在本节课中,我们将学习博弈论这一数学框架,并探讨它如何帮助我们理解生成式人工智能(AI)发展中的潜在风险与策略选择。我们将通过经典的“囚徒困境”模型,来分析在缺乏合作与长远规划的情况下,个体理性决策如何可能导致整体利益受损的“逐底竞争”。

博弈论简介

上一节我们介绍了生成式AI的宏观影响,本节中我们来看看一个分析互动决策的强大工具——博弈论。

博弈论是一个数学框架,用于研究多方互动。它将互动视为一场“游戏”,其中包含参与者策略收益,旨在找出最优策略,或分析在特定情境下(如囚徒困境)导致次优结果的占优策略

经典案例:囚徒困境

为了理解博弈论的核心思想,我们先来看一个经典问题。

囚徒困境是博弈论中的一个经典问题。在这个情境中,自私的占优策略将导致对所有人而言的次优结果。假设你是一名检察官,你逮捕了涉嫌犯罪的Sam和Helen,但缺乏给两人定罪的证据。

从他们身上获取所需信息的唯一方法,是设法让他们互相背叛。我们来看这个收益矩阵:如果两人都保持沉默(不招供),每人只会被判1年监禁。但如果你分别提审他们并说:“如果你招供,你将获得自由,无需坐牢。”

“但如果我们带来的另一个人不招供,那个人将入狱20年。”

你这是在激励每个人都选择招供。而当你们都招供时,你们每人将获得5年刑期。如果一人沉默而另一人招供,沉默者的惩罚将非常严重,以至于保持沉默变得毫无意义。因此,即使他们能够沟通时的最优策略是每人只坐1年牢。

但这两个人互不信任。结果,你成功地让他们采取了这种次优的占优策略,即两人都招供,并且坐牢的时间比合作情况下长了五倍。

生成式AI中的博弈论应用

理解了囚徒困境,我们可以将这种博弈论视角同样应用于生成式AI领域。

这是一个很好的例子:假设我们进入了一个所有内容审核都由某个AI系统完成的世界。我们会发现,确保你的内容具有吸引力的激励非常强烈。

如果你的内容缺乏足够的参与度,你的公司就会亏损。因此,尽管平台上出现了大量低质量的生成式AI内容,你实际上会减少审核力度,因为这会影响你公司的利润底线。一旦你的竞争对手意识到他们也开始亏损。

你们双方都会继续调整各自的内容审核策略。

导致审核标准越来越宽松,最终你们的平台可能变成一个完全由生成式AI内容充斥的平台,这些内容质量可能非常低劣,甚至包含有害信息。我们可以称此为“逐底竞争”。同样,在生成式AI领域,你可能会陷入这样一种假设:假设某个竞争对手、其他国家或政府会窃取知识产权。

因此,你觉得你也必须窃取相同的知识产权。结果就是,大家都不再尊重知识产权,所有人都开始盗用,最终导致知识产权概念不复存在。这同样可能是一种“逐底竞争”,因为你预判了对方会做坏事。

另一个与囚徒困境对应的情况是使用劣质创意作品。

如果你觉得你的竞争对手可能会开始使用通过某些提示词生成的劣质AI作品,那么你也会做同样的事情。最终,你们双方开始生产越来越差的艺术品,直到最后。

你们生产的所有艺术品质量都很差。于是所有人都输了。顾客输了。当然,创作艺术的人也不再被雇佣。

那么,我们就再也看不到真正优秀的艺术品了。由此可见。

如果你只从试图采取占优策略的角度思考问题,并且不进行迭代思考。

只考虑单次博弈,你可能会选择一种导致游戏中所有参与者都获得次优结果的占优策略。

总结与启示

本节课中,我们一起学习了博弈论的基本概念及其在生成式AI领域的应用。通过囚徒困境模型,我们看到了个体理性决策如何可能引发损害整体利益的“逐底竞争”,例如在内容审核放松、知识产权侵犯和艺术品质量下降等场景中。

因此,在考虑像生成式AI这样的新技术时,这是一个非常重要的问题:我们需要思考其潜在影响,以及如何创建一种迭代的合作策略,以避免陷入“逐底竞争”的困境?

038:完全竞争

概述

在本节课中,我们将要学习经济学中的“完全竞争”概念,并探讨其在人工智能领域,特别是大语言模型市场中的潜在应用与启示。我们将通过一个餐馆竞争厨师的例子来理解核心原理,并分析在封闭模型和开放模型两种不同情境下,AI公司可能面临的商业挑战。

完全竞争的核心含义

在完全竞争的市场中,所有经济利润最终会趋向于零。这意味着,任何超出正常水平的利润都会被竞争所消除。

为了理解这一点,我们可以观察一个小镇餐馆的例子。

案例分析:小镇餐馆与天才厨师

假设一个小镇上有四家餐馆。起初,它们之间没有显著的竞争差异,直到一位天才厨师来到镇上。

由于这位厨师才华横溢,能为餐馆吸引所有顾客,因此他成为了一种稀缺资源。以下是接下来发生的情况:

  1. 所有餐馆都开始竞相争夺这位厨师的服务。
  2. 它们通过不断提高薪资报价来相互竞争。
  3. 最终,厨师通过谈判获得了极高的薪水,从而攫取了餐馆的全部经济利润。

在这个例子中,厨师因其稀有且备受需求的技能,捕获了所有的经济剩余。

关键概念解析

我们需要明确几个利润相关的概念:

  • 经济利润:指总收入减去总经济成本后的剩余。
  • 正常利润:指吸引投资所需的最低回报,可被视为成本的一部分。
  • 超额利润:即经济利润,是高于正常利润的部分。

用公式表示即:
经济利润 = 总收入 - (显性成本 + 隐性成本(含正常利润))

在完全竞争下,这种超额利润无法长期维持。

对商业模式的启示

上一节我们通过餐馆例子看到了稀缺资源如何捕获全部利润。本节中我们来看看,这可能导致一个严峻的商业后果:完全竞争最终可能导致企业破产。

因为实际雇佣厨师的餐馆,必须将全部利润支付给厨师,自身无法通过其他方式构建竞争壁垒。同理,我们可以在生成式人工智能领域发现类似的情况。

以下是两种可能的情景分析:

情景一:依赖单一封闭模型
假设存在一个性能卓越的封闭模型,所有人都必须使用它。这就如同那位天才厨师。

  • 所有公司都使用同一个封闭模型,无法借此获得竞争优势。
  • 这些公司的大部分利润,将通过API调用费的形式,流向模型提供方,正如利润流向厨师一样。

情景二:模型完全开源
如果所有模型都是开源的,情况则会走向另一个极端。

  • 由于可以平等地使用任何模型,模型本身无法产生差异化。
  • 训练模型的公司将难以盈利,最终可能退出市场,因为无法通过模型本身赚钱。

战略思考

这是一种假设性情境,但当你评估自身AI战略时,从经济学角度思考这个问题至关重要。

你需要权衡:全力投入封闭模型是否会带来持久的竞争优势?还是说,在未来模型数量近乎无限的情况下,模型本身会像Linux一样,成为一种免费的通用商品?如果答案是后者,那么竞争将不得不转向其他技术形式或商业模式。

总结

本节课中我们一起学习了完全竞争的经济学原理。我们看到,当关键资源(如天才厨师或顶级AI模型)稀缺且成为竞争焦点时,它可能吸走产业链中的大部分利润。这对AI公司的启示是:必须认真思考其战略是建立在拥有稀缺资源上,还是准备在资源商品化后,在其他维度构建自己的核心竞争力。

039:负外部性

📖 概述

在本节课中,我们将要学习经济学中的一个重要概念——负外部性,并探讨它如何应用于生成式人工智能等科技领域。我们将通过具体例子来理解,为何在追求利润的同时,也必须审视技术可能带来的潜在社会成本。


🔍 理解负外部性

负外部性是指,某些行为或产品在给一方带来利益的同时,却让未直接参与的第三方承担了成本,而第三方并未从中获得任何好处。

公式可以表示为:
社会总成本 = 私人成本 + 外部成本(负外部性)

上一节我们介绍了技术发展的基本驱动力,本节中我们来看看当发展不考虑社会影响时,可能产生的问题。


🏭 一个现实世界的例子

为了清晰地理解这个概念,我们可以看一个基于真实事件的极端例子。

假设一个犯罪组织受雇于某公司,将有毒废物(例如核废料或农药)非法倾倒在某个小镇上。以下是这个过程中的利益与成本分配:

  • 犯罪组织:获得了100%的利润。
  • 小镇居民:没有从倾倒有毒废物中获得任何利润,却要承担全部代价。这些代价可能包括:
    • 癌症风险增加。
    • 各种疾病。
    • 土地污染导致农业减产。

这个犯罪行为导致了所有的负外部性都转移给了小镇居民。虽然这是一个极端案例,但它清晰地展示了负外部性的核心矛盾。


🤖 生成式AI中的潜在负外部性

理解了基本概念后,让我们将视角转向生成式人工智能。一个商业公司发布AI模型并通过API调用获利,情况可能类似。

  • AI公司:通过提供AI服务获得100%的利润。
  • 社会公众:可能并未从AI服务中直接获利,却有可能需要承担其滥用或意外后果带来的成本。

以下是AI可能产生的一些潜在负外部性场景:

  • 生成高度逼真、模仿知名政治人物的语音,干扰选举结果。
  • 生成关于特定个人的不实信息,损害其个人声誉或公司利益。
  • 传播错误的健康信息,导致公众做出有害健康的决策。

理论上,这些都可能因生成式AI的不负责任使用而发生。商业公司获得了所有利润,而承担代价(即承受负外部性)的却是广大公众。


💡 如何负责任地思考

因此,负外部性是一个重要的思考框架。它并不意味着所有技术都会产生极端的负面影响,但其重要性在于提醒我们,在提出技术解决方案时,不能只关注利润。

在评估任何技术方案时,应采取负责任的思维方式:

  1. 审视利润:这是商业运营的基础。
  2. 评估负外部性:分析你的技术可能产生的、甚至是无意的真实社会影响。

这是一种负责任地部署人工智能解决方案的思考方式。


📝 总结

本节课中我们一起学习了负外部性的概念。我们了解到,它描述了利益由一方独占,而成本却由不相关的第三方承担的现象。我们通过犯罪组织倾倒废物的例子理解了其核心机制,并探讨了生成式AI可能带来的类似风险,如传播错误信息。最后,我们强调了在技术开发中,除了追求利润,必须主动评估和尽量减少潜在的负外部性,这是构建负责任AI的关键一步。

040:监管创业

概述

在本节课中,我们将要学习“监管创业”这一概念。我们将了解其定义、核心策略、促成因素、社会影响,并通过具体案例来分析这一现象在现实世界中的表现。

什么是监管创业?

监管创业是由Paulllman和Barry在2017年提出的一个术语。其核心含义是:通过在法律灰色地带创立公司,甚至不惜违反现有法律,来推动后续法律的改变。

上一节我们介绍了监管创业的基本定义,本节中我们来看看涉及这一模式的一些知名公司。

典型案例

以下是几个典型的监管创业公司案例:

  • Airbnb:模糊了酒店与个人短租的界限。
  • Uber:改变了出租车与私人交通服务的法规。
  • Tesla:在汽车直销等领域挑战传统法规。
  • DraftKings:推动了在线体育博彩的合法化。

这些公司的共同点是,它们都游走于商业创新与改变法律法规的边界。

核心策略与促成因素

那么,这些公司是如何运作的呢?以下是它们常用的一些关键策略:

  • 识别法律灰色地带:寻找法规尚未明确或存在漏洞的领域进入。
  • 快速扩张:迅速扩大规模,使得政府难以简单禁止,即形成“太大而不能禁”的局面。
  • 动员用户获取政治支持:利用社交媒体等渠道,让公众支持其产品,形成舆论压力。

这些策略的成功,依赖于一些特定的促成因素:

  • 资金充足:获得大量风险投资支持。
  • 模式可扩展:业务能够快速复制和增长。
  • 公众吸引力:提供的服务深受大众欢迎。
  • 瞄准地方法规:相较于国家法律,挑战州或地方性法规的难度更低。
  • 诉讼政治化:将法律争议转化为公众政治议题。

社会影响与外部性

监管创业模式带来了深远的社会影响。其直接结果是降低了这些公司进入市场的合规成本,并可能促使政策快速变更。然而,这些变更有时可能偏离公共利益,产生需要由社会承担的“负外部性”。

接下来,让我们通过具体案例来剖析这些负外部性。

以下是Airbnb、Uber、DraftKings和OpenAI四家公司带来的社会收益与成本分析:

  • Airbnb

    • 收益:公司获得巨额利润和估值,为创始人和投资者带来回报。
    • 社会成本:全球范围内可负担的租赁住房单元减少,社会需承担此损失。
  • Uber

    • 收益:公司获得高估值,风险投资者得以退出获利。
    • 社会成本:以旧金山为例,交通拥堵和尾气排放增加。这不仅加剧全球变暖,当地居民也需承受污染和拥堵的代价,且无人为此获得补偿。
  • DraftKings

    • 收益:创造了一个价值数十亿美元的梦幻体育产业。
    • 社会成本:导致更多人赌博成瘾、个人破产增加。例如,若有人将钱用于赌博而无力支付房贷,其家庭可能流离失所,这个代价最终由社会承担。
  • OpenAI / 生成式AI

    • 收益:推动了生成式AI的技术进步,并创造了商业利润。
    • 社会成本:AI可能被用于破坏性用途;艺术家们的版权作品在未经明确许可下被用于模型训练,其权益受到侵害。使用受版权保护的数据进行训练的成本,也是一种社会需要承担的损失。

总结

本节课中,我们一起学习了“监管创业”的概念。我们看到,这并非全新现象,而是风险投资中一个长期存在的模式。其典型路径是:快速成长至规模巨大、需求庞大、资金雄厚,并由能游说国会的人士推动。关于未来监管会更多还是更少,目前尚无定论,但这种“先突破,后改变规则”的模式值得我们持续关注和思考。

041:创建或强化偏见 🧠

在本节课中,我们将探讨生成式人工智能中一个关键的伦理问题:偏见如何在数据集中产生,并可能被模型训练和系统推荐所创建或强化。

上一节我们讨论了生成式AI的伦理挑战,本节中我们来看看一个具体且影响深远的问题——偏见的创建与强化。

概述:偏见的产生与放大

创建或强化偏见是生成式人工智能在伦理方面必须解决的一个问题。其核心机制在于,如果一个模型在包含偏见的数据集上进行训练,并且未对此进行处理,那么该系统就可能生成甚至放大这些偏见。

偏见的创建与强化机制

以下是偏见从数据到社会影响的典型传导路径:

  1. 收集有偏见的数据集:初始数据集可能包含了刻板印象、歧视或仇恨言论。
  2. 基于偏见数据训练模型:在此类数据上训练模型,而不加以修正,会将偏见编码进模型中。
  3. 系统生成更多偏见:训练好的模型在生成内容时,会输出带有偏见的信息。
  4. 推荐系统放大偏见:若将生成的内容置于推荐引擎中,有害的偏见内容可能因其高互动率(如点击、分享)而被算法优先推荐。
  5. 形成恶性循环:推荐导致更多曝光和互动,这反过来“告诉”系统此类内容更受欢迎,从而进一步强化推荐,形成“过滤气泡”和有害的个性化推送。

公式表示偏见输出 ∝ 偏见数据 × 优化目标(如互动率)

短期利益与长期社会成本

这个过程会带来一个严重的矛盾:短期商业利益与长期社会危害的冲突。

  • 对公司的短期收益:煽动性、误导性的偏见内容往往能带来更高的用户参与度和停留时间,这意味着更多的广告收入和利润增长。在短期内,这看起来像是正确的商业方向。
  • 对社会的长期成本:然而,被传播的歧视性数据和对边缘群体的刻板印象,其代价需要由整个社会来承担。这可能导致:
    • 进一步加深社会隔阂与分裂。
    • 侵蚀对复杂议题的理解。
    • 助长极端主义、暴力事件的风险上升。

因此,这绝不是一个理论问题,而是必须认真对待的现实挑战。

可能的解决方案

要应对这一问题,需要从多个层面进行思考和改革:

  • 算法监督:对优化用户参与度的算法进行伦理审查和监督,改变仅以互动率为核心的优化目标。
  • 纳入伦理考量:在系统设计之初就将伦理因素作为必要组成部分。
    • 代码示例(理念层面)
      # 伪代码:在损失函数中加入偏见惩罚项
      def custom_loss_function(predictions, labels, bias_penalty):
          standard_loss = calculate_standard_loss(predictions, labels)
          total_loss = standard_loss + lambda * bias_penalty # lambda是权衡系数
          return total_loss
      
  • 政策改革:通过立法和税收等手段,治理这些有毒的商业模式。例如,对传播有害偏见内容的行为征税,或出台相关法规进行约束。
  • 伦理培训:加强对AI开发人员、产品经理的伦理教育。

所有这些措施的共同起点是,我们需要超越单纯的利润思维,全面审视所构建的系统将如何被人们使用,以及它会对社会产生何种影响。

总结

本节课中,我们一起学习了偏见在生成式AI中的创建与强化机制。我们了解到,基于偏见数据训练并追求互动率的模型,会通过推荐系统放大有害内容,为公司带来短期利润,却让社会承担歧视、分裂等长期成本。解决这一问题需要算法监督、伦理设计、政策改革等多管齐下,以实现更负责任的技术发展。

posted @ 2026-03-26 12:26  布客飞龙III  阅读(26)  评论(0)    收藏  举报