2、预备知识
2.1、数据操作
batch:以图片数据为例,一次读入的图片数量。
小批量样本可以充分利用GPU进行并行计算提高计算效率。
数据访问
数组:np.array To pd.Series To torch.tensor
二维张量的写法
a = torch. ones( 4 , 9 )
a = torch. ones( ( 4 , 9 ) ) #李沐老师
a = torch. arange( 36 ) . reshape( 4 , 9 )
a = torch. arange( 36 ) . reshape( ( 4 , 9 ) ) #李沐老师
多加一个括号,结果都是一致的,都是表示二维张量,张量形状都是(4,9),所以二维有两种写法,但再加一层括号,形状就变成了(1,4,9)三维,判断维数技巧:最外面的括号去掉开始数,比如:
a = torch. ones( ( ( ( ( ( 4 , 9 ) ) ) ) ) )
这个形状是(1,1,1,1,1,4,9)
将多个张量沿指定的维度进行连接
torch. cat( inputs, dim= 0 , out= None )
inputs
:一个或多个输入张量(可以是相同形状的多个张量)。
dim
:指定的连接维度,默认为0。
out
:输出的张量,默认为None
不同形状向量相加广播机制(broadcasting mechanism)【必须同纬度】
a = torch. arange( 3 ) . reshape( 3 , 1 )
b = torch. arange( 2 ) . reshape( 1 , 2 )
a + b
(
0
1
2
)
−
>
(
0
0
1
1
2
2
)
\begin{pmatrix} 0 \\ 1 \\ 2\\ \end{pmatrix} ->\begin{pmatrix} 0 & 0 \\ 1 & 1\\ 2 & 2\\ \end{pmatrix}
0 1 2
− >
0 1 2 0 1 2
(
0
1
)
−
>
(
0
1
0
1
0
1
)
\begin{pmatrix} 0 &1 \end{pmatrix} ->\begin{pmatrix} 0 & 1 \\ 0 & 1\\ 0 & 1\\ \end{pmatrix}
( 0 1 ) − >
0 0 0 1 1 1
(
0
0
1
1
2
2
)
+
(
0
1
0
1
0
1
)
=
(
0
1
1
2
2
3
)
\begin{pmatrix} 0 & 0 \\ 1 & 1\\ 2 & 2\\ \end{pmatrix} + \begin{pmatrix} 0 & 1 \\ 0 & 1\\ 0 & 1\\ \end{pmatrix} =\begin{pmatrix} 0 & 1 \\ 1 & 2\\ 2 & 3\\ \end{pmatrix}
0 1 2 0 1 2
+
0 0 0 1 1 1
=
0 1 2 1 2 3
向量|张量相加得到了意外的结果,可以考虑是不是误将不同形状的向量相加了,触发了广播机制。
使用sum求和(沿某个轴方向 axis )
axis = ?意味着把那一维压缩 keepdims=True 表示保持求和结果的维度和原数组一致。保持维度一致通常是为了方便后续的运算或对结果的处理。
a. sum ( axis= 0 , keepdims= True ) . shape, a. sum ( axis= 0 , keepdims= True )
(torch.Size([1, 5, 4]), tensor([[[2., 2., 2., 2.], [2., 2., 2., 2.], [2., 2., 2., 2.], [2., 2., 2., 2.], [2., 2., 2., 2.]]]))
这里keepdims=True和广播有很好的搭配效果。每一个元素/sum
,维度同但形状不同,广播,维度同形状也同,可以执行。
复制,可能会导致开辟新内存
before = id ( y)
x = x + y
id ( y) == before
False
执行原地操作的两种方式:
x[ : ] = x + y
x += y
注意
b[ : ] = a; #类似于view b变a也一起变,这种写法实际使用时b不轻易改变
避免大张量的过度复制,减少内存开销。
z = X. clone( ) #Z得到一个X的副本
numpy 转 torch ,反之不可行
a = x. numpy( )
b = torch. tensor( a)
type ( a) , type ( b)
(numpy.ndarray, torch.Tensor)
在jupyter 中一次性输出多个内容使用逗号间隔实现
将大小为1的张量转换为 Python标量
使用 item(),或者强制类型转换实现
a = torch. tensor( [ 3.5 ] )
a, a. item( ) , float ( a) , int ( a)
(tensor([3.5000]), 3.5, 3.5, 3)
pandas读入,再缺失值处理,转为torch张量的过程
import pandas as pd
data = pd. read_csv( data_file)
缺失值处理:插值法 or 删除
inputs, outputs = data. iloc[ : , 0 : 2 ] , data. iloc[ : , 2 ]
inputs = inputs. fillna( inputs. mean( ) )
inputs = pd. get_dummies( inputs, dummy_na = True )
pd.get_dummies()
函数将输入的数据集inputs
中的每个分类变量【不是数值的,比如字符串值】都拆分为多个二进制变量,每个变量表示一种可能的分类。dummy_na=True
参数表示要在创建虚拟变量时包含对缺失值的处理【把NaN也视为一类情况】。
import torch
X, y = torch. tensor( inputs. values) , torch. tensor( outputs. values)
X, y
2.2、线性代数&矩阵计算
乘法(矩阵乘向量)
c
=
A
b
w
h
e
r
e
c
i
=
∑
i
A
i
j
b
j
c = Ab \ \ \ where \ \ \ c_i = \sum_i A_{ij}b_j
c = A b w h ere c i = i ∑ A ij b j
乘法(矩阵乘矩阵)
C
=
A
B
w
h
e
r
e
C
i
k
=
∑
j
A
i
j
B
j
k
C = AB\ \ \ where\ \ \ C_{ik} = \sum_j A_{ij}B_{jk}
C = A B w h ere C ik = j ∑ A ij B jk
求范数
向量的模推广到矩阵,范数就是‘矩阵的模’。
∣
∣
a
∣
∣
2
=
[
∑
i
=
1
m
a
i
2
]
1
2
||a||_2 =[\sum_{i=1}^ma_i^2]^{\frac{1}{2}}
∣∣ a ∣ ∣ 2 = [ i = 1 ∑ m a i 2 ] 2 1
下面是计算张量的2范数|F范数【Frobenius范数】:
torch. norm( torch. ones( ( 4 , 9 ) ) )
∣
∣
A
∣
∣
F
r
o
b
=
[
∑
i
j
A
i
j
2
]
1
2
||A||_{Frob} =[\sum_{ij}A_{ij}^2]^{\frac{1}{2}}
∣∣ A ∣ ∣ F ro b = [ ij ∑ A ij 2 ] 2 1
2.3、导数
用的少,pytorch 实现了自动微分计算自动求导。
压导数
将导数拓展到不可微的函数。
计算图
张量的计算通常会生成计算图。当你执行张量操作时,例如加法、乘法、矩阵乘法、激活函数等,这些操作会被记录到计算图中。计算图是一个有向无环图(DAG),其中节点表示张量操作,边表示操作之间的依赖关系。
自动求导的两种模式
链式法则:
∂
y
∂
x
=
∂
y
∂
u
n
∂
u
n
∂
u
n
−
1
.
.
.
∂
u
2
∂
u
1
∂
u
1
∂
x
\frac{∂y}{∂x}=\frac{∂y}{∂u_n}\frac{∂u_n}{∂u_{n-1}}...\frac{∂u_2}{∂u_1}\frac{∂u_1}{∂x}
∂ x ∂ y = ∂ u n ∂ y ∂ u n − 1 ∂ u n ... ∂ u 1 ∂ u 2 ∂ x ∂ u 1
正向积累
∂
y
∂
x
=
∂
y
∂
u
n
(
∂
u
n
∂
u
n
−
1
(
.
.
.
(
∂
u
2
∂
u
1
∂
u
1
∂
x
)
)
)
\frac{∂y}{∂x}=\frac{∂y}{∂u_n}(\frac{∂u_n}{∂u_{n-1}}(...(\frac{∂u_2}{∂u_1}\frac{∂u_1}{∂x})))
∂ x ∂ y = ∂ u n ∂ y ( ∂ u n − 1 ∂ u n ( ... ( ∂ u 1 ∂ u 2 ∂ x ∂ u 1 )))
反向积累、又称反向传递
∂
y
∂
x
=
(
(
(
∂
y
∂
u
n
∂
u
n
∂
u
n
−
1
)
.
.
.
)
∂
u
2
∂
u
1
)
∂
u
1
∂
x
\frac{∂y}{∂x}=(((\frac{∂y}{∂u_n}\frac{∂u_n}{∂u_{n-1}})...)\frac{∂u_2}{∂u_1})\frac{∂u_1}{∂x}
∂ x ∂ y = ((( ∂ u n ∂ y ∂ u n − 1 ∂ u n ) ... ) ∂ u 1 ∂ u 2 ) ∂ x ∂ u 1
反向传播逻辑与高数手写复合函数求导完全一致。
求导和反向传播 :计算图可以帮助自动计算函数的导数,特别是在深度学习中的反向传播算法中。通过在计算图中计算每个节点的梯度,可以从输出端反向传播梯度到输入端,以便优化模型的参数。
x. requires_grad_( True ) #使用requires_grad=True参数来指定需要对其求导,计算时会存储梯度
x. grad#访问梯度,目前未计算是空的
y = 2 * torch. dot( x, x ) #内积
y
tensor(28., grad_fn=<MulBackward0>)
y. backward( ) #求导
x. grad
tensor(28., grad_fn=<MulBackward0>)
x. grad == 4 * x#判断 导数是不是 4x
tensor([True, True, True, True])
x. grad. zero_( ) #默认情况pytorch会累积梯度,需要清除之前的值。
y = x. sum ( ) # y =x1+x2+x3+...
y. backward( )
y, x. grad
(tensor(6., grad_fn=<SumBackward0>), tensor([1., 1., 1., 1.]))
非标量调用 backward,需要传入 gradient 参数
【在PyTorch中,反向传播(backward)函数用于计算非标量张量的梯度。当计算标量的梯度时,PyTorch会自动计算并传播梯度,而无需明确传入梯度参数。然而,当处理非标量张量时,需要手动传入梯度参数。】
x. grad. zero_( )
y = x * x
#等价于 y.backword(torch.ones(len(x)))
y. sum ( ) . backward( )
y, x. grad
(tensor([0., 1., 4., 9.], grad_fn=<MulBackward0>), tensor([0., 2., 4., 6.]))
y.sum().backward()
是使用 PyTorch 的自动微分功能进行反向传播。它计算了 y
张量的和,并通过链式法则将梯度传播回各个输入张量。这里的输入张量是 x
。
x. grad. zero_( )
y = x * x
#由于 y 是通过对 x 进行元素级乘法实现的(y = x * x),因此 y 对于每个元素 x[i] 的梯度是 2 * x[i]
u = y. detach( )
#用于将张量 y 从计算图中分离出来,并且将其梯度信息置为无。这样做的目的是防止梯度回传时对 u 的梯度计算,从而实现对 u 的一种冻结。通常,当希望保留某个张量的值,但不想在反向传播过程中计算它的梯度时,就会使用 detach() 方法。通过将张量分离并赋给一个新的变量,在接下来的计算过程中使用这个新变量 u,而且它的梯度会被忽略,从而实现参数冻结或临时截断梯度流的目的。
z = u * x
z. sum ( ) . backward( )
x. grad == u
tensor([True, True, True, True])
2.4、基础优化方法
梯度计算往往是深度学习中成本最贵的。
小批量随机梯度下降是深度学习默认的求解方法。
两个重要的超参数是 批量大小和学习率。