多模态学习路线(2)——DL基础系列

目录

前言

一、归一化

1. Layer Normalization (LN)

2. Batch Normalization (BN) 

3. Instance Normalization (IN) 

4. Group Normalization (GN)

5. Root Mean Square Normalization(RMSNorm)

二、激活函数

1. Sigmoid激活函数(二分类)

2. Tanh激活函数

3. ReLU

4. Leaky Relu / PeLU (α不可学习 / 可学习)

5. Softmax激活函数(多分类)

三、优化器

1. SGD

2. BGD

3. MBGD-Mini-batch GD

4. Momentum

5. Adam

6. AdamW(Adam + Weight Decay)​​​

四、损失函数

1. ​​L1 Loss(MAE)

2. L2 Loss(MSE)

3. ​​BCE Loss(二元交叉熵,Binary Cross-Entropy)

4. CE Loss(多元交叉熵 / 交叉熵,Cross-Entropy)

5. KL散度(Kullback-Leibler Divergence)

6. ​​Focal Loss(二分类,类别不平衡)

7. InfoNCE Loss

五、注意力机制

1. Self Attention 与 Cross Attention

2. 双向Attention 与 Casual Attention

3. Sclaed Dot-Product Attention 与 Multi-Head Attention(MHA)

4. 分组查询注意力(Grouped-Query Attention, GQA)​​

5. 多层注意力(Multi-Level Attention, MLA)​

6. 多查询注意力(Multi-Query Attention, MQA)​

六、位置编码(正余弦PE,ROPE,RPE,ROPE+ 2D RPE)

1. 绝对位置编码(正余弦位置编码)

2. 旋转位置编码(ROPE)

七、Decoder解码

1. Greedy Search

2. Beam Search

3. Temperature Sampling

4. Top-k Sampling(手撕)

5. Top-p Sampling(手撕)

八、NLP、CV、AIGC、MLLMs经典算法

1. NLP

2. CV

3. AIGC

4. MLLMs


前言

一、归一化:Layer Normalization (LN)、Batch Normalization (BN)、Instance Normalization (IN)、Group Normalization (GN)、Root Mean Square Normalization(RMSNorm);

二、激活函数:sigmoid,Tanh,ReLU,Leaky ReLU,Softmax;

三、优化器:SGD,BGD,MBGD-Mini-batch GD,Momentum,Adam,AdamW;

四、损失函数:​​L1 Loss(MAE),​​L2 Loss(MSE),BCE Loss(二元交叉熵,Binary Cross-Entropy),​​交叉熵(多元交叉熵,Cross-Entropy),KL散度(Kullback-Leibler Divergence),Focal Loss(二分类,类别不平衡),InfoNCE Loss​​;

五、注意力机制:Self Attention 与 Cross Attention,双向Attention 与 Casual Attention,Scaled Dot-Product Attention 与 Multi-Head Attention(MHA),分组查询注意力(GQA),多层注意力(MLA),多查询注意力(MQA);

六、位置编码:绝对位置编码(正余弦位置编码),旋转位置编码(ROPE),相对位置编码(PRE),RoPE(文本)+ 2D RPE(视觉);

七、Decoder解码:Greedy Search,Beam-Search,Temperature Sampling,Top-k Sampling,Top-p Sampling;

八、NLP、CV、AIGC、MLLMs经典算法


一、归一化

1. Layer Normalization (LN)

输入:(N, L, D);

计算:N不变,对每个样本上的(L, D)求总体均值和方差;

### 输入:(batch_size, seq_len, feature_dim)(常见于序列模型Transformer/RNN)
def layernorm(x, gamma, beta, eps=1e-5):
    # x shape: (N, L, D)
    N = x.shape[0]
    mean = np.mean(x, axis=(1, 2)).reshape(N, 1, 1)  # (N, 1, 1)
    var = np.var(x, axis=(1, 2)).reshape(N, 1, 1)    # (N, 1, 1)
    x_normalized = (x - mean) / np.sqrt(var + eps)   # 归一化
    return gamma * x_normalized + beta 

### e.g. x形状为 (2, 3, 4)(batch_size=2, seq_len=3, feature_dim=4)
x = [
    [ # 样本 1
        [1, 2, 3, 4],  # seq_len1
        [5, 6, 7, 8],  # seq_len2
        [9, 10, 11, 12] # seq_len3
    ],
    [ # 样本 2
        [-1, -2, -3, -4],  # seq_len1
        [-5, -6, -7, -8],  # seq_len2
        [-9, -10, -11, -12] # seq_len3
    ]
]
x = np.array(x)
gamma, beta = 0.1, 0.1
x_ln = layernorm(x, gamma, beta, eps=1e-5)
print(x_ln.shape)
"""
LN 计算:对样本1归一化:计算所有时间步和特征的均值
mean = (1+2+...+12) / 12 = 6.5
var = (1²+2²+...+12²)/12 - mean² = 11.9167
样本2同理(每个样本独立计算)。
"""
  • Adaptive LayerNorm:对传统 LayerNorm 的增强,通过引入可学习的缩放(γ)和偏移(β)参数,使得归一化过程能够根据输入数据的不同进行调整,从而提高了模型的灵活性和表现。这种方法尤其适用于多任务学习和多模态学习等复杂任务,可以更好地适应不同的数据分布,提高训练的稳定性和效率。

class AdaptiveLayerNorm(nn.Module):
    def __init__(self, input_dim):
        super(AdaptiveLayerNorm, self).__init__()
        # 可学习的缩放和偏移参数
        self.gamma = nn.Parameter(torch.ones(input_dim))
        self.beta = nn.Parameter(torch.zeros(input_dim))
    def forward(self, x):
        # 计算均值和方差
        mean = x.mean(dim=-1, keepdim=True)
        var = x.var(dim=-1, keepdim=True, unbiased=False)
        # 归一化
        x_norm = (x - mean) / torch.sqrt(var + 1e-5)
        # 自适应缩放和偏移
        return self.gamma * x_norm + self.beta

2. Batch Normalization (BN) 

输入:(N, C, H, W);

计算:C不变,对每个C上的(N, H, W)求总体均值和方差;

# 输入x:(batch_size, channels, height, width)(常见于CNN)
def batchnorm(x, gamma, beta, eps=1e-5):
    # x shape: (N, C, H, W)
    c = x.shape[1]
    mean = np.mean(x, axis=(0, 2, 3)).reshape(1,c,1,1)  # 计算每个通道的均值,维度 (1,C,1,1)
    var = np.var(x, axis=(0, 2, 3)).reshape(1,c,1,1)   # 计算每个通道的方差,维度 (1,C,1,1)
    x_normalized = (x - mean) / np.sqrt(var + eps)  # 归一化
    return gamma * x_normalized + beta  # 缩放和偏移

# e.g. x形状为 (2, 3, 2, 2)(batch_size=2, channels=3, height=2, width=2)
x = [
    [  # 样本 1
        [[1, 2], [3, 4]],  # 通道 1
        [[5, 6], [7, 8]],  # 通道 2
        [[9, 10], [11, 12]] # 通道 3
    ],
    [  # 样本 2
        [[-1, -2], [-3, -4]],  # 通道 1
        [[-5, -6], [-7, -8]],  # 通道 2
        [[-9, -10], [-11, -12]] # 通道 3
    ]
]
x = np.array(x)
gamma, beta = 0.1, 0.1
x_bn = batchnorm(x, gamma, beta, eps=1e-5)
print(x_bn.shape)
"""
对通道1归一化:计算所有样本和空间位置的均值. 
mean = (1+2+3+4 + (-1-2-3-4)) / 8 = 0
var = (1²+2²+...+(-4)²)/8 - mean² = 7.5
其他通道类似(每个通道独立计算)。
"""

3. Instance Normalization (IN) 

输入:(N, C, H, W);

计算:(N, C)不变,对每个(N, C)上的(H, W)求总体均值和方差;

def instance_norm(x, gamma, beta, eps=1e-5):
    # x shape: (N, C, H, W)
    N, C = x.shape[0], x.shape[1] 
    mean = np.mean(x, axis=(2, 3)).reshape(N, C, 1, 1)  # 计算每个样本每个通道的均值,维度 (N, C)
    var = np.var(x, axis=(2, 3)).reshape(N, C, 1, 1)   # 计算方差,维度 (N, C)
    x_normalized = (x - mean) / np.sqrt(var + eps)  # 归一化
    return gamma * x_normalized + beta  # 缩放和偏移

# 形状为 (2, 3, 2, 2)(batch=2, channels=3, height=2, width=2):
x = [
    [ # 样本 1
        [[1, 2], [3, 4]],  # 通道 1
        [[5, 6], [7, 8]],  # 通道 2
        [[9, 10], [11, 12]] # 通道 3
    ],
    [ # 样本 2
        [[-1, -2], [-3, -4]],  # 通道 1
        [[-5, -6], [-7, -8]],  # 通道 2
        [[-9, -10], [-11, -12]] # 通道 3
    ]
]
x = np.array(x)
gamma, beta = 0.1, 0.1
x_in = instance_norm(x, gamma, beta, eps=1e-5)
print(x_in.shape)
"""
对样本1的通道1归一化:
mean = (1+2+3+4)/4 = 2.5
var = [(1-2.5)² + (2-2.5)² + (3-2.5)² + (4-2.5)²]/4 = 1.25
其他通道和样本同理(每个样本的每个通道独立计算)。
"""

4. Group Normalization (GN)

输入:(N, C, H, W), group nums=G;

输出:(N, C, H, W) --> (N, G, C//G, H, W) --> (N, C, H, W)

计算:(N, G)不变,对每个(N, G)上的(C//G, H, W)求总体均值和方差;

# 核心思想:将通道分组,对每个样本的每组通道归一化,解决小 batch 下 BN 不稳定的问题,适用于检测、分割等任务。
def group_norm(x, gamma, beta, G, eps=1e-5):
    # x shape: (N, C, H, W) 或 (N, C, L)
    # G: 分组数(如 C=32, G=8 → 每组 4 个通道)
    N, C, H, W = x.shape[0], x.shape[1], x.shape[2], x.shape[3]
    x = np.reshape(x, (N, G, C//G, H, W))  # 分组后形状 (N, G, C/G, H, W)
    mean = np.mean(x, axis=(2, 3, 4)).reshape(N, G)  # 计算每组均值,维度 (N, G)
    var = np.var(x, axis=(2, 3, 4)).reshape(N, G)   # 计算每组方差,维度 (N, G)
    x_normalized = (x - mean) / np.sqrt(var + eps)  # 归一化
    x_normalized = np.reshape(x_normalized, (N, C, H, W))  # 恢复原始形状
    return gamma * x_normalized + beta  # 缩放和偏移

# x形状为 (2, 4, 2, 2)(batch=2, channels=4, height=2, width=2),分组数 G=2
x = [
    [ # 样本 1
        [[1, 2], [3, 4]],  # 通道 1 (组 1)
        [[5, 6], [7, 8]],  # 通道 2 (组 1)
        [[9, 10], [11, 12]], # 通道 3 (组 2)
        [[13, 14], [15, 16]] # 通道 4 (组 2)
    ],
    [ # 样本 2
        [[-1, -2], [-3, -4]],  # 通道 1 (组 1)
        [[-5, -6], [-7, -8]],  # 通道 2 (组 1)
        [[-9, -10], [-11, -12]], # 通道 3 (组 2)
        [[-13, -14], [-15, -16]] # 通道 4 (组 2)
    ]
]
x = np.array(x)
gamma, beta = 0.1, 0.1
x_gn = group_norm(x, gamma, beta, G=2, eps=1e-5)
print(x_gn.shape)
"""
GN 计算(G=2,每组 2 个通道):
对样本 1 的组 1(通道 1+2)归一化:
mean = (1+2+3+4 + 5+6+7+8)/8 = 4.5
var = [(1-4.5)² + ... + (8-4.5)²]/8 = 5.25
组 2(通道 3+4)和其他样本同理。
"""

5. Root Mean Square Normalization(RMSNorm)

传统LN计算均值和方差;RMSNorm去除均值中心化,仅使用均方根RMS进行缩放。避免减法,提高数值稳定性,简化计算;仅使用均方根RMS归一化,计算量更小。只对特征维度(feature_dim)计算。

# (batch_size, seq_len, feature_dim)
def rms_norm(x, gamma, eps=1e-5):
    rms = np.sqrt(np.mean(x**2, axis=-1, keepdims=True))  # 只在特征维度(D)计算
    return gamma * x / (rms + eps)

# x形状为 (2, 3, 4)
x = [
    [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],  # 样本1
    [[-1, -2, -3, -4], [-5, -6, -7, -8], [-9, -10, -11, -12]]  # 样本2
]
x = np.array(x)
gamma = 1.0
x_rms = rms_norm(x, gamma, eps=1e-5)
print(x_rms.shape)

"""
对样本1的第一个时间步 [1, 2, 3, 4] 计算:
RMS = sqrt(1**2 + 2**2 + 3**2 + 4**2)/4 = sqrt(7.5) ≈ 2.7386
归一化后:
[1,2,3,4]/2.7386 ≈ [0.3651,0.7303,1.0954,1.4606]
最终输出(γ=1):与归一化值相同。
"""
方法归一化维度依赖 Batch适用场景python实现
Layer Norm (LN)(C, H, W) 或 (L, D)Transformer/RNN
  
torch.nn.LayerNorm()      
Batch Norm (BN)(N, H, W)CNN(大 batch)torch.nn.BatchNorm2d()
Instance Norm (IN)(H, W)(单样本单通道)风格迁移、图像生成torch.nn.InstanceNorm2d()
Group Norm (GN)(G, C//G, H, W)小 batch 检测/分割(如 Mask R-CNN)torch.nn.GroupNorm()

归一化

公式

计算量

适用场景

LN

(x−μ)/σ * γ

较高(需均值)

RNN/Transformer序列模型

BN

(x−μ)/σ * γ

高(依赖batch,需维护running mean/var)

CNN、CV任务

RMSNorm

x/RMS * γ

较低

大模型(如LLaMA)

二、激活函数

        激活函数实际是神经网络中的一种非线性变换,使神经网络能够捕捉到非线性关系,从而更好地逼近复杂的函数或映射。

        大模型常用的激活函数包括ReLU、Leaky ReLU、ELU、Swish和GELU。ReLU计算简单且有效避免梯度消失问题,加快训练速度,但可能导致神经元死亡;Leaky ReLU通过引入小斜率缓解ReLU的缺点;GeLU一种改进的ReLU函数,可以提供更好的性能和泛化能力;Swish一种自门控激活函数,可以提供非线性变换,并具有平滑和非单调的特性,在平滑性和性能上表现优异,但计算开销较大。

1. Sigmoid激活函数(二分类)

  • 特点:输出范围(0,1),适合二分类输出层,但易导致梯度消失。

import numpy as np
import matplotlib.pyplot as plt

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.linspace(-5, 5, 100)
y = sigmoid(x)
plt.plot(x, y)
plt.title("Sigmoid")
plt.show()

2. Tanh激活函数

  • 输出范围(-1,1),梯度比Sigmoid更强,但仍存在梯度消失。

import numpy as np
import matplotlib.pyplot as plt

def relu(x):
    return np.maximum(0, x)
x = np.linspace(-5, 5, 100)
y = relu(x)
plt.plot(x, y)
plt.title("ReLU")
plt.show()

3. ReLU

  •  输出范围([0, +♾️),计算高效,缓解梯度消失,但可能导致“神经元死亡”(负输入梯度为0)。

import numpy as np
import matplotlib.pyplot as plt

def relu(x):
    return np.maximum(0, x)
x = np.linspace(-5, 5, 100)
y = relu(x)
plt.plot(x, y)
plt.title("ReLU")
plt.show()

4. Leaky Relu / PeLU (α不可学习 / 可学习)

  • 输出范围(-♾️, +♾️),引入小的负斜率(α),缓解“神经元死亡”问题。

import numpy as np
import matplotlib.pyplot as plt

def leaky_relu(x, alpha=0.01):
    return np.where(x >= 0, x, alpha * x)

x = np.linspace(-5, 5, 100)
y = leaky_relu(x)
plt.plot(x, y)
plt.title("Leaky ReLU")
plt.show()

5. Softmax激活函数(多分类)

  • 将输出转化为概率分布,适用于多分类输出层。

import numpy as np
import matplotlib.pyplot as plt

def softmax(x):
    exp_x = np.exp(x - np.max(x))  # 防溢出
    return exp_x / np.sum(exp_x, axis=0)

x = np.array([1.0, 2.0, 3.0])
print(softmax(x))  # 输出: [0.09003057 0.24472847 0.66524096]
"""
np.exp(x - np.max(x))通过减去输入向量x的最大值(x - np.max(x)),
可以保证:所有指数部分的输入(xi−max(x))≤0​​,因此exi−max(x)的范围是(0,1],
避免exi的爆炸性增长,同时不改变softmax的输出结果。
(数学上等价:分子分母同时除以np.exp(max(x)),和原来等价)。
"""

三、优化器

优化器分类

优化器

核心思想

优点

缺点

基础梯度下降类

BGD

全批量梯度更新

稳定收敛

计算成本高

SGD

单样本随机更新

计算快

噪声大

MBGD

小批量梯度更新

平衡速度与稳定性

需调批量大小

动量类

Momentum

历史梯度加权平均

加速收敛,减少震荡

需调动量系数

自适应学习率类

Adam

自适应动量 + 学习率

适合大多数场景

内存占用高

AdamW

Adam + 权重衰减

更稳定的权重衰减

超参更多

1. SGD

目标​​:每次迭代​​随机选择一个样本​​计算梯度,更新参数。​​更新公式​​如下,其中 (xi​,yi​) 是随机选择的样本。

​特点​:计算快,但噪声大,收敛不稳定。

2. BGD

目标​​:在每次迭代中使用​​全部训练样本​​计算损失函数的梯度,更新参数。更新公式如下,其中,η是学习率,∇θ​J(θ)是损失函数J对参数θ的梯度(所有样本的平均梯度)。

特点:全局最优解,但计算成本高,不适合大规模数据。

3. MBGD-Mini-batch GD

目标​​:每次迭代使用​​一个小批量(Mini-batch)​​样本计算梯度。​更新公式如下,其中,m是批量大小。

​特点​​:平衡了BGD的稳定性和SGD的速度。

4. Momentum

​目标​​:引入动量项(指数加权平均梯度)加速收敛,减少震荡更新公式如下,其中,β是动量系数(通常0.9)。

​推导​​:动量本质是对历史梯度的加权平均,帮助参数在梯度方向一致时加速,不一致时减速。

5. Adam

目标​​:结合动量(一阶矩)和自适应学习率(二阶矩)。

特点:收敛快。主流选择,适用于大多数任务。

6. AdamW(Adam + Weight Decay)​​​

​改进点​​:将权重衰减(L2正则化)从梯度更新中解耦,直接加到参数上。更新公式如下,其中,λ是权重衰减系数。

特点:Adam的权重衰减会因自适应学习率被缩放,AdamW则直接应用衰减,更稳定。

四、损失函数

1. ​​L1 Loss(MAE)

  • 原理:预测值与真实值的绝对误差,对异常值不敏感(鲁棒性强);
  • 公式

import numpy as np

def l1_loss(y_true, y_pred):
    return np.mean(np.abs(y_true - y_pred))

y_true, y_pred = 1.0, 0.9
print(round(l1_loss(y_true, y_pred), 4))

2. L2 Loss(MSE)

  • 原理​​:计算预测值与真实值的平方误差,对异常值敏感(放大误差);
  • 公式

    import numpy as np
    
    def l2_loss(y_true, y_pred):
        return np.mean((y_true - y_pred) ** 2)
    
    y_true, y_pred = 1.0, 0.9
    print('l2 loss(mse): ', round(l2_loss(y_true, y_pred), 4))

    3. ​​BCE Loss(二元交叉熵,Binary Cross-Entropy)

    • 原理​​:二分类任务的交叉熵,输出通过Sigmoid压缩到[0,1]概率;
    • 公式

    # BCE Loss二元交叉熵(二分类)
    def binary_cross_entropy(y_true, y_pred, epsilon=1e-7):
        """
        y_true : 真实标签0或1
        y_pred : 预测概率,经过Sigmoid)后的概率分布
        epsilon : 极小值,防止log(0)数值错误
        """
        y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
        return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))
    y_true = np.array([1, 1, 0])   # 真实值/label: 1, 1, 0
    y_pred = np.array([0.9, 0.8, 0.6])   # 预测的概率分布: 0.9(1), 0.8(1), 0.6(1),最后一个预测错误
    print("bce Loss:", round(binary_cross_entropy(y_true, y_pred), 4))
    """
    np.clip(y_pred, epsilon, 1 - epsilon)   
    (1) ​​防止数值不稳定,通过clip将y_pred限制在[eps, 1-eps]内;
    (2)​​避免梯度消失或爆炸。
    """

    4. CE Loss(多元交叉熵 / 交叉熵,Cross-Entropy)

    • 原理​​:衡量预测概率分布与真实分布的差异,用于多分类任务,输出通过Softmax压缩到概率分布。
    • ​​公式​​(多分类)

    # CE Loss多元交叉熵/交叉熵(多分类)
    def cross_entropy(y_true, y_pred, eps=1e-15):
        """
        y_true : 真实标签0或1
        y_pred : 预测概率,经过Sigmoid)后的概率分布
        epsilon : 极小值,防止log(0)数值错误
        """
        y_pred = np.clip(y_pred, eps, 1 - eps)
        return -np.mean(np.sum(y_true * np.log(y_pred), axis=1))
    y_true = np.array([
        [1, 0, 0],  # Class 0
        [0, 1, 0],  # Class 1
        [0, 0, 1],  # Class 2
        [0, 1, 0]   # Class 1
    ])
    # 预测的概率分布,输出经过softmax转为概率分布
    y_pred = np.array([
        [0.7, 0.2, 0.1],  # class 0
        [0.1, 0.6, 0.3],  # class 1
        [0.2, 0.3, 0.5],  # class 2
        [0.4, 0.5, 0.1]   # class 1
    ])
    print('cross_entropy交叉熵: ', round(cross_entropy(y_true, y_pred), 4))
    """
    np.clip(y_pred, eps, 1 - eps) 
    (1)​​防止数值不稳定,通过clip将y_pred限制在[eps, 1-eps]内;
    (2)​​避免梯度消失或爆炸​。
    """

    5. KL散度(Kullback-Leibler Divergence)

    • 原理​​:衡量两个概率分布 P 和 Q 之间的差异,非对称且非负;
    • 公式

    import numpy as np
    
    def kl_divergence(p, q):
        p = np.clip(p, 1e-10, 1)   #  # 避免log(0)的情况;保证数值稳定;防止梯度消失和梯度爆炸
        q = np.clip(q, 1e-10, 1)
        return np.sum(p * np.log(p / q))
    
    # 示例
    p = np.array([0.1, 0.4, 0.5])
    q = np.array([0.3, 0.3, 0.4])
    print("KL Divergence: ", round(kl_divergence(p, q), 4))

    6. ​​Focal Loss(二分类,类别不平衡)

    • 原理​​:解决不平衡的二类别问题,输出通过Sigmoid压缩到[0,1]概率。通过权重α和聚焦参数γ,减少易分类样本的损失贡献,解决类别不平衡问题。
    • ​​公式​​(二分类)

    import numpy as np
    
    def focal_loss(y_true, y_pred, alpha=0.25, gamma=2.0):
        y_pred = np.clip(y_pred, 1e-8, 1 - 1e-8)
        loss = -alpha * (1 - y_pred) ** gamma * (y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))
        return np.mean(loss)
    
    y_true = np.array([1, 0, 1, 0, 1])  # 真实二类别标签
    y_pred = np.array([0.9, 0.1, 0.8, 0.2, 0.7])  # 预测概率分布0.9(1), 0.1(0), 0.8(1), 0.2(0), 0.7(1)
    
    loss = focal_loss(y_true, y_pred)
    print(f"Focal Loss: ", round(loss, 4))

    7. InfoNCE Loss

    • 原理​​:对比学习中的损失函数,最大化正样本对的相似度,最小化负样本对的相似度;
    • ​​公式​​:其中τ是温度系数。

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import numpy as np
    np.random.seed(42)
    torch.manual_seed(42)
    
    
    # PyTorch 实现
    def infonce_loss_torch(query, positive_key, negative_keys, temperature=1.0):
        """
        Args:
            query: (batch_size, embedding_size)
            positive_key: (batch_size, embedding_size)
            negative_keys:(num_negative, embedding_size)
        """
        positive_logits = torch.sum(query * positive_key, dim=1, keepdim=True) # (batch_size, 1)
        negative_logits = torch.matmul(query, negative_keys.t())  # (batch_size, num_negative)
        logits = torch.cat([positive_logits, negative_logits], dim=1)  # (batch_size, 1 + num_negative)
        
        # Compute cross-entropy loss (positive sample is at index 0)
        labels = torch.zeros(query.size(0), dtype=torch.long, device=query.device)
        loss = F.cross_entropy(logits / temperature , labels)
        return loss
    
    # Numpy 实现
    def infonce_loss_numpy(query, positive_key, negative_keys, temperature=1.0):
        """
        Args:
            query: (batch_size, embedding_size)
            positive_key: (batch_size, embedding_size)
            negative_keys:(num_negative, embedding_size)
        """
        positive_logits = np.sum(query * positive_key, axis=1, keepdims=True) / temperature  # (batch_size, 1)
        negative_logits = np.dot(query, negative_keys.T) / temperature  # (batch_size, num_negative)
        logits = np.concatenate([positive_logits, negative_logits], axis=1)  # (batch_size, 1 + num_negative)
        
        labels = np.zeros(query.shape[0], dtype=np.int64)  # positive sample is at index 0
        exp_logits = np.exp(logits - np.max(logits, axis=1, keepdims=True))  # numerical stability
        softmax_probs = exp_logits / np.sum(exp_logits, axis=1, keepdims=True)
        loss = -np.mean(np.log(softmax_probs[:, 0] + 1e-8))  # avoid log(0)
        return loss
    
    
    # 定义参数,生成随机数据
    batch_size, num_negative, embedding_size = 32, 36, 512
    query = np.random.randn(batch_size, embedding_size)
    positive_key = np.random.randn(batch_size, embedding_size)
    negative_keys = np.random.randn(num_negative, embedding_size)
    # 归一化
    query = query / np.linalg.norm(query, axis=1, keepdims=True)
    positive_key = positive_key / np.linalg.norm(positive_key, axis=1, keepdims=True)
    negative_keys = negative_keys / np.linalg.norm(negative_keys, axis=1, keepdims=True)
    query_torch = torch.from_numpy(query)
    positive_key_torch = torch.from_numpy(positive_key)
    negative_keys_torch = torch.from_numpy(negative_keys)
    
    loss_torch = infonce_loss_torch(query_torch, positive_key_torch, negative_keys_torch)
    print(f"InfoNCE loss(PyTorch): {loss_torch.item():.4f}")
    loss_numpy = infonce_loss_numpy(query, positive_key, negative_keys)
    print(f"InfoNCE loss(Numpy): {loss_numpy: 4f}")

    五、注意力机制

    ​​类型​​

    ​​Q/K/V来源​​

    是否共享参数

    典型应用​​

    Self Attention(手撕)

    同序列

    BERT文本编码

    Cross Attention

    Q来自A序列,K/V来自B

    机器翻译、多模态

    双向Attention

    全面捕捉上下文信息

    需要全局理解的场景(BERT)

    Causal Attention

    同序列+三角掩码

    GPT文本生成

    MHA(手撕)

    同序列,多组独立

    通用Transformer

    GQA

    Q分组,组内共享K/V

    部分共享

    Llama 2等大模型

    MQA

    多Q,共享K/V

    是(K/V共享)

    轻量级模型推理

    MLA

    捕获不同粒度的特征

    长文档处理、层次化结构建模(如Hierarchical Transformer)

    1. Self Attention 与 Cross Attention

    • Self Attention
      • ​​原理​​:计算序列中每个位置与其他位置的关联权重,生成上下文感知的表示。公式如,其中 Q,K,V 来自同一输入序列。
      • 特点​​:捕捉序列内部依赖关系,无方向性限制。
      • 应用​​:Transformer的核心组件,适用于文本/图像等序列数据(如BERT、ViT)。

    • Cross Attention
      • ​​原理​​:一个序列的Q与另一序列的K,V交互,例如解码器关注编码器输出。
      • ​​特点​​:建立跨序列关系(如源语言到目标语言)。
      • ​​应用​​:机器翻译(Seq2Seq)、多模态融合(文本-图像对齐)。

    2. 双向Attention 与 Casual Attention

    • 双向Attention
      • ​​原理​​:自注意力的特例,允许每个位置同时关注前后文(无方向限制)。
      • ​​特点​​:全面捕捉上下文信息。
      • ​​应用​​:需要全局理解的场景(如BERT的预训练)。
    • Casual Attention
      • 原理​​:通过掩码限制当前位置仅关注之前的位置(未来信息不可见)。
      • ​​特点​​:保证自回归生成的时序性。
      • ​​应用​​:文本生成(如GPT)、自回归模型。

    3. Sclaed Dot-Product Attention 与 Multi-Head Attention(MHA)

    • Sclaed Dot-Product Attention(常考手撕)

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import math
    import numpy as np
    np.random.seed(42)
    torch.manual_seed(42)
    
    class DotProductAttention(nn.Module):
        def __init__(self, d_model):
            super().__init__()
            self.scale = math.sqrt(d_model)
    
        def forward(self, q, k, v, mask=None):
            scores = torch.matmul(q, k.transpose(-2, -1)) / self.scale
            if mask is not None:
                scores = scores.masked_fill(mask == 0, -1e9)
            weights = F.softmax(scores, dim=-1)
            output = torch.matmul(weights, v)
            return output, weights
    
    
    batch_size, seq_len, d_model = 4, 12, 512
    attn = DotProductAttention(d_model)
    q = torch.randn(batch_size, seq_len, d_model)
    k = v = torch.randn(batch_size, seq_len, d_model)  # 简单起见,k和v相同
    output, weights = attn(q, k, v)
    print(f"输入: q{k.shape}, k{k.shape}, v{v.shape}")
    print(f"输出: {output.shape}, 权重形状: {weights.shape}")
    print("第一个样本的注意力权重:\n", weights[0].detach().numpy().round(3))
    • Multi-Head Attention(MHA
      • ​​原理​​:将 Q,K,V 投影到多个子空间,并行计算多个自注意力头,拼接后融合如下,每个头关注不同方面的特征。
      • ​​特点​​:增强模型捕捉多样化模式的能力。
      • ​​应用​​:几乎所有Transformer变体(如GPT、BERT)。

    • MHA手撕代码(常考手撕)
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    class MultiHeadAttention(nn.Module):
        def __init__(self, dim, n_head, dropout=0.1):
            super(MultiHeadAttention, self).__init__()
            self.dim = dim
            self.head_dim = dim // n_head
            self.n_head = n_head
            assert self.dim == self.head_dim * n_head
            self.linear_q = nn.Linear(dim, dim)
            self.linear_k = nn.Linear(dim, dim)
            self.linear_v = nn.Linear(dim, dim)
            self.dropout = nn.Dropout(dropout)
            self.fc_out = nn.Linear(dim, dim)
    
        def forward(self, x, mask=None):
            b, t, d = x.size()
            Q = self.linear_q(x)
            K = self.linear_k(x)
            V = self.linear_v(x)
            
            # Reshape and transpose for multi-head attention
            Q = Q.view(b, t, self.n_head, self.head_dim).transpose(1, 2)
            K = K.view(b, t, self.n_head, self.head_dim).transpose(1, 2)
            V = V.view(b, t, self.n_head, self.head_dim).transpose(1, 2)
            
            # Scaled dot-product attention
            score = torch.matmul(Q, K.transpose(2, 3)) / torch.sqrt(torch.tensor(self.head_dim, dtype=torch.float32))
            
            # Apply mask (for decoder)
            if mask is not None:
                mask = mask.unsqueeze(1)  # Add head dimension
                score = score.masked_fill(mask == 0, -1e9)
            
            # Softmax and dropout
            score = F.softmax(score, dim=-1)
            if self.dropout is not None:
                score = self.dropout(score)
            
            # Combine heads and project back to original dimension
            output = torch.matmul(score, V).transpose(1, 2).contiguous().view(b, t, d)
            output = self.fc_out(output)
            return output
    
    def generate_mask(len_seq):
        """生成下三角掩码矩阵(用于Decoder的自回归掩码)"""
        return torch.tril(torch.ones(len_seq, len_seq)).bool()
    
    # 示例用法
    embed_dim = 512
    num_heads = 8
    model = MultiHeadAttention(embed_dim, num_heads)
    x = torch.randn(16, 10, 512)  # 输入形状: [batch_size, seq_len, dim]
    mask = generate_mask(10).unsqueeze(0).expand(16, 10, 10)  # 扩展掩码到batch维度
    output = model(x, mask)
    print(output.shape)  # 输出: torch.Size([16, 10, 512])

    4. 分组查询注意力(Grouped-Query Attention, GQA)​

    • 原理​​:折中方案,将查询头分组,每组共享键和值头(介于MHA和MQA之间)。
    • ​​特点​​:平衡计算效率和模型性能。
    • ​​应用​​:大模型推理优化(如Llama 2的70B版本)。
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    
    class GroupedQueryAttention(nn.Module):
        def __init__(self, dim, n_head, n_group=2, dropout=0.1):
            super(GroupedQueryAttention, self).__init__()
            self.dim = dim
            self.head_dim = dim // n_head
            self.n_head = n_head
            self.n_group = n_group
            assert n_head % n_group == 0, "n_head must be divisible by n_group"
            assert self.dim == self.head_dim * n_head
            
            # 分组参数
            self.heads_per_group = n_head // n_group
            
            # 投影层
            self.linear_q = nn.Linear(dim, dim)  # 每个头独立的Q
            self.linear_k = nn.ModuleList([nn.Linear(dim, self.head_dim * self.heads_per_group) for _ in range(n_group)])
            self.linear_v = nn.ModuleList([nn.Linear(dim, self.head_dim * self.heads_per_group) for _ in range(n_group)])
            
            self.dropout = nn.Dropout(dropout)
            self.fc_out = nn.Linear(dim, dim)
    
        def forward(self, x, mask=None):
            b, t, d = x.size()
            
            # 投影Q (和MHA一样)
            Q = self.linear_q(x).view(b, t, self.n_head, self.head_dim).transpose(1, 2)  # [b, h, t, d_h]
            
            # 分组处理K和V
            K_list, V_list = [], []
            for g in range(self.n_group):
                # 获取当前组的头范围
                start = g * self.heads_per_group
                end = (g + 1) * self.heads_per_group
                
                # 投影当前组的K和V
                K_g = self.linear_k[g](x).view(b, t, self.heads_per_group, self.head_dim).transpose(1, 2)  # [b, h_g, t, d_h]
                V_g = self.linear_v[g](x).view(b, t, self.heads_per_group, self.head_dim).transpose(1, 2)  # [b, h_g, t, d_h]
                
                K_list.append(K_g)
                V_list.append(V_g)
            
            K = torch.cat(K_list, dim=1)  # [b, h, t, d_h]
            V = torch.cat(V_list, dim=1)  # [b, h, t, d_h]
            
            # 注意力计算
            score = torch.matmul(Q, K.transpose(2, 3)) / torch.sqrt(torch.tensor(self.head_dim, dtype=torch.float32))
            
            if mask is not None:
                mask = mask.unsqueeze(1)  # 添加头维度
                score = score.masked_fill(mask == 0, -1e9)
            
            score = F.softmax(score, dim=-1)
            if self.dropout is not None:
                score = self.dropout(score)
            
            output = torch.matmul(score, V).transpose(1, 2).contiguous().view(b, t, d)
            output = self.fc_out(output)
            return output
    
    def generate_mask(len_seq):
        """生成下三角掩码矩阵(用于Decoder的自回归掩码)"""
        return torch.tril(torch.ones(len_seq, len_seq)).bool()
    
    # 测试代码
    batch_size, num_heads, seq_len, embed_dim = 16, 8, 10, 512
    x = torch.randn(batch_size, seq_len, embed_dim)
    mask = generate_mask(seq_len).unsqueeze(0).expand(batch_size, seq_len, seq_len)
    gqa = GroupedQueryAttention(embed_dim, num_heads)
    print("GQA output:", gqa(x, mask).shape)  # [16, 10, 512]

    5. 多查询注意力(Multi-Query Attention, MQA)​

    • 原理​​:多个查询头(Q)共享同一组键(K)和值(V)头,减少计算量。
    • ​​特点​​:牺牲部分表达能力以提升推理速度。
    • ​​应用​​:资源受限场景(如边缘设备部署的轻量级模型)。
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    class MultiQueryAttention(nn.Module):
        def __init__(self, dim, n_head, dropout=0.1):
            super(MultiQueryAttention, self).__init__()
            self.dim = dim
            self.head_dim = dim // n_head
            self.n_head = n_head
            assert self.dim == self.head_dim * n_head
            
            # 共享的K和V投影(MQA核心)
            self.linear_q = nn.Linear(dim, dim)  # 每个头独立的Q
            self.linear_k = nn.Linear(dim, self.head_dim)  # 共享的K
            self.linear_v = nn.Linear(dim, self.head_dim)  # 共享的V
            self.dropout = nn.Dropout(dropout)
            self.fc_out = nn.Linear(dim, dim)
    
        def forward(self, x, mask=None):
            b, t, d = x.size()
            # 投影Q/K/V
            Q = self.linear_q(x).view(b, t, self.n_head, self.head_dim).transpose(1, 2)  # [b, h, t, d_h]
            K = self.linear_k(x).unsqueeze(1).expand(-1, self.n_head, -1, -1)  # [b, h, t, d_h]
            V = self.linear_v(x).unsqueeze(1).expand(-1, self.n_head, -1, -1)  # [b, h, t, d_h]
            
            # Scaled dot-product attention
            score = torch.matmul(Q, K.transpose(2, 3)) / torch.sqrt(torch.tensor(self.head_dim, dtype=torch.float32))
            if mask is not None:
                mask = mask.unsqueeze(1)  # 添加头维度
                score = score.masked_fill(mask == 0, -1e9)
            
            score = F.softmax(score, dim=-1)
            if self.dropout is not None:
                score = self.dropout(score)
            
            output = torch.matmul(score, V).transpose(1, 2).contiguous().view(b, t, d)
            output = self.fc_out(output)
            return output
    def generate_mask(len_seq):
        """生成下三角掩码矩阵(用于Decoder的自回归掩码)"""
        return torch.tril(torch.ones(len_seq, len_seq)).bool()
    
    # 测试代码
    batch_size, num_heads, seq_len, embed_dim = 16, 8, 10, 512
    x = torch.randn(batch_size, seq_len, embed_dim)
    mask = generate_mask(seq_len).unsqueeze(0).expand(batch_size, seq_len, seq_len)
    mqa = MultiQueryAttention(embed_dim, num_heads)
    print("MQA output:", mqa(x, mask).shape)  # [16, 10, 512]

    6. 多层注意力(Multi-Head Latent Attention, MLA)​

    • ​​原理​​:通过低秩矩阵将KV Cache压缩为潜在问量,显者降低了KV Cache缓存占用和计算成本,提高了推理速度和吞吐量。(deepseek v2)
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    class MultiHeadLatentAttention(nn.Module):
        def __init__(self, input_dim, latent_dim, n_head, dropout=0.1):
            """
            Args:
                input_dim: Dimension of input features
                latent_dim: Dimension of latent space
                n_head: Number of attention heads
                dropout: Dropout probability
            """
            super(MultiHeadLatentAttention, self).__init__()
            self.input_dim = input_dim
            self.latent_dim = latent_dim
            self.n_head = n_head
            self.head_dim = latent_dim // n_head
            
            assert self.latent_dim == self.head_dim * n_head, "latent_dim must be divisible by n_head"
            
            # Projections from input space to latent space
            self.linear_q = nn.Linear(input_dim, latent_dim)
            self.linear_k = nn.Linear(input_dim, latent_dim)
            self.linear_v = nn.Linear(input_dim, latent_dim)
            
            # Latent space projections
            self.latent_proj_q = nn.Linear(latent_dim, latent_dim)
            self.latent_proj_k = nn.Linear(latent_dim, latent_dim)
            
            self.dropout = nn.Dropout(dropout)
            self.fc_out = nn.Linear(latent_dim, input_dim)  # Project back to input dimension
            
        def forward(self, x, mask=None):
            """
            Args:
                x: Input tensor of shape [batch_size, seq_len, input_dim]
                mask: Optional mask tensor of shape [batch_size, seq_len, seq_len]
                
            Returns:
                Output tensor of shape [batch_size, seq_len, input_dim]
            """
            b, t, d = x.size()
            
            # Project input to latent space
            Q = self.linear_q(x)  # [b, t, latent_dim]
            K = self.linear_k(x)  # [b, t, latent_dim]
            V = self.linear_v(x)  # [b, t, latent_dim]
            
            # Further project queries and keys in latent space
            Q_latent = self.latent_proj_q(Q)  # [b, t, latent_dim]
            K_latent = self.latent_proj_k(K)  # [b, t, latent_dim]
            
            # Reshape for multi-head attention
            Q_latent = Q_latent.view(b, t, self.n_head, self.head_dim).transpose(1, 2)  # [b, n_head, t, head_dim]
            K_latent = K_latent.view(b, t, self.n_head, self.head_dim).transpose(1, 2)  # [b, n_head, t, head_dim]
            V = V.view(b, t, self.n_head, self.head_dim).transpose(1, 2)  # [b, n_head, t, head_dim]
            
            # Scaled dot-product attention in latent space
            scores = torch.matmul(Q_latent, K_latent.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.head_dim, dtype=torch.float32))
            
            # Apply mask if provided
            if mask is not None:
                mask = mask.unsqueeze(1)  # Add head dimension
                scores = scores.masked_fill(mask == 0, -1e9)
            
            # Softmax and dropout
            attn_weights = F.softmax(scores, dim=-1)
            if self.dropout is not None:
                attn_weights = self.dropout(attn_weights)
            
            # Apply attention to values
            output = torch.matmul(attn_weights, V)  # [b, n_head, t, head_dim]
            
            # Combine heads and project back to input dimension
            output = output.transpose(1, 2).contiguous().view(b, t, self.latent_dim)
            output = self.fc_out(output)  # [b, t, input_dim]
            
            return output
    
    def generate_mask(len_seq):
        """Generate lower triangular mask matrix (for autoregressive masking in decoder)"""
        return torch.tril(torch.ones(len_seq, len_seq)).bool()
    
    # 使用案例
    num_heads, input_dim, latent_dim = 8, 512, 256
    model = MultiHeadLatentAttention(input_dim, latent_dim, num_heads)
    x = torch.randn(16, 10, input_dim)  # Input shape: [batch_size, seq_len, input_dim]
    mask = generate_mask(10).unsqueeze(0).expand(16, 10, 10)
    output = model(x, mask)
    print(output.shape)  # Output: torch.Size([16, 10, 512])

      六、位置编码(正余弦PE,ROPE,RPE,ROPE+ 2D RPE)

      场景

      推荐方法

      代表模型

      RoPE

      目前LLM的主流选择,外推能力强

      LLaMA、BaiChuan、ChatGLM、GPT-NeoX

      RPE(相对位置编码)

      固定长度/局部依赖强(如 BERT、T5、Swin Transformer)

      T5、Swin Transformer

      RoPE(文本)+ 2D RPE(视觉)

      多模态模型(文本+视觉)​​

      BLIP-2(Q-Former)、Fuyu-8B

      ALiBi/NTK-aware RoPE

      长序列推理,长文本优化

      BLOOM、CodeLlama、Mistral

      LAPR(可学习绝对位置编码)

      早期模型,现在逐渐被淘汰;

      外推能力差;无法显式建模相对位置关系

      GPT-2、原始BERT

      1. 绝对位置编码(正余弦位置编码)

      • 对于偶数维度(即2*i位置);对于奇数维度(即2*i+1位置),其中i = d_model // 2。

      import torch
      import math
      
      def position_encoding(seq_len, d_model):
          # 创建一个空的张量用于存储位置编码
          position = torch.arange(0, seq_len).unsqueeze(1).float()  # [seq_len, 1] --> (10, 1)
          dim = torch.arange(0, d_model // 2).float()  # [d_model // 2]   # (1, 8)
          # 计算位置编码
          encoding = position / (10000 ** (2 * dim / (d_model)))  # [10, 16 // 2]
          # 分别计算sin和cos
          sin_enc, cos_enc = torch.sin(encoding), torch.cos(encoding)  # sin编码, cos编码   
          print(sin_enc.shape)
          # 将sin和cos交替插入,得到完整的编码(sin值放到偶数位置, cos值放到奇数位置)
          encoding = torch.zeros(seq_len, d_model)  # [seq_len, d_model]
          encoding[:, 0::2], encoding[:, 1::2] = sin_enc, cos_enc
          return encoding
      
      seq_len, d_model = 10, 16  # 序列长度, 嵌入维度
      encoding = sinusoidal_position_encoding(seq_len, d_model)
      print("Sin_Cos PE:\n", encoding)

      2. 旋转位置编码(ROPE)

      • Reference

              (1) 大模型Embedding整明白了!RoPE位置编码真简单!_哔哩哔哩_bilibili

              (2) 十分钟读懂旋转编码(RoPE)

      • 核心思想:将位置信息编码为旋转矩阵,作用于查询(Query)和键(Key)的注意力计算中,隐式融合绝对位置(θj = 10000−2j/d)与相对位置(m-n)的双重特性。ROPE 位置编码通过将一个向量旋转某个角度,为其赋予位置信息。对Query和Key的每个token维度对 (xi, xi+1) 应用旋转矩阵,其中θi = 10000−2i/d(频率递减)。
      • 优点:
        • 外推性强:支持超长序列推理(如LLaMA-2支持4k→16k扩展);
        • 远程衰减:通过波长设计自动衰减远发离依赖;
        • 数学等价:通过旋转操作实现相对位置偏移的数学等价性。
      • 使用场景:
        • 长文本生成(如Qwen系列模型),需处理超长上下文(如32Ktoken以上);
        • 多模态任务(如图文对齐),需灵活适配不同态的序列长度。
      • 关键公式:

      ### 手撕ROPE
      import torch
      import torch.nn as nn
      import numpy as np
      np.random.seed(42)
      torch.manual_seed(42)
      
      
      class RotaryEmbedding(nn.Module):
          def __init__(self, d_model, num_heads, base=10000, max_len=512):
              super().__init__()
              self.head_dim = d_model // num_heads
              half_dim = self.head_dim // 2
              pos = torch.arange(max_len).float()
              freqs = 1.0 / (base ** (2 * torch.arange(half_dim).float() / self.head_dim))
              angles = torch.einsum("i,j->ij", pos, freqs)  # m*θ  [max_len, half_dim]
              sin = torch.sin(angles).repeat_interleave(2, -1)   # sin(m*θ)
              cos = torch.cos(angles).repeat_interleave(2, -1)   # cos(m*θ)
              self.register_buffer("cos", cos[None, :, :])  # [1, max_len, head_dim]
              self.register_buffer("sin", sin[None, :, :])  # [1, max_len, head_dim]
      
          def forward(self, q):  # q: [B, T, D]
              B, T, D = q.shape
              q = q.view(B, T, -1, self.head_dim).transpose(1, 2)  # [B, H, T, D/H]
              cos = self.cos[:, :T, :].unsqueeze(1)  # [1, 1, T, D/H]
              sin = self.sin[:, :T, :].unsqueeze(1)
              # 把 q(形状 [B, H, T, D/H])分成两个子张量:
              # q1: 取偶数维度(即第 0、2、4…),q2: 取奇数维度(即第 1、3、5…)
              q1, q2 = q[..., ::2], q[..., 1::2]  
              q_rot = torch.stack([q1 * cos[..., ::2] - q2 * sin[..., ::2],
                                   q1 * sin[..., ::2] + q2 * cos[..., ::2]], dim=-1)
              return q_rot.flatten(-2, -1)  # [B, H, T, (D/H)/2, 2]--> [B, H, T, D/H]
      
      # 示例调用
      bs, num_heads, T, d_model = 4, 8, 128, 512
      q = torch.randn(bs, seq_len, d_model)
      rope = RotaryEmbedding(d_model, num_heads)
      q_rope = rope(q)
      q_rope.shape   # [4, 8, 128, 64]
      
      • ROPE V.S. 绝对PE(正余弦编码)

      特性​​

      RoPE​​

      绝对PE(LAPR)​​

      位置信息注入

      通过旋转融入Q/K计算(动态)

      直接加到输入上(静态)

      相对位置感知

      ✅ 自动编码相对距离

      ❌ 需模型自行学习

      外推能力

      ✅ 支持任意长度(旋转矩阵普适)

      ❌ 超出训练长度时失效

      计算效率

      O(1)(无需存储位置矩阵)

      O(L)(需存储位置嵌入)

      适用场景​​

      大模型、长文本

      固定长度任务(如BERT)

      • 为什么理论上可以无限外推
        • 数学原理:RoPE的旋转矩阵性质保证:对于任意位置差 k=m−n,旋转角度 θj​k 始终有效,与 m,n 的绝对值无关。
        • 频率衰减设计:高频(小 j)编码局部信息,低频(大 j)编码全局信息,确保长程依赖仍能被捕捉。
        • 工程表现:即使推理时输入远超训练长度(如训练4k,推理32k),RoPE无需调整仍能保持性能(如LLaMA-2的上下文扩展)。
      • 无限长输入的原因与解决方案:原因——注意力计算复杂度,原始自注意力的 O(L2) 计算成本;训练数据偏差,长文本的稀疏性使模型未充分学习远距离依赖。解决方案——稀疏注意力,如Longformer的滑动窗口;外推优化,NTK-aware RoPE:动态调整频率基θj​(如CodeLlama);线性插值,将位置索引 m 压缩为 m/α(α 为缩放因子)。
      • 怎么控制image token的长度
        • Patch合并:ViT将图像分割为 N×N patches(如14×14=196),通过卷积步长或池化减少token数。
        • 可学习查询压缩:Q-Former用少量可学习query(如32个)通过交叉注意力聚合视觉特征。
        • Perceiver Resampler:使用固定数量的latent token(如64)压缩原始视觉token。

      七、Decoder解码

      策略​​

      优点​​

      缺点​​

      适用场景​​

      Greedy Search

      简单高效

      缺乏多样性

      确定性任务(如机器翻译)

      Beam Search

      更全局化的决策

      计算开销大,可能生成通用文本,改进用n-gram策略。

      文本摘要、翻译

      Temperature Sampling

      灵活控制多样性

      需调参 T

      开放域生成(如对话)

      Top-k Sampling

      过滤低质量词

      k 固定,可能不适应所有上下文

      通用文本生成

      Top-p Sampling

      动态候选词数量,更鲁棒

      需调参 p

      创意写作、故事生成

      1. Greedy Search

      • ​​原理​​:每一步选择当前概率最高的词,即 argmax(P(w|context));
      • ​​特点​​:计算高效,但可能陷入重复或局部最优;缺乏多样性,生成结果可能过于保守。
      def greedy_search(logits):
          return torch.argmax(logits, dim=-1)
      # 示例
      logits = torch.tensor([[0.1, 0.5, 0.4]])
      print(greedy_search(logits))  # 输出: tensor([1])
      • 原理​​:保留Top-K(beam_width)个候选序列,每一步扩展这些序列并保留总概率最高的K个;
      • ​​特点​​:比贪心搜索更全局化,但可能生成过于通用的文本(使用n-gram策略改进);超参数 beam_width 影响效果和计算开销。
      def beam_search(logits, beam_width=2):
          probs, indices = torch.topk(logits.softmax(dim=-1), beam_width, dim=-1)
          return indices, probs
      # 示例
      logits = torch.tensor([[0.1, 0.5, 0.4]])
      indices, probs = beam_search(logits)
      print(indices)  # 输出: tensor([[1, 2]])

      3. Temperature Sampling

      • ​原理​​:通过温度参数 T 调整概率分布:
        • T=1:原始分布;
        • T>1:平滑分布(更多多样性);
        • T<1:尖锐分布(更确定性);
        • P(w) = exp(logits/T) / sum(exp(logits/T))
      def temperature_sampling(logits, temperature=1.0):
          probs = torch.softmax(logits / temperature, dim=-1)
          return torch.multinomial(probs, num_samples=1)    # 概率分布采样
      # 示例
      logits = torch.tensor([[1.0, 2.0, 3.0]])
      print(temperature_sampling(logits, temperature=0.5))  # 可能输出: tensor([2])

      4. Top-k Sampling(手撕)

      • 原理:仅从概率最高的前 k 个词中采样,过滤低概率词;
      • 特点:平衡多样性与质量,但 k 是固定值,可能在某些上下文下不合适。
      def top_k_sampling(logits, k=2):
          values, indices = torch.topk(logits, k)
          probs = torch.softmax(values, dim=-1)
          return indices[torch.multinomial(probs, 1)]
      # 示例
      logits = torch.tensor([[1.0, 2.0, 3.0, 4.0]])
      print(top_k_sampling(logits, k=2))  # 可能输出: tensor([3])

      5. Top-p Sampling(手撕)

      • 原理:从累积概率超过阈值 p 的最小词集合中采样;
      • 特点:动态调整候选词数量,适应不同上下文。
      def top_p_sampling(logits, p=0.9):
          probs = torch.softmax(logits, dim=-1)
          sorted_probs, sorted_indices = torch.sort(probs, descending=True)
          cum_probs = torch.cumsum(sorted_probs, dim=-1)
          mask = cum_probs <= p
          mask = torch.cat([torch.ones_like(mask[:1]), mask[:-1]], dim=0)
          filtered_probs = sorted_probs * mask
          sampled_idx = torch.multinomial(filtered_probs, 1)
          return sorted_indices[sampled_idx]
      # 示例
      logits = torch.tensor([[0.1, 0.3, 0.2, 0.4]])
      print(top_p_sampling(logits, p=0.8))  # 可能输出: tensor([3])

      八、NLP、CV、AIGC、MLLMs经典算法

      (写不动了,后面再填坑吧......)

      1. NLP

              rnn, lstm, attention, bert, gpt, transformer;

      2. CV

              cnn, vit, resnet, clip, blip, yolo系列, fastcnn, detr, inception, sam, dino;

      3. AIGC

              VAE系列, GAN系列,Text-Guided系列, SD系列-ddpm/ddim/ldm/repaint-fastpaint论文,SD优劣&常见的采样方式(ddim,dpm++,lcm,turbo)&原理&loss函数,sdxl相对sd的改进方式;

      4. MLLMs

              CLIP,SigLIP,EVA-CLIP,ALBEF,BLIP,BLIP2,Instruct-BLIP,Flamingo,MiniCPM-V,LlaVA,CogVLM,Qwen-VL系列,InternVL系列,Deepseek-多模系列;

              在“ 多模态学习路线(3)—— MLLMs主流模型 ”里会更新 & 详细介绍。

      ### 共振稀疏分解在对比学习中的应用及其作用 共振稀疏分解(Resonance Sparse Decomposition, RSD)是一种基于信号稀疏表示的技术,其主要目的是将复杂的信号或数据分解为若干个具有物理意义的基础成分。这种方法通常用于处理非平稳信号、图像或其他高维数据集,并能够有效提取出隐藏的关键特征[^1]。 #### 1. **对比学习背景** 对比学习(Contrastive Learning)是无监督学习的一种重要范式,旨在通过最大化正样本对之间的相似性和最小化负样本对之间的相似性来构建有意义的数据表示。这一方法近年来在计算机视觉、自然语言处理等多个领域取得了显著成果。具体来说,对比学习依赖于设计合适的损失函数(如InfoNCE Loss),并通过优化这些函数实现更好的特征嵌入效果[^4]。 #### 2. **RSD在对比学习中的角色** 当我们将共振稀疏分解引入到对比学习框架时,它可以发挥以下几个关键作用: - **增强特征表达能力** 利用RSD技术可以先对输入数据进行预处理,将其转化为一组更易于区分的基础向量组合形式。这样做的好处在于不仅减少了冗余信息的影响,还使得后续网络更容易捕捉到不同类别间本质差异所在之处[^3]。 - **降低噪声干扰** 如同小波变换那样[RSD也具备一定的去噪功能](#),尤其是在面对含有大量随机扰动项的情况下尤为突出。经过这样的初步净化之后再送入对比学习模块,则有助于提高整体系统的鲁棒性以及泛化性能[^2]。 - **促进跨模态融合** 当涉及到多源异构数据联合建模的时候——例如医学影像分析里的PET/SPECT共配准任务——单独依靠传统CNN可能难以充分挖掘两者之间潜在关联规律。而借助RSD则可分别针对每种类型的资料执行独立却相互补充式的编码操作,进而达到更高层次上的语义一致性匹配目的[^3]。 --- 以下是关于如何结合这两种理论的一个简单伪代码示例: ```python from sklearn.decomposition import DictionaryLearning import numpy as np class ResonanceSparseDecomposer: def __init__(self, n_components=100): self.dl_model = DictionaryLearning(n_components=n_components) def fit_transform(self, X): sparse_codes = self.dl_model.fit_transform(X) return sparse_codes def contrastive_loss(anchor_embeddings, positive_embeddings, negative_embeddings, margin=1.0): pos_distance = ((anchor_embeddings - positive_embeddings)**2).sum(axis=-1) neg_distance = ((anchor_embeddings - negative_embeddings)**2).sum(axis=-1) loss = (pos_distance + margin - neg_distance).clip(min=0.).mean() return loss if __name__ == "__main__": rsd = ResonanceSparseDecomposer() # Assume we have three sets of data points sampled from different distributions. anchors = np.random.randn(100, 512) # Anchor samples positives = np.random.randn(100, 512) # Positive counterparts to 'anchors' negatives = np.random.randn(100, 512) # Negative examples w.r.t both above groups. anchor_sparse_reprs = rsd.fit_transform(anchors) pos_sparse_reprs = rsd.transform(positives) neg_sparse_reprs = rsd.transform(negatives) total_contrastive_loss = contrastive_loss( anchor_sparse_reprs, pos_sparse_reprs, neg_sparse_reprs ) print("Total Contrastive Loss:", total_contrastive_loss) ``` ---
      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值