卷积神经网络(CNN)
卷积神经网络(Convolutional Neural Network, CNN):至少在网络中的一层使用卷积运算来代替一般矩阵运算的神经网络。结构类似人类或动物的视觉系统的人工神经网络,通常包含一个或多个卷积层(Convolutional Layer)、池化层(Pooling Layer)和全连接层(Fully-connected Layer)。
1、卷积运算
S
(
t
)
=
∫
x
(
a
)
w
(
x
−
a
)
d
a
S(t)=\int x(a)w(x-a)da
S(t)=∫x(a)w(x−a)da
S
(
t
)
=
∑
a
=
−
∞
+
∞
x
(
a
)
w
(
x
−
a
)
S(t)=\sum\limits_{a=-\infty}^{+\infty}x(a)w(x-a)
S(t)=a=−∞∑+∞x(a)w(x−a)
上面两种分别是连续卷积核离散卷积。CNN中卷积是离散卷积。
离散卷积本质上是一种加权求和,所以CNN中的卷积本质上就是利用一个共享参数的过滤器,通过计算中心点以及相邻像素点的加权求和来构成特征图,实现空间特征的提取。
相比于全连接层从输入空间学习到的是全局模式,卷积层学习到的是局部模式。这个重要的特性使得卷积神经网络具有两个性质:
1、平移不变性:
2、模式的空间层次结构
2、参数共享和稀疏交互
1、什么叫稀疏交互?
卷积神经网络中,卷积核的尺度远小于输入的维度,这样每个输出神经元仅与前一层局部区域内的神经元存在连接权重,我们称这种特性为稀疏交互。
2、稀疏交互的物理意义?
通常图像、文本、语音等现实世界中的数据都具有局部的特征结构,我们可以先学习局部特征,由局部特征组合起来形成更高级、更复杂的特征。
3、什么叫共享参数?
参数共享是指在同一个模型的不同模块使用相同的参数,他是卷积运算固有的属性。卷积神经网络中,卷积核中的每一个元素作用于每一次局部输入的特定位置。
4、共享参数的物理意义?
使卷积层具有平移不变性。
3、池化
池化函数使用某一位置相邻输出的总体统计特征来代替网络在位置的输出。通常有最大池化、平均值、
L
2
L^2
L2范数,基于中心点的加权平均。
池化操作能显著降低参数量,保持平移、伸缩、旋转操作的不变性。平移不变性是指输出结果对输入的小幅平移基本保持不变。例如(1,12,3),通过最大池化将会取12,如果将输入平移得到(1,12,4)结果还是12。
其中卷积步幅和池化都是对特征图进行下采样的方法,通过是利用池化的方法进行下采样。
4、使用tensorflow中keras模块实现卷积神经网络
class CNN(tf.keras.Model):
def __init__(self):
super().__init__()
self.conv1 = tf.keras.layers.Conv2D(
filters=32, # 卷积层神经元(卷积核)数目
kernel_size=[5, 5], # 感受野大小
padding='same', # padding策略(vaild 或 same)
activation=tf.nn.relu # 激活函数
)
self.pool1 = tf.keras.layers.MaxPool2D(pool_size=[2, 2], strides=2)
self.conv2 = tf.keras.layers.Conv2D(
filters=64,
kernel_size=[5, 5],
padding='same',
activation=tf.nn.relu
)
self.pool2 = tf.keras.layers.MaxPool2D(pool_size=[2, 2], strides=2)
self.flatten = tf.keras.layers.Reshape(target_shape=(7 * 7 * 64,))
self.dense1 = tf.keras.layers.Dense(units=1024, activation=tf.nn.relu)
self.dense2 = tf.keras.layers.Dense(units=10)
def call(self, inputs):
x = self.conv1(inputs) # [batch_size, 28, 28, 32]
x = self.pool1(x) # [batch_size, 14, 14, 32]
x = self.conv2(x) # [batch_size, 14, 14, 64]
x = self.pool2(x) # [batch_size, 7, 7, 64]
x = self.flatten(x) # [batch_size, 7 * 7 * 64]
x = self.dense1(x) # [batch_size, 1024]
x = self.dense2(x) # [batch_size, 10]
output = tf.nn.softmax(x)
return output
实例
利用MNIST手写数字图片数据集,通过CNN进行分类。
数据获取
使用
t
f
.
k
e
r
a
s
.
d
a
t
a
s
e
t
s
tf.keras.datasets
tf.keras.datasetsl类快速载入MNIST数据集
mnist = tf.keras.datasets.mnist
mnist = tf.keras.datasets.mnist将从网络上自动下载MNIST数据集并加载,如果因为网络连接错误,可以从这里下载
https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
下载 MNIST 数据集 mnist.npz 文件,并放置于用户目录的 .keras/dataset 目录下(Windows 下用户目录为 C:\Users\用户)。
以下是完整代码过程:
import tensorflow as tf
import os
#os.environ["CUDA_VISIBLE_DEVICES"] = "2"
import numpy as np
#数据获取及预处理
class MNISTLoader():
def __init__(self):
mnist = tf.keras.datasets.mnist
(self.train_data, self.train_label),(self.test_data,self.test_label)=mnist.load_data()
#给数据增加一个通道,也就是一个维度,图片色彩维度
self.train_data = np.expand_dims(self.train_data.astype(np.float32)/255.0,axis=-1)
self.test_data = np.expand_dims(self.test_data.astype(np.float32)/255.0,axis=-1)
self.train_label = self.train_label.astype(np.float32)
self.test_label = self.test_label.astype(np.float32)
self.num_train_data, self.num_test_data = self.train_data.shape[0],self.test_data.shape[0]
def get_batch(self, batch_size):
index = np.random.randint(0, np.shape(self.train_data)[0],batch_size)
return self.train_data[index, :], self.train_label[index]
#模型的构建 tf.keras.Model 和 tf.keras.layers 这里使用的是函数式编程
class CNN(tf.keras.Model):
def __init__(self):
#关联父类构造函数
super(CNN, self).__init__()
self.conv1 = tf.keras.layers.Conv2D(
filters=32, #卷积层神经元个数
kernel_size=[5, 5], #感受野大小
padding='same', #是否进行边界填充,Same填充后输入输出维度一样
activation=tf.nn.relu #激活函数
)
self.pool1 = tf.keras.layers.MaxPooling2D(pool_size=[2,2], strides=2)
self.conv2 = tf.keras.layers.Conv2D(
filters=64,
kernel_size=[5,5],
padding='same',
activation=tf.nn.relu
)
self.pool2 = tf.keras.layers.MaxPooling2D(pool_size=[2, 2], strides=2)
self.flatten = tf.keras.layers.Reshape(target_shape=(7*7*64,))
self.dense1 = tf.keras.layers.Dense(units=1024, activation=tf.nn.relu)
self.dense2 = tf.keras.layers.Dense(units=10)
def call(self, inputs):
x = self.conv1(inputs)
x = self.pool1(x)
x = self.conv2(x)
x = self.pool2(x)
x = self.flatten(x)
x = self.dense1(x)
x = self.dense2(x)
output = tf.nn.softmax(x)
return output
#模型训练
#定义一些超参数
num_epochs = 10 #迭代次数
batch_size = 50 #每批数据的大小
learning_rate = 0.001 #学习率
model = CNN()
data_loader = MNISTLoader()
optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)
num_batches = int(data_loader.num_train_data//batch_size*num_epochs)
for batch_index in range(num_batches):
X, y = data_loader.get_batch(batch_size)
with tf.GradientTape() as tape:
y_pred = model(X)
loss = tf.keras.losses.sparse_categorical_crossentropy(y_true=y,y_pred=y_pred)
loss = tf.reduce_mean(loss)
#print("epoch %d: loss %f" % (, loss.numpy()))
grads = tape.gradient(loss, model.variables)
optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables))
#模型评估
sparse_categorical_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()
num_batches = int(data_loader.num_test_data // batch_size)
for batch_index in range(num_batches):
start_index, end_index = batch_index * batch_size, (batch_index + 1) * batch_size
y_pred = model.predict(data_loader.test_data[start_index: end_index])
sparse_categorical_accuracy.update_state(y_true=data_loader.test_label[start_index: end_index], y_pred=y_pred)
print("test accuracy: %f" % sparse_categorical_accuracy.result())