【动手学深度学习v2】学习笔记02:线性代数、矩阵计算、自动求导

前文回顾:数据操作、数据预处理

一、线性代数实现

1.1 标量和向量

在pytorch中,我们使用一个元素的张量来表示标量
我们可以将向量视为标量值组成的列表。

x = torch.tensor([3.0])				# 标量
y = torch.tensor([2.0, 1.0, 4.0])	# 向量

1.2 矩阵和多维张量

我们可以通过制定两个分量m和n来创建一个形状为 m × n m \times n m×n矩阵。并且,通过T运算,我们可以对矩阵进行转置。

A = torch.arange(20).reshape(4, 5)	# 矩阵
AT = A.T	# 转置

对称矩阵B,等于其转置: B = B τ B = B^\tau B=Bτ

就像向量是标量的推广,矩阵是向量的推广一样,我们可以构建具有更多轴的数据结构

X = torch.arange(24).reshape(2, 3, 4)	# 三维张量

上例中X的内容为:
[ [ 0 1 2 3 4 5 6 7 8 9 10 11 ] [ 12 13 14 15 16 17 18 19 20 21 22 23 ] ] \begin{bmatrix} \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} \end{bmatrix} 04815926103711 121620131721141822151923

1.3 张量的运算

给定任意两个具有相同形状的张量,任何按元素二元运算的结果都将是相同形状的向量

1.3.1 基础运算

加减乘除:下例中,A、B和C三个矩阵的形状相同。

A = torch.arange(20, dtype=torch.float32).reshape(4, 5)
B = A.clone()	# 通过重新分配内存,将A的一个副本分配给B
C = A + B

哈达玛积:两个矩阵的按元素乘法称为哈达玛积(数学符号 ⨀ \bigodot

A = torch.arange(20, dtype=torch.float32).reshape(4, 5)
B = A.clone()
C = A * B	# 哈达玛积

上例和视为如下运算:
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. ] ⨀ [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. ] = [ 0. 1. 4. 9. 16. 25. 36. 49. 64. 81. 100. 121. 144. 169. 196. 225. 256. 289. 324. 361. ] \begin{bmatrix} 0. & 1. & 2. & 3. \\ 4. & 5. & 6. & 7. \\ 8. & 9. & 10. & 11. \\ 12. & 13. & 14. & 15. \\ 16. & 17. & 18. & 19. \end{bmatrix} \bigodot \begin{bmatrix} 0. & 1. & 2. & 3. \\ 4. & 5. & 6. & 7. \\ 8. & 9. & 10. & 11. \\ 12. & 13. & 14. & 15. \\ 16. & 17. & 18. & 19. \end{bmatrix} = \begin{bmatrix} 0. & 1. & 4. & 9. \\ 16. & 25. & 36. & 49. \\ 64. & 81. & 100. & 121. \\ 144. & 169. & 196. & 225. \\ 256. & 289. & 324. & 361. \end{bmatrix} 0.4.8.12.16.1.5.9.13.17.2.6.10.14.18.3.7.11.15.19. 0.4.8.12.16.1.5.9.13.17.2.6.10.14.18.3.7.11.15.19. = 0.16.64.144.256.1.25.81.169.289.4.36.100.196.324.9.49.121.225.361.
与标量的运算

a = 2
X = torch.arange(24).reshape(2, 3, 4)
Y = a + X

1.3.2 按特定轴运算

运算方法保持维度不变
按特定轴求和sum(axis=n)sum(axis=n, keepdims=True)
按特定轴求均值mean(axis=n)mean(axis=n, keepdims=True)
按特定轴累加cumsum(axis=n)cumsum(axis=n, keepdims=True)

按特定轴求和:我们可以使用sum()方法,计算其所有元素的和。也可以通过指定axis参数来对张量的部分维度求和。

A = torch.arange(40, dtype=torch.float32).reshape(2, 5, 4)
A_sum_axis0 = A.sum(axis=0)

上例中,我们创建了一个形状为 2 × 5 × 4 2\times5\times4 2×5×4的三维张量A,并通过sum(axis=0)方法对其第一维度进行求和。
[ [ 0. 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. ] ] ⟶ [ 20. 22. 24. 26. 28. 30. 32. 34. 36. 38. 40. 42. 44. 46. 48. 50. 52. 54. 56. 58. ] \begin{bmatrix} \begin{bmatrix} 0. & 1. & 2. & 3. \\ 4. & 5. & 6. & 7. \\ 8. & 9. & 10. & 11. \\ 12. & 13. & 14. & 15. \\ 16. & 17. & 18. & 19. \end{bmatrix} \begin{bmatrix} 20. & 21. & 22. & 23. \\ 24. & 25. & 26. & 27. \\ 28. & 29. & 30. & 31. \\ 32. & 33. & 34. & 35. \\ 36. & 37. & 38. & 39. \end{bmatrix} \end{bmatrix} \longrightarrow \begin{bmatrix} 20. & 22. & 24. & 26. \\ 28. & 30. & 32. & 34. \\ 36. & 38. & 40. & 42. \\ 44. & 46. & 48. & 50. \\ 52. & 54. & 56. & 58. \end{bmatrix} 0.4.8.12.16.1.5.9.13.17.2.6.10.14.18.3.7.11.15.19. 20.24.28.32.36.21.25.29.33.37.22.26.30.34.38.23.27.31.35.39. 20.28.36.44.52.22.30.38.46.54.24.32.40.48.56.26.34.42.50.58.
同理,我们也可以按照其他的维度进行按维度求和。
相似地,我们可以按特定轴求均值

A = torch.arange(40, dtype=torch.float32).reshape(2, 5, 4)
A_ave_axis0 = A.mean(axis=0)

按特定轴累加求和:下例是按第1维度累加求和

A.cumsum(axis=1)

[ [ 0. 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. ] ] ⟶ [ [ 0. 1. 2. 3. 4. 6. 8. 10. 12. 15. 18. 21. 24. 28. 32. 36. 40. 45. 50. 55. ] [ 20. 21. 22. 23. 44. 46. 48. 50. 72. 75. 78. 81. 104. 108. 112. 116. 140. 145. 150. 155. ] ] \begin{bmatrix} \begin{bmatrix} 0. & 1. & 2. & 3. \\ 4. & 5. & 6. & 7. \\ 8. & 9. & 10. & 11. \\ 12. & 13. & 14. & 15. \\ 16. & 17. & 18. & 19. \end{bmatrix} \begin{bmatrix} 20. & 21. & 22. & 23. \\ 24. & 25. & 26. & 27. \\ 28. & 29. & 30. & 31. \\ 32. & 33. & 34. & 35. \\ 36. & 37. & 38. & 39. \end{bmatrix} \end{bmatrix} \longrightarrow \begin{bmatrix} \begin{bmatrix} 0. & 1. & 2. & 3. \\ 4. & 6. & 8. & 10. \\ 12. & 15. & 18. & 21. \\ 24. & 28. & 32. & 36. \\ 40. & 45. & 50. & 55. \end{bmatrix} \begin{bmatrix} 20. & 21. & 22. & 23. \\ 44. & 46. & 48. & 50. \\ 72. & 75. & 78. & 81. \\ 104. & 108. & 112. & 116. \\ 140. & 145. & 150. & 155. \end{bmatrix} \end{bmatrix} 0.4.8.12.16.1.5.9.13.17.2.6.10.14.18.3.7.11.15.19. 20.24.28.32.36.21.25.29.33.37.22.26.30.34.38.23.27.31.35.39. 0.4.12.24.40.1.6.15.28.45.2.8.18.32.50.3.10.21.36.55. 20.44.72.104.140.21.46.75.108.145.22.48.78.112.150.23.50.81.116.155.
保持维度不变:我们可以通过指定keepdims参数,在计算总和或均值时保持轴数(维度)不变。这样做的好处是,我们可以保持原张量的维度,便于利用广播机制——因为广播机制只能作用于维度相同的两个张量。

A = torch.arange(40, dtype=torch.float32).reshape(2, 5, 4)
sum_A = A.sum(axis=1, keepdims=True)

上例对矩阵A的第1维度进行按轴求和,并保持轴数不变,这样做实际上是将第1维度的大小设置为1。
[ [ 0. 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. 45. 50. 55. ] [ 140. 145. 150. 155. ] ] ] \begin{bmatrix} \begin{bmatrix} 0. & 1. & 2. & 3. \\ 4. & 5. & 6. & 7. \\ 8. & 9. & 10. & 11. \\ 12. & 13. & 14. & 15. \\ 16. & 17. & 18. & 19. \end{bmatrix} \begin{bmatrix} 20. & 21. & 22. & 23. \\ 24. & 25. & 26. & 27. \\ 28. & 29. & 30. & 31. \\ 32. & 33. & 34. & 35. \\ 36. & 37. & 38. & 39. \end{bmatrix} \end{bmatrix} \longrightarrow \begin{bmatrix} \begin{bmatrix} \begin{bmatrix} 40. & 45. & 50. & 55. \end{bmatrix} \begin{bmatrix} 140. & 145. & 150. & 155. \end{bmatrix} \end{bmatrix} \end{bmatrix} 0.4.8.12.16.1.5.9.13.17.2.6.10.14.18.3.7.11.15.19. 20.24.28.32.36.21.25.29.33.37.22.26.30.34.38.23.27.31.35.39. [[[40.45.50.55.][140.145.150.155.]]]

1.3.3 乘积运算

乘积方法
向量向量点积dot(x, y)
矩阵向量积mv(A, x)
矩阵矩阵乘法mm(A, B)

点积:相同位置的按元素乘积的和。

X = torch.arange(4, dtype=torch.float32)
Y = torch.ones(4, dtype=torch.float32)
Z = torch.dot(X, Y)

上例可视为如下运算:
[ 0. 1. 2. 3. ] ⋅ [ 1. 1. 1. 1. ] = 0 × 1 + 1 × 1 + 2 × 1 + 3 × 1 = 6 \begin{bmatrix} 0. & 1. & 2. & 3. \end{bmatrix} \cdot \begin{bmatrix} 1. & 1. & 1. & 1. \end{bmatrix} = 0\times1+1\times1+2\times1+3\times1 =6 [0.1.2.3.][1.1.1.1.]=0×1+1×1+2×1+3×1=6
此外,我们还可以通过执行按元素乘法,然后进行求和来表示两个向量的点积:torch.sum(X * Y)

矩阵向量积 A x ⃗ A\vec{x} Ax :是一个长度为m的列向量,其 i t h i^{th} ith元素是点积 a ⃗ i τ x \vec{a}_i^\tau x a iτx

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
X = torch.arange(4, dtype=torch.float32)
torch.mv(A, X)

上例可视为如下运算:
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. ] [ 0. 1. 2. 3. ] = [ 14. 38. 62. 86. 110. ] \begin{bmatrix} 0. & 1. & 2. & 3. \\ 4. & 5. & 6. & 7. \\ 8. & 9. & 10. & 11. \\ 12. & 13. & 14. & 15. \\ 16. & 17. & 18. & 19. \end{bmatrix} \begin{bmatrix} 0. \\ 1. \\ 2. \\ 3. \end{bmatrix} = \begin{bmatrix} 14. \\ 38. \\ 62. \\ 86. \\ 110. \end{bmatrix} 0.4.8.12.16.1.5.9.13.17.2.6.10.14.18.3.7.11.15.19. 0.1.2.3. = 14.38.62.86.110.
矩阵矩阵乘法

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = torch.ones(4, 3)
torch.mm(A, B)

上例可看成如下运算:
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. ] [ 1 1 1 1 1 1 1 1 1 1 1 1 ] = [ 6. 6. 6. 22. 22. 22. 38. 38. 38. 54. 54. 54. 70. 70. 70. ] \begin{bmatrix} 0. & 1. & 2. & 3. \\ 4. & 5. & 6. & 7. \\ 8. & 9. & 10. & 11. \\ 12. & 13. & 14. & 15. \\ 16. & 17. & 18. & 19. \end{bmatrix} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix} = \begin{bmatrix} 6. & 6. & 6. \\ 22. & 22. & 22. \\ 38. & 38. & 38. \\ 54. & 54. & 54. \\ 70. & 70. & 70. \end{bmatrix} 0.4.8.12.16.1.5.9.13.17.2.6.10.14.18.3.7.11.15.19. 111111111111 = 6.22.38.54.70.6.22.38.54.70.6.22.38.54.70.

1.3.4 范数

范数方法
L 1 L_1 L1范数abs(向量).sum()
L 2 L_2 L2范数norm(向量)
F范数norm(矩阵)

L 1 L_1 L1范数:它表示为向量元素的绝对值之和:
∣ ∣ x ∣ ∣ 1 = ∑ i = 1 n ∣ x i ∣ ||x||_1=\sum_{i=1}^{n} |x_i| ∣∣x1=i=1nxi

u = torch.tensor([3.0, -4.0])
v = torch.abs(u).sum()

L 2 L_2 L2范数:是向量元素平方和的平方根:
∣ ∣ x ∣ ∣ 2 = ∑ i = 1 n x i 2 ||x||_2=\sqrt{\sum_{i=1}^{n} x_i^2} ∣∣x2=i=1nxi2

u = torch.tensor([3.0, -4.0])
v = torch.norm(u)

F范数:是矩阵元素的平方和的平方根:
∣ ∣ x ∣ ∣ F = ∑ i = 1 m ∑ j = 1 n x i j 2 ||x||_F=\sqrt{\sum_{i=1}^{m} \sum_{j=1}^{n} x_{ij}^2} ∣∣xF=i=1mj=1nxij2

vf = torch.norm(torch.ones((4, 9)))

二、矩阵计算

2.1 标量导数

y y y a a a(常数) x n x^n xn e x e^x ex ln ⁡ x \ln{x} lnx sin ⁡ x \sin{x} sinx
d y d x \frac{dy}{dx} dxdy 0 0 0 n x n − 1 nx^{n-1} nxn1 e x e^x ex 1 x \frac{1}{x} x1 cos ⁡ x \cos{x} cosx
y y y u + v u+v u+v u v uv uv y = f ( u ) , u = g ( x ) y=f(u),u=g(x) y=f(u),u=g(x)
d y d x \frac{dy}{dx} dxdy d u d x + d v d x \frac{du}{dx}+\frac{dv}{dx} dxdu+dxdv d u d x v + d v d x u \frac{du}{dx}v+\frac{dv}{dx}u dxduv+dxdvu d y d u d u d x \frac{dy}{du}\frac{du}{dx} dudydxdu

2.2 向量导数

类别 x x x x ⃗ \vec{x} x
y y y ∂ y ∂ x \frac{\partial y}{\partial x} xy(标量) ∂ y ∂ x ⃗ \frac{\partial y}{\partial \vec{x}} x y(向量)
y ⃗ \vec{y} y ∂ y ⃗ ∂ x \frac{\partial \vec{y}}{\partial x} xy (向量) ∂ y ⃗ ∂ x ⃗ \frac{\partial \vec{y}}{\partial \vec{x}} x y (矩阵)

2.2.1 标量-向量求导

这种情况实际上是标量对向量中的每一个元素分别求偏导,再将结果组合成一个行向量。
x ⃗ = [ x 1 x 2 ⋮ x n ] ∂ y ∂ x ⃗ = [ ∂ y ∂ x 1 ∂ y ∂ x 2 ⋯ ∂ y ∂ x n ] \vec{x}= \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix} \qquad \qquad \frac{\partial y}{\partial \vec{x}}= \begin{bmatrix} \frac{\partial y}{\partial x_1} & \frac{\partial y}{\partial x_2} & \cdots & \frac{\partial y}{\partial x_n} \end{bmatrix} x = x1x2xn x y=[x1yx2yxny]
常见的导数如下所示:

y y y a a a(常数) a u au au s u m ( x ) sum(x) sum(x) ∣ ∣ x ∣ ∣ 2 \mid \mid x\mid \mid ^2 ∣∣x2
∂ y ∂ x ⃗ \frac{\partial y}{\partial \vec{x}} x y 0 ⃗ T \vec{0}^T 0 T a ∂ u ∂ x ⃗ a\frac{\partial u}{\partial \vec{x}} ax u 1 ⃗ T \vec{1}^T 1 T 2 x ⃗ T 2\vec{x}^T 2x T
y y y u + v u+v u+v u v uv uv ⟨ u ⃗ , v ⃗ ⟩ \langle \vec{u}, \vec{v} \rangle u ,v
∂ u ∂ x ⃗ \frac{\partial u}{\partial \vec{x}} x u ∂ u ∂ x ⃗ + ∂ v ∂ x ⃗ \frac{\partial u}{\partial \vec{x}}+\frac{\partial v}{\partial \vec{x}} x u+x v ∂ u ∂ x ⃗ v + ∂ v ∂ x ⃗ u \frac{\partial u}{\partial \vec{x}}v+\frac{\partial v}{\partial \vec{x}}u x uv+x vu u ⃗ T ∂ v ⃗ ∂ x ⃗ + v ⃗ T ∂ u ⃗ ∂ x ⃗ \vec{u}^T\frac{\partial \vec{v}}{\partial \vec{x}}+\vec{v}^T\frac{\partial \vec{u}}{\partial \vec{x}} u Tx v +v Tx u

2.2.2 向量-标量求导

这种情况相当于向量的每一个元素分别求导。
y ⃗ = [ y 1 y 2 ⋮ y m ] ∂ y ⃗ ∂ x = [ ∂ y 1 ∂ x ∂ y 2 ∂ x ⋮ ∂ y m ∂ x ] \vec{y}= \begin{bmatrix} y_1 \\ y_2 \\ \vdots \\ y_m \end{bmatrix} \qquad \qquad \frac{\partial \vec{y}}{\partial x}= \begin{bmatrix} \frac{\partial y_1}{\partial x} \\ \frac{\partial y_2}{\partial x} \\ \vdots \\ \frac{\partial y_m}{\partial x} \end{bmatrix} y = y1y2ym xy = xy1xy2xym
我们发现 ∂ y ∂ x ⃗ \frac{\partial y}{\partial \vec{x}} x y是行向量,而 ∂ y ⃗ ∂ x \frac{\partial \vec{y}}{\partial x} xy 是列向量。这个被称之为分子布局符号,反过来的版本叫分母布局符号。

2.2.3 向量-向量求导

这种情况相当于分别进行前述两种求导。
x = [ x 1 x 2 ⋮ x n ] y = [ y 1 y 2 ⋮ y m ] ∂ y ⃗ ∂ x ⃗ = [ ∂ y 1 ∂ x ⃗ ∂ y 2 ∂ x ⃗ ⋮ ∂ y m ∂ x ⃗ ] = [ ∂ y 1 ∂ x 1 ∂ y 1 ∂ x 2 ⋯ ∂ y 1 ∂ x n ∂ y 2 ∂ x 1 ∂ y 2 ∂ x 2 ⋯ ∂ y 2 ∂ x n ⋮ ⋮ ⋮ ∂ y m ∂ x 1 ∂ y m ∂ x 2 ⋯ ∂ y m ∂ x n ] x= \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix} \qquad y= \begin{bmatrix} y_1 \\ y_2 \\ \vdots \\ y_m \end{bmatrix} \qquad \frac{\partial \vec{y}}{\partial \vec{x}}= \begin{bmatrix} \frac{\partial y_1}{\partial \vec{x}} \\ \frac{\partial y_2}{\partial \vec{x}} \\ \vdots \\ \frac{\partial y_m}{\partial \vec{x}} \end{bmatrix}= \begin{bmatrix} \frac{\partial y_1}{\partial x_1} & \frac{\partial y_1}{\partial x_2} & \cdots & \frac{\partial y_1}{\partial x_n} \\ \frac{\partial y_2}{\partial x_1} & \frac{\partial y_2}{\partial x_2} & \cdots & \frac{\partial y_2}{\partial x_n} \\ \vdots & \vdots & & \vdots \\ \frac{\partial y_m}{\partial x_1} & \frac{\partial y_m}{\partial x_2} & \cdots & \frac{\partial y_m}{\partial x_n} \\ \end{bmatrix} x= x1x2xn y= y1y2ym x y = x y1x y2x ym = x1y1x1y2x1ymx2y1x2y2x2ymxny1xny2xnym
常见的导数如下所示:

y ⃗ \vec{y} y a ⃗ \vec{a} a (常数) x ⃗ \vec{x} x A x ⃗ A\vec{x} Ax x ⃗ T A \vec{x}^TA x TA
∂ y ⃗ ∂ x ⃗ \frac{\partial \vec{y}}{\partial \vec{x}} x y 0 ⃗ \vec{0} 0 I I I(单位矩阵) A A A A T A^T AT
y ⃗ \vec{y} y a u ⃗ a\vec{u} au A u ⃗ A\vec{u} Au u ⃗ + v ⃗ \vec{u}+\vec{v} u +v
∂ y ⃗ ∂ x ⃗ \frac{\partial \vec{y}}{\partial \vec{x}} x y a ∂ u ⃗ ∂ x ⃗ a\frac{\partial \vec{u}}{\partial \vec{x}} ax u A ∂ u ⃗ ∂ x ⃗ A\frac{\partial \vec{u}}{\partial \vec{x}} Ax u ∂ u ⃗ ∂ x ⃗ + ∂ v ⃗ ∂ x ⃗ \frac{\partial \vec{u}}{\partial \vec{x}}+\frac{\partial \vec{v}}{\partial \vec{x}} x u +x v

除本文中提到的求导,张量间的求导还可以进一步往矩阵扩展,甚至向更高维度扩展。

三、自动求导

3.1 链式法则

我们可以通过求导的链式法则,实现对复杂函数导数的求解。

例:
假设 X ∈ R m × n X \in R^{m \times n} XRm×n w ⃗ ∈ R n \vec{w} \in R^n w Rn y ⃗ ∈ R m \vec{y} \in R^m y Rm z = ∣ ∣ X w ⃗ − y ⃗ ∣ ∣ 2 z=||X\vec{w}-\vec{y}||^2 z=∣∣Xw y 2
计算 ∂ z ∂ w ⃗ \frac{\partial z}{\partial \vec{w}} w z


分解 a ⃗ = X w ⃗ \vec{a}=X\vec{w} a =Xw b ⃗ = a ⃗ − y ⃗ \vec{b}=\vec{a}-\vec{y} b =a y z = ∣ ∣ b ⃗ ∣ ∣ 2 z=||\vec{b}||^2 z=∣∣b 2
求导
∂ z ∂ w ⃗ = ∂ z ∂ b ⃗ ∂ b ⃗ ∂ a ⃗ ∂ a ⃗ ∂ w ⃗ = ∂ ∣ ∣ b ⃗ ∣ ∣ 2 ∂ b ⃗ ∂ a ⃗ − y ⃗ ∂ a ⃗ ∂ X w ⃗ ∂ w ⃗ = 2 b ⃗ T × I × X = 2 ( X w ⃗ − y ⃗ ) T X \frac{\partial z}{\partial \vec{w}}= \frac{\partial z}{\partial \vec{b}}\frac{\partial \vec{b}}{\partial \vec{a}}\frac{\partial \vec{a}}{\partial \vec{w}}=\frac{\partial ||\vec{b}||^2}{\partial \vec{b}}\frac{\partial \vec{a}-\vec{y}}{\partial \vec{a}}\frac{\partial X \vec{w}}{\partial \vec{w}}=2\vec{b}^T \times I \times X=2(X \vec{w}-\vec{y})^TX w z=b za b w a =b ∣∣b 2a a y w Xw =2b T×I×X=2(Xw y )TX

3.2 自动求导

自动求导:计算一个函数在指定值上的导数。
它有别于

  • 符号求导(显式计算)
    I n [ 1 ] : = D [ 4 x 3 + x 2 + 3 , x ] O u t [ 1 ] : = 2 x + 12 x 2 In[1]: = D[4x^3+x^2+3, x] \\ Out[1]: = 2x+12x^2 In[1]:=D[4x3+x2+3,x]Out[1]:=2x+12x2
  • 数学求导
    ∂ f ( x ) ∂ x = lim ⁡ h → 0 f ( x + h ) − f ( x ) h \frac{\partial f(x)}{\partial x}=\lim \limits_{h \rightarrow 0} \frac{f(x+h)-f(x)}{h} xf(x)=h0limhf(x+h)f(x)

3.3 计算图

  • 将代码分解成操作子
  • 将计算表示成无环图
  • 对于一个计算图,我们可以显式构造(Tensorflow/Theano/MXNet)也可以隐式构造(PyTorch/MXNet)。
    计算图

3.4 两种模式

3.4.1 正向累积与反向累积

  • 链式法则: ∂ y ∂ x = ∂ y ∂ u n ∂ u n ∂ u n − 1 ⋯ ∂ u 2 ∂ u 1 ∂ u 1 ∂ x \frac{\partial y}{\partial x}=\frac{\partial y}{\partial u_n}\frac{\partial u_n}{\partial u_{n-1}} \cdots \frac{\partial u_2}{\partial u_1}\frac{\partial u_1}{\partial x} xy=unyun1unu1u2xu1
  • 正向累积: ∂ y ∂ x = ∂ y ∂ u n ( ∂ u n ∂ u n − 1 ( ⋯ ( ∂ u 2 ∂ u 1 ∂ u 1 ∂ x ) ) ) \frac{\partial y}{\partial x}=\frac{\partial y}{\partial u_n}(\frac{\partial u_n}{\partial u_{n-1}}( \cdots (\frac{\partial u_2}{\partial u_1}\frac{\partial u_1}{\partial x}))) xy=uny(un1un((u1u2xu1)))
  • 反向累积(反向传递): ∂ y ∂ x = ( ( ( ∂ y ∂ u n ∂ u n ∂ u n − 1 ) ⋯   ) ∂ u 2 ∂ u 1 ) ∂ u 1 ∂ x \frac{\partial y}{\partial x}=(((\frac{\partial y}{\partial u_n}\frac{\partial u_n}{\partial u_{n-1}}) \cdots )\frac{\partial u_2}{\partial u_1})\frac{\partial u_1}{\partial x} xy=(((unyun1un))u1u2)xu1

3.4.2 反向累积步骤

首先,构造计算图。
前向:执行图,存储中间结果。
反向:从相反方向执行图,并去除不需要的枝。
反向累积

3.4.3 复杂度

  • 正向累积
    • 计算复杂度:O(n),用来计算一个变量的梯度。
    • 内存复杂度:O(1)
  • 反向累积
    • 计算复杂度:O(n),n是操作子个数。
    • 内存复杂度:O(n),因为需要存储正向的所有中间结果。

四、自动求导实现

4.1 常用函数

函数功能
x.requires_grade_(True)表示需要存储梯度
y.backward()自动计算梯度
y.sum().backward()先求和再自动计算梯度
x.grad查看梯度
x.grad.zero_()梯度清零
y.detach()将与x相关的函数y转变为与x无关的常数

4.2 简易流程

在我们计算y关于x的梯度之前,我们需要一个地方来存储梯度

x = torch.arange(4.0)
x.requires_grad_(True)	# 表示需要存储梯度

我们也可以通过requires_grad参数来表示需要存储梯度。上下两段代码是等价的,但是要主要上面的代码中requires_grad_()方法最后还有一个_

x = torch.arange(4.0, requires_grad=True)

现在让我们计算y。

y = torch.dot(x, x) * 2

通过调用反向传播函数来自动计算y关于x每个分量的梯度。

y.backward()	# 自动求梯度
print(x.grad)	# 查看梯度

接下来,我们来计算另一个函数中y关于x每个分量的梯度。
在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
我们这里使用的是zero_()方法,_的意识是“把xx写进xx”,因此x.grad.zero_()的含义为:把零写进x的梯度。

x.grad.zero_()  # _表示将xx写进xx,即将zero写进x.grad
y = x.sum()		# 另一个函数
y.backward()
print(x.grad)

4.3 进一步

深度学习中,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和。

x.grad.zero_()
y = x * x

# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()	# 先求和再自动求梯度
print(x.grad)

我们还可以将一些计算移动到记录的计算图之外。
其中,我们用detach()方法将与x相关的函数转变为与x无关的常数,这个技巧可以用来固定网络中的参数。

x.grad.zero_()
y = x * x
u = y.detach()		# 将u转变为与x无关的常数
z = u * x
z.sum().backward()	# 先求和,再求梯度
print(x.grad, x.grad==u)

即使构建函数的计算图需要通过Python控制流(例如,条件、循环或者任意函数调用),我们仍然可以计算得到的变量的梯度。

def f(a):
    b = a * 2
    while b.norm() < 1000:
        b = b * 2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c

# size = () 意为:a为标量
a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()
print(a.grad, a.grad==d/a)

下一篇:【动手学深度学习v2】学习笔记03:线性回归、基础优化算法、线性回归实现

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼儿听雨眠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值