Keras搭建AlexNet网络以及网络优化(附代码数据集)

搭建AlexNet网络

  • AlexNet
  • 数据集
  • 实战

AlexNet

AlexNet是2012年ImageNet竞赛冠军获得者Hinton和他的学生Alex Krizhevsky设计的。也是在那年之后,更多的更深的神经网络被提出,比如优秀的vgg,GoogLeNet。 这对于传统的机器学习分类算法而言,已经相当的出色。
Keras搭建AlexNet网络以及网络优化(附代码数据集)_第1张图片
Keras搭建AlexNet网络以及网络优化(附代码数据集)_第2张图片
结构呢现在看来还是很清晰的不复杂,不过对于当时2012年来说,这个网络的结构可谓是巨兽,参数超级多。结构如下:
**1、一张原始图片被resize到(224,224,3);
2、使用步长为4x4,大小为11的卷积核对图像进行卷积,输出的特征层为96层,输出的shape为(55,55,96);
3、使用步长为2的最大池化层进行池化,此时输出的shape为(27,27,96)
4、使用步长为1x1,大小为5的卷积核对图像进行卷积,输出的特征层为256层,输出的shape为(27,27,256);
5、使用步长为2的最大池化层进行池化,此时输出的shape为(13,13,256);
6、使用步长为1x1,大小为3的卷积核对图像进行卷积,输出的特征层为384层,输出的shape为(13,13,384);
7、使用步长为1x1,大小为3的卷积核对图像进行卷积,输出的特征层为384层,输出的shape为(13,13,384);
8、使用步长为1x1,大小为3的卷积核对图像进行卷积,输出的特征层为256层,输出的shape为(13,13,256);
9、使用步长为2的最大池化层进行池化,此时输出的shape为(6,6,256);
10、全连接层,4096神经元;
11、全连接层,4096神经元;
12、最后得到1000个分类。
可以看出它的结构其实和vgg特别像,基本上就是卷积池化,只是参数不同,这也导致了最后的训练参数太大,特别是第一个全连接层,最大的消耗在第一个4096的全连接层。

数据集

首先附上数据集,猫和狗各4000张图片。
链接:https://pan.baidu.com/s/1_DIrQJCjNqGYnnc-TL4vRw
提取码:al2i

实战

首先导入库,这些库都是比较常见的,如果有特殊用处的下面会解释。

import os
import keras
from keras.preprocessing.image import img_to_array, load_img
import numpy as np
from sklearn.preprocessing import LabelBinarizer
from keras.models import Sequential
import matplotlib.pyplot as plt
from matplotlib.image import imread
from keras.models import load_model
from PIL import Image
from keras.layers import Dense,Activation,Conv2D,MaxPooling2D,Flatten,Dropout,BatchNormalization
from keras.optimizers import Adam
from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau, TensorBoard
from keras.utils.np_utils import to_categorical

这里就是先导入我们的数据集了,一些常规操作就不细讲了,如果阅读吃力可以先看博主最早的卷积网络实战,那里面有很详细的说明。
这里all_path就是所有图片地址了,最后shuffle打乱一下。避免出现验证集正确率反复横跳的情况。

all_path = []
father_path  ='D:/桌面/机器学习+深度学习/源码/Artificial_Intelligence-\
master/artificial_intelligence(更多IT课程公众号:某课联盟 )/week8/dataset/training_set'
class_folders = os.listdir(father_path)  # 读取爷爷路径下的所有文件名,也就是5个分类标签
for class_folder in class_folders:
    img_names = os.listdir(father_path + '/' + class_folder)
    for img_name in img_names:
        path = father_path + '/' + class_folder + '/' + img_name
        all_path.append(path)
np.random.shuffle(all_path)

这里定义这个网络的结构,不过博主用原来网络的参数进行训练的时候发现效果不太好,反复尝试,查阅资料后,认为应该是数据集过小,才8000张,可能不太适用这个大网络。将filters全部缩减一半,在全连接层改变神经元。这样子训练完效果好得多。具体就不说了,阅读吃力地小伙伴可以看看前面的文章,有更详细的关于卷积神经网络的实战。

def AlexNet(input_shape=(224,224,3),output_shape=2):
    # AlexNet
    model = Sequential()
    # 使用步长为4x4,大小为11的卷积核对图像进行卷积,输出的特征层为96层,输出的shape为(55,55,96);
    # 所建模型后输出为48特征层
    model.add(Conv2D(
                     filters=48, 
                     kernel_size=(11,11),
                     strides=(4,4),
                     padding='valid',
                     input_shape=input_shape,
                     activation='relu'
                    )
             )
    model.add(BatchNormalization())
    # 使用步长为2的最大池化层进行池化,此时输出的shape为(27,27,96)
    model.add(MaxPooling2D(
                           pool_size=(3,3), 
                           strides=(2,2), 
                           padding='valid'
                          )
             )
    # 使用步长为1x1,大小为5的卷积核对图像进行卷积,输出的特征层为256层,输出的shape为(27,27,256);
    # 所建模型后输出为128特征层
    model.add(
        Conv2D(
            filters=128, 
            kernel_size=(5,5), 
            strides=(1,1), 
            padding='same',
            activation='relu'
        )
    )
    
    model.add(BatchNormalization())
    # 使用步长为2的最大池化层进行池化,此时输出的shape为(13,13,256);
    model.add(
        MaxPooling2D(
            pool_size=(3,3),
            strides=(2,2),
            padding='valid'
        )
    )
    # 使用步长为1x1,大小为3的卷积核对图像进行卷积,输出的特征层为384层,输出的shape为(13,13,384);
    # 所建模型后输出为192特征层
    model.add(
        Conv2D(
            filters=192, 
            kernel_size=(3,3),
            strides=(1,1), 
            padding='same', 
            activation='relu'
        )
    ) 
    # 使用步长为1x1,大小为3的卷积核对图像进行卷积,输出的特征层为384层,输出的shape为(13,13,384);
    # 所建模型后输出为192特征层
    model.add(
        Conv2D(
            filters=192, 
            kernel_size=(3,3), 
            strides=(1,1), 
            padding='same', 
            activation='relu'
        )
    )
    # 使用步长为1x1,大小为3的卷积核对图像进行卷积,输出的特征层为256层,输出的shape为(13,13,256);
    # 所建模型后输出为128特征层
    model.add(
        Conv2D(
            filters=128, 
            kernel_size=(3,3), 
            strides=(1,1), 
            padding='same', 
            activation='relu'
        )
    )
    # 使用步长为2的最大池化层进行池化,此时输出的shape为(6,6,256);
    model.add(
        MaxPooling2D(
            pool_size=(3,3), 
            strides=(2,2), 
            padding='valid'
        )
    )
    # 两个全连接层,最后输出为1000类,这里改为2类
    # 缩减为1024
    model.add(Flatten())
    model.add(Dense(256, activation='relu'))
    model.add(Dropout(0.5))
    
    model.add(Dense(128, activation='relu'))
    model.add(Dropout(0.5))
    
    model.add(Dense(output_shape, activation='softmax'))

    return model

像vgg,Alexnet这种网络,都是比较大,参数多。一次性fit容易爆内存。所以定义一个生成器,不断生成训练数据是很有必要的。这里详细说明以下。首先我们每次返回的数量是一个batchsize,这里是64,也就是每次返回64个数据到model训练。根据路径我们分割出图片路径和图片标签,存于Xtrain和Ytrain,不过这里要注意Ytrain返回的是独热编码后的。也就是to_categorical(np.array(Y_train), num_classes=2),numclasses代表种类,猫和狗就是2类。to_categorical是keras处理编码的一个函数。其实就是独热编码了。要注意,这个必须从0开始,就是2类的话Ytrain就是0和1,如果3类就是0,1,2.不然报错。循环生成数据训练。可以做到边生成数据边训练,这样大大节约了内存。

def generate_arrays_from_file(lines,batch_size):
    # 获取总长度
    n = len(lines)
    i = 0
    while 1:
        X_train = []
        Y_train = []
        # 获取一个batch_size大小的数据
        for b in range(batch_size):
            # 从文件中读取图像
            img = load_img(lines[i], target_size=(224, 224))  # 读取图片途径,裁成224,224
            img = img_to_array(img)  # 转换成图像数组
            img = img/255
            X_train.append(img)
            if lines[i].split('.')[-3][-3:]=='cat':
                Y_train.append(0)
            else:
                Y_train.append(1)
            # 读完一个周期后重新开始
            i = (i+1) % n
        

        # 处理图像
        yield (np.array(X_train).reshape(-1,224,224,3), to_categorical(np.array(Y_train), num_classes=2))

最后这里的优化呢,是加上了学习率优化,和早停设置,保存训练好的权重。
3个函数的monitor='acc’代表的意思是评判的标准,都是以准确率来判断的。ReduceLROnPlateau和EarlyStopping的参数很相似,patience表示几个epoch。拿学习率来说,如果3个epoch的正确率都不下降,那么当前学习率乘factor就是新的学习率。这个factor就是学习率下降的比例了。最后这里个函数要在fitgenerator里面的callback以列表方式写入。最后保存我们训练好的权重到目录下。

# 80%用于训练,10%用于估计。
num_val = int(len(all_path)*0.2)
num_train = len(all_path) - num_val

# 建立AlexNet模型
model = AlexNet()

# 保存的方式,10epoch保存一次
checkpoint_period1 = ModelCheckpoint(
                                     './ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
                                     monitor='acc', 
                                     save_weights_only=False, 
                                     save_best_only=True, 
                                     period=10
                                     )
# 学习率下降的方式,acc三次不下降就下降学习率继续训练
reduce_lr = ReduceLROnPlateau(
                              monitor='acc', 
                              factor=0.5, 
                              patience=3, 
                              verbose=1
                              )
# 是否需要早停,当val_loss一直不下降的时候意味着模型基本训练完毕,可以停止
early_stopping = EarlyStopping(
                               monitor='val_loss', 
                               min_delta=0, 
                               patience=3, 
                               verbose=1
                               )

# 交叉熵
model.compile(loss = 'categorical_crossentropy',
              optimizer = Adam(0.001),
              metrics = ['accuracy'])
# 一次的训练集大小
batch_size = 64

print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))

# 开始训练
model.fit_generator(generate_arrays_from_file(all_path[:num_train], batch_size),
                    steps_per_epoch=max(1, num_train//batch_size),
                    validation_data=generate_arrays_from_file(all_path[num_train:], batch_size),
                    validation_steps=max(1, num_val//batch_size),
                    epochs=150,
                    initial_epoch=0,
                    callbacks=[checkpoint_period1, reduce_lr])
model.save_weights('./last_catdog.h5')

训练的结果还是很不错的,训练集在50epoch左右达到了100%的ac,验证集也有快80%,博主偷个懒没写如何测试,感兴趣的小伙伴可以参考之前的博文,自己动手写写如何测试吧!
Keras搭建AlexNet网络以及网络优化(附代码数据集)_第3张图片

你可能感兴趣的:(Keras搭建AlexNet网络以及网络优化(附代码数据集))