当前位置: 首页 > news >正文

动手学深度学习-学习笔记【二】(基础知识)

文章目录

  • 1、概述
  • 2、课程学习
    • 2.1、深度学习介绍
    • 2.2、安装
    • 2.3、数据操作
    • 2.4、数据预处理
    • 2.5、线性代数
    • 2.6、微积分
    • 2.7、自动微分
    • 2.8、概率
      • 2.8.1、基本概率论
      • 2.8.2、处理多个随机变量
      • 2.8.3、期望和方差
    • 2.9、查阅文档

1、概述

本篇博客用来记录我学习深度学习的学习笔记,本篇博客主要深度学习所需的一些预备知识,
包括数据操作,线性代数,微积分,概率论等

在这里插入图片描述


2、课程学习

2.1、深度学习介绍

在这里插入图片描述

深度学习是机器学习的一种

深度学习在广告推荐中的案例

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


2.2、安装

在这里插入图片描述

Conda 是一个开源的 软件包管理系统 和 环境管理系统,专为数据科学、机器学习、生物信息学等领域设计。它允许用户高效地安装、管理和切换多个版本的软件包及其依赖项,同时支持多种编程语言(如 Python、R、Ruby、Java 等)和跨平台(Linux、macOS、Windows)。
Jupyter Notebook 是一个功能强大的交互式计算工具,广泛应用于数据科学、教育、研究和开发领域。作用包括:数据分析与可视化,机器学习与建模,教育与教学,科研与报告,合作与共享
D2L 是一个面向中文读者的深度学习开源项目,由 李沐博士 等人开发,旨在通过 可运行代码、数学公式和实践案例 的结合,帮助用户系统性地学习深度学习。
PyTorch 是一个开源的深度学习框架,由 Facebook 的 AI 研究团队开发。它基于 Python 语言,支持动态计算图(Dynamic Computation Graph),广泛应用于计算机视觉、自然语言处理等领域。
TorchVision 是 PyTorch 生态中专注于 计算机视觉 的核心库


2.3、数据操作

N 维数组样例
N维数组是机器学习和神经网络的主要数据结构。

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

具有两个轴以上的张量没有特殊的数学名称。

N 维数组样例
N 维数组是机器学习和神经网络的主要数据结构

在这里插入图片描述

在这里插入图片描述

创建数组的前提条件

  • 形状:例如 3 * 4 矩阵
  • 每个元素的数据类型:例如 32 位浮点数
  • 每个元素的值:例如全是0,或者随机数

在这里插入图片描述

访问元素

在这里插入图片描述


首先,我们可以使用 arange 创建一个行向量 x
这个行向量包含以0开始的前12个整数,它们默认创建为整数。
也可指定创建类型为浮点数。张量中的每个值都称为张量的 元素(element)

import torchA = torch.arange(5, dtype=torch.float32)
print(A)   # tensor([0., 1., 2., 3., 4.])

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


可以通过张量的shape属性来访问张量(沿每个轴的长度)的形状

import torchA = torch.arange(5, dtype=torch.float32)
print(A)        # tensor([0., 1., 2., 3., 4.])
print(A.shape)  # torch.Size([5])

如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)

import torchA = torch.arange(5, dtype=torch.float32)
print(A)          # tensor([0., 1., 2., 3., 4.])
print(A.numel())  # 5

改变一个张量的形状而不改变元素数量和元素值,我们可以调用 reshape 函数

import torchA = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A)         
# tensor([[ 0.,  1.,  2.,  3.],
#         [ 4.,  5.,  6.,  7.],
#         [ 8.,  9., 10., 11.],
#         [12., 13., 14., 15.],
#         [16., 17., 18., 19.]])

使用全0全1,其他常量或者从特定分布中随机采样的数字

在这里插入图片描述

通过提供包含数值的 Python 列表(或嵌套列表)来为所需张量中的每个元素赋予确定值

在这里插入图片描述

操作符运算
常见的标准算术运算符(+,-,*,/,**)都可以按升级为按照元素运算

在这里插入图片描述

我们也可以把多个张量连结在一起

dim=0 表示将 X 和 Y 按照行进行合并
dim=1 表示将 X 和 Y 按照列进行合并

在这里插入图片描述


我们也可以通过逻辑运算符构建二元张量
在这里插入图片描述
对张量中的所有元素进行求和会产生一个只有一个元素的张量。
在这里插入图片描述

广播机制

即使形状不同,依然可以进行张量的加减法

广播(Broadcasting)是 PyTorch 中一种允许不同形状张量进行逐元素运算的机制。
核心思想是:自动扩展较小的张量,使其形状与较大的张量兼容,从而避免手动复制数据,节省内存并简化代码。

即使形状不同,我们任然可以通过调用广播机制来执行按元素操作

在这里插入图片描述

特殊元素获取

可以使用 X[-1] 取出最后一行
使用 X[1:3] 取出第二行和第三行

注意:
行和列都是从 0 开始

在这里插入图片描述

除读取外,我们还可以通过指定索引来将元素写入矩阵。

在这里插入图片描述

按照区域赋值
为多个元素赋值相同的值,我们只需要索引所有元素,然后为他们赋值
在这里插入图片描述

python 使用 id()获取变量的地址(类似 C 中的指针)
在 PyTorch 中,id() 是 Python 内置函数,用于返回对象的 唯一标识符(即内存地址)。

运行一些操作可能会导致为新结果分配内存
在这里插入图片描述

执行原地操作

在这里插入图片描述

其中上图第二个样例中,Z 的内存没有发生变化,前后是一致的

如果在后续计算中,没有重复使用 X,我们也可以使用 X[:] = X + YX += Y 来减少操作的内存开销

在这里插入图片描述

numPytorch 使用不一样的数据类型

在这里插入图片描述

numPytorch 可以互相转换

torch 转化为 NumPy 张量
在这里插入图片描述

将大小为 1 的张量转换为 Python 标量

在这里插入图片描述


2.4、数据预处理

为了能用深度学习来解决现实世界的问题,我们经常从预处理原始数据开始, 而不是从那些准备好的张量格式数据开始。

读取数据集
我们首先创建一个人工数据集,并存储在CSV(逗号分隔值)文件 ../data/house_tiny.csv

要从创建的CSV文件中加载原始数据集,我们导入pandas包并调用read_csv函数。

import os
import pandas as pdos.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n')  # 列名f.write('NA,Pave,127500\n')  # 每行表示一个数据样本f.write('2,NA,106000\n')f.write('4,NA,178100\n')f.write('NA,NA,140000\n')data = pd.read_csv(data_file)
print(data)
#    NumRooms Alley   Price
# 0       NaN  Pave  127500
# 1       2.0   NaN  106000
# 2       4.0   NaN  178100
# 3       NaN   NaN  140000

“NaN”项代表缺失值。

处理缺失值

对于缺失值的处理,有两种处理办法,插值法删除法

  • 插值法用一个替代值弥补缺失值
  • 删除法则直接忽略缺失值。

通过位置索引iloc,我们将data分成inputsoutputs, 其中前者为data的前两列,而后者为data的最后一列。
对于inputs中缺少的数值,我们用同一列的均值替换“NaN”项。

在这里插入图片描述

对于inputs中的类别值或离散值,我们将“NaN”视为一个类别。 由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。

在这里插入图片描述

转换为张量格式
现在inputsoutputs中的所有条目都是数值类型,它们可以转换为张量格式。

在这里插入图片描述


2.5、线性代数

标量
仅包含一个数值被称为标量(scalar)

  • 确定的数值被称为标量值
  • 不确定的符号被称为变量

标量由只有一个元素的张量表示。

在这里插入图片描述

向量
向量可以被视为标量值组成的列表。
这些标量值被称为向量的元素(element)分量(component)

通过一维张量表示向量。

在这里插入图片描述

在数学中,我们可以使用下标来引用向量的任一元素
在这里插入图片描述
在代码中,我们通过张量的索引来访问任一元素。
在这里插入图片描述

长度,维度和形状
向量的长度通常称为向量的维度(dimension)
在这里插入图片描述
当用张量表示一个向量(只有一个轴)时,我们也可以通过.shape属性访问向量的长度。
形状(shape)是一个元素组,列出了张量沿每个轴的长度(维数)。 对于只有一个轴的张量,形状只有一个元素。

在这里插入图片描述

矩阵
正如向量将标量从零阶推广到一阶,矩阵将向量从一阶推广到二阶。
我们可以将任意矩阵 A ∈ R m × n A \in \mathbb{R}^{m \times n} ARm×n
视为一个表格,其中每个元素 aij 属于第 i 行,第 j
在这里插入图片描述

当矩阵具有相同数量的行和列时,其形状将变为正方形; 因此,它被称为方阵(square matrix)

当调用函数来实例化张量时, 我们可以通过指定两个分量mn来创建一个形状为m * n的矩阵。

在这里插入图片描述

当我们交换矩阵的行和列时,结果称为矩阵的转置(transpose)

在这里插入图片描述

作为方阵的一种特殊类型,对称矩阵(symmetric matrix)等于其转置

在这里插入图片描述

张量

就像向量是标量的推广,矩阵是向量的推广一样,我们可以构建具有更多轴的数据结构。 张量(本小节中的“张量”指代数对象)是描述具有任意数量轴的n维数组的通用方法。

在这里插入图片描述

张量算法的基本形式

同样,给定具有相同形状的任意两个张量,任何按元素二元运算的结果都将是相同形状的张量。
例如,将两个相同形状的矩阵相加,会在这两个矩阵上执行元素加法。

在这里插入图片描述
具体而言,两个矩阵的按元素乘法称为Hadamard积(Hadamard product)

在这里插入图片描述

将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘。

在这里插入图片描述

降维
我们可以对任意张量进行的一个有用的操作是计算其元素的和
在代码中,求和的函数

import torchx = torch.arange(4, dtype=torch.float32)
print(x)         # tensor([0., 1., 2., 3.])
print(x.sum())   # tensor(6.)

我们可以表示任意形状张量的元素和。

在这里插入图片描述

import torchA = torch.arange(20).reshape(5, 4)
print(A)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19]])print(A.shape)  # torch.Size([5, 4])
print(A.sum())  # tensor(190)

默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。
我们还可以指定张量沿哪一个轴来通过求和降低维度。
以矩阵为例,为了通过求和所有行的元素来降维(轴0)

  • 可以在调用函数时指定axis=0。 由于输入矩阵沿0轴降维以生成输出向量,因此输入轴0的维数在输出形状中消失。(只保留一行
  • 指定axis=1将通过汇总所有列的元素降维(轴1)。因此,输入轴1的维数在输出形状中消失。(只保留一列
import torchA = torch.arange(20).reshape(5, 4)
print(A)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19]])# 对所有行进行降维,只保留一行
A_sum_axis0 = A.sum(axis=0)
print(A_sum_axis0)         # tensor([40, 45, 50, 55])
print(A_sum_axis0.shape)   # torch.Size([4])# 对所有列进行降维,只保留一列
A_sum_axis1 = A.sum(axis=1)
print(A_sum_axis1)         # tensor([ 6, 22, 38, 54, 70])
print(A_sum_axis1.shape)   # torch.Size([5])

torch.Size([4]) 表示一个 一维张量(向量),其形状(shape)为 (4,),即该张量只有一个维度,且该维度的长度为 4。
维度(Dimensions):张量的维度数由 torch.Size 中的元素个数决定。例如:

  • torch.Size([]):0 维张量(标量)。
  • torch.Size([4]):1 维张量(向量)。
  • torch.Size([2, 3]):2 维张量(矩阵)。
  • torch.Size([2, 3, 4]):3 维张量。

沿着行和列对矩阵求和(A.sum(axis=[0, 1])),等价于对矩阵的所有元素进行求和(A.sum())。

import torchA = torch.arange(20).reshape(5, 4)
print(A)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19]])print(A.sum(axis=[0, 1]))  # tensor(190)
print(A.sum())             # tensor(190)

一个与求和相关的量是平均值(mean或average)。 我们通过将总和除以元素总数来计算平均值。

import torchA = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19]])print(A.mean())              # tensor(9.5000)
print(A.sum() / A.numel())   # tensor(9.5000)

numel()PyTorch 中的一个方法,用于返回张量(Tensor)中 所有元素的总数(即 number of elements 的缩写)。
它通过将张量的所有维度大小相乘,计算出总元素数量。


同样,计算平均值的函数也可以沿指定轴降低张量的维度。

import torchA = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19]])print(A.mean())              # tensor(9.5000)
print(A.mean(axis=0))        # tensor([ 8.,  9., 10., 11.])
print(A.mean(axis=1))        # tensor([ 1.5000,  5.5000,  9.5000, 13.5000, 17.5000])

非降维求和
有时在调用函数来计算总和或均值时保持轴数不变会很有用。

import torchA = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19]])# 降维求和
sum_A_old = A.sum(axis=1)
print(sum_A_old)         # tensor([ 6., 22., 38., 54., 70.])
print(sum_A_old.shape)   # torch.Size([5])# 非降维求和
sum_A = A.sum(axis=1, keepdims=True)
print(sum_A)             
# tensor([[ 6.],
#         [22.],
#         [38.],
#         [54.],
#         [70.]])
print(sum_A.shape)       # torch.Size([5, 1])

如果我们想沿某个轴计算A元素的累积总和, 比如axis=0(按行计算),可以调用cumsum函数。 此函数不会沿任何轴降低输入张量的维度。

import torchA = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19]])print(A.cumsum(axis=0))
# tensor([[ 0.,  1.,  2.,  3.],
#         [ 4.,  6.,  8., 10.],
#         [12., 15., 18., 21.],
#         [24., 28., 32., 36.],
#         [40., 45., 50., 55.]])

点积

给定两个向量,他们的点积是相同位置的元素乘积的和

import torchx = torch.arange(4, dtype=torch.float32)
y = torch.ones(4, dtype = torch.float32)print(x)              # tensor([0., 1., 2., 3.])
print(y)              # tensor([1., 1., 1., 1.])
print(torch.dot(x,y)) # tensor(6.)

矩阵-向量积

矩阵向量积 Ax 是一个长度为 m 的列向量, 其第 i 个元素是点积 aiTx
在这里插入图片描述

在代码中使用张量表示矩阵-向量积,我们使用mv函数。
当我们为矩阵A和向量x调用torch.mv(A, x)时,会执行矩阵-向量积。

import torchA = torch.arange(20, dtype=torch.float32).reshape(5, 4)
x = torch.arange(4, dtype=torch.float32)print(A)
# tensor([[ 0.,  1.,  2.,  3.],
#         [ 4.,  5.,  6.,  7.],
#         [ 8.,  9., 10., 11.],
#         [12., 13., 14., 15.],
#         [16., 17., 18., 19.]])
print(x)              # tensor([0., 1., 2., 3.])print(torch.mv(A, x)) # tensor([ 14.,  38.,  62.,  86., 110.])

注意,A的列维数(沿轴1的长度)必须与x的维数(其长度)相同。


矩阵-矩阵乘法

我们可以将矩阵-矩阵乘法 AB 看作简单地执行 m 次矩阵-向量积,并将结果拼接在一起,形成一个 n * m 矩阵。
在这里插入图片描述

import torchA = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = torch.ones(4, 3)print(A)
# tensor([[ 0.,  1.,  2.,  3.],
#         [ 4.,  5.,  6.,  7.],
#         [ 8.,  9., 10., 11.],
#         [12., 13., 14., 15.],
#         [16., 17., 18., 19.]])
print(B)
# tensor([[1., 1., 1.],
#         [1., 1., 1.],
#         [1., 1., 1.],
#         [1., 1., 1.]])
print(torch.mm(A, B))
# tensor([[ 6.,  6.,  6.],
#         [22., 22., 22.],
#         [38., 38., 38.],
#         [54., 54., 54.],
#         [70., 70., 70.]])

范数
向量的范数是表示一个向量有多大。 这里考虑的大小(size)概念不涉及维度,而是分量的大小。

范数的常见性质

  1. 按照常量因子缩放
    在这里插入图片描述

  2. 三角不等式
    在这里插入图片描述

  3. 范数的非负性
    在这里插入图片描述


L2范数
向量元素平方和的平方根
在这里插入图片描述

import torchu = torch.tensor([3.0, -4.0])
print(torch.norm(u))    # tensor(5.)

L1范数
向量元素的绝对值之和
在这里插入图片描述

import torchu = torch.tensor([3.0, -4.0])
print(torch.abs(u).sum())  # tensor(7.)

Lp 范数
在这里插入图片描述


Frobenius范数
Frobenius范数(Frobenius norm)是矩阵元素平方和的平方根

在这里插入图片描述

import torchA = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A)   
# tensor([[ 0.,  1.,  2.,  3.],
#         [ 4.,  5.,  6.,  7.],
#         [ 8.,  9., 10., 11.],
#         [12., 13., 14., 15.],
#         [16., 17., 18., 19.]])
print(torch.norm(A))  # tensor(49.6991)

2.6、微积分

在2500年前,古希腊人把一个多边形分成三角形,并把它们的面积相加,才找到计算多边形面积的方法。 为了求出曲线形状(比如圆)的面积,古希腊人在这样的形状上刻内接多边形。
如下图所示,内接多边形的等长边越多,就越接近圆。 这个过程也被称为逼近法(method of exhaustion)

在这里插入图片描述

逼近法就是积分(integral calculus)的起源。

在深度学习中,我们“训练”模型,不断更新它们,使它们在看到越来越多的数据时变得越来越好。 通常情况下,变得更好意味着最小化一个损失函数(loss function), 即一个衡量“模型有多糟糕”这个问题的分数。 最终,我们真正关心的是生成一个模型,它能够在从未见过的数据上表现良好。 但“训练”模型只能将模型与我们实际能看到的数据相拟合。
因此,我们可以将拟合模型的任务分解为两个关键问题:

  • 优化(optimization):用模型拟合观测数据的过程;
  • 泛化(generalization):数学原理和实践者的智慧,能够指导我们生成出有效性超出用于训练的数据集本身的模型。

导数和微分
在深度学习中,我们通常选择对于模型参数可微的损失函数。
简而言之,对于每个参数, 如果我们把这个参数增加或减少一个无穷小的量,可以知道损失会以多快的速度增加或减少,

导数的定义:
在这里插入图片描述

如果 f ′ ( α ) f'(\alpha) f(α) 存在,则 f f f α \alpha α 处是可微的
如果 f f f在一个区间内的每个数上都是可微的,则此函数在此区间中是可微的。


微分常见法则
在这里插入图片描述


绘制图及其切线

import numpy as np
from matplotlib_inline import backend_inline
from d2l import torch as d2ldef use_svg_display():  #@save"""使用svg格式在Jupyter中显示绘图"""backend_inline.set_matplotlib_formats('svg')def set_figsize(figsize=(3.5, 2.5)):  #@save"""设置matplotlib的图表大小"""use_svg_display()d2l.plt.rcParams['figure.figsize'] = figsize#@save
def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):"""设置matplotlib的轴"""axes.set_xlabel(xlabel)axes.set_ylabel(ylabel)axes.set_xscale(xscale)axes.set_yscale(yscale)axes.set_xlim(xlim)axes.set_ylim(ylim)if legend:axes.legend(legend)axes.grid()#@save
def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None,ylim=None, xscale='linear', yscale='linear',fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None):"""绘制数据点"""if legend is None:legend = []set_figsize(figsize)axes = axes if axes else d2l.plt.gca()# 如果X有一个轴,输出Truedef has_one_axis(X):return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list)and not hasattr(X[0], "__len__"))if has_one_axis(X):X = [X]if Y is None:X, Y = [[]] * len(X), Xelif has_one_axis(Y):Y = [Y]if len(X) != len(Y):X = X * len(Y)axes.cla()for x, y, fmt in zip(X, Y, fmts):if len(x):axes.plot(x, y, fmt)else:axes.plot(y, fmt)set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend)def f(t):return 3 * t ** 2 - 4 * tx = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
d2l.plt.show()

在这里插入图片描述


偏导数
到目前为止,我们只讨论了仅含一个变量的函数的微分。
在深度学习中,函数通常依赖于许多变量。
因此,我们需要将微分的思想推广到多元函数(multivariate function上。

  • 导数:在一元函数 y = f ( x ) y=f(x) y=f(x)中,导数 f ′ ( x ) f'(x) f(x)表示曲线在点 ( x , f ( x ) ) (x, f(x)) x,f(x)处的切线斜率
  • 偏导数:在多元函数 z = f ( x , y ) z=f(x, y) z=f(x,y)中,偏导数 ∂ f ∂ x \frac{\partial f}{\partial x} xf表示曲面在点 ( x , y , f ( x , y ) ) (x,y,f(x,y)) (x,y,f(x,y))处沿着x轴方向的切线斜率(固定y不变)

在这里插入图片描述


梯度
我们可以连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量

在这里插入图片描述


链式法则
然而,上面方法可能很难找到梯度。
这是因为在深度学习中,多元函数通常是复合(composite)的, 所以难以应用上述任何规则来微分这些函数。
幸运的是,链式法则可以被用来微分复合函数。

链式法则是微积分中用于求解复合函数导数的核心工具
其核心思想是:复合函数的导数等于外层函数的导数乘以内层函数的导数,层层传递,如同链条一样。

在这里插入图片描述


2.7、自动微分

深度学习框架通过自动计算导数,即自动微分(automatic differentiation)来加快求导。
实际中,根据设计好的模型,系统会构建一个计算图(computational graph), 来跟踪计算是哪些数据通过哪些操作组合起来产生输出。
自动微分使系统能够随后反向传播梯度。
这里,反向传播(backpropagate)意味着跟踪整个计算图,填充关于每个参数的偏导数。

反向传播:反向传播(Back Propagation, BP) 是神经网络训练的核心算法之一,主要用于计算损失函数对网络参数的梯度,并通过梯度下降等优化方法更新参数,从而最小化损失函数。


计算图
PyTorch 中的 计算图(Computational Graph)是深度学习框架中用于描述数学运算的 有向无环图(DAG)
它通过节点记录张量(Tensor)之间的运算关系,是 PyTorch 实现 自动求导(Autograd)的核心机制。

  • 节点(Node)
    叶子节点(Leaf Node):用户直接创建的张量(如 x = torch.tensor(..., requires_grad=True)),grad_fnNone
    中间节点:由运算生成的张量(如 y = x + 2),grad_fn 记录生成该张量的操作(如 <AddBackward0>)。
  • 边(Edge)
    表示张量之间的依赖关系,即数据流。例如,y = x * x 中,边连接 x 和 y,表示 y 的计算依赖于 x。

在这里插入图片描述


一个简单的例子
一个简单的例子,假设我们想要对函数 y = 2 ∗ x T ∗ x y=2*x^T*x y=2xTx关于列向量 x x x求导。首先,我们创建变量 x x x并为其分配一个初始值。
在我们计算 y y y关于 x x x的梯度之前,需要一个地方来存储梯度。(避免每次都用新的内存来存储梯度,造成内存耗尽)

PyTorch 中,x.requires_grad = Truex.requires_grad_(True) 的作用是启用对张量 x 的梯度追踪,这是自动求导(Autograd)的核心机制之一。

PyTorch 默认不会追踪张量的操作,以节省内存
启用后的作用

  • PyTorch 会记录所有对 x 的操作(如加法、乘法、矩阵运算等),构建一个动态计算图
  • 在调用 backward() 进行反向传播时,可以自动计算 x 的梯度(x.grad)。
import torchx = torch.arange(4.0)
print(x)               # tensor([0., 1., 2., 3.])# 启动对张量 x 的梯度追踪
x.requires_grad_(True)
print(x.grad)          # 默认值是 None# 计算 y = 2x^2
y = 2 * torch.dot(x, x)
print(y)               # tensor(28., grad_fn=<MulBackward0>)# 调用反向传播函数来自动计算 y 关于 x 每个分量的梯度,并打印
y.backward()
print(x.grad)          # tensor([ 0.,  4.,  8., 12.])# 验证梯度是否正确
print(x.grad == 4 * x) # tensor([True, True, True, True])

grad_fn=<MulBackward0>

  • 含义grad_fn 是张量的一个属性,表示生成该张量的操作对应的梯度函数(Gradient Function)。
  • <MulBackward0> 表示该张量是通过 乘法操作(*)生成的,并且在反向传播时会使用乘法的反向传播规则(即乘法的导数)。
  • 动态计算图:PyTorch 使用动态计算图记录操作历史。grad_fn 是计算图中的一部分,用于追踪该张量是如何从其他张量(叶子节点或中间节点)生成的。

非标量变量的反向传播
y不是标量时,向量y关于向量x的导数的最自然解释是一个矩阵。
对于高阶和高维的y和x,求导的结果可以是一个高阶张量。

import torchx = torch.arange(4.0)
print(x)               # tensor([0., 1., 2., 3.])# 启动对张量 x 的梯度追踪
x.requires_grad_(True)
print(x.grad)          # 默认值是 None# 对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。
# 本例只想求偏导数的和,所以传递一个1的梯度是合适的
y = x * x
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
print(x.grad)          # tensor([0., 2., 4., 6.])

为什么需要 .sum()
backward() 默认处理标量。若 y 是向量,需通过 .sum()提供梯度权重(gradient 参数)将其转换为标量。
对于 y = x²,其导数为 dy/dx = 2x。由于 y.sum()x₁² + x₂² + x₃²,其对 x 的梯度是 [2x₁, 2x₂, 2x₃]


分离计算
有时,我们希望将某些计算移动到记录的计算图之外。
例如,假设 y y y是作为 x x x的函数计算的,而 z z z则是作为 y y y x x x的函数计算的。
想象一下,我们想计算 z z z关于 x x x的梯度,但由于某种原因,希望将 y y y视为一个常数, 并且只考虑到 x x x y y y被计算后发挥的作用。
这里可以分离 y y y来返回一个新变量 u u u,该变量与 y y y具有相同的值, 但丢弃计算图中如何计算 y y y的任何信息。

import torchx = torch.arange(4.0)
print(x)               # tensor([0., 1., 2., 3.])# 启动对张量 x 的梯度追踪
x.requires_grad_(True)
print(x.grad)          # 默认值是 Noney = x * x
# 创建新张量,与原张量数据相同,但不记录梯度,防止梯度传播
u = y.detach()
z = u * xz.sum().backward()
print(x.grad == u)     # tensor([True, True, True, True])

u u u 作为常数处理
随后可以单独计算 y y y 关于 x x x 的导数


Python 控制梯度流的计算
使用自动微分的一个好处是: 即使构建函数的计算图需要通过 Python 控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度
在下面的代码中,while循环的迭代次数和 if 语句的结果都取决于输入 a 的值。

import torchdef f(a):b = a * 2while b.norm() < 1000:b = b * 2if b.sum() > 0:c = belse:c = 100 * breturn c# 创建一个标量张量(scalar tensor),
# 其数值从标准正态分布(均值为 0,标准差为 1)中随机生成,
# 并且启用了梯度追踪功能。
a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()print(a.grad == d / a)    # tensor(True)

我们现在可以分析上面定义的 f 函数。 请注意,它在其输入 a 中是分段线性的。 换言之,对于任何 a,存在某个常量标量 k,使得 f(a)=k*a,其中 k 的值取决于输入 a,因此可以用 d/a 验证梯度是否正确。


2.8、概率

简单地说,机器学习就是做出预测。

2.8.1、基本概率论

大数定律(law of large numbers): 将出现的次数除以投掷的总次数, 即此事件(event)概率的估计值。随着投掷次数的增加,估计值会越来越接近真实的潜在概率。
在统计学中,我们把从概率分布中抽取样本的过程称为抽样(sampling)

import torch
from torch.distributions import multinomial
from d2l import torch as d2l# torch.ones([6]):生成一个形状为 (6,) 的张量,所有元素初始化为 1,即 [1, 1, 1, 1, 1, 1]。
fair_probs = torch.ones([6]) / 6        # tensor([0.1667, 0.1667, 0.1667, 0.1667, 0.1667, 0.1667])# total_count 实验的总次数, fair_probs 指定每个类别的概率分布
# sample 从分布中采样一次,返回一个形状为 (6, )的张量(one-hot 向量)
# 返回一个 one-hot 向量,其中只有一个元素为 1(表示该类别被选中),其余为 0。
res = multinomial.Multinomial(10000, fair_probs).sample()print(res / 10000)                      # tensor([0.1629, 0.1630, 0.1709, 0.1685, 0.1623, 0.1724])# 绘图  看到这些概率如何随着时间的推移收敛到真实概率。 
counts = multinomial.Multinomial(10, fair_probs).sample((500,))
cum_counts = counts.cumsum(dim=0)
estimates = cum_counts / cum_counts.sum(dim=1, keepdims=True)d2l.set_figsize((6, 4.5))
for i in range(6):d2l.plt.plot(estimates[:, i].numpy(),label=("P(die=" + str(i + 1) + ")"))
d2l.plt.axhline(y=0.167, color='black', linestyle='dashed')
d2l.plt.gca().set_xlabel('Groups of experiments')
d2l.plt.gca().set_ylabel('Estimated probability')
d2l.plt.legend()
d2l.plt.show()

在这里插入图片描述


概率论公理
我们将集合 S = { 1 , 2 , 3 , 4 , 5 , 6 } S = \{1, 2, 3,4,5,6\} S={1,2,3,4,5,6}称为样本空间(sample space)结果空间(outcome space)
事件(event)是一组给定样本空间的随机结果。
在这里插入图片描述


随机变量
随机变量几乎可以是任何数量,并且它可以在随机实验的一组可能性中取一个值。
我们可以将 P ( X ) P(X) P(X)表示为随机变量 X X X上的分布(distribution): 分布告诉我们 X X X获得某一值的概率。
另一方面,我们可以简单用 P ( a ) P(a) P(a)表示随机变量取值 a a a的概率。
离散(discrete)随机变量(如骰子的每一面) 和连续(continuous)随机变量(如人的体重和身高)之间存在微妙的区别。
如果我们进行足够精确的测量,最终会发现这个星球上没有两个人具有完全相同的身高。 在这种情况下,询问某人的身高是否落入给定的区间,比如是否在1.79米和1.81米之间更有意义。 在这些情况下,我们将这个看到某个数值的可能性量化为密度(density)

2.8.2、处理多个随机变量

联合概率
P ( A = a , B = b ) P(A=a, B=b) P(A=a,B=b)表示: A = a A=a A=a B = b B=b B=b同时满足的概率

条件概率
P ( B = b ∣ A = a ) P(B=b | A=a) P(B=bA=a)表示:在 a a a已经发生的条件下, A = a A=a A=a B = b B=b B=b同时满足的概率
P ( B = b ∣ A = a ) = P ( A = a , B = b ) P ( A = a ) P(B=b | A=a) = \frac{P(A=a, B=b)}{P(A = a)} P(B=bA=a)=P(A=a)P(A=a,B=b)

贝叶斯定理
P ( B = b ∣ A = a ) = P ( A = a ∣ B = b ) ∗ P ( B = b ) P ( A = a ) P(B=b | A=a) = \frac{P(A=a | B=b) * P(B=b)}{P(A = a)} P(B=bA=a)=P(A=a)P(A=aB=b)P(B=b)

边际化
B B B的概率相当于计算 A A A的所有可能选择,并将所有选择的联合概率聚合在一起
P ( B ) = ∑ A P ( A , B ) P(B) = \sum_{A} P(A,B) P(B)=AP(A,B)

独立性
如果两个随机变量 A A A B B B独立的,意味着事件 A A A的发生跟事件 B B B的发生无关。
P ( B = b ∣ A = a ) = P ( B = b ) P(B=b | A=a) =P(B=b) P(B=bA=a)=P(B=b)
P ( B = b , A = a ) = P ( B = b ) ∗ P ( A = a ) P(B=b, A=a) =P(B=b) * P(A=a) P(B=b,A=a)=P(B=b)P(A=a)


2.8.3、期望和方差

一个随机变量 X X X期望(expectation,或平均值(average))表示为
E [ X ] = ∑ x x ∗ P ( X = x ) E[X] = \sum_{x} x * P(X = x) E[X]=xxP(X=x)

衡量随机变量 X X X与其期望值的偏置。这可以通过方差来量化
V a r [ X ] = E [ ( X − E [ X ] ) 2 ] Var[X]=E[(X-E[X])^2] Var[X]=E[(XE[X])2]

方差的平方根就是标准差


2.9、查阅文档

查找指定函数的方法的作用

help(torch.ones)

返回结果

Help on built-in function ones in module torch:ones(...)ones(*size, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) -> TensorReturns a tensor filled with the scalar value `1`, with the shape definedby the variable argument :attr:`size`.Args:size (int...): a sequence of integers defining the shape of the output tensor.Can be a variable number of arguments or a collection like a list or tuple.Keyword arguments:out (Tensor, optional): the output tensor.dtype (:class:`torch.dtype`, optional): the desired data type of returned tensor.Default: if ``None``, uses a global default (see :func:`torch.set_default_dtype`).layout (:class:`torch.layout`, optional): the desired layout of returned Tensor.Default: ``torch.strided``.device (:class:`torch.device`, optional): the desired device of returned tensor.Default: if ``None``, uses the current device for the default tensor type(see :func:`torch.set_default_device`). :attr:`device` will be the CPUfor CPU tensor types and the current CUDA device for CUDA tensor types.requires_grad (bool, optional): If autograd should record operations on thereturned tensor. Default: ``False``.Example::>>> torch.ones(2, 3)tensor([[ 1.,  1.,  1.],[ 1.,  1.,  1.]])>>> torch.ones(5)tensor([ 1.,  1.,  1.,  1.,  1.])

http://www.dtcms.com/a/267314.html

相关文章:

  • 若 VSCode 添加到文件夹内右键菜单中显示(通过reg文件方式)
  • 在 Windows 上安装和运行 Apache Kafka
  • Android Input 系列专题【事件的读取与分发】
  • 在SSM+vue项目中上传表单数据和文件
  • android开发中的 AndroidX 版本的查看 及 constraintLayout的简单用法
  • 【性能优化】程序性能优化:疏通胜于堵塞
  • 【Elasticsearch】检索高亮
  • 成为git砖家(12): 看懂git合并分支时冲突提示符
  • HTML初学者第三天
  • hono框架绑定cloudflare的d1数据库操作步骤
  • Redis基础的介绍与使用(一)(Redis简介以及Redis下载和安装)
  • Git 版本控制完全指南:从入门到精通
  • 【Halcon】WPF 自定义Halcon显示控件完整流程与 `OnApplyTemplate` 未触发的根本原因解析!
  • Web3 Study Log 003
  • 蓝牙墨水屏上位机学习(3)
  • Java 与 Vue 全栈开发:“一课一得“ 学习笔记系统实战
  • OneCode图表配置速查手册
  • CMake是什么
  • NV183NV185美光固态闪存NV196NV201
  • 供应链管理-采购管理:国际贸易及支付领域中常见的支持方式
  • FLUX.1-Kontext 高效训练 LoRA:释放大语言模型定制化潜能的完整指南
  • 软件版本FCCU(故障采集与控制单元)设计
  • 如何选择不会降低网站速度的WordPress主题
  • 动手实践OpenHands系列学习笔记11:现代开发流程
  • C#指针:解锁内存操作的底层密码
  • DVWA靶场通关笔记-验证码绕过reCAPTCHA(Medium级别)
  • 网安系列【6】之[特殊字符] SQL注入揭秘:从入门到防御实战指南
  • cloudflare配合github搭建免费开源影视LibreTV一个独享视频网站 详细教程
  • React Native 亲切的组件们(函数式组件/class组件)和陌生的样式
  • 百度开源文心一言4.5:论文解读和使用入门