机器之心报道
机器之心部
「这是有史以来最大、最令人困惑的研究生涯错误,」Andrej Karpathy 感叹道。
上个周末,OpenAI 创始成员、研究科学家、原特斯拉前 AI 高级总监、AI 领域的大神 Andrej Karpathy 一直在后悔。后悔自己没有早点带领 OpenAI 开创大模型时代。
是怎么一回事?看起来 Karpathy 认为当年早已认识到自回归语言模型的强大潜力,但却在很长一段时间里「误入歧途」,随大溜一起搞强化学习。
2013 年的 Atari RL 论文被认为是深度强化学习的开山之作:一个通用学习算法就发现了 Breakout 和许多其他游戏的最佳策略,看起来,在很多任务上我们只需要对其进行足够的改进和扩展,就可以构建出强大的 AI 模型了。
我们也还记得,在 Karpathy 跳槽去到特斯拉一年后,2018 年 OpenAI 推出了 OpenAI Five,利用强化学习的方法在 Dota 2 游戏上开始与职业选手过招。
在 2019 年,OpenAI 的研究者还训练神经网络,利用一只类人机械手来玩魔方,表明强化学习工具不仅仅可以处理虚拟任务,而且还能够解决需要高度灵活性的真实世界问题。
这个时候 OpenAI 在另一边已经推出「迄今为止最大模型」GPT-2 了,强化学习的盛世,似乎很快就被后来兴起的大语言模型(LLM)所覆盖。
Karpathy 还提到:「Yann LeCun 当时就不太看好强化学习,他一遍又一遍地谈论『蛋糕』,而强化学习(RL)只是蛋糕顶部最后一颗樱桃,表征学习是蛋糕主体,监督学习是锦上添花。至少在今天看来,他在概念上是完全正确的(预训练 = 蛋糕主体,监督微调(SFT)= 糖衣,RLHF = 樱桃,即基本的 ChatGPT 训练 pipeline)。这很有趣,因为今天他仍然不太看好 LLM。」
说了这么多,如今已是「事后诸葛亮」了,当初明明看好却没把握住,看起来比当初根本没想过这回事还让人懊恼。
让我们看看 Karpathy 那篇预言了如今大模型时代的文章,说了些什么。
Andrej Karpathy 当初是怎么看好的
其实关于 RNN,Karpathy 早在 15 年就已经注意到了。为此他还专门写了一篇名为《RNN 的不合理有效性》 文章。
文章深入探讨了循环神经网络(RNN)的潜力与实际应用。文中提到了几个实验示例,包括使用 RNN 来生成类似作品的文本,以及模拟编程代码和数学公式的生成。
Karpathy 用简单易懂的语言介绍了 RNN。RNN 是一种能够处理序列数据的神经网络,它通过其循环连接能够记住之前的信息,这对于时间序列数据或任何序列数据的处理尤为关键。
Karpathy 描述了使用 RNN 进行图像描述任务的初次尝试,并分享了这一过程中的神奇体验。他回忆称,在使用 RNN 进行训练后不久,即使是随意选择的超参数配置下,他的模型开始生成看起来非常不错的图像描述,这些描述接近于有意义。这种简单模型与所获得的结果质量之间的比例,有时会远远超出预期,这让人感到惊讶。
当时的普遍看法认为 RNN 难以训练,但 Karpathy 后来的经验却让他得出了相反的结论。 随着时间的推移,Karpathy 频繁地训练 RNN,并多次见证了它们的强大和稳健,尽管如此,这些网络产生的有趣输出仍然让他感到新奇和有趣。
关于如何利用 RNN 逐字符生成文本的介绍,引发了对「这怎么可能?」这一问题的思考。
事实上,众所周知,RNN 是图灵完备的,因为它们可以模拟任意程序(具有适当的权重)。但与神经网络的通用近似定理类似,你不应该对此进行过多的解读。
如果训练普通神经网络是对函数的优化,那么训练循环网络就是对程序的优化。
接下来,Karpathy 在博客中讲解了 RNN 的基本工作原理,并通过一个具体的字符级语言模型应用来说明其实际操作过程。
具体而言,Karpathy 为 RNN 提供一大段文本,并要求它根据前面的字符序列对序列中下一个字符的概率分布进行建模。这样,就可以一次一个字符地生成新文本。
假设词汇表为 hello, 这段训练数据可以被拆分为 4 个独立的训练样本:
每个字符会被编码为一个向量,采用 1-of-k 编码,即向量中只有一个位置为 1,其余位置为 0,然后使用 step 函数将它们逐个输入到 RNN。接着会观察到一个 4 维输出向量序列(每个字符一维),并将其解释为 RNN 当前分配给序列中下一个字符的置信度。
接下来可以看到 RNN 的训练过程及其背后的逻辑:
在第一个 step 中,RNN 看到字符 h 后,预测下一个字符的概率分布如下:
但根据训练数据 hello,正确的下一个字符应该是 e。因此,需要提高 e 的置信度(绿色表示),同时降低其他字符的置信度(红色表示)。
在这过程中,每个 step 都有一个期望的目标字符。目标是让网络对正确字符的置信度更高,而对错误字符的置信度更低。因此需要反向传播算法计算每个权重的梯度。
根据梯度调整 RNN 的权重(参数),让正确字符的置信度提高(例如 e 的置信度从 2.2 提高到 2.3)。错误字符的置信度则会相应降低。
这一过程会重复多次,直到模型收敛。收敛后,RNN 的预测会与训练数据更加一致,即每一步都能够正确预测下一个字符。
为了进一步说明,出于教学目的,Karpathy 还用 Python/numpy 编写了一个最小的字符级 RNN 语言模型。代码大约只有 100 行。感兴趣的读者可以参考:
更进一步的,Karpathy 在这篇博客中还列举了 5 个其他示例展示。所有示例字符模型都是在 Github 上发布的代码进行训练的。
我们以「莎士比亚」这个示例为例。
Karpathy 希望探索 RNN 是否能够学习并生成具有更多结构和风格的文本内容。为此,他下载了莎士比亚的所有作品,并将它们合并成一个 4.4MB 的文件,用作训练数据。
接着,Karpathy 使用了一个包含 3 层 RNN 的模型,每层有 512 个隐藏节点,训练这个模型耗费了数小时。最后,模型生成了一些文本样本,包括角色名字和内容对话,有时还能生成较长的独白片段。
不过,从结果来看,尽管生成的文本看起来像莎士比亚的作品,但仍有一定的差异。Karpathy 认为这些生成结果表现出了模型的能力和局限性,同时也展现了 RNN 在字符级语言建模上的潜力。
Karpathy 还列举了如何生成婴儿名字这种有趣的示例,感兴趣的读者可以参考原博客了解更多内容。
随后的故事我们都知道了,2017 年谷歌发布了 Transformer 论文,提出了自注意力机制。在这个基础上,人们逐步探索出大模型的 Scaling Laws,将 AI 技术向通用化快速延伸,直到今天。
既然连 Andrej Karpathy 这样的 AI 大佬也在研究方向上「走过弯路」,我们是不是也该回看一下过去?
参考链接:https://karpathy.github.io/2015/05/21/rnn-effectiveness/
理解 LSTM 网络
: Christopher Olah (OpenAI) 译者 :朱小虎 Xiaohu (Neil) Zhu(CSAGI / University AI) 原文链接 :
术语 : 循环神经网络(Recurrent Neural Network, 简称 RNN); 长短期记忆(Long Short-Term Memory, 简称 LSTM); 门限循环单元(Gated Recurrent Unit, 简称 GRU)
人类并不是每时每刻都从一片空白的大脑开始他们的思考。 在你阅读这篇文章时候,你都是基于自己已经拥有的对先前所见词的理解来推断当前词的真实含义。 我们不会将所有的东西都全部丢弃,然后用空白的大脑进行思考。 我们的思想拥有持久性。
传统的神经网络并不能做到这点,看起来也像是一种巨大的弊端。 例如,假设你希望对电影中的每个时间点的时间类型进行分类。 传统的神经网络应该很难来处理这个问题——使用电影中先前的事件推断后续的事件。
RNN 解决了这个问题。 RNN 是包含循环的网络,允许信息的持久化。
在上面的示例图中,神经网络的模块,,正在读取某个输入 ,并输出一个值 。 循环可以使得信息可以从当前步传递到下一步。
这些循环使得 RNN 看起来非常神秘。 然而,如果你仔细想想,这样也不比一个正常的神经网络难于理解。 RNN 可以被看做是同一神经网络的多次复制,每个神经网络模块会把消息传递给下一个。 所以,如果我们将这个循环展开:
链式的特征揭示了 RNN 本质上是与序列和列表相关的。 他们是对于这类数据的最自然的神经网络架构。
并且 RNN 也已经被人们应用了!在过去几年中,应用 RNN 在语音识别,语言建模,翻译,图片描述等问题上已经取得一定成功,并且这个列表还在增长。 我建议大家参考 Andrej Karpathy 的博客文章—— The Unreasonable Effectiveness of Recurrent Neural Networks来看看更丰富有趣的 RNN 的成功应用。
而这些成功应用的关键之处就是 LSTM 的使用,这是一种特别的 RNN,比标准的 RNN 在很多的任务上都表现得更好。 几乎所有的令人振奋的关于 RNN 的结果都是通过 LSTM 达到的。 这篇博文也会就 LSTM 进行展开。
RNN 的关键点之一就是他们可以用来连接先前的信息到当前的任务上,例如使用过去的视频段来推测对当前段的理解。 如果 RNN 可以做到这个,他们就变得非常有用。 但是真的可以么?答案是,还有很多依赖因素。
有时候,我们仅仅需要知道先前的信息来执行当前的任务。 例如,我们有一个语言模型用来基于先前的词来预测下一个词。 如果我们试着预测 “the clouds are in the sky” 最后的词,我们并不需要任何其他的上下文 —— 因此下一个词很显然就应该是 sky。 在这样的场景中,相关的信息和预测的词位置之间的间隔是非常小的,RNN 可以学会使用先前的信息。
但是同样会有一些更加复杂的场景。 假设我们试着去预测“I grew up in France... I speak fluent French”最后的词。 当前的信息建议下一个词可能是一种语言的名字,但是如果我们需要弄清楚是什么语言,我们是需要先前提到的离当前位置很远的 France 的上下文的。 这说明相关信息和当前预测位置之间的间隔就肯定变得相当的大。
不幸的是,在这个间隔不断增大时,RNN 会丧失学习到连接如此远的信息的能力。
在理论上,RNN 绝对可以处理这样的 长期依赖 问题。 人们可以仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN 肯定不能够成功学习到这些知识。 Bengio, et al. (1994) 等人对该问题进行了深入的研究,他们发现一些使训练 RNN 变得非常困难的相当根本的原因。
然而,幸运的是,LSTM 并没有这个问题!
Long Short Term 网络—— 一般就叫做 LSTM ——是一种 RNN 特殊的类型,可以学习长期依赖信息。 LSTM 由 Hochreiter & Schmidhuber (1997) 提出,并在近期被 Alex Graves 进行了改良和推广。 在很多问题,LSTM 都取得相当巨大的成功,并得到了广泛的使用。
LSTM 通过刻意的设计来避免长期依赖问题。记住长期的信息在实践中是 LSTM 的默认行为,而非需要付出很大代价才能获得的能力!
所有 RNN 都具有一种重复神经网络模块的链式的形式。 在标准的 RNN 中,这个重复的模块只有一个非常简单的结构,例如一个tanh层。
LSTM 同样是这样的结构,但是重复的模块拥有一个不同的结构。 不同于 单一神经网络层,这里是有四个,以一种非常特殊的方式进行交互。
不必担心这里的细节。 我们会一步一步地剖析 LSTM 解析图。 现在,我们先来熟悉一下图中使用的各种元素的图标。
在上面的图例中,每一条黑线传输着一整个向量,从一个节点的输出到其他节点的输入。 粉色的圈代表按位 pointwise 的操作,诸如向量的和,而黄色的矩阵就是学习到的神经网络层。 合在一起的线表示向量的连接,分开的线表示内容被复制,然后分发到不同的位置。
LSTM 的关键就是细胞状态,水平线在图上方贯穿运行。
细胞状态类似于传送带。 直接在整个链上运行,只有一些少量的线性交互。 信息在上面流传保持不变会很容易。
LSTM 有通过精心设计的称作为“门”的结构来去除或者增加信息到细胞状态的能力。 门是一种让信息选择式通过的方法。 他们包含一个sigmoid神经网络层和一个按位的乘法操作。
Sigmoid 层输出到之间的数值,描述每个部分有多少量可以通过。 代表“不许任何量通过”, 就指“允许任意量通过”!
LSTM 拥有三个门,来保护和控制细胞状态。
在我们 LSTM 中的第一步是决定我们会从细胞状态中丢弃什么信息。 这个决定通过一个称为 忘记门层 完成。 该门会读取和 ,输出一个在到之间的数值给每个在细胞状态中的数字。 表示“完全保留”, 表示“完全舍弃”。
让我们回到语言模型的例子中来基于已经看到的预测下一个词。 在这个问题中,细胞状态可能包含当前 主语 的性别,因此正确的 代词 可以被选择出来。 当我们看到新的 主语 ,我们希望忘记旧的 主语 。
下一步是确定什么样的新信息被存放在细胞状态中。 这里包含两个部分。 第一, sigmoid层称 “输入门层” 决定什么值我们将要更新。 然后,一个tanh层创建一个新的候选值向量,,会被加入到状态中。 下一步,我们会讲这两个信息来产生对状态的更新。
在我们语言模型的例子中,我们希望增加新的主语的性别到细胞状态中,来替代旧的需要忘记的主语。
现在是更新旧细胞状态的时间了, 更新为 。 前面的步骤已经决定了将会做什么,我们现在就是实际去完成。
我们把旧状态与相乘,丢弃掉我们确定需要丢弃的信息。 接着加上 。 这就是新的候选值,根据我们决定更新每个状态的程度进行变化。
在语言模型的例子中,这就是我们实际根据前面确定的目标,丢弃旧代词的性别信息并添加新的信息的地方。
最终,我们需要确定输出什么值。 这个输出将会基于我们的细胞状态,但是也是一个过滤后的版本。 首先,我们运行一个sigmoid层来确定细胞状态的哪个部分将输出出去。 接着,我们把细胞状态通过tanh进行处理(得到一个在到之间的值)并将它和sigmoid门的输出相乘,最终我们仅仅会输出我们确定输出的那部分。
在语言模型的例子中,因为他就看到了一个 代词 ,可能需要输出与一个 动词相关的信息。 例如,可能输出是否代词是单数还是负数,这样如果是动词的话,我们也知道动词需要进行的词形变化。
我们到目前为止都还在介绍正常的 LSTM。 但是不是所有的 LSTM 都长成一个样子的。 实际上,几乎所有包含 LSTM 的论文都采用了微小的变体。 差异非常小,但是也值得拿出来讲一下。
其中一个流形的 LSTM 变体,就是由Gers & Schmidhuber (2000)提出的,增加了 “peephole connection”。 是说,我们让 门层 也会接受细胞状态的输入。
上面的图例中,我们增加了 peephole 到每个门上,但是许多论文会加入部分的 peephole 而非所有都加。
另一个变体是通过使用 coupled 忘记和输入门。 不同于之前是分开确定什么忘记和需要添加什么新的信息,这里是一同做出决定。 我们仅仅会当我们将要输入在当前位置时忘记。 我们仅仅输入新的值到那些我们已经忘记旧的信息的那些状态 。
另一个改动较大的变体是 Gated Recurrent Unit (GRU),这是由Cho, et al. (2014)提出。 它将忘记门和输入门合成了一个单一的 更新门。 同样还混合了细胞状态和隐藏状态,和其他一些改动。 最终的模型比标准的 LSTM 模型要简单,也是非常流行的变体。
这里只是部分流行的 LSTM 变体。 当然还有很多其他的,如 Yao, et al. (2015)提出的 Depth Gated RNN。 还有用一些完全不同的观点来解决长期依赖的问题,如 Koutnik, et al. (2014)提出的 Clockwork RNN。
要问哪个变体是最好的?其中的差异性真的重要吗? Greff, et al. (2015)给出了流行变体的比较,结论是他们基本上是一样的。 Jozefowicz, et al. (2015)则在超过 1 万种 RNN 架构上进行了测试,发现一些架构在某些任务上也取得了比 LSTM 更好的结果。
刚开始,我提到通过 RNN 得到重要的结果。 本质上所有这些都可以使用 LSTM 完成。 对于大多数任务确实展示了更好的性能!
由于 LSTM 一般是通过一系列的方程表示的,使得 LSTM 有一点令人费解。 然而本文中一步一步地解释让这种困惑消除了不少。
LSTM 是我们在 RNN 中获得的重要成功。 很自然地,我们也会考虑:哪里会有更加重大的突破呢?在研究人员间普遍的观点是:“Yes! 下一步已经有了——那就是 注意力 !” 这个想法是让 RNN 的每一步都从更加大的信息集中挑选信息。 例如,如果你使用 RNN 来产生一个图片的描述,可能会选择图片的一个部分,根据这部分信息来产生输出的词。 实际上, Xu,et al. (2015) 已经这么做了——如果你希望深入探索 注意力 可能这就是一个有趣的起点!还有一些使用注意力的相当振奋人心的研究成果,看起来有更多的东西亟待探索…… 注意力也不是 RNN 研究领域中唯一的发展方向。 例如, Kalchbrenner,et al.(2015)提出的 Grid LSTM 看起来也是很有前途。 使用生成模型的 RNN,诸如 Gregor,et al.(2015) Chung,et al.(2015)和Bayer & Osendorfer (2015)提出的模型同样很有趣。 在过去几年中,RNN 的研究已经相当的燃,而研究成果当然也会更加丰富!
I’m grateful to a number of people for helping me better understand LSTMs, commenting on the visualizations, and providing feedback on this post. I’m very grateful to my colleagues at Google for their helpful feedback, especiallyOriol Vinyals , Greg Corrado ,Jon Shlens ,Luke Vilnis , andIlya Sutskever . I’m also thankful to many other friends and colleagues for taking the time to help me, includingDario Amodei , andJacob Steinhardt . I’m especially thankful toKyunghyun Chofor extremely thoughtful correspondence about my diagrams. Before this post, I practiced explaining LSTMs during two seminar series I taught on neural networks. Thanks to everyone who participated in those for their patience with me, and for their feedback.
五年后的今天,训练GPT-2只需不到700刀、24小时,Karpathy又整新活
五年光阴荏苒,昔日OpenAI发布的GPT-2大模型的训练成本如今已大幅缩水。 在2019年的那个时刻,15亿参数的模型训练需要耗费高昂的资源,如今只需672美元,仅用一个8XH100的GPU节点运行24小时就能完成。 OpenAI的科学家Andrej Karpathy在他的C语言项目llm.c中分享了这一惊人的转变,得益于计算硬件、软件和数据集质量的飞跃提升。 Karpathy回忆起,五年前训练GPT-2的成本可能高达10万美元,而如今只需花费当时的百分之一。 这种效率的巨大提升,充分体现了AI领域和计算基础设施的迅猛发展。 他的项目llm.c简洁易用,无需复杂的环境设置,只需云GPU节点,安装必要库,下载数据分片,几分钟内即可启动训练。 尽管Karpathy在项目开发过程中遇到过挑战,从Python转向C/CUDA,但这个过程使他收获良多,对CUDA有了更深的理解。 llm.c现已成为一个功能齐全的项目,支持较小规模的训练,内存占用稳定,且采用混合精度和多节点训练。 Karpathy透露,未来他将探索fp8、推理、微调等新领域,目标是提供更现代化的训练栈。 Karpathy用400B token的GPT-2运行展示了训练成果,尽管在330B步后出现不稳定,但整体表现优于相同规模的GPT-2和GPT-3。 然而,他认识到在模型初始化、激活范围等方面还有改进空间,尤其是在更大规模和长时间训练时。 他的llm.c项目不仅是一个训练工具,也是一个学习平台,旨在简化通用大语言模型的训练过程。 有兴趣的读者可以通过GitHub获取项目详情和训练指南,而Karpathy对技术的热情和对教育的投入,将这个领域的进步清晰地呈现给大众。
本地运行“李开复”的零一万物 34B 大模型
本地运行“李开复”的零一万物 34B 大模型,本文将指导你如何实现这一目标。 首先,要了解零一万物的大模型争议颇多,针对模型、代码、团队乃至李开复本人都有不同的声音。 然而,对于开发者和终端用户来说,最实际的问题是:这个 34B 模型是否可行,特别是能否在本地机器上运行并深入了解其性能。 在众多争议中,关于模型本地运行的问题引起了我的兴趣。 在多数负面反馈中,提及本地运行34B模型有难度。 然而,实际上,通过使用流行的模型量化方案,模型尺寸可以从70GB缩减到24GB,基本能够满足本地运行的需求。 但如果未采取优化方案,模型应用很容易因内存不足而崩溃。 为解决此问题,让我们探讨一种可行的方案:使用 CPU 和 GPU 共同运行模型。 gggerganov/ 是一款开源软件,非常适合此目的。 它几乎伴随着模型的成长、爆火、出圈,且之前主打使用纯 CPU 进行模型推理,对没有 GPU 的电脑特别友好。 然而,对于更大尺寸的模型,仅使用 CPU 推理无疑会挑战用户耐心,且可能浪费电。 幸运的是,gguf 模型格式和 对 offloading 模型层到 GPU 的功能日渐完善,使得 CPU 推理模型的同时,模型的一部分装到高计算性能的 GPU 中,大大提升了用户体验。 以下是实施步骤:1. 安装 Docker,不论设备是否配备显卡,均可根据操作系统喜好完成基础环境配置。 2. 使用 Nvidia 提供的深度学习环境 /nvidia/pytorch:23.10-py3 作为基础镜像,其中的 CUDA 版本通常效率高于开源社区版本。 3. 保存 Dockerfile,构建镜像。 4. 进入容器环境,使用nvidia-smi 查看显卡是否正确使用。 5. 下载零一万物的官方模型文件,选择合适的模型文件,如 Q4 或 Q5 的量化模型,这些模型可适应不同大小的显存。 6. 编译使用 GPU 的 ,启动服务进行模型运行。 7. 使用 server 程序运行模型,调用模型并输入处理内容。 使用机器之心发布的关于 OpenAI 的文章作为测试内容,如“OpenAI内斗时,Karpathy在录视频:《大型语言模型入门》上线”。 模型处理速度快,结果令人满意。 8. 通过调整 加载模型层数,以优化性能,如将模型的 55 层放在显卡中,减少程序在大模型程序和数据中查找答案的时间。 9. 选择更小巧的模型,以进一步提升吞吐速度,例如 Q4 版本的量化模型,设置--ctx-size 到 200K,适用于多张卡或大显存资源。 10. 测试模型在不同格式下的输出,如 JSON 格式,以验证模型的灵活性和实用性。 通过以上步骤,你可以在本地机器上运行零一万物的 34B 大模型,并对其性能有深入的了解。 本文详细介绍了实施过程,包括环境配置、模型下载、编译和运行等关键步骤,旨在帮助开发者和终端用户成功实现本地运行目标。