在学习了大约2周的机器学习和深度学习的基础知识,并跑了十多个模型之后,老师给我布置了一项真正的实际任务:利用已经收集到的图片信息,构建并训练模型,一期目标使得精度达到84%,二期目标使得精度达到90%。
一开始并没有认识到,真正的实际数据会和demo中最后的结果差距如此之大,使得自己消沉了一段时间,不过经过将近15天的努力,总算是完成了任务,亦有所收获。
故,在此把我这段时间踩过的坑都一一标注出来,希望可以帮助到那些初次接触卷积神经网络的新人(虽然我也是啦)
# 建立模型
model = Sequential()
# 这里使用卷积神经网络,传入100*100像素的彩色图片,传出时为94*94*32
model.add(Conv2D(32, (7, 7), strides = (1, 1), name = 'conv0', input_shape = (100, 100, 3)))
# 使用批标准化
model.add(BatchNormalization(axis = 3, name = 'bn0'))
# 激活函数为ReLU(线性整流函数)
model.add(Activation('relu'))
# 对于空间数据的最大池化
model.add(MaxPooling2D((2, 2), name='max_pool'))
model.add(Conv2D(64, (3, 3), strides = (1, 1), name="conv1"))
model.add(Activation('relu'))
# 对于空间数据的平均池化
model.add(AveragePooling2D((3, 3), name='avg_pool'))
model.add(Flatten())
model.add(Dense(500, activation="relu", name='rl'))
model.add(Dropout(0.5))
model.add(Dense(y.shape[1], activation='softmax', name='sm'))
# 编译模型
model.compile(loss='categorical_crossentropy', optimizer="adam", metrics=['accuracy'])
当然,在此之前我们要先处理图片数据和标签
实现代码:
train_df = pd.read_csv("input/train.csv")
# 这个函数用来预处理图片数据,把图片转化为100*100像素
# 的彩色图片,其中data是数据集,m是数据集中图片的数目,dataset是图片存放的目录
def prepareImages(data, m, dataset):
print("Preparing images")
X_train = np.zeros((m, 100, 100, 3))
count = 0 # 这个count用来记录图片处理的数目,函数中实现了每处理500张就通报一声,其实没啥用
for fig in data['Image']:
# fig读到的是Image这一特征下的具体数据,也就是图片名(一个接一个的)
img = image.load_img("input/" + dataset + "/" + fig, target_size=(100, 100, 3))
x = image.img_to_array(img)
# preprocess_input()函数完成数据预处理的工作
x = preprocess_input(x)
# 然后把处理过后的图片信息记录到x_train中去
X_train[count] = x
if count % 500 == 0:
print("Processing image: ", count + 1, ", ", fig)
count += 1
return X_train
# 这个函数用来处理标签,返回两个输出,y和label_encoder,
# 前者是one-hot编码形式,如:[1;2;3]会编码成[1 0 0; 0 1 0; 0 0 1]。
# 后者是分组的组号,如:[1;2;2;4;2]会编码成[1;2;2;3;2]。
def prepare_labels(y):
values = np.array(y)
# 为标签分组,并用label_encoder记录,为以后测试集分好组之后重新从组号转变成对应的描述做准备
label_encoder = LabelEncoder()
integer_encoded = label_encoder.fit_transform(values)
# print(integer_encoded)
# 将分好组的标签转化为one-hot编码,并用y记录
onehot_encoder = OneHotEncoder(sparse=False)
integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)
onehot_encoded = onehot_encoder.fit_transform(integer_encoded)
# print(onehot_encoded)
y = onehot_encoded
return y, label_encoder
# 处理训练数据,并归一化
X = prepareImages(train_df, train_df.shape[0], "train")
X /= 255
# 处理标签
y, label_encoder = prepare_labels(train_df['Id'])
值得注意的是,此时的图片数据分为两个文件夹,train和valication,其信息被写在两个.csv文件中。
而跑起来之后,我们会发现,最终的验证准确率非常低,仅仅有23.31%
一些确定的参数
class_num = 168
num_train_sample = 20967
num_validation_sample = 9097
img_width = 356
img_height = 356
epochs = 40
batch_size = 128
至此,第一阶段结束。
# 实例化一个VGG16卷积基
base_model = VGG16(weights='imagenet',
include_top=False,
input_shape=(img_width, img_height, 3))
# 冻结卷积基
base_model.trainable = False
# 自定义顶层网络
top_model = Sequential()
top_model.add(layers.Flatten(input_shape=base_model.output_shape[1:]))
top_model.add(layers.Dense(4096, activation='relu'))
top_model.add(layers.Dropout(0.5))
top_model.add(layers.Dense(1024, activation='relu'))
top_model.add(layers.Dropout(0.5))
top_model.add(layers.Dense(class_num, activation='softmax'))
# 实例化这个模型
model = Model(inputs=base_model.input,
outputs=top_model(base_model.output))
# 编译模型
model.compile(loss='categorical_crossentropy',
optimizer=optimizers.RMSprop(lr=2e-5),
metrics=['acc'])
训练模型后,验证集的准确率只有54.52%
# 可以对训练集进行数据增强处理
train_datagen = ImageDataGenerator(rescale=1./255)
# 测试集不许动,归一化完了之后不许动
validation_datagen = ImageDataGenerator(rescale=1./255)
训练模型后,验证集的准确率上涨到60.17%。
进一步,我们可以调整学习率lr,经过多次调试,最终我们将初试学习率调整为lr = 1e-5。
训练模型后,验证集的准确率上涨到63.66%。
可以的话,我们可以在回调函数中加入对学习率的调整。
代码实现
# 回调函数用来监控val_loss,一旦超过5次迭代没有下降,降低学习率
callback_list = [
keras.callbacks.ReduceLROnPlateau(
monitor='val_loss',
factor=0.1,
patience=5,
verbose=1,
)
]
训练模型后,验证集的准确率上涨到66.34%。
至此,我们将自己能做的一些简单的调整执行完毕,很显然,不仅最终的结果依旧不能满足任务的要求,而且由于VGG16网络的特点,需要训练大量的参数,不仅让整个网路臃肿,而且训练效率很低。
故而,我选择换一种预训练网络,这里,就让我们请出本次任务的最大功臣:ResNet50。
有关ResNet50的内容太多,我们下一期慢慢讲
关于深度学习中的batch_size
https://www.cnblogs.com/gengyi/p/9853664.html