课程概述:理解生成式AI背后的原理
大家好,今天这堂课的目标是让大家一堂课搞懂生成式人工智慧的原理。我相信大家在日常生活中或多或少都有使用生成式人工智慧平台的经验,例如OpenAI的ChatGPT、Google的Gemini或是Anthropic的Claude。本课程希望让大家对这些生成式AI背后的运作原理有基本的认识。
课程分为上下两部分:上半部我们会讲解这些生成式AI背后的原理;下半部分则会带大家进行一些实作,我会运行一些开源模型来印证上半部所讲的原理。
生成式AI的核心:万物皆为“文字接龙”
目前,像ChatGPT、Gemini、Claude这些平台,最基本的功能就是你输入一段文字(例如一个问题),它会给你一个回应。虽然输入输出文字听起来简单,但它却有千变万化的应用,你可以用它来写Email、找语法错误,甚至协助你写作业。当然,这些平台除了处理文字,现在也能够输入输出语音和影像。我们暂时先专注于文字部分,稍后会再讨论它们如何处理语音和影像。
这些人工智慧基本上就是语言模型。一言以蔽之,语言模型就是一个在做文字接龙的人工智慧。事实上,ChatGPT这类AI真正会做的事情只有一件:文字接龙。你给它一个未完成的句子,它会去猜测后面可以接哪一个字。例如,你输入“人工智”,它可能会接“慧”;你输入“大型语言模”,它知道后面可以接“型”。
语言模型在进行文字接龙时,其输出的单位有一个专有名词叫做Token(Token: 在此语境下指模型在文字接龙时可以选择的基本单位,常被译为“词元”或“标记”)。如果你查字典,Token常被翻译成“代币”,但在这里它有独特的意思。而那些提供给语言模型的未完成句子,则被称为Prompt(Prompt: 指用户提供给模型的输入文本,用于引导其生成后续内容)。所以,语言模型真正能做的事情,就是给它一个Prompt,它去预测下一个Token应该是什么。
那么,这些语言模型是如何回答问题的呢?当你问ChatGPT“台湾最高的山是哪座?”时,它实际上是把你的问题当作一个未完成的句子来做文字接龙。在这个句子后面,也许可以接“玉”。输出“玉”这个Token后,它会把“玉”加到原有的Prompt后面,形成新的Prompt:“台湾最高的山是哪座?玉”。接着,模型会再次预测下一个Token,也许是“山”。再把“山”加到后面,Prompt就变成了“台湾最高的山是哪座?玉山”。此时,也许没什么好接的了,模型就会输出一个代表结束的符号,表示接龙结束。你所看到的答案,就是这一连串生成的符号组合起来的结果。
语言模型如何“思考”:概率、知识与参数
你可能会想,对于同一个Prompt,可以接的Token应该有很多种可能。例如,Prompt是“人工”,后面既可以接“智”(人工智慧),也可以接“呼”(人工呼吸)。语言模型是如何决定接哪一个Token呢?
实际上,当语言模型接收一个Prompt时,它真正的输出是一个Token的概率分布。它会为每一个可能的Token打一个分数,代表这个Token接在Prompt后面的概率。例如,模型可能会给“呼”20%的概率,给“智”50%的概率,而给英文单词“how”或韩文、日文符号极低的概率,因为它们接在“人工”后面的可能性非常低。
有了这个概率分布后,语言模型会根据这个分布来“掷骰子”。也就是说,“人工”后面有20%的机率接“呼”,50%的机率接“智”。所有语言模型能够评分的Token集合起来,有一个专有名词叫做Vocabulary(Vocabulary: 指语言模型所有能够处理和生成的所有Token的集合,可理解为模型的“词典”)。一个典型的语言模型,其Vocabulary的大小往往有数十万个Token,包罗万象,包含各种语言的文字和符号。
我们再回顾一次回答问题的过程:当你问“台湾最高的山是哪座?”,模型会产生一个概率分布并掷骰子。可能“玉”的概率很高,就掷出了“玉”。然后把“玉”加到Prompt后面,再产生新的概率分布,可能“山”的概率很高,就掷出了“山”。如此反复,直到掷出结束符号为止。
正是因为每一次生成Token都需要掷骰子,所以你问语言模型同样的问题,每次得到的答案都可能略有不同。即使“玉”的概率最高,其他词(如“是”或“答”)也有一定的概率被选中。如果掷出“是”,最终答案可能变成“是玉山”;如果掷出“答”,则可能变成“答案是玉山”。
你或许会担心,既然是掷骰子,模型会不会给出荒谬的答案,比如回答“冰淇淋”?这基本不用担心,因为“冰”接在“台湾最高的山是哪座?”后面的概率极低,几乎不可能被掷中。
文字接龙的挑战:语言知识与世界知识
不要以为文字接龙是一件容易的事。一个AI要能正确地进行文字接龙,至少需要具备两方面的知识。
第一是语言知识。它必须理解人类语言的语法,知道什么样的词汇后面可以接什么样的词汇。例如,在“黄色的”后面,接名词的概率应该远高于接动词。
第二是世界知识。仅有语言知识是不够的,否则模型可能会说出语法正确但毫无意义的句子。例如,当Prompt是“水的沸点是摄氏”时,模型必须知道“100”的概率应该是最高的,这代表它了解物理世界的基本事实。
语言知识相对容易学习,只要有足够多的文本数据,模型就不太会犯语法错误。但世界知识的学习难度要大得多,可以说是无穷无尽的。例如,模型知道水的沸点是100摄氏度,但如果Prompt变成“在0.5大气压下,水的沸点是摄氏多少度?”,它就必须知道前提条件改变了,答案不再是100。这说明,模型需要掌握海量的世界知识才能持续做出正确的文字接龙。
从简单回复到多轮对话:揭秘背后机制
我们可以将语言模型想象成一个函数 f。输入 x(即Prompt)通过函数 f 后,输出 f(x)(即概率分布)。由于输入和输出的关系极其复杂,这个函数内部需要海量的参数(Parameter)。如今,“百亿参数遍地走,十亿参数谁都有”,一个模型若只有十亿参数,甚至不好意思自称“大型语言模型”。这些参数并非人工设定,而是通过机器学习从海量数据中自动学习得到的。
语言模型的学习来源主要有三个:
- 网络数据:从互联网上爬取海量文本,每一句话都是学习文字接龙的教材。
- 人类标注:由开发者提供高质量的问答对,直接告诉模型在特定问题后应该接什么答案。
- 用户反馈:用户在使用ChatGPT等平台时,可以对答案点赞或点踩,这些反馈会帮助模型调整,提升受欢迎答案的概率,降低不受欢迎答案的概率。
你可能会困惑,为什么仅仅做文字接龙就能回答问题呢?为什么模型在“台湾最高的山是哪座?”后面一定会接答案,而不是接“谁来告诉我呀?”或出一个选择题呢?
没错,单纯的文字接龙确实不一定能回答问题。你在使用这些平台时,背后其实动了一些手脚。你以为你输入的Prompt只是你的问题,但平台实际上会为你“加料”,在你的问题前后添加额外的内容。这个额外添加的Prompt有一个专有名词,叫做Chat Template(聊天模板)。例如,平台可能会将你的输入变成“使用者问:台湾最高的山是哪座?AI回答:”,这样模型在接龙时,就只能接出问题的答案了。
那么,模型是如何实现多轮对话的呢?当你问完第一个问题并得到答案后,再追问“那第二高的呢?”,模型之所以能理解你问的是“第二高的山”,是因为它在处理你的新问题时,会将整个对话历史记录都作为新的Prompt。它看到的输入实际上是:“使用者问:台湾最高的山是哪座?AI答:玉山。使用者问:第二高的呢?AI答:”,基于这样完整的上下文,它才能正确地接出“雪山”。
AI幻觉与应对策略
了解了文字接龙的原理,你就不难理解为什么ChatGPT常常会“一本正经地胡说八道”。当我问它一个具体组织的课程信息和官网时,它可能会编造一个看起来很像样的网址,但实际上根本不存在。这个现象叫做AI幻觉(AI Hallucination: 指AI模型生成看似合理但实际上不符合事实、凭空捏造或逻辑矛盾的内容的现象)。
很多人误以为是模型背后的数据库出了问题,但事实是,这些语言模型背后并没有所谓的数据库。它产生的一切内容,都是通过文字接龙生成的。你甚至可以认为,它所有的答案都是在“幻觉”中产生的,我们应该惊讶的是,它的幻觉中竟然有那么多与现实相符的内容。
要减少AI幻觉,一种常见的技术叫做RAG(Retrieval-Augmented Generation: 检索增强生成,一种将外部知识库检索与语言模型生成相结合的技术,以提高答案的准确性和时效性)。简单来说,就是让AI在回答前先通过搜索引擎查找相关资料。目前,许多平台默认开启了此功能。
你可以把语言模型想象成一个被关在小黑屋里的人,他从未见过外面的世界,唯一会做的事就是根据你给的纸条(Prompt)进行文字接龙。因此,对于某些问题,比如“今天是几月几号?”,它是不可能凭空知道答案的。确保输入信息足够,让模型能做出正确接龙,是人类的责任,这门学问就叫做Context Engineering(上下文工程: 指人类通过优化和构建提供给模型的Prompt,确保输入信息足够丰富和清晰,以引导模型生成准确输出的过程)。
实际上,当你问ChatGPT日期时,它通常能答对。这背后的秘密在于,平台可能在每次对话开始时,就自动在你的Prompt前加入了一些基本信息,比如当前日期、模型名称等。这些由开发者预设好的Prompt,被称为System Prompt(系统提示: 由开发者预设并自动添加到用户输入之前的一段指令,用于设定模型的角色、行为准则或提供通用背景信息)。
超越文字:图像与声音的生成原理
既然我们理解了文字生成,那么图像和声音的生成原理也就迎刃而解了。同样是“接龙”的概念。
- 图像生成:可以看作是“像素接龙”,一个像素一个像素地生成,最终构成一张图片。
- 声音生成:声音由一个个取样点构成,通过“取样点接龙”,就可以生成一段音频。
早在2016年,就已经有通过像素接龙生成宝可梦图片的技术。Google DeepMind的WaveNet模型也是通过取样点接龙生成了极其逼真的语音。但这种方法有一个重大问题:计算成本太高。生成一张1024x1024的图片需要进行一百万次像素接龙,比写一部《红楼梦》的工程量还大。生成一分钟的音频则需要超过一百万次的取样点接龙。
因此,现在流行的方法是先对图像或声音进行压缩,将其转换为一系列Token。例如,将一个16x16的像素块表示为一个Token,代表草地、眼睛或毛茸茸的纹理。声音也可以被编码成代表0.02秒音频的符号。然后,模型对这些压缩后的Token进行接龙,最后再通过解码器(decoder)将Token还原成图像或声音。
这正是黄仁勋在2024年COMPUTEX演讲中所说:“万事万物都是Token”的含义。生成式AI的核心思想就是将世间万物表示为Token,然后通过Token接龙来生成新的事物。
生成式AI的统一定义与实现策略
那么,到底什么是生成式人工智慧呢?它的定义是:让机器学会产生复杂而有结构的对象。这里的“有结构”指的是对象由有限的基本单位(即Token)构成。虽然Token种类有限,但它们的组合却能创造出无穷无尽的可能,无论是文字、图片、声音,甚至是蛋白质(由有限的氨基酸构成)。
我们期待的不仅是生成,而是根据输入进行生成。这可以概括为:输入一个X,产生一个Y,而Y是一个复杂且有结构的对象。解决这个问题的基本套路,就是将Y拆解成一系列Token,然后教机器如何一步步选择合适的Token。
最常见的一种生成策略就是我们一直在讲的“文字接龙”,它的专业术语叫做Auto-Regressive Generation(自回归生成: 一种序列生成模型,其中下一个元素的生成依赖于之前所有已生成的元素,如同文字接龙一样逐个产生)。在自回归生成的每一步,模型都是在解决一个分类问题:从所有可能的Token中选择下一个最合适的。当然,这不是唯一的策略,例如在图像生成领域,你可能更常听到Diffusion Model(扩散模型: 一种生成模型,通过模拟从噪声中逐步去噪来生成数据,在图像生成领域尤为流行),它也可以被视为另一种生成策略。
实战环节:上手开源语言模型
接下来,我们进入课程的后半段,教大家如何使用开源的语言模型。
与Gemini、ChatGPT这类非开源(闭源)模型不同,开源模型(如Meta的LLaMA、Mistral的模型以及Google的Gemma)会公开其内部的函数结构和所有参数。你可以在一个名为Hugging Face的网站上找到各式各样的开源模型,它就像是模型的“Facebook”。
我们将使用Colab这个平台来运行范例代码。首先,你需要将范例代码副本保存在自己的云端硬盘中,并选择合适的GPU(图形处理器)。我们的代码将使用由Hugging Face开发的Transformers这个套件,它是一个非常通用的工具,可以方便地调用Hugging Face平台上的任何模型。
在开始之前,你需要一个Hugging Face的凭证(Token)来连接并下载模型。我们今天的示例将使用Llama-3.2-3B-Instruct这个模型,其中“3B”代表它有30亿个参数,这在今天算是一个小型模型。
探索模型内部:Tokenizer与词汇表 (Vocabulary)
下载模型后,你会得到两个主要部分:Tokenizer和Model。Tokenizer存储了模型的Vocabulary定义,即它能处理和生成哪些Token;Model则包含了模型的参数。
我们可以通过Tokenizer.vocab_size来查看LLaMA的词汇表大小,结果是128,000个。每个Token都有一个从0到127999的唯一编号。我们可以使用Tokenizer.decode()函数将编号转换为文字,反之,Tokenizer.encode()则将文字转换为编号。
通过探索,你会发现Vocabulary里包罗万象:有各种符号、不完整的单词、各种语言的文字(中文、日文、阿拉伯文),甚至一个爱心表情也是一个独立的Token。有趣的是,同一个英文单词,由于前面是否有空格,或者大小写的不同,都可能被视为不同的Token,拥有不同的编号。
动手实现文字接龙:从单步预测到连续生成
现在我们来真正实现文字接龙。流程如下:
- 使用
Tokenizer.encode()将文字Prompt(如"1+1=")转换为一连串的ID。 - 将这串ID输入到
model函数中。 model会输出一个庞杂的结果,我们从中提取出关于下一个Token的概率分布。
实验发现,对于Prompt "1+1=",模型预测下一个Token是"2"的概率高达65.7%。而如果我们将Prompt改为“在二进位中,1+1=”,模型则会预测“10”的概率为70.12%,显示它能理解上下文。
为了连续生成多个Token,我们可以编写一个循环:每次生成一个Token后,将其附加到Prompt末尾,再进行下一次预测。然而,如果每次都选择概率最高的Token(贪心策略),生成的回答会非常固定和死板。
更真实的做法是根据概率分布来“掷骰子”,这样每次运行结果都会不同。但完全随机也可能导致问题:一旦掷到一个概率很低的奇怪Token,后续的句子就可能完全跑偏。因此,实际应用中常采用Top-K采样(Top-K采样: 一种文本生成策略,仅在概率最高的K个词元中进行随机抽样,以平衡生成文本的多样性和连贯性),即只在概率最高的K个Token中进行抽样,以兼顾多样性和合理性。
让模型“会说话”:Chat Template的重要性
幸运的是,我们不需要自己编写循环。Hugging Face的Transformers库提供了一个便捷的model.generate()函数,它封装了连续生成Token的逻辑。
然而,到目前为止,我们的模型表现得并不像一个聊天机器人。当我们问“你是谁?”时,它可能会续写成“你是谁的朋友?”,而不是回答问题。这正是因为我们没有使用Chat Template。
当我们手动为Prompt加上“使用者说:... AI回答:...”这样的模板后,模型立刻开始像模像样地回答问题了。每个模型都有其官方推荐的Chat Template,使用官方模板通常能获得最佳效果。Llama的官方模板比较复杂,但我们可以通过Tokenizer.apply_chat_template()函数轻松应用它。
应用官方模板后,模型真正看到的输入会包含很多额外信息,比如System Prompt,里面可能预设了模型的知识截止日期、当前日期等。这时再问“你是谁?”,它就能给出更像样的回答了。我们甚至可以在System Prompt里明确告诉它“你的名字是Llama”,这样它就不会在回答时误称自己是GPT-3.5了。
实现多轮对话与Pipeline简化流程
要实现多轮对话,关键在于将完整的对话历史作为下一次生成的输入。apply_chat_template函数可以方便地处理包含多轮对话历史的message列表,每一轮对话都包含角色(user, assistant)和内容。
通过维护一个不断增长的对话历史列表,并将其传递给模型,我们就可以构建一个功能完整的聊天机器人。
最后,为了进一步简化流程,Hugging Face提供了一个更高级的接口,叫做Pipeline(管道: 在Hugging Face Transformers库中,这是一个高级接口,将模型预处理、推理和后处理等多个步骤封装在一起,简化了调用流程)。使用Pipeline,你甚至不需要关心encode和decode的过程,可以直接将对话消息列表扔给它,它会直接返回文字结果,代码变得异常简洁。
更换模型也同样简单,只需在创建Pipeline时更改模型的ID即可。例如,将meta-llama/Llama-3.2-3B-Instruct换成google/gemma-3-4b-it,你的聊天机器人就立刻换上了Gemma的大脑。
📌 文中提及的人物和组织
人物: Jensen Huang
公司/组织: OpenAI, Google, Anthropic, Meta, Mistral, Google DeepMind, Hugging Face
产品/模型: ChatGPT, Gemini, Claude, Llama, Diffusion Model, Gemma, Colab, Transformers, PyTorch
媒体/书籍: 《红楼梦》