diff --git a/人人都能看懂的Transformer/第一章——Transformer网络架构.md b/人人都能看懂的Transformer/第一章——Transformer网络架构.md index 0b912a3..1cc5a3a 100644 --- a/人人都能看懂的Transformer/第一章——Transformer网络架构.md +++ b/人人都能看懂的Transformer/第一章——Transformer网络架构.md @@ -140,7 +140,7 @@ Add & Norm的过程可以理解为相同位置元素相加,再做层归一化 > Linear -image-20240503172310152 +image-20240503172310152 前面数据经过最后一次缩放后,线形变换用于前者的输出,映射到一个词汇表大小的向量上,并选举出最大可能性的词或句子作为最终输出 @@ -167,7 +167,7 @@ Add & Norm的过程可以理解为相同位置元素相加,再做层归一化 > WHY:转化到0-1区间,便于比较(同维度)和处理。将注意力分数转换为概率分布。 -image-20240503172341945 +image-20240503172341945 可以简单理解为,将前面线形层输出的值,转化成0-1的概率分布区间进行输出。 diff --git a/人人都能看懂的Transformer/第七章——前馈神经网络.md b/人人都能看懂的Transformer/第七章——前馈神经网络.md index 48c129e..55d32e4 100644 --- a/人人都能看懂的Transformer/第七章——前馈神经网络.md +++ b/人人都能看懂的Transformer/第七章——前馈神经网络.md @@ -1,6 +1,6 @@ # 第七章——前馈神经网络 -前馈神经网络 +前馈神经网络 ### 前言 diff --git a/人人都能看懂的Transformer/第三章——位置编码.md b/人人都能看懂的Transformer/第三章——位置编码.md index 9170925..94253ae 100644 --- a/人人都能看懂的Transformer/第三章——位置编码.md +++ b/人人都能看懂的Transformer/第三章——位置编码.md @@ -1,6 +1,6 @@ # 第三章——位置编码 -文字向量化 +文字向量化 ### 前言 @@ -77,7 +77,7 @@ tensor([[ 0.0000e+00, 0.0000e+00, 0.0000e+00, ..., 0.0000e+00, """ ~~~ -image-20240427180449855 +image-20240427180449855 > 我们上面参数也参考GPT-2的,比如max_seq_len=1024、d_model=768 @@ -97,7 +97,7 @@ tensor([-1.0000, 0.0044, -0.9673, -0.2535, -0.8724, -0.4889, -0.7253, -0.6884, """ ~~~ -image-20240427180954246 +image-20240427180954246 为什么是用正弦和余弦函数,对于正弦函(sin):最大值是 +1,最小值是 -1。对于余弦函数(cos):最大值是 +1,最小值是 -1。也就是它们可以保证值是比较小的,而且也是符合深度学习模型可学习的参数。 @@ -117,7 +117,7 @@ We chose this function because we hypothesized it would allow the model to easil [可视化地址](https://erdem.pl/2021/05/understanding-positional-encoding-in-transformers#positional-encoding-visualization)你可以看到4个相对靠近的值,但是因为在不同的位置,值相差的完全不一样,也就是由正弦和余弦函数造的数据,在每个位置的值都是唯一的! -image-20240430152239833 +image-20240430152239833 @@ -182,9 +182,9 @@ tf.Tensor( """ ~~~ -image-20240427181354113 +image-20240427181354113 -image-20240427181415430 +image-20240427181415430 可以看到GPT-2就是直接用模型训练的方法去迭代位置编码的参数。可以直接去看[源码](https://github.com/openai/gpt-2/blob/9b63575ef42771a015060c964af2c3da4cf7c8ab/src/model.py)。两者的优点前面也已经说了,这也是为什么大家都称GPT为“暴力美学”。当然不仅是这里,后面还有“暴力美学”的相关佐证。 @@ -232,7 +232,7 @@ torch.Size([1, 4, 768]) """ ~~~ -image-20240427182734627 +image-20240427182734627 从代码来看,embedding跟position_embeddings就是同位置元素相加。以下面的方法来检验 @@ -252,7 +252,7 @@ tensor(-0.1923, grad_fn=) """ ~~~ -image-20240427183034655 +image-20240427183034655 diff --git a/人人都能看懂的Transformer/第二章——文字向量化.md b/人人都能看懂的Transformer/第二章——文字向量化.md index 719c371..41a0ef1 100644 --- a/人人都能看懂的Transformer/第二章——文字向量化.md +++ b/人人都能看懂的Transformer/第二章——文字向量化.md @@ -1,6 +1,6 @@ # 第二章——文字向量化 -文字向量化 +文字向量化 ### 前言 @@ -28,7 +28,7 @@ print(inputs) """ ~~~ -image-20240425205340177 +image-20240425205340177 我们可以看到"LLM with me"明明是3个词,但是输出的有4个索引。我们拆开文本来看 @@ -46,7 +46,7 @@ print(tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])) """ ~~~ -image-20240425205432896 +image-20240425205432896 通过上面的代码,我们发现"LLM"文本被分成了"LL"和"M"两个词。 @@ -67,7 +67,7 @@ The vocabulary size of GPT2Tokenizer is: 50257 """ ~~~ -image-20240426165150933 +image-20240426165150933 ### 索引向量化 @@ -102,7 +102,7 @@ torch.Size([1, 4, 768]) """ ~~~ -image-20240426174122893 +image-20240426174122893 可以看到最终维度是转成了768列,4行。也就对应着4个索引,和GPT2的嵌入向量矩阵的维度768。 @@ -139,7 +139,7 @@ print(output.tokens) """ ~~~ -image-20240427110733995 +image-20240427110733995 > 作为结束语,后面会用到大家先不用管。这是是比较简单的,还有开头语和换行等,我们这里只是用于演示和流程。 @@ -165,7 +165,7 @@ tensor([[14, 15, 12]]) """ ~~~ -image-20240427111019015 +image-20240427111019015 得到的索引是[14, 15, 12],那0-11又分别是什么呢? @@ -178,7 +178,7 @@ print(vocab) """ ~~~ -image-20240427111107647 +image-20240427111107647 可以看到Tokenizer会讲输入的"LLM with me"进行逐个拆分,然后每个占一个索引空间。想要对BPE算法有更深入了解的,可以网上搜索下相关算法。简单来说,一种数据压缩技术,后来被应用于NLP中作为一种子词分割方法,通过迭代合并最频繁的字节对来减少词汇量,同时有效处理未知或罕见词汇。 @@ -218,7 +218,7 @@ torch.Size([1, 3, 768]) """ ~~~ -image-20240427111216448 +image-20240427111216448 > 注意,每次embedding的值不一定是完全一样的,因为每次初始化的权重值都是不一样的,这也是深度学习的一大特点。 @@ -257,7 +257,7 @@ torch.Size([3, 10]) """ ~~~ -image-20240427111332242 +image-20240427111332242 一开始,会用生成正态分布的随机值,并且是指定的维度的,如我们上面的10维。当索引进来使用的时候,就会去获取对应的向量,比如13、14、11这三个位置的 @@ -275,7 +275,7 @@ tensor([-0.0288, -0.2724, -0.3007, 0.4584, -0.1438, 0.3389, -0.0454, -0.4382, """ ~~~ -image-20240427111510914 +image-20240427111510914 > 'LLM': 14, 'wit': 13, 'me': 12。14是对应LLM,可以看到14的值是对应上的。 @@ -354,9 +354,9 @@ Epoch 100, Loss: 1.8669122457504272 """ ~~~ -image-20240427111731974 +image-20240427111731974 -image-20240427111825765 +image-20240427111825765 ~~~python token_indices = input_ids[0] @@ -376,7 +376,7 @@ torch.Size([4, 10]) """ ~~~ -image-20240427111912696 +image-20240427111912696 如果我们要训练一个完全自己的大模型,模型结构需要更复杂,另外最重要的就是input_ids跟labels的数据量一定要足够多。前面我们已经演示如何训练简易的GPT,并训练好了,那怎么用起来呢? @@ -410,7 +410,7 @@ LLM with me A:[UNK]iLLwitMimeLMethihitMehw """ ~~~ -image-20240427112856781 +image-20240427112856781 我们设置了temperature=0.7,让模型生成更开放的答案,当你改成0.1的时候你会发现每次生成的结果会变得非常确定,甚至每次生成的完全一样。 diff --git a/人人都能看懂的Transformer/第五章——多头注意力机制——全流程.md b/人人都能看懂的Transformer/第五章——多头注意力机制——全流程.md index 7b96210..665198a 100644 --- a/人人都能看懂的Transformer/第五章——多头注意力机制——全流程.md +++ b/人人都能看懂的Transformer/第五章——多头注意力机制——全流程.md @@ -1,6 +1,6 @@ # 第五章——多头注意力机制——全流程 -image-20240502141958851 +image-20240502141958851 ### 前言 @@ -14,7 +14,7 @@ $$ \operatorname{Attention}(Q, K, V)=\operatorname{softmax}\left(\frac{Q K^{T}}{\sqrt{d_{k}}}\right) V $$ -image-20240502140356134 +image-20240502140356134 我们单独拿1个批次的第一个头出来 @@ -22,7 +22,7 @@ $$ 第一行的所有数据,分别上`LL`分别跟`LLM with me.郭同学热爱AI喜欢游戏`每个词的相关性。第二行则是`M`分别跟`LLM with me.郭同学热爱AI喜欢游戏`每个词的相关性。越高则代表两个字的相关性越高,越低则代表两个字的相关性越低。 -image-20240502141342857 +image-20240502141342857 @@ -32,7 +32,7 @@ $$ 也就是下面这个表的每一个值都会除以64,相当于值会进行缩小。 -image-20240502142816044 +image-20240502142816044 @@ -42,7 +42,7 @@ $$ 将矩阵的上三角部分(包括对角线)填充为 `-inf`或者0。 -image-20240502150743024 +image-20240502150743024 训练的过程中(语言模型场景),是从前往后训练,然后去预测后面的词(句子),所以训练过程中,我们要遮挡一部分,让模型不知道后面的信息,自动去生成然后校验生成与实际之间的差距。 @@ -52,7 +52,7 @@ $$ 将上面的值转成0到1直接的值,即百分比的概率分布。它会放大分数较高的元素,并抑制分数较低的元素。在注意力机制中,这意味着模型可以更加集中地关注那些与当前查询(Query)最相关的键(Key),从而获取对应的值(Value)。 -image-20240502212834597 +image-20240502212834597 Softmax函数的公式如下: $$ @@ -60,7 +60,7 @@ $$ $$ 这里就不做详解,你就简单理解为:它可以将一批值,转换为一个概率分布,其中每个元素的值介于0和1之间,并且所有元素的和为1。 -image-20240502214058704 +image-20240502214058704 可以看到`LL`对全部文本信息(包括它本身)的概率总和是1,`M`对全部文本信息的概率总和也是1。以此类推,此时整个矩阵的形状是没有变的。 @@ -80,7 +80,7 @@ $$ 前面我们QK矩阵相乘,并经过了Scale、Mask和Softmax,接下来就要跟V去做矩阵相乘 -image-20240502223139032 +image-20240502223139032 如上图所示,V跟一开始的QK一样,都是[4, 12, 16, 64]即64维,前面的矩阵相乘我们知道,只要第一个矩阵的第二个维度值 跟 第二个矩阵的第一个维度值一样,它们就能相乘。即[16, 16]跟[16, 64]是可以矩阵相乘的,得出的矩阵A。 @@ -90,7 +90,7 @@ A就代表着我们这个Attention关注度计算。 ### QKV机制的数学逻辑 -image-20240502223231828 +image-20240502223231828 我们先看Q@K的结果,里面是每一个token(文字的信息)对应其它文字token的权重关系且是一个百分比的形式。V是原始形状[16, 64],每行就是每个文字随机初始化的数字矩阵(如果是推理截断,这些值则是训练好的),即每个token对应的64维的向量数据,64维。两者矩阵相乘是矩阵A。 @@ -113,13 +113,13 @@ print("\nProduct Matrix (3x6):") print(product_matrix) ~~~ -image-20240502222706741 +image-20240502222706741 可以看到,A1的结果是QK矩阵的第一行跟V矩阵的第一列相乘的结果。而第一行是QK最终通过softmax输出的`LL`对全部文本信息的概率总和,而V矩阵的第一列,则是全部文字向量化后,在64维里的第一个维度的数值。即`LL`的概率总和(向量) 与 `LLM with me.郭同学热爱AI喜欢游戏`的一维向量 相乘。等于`LL`在这个维度里(第一维),对所有文本(包括它本身)的关注度的总权重(权重总和)。 当然,我们的V还有第二维,如下所示 -image-20240502224309316 +image-20240502224309316 A矩阵里的第一行第二列的值,则是QK矩阵第一行跟V的第二列 相乘的结果,也就是`LL`对于在第二维,对所有文本的总权重。后续的值则以此类推。 @@ -129,7 +129,7 @@ A矩阵里的第一行第二列的值,则是QK矩阵第一行跟V的第二列 ### 注意力输出结果 -image-20240503135051251 +image-20240503135051251 前面我们的矩阵A已经算好了,矩阵A经过Concat合并成一开始的[4, 16, 768]的形状,即12个头都合并到一起。再结果一次W的权重矩阵相乘,最终输出[4, 16, 768]维度的矩阵。 diff --git a/人人都能看懂的Transformer/第八章——最后的输出.md b/人人都能看懂的Transformer/第八章——最后的输出.md index 9b25aa2..1fd30d5 100644 --- a/人人都能看懂的Transformer/第八章——最后的输出.md +++ b/人人都能看懂的Transformer/第八章——最后的输出.md @@ -1,6 +1,6 @@ # 第八章——最后的输出 -image-20240503172501503 +image-20240503172501503 ### 前言 @@ -58,7 +58,7 @@ Output Y: [-2.59709604 -0.78316274 -4.6765379 3.25016417] """ ~~~ -image-20240503181100601 +image-20240503181100601 可以看到线形层的目标,就是把高维降低到低维,并且跟总词汇量是一致的。同时你也会发现,输出的并不是概率(当然值也不一定有那么多大的幅度),维度不统一就无法比较,所以我们还需要用Softmax去转成概率。 diff --git a/人人都能看懂的Transformer/第六章——数值缩放.md b/人人都能看懂的Transformer/第六章——数值缩放.md index e40ad76..7ded5af 100644 --- a/人人都能看懂的Transformer/第六章——数值缩放.md +++ b/人人都能看懂的Transformer/第六章——数值缩放.md @@ -1,6 +1,6 @@ # 第六章——数值缩放 -数值缩放 +数值缩放 ### 前言 @@ -36,7 +36,7 @@ print("\nResidual Output (Input + x):") print(residual_output) ~~~ -image-20240503154451656 +image-20240503154451656 可以看到结果是同个位置的元素相加。 diff --git a/人人都能看懂的Transformer/第四章——多头注意力机制——QK矩阵相乘.md b/人人都能看懂的Transformer/第四章——多头注意力机制——QK矩阵相乘.md index b90b4ff..c255d22 100644 --- a/人人都能看懂的Transformer/第四章——多头注意力机制——QK矩阵相乘.md +++ b/人人都能看懂的Transformer/第四章——多头注意力机制——QK矩阵相乘.md @@ -1,6 +1,6 @@ # 第四章——多头注意力机制——QK矩阵相乘 -语义关系学习 +语义关系学习 ### 前言 @@ -8,7 +8,7 @@ 放大语义关系学习(注意力机制)内部 -image-20240501143058994 +image-20240501143058994 > Wq/Wk/Wv(Linear):线性层。数学表达式是 `y = wx + b`,其中 `x` 是输入向量,`W`是权重矩阵,`b` 是偏置向量,`y` 是输出向量。 > @@ -28,13 +28,13 @@ 一个值是标量(Scalar),一组值是向量(Vector),多组值是矩阵(Matrix) -Scalar, Vector, Matrix +Scalar, Vector, Matrix 矩阵也就是多维的向量,矩阵是可以多种维度的,如3列2行(上面图的),亦或者2行3列。 矩阵相乘(又叫点积相乘)如下: -矩阵乘法 +矩阵乘法 ~~~markdown "点积" 是把 对称的元素相乘,然后把结果加起来: @@ -45,7 +45,7 @@ 这里顺便补充下, 我们平时说的线形变换,其实就是一种特殊的矩阵相乘,即,矩阵乘以一个向量 -L15.png +L15.png 最终输出的是[3,1]的矩阵,即一个向量`[16 4 7]`。从上面我们也知道,只要[3,2]里的这个2,能够对应上另外一个矩阵的行,就能够相乘。即[3,2]对应上面的[2,1],2跟2对应。即第一个矩阵的第二个数 能跟 第二个矩阵的第一个数对应上,就能相乘。 @@ -55,7 +55,7 @@ 假设我们有两个矩阵:A [1 2] 和 B [3 3]两个矩阵,画到象限表,如下图 -image-20240430191643501 +image-20240430191643501 我们说A和B相似,如何判断相似,就看它们离的近不近,或者两个向量的夹角a比较小。并且我们肉眼看,A和C离的相对更远。 @@ -71,7 +71,7 @@ A矩阵*B矩阵=B长度*A长度*cos(\theta) $$ 我们做一个浅绿色的垂线,它就变成一个直角三角形。在数学三角函数中,cos的邻边等于cos(θ)乘以斜边。也就是A的长度乘以cos(θ),等于黑色的线(B上的黑色线) -image-20240430191813984 +image-20240430191813984 也就是公式等同于,也就是红色乘以黑色的部分 $$ @@ -81,19 +81,19 @@ $$ 如果是C做垂线B,可能就是负数了。如果是三维平面或者四维屏幕,则是如下增加多条线 -image-20240430192046147 +image-20240430192046147 现在我们知道矩阵相乘能代表相似度的高低,回到实际中,过程图如下 -image-20240430194452839 +image-20240430194452839 上面我放的文字,实际传给机器的时候是数值。 -image-20240430194746805 +image-20240430194746805 通过矩阵相乘,即`LLM`和`me`的相似度是23,最终它们都会被投射到多维平面上。 -image-20240430194034870 +image-20240430194034870 当然时间向量的值一般是[-1,+1]区间的,而不是整数型,这里是一个简单的示例。而且还会经过不断的训练循环,来不断的调整每个文本的多维表达数值分别是多少,也就是LLM初始值假设是[1,2,3],可能训练的下一轮是[-1,3,1]下一轮又是[3,1,2],直到最终训练结束。 @@ -119,19 +119,19 @@ $$ 比如现在我们有4句话,同时我们复用GPT-2的768维向量 -image-20240502132738816 +image-20240502132738816 [4, 16, 768] = [batch_size, max_length, d_model],batch_size就是我们可以做并行的设置,做算法建模的同学应该对这个比较熟悉,越大的batch_size,意味着需要越大的内存和显存。max_length则是我们设置的最大长度,超过则截断(因为资源也是有限的,我们一般取能获取到绝大多数完整句子的长度即可)。768则是GPT-2的默认向量维度。 看上面的图,[4, 16, 768]复制成3份,分别去与Wq、Wk和Wv矩阵相乘。 -image-20240502132811665 +image-20240502132811665 如上图所示,Wq的也是[768, 768]维的矩阵,Wk、Wv同理,它们一开始会初始化值,训练过程会自动调整。 单独拿一个Q出来细看,[4, 16, 768]跟[768, 768]是怎么矩阵相乘的,实际上,相乘都是后两个维度跟768相乘,也就是[16,768]跟[768,768]。如下图所示: -image-20240501162941113 +image-20240501162941113 把4个[16, 768]维的矩阵分别拿出来,去与[768, 768]维的矩阵相乘。原矩阵里的数值,经过W权重后,出来的Q里的值会不一样。即最终出来的QKV三个矩阵里的值都跟原始的有所变化。 @@ -141,7 +141,7 @@ $$ 上面我们看到单个头的是[4, 16, 768],前面我们也一直提到QKV的多头机制,如果按照GPT里的12头(Transformer原文中并没有规定是多少头),那么会这么切分,如下图: -image-20240502134443646 +image-20240502134443646 可以看到我们将768维的矩阵,切成了12分,每份是64维。另外,由于大模型都是后两位数矩阵相乘,所以我们把头跟长互换,即[4, 16, 12, 64]转为[4, 12, 16, 64]。 @@ -153,7 +153,7 @@ $$ QKV分别获得后,QK则是根据路线进行矩阵相乘,如下图 -image-20240502212200231 +image-20240502212200231 其中我们把K进行了翻转,方便相乘。矩阵相乘则是每个batch_size里的每个头进行矩阵相乘,即[16, 64]和[64, 16]进行矩阵相乘,相乘后则是变成了[16, 16]的矩阵。