快速了解 TensorFlow 基础 (六) 神经网络基础

机器学习说白了, 就是找到一组参数 $\theta$ 使得 $f_\theta:x \to y, \quad x,y\in\mathbb{D}^{train}$ 并且通过 $f_\theta(x), x\in\mathbb{D}^{test}$ 进行验证, 之后将其应用到新的样本 $\mathbb{D}^{new}$

感知机

感知机模型

感知机模型接受向量 $x = [\vec{x_1},\vec{x_2},\dots,\vec{x_n}]$, 并且以权重 $w_i$ 对每个维度进行权重约束, 之后将其链接一起汇聚成变量 $z$.

$$z = \sum_{i=1}^{n}\vec{w_i}\vec{x_i}+\vec{b}$$

其中 $\vec{b}$ 为感知机的偏置(bias), $\omega=[\vec{w_1},\vec{w_2},\dots,\vec{w_n}]$ 为感知机权重(weights), $z$ 为感知机的净活性值(net activation).

$$z = w^Tx+b$$

通过激活函数 $\sigma$ 将净活性值转换为活性值(activation) $a$:

$$a = \sigma(z) = \sigma(w^Tx+b)$$

激活函数

阶跃函数(step function) $a = \begin{cases}1, z\geq0\\0,z<0\end{cases}$

符号函数(sign function) $a = \begin{cases}1, z\geq0\\-1,z<0\end{cases}$

这两种激活函数可以用于完成二分类任务.

全连接层

在感知机的基础上, 将阶跃函数替换成连续可导的函数 $\sigma$. 堆叠多个网络层(实现多维输出)增强表达能力.

$$[o_1 o_2] = \begin{bmatrix} x_1 x_2 x_3 \end{bmatrix} @ \begin{bmatrix} w_{11} w_{12} \\ w_{21} w_{22} \\ w_{31} w_{32} \end{bmatrix} + [b_1 b_2]$$

即为

$$O = X@W+b$$

每个输出节点与全部的输入节点相连接, 这种网络层称为全连接层(fully-connected layer), 或者稠密连接层(dense layer). $𝑾$ 矩阵叫做全连接层的权值矩阵, $𝒃$ 向量叫做全连接层的偏置向量.

1
2
3
4
5
6
7
8
9
10
# 定义张量和偏置
w1 = tf.Variable(tf.random.truncated_normal([28*28, 256], stddev=1.0))
b1 = tf.Variable(tf.zeros([256]))

# 输入的训练数据
x = tf.random.normal([2, 28*28])

# 计算 X@W+b
o1 = x@w1 + b
o1 = tf.nn.relu(o1)

使用 Keras 简写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 输入的训练数据
x = tf.random.normal([2, 28*28])

# 构建全连接层 fully connected layer
fcl = tf.keras.layers.Dense(256, activation=tf.nn.relu)

# 计算
o1 = fcl(x)
w1 = fcl.kernel
b1 = fcl.bias

# 所有参数列表
v1 = fcl.variables
# 待优化参数列表 - 全连接层中, 所有参数均为待优化参数
v2 = fcl.trainable_variables

在执行 dense 函数 fcl(x) 的时候, 实际是执行类的 __call__ 方法, 由 TensorFlow 框架自行完成.

神经网络

将多个全连接层进行堆叠, 保证前一层的输出节点数与当前层的输入节点数一致, 即可堆叠出任意层数的网络, 称之为神经网络.

4 层的神经网络由 4 个全连接层组合而成.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 隐藏层
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=1.0))
b1 = tf.Variable(tf.zeros([256]))
w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=1.0))
b2 = tf.Variable(tf.zeros([128]))
w3 = tf.Variable(tf.random.truncated_normal([128, 64], stddev=1.0))
b3 = tf.Variable(tf.zeros([64]))
# 输出层
w4 = tf.Variable(tf.random.truncated_normal([64, 10], stddev=1.0))
b4 = tf.Variable(tf.zeros([10]))

with tf.GradientTape() as tape:
o1 = tf.nn.relu(x@w1+b1)
o2 = tf.nn.relu(o1@w2+b2)
o3 = tf.nn.relu(o2@w3+b3)
o4 = tf.nn.relu(o3@w4+b4)

使用 Keras 简写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fcl1 = tf.keras.layers.Dense(256, activation=tf.nn.relu) # 隐藏层1
fcl2 = tf.keras.layers.Dense(128, activation=tf.nn.relu) # 隐藏层2
fcl3 = tf.keras.layers.Dense(64, activation=tf.nn.relu) # 隐藏层3
fcl4 = tf.keras.layers.Dense(10, activation=None) # 输出层

# 直接写
o1 = fcl1(x)
o2 = fcl2(o1)
o3 = fcl3(o2)
o4 = fcl4(o3)

# 使用 sequential 序列化
model = tf.keras.Sequential([fcl1, fcl2, fcl3, fc4l])
o = model(x)

前向传播

神经网络从输入到输出的计算过程叫做前向传播(forward propagation)或前向计算. 该过程是数据张量(tensor)从逐层流动(flow)直到得到输出层的过程, 输入数据流经每个层直到得到输出并且计算误差.

$$\mathscr{L} = g(f_\theta(x), y)$$

其中 g() 为误差函数, $f_\theta()$ 为利用 $\theta$ 参数化的神经网络模型. $\mathscr{L}$ 成为误差(error) 或者 损失(loss)

通过在 $\mathbb{D}^{train}$ 训练得到一组 $\theta$ 使得 误差 $\mathscr{L}$ 最小

$$\theta^{*}=argmin \ g(f_\theta(x)),\ x \in \mathbb{D}^{train}$$

一般采用误差反向传播(backward propagation)求解 $\theta$, 并利用梯度下降(gradient descent)更新迭代参数.

$$\theta^{‘}=\theta-\eta\nabla_{\theta}\mathscr{L}$$

$\eta$ 为学习效率

激活函数

区别于 阶跃函数符号函数 不可导, 下面的都是平滑可导的, 适合梯度下降算法.

Sigmoid 函数

也成为 Logistic 函数

$$Sigmoid(x) \stackrel{\Delta}{=} \frac{1}{1+e^{-x}}$$

将 $x \in R$ 映射到 $x \in (0,1)$, 可以表示概率, 信号强度等.

1
2
x = tf.linspace(-6,6,10)
y = tf.nn.sigmoid(x)

ReLU 函数

ReLU(REctified Linear Unit, 修正线性单元). Sigmoid 函数在网络层数较高的时候, 容易出现梯度近似于 0, 训练容易出现不收敛或者停滞不前的状况. ReLU 则应用的较为广泛.

$$ReLU(x) \stackrel{\Delta}{=} max(0,x)$$

模拟人脑单侧抑制, 相对宽松的兴奋边界等特性.

1
tf.nn.relu(x)

LeakyReLU 函数

在 ReLU 函数 $x < 0$ 时导数值恒为 0, 也可能造成梯度函数弥漫现象.

$$
LeakyReLU(x)
\stackrel{\Delta}{=}
\begin{cases}
x, x \geq 0 \\
\alpha x, x \lt 0
\end{cases}
$$

1
tf.nn.leaky_leru(x, alpha=0.1)

Tanh 函数

将 $x \in R$ 映射到 (-1,1) 区间内.

$$tanh(x) = \frac{e^x-e^{-x}}{e^x+e^{-x}} = 2 sigmoid(2x) - 1$$

函数可以根据 sigmoid 函数平移得来.

误差计算

均方差, 交叉熵, KL 散度, Hinge Loss 函数等

均方差(mean squared error, 简称 MSE) 误差函数

将输出向量与真实向量个射到笛卡尔坐标系的两点上, 通过计算欧氏距离来衡量两个向量直接的差距. 常用于回归问题.

$$MSE(y, \sigma) \stackrel{\Delta}{=} \frac{1}{d_{out}} \sum_{i=1}^{d_{out}} (y_i - o_i)^2$$

MSE 值总是大于 0, 当 MSE 趋近于 0 的时候, 输出值趋近于真实值.

1
2
3
4
5
6
7
8
9
10
11
o = rf.random.normal([2, 10])
y = tf.constant([1,3])
y_onehot = tf.one_hot(y, depth=10)

# MSE 函数返回的是每个样本的均方差, 整体均方差需要再进行 reduce_mean 计算.
loss = tf.keras.losses.MSE(y_onehot, o)
loss_mean = tf.reduce_mean(loss)

# 使用 keras 简写
mse = keras.losses.MeanSquaredError()
loss = mse(y_onehot, o)

KL 散度

用于衡量两个分布直接距离的指标.

$$D_{KL}(p||q)=\sum_{i}p(i)log(\frac{p(i)}{q(i)})$$

当 p=q 时 $D_{KL}(p||q)$ 最小为 0, 当 p 与 q 之间的差距越大, $D_{KL}(p||q)$ 越大.

交叉熵

熵(Entropy) 来源于信息学, 也称为信息熵, 香农熵. 熵越大代表不确定性越大, 信息量越大. 熵为 0 表示确定.

某个分布 $P(i)$ 的

$$H(P) \stackrel{\Delta}{=} - \sum_{i}P(i)log_2P(i)$$

交叉熵(Cross Entropy) 衡量两个分布之间的距离.

$$H(p||q) \stackrel{\Delta}{=} - \sum_{i}p(i)log_2{q(i)} = H(p) + D_{KL}(p||q)$$

其中 $D_{KL}(p||q)$ 为 KL 散度.

当处理分类问题, y 的编码分布 p 采用 OneHot 编码 $y$ 时, $H(p) = 0$, 此时

$$H(p||q) = D_{KL}(p||q) = \sum_{j}y(j)log(\frac{y(j)}{o(j)}) = y_ilog{\frac{y_i}{o_i}} + \sum_{j \neq i}{y_jlog{\frac{y_j}{o_j}}}$$

退化到 $y$ 与 $o$ 之间的 KL 散度. 其中 i 为 OneHot 编码为 1 的编号, 有 $y_i = 1$ 且 $y_j = 0 (j \neq i)$

经整理

$$H(p||q) = 1log{\frac{1}{o_i}} + \sum_{j \neq i}{0log{\frac{0}{o_j}}} = -log{o_i}$$

可以看到 loss 误差 $\mathscr{L}$ 仅与类别 $i$ 上的概率 $o_i$ 有关. 概率 $o_i$ 越大 $H(p||q)$ 越小, 对应类别上概率为 1 交叉熵 $H(p||q)$ 得到最小值为 0, 此时 $o$ 与 $y$ 完全一致 神经网络取得最优状态.

因此最小化交叉熵的过程, 即为最大化正确预测概率的过程.

常见的神经网络类型

全连接网络是最基本类型, 计算流程和梯度求导方便, 但是处理较大特征长度的数据, 全连接层参数多, 造成网络参数巨大, 训练困难.

  • 卷积神经网络(Convolutional Neural Network, CNN): 局部相关性和权值共享, 应用于图片视频的计算机视觉领域, 等具有规则空间时间结构的数据.
  • 循环神经网络(Recurrent Neural Network, RNN): 解决缺乏内存和处理不定长序列信号, 应用于文本数据等序列信号数据.
  • 注意力(机制)网络(Attention Mechanism): 自然语言处理中 RNN 训练不稳定, 难并行化. 应用于图片生成和自然语言处理.
  • 图卷积神经网络(Graph Convolution Network, GCN): 社交网络, 通信网络, 蛋白质分子结构等不规则空间拓扑结构数据.
Donate - Support to make this site better.
捐助 - 支持我让我做得更好.