准备训练图片,和测试图片
对模型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张预测错误,训练集图片还是太少。。