Github开源生信云平台 DEMO
下面我会从 “为什么需要 Attention” → “数学公式” → “Python 手写实现” → “训练” → “完整 Transformer Block” 一步一步讲。
目标不是“调用库”,而是你真正理解:
我会先从最小版本开始,只用:
numpy
torch
这样你能真正理解原理。
先看一句话:
"Tom likes cats because he has three of them"
模型在处理 "he" 时:
"he"
传统 RNN:
Attention:
所以:
Attention 本质:
“当前词应该关注输入中的哪些词”
假设输入:
I love transformer
token:
["I", "love", "transformer"]
每个 token 会变成向量:
I -> x1 love -> x2 transformer -> x3
Attention 会让:
这是 Transformer 最核心部分。
输入向量:
X
通过三个线性层:
Q = XWq K = XWk V = XWv
得到:
可以理解:
类似搜索引擎:
Query: "cat" Key: doc1: "dog" doc2: "cat" 匹配度: doc2 更高
于是更多关注 doc2。
核心公式:
Attention(Q,K,V)=softmax\left(\frac{QK^T}{\sqrt{d_k}}\right)V
这是 Transformer 灵魂。
下面一步一步拆。
QK^T
表示:
例如:
Q = [1, 2] K = [2, 1]
点积:
1×2 + 2×1 = 4
越大:
公式:
\frac{QK^T}{\sqrt{d_k}}
原因:
维度大时:
举例:
[10, 11, 9]
softmax:
[0.24, 0.66, 0.09]
如果:
[1000, 1100, 900]
[0, 1, 0]
梯度几乎没了。
\sqrt{d_k}
用于稳定训练。
softmax(x_i)=\frac{e^{x_i}}{\sum_j e^{x_j}}
作用:
把分数变成概率。
[2, 1, 0]
变成:
[0.67, 0.24, 0.09]
公式最后:
softmax(...)V
weights = [0.8, 0.2] V1 = [1, 1] V2 = [10, 10]
结果:
0.8*V1 + 0.2*V2
现在开始真正实现。
import numpy as np # 输入序列 X = np.array([ [1, 0, 1, 0], [0, 2, 0, 2], [1, 1, 1, 1] ], dtype=np.float32) # 随机初始化权重 Wq = np.random.randn(4, 4) Wk = np.random.randn(4, 4) Wv = np.random.randn(4, 4) # 生成 Q K V Q = X @ Wq K = X @ Wk V = X @ Wv # attention score scores = Q @ K.T # 缩放 dk = K.shape[-1] scores = scores / np.sqrt(dk) # softmax exp_scores = np.exp(scores) attention_weights = exp_scores / np.sum(exp_scores, axis=-1, keepdims=True) # 输出 output = attention_weights @ V print(output)
输入 token embedding:
[seq_len, hidden_dim]
这里:
[3, 4]
线性变换:
Q = XWq
本质:
神经网络全连接层。
Q @ K.T
[3, 3]
每个 token 对其他 token 的关注程度。
语言模型训练时:
不能偷看未来。
I love ?
预测:
transformer
不能提前看到 transformer。
所以需要:
矩阵:
\begin{bmatrix} 1 & 0 & 0 \ 1 & 1 & 0 \ 1 & 1 & 1 \end{bmatrix}
未来位置设为:
-1e9
softmax 后接近 0。
mask = np.triu(np.ones((3, 3)), k=1) scores = scores - mask * 1e9
为什么多头?
因为:
不同 head 学不同关系:
head_i = Attention(Q_i,K_i,V_i)
最后:
Concat(head_1,...,head_h)W^O
现在开始真正深度学习版本。
import torch import torch.nn as nn import torch.nn.functional as F class SelfAttention(nn.Module): def __init__(self, dim): super().__init__() self.Wq = nn.Linear(dim, dim) self.Wk = nn.Linear(dim, dim) self.Wv = nn.Linear(dim, dim) def forward(self, x): Q = self.Wq(x) K = self.Wk(x) V = self.Wv(x) scores = Q @ K.transpose(-2, -1) scores = scores / (K.size(-1) ** 0.5) attn = F.softmax(scores, dim=-1) out = attn @ V return out
x = torch.randn(2, 5, 32) model = SelfAttention(32) y = model(x) print(y.shape)
输出:
[2, 5, 32]
含义:
[batch, seq_len, hidden]
Transformer 不只是 attention。
完整结构:
Attention ↓ Add & Norm ↓ Feed Forward ↓ Add & Norm
FFN:
Linear ReLU Linear
FFN(x)=W_2(ReLU(W_1x))
class TransformerBlock(nn.Module): def __init__(self, dim): super().__init__() self.attn = SelfAttention(dim) self.norm1 = nn.LayerNorm(dim) self.ffn = nn.Sequential( nn.Linear(dim, dim * 4), nn.ReLU(), nn.Linear(dim * 4, dim) ) self.norm2 = nn.LayerNorm(dim) def forward(self, x): x = x + self.attn(x) x = self.norm1(x) x = x + self.ffn(x) x = self.norm2(x) return x
x + attention(x)
避免深层网络梯度消失。
标准化:
让训练稳定。
Transformer 语言模型:
下一个 token 预测
输入:
I love
目标:
模型输出:
[batch, seq_len, vocab_size]
[2, 5, 10000]
每个位置预测 10000 个词概率。
Loss = -\log(p_{true})
真实词:
cat
模型预测:
[dog=0.1, cat=0.7, fish=0.2]
loss: -\log(0.7)
如果预测正确:
预测错误:
model = TransformerBlock(32) optimizer = torch.optim.Adam( model.parameters(), lr=1e-3 ) x = torch.randn(8, 10, 32) target = torch.randn(8, 10, 32) for step in range(100): pred = model(x) loss = F.mse_loss(pred, target) optimizer.zero_grad() loss.backward() optimizer.step() print(loss.item())
真正 GPT:
target:
love transformer <eos>
即:
输入: I 预测: love 输入: I love 预测: transformer
文本不能直接输入网络。
需要:
nn.Embedding
self.embedding = nn.Embedding(vocab_size, dim)
把 token id:
15
[0.1, -0.5, ...]
Attention 本身不知道顺序。
需要加入位置。
经典公式:
PE(pos,2i)=sin(pos/10000^{2i/d})
PE(pos,2i+1)=cos(pos/10000^{2i/d})
给模型位置感。
相比 RNN:
Transformer:
几乎统一 NLP。
建议顺序:
真正吃透:
手写:
实现:
理解:
Transformer 本质:
用 Query 与 Key 计算相关性,再对 Value 做加权聚合。
softmax(QK^T/\sqrt{d})V
这一个公式,基本统治了现代 AI。