news 2026/5/19 17:24:11

【学习笔记】动手学深度学习(自用)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【学习笔记】动手学深度学习(自用)

数据操作

数据操作实现

import torch

张量表示一个数值组成的数组,可以有很多维度

id类似c++的指针

数据预处理

QA:NumPy 里的 ndarray(数组)和PyTorch 里的 Tensor(张量)的区别?

numpy.array = 普通数字数组(CPU 上跑)

torch.tensor = 可以跑在 GPU 上的 “超级数组”(深度学习专用)

  • numpy 不能 GPU 加速,torch tensor 可以
  • numpy 不能自动求导,torch tensor 可以(训练必备)
  • 两者长得几乎一样,可以互相转换
# numpy → tensor tensor = torch.from_numpy(numpy_array) # tensor → numpy arr = tensor.numpy()

线性代数实现

轴的本质

axis=N表示沿着第 N 个维度的方向进行操作,操作后该维度会被压缩(长度从原长度变为 1,最终从 shape 中消失)。

  • 三维张量(a, b, c)
    • axis=0:沿 a 方向操作,结果 shape(b, c)
    • axis=1:沿 b 方向操作,结果 shape(a, c)
    • axis=2:沿 c 方向操作,结果 shape(a, b)
    • axis=[0,1]:沿 a、b 方向操作,结果 shape(c,)

函数核心效果输出 shape
A.sum(axis=0)沿轴全量求和,最终只保留一个总和会压缩 / 消失指定的 axis 维度
A.cumsum(axis=0)沿轴逐元素累积求和,记录每一步的累加中间结果和原始张量的 shape完全一致
import torch A = torch.tensor([ [0., 1., 2., 3.], # 第0行 [4., 5., 6., 7.], # 第1行 [8., 9., 10., 11.], # 第2行 [12., 13., 14., 15.],# 第3行 [16., 17., 18., 19.] # 第4行 ])

axis=0核心计算规则

axis=0代表 ** 沿第 0 轴(行方向,从上到下)** 做累积求和,规则非常简单:

  • 第 0 行的结果 = 原始张量的第 0 行(累加起点,无前置元素)
  • 第 N 行的结果 = 原始张量第 N 行 + 累积和结果的第 N-1 行

按特定轴求和

矩阵计算

亚导数

一、为什么需要亚导数?

普通导数只能定义在处处可导的函数上,但很多实用函数(比如y=|x|y=max(x,0))在某一点(x=0)不可导:

  • y=|x|x=0处左导数为-1,右导数为1,左右不相等,传统导数不存在
  • y=max(x,0)(ReLU 激活函数)在x=0处左导数为0,右导数为1,同样不可导

亚导数就是把导数的概念拓展到这类不可导的凸函数,让我们依然能做梯度下降等优化操作。

二、两个核心例子拆解

1. 例子 1:y = |x|(绝对值函数)

亚导数定义:

逐点解读:

  • x>0:函数是y=x,斜率为1,亚导数 = 普通导数 = 1
  • x<0:函数是y=-x,斜率为-1,亚导数 = 普通导数 =-1
  • x=0(不可导点):亚导数是区间[-1,1]内的任意实数a
    • 图中画的-0.30.5都是合法的亚导数,只要在[-1,1]内都成立
    • 几何意义:所有夹在左右两条切线(斜率 - 1 和 1)之间的直线,都可以作为该点的「次梯度」

2. 例子 2:y = max(x,0)(ReLU 激活函数)

亚导数定义:

逐点解读:

  • x>0:函数是y=x,斜率为1,亚导数 = 1
  • x<0:函数是y=0,斜率为0,亚导数 = 0
  • x=0(不可导点):亚导数是区间[0,1]内的任意实数a
    • 深度学习中通常直接取a=0a=1(最常用的是取 1,即「次梯度选择」),不影响梯度下降的收敛性

三、亚导数的核心本质

  1. 适用对象:仅适用于凸函数|x|max(x,0)都是典型凸函数)
  2. 几何意义:对于不可导点,亚导数是所有「支撑超平面」的斜率集合,也就是夹在左右切线之间的所有直线的斜率
  3. 和普通导数的关系
    • 函数可导时,亚导数集合只有一个元素,就是普通导数
    • 函数不可导时,亚导数是一个区间,包含所有合法的次梯度
  4. 深度学习中的意义
    • ReLU 激活函数的反向传播,本质就是用亚导数计算梯度
    • L1 正则化的梯度计算,依赖|x|的亚导数
    • 解决了不可导函数的优化问题,让梯度下降算法可以正常工作

四、补充:次梯度的选择规则(工程实践)

在深度学习中,我们不会真的用区间,而是选一个固定值作为梯度:

  • y=|x|x=0处,通常选a=0(也可以选 - 1/1,不影响收敛)
  • y=max(x,0)x=0处,通常选a=1(PyTorch/TensorFlow 等框架的默认实现)

QA:关于凹凸性的判断怎么不太一样

视觉直观描述国内部分高数教材叫法国际标准叫法(机器学习 / 凸优化必用)二阶导数判断(硬标准,无歧义)典型函数
中间低、两边高,向下凹陷,碗口朝上凹函数 / 下凸函数凸函数(Convex Function)二阶导数 ≥ 0y=x、y=max(x,0)、y=x²
中间高、两边低,向上凸起,碗口朝下凸函数 / 上凸函数凹函数(Concave Function)二阶导数 ≤ 0y=-x²、y=lnx

求导后的形状

x是向量,y是标量

自动求导

例子1

l

QA:z对w的求导为什么是x的转置

一、先明确所有前提(维度是核心!)

先把所有变量的维度、定义卡死,这是所有推导的基础,线性代数里默认约定:

二、核心矛盾:向量求导的 2 种布局约定

90% 的人搞不懂转置,都是因为没搞懂「布局约定」,这是教材 / 课程之间写法差异的根源。

1. 两种布局的核心区别

布局类型核心规则(标量对列向量求导)导数形状对应结果
分子布局(雅可比布局,你截图里用的就是这个)导数形状 = 「分子维度在前,分母维度在后」,标量 (1,1) 对列向量 (n,1) 求导,结果排成行向量(1,n)∂w∂⟨x,w⟩​=xT
分母布局(梯度布局,机器学习 / 深度学习最常用)导数形状 = 和分母 w 的形状完全一致,列向量对列向量求导,结果排成列向量(n,1)∂w∂⟨x,w⟩​=x

划重点:两种写法都是对的,只是约定不同,元素完全一致,只是排列成了行 / 列的区别。

三、一步步拆解推导,彻底搞懂转置来源

我们按照你截图里的链式法则,逐行拆解,每一步都标清楚维度和导数,保证逻辑闭环。

五、补充:为什么机器学习里经常看不到转置?

因为深度学习框架(PyTorch/TensorFlow)、梯度下降算法里,我们用的是分母布局

两种写法本质完全等价,只是行 / 列的排列不同,元素没有任何区别,只是约定不同。

六、一句话总结

你截图里的转置,本质是分子布局约定下,标量对列向量求导要排成行向量,而内积的偏导数刚好是 x 的元素,排成行向量就是 x 的转置xT,同时完美满足链式法则的维度匹配。

例子2

一、先卡死所有前提(维度是核心!)

先把所有变量的维度、定义明确,线性代数求导的核心就是维度匹配,所有推导都不能脱离维度:

变量定义与维度含义
X∈Rm×nm 行 n 列的矩阵m 个样本,每个样本有 n 个特征,每行对应 1 个样本
w∈Rnn 维列向量,形状(n,1)线性回归的权重参数,我们要求导的目标变量
y∈Rmm 维列向量,形状(m,1)m 个样本的真实标签
a=Xw矩阵乘法,结果是 m 维列向量,形状(m,1)模型对 m 个样本的预测值
b=a−y=Xw−ym 维列向量,形状(m,1)模型预测的残差(预测值 - 真实值)
z=∣b∣2L2 范数的平方,结果是标量(1 个实数),形状(1,1)线性回归的损失函数(残差平方和,MSE 损失的核心部分)

补充:∥b∥2 等价于 bTb(列向量的转置乘自身),展开就是 b12​+b22​+⋯+bm2​,也就是所有残差的平方和。

二、核心约定:和上一个例子完全一致的「分子布局(雅可比布局)」

你截图里的推导,和上一个单样本例子用的是完全相同的求导布局,核心规则:

  • 标量对列向量求导,结果排成行向量(形状和分母向量的转置一致)
  • 向量对向量求导,结果排成雅可比矩阵(分子维度在前,分母维度在后)

三、逐步骤拆解链式法则推导

你截图里的链式法则分解完全正确,和上一个例子结构完全一致:

我们逐行拆解每一个偏导数的来源,每一步都标注维度,保证你完全看懂。

步骤 1:计算

  • 核心逻辑:标量z 对 m 维列向量b 求导,分子布局下结果是行向量,形状(1,m)
  • 展开计算:z=b12​+b22​+⋯+bm2​,对每个分量求偏导:
  • 排成行向量,结果为:
  • 对应上一个例子:上一个例子里b是标量,导数是2b;这里b是列向量,导数就是2bT,完全对应。

步骤 2:计算

  • 核心逻辑:m 维列向量b 对 m 维列向量a 求导,分子布局下是m×m 的雅可比矩阵
  • 计算:b=a−y,y是常数向量,对a求导为 0,因此:
  • ​这个就是m 阶单位矩阵I
  • 对应上一个例子:上一个例子里a,b是标量,导数是1;这里是向量,导数就是单位矩阵I,完全对应。

步骤 3:计算

  • 核心逻辑:m 维列向量a 对 n 维列向量w 求导,分子布局下是m×n 的雅可比矩阵
  • 展开计算:a=Xw,a的第i个元素为 ai​=Xi1​w1​+Xi2​w2​+⋯+Xin​wn​(X 的第 i 行和 w 的内积)对雅可比矩阵的每个元素求偏导:∂wj​∂ai​​=Xij​也就是雅可比矩阵的第 i 行第 j 列,正好是 X 的第 i 行第 j 列,因此整个雅可比矩阵就是X本身,形状(m,n)
  • 对应上一个例子:上一个例子是单样本(m=1),X 就是xT(1×n 的行向量),导数是xT,和这里的结论完全统一。

自动求导

自动求导实现

y.detach()的核心作用是将张量 y 从 PyTorch 的计算图中完全剥离,切断反向传播的梯度流,返回一个和 y 数值完全相同、但不再关联计算历史、无梯度属性的新张量。

可以用于一些网络的参数固定。

b.norm()是求绝对值,b.sum()求和这里等于b

一.梯度验证a.grad == d / a的本质

1. 梯度的数学推导

假设循环执行了n*2,最终b = a * 2^(n+1)

  • 若走if分支(b.sum()>0):d = c = b = a * 2^(n+1),则d/a = 2^(n+1)
  • 若走else分支(b.sum()≤0):d = c = 100*b = a * 2^(n+1) * 100,则d/a = 2^(n+1)*100

而根据求导链式法则:

  • if分支:d对a的导数 = 2^(n+1),等于d/a
  • else分支:d对a的导数 = 2^(n+1)*100,也等于d/a

2. 核心结论

无论循环多少次、走哪个分支,a.grad永远等于d/a,这验证了 PyTorch 自动求导的准确性:它会精准记录实际执行的每一步运算,动态构建计算图,反向传播时严格按实际路径求导,不受循环、分支等动态逻辑的影响。

三、关键知识点

  1. 动态计算图:PyTorch 是「运行时构建图」,每一次前向传播都会根据实际执行的代码(循环次数、分支选择)生成专属计算图,完美适配动态逻辑;
  2. 自动求导的鲁棒性:即使前向传播有循环、条件判断,反向传播依然能正确计算梯度,这是 PyTorch 易用性的核心;
  3. 梯度的物理意义a.grad就是da的导数,这里da的线性函数,因此导数等于d/a(线性函数的导数为系数)。
a = 1 b = 2 b.norm()=2 <1000 → b=4 → 8→16→32→64→128→256→512→1024 b=1024 sum>0 → c=1024 d=1024 a.grad = d/a = 1024
a=-1 b=-2→-4→...→-1024 b.norm()=1024 ≥1000 停止 b.sum() = -1024 <0 → c=100*b = -102400 d=-102400 a.grad = d/a = 102400
a=5 b=10→20→40→80→160→320→640→1280 b=1280 sum>0 → c=1280 d=1280 a.grad = 1280/5 = 256

线性回归

主要讲了线性回归的基本概念,已学过

基础优化算法

线性回归如何从零实现

和我之前写过的linear代码一样

num_examples:n个样本

def synthetic_data(w, b, num_examples): """生成 y = Xw + b + 噪声。""" # 1. 生成输入特征X:服从N(0,1)的正态分布,形状为(样本数, 特征数) X = torch.normal(0, 1, (num_examples, len(w))) # 2. 计算真实标签y:线性模型 y = Xw + b(无噪声) y = torch.matmul(X, w) + b # 3. 加高斯噪声:给y叠加均值0、标准差0.01的正态分布噪声,模拟真实数据的误差 y += torch.normal(0, 0.01, y.shape) # 4. 返回特征X,以及reshape为列向量的标签y return X, y.reshape((-1, 1)) # 设定真实权重w = [2, -3.4],真实偏置b = 4.2 true_w = torch.tensor([2, -3.4]) true_b = 4.2 # 生成1000个样本的特征和标签 features, labels = synthetic_data(true_w, true_b, 1000)

y.reshape((-1, 1))核心规则

  • -1自动计算行数(PyTorch 自己算,不用你写)
  • 1:固定1 列

效果

  • 输入:shape=(1000,)一维向量
  • 输出:shape=(1000, 1)二维列向量

为什么indices必须转成tensor

核心结论:PyTorch 张量的索引,只能用「整数张量 / 整数列表 / 切片」,不能直接用 Python 原生列表做批量索引,转成 tensor 是为了满足 PyTorch 的索引语法要求。

核心原因拆解

PyTorch 中,用批量索引(一次性取多个位置的元素)时,索引必须是torch.Tensor类型(整数型),不能直接用 Python 原生list

  • ✅ 合法:features[torch.tensor([0,2,5])](张量索引,一次性取第 0、2、5 行)
  • ❌ 不推荐 / 有歧义:features[[0,2,5]](虽然部分场景兼容,但属于 NumPy 风格,PyTorch 官方推荐用张量索引,避免维度歧义)
  • 你代码里的batch_indices = torch.tensor(...),就是把切片后的 Python 列表转成张量,用来批量取featureslabels的对应行。

线性回归的简洁实现

Softmax回归

QA:softmax实例化

计算softmax概率函数

计算交叉熵损失

计算反向传播梯度

如何根据梯度进行SGD参数更新

损失函数

蓝色是损失线,绿色是似然函数,橙色为损失函数的梯度

当真实值和预测值相距较远时,梯度大,参数更新幅度大,随着靠近零点参数更新的幅度越来越小

坏处:对于原点远的位置,不一定想要那么大的梯度

特性:不管隔多远,梯度永远是常数,权重更新不会特别大,稳定性有好处

坏处:零点处不可导,不平滑性,当趋于零点时,变化幅度会突然变大,优化到后期会不太稳定

保证了在当预测值和真实值差的远时,有一个均匀的力来靠拢,往回拉,在靠近零点时,优化末期时,梯度绝对值会越来越小,保证优化是平滑的。

图片分类数据集

提前看一下数据读取有多快,保证模型读的比训练快

Softmax回归从零开始实现

1.这是一个Softmax 回归模型的前向传播函数,我们分三部分看:

  • X:输入数据(比如图片,形状通常是[batch_size, channels, height, width]
  • W:权重矩阵
  • b:偏置向量

2. 重点解释W.shape[0]

(1)什么是W

在 Softmax 回归里,权重矩阵W的形状是:[输入特征数, 类别数]

  • 比如:输入是 28×28 的 MNIST 图片,拉平后有28×28=784个像素特征,要分 10 类
  • 那么W的形状就是[784, 10]

(2)W.shape[0]是什么?

W.shape会返回(784, 10),所以:

  • W.shape[0]→ 取第一个维度,就是输入特征的数量(784)
  • W.shape[1]→ 取第二个维度,就是类别数(10)

3. 为什么要X.reshape((-1, W.shape[0]))

这里的核心是让输入数据和权重矩阵的维度匹配,才能做矩阵乘法

举个具体例子:

  • 输入X是一批 MNIST 图片,形状是[32, 1, 28, 28](batch_size=32,单通道,28×28)
  • W.shape[0] = 784(28×28)
  • X.reshape((-1, 784))会把X变成[32, 784],也就是把每张图片拉成一维向量

这样矩阵乘法才能进行:torch.matmul(X_reshaped, W)[32,784] × [784,10] = [32,10]得到每个样本在 10 个类别上的分数(logits)。

4. 完整流程拆解

  1. X.reshape((-1, W.shape[0])):把输入数据从图片格式拉平成[批量数, 特征数]的二维矩阵,-1表示自动计算批量数。
  2. torch.matmul(...):矩阵乘法,得到每个样本的类别分数logits
  3. + b:加上偏置项,给每个类别分数加一个偏移。
  4. softmax(...):把分数转换成概率分布,所有类别概率和为 1。

5. 为什么用W.shape[0]

  • 这样写更通用:不管你的输入特征数是多少(比如 784、1024 等),都能自动适配,不用硬编码数字。
  • 避免了 “魔法数字”:如果直接写28*28,换了其他数据集就会报错,用W.shape[0]可以自动和权重矩阵的输入维度对齐。

softmax回归的简洁实现

本人在学习过程中,发现官网的书籍和jupyter学习比自己记笔记更有效率,所以决定改进学习方式,不在进行内容记录,之后有新的感悟再进行总结。

官网地址:《动手学深度学习》 — 动手学深度学习 2.0.0 documentation

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

离线安装 Nginx,docker换源,apt换源

离线安装 Nginx参考来源一、下载二、编译参数三、配置环境变量四、创建nginx服务五、启动及验证六、编译参数其他、1、下载功能2、当图片服务器参考来源 1、离线安装 Nginx 2、Nginx服务配合负载均衡做代理网关 3、nginx好用的模块 一、下载 Nginx官网&#xff1a;Nginx官网…

作者头像 李华
网站建设 2026/5/19 17:16:03

QEMU理解与分析系列(5):RISCV虚拟版卡初始化

文章目录 1、QOM简介 `register_module_init` 的实现 Machine 类型注册 Machine 类定义 MachineClass 结构体定义 MachineState 结构体定义 virt 机器初始化流程 自定义设备初始化 1、QOM简介 QEMU Object Model (QOM) 是 QEMU 中的一种对象系统,用于实现 QEMU 设备模型和设备…

作者头像 李华
网站建设 2026/5/19 17:15:05

如何轻松解密科学文库PDF:完整实用的3步永久解密指南

如何轻松解密科学文库PDF&#xff1a;完整实用的3步永久解密指南 【免费下载链接】ScienceDecrypting 破解CAJViewer带有效期的文档&#xff0c;支持破解科学文库、标准全文数据库下载的文档。无损破解&#xff0c;保留文字和目录&#xff0c;解除有效期限制。 项目地址: htt…

作者头像 李华