from tensorflow.keras import layers, models, Model, Sequential
import tensorflow as tf
import os
import json
import matplotlib.pyplot as plt
import csv
#.........................第一部分先建立model...............................................................#
def VGG(feature, im_height=224, im_width=224, num_classes=1000): #num_calsses可以改成你自己的类别
# tensorflow中的tensor通道排序是NHWC
input_image = layers.Input(shape=(im_height, im_width, 3), dtype="float32")
x = feature(input_image) #因为vggnet的卷积层十分规律,这里做了个for循环
x = layers.Flatten()(x)
x = layers.Dropout(rate=0.5)(x)
x = layers.Dense(2048, activation='relu')(x) #论文原作者使用的是4096,这里神经元的个数视数据集而定
x = layers.Dropout(rate=0.5)(x) #dropout层选择随机失活的比例可按训练结果修改
x = layers.Dense(2048, activation='relu')(x)
#x = layers.Dropout(rate=0.5)(x) #这一层也可以加上dropout层,具体参考自己的训练结果
x = layers.Dense(num_classes)(x)
output = layers.Softmax()(x)
model = models.Model(inputs=input_image, outputs=output)
return model
def features(cfg):
feature_layers = []
for v in cfg:
if v == "M":
feature_layers.append(layers.MaxPool2D(pool_size=2, strides=2)) #最大池化下采样层,size=2*2,步长为2
else:
conv2d = layers.Conv2D(v, kernel_size=3, padding="SAME", activation="relu")
feature_layers.append(conv2d)
return Sequential(feature_layers, name="feature")
cfgs = {
'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}
def vgg(model_name="vgg16", im_height=224, im_width=224, num_classes=1000):
assert model_name in cfgs.keys(), "not support model {}".format(model_name)
cfg = cfgs[model_name]
model = VGG(features(cfg), im_height=im_height, im_width=im_width, num_classes=num_classes)
return model
#................................模型建立完毕................................................................#
'''
第二部分就是输入数据训练自己的网络了,这一块有很多问题,我注释的会比较多。
分成三个情况:一是训练tensorflow自带的数据集,这个最简单
二是训练自己的数据集,这个需要自己划分好自己的文件夹,分好训练集和测试集等前期工作
三是基于迁移学习来训练自己的网络,这一块前置条件依然是处理好自己的数据集
***我会在后面的附录上给上我自己的数据集的截图供大家参考
'''
'''
....................开始第一种情况,以cifar——10数据集为例.......................................................
'''
# import部分放在前面了
cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
# 由于是自带的数据集可以直接加载进去
x_train, x_test = x_train / 255.0, x_test / 255.0
print(x_train.shape)
# 这里的归一化处理是将所有的像素值归一在【0,1】
'''
...........本来接下去是图像预处理环节,自带的数据集好像意义不大,自己动手弄一下,我在第二种情况中详细说明..............................
'''
model = vgg('vgg16',32,32,10) #这里的四个参数前面有定义,由于是cifar10数据集
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
'''
model.compile函数参数详解: model.compile(optimizer = 优化器,
loss = 损失函数,
metrics = ["准确率”])
'''
############下面这几行代码是保存网络权重和断点续训的功能#######
checkpoint_save_path = "./checkpoint/VGG16.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path)
######################################################
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
'''
tf.keras.callbacks.ModelCheckpoint参数:
filepath:string,保存模型文件的路径。
monitor:要监测的数量。
verbose:详细信息模式,0或1。
save_best_only:如果save_best_only=True,被监测数量的最佳型号不会被覆盖。
mode:{auto,min,max}之一。如果save_best_only=True,那么是否覆盖保存文件的决定就取决于被监测数据的最大或者最小值。对于val_acc,这应该是max,对于val_loss这应该是min,等等。在auto模式中,方向是从监测数量的名称自动推断出来的。
save_weights_only:如果为True,则仅保存模型的权重(model.save_weights(filepath)),否则保存完整模型(model.save(filepath))。
period:检查点之间的间隔(epoch数)。
初始参数设置如下:
filepath,
monitor='val_loss',
verbose=0,
save_best_only=False,
save_weights_only=False,
mode='auto',
period=1
'''
history = model.fit(x_train, y_train, batch_size=32, epochs=2, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
'''
model.fit函数参数:
fit(x=None,
y=None,
batch_size=None,
epochs=1, #迭代的次数
verbose=1, #日志展示,0:为不在标准输出流输出日志信息1:显示进度条2:每个epoch输出一行记录
callbacks=None, #其中的元素是keras.callbacks.Callback的对象。这个list中的回调函数将会在训练过程中的适当时机被调用,参考回调函数
validation_split=0.0, #浮点数0-1之间用作验证集的训练数据的比例。模型将分出一部分不会被训练的验证数据,并将在每一轮结束时评估这些验证数据的误差和任何其他模型指标
validation_data=None, #元组 (x_val,y_val) 或元组 (x_val,y_val,val_sample_weights), 用来评估损失,以及在每轮结束时的任何模型度量指标。模型将不会在这个数据上进行训练。这个参数会覆盖 validation_split。
shuffle=True, #是否在每轮迭代之前混洗数据
class_weight=None,
sample_weight=None,
initial_epoch=0,
steps_per_epoch=None, #一个epoch包含的步数(每一步是一个batch的数据送入),当使用如TensorFlow数据Tensor之类的输入张量进行训练时,默认的None代表自动分割,即数据集样本数/batch样本数。
validation_steps=None, #仅当steps_per_epoch被指定时有用,在验证集上的step总数
validation_freq=1, #实施验证的频率。当等于1时代表每个epoch结束都验证一次
max_queue_size=10, #没有找到这个参数的作用,等会再查资料
workers=1, #最大线程数
use_multiprocessing=False) #是否启用多线程
。
'''
model.summary()
########################这个是保存权重文件###############
# print(model.trainable_variables)
# file = open('./weights.txt', 'w')
# for v in model.trainable_variables:
# file.write(str(v.name) + '\n')
# file.write(str(v.shape) + '\n')
# file.write(str(v.numpy()) + '\n')
# file.close()
###################一般我是不保存成txt########################
############################################### show ###############################################
# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
file='./history.csv'
with open(file,'a',encoding='utf-8',newline='')as f:
writer=csv.writer(f)
writer.writerow(['acc','val_acc','loss','val_loss'])
for i in range(len(acc)):
writer.writerow([acc[i], val_acc[i], loss[i], val_loss[i]])
plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()