图解人工智能的数学基础(最优化)

本文涉及的产品
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 本文深入解析人工智能中最优化问题的核心:通过最小化损失函数来训练模型。涵盖回归(MSE)与分类(交叉熵)任务的典型损失函数,详解梯度下降原理及BGD、SGD、Mini-batch等算法差异,并介绍Momentum、Adam等现代优化技巧,辅以PyTorch代码实现。

 人工智能中的最优化问题

   机器学习(如训练神经网络)的全部过程,就是寻找一组参数,使得模型在特定任务上的错误率

最小。这,就是最优化问题。

最优化的基本数学模型如下公式所示:

image.gif

image.gif

image.gif


损失函数(Loss Function):衡量“好坏”的标准

目标函数的选择完全取决于你希望模型完成什么任务。不同的任务需要不同的“量尺”来衡量错误

适用于回归问题 (Regression)

回归问题是预测一个连续数值(如房价、股票价格、气温)

均方误差 (Mean Squared Error, MSE)

image.gif 编辑

import torch
import torch.nn as nn
# --- 均方误差损失 (MSELoss) ---
print("--- 均方误差损失 (MSELoss) ---")
# 模拟回归任务的预测值和真实值
# 预测值 (模型的输出)
predictions_mse = torch.tensor([10.0, 12.0, 15.0], requires_grad=True)
# 真实值 (数据的标签)
targets_mse = torch.tensor([11.0, 13.0, 14.0])
# 实例化 MSELoss
mse_loss_fn = nn.MSELoss()
# 计算损失
loss_mse = mse_loss_fn(predictions_mse, targets_mse)
print(f"预测值: {predictions_mse}")
print(f"真实值: {targets_mse}")
print(f"MSE 损失: {loss_mse.item():.4f}") # .item() 获取张量中的标量值
# 演示反向传播 (梯度计算)
loss_mse.backward()
print(f"MSE 损失对预测值的梯度: {predictions_mse.grad}")
print("-" * 30)

image.gif

image.gif

适用于分类问题 (Classification)

分类问题是预测一个离散的类别(如猫/狗、垃圾邮件/非垃圾邮件)

交叉熵损失 (Cross-Entropy Loss)

这是几乎所有现代分类任务(如图像识别、自然语言处理)的首选损失函数

image.gif 编辑

import torch
import torch.nn as nn
# --- 二分类交叉熵损失 (BCELoss 和 BCEWithLogitsLoss) ---
print("--- 二分类交叉熵损失 (BCELoss & BCEWithLogitsLoss) ---")
# 模拟二分类任务的预测值和真实值
# 真实标签 (0 或 1)
targets_bce = torch.tensor([1.0, 0.0, 1.0]) 
# ----------------- 使用 nn.BCELoss -----------------
# 预测概率 (0到1之间)。注意:如果模型输出的是 logits (Logit是Sigmoid之前的原始输出值), 
# 则需要先手动通过 sigmoid 激活函数。
predictions_bce_prob = torch.tensor([0.9, 0.2, 0.8], requires_grad=True) 
bce_loss_fn = nn.BCELoss()
loss_bce_prob = bce_loss_fn(predictions_bce_prob, targets_bce)
print("\n--- 使用 nn.BCELoss (需要手动 Sigmoid) ---")
print(f"预测概率: {predictions_bce_prob}")
print(f"真实标签: {targets_bce}")
print(f"BCELoss 损失: {loss_bce_prob.item():.4f}")
loss_bce_prob.backward()
print(f"BCELoss 对预测概率的梯度: {predictions_bce_prob.grad}")
# ----------------- 使用 nn.BCEWithLogitsLoss (推荐) -----------------
# 模型的原始输出 (logits),无需手动 sigmoid
predictions_bce_logits = torch.tensor([2.2, -1.4, 1.8], requires_grad=True) # 对应 sigmoid(2.2)=0.9, sigmoid(-1.4)=0.2, sigmoid(1.8)=0.8
bce_logits_loss_fn = nn.BCEWithLogitsLoss()
loss_bce_logits = bce_logits_loss_fn(predictions_bce_logits, targets_bce)
print("\n--- 使用 nn.BCEWithLogitsLoss (内部包含 Sigmoid, 更稳定) ---")
print(f"预测 Logits: {predictions_bce_logits}")
print(f"真实标签: {targets_bce}")
print(f"BCEWithLogitsLoss 损失: {loss_bce_logits.item():.4f}")
loss_bce_logits.backward()
print(f"BCEWithLogitsLoss 对 Logits 的梯度: {predictions_bce_logits.grad}")
print("-" * 30)

image.gif

多分类交叉熵损失

image.gif 编辑

import torch
import torch.nn as nn
# --- 多分类交叉熵损失 (CrossEntropyLoss) ---
print("--- 多分类交叉熵损失 (CrossEntropyLoss) ---")
# 模拟三分类任务的预测值和真实值 (例如:猫, 狗, 鸟)
num_classes = 3
# 模型的原始输出 (logits)。假设 batch_size=2
# 第一个样本: 倾向于第0类 (猫)
# 第二个样本: 倾向于第1类 (狗)
predictions_ce_logits = torch.tensor([
    [2.0, 0.5, -1.0],  # 第一个样本的 logits (对应 Cat, Dog, Bird)
    [0.1, 1.5, 0.3]   # 第二个样本的 logits
], requires_grad=True)
# 真实类别索引 (不需要 One-Hot 编码)
# 第一个样本真实是第0类 (猫)
# 第二个样本真实是第1类 (狗)
targets_ce = torch.tensor([0, 1]) 
# 实例化 CrossEntropyLoss
cross_entropy_loss_fn = nn.CrossEntropyLoss()
# 计算损失
loss_ce = cross_entropy_loss_fn(predictions_ce_logits, targets_ce)
print(f"预测 Logits:\n{predictions_ce_logits}")
print(f"真实类别索引: {targets_ce}")
print(f"CrossEntropyLoss 损失: {loss_ce.item():.4f}")
# 演示反向传播
loss_ce.backward()
print(f"CrossEntropyLoss 对预测 Logits 的梯度:\n{predictions_ce_logits.grad}")
print("-" * 30)

image.gif

image.gif


优化算法的核心——梯度下降

目的:求损失函数的最小值和对应的参数

原理:梯度方向是函数值变化最快的方向

image.gif 编辑

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
# --- 1. 定义目标函数和梯度 ---
# 假设目标函数是简单的二次函数 (简化版,类似上图中的盆地)
def loss_function(w, b):
    # J(w, b) = (w - w_optimal)^2 + (b - b_optimal)^2 + offset
    return (w - 0.05)**2 + (b + 0.5)**2 + 0.2
# 定义梯度(偏导数)
def gradient(w, b):
    dw = 2 * (w - 0.05) # 损失函数对 w 的偏导
    db = 2 * (b + 0.5)  # 损失函数对 b 的偏导
    return dw, db
# --- 2. 运行梯度下降并记录历史 ---
def run_gd(start_w, start_b, alpha, n_iterations):
    w, b = start_w, start_b
    history = [(w, b, loss_function(w, b))]
    for _ in range(n_iterations):
        dw, db = gradient(w, b)
        
        # 参数更新核心公式
        w = w - alpha * dw
        b = b - alpha * db
        
        current_loss = loss_function(w, b)
        history.append((w, b, current_loss))
    return np.array(history)
# 设置参数
alpha = 0.05       # 学习率
n_iterations = 50  # 迭代次数 (动画帧数)
start_w, start_b = 0.5, 0.5 # 随机起点
path_history = run_gd(start_w, start_b, alpha, n_iterations)
# --- 3. 3D 可视化设置 ---
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# 生成曲面数据
w_range = np.linspace(path_history[:, 0].min() - 0.1, path_history[:, 0].max() + 0.1, 100)
b_range = np.linspace(path_history[:, 1].min() - 0.1, path_history[:, 1].max() + 0.1, 100)
W, B = np.meshgrid(w_range, b_range)
Z = loss_function(W, B)
# 绘制曲面
ax.plot_surface(W, B, Z, cmap=plt.cm.coolwarm, alpha=0.8, antialiased=True)
# 设置轴标签
ax.set_xlabel('w', fontsize=16)
ax.set_ylabel('b', fontsize=16)
ax.set_zlabel('costs', fontsize=16)
ax.set_title('梯度下降 3D 动态可视化', fontsize=18)
# --- 4. 动画函数 ---
# 初始化路径线和当前点
line, = ax.plot([], [], [], 'k--', alpha=0.6, label='下降路径') # 虚线路径
point, = ax.plot([], [], [], 'o', color='navy', markersize=8, label='当前参数') # 移动的点
def animate(i):
    # 更新虚线路径 (从起点到第 i 步)
    line.set_data(path_history[:i, 0], path_history[:i, 1])
    line.set_3d_properties(path_history[:i, 2])
    
    # 更新当前点的位置
    point.set_data(path_history[i:i+1, 0], path_history[i:i+1, 1])
    point.set_3d_properties(path_history[i:i+1, 2])
    
    # 更新迭代次数标签 (对应您图中的 'epochs: 140000')
    ax.legend([line, point], [f'迭代次数: {i}', '当前参数'], loc='upper left')
    return line, point,
# 5. 创建动画并显示/保存
ani = FuncAnimation(fig, animate, frames=len(path_history), interval=100, blit=False)
# 在 Jupyter/Colab 中,可以使用 HTML(ani.to_jshtml()) 显示
# 在本地,可以使用 ani.save('gradient_descent.gif', writer='pillow') 保存为GIF
plt.show()

image.gif

分类

更新参数                                                                                            局部最优解    

批量梯度下降(BGD)               基于所有样本                                   可能停留  

随机梯度下降(SGD)               基于一个样本                                    可跳出  

小批量梯度下降(MGD)           基于一个小批量样本                         可跳出    

 

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt
# --- 1. 模拟数据 (与之前相同) ---
true_weight = 2.0
true_bias = 3.0
X_train = torch.randn(100, 1) * 10 # 100个样本
y_train = true_weight * X_train + true_bias + torch.randn(100, 1) * 2
# 将数据打包成 TensorDataset
train_dataset = TensorDataset(X_train, y_train)
# --- 2. 定义模型 ---
class LinearRegression(nn.Module):
    def __init__(self):
        super(LinearRegression, self).__init__()
        self.linear = nn.Linear(1, 1)
    def forward(self, x):
        return self.linear(x)
# --- 3. 定义损失函数 ---
criterion = nn.MSELoss()
# --- 4. 训练函数 (封装训练过程,方便复用) ---
def train_model(model_instance, optimizer_instance, data_loader_instance, num_epochs, description):
    loss_history = []
    print(f"\n--- 开始训练: {description} ---")
    
    for epoch in range(num_epochs):
        current_epoch_loss = 0.0
        num_batches = 0
        for batch_X, batch_y in data_loader_instance:
            optimizer_instance.zero_grad()
            outputs = model_instance(batch_X)
            loss = criterion(outputs, batch_y)
            loss.backward()
            optimizer_instance.step()
            
            current_epoch_loss += loss.item()
            num_batches += 1
        
        # 记录每个 epoch 的平均损失
        avg_epoch_loss = current_epoch_loss / num_batches
        loss_history.append(avg_epoch_loss)
        
        if (epoch + 1) % (num_epochs // 5) == 0: # 每 1/5 打印一次
            print(f'Epoch [{epoch+1}/{num_epochs}], Avg Loss: {avg_epoch_loss:.4f}')
            
    print(f"--- 训练完成: {description} ---")
    print(f"最终模型参数: W={model_instance.linear.weight.item():.4f}, B={model_instance.linear.bias.item():.4f}")
    return loss_history
# --- 5. 实例化模型和优化器 (每次实验都重新实例化,避免参数污染) ---
# --- 实验 1: 批量梯度下降 (Batch Gradient Descent, BGD) ---
# batch_size = 总样本数,确保每个 epoch 只进行一次参数更新
model_bgd = LinearRegression()
optimizer_bgd = optim.SGD(model_bgd.parameters(), lr=0.001) # BGD通常需要较小的学习率,否则容易震荡
dataloader_bgd = DataLoader(dataset=train_dataset, batch_size=len(X_train), shuffle=False) # BGD一般不shuffle
loss_history_bgd = train_model(model_bgd, optimizer_bgd, dataloader_bgd, num_epochs=200, description="批量梯度下降 (BGD)")
# --- 实验 2: 随机梯度下降 (Stochastic Gradient Descent, SGD) ---
# batch_size = 1,每次只用一个样本
model_sgd = LinearRegression()
optimizer_sgd = optim.SGD(model_sgd.parameters(), lr=0.01) # SGD通常需要较大的学习率,因为梯度噪声大
dataloader_sgd = DataLoader(dataset=train_dataset, batch_size=1, shuffle=True) # SGD通常shuffle
loss_history_sgd = train_model(model_sgd, optimizer_sgd, dataloader_sgd, num_epochs=200, description="随机梯度下降 (SGD)")
# --- 实验 3: 小批量梯度下降 (Mini-batch Gradient Descent, MGD) ---
# batch_size = 一个合理的小批量大小 (例如 16, 32, 64)
model_mgd = LinearRegression()
optimizer_mgd = optim.SGD(model_mgd.parameters(), lr=0.01) # MGD学习率也比较灵活
dataloader_mgd = DataLoader(dataset=train_dataset, batch_size=16, shuffle=True) # MGD通常shuffle
loss_history_mgd = train_model(model_mgd, optimizer_mgd, dataloader_mgd, num_epochs=200, description="小批量梯度下降 (MGD)")

image.gif

image.gif

优化技巧

小批量梯度下降 (Mini-batch GD):每个批的反向传播后就直接对参数进行更新。

带动量的随机梯度下降 (Momentum):通过计算前几步的滑动平均来计算动量,用于当前步的更新。

Nesterov 加速的梯度下降 (Nesterov Accelerated GD):不算当前位置的梯度,而是计算这个位置大概的梯度。

自适应梯度Adagrad:对每个参数的学习率惊醒不同动态的改变,首先得到的是每个参数的梯度,在更新时候,学习率除于一个正则项。

自适应动量Adam:不仅对梯度进行自适应的调整,也对动量进行自适应的调整。

image.gif

import torch
import torch.nn as nn
import torch.optim as optim
# 假设模型和损失函数已经定义
# model = MyModel() 
# criterion = nn.CrossEntropyLoss()
# 假设 model 已经实例化
# 示例:定义模型参数
model_params = model.parameters() 
learning_rate = 0.001 
# 1. 动量法 (Momentum) 的实现
# PyTorch 的 optim.SGD 可以通过设置 momentum 参数实现动量法
# 通常 momentum=0.9
optimizer_momentum = optim.SGD(model_params, lr=learning_rate, momentum=0.9)
# 2. Nesterov 加速的梯度下降 (NAG) 的实现
# 在 optim.SGD 中设置 nesterov=True
# 注意:只有当 momentum > 0 时,nesterov 才会生效
optimizer_nesterov = optim.SGD(model_params, lr=learning_rate, momentum=0.9, nesterov=True)
# 3. 自适应梯度 (Adagrad) 的实现
# 学习率 lr 仍然需要传入
optimizer_adagrad = optim.Adagrad(model_params, lr=0.01)
# 4. 自适应动量 (Adam) 的实现
# Adam 不需要动量因子,它使用 beta1 和 beta2 参数,默认值通常很好
# lr=0.001 是 Adam 的常见起始学习率
optimizer_adam = optim.Adam(model_params, lr=0.001)

image.gif

image.gif


目录
相关文章
|
16天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
5871 30
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
1天前
|
数据采集 人工智能 前端开发
让 Coding Agent 从黑盒到透明:阿里云 Agent 观测审计数据采集实践
AI Agent 规模化落地带来执行黑盒、行为难追溯、成本难度量三大难题。阿里云基于 OTel 标准,面向 Coding Agent、个人通用助理和框架型 Agent,推出 LoongSuite Pilot、插件及探针等无侵入采集方案,让 Agent 实现可看见、可分析、可审计、可治理。
561 134
|
10天前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1177 2
|
8天前
|
人工智能 安全 定位技术
CodeGraph深度解析 让Claude Code工具调用直降七成的核心原理与实操教程
如今以Claude Code为代表的AI编程智能体已经成为开发者日常编码、项目重构、漏洞修复的必备工具。但在长期使用过程中,几乎所有开发者都会遇到同一个明显痛点:AI虽然具备强大的代码生成与分析能力,却常常陷入盲目探索的循环中。
959 1
|
17天前
|
人工智能 自然语言处理 供应链
|
8天前
|
人工智能 弹性计算 安全
阿里云618活动时间、活动入口、优惠活动详细解读
2026年阿里云618创新加速季已全面开启,作为年度力度最大的云产品促销活动,本次大促覆盖轻量应用服务器、ECS云服务器、GPU云服务器、数据库、AI算力、安全服务、CDN等全品类产品,推出5亿元算力补贴、新用户限时秒杀、普惠满减、企业专享、免费试用、云大使返佣等多重福利,个人开发者、中小企业、AI团队均可享受专属低价。本文将系统梳理2026年阿里云618活动的完整时间节点、官方参与入口、各类优惠细则、使用规则、热门产品推荐及实操代码,帮助用户精准参与、高效省钱,以最低成本完成上云部署。
764 4
|
8天前
|
运维
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
1432 0