文章目录
前言
创作开始时间:2021年4月5日11:01:46
如题。主要是了解一些神经网络的基础知识。
正文
from __future__ import absolute_import, division, print_function
import tensorflow as tf
from tensorflow.keras import Model, layers
import numpy as np
# MNIST dataset parameters.
num_classes = 10 # total classes (0-9 digits).
num_features = 784 # data features (img shape: 28*28).
# Training parameters.
learning_rate = 0.1
training_steps = 2000
batch_size = 256
display_step = 100
# Network parameters.
n_hidden_1 = 128 # 1st layer number of neurons.
n_hidden_2 = 256 # 2nd layer number of neurons.
# Prepare MNIST data.
from tensorflow.keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# Convert to float32.
x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)
# Flatten images to 1-D vector of 784 features (28*28).
x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])
# Normalize images value from [0, 255] to [0, 1].
x_train, x_test = x_train / 255., x_test / 255.
# Use tf.data API to shuffle and batch data.
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)
# Create TF Model.
class NeuralNet(Model):
# Set layers.
def __init__(self):
super(NeuralNet, self).__init__()
# First fully-connected hidden layer.
self.fc1 = layers.Dense(n_hidden_1, activation=tf.nn.relu)
# First fully-connected hidden layer.
self.fc2 = layers.Dense(n_hidden_2, activation=tf.nn.relu)
# Second fully-connecter hidden layer.
self.out = layers.Dense(num_classes)
# Set forward pass.
def call(self, x, is_training=False):
x = self.fc1(x)
x = self.fc2(x)
x = self.out(x)
if not is_training:
# tf cross entropy expect logits without softmax, so only
# apply softmax when not training.
x = tf.nn.softmax(x)
return x
# Build neural network model.
neural_net = NeuralNet()
# Cross-Entropy Loss.
# Note that this will apply 'softmax' to the logits.
def cross_entropy_loss(x, y):
# Convert labels to int 64 for tf cross-entropy function.
y = tf.cast(y, tf.int64)
# Apply softmax to logits and compute cross-entropy.
loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=x)
# Average loss across the batch.
return tf.reduce_mean(loss)
# Accuracy metric.
def accuracy(y_pred, y_true):
# Predicted class is the index of highest score in prediction vector (i.e. argmax).
correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))
return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis=-1)
# Stochastic gradient descent optimizer.
optimizer = tf.optimizers.SGD(learning_rate)
# Optimization process.
def run_optimization(x, y):
# Wrap computation inside a GradientTape for automatic differentiation.
with tf.GradientTape() as g:
# Forward pass.
pred = neural_net(x, is_training=True)
# Compute loss.
loss = cross_entropy_loss(pred, y)
# Variables to update, i.e. trainable variables.
trainable_variables = neural_net.trainable_variables
# Compute gradients.
gradients = g.gradient(loss, trainable_variables)
# Update W and b following gradients.
optimizer.apply_gradients(zip(gradients, trainable_variables))
# Run training for the given number of steps.
for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):
# Run the optimization to update W and b values.
run_optimization(batch_x, batch_y)
if step % display_step == 0:
pred = neural_net(batch_x, is_training=True)
loss = cross_entropy_loss(pred, batch_y)
acc = accuracy(pred, batch_y)
print("step: %i, loss: %f, accuracy: %f" % (step, loss, acc))
# Test model on validation set.
pred = neural_net(x_test, is_training=False)
print("Test Accuracy: %f" % accuracy(pred, y_test))
# Visualize predictions.
import matplotlib.pyplot as plt
# Predict 5 images from validation set.
n_images = 1
test_images = x_test[:n_images]
predictions = neural_net(test_images)
# Display image and model prediction.
for i in range(n_images):
plt.imshow(np.reshape(test_images[i], [28, 28]), cmap='gray')
plt.show()
print("Model prediction: %i" % np.argmax(predictions.numpy()[i]))
1、tf.keras
mplementation of the Keras API meant to be a high-level API for TensorFlow.
class Model:
groups layers into an object with training and inference features.
2、tensorflow1.x到2.0的区别
The transition from TensorFlow 1.x to TensorFlow 2.0 is going to be a bit of a rocky one, at least to start, but with the right understanding, you’ll be able to navigate the migration with ease.
3、tensorflow.keras 和 keras的区别
Next, I’ll discuss the concept of a “computational backend” and how TensorFlow’s popularity enabled it to become Keras’ most prevalent backend, paving the way for Keras to be integrated into the tf.keras submodule of TensorFlow.
Once TensorFlow became the default backend for Keras, by definition, both TensorFlow and Keras usage grew together — you could not have Keras without TensorFlow, and if you installed Keras on your system, you were also installing TensorFlow.
keras以后不再维护了,大家都用tf.keras就行。
参考:
4、在放到神经网络作为输入之前,为什么要归一化图片?
- 方便学习数据分布(训练数据和测试数据需要归一化)
- 对每次训练的数据,保持一致。提升网络训练的速度。
5、prefetch函数
Creates a Dataset that prefetches elements from this dataset.
Most dataset input pipelines should end with a call to prefetch. This allows later elements to be prepared while the current element is being processed. This often improves latency and throughput, at the cost of using additional memory to store prefetched elements.
Note: Like other Dataset methods, prefetch operates on the elements of the input dataset. It has no concept of examples vs. batches. examples.prefetch(2) will prefetch two elements (2 examples), while examples.batch(20).prefetch(2) will prefetch 2 elements (2 batches, of 20 examples each).
6、tf.nn.relu
线性整流函数(Rectified Linear Unit, ReLU),又称修正线性单元。其定义如下图,在横坐标的右侧,ReLU函数为线性函数。在横坐标的右侧,ReLU函数为值为0。
因此,tf.nn.relu()函数的目的是,将输入小于0的值幅值为0,输入大于0的值不变。
- https://blog.csdn.net/tiaojingtao1293/article/details/81433066
7、keras.layers.Dense()
keras.layers.Dense()是定义网络层的基本方法,执行的操作是:output = activation(dot(input,kernel)+ bias。
其中activation是激活函数,kernel是权重矩阵,bias是偏向量。如果层输入大于2,在进行初始点积之前会将其展平。
参考:
- https://blog.csdn.net/brandday/article/details/98182238/
8、前馈神经网络
百度百科:
前馈神经网络(feedforward neural network,FNN),简称前馈网络,是人工神经网络的一种。前馈神经网络采用一种单向多层结构。其中每一层包含若干个神经元。在此种神经网络中,各神经元可以接收前一层神经元的信号,并产生输出到下一层。第0层叫输入层,最后一层叫输出层,其他中间层叫做隐含层(或隐藏层、隐层)。隐层可以是一层。也可以是多层 [1]
感知器网络
感知器(又叫感知机)是最简单的前馈网络,它主要用于模式分类,也可用在基于模式分类的学习控制和多模态控制中。感知器网络可分为单层感知器网络和多层感知器网络。
BP网络
BP网络是指连接权调整采用了反向传播(Back Propagation)学习算法的前馈网络。与感知器不同之处在于,BP网络的神经元变换函数采用了S形函数(Sigmoid函数),因此输出量是0~1之间的连续量,可实现从输入到输出的任意的非线性映射。
RBF网络
RBF网络是指隐含层神经元由RBF神经元组成的前馈网络。RBF神经元是指神经元的变换函数为RBF(Radial Basis Function,径向基函数)的神经元。典型的RBF网络由三层组成:一个输入层,一个或多个由RBF神经元组成的RBF层(隐含层),一个由线性神经元组成的输出层。
9、多层感知机(MLP)
参考:
- 一文理清深度学习前馈神经网络 https://www.cnblogs.com/samshare/p/11801806.html
而深度学习模型,类似的模型统称是叫 深度前馈网络(Deep Feedforward Network),其目标是拟合某个函数f,由于从输入到输出的过程中不存在与模型自身的反馈连接,因此被称为“前馈”。常见的深度前馈网络有:多层感知机、自编码器、限制玻尔兹曼机、卷积神经网络等等。
说起多层感知器(Multi-Later Perceptron),不得不先介绍下单层感知器(Single Layer Perceptron),它是最简单的神经网络,包含了输入层和输出层,没有所谓的中间层(隐含层)
我选择了0个隐含层,也就是我们介绍的单层感知机,对于可以线性可分的数据,效果还是可以的。如果我换成线性不可分的数据集,如下图,那么跑半天都跑不出个什么结果来。
这个时候就引入多层感知器,它相比单层感知器多了一个隐含层的东西,同样的数据集,我加入两层 隐含层,瞬间就可以被分类得很好。
9.1、深度神经网络的激活函数
感知机算法中包含了前向传播(FP)和反向传播(BP)算法,但在介绍它们之前,我们先来了解一下深度神经网络的激活函数。
为了解决非线性的分类或回归问题,我们的激活函数必须是非线性的函数,另外我们使用基于梯度的方式来训练模型,因此激活函数也必须是连续可导的。 @ 磐创 AI
常用的激活函数主要是:
- Sigmoid激活函数:Sigmoid激活函数在定义域上是单调递增的,越靠近两端变化越平缓,而这会导致我们在使用BP算法的时候出现梯度消失的问题。
- Tanh激活函数:同样的,tanh激活函数和sigmoid激活函数一样存在梯度消失的问题,但是tanh激活函数整体效果会优于Sigmoid激活函数。
Q:为什么Sigmoid和Tanh激活函数会出现梯度消失的现象?
A:两者在z很大(正无穷)或者很小(负无穷)的时候,其导函数都会趋近于0,造成梯度消失的现象。
- ReLU激活函数:ReLU激活函数的收敛速度要比上面两种要快得多,ReLU激活函数的X轴左侧值恒为0,使得网络具有一定的稀疏性,从而减少参数之间的依存关系,缓解了过拟合的情况,而且它的导函数有部分为常数1,因此不存在梯度消失的问题。但ReLU激活函数也有弊端,那就是会丢失一些特征信息。
- LReLU激活函数:上面可以看到LReLU激活函数的图像了,它和ReLU激活函数的区别在于当z<0时,其值不为0,而是一个斜率为a的线性函数(一般a会是一个十分小的正数),这样子即起到了单侧抑制,也不完全丢失负梯度信息。
9.2 深度神经网络的损失函数
损失函数(Loss Function)又被称为Cost Function,作用是用来表示预测值与真实值之间的误差,深度学习模型的训练是基于梯度的方法最小化Loss Function的过程,下面就介绍几种常见的损失函数。
均方误差(Mean Squared Error,MSE)是比较常用的损失函数。
交叉熵(Crocs Entropy)损失函数使用训练数据的预测值与真实值之间的交叉熵来作为损失函数
一般来说,MSE更适合输出值为连续值,并且最后一层不含Sigmoid或Softmax激活函数的神经网络;而交叉熵则适合二分类或者多分类的场景。
9.3 多层感知机的反向传播算法
在MLP中,输入信号通过各个网络层的隐节点产生输出的过程,我们称之为“前向传播“,而前向传播最终是产生一个标量损失函数。而反向传播算法(Backpropagation)则是将损失函数的信息沿着网络层向后传播用以计算梯度,达到优化网络参数的目的。
9.4 神经网络的训练技巧
- Data Augmentation(数据增广)
- Regularization(正则化)
- Model Ensemble(模型集成)
- Dropout:Dropout在深度学习网络训练中是十分常用的,指的是以一定的概率p随机丢弃一部分神经元节点,而这个“丢弃”只是临时的,是针对每一次小批量的训练数据而言,由于是随机丢弃,所以每一次的神经网络结构都会不一样,相当于每次迭代都是在训练不同结构的神经网络,有点像传统机器学习中的Bagging方法。
Dropout可以比较有效的缓解过拟合的发生,在一定程度上达到正则化的效果。
具体可参考:深度学习中Dropout原理解析 - 学习率
- 权重衰减系数
- Dropout比例的调参
- Batch Normalization,BN(批量归一化),可参考:什么是批标准化 (Batch Normalization)
9.5、深度卷积神经网络(CNN)
卷积神经网络(Convolutional Neural Network,CNN),它也是属于前馈神经网络的一种,其特点是每层的神经元节点只响应前一层局部区域范围内的神经元(全连接网络中每个神经元节点则是响应前一层的全部节点)。
一个深度卷积神经网络模型,一般由若干卷积层叠加若干全连接层组成,中间包含各种的非线性操作、池化操作。卷积运算主要用于处理网格结构的数据,因此CNN天生对图像数据的分析与处理有着优势,简单地来理解,那就是CNN是利用滤波器(Filter)将相邻像素之间的轮廓过滤出来。
- Convolution(卷积)
参考:
卷积神经网络之卷积计算、作用与思想 卷积是用来映射复杂函数的,多层卷积可以学到复杂的特征。 - Padding(填充),参考:https://blog.csdn.net/baidu_36161077/article/details/81165531
是对原始图像进行填充。从而对边缘信息的提取更加充分。
可以看到上图在原始图像的边缘用了像素填充,像素填充后的尺寸变为8×88×8,卷积核的大小为3×33×3,步长为1,经过卷积操作之后得到的feature map的尺寸是 (8−3+1)×(8−3+1)=6×6(8−3+1)×(8−3+1)=6×6,feature map的尺寸和原始输入图像的尺寸是一样的。这样就解决了feature map尺寸越来越小的问题。同时从图中可以看出,卷积核对边缘信息的处理不止处理了一次,对边缘信息的提取更加充分了。
- Stride(步长)
也就是Filter移动的步伐大小,上面的例子为1,其实可以由我们自己来指定,有点像是学习率。
- Depth(深度)
深度指的是图片的深度,一张6X6X3大小的图片经过3X3X3的Filter过滤后会得到一个4X4X1大小的图片,因此深度为1。我们也可以通过增加Filter的个数来增加深度
- Pooling(池化):减少冗余的运算。降维。增加鲁棒性。
因为滤波器在进行窗口移动的过程中会有很多冗余计算,效率很慢,池化操作的目的在于加速卷积操作,最常用的有Maxpooling
另参考:https://www.zhihu.com/question/49376084
卷积层由一组滤波器组成,滤波器可以视为二维数字矩阵。这是一个示例3x3滤波器
10、 深度神经网络的激活函数
参考:
- 深度学习中的激活函数导引 https://zhuanlan.zhihu.com/p/22142013
近年来,深度学习在计算机视觉领域取得了引人注目的成果,其中一个重要因素是激活函数的发展。新型激活函数ReLU克服了梯度消失,使得深度网络的直接监督式训练成为可能。本文将对激活函数的历史和近期进展进行总结和概括。
简单来说,神经元的输出(例如,全连接网络中就是输入向量与权重向量的内积再加上偏置项)经过激活函数处理后再作为输出。加拿大蒙特利尔大学的Bengio教授在 ICML 2016 的文章[1]中给出了激活函数的定义:激活函数是映射 h:R→R,且几乎处处可导。
神经网络中激活函数的主要作用是提供网络的非线性建模能力,如不特别说明,激活函数一般而言是非线性函数。假设一个示例神经网络中仅包含线性卷积和全连接运算,那么该网络仅能够表达线性映射,即便增加网络的深度也依旧还是线性映射,难以有效建模实际环境中非线性分布的数据。加入(非线性)激活函数之后,深度神经网络才具备了分层的非线性映射学习能力。因此,激活函数是深度神经网络中不可或缺的部分。
可以说,加入了激活函数,才让深度神经网络具备了分层的非线性映射学习能力。
从定义来看,几乎所有的连续可导函数都可以用作激活函数。但目前常见的多是分段线性和具有指数形状的非线性函数。下文将依次对它们进行总结。
小结
创作结束时间:2021年4月5日16:51:44