CNN迁移模型图片分类完整案例使用,base vgg16

准备训练图片,和测试图片

对模型training

import tensorflow as tf
from  tensorflow import keras
from tensorflow.keras.preprocessing.image import ImageDataGenerator,load_img,img_to_array
from tensorflow.keras.applications.vgg16 import preprocess_input
from tensorflow.keras.applications.vgg16 import VGG16
import numpy as np


#图片读取处理
def load_img1(files_path):
    images=[]
    #加载图片
    for path in files_path:
        print(path)
        image = load_img(path, target_size=(224,224))
        #图片进行数组转换
        image = img_to_array(image)
        print('-----')
        print(image.shape)
        #形状修改,输入到卷积需要四维[1,224,224,3],1代表batch,如果多张图片就使用多张
        image = image.reshape(1, image.shape[0], image.shape[1], image.shape[2])
        print(image.shape)
        # print(image)
        #对图片进行归一化处理
        images.append(preprocess_input(image))
    return images

def get_files_path(file_path):
    import os
    # 获取当前路径
    # path = os.getcwd()
    # files = os.listdir(os.path.join(path, "tiger/test_img"))
    files = os.listdir(file_path)
    file_count = len(files)
    print(file_count)
    files_path = []
    for i in range(file_count):
        print(i)
        files_path.append(os.path.join(file_path, files[i]))

    return files_path


#使用vgg16模型修改,完成迁移模型使用。
#迁移模型 主要是对已有模型参数进行微调
#通常我们只需要自己训练全连接层参数即可满足,而卷积层可以copy已有训练好的模型。
class TransferModel(object):

    def __init__(self):
        #定义训练和测试图片的变化方式,对图片进行标准化和图片增强
        #rescale: 数据标准化 归一化
        self.train_generator = ImageDataGenerator(rescale=1.0/255.0)
        self.test_generator = ImageDataGenerator(rescale=1.0/255.0)

        #指定目录
        self.train_dir = "./data/train"
        self.test_dir = "./data/test"
        #定义图片训练相关参数
        self.image_size = (224,224)
        self.batch_size = 32

        #定义基类模型, include_top 是否包含全连接层?
        # 我们不需要包含全连接层,只是用卷积层和池化层参数即可。
        #vgg共有5层卷积层
        self.base_mode = VGG16(weights='imagenet', include_top=False)

        #图片解码
        self.img_dict = {0:'bus',1:'dinosaur',2:'elephant',3:'flower',4:'horse'}

        pass


    def get_local_data(self):
        """
        读取本地图片数据
        使用ImageDataGenerator.flow_from_directory()
        :return:Traning dataset and Test dataset
        """
        train_gen = self.train_generator.flow_from_directory(
            directory=self.train_dir, #所要读取的图片文件目录
            target_size= self.image_size, #对读取到的图片size进行更改
            batch_size=self.batch_size, #每批次返回多少样本?
            class_mode='binary', #分类选项
            shuffle=True #是否打乱顺序
        )
        test_gen = self.train_generator.flow_from_directory(
            directory=self.train_dir,
            target_size= self.image_size,
            batch_size=self.batch_size, #每批次返回多少样本?
            class_mode='binary',
            shuffle=True #是否打乱顺序
        )
        # for data in train_gen:
        #     print(data[0].shape,data[1].shape)

        self.train_generator = train_gen
        self.test_generator = test_gen

        return train_gen,test_gen

    def refine_base_mode(self):
        """
        微调VGG16结构,
        原有卷积层5block后 + 全局平局池化+两个全连接层
        :return:
        """
        #1 获取原notop模型 [None,None,None,512]
        x = self.base_mode.outputs[0]
        #2在输出结果上加上全局池化,输出[None,1,1,512]
        x=keras.layers.GlobalAveragePooling2D()(x)
        #
        #新增全连接层两个,其中一个1024个神经元,最后一个我们图片要分的种类,5类
        x=keras.layers.Dense(1024,activation='relu')(x)
        y_predict = keras.layers.Dense(5,activation='softmax')(x)
        #定义新的迁移模型
        trans_model = keras.models.Model(inputs=self.base_mode.inputs, outputs = y_predict)
        # print(trans_model.summary())

        return trans_model

    #关闭VGG原有5个卷积池block,只训练后面全连接层参数
    def frezee_vgg_model(self):
        for layer in self.base_mode.layers:
            layer.trainable = False
        return None

    #对模型进行编译
    def compile(self,model):
        model.compile(optimizer='Adam',
                            loss='sparse_categorical_crossentropy',
                            metrics=['accuracy'])
        return None

    #训练模型
    def fit_generator(self,model):
        # #对模型训练过程监视
        # ckpt = keras.callbacks.ModelCheckpoint('./ckpt/trans_models.hdf5',
        #                                        monitor='val_acc',#需要监视的值
        #                                        save_best_only=True,
        #                                        save_weights_only=True,
        #                                        verbose=0,
        #                                        mode='auto',
        #                                        save_freq=1 #
        #                                         )
        #
        # #对模型进行训练
        # #这里必须用fit_generator,运行完发现代码提示旧版,新版直接用fit也行。。
        # #迭代次数
        # #validation_data  测试集
        # model.fit(self.train_generator,epochs=3,validation_data=self.test_generator,callbacks=[ckpt] )


        """
        很奇怪,ckpt存不起来,所以直接copy api范例测试
        然后就有了下面的代码,但仍然存不到
        太奇怪了
        找到问题点了,与api比较发现:
        tensorflow2.4 api中monitor参数不是val_acc,而是val_accuracy
        :param model: 
        :return: 
        """
        EPOCHS = 5
        checkpoint_filepath = './tmp/ckpt/checkpoint.h5'
        model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
                filepath=checkpoint_filepath,
                save_weights_only=True,
                monitor='val_accuracy',
                mode='max',
                save_best_only=True,
                save_freq='epoch')

        # Model weights are saved at the end of every epoch, if it's the best seen
        # so far.
        # model.fit(epochs=EPOCHS, callbacks=[model_checkpoint_callback])
        model.fit(self.train_generator, validation_data=self.test_generator,
                  epochs=EPOCHS, callbacks=[model_checkpoint_callback])

        # # The model weights (that are considered the best) are loaded into the model.
        # model.load_weights(checkpoint_filepath)
        return model

    #保存训练好的模型
    def save_model(self,model,PATH):
        model.save(filepath=PATH)
        return model

    #模型预测
    def predict(self,model):
        """
        用自己训练的模型进行预测
        :return:
        """
        #首先加载模型
        model.load_weights('./tmp/ckpt/checkpoint.h5')

        # #加载图片,并对图片进行处理
        # img = load_img(path='tiger/test_img/cc.jpg', target_size=(224, 224))
        # img= img_to_array(img)
        # print(img.shape) #[224,224,3]
        # #输出[224 224 3],单输入到卷积神经网络必须是四维
        # img = tf.reshape(img,shape=[1,img.shape[0],img.shape[1],img.shape[2]])
        # print(img.shape)
        # #归一化处理
        # img = preprocess_input(img)
        # #进行预测,得出一组概率,对概率进行转化
        # output = model.predict(img)
        # print(type(output))
        # print(np.max(output[0]))
        # print(output)
        # #output [[5.6389440e-18 1.0181949e-02 9.8981386e-01 4.2919934e-26 4.3013383e-06]]
        # #进行解码,获取概率最大的ndarray下标,对应的输出结果
        # index = np.argmax(output,axis=1)
        # print(index,output[0][index[0]])
        # print('预测结果是: ',self.img_dict[index[0]])

        #批量加载图片
        files_path = get_files_path(file_path="./tiger/test_img")
        print(files_path)
        images = load_img1(files_path)
        outputs=[]
        for image in images:
           outputs.append(model.predict(image))
        #outputs [[[5.6389440e-18 1.0181949e-02 9.8981386e-01 4.2919934e-26 4.3013383e-06]],[[5.6389440e-18 1.0181949e-02 9.8981386e-01 4.2919934e-26 4.3013383e-06]]]
        #进行解码,获取概率最大的ndarray下标,对应的输出结果
        num=0
        for output in outputs:
            num+=1
            index = np.argmax(output,axis=1)
            #print(index,output[0][index[0]])
            print('第%d张图片的预测结果是: %s,  ----预测概率为: %0.2f'%(num,self.img_dict[index[0]],output[0][index[0]]*100),"%")

        return None


if __name__ == '__main__':
    #生成迁移模型对象
    tm = TransferModel()
    MODEL_PATH = './tmp/model/transfer_cnn.h5'

    #训练模型
    #获取本地训练和测试图片数据
    tm.get_local_data()
    #重新定义原有vgg16模型
    trans_model = tm.refine_base_mode()
    #冻结原有vgg16 卷积层参数,仅针对全连接层参数进行训练
    tm.frezee_vgg_model()
    #模型编译,梯度优化
    tm.compile(trans_model)
    #模型训练

    model = tm.fit_generator(trans_model)
    tm.save_model(model,MODEL_PATH)
    print('this is end lable')


    #
    # # 模型预测
    # model = tm.refine_base_mode()
    # tm.predict(model)
    # print('this is end lable')

迭代5次,训练结果准确率99.75%

 1/13 [=>............................] - ETA: 1:11 - loss: 1.5583 - accuracy: 0.21052022-03-18 17:39:55.460516: W tensorflow/core/common_runtime/bfc_allocator.cc:248] Allocator (GPU_0_bfc) ran out of memory trying to allocate 3.46GiB with freed_by_count=0. The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory were available.
13/13 [==============================] - 17s 915ms/step - loss: 1.2470 - accuracy: 0.5205 - val_loss: 0.4310 - val_accuracy: 0.9380
Epoch 2/5
13/13 [==============================] - 4s 296ms/step - loss: 0.3504 - accuracy: 0.9685 - val_loss: 0.1938 - val_accuracy: 0.9677
Epoch 3/5
13/13 [==============================] - 4s 296ms/step - loss: 0.1766 - accuracy: 0.9619 - val_loss: 0.1033 - val_accuracy: 0.9876
Epoch 4/5
13/13 [==============================] - 4s 296ms/step - loss: 0.0933 - accuracy: 0.9870 - val_loss: 0.0645 - val_accuracy: 0.9926
Epoch 5/5
13/13 [==============================] - 4s 298ms/step - loss: 0.0606 - accuracy: 0.9926 - val_loss: 0.0463 - val_accuracy: 0.9975
this is end lable

准备了15张自己的图片预测

import tensorflow as tf
from  tensorflow import keras
from tensorflow.keras.preprocessing.image import ImageDataGenerator,load_img,img_to_array
from tensorflow.keras.applications.vgg16 import preprocess_input
from tensorflow.keras.applications.vgg16 import VGG16
import numpy as np


#图片读取处理
def load_img1(files_path):
    images=[]
    #加载图片
    for path in files_path:
        print(path)
        image = load_img(path, target_size=(224,224))
        #图片进行数组转换
        image = img_to_array(image)
        print('-----')
        print(image.shape)
        #形状修改,输入到卷积需要四维[1,224,224,3],1代表batch,如果多张图片就使用多张
        image = image.reshape(1, image.shape[0], image.shape[1], image.shape[2])
        print(image.shape)
        # print(image)
        #对图片进行归一化处理
        images.append(preprocess_input(image))
    return images

def get_files_path(file_path):
    import os
    # 获取当前路径
    # path = os.getcwd()
    # files = os.listdir(os.path.join(path, "tiger/test_img"))
    files = os.listdir(file_path)
    file_count = len(files)
    print(file_count)
    files_path = []
    for i in range(file_count):
        print(i)
        files_path.append(os.path.join(file_path, files[i]))

    return files_path


#使用vgg16模型修改,完成迁移模型使用。
#迁移模型 主要是对已有模型参数进行微调
#通常我们只需要自己训练全连接层参数即可满足,而卷积层可以copy已有训练好的模型。
class TransferModel(object):

    def __init__(self):
        #定义训练和测试图片的变化方式,对图片进行标准化和图片增强
        #rescale: 数据标准化 归一化
        self.train_generator = ImageDataGenerator(rescale=1.0/255.0)
        self.test_generator = ImageDataGenerator(rescale=1.0/255.0)

        #指定目录
        self.train_dir = "./data/train"
        self.test_dir = "./data/test"
        #定义图片训练相关参数
        self.image_size = (224,224)
        self.batch_size = 32

        #定义基类模型, include_top 是否包含全连接层?
        # 我们不需要包含全连接层,只是用卷积层和池化层参数即可。
        #vgg共有5层卷积层
        self.base_mode = VGG16(weights='imagenet', include_top=False)

        #图片解码
        self.img_dict = {0:'bus',1:'dinosaur',2:'elephant',3:'flower',4:'horse'}

        pass


    def get_local_data(self):
        """
        读取本地图片数据
        使用ImageDataGenerator.flow_from_directory()
        :return:Traning dataset and Test dataset
        """
        train_gen = self.train_generator.flow_from_directory(
            directory=self.train_dir, #所要读取的图片文件目录
            target_size= self.image_size, #对读取到的图片size进行更改
            batch_size=self.batch_size, #每批次返回多少样本?
            class_mode='binary', #分类选项
            shuffle=True #是否打乱顺序
        )
        test_gen = self.train_generator.flow_from_directory(
            directory=self.train_dir,
            target_size= self.image_size,
            batch_size=self.batch_size, #每批次返回多少样本?
            class_mode='binary',
            shuffle=True #是否打乱顺序
        )
        # for data in train_gen:
        #     print(data[0].shape,data[1].shape)

        self.train_generator = train_gen
        self.test_generator = test_gen

        return train_gen,test_gen

    def refine_base_mode(self):
        """
        微调VGG16结构,
        原有卷积层5block后 + 全局平局池化+两个全连接层
        :return:
        """
        #1 获取原notop模型 [None,None,None,512]
        x = self.base_mode.outputs[0]
        #2在输出结果上加上全局池化,输出[None,1,1,512]
        x=keras.layers.GlobalAveragePooling2D()(x)
        #
        #新增全连接层两个,其中一个1024个神经元,最后一个我们图片要分的种类,5类
        x=keras.layers.Dense(1024,activation='relu')(x)
        y_predict = keras.layers.Dense(5,activation='softmax')(x)
        #定义新的迁移模型
        trans_model = keras.models.Model(inputs=self.base_mode.inputs, outputs = y_predict)
        # print(trans_model.summary())

        return trans_model

    #关闭VGG原有5个卷积池block,只训练后面全连接层参数
    def frezee_vgg_model(self):
        for layer in self.base_mode.layers:
            layer.trainable = False
        return None

    #对模型进行编译
    def compile(self,model):
        model.compile(optimizer='Adam',
                            loss='sparse_categorical_crossentropy',
                            metrics=['accuracy'])
        return None

    #训练模型
    def fit_generator(self,model):
        # #对模型训练过程监视
        # ckpt = keras.callbacks.ModelCheckpoint('./ckpt/trans_models.hdf5',
        #                                        monitor='val_acc',#需要监视的值
        #                                        save_best_only=True,
        #                                        save_weights_only=True,
        #                                        verbose=0,
        #                                        mode='auto',
        #                                        save_freq=1 #
        #                                         )
        #
        # #对模型进行训练
        # #这里必须用fit_generator,运行完发现代码提示旧版,新版直接用fit也行。。
        # #迭代次数
        # #validation_data  测试集
        # model.fit(self.train_generator,epochs=3,validation_data=self.test_generator,callbacks=[ckpt] )


        """
        很奇怪,ckpt存不起来,所以直接copy api范例测试
        然后就有了下面的代码,但仍然存不到
        太奇怪了
        找到问题点了,与api比较发现:
        tensorflow2.4 api中monitor参数不是val_acc,而是val_accuracy
        :param model: 
        :return: 
        """
        EPOCHS = 5
        checkpoint_filepath = './tmp/ckpt/checkpoint.h5'
        model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
                filepath=checkpoint_filepath,
                save_weights_only=True,
                monitor='val_accuracy',
                mode='max',
                save_best_only=True,
                save_freq='epoch')

        # Model weights are saved at the end of every epoch, if it's the best seen
        # so far.
        # model.fit(epochs=EPOCHS, callbacks=[model_checkpoint_callback])
        model.fit(self.train_generator, validation_data=self.test_generator,
                  epochs=EPOCHS, callbacks=[model_checkpoint_callback])

        # # The model weights (that are considered the best) are loaded into the model.
        # model.load_weights(checkpoint_filepath)
        return model

    #保存训练好的模型
    def save_model(self,model,PATH):
        model.save(filepath=PATH)
        return model

    #模型预测
    def predict(self,model):
        """
        用自己训练的模型进行预测
        :return:
        """
        #首先加载模型
        model.load_weights('./tmp/ckpt/checkpoint.h5')

        # #加载图片,并对图片进行处理
        # img = load_img(path='tiger/test_img/cc.jpg', target_size=(224, 224))
        # img= img_to_array(img)
        # print(img.shape) #[224,224,3]
        # #输出[224 224 3],单输入到卷积神经网络必须是四维
        # img = tf.reshape(img,shape=[1,img.shape[0],img.shape[1],img.shape[2]])
        # print(img.shape)
        # #归一化处理
        # img = preprocess_input(img)
        # #进行预测,得出一组概率,对概率进行转化
        # output = model.predict(img)
        # print(type(output))
        # print(np.max(output[0]))
        # print(output)
        # #output [[5.6389440e-18 1.0181949e-02 9.8981386e-01 4.2919934e-26 4.3013383e-06]]
        # #进行解码,获取概率最大的ndarray下标,对应的输出结果
        # index = np.argmax(output,axis=1)
        # print(index,output[0][index[0]])
        # print('预测结果是: ',self.img_dict[index[0]])

        #批量加载图片
        files_path = get_files_path(file_path="./tiger/test_img")
        print(files_path)
        images = load_img1(files_path)
        outputs=[]
        for image in images:
           outputs.append(model.predict(image))
        #outputs [[[5.6389440e-18 1.0181949e-02 9.8981386e-01 4.2919934e-26 4.3013383e-06]],[[5.6389440e-18 1.0181949e-02 9.8981386e-01 4.2919934e-26 4.3013383e-06]]]
        #进行解码,获取概率最大的ndarray下标,对应的输出结果
        num=0
        for output in outputs:
            num+=1
            index = np.argmax(output,axis=1)
            #print(index,output[0][index[0]])
            print('第%d张图片的预测结果是: %s,  ----预测概率为: %0.2f'%(num,self.img_dict[index[0]],output[0][index[0]]*100),"%")

        return None


if __name__ == '__main__':
    #生成迁移模型对象
    tm = TransferModel()
    MODEL_PATH = './tmp/model/transfer_cnn.h5'
    # 
    # #训练模型
    # #获取本地训练和测试图片数据
    # tm.get_local_data()
    # #重新定义原有vgg16模型
    # trans_model = tm.refine_base_mode()
    # #冻结原有vgg16 卷积层参数,仅针对全连接层参数进行训练
    # tm.frezee_vgg_model()
    # #模型编译,梯度优化
    # tm.compile(trans_model)
    # #模型训练
    # 
    # model = tm.fit_generator(trans_model)
    # tm.save_model(model,MODEL_PATH)
    # print('this is end lable')
    # 


    # 模型预测
    model = tm.refine_base_mode()
    tm.predict(model)
    print('this is end lable')

上边15张图片的输出结果,其中第12张预测错误,训练集图片还是太少。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,实现猫狗识别任务需要进行迁移学习,使用预训练的VGG16模型可以帮助我们快速搭建一个高效的卷积神经网络。 以下是基本的实现步骤: 1. 导入相关库 ```python import os import numpy as np from keras.preprocessing.image import load_img, img_to_array from keras.applications.vgg16 import VGG16, preprocess_input from keras.models import Model from keras.layers import Dense, Dropout, Flatten from keras.optimizers import Adam from sklearn.model_selection import train_test_split ``` 2. 加载数据集 我们使用的数据集是Kaggle上的猫狗分类数据集,可以从以下链接下载:https://www.kaggle.com/c/dogs-vs-cats/data 数据集中包含25,000张大小不一的猫和狗的图片,我们需要将其转换为模型可以处理的格式。 ```python # 定义数据集根目录 data_dir = 'path/to/data' # 定义图片大小和批次大小 img_size = (224, 224) batch_size = 32 # 加载数据集并将其转换为模型可以处理的格式 def load_dataset(): X = [] Y = [] for file in os.listdir(data_dir): if file.endswith('.jpg'): img = load_img(os.path.join(data_dir, file), target_size=img_size) img = img_to_array(img) X.append(img) if 'cat' in file: Y.append(0) else: Y.append(1) return np.array(X), np.array(Y) X, Y = load_dataset() ``` 3. 划分数据集 我们将数据集划分为训练集、验证集和测试集。训练集用于训练模型,验证集用于调整模型参数,测试集用于评估模型性能。 ```python # 划分数据集 X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42) X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=0.2, random_state=42) ``` 4. 加载预训练模型 我们使用Keras中已经训练好的VGG16模型作为特征提取器,将其载入并输出模型结构。 ```python # 加载预训练模型 base_model = VGG16(include_top=False, weights='imagenet', input_shape=img_size+(3,)) # 输出模型结构 for layer in base_model.layers: print(layer.name, layer.input_shape, layer.output_shape) ``` 5. 冻结模型权重 我们将模型的卷积层权重冻结,只训练新添加的全连接层的权重。 ```python # 冻结模型权重 for layer in base_model.layers: layer.trainable = False ``` 6. 构建模型 我们在VGG16模型的顶部添加了几个全连接层,用于分类任务。 ```python # 添加新的全连接层 x = base_model.output x = Flatten()(x) x = Dense(256, activation='relu')(x) x = Dropout(0.5)(x) x = Dense(1, activation='sigmoid')(x) # 构建新模型 model = Model(inputs=base_model.input, outputs=x) # 输出模型结构 model.summary() ``` 7. 训练模型 我们使用Adam优化器和二元交叉熵损失函数训练模型。 ```python # 编译模型 model.compile(optimizer=Adam(lr=0.001), loss='binary_crossentropy', metrics=['accuracy']) # 训练模型 history = model.fit( preprocess_input(X_train), Y_train, batch_size=batch_size, epochs=10, validation_data=(preprocess_input(X_val), Y_val), verbose=1 ) ``` 8. 评估模型 我们使用测试集评估模型性能。 ```python # 评估模型 score = model.evaluate(preprocess_input(X_test), Y_test, verbose=0) print('Test loss:', score[0]) print('Test accuracy:', score[1]) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值