news 2026/9/6 1:58:46

深度学习入门:从梯度下降、PyTorch到线性回归实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深度学习入门:从梯度下降、PyTorch到线性回归实战

1. 项目概述:为什么“重学”比“初学”更重要

最近在整理硬盘,翻到了几年前学习《动手学深度学习》时做的笔记。看着那些当时觉得“已经懂了”的公式和代码,现在却有了完全不同的感受。这种感觉,就像重读一本经典小说,在不同的年龄和阅历下,总能发现新的细节和深意。于是,我决定开启一个“重学”计划,不是简单地复习,而是带着这几年在工业界摸爬滚打的经验,重新审视这本经典教材的每一个章节。今天这篇笔记,就从最基础的“深度学习简介”开始。

你可能觉得第一章“简介”没什么好讲的,无非是些概念和历史。但恰恰相反,我认为这一章是全书的地基。很多初学者(包括当年的我)急于跳进代码和模型,忽略了这些“软知识”,导致后续学习像在沙地上盖楼,遇到复杂问题就容易根基不稳。这次重学,我的目标很明确:打通概念与实践的任督二脉。我会结合最新的行业动态(比如大模型热潮对基础概念的冲击)和工程实践中的教训,来重新解读这些基础知识。无论你是刚刚打开这本书的新手,还是和我一样想“温故知新”的老兵,希望这篇笔记都能给你带来一些不一样的启发。

2. 核心思路拆解:从“是什么”到“为什么”与“怎么用”

2.1 超越概念定义:理解深度学习的“生态位”

教材开篇会告诉你,深度学习是机器学习的一个分支,使用多层神经网络。这个定义没错,但太“教科书”了。重学之后,我更喜欢从“解决问题的方式”来理解它。

你可以把传统的机器学习方法(如逻辑回归、SVM)想象成精密的“手工工具”。一个经验丰富的工匠(数据科学家)需要花费大量时间进行“特征工程”——手动设计、筛选、组合数据的特征,就像为不同的木材选择不同的刨子和凿子。工具(模型)本身可能不复杂,但威力高度依赖于工匠的手艺(特征工程能力)。

而深度学习,则像一台“自适应多功能机床”。你喂给它大量的原始数据(比如图片的原始像素、文本的原始字符),它通过多层神经网络,自己学习如何从数据中逐层抽象出有用的特征。底层网络可能识别线条和边缘,中层组合成形状,高层则对应更复杂的概念如“车轮”、“人脸”。它的核心优势在于“端到端”学习,减少了对手工特征工程的依赖。

那么,什么时候该用这台“机床”,什么时候该用“手工工具”呢?这里就涉及到深度学习的“生态位”:

  • 数据量大、结构复杂:如图像、语音、自然语言、视频。这些数据维度高、内部结构复杂(空间、时序关系),手工设计特征极其困难,深度学习的优势巨大。
  • 问题定义相对模糊:如图像分类、机器翻译。我们很难用明确的规则(if-else)来描述“这是一只猫”或如何将中文转化为地道的英文,但深度学习可以从海量样本中学习这种映射关系。
  • 计算资源充足:这是使用“机床”的代价。训练深度模型需要强大的GPU和大量的时间。

注意:不要陷入“深度学习万能论”。对于表格数据、特征清晰、数据量小的场景(比如预测小额信贷风险),一个精心调优的梯度提升树模型(如XGBoost)的性能和效率往往远超复杂的深度学习模型,且更易解释。选择工具,首先要看问题本身。

2.2 关键思想溯源:梯度下降与反向传播的“工程直觉”

教材会介绍梯度下降和反向传播的数学公式。重学时,我建议先建立强烈的“物理直觉”和“工程直觉”。

梯度下降:想象你被蒙上眼睛,身处一个连绵起伏的山丘(损失函数曲面),你的目标是找到最低的山谷(最小化损失)。你唯一能感知的是脚下山坡的倾斜方向(梯度)。梯度下降的策略就是:沿着当前最陡的下坡方向(负梯度方向),迈出一小步(学习率)。重复这个过程,你最终会到达某个谷底。

这里的工程核心在于“学习率”:

  • 学习率太大:步子迈得太大,可能会直接从山谷这边跨到对面山上,导致震荡甚至发散(无法收敛)。
  • 学习率太小:步子太小,下山速度极慢,训练时间漫长,且容易卡在某个小坑里(局部最优)。
  • 实践心得:现代深度学习框架(如PyTorch)提供了像AdamRMSProp这样的自适应优化器,它们可以动态调整每个参数的学习率,相当于给你一双能感知不同地形摩擦力的鞋子,比传统的固定学习率SGD(随机梯度下降)要稳健得多。在初学阶段,可以直接使用torch.optim.Adam作为默认选择,它能解决大部分学习率设置的问题。

反向传播:这是深度学习的“引擎”。它的核心思想是链式法则的巧妙应用。网络前向传播计算预测值和损失,反向传播则负责将损失这个“误差信号”从网络输出层,一层一层地反向传递到每一层,并计算每一层参数(权重和偏置)对这个损失的“贡献度”(梯度)。

一个重要的工程类比:反向传播是一种高效的算法,而不是深度学习独有的“魔法”。它解决了在多层网络中高效计算梯度的问题。在没有反向传播的年代,研究人员可能需要为每种网络结构手动推导复杂的梯度公式,这几乎不可行。反向传播的自动化,使得我们能够轻松构建和训练成百上千层的网络。

2.3 框架选择:PyTorch 为何成为当前主流

《动手学深度学习》最早使用MXNet,后续版本也全面转向PyTorch。这次重学,我自然使用PyTorch。为什么是PyTorch?这背后是设计哲学的不同。

  • 命令式编程(PyTorch) vs 声明式编程(旧框架如Theano, MXNet的符号式API):PyTorch采用“动态图”模式,它的执行就像普通的Python代码,写一行,执行一行,调试起来非常直观。你可以用print()pdb或者IDE的调试器随时查看张量的值。这种“Define-by-Run”的方式,让研究和实验变得异常灵活,尤其适合模型结构动态变化的场景(如循环神经网络RNN)。
  • TensorFlow 2.x 的转变:TensorFlow 1.x的静态图模式虽然部署效率高,但构建和调试极其繁琐。TensorFlow 2.x 全面拥抱Eager Execution(急切执行),并内置Keras高层API,很大程度上是在向PyTorch的易用性靠拢。但目前社区和学术界,PyTorch的活跃度和生态(特别是在最新研究论文的复现上)有显著优势。
  • 实践中的选择:对于初学者、研究人员和需要快速原型验证的团队,PyTorch是更友好的起点。它的API设计非常“Pythonic”,学习曲线平缓。当你需要将模型部署到生产环境时,可以利用PyTorch的TorchScriptONNX格式进行转换和优化,兼顾开发效率和运行性能。

3. 环境搭建与数据操作:避开第一个坑

3.1 开发环境配置:不只是安装PyTorch

很多教程只告诉你pip install torch,但一个稳定、可复现的环境远不止于此。

1. 虚拟环境是必须品:永远不要在系统全局Python环境里直接安装项目依赖。使用condavenv创建独立环境。

# 使用 conda (推荐,尤其对Windows用户和需要管理非Python依赖的情况) conda create -n d2l-zh python=3.9 conda activate d2l-zh # 使用 venv (轻量,纯Python环境) python -m venv d2l-zh # Linux/Mac source d2l-zh/bin/activate # Windows d2l-zh\Scripts\activate

2. PyTorch安装的“正确姿势”:一定要去 PyTorch官网 使用安装命令生成器。你需要根据你的操作系统、包管理工具(pip或conda)、Python版本以及最重要的——CUDA版本来选择命令。CUDA是NVIDIA GPU的并行计算平台,能极大加速训练。

  • 有NVIDIA GPU:在命令行输入nvidia-smi查看CUDA版本(如11.7),然后选择对应的PyTorch安装命令。例如:conda install pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia
  • 只有CPU:选择CUDA版本为None的命令。例如:conda install pytorch torchvision torchaudio cpuonly -c pytorch

3. 配套工具链

  • Jupyter Notebook/Lab:交互式学习的绝佳工具。conda install jupyter jupyterlab
  • 常用数据处理库pip install numpy pandas matplotlib
  • 代码编辑器/IDE:VS Code + Python扩展 + Pylance,或PyCharm。它们能提供代码补全、调试、语法高亮等强大功能。

3.2 张量(Tensor)操作:深度学习的“乐高积木”

张量是PyTorch(也是几乎所有深度学习框架)中最基本的数据结构。你可以把它理解为多维数组。

  • 0维张量:标量(一个数)。
  • 1维张量:向量([1, 2, 3])。
  • 2维张量:矩阵。
  • 3维张量:如RGB图片(通道×高×宽)。
  • 4维张量:如一个批次的图片(批量大小×通道×高×宽)。

核心操作与陷阱

1. 创建张量

import torch # 从列表创建 x = torch.tensor([1, 2, 3]) # 创建特定形状的全0/全1张量 zeros = torch.zeros(2, 3) # 2行3列的矩阵,元素全为0 ones = torch.ones(2, 3) # 创建未初始化的张量(内容为内存残留值,慎用) empty = torch.empty(2, 3) # 从numpy数组创建(共享内存,修改一个会影响另一个) import numpy as np np_arr = np.array([1, 2, 3]) tensor_from_np = torch.from_numpy(np_arr)

2. 形状操作view()vsreshape()

  • view():返回一个数据共享但形状不同的新张量视图。要求原始张量在内存中是连续的,否则会报错。更高效。
  • reshape():尽可能返回一个视图,如果原始数据不连续,则返回一个副本。更安全,是view()的增强版。初学者建议先用reshape()
x = torch.arange(12) print(x.shape) # torch.Size([12]) y = x.reshape(3, 4) # 改为3行4列 print(y.shape) # torch.Size([3, 4]) # 一个常用技巧:-1表示自动推断该维度大小 z = x.reshape(-1, 4) # 等价于 reshape(3, 4)

3. 广播机制:这是张量运算中一个强大但容易出错的特性。当两个形状不同的张量进行运算时,PyTorch会自动扩展(复制)较小张量的维度,使其与较大张量的形状兼容。

a = torch.ones(3, 1, 2) # 形状 (3, 1, 2) b = torch.ones(1, 4, 2) # 形状 (1, 4, 2) c = a + b # 结果形状为 (3, 4, 2) # 广播规则:从后往前比较维度,要么维度相等,要么其中一个为1,要么其中一个不存在。

常见坑:广播有时会导致意想不到的维度扩张,如果你对结果形状不确定,在关键运算前先用print(a.shape, b.shape)检查一下。

4. 内存与计算设备

  • CPU vs GPU:使用.to(device)在设备间移动张量。
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') x = torch.tensor([1, 2, 3]) x = x.to(device) # 移动到GPU(如果可用)
  • 就地操作:以_结尾的操作(如x.add_(y))会直接修改x,节省内存但会丢失原始数据。在需要保留计算图进行梯度计算时需谨慎使用。

4. 从线性回归开始:第一个完整的训练循环

第一章通常会用一个简单的线性回归作为例子,展示完整的深度学习流程。让我们深入这个流程的每一个环节。

4.1 模型定义:nn.Module的模板

在PyTorch中,我们通过继承nn.Module类来定义模型。

import torch.nn as nn class LinearRegression(nn.Module): def __init__(self, input_dim, output_dim): super().__init__() # 必须调用父类初始化 # 定义网络层 self.linear = nn.Linear(input_dim, output_dim) # 一个全连接层 def forward(self, x): # 定义前向传播逻辑 out = self.linear(x) return out
  • __init__:在这里定义模型需要用到的所有“组件”(层)。nn.Linear是一个线性层,内部包含了权重(weight)和偏置(bias)两个可学习参数。
  • forward:在这里定义数据如何通过这些组件流动。注意:你永远不需要直接调用forward()方法!而是调用模型实例本身,如output = model(input),PyTorch会自动调用forward

4.2 损失函数与优化器:目标的量化与达成路径

  • 损失函数:衡量模型预测值与真实值差距的标量。对于回归问题,常用均方误差(MSE)。
    criterion = nn.MSELoss() # 损失函数
  • 优化器:负责根据损失函数计算出的梯度,更新模型参数。我们之前提到的Adam是很好的默认选择。
    from torch import optim optimizer = optim.Adam(model.parameters(), lr=0.01) # 优化器,传入模型参数和学习率

4.3 训练循环:深度学习的“心跳”

这是最核心的代码块,体现了梯度下降的迭代过程。

# 假设我们已经有了训练数据 X_train, y_train model = LinearRegression(input_dim=1, output_dim=1) # 实例化模型 criterion = nn.MSELoss() optimizer = optim.Adam(model.parameters(), lr=0.01) num_epochs = 100 # 整个数据集遍历100次 for epoch in range(num_epochs): # 1. 前向传播 predictions = model(X_train) # 等价于 model.forward(X_train) loss = criterion(predictions, y_train) # 2. 反向传播 optimizer.zero_grad() # !!!关键步骤:清空上一轮梯度 loss.backward() # 计算损失关于所有参数的梯度 # 3. 参数更新 optimizer.step() # 根据梯度更新参数 # 打印日志 if (epoch+1) % 10 == 0: print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

逐行解析与避坑指南

  1. predictions = model(X_train):执行前向传播,得到模型预测。
  2. loss = criterion(predictions, y_train):计算预测与标签的差距。
  3. optimizer.zero_grad()这是新手最容易忘记的一步!PyTorch会累积梯度(.grad属性)。如果不在每次迭代前清空,梯度会不断累加,导致更新方向错误。务必在loss.backward()之前调用。
  4. loss.backward():反向传播的引擎。自动计算图中所有requires_grad=True的张量的梯度。
  5. optimizer.step():执行优化算法(如Adam),用计算出的梯度更新模型参数。

4.4 可视化:让训练过程“看得见”

在Jupyter Notebook中,实时可视化损失下降曲线和拟合结果,能极大增强学习信心和理解。

import matplotlib.pyplot as plt %matplotlib inline # 在Notebook中显示图表 losses = [] # 记录每个epoch的损失 for epoch in range(num_epochs): # ... 训练循环代码 ... losses.append(loss.item()) # 绘制损失曲线 plt.plot(range(num_epochs), losses) plt.xlabel('Epoch') plt.ylabel('Loss') plt.title('Training Loss') plt.show() # 绘制拟合结果 model.eval() # 将模型设置为评估模式(关闭Dropout等训练特定层) with torch.no_grad(): # 关闭梯度计算,节省内存和计算 predictions = model(X_train).detach().cpu().numpy() plt.scatter(X_train.cpu().numpy(), y_train.cpu().numpy(), label='True Data') plt.plot(X_train.cpu().numpy(), predictions, color='red', label='Fitted Line') plt.legend() plt.show()

5. 常见问题与调试技巧实录

即使是一个简单的线性回归,新手也会遇到各种问题。以下是我从自己和学员那里总结的“高频坑点”。

5.1 损失不下降或为NaN

这是最令人沮丧的情况之一。请按以下清单排查:

问题现象可能原因排查方法与解决方案
损失值非常大,甚至变成NaN1.学习率过大:这是最常见原因。步子太大,直接“飞”出了损失曲面。
2.数据未标准化/归一化:特征尺度差异巨大,导致梯度爆炸。
3.网络层初始化不当:权重初始值过大。
1.降低学习率:尝试lr=1e-3,1e-4
2.标准化数据:对每个特征,减去均值,除以标准差:X = (X - X.mean()) / X.std()
3.使用PyTorch默认初始化nn.Linear等层已有合理的默认初始化(如Kaiming初始化),通常无需手动改。
损失几乎不变,在初始值附近震荡1.学习率过小:更新步伐太慢,像在平地上蠕动。
2.模型架构过于简单:无法拟合数据复杂度。
3.优化器选择不当:对于某些问题,SGD可能比Adam更有效(虽然少见)。
4.数据本身没有规律
1.适当增大学习率
2.检查模型容量:增加层数或每层神经元数量。
3.尝试SGD优化器optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
4.可视化数据,检查X和y是否存在明显关系。
损失先下降后上升过拟合的早期迹象,或学习率调度不当(后期学习率太大)。1.监控验证集损失,在验证损失开始上升时停止训练(早停)。
2.使用学习率衰减scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1), 每30个epoch学习率乘以0.1。

5.2 维度不匹配错误

PyTorch的错误信息通常很详细,但维度错误对新手仍不友好。

  • 错误示例RuntimeError: mat1 and mat2 shapes cannot be multiplied (axb and cxd)
  • 原因:线性层nn.Linear(in_features, out_features)要求输入张量的最后一个维度等于in_features
  • 调试
    1. 在模型forward函数开头和结尾添加print(x.shape)
    2. 确保输入数据X的形状是[batch_size, input_dim]。如果你有一个特征,input_dim=1,那么形状应为[N, 1],而不是[N](后者是1维张量)。使用X = X.reshape(-1, 1)来增加维度。
    3. 检查数据加载器(DataLoader)输出的批次形状。

5.3 过拟合与欠拟合的“望闻问切”

在第一章的简单例子中可能不明显,但建立这个概念至关重要。

  • 欠拟合:模型在训练集上表现就很差(损失高)。好比一个学生连课本例题都做不对。
    • 诊断:训练损失居高不下。
    • 药方:增加模型复杂度(更多层、更多神经元)、训练更长时间、检查特征工程是否有效。
  • 过拟合:模型在训练集上表现很好,但在没见过的测试集上表现很差。好比一个学生死记硬背了所有例题,但不会解新题。
    • 诊断:训练损失持续下降,但验证/测试损失在某个点后开始上升。两者差距越来越大。
    • 药方:获取更多训练数据、使用正则化技术(如权重衰减weight_decay参数)、Dropout层、或简化模型。

一个简单的实践:在训练循环中,同时计算训练集和一个小型验证集的损失,并绘制两条曲线。这是监控模型状态最重要的工具。

5.4 GPU相关陷阱

  • “CUDA out of memory”:显存溢出。
    • 减小批次大小DataLoader中的batch_size是首要调整对象。
    • 使用梯度累积:如果硬件限制只能用小批次,可以多次前向传播累积梯度后再更新一次参数,模拟大批次效果。
    • 检查是否有张量长期驻留GPU:确保在不需要时及时将中间变量.detach().cpu()
  • 设备不匹配错误RuntimeError: Expected all tensors to be on the same device
    • 确保模型和数据在同一设备model.to(device)后,送入的每个batch数据也要batch.to(device)

重学第一章,我最大的体会是,深度学习的“基础”不是一堆需要记忆的名词,而是一套完整的思维模式和工程实践框架。从理解问题与工具的匹配(生态位),到掌握核心算法的直觉(梯度下降),再到熟练使用框架(PyTorch)将想法实现,最后通过系统的调试方法(可视化、损失分析)来验证和迭代——这个过程本身,就是深度学习工程师最核心的日常。很多人觉得第一章简单就跳过了,但恰恰是这些“简单”的概念,构成了你未来理解Transformer、Diffusion等复杂模型的基石。下次,当我们重学线性神经网络和Softmax回归时,我们会看到这些基石如何被一块块垒高。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 11:30:00

STM32WB无线SoC选型指南:双核架构、功耗与协议栈实践

做嵌入式这些年,但凡是项目里冒出“要加蓝牙”或者“要加 Zigbee”的需求,我最怕的往往不是写协议,而是选型。选错一颗芯片,后面所有的工作都是在为这个决定买单。今天想认真聊聊 STM32WB 这颗无线 SoC——它是我这几年评估过的 2…

作者头像 李华
网站建设 2026/8/31 11:33:52

奶茶店收银系统横评:扫码点单、出杯效率与会员沉淀的实现路径

奶茶店的经营节奏决定了收银系统的关注点:扫码点单要顺、出杯要快、外卖接单要稳、会员要能自动沉淀。四个方案在硬件出身、平台生态、软件深度上的侧重点各不相同。本文按奶茶店实际运营场景,对比商米、美团收银、收钱吧、客如云的能力差异。 商米&…

作者头像 李华
网站建设 2026/9/1 5:26:55

直流高电压(100Vdc~1000Vdc)检测的采样电路(隔离方案)

在电力系统自动化、新能源电动汽车及高压储能领域,常需采样检测 100Vdc~1000Vdc 的高压直流母线电压。设计人员需结合成本与精度选择合适的采样方案。本文分享一种强电与弱电完全隔离的高压直流母线电压采样电路,可有效抑制高压侧对低压侧的电…

作者头像 李华
网站建设 2026/9/2 10:04:11

Python安装与PyCharm配置详解:从搭环境到跑通第一个项目

Python 安装和 PyCharm 配置,是每个刚接触编程的人都要过的一道门槛。表面上看就是“去官网下载、点下一步、打开写代码”,但实际执行时经常会出现python命令找不到、PyCharm 提示没有解释器、pip 装不上包、终端输出中文乱码一类的问题。这篇文章按真实…

作者头像 李华
网站建设 2026/8/31 17:01:44

招行信用卡中心IT笔试全复盘:行测、技术单选与编程题备考指南

1. 这批笔试的整体结构:行测、技术单选、编程题三段的真实时间分配招行信用卡中心的IT笔试有个特点:它不是纯粹的算法竞赛,而是按“银行校招综合笔试专业技术笔试”的复合模式设计。2019秋招开发方向第三批在赛码网平台上进行,整场…

作者头像 李华