查看原文
其他

【老万】从0开始学chatGPT(五):模型眼里的单词

老万 老万故事会
2024-08-23

本文是我的 chatGPT 学习心得《从0开始学chatGPT》系列第五篇。欢迎依次阅读:


~~~~

在这个系列里我们讲过,可以把 GPT 之类以文本生成为目的的大语言模型看成是高阶的马尔可夫链模型,在给定上文的情况下,模型可以根据它学过的巨量语料给出下一个单词或记号的概率分布,让 chatGPT 从候选词中选一个概率高的,然后把选中的词加入上文,根据新的上文继续往下猜,直到回答结束。也就是说 chatGPT 跟人对话全凭经验和概率。

当然,实际的 chatGPT 没那么简单,以上说法只是一种非常粗糙的近似。要进一步了解 chatGPT 的工作原理和它的边界,在使用中扬长避短,我们还需要深化自己的理解。今天我们就来抠一抠它的细节。

我们知道,为了提高效率和效果,大语言模型通常并不是以单词为单位处理文字,而是把单词拆解成一个一个的记号(token),以记号为单位处理。一个记号大概相当于3/4个英文单词或者半个汉字。要真正理解 GPT 的原理,我们就要知道这些记号在大模型里是怎样表示的,又是怎么样被处理的。

我们不妨想象有一张记号表,列出了模型能理解的所有记号。要是这张表上面有 10 万个记号,我们就可以用从 1 到 10 万的整数来表示这些记号。这大概是码工最容易想到的做法。(为了便于描述和理解,我们有时会混用“记号”和“单词”的概念。在下文中看见“单词”时,请自行替换为“记号”。)

然而,我们的大语言模型是用神经网络来实现的,它能做的计算和普通 CPU 能做的计算有很大区别,这种编码方式并不适用。要找到合适的编码方式,我们先得回顾一下神经网络是怎样做计算的。

~~~~

神经网络(neural network)是一种受到人脑神经元结构启发的计算模型。它的基本单元叫做神经元(neuron),它们负责接收输入信号(一组实数)、处理信号并产生输出信号(一个实数)。这些神经元通常被称为节点。神经元之间通过连接单向传递信息。这些连接模拟了人脑中神经元之间的突触连接。

和人脑不同,人工神经网络中的神经元连接通常是有向无环的,而且一般采取分层架构:每一层神经元的输出只接到下面某层神经元的输入,井井有条,不像人脑的神经元连接那么任性。

连接的强度用权重表示。权重可以理解为连接的"重要性",它决定了神经元之间信号传递的强度。权重可以是 0、正数或负数,0 相当于没有连接,正数表示两个神经元之间的关系是正相关的,负数表示关系是负相关的。在训练过程中,权重会根据模型的预期输出和实际输出不断调整,以达到最佳性能。我们也把这些权重叫做神经网络的参数。比如 GPT-3.5 有 1750 亿个参数(也就说有这么多连接)。

下图是一个简单的两层神经网络。它的第一层有三个神经元 m1,m2,和 m3。第二层有两个神经元 n1 和 n2。m1 只连到 n1,连接权重为 0.2。m2 和 m3 每个都连到第二层的全部神经元,其中 m2 到 n2 的连接权重是负数。

神经元接收到来自其他神经元的输入信号后,会将这些信号与对应的权重相乘并相加然后,神经元会通过一个激活函数(activation function)处理这个加权和。这个激活函数可以有很多选择,但通常我们会选一个非线性函数(nonlinear function)。非线性的激活函数可以引入更复杂的映射关系,使神经网络能够学习和逼近非线性问题。常用的激活函数有ReLU、Sigmoid 和 Tanh 等。其中 ReLU(Rectified Linear Unit)的计算最简单,在深度学习中应用广泛。

ReLU 函数的名字听着挺吓人,其实非常简单。它就是把所有负的输入变成 0,其它输入不变:

ReLU 函数的定义

ReLU 函数的图形


Sigmoid 是值在(0,1)区间的 S 曲线。

Tanh 和 Sigmoid 很像,但是值在(-1,1)区间。

我们来看一个具体的例子。如果下面这个神经网络每个节点都用 ReLU 做激活函数,它的第二层的输出应该是什么?

先看 n1。它的输入来自 m1、m2 和 m3。m1 的贡献是 1.00*0.2,m2 贡献了 0.50*0.5,m3 的贡献是 1.20*0.4,所以 n1 得到的全部输入是 1.00*0.2 + 0.50*0.5 + 1.20*0.4 = 0.93,经 ReLU 函数处理后仍然是 0.93,所以它的输出是 0.93。

再看 n2。它的输入加权和是 0.50*(-0.3) + 1.20*0.1 = -0.03,ReLU 函数会把负数变成 0,所以它的输出是 0。

恭喜你!至此你已经掌握了神经网络的基本工作原理,可以做更深入的学习了。

~~~~

在机器学习的时候,每一个参数或者说神经元连接的强度都会被不断调整,向理想值靠近,但永远达不到一个完全正确的答案。同时,神经网络的输入也经常包含一定的误差。比如自然语言处理系统的输入可能有拼写错误,图像识别系统的输入可能有阴影和噪点。

如果我们不想让神经网络发神经,系统必须有一定的容错性,允许适当的误差。当神经元参数或输入发生微小变化的时候,神经网络输出结果不应该发生突变。

假设一个神经网络的第一层输入是向量 v,网络中全部连接的权重排成一个巨长的向量 w,最后一层输出是向量 u,我们可以把 u 看成是 v 和 w 的一个函数:u = N(v, w)。为了保证容错性,N(v, w) 应该是一个连续函数:要是 v 和 w 差之毫厘,u 不应该谬以千里。

那么我们上面说到的神经网络符合这一要求吗?

我们看:加权求和是连续函数,只要我们选取的激活函数也是连续函数,它们组合在一起还是连续函数。而常用的激活函数(ReLU、Sigmoid 还有 Tanh)都符合这一要求。

也就是说,如果不考虑浮点运算的舍入误差,只要输入值和连接权重变化足够小,输出值的变化可以任意小

神经网络的容错性是一柄双刃剑,它同时也限制了神经网络的计算能力。比如,if C then A else B 这样的条件逻辑和 x = a[i](i 是一个变量)这样的基本操作在神经网络上都很难进行,因为它们可能导致不连续的函数。

如果我们给不同的记号任意指定一个不同的整数,而这个数值本身没有语法或语义上的意义,势必需要条件逻辑或数组下标访问才能完成比较高级的自然语言处理任务。而我们已经发现这些操作对神经网络来说是勉为其难的。

所以,我们必须寻找有语法和语义意义的记号表示方式。

~~~~

因为我们的语言模型目的是处理人类的自然语言,有一个很自然的要求就是语义上面相近的、可以互相替换的记号应该在编码空间里靠得很近。这样,即使运算有误差,我们也只是从一个答案偏离到一个很接近的答案,无伤大雅。一个理想的编码方案,应该做到两个记号意思越接近,它们的编码也越接近,反之亦然。这样,我们就可以通过一个简单的计算(求两个编码的距离)来算出两个记号有多近似,在生成文字时得到最佳选择。

推而广之,我们也可以给每段文字指定一个编码。要是方案选得好,一样可以靠比较两段文字的编码的近似度来得到它们意思的近似度。

如果我们只用一个实数来表达一个记号,就相当于把这些记号映射到一个一维空间(直线)上面。这么多不同意思的记号在一维空间上是排不开的,无法做到意思越近距离越近。

举个例子。我们在孩童时代就形成了男人和女人的概念。后来我们大一点了,知道没有绝对男女之分。男人中有张飞岳飞一样的猛男,也有伪娘和太监。女人中有侍儿扶起娇无力的杨玉环,也有顾大嫂一样的女汉子。还有一些非二元性别者,很难用传统观念认定是男人还是女人。所以说男女是一个维度,而不是一个二元概念。

我们可以按“性别”属性给每个关于“人”的词汇指定一个实数值,越大越男性化,越小越女性化。

为了增强模型的表达能力,我们不妨给它加入另外一个维度,比如说社会地位的高低。这样我们就把单词的编码从一维变成二维,每个单词对应于二维空间的一个向量。在这个系统里面,我们可以表达更为复杂的概念,除了男人女人,还有贵人贱人。这两个维度可以任意组合,比如贵男贵女、贱男贱女。

这种用二维向量表示单词的方式有一个突出的优点:距离近的单词意思也近。比如,在“岳飞武艺过人,治军严明”这段文字中,如果我们把“岳飞”换成离它最近的“张飞”也能说得过去,换成隔得最远的“杨玉环”就不伦不类。

更有意思的是,我们还可以对这些单词向量做一些有意义的运算。比如在上图中我们分别用向量(1,0)表示“男”,(1,1)表示“皇帝”,(-1,0)表示“女”,(-1,1)表示“皇后”。大家可以验算一下:皇帝 - 男 + 女 = 皇后 这个式子竟然是成立的!奇妙不奇妙?惊喜不惊喜?

做过 GRE 考试类比题的同学可能已经想到了:用这种单词向量的加减法来做类比题手到擒来。比如老师提问:张飞对岳飞相当于杨玉环对___?我们只需找出对应于这三个单词的向量,再求解方程 张飞 - 岳飞 = 杨玉环 - x,就能发现答案“赵飞燕”,妙哉。 

当然两个维度是远远不足以表达对人群的划分的。除了男女贵贱,还会有好人坏人,高个矮个,老人小孩,......的区别,所以还需要引入第三、四、五、......个维度。实际上在GPT里面,把一个记号作为一个 N 维浮点数向量来表示。这种用一个向量来表示一个单词或记号的方法按神经网络的术语就叫做嵌入(embedding )。此外,我们也把一个单词对应的向量叫这个单词的嵌入向量。

那么有多少个维度才够处理自然语言呢?除了人群的划分我们还需要表达很多别的概念,所以维数低了不行。据 GPT-4 爆料,GPT-2 的嵌入向量用了 768 维,GPT-3 是 1600 维,GPT-3.5 2048 维。至于 GPT-4,它自己不肯说。

当维度足够的时候,我们所有的单词就可以排得开了,真正做到字以类聚,词以群分,意思接近的词,它们的嵌入向量距离也近。这种表示方式大大提高了自然语言处理的效率和效果。

~~~~

有了单词的嵌入向量之后,很多任务都要求我们计算两个嵌入向量的距离。

看两个向量有多接近可以有不同的算法。如果这些向量都已经被归一化(normalize)成长度为 1 了(GPT 的嵌入向量就是这样),最简单的做法就是看它们的夹角有多大。

如果两个向量完全重合,夹角 θ 为0,cos(θ) = 1。如果意思完全相反(比如“男”和“女”),θ 为 π,cos(θ) = -1。如果意思有一定的正相关,θ 就是锐角,0 < cos(θ) < 1。如果意思既没有正相关也没有负相关(比如“男”和“勤快”),θ 就是 π/2,cos(θ) = 0。这个规律对任意维数的向量都是适用的。

cos(θ) 又叫这两个向量的余弦相似度(cosine similarity),它很好地表示了两个单词的相关性。

在解析几何课上我们学过,两个向量 p 和 q 的余弦相似度 

其中 p ∙ q 是向量 p = (p1, p2, ..., pn) 和 q = (q1, q2, ..., qn) 的点积(dot product)

|p| 和 |q| 分别是 p 和 q 的长度。因为 GPT 已经把嵌入向量归一化了,它们的长度都是 1,所以计算可以简化为:

有趣的是,当向量长度都是 1 的时候,用余弦相似度排序和用欧几里得距离(|p - q|)排序结果是一样的,而后者计算麻烦得多:

可见余弦相似度是一种很好的计算嵌入向量相似度的方法。

~~~~

一些介绍“嵌入”概念的文章里说嵌入向量是一个低维向量。看到这里,我们不禁有疑问:这些向量明明有几百几千维,为什么还说它们是低维的呢?

这是相对于另一种在神经网络里面把单词编码成向量的方式“一阳码”(one-hot encoding)而言的。一阳码非常简单:单词表有多少个单词,向量就有多少维,这些维度里面除了一个是 1,其它都是 0。第几维是 1 就代表这是单词表上面的第几个单词。

比如我们定义一个十个词的单词表:

那么这些词的“一阳码”分别是:

你:(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)我:(0, 1, 0, 0, 0, 0, 0, 0, 0, 0)他:(0, 0, 1, 0, 0, 0, 0, 0, 0, 0)......

这里,任何两个单词的内积都是0,也就是说它们的夹角都是90度。很显然这种方式不能表达单词之间的相似性。不是一种很好的编码方式。而且这种编码方式维度巨高,十万单词需要十万维,相应地需要很多神经元和连接,造成模型尺寸巨幅增长,非常不实际。所以,大模型通常会用一阳码做第一层的输入,用一层神经网络把它转化为嵌入向量,再做进一步处理。这样一举两得:既降了维,又找到了语义上的相似性。

一阳指遭到“降维”打击!好吧,这是个烂哏。

如果我们的单词表有 10 万单词,嵌入向量有 768 维,神经网络的输入是 1024 个单词,我们可以这样设计神经网络输入部分的架构:

  • 第一层有 1024 组神经元。每组 100,000 个,代表一个输入单词的一阳码。

  • 第二层也有 1024 组神经元。但每组只有 768 个,代表一个输入单词的嵌入向量。

  • 第一层的第 i 组神经元和第二层的第 i 组神经元之间是全连接(100,000*768 个连接)。除此之外两层之间没有连接。

从第一层到第二层的变换可以看成是一个 100,000维的稀疏向量(只有一维不是 0)乘以一个 100,000x768 的矩阵。这个矩阵叫嵌入矩阵(embedding matrix),因为它的第 j 行就是单词表中第 j 个单词的嵌入向量。

嵌入矩阵是怎么得来的呢?自然也不是从天上掉下来的,而是通过学习得来的。GPT 在训练的时候,先随机初始化这个矩阵的元素,然后再通过反向传播学习模型的所有参数,包括嵌入矩阵的参数。

看到这里,聪明的同学可能已经有了疑问:我们给每个单词赋予一个嵌入向量。那么多义词怎么办?比如:我一把把把把住,因为我想过过过儿过过的生活。看到这些,GPT 是不是就抓瞎了?

不急。欲知 GPT 如何拆招,I'll be back。

参考资料:

  • ReLU 和其它激活函数的比较:https://builtin.com/machine-learning/relu-activation-function

  • openAI 的嵌入 API:https://platform.openai.com/docs/guides/embeddings/embeddings

  • chatGPT 原理:https://writings.stephenwolfram.com/2023/02/what-is-chatgpt-doing-and-why-does-it-work/


~~~~~~~~~~

猜你会喜欢:

~~~~~~~~~~

关注老万故事会公众号:

码字不易,呕心沥血只是希望更多人看到。如果喜欢这篇文章,请不吝订阅、转发、评论。谢谢!🙏

继续滑动看下一个
老万故事会
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存