关于CNN的一些理解
卷积神经网络和多层感知机比较类似,不过他相对于多层感知机加入了一些层。这些层由卷积成和池化组成。所以,在CNN中神经网络的层次和结构都是不固定的可用调整的。例如你可以只用一次卷积核一次池化,也可以用多次卷积多次池化,也可根据训练数据集的不同,去修改全连接层或者参数,例如学率,轮次等。
CNN实践
这次实践用的LeNet-5这个模型去跑这mnist这个手写数字的集合。LeNet-5是什么呢?LeNet-5是一个网络结构,由5层组成,第一层为卷积层,第二层为池化层,第三层为卷积层,第四层为池化层,第五层为卷积层。这个是卷积部分,最后还是得依赖一个分类器即全连接层将输出结果转变为值的可能性,转变成one_hot的形式。
第一层 输入的是2828大小的图片,使用的55卷积核为6个。那么可以得到结果为6种特征的图片,图片大小为2424.。
第二层 输入的是2424大小的图片,使用的22区域采样种6个与上层卷积对应。那么可以得到结果为6种池化图片,图片大小为1212.。
第三层 输入的是1212大小的图片,使用的55卷积核为16个。那么可以得到结果为16种特征的图片,图片大小为88.。
第四层 输入的是88大小的图片,使用的22区域采样种16个与上层卷积对应。那么可以得到结果为16种池化图片,图片大小为44.。
第五层 输入的是44大小的图片,使用的44卷积核为120个。那么可以得到结果为6种特征的图片,图片大小为11。
第六层为全连接层,这一层将前面数据进行拉直,将11大小的图片特征集合拉直转换为1列,输入全连接成,进行计算后输出84个数据给输出层。
第七层为输出层,接受全连接成的84个数据,计算后输出10个值,代表0-9的可能性。
参数调整:通过读取mnist的数据结果,将结果集label中的标签改写成one_hot的形式,与输出结果进行loss计算。计算后通过优化器传递回去进行参数调整。
实践代码
import random
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
mnist = tf.keras.datasets.mnist;
(train_data, train_label), (test_data,test_label) = mnist.load_data("train-images.idx3-ubyte")
#数据归一化
train_data = np.expand_dims(train_data.astype(np.float32) / 255.0,axis=-1);
train_label = train_label.astype(np.int32)
test_label = test_label.astype(np.int32)
class CNN(tf.keras.Model):
def __init__(self):
super().__init__()
self.C1 = tf.keras.layers.Conv2D(
filters=6, #卷积核数目
kernel_size=[5, 5], # 卷积大小
padding='same',
strides=(1, 1),
activation=tf.nn.relu # 激活函数
);
#池化层1
self.S2 = tf.keras.layers.MaxPool2D();
#设置卷积层2
self. C3 = tf.keras.layers.Conv2D(
filters=16, #卷积核数目
kernel_size=[5, 5], # 卷积大小
padding='same',
strides=(1, 1),
activation=tf.nn.relu # 激活函数
);
#池化层2
self.S4 = tf.keras.layers.MaxPool2D();
#设置卷积层3
self.C5 = tf.keras.layers.Conv2D(
filters=120, #卷积核数目
kernel_size=[4, 4], # 卷积大小
padding='same',
strides=(1, 1),
activation=tf.nn.relu # 激活函数
);
#全连接层分类
self.F = tf.keras.layers.Flatten();
self.F6 = tf.keras.layers.Dense(units=84, activation=tf.nn.sigmoid);
#全连接层输出
self.OutPut = tf.keras.layers.Dense(units=10);
model = CNN()
class Count():
def call(input):
x = model.C1(input)
x = model.S2(x)
x = model.C3(x)
x = model.S4(x)
x = model.C5(x)
x = model.F(x)
x = model.F6(x)
x = model.OutPut(x)
output = tf.nn.softmax(x)
return output
def get_batch(input):
# 从数据集中随机取出batch_size个元素并返回
index = np.random.randint(0, np.shape(train_data)[0], input)
return train_data[index, :], train_label[index]
optimizer = tf.keras.optimizers.Adam(learning_rate=0.0005) # 实例化优化器
i = 0;
n = 0;
listX = np.zeros([6000],dtype=float);
listY = np.zeros([6000],dtype=float);
for begin in range(2):
for index in range(6000):
ty = np.zeros([50, 10], dtype=int)
X,Y = Count.get_batch(50)
# 处理数据集
# X对应图像
# Y对应着label 转为(0-9)的 one_hot
for times in range(50):
one_y = Y[times]
ty[times][one_y] = 1;
y = ty
with tf.GradientTape() as tape:
# 计算模型预测值
y_pred = Count.call(X)
# 计算损失函数
# y_pred2 = np.argmax(y_pred, 1) #最大可能性的位置结果
loss = tf.keras.losses.categorical_crossentropy(y_true=y, y_pred=y_pred)
loss = tf.reduce_mean(loss)
# 计算模型变量的导数
grads = tape.gradient(loss, model.variables)
if (i%600) == 0:
listX[n] = n;
listY[n] = loss;
n = n + 1;
print("batch %d: loss ", loss.numpy())
# 优化器的使用
optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables))
i = i + 1;
print("times ", begin)
plt.plot(listX,listY,'r--')
plt.xlabel('this is x')
plt.ylabel('this is y')
plt.title('this is a Loss')
plt.show()
# 评估器
sparse_categorical_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()
# 迭代轮数
for times in range(5):
i = 0;
for batch_index in range(10000):
# 模型预测的结果
index = random.randint(0, 10000)
X = test_data[index]
y = test_label[index]
# plt.imshow(X, cmap='Greys', interpolation='nearest')
# plt.tight_layout()
# plt.show()
X = np.expand_dims(X.astype(np.float32) / 255.0, axis=-1);
X = np.expand_dims(X, axis=0);
y_pred = Count.call(X)
y_pred2 = np.argmax(y_pred, 1) # 最大可能性的位置结果
if (y == y_pred2[0]):
i = i + 1
print("test accuracy: %f" % (i / 10000 * 100))
loss下降图像

可以看到loss有2次小的起伏,这种小的起伏说明的学习率不够小,无法准确的找准极小的情况下的下降梯度,无法确定极小值的位置,产生波动,第一个山型和第二个山新曲线应该都是这种原因。
测试结果
测试5轮测试集中数据,每轮测试10000次,每次测试取0-9999图片中随机一张。
for times in range(5):
i = 0;
for batch_index in range(10000):
# 模型预测的结果
index = random.randint(0, 9999)
X = test_data[index]
y = test_label[index]
# plt.imshow(X, cmap='Greys', interpolation='nearest')
# plt.tight_layout()
# plt.show()
X = np.expand_dims(X.astype(np.float32) / 255.0, axis=-1);
X = np.expand_dims(X, axis=0);
y_pred = Count.call(X)
y_pred2 = np.argmax(y_pred, 1) # 最大可能性的位置结果
if (y == y_pred2[0]):
i = i + 1
print("test accuracy: %f" % (i / 10000 * 100))

命中率都在99%左右。