【深度学习】小批量随机梯度下降(Mini-batch SGD)

算法步骤

  1. 初始化参数:随机选择一组模型参数的初始值。
  2. 迭代更新 :对参数进行多次迭代,每次迭代的目标是降低损失函数的值。
    • 在每次迭代中,从固定数量的训练数据样本中随机抽取一个小批量(mini-batch)。
    • 计算该小批量中所有样本的平均损失关于模型参数的导数(梯度)。
    • 使用预先设定的学习率(一个正数),将梯度乘以学习率作为参数的减量,更新参数。

举例

假设你要训练一个模型,根据学生的"学习时间"和"刷题数量"预测"考试分数"。

  • 数据:你有 1000 个学生的数据(比如学生 A 学习 5 小时,刷题 20 道,考了 80 分)。
  • 目标 :找到最佳的参数(比如权重 <math xmlns="http://www.w3.org/1998/Math/MathML"> w 1 , w 2 w_1, w_2 </math>w1,w2 和偏置 <math xmlns="http://www.w3.org/1998/Math/MathML"> b b </math>b),让模型预测的分数尽可能接近真实分数。

传统方法的问题

  • 全量梯度下降:每次计算都用全部 1000 个数据,计算量太大,像每次下山都要绕山走一圈才决定方向,太慢!

  • 随机梯度下降(SGD):每次只用 1 个数据,比如只看学生 A 的数据调整参数,但噪声太大,像闭着眼睛乱跳,可能跳错方向。

使用 小批量随机梯度下降

  • 折中方案 :每次随机选一小批数据(比如 32 个学生),计算他们的平均误差,再调整参数。
    • 优点:比全量数据快,比单个数据更稳定,就像每次看一小片区域的地形再决定下山方向。

计算步骤

(1)初始化参数

  • 随机给模型参数(比如权重 (w_1, w_2) 和偏置 (b))赋初始值,比如 (w_1=0.5, w_2=0.3, b=10)。

(2)随机划分小批量(Mini-batch)

  • 把训练数据(比如 1 万条)随机打乱,分成多个小批量(比如每批 32 条)。
    • 举例:1 万条数据 → 划分成 313 个小批量(每批 32 条,最后一批可能不足 32 条)。

(3)迭代更新参数

对每个小批量重复以下步骤:

  1. 计算梯度 :用当前小批量的数据,计算损失函数对每个参数的梯度(即"下山方向")。
    • 梯度公式 (以线性回归为例):
      <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 梯度 = 1 批量大小 ∑ i ∈ 小批量 ( 预测值 − 真实值 ) × 输入特征 \text{梯度} = \frac{1}{\text{批量大小}} \sum_{i \in \text{小批量}} (\text{预测值} - \text{真实值}) \times \text{输入特征} </math>梯度=批量大小1i∈小批量∑(预测值−真实值)×输入特征
  2. 更新参数
    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> w ← w − 学习率 × 梯度 w \leftarrow w - \text{学习率} \times \text{梯度} </math>w←w−学习率×梯度

(4)重复直到收敛

  • 不断重复步骤(2)-(3),直到损失函数不再明显下降,或达到预设的迭代次数。

关键概念解释

学习率(Learning Rate)

  • 比喻 :就像下山时的步长。
    • 步子太大(学习率过高):可能直接跨过最低点,甚至冲上对面山坡。
    • 步子太小(学习率过低):下山太慢,浪费时间。
    • 调参目标:找到一个"刚刚好"的步长。

批量大小(Batch Size)

  • 比喻 :就像每次下山前观察的区域大小。
    • 区域太大(Batch Size=1000):计算量大,但方向准确。
    • 区域太小(Batch Size=1):计算快,但方向可能不准。
    • 折中选择:通常选 32、64、128 等较小的批量。

实现代码

python 复制代码
import torch
import numpy as np
from torch.utils.data import DataLoader, TensorDataset

# =============================== 1. 生成模拟数据(n 个样本) ===============================
def generate_data(n_samples):
    np.random.seed(42)  # 固定随机种子保证结果可复现
    # 房屋面积(40-150 平米),均匀分布生成
    x1 = np.random.uniform(40, 150, n_samples).astype(np.float32)
    # 房间数(1-5 个),整数均匀分布生成后转为浮点数
    x2 = np.random.randint(1, 6, n_samples).astype(np.float32)
    # 真实房价:基础价50万 + 面积*1.2万 + 房间数*30万 + 高斯噪声(均值0,标准差10)
    y = (50 + x1 * 1.2 + x2 * 30 + np.random.normal(0, 10, n_samples)).astype(np.float32)
    return x1, x2, y

n_samples = 1000  # 总样本数
x1, x2, y_true = generate_data(n_samples)

# 将NumPy数组转换为PyTorch张量
x1_tensor = torch.tensor(x1)   # 输入特征1(面积)
x2_tensor = torch.tensor(x2)   # 输入特征2(房间数)
y_tensor = torch.tensor(y_true)  # 真实房价标签
# 输出x1_tensor, x2_tensor, y_tensor
print("x1_tensor:", x1_tensor)
print("=" * 100)
print("x2_tensor:", x2_tensor)
print("=" * 100)
print("y_tensor:", y_tensor)
print("=" * 100)

# 创建数据集和DataLoader(用于小批量加载)
batch_size = 32  # 每个小批量的样本数
dataset = TensorDataset(x1_tensor, x2_tensor, y_tensor)  # 将输入和标签打包为数据集
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)  # 支持随机打乱和批量加载

# =============================== 2. 初始化参数和优化器 ===============================
# 初始化模型参数(权重和偏置),并启用梯度跟踪
w1 = torch.tensor(0.0, requires_grad=True)  # 面积权重
w2 = torch.tensor(0.0, requires_grad=True)  # 房间数权重
b = torch.tensor(0.0, requires_grad=True)   # 偏置项
params = [w1, w2, b]  # 所有参数的列表

# 定义学习率和训练轮次
learning_rate = 0.0001  # 较小的学习率防止梯度更新过大
n_epochs = 100  # 整个数据集迭代的次数

# 使用随机梯度下降优化器(SGD)
optimizer = torch.optim.SGD(params, lr=learning_rate)

# =============================== 3. 训练循环(小批量梯度下降) ===============================
loss_history = []  # 记录每轮的平均损失值

for epoch in range(n_epochs):
    epoch_loss = 0.0  # 累计当前轮次的总损失
    for batch_idx, (x1_batch, x2_batch, y_batch) in enumerate(dataloader):
        # 前向传播:计算当前批量的预测值
        y_pred = w1 * x1_batch + w2 * x2_batch + b
        # 计算均方误差损失(MSE)
        loss = torch.mean((y_pred - y_batch) ** 2)
        epoch_loss += loss.item()  # 累加损失值(转换为Python数值)
        
        # 反向传播与参数更新
        optimizer.zero_grad()  # 清空之前的梯度
        loss.backward()        # 计算当前损失的梯度
        optimizer.step()       # 根据梯度更新参数
    
    # 计算当前轮次的平均损失并记录
    avg_loss = epoch_loss / len(dataloader)  # 总损失除以批次数
    loss_history.append(avg_loss)
    print(f"Epoch [{epoch+1}/{n_epochs}], Loss: {avg_loss:.2f}")

# =============================== 4. 结果分析 ===============================
print("\n训练结果:")
# 输出训练后的参数值(理想值应为w1≈1.2, w2≈30, b≈50)
print(f"最终参数: w1={w1.item():.2f}, w2={w2.item():.2f}, b={b.item():.2f}")
相关推荐
ん贤13 分钟前
单调栈详解【C/C++】
数据结构·c++·算法·贪心算法·单调栈
空雲.28 分钟前
ABC 373
算法·深度优先
点云登山者1 小时前
登山第二十梯:无人机实时自主探索——我是一只小小小鸟
算法·计算机视觉·机器人·无人机·路径规划·激光点云·自主探索
大胆飞猪2 小时前
优选算法训练篇08--力扣15.三数之和(难度中等)
算法·leetcode
<但凡.3 小时前
C++修炼:内存管理
c++·算法
tpoog3 小时前
[贪心算法]买卖股票的最佳时机 && 买卖股票的最佳时机Ⅱ && K次取反后最大化的数组和 && 按身高排序 && 优势洗牌(田忌赛马)
算法·贪心算法
大模型铲屎官3 小时前
支持向量机(SVM):从入门到精通的机器学习利器
开发语言·人工智能·深度学习·算法·机器学习·llm·支持向量机(svm)
小檀6103 小时前
C语言基础08
c语言·算法·排序算法
进击的jerk4 小时前
力扣45.跳跃游戏
开发语言·c++·算法·leetcode·游戏
rigidwill6664 小时前
LeetCode hot 100—颜色分类
数据结构·c++·算法·leetcode·职场和发展