动手学深度学习

本文最后更新于:2025年4月7日 晚上

参考知名教程《动手学深度学习》,系统学一下~

机器学习任务:回归 vs 分类

1. 回归(Regression)

核心目标

  • 预测连续型数值(如价格、温度等)。

特点

  • 输出为实数范围内的任意数值(整数或浮点数)。

应用场景

  • 房价预测
  • 股票价格趋势分析
  • 用户停留时长估计

常用算法

  • 线性回归(Linear Regression)
  • 决策树回归(Decision Tree Regression)
  • 支持向量回归(SVR)
  • 神经网络

评估指标

  • 均方误差(MSE)
  • 平均绝对误差(MAE)
  • R²分数(R-squared)

2. 分类(Classification)

核心目标

  • 预测离散型类别标签(如类别、状态等)。

特点

  • 输出为有限数量的预定义类别(如“是/否”“猫/狗”)。

应用场景

  • 垃圾邮件检测(垃圾/正常)
  • 图像识别(如MNIST手写数字分类)
  • 疾病诊断(阳性/阴性)

分类类型

  • 二分类:仅两个类别(例:通过/未通过)。
  • 多分类:多个互斥类别(例:动物分类为猫/狗/鸟)。

常用算法

  • 逻辑回归(Logistic Regression)
  • 支持向量机(SVM)
  • 随机森林(Random Forest)
  • 卷积神经网络(CNN)

评估指标

  • 准确率(Accuracy)
  • 精确率(Precision)与召回率(Recall)
  • F1分数、ROC-AUC曲线

关键区别对比表

特征 回归 分类
输出类型 连续数值(如100.5元) 离散类别(如“猫”“狗”)
预测目标 数值大小 类别归属
损失函数 MSE、MAE 交叉熵(Cross-Entropy)
示例输出 房价=650万 邮件类型=“垃圾”

注意事项

  1. 逻辑回归是分类算法,尽管名称含“回归”。
  2. 数值标签的离散化(如成绩等级A/B/C)仍属分类任务。
  3. 部分算法(如神经网络)可同时处理回归和分类任务,需调整输出层设计。

神经网络是机器学习中的一类模型吗?

神经网络(Neural Networks)是机器学习领域中的一类重要模型,属于监督学习、无监督学习或强化学习的范畴。

以下是其关键定位和特点:


1. 神经网络的定位

  • 机器学习的分支:神经网络是机器学习的核心模型类型之一,与以下模型并列:

    • 传统统计模型(如线性回归)
    • 树模型(如决策树、随机森林)
    • 核方法(如SVM)
    • 聚类算法(如K-Means)
  • 深度学习的核心
    当神经网络具有多层隐藏层时(通常≥3层),被称为深度学习(Deep Learning),属于机器学习的子领域。


2. 神经网络的关键特性

(1)结构特点

  • 神经元层(输入层、隐藏层、输出层)组成。
  • 通过权重连接模拟生物神经元的信号传递。
  • 示例结构:
    1
    输入层 → 隐藏层1(激活函数:ReLU)→ 隐藏层2 → 输出层(Softmax)

(2)学习能力

  • 自动特征提取:无需人工设计特征,可从原始数据中学习高阶表征。
  • 端到端学习:直接从输入映射到输出(如:图像像素→分类结果)。
  • 非线性建模:通过激活函数(如Sigmoid、ReLU)处理复杂关系。

(3)应用场景

任务类型 典型应用 网络类型示例
分类 图像识别、文本分类 CNN、Transformer
回归 房价预测、股票价格估计 全连接网络(MLP)
生成任务 图像生成、文本创作 GAN、VAE、RNN/LSTM
强化学习 游戏AI(如AlphaGo)、机器人控制 DQN(深度Q网络)

3. 神经网络 vs 传统机器学习模型

对比维度 神经网络 传统机器学习模型(如SVM、决策树)
数据需求 需要大量数据,数据少时易过拟合 小数据集表现更好
特征工程 自动学习特征,减少人工干预 依赖人工特征工程
可解释性 黑箱模型,解释性差 部分模型可解释性强(如决策树)
计算资源 需要GPU加速训练 CPU即可运行
适用任务 图像、语音、NLP等复杂高维数据 结构化数据(表格数据)

4. 神经网络的典型变体

类型 特点 应用场景
全连接网络(MLP) 基础神经网络,每层神经元全部连接 简单分类/回归任务
卷积网络(CNN) 通过卷积核提取空间特征 图像处理、视频分析
循环网络(RNN) 处理序列数据,具有时间记忆能力 文本生成、语音识别
Transformer 基于自注意力机制,并行处理长序列 NLP(如BERT、GPT)
图神经网络(GNN) 处理图结构数据(如社交网络、分子结构) 推荐系统、化学分析

5. 关键结论

  • ✅ 神经网络是机器学习的核心模型类别之一。
  • ✅ 深度学习是神经网络的扩展(多层结构+大规模数据)。
  • 适用条件:优先选择神经网络当数据量大、任务复杂(如非结构化数据)且算力充足时。
  • ⚠️ 局限性:数据少或需要强解释性的场景下,传统模型可能更优。

MLP vs CNN:分类任务中的选择

核心结论

  • MLP(全连接网络):适合结构化数据(如表格数据)的分类,但对图像等高维非结构化数据效率较低。
  • CNN(卷积神经网络):专为空间数据(如图像、视频)设计,在图像分类中表现更优。

1. MLP(全连接网络)的特点

优势

  • 简单通用:可处理任意维度的输入(需展平为一维向量)。
  • 结构化数据友好:适合特征之间无空间关联的数据(如用户年龄、收入等表格数据)。
  • 计算成本较低:对低维数据训练速度较快。

劣势

  • 图像处理缺陷
    • 需将图像展平破坏空间结构(如将28×28的MNIST图像变为784维向量)。
    • 参数量爆炸(如处理1000×1000像素的RGB图像,输入层即需3百万参数)。
    • 无法有效捕捉局部特征(如边缘、纹理)。

适用场景

  • 手写数字分类(MNIST,简单图像任务)
  • 客户流失预测(结构化数据)
  • 二分类问题(如垃圾邮件检测)

2. CNN(卷积神经网络)的特点

优势

  • 空间特征提取
    • 卷积核自动学习局部特征(如边缘、形状)。
    • 通过池化层实现平移不变性(物体位置变化不影响分类)。
  • 参数共享:大幅减少参数量(同一卷积核扫描整个图像)。
  • 层次化特征学习
    • 浅层学习基础特征(边缘、颜色)
    • 深层学习抽象特征(物体部件、整体)

劣势

  • 结构化数据不适用:对无空间关联的数据优势消失。
  • 计算成本较高:需要GPU加速训练深层网络。

适用场景

  • 图像分类(ImageNet、CIFAR-10)
  • 医学影像分析(X光片分类)
  • 视频动作识别
  • 自然语言处理(通过1D卷积处理文本序列)

3. 实验对比(以MNIST手写数字分类为例)

指标 MLP CNN
准确率 ~98% ~99%+
参数量 约10万(784-128-10) 约5万(卷积+全连接)
训练时间(CPU) 较快 较慢
特征可视化 难以解释 可可视化卷积核

说明:即使是简单图像任务,CNN在准确率和参数量上仍占优。


4. 如何选择?

选择MLP的情况

  • 输入数据为结构化特征(如Excel表格数据)
  • 图像分辨率极低(如28×28)且算力受限
  • 需要快速实现原型验证

选择CNN的情况

  • 输入数据具有空间结构(图像、视频帧、频谱图)
  • 任务需要识别局部模式(如物体、纹理)
  • 追求更高分类精度

5. 混合使用案例

实际应用中常组合两种网络:

1
2
3
4
5
6
7
8
# 示例:CNN提取特征 + MLP分类
model = Sequential([
Conv2D(32, (3,3), activation='relu', input_shape=(64,64,3)), # CNN部分
MaxPooling2D(),
Flatten(), # 展平为向量
Dense(128, activation='relu'), # MLP部分
Dense(10, activation='softmax')
])

总结

图像/视频分类:优先选择CNN(如ResNet、EfficientNet)

表格数据分类:优先MLP或传统模型(随机森林/XGBoost)

简单任务快速验证:可先用MLP作为基线模型

附:经典网络架构参考

  • MLP:LeNet-5(原始版本含全连接主导)
  • CNN:AlexNet、VGG、ResNet

杂七杂八

Linux的系统盘和数据盘

由于博主的电脑配置太辣鸡,所以选择在autodl租用显卡进行相关学习。公共资源需要靠抢,所以常常需要复制镜像,每次都随机抢不同的显卡……

  • 镜像保存了Linux除了数据盘以外的所有数据;
    • 数据盘的内容需要拷贝,只能在同一地区的显卡上拷贝数据。
  • Linux数据盘和系统盘的查看
    • 系统盘如同Windows的C盘。 使用df -lh查看,根路径下的都属于系统盘。
    • 如果单独有数据盘,且数据盘没有分区和挂载,使用df -l命令是看不到的。可以使用fdisk -l,可以看到有哪些硬盘。

环境配置

配置一个环境来运行 Python、Jupyter Notebook、相关库以及运行本书所需的代码,以快速入门并获得动手学习经验。

安装Miniconda

Miniconda 是一个轻量级的 Conda 发行版,主要用于管理 Python 环境和软件包。简而言之,Miniconda 的作用是管理 Python 版本和依赖,比如你电脑中运行了多个项目,这些项目需要不同的 Python 版本和库,就可以用 Miniconda 创建独立的虚拟环境,避免相互干扰。

Linux 系统中安装和部署 Miniconda 的详细教程

检查pytorch是否安装成功、查看torch和cuda的版本

预备知识

基本概念

数据操作

PyTorch:一个开源的深度学习框架,支持GPU加速计算和动态计算图

核心组件:

  • torch.Tensor:支持梯度追踪的多维数组。
  • torch.nn:神经网络层和损失函数。
  • torch.optim:优化算法(如SGD、Adam)。

导入torch。请注意,虽然它被称为PyTorch,但是代码中使用torch而不是pytorch。

1
import torch

张量

张量表示一个由数值组成的数组,这个数组可能有多个维度。 具有一个轴的张量对应数学上的向量(vector); 具有两个轴的张量对应数学上的矩阵(matrix); 具有两个轴以上的张量没有特殊的数学名称。

  • 张量中的每个值都称为张量的元素(element)。
  • 除非额外指定,新的张量将存储在内存中,并采用基于CPU的计算。
  • 使用 arange 创建一个行向量 x。这个行向量包含以0开始的前12个整数,它们默认创建为整数。也可指定创建类型为浮点数。
    • 可以通过张量的shape属性来访问张量(沿每个轴的长度)的形状 。如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)。
    • 改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数。注意,通过改变张量的形状,张量的大小不会改变。
      • 例如,可以把张量x从形状为(12,)的行向量转换为形状为(3,4)的矩阵。 这个新的张量包含与转换前相同的值,但是它被看成一个3行4列的矩阵。 要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变。
      • 通过-1来调用此自动计算出维度的功能。即我们可以用x.reshape(-1,4)x.reshape(3,-1)来取代x.reshape(3,4)
    • 使用全0、全1、其他常量,或者从特定分布中随机采样的数字来初始化矩阵。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      x = torch.arange(12)
      x.shape
      x.numel()
      X = x.reshape(3, 4)
      torch.zeros((2, 3, 4)) # 创建一个形状为(2,3,4)的张量,其中所有元素都设置为0
      torch.ones((2, 3, 4))
      torch.randn(3, 4) # 随机初始化参数的值,每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。
      torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) # 通过提供包含数值的Python列表(或嵌套列表),来为所需张量中的每个元素赋予确定值。
      print(X) # 打印输出X的值

独热编码(One-Hot Encoding)

定义:用二进制向量表示离散类别。又称为一位有效编码,主要是采用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。
例如:

  • 类别1 → [1, 0, 0]
  • 类别2 → [0, 1, 0]
  • 类别3 → [0, 0, 1]

作用:将分类标签转换为模型可处理的数值形式。具体参考:

机器学习:数据预处理之独热编码(One-Hot)详解

  • 标签(Label):真实答案,此处表示样本属于第一个类别。
    1
    target = torch.tensor([1.0, 0.0, 0.0])  # 独热编码

交叉熵损失 (Cross-Entropy Loss)

衡量预测概率分布与真实分布的差异。

1
loss = -torch.sum(target * torch.log(prob))

计算过程:

  • target * torch.log(prob) → 仅保留真实类别对应的概率对数。
  • -sum(…) → 取负数得到损失值(越小表示预测越准)。

反式传播

  • 原理:通过链式法则自动计算损失对参数的梯度,只需调用backward()。
    1
    2
    loss.backward()
    print("梯度:", x.grad) # 输出如 [-0.3410, 0.2424, 0.0986]
  • 步骤:
    • 从loss开始,反向遍历计算图。
    • 计算每个参与运算的张量的梯度。
  • 结果:梯度存储在x.grad中。

梯度

  • 意义:表示损失函数对x各元素的敏感度。
    • 例如:输出 [-0.3410, 0.2424, 0.0986]
      • 负梯度(如-0.3410)→ 增大x[0]可减少损失,概率越接近1,损失越小
      • 正梯度(如0.2424)→ 减小x[1]可减少损失。
  • 应用:通过优化器(如SGD)更新参数:x = x - 学习率 * x.grad
  • 梯度下降的直观理解
    将损失函数想象为一座山,目标是找到山谷最低点(最小损失):
    • 梯度:指向当前所在位置最陡的上坡方向。
    • 负梯度方向:下山最快的方向。
    • 学习率:决定每一步迈多大。
      • 步子太小 → 下山慢。
      • 步子太大 → 可能跨过山谷或摔倒(发散)。

优化器

随机梯度下降(SGD)的参数更新公式为:

$$\theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta J(\theta_t)$$

其中,$\theta$ 是模型参数,$\eta$ 是学习率,$\nabla_\theta J(\theta_t)$是损失函数对参数的梯度。

1
torch.optim.SGD(params, lr=0.1)
  • 学习率($\eta$)的作用
    • 过小,如 $\eta = 0.001$ :更新步长小,收敛速度慢。
    • 过大,如 $\eta = 1.0$ :可能跳过最优解,甚至发散。
    • 合理选择:通常通过实验调整
优化器 适用场景 核心特点 常见应用领域
SGD 简单模型/凸优化问题 基础更新,收敛稳定但需手动调学习率 线性回归/简单分类任务
Momentum 高维非凸优化场景 引入动量项加速收敛,减少参数更新振荡 计算机视觉/复杂网络结构
Adagrad 稀疏数据特征处理 自适应调整学习率,对低频特征更敏感,内存消耗较大 NLP词向量训练
RMSprop 非平稳目标/循环网络 改进Adagrad的学习率衰减问题,采用指数移动平均 RNN/LSTM训练
Adam 通用深度学习任务(默认首选) 结合动量+自适应学习率,收敛速度快,超参数鲁棒性好 CNN/GAN/大多数深度学习模型
AdamW 需要精细权重衰减的任务 解耦权重衰减项,解决Adam中权重衰减与梯度更新耦合的问题 Transformer/BERT系列模型

运算符

按元素(elementwise)运算。

  • 它们将标准标量运算符应用于数组的每个元素。 对于将两个数组作为输入的函数,按元素运算将二元运算符应用于两个数组中的每对位置对应的元素。 我们可以基于任何从标量到标量的函数来创建按元素函数。
    1
    2
    3
    4
    x = torch.tensor([1.0, 2, 4, 8])
    y = torch.tensor([2, 2, 2, 2])
    x + y, x - y, x * y, x / y, x ** y # **运算符是求幂(y次方)
    torch.exp(x) # 计算输入张量 x 中每个元素的指数函数值(即自然常数 e 的对应元素次方)

torch.exp()函数的应用场景

  • Softmax函数:用于将神经网络的输出转换为概率分布(所有值在0~1之间,且和为1):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import torch

    # 定义输入(假设是神经网络的原始输出)
    x = torch.tensor([2.0, 1.0, 0.1]) # 原始分数

    # 数值稳定的Softmax实现
    def softmax(x):
    # 1. 减去最大值,避免指数爆炸(数值稳定性技巧)
    max_x = torch.max(x)
    shifted_x = x - max_x

    # 2. 计算指数
    exp_x = torch.exp(shifted_x)

    # 3. 计算概率分布
    sum_exp = torch.sum(exp_x)
    probabilities = exp_x / sum_exp

    return probabilities

    # 调用函数
    prob = softmax(x)
    print("Softmax结果:", prob) # 输出如 [0.6590, 0.2424, 0.0986],每个值表示对应类别的概率,总和为1。

  • 激活函数:如高斯误差线性单元(GELU):

    • GELU是一种平滑的激活函数,常用于Transformer模型(如BERT)。GELU近似于用概率门控机制决定是否激活神经元,公式为(0.044715为经验系数):

    $$\text{GELU}(x) = 0.5x \left( 1 + \tanh\left( \sqrt{\frac{2}{\pi}} \left( x + 0.044715x^3 \right) \right) \right)$$

    • GELU vs ReLU:
      • ReLU:当输入>0时输出原值,否则输出0(不光滑)。
      • GELU:通过双曲正切函数(tanh)实现平滑过渡,更适合复杂模型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import torch

# 定义GELU函数
def gelu(x):
# 数学公式中的常数项
sqrt_2_over_pi = torch.sqrt(torch.tensor(2.0 / torch.pi)) # √(2/π)
alpha = 0.044715 # 经验系数

# 计算GELU的核心部分
inner = x + alpha * x**3
scaled_inner = sqrt_2_over_pi * inner
tanh_output = torch.tanh(scaled_inner)

# 最终结果
return 0.5 * x * (1 + tanh_output)

# 示例输入
x = torch.tensor([1.0, -1.0, 0.5])
output = gelu(x)
print("GELU输出:", output) # 输出如 [0.8413, -0.1587, 0.3457]
  • 如果 x 是需计算梯度的张量(requires_grad=True),torch.exp(x) 的梯度会自动计算,用于反向传播更新权重。
    • 梯度是函数在某一点的导数,表示该点对输出的影响程度。
    • 在训练神经网络时,梯度告诉我们应该如何调整参数(如 x)以减少误差。例如,如果梯度为7.3891,说明 x 增加1,y 会增加约7.3891。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch

# 创建一个需要计算梯度的张量
x = torch.tensor(2.0, requires_grad=True) # requires_grad=True表示跟踪梯度

# 定义计算过程
y = torch.exp(x) # 计算 y = e^x

# 反向传播计算梯度
y.backward() # 自动计算 dy/dx

# 查看梯度
print("梯度值:", x.grad) # 输出 tensor(7.3891),即 e^2 ≈ 7.3891

一些语法解释

  1. x - max_x这样的操作默认会对张量(矩阵)中的每个元素进行运算,但具体行为取决于max_x的维度。
  • 假设x是一个矩阵,而max_x是通过torch.max(x)得到的全局最大值标量,此时x - max_x会对x中的每个元素都减去这个标量值。
  • 如果max_x是通过指定维度(如dim=1)计算得到的向量,PyTorch会通过广播机制自动对齐维度,确保逐元素减法正确执行。
    • 在按维度计算最大值时,keepdim=True会保留原始维度信息,确保广播正确。
      1
      2
      3
      4
      5
      6
      7
      8
      x = torch.tensor([[3.0, 5.0], 
      [2.0, 4.0]])

      max_x = torch.max(x, dim=1, keepdim=True).values # max_x是按行(dim=1)计算的最大值,形状为(2, 1)。
      print(max_x) # 输出 tensor([[5.0], [4.0]])

      shifted_x = x - max_x
      print(shifted_x)
      广播规则下,max_x会被扩展为与x同形状,最终每个元素减去对应行的最大值。

综合示例 (结合Softmax、梯度与优化器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import torch

def softmax(x):
max_x = torch.max(x)
shifted_x = x - max_x
exp_x = torch.exp(shifted_x)
return exp_x / exp_x.sum() # 归一化:除以总和,得到概率。

# 创建一个需要梯度的张量, requires_grad=True:允许自动计算该张量的梯度,用于后续反向传播。
x = torch.tensor([3.0, 1.0, 0.2], requires_grad=True)

# 定义优化器(以SGD为例)
# lr=0.1:学习率,控制参数更新步长
# [x]:待优化的参数列表
optimizer = torch.optim.SGD([x], lr=0.1) # 将x传入优化器

# 假设真实标签是第一个类别
target = torch.tensor([1.0, 0.0, 0.0])

# 计算Softmax概率
prob = softmax(x) # 使用之前定义的softmax函数

# 假设真实标签是第一个类别(概率应接近1)
target = torch.tensor([1.0, 0.0, 0.0]) # 独热编码

# 模拟多次训练迭代
for epoch in range(3): # 假设训练3
# 清零梯度(重要!避免梯度累积)
optimizer.zero_grad()

# 前向传播
prob = softmax(x)
loss = -torch.sum(target * torch.log(prob))

# 反向传播
loss.backward()

# 更新参数(关键步骤)
optimizer.step()

# 打印结果
print(f"Epoch {epoch+1}:")
print(f"x = {x.detach().numpy().round(4)}") # 更新后的参数值,始终遵循 detach() → numpy() 的转换顺序。
print(f"loss = {loss.item():.4f}\n")

注意:

  • PyTorch默认会累积梯度(例如在RNN中可能需要)。在每次迭代前必须手动清零梯度,否则梯度会不断累加,导致更新错误。
  • 参数更新:根据优化器规则(如SGD)和当前梯度更新参数。
    1
    optimizer.step()
  • 若使用其他优化器(如Adam),只需修改定义:
    • 不同优化器需调整学习率(如Adam常用 lr=0.001)。
      1
      2
      # 替换SGD为Adam
      optimizer = torch.optim.Adam([x], lr=0.01)
  • f"...":格式化字符串,指在字符串中直接嵌入变量或表达式。
  • x.detach():分离计算图
    • 作用:将张量 x 从当前计算图中分离,返回一个不关联梯度的新张量
    • 必要性:
      • PyTorch张量若参与过梯度计算(如 requires_grad=True),直接操作可能引发错误。
      • detach() 切断与计算图的联系,避免不必要的梯度追踪。
  • .numpy():转换为NumPy数组
    • 作用:将PyTorch张量转换为NumPy数组。
    • 要求:
      • 张量必须位于CPU上(若在GPU需先 .cpu())。
      • 张量不能关联梯度(需先 .detach()
    • 为何不直接打印x?
      • PyTorch张量直接打印会显示梯度信息、设备位置(如GPU)等冗余内容,转换为NumPy数组后更简洁。
  • .round(4):数值四舍五入到指定小数位(此处保留4位)。

广播机制

当对两个形状不同的张量进行按元素操作(如加减乘除)时,PyTorch会自动触发广播机制,尝试将张量扩展为兼容的形状以完成运算。

示例:

  • 向量 + 标量
    1
    2
    3
    4
    5
    6
    7
    8
    import torch

    a = torch.tensor([1, 2, 3]) # 形状 (3,)
    b = torch.tensor(10) # 形状 () → 标量

    # PyTorch自动将b广播为[10, 10, 10],然后相加
    c = a + b
    print(c) # 输出 tensor([11, 12, 13])
  • 矩阵 + 向量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    a = torch.tensor([[1], [2], [3]])  # 形状 (3, 1)
    b = torch.tensor([10, 20, 30]) # 形状 (3,)

    # PyTorch将a扩展为 (3,3),b扩展为 (3,3):
    # a → [[1, 1, 1],
    # [2, 2, 2],
    # [3, 3, 3]]
    # b → [[10, 20, 30],
    # [10, 20, 30],
    # [10, 20, 30]]
    c = a + b
    print(c)
    # 输出:
    # tensor([[11, 21, 31],
    # [12, 22, 32],
    # [13, 23, 33]])

索引和切片

张量中的元素可以通过索引访问。第一个元素的索引是0,最后一个元素索引是-1。

基本语法:X[start:end]

  • start:起始索引(包含该位置的元素)
  • end:结束索引(不包含该位置的元素)
  • 范围:[start, end),即左闭右开区间。
1
2
3
X[-1]       # 选择最后一个元素

X[1:3] # 选择第二个和第三个元素

指定索引赋值

  • 指定单个元素
    1
    X[1, 2] = 9
  • 为多个元素赋值相同的值
    示例:
    [0:2, :]访问第1行和第2行,其中“:”代表沿轴1(列)的所有元素。
    1
    X[0:2, :] = 12

执行原地操作节省内存

使用切片表示法将操作的结果分配给先前分配的数组。

  • 使用X[:] = X + Y或X += Y来减少操作的内存开销。

将张量转换为其他Python对象

  • 转换为Python标量:.item()

    • 仅限单元素张量
  • 转换为NumPy数组:.detach().numpy()

    • NumPy数组与原始张量共享内存,修改一方会影响另一方
  • 转换为Python列表:.tolist()

完整的深度学习项目框架

之前的示例代码仅展示了深度学习流程中的核心片段(前向传播、损失计算、反向传播与参数更新)。

一个完整的深度学习项目包括:

  • 数据准备
    • 数据加载:从文件(CSV、图像等)或数据库读取原始数据。
    • 数据预处理:标准化、归一化、数据增强(图像旋转/翻转)。
    • 数据划分:分为训练集、验证集、测试集。
  • 模型定义
    • 结构化模型类:使用 nn.Module 定义网络结构。
    • 设备选择:CPU/GPU
  • 训练流程
    • 完整的训练循环:包含多个Epoch和批次训练。
    • 验证与测试:评估模型泛化性能。
  • 模型评估与优化
    • 评估指标:除准确率外,可加入混淆矩阵、F1分数等。
    • 超参数调优:使用网格搜索或自动化工具(如Optuna)。
    • 学习率调度:动态调整学习率。
  • 结果保存与部署
    • 模型保存:保存训练好的模型参数。
    • 结果可视化:绘制损失/精度曲线。
    • 部署接口:构建预测API或导出为ONNX格式。

注释:

数据预处理

读取数据集

数据集的读取方式因数据类型(如图像、文本、结构化数据)和存储形式(如本地文件、数据库、云存储)而异。

关键:

  • 使用绝对路径或统一管理路径变量。
    示例:
    1
    2
    import os
    DATA_DIR = os.path.expanduser("~/datasets/mnist") # 跨平台兼容路径
  • 数据校验:读取后检查数据量和维度是否合理。
    示例:
    1
    2
    print(f"加载图像数量: {len(dataset)}")
    print(f"单张图像形状: {dataset[0][0].shape}")
  • 文本文件需指定正确编码(如utf-8、gbk)。
  • 图像需统一通道顺序(如RGB vs BGR)。

结构化数据(CSV/Excel)

  • 常见场景:表格数据(如房价预测、用户行为分析)。
  • 核心工具:Pandas库。

第一步:读取CSV文件

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd  # 导入pandas包(数据分析工具)

# 读取CSV文件
data = pd.read_csv("data.csv")

# 查看前5行
print(data.head())

# 分离特征和标签(假设最后一列为标签)
features = data.iloc[:, :-1].values # 转为NumPy数组
labels = data.iloc[:, -1].values

注释:

  • 在代码 data.iloc[:, :-1] 中,-1 是 Python 的负索引语法,表示从后向前计数。这里的 :-1 表示选取从第一列到倒数第二列的所有数据(即排除最后一列),目的是将最后一列作为标签(Label),其余列作为特征(Features)。
    • ::选取所有行。
    • -1:直接选取最后一列。

第二步:处理缺失值

1
2
3
4
5
6
7
# 策略1:删除包含缺失值的行
data.dropna(inplace=True) # inplace=True:直接修改原数据框,不返回新对象。

# 策略2:用均值填充缺失值
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="mean") # 初始化填充器:指定填充策略为均值(strategy="mean"
features_filled = imputer.fit_transform(features) # 拟合并转换:fit_transform 会先计算各特征均值,再用均值填充缺失值

注释:

  • 策略1的使用场景:

    • 缺失值占比小(如<5%),删除后不影响数据量。
    • 缺失值随机分布,删除不会引入偏差。
  • 策略2中的SimpleImputer的strategy可选值:

    • “mean”(均值):是计算每个特征的均值
    • “median”(中位数)
    • “most_frequent”(众数)
    • “constant”(固定值):需配合 fill_value 参数
  • 策略2 的使用场景:

    • 特征符合正态分布或接近对称分布(均值能较好代表中心趋势)。
    • 缺失值较多或删除会导致数据不足的情况。
    • 均值策略仅适用于数值型特征。
    • 分类特征需改用众数——strategy="most_frequent"

图像数据

  • 常见场景:计算机视觉任务(分类、检测)。
  • 核心工具:PIL/Pillow、OpenCV、TensorFlow/PyTorch工具。

从文件夹读取(按类别存储)

假设数据集结构如下:

1
2
3
4
5
6
7
8
9
10
dataset/
train/
cat/
cat001.jpg
cat002.jpg
dog/
dog001.jpg
dog002.jpg
val/
...

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
import torchvision.transforms as transforms

# 定义预处理转换
transform = transforms.Compose([
transforms.Resize((224, 224)), # 调整大小
transforms.ToTensor(), # 转为张量
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 归一化
])

# 加载数据集
train_dataset = ImageFolder(root="dataset/train", transform=transform)
val_dataset = ImageFolder(root="dataset/val", transform=transform)

# 创建数据加载器(自动分批)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32)

注释:

  • batch_size=32定义了每个批次(batch)中包含的样本数量。
    • 将整个数据集分成小批量,避免一次性加载全部数据导致内存不足,平衡内存占用与训练效率。
    • batch_size的常用值为 32、64、128
    • 每个批次计算一次梯度并更新参数(即小批量梯度下降)。
  • shuffle=True设置:在每个训练轮次(epoch)开始时,随机打乱数据顺序。
    • 防止顺序偏差:避免模型因数据排列顺序而学到非泛化性模式(如总是先看到某一类样本)。
    • 增强泛化性:打乱数据后,模型更均匀地学习不同特征,减少过拟合风险。
      • 训练阶段:shuffle通常设为 True。
      • 验证/测试阶段:通常设为 False(保持数据顺序一致)。

单张图像读取

1
2
3
4
5
from PIL import Image

# 读取图像并转换
image = Image.open("cat001.jpg").convert("RGB") # 确保RGB格式
image_tensor = transform(image) # 应用上述预处理

文本处理

  • 常见场景:自然语言处理(情感分析、机器翻译)。
  • 核心工具:Python内置文件操作、NLTK、Hugging Face Datasets。

读取文本文件

1
2
3
4
5
6
7
8
9
10
# 逐行读取
with open("text_data.txt", "r", encoding="utf-8") as f:
lines = f.readlines()

# 示例:情感分析数据(每行格式:标签\t文本)
texts, labels = [], []
for line in lines:
label, text = line.strip().split("\t")
labels.append(int(label))
texts.append(text)

使用Hugging Face数据集库

1
2
3
4
5
6
from datasets import load_dataset

# 加载公开数据集(如IMDB影评)
dataset = load_dataset("imdb")
train_texts = dataset["train"]["text"]
train_labels = dataset["train"]["label"]

音频数据

  • 常见场景:语音识别、声音分类。
  • 核心工具:Librosa、TorchAudio。
1
2
3
4
5
6
7
import librosa

# 加载音频文件
audio, sr = librosa.load("audio.wav", sr=16000) # sr为采样率

# 转换为梅尔频谱图(常见预处理)
mel_spectrogram = librosa.feature.melspectrogram(y=audio, sr=sr)

线性回归网络


动手学深度学习
http://zoechen04616.github.io/2025/03/14/动手学深度学习/
作者
Yunru Chen
发布于
2025年3月14日
许可协议