一、什么是线性变换与非线性变换?
线性变换:规则的数学映射
核心定义:线性变换是满足叠加性和齐次性的数学运算:
- 叠加性:f(x + y) = f(x) + f(y)
- 齐次性:f(αx) = αf(x)
在神经网络中,线性变换通常表示为:
输出 = 权重矩阵 × 输入 + 偏置向量 y = Wx + b
非线性变换:打破规则的创造力
核心定义:非线性变换不满足上述线性条件,能够引入弯曲和复杂的决策边界。常见的有ReLU、GELU、Sigmoid等激活函数。
生动比喻:烹饪的艺术
- 线性变换:像按食谱配菜
- 食材按固定比例组合
- 可预测的结果:2倍食材 = 2倍成品
- 缺乏创造力和适应性
- 非线性变换:像大厨的调味魔法
- 加入秘制酱料、火候控制
- 产生化学反应,创造全新风味
- 小小的调整带来质的飞跃
数学直观对比
二、线性与非线性在Transformer中的应用位置
Transformer架构全景图
具体应用位置详解
1.线性变换的主要应用
1.1 投影操作
import torch import torch.nn as nn # Q、K、V投影 - 典型的线性变换 class MultiHeadAttention(nn.Module): def __init__(self, d_model, num_heads): super().__init__() self.d_model = d_model self.num_heads = num_heads self.d_k = d_model // num_heads # 线性投影层 self.w_q = nn.Linear(d_model, d_model) # Query投影 self.w_k = nn.Linear(d_model, d_model) # Key投影 self.w_v = nn.Linear(d_model, d_model) # Value投影 self.w_o = nn.Linear(d_model, d_model) # 输出投影 def forward(self, x): # 线性变换:将输入映射到Q、K、V空间 Q = self.w_q(x) # [batch_size, seq_len, d_model] K = self.w_k(x) V = self.w_v(x) # ... 注意力计算 ... # 输出线性投影 output = self.w_o(attention_output) return output
1.2 前馈网络中的线性层
class FeedForwardNetwork(nn.Module): def __init__(self, d_model, d_ff, dropout=0.1): super().__init__() # 两个线性变换层 self.linear1 = nn.Linear(d_model, d_ff) # 扩展维度 self.linear2 = nn.Linear(d_ff, d_model) # 压缩回原维度 self.dropout = nn.Dropout(dropout) def forward(self, x): # 第一个线性变换:d_model → d_ff (通常d_ff = 4×d_model) x = self.linear1(x) # 中间有非线性激活... x = self.linear2(x) return self.dropout(x)
1.3 词嵌入和输出层
# 词嵌入层:本质是查找表,可视为线性变换 embedding = nn.Embedding(vocab_size, d_model) # 输出层:隐藏状态 → 词汇表概率分布 output_layer = nn.Linear(d_model, vocab_size)
2.非线性变换的主要应用
2.1 激活函数 - 前馈网络的核心
class FeedForwardWithActivation(nn.Module): def __init__(self, d_model, d_ff, activation="relu"): super().__init__() self.linear1 = nn.Linear(d_model, d_ff) self.linear2 = nn.Linear(d_ff, d_model) # 非线性激活函数 if activation == "relu": self.activation = nn.ReLU() elif activation == "gelu": self.activation = nn.GELU() elif activation == "swish": self.activation = nn.SiLU() def forward(self, x): # 线性变换 → 非线性激活 → 线性变换 x = self.linear1(x) x = self.activation(x) # 非线性变换! x = self.linear2(x) return x
2.2 Softmax - 注意力权重的非线性归一化
def attention_with_softmax(Q, K, V, mask=None): # 计算注意力分数(线性操作后的点积) scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k) if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) # 非线性变换:Softmax归一化 attention_weights = F.softmax(scores, dim=-1) # 非线性! # 加权求和 output = torch.matmul(attention_weights, V) return output
2.3 层归一化中的非线性元素
class LayerNorm(nn.Module): def __init__(self, features, eps=1e-6): super().__init__() self.gamma = nn.Parameter(torch.ones(features)) # 可学习缩放 self.beta = nn.Parameter(torch.zeros(features)) # 可学习偏置 self.eps = eps def forward(self, x): mean = x.mean(-1, keepdim=True) std = x.std(-1, keepdim=True) # 归一化是线性的,但gamma和beta引入了元素级的非线性变换 normalized = (x - mean) / (std + self.eps) return self.gamma * normalized + self.beta # 非线性缩放和偏移
三、为什么Transformer需要两者结合?
1. 只有线性变换的局限性
理论限制:线性系统的组合仍是线性
# 多个线性层堆叠的效果 def pure_linear_stack(x, layers): for layer in layers: x = layer(x) # 每个layer都是nn.Linear return x # 数学上等价于: # 输出 = (W_n × ... × W_2 × W_1) × 输入 + 偏置组合 # 仍然是一个线性变换!
实际演示:线性网络的表达能力局限
import matplotlib.pyplot as plt import numpy as np # 生成非线性数据 x = np.linspace(-5, 5, 100) y = np.sin(x) + 0.3 * np.random.normal(size=100) # 正弦波+噪声 # 只有线性层的网络 class LinearNet(nn.Module): def __init__(self, hidden_sizes): super().__init__() layers = [] prev_size = 1 for size in hidden_sizes: layers.append(nn.Linear(prev_size, size)) prev_size = size layers.append(nn.Linear(prev_size, 1)) self.network = nn.Sequential(*layers) def forward(self, x): return self.network(x) # 有非线性激活的网络 class NonLinearNet(nn.Module): def __init__(self, hidden_sizes): super().__init__() layers = [] prev_size = 1 for size in hidden_sizes: layers.append(nn.Linear(prev_size, size)) layers.append(nn.ReLU()) # 非线性! prev_size = size layers.append(nn.Linear(prev_size, 1)) self.network = nn.Sequential(*layers) def forward(self, x): return self.network(x)
结果对比:
- 纯线性网络:只能拟合直线,无法捕捉正弦波
- 非线性网络:能够拟合复杂的曲线模式
2. 非线性变换的核心价值
2.1 实现万能近似定理
理论上,一个包含非线性激活的前馈网络可以近似任何连续函数。
2.2 创建层次化特征表示
# Transformer中的层次化特征学习过程 def hierarchical_feature_learning(input_vectors): # 第一层:基础特征 layer1 = linear1(input_vectors) # 线性组合 layer1 = gelu(layer1) # 非线性变换 # 第二层:组合特征 layer2 = linear2(layer1) # 线性组合基础特征 layer2 = gelu(layer2) # 非线性创建高级特征 # 第三层:抽象特征 layer3 = linear3(layer2) # 线性组合高级特征 layer3 = gelu(layer3) # 非线性创建抽象概念 return layer3
3. Transformer的设计哲学:线性与非线性舞蹈
交替使用模式
完整的前馈网络示例
class TransformerFFN(nn.Module): """标准的Transformer前馈网络""" def __init__(self, d_model, d_ff, dropout=0.1): super().__init__() # 线性-非线性-线性 结构 self.linear1 = nn.Linear(d_model, d_ff) # 扩展:线性 self.activation = nn.GELU() # 激活:非线性 self.dropout = nn.Dropout(dropout) self.linear2 = nn.Linear(d_ff, d_model) # 压缩:线性 def forward(self, x): # 输入: [batch_size, seq_len, d_model] # 第一阶段:线性扩展 + 非线性变换 x = self.linear1(x) # 线性: d_model → d_ff x = self.activation(x) # 非线性: 引入复杂性 x = self.dropout(x) # 第二阶段:线性压缩 x = self.linear2(x) # 线性: d_ff → d_model return x
4. 不同非线性函数的对比
常用激活函数在Transformer中的应用
# 不同激活函数的实现对比 def test_activation_functions(): x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0]) activations = { "ReLU": nn.ReLU(), "GELU": nn.GELU(), "SiLU(Swish)": nn.SiLU(), "Tanh": nn.Tanh(), } results = {} for name, activation in activations.items(): results[name] = activation(x) return results
激活函数特性对比表
激活函数 |
公式 |
优点 |
在Transformer中的应用 |
ReLU |
max(0, x) |
计算简单,缓解梯度消失 |
早期Transformer使用 |
GELU |
x × Φ(x) |
更平滑,性能更好 |
BERT、GPT等现代模型 |
Swish |
x × σ(x) |
平滑,有界 |
部分变体中使用 |
Tanh |
(e^x - e^{-x})/(e^x + e^{-x}) |
输出有界 |
较少在Transformer中使用 |
5. 实际配置与性能影响
不同激活函数的性能对比
# 实验:不同激活函数对Transformer性能的影响 def compare_activations(): configs = { "gelu": {"activation": "gelu", "d_ff": 2048}, "relu": {"activation": "relu", "d_ff": 2048}, "swish": {"activation": "swish", "d_ff": 2048}, } results = {} for name, config in configs.items(): model = TransformerLayer(d_model=512, **config) # 训练和评估... results[name] = evaluate_model(model) return results
典型结果:
- GELU:通常表现最好,训练稳定
- ReLU:计算高效,但可能不如GELU
- Swish:有潜力,但计算稍复杂
四、代码实战:完整的Transformer层
import torch import torch.nn as nn import torch.nn.functional as F class TransformerLayer(nn.Module): def __init__(self, d_model, num_heads, d_ff, dropout=0.1): super().__init__() # 自注意力机制 self.self_attention = MultiHeadAttention(d_model, num_heads) self.norm1 = nn.LayerNorm(d_model) self.dropout1 = nn.Dropout(dropout) # 前馈网络:线性-非线性-线性结构 self.ffn = FeedForwardNetwork(d_model, d_ff, dropout) self.norm2 = nn.LayerNorm(d_model) self.dropout2 = nn.Dropout(dropout) def forward(self, x, mask=None): # 自注意力子层 residual = x x = self.self_attention(x, mask) x = self.dropout1(x) x = self.norm1(x + residual) # 残差连接 + 层归一化 # 前馈网络子层 residual = x x = self.ffn(x) x = self.dropout2(x) x = self.norm2(x + residual) # 残差连接 + 层归一化 return x class MultiHeadAttention(nn.Module): def __init__(self, d_model, num_heads): super().__init__() self.d_model = d_model self.num_heads = num_heads self.d_k = d_model // num_heads # 线性投影层 self.w_q = nn.Linear(d_model, d_model) self.w_k = nn.Linear(d_model, d_model) self.w_v = nn.Linear(d_model, d_model) self.w_o = nn.Linear(d_model, d_model) def forward(self, x, mask=None): batch_size, seq_len = x.size(0), x.size(1) # 线性变换:生成Q, K, V Q = self.w_q(x) # 线性 K = self.w_k(x) # 线性 V = self.w_v(x) # 线性 # 注意力计算(包含非线性softmax) scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k) if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) attn_weights = F.softmax(scores, dim=-1) # 非线性! attention_output = torch.matmul(attn_weights, V) # 输出线性投影 output = self.w_o(attention_output) # 线性 return output class FeedForwardNetwork(nn.Module): def __init__(self, d_model, d_ff, dropout=0.1): super().__init__() # 线性-非线性-线性结构 self.linear1 = nn.Linear(d_model, d_ff) # 线性扩展 self.activation = nn.GELU() # 非线性激活 self.dropout = nn.Dropout(dropout) self.linear2 = nn.Linear(d_ff, d_model) # 线性压缩 def forward(self, x): x = self.linear1(x) # 线性变换 x = self.activation(x) # 非线性变换 x = self.dropout(x) x = self.linear2(x) # 线性变换 return x
总结:智能的数学交响曲
Transformer中线性与非线性变换的精妙配合,就像一场完美的交响乐:
设计哲学总结
- 线性变换:提供结构和可学习参数
- 像乐谱的音符,定义了基本元素
- 负责信息传递、维度变换、特征组合
- 非线性变换:提供表现力和创造力
- 像音乐家的演绎,注入灵魂和情感
- 负责复杂模式学习、层次特征提取
黄金组合的价值
这种线性与非线性的交替舞蹈,让Transformer既保持了数学的优雅,又获得了现实的复杂性理解能力,最终成就了现代人工智能的辉煌成就。