Keras-LeNet5-图片分类问题

1、原始数据集

下载mnist.pkl.gz数据集,得到raw_train, raw_validation, raw_test。

raw_train是一个三维列表,第1个分量是(50000,784)的列表,表示50000个样本,每个样本是(28,28)图片拉直成的向量。第2个分量是50000大小的一维列表,记录每个样本的数字。

raw_validation是一个三维列表,第1个分量是(10000,784)的列表,表示10000个样本,每个样本是(28,28)图片拉直成的向量。第2个分量是10000大小的一维列表,记录每个样本的数字。

raw_test是一个三维列表,第1个分量是(10000,784)的列表,表示10000个样本,每个样本是(28,28)图片拉直成的向量。第2个分量是10000大小的一维列表,记录每个样本的数字。

对原始数据集进行加工,制作训练数据集,得到train_x、train_y、validation_x、validation_y,、test_x、test_y。train数据集都是三维数组,每个样本是(28,28)大小的数组。test数据集都是二维数组,每个样本是10维的向量。

train_x : 50000, 28, 28
train_y : 50000, 10
validation_x : 10000, 28, 28
validation_y : 10000, 10
test_x : 10000, 28, 28
test_y : 10000, 10

2、LeNet5网络细节

LeNet5网络结构如下:
在这里插入图片描述第1步、初始输入图片大小:(28,28),灰度图,只含有单通道。

第2步、卷积层,利用6个(5,5)的卷积核,stride=1,映射得到6张(24,24)的feature map。

第3步、最大池化层,取池化核(2,2),stride=2,映射得到6张(12,12)的feature map,然后利用sigmoid激活函数进行加工。

第4步、卷积层。将6张(12,12)的feature map,映射成16张(8,8)的feature map。具体映射细节如下:

将6张(12,12)的feature map分别标记为A0-A5,将16张(8,8)的feature map分别标记为B0-B15。举个特例,如何由A0-A5映射到B0呢?
在这里插入图片描述
由上面表格可以看到详细的连接关系。利用3个(5,5)的卷积核,分别对A0、A1、A2进行卷积再加上偏置,得到3个(8,8)的矩阵,进行相加,得到B0的feature map。

如何由A0-A5映射到B15呢?利用6个(5,5)的卷积核,分别对A0-A5进行卷积再加上偏置,得到6个(8,8)的矩阵,进行相加,得到B15的feature map。

第5步、最大池化层,取池化核(2,2),stride=2,映射得到16张(4,4)的feature map,然后利用sigmoid激活函数进行加工。

第6步、卷积拉直层,取120个(16,4,4)的卷积核,全连接。由于上一层的feature map尺寸(4,4),用(4,4)卷积后恰好得到了一个数字。这就完成了由CNN特征图到BP神经元的映射。然后利用sigmoid激活函数进行加工。

第7步、全连接层,取84个神经元,取tanh激活函数。

第8步:全连接层,取10个神经元,取tanh激活函数。

3、对模型结果的深入思考

自己训练了10000次,大概花费30h,模型分类准确率98.47%,这个分类问题已经算解决的比较好了。
在这里插入图片描述
误差损失函数图像如下:
在这里插入图片描述

思考1:为什么网络一开始训练的准确率都在0.1040附近?

因为网络刚开始是随机初始化,相当于随机乱猜。这是10分类问题,数学意义上每次猜对的概率p=0.1,由大数定律可知,样本准确率就应该在0.1附近。

思考2:卷积层到底提取到了什么特征?

这是原始输入图像:
在这里插入图片描述
利用已经训练好的模型,对原始(28,28)灰度图做第一次卷积,得到6个(24,24)的feature map。由图中可以看出,前期的卷积层提取得到的图片特征较为直观、具体,能够很清晰地被我们所理解。
在这里插入图片描述
对6个(24,24)的feature map做第一次池化,得到6个(12,12)的feature map。

在这里插入图片描述
对6个(12,12)的feature map做第二次卷积,得到16个(8,8)的feature map。可以看出,后期的卷积层提取得到的图片特征较为抽象。我们虽然无法直观上去理解这种特征提取背后的idea,但这种由机器自动提取的特征,往往能取得比较好的分类效果。

在这里插入图片描述这是图片最后映射成的向量,可以看出,映射结果与实际y向量非常接近了。

在这里插入图片描述
在这里插入图片描述

思考3:对于超大型数据集,无法加载进内存进行训练,如何分批量加载数据训练?

对于大型图片数据集,如果一次性加载进内存,利用fit函数进行网络训练,消耗空间太大,很可能出现程序异常终止、电脑卡死的情况。
在这里插入图片描述

CNN背后的优化原理是,每次计算一个batch的梯度以代替所有样本梯度,从而进行目标函数优化。所以我们可以利用生成器函数yield,每次只加载进一个batch的图片进入内存,这也大大加快了网络训练速度。

思考4:网络训练调节技巧。在每一大轮的训练过程中,应该怎样调节学习率?

对网络进行训练时,如果直接设置一个model.fit_generator函数,我们无法知道网络多少轮训练好了、多少轮已经过拟合、什么时候应该手动调节学习率。无法真正做到实时调控,让模型朝着我们想要的方向优化。这时可以这样做:

假设我们想训练10000个epoch,我们把每1000个epoch记为一个大轮。我们每次只用model.fit_generator函数训练epoch=1000,然后保存模型,输出loss函数时序图。然后我们再加载已经保存好的模型,在此基础上继续进行训练,调整优化参数。
在这里插入图片描述

如果上一轮loss函数是不断降低的,我们这一轮可以增大学习率。如果上一轮loss函数已经趋于停滞,这时应该分情况处理。如果此时测试集精度已经达到0.95,说明模型现在真的在全局最优附近,这时应该减小学习率,细化,一点一点的继续优化。如果测试集精度在0.6附近就停滞了,说明模型此时陷入了一个局部最优解,应该增大学习率,从局部最优跳出来。

4、具体代码

数据集加工

import pickle
import gzip
import numpy as np
import cv2


def number_vector(j):
    vector = np.zeros(10)
    vector[j] = 1.0
    return vector


def vector_image(v):
    img = np.zeros((28, 28))

    for i in range(28):
        img[i, :] = v[28*i:(28*i+28)]
    return img

# raw_train : [50000*78450000]
# raw_validation : [10000*78410000]
# raw_test : [10000*78410000]

# train_x : 50000, 28, 28
# train_y : 50000, 10
# validation_x : 10000, 28, 28
# validation_y : 10000, 10
# test_x : 10000, 28, 28
# test_y : 10000, 10


def make_dataset():
    f = gzip.open('/home/archer/CODE/PF/data/mnist.pkl.gz', 'rb')
    (raw_train, raw_validation, raw_test) = pickle.load(f, encoding='bytes')
    f.close()

    train_x = np.zeros((50000, 28, 28))
    train_y = np.zeros((50000, 10))
    for i in range(50000):
        train_x[i, :, :] = vector_image(raw_train[0][i])
        train_y[i, :] = number_vector(raw_train[1][i])

    validation_x = np.zeros((10000, 28, 28))
    validation_y = np.zeros((10000, 10))
    test_x = np.zeros((10000, 28, 28))
    test_y = np.zeros((10000, 10))

    for i in range(10000):
        validation_x[i, :, :] = vector_image(raw_validation[0][i])
        validation_y[i, :] = number_vector(raw_validation[1][i])

        test_x[i, :, :] = vector_image(raw_test[0][i])
        test_y[i, :] = number_vector(raw_test[1][i])

    # display 0-9 picture
    # display_img = test_x[0]
    # print('number - vector', test_y[0])
    # cv2.namedWindow("Image")
    # cv2.imshow("Image", display_img)
    # cv2.waitKey(0)

    return train_x, train_y, validation_x, validation_y, test_x, test_y



LeNet5网络搭建

import numpy as np
from keras.models import Sequential, Model
from keras.layers import Dense, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.models import load_model
import matplotlib.pyplot as plt


# LeNet network
# input : 28 * 28
# conv1 :  6 * 24 *24
# pool1 : 6 * 12 * 12
# activation : 6 * 12 * 12
# conv2 :  16 * 8 * 8
# pool2 : 16 * 4 * 4
# activation : 16 * 4 * 4
# conv3 : 120
# activation : 120
# dens 84
# activation : 84
# dense : 10
# activation : 10


def create_network():
    np.random.seed(1)
    model = Sequential()
    model.add(Conv2D(6, (5, 5), input_shape=(28, 28, 1), strides=1))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=2))
    model.add(Activation('sigmoid'))

    model.add(Conv2D(16, (5, 5), strides=1))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=2))
    model.add(Activation('sigmoid'))

    model.add(Conv2D(120, (4, 4), strides=1))
    model.add(Flatten())
    model.add(Activation('tanh'))

    model.add(Dense(84))
    model.add(Activation('tanh'))

    model.add(Dense(10))
    model.add(Activation('softmax'))

    model.summary()
    return model


# batch generator: reduce the consumption of computer memory
def generator(train_x, train_y, batch_size):

    while 1:
        row = np.random.randint(0, len(train_x), size=batch_size)
        x = train_x[row]
        y = train_y[row]
        yield x, y


# create model and train and save
def train_network(train_x, train_y, test_x, test_y, epoch, batch_size):
    train_x = train_x[:, :, :, np.newaxis]
    test_x = test_x[:, :, :, np.newaxis]

    model = create_network()
    model.compile(loss='categorical_crossentropy', optimizer='adadelta', metrics=['accuracy'])

    model.fit_generator(generator(train_x, train_y, batch_size), epochs=epoch,
                        steps_per_epoch=len(train_x) // batch_size)

    score = model.evaluate(test_x, test_y, verbose=0)
    print('first_model test accuracy:', score[1])

    model.save('first_model.h5')


# Load the partially trained model and continue training and save
def load_network_then_train(train_x, train_y, test_x, test_y, epoch, batch_size, input_name, output_name):
    train_x = train_x[:, :, :, np.newaxis]
    test_x = test_x[:, :, :, np.newaxis]

    model = load_model(input_name)
    history = model.fit_generator(generator(train_x, train_y, batch_size),
                                  epochs=epoch, steps_per_epoch=len(train_x) // batch_size)

    score = model.evaluate(test_x, test_y, verbose=0)
    print(output_name, 'test accuracy:', score[1])

    model.save(output_name)
    show_plot(history)


# plot the loss and the accuracy
def show_plot(history):
    # list all data in history
    print(history.history.keys())

    plt.plot(history.history['loss'])
    plt.title('model loss')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.savefig('loss1.jpg')
    plt.show()

    plt.plot(history.history['accuracy'])
    plt.title('model accuracy')
    plt.ylabel('accuracy')
    plt.xlabel('epoch')
    plt.savefig('accuracy1.jpg')
    plt.show()


# Show network intermediate results - feature map
def show_feature_map(test_x, input_name):
    model = load_model(input_name)

    model.summary()
    test_x = test_x[:, :, :, np.newaxis]

    conv1_model = Model(inputs=model.input, outputs=model.layers[0].output)
    conv1_output = conv1_model.predict(test_x)

    feature_img = conv1_output[0, :, :, 0]
    plt.imshow(feature_img, cmap='gray')
    plt.show()




主函数调用

import getdata as gt
import network as nt


if __name__ == "__main__":
    train_x, train_y, validation_x, validation_y, test_x, test_y = gt.make_dataset()
    nt.train_network(train_x, train_y, test_x, test_y, epoch=10,  batch_size=128)
    nt.load_network_then_train(train_x, train_y, test_x, test_y, epoch=10,  batch_size=128, input_name='first_model.h5', output_name='second_model.h5')
    nt.show_feature_map(test_x, input_name='second_model.h5')


5、项目链接

如果代码跑不通,可以去下载我的项目链接:https://blog.csdn.net/Twilight737

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值