【本文正在参加金石计划附加挑战赛——第一期命题】
全网最通俗易懂的,最简洁的,从输入开始直到整个decoder-only架构结束全流程串讲
本篇阅读时间约2分钟
tips:可以多看下代码的注释
针对语言序列,图片那种batch norm的不涉及
尽可能简化表述,主要是为了准备面试突击,后续有时间再放图
序列输入模型(分词,embedding)
首先我们输入两句话:
- “我爱你中华”
- “你是我的眼。”
那么 它们经过tokenizer可能会变成
- “我,爱你,中华”
- “你,是,我的,眼,。”
假设会变成
- (88,8,9)
- (2,5,7,9,342)
那么我们都知道tokenizer其实是个词表映射,现在大模型很多词表都很大,比如128000以上的词表啦,65280的词表啦,llama2用的32000已经跟不上时代啦等等
那么上面的
- (88,8,9)
- (2,5,7,9,342)
数字其实代表索引。
embedding层的大小其实是(vocab_size, hidden_size)
我们理解说比如65280是词表大小,那么 hidden size就代表 embedding在使用hidden_size个维度特征在表示这个token。
我们随便找个大模型的config.json
{ | |
| - | ----------------------------------------------------------- |
| | "architectures": [ |
| | "XModelForCausalLM" |
| | ], |
| | "auto_map": { |
| | "AutoConfig": "configuration_xmodel.XModelConfig", |
| "AutoModelForCausalLM": "modeling_xmodel.XModelForCausalLM" |
| | }, |
| | "bos_token_id": 1, # begin of sentence |
| | "eos_token_id": 2, # end of sentece |
| | "hidden_act": "silu", #比较流行的swiglu这里都会写silu,我们看ffn源码就好 |
| | "hidden_size": 2048,
# 这个就是d_model,传说中的模型维度,很多论文里说的模型维度就是指这个东西,
# 它要和hidden_states相区别,hidden_states是计算出来的可变的隐藏状态,这东西和前向传播有关系,
# d_model就是设置之后就不变啦,超级重要的 |
| | "initializer_range": 0.02, |
| | "intermediate_size": 5632,
# 中间层,其实是ffn的隐藏维度,
# 一般是d_model=hidden_size的4~5倍,用来升维,方便激活函数激活 |
| | "max_position_embeddings": 32768,
# 这个和rope有关系,后面结合代码新开一系列专门讲rope |
| | "model_type": "xmodel", |
| | "num_attention_heads": 32, # 多头,32个头 |
| | "num_hidden_layers": 24, |
| | "num_key_value_heads": 4,
# 这里kv只有4个头,分成了8组,其实个人感觉不是很好,压缩的过分了 |
| | "pad_token_id": 0, |
| | "pretraining_tp": 1, |
| | "rms_norm_eps": 1e-05, # 这是rmsnorm里面防止分母为0的epsilon |
| | "tie_word_embeddings": false, |
| | "torch_dtype": "float32", |
| | "transformers_version": "4.38.1", |
| | "use_cache": true, # 是否使用kv cache(一般用于gqa,mqa,mla等) |
| | "vocab_size": 32000 # 词表大小 |
| | }
可以看到hidden_size是2048,词表vocab_size是32000
这样我们的第一个token88,其实在embedding输出就是找到第88行的2048维度的向量放到第一个位置, 依次类推,我们就得到了
这里要画个图
(2,5,2048)的输出
2 就是 batch size——因为我们有两句话嘛
5 就是这个batch里面的最大序列token长度 max_seq_len,那第一句话只有3的长度,空下来的就只能置 0 了,这就会造成很大的显存浪费,显卡怠惰怎么能行呢?(详情见kv cache系列)
2048 就是hidden_size,就是d_model,就是特征维度 (你是电你是光你是唯一的神话)
位置编码
如果不加位置信息,计算注意力分数其实是位置无关的,只是针对token之间计算而已,但是序列里位置信息很重要,在前面在后面表达的意思都可能截然不同,所以要引入位置编码。
绝对正弦编码sinPE会把单双数间隔地做cos和sin然后加到序列上面去
但是无法解决长程衰减问题
所以会有RoPE,但是这个东西它是在注意力qkv之后,计算注意力分数之前添加进去的,所以留着后面讲,我们先进入layer norm
层归一化
语言模型一般使用layer norm而且现在更多使用pre-LN
RMSNorm 就类似于 T5 的 layer norm
看现在我们的输入是(2,5,2048)
layer norm 就是针对(1,5,2048)做norm “我爱你中华”
而batch norm 是针对(2,1,2048)做norm ,这个就啼笑皆非了 它是对
- “我爱你中华”
- “你是我的眼。”
这两句话的 “我”和“你”做norm
都不是一个序列,怎么能做batch norm呢?你说对吧
具体的norm方法呢,rmsnorm有公式,大家可以自行检索 (这里也应该补个图,还有公式)
那么layer norm出来之后我们的特征维度的值域就会尽可能往[-1,1] 去靠了
下面就要进入大名鼎鼎的注意力机制啦
会在下一篇文章中叙述~