《TensorFlow 2.0深度学习算法实战教材》学习笔记(五、神经网络)

DL is essentially a new style of programming–“differentiable programming”–and the field istrying to work out the reusable constructs in this style. We have some: convolution, pooling,LSTM, GAN, VAE, memory units, routing units – Thomas G. Dietterich

机器学习的最终目的是找到一组良好的参数𝜃,使得𝜃表示的数学模型能够很好地从训练集中学到映射关系𝑓𝜃: 𝒙 → 𝒚, 𝒙, 𝒚 ∈ 𝔻𝑡𝑟𝑎𝑖𝑛,从而利用训练好的𝑓𝜃(𝒙), 𝒙 ∈ 𝔻𝑡𝑒𝑠𝑡去预测新样本。神经网络属于机器学习的一个研究分支,它特指利用多个神经元去参数化映射函数𝑓𝜃的模型。

神经网络

使用张量方式和层方式实现如下神经网络:
在这里插入图片描述

张量方式实现

对于多层神经网络,以图 6.6 网络结构为例,分别定义各层的权值矩阵W 和偏置向量b 如下:

# 隐藏层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([128, 64], 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.GradientTape() as tape: # 梯度记录器
	# 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)
	# 隐藏层2 前向计算,[b, 256] => [b, 128]
	h2 = h1@w2 + b2
	h2 = tf.nn.relu(h2)
	# 隐藏层3 前向计算,[b, 128] => [b, 64]
	h3 = h2@w3 + b3
	h3 = tf.nn.relu(h3)
	# 输出层前向计算,[b, 64] => [b, 10]
	h4 = h3@w4 + b4

在使用 TensorFlow 自动求导功能计算梯度时,需要将前向计算过程放置在
tf.GradientTape()环境中,从而利用GradientTape 对象的gradient()方法自动求解参数的梯度,并利用optimizers 对象更新参数。

层方式实现

通过层方式实现起来更加简洁,首先新建各个网络层,并指定各层的激活函数类型:

fc1 = layers.Dense(256, activation=tf.nn.relu) # 隐藏层1
fc2 = layers.Dense(128, activation=tf.nn.relu) # 隐藏层2
fc3 = layers.Dense(64, activation=tf.nn.relu) # 隐藏层3
fc4 = layers.Dense(10, activation=None) # 输出层

在前向计算时,依序通过各个网络层即可:

x = tf.random.normal([4,28*28])
h1 = fc1(x) # 通过隐藏层1 得到输出
h2 = fc2(h1) # 通过隐藏层2 得到输出
h3 = fc3(h2) # 通过隐藏层3 得到输出
h4 = fc4(h3) # 通过输出层得到网络输出

对于这种数据依次向前传播的网络,也可以通过Sequential 容器封装成一个网络大类对象,调用大类的前向计算函数即可完成所有层的前向计算:

# 通过Sequential 容器封装为一个网络类
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) # 前向计算得到输出

优化目标

我们把神经网络从输入到输出的计算过程叫做前向传播(Forward propagation)。神经网络的前向传播过程,也是数据张量(Tensor)从第一层流动(Flow)至输出层的过程:从输入数据开始,途径每个隐藏层,直至得到输出并计算误差,这也是TensorFlow 框架名字意义所在。
在这里插入图片描述
利用误差反向传播算法进行反向计算的过程也叫反向传播(Backwardpropagation)。

激活函数

Sigmoid

Sigmoid 函数也叫Logistic 函数,定义为
在这里插入图片描述

它的一个优良特性就是能够把𝑥 ∈ 𝑅的输入“压缩”到𝑥 ∈ [0,1]区间,这个区间的数值在机器学习常用来表示以下意义:

❑ 概率分布 [0,1]区间的输出和概率的分布范围契合,可以通过Sigmoid 函数将输出转译为概率输出
❑ 信号强度 一般可以将0~1 理解为某种信号的强度,如像素的颜色强度,1 代表当前通道颜色最强,0 代表当前通道无颜色;抑或代表门控值(Gate)的强度,1 代表当前门控全部开放,0 代表门控关闭

Sigmoid 函数连续可导,如图 6.8 所示,相对于阶跃函数,可以直接利用梯度下降算法优化网络参数,应用的非常广泛。
在这里插入图片描述
在 TensorFlow 中,可以通过tf.nn.sigmoid 实现Sigmoid 函数:

In [7]:x = tf.linspace(-6.,6.,10)
In [8]:tf.nn.sigmoid(x)

Sigmoid 函数在输入值较大或较小时容易出现梯度值接近于0 的现象,称为梯度弥散现象,网络参数长时间得不到更新,很难训练较深层次的网络模型。

ReLU

ReLU(REctified Linear Unit,修正线性单元)函数定义为:
在这里插入图片描述
函数曲线如图 6.9 所示,可以看到,ReLU 对小于0 的值全部抑制为0;对于正数则直接输出,这种单边抑制特性来源于生物学。
在这里插入图片描述
除了可以使用函数式接口tf.nn.relu 实现ReLU 函数外,还可以像Dense 层一样将
ReLU 函数作为一个网络层添加到网络中,对应的类为layers.ReLU()类。一般来说,激活函数类并不是主要的网络运算层,不计入网络的层数。

ReLU 函数的设计源自神经科学,计算十分简单,同时有着优良的梯度特性,在大量的深度学习应用中被验证非常有效,是应用最广泛的激活函数之一。

LeakyReLU

ReLU 函数在𝑥 < 0时梯度值恒为0,也可能会造成梯度弥散现象,为了克服这个问题,LeakyReLU 函数被提出,如图 6.11 所示,LeakyReLU 表达式为:
在这里插入图片描述
其中𝑝为用户自行设置的某较小数值的超参数,如0.02 等。当𝑝 = 0时,LeayReLU 函数退化为ReLU 函数;当𝑝 ≠ 0时,𝑥 < 0能够获得较小的梯度值𝑝,从而避免出现梯度弥散现象。

在这里插入图片描述
在 TensorFlow 中,可以通过tf.nn.leaky_relu 实现LeakyReLU 函数:

In [10]:tf.nn.leaky_relu(x, alpha=0.1) # 通过LeakyReLU 激活函数

Tanh

Tanh 函数能够将𝑥 ∈ 𝑅的输入“压缩”到[−1,1]区间,定义为:
在这里插入图片描述
可以看到tanh 激活函数可通过Sigmoid 函数缩放平移后实现,函数曲线如图 6.12 所示。
在这里插入图片描述
在 TensorFlow 中,可以通过tf.nn.tanh 实现tanh 函数:

In [11]:tf.nn.tanh(x) # 通过tanh 激活函数

输出层设计

我们来特别地讨论网络的最后一层的设计,它除了和所有的隐藏层一样,完成维度变换、特征提取的功能,还作为输出层使用,需要根据具体的任务场景来决定是否使用激活函数,以及使用什么类型的激活函数。

我们将根据输出值的区间范围来分类讨论。常见的几种输出类型包括:

❑ 𝒐 ∈ 𝑅𝑑 输出属于整个实数空间,或者某段普通的实数空间,比如函数值趋势的预测,年龄的预测问题等

❑ 𝒐 ∈ [0,1] 输出值特别地落在[0, 1]的区间,如图片生成,图片像素值一般用[0, 1]表示;或者二分类问题的概率,如硬币正反面的概率预测问题

❑ 𝒐 ∈ [0, 1], 𝑖 𝑜𝑖 = 1 输出值落在[0, 1]的区间,并且所有输出值之和为 1,常见的如多分类问题,如MNIST 手写数字图片识别,图片属于10 个类别的概率之和为1

❑ 𝒐 ∈ [−1, 1] 输出值在[-1, 1]之间

普通实数空间

这一类问题比较普遍,像正弦函数曲线预测、年龄的预测、股票走势的预测等都属于整个或者部分连续的实数空间,输出层可以不加激活函数。误差的计算直接基于最后一层的输出𝒐和真实值y 进行计算,如采用均方差误差函数度量输出值𝒐与真实值𝒚之间的距离:

ℒ = 𝑔(𝒐, 𝒚)

其中𝑔代表了某个具体的误差计算函数。

[0, 1]区间

输出值属于[0, 1]区间也比较常见,比如图片的生成,二分类问题等。在机器学习中,一般会将图片的像素值归一化到[0,1]区间,如果直接使用输出层的值,像素的值范围会分布在整个实数空间。为了让像素的值范围映射到[0,1]的有效实数空间,需要在输出层后添加某个合适的激活函数𝜎,其中Sigmoid 函数刚好具有此功能。

对于二分类问题,如硬币的正反面的预测,输出层可以只需要一个节点,表示某个事件A 发生的概率P(𝐴|𝑥)。

P(𝐴|𝑥) + P(𝑛𝑜𝑡 𝐴|𝑥) = 1

[0,1]区间,和为1

输出值𝑜𝑖 ∈ [0,1],所有输出值之和为1,这种设定以多分类问题最为常见.如何实现此约束逻辑呢?可以通过在输出层添加Softmax 函数实现。Softmax 函数定义
为:
在这里插入图片描述
Softmax 函数不仅可以将输出值映射到[0,1]区间,还满足所有的输出值之和为1 的特性。

与Dense 层类似,Softmax 函数也可以作为网络层类使用,通过类layers.Softmax(axis=-1)可以方便添加Softmax 层,其中axis 参数指定需要进行计算的维度。

在 Softmax 函数的数值计算过程中,容易因输入值偏大发生数值溢出现象;在计算交叉熵时,也会出现数值溢出的问题。为了数值计算的稳定性,TensorFlow 中提供了一个统一的接口,将Softmax 与交叉熵损失函数同时实现,同时也处理了数值不稳定的异常,一般推荐使用,避免单独使用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 函数的输出。

In [13]:
z = tf.random.normal([2,10]) # 构造输出层的输出
y_onehot = tf.constant([1,3]) # 构造真实值
y_onehot = tf.one_hot(y_onehot, depth=10) # one-hot 编码
# 输出层未使用Softmax 函数,故from_logits 设置为True
loss = keras.losses.categorical_crossentropy(y_onehot,z,from_logits=True)
loss = tf.reduce_mean(loss) # 计算平均交叉熵损失
loss
Out[13]:
<tf.Tensor: id=210, shape=(), dtype=float32, numpy= 2.4201946>

也可以利用losses.CategoricalCrossentropy(from_logits)类方式同时实现Softmax 与交叉熵损失函数的计算

In [14]: # 创建Softmax 与交叉熵计算类,输出层的输出z 未使用softmax
criteon = keras.losses.CategoricalCrossentropy(from_logits=True)
loss = criteon(y_onehot,z) # 计算损失
loss
Out[14]:
<tf.Tensor: id=258, shape=(), dtype=float32, numpy= 2.4201946>

[-1, 1]

如果希望输出值的范围分布在[−1, 1],可以简单地使用tanh 激活函数

In [15]:
x = tf.linspace(-6.,6.,10)
tf.tanh(x) # tanh 激活函数
Out[15]:
<tf.Tensor: id=264, shape=(10,), dtype=float32, numpy=
array([-0.9999877 , -0.99982315, -0.997458 , -0.9640276 , -0.58278286,
0.5827831 , 0.9640276 , 0.997458 , 0.99982315, 0.9999877 ],
dtype=float32)>

误差计算

常见的误差计算函数有均方差、交叉熵、KL 散度、Hinge Loss 函数等,其中均方差函数和交叉熵函数在深度学习中比较常见,均方差主要用于回归问题,交叉熵主要用于分类问题。

均方差

均方差误差(Mean Squared Error, MSE)函数把输出向量和真实向量映射到笛卡尔坐标系的两个点上,通过计算这两个点之间的欧式距离(准确地说是欧式距离的平方)来衡量两个向量之间的差距:
在这里插入图片描述
MSE 误差函数的值总是大于等于0,当MSE 函数达到最小值0 时,输出等于真实标签,此时神经网络的参数达到最优状态。

均方差广泛应用在回归问题中,在分类问题中也可以应用均方差误差。在TensorFlow中,可以通过函数方式或层方式实现MSE 误差计算:

In [16]:
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
Out[16]:
<tf.Tensor: id=27, shape=(2,), dtype=float32, numpy=array([0.779179 ,
1.6585705], dtype=float32)>

TensorFlow MSE 函数返回的是每个样本的均方差,需要在样本数量上再次平均来获得batch 的均方差:

In [17]:
loss = tf.reduce_mean(loss) # 计算batch 均方差
loss
Out[17]:
<tf.Tensor: id=30, shape=(), dtype=float32, numpy=1.2188747>

也可以通过层方式实现,对应的类为keras.losses.MeanSquaredError():

In [18]: # 创建MSE 类
criteon = keras.losses.MeanSquaredError()
loss = criteon(y_onehot,o) # 计算batch 均方差
loss
Out[18]:
<tf.Tensor: id=54, shape=(), dtype=float32, numpy=1.2188747>

交叉熵

熵在信息学科中也叫信息熵,或者香农熵。熵越大,代表不确定性越大,信息量也就越大。某个分布 (𝑖)的熵定义为
在这里插入图片描述
在介绍完熵的概念后,我们基于熵引出交叉熵(Cross Entropy)的定义:
在这里插入图片描述
交叉熵可以很好地衡量2 个分布之间的差别.

最小化交叉熵的过程也是最大化正确类别的预测概率的过程。

神经网络类型

卷积神经网络

通过利用局部相关性和权值共享的思想,Yann Lecun 在1986 年提出了卷积神经网络(Convolutional NeuralNetwork,CNN)。其中比较流行的模型有用于图片
分类的AlexNet,VGG,GoogLeNet,ResNet,DenseNet 等,用于目标识别的RCNN,FastRCNN,Faster RCNN,Mask RCNN 等。

循环神经网络

除了具有空间结构的图片、视频等数据之外,序列信号也是非常常见的一种数据类
型,其中一个最具代表性的序列信号就是文本数据。如何处理并理解文本数据是自然语言处理的一个核心问题。卷积神经网络由于缺乏Memory 机制和处理不定长序列信号的能力,并不擅长自然语言处理任务。循环神经网络(Recurrent Neural Network,RNN)在Yoshua Bengio, Jürgen Schmidhuber 等人的持续研究下,被证明非常擅长处理序列信号。

注意力(机制)网络

RNN 并不是自然语言处理的最终解决方案,近年来随着注意力机制(Attention)的提出,克服了RNN 训练不稳定、难以并行化等缺陷,在自然语言处理和图片生成等领域中逐渐崭露头角。注意力机制最初在图片分类任务上提出,但逐渐开始侵蚀NLP 各大任务。2017 年,Google 提出了第一个利用纯注意力机制实现的网络模型Transformer,随后基于Transformer 相继提出了一系列的用于机器翻译的注意力网络模型,如GPT,BERT,GPT-2等。在其他领域,基于注意力机制,尤其是自注意力(Self-attention)机制构建的网络也取得了不错的效果,比如基于自注意力机制的BigGAN 模型等。

图神经网络

图片、文本等数据具有规则的空间、时间结构,称之为Euclidean Data。卷积神经网络和循环神经网络被证明非常擅长处理这种类型的数据。而像类似于社交网络、通信网络,蛋白质分子结构等一系列不规则的空间拓扑结构的数据,它们显得力不从心。2016 年,Thomas Kipf 等人基于前人在一阶近似的谱卷积算法上提出了图卷积网络(GraphConvolution Network,GCN)模型。GCN 算法实现简单,从空间一阶邻居信息聚合的角度也能直观理解,在半监督任务上取得了不错效果。随后,一系列的网络模型相继被提出,如GAT,EdgeConv,DeepGCN 等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汀桦坞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值