使用Alexnet模型识别猫和狗

一、测试平台

python:3.6.5
tensorflow:1.6.0
keras:2.1.5

二、前期准备

1、数据收集:本例收集了25000张小猫、小狗的图片,二者各占一半,当然,训练集数据多多益善;
2、数据集预处理:

①猫和狗的图片分别重命名,命名格式为:“cat.number”/“dog.number”,其中number=1,2,3,4……n。文件重命名这一块如果有问题请参考:图片批量重命名

②将猫和狗的图片放在文件夹:“./data/image/pre_train/”这个文件夹下,并在同一目录创建文件夹train(“./data/image/train/”);

③将预训练的数据集中的图片resize成需要的大小(本例中Alexnet模型input的图片大小为(-1,224,224,3)),所以我们将pre_train文件夹下的图片resize成(224,224),并存储在train文件夹下;这一步如果有疑问,请参考文章:resize训练集图片大小并存储的方法

④在data文件夹下新建空的dataset.txt文件,根据train文件夹中已训练好的数据集制作label,格式为"cat.number.jpg;0"/“dog.number.jpg;1”

③、④步代码块如下:

#coding=utf-8

[1]

'''
对收集到的数据进行预处理,以减小训练时间;
1、我们将搜集到的图片存放在与软件脚本同一根目录下的'./data/image/pre_train/'文件夹下;
2、我们使用Image模块从pre_train文件夹读取图片,resize,并存储入'./data/image/train/'文件夹;
'''

from PIL import Image
import os

src_path = './data/image/pre_train/'
dst_path = './data/image/train/'

filelist=os.listdir(src_path)
for img in filelist:
    image=Image.open(src_path+img)
    image_resize=image.resize((224,224),resample=2)
    '''
    # image.resize(size,resample=0)  #sesam用于表示改变图像过程中的插值方法,0:双线性插值;1:最邻近插值;2:双三次插值;3|面积插值法
    # 参考:python: PIL的Image.resize()函数:
    '''
    image_resize.save(dst_path+img)


[2]

'''
生成标签label
'''
import os

photos=os.listdir('./data/image/train/')  #os.listdir:用于返回一个由文件名和目录组成的列表
with open('./data/dataset.txt','w') as f:
    for photo in photos:
        name=photo.split('.')[0]
        if name=='cat':
            f.write(photo+';0\n')  #\n:换行
        elif name=='dog':
            f.write(photo+';1\n')
f.close()

三、训练
Alexnet模型如下所示
在这里插入图片描述
注释:
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、两个全连接层,最后输出为1000类

代码块:

from keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau, EarlyStopping
from keras.utils import np_utils
from keras.optimizers import Adam
from model.AlexNet import AlexNet

import numpy as np
import utils
import cv2
from keras import backend as K

K.set_image_dim_ordering('tf')


# K.image_data_format()=="channel_first"

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):
            if i == 0:
                np.random.shuffle(lines)     #shuffle:洗牌
            name = lines[i].split(';')[0]
            # 从文件中读取图像
            img = cv2.imread(r".\data\image\train" + '/' + name)
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            img = img / 255
            X_train.append(img)
            Y_train.append(lines[i].split(';')[1])
            # 读完一个周期后重新开始
            i = (i + 1) % n  #取模,返回除法的余数
        # 处理图像
        X_train=np.array(X_train)
        #X_train = utils.resize_image(X_train, (224, 224))
        #X_train = X_train.reshape(-1, 224, 224, 3)  #-1是模糊控制的意思,n,h,w,c,n=-1代表取值不固定
        Y_train = np_utils.to_categorical(np.array(Y_train), num_classes=2)
        #np_utils.to_categorical(y_train,2)将原来标签(sample,label)转化为一行两列的 独热码
        yield (X_train, Y_train)  #并发编程中完成一个“并发”程序


if __name__ == "__main__":
    # 模型保存的位置
    log_dir = "./logs/"

    # 打开数据集的txt
    with open(r".\data\dataset.txt", "r") as f:
        lines = f.readlines()

    # 打乱行,这个txt主要用于帮助读取数据来训练
    # 打乱的数据更有利于训练
    np.random.seed(10101)  #预先使用random.seed(x)设定好种子之后,其中x可以是任意数字,比如10,先调用它的情况下,使用random生成的随数将会是同一个,设置的seed()值仅有一个有效
    np.random.shuffle(lines)
    np.random.seed(None)

    # 90%用于训练,10%用于估计。
    num_val = int(len(lines) * 0.1)
    num_train = len(lines) - num_val

    # 建立AlexNet模型
    model = AlexNet()

    # 保存的方式,3世代保存一次
    checkpoint_period1 = ModelCheckpoint(   #checkpoint:检查站
        log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
        monitor='acc',   #monitor:三代保存一次
        save_weights_only=False,
        save_best_only=True,
        period=3  #period:周期
    )
    # 学习率下降的方式,acc三次不下降就下降学习率继续训练
    reduce_lr = ReduceLROnPlateau(   #plateau:稳定水平,达到平衡时期
        monitor='acc',
        factor=0.5,  #factor:要素
        patience=3,  #patience:耐心
        verbose=1    #verboss:冗长的
    )
    # 是否需要早停,当val_loss一直不下降的时候意味着模型基本训练完毕,可以停止
    early_stopping = EarlyStopping(
        monitor='val_loss',
        min_delta=0,
        patience=10,
        verbose=1
    )

    # 交叉熵
    model.compile(loss='categorical_crossentropy',   #compile:编译;     categorical_crossentropy:交叉熵
                  optimizer=Adam(lr=1e-3),   #optimizer:优化器
                  metrics=['accuracy'])      #metrics:度量;  accuracy:精度,准确度

    # 一次的训练集大小
    batch_size = 128  #batch:一批

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

    # 开始训练
    model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size),
                        steps_per_epoch=max(1, num_train // batch_size),
                        '''
                        #由于generator()函数循环没有终止条件,fit_generator也不知道一个epoch什么时候结束,所以我们需要动手指定steps_per_epoch参数
                        一般的数值即为len(y)//batch_size.如果过数据集大小不能整除batch_size,而且你打算使用最后一个batch的数据(该batch比batch_size要小),
                        此时使用np.ceil(len(y)//batch_size)
                        '''
                        validation_data=generate_arrays_from_file(lines[num_train:], batch_size),
                        validation_steps=max(1, num_val // batch_size),
                        epochs=50,
                        initial_epoch=0,
                        callbacks=[checkpoint_period1, reduce_lr])
    model.save_weights(log_dir + 'last1.h5')

训练结束后,“./logs/”文件夹将生成last1.h5文件,预测环节将调用这个文件

四、预测

1、在"./data/model/"文件夹下,新建text文件,并命名为“index_word”,写入:
在这里插入图片描述
预测时,可以通过下面四行代码,调用index_word文件。

#coding=utf-8

#读index_word.txt文档,给预测提供结果

def print_answer(argmax):
    with open("./data/model/index_word.txt", "r", encoding='utf-8') as f:
        synset = [l.split(";")[1][:-1] for l in f.readlines()]   #[:-1]从0到位置为-1的数,这里主要是为了过滤换行符“\n”

    #print(synset[argmax])
    return synset[argmax]

2、预测新图片时,我们不能保证输入图片的尺寸一定是满足要求的,所以还是要对输入的图片resize,

import numpy as np
import utils
import cv2
from keras import backend as K
from model.AlexNet import AlexNet

K.set_image_dim_ordering('tf')   #tf1版本的用法,'tf'的意思是;数据按照nhwc分布

#将预测图片resize成(224,224)
def resize_image(image,size):
    with tf.name_scpoe('resize image'):
        images=[]
        for i in image:
            i=cv2.resize(i,size)
            images.append(i)
        images=np.array(images)
        return images

if __name__ == "__main__":
    model = AlexNet()
    model.load_weights("./logs/last1.h5")  #调用last1.h5文件
    img = cv2.imread("./Test.jpg")
    img_RGB = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    img_nor = img_RGB/255
    img_nor = np.expand_dims(img_nor,axis = 0)
    img_resize = resize_image(img_nor,(224,224))
    #utils.print_answer(np.argmax(model.predict(img)))
    print(utils.print_answer(np.argmax(model.predict(img_resize))))
    cv2.imshow("ooo",img)
    cv2.waitKey(0)
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值