Tech Blog

本系列用彩色的 Latex 公式呈现总结 MIT Gilbert Strang 教授经典的线性代数课程18.06之精华,便于大家回顾复习。

Gauss-Jordan 算法是一种手工计算矩阵逆的技术。现在,你几乎不应该手动计算矩阵逆,即使是在计算机上,但 Gauss-Jordan 仍然有用,因为

  • 它有助于我们理解逆矩阵何时以及为何存在。

  • 它给了我们另一个例子来帮助我们理解消元的结构

回顾:矩阵的逆

线性算子的 \(A^{-1}\) 是“撤消”操作的操作 \(A\) ,对于任何 \(x\),有:

\[\boxed{Ax=b \implies x = A^{-1} b}\]

这意味着

  • 对于m×m 方阵 \(A\)\(A^{-1}\) 存在仅当 \(A\) 具有 m 个主元的

因为对于非方阵或具有一个或多个“零主元”的矩阵,我们不能总是解决 \(Ax=b\) (会在反向替换期间除以零)。也很容易看出 \(\boxed{(A^{-1})^{-1} = A}\) , 即 \(A\) 撤消操作 \(A^{-1}\) .

等价地

这里, \(I\) 是 m×m 单位矩阵——在线性代数中,我们通常从上下文来推断 \(I\) 的形状,但如果它是模棱两可时,可以写成 \(I_m\).

乘积的逆: (AB)⁻¹ = B⁻¹A⁻¹

很容易看出乘积 \(AB\) 的逆是两者逆的反向乘积:

\[\boxed{(AB)^{-1} = B^{-1} A^{-1}}\]

直观理解为,当反转一系列操作时,总是需要以倒序的顺序回溯操作。

容易证明这个结论,因为

例如,我们看到高斯消元对应于因式分解 \(A = LU\), 此时,\(U\) 是消除的结果,并且 \(L\) 只是消除步骤的记录。

然后

注意:逆允许我们“除以矩阵”,但我们总是要清楚我们是在左边还是右边。以下符号很方便,可用于 Julia 和 Matlab 中

通过线性方程求逆

方程 \(A A^{-1} = I\) 实际上给了我们计算的算法 \(A^{-1}\) .

假设我们将 \(A\) 以及 \(I\) 表示成列的形式,即

那么

这里的关键事实是,将 \(A\) 乘以右侧的矩阵相当于将 \(A\) 乘以该矩阵的每一列,通过写出计算可以很容易地看到这一点。

如此,有了 m 个方程组,第 k 个方程组对应 \(A x_k = e_k\),解得这个方程组就可以得到 \(A^{-1}\) 的第 k 列 。也就是说,要找到 m×m 矩阵 \(A\)\(A^{-1}\),我们必须求解 m 个 \(Ax=b\)

  • 换句话说,对于任何矩阵 \(B\) , \(Be_k\)\(B\) 的第 k 列。 所以 \(A^{-1}\) 第 k 列是 \(x_k = A^{-1} e_k\) ,即 \(Ax_k = e_k\) 的解

  • 理想情况下,当我们进行一次高斯消元 \(A=LU\) 后,就可以计算出 \(x_k = U^{-1} L^{-1} e_k\) ,做法是通过在 \(I\) 的每一列上做向前和向后替换(这本质上是计算机的做法)。

举例计算 L⁻¹ = E

例如,我们如何计算我们在上一课中从高斯消元中得到的 \(L\) 矩阵的逆矩阵,我们知道,结果应该是 \(L^{-1} = E\)

对于每个 \(e_1,e_2,e_3\),我们解对应方程组

\(e_k\) 是 3×3 \(I\) 的第 k 列。

例如对于 \(e_1\), 找到第一列 \(x_1\)\(L^{-1} = E\):

通过前向替换(从上到下),我们解得

The Gauss–Jordan 算法

Gauss-Jordan 可以被视为求解的一种技巧(主要用于手工计算)\(A b_k = e_k\). 但是用代数的方式思考也很好——这是我们高斯消元的“矩阵观点”的一个很好的应用。

简而言之,Gauss-Jordan 的想法是:如果我们对 \(A\) 执行一些行操作以获得 \(I\),那么对 \(I\) 执行相同的行操作会得到 \(A^{-1}\)。为什么?

  • 行操作对应于从 \(A\) 左边乘以一组矩阵 \(E=\cdots E_2 E_1\)

  • 所以,对 \(A\) 做行操作将其变成 \(I\) 意思等价于 \(EA = I\), 因此 \(E = A^{-1}\) .

  • \(I\) 执行相同的行操作,相当于 \(I\) 左乘矩阵 \(E\) , 即 \(EI\),因为 \(EI = E\) 并且 \(E = A^{-1}\),所以结果就是 \(A^{-1}\)

这就是我们可以用扩展矩阵来进行高斯消除,对 \(A\)\(I\) 同时执行相同的行操作,即

\(A \to I\) 消除步骤

下面是具体将 \(A\) 通过行操作变换成 \(I\) 的步骤

  1. 做普通高斯消元“向下”将 \(A\) 转变成 \(U\) (上三角矩阵)。
  2. 然后,做高斯消去“向上” \(U\) 消除对角线以上的元素,将 \(U\) 转换成对角矩阵 \(D\)
  3. 最后,将 \(D\) 的每一行除以对角线元素值,得到 \(I\)

Gauss-Jordan 示例

让我们执行这些 \(A \to I\) 消除步骤 \(3 \times 3\) 矩阵 \(A\) : 先向下消除获得 \(U\) , 然后向上消除得到 \(D\) ,最后除以对角线元素值得到 \(I\):

没问题!很容易看出,只要 \(A\) 具有所有主元(即它是非奇异的),这将起作用。

永远不要计算矩阵逆!

矩阵逆很有趣,它在分析操作中非常方便,因为它们允许您轻松地将矩阵从方程的一侧移动到另一侧。但是,在“严肃的”数值计算中几乎从不计算逆矩阵。每当你看到 \(A^{-1} B\) 或者 \(A^{-1} b\),当您在计算机上实现它时,您应该阅读 \(A^{-1} B\) 作为“解决 \(AX = B\) 通过某种方法。”例如,通过 \(A \backslash B\) 或通过首先计算 \(LU\) 分解来解决它 \(A\) 然后用它来解决 \(AX = B\) .

你通常不计算逆矩阵的一个原因是它很浪费:一旦你有 \(A=LU\)(稍后我们将把它概括为“\(PA = LU\)"),你可以解决 \(AX=B\) 直接不用找 \(A^{-1}\), 和计算 \(A^{-1}\) 如果您只需要解决几个右手边,则需要更多的工作。

另一个原因是,对于很多特殊的矩阵,有办法解决 \(AX=B\) 比你能找到的要快得多 \(A^{-1}\). 例如,实践中的许多大矩阵是稀疏的(大部分为零),通常对于稀疏矩阵,您可以安排 \(L\)\(U\) 也要稀疏。稀疏矩阵比一般的“密集”矩阵更有效,因为您不必乘以(甚至存储)零。即使 \(A\) 是稀疏的,然而, \(A^{-1}\) 通常是非稀疏的,因此如果计算逆矩阵,则会失去稀疏的特殊效率。

例如:

  • 如果你看到 \(U^{-1} b\) 在哪里 \(U\) 是上三角,不用计算 \(U^{-1}\) 明确!只要解决 \(Ux = b\) 通过反向替换(从底行向上)。

  • 如果你看到 \(L^{-1} b\) 在哪里 \(L\) 是下三角,不用计算 \(L^{-1}\) 明确!只要解决 \(Lx = b\) 通过前向替换(从顶行向下)。

PaddleOCR CPU 镜像

本期和大家分享一个一键就可以开箱集用的OCR docker 本地服务。

众所周知 PaddleOCR 是一个百度 PaddlePaddle 中非常有名的OCR框架,它包含了丰富的中英文模型。官方也提供给了 GPU 的 docker 镜像,但是基于 GPU 的镜像,对于很多小伙伴来说有点大材小用了。因为很多小伙伴不需要去训练自己的数据集和调参,只要用成熟的模型就足够了。另外,无论在 Windows 还是 Linux 中,大家配置安装 nvidia GPU 镜像都是比较麻烦。因此,这次特地为大家打造了这样一个 CPU 的 PaddleOCR 镜像,并且把常规的模型都预装到镜像中了,大家一键就能部署强悍的本地 OCR 服务。只需要安装 Docker 即可,也支持 Mac。

文字效果

先来看一个文字识别的效果吧,图片中这家店铺使用了非常规的中文字体,但PaddleOCR依然完全识别了出来。

获取镜像

具体获取方式为:关注 MyEncyclopedia 公众号,回复 docker-paddleocr

下面的命令一键起了这个镜像,注意,大家要将 me-paddleocr 替换成公众号中的镜像名

1
docker run -it me-paddleocr bash 

进入容器后,我们已经在 /proj目录,通过 ls 可以发现有如下文件,三个图片demo输入文件,scene.png, engtest.jpg, me.pngdemo_ch.py

命令行识别

通过 PaddleOCR 内置的命令可以最快捷地识别文字,命令格式如下。

1
paddleocr --image_dir scene.png --use_angle_cls true --lang ch --use_gpu false

输出为,最后两行为识别出的两处文字及其位置。

1
2
3
4
5
6
ppocr INFO: **********scene.png**********
ppocr DEBUG: dt_boxes num : 2, elapse : 0.1184682846069336
ppocr DEBUG: cls num : 2, elapse : 0.016251802444458008
ppocr DEBUG: rec_res num : 2, elapse : 0.041384220123291016
ppocr INFO: [[[152.0, 83.0], [402.0, 81.0], [402.0, 135.0], [153.0, 137.0]], ('匠牛饺子', 0.94130754)]
ppocr INFO: [[[412.0, 183.0], [450.0, 183.0], [450.0, 206.0], [412.0, 206.0]], ('匠牛', 0.7368352)]

代码识别

当然了,为了方便查看结果,需要通过代码来实现。下面展示镜像内置的三个demo图片的执行python 代码 demo_ch.py 后的输出效果。

结果展示

图片一:门头照片

图片二:中英文混合的试卷

图片三:中英文混合Logo

demo_ch.py

附上 demo_ch.py 源代码,不做赘述。

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
from paddleocr import PaddleOCR
import numpy as np
import cv2
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont

def cv2ImgAddText(img, text, left, top, textColor=(0, 0, 0), textSize=20):
if (isinstance(img, np.ndarray)):
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
fontStyle = ImageFont.truetype('wqy-zenhei.ttc', textSize, encoding="utf-8")
draw.text((left, top), text, textColor, font=fontStyle)
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

ocr = PaddleOCR()
img_path = '/proj/scene.png'

result = ocr.ocr(img_path, rec=True)
print(f"The predicted text box of {img_path} are follows.")

image = cv2.imread(img_path)
boxes = [[line[0][0], line[0][1], line[0][2], line[0][3]] for line in result]
texts = [line[1][0] for line in result]

for box, text in zip(boxes, texts):
box = np.reshape(np.array(box), [-1, 1, 2]).astype(np.int64)
print(box)
image = cv2.polylines(np.array(image), [box], True, (255, 0, 0), 2)
image = cv2ImgAddText(image, text, box[0][0][0] - 10, box[0][0][1] - 10, textColor=(0, 255, 0), textSize=20)

plt.figure(figsize=(10, 10))
plt.imshow(image)
plt.savefig('/proj/result.jpg')

结束语

PaddleOCR最为市面上最好的开源中文 OCR 引擎之一,其强悍的效果可以达到开箱即用,本期基于 CPU 的预制镜像更降低了大家使用的门槛。本系列后续会和大家分享如何在 PaddleOCR GPU 镜像中去训练自己的数据集,来提升特定字体的准确度,大家喜欢的话不要忘记一键三连哦。同时也可以关注 MyEncyclopedia 微信公众号以及B站频道,下次再见。

本系列用彩色的 Latex 公式呈现总结 MIT Gilbert Strang 教授经典的线性代数课程18.06之精华,便于大家回顾复习。

本文在矩阵乘法和解方程组的基础上引入消元对应的矩阵乘法和矩阵的逆。

消元操作的矩阵表示

回顾上一节,解方程组的意义和过程 中,在消元步骤时,将矩阵 A 依行顺序向下,每一行通过减去上面行的若干倍,将矩阵 A 逐行规范成上三角矩阵形式。

那么每一行规范的操作,例如操作 \(E_{21}\) (第二行减去三倍第一行)是否可以通过矩阵乘法表示出来呢?
结合矩阵乘法的五种理解一节中行行切分,我们将 \(E_{21}\) 视为三个行相加。

如此,第一个矩阵 \([1 \quad 0 \quad 0]\) 乘以 \(A\) 形成了乘积矩阵的第一行,具体来说, \([1 \quad 0 \quad 0]\) 表示取 \(A\) 的1个第一行,0个第二行,0个第三行的和组成乘积矩阵第一行。 同样地,乘积矩阵第二行由第二个矩阵 \([-3 \quad 1 \quad 0]\) 形成,表示取 \(A\) 的-3个第一行,1个第二行,0个第三行之和。

具体转换如下

至此,操作 \(E_{21}\) 可以用矩阵 \(E_{21}\) 右乘以 \(A\) 来表示,即

上述矩阵乘法可以用简单地 Python 代码来核实

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np

A = np.array([
[1., 2., 1.],
[3., 8., 1.],
[0., 4., 1.]
])
e_21 = np.array([
[1., 0., 0.],
[-3., 1., 0.],
[0.,0.,1.]
])

e_21 @ A

运行结果为

1
2
3
array([[ 1.,  2.,  1.],
[ 0., 2., -2.],
[ 0., 4., 1.]])

\(A\) 矩阵消元的第二个操作是 \(E_{32}\),第三行减去2倍第二行,这个操作依样画葫芦可以写成

\(E_{32}\) 右乘以上面的中间结果可以得到最后的上三角矩阵形式,完成消元步骤。

\(A\) 在两次左乘相应矩阵后变成上三角矩阵 \(U\)

由于乘法结合律,可以将两次操作 \(E_{32}, E_{21}\) 视为统一的操作 \(E\)

注意上式中, \(E_{32},E_{21}\) 为下三角矩阵。

我们再用代码来验证下矩阵乘法结合律

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np

e_32 = np.array([
[1., 0., 0.],
[0., 1., 0.],
[0., -2., 1.]
])

e_21 = np.array([
[1., 0., 0.],
[-3., 1., 0.],
[0.,0.,1.]
])

A = np.array([
[1., 2., 1.],
[3., 8., 1.],
[0., 4., 1.]
])
1
2
e_32 @ e_21 @ A
e_32 @ (e_21 @ A)

结果都为

1
2
3
array([[ 1.,  2.,  1.],
[ 0., 2., -2.],
[ 0., 0., 5.]])

因此,矩阵 \(A\) 的消元过程可以用一个矩阵 \(E\) 表示出来。

矩阵的逆

以基本行操作 \(E_{21}^{-1}\) 为例,此变换是从第二行中减去三倍的第一行

那么其逆变换就是给第二行加上三倍的第一行,所以逆矩阵就是

\(E_{32}^{-1}\) 也是同样

我们已经知道这两个行变换的综合效果为 \(E\)

那么 \(E^{-1}\)\(E_{32}^{-1},E_{21}^{-1}\) 有什么关系呢?

发现综合结果的逆以相反的顺序相乘,这个也容易理解,因为逆操作将一个上三角矩阵 \(U\) 恢复成 \(A\),这个过程是自下而上的行变换。

最后,来核实 \(E E^{-1} = I\)

本文继续以彩色Latex 方式总结了方程组的行视角,列视角的几何意义;并回顾了解方程的两个步骤:消元和回代。内容对应于MIT 18.06 Gilbert Strang 线性代数视频课程第一,二节。

本系列链接如下

01 - 矩阵乘法的五种理解

02 - 解方程组的意义和过程

方程组两种几何解释

二元方程组

来看一个具体的二元线性方程组

\[ \begin{cases} 2x&-y&=0 \\ -x&+2y&=3 \end{cases} \]

写成矩阵形式

\[ \begin{bmatrix} 2&-1 \\ -1&2 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} 0 \\ 3 \end{bmatrix} \]

行视角

回顾 $2x - y = 0 $ 为所有满足此条件的 (x, y) 的集合,即集合组成二维平面的一条直线,如下图蓝线所示。

$ -x + 2y = 3$ 则对应绿线。

因此方程组的解 x = 1, y = 2 为两条直线的交点,这就是方程组的行视角:将系数矩阵按行切分,则每一行表示一个约束条件,几何意义是N维空间的一个子空间。在二元方程中,一行表示一条线,三元方程中,一行表示一个平面(详见后一小节)。 \[ \begin{bmatrix} \color{blue} 2 x & {\color{blue} -1 y} \\ \color{green} -1 x & \color{green} 2 y \end{bmatrix} = \begin{bmatrix} \color{blue} 0 \\ \color{green} 3 \end{bmatrix} \]

列视角

若将系数矩阵按列切分,则每一列表示一个向量,方程组的解 (x, y) 表示每个列向量以 x, y 为权重的线性组合刚好形成 b 向量。这个就是方程组 \(Ax=b\) 有解的条件:b 在 A 的列空间,此时,x 为 列向量的组合系数。 \[ x \begin{bmatrix} \color{Green} 2 \\ \color{Green} -1 \end{bmatrix} + y \begin{bmatrix} \color{Turquoise} -1 \\ \color{Turquoise} 2 \end{bmatrix} = \begin{bmatrix} \color{Red} 0\\ \color{Red} 3 \end{bmatrix} \]

三元方程组

行视角

对于三元方程组行视角来说,每一行的方程组成一个三维空间中的一个平面。解是三个平面的交点,通常来说为一个点。 \[ \begin{bmatrix} \color{magenta} m_{11} & \color{magenta} m_{12} & \color{magenta} m_{13} \\ \color{cyan} m_{21} & \color{cyan} m_{22} & \color{cyan} m_{23} \\ \color{green} m_{31} & \color{green} m_{32} & \color{green} m_{33} \\ \end{bmatrix} \begin{bmatrix} w_{1} \\ w_{2} \\ w_{3} \end{bmatrix}= \begin{bmatrix} \color{magenta} y_{1} \\ \color{cyan} y_{2} \\ \color{green} y_{3} \end{bmatrix} \]

图片来自 Introduction to Linear Algebra for Applied Machine Learning with Python,解为一直线而非一个点。

列视角

三元列视角下,A 的列向量为三维平面的一个向量,x(下图为 w) 表示每个列向量取多少倍数可以组成 b 向量。 \[ \color{cyan} w_1 \color{black} \begin{bmatrix} \color{cyan} m_{11} \\ \color{cyan} m_{21} \\ \color{cyan} m_{31} \end{bmatrix}+ \color{magenta} w_2 \color{black} \begin{bmatrix} \color{magenta} m_{12} \\ \color{magenta} m_{22} \\ \color{magenta} m_{32} \end{bmatrix}+ \color{green} w_3 \color{black} \begin{bmatrix} \color{green} m_{13} \\ \color{green} m_{23} \\ \color{green} m_{33} \end{bmatrix}= \begin{bmatrix} \color{red} y_{1} \\ \color{red} y_{2} \\ \color{red} y_{3} \end{bmatrix} \]

解方程的步骤

总结了二元三元方程组的行和列视角后,我们回顾求解方程的具体步骤。用两个过程,消元和回代便可以解得方程。

  • 消元的目的是将系数矩阵表示成变量依次依赖的上矩阵形式 (Upper Triangular Matrix)。 \[ \begin{bmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{n1} & a_{n2} & \cdots & a_{nn} \\ \end{bmatrix} \underrightarrow{Eliminate} \begin{bmatrix} \color{red} a_{11} & a_{12} & \cdots & a_{1n} \\ \color{gray} 0 & \color{red} a'_{22} & \cdots & a'_{2n} \\ \color{gray} 0 & \vdots & \ddots & \vdots \\ \color{gray} 0 & \color{gray} 0 & \color{gray} 0 & \color{red} a'_{nn} \\ \end{bmatrix} \]

  • 回代则在上矩阵的基础上依次解得每个分量的值。

三元方程示例

举个三元方程组为例 \[ \begin{cases} x&+2y&+z&=2 \\ 3x&+8y&+z&=12 \\ &+4y&+z&=2 \end{cases} \]

写成矩阵形式为

\[ \begin{bmatrix}1&2&1\\3&8&1\\0&4&1\end{bmatrix}\begin{bmatrix}x\\y\\z\end{bmatrix}=\begin{bmatrix}2\\12\\2\end{bmatrix} \]

消元过程

在消元过程中,有两类操作,一是将上一行乘以某系数后被下一行减去,依次消除这一行的元。第二类操作是交换当前行和后面某行,行交换用于当某行对应的元已经为0的情况下。

以上述三元方程为例,第二行消元的具体过程为第二行减去3倍的第一行。接着,再进行第三行消元。

\[ \left[\begin{array}{c|c}A&b\end{array}\right] = \left[\begin{array}{ccc|c} \color{red} 1&2&1&2 \\ 3&8&1&12 \\ 0&4&1&2 \end{array}\right] \xrightarrow{row_2-3row_1} \left[\begin{array}{ccc|c} \color{red} 1&2&1&2 \\ \color{blue} \boxed{0} & \color{red} 2& \color{blue} -2& \color{blue}6 \\ 0&4&1&2 \end{array}\right] \xrightarrow{row_3-2row_2} \left[\begin{array}{ccc|c} \color{red}1&2&1&2 \\ 0& \color{red} 2&-2&6 \\ \color{blue} 0 & \color{blue} \boxed{0} & \color{red} 5& \color{blue}-10 \end{array}\right] \]

最终,上矩阵为 \[ U=\begin{bmatrix} \color{red} 1 & 2& 1 \\ \color{gray} 0 & \color{red} 2 & -2 \\ \color{gray} 0 & \color{gray} 0& \color{red} 5 \end{bmatrix} \]

回代过程

回代过程比较直白,由上矩阵 U 对应方程组 \[ \begin{cases} \color{red} x&+2y&+z&=2 \\ & \color{red} +2y &-2z&=6 \\ && \color{red} +5z&=-10 \end{cases} \]

自下向上,容易解得 \[ \begin{cases} \color{red}x \color{black}= 2 \\ \color{red}y \color{black}= 1 \\ \color{red}z \color{black}= -2 \end{cases} \]

消元的行视角意义

注意到消元时的两类操作都不改变系数矩阵的行空间,只是改变了行空间的线性组合方式。

由于每一行代表一个拘束子空间,因此每一次消元改变了该行的拘束子空间。

举个例子,对于二元方程组和行空间 \[ \begin{bmatrix} \color{blue} 2 x & \color{blue} -1 y \\ \color{green} -1 x & \color{green} 2 y \end{bmatrix} = \begin{bmatrix} \color{blue} 0 \\ \color{green} 3 \end{bmatrix} \]

对应了两条直线

第二行的 x 消除后其几何意义为:蓝色直线不变,绿色直线从包含 x 的成分变成不含 x 成分,并且维持交点 (1, 2)不变。 \[ \begin{bmatrix} \color{blue} 2 x & \color{blue} -1 y \\ & \color{green} {3 \over 2} y \end{bmatrix} = \begin{bmatrix} \color{blue} 0 \\ \color{green} 3 \end{bmatrix} \]

最后还有一个问题可以思考一下:消元对于列视角的几何意义是什么呢?

在 MIT Gilbert Strang 教授经典的线性代数课程第三节中详细解释了矩阵相乘的5种方法及其理解,本文用彩色的 Latex 公式呈现出来,便于大家回顾复习。

01 - 矩阵乘法的五种理解

02 - 解方程组的意义和过程

假定 \(A \times B =C\) 中, A 是 \(m\)\(n\) 列的矩阵, \(B\)\(n\)\(p\) 列的矩阵,\(C\)\(m\)\(p\) 列矩阵:

\[ A_{m \cdot n} \times B_{n \cdot p} = C_{m \cdot p} \]

B矩阵的列组合:列列切分

这是最经典的理解方式,沿袭了第一部分 \(A \vec x= \vec b\) 的方式。

回顾可以将 \(A \vec x\) 视为 \(A\) 的列向量关于每个 \(x\) 分量的线性组合。

那么 \(A B\) 相乘可以理解为将矩阵 \(B\) 按列切分成列向量,即 \[ B = [\vec B_{col_1} \vec B_{col_2} \cdots \vec B_{col_p} ] \]

如此,结果矩阵的第 \(j\) 列就是 \(A \vec B_{col_j}\)\(A\) 的列向量关于 $ B_{col_j} $ 的线性组合。

由于我们将 \(A\)\(B\) 都按列来切,这种方式可以助记成 列列 切分。

A矩阵行组合:行行切分

同样的,对应与 \(A\) 右乘向量等同于\(A\)列的组合,\(A\) 左乘行向量等同于 \(A\) 行的组合: \[ [x_1, x_2, ..., x_n]\begin{bmatrix} A_{row_1} \\ A_{row_2} \\ \vdots \\A_{row_n} \end{bmatrix} = x_1 \cdot A_{row_1} + x_2 \cdot A_{row_2} +...+ x_n \cdot A_{row_n} \] 其结果是一个行向量。

那么 \(A B\) 相乘可以理解为将矩阵 \(A\) 按行切分成行向量,即 \[ A = \begin{bmatrix} A_{row_1} \\ A_{row_2}\\ \vdots \\ A_{row_m} \end{bmatrix} \]

如此,结果矩阵的第 \(i\) 行就是 \(\vec A_{row_i} B\)\(B\) 的行向量关于 $ A_{row_i} $ 的线性组合。

这种方式可以助记成 行行 切分。

A行 x B列 点乘:行列切分

\(A\) 矩阵按行切,\(B\) 矩阵按列切,即住记成 行列 切分时如下所示。

\[ \begin{eqnarray*} && A_{m \cdot n} \times B_{n \cdot p} \\ &=& \begin{bmatrix} A_{row_1} \\ A_{row_2} \\ \vdots \\ A_{row_i} \\ \vdots \\ \end{bmatrix}_{m \cdot n} \begin{bmatrix} B_{col_1} & B_{col_2} & \cdots & B_{col_p} \end{bmatrix}_{n \cdot p} \\ &=& \begin{bmatrix} &\vdots&\\& A_{row_i}&\\&\vdots& \end{bmatrix}_{m \cdot n} \begin{bmatrix} &&\\\cdots& B_{col_j} & \cdots\\&& \end{bmatrix}_{n \cdot p} \\ &=& \begin{bmatrix} &\vdots&\\\cdots&c_{ij}&\cdots\\& \vdots \end{bmatrix}_{m \cdot p} \end{eqnarray*} \]

行乘以列即列向量点乘,结果是一个标量。因此 \(c_{ij}\) 为结果矩阵 \(C\) 的第 \(i\)\(j\) 列的值。

\[ c_{ij}=A_{row_i} \cdot B_{col_j} = \begin{bmatrix} a_{i1} & a_{i2} & \cdots & a_{in} \end{bmatrix} \begin{bmatrix} b_{1j} \\ b_{2j} \\ \vdots \\ b_{nj} \end{bmatrix} =\sum_{k=i}^na_{ik}b_{kj} \]

A列 x B行 矩阵和:列行切分

最后,也可也按列行来切分。注意列乘以行时的结果是一个矩阵。

分块相乘

第五种方式是分块相乘,可以认为是点乘理解下的扩展。

获取服务地址

1. 最直接方式

在桌面版或者手机版浏览器中打开链接

https://app.myencyclopedia.top/paper

2. 微信扫码

3. 从公众号中获取

关注 MyEncyclopedia 公众号

在公众号对话中回复 ?

点击 AI在线服务

在微信浏览器中直接打开

或者获取链接

进入服务后需要首次使用需要登录,见文末附录部分。

开始搜索主题关键词

已最常见的 deep learning 为例,在搜索框中输入 deep learning 后稍等片刻,出现如下和其关联的词组对。

点击节点展开更多关联

由于和 deep learning 关联的概念词语较多,每次只会展现 10 个。单击节点可以加载更多关联词。

探索其他节点

双击节点增加至查询论文条件

查看综述

增加联合条件

登录流程

目前提供两种方式。 在国内的小伙伴建议使用微信公众号的方式登录,在国外的朋友们可以使用 Google 账号登录。

微信登录,按提示在为公众号中输入验证码

Google登录

今天和大家分享强化学习的经典训练环境,任天堂的红白机训练环境。

这次的环境,我将分装成 docker 镜像,这样在任何平台:Windows,Linux 甚至 Mac 上都可以运行。

这里将通过大家最常用的 Windows 系统来演示环境的使用。

X window 服务器

在 Windows上,首先,我们要装 X Window Server。可以用 Cygwin 或者是 XLaunch。

这里采用 XLaunch 是因为安装比较方便,安装包也很小。

如果 XLaunch 正常启动的话,就会在系统托管的地方显示出来。接着我们来下载环境的 docker images。

拉取镜像

用 docker pull 命令我们将预制的公开镜像拉下来

1
docker pull myencyclopedia/gym-nes

拉下来以后,可以用 docker image命令来检查是否存在

1
docker images

下一步,我们需要找到物理机或者 docker host 机器的 IP 地址。

在windows上,我们执行 ipconfig 命令,注意我们要的是 WSL 对应的 IP 地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ipconfig.exe

Windows IP 配置

以太网适配器 以太网:
媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . :
以太网适配器 vEthernet (WSL):
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::8841:6bd8:5064:9a3c%45
IPv4 地址 . . . . . . . . . . . . : 172.23.0.1
子网掩码 . . . . . . . . . . . . : 255.255.240.0
默认网关. . . . . . . . . . . . . :

得到了物理机的 docker 网段地址以后,我们将地址保存在物理机的 Display 环境变量中,注意最后需要加上 :0

1
export DISPLAY=172.23.0.1:0

至此,我们可以一键跑超级玛丽了。

1
docker run -e DISPLAY=$DISPLAY myencyclopedia/gym-nes bash -c 'python gym_nes_demo.py'

解释一点,-e DISPLAY=$DISPLAY 将 Display 环境变量从当前 shell 注入到 container 中。

一切顺利的话,有个 X window的窗口会跳出来,无人控制得超级玛丽运行了起来,它会随机执行一些动作。

结束程序记得要把 docker container 显示关掉,需要执行 docker stop

各种游戏

其实,预制的 docker 环境给大家装了更多的游戏,大家也可以修改源码跑其他游戏

具体方法是,将上面命令稍微修改一下 次我们进入 interactive bash。

1
docker run -it  -e DISPLAY=$DISPLAY myencyclopedia/gym-nes bash

进了 container 之后,我们发现之前执行的 python 原代码是当前目录的 gym_nes_demo.py

先列出所有的游戏的 rom 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(py3.7) root@aff72945133b:/proj/nes_py# find . -name '*.nes'
./tests/games/excitebike.nes
./tests/games/super-mario-bros-2.nes
./tests/games/super-mario-bros-3.nes
./tests/games/empty.nes
./tests/games/super-mario-bros-lost-levels.nes
./tests/games/super-mario-bros-1.nes
./tests/games/the-legend-of-zelda.nes
./tests/nes-roms/1942 (Japan, USA).nes
./tests/nes-roms/contra.nes
./tests/nes-roms/Battle City (J).nes
./tests/nes-roms/red.nes
./tests/nes-roms/Gradius 2 (J).nes
./tests/nes-roms/super-mario.nes
./tests/nes-roms/Contra Force (USA).nes
./tests/nes-roms/Rush'n Attack (U).nes

文件夹里有很多游戏, 包括魂斗罗,坦克大战等等。

修改 gym_nes_demo.py,将超级玛丽替换成魂斗罗 nes。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from nes_py import NESEnv
import tqdm
env = NESEnv('/proj/nes_py/tests/games/super-mario-bros-1.nes')

done = True

try:
for _ in tqdm.tqdm(range(5000)):
if done:
state = env.reset()
done = False
else:
state, reward, done, info = env.step(env.action_space.sample())
env.render()
except KeyboardInterrupt:
pass

1
env = NESEnv('./tests/nes-roms/contra.nes')

保存后,执行

1
python gym_nes_demo.py

魂斗罗的 random agent 也跑起来了。

下一期,我会把一些很经典的深度强化学习的算法应用到这个环境中,让大家可以很方便得训练调试深度强化学习算法来挑战各种红白机游戏。

最后,感谢大家关注 MyEncyclopedia 公众号,B站频道或者 Youtube 频道。谢谢大家再见。

资深老程序员,兴趣广泛,精通各个方面的计算机技术,包括

  • 深度学习:RL,DNN,CV,NLP
  • 统计和数学
  • 算法
  • 计算机工程
  • 多线程编程
  • 分布式计算和大数据
欢迎关注我的公众号

我的微信号,注明目的

当下两种静态博客 Hugo 和 Hexo 都很出色,都是将 markdown 格式的文件编译成 html。 Hugo 是用 go 语言来写的,Hexo 是用 node.js 来实现的。但是在经过了一年多的 Hugo 使用之后,终于下定决心切换到 Hexo。这里记录了自己的一些使用心得,比较和一些小技巧。

发布到 GitHub

注意到 Hexo 是发现 Github 可以 host 静态博客,并且 Hexo 默认支持直接发布到 Github,就抱着好奇心尝试了下 Hexo。

下面的链接是站点发布到 Github后通过二级域名可以访问,打开速度比自己的站点快不少。

https://myencyclopedia.github.io/archives/

两者共同优点

总的来说,hugo 对于不懂前端但是想部署博客的同学来说是很友好的。但是,对于熟悉大前端的同学,可以很方便在静态页面基础上加入动态元素以及快速开发插件,Hexo 是比较完美的解决方案。

下面是我在考虑静态博客时一些两者都满足的特性:

需求点 Hexo Hugo
Markdown 源文件
代码高亮
Latex支持
多语言频道
发布到 Github
Markdown 中引用其他页面
桌面版+手机版
嵌入 Youtube 和 B站视频
接入 Google Analytics
用户评论

Hexo 优势

当遇到默认框架无法支持的功能时,两者的差别就体现了出来。

Hugo 的插件系统主要是通过 shortcode 来完成。记得之前为了开发hugo最简单的 shortcode 花费了九牛二虎之力,既包含了部署 Go语言环境,也包含调试开发的小插件,并且由于开发工具的匮乏,只能通过log打印出来内部状态,非常耗时。另外,配置 mathjax 等也花了很多时间,出现莫名其妙的 quote 错误时只能不断试错,没有明显的错误提示。又比如,很多时候需要调整编译 pipeline 来完成类似图片加水印,图片文件压缩等功能,也没有现成方案。 Hexo 则相反,首先它有着更丰富的插件。其次,可以很方便的通过 node.js 生态来开发新插件,控制编译 pipeline。当然,这需要对 npm,javascript 等大前端的编程环境比较熟悉,所以 Hexo 很适合前端程序员。例如,Hexo 可以通过现有插件来实现手机版炫酷的无限下滚功能,或者类似于微信公众号的图片异步加载功能,这些都是 Hugo 无法简单完成的。可以说,使用大前端 js 的 Hexo 将静态markdown 动态编译和动态灵活的客户端技术完美结合为一体。

下面是一些我目前想到的 Hexo 相对于 Hugo 的优势特性

需求点 Hexo Hugo
新文章自动提交 Google Baidu 收录
图片水印
简单搜索支持
SVG 动画
内嵌 PDF 阅读
动态CDN
方便的插件开发 方便 比较麻烦

Typora 统一资源路径

在 Hexo 启用了文章资源目录后,可以将相关资源拷贝到专有同名目录下,并在文章中直接引用资源文件名。

具体步骤如下

1. 启用资源目录

**_config.yml** 中编辑

1
post_asset_folder: true

2. 文件存放结构

1
2
3
4
5
6
7
_posts/

└───article1.md
└───article1/
│ │ img1.png
│ │ img2.png

3. markdown 中引用资源文件

article1.md

1
![](img1.png)

4. Typora 启用同样资源引用模式

平时我用 Typora 编辑,Typora 也支持这种资源引用模式。

article1.md

1
typora-root-url: article1

在 VS Code 中调试插件

VS Code 已经默认支持调试项目 npm 或者 yarn 安装的 node.js 模块,这对于开发或者改动第三方插件而言非常方便,具体方法为。

打开 package.json,将鼠标放在 scripts 的目标中,会弹出 Run Script | Debug Script

选择 Debug Script 在 node_modules 中相关源文件中设置断点,就可以调试了!

请在桌面浏览器中打开此链接,保证最好的浏览体验

关注公众号后回复 docker-geometric,获取 Docker Image

1
2
3
4
5
# 导入image
docker load < env-torch1.8-geometric.tar

# 运行 jupyter notebook
docker run -p 8888:8888 -it env-torch1.8-geometric jupyter notebook --allow-root --ip '0.0.0.0' --notebook-dir=/proj

在线代码

公众号系列视频链接

B站视频系列链接

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×