一次函数 $$ y = ax + b $$
二次函数 $$ y = ax^2 + bx + c $$
单位越阶函数 $$ u(x) = \left{ \begin{array}{ll}
0 & (x < 0) \\
1 & (x \ge 0)
\end{array} \right. $$ 单位越阶函数的表示方式如上,该函数在原点处不连续,也就是在原点处不可导。由于这个不可导的性质,单位越阶函数函数不能成为主要的激活函数。
指数函数与 Sigmoid 函数
指数函数的形状如下,其中常数 a 被称为指数函数的底数,且 a 为正的常数 $$ y = a^x \ (a \ne 1) $$
纳皮尔数是一个重要的底数,其近似值为: $$ e = 2.71828\ ... $$
Sigmoid 函数是神经网络中具有代表性的激活函数 $$ \sigma(x) = \frac{1}{1 + e^{-x}} = \frac{1}{1 + exp(-x)} $$ 注:exp 是 exponential function(指数函数)的简略记法,exp(x) 表示指数函数 $e^x$
(此处显示 Sigmoid 函数图像)从 Sigmoid 的函数图像可以看出,这个函数时光滑的,也就是处处可导。函数的取值在 0 到 1 之间,因此函数值可以用概率来解释
用计算机实际确定神经网络时,必须设定权重和偏置的初始值,求初始值时,正态分布是一个有用的工具,使用服从这个分布的随机数,容易取得好结果。正太分布是一种概率分布,它的概率密度函数如下: $$ f(x) = \frac{1}{\sqrt{2\pi\sigma}}e^{\frac{(x - \mu)^2}{2\sigma^2}} $$ 其中常数 $\mu$ 称为期望值(平均值),常数 $\sigma$ 称为标准差。
(此处显示图像)
按照正态分布产生的随机数成为正态分布随机数。在神经网络的计算中,经常用到正态分布随机数作为初始值。
向量是具有方向与大小的量,用箭头表示,如 $$ \vec{a} $$ 向量也可以用不带箭头的黑斜体字母 $\bold{a}$ 表示
向量的坐标表示
把向量箭头放在坐标平面上,就可以用坐标的形式表示向量。把箭头的起点放在原点,用箭头终点的坐标表示向量,就构成了向量的坐标表示 $$ \bold{a} = (a_1, a_2) $$
向量的大小即向量的长度,表示为 $$ |\bold{a}| $$ 两个向量 $\bold{a}$ 与 $\bold{b}$ 的内积定义如下,其中 $\theta$ 是 $\bold{a}$ 与 $\bold{b}$ 之间的夹角 $$ \bold{a} \cdot \bold{b} = |\bold{a}||\bold{b}|\cos{\theta} $$ 当 $\bold{a} = (a_1, a_2)\ \ \bold{b} = (b_1, b_2)$ 时,$\bold{a}$ 与 $\bold{b}$ 的内积的坐标表示为: $$ \bold{a} \cdot \bold{b} = a_1b_1 + a_2b_2 $$
柯西施瓦兹不等式 $$ -|\bold{a}||\bold{b}| \le \bold{a} \cdot \bold{b} \le |\bold{a}||\bold{b}| $$ 证明:根据余弦函数的性质,对于任意的 $\theta$ 有 $-1 \le \cos{\theta} \le 1$ ,故有 $$ -|\bold{a}||\bold{b}| \le |\bold{a}||\bold{b}|\cos{\theta} \le |\bold{a}||\bold{b}| $$ 根据柯西施瓦兹不等式,可以得到以下事实
矩阵是数的阵列,如下所示 $$\begin{pmatrix} a{1,1} & a{1,2} & \cdots & a{1,n} \ a{2,1} & a{2,2} & \cdots & a{2,n} \ \vdots & \vdots & \ddots & \vdots \ a{m,1} & a{m,2} & \cdots & a{m,n} \end{pmatrix}$$ 这是一个 m 行 n 列的矩阵。位于第 i 行第 j 列的元素用 $a{ij}$ 表示。如果 m = n,则可以称该矩阵为方阵。
单位矩阵是对角线元素为 1,其他元素为 0 的方阵。通常用 E 表示,如二阶与三阶的单位矩阵表示如下 $$ E = \begin{pmatrix} 1 & 0 \ 0 & 1 \end{pmatrix}, \ \ \ \ \ E = \begin{pmatrix} 1 & 0 & 0 \ 0 & 1 & 0 \ 0 & 0 & 1 \end{pmatrix} $$
两个矩阵 A 与 B 相等是指它们对应的元素相等,记为 $A = B$ 。如 $$ A = \begin{pmatrix} 2 & 7 \ 8 & 1 \end{pmatrix}, \ \ \ \ \ B = \begin{pmatrix} x & y \ u & v \end{pmatrix} $$ 当 A = B 时,有 $$ x = 2, \ y = 7, \ u = 8, \ v = 1 $$
两个矩阵 A 和 B 的和 A + B,差 A - B 定义为相同位置元素的和、差所产生的矩阵。矩阵的常数倍定义为各个位置的元素的常数倍所产生的矩阵。如: $$ A = \begin{pmatrix} 2 & 7 \ 1 & 8 \end{pmatrix}, \ \ \ \ \ B = \begin{pmatrix} 2 & 8 \ 1 & 3 \end{pmatrix} $$ 则有 $$A + B = \begin{pmatrix} 2 + 2 & 7 + 8 \ 1 + 1 & 8 + 3 \end{pmatrix} = \begin{pmatrix} 4 & 15 \ 2 & 11 \end{pmatrix} \ \ \ A - B = \begin{pmatrix} 2 - 2 & 7 - 8 \ 1 - 1 & 8 - 3 \end{pmatrix} = \begin{pmatrix} 0 & -1 \ 0 & 5 \end{pmatrix} \ \ \ 3A = \begin{pmatrix} 3 \times 2 & 3 \times 7 \ 3 \times 1 & 3 \times 8 \end{pmatrix} = \begin{pmatrix} 6 & 21 \ 3 & 24 \end{pmatrix}$$
对于两个矩阵 A、B,将 A 的第 i 行看做行向量,将 B 的第 j 列看做列向量,将他们的内积作为第 i 行第 j 列的元素,由此产生的矩阵就是矩阵 A、B 的乘积。
(此处显示示意图)
函数 f(x) 的导函数 $f'(x)$ 的定义如下 $$ f'(x) = \lim_{\Delta{x} \rightarrow 0} \frac{f(x + \Delta{x}) - f(x)}{\Delta{x}} $$ (此处用图显示导函数的含义)
神经网络中用到的函数的导数公式 $$ (e)' = 0,\ (x)' = 1,\ (x^2)' = 2x,\ (e^x)' = e^x,\ (e^{-x})' = e^{-x} $$
导数的性质 $$ {f(x) + g(x)}' = f'(x) + g'(x),\ c{f(x)}' = cf'(x) $$ 其中 c 为常数
上式成为导数的线性性。用文字可以表述为:和的导数为导数的和,常数倍的导数为导数的常数倍。
分数函数的导数表示如下 $$ {\frac{1}{f(x)}}' = - \frac{f'(x)}{{f(x)}^2} $$ Sigmoid 函数表示如下 $$ \sigma(x) = \frac{1}{1 + e^{-x}} $$ 对 Sigmoid 函数求导可得到如下式子 $$ \sigma'(x) = \sigma(x)(1 - \sigma(x)) $$
由于导函数 f'(x) 表示切线斜率,我们可以得到一下原理:当函数 f(x) 在 x = a 处取得最小值时,f'(a) = 0
应用这个原理时,应记住以下事实:f'(a) = 0 是函数 f(x) 在 x = a 处取得最小值的必要条件
多变量函数即拥有多个自变量的函数。单变量函数用 f(x) 表示,仿照单变量函数,多变量函数可以表示如下: $$ f(x, y) = f(x2, x2, x_n) $$
求导的方法同样适用于多变量函数的情况。但是,由于有多个变量,所以必须指明对哪一个变量进行求导。在这个意义上,关于某个特定变量的导数就成为偏导数(partial derivative)。
让我们考虑有两个变量 x、y 的函数 z = f(x, y)。则关于 x 的偏导数可表示为 $$ \frac{\partial{z}}{\partial{x}} = \frac{\partial{f(x, y)}}{\partial{x}} = \lim{\Delta{x} \rightarrow 0} \frac{f(x + \Delta{x}, y) - f(x, y)}{\Delta{x}} $$ 同理,关于 y 的偏导数可以表示为 $$ \frac{\partial{z}}{\partial{y}} = \frac{\partial{f(x, y)}}{\partial{y}} = \lim{\Delta{y} \rightarrow 0} \frac{f(x, y + \Delta{y}) - f(x, y)}{\Delta{y}} $$
光滑的单变量函数 y = f(x) 在点 x 处取得最小值的必要条件是导函数在该点取值为 0。这个事实对于多变量函数同样适用。例如,对于有两个变量的函数,可以如下表示:函数 $z = f(x,\ y)$ 取得最小值的必要条件是 $\frac{\partial{f}}{\partial{x}} = 0$,$\frac{\partial{f}}{\partial{y}} = 0$ 。
已知函数 y = f(u),当 u 表示为 u = g(x) 时,y 作为 x 的函数可以表示为形如 y = f(g(x)) 的嵌套结构(u 和 x 表示多变量)。这时,嵌套结构的函数 f(g(x)) 称为 f(u) 和 g(u) 的复合函数。
(此处用图显示一个复合函数的例子)
已知单变量函数 y = f(u),当 u 表示为单变量函数 u = g(x) 时,复合函数 f(g(x)) 的导函数可以如下简单地求出来 $$ \frac{\mathrm{d}y}{\mathrm{d}x} = \frac{\mathrm{d}y}{\mathrm{d}u} \frac{\mathrm{d}u}{\mathrm{d}x} $$ 这个公式称为单变量函数的复合函数求导公式,也称为链式法则。(此处显示链式法则的示意图)
在多变量函数的情况下,链式法则的思想同样适用。我们来考察两个变量的情形。变量 z 为 u、v 的函数,如果 u、v 分别为 x 和 y 的函数,则 z 为 x 和 y 的函数,此时下式(多变量函数的链式法则)成立 $$ \frac{\partial{z}}{\partial{x}} = \frac{\partial{z}}{\partial{u}}\frac{\partial{u}}{\partial{x}} + \frac{\partial{z}}{\partial{v}}\frac{\partial{v}}{\partial{x}} $$ (此处显示多变量函数链式法则的示意图)
首先我们来考察单变量函数 y = f(x)。如果 x 做微小的变化,那么函数值 y 将怎样变化呢?答案就在导函数的定义式中 $$ f'(x) = \lim_{\Delta{x} \rightarrow 0} \frac{f(x + \Delta{x}) - f(x)}{\Delta{x}} $$ 在这个定义式中,$\Delta{x}$ 是“无限小的值”,不过若将它替换为“微小的值”,也不会造成很大的误差。因而下式近似成立: $$ f'(x) \fallingdotseq \frac{f(x + \Delta{x}) - f(x)}{\Delta{x}} $$ 其中,$\Delta{x}$ 为微小的值。将上式变形可以得到一下单变量函数的近似公式: $$ f(x + \Delta{x}) \fallingdotseq f'(x) \Delta{x} + f(x) $$ (此处插入表达近似公式意思的可视化程序:给出函数图像 --> 在函数图像上标注一个点并画出该点切线 --> 允许用户移动x点观察切线的值与函数的值的变化)
下面我们将单变量函数的近似公式扩展到两个变量的函数: $$ f(x + \Delta{x}, y + \Delta{y}) \fallingdotseq f(x, y) + \frac{\partial{f}}{\partial{x}} \Delta{x} + \frac{\partial{f}}{\partial{y}} \Delta{y} $$ 进一步可以扩展到三个变量的函数,如变量 z 为 w、x、y 的函数时,近似公式如下所示: $$ \Delta{z} \fallingdotseq \frac{\partial{z}}{\partial{w}}\Delta{w} + \frac{\partial{z}}{\partial{x}}\Delta{x} + \frac{\partial{z}}{\partial{y}}\Delta{y} $$ 其中 $\Delta{z}$ 表示 $$ \Delta{z} = f(w + \Delta{w}, x + \Delta{x}, y + \Delta{y}) - f(w, x, y) $$
三个变量的函数的近似公式可以表示为如下两个向量的内积 $$ \nabla{z} \cdot \Delta{x} \ \nabla{z} = (\frac{\partial{z}}{\partial{w}}, \frac{\partial{z}}{\partial{x}}, \frac{\partial{z}}{\partial{y}}) ,\ \ \Delta{x} = (\Delta{w},\ \Delta{x},\ \Delta{y}) $$
梯度下降是一个著名的寻找函数最小值的点的方法。
我们将函数图像看做斜坡,在斜坡上的点 P 处放一个乒乓球,然后轻轻地松开手,球会沿着最陡的坡面开始滚动,待球稍微前进一点,把球止住,然后在止住的位置再松手,乒乓球会从这个点再次沿着最陡的斜坡开始运动。这个操作反复若干次后,乒乓球沿着最短的路径到达图像的底部,也就是函数的最小值点。梯度下降法模拟的就是球的运动过程。
对于函数 z = f(x, y) ,我们定义 $\Delta{z}$ 为 $$ \Delta{z} = f(x + \Delta{x}, y + \Delta{y}) - f(x, y) $$ 根据近似公式有 $$ \Delta{z} = \frac{\partial{f}}{\partial{x}}\Delta{x} + \frac{\partial{f}}{\partial{y}}\Delta{y} $$ 上述公式的右边部分可以表示为如下两个向量的内积形式 $$ \bold{a} = (\frac{\partial{f}}{\partial{x}}, \frac{\partial{f}}{\partial{y}}),\ \ \bold{b} = (\Delta{x}, \Delta{y}) $$ 由柯西施瓦兹不等式 $$ -|\bold{a}||\bold{b}| \le \bold{a} \cdot \bold{b} \le |\bold{a}||\bold{b}| $$ 当 $\bold{a} = -k\bold{b}$ (k 为正的常数)时,a 和 b 的内积取得最小值。
综上所述,当 $$ (\Delta{x}, \Delta{y}) = - \eta (\frac{\partial{f}}{\partial{x}}, \frac{\partial{f}}{\partial{y}}) $$ 成立(其中 $\eta$ 是正的微小常数),函数 z = f(x, y) 减小得最快,上述关系式同时就是二变量函数的梯度下降法的基本式。
(此处显示梯度下降算法的伪代码和流程图)
直接将“用 Excel 体验梯度下降法”改写成能够互动的可视化程序,大概由以下几个部分所构成
用类似“Learn Git Branching”的方式引导用户体验整个梯度下降的流程
将梯度下降算法抽象成一系列步骤,每个步骤看做一个几点。给出节点,然后让用户在节点之间连线,以构成正确的梯度下降法的流程
简单实现针对一次函数的梯度下降算法,具体方式为:
def gradient_descent(start_x, start_y, target_func, step_size=0.001, stop_condition=0.0001):
pass
判定方法(选择其中一个)
可视化
Keras API 是一个用 Python 编写的高级神经网络 API,通过这个 API 我们能够十分容易地搭建、训练、评估和使用许多神经网络模型。同时 Keras 也是一个 Pyhton 的库,它依靠后端引擎来进行计算任务,且 Keras 提供了多种引擎供用户选择,包括 TensorFlow,Theano 和 CNTK。
另外,Tensorflow 实现了自己的 Keras API,即 tf.keras,可以把它理解为只支持 Tensorflow 作为后端的 Keras,但与 Keras 不同的是 tf.keras 实现了一些非常有用的额外特性,比如它能很好地支持 Tensorflow 的 Data API,这使得 tf.keras 能够十分方便对数据进行加载和预处理。
(此处显示Keras的原生实现示意图以及 Tensorflow 版本的示意图)
tf.keras 加载 Fashion MINIST 数据集(可以加点介绍这两个数据集的内容) import tensorflow as tf
from tensorflow import keras
fashion_mnist = keras.datasets.fashion_mnist
(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()
在 keras 中,MINST 以及 Fasion MINIST 的每一章图片被表示为 28 x 28 的数组, 数组中每个单元的值是在 0 到 255 之间的一个整数。所以在命令行输入以下命令时,应该会出现如下结果
>>> X_train_full.shape
(60000, 28, 28)
>>> X_train_full.dtype
dtype('uint8')
在将数据传给神经网络之前,需要把数据的每个单元的值调整为 0 到 1 之间。另外,由于 keras 并没有划分出验证集,所以需要我们手动进行划分
X_valid, X_train = X_train_full[:5000] / 255.0, X_train_full[5000:] / 255.0
y_valid, y_train = y_train_full[:5000], y_train_full[5000:]
model = keras.Sequential([
keras.layers.Flatten(input_shape=(28, 28)),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dense(10)
])
让我们仔细介绍上述代码:
Sequential 模型是 keras 中最简单的一种神经网络模型,它单纯地按照添加的顺序将层与层连接起来。而在 Sequential 构造函数中传入的是构成模型的层。Flatten 层的作用是将输入的数据铺开成一个一维数组,面对 Fashion MINST 数据集,一个数据的形状是 28 x 28,那么 Flatten 层将把它转换成大小为 784 的一维数组Dense 层(即全连接层)是一个隐藏层,我们在这一层设置了128个神经元,每个神经元的激活函数设置为 ReLU 函数。Dense 层,模型建立完毕后,我们可以使用 model.summary() 来查看我们构建的模型的信息。
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
我们使用 SparseCategoryCrossentropy 作为损失函数,是因为我们使用的数据集的标签是一个从 0 到 9 之间的一个数字。如果我们使用如 [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.] 形式的数组(称为 one-hot vector)来表示一个数据的标签,那么我们应该使用的损失函数应该是 CategoricalCrossentropy。
history = model.fit(X_train, y_train, epochs=30, validation_data=(X_valid, y_valid))
使用 fit 方法开始训练我们的模型,待训练结束之后,我们便得到一个可用的针对 Fashion MINIST 的图片分类器了。
model.evaluate(X_test, y_test)
使用 evaluate 方法对我们的模型进行评估,我们将得到类似如下的输出
10000/10000 [==========] - 0s 29us/sample - loss: 0.3340 - accuracy: 0.8851 [0.3339798209667206, 0.8851]
X_new = X_test[:3]
y_proba = model.predict(X_new)
y_proba.round(2)
用 tf.keras 搭建 ResNet