Fork me on GitHub

TF-char6-神经网络

Char6-神经网络neural networks

本章中主要讲解的内容包含:

  • 神经模型的简介
  • 感知机模型
  • 全连接网络
  • 神经网络介绍
  • 常见的激活函数
  • 输出层设计方案
  • 误差类型
  • 神经网络类型

机器学习的目的是找到一组良好的参数$\theta$,使得其表示的数学模型能够很好地从训练数据集中学到映射关系$f_{\theta}$

神经元模型

MP-神经模型

神经网络是由具有自适应性的简单单元组成的并行互连的网络。下图是经典的MP-神经元模型。

  • 当前神经元接收来自$n$个其他的神经元传递过来的输入信号$x_i$
  • 这些信号带着自己的连接权重$w_i$一起过来
  • 当前神经元的总输入:$\sum^n_{i=1}x_iw_i$
  • 将神经元的总输入和阈值$\theta$进行比较,再通过激活函数进行处理并输出

$$
y = f(\sum^n_{i=1}w_ix_i-\theta)
$$

神经网络就是包含了多个参数的数学模型,这个模型就是若干个参数。
$$
y_i = f(\sum_{i=1}w_ix_i-\theta _i)
$$

感知机

感知机简介

简单的两层神经网络,感知机是线性分类判别模型。

  • 输入层接收来自外界的出入信号$x=[x_1,…,x_n]$,传递给输出层
  • 输入信号有自己的权值$[w_1,…,w_n]$
  • 输出层是MP-神经元模型(也叫阈值逻辑单元)
  • 感知机还有自己的偏置$b$

$$
z=w_1x_1+w_2x_2+…+w_nx_n+b
$$

上式的向量形式为
$$
z=w^T \cdot x+b
$$
上面的$z$表示感知机的净活性值,加上激活函数之后变成活性值:
$$
a=\sigma(z)=\sigma(w^T \cdot x+b)
$$
激活函数可以是阶跃函数,也可以是符号函数;一般选用的符号函数$sign(x)$

感知机模型

感知机模型就是上文中的
$$
sign(w^Tx+b)=0
$$

为了方便,直接将模型改成:
$$
sign(w\cdot x+b)=0
$$
$wx+b$是一个n维空间中的超平面S,其中w是超平面的法向量,b是超平面的截距,这个超平面将特征空间划分成两部分,位于两部分的点分别被分为正负两类,所以超平面S称为分离超平面。

对于二分类的正确分类有:

  • 正例输出$y_i = +1, w\cdot x_i+b>0$
  • 负例输出$y_i = -1, w\cdot x_i+b<0$

那么,输入空间的某个点$x_0$到$S$的距离为
$$
\frac{1}{||w||}|w\cdot x_0+b|
$$
$|wx+b|$叫函数间隔,除模长之后叫几何间隔,几何间隔可以认为是物理意义上的实际长度。

对于误分类点有:

  • 输出$y_i = -1, w\cdot x_i+b>0$
  • 输出$y_i = +1, w\cdot x_i+b<0$

那么总有:
$$
-y_i(w\cdot x_i+b) > 0
$$

因此误分类点$x_i$到超平面的距离总是:
$$
-\frac{1}{||w||}y_i(w\cdot x_i+b) > 0
$$
全部的误分类点到超平面的距离是
$$
-\frac{1}{||w||}\sum_{x_i\in M}y_i(w\cdot x_i+b) > 0
$$
不考虑前面的系数,得到感知机$sign(w\cdot x)$的损失函数
$$
L(w,b)=-\sum_{x_i \in M}y_i(w \cdot x_i+b)
$$

感知机算法

1
2
3
4
5
6
7
8
9
1. 初始化参数w_0,b_0
2. 循环过程:
3. 在训练集中选取数据(x_i,y_i)
4. 计算感知机的输出a=sign(w^Tx_i+b)
5. 如果输出a和真实值y_i不等:
6. 更新w
7. 更新b
8.转至步骤2,直至训练集中没有误分类点
9. 输出w,b

过程解释:如果一个误分类点在超平面的一侧,调整两个参数,使得超平面向着该误分类点移动,以此来减少误分类点的和分离超平面的距离,直至超平面将该点正确分类。

全连接层

感知机模型的不可导限制了它的潜力,全连接层将不连续的阶跃函数换成了平滑连续的激活函数,通过堆叠多层网络实现。

每个输出节点和全部的输出相连接的网络层称之为全连接层

下图中3个输出,2个神经元,2个输出节点。
$$
o_1=\sigma(w_{11}*x_1+…+w_{31}*x_3+b_1 \o_2=\sigma(w_{12}*x_1+…+w_{32}*x_3+b_2)
$$

通过矩阵表示上述网络层的运算:
$$
O=X@W+b
$$

  • 输入$x$的$shape$为$[b,d_{in}]$,b是样本数量,$d_{in}$是输入节点数
  • 权值矩阵W,其$shape$为$[d_{in}, d_{out}]$,其中$d_{out}$是输出节点数
  • 偏置b的$shape$是$d_{out}$

如果是2个样本参与运算,$𝒙^1 = [𝑥^1_1, 𝑥^1_2, 𝑥1_3],𝒙2 = [𝑥_1^2, 𝑥^2_2, 𝑥^2_3]$

输出矩阵O的$shape$为$[b,d_{out}]$

张量实现

定义好权值张量和偏置张量,利用批量矩阵相乘函数`tf.matmul()``

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"""
输入X矩阵为𝑏=2个样本(2行,2个样本)
每个样本的输入特征长度为𝑑𝑖𝑛 = 784(784个属性特征)
输出节点数为𝑑𝑜𝑢𝑡 = 256
定义权值矩阵W的shape为[784,256],并采用正态分布初始化W;
偏置向量b的shape定义为[256]
在计算完X@W后相加即可,最终全连接层的输出O的shape为[2,256],即2个样本的特征,每个特征长度为 256。
"""

x = tf.random.normal([2,784])
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros([256]))
o1 = tf.matmul(x, w1) + b1
o1 = tf.nn.relu(o1)

层方式

通过layers.Dense(units, activation)

1
2
3
4
5
6
7
8
9
10
import tensorflow as tf
x = tf.random.normal([4, 28*28])
from tf.keras import layers
# 创建全连接层,指定输出节点数和激活函数
fc = layers.Denss(512, activation=tf.nn.relu) # 激活函数也可以不指定
h1 = fc(x) # 通过fc完成一次全连接的计算
fc.kernal # 获取权值矩阵
fc.bias # 获取Dense类的偏置
fc.trainable_variables # 返回的是待优化参数的列表
fc.variables # 返回所有的参数列表

神经网络

保证前一层的输出节点数与当前层的输入节点数匹配,即可堆叠出任意层数的网络。

张量实现

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
# 定义隐藏层1的张量
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros([256]))

# 定义隐藏层2的张量
w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))
b2 = tf.Variable(tf.zeros([128]))

# 定义隐藏层3的张量
w3 = tf.Variable(tf.random.truncated_normal([12864], stddev=0.1))
b3 = tf.Variable(tf.zeros([64]))

# 输出层张量
w4 = tf.Variable(tf.random.truncated_normal([64,10], stddev=0.1))
b4 = tf.Variable(tf.zeros([10]))

"""
计算时,只需要按照网络层的顺序,将上一层的输出送入当前层的输入即可,重复直至 最后一层,将输出层的输出作为网络的输出:
"""

with tf.GradienTape() as tape: # 必须将前向计算过程放入tf.GradienTape()环境中,利用 GradientTape 对象的 gradient()方法自动求解参数的梯 度,并利用 optimizers 对象更新参数
# x: [b, 28*28]
# 隐藏层 1 前向计算,[b, 28*28] => [b, 256]
h1 = x@w1 + tf.broadcast_to(b1, [x.shape[0],256])
h1 = tf.nn.relu(h1)

h2 = h1@w2 + b2
h2 = tf.nn.relu(h2)

h3 = h2@w3 + b3
h3 = tf.nn.relu(h3)

# 输出层前向计算,[b, 64] => [b, 10]
h4 = h3@w4 + b4

层方式

通过layer.Dense类来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 方式1:将每个网络分别建立出来,并且指定激活函数
x = tf.random.normal([4,28*28])
fc1 = layers.Dense(256, activation=tf.nn.relu)
fc2 = layers.Dense(128, activation=tf.nn.relu)
fc3 = layers.Dense(64, activation=tf.nn.relu)
fc4 = layers.Dense(10, activation=None)

# 依次通过各个层
x = tf.random.normal([4,28*28])
h1 = fc1(x)
h2 = fc2(h1)
h3 = fc3(h2)
h4 = fc4(h3) # 通过输出层得到网络输出

# 通过Sequential容器来进行封装
x = tf.random.normal([4,28*28])
model = layers.Sequential({
layers.Dense(256, activation=tf.nn.relu), # 创建隐藏层 1
layers.Dense(128, activation=tf.nn.relu) , # 创建隐藏层 2
layers.Dense(64, activation=tf.nn.relu) , # 创建隐藏层 3
layers.Dense(10, activation=None) , # 创建输出层
})

out = model(x)

激活函数

Sigmoid

Sigmoid 函数也叫 Logistic 函数,tf.nn.sigmoid(x)实现
$$
Sigmoid := \frac{1}{1+e^{-x}}
$$
在输入值较大或者较小的时候都是很容易出现梯度弥散现象。

ReLU

  • 单边抑制
  • 相对宽松的边界
  • 通过tf.nn.relu实现

LeakyReLU

ReLU 函数在𝑥 < 0时梯度值恒为 0,也可能会造成梯度弥散现象,为了克服这个问 题,LeakyReLU函数被提出。通过tf.nn.leaky_relu来实现

  • $p=0$,上述函数退化为$ReLUctant$
  • $p \neq 0$ ,当$x<0$能够获得较小的梯度值$p$

Tanh

Tanh函数能够将$x\in R$的输入压缩到[-1,1]之间,定义为
$$
\begin{align}tanh(x)& = \frac{ex-e{-x}}{ex+e{-x}} \& = 2*sigmoid(2x) - 1\end{align}
$$
tanh 激活函数可通过 Sigmoid 函数缩放平移后实现。通过tf.nn.tanh()实现

输出层设计

四种设计

根据输出值的区间来进行分类:

  • $o \in R^d$输出属于整个实数区间,如正弦函数曲线预测、年龄的预测、股票的走势预测等
  • $o \in [0,1]$输出值落在[0,1]之间,如图片的像素归一化到[0,1]之间
  • $o \in [0,1], \sum_io_i=1$,输出值落在[0,1]之前,且所有的的输出值之和为1。通过添加Softmax函数来实现功能
  • $o \in [-1,1]$输出值在[-1,1],使用tf.tanh(x)来实现

softmax函数

Dense层类似,Softmax函数还可以作为网络层来使用。通过类layers.Softmax(axis=-1)

输入值较大,会出现溢出现象。同时实现Softmax函数和交叉熵损失函数,接口为tf.keras.losses.categorical_crossentropy(y_true, y_pred, from_logits=False)

  • y_true 代表了 one-hot 编码后的真实标签
  • y_pred 表示网络的预测值
    • from_logits = True 时, y_pred 表示须为未经过 Softmax函数的变量 z
    • from_logits = False 时,y_pred 表示为经过Softmax 函数的输出。
1
2
3
4
5
6
7
8
9
10
z = tf.random.normao([2,10])
y_onehot = tf.constant([1,3])
y_onehot = tf.one_hot(y_onehot, depth=10)
# 输出层未使用Softmax函数,设置成True
loss = keras.losses.categorical_crossentropy(y_onehot,z,from_logits=True)
loss = tf.reduce_mean(loss)

# 可以利用 losses.CategoricalCrossentropy(from_logits)类方式同时实现 Softmax与交叉熵损失函数的计算
ctiteon = keras.losses.CategoricalCrossentropy(from_logits=True)
loss = criteon(y_onehot, z)

误差类型

均方差

均方差MSE,mean squared error:将输出向量和真实向量映射到笛卡尔坐标系上的两个点,计算两点之间的欧式距离(平方)
$$
MSE=\frac{1}{d_{out}}\sum{d_{out}}_{i=1}(y_i-o_i)2
$$

  • MSE的值总是大于等于0;当其为0表示输出值等于真实标签
  • 主要用于回归问题
1
2
3
4
5
6
7
8
9
o = tf.random.normal([2,10]) # 构造网络
y_onehot = tf.constant([1,3]) # 真实输出
y_onehot = tf.one_hot(y_onehot, depth=10)
loss = keras.losses.MSE(y_onehot, o) # 计算均方差,返回的是每个样本的均方差
loss = tf.reduce_mean(loss) # 计算batch均方差

# 通过类的方式实现
criteon = keras.losses.MeanSquaredError()
loss = criteon(y_onehot, o)

交叉熵

熵是用来衡量信息的不确定性。熵越大,代表不确定越大,信息量越大。

假设数据集$D$中第$k$类样本占的比例是$p_k(k=1,2,…,K)$,则$D$的信息熵定义为:
$$
Ent(D)=-\sum^K_{k=1} p_klog_2{p_k}
$$
比如:某个事件发生的结果有3中情形,出现的概率分别是:

结果1 结果2 结果3
$\frac{1}{3}$ $\frac{1}{2}$ $\frac{1}{6}$

信息熵的计算如下:

$$
Ent=-(\frac{1}{3}log_2\frac{1}{3}+\frac{1}{2}log_2\frac{1}{2}+\frac{1}{6}log_2\frac{1}{6})
$$
交叉熵 Cross Entropy 的定义为
$$
H(p,q)= -\sum_{i=0}p(i)log_2q(i)
$$
通过变换,交叉熵可以分解为 p 的熵𝐻(𝑝)与 p,q 的 KL 散度(Kullback-Leibler Divergence)的和

$$
H(p,q)=H§+D_{KL}(p|q)
$$

其中 KL 定义为

$$
𝐷_{𝐾𝐿}(𝑝|𝑞) = ∑_{x\in X} 𝑝(𝑥)𝑙𝑜𝑔 \frac{𝑝(𝑥)}{q(x)}
$$

KL散度是衡量连个分布之间距离的指标。交叉熵和KL散度都是不对称的。

$$
H(p,q) \neq H(q,p) \D_{KL} (p|q) \neq D_{KL}(q|p)
$$

神经网络类型

  • 卷积神经网络CNN
  • 循环神经网络RNN
  • 注意力网络Transformer
  • 图神经网络GCN

本文标题:TF-char6-神经网络

发布时间:2019年12月02日 - 23:12

原始链接:http://www.renpeter.cn/2019/12/02/TF-char6-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea