Run this notebook online: or Colab:

# 2.3. 线性代数¶

## 2.3.1. 标量¶

%load ../utils/djl-imports

NDManager manager = NDManager.newBaseManager();
NDArray x = manager.create(3f);
NDArray y = manager.create(2f);

x.add(y)

ND: () gpu(0) float32
5.

x.mul(y)

ND: () gpu(0) float32
6.

x.div(y)

ND: () gpu(0) float32
1.5

x.pow(y)

ND: () gpu(0) float32
9.


## 2.3.2. 向量¶

NDArray x = manager.arange(4f);
x

ND: (4) gpu(0) float32
[0., 1., 2., 3.]


(2.3.1)$\begin{split}\mathbf{x} =\begin{bmatrix}x_{1} \\x_{2} \\ \vdots \\x_{n}\end{bmatrix},\end{split}$

x.get(3)

ND: () gpu(0) float32
3.


### 2.3.2.1. 长度、维度和形状¶

x.size(0)

4


x.getShape()

(4)


## 2.3.3. 矩阵¶

(2.3.2)$\begin{split}\mathbf{A}=\begin{bmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & \cdots & a_{mn} \\ \end{bmatrix}.\end{split}$

NDArray A = manager.arange(20f).reshape(5,4);
A

ND: (5, 4) gpu(0) float32
[[ 0.,  1.,  2.,  3.],
[ 4.,  5.,  6.,  7.],
[ 8.,  9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.],
]


(2.3.3)$\begin{split}\mathbf{A}^\top = \begin{bmatrix} a_{11} & a_{21} & \dots & a_{m1} \\ a_{12} & a_{22} & \dots & a_{m2} \\ \vdots & \vdots & \ddots & \vdots \\ a_{1n} & a_{2n} & \dots & a_{mn} \end{bmatrix}.\end{split}$

A.transpose()

ND: (4, 5) gpu(0) float32
[[ 0.,  4.,  8., 12., 16.],
[ 1.,  5.,  9., 13., 17.],
[ 2.,  6., 10., 14., 18.],
[ 3.,  7., 11., 15., 19.],
]


NDArray B = manager.create(new float[][] {{1, 2, 3}, {2, 0, 4}, {3, 4, 5}});
B

ND: (3, 3) gpu(0) float32
[[1., 2., 3.],
[2., 0., 4.],
[3., 4., 5.],
]


B.eq(B.transpose())

ND: (3, 3) gpu(0) boolean
[[ true,  true,  true],
[ true,  true,  true],
[ true,  true,  true],
]


## 2.3.4. NDArray¶

NDArray X = manager.arange(24f).reshape(2, 3, 4);
X

ND: (2, 3, 4) gpu(0) float32
[[[ 0.,  1.,  2.,  3.],
[ 4.,  5.,  6.,  7.],
[ 8.,  9., 10., 11.],
],
[[12., 13., 14., 15.],
[16., 17., 18., 19.],
[20., 21., 22., 23.],
],
]


## 2.3.5. NDArray算法的基本性质¶

NDArray A = manager.arange(20f).reshape(5,4);
NDArray B = A.duplicate(); // 通过分配新内存，将A的一个副本分配给B
A

ND: (5, 4) gpu(0) float32
[[ 0.,  1.,  2.,  3.],
[ 4.,  5.,  6.,  7.],
[ 8.,  9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.],
]

A.add(B)

ND: (5, 4) gpu(0) float32
[[ 0.,  2.,  4.,  6.],
[ 8., 10., 12., 14.],
[16., 18., 20., 22.],
[24., 26., 28., 30.],
[32., 34., 36., 38.],
]


(2.3.4)$\begin{split}\mathbf{A} \odot \mathbf{B} = \begin{bmatrix} a_{11} b_{11} & a_{12} b_{12} & \dots & a_{1n} b_{1n} \\ a_{21} b_{21} & a_{22} b_{22} & \dots & a_{2n} b_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} b_{m1} & a_{m2} b_{m2} & \dots & a_{mn} b_{mn} \end{bmatrix}.\end{split}$
A.mul(B)

ND: (5, 4) gpu(0) float32
[[  0.,   1.,   4.,   9.],
[ 16.,  25.,  36.,  49.],
[ 64.,  81., 100., 121.],
[144., 169., 196., 225.],
[256., 289., 324., 361.],
]


int a = 2;
NDArray X = manager.arange(24f).reshape(2, 3, 4);

ND: (2, 3, 4) gpu(0) float32
[[[ 2.,  3.,  4.,  5.],
[ 6.,  7.,  8.,  9.],
[10., 11., 12., 13.],
],
[[14., 15., 16., 17.],
[18., 19., 20., 21.],
[22., 23., 24., 25.],
],
]

(X.mul(a)).getShape()

(2, 3, 4)


## 2.3.6. 降维¶

NDArray x = manager.arange(4f);
x

ND: (4) gpu(0) float32
[0., 1., 2., 3.]

x.sum()

ND: () gpu(0) float32
6.


A.getShape()

(5, 4)

A.sum()

ND: () gpu(0) float32
190.


NDArray ASumAxis0 = A.sum(new int[] {0});
ASumAxis0

ND: (4) gpu(0) float32
[40., 45., 50., 55.]

ASumAxis0.getShape()

(4)


NDArray ASumAxis1 = A.sum(new int[] {1});
ASumAxis1

ND: (5) gpu(0) float32
[ 6., 22., 38., 54., 70.]

ASumAxis1.getShape()

(5)


A.sum(new int[] {0,1}) // Same as A.sum()

ND: () gpu(0) float32
190.


A.mean()

ND: () gpu(0) float32
9.5

A.sum().div(A.size())

ND: () gpu(0) float32
9.5


A.mean(new int[] {0})

ND: (4) gpu(0) float32
[ 8.,  9., 10., 11.]

A.sum(new int[] {0}).div(A.getShape().get(0))

ND: (4) gpu(0) float32
[ 8.,  9., 10., 11.]


### 2.3.6.1. 非降维求和¶

NDArray sumA = A.sum(new int[] {1}, true);
sumA

ND: (5, 1) gpu(0) float32
[[ 6.],
[22.],
[38.],
[54.],
[70.],
]


A.div(sumA)

ND: (5, 4) gpu(0) float32
[[0.    , 0.1667, 0.3333, 0.5   ],
[0.1818, 0.2273, 0.2727, 0.3182],
[0.2105, 0.2368, 0.2632, 0.2895],
[0.2222, 0.2407, 0.2593, 0.2778],
[0.2286, 0.2429, 0.2571, 0.2714],
]


A.cumSum(0)

ND: (5, 4) gpu(0) float32
[[ 0.,  1.,  2.,  3.],
[ 4.,  6.,  8., 10.],
[12., 15., 18., 21.],
[24., 28., 32., 36.],
[40., 45., 50., 55.],
]


## 2.3.7. 点积（Dot Product）¶

NDArray y = manager.ones(new Shape(4));
x

ND: (4) gpu(0) float32
[0., 1., 2., 3.]

y

ND: (4) gpu(0) float32
[1., 1., 1., 1.]

x.dot(y)

ND: () gpu(0) float32
6.


x.mul(y).sum()

ND: () gpu(0) float32
6.


## 2.3.8. 矩阵-向量积¶

(2.3.5)$\begin{split}\mathbf{A}= \begin{bmatrix} \mathbf{a}^\top_{1} \\ \mathbf{a}^\top_{2} \\ \vdots \\ \mathbf{a}^\top_m \\ \end{bmatrix},\end{split}$

(2.3.6)$\begin{split}\mathbf{A}\mathbf{x} = \begin{bmatrix} \mathbf{a}^\top_{1} \\ \mathbf{a}^\top_{2} \\ \vdots \\ \mathbf{a}^\top_m \\ \end{bmatrix}\mathbf{x} = \begin{bmatrix} \mathbf{a}^\top_{1} \mathbf{x} \\ \mathbf{a}^\top_{2} \mathbf{x} \\ \vdots\\ \mathbf{a}^\top_{m} \mathbf{x}\\ \end{bmatrix}.\end{split}$

A.getShape()

(5, 4)

x.getShape()

(4)

A.dot(x)

ND: (5) gpu(0) float32
[ 14.,  38.,  62.,  86., 110.]


## 2.3.9. 矩阵-矩阵乘法¶

(2.3.7)$\begin{split}\mathbf{A}=\begin{bmatrix} a_{11} & a_{12} & \cdots & a_{1k} \\ a_{21} & a_{22} & \cdots & a_{2k} \\ \vdots & \vdots & \ddots & \vdots \\ a_{n1} & a_{n2} & \cdots & a_{nk} \\ \end{bmatrix},\quad \mathbf{B}=\begin{bmatrix} b_{11} & b_{12} & \cdots & b_{1m} \\ b_{21} & b_{22} & \cdots & b_{2m} \\ \vdots & \vdots & \ddots & \vdots \\ b_{k1} & b_{k2} & \cdots & b_{km} \\ \end{bmatrix}.\end{split}$

(2.3.8)$\begin{split}\mathbf{A}= \begin{bmatrix} \mathbf{a}^\top_{1} \\ \mathbf{a}^\top_{2} \\ \vdots \\ \mathbf{a}^\top_n \\ \end{bmatrix}, \quad \mathbf{B}=\begin{bmatrix} \mathbf{b}_{1} & \mathbf{b}_{2} & \cdots & \mathbf{b}_{m} \\ \end{bmatrix}.\end{split}$

(2.3.9)$\begin{split}\mathbf{C} = \mathbf{AB} = \begin{bmatrix} \mathbf{a}^\top_{1} \\ \mathbf{a}^\top_{2} \\ \vdots \\ \mathbf{a}^\top_n \\ \end{bmatrix} \begin{bmatrix} \mathbf{b}_{1} & \mathbf{b}_{2} & \cdots & \mathbf{b}_{m} \\ \end{bmatrix} = \begin{bmatrix} \mathbf{a}^\top_{1} \mathbf{b}_1 & \mathbf{a}^\top_{1}\mathbf{b}_2& \cdots & \mathbf{a}^\top_{1} \mathbf{b}_m \\ \mathbf{a}^\top_{2}\mathbf{b}_1 & \mathbf{a}^\top_{2} \mathbf{b}_2 & \cdots & \mathbf{a}^\top_{2} \mathbf{b}_m \\ \vdots & \vdots & \ddots &\vdots\\ \mathbf{a}^\top_{n} \mathbf{b}_1 & \mathbf{a}^\top_{n}\mathbf{b}_2& \cdots& \mathbf{a}^\top_{n} \mathbf{b}_m \end{bmatrix}.\end{split}$

NDArray B = manager.ones(new Shape(4,3));
A.dot(B)

ND: (5, 3) gpu(0) float32
[[ 6.,  6.,  6.],
[22., 22., 22.],
[38., 38., 38.],
[54., 54., 54.],
[70., 70., 70.],
]


## 2.3.10. 范数¶

(2.3.10)$f(\alpha \mathbf{x}) = |\alpha| f(\mathbf{x}).$

(2.3.11)$f(\mathbf{x} + \mathbf{y}) \leq f(\mathbf{x}) + f(\mathbf{y}).$

(2.3.12)$f(\mathbf{x}) \geq 0.$

(2.3.13)$\forall i, [\mathbf{x}]_i = 0 \Leftrightarrow f(\mathbf{x})=0.$

(**

(2.3.14)$\|\mathbf{x}\|_2 = \sqrt{\sum_{i=1}^n x_i^2},$

**)

public NDArray l2Norm(NDArray w){
return ((w.pow(2)).sum()).sqrt();
}

NDArray u = manager.create(new float[] {3,-4});
l2Norm(u)

ND: () gpu(0) float32
5.


(**

(2.3.15)$\|\mathbf{x}\|_1 = \sum_{i=1}^n \left|x_i \right|.$

**)

$$L_2$$ 范数相比，$$L_1$$ 范数受异常值的影响较小。为了计算 $$L_1$$ 范数，我们将绝对值函数和按元素求和组合起来。

u.abs().sum()

ND: () gpu(0) float32
7.


$$L_2$$ 范数和 $$L_1$$ 范数都是更一般的$$L_p$$范数的特例：

(2.3.16)$\|\mathbf{x}\|_p = \left(\sum_{i=1}^n \left|x_i \right|^p \right)^{1/p}.$

(**

(2.3.17)$\|\mathbf{X}\|_F = \sqrt{\sum_{i=1}^m \sum_{j=1}^n x_{ij}^2}.$

**)

l2Norm(manager.ones(new Shape(4,9)))

ND: () gpu(0) float32
6.


## 2.3.12. 小结¶

• 标量、向量、矩阵和 NDArray 是线性代数中的基本数学对象。

• 向量泛化自标量，矩阵泛化自向量。

• 标量、向量、矩阵和 NDArray 分别具有零、一、二和任意数量的轴。

• 一个 NDArray 可以通过summean沿指定的轴降低维度。

• 两个矩阵的按元素乘法被称为他们的哈达玛积。它与矩阵乘法不同。

• 在深度学习中，我们经常使用范数，如 $$L_1$$范数、$$L_2$$范数和弗罗贝尼乌斯范数。

• 我们可以对标量、向量、矩阵和 NDArray 执行各种操作。

## 2.3.13. 练习¶

1. 证明一个矩阵 $$\mathbf{A}$$ 的转置的转置是 $$\mathbf{A}$$$$(\mathbf{A}^\top)^\top = \mathbf{A}$$

2. 给出两个矩阵 $$\mathbf{A}$$$$\mathbf{B}$$, 显示转置的和等于和的转置：$$\mathbf{A}^\top + \mathbf{B}^\top = (\mathbf{A} + \mathbf{B})^\top$$.

3. 给定任意方矩阵$$\mathbf{A}$$$$\mathbf{A} + \mathbf{A}^\top$$总是对称的吗?为什么?

4. 我们在本节中定义了形状（2, 3, 4）的 NDArray XX.size()的输出结果是什么？

5. 对于任意形状的 NDArray X, X.size()是否总是对应于X特定轴的长度?这个轴是什么?

6. 运行 A / A.sum(new int[] {1})，看看会发生什么。你能分析原因吗？

7. 当你在曼哈顿的两点之间旅行时，你需要在坐标上走多远，也就是说，就大街和街道而言？你能斜着走吗？

8. 考虑一个具有形状（2, 3, 4）的 NDArray ，在轴 0,1,2 上的求和输出是什么形状?

9. l2Norm() 函数提供 3 个或更多轴的 NDArray ，并观察其输出。对于任意形状的 NDArray 这个函数计算得到什么?