Commit 1e37bffc authored by Administrator's avatar Administrator

上传新文件

parent 5d211f54
# coding: utf-8
import os
import tensorflow as tf
import keras
import keras.backend as K
from keras.models import Model
from keras.layers import Conv2D, DepthwiseConv2D
from keras.layers import Input, Reshape, Dropout, BatchNormalization, Flatten, Dense, Concatenate
from keras.layers import Activation, GlobalAveragePooling2D, GlobalMaxPooling2D, MaxPooling2D
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adam
from keras_applications.imagenet_utils import _obtain_input_shape
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
from sklearn.metrics import classification_report, confusion_matrix
from collections import defaultdict
from shutil import copy
import matplotlib.pyplot as plt
import seaborn as sns
print('Tensorflow version ', tf.__version__)
print('Keras version ', keras.__version__)
####################################
## 准备数据集
# 创造一个更小的子数据集(18类)
subset_class_names = [
'apple_pie', 'beignets', 'caesar_salad', 'ceviche', 'cheesecake',
'chicken_wings', 'chocolate_cake', 'creme_brulee', 'cup_cakes', 'donuts',
'french_fries', 'hamburger', 'hot_dog', 'lasagna', 'pizza', 'risotto',
'spaghetti_bolognese', 'steak'
]
nbr_classes = len(subset_class_names)
print(nbr_classes)
# 根据txt文件划分训练集和测试集
# max_imgs_per_class = np.inf
#def prepare_data(filepath, src, dest):
# classes_images = defaultdict(list)
# if os.path.isdir(dest) and len(os.listdir(dest)) == nbr_classes:
# print('Data preparation seems to be already done. Skipping')
# #return
# with open(filepath, 'r') as txt:
# paths = [read.strip() for read in txt.readlines()]
# for p in paths:
# food = p.split('/')
# if food[0] in subset_class_names: # Filter out the subset
# classes_images[food[0]].append(food[1] + '.jpg')
# for food in classes_images.keys():
# print("Copying images into ", food)
# if not os.path.exists(os.path.join(dest, food)):
# os.makedirs(os.path.join(dest, food))
# for count, i in enumerate(classes_images[food]):
# copy(os.path.join(src, food, i), os.path.join(dest, food, i))
# if count > max_imgs_per_class: break
# print("Copying Done!")
# Prepare train dataset by copying images from food-101/images to ./train using the file train.txt
#print("Creating train data...")
#prepare_data(dataset_dir+'./food-101/meta/train.txt', dataset_dir+'./food-101/images', './train')
# Prepare test data by copying images from food-101/images to ./test using the file test.txt
#print("Creating test data...")
#prepare_data(dataset_dir+'./food-101/meta/test.txt', dataset_dir+'./food-101/images', './test')
# Check how many files are in the train folder
nb_train_files = sum([len(files) for i, j, files in os.walk("../Image_Food/train")])
print("Total number of samples in train folder {}".format(nb_train_files))
# Check how many files are in the test folder
nb_test_files = sum([len(files) for i, j, files in os.walk("../Image_Food/test")])
print("Total number of samples in test folder {}".format(nb_test_files))
## 数据集划分结束
####################################
####################################
## 数据增强
# In this section use Keras ImageDataGenerator to create generators for train, validation and test data. The train and validation data are augmented through a series of transformation (shift, zoom, rotation...)
batch_size = 16
validation_split = 0.25 # 在训练集中再以3:1选取训练集和验证集
# ImageDataGenerator()是keras.preprocessing.image模块中的图片生成器,可以每一次给模型“喂”一个batch_size大小的样本数据,同时也可以
# 在每一个批次中对这batch_size个样本数据进行增强,扩充数据集大小,增强模型的泛化能力。比如进行旋转,变形,归一化等等。
data_gen = ImageDataGenerator(
rescale=1. / 255,
rotation_range=30,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.1,
zoom_range=[0.6, 1.1],
horizontal_flip=True,
brightness_range=[0.8, 1.3],
channel_shift_range=2.0,
validation_split=validation_split,
fill_mode='nearest')
train_generator = data_gen.flow_from_directory('../Image_Food/train',
target_size=(224, 224),
batch_size=batch_size,
class_mode='categorical',
subset='training')
validation_generator = data_gen.flow_from_directory('../Image_Food/train',
target_size=(224, 224),
batch_size=batch_size,
class_mode='categorical',
subset='validation')
test_data_gen = ImageDataGenerator(rescale=1. / 255)
test_generator = test_data_gen.flow_from_directory('../Image_Food/test',
target_size=(224, 224),
batch_size=nb_test_files,
class_mode='categorical')
## 数据环节结束
####################################
####################################
### 创造变体模型
## 压缩算子模块
# 普通conv(conv,bn,relu)
def _conv_block(inputs, filters, alpha, block_id, kernel=(3, 3), strides=(1, 1)):
filters = int(filters * alpha)
x = Conv2D(filters,
kernel,
padding='same',
use_bias=False,
strides=strides,
name='conv%d' % block_id)(inputs)
x = BatchNormalization(axis= -1, name='conv%d_bn' % block_id)(x)
return Activation('relu', name='conv%d_relu' % block_id)(x)
# depth/point-wise卷积
def _depthwise_conv_block(inputs,
pointwise_conv_filters,
alpha,
depth_multiplier=1,
strides=(1, 1),
block_id=1):
pointwise_conv_filters = int(pointwise_conv_filters * alpha)
x = DepthwiseConv2D((3, 3),
padding='same',
depth_multiplier=depth_multiplier,
strides=strides,
use_bias=True,
name='conv%d_dw' % block_id)(inputs)
x = Conv2D(pointwise_conv_filters, (1, 1),
padding='same',
use_bias=False,
strides=(1, 1),
name='conv%d_pw' % block_id)(x)
x = BatchNormalization(axis=-1, name='conv%d_pw_bn' % block_id)(x)
return Activation('relu', name='conv%d_pw_relu' % block_id)(x)
# fire模块(conv,relu,concat,pool)
def fire_block(input, filters, alpha, block_id):
filters = int(filters/8 * alpha)
fire1_squeeze = Conv2D(filters, (1, 1), activation='relu', kernel_initializer='glorot_uniform',
name='conv%d_fire_squeeze' % block_id, data_format="channels_last")(input)
fire1_expand1 = Conv2D(filters * 4, (1, 1), activation='relu', kernel_initializer='glorot_uniform',
padding='same', name='conv%d_fire_expand1' % block_id, data_format="channels_last")(fire1_squeeze)
fire1_expand2 = Conv2D(filters * 4, (3, 3), activation='relu', kernel_initializer='glorot_uniform',
padding='same', name='conv%d_fire_expand2' % block_id, data_format="channels_last")(fire1_squeeze)
merge = Concatenate(axis=3)([fire1_expand1, fire1_expand2])
maxpool = MaxPooling2D(
pool_size=(2, 2), strides=(2, 2), name='conv%d_fire_maxpool' % block_id,
data_format="channels_last")(merge)
return maxpool
# svd分解模块(conv,bn,relu)
def svd_block(inputs, filters, alpha, beta, block_id, kernel, strides=(1, 1)):
filters = int(filters * alpha)
filters1 = int(filters * beta) # beta为svd中间卷积核缩放比例,可设为1/2和1/4等
x = Conv2D(filters1,
(1, kernel),
padding='same',
use_bias=False,
strides=strides,
name='conv%d_svd1' % block_id)(inputs)
x = Conv2D(filters,
(kernel, 1),
padding='same',
use_bias=False,
strides=strides,
name='conv%d_svd2' % block_id)(x)
x = BatchNormalization(axis= -1, name='conv%d_svd_bn' % block_id)(x)
return Activation('relu', name='conv%d_svd_relu' % block_id)(x)
# inception分解模块1(conv,bn,relu)
def inception_block1(inputs,
filters,
alpha,
block_id,
strides=(1, 1)):
filters = int(filters * alpha)
filters1 = filters * 0.25
x = Conv2D(filters1, (1, 1),
padding='same',
use_bias=False,
strides=(1, 1),
name='conv%d_inception1_pw1' % block_id)(inputs)
x = Conv2D(filters1, (3, 3),
padding='same',
use_bias=False,
strides=(1, 1),
name='conv%d_inception1' % block_id)(x)
x = Conv2D(filters, (1, 1),
padding='same',
use_bias=False,
strides=(1, 1),
name='conv%d_inception1_pw2' % block_id)(x)
x = BatchNormalization(axis=-1, name='conv%d_inception1_bn' % block_id)(x)
return Activation('relu', name='conv%d_inception2_relu' % block_id)(x)
# inception分解模块2(conv,bn,relu)
def inception_block2(inputs,
filters,
alpha,
block_id,
strides=(1, 1)):
filters = int(filters * alpha)
filters1 = filters * 0.5
son1 = Conv2D(filters1, (1, 3), activation='relu', kernel_initializer='glorot_uniform',
name='conv%d_inception2_son1' % block_id, data_format="channels_last")(inputs)
son2 = Conv2D(filters1, (3, 1), activation='relu', kernel_initializer='glorot_uniform',
name='conv%d_inception2_son2' % block_id, data_format="channels_last")(inputs)
merge = Concatenate(axis=3)([son1, son2])
x = BatchNormalization(axis=-1, name='conv%d_inception2_bn' % block_id)(merge)
return Activation('relu', name='conv%d_inception2_relu' % block_id)(x)
## 主体模型——变体
def create_Alexnet(input_shape=None,
alpha=0.25,
depth_multiplier=1,
dropout=1e-3,
classes=6):
if K.backend() != 'tensorflow':
raise RuntimeError('Only Tensorflow backend is currently supported, '
'as other backends do not support '
'depthwise convolution.')
if K.image_data_format() != 'channels_last':
raise RuntimeError('The channel_last data format is not available '
'You should set `image_data_format="channels_last"` '
'in your Keras config located at ~/.keras/keras.json.')
# depth/point-wise conv样例
# x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=n)
# fire样例
# x = fire_block(x, 1024, alpha, block_id=n)
# svd样例
# x = svd_block(x, filters=1024, alpha, beta=0.5, block_id, kernel=3, strides=(1, 1))
# inception1样例
# x = inception_block1(inputs, filters, alpha, block_id, strides=(1, 1))
# inception2样例
# x = inception_block2(inputs, filters, alpha, block_id, strides=(1, 1))
# 设定模型输入大小(shape——224, 224, 3)
input_shape = _obtain_input_shape(input_shape,
default_size=224,
min_size=32,
require_flatten=True,
data_format=K.image_data_format())
# 模型输入
img_input = Input(shape=input_shape)
# 模型本体
x = _conv_block(img_input, 96, alpha, kernel=(11, 11), strides=(4, 4), block_id=1)
x = MaxPooling2D(pool_size=[3, 3], strides=[2, 2], padding='valid', name='conv1_pool')(x)
x = _conv_block(x, 256, alpha, strides=(1, 1), block_id=2)
x = MaxPooling2D(pool_size=[3, 3], strides=[2, 2], padding='valid', name='conv2_pool')(x)
####################################
# svd变体
x = svd_block(x, 384, alpha, beta=0.75, block_id=3, kernel=3, strides=(1, 1))
x = svd_block(x, 384, alpha, beta=0.75, block_id=4, kernel=3, strides=(1, 1))
x = svd_block(x, 256, alpha, beta=0.75, block_id=5, kernel=3, strides=(1, 1))
####################################
x = MaxPooling2D(pool_size=[3, 3], strides=[2, 2], padding='valid', name='conv5_pool')(x)
# 模型输出——GAP
shape = (1, 1, int(256 * alpha))
x = GlobalAveragePooling2D()(x)
x = Reshape(shape, name='reshape_1')(x)
x = Dropout(dropout, name='dropout')(x)
x = Conv2D(classes, (1, 1), padding='same', name='conv_preds')(x)
x = Activation('softmax', name='act_softmax')(x)
output = Reshape((classes, ), name='reshape_2')(x)
# 定义模型
model = Model(img_input, output, name='Food_4th_svd_0.75')
return model
### 模型环节结束
####################################
####################################
## 模型训练——整个流程:会话开启→定义模型→设置CKPT→编译模型(设置loss、优化器)→开始训练(训练的一些超参数)
# 开启会话
K.clear_session()
# 定义模型,这个模型就存在于K的这个会话
model = create_Alexnet(input_shape=None,
alpha=0.25,
depth_multiplier=1,
dropout=0.2,
classes=nbr_classes)
# model.load_weights('./4th_svd_0.75_Food.h5', by_name=True)
model.load_weights('./4th_svd_0.75_Food_150.h5', by_name=True)
# 冻结权重
for layer in model.layers:
# print(layer.name)
if 'conv3' not in layer.name and 'conv4' not in layer.name and 'conv5' not in layer.name and 'conv_preds' not in layer.name:
layer.trainable = False
print(model.trainable_weights)
# 验证是否加载/冻结成功
conv1 = model.get_layer('conv1').get_weights()[0]
conv2 = model.get_layer('conv2').get_weights()[0]
model.summary()
# 训练Model——demo数据集和CPU训练
# CKPT,设置保存点,保存结果是.h5文件(包含structure和weights),保存的就是这个K会话中的模型
checkpointer = ModelCheckpoint('4th_svd_0.75_Food_150.h5',
monitor='val_acc', # 需要监视的值,通常为:val_acc 或 val_loss 或 acc 或 loss
save_best_only=True,
verbose=1)
# 编译Model,这里设定的就是loss和优化算法
model.compile(loss='categorical_crossentropy',
optimizer=Adam(0.0002),
metrics=['accuracy'])
# 开始训练,这里设定轮数,训练一共多少步,验证一共多少步,记得调动checkpointer
history = model.fit_generator(train_generator,
epochs=0,
validation_data=validation_generator,
steps_per_epoch=int( (nb_train_files*(1-validation_split)) / batch_size),
validation_steps=int( (nb_train_files*(validation_split)) / batch_size),
callbacks=[checkpointer])
# 记录loss日志
# acc_history = history.history["acc"]
# valacc_history = history.history["val_acc"]
# loss_history = history.history["loss"]
# valloss_history = history.history["val_loss"]
# numpy_acc_history = np.array(acc_history)
# numpy_valacc_history = np.array(valacc_history)
# numpy_loss_history = np.array(loss_history)
# numpy_valloss_history = np.array(valloss_history)
# np.savetxt("acc_history_svd_0.75_150.txt", numpy_acc_history, delimiter=",")
# np.savetxt("valacc_history_svd_0.75_150.txt", numpy_valacc_history, delimiter=",")
# np.savetxt("loss_history_svd_0.75_150.txt", numpy_loss_history, delimiter=",")
# np.savetxt("valloss_history_svd_0.75_150.txt", numpy_valloss_history, delimiter=",")
## 训练完毕
####################################
####################################
## 模型验证
# 画出accuracy and loss
#def plot_history(history, field, title):
# plt.title(title)
# plt.plot(history.history[field])
# plt.plot(history.history['val_{}'.format(field)])
# plt.ylabel(field)
# plt.xlabel('epoch')
# plt.legend(['train_{}'.format(field), 'validation_{}'.format(field)],
# loc='best')
# plt.show()
#plot_history(history, 'acc', 'Food Detection')
#plot_history(history, 'loss', 'Food Detection')
# 评估network
def evaluate_network(model, test_data):
X_test, y_test = test_data # 分别为test的输入和标签输出
y_pred = model.predict(X_test) # test的预测输出
y_test = np.argmax(y_test, axis=1)
y_pred = np.argmax(y_pred, axis=1)
# 显示测试报告
print(classification_report(y_test, y_pred, target_names=subset_class_names, digits=4))
# 可视化测试结果
# cm = confusion_matrix(y_test, y_pred)
# fig, ax = plt.subplots(1, 1, figsize=(15, 15))
# ax = sns.heatmap(cm,
# square=True,
# annot=True,
# yticklabels=subset_class_names,
# xticklabels=subset_class_names,
# cbar=False,
# fmt='d')
test_data = next(test_generator) # test部分是test集一下子全test了
evaluate_network(model, test_data)
## 验证结束
####################################
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment