前文回顾:数据操作、数据预处理
在pytorch中,我们使用一个元素的张量来表示标量。
我们可以将向量视为标量值组成的列表。
x = torch.tensor([3.0]) # 标量
y = torch.tensor([2.0, 1.0, 4.0]) # 向量
我们可以通过制定两个分量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⎦ ⎤⎦ ⎤
给定任意两个具有相同形状的张量,任何按元素二元运算的结果都将是相同形状的向量。
加减乘除:下例中,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
运算 | 方法 | 保持维度不变 |
---|---|---|
按特定轴求和 | 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.]]]
乘积 | 方法 |
---|---|
向量向量点积 | 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 aiτ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.⎦ ⎤
范数 | 方法 |
---|---|
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| ∣∣x∣∣1=i=1∑n∣xi∣
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} ∣∣x∣∣2=i=1∑nxi2
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} ∣∣x∣∣F=i=1∑mj=1∑nxij2
vf = torch.norm(torch.ones((4, 9)))
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} nxn−1 | 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 |
类别 | x x x | x ⃗ \vec{x} x |
---|---|---|
y y y | ∂ y ∂ x \frac{\partial y}{\partial x} ∂x∂y(标量) | ∂ y ∂ x ⃗ \frac{\partial y}{\partial \vec{x}} ∂x∂y(向量) |
y ⃗ \vec{y} y | ∂ y ⃗ ∂ x \frac{\partial \vec{y}}{\partial x} ∂x∂y(向量) | ∂ y ⃗ ∂ x ⃗ \frac{\partial \vec{y}}{\partial \vec{x}} ∂x∂y(矩阵) |
这种情况实际上是标量对向量中的每一个元素分别求偏导,再将结果组合成一个行向量。
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=⎣ ⎡x1x2⋮xn⎦ ⎤∂x∂y=[∂x1∂y∂x2∂y⋯∂xn∂y]
常见的导数如下所示:
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 ∣∣x∣∣2 |
---|---|---|---|---|
∂ y ∂ x ⃗ \frac{\partial y}{\partial \vec{x}} ∂x∂y | 0 ⃗ T \vec{0}^T 0T | a ∂ u ∂ x ⃗ a\frac{\partial u}{\partial \vec{x}} a∂x∂u | 1 ⃗ T \vec{1}^T 1T | 2 x ⃗ T 2\vec{x}^T 2xT |
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}} uT∂x∂v+vT∂x∂u |
这种情况相当于向量的每一个元素分别求导。
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=⎣ ⎡y1y2⋮ym⎦ ⎤∂x∂y=⎣ ⎡∂x∂y1∂x∂y2⋮∂x∂ym⎦ ⎤
我们发现 ∂ y ∂ x ⃗ \frac{\partial y}{\partial \vec{x}} ∂x∂y是行向量,而 ∂ y ⃗ ∂ x \frac{\partial \vec{y}}{\partial x} ∂x∂y是列向量。这个被称之为分子布局符号,反过来的版本叫分母布局符号。
这种情况相当于分别进行前述两种求导。
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=⎣ ⎡x1x2⋮xn⎦ ⎤y=⎣ ⎡y1y2⋮ym⎦ ⎤∂x∂y=⎣ ⎡∂x∂y1∂x∂y2⋮∂x∂ym⎦ ⎤=⎣ ⎡∂x1∂y1∂x1∂y2⋮∂x1∂ym∂x2∂y1∂x2∂y2⋮∂x2∂ym⋯⋯⋯∂xn∂y1∂xn∂y2⋮∂xn∂ym⎦ ⎤
常见的导数如下所示:
y ⃗ \vec{y} y | a ⃗ \vec{a} a(常数) | x ⃗ \vec{x} x | A x ⃗ A\vec{x} Ax | x ⃗ T A \vec{x}^TA xTA |
---|---|---|---|---|
∂ 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}} a∂x∂u | A ∂ u ⃗ ∂ x ⃗ A\frac{\partial \vec{u}}{\partial \vec{x}} A∂x∂u | ∂ u ⃗ ∂ x ⃗ + ∂ v ⃗ ∂ x ⃗ \frac{\partial \vec{u}}{\partial \vec{x}}+\frac{\partial \vec{v}}{\partial \vec{x}} ∂x∂u+∂x∂v |
除本文中提到的求导,张量间的求导还可以进一步往矩阵扩展,甚至向更高维度扩展。
我们可以通过求导的链式法则,实现对复杂函数导数的求解。
例:
假设 X ∈ R m × n X \in R^{m \times n} X∈Rm×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∂z∂a∂b∂w∂a=∂b∂∣∣b∣∣2∂a∂a−y∂w∂Xw=2bT×I×X=2(Xw−y)TX
自动求导:计算一个函数在指定值上的导数。
它有别于
首先,构造计算图。
前向:执行图,存储中间结果。
反向:从相反方向执行图,并去除不需要的枝。
函数 | 功能 |
---|---|
x.requires_grade_(True) |
表示需要存储梯度 |
y.backward() |
自动计算梯度 |
y.sum().backward() |
先求和再自动计算梯度 |
x.grad |
查看梯度 |
x.grad.zero_() |
梯度清零 |
y.detach() |
将与x相关的函数y转变为与x无关的常数 |
在我们计算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)
深度学习中,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和。
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)