DLAI-大模型预训练笔记-全-
DLAI 大模型预训练笔记(全)
001:课程介绍 🎬
在本节课中,我们将要学习大语言模型预训练的基本概念、适用场景以及本课程的整体安排。
欢迎来到由Upstage合作开发、并由Upstage首席科学官Lucy Park教授的大语言模型预训练课程。欢迎你,Lucy。感谢你,Andrew。我们非常高兴来到这里。
预训练一个大语言模型,是指对一个模型(通常是Transformer神经网络)在大规模文本语料上进行监督学习的过程,目的是让模型学会根据给定的输入提示,反复预测下一个词元。这个过程之所以被称为“预训练”,是因为它是训练一个语言模型的第一步,之后才会进行指令微调或与人类偏好对齐等步骤。
预训练的产出被称为基础模型。本课程将涵盖两种方式:从零开始训练(即从随机初始化的权重开始),以及在已有预训练模型的基础上,使用自己的数据继续预训练。
从零开始训练一个大模型计算成本高昂,通常需要多台GPU服务器,训练过程可能持续数周甚至数月。因此,大多数开发者不会选择从零开始预训练模型,而是会采用现有的模型,通过提示工程或微调来使其适应自己的任务。
然而,在某些情况下,预训练模型仍然是必需或更优的选择,这也正是Upstage为其客户提供的服务。没错,Andrew。我们的客户出于各种原因进行模型预训练。有些是为了在特定领域(如法律、医疗、电子商务)测试模型性能;有些则需要模型在特定语言(如泰语、日语)上具备更强的能力。此外,新的训练方法(如深度扩展)使得更高效的预训练成为可能,这种方法利用两个或多个现有模型来构建更大的模型。例如,我们的Solar模型就是使用深度扩展技术训练的。正因为这些技术进步,我们看到人们对预训练的兴趣与日俱增。
深度扩展通过复制一个较小预训练模型的层来创建一个新的、更大的模型。然后对这个新模型进行进一步的预训练,最终得到一个比原始模型更大、更好的模型。Upstage团队通过实验发现,以这种方式创建的模型,其预训练所需的计算量比传统方法最多可减少70%,这意味着巨大的成本节约。
预训练是否适合您的工作,取决于几个因素:是否存在无需预训练就能满足您任务的现成模型、您拥有哪些数据、您能获取多少计算资源(包括训练和部署),以及您可能面临的隐私和合规性要求。因此,根据您所在的公司或行业,您可能会被要求预训练一个模型,或者至少需要考虑这个选项。
在本课程中,您将学习从零开始预训练一个模型所需的所有步骤,从收集和准备训练数据,到配置模型并进行训练。您将首先了解一些预训练模型是获得最佳性能的首选方案的用例,并讨论预训练与微调的区别。接下来,您将逐步了解预训练所需的数据准备步骤,探索如何从互联网或Hugging Face等现有仓库收集数据,然后学习获取高质量训练数据的步骤,包括去重、基于文本长度的过滤以及语言清洗。之后,您将探索配置模型架构的一些选项,了解如何修改Meta的Llama模型来创建更大或更小的模型,并了解几种初始化权重的方法(随机初始化或从其他模型加载)。最后,您将看到如何使用开源的Hugging Face库训练模型,并实际运行几步训练,观察损失值如何随着训练进程下降。
本课程使用仅包含数百万参数的小型模型,以确保内容足够轻量,能在CPU上运行。但您可以将课程中的代码扩展到更大的数据集和模型,并用于GPU训练。
感谢Lucy,这听起来对于建立关于“何时预训练有意义”以及“执行预训练需要什么”的直觉会非常有帮助。许多人为创建这门课程付出了努力。我要感谢Changjun Park、Sanang Juung Kim、Jerry Kim、Stan Lee Jung Hi Kimim,以及他们的合作者Ian Park。Tommy Nelson和Jeff Ladwick也为本课程做出了贡献。我想重申,在大数据上预训练大模型是一项昂贵的活动,最小模型的成本可能从约100美元起,到数十亿参数规模的模型则可能高达数万甚至数十万美元。因此,如果您选择自己尝试,请务必谨慎。课程中会提到像Hugging Face提供的计算器这样的工具,可以帮助您在开始前估算预训练场景的成本,从而避免云服务商带来的意外账单。

但预训练是语言模型技术栈的关键部分。无论您只是想建立对语言模型的直觉,还是想继续预训练一个现有模型,甚至尝试从零开始预训练以在模型排行榜上竞争,我都希望您能享受这门课程。

那么,让我们进入下一个视频,正式开始学习。
002:为何需要预训练

在本节课中,我们将探讨预训练为何是获得良好模型性能的最佳选择。你将通过对比同一模型的不同版本(基础通用模型、微调模型和特定领域预训练模型)的文本生成效果,直观地理解预训练的重要性。
概述
预训练是大语言模型训练的第一阶段。模型通过在海量非结构化文本数据上反复预测下一个词来学习生成文本。在此过程中,模型编码了关于世界的知识。然而,这些基础模型虽然擅长生成文本,却不一定擅长遵循指令或确保行为安全。因此,消费级应用中的模型(如ChatGPT)通常在初始预训练后,会通过指令微调和对齐训练来提升其遵循指令和符合人类偏好的能力。
模型的知识仅限于其训练数据。若要让模型学习新知识,必须进行额外的训练或引入更多数据。微调或对齐训练可用于教授模型新行为(例如,以特定风格撰写摘要或避免特定话题)。但如果希望模型深入理解一个新领域,则必须在该领域的特定文本上进行额外的预训练。
人们常试图绕过预训练,仅用较小的数据集对模型进行微调来添加新知识。然而,这在某些情况下并不奏效,特别是当新知识在基础模型中代表性不足时。此时,额外的预训练是获得良好性能的必要条件。
具体示例:韩语能力
让我们看一个具体例子。假设你想创建一个擅长韩语的大语言模型。一个未在大量韩语文本上训练过的基础模型(例如Llama 7b)无法用韩语书写。如果你要求该模型介绍韩服(Hanbok),它会完全答错,甚至误以为“Hanbok”是一个冒犯性词汇。
第二个模型在少量数据上进行了微调,这些数据很可能是英韩句子对形式的直接翻译。其回答仅部分使用韩语。虽然懂双语的人能理解其意,但该模型可能适合写韩语歌词,却不足以支撑一个韩语聊天机器人。
最后一个模型是我们的团队通过额外预训练创建的。我们在海量的英韩非结构化文本上对一个英语模型进行了额外预训练。如你所见,这个模型现在可以流利地使用韩语。由此可见,预训练对于获得一个优秀的韩语模型至关重要。
代码实践:Python代码生成
接下来,我们通过一个笔记本来查看另一个预训练至关重要的例子,并体验不同预训练模型和微调模型在Python语言上的差异。
我们已经为你安装了所需的包。同时,我们过滤掉一些警告,以保持笔记本整洁。你还需要导入torch,以便为可复现性设置随机种子。你可以将此数字更改为你选择的任何数字,然后运行fixed torch seed来固定种子。
首先,我们尝试让一个小的预训练模型生成文本。这里我们使用由Upstage构建的、拥有2.48亿参数的Ti Solar模型。
要加载此模型,我们将使用transformers库中的AutoModelForCausalLM。加载模型需要三个参数:我们刚刚初始化的模型路径名、设备(这里使用CPU)以及数据类型(这里使用bfloat16)。在CPU上加载模型需要一些时间,你也可以通过设置为auto来使用GPU。
接着,我们通过调用transformers中的AutoTokenizer来设置分词器。只需提及模型路径名,分词器就会被加载。
有了模型和分词器,我们就可以开始生成文本了。语言模型是解码器,因此可以自动补全给定的输入文本或提示。现在,让我们尝试使用预训练模型自动补全一个提示。
我将输入一个提示:“I am an engineer I love”。给定这样的提示,你能想象我们的模型会如何自动补全吗?让我们看看。


我们首先需要用分词器对提示进行分词。然后创建一个文本流实例来流式输出文本。我们将传入分词器,并设置跳过提示本身和特殊标记。
现在你将看到神奇的一幕。这段代码将让我们生成最多128个标记。对于随机输出,你可以简单地将do_sample设置为True并选择你想要的temperature值。
输出是:“I am an engineer. I love to travel and have a great time, but I’m not sure if I can do it all again and so on.” 可以看出,它在生成英语文本方面相当不错。
然而,当我尝试让它自动补全Python代码片段时,这个模型就显得力不从心了,因为它主要是在英语文本上训练的。让我们试试。
这次,我们尝试一个Python代码提示:“Define max numbers.” 顾名思义,我们期望模型编写一个函数,用于在给定的数字列表中找到最大值。我们将像之前一样,从提示创建输入并设置流式输出。
现在创建输出。嗯,这并没有很好地完成任务,对吧?看起来有点像Python代码,但仔细看,结果只是随机的。这里有注释和一个返回格式,但内部没有计算逻辑,这使得这段Python代码无效。这可能是因为我们的预训练模型没有在Python代码本身上进行训练。
那么,如何改善结果呢?有些人会想到微调。微调涉及在少量特定任务的数据上训练你的模型。让我们看看微调模型的表现如何,而不是通用的预训练模型。
我们尝试一个新模型。这个模型与我们之前的小型Solar模型相同,但在Python代码上进行了微调。由于此模型在代码上进行了微调,我们期望它在代码生成方面表现更好。
像之前一样,加载我们的模型和分词器,并流式输出结果。我们将输入相同的提示,将其分词为输入,设置流式输出,然后创建输出。
你认为如何?看输出,可以看到有一些操作,比如if else,但模型似乎仍需要学习更多代码知识,因为它仍然没有写对函数。

现在,让我们用一个预训练的Python模型来生成Python示例。这里我们将使用一个拥有2.48亿参数、最大标记长度为4K、在Python代码上预训练的模型。这个模型与之前模型的不同之处在于,之前的模型是在指令数据上训练的,而这个模型是在纯Python代码上训练的。另一个区别是,其数据集至少大了100倍。
现在,像之前一样加载模型和分词器。将相同的Python代码片段输入给这个定制的预训练模型,并流式输出文本。
好多了!这个函数终于有意义了。这是我们的大语言模型刚刚输出的函数。让我们把它放进去,看看它是否真的有效。我们将输入一个包含不同数字的列表,看看它是否能定义出最大值。
成功了!最大值是7,这是正确的。
通过运行这三个模型,你可以清楚地看到预训练的好处。最后一个模型学习了大量Python知识,在编写可运行代码方面比完全不懂Python的原始模型好得多。而微调模型只学了一点点,还不够流利。
成本考量
你看到了两个必须通过预训练模型才能使其表现良好的用例。值得注意的是,与微调(有时仅需数万个标记即可完成,且成本可能很低)相比,预训练需要大量数据,因此成本高昂。
在下表中,你可以看到训练你在笔记本中尝试过的2.48亿参数模型,针对不同训练数据规模所需的成本。最后一行,使用3900万标记进行训练,对应你在笔记本中看到的那个特定于Python的模型。该训练在6800个GPU上进行了7小时,花费了1500美元。
因此,在开始预训练任务之前,请务必考虑成本。在本课程后面,你将看到一些可用于估算成本的资源。
总结

本节课中,我们一起学习了预训练在大语言模型开发中的核心作用。我们了解到,预训练是模型获取世界知识和语言能力的基础阶段。通过韩语模型和Python代码生成的对比示例,我们直观地看到,当模型需要深入掌握一个在基础训练数据中代表性不足的新领域(如特定语言或编程语言)时,额外的领域特定预训练是获得优异性能的关键,其效果远超单纯的指令微调。同时,我们也认识到预训练需要海量高质量数据和可观的计算成本。创建优秀的模型始于高质量的训练数据,下一节课我们将学习如何构建好的训练数据集。
003:数据准备 📚

在本节课中,我们将学习如何为大语言模型的预训练准备高质量的训练数据。我们将了解预训练数据与微调数据的区别,并掌握一系列数据清洗的关键步骤,以确保模型训练的有效性。
概述
预训练模型的第一步是获取高质量的训练数据。本节课将介绍如何利用来自网络和现有数据集的文本来创建训练集。我们将对比预训练数据与微调数据的差异,并详细讲解数据清洗的核心流程。
预训练数据与微调数据
上一节我们介绍了大语言模型的基本概念,本节中我们来看看训练它们所需的数据有何不同。
用于预训练大语言模型的数据集由海量非结构化文本组成。在训练过程中,每个文本样本都被用来训练大语言模型反复预测下一个词,这被称为自回归文本生成。模型参数在处理训练数据中的每个样本时不断更新,最终模型变得擅长预测下一个词。你可以将此过程想象成“阅读”,输入文本以其原始形式被使用,无需对训练样本进行任何额外的结构化处理。
语言模型需要海量的训练文本(相当于数百万本完整的书籍)才能精通下一个词的预测,并在其参数中编码关于世界的可靠知识。
用于微调的数据则高度结构化,例如问答对、指令-回复对等。左侧是关于“朋友”的文本,这对预训练很有用;右侧是一个微调数据样本,它是一个关于“朋友”的具体问题及其合适的回答。因此,微调样本的形式截然不同。微调的目标是让模型以特定方式行为,或擅长完成特定任务。如果预训练像是阅读许多许多书籍,那么微调就像是参加模拟考试。你并非在学习新知识(这些知识应该已在预训练阅读中掌握),而是在学习如何以特定方式回答问题。
数据来源
如果你想阅读大量文本,就必须找到许多书籍、代码示例、文章和网页。因此,预训练数据集通常从大量文本文档集合中填充,其中许多来源于互联网。世界上充满了文本,因此为预训练找到大量文本相对容易。
另一方面,微调数据需要精确的问题和高质量的对应答案。传统上,这项工作由人工完成,耗时且昂贵。最近,人们开始使用大语言模型来生成微调数据,但这需要一个能力非常强的模型才能取得良好效果。因此,创建高质量的微调数据集需要付出更多努力。
在本节课稍后的笔记本练习中,你将与Lucy一起比较和对比一些预训练和微调数据集的样本。
数据质量的重要性
数据质量对于预训练大语言模型至关重要。如果你的训练数据存在问题,例如大量重复样本、拼写错误、事实不一致或不准确、以及有毒语言,那么最终得到的大语言模型将表现不佳。采取措施解决这些问题并确保训练数据的高质量,将带来更好的模型效果和更高的训练投资回报率。
以下是清理训练文本数据时应完成的主要任务。
去重
以下是数据清洗的首要步骤:去重。存在重复数据会使模型偏向特定的模式和例子。它还会增加训练时间,却不一定能提升模型性能。因此,移除重复文本是数据清洗的关键步骤。这应包括文档内部和所有文档之间的去重。
质量过滤
我们希望训练数据的内在质量高,因此文本应是你感兴趣的语言,与你希望大语言模型建立知识的主题相关,并满足你设定的其他质量标准。你可以设计质量过滤器来清理训练数据的这方面问题。
例如,这里的样本包含日语、中文甚至韩语文本。如果你想训练英语大语言模型,就应该移除这些内容。
安全过滤
另一个步骤是应用内容过滤器,以移除可能有害或有偏见的内容。安全性是一个重要考量。
隐私保护
为了避免潜在的数据泄露,你应该始终移除个人身份信息。一个常见的策略是对训练文本中的此类信息进行脱敏处理,就像你在这里看到的一样。
格式修正
最后,你可以制定规则来修复常见的质量问题,例如全大写、多余标点和格式混乱的文本。
Lucy将在本节课的笔记本中向你详细展示如何执行其中一些步骤。正如你所见,数据清洗可能很复杂且耗时。幸运的是,有越来越多的工具可以帮助你完成这一重要步骤。一个例子是DataBurst,这是我们在Upstage研究的一个开源项目。DataBurst是一个即用型数据清洗流水线,它会接收你的原始训练数据,应用你刚刚看到的清洗步骤以及其他步骤,然后将你的数据打包成适合训练的形式。你可以查看其GitHub页面以了解更多关于如何使用DataBurst的信息。
动手实践:数据收集与清洗
现在,让我们进入笔记本,亲身体验一些数据清洗步骤。
数据收集
让我们从数据收集开始。由于预训练的目标是执行下一个词元预测,因此需要一个庞大的无标签语料库。你通常可以通过网络爬取、收集组织内部文档或直接从数据枢纽下载开放数据集来获取这些数据。这里,我们将使用Hugging Face的datasets库下载两个数据集。
第一个是来自Upstage的预训练数据集。该数据集是从包含1万亿词元的Red Pajama数据集中截取60000个样本创建的。请注意,Red Pajama是用于Llama 1预训练的数据集,由来自Common Crawl、C4、Github等的数据组成。
在我们继续之前,让我们查看一下数据集的信息。在这里,你可以看到每个示例都包含文本和相关的元数据。为简化起见,我们将只使用数据集的文本列。让我们也看一个示例。这是一段关于阿富汗的文本。内容本身并不重要,重要的是它是一个由纯文本组成的预训练示例。这正是我们想要的:没有任何指令类型的结构化(例如问答对)的纯文本。你可以随意更改这里的索引号,以探索数据集中的其他示例。
现在,让我们下载另一个名为Alpaca的数据集。Alpaca是一个微调数据集,包含52000条由GPT-4生成的指令遵循数据。在这里,你可以看到数据由指令、输入和输出组成。让我们看看一个示例的样子。这里我们将查看第一个示例,并打印指令、输入和输出。这是“保持健康的三个建议”。请注意,与仅由文本组成的预训练数据集不同,这个指令数据集(Alpaca)包含了指令、输入和输出列。由于我们关注预训练,从现在起我们将选择只使用预训练数据。
构建自定义数据集
现在,让我们尝试从网络爬取并构建一个自定义数据集。为此,我们将下载九个随机的Python脚本。然而请注意,在实践中你会有多得多(多达数十亿)的样本。让我们从导入一些必需的包开始:os用于与文件系统交互,requests用于向网站发出请求。
这是一个托管在GitHub上的随机Python脚本列表。使用requests库,我们可以初始化并请求URL以检索Python脚本,并将其存储到代码目录中的一个文件中。让我们通过列出代码目录中的结果文件来检查文件是否已成功下载。很好。
现在,我们将把它们转换成Hugging Face数据集,以便可以将它们用作训练文件。为此,我们将首先创建一个字典列表,其中键名为“text”,然后使用from_list方法加载它们。你可以看到,所有九个文件都已成功加载。如果你还记得,这与我们上面下载的预训练数据的结构完全相同。
合并数据集
现在,让我们合并我们下载的预训练数据和从网络爬取的代码数据集。我们将通过调用datasets库中的concatenate_datasets方法来实现。这是在你预训练自己的模型时会经常进行的非常实用的操作:下载一些数据,添加一些自定义数据,然后合并。现在我们总共有60009行数据。
数据清洗步骤
让我们进行一些典型的数据清洗步骤,看看随着清洗的进行,行数是如何减少的。
首先,我们将过滤掉太短的样本。这是一个描述预训练数据常见实践的函数。简而言之,我们保留至少有三行或三个句子,且每行文本至少包含三个词的文本。我们这样做是因为预训练的目标是预测下一个词元,但短样本对该任务不是很有用。让我们尝试运行这个函数。请注意,datasets库有一个filter方法,它将一个函数应用于数据中的每个示例。所以如果你检查行数,你可以看到超过7000行被剔除了。
现在我们将进入第二部分:移除重复内容。这基本上是一个函数,给定输入段落,可以找到重复部分。我们使用这个函数来查找段落内的重复,并判断如果一个段落与其长度相比有太多重复,则返回False以丢弃该段落。我们将在整个数据上运行这个函数。现在我们剩下大约52000个示例,减少了30行。这是一个非常小的减少,但这是从Hugging Face下载数据集的一个优势,因为Hugging Face上的数据集已经完成了大量的预处理。
对于预处理的第三部分,让我们继续进行去重。这个函数通过存储唯一的文本片段并将每个文本与之比较来移除重复条目。让我们尝试运行那个函数。结果,移除了8000行,这是一个很大的减少。实际上,文档之间也存在大量重复,所以请确保你涵盖了这个步骤。
最后一步是语言过滤。这是Sung之前提到的质量过滤器之一。如果你想专注于特定语言或领域,最好过滤掉其他语言或领域,以便模型在相关文本上进行训练。这里我们将使用fastText语言分类器,只保留英语样本来训练我们的模型。你会看到这个警告,但不必太担心。另外请注意,运行速度比我们上面运行的过滤器要慢。这是因为这实际上是一个真正的机器学习模型在运行。如果你想为另一种语言训练模型,只需将语言“English”更改为该特定语言即可。让我们检查一下行数。现在,在移除了大约3000行之后,我们剩下大约40000行。
我想指出,从一开始就拥有一个大数据集非常重要,因为你会在清洗数据集的过程中不断丢弃行。
保存数据
最后,我们将以parquet格式将数据保存到本地文件系统中。请注意,在实践中,你希望在清洗的每个阶段都保存数据,因为你处理的是大量数据,且数据可能无法完全装入内存。Parquet是一种列式存储文件格式,在大数据和数据分析场景中广泛使用。你也可以自由使用任何其他格式,如CSV或JSON。但由于parquet速度非常快,我们在这里选择它。
总结
在本节课中,我们一起学习了如何为大语言模型的预训练准备数据。我们了解了预训练数据(非结构化文本)与微调数据(结构化任务数据)的根本区别。我们深入探讨了数据清洗的关键步骤,包括去重、质量过滤、安全过滤、隐私保护和格式修正,并亲自动手实践了数据收集、合并和清洗流程。最后,我们学习了如何将清洗后的数据保存为适合后续处理的格式。数据准备是模型成功的基石,高质量的数据是训练出优秀大语言模型的前提。

流程的下一步是准备你的安全数据集以进行训练。这涉及对数据的一些额外操作。请加入下一节课,看看这是如何完成的。
004:第三课 数据打包 📦
在本节课中,我们将学习如何为模型训练准备数据。具体来说,我们将了解如何对清洗后的数据集进行分词和打包,使其符合Hugging Face训练流程的要求。

概述
上一节我们介绍了数据清洗,本节中我们来看看数据打包。在将数据用于训练之前,你需要对数据进行进一步处理。两个主要步骤是:对数据进行分词,然后将其打包。大语言模型并不直接处理文本,其内部计算需要数字。分词是将文本数据转换为数字的步骤。
分词
分词的具体细节取决于模型的词汇表和分词算法。每个模型都有特定的分词器,选择正确的分词器至关重要,否则模型将无法正常工作。
以下是使用Hugging Face数据集库进行分词的步骤。我们将使用之前课程中创建的.parquet文件。为了减少执行时间,我们将整个数据集分成10份,只处理其中一份。
# 示例:加载数据集并分片
from datasets import load_dataset
dataset = load_dataset('parquet', data_files='your_data.parquet')
# 此处进行分片操作,例如取前1/10
现在加载分词器。你可以从Hugging Face上托管的现有模型中选择一个,或创建自己的。通常,同一系列的模型会使用相同的分词器。在本例中,我们将使用来自So的tiny-solar分词器。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("your-model-name", use_fast=False)
我们禁用了use_fast标志。如果use_fast为True,自动分词器会使用Rust实现的并行分词器,速度更快。但在本例中,处理长文本样本有时会导致挂起,因此我们将其设为False,并单独使用数据集库的map函数进行并行处理。
分词器的输出是怎样的?让我们在处理批次之前先尝试一下。
sample_text = "这是一个示例句子。"
tokens = tokenizer.tokenize(sample_text)
print(tokens)
正如幻灯片中讨论的,你可以看到输入被分成了多个词元(token),其中特殊词元▁指示了原始空格的位置。注意,输出仍然是文本形式。
现在我们将把这些词元转换为数字ID。我们将创建一个辅助函数,以便利用Hugging Face数据集库的map方法。我们的函数将执行以下操作:对文本进行分词、将词元转换为ID、添加起始(BOS)和结束(EOS)词元。
我们添加BOS和EOS词元是因为我们希望模型能够理解哪些词元是连贯的。然后,我们计算每个样本的词元数量。
def tokenize_function(examples):
# 分词并转换为ID
tokenized_output = tokenizer(examples['text'])
# 添加BOS和EOS词元(如果分词器没有自动添加)
# 注意:许多分词器会自动添加,请根据你的分词器调整
input_ids_with_special = [tokenizer.bos_token_id] + tokenized_output['input_ids'] + [tokenizer.eos_token_id]
examples['input_ids'] = input_ids_with_special
examples['n_tokens'] = len(input_ids_with_special)
return examples
# 映射函数到数据集
tokenized_dataset = dataset.map(tokenize_function, batched=True)
让我们看看结果。我们现在将分词函数映射到数据上。你注意到区别了吗?现在我们的数据中有了input_ids和n_tokens字段。让我们查看第一个样本。
print(tokenized_dataset[0])
这里你可以看到我们有原始文本、与之对应的输入ID,以及文本对应的总词元数。一切看起来都很好。如果你想查看其他样本,可以自由更改索引。
计算总词元数
现在我们来计算数据中的总词元数。在训练大语言模型时,我们经常需要计算总词元数,这可以很容易地用NumPy来检查。
import numpy as np
total_tokens = np.sum(tokenized_dataset['n_tokens'])
print(f"总词元数: {total_tokens}")
对于这个最初大约有4000个文本样本的小数据集,你实际上拥有约500万个词元。你可以想象,一个基于大部分互联网或整个图书馆书籍构建的数据集,最终可能达到数十亿甚至数万亿的词元。
数据打包
现在我们进入准备数据集的最后一步:打包数据集。我们当前的数据集包含长度不等的行,即样本的长度各不相同。但在训练大语言模型时,我们希望将可变长度转换为等长的序列。
为此,我们将执行以下操作:
- 首先,我们将连接所有的
input_ids,使其变成一个巨大的列表。这也称为序列化。 - 其次,我们通过将这个巨大列表分割成具有最大序列长度的小列表来重塑它。
以下是具体操作:
# 1. 将所有样本的input_ids连接成一个列表
all_input_ids = []
for example in tokenized_dataset:
all_input_ids.extend(example['input_ids'])
all_input_ids = np.array(all_input_ids)
print(f"连接后的input_ids数量: {len(all_input_ids)}")
你可以看到,输入ID的数量等于我们上面计算的词元总数。
现在,我们将选择一个最大序列长度。这里我们将其设置为32,但如果你的设备有足够内存,可以设置为更长的长度。更长的最大序列长度能使模型在处理长文本时表现更好。请注意,最近的模型如Solar、Llama2使用4096。
max_sequence_length = 32
接下来,我们计算总输入ID数,使其除以最大序列长度时余数为0。
# 计算需要保留的长度,使其能被max_sequence_length整除
total_length = (len(all_input_ids) // max_sequence_length) * max_sequence_length
# 从数据末尾丢弃余下的输入ID,以便能按最大序列长度精确分区
all_input_ids = all_input_ids[:total_length]
print(f"调整后的总长度: {len(all_input_ids)}")
你可以看到输入ID的数量略有减少。
现在,让我们将input_ids重塑为(-1, max_sequence_length)的形状。使用-1可以让重塑函数自动确定行数。我们还可以确保其数据类型为int32。
# 重塑为二维数组
input_ids_reshaped = all_input_ids.reshape(-1, max_sequence_length).astype(np.int32)
print(f"重塑后的形状: {input_ids_reshaped.shape}")
检查input_ids_reshaped的类型,该对象目前是一个NumPy数组,但你需要将其转换为Hugging Face数据集,以便用于训练。
# 转换回Hugging Face数据集
from datasets import Dataset
# 首先转换为字典列表
data_dict = {"input_ids": input_ids_reshaped.tolist()}
packed_dataset = Dataset.from_dict(data_dict)
最后,让我们将数据保存到本地文件系统,以便后续再次使用。
packed_dataset.save_to_disk("./packed_training_data")
总结
本节课中我们一起学习了数据准备的最后阶段。我们首先对清洗后的文本进行了分词,将其转换为模型可以理解的数字ID,并添加了特殊的起始和结束标记。接着,我们计算了数据的总词元量。最后,我们通过连接和重塑操作,将长度不一的样本打包成固定长度的序列,为高效训练做好了准备。

现在,我们拥有了经过清洗、分词并打包成适合训练形状的干净数据。下一步是配置我们的模型。让我们进入下一课,看看如何操作。
005:模型初始化 🏗️

在本节课中,我们将学习如何为大型语言模型的预训练配置和初始化模型。模型初始化的选择会直接影响预训练的速度和效果。我们将探讨几种不同的初始化方法,包括随机初始化、使用预训练权重、以及通过模型缩放(上采样和下采样)来调整模型大小。
模型架构选择
上一节我们介绍了预训练的整体流程,本节中我们来看看如何为训练准备一个模型。首先需要确定模型的基础架构。
尽管大语言模型有多种Transformer架构变体,本课程主要关注仅解码器或自回归模型。仅解码器架构简化了模型,并且对于下一个词元预测任务更高效。OpenAI的GPT模型以及其他流行的LLM,如Llama、Mistral、Tekcon等,都采用仅解码器架构。这也是我们在Upstage开发Solar系列模型时使用的架构。
一个仅解码器模型由以下几部分组成:
- 嵌入层:将文本转换为向量表示。
- 多个解码器层:每一层都包含基于神经网络的不同组件。
- 分类器层:从词汇表中预测最可能的下一个词元。
权重初始化方法
一旦确定了架构,下一步就是初始化模型的权重。这些权重会在训练过程中根据训练数据中的示例学习预测下一个词元而不断更新。
以下是几种初始化权重的方法:
1. 随机初始化
最简单的方法是使用随机值初始化权重。这种方法可行,但意味着训练需要非常长的时间和海量的数据。在代码中,这通常意味着从一个默认配置创建模型实例。
2. 使用预训练权重
一个更好的方法是重用现有的权重。例如,如果你想构建一个70亿参数的模型,可以从Llama-7B或Mistral-7B的权重开始。这意味着你的模型已经具备了一定的知识基础,能够生成像样的文本。如果你想在新领域的数据上继续预训练模型,这是最佳的起点。在这种场景下,训练所需的数据量和时间远少于从随机权重开始,但仍远多于微调。
鉴于目前开源模型众多,这为创建自定义LLM提供了一个绝佳的选择。例如,我们的团队在Upstage创建韩语版Solar模型时,就是以Solar-10.7B英文模型为基础,使用相同的架构规模,但混合了2000亿个韩语和英语词元进行继续预训练。
模型缩放技术
有时,你可能需要调整模型的大小。这里有两种主要技术:
下采样
如果你想得到一个更小的模型,可以选择下采样。这涉及从现有模型中移除一些层来产生一个更小的模型。这种方法通常对大型模型效果较好,但对小型模型效果不佳。通常,会移除靠近模型中间的层,然后对得到的较小模型进行大量文本的预训练,使其权重恢复一致性。
上采样
更好的方法是上采样。即从一个较小的模型开始,然后复制一些层来构建一个更大的模型。例如,要创建一个100亿参数的模型,可以从一个70亿参数的模型开始。通过复制模型并组合不同副本的层来增加总层数。此时,模型的层之间缺乏一致性,推理效果不佳,因此需要进行额外的预训练来使模型恢复一致并能够生成文本。
然而,由于复制层的权重已经编码了一定的语言理解和知识,因此创建一个好模型所需的数据和时间更少。实际上,上采样可以让你用比从头训练同等规模模型少70%的数据来训练一个性能良好的更大模型。我们在构建Solar模型时就采用了这种方法,从两个Mistral-7B模型的副本开始,扩展到100亿参数,然后继续用1万亿词元进行预训练。
动手实践:初始化模型
现在,让我们进入实践环节,看看如何使用上述每种方法创建模型。
首先,我们设置环境以确保结果可复现,并基于流行的Llama2架构(一种仅解码器模型)进行实验。为了在有限的计算资源下运行,我们将调整一些参数来缩小模型尺寸。
# 设置配置以减少警告并确保可复现性
import warnings
warnings.filterwarnings('ignore')
import torch
import transformers
torch.manual_seed(42)
# 基于Llama2架构配置一个较小的模型
from transformers import LlamaConfig
config = LlamaConfig(
hidden_size=512, # 隐藏层大小
intermediate_size=1024, # 前馈网络中间层大小
num_hidden_layers=12, # 解码器层数
num_attention_heads=8, # 注意力头数
num_key_value_heads=4, # 键值头数(用于分组查询注意力)
vocab_size=32000, # 词汇表大小
)
方法一:随机初始化
使用随机权重初始化模型非常简单。我们只需将配置传递给模型类即可。
from transformers import LlamaForCausalLM
# 随机初始化模型
model_random = LlamaForCausalLM(config)
print(f"随机初始化模型的参数量:{model_random.num_parameters():,}")
此时模型的权重遵循截断正态分布(标准差0.02,均值0)。由于模型未经任何数据训练,如果尝试用它进行推理,将会输出完全随机的乱码。
方法二:加载预训练权重
我们可以加载一个现有的预训练模型(例如一个小型的Solar模型)作为起点。这被称为继续预训练,是比从头开始训练快得多的方法。
from transformers import AutoModelForCausalLM
# 加载预训练模型权重
model_pretrained = AutoModelForCausalLM.from_pretrained("upstage/SOLAR-10.7B-248M")
print(f"预训练模型的参数量:{model_pretrained.num_parameters():,}")
方法三:下采样(缩小模型)
下采样通过移除现有模型的中间层来创建一个更小的模型。以下是如何操作的示例:
# 假设我们有一个12层的模型
original_model = AutoModelForCausalLM.from_pretrained("upstage/SOLAR-10.7B-248M")
print(f"原始模型层数:{len(original_model.model.layers)}")
# 创建下采样模型:移除第5和第6层(索引从0开始)
layers_to_keep = list(range(0, 5)) + list(range(7, 12)) # 保留除第5、6层外的所有层
downscaled_layers = [original_model.model.layers[i] for i in layers_to_keep]
# 注意:此处仅为概念演示。实际创建新模型需要更复杂的配置和权重复制。
方法四:上采样(扩大模型)
上采样从一个较小的预训练模型开始,通过复制其层来构建一个更大的模型。首先,我们为目标大模型创建配置。
# 配置一个更大的模型(例如16层)
config_large = LlamaConfig(
hidden_size=512,
intermediate_size=1024,
num_hidden_layers=16, # 目标:16层
num_attention_heads=8,
num_key_value_heads=4,
vocab_size=32000,
)
# 随机初始化这个大模型
model_large_random = LlamaForCausalLM(config_large)
接下来,我们用预训练小模型的权重来覆盖这个大模型的权重。我们复制小模型的底部若干层和顶部若干层,并可能重复某些层来填满大模型的层数。
# 加载小预训练模型
small_pretrained = AutoModelForCausalLM.from_pretrained("upstage/SOLAR-10.7B-248M")
small_layers = small_pretrained.model.layers # 假设有12层
# 构建大模型的层:例如,取小模型的前8层和后8层(有重叠)
# 目标是拼出16层
new_layers = []
# 复制前8层
new_layers.extend(small_layers[:8])
# 复制后8层(如果小模型只有12层,则从第4层开始取)
new_layers.extend(small_layers[-8:])
# 注意:此处需要将new_layers的权重精确复制到model_large_random的对应层中。
# 同时还需要复制嵌入层和分类器的权重。
# 此过程需要精细的代码实现,此处省略具体赋值细节。
print(f"上采样后模型的参数量:{model_large_random.num_parameters():,}")
经过上采样初始化后,模型已经具备了一些生成英语文本的能力,但由于新组合的层之间缺乏协调,输出可能不流畅。因此,必须用更多数据对这个模型进行继续预训练,以使所有层能够协同工作。尽管如此,这比起从随机权重开始,已经前进了一大步。
总结 🎯

本节课中我们一起学习了大型语言模型预训练中的模型初始化策略。我们了解了仅解码器架构,并探讨了四种关键的初始化方法:随机初始化、加载预训练权重、下采样和上采样。选择正确的初始化方法能显著影响训练效率和最终模型性能。通常,从预训练模型出发并进行调整(缩放或继续预训练),是比从头开始随机训练更高效、更经济的路径。在接下来的课程中,我们将学习如何实际训练这些初始化好的模型。
006:模型训练 🚀
在本节课中,我们将学习如何配置一次训练任务并训练你自己的模型。上一节我们介绍了如何准备训练数据集和初始化模型,本节中我们来看看具体的训练过程。

概述
现在,你已经拥有了训练数据集,并且知道了如何用你偏好的训练配置来初始化一个模型。接下来,我们将进入有趣的部分——训练。你将学习如何使用 Hugging Face 的 Trainer 来配置并执行一次训练任务。
使用 Hugging Face Trainer 进行训练
我们将使用 Hugging Face 的 Trainer 来处理大部分训练步骤。首先,我们需要加载数据并设置训练的超参数。
加载数据与设置参数
以下是训练准备的核心步骤:
- 加载数据:使用 Hugging Face 的
Dataset类加载数据。这个类实现了训练和数据加载所需的一些方法。 - 设置超参数:为训练设置超参数,包括批处理大小、学习率等。我们将在本课的笔记中为你提供这些参数的推荐起始值。
- 启动训练:将你的数据、配置好的模型和训练参数传递给
Trainer,然后调用train函数即可开始训练。
此时,一切就绪,你可以坐下来监控训练过程了。我知道你会迫不及待地想使用新模型,但请保持耐心。训练需要很长时间,具体取决于你的硬件。对于大型模型和大型数据集,可能需要数周甚至数月。
训练内存需求
一个重要的注意事项是:训练模型比推理需要更多的内存。额外的内存用于存储在训练过程中更新的梯度和激活值。

假设你需要存储一个 100亿 参数的模型,那么实际上你可能需要高达 20倍 的内存,即 200 GB。这通常需要多个 GPU,并且成本会非常高昂。在开始之前,你可以使用一些计算器来估算训练任务的成本。例如,Hugging Face 提供了一个计算器。

在这个例子中,使用 200 个 GPU 在 9800 亿个词元 上训练一个 170 亿 参数的模型,估计成本约为 100 万美元,耗时约 两周。

代码实践:设置训练任务
让我们进入笔记,看看如何为你的模型设置训练任务。我将向你展示如何设置训练流程,我们将使用前几课中保存的数据和 Upscaled 模型作为示例。请注意,我们将在 CPU 上进行训练。

当然,如果你有 GPU,可以随时将其更改为 auto。
1. 加载模型和数据
首先,像之前一样加载模型。
# 加载模型
model = AutoModelForCausalLM.from_pretrained("your_model_path")
现在加载我们的数据。我们需要为 Dataset 类更新两个方法,以便它能与 Trainer 交互。
__len__方法:返回训练样本的数量。__getitem__方法:以字典格式返回每个样本的输入和输出词元。
我们创建这个自定义类是为了加载在第三课中创建的预处理后的 .pkl 文件,并返回 input_ids 和 labels。
class CustomDataset(Dataset):
def __init__(self, file_path):
# 加载预处理数据
with open(file_path, 'rb') as f:
self.data = pickle.load(f)
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
# 返回输入ID和标签
# 对于因果语言模型,标签通常与输入ID相同(用于下一个词元预测)
item = self.data[idx]
return {'input_ids': item, 'labels': item}
请注意,这里我们将 labels 设置为等于 input_ids,因为我们想要执行下一个词元预测。然后,LlamaForCausalLM 模型会在内部对标签进行偏移,以创建用于监督学习的输入-输出对,正如你在第一课中看到的那样。
2. 定义训练参数
现在,让我们定义一个自定义的参数类来设置训练参数。为 LLM 选择参数可能具有挑战性,通常涉及大量研究,因为训练 LLM 成本高昂,通常不允许进行太多试错,这与传统的小规模机器学习不同。
这里有许多重要的配置,但我只强调最重要的几个设置:
optim:指示优化器。虽然你可以自由探索不同的优化器,如普通的Adam,但如今AdamW(带权重衰减的 Adam)是训练 LLM 的首选优化器。max_steps:指示训练的最大步数。在进行预训练时,通常将num_train_epochs设置为 1,而不是设置步数,这意味着你将完整处理一次所有数据。per_device_train_batch_size:决定批处理大小主要取决于你,但经验法则是根据训练设备的内存容量最大化批处理大小。
回想一下,在你创建的数据集中,最大序列长度是 32 个词元。如果批处理大小为 2,那么整个批次就由 64 个词元组成。此外,这个设置是 每个加速器 的批处理大小。因此,如果你有 8 个可用的 GPU,你每训练一步将处理 8 倍 的批处理大小,即 512 个词元。
在配置的末尾,有一些被注释掉的代码行,它们会保存模型的中间检查点。我将在本课末尾详细讨论这一点。
3. 配置训练器并开始训练
现在,创建一个 Hugging Face 参数解析器,它将解析输入参数,并添加一个参数以便我们可以指定输出目录。
# 设置训练参数
training_args = TrainingArguments(
output_dir="./output", # 模型保存目录
overwrite_output_dir=True,
num_train_epochs=1,
per_device_train_batch_size=2,
save_steps=3, # 每3步保存一个检查点
save_total_limit=2, # 最多只保留2个检查点
logging_steps=3,
optim="adamw_torch",
max_steps=30 # 仅训练30步作为演示
)
这行代码将输出目录设置为 ./output。这是运行结束时模型将被保存的目录。
将参数传递给自定义数据集后,我们的数据集将按照 Trainer 的需要进行配置。配置好数据集后,我建议你总是打印一个样本行或至少数据的形状,以确保你正在处理正确的数据。你绝对不希望花费大量的 GPU 时间在错误的数据上。
在这里,你可以看到词元的长度是 32,这正是我们上面配置的最大序列长度。所以这看起来没问题。
现在,我们终于准备好开始训练了。这是最重要,却也是最简单的部分。你所要做的就是用一个待训练的模型、训练参数和训练数据集来初始化一个 Trainer 对象。
# 初始化训练器
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
)
# 开始训练
trainer.train()
在这里,我们将让训练器运行 30 步,并每 3 步打印一次日志。让我们开始训练运行。我们将加快视频速度,这在笔记中运行需要几分钟。
请注意,你可能需要运行相当长的时间才能观察到损失的下降,因为模型已经具备了一些书写英语的能力,每一步训练带来的损失变化较小。在这里,仅仅 30 步,你会看到损失值波动,可能看起来并没有下降。但如果你运行 1000 或 10000 步,你会看到这个值下降。通常,你会预训练一个模型数周,甚至数月。
训练监控与检查点
在训练过程中,我们并非无所事事,而是密切监控整个过程。我们首先会使用 Weights & Biases 记录损失,这允许我们的团队随时检查进度。如果你感兴趣,可以查看 Weights & Biases 关于深度学习的短期课程。
我们还会创建 检查点,即模型的中间版本,例如每天保存一次。像这样保存模型可以确保在计算资源宕机或崩溃时,我们不必从头开始。
在训练器运行之后,它实际上会输出一些关于训练的信息,包括全局步数、训练损失和训练运行时间。回想一下,这些是我在上面注释掉的保存配置。在这里,我们提到了保存策略,默认是 steps。你也可以将其替换为 epochs。我们还提到 save_steps 是每 3 步一次,所以我们将每 3 步有一个检查点。
你总是可以用另一个数字替换它,在实践中,可能是每 10,000 步、100,000 步等等。我们还想限制检查点的数量,因为检查点会占用大量存储空间。这里我们将其限制为 2,这意味着只有 2 个检查点会被保存在存储中。
检查训练效果
如果你好奇,这里可以看看模型在训练 10,000 步后的表现。这里你加载一个分词器,然后加载一个在 10,000 步后保存的检查点,并设置它来生成文本。让我们看看它的输出。
# 示例:加载检查点并生成文本
tokenizer = AutoTokenizer.from_pretrained("your_tokenizer_path")
model_checkpoint = AutoModelForCausalLM.from_pretrained("./output/checkpoint-10000")
inputs = tokenizer("Once upon a time", return_tensors="pt")
outputs = model_checkpoint.generate(**inputs, max_length=50)
print(tokenizer.decode(outputs[0]))
好的,看起来和之前有点不同。文本仍然没有太多意义,但部分原因是这么小的模型在生成长段连贯文本时本身就会有问题。不过,性能仍然比以前好很多。它不再重复相同的文本,每个句子都不同,并且句子之间存在一些相关内容。总的来说,我们看到了一些变化,如果我们继续进行完整的预训练,我们期望看到更多改进。
像这样保存检查点可以让你获得模型的中间版本,你可以对其进行评估,了解训练进展如何。让我们进入下一课,更多地讨论评估。

总结

本节课中,我们一起学习了如何配置和启动大语言模型的训练任务。我们介绍了使用 Hugging Face Trainer 的流程,包括加载数据、设置关键超参数(如优化器、批处理大小和最大步数),并启动了训练。我们还讨论了训练的内存需求和成本估算,以及通过保存检查点和监控损失来有效管理长期训练的重要性。最后,我们观察了模型在部分训练后的输出变化,为下一课的模型评估做好了准备。
007:模型评估 🧪
在本节课中,我们将学习如何评估预训练后的大语言模型。评估是衡量模型性能、发现改进方向的关键步骤。我们将介绍几种常见的评估方法,并了解如何使用基准测试来公平地比较不同模型。

概述
你已经了解了预训练模型的所有步骤。但训练完成后,如何知道模型的好坏呢?在这最后一课中,你将探索一些针对大语言模型的常见评估策略,包括一些用于比较不同模型性能的重要基准任务。
评估方法

评估就像一场测试,虽然有时令人不快,但为了了解模型的优势与不足,这是必要的。评估模型有四种常见方法:观察损失、检查输出、模型比较和使用基准数据集。这不是一次性的工作,而是一个持续的过程。
1. 观察训练损失
第一种方法是在训练过程中观察损失。如果训练进展顺利,损失值应该以一种一致的方式随时间下降。
# 伪代码示例:监控训练损失
for epoch in range(num_epochs):
loss = train_one_epoch(model, data_loader)
print(f"Epoch {epoch}: Loss = {loss}")
# 绘制损失曲线以观察趋势
如果损失没有下降,你可能需要重新考虑训练超参数,特别是学习率。如果你观察到损失曲线趋于平缓(即达到平台期),也可能存在一些问题。一种可能是你的模型已达到其最大学习能力,无法从额外数据中学到更多。这在训练小规模模型时可能发生。这也可能表明你的训练数据集存在问题,例如存在低质量样本。因此,如果训练大型模型时出现这种情况,一定要检查你的数据。
2. 检查模型输出

在训练大语言模型时,亲自检查模型的输出非常重要。你可以定期(例如每10,000个训练步)创建模型的检查点,并确保模型的输出符合你的预期。随着时间的推移,模型生成的文本应该越来越好。人工评估对于大语言模型仍然非常重要,不要忽视这一步。
以下是检查模型输出的步骤:
- 定期保存模型检查点。
- 使用固定的提示词(prompt)测试不同检查点的模型。
- 人工评估生成文本的质量、连贯性和相关性。
3. 与其他模型比较
将结果并排比较,更容易判断哪个更好。有一些在线工具可用于比较模型,包括Uptage Console和Buckley的LMC,你可以在笔记中找到相关链接。这是衡量性能的最佳方法之一,但确实需要人工投入。一个活跃的研究领域是使用大语言模型作为“裁判”来判断哪个模型输出更好,但“裁判”模型可能存在偏见,这取决于它是如何训练的。
4. 使用基准数据集
最常见的评估方法是使用基准数据集。这就像让大语言模型参加标准化考试,通过在相同的基准上测试所有模型,你可以公平地比较不同模型的能力和性能。
有几个著名的基准数据集,如ARC、MMLU、HellaSwag、TruthfulQA、MGSM和GSM8K。这些数据集衡量大语言模型的通用能力,如推理、常识和数学技能。
但根据你使用大语言模型的目的,测量其组合能力可能更有用。为此,开发了像BIG-Bench、EQ-Bench和Instruct这样的基准。
实践:运行基准测试
在最后的笔记中,你将学习如何下载并在你的模型上运行一些基准测试。这里我们将使用一个流行的开源评估库,名为LM Evaluation Harness,来自EleutherAI项目。
它已安装在当前学习环境中,但如果你在其他环境中需要,可以运行这行代码进行安装:
pip install lm-eval
该工具包含许多用于评估的任务。我们将选择在TruthfulQA MC2任务上评估TinyStories模型。MC2任务由牛津大学和OpenAI开发的多选题组成,是Hugging Face开放大语言模型排行榜中包含的评估任务之一。
MC2任务的工作原理如下:给定一个问题以及多个真/假参考答案,得分是分配给所有真实答案集合的归一化总概率。我们将在CPU上运行此评估,并且只运行五个示例,以便评估能在10分钟内完成。
请注意,评估任务需要相当长的时间,因为需要为每个候选答案计算对数似然。为了你的方便,我们将在此处加速视频播放。
现在评估完成了。在最终的表格中,你可以看到最终得分约为0.4。请记住,我们的模型非常小,因此与拥有数十亿参数的其他模型进行比较是不公平的。
你可以使用这里的代码运行整个评估套件。如果你训练了自己的模型并希望登上Hugging Face排行榜,这里的自定义函数将使用相应的few-shot参数运行所有六个任务。
我们不会在这里运行它,因为对于短期课程来说时间太长了。few-shot参数表示提示中包含多少个示例供大语言模型参考。例如,对于ARC挑战,将包含25个示例。请注意,这些是Hugging Face排行榜的固定数字,因此所有模型都以相同的方式进行测试。我希望如果你训练了自己的模型,能在这些基准上进行评估,以便社区能看到你的模型有多棒。
评估工具
在笔记中,你看到了几种手动运行基准测试的方法。幸运的是,有一些工具可以使评估变得容易得多。
例如,Eververse是我们在Upsage创建的一个开源项目,旨在支持你的大语言模型评估,我们公司内部也使用它。Eververse使用Git子模块统一了各种大语言模型评估框架,设置和运行相当容易。你可以一次在多个模型上运行Eververse,它将提供其性能的综合报告,包括基准分数、排名和其他标准。
最后,在完成预训练后,你可能会对模型进行微调和对齐,以便在实际应用中使用。不要忘记评估模型的行为及其与人类价值观的一致性。关于真实性、安全性、公平性等方面的基准测试也已存在,并且仍在积极开发中。你可以在笔记中查看链接以获取更多信息。
总结

本节课中,我们一起学习了评估预训练大语言模型的关键方法。我们探讨了通过观察损失曲线、人工检查输出、与其他模型比较以及使用标准化基准数据集来评估模型性能。记住,评估是一个持续的过程,对于理解模型能力、指导后续的微调和对齐至关重要。选择合适的评估策略和工具,将帮助你更客观地衡量模型表现,并推动其不断改进。
008:课程总结 🎉
在本节课中,我们将对《大语言模型预训练》课程的全部内容进行总结,回顾从数据准备到模型评估的完整流程。
感谢您完成本课程的学习。您现在已掌握了预训练一个模型所需的所有步骤,包括准备训练数据、配置模型、进行训练以及评估其性能。
尽管您在此处看到的示例是在CPU上运行的,但您可以调整Notebook中的代码,以便在GPU上训练更大的模型。只是请务必注意,预训练工作会伴随相应的成本。
掌握了本课程的知识后,我们期待看到您创造出更多突破性的预训练模型。

如果您真的训练了自己的模型,请务必与社区分享您的工作成果。
我们希望您喜欢这门课程。祝您预训练顺利愉快!


课程总结

本节课中我们一起回顾了整个大语言模型预训练的流程。我们学习了从数据准备、模型配置、实际训练到性能评估的核心步骤。虽然示例基于CPU,但您已具备将代码迁移至GPU以训练更大模型的能力。请记住关注计算成本,并鼓励您将成果与社区分享。预训练是一个强大的工具,期待您运用所学,构建出创新的模型。


浙公网安备 33010602011771号