文章目录

一、介绍cifar 数据集二、resnet网络简介a. 网络结构图b. 使用resnet进行炼丹:c. 第一次炼丹:d. 第二次炼丹:完整代码(jupyter notebook)

三、vggnet网络简介a. vggnet结构图b. vggnet的特点c. 第三次炼丹(使用vgg16D 最高的准确率曾达到69%)完整代码:

四、本轮学习中学到的一些小技巧:1.过拟合问题如何处理过拟合产生的原因:如何解决过拟合问题:注意事项

2.超参数的选取batchsize(批大小):epoch(训练次数):learning_rate(学习率):

3.关于不同loss&&val_loss变化的处理方法:

参考文献:

一、介绍cifar 数据集

CIFAR-10数据集由10个类的60000个32x32彩色图像组成,每个类有6000个图像。有50000个训练图像和10000个测试图像。数据集分为五个训练批次和一个测试批次,每个批次有10000个图像。测试批次包含来自每个类别的恰好1000个随机选择的图像。训练批次以随机顺序包含剩余图像,但一些训练批次可能包含来自一个类别的图像比另一个更多。总体来说,五个训练集之和包含来自每个类的正好5000张图像。以下是数据集中的类,以及来自每个类的10个随机图像: 而cifar-100数据集是由cifar-10拓展而来,这个数据集就像CIFAR-10,除了它有100个类,每个类包含600个图像。,每类各有500个训练图像和100个测试图像。CIFAR-100中的100个类被分成20个超类。每个图像都带有一个**“精细”标签**(它所属的类)和一个“粗糙”标签(它所属的超类)

二、resnet网络简介

残差神经网络(ResNet)是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的。ResNet 在2015 年的ILSVRC(ImageNet Large Scale Visual Recognition Challenge)中取得了冠军。

残差神经网络的主要贡献是发现了“退化现象(Degradation)”,并针对退化现象发明了 “快捷连接(Shortcut connection)”,极大的消除了深度过大的神经网络训练困难问题。神经网络的“深度”首次突破了100层、最大的神经网络甚至超过了1000层。

resnet网络最主要的特点就是提出了residual结构使得深层的网络也可以继续优化准确率。

a. 网络结构图

b. 使用resnet进行炼丹:

网络结构: (resnet网络里面直接将BN包含在内) resnet网络 + 全连接层f-c 512 + BN + Dropout(0.5) + f-c (100) softmax

c. 第一次炼丹:

学习率learning_rate为0.001

对batchsize64.128,256多次实验发现batchsize = 64 效果较好

优化器为adam

训练次数epoch = 50 对四种不同结果实践下来的发现在我写的这个结构中resnet34和resnet50表现较好,因此我选择其中一个resnet34继续进行优化

resnet34 Model accuracy图

resnet34 Model loss 图

d. 第二次炼丹:

此次炼丹,我将输出的照片进行一些图像增强处理 。如图像旋转、图像切割、水平竖直平移、垂直翻转等等

准确率达到近55% resnet34 Model accuracy图

resnet34 Model loss 图

完整代码(jupyter notebook)

from tensorflow.keras import layers, Model, Sequential

from __future__ import print_function

from tensorflow import keras

from tensorflow.keras.datasets import cifar100

from tensorflow.keras.preprocessing.image import ImageDataGenerator

from tensorflow.keras.models import Sequential

from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten,BatchNormalization

from tensorflow.keras.layers import Conv2D, MaxPooling2D

import matplotlib.pyplot as plt

%matplotlib inline

class BasicBlock(layers.Layer): # 继承于layer类,是一个块

expansion = 1 # 用于卷积核个数发生改变的卷积层

def __init__(self, out_channel, strides=1, downsample=None, **kwargs):

'''

out_channel : 输出的数据的通道数,也就是第一层使用的卷积核的个数

strides: 卷积步长,默认为1 实残差结构,为2的时候是虚连接残差结构

downsample: 下采样操作使得图像缩小,让Block既有实残差结构也有虚残差结构

'''

super(BasicBlock, self).__init__(**kwargs)

self.conv1 = layers.Conv2D(out_channel, kernel_size=3, strides=strides,

padding="SAME", use_bias=False)

self.bn1 = layers.BatchNormalization(momentum=0.9, epsilon=1e-5)

# -----------------------------------------

self.conv2 = layers.Conv2D(out_channel, kernel_size=3, strides=1,

padding="SAME", use_bias=False)

self.bn2 = layers.BatchNormalization(momentum=0.9, epsilon=1e-5)

# -----------------------------------------

self.downsample = downsample

self.relu = layers.ReLU()

self.add = layers.Add()

def call(self, inputs, training=False):

identity = inputs

if self.downsample is not None:

identity = self.downsample(inputs)

x = self.conv1(inputs)

x = self.bn1(x, training=training)

x = self.relu(x)

x = self.conv2(x)

x = self.bn2(x, training=training)

x = self.add([identity, x])

x = self.relu(x)

return x

class Bottleneck(layers.Layer):

expansion = 4

'''

strides : = 1 左边的实连接结构 = 2 :对应右边实残差结构

out_channel : 输入/出 的数据的通道数,也就是第一层使用的卷积核的个数

expansion : 扩展系数:后面的卷积核的个数是前面的倍数

downsample : 是传入的一个下采样函数,在后面由自己定义。

'''

def __init__(self, out_channel, strides=1, downsample=None, **kwargs):

super(Bottleneck, self).__init__(**kwargs)

self.conv1 = layers.Conv2D(out_channel, kernel_size=1, use_bias=False, name="conv1")

self.bn1 = layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name="conv1/BatchNorm")

# -----------------------------------------

self.conv2 = layers.Conv2D(out_channel, kernel_size=3, use_bias=False,

strides=strides, padding="SAME", name="conv2")

self.bn2 = layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name="conv2/BatchNorm")

# -----------------------------------------

self.conv3 = layers.Conv2D(out_channel * self.expansion, kernel_size=1, use_bias=False, name="conv3")

self.bn3 = layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name="conv3/BatchNorm")

# -----------------------------------------

self.relu = layers.ReLU()

self.downsample = downsample

self.add = layers.Add()

# 定义数据流的流向

def call(self, inputs, training=False):

identity = inputs

if self.downsample is not None: #None 虚残差连接结构 is not None 实残差连接结构

identity = self.downsample(inputs)

x = self.conv1(inputs)

x = self.bn1(x, training=training)

x = self.relu(x)

x = self.conv2(x)

x = self.bn2(x, training=training)

x = self.relu(x)

x = self.conv3(x)

x = self.bn3(x, training=training)

x = self.add([x, identity])

x = self.relu(x)

return x

def _make_layer(block, in_channel, channel, block_num, name, strides=1):

downsample = None

'''

in_channel : 输出的通道数

channel : 本层输出的通道数

block_num : 该残差块的个数 x3 x6等

name : 残差结构的名字 如第一个和第二个

实线的残差结构都是stride 都等于1 虚线的会有等于2的时候

50层以上 cov2第一层对应的也是虚残差结构 但是cov2的高度宽不需要发生改变 而cov345的虚残差结构的高宽也要变

'''

if strides != 1 or in_channel != channel * block.expansion:

# 50 层以上的网络才会满足以上条件出现stride=2 以及in_channel != channel * block.expansion

downsample = Sequential([

layers.Conv2D(channel * block.expansion, kernel_size=1, strides=strides,

use_bias=False, name="conv1"),

layers.BatchNormalization(momentum=0.9, epsilon=1.001e-5, name="BatchNorm")

], name="shortcut")

# shortcut : 竭尽分支

layers_list = []

layers_list.append(block(channel, downsample=downsample, strides=strides, name="unit_1"))

for index in range(1, block_num):

layers_list.append(block(channel, name="unit_" + str(index + 1)))

return Sequential(layers_list, name=name)

def _resnet(block, blocks_num, im_width=224, im_height=224, num_classes=1000, include_top=True):

# tensorflow中的tensor通道排序是NHWC

# (None, 224, 224, 3)

'''

block : 传入对应的网络结构 basicblock : 18 和 34 层网络结构 bottleblock:50 101 151

blocks_num : 每一层 残差结构的个数 倍数

im_height : 高度

im_width : 宽度

num_classes : 最终的分类个数

include_top :

'''

input_image = layers.Input(shape=(im_height, im_width, 3), dtype="float32")

x = layers.Conv2D(filters=64, kernel_size=7, strides=2,

padding="SAME", use_bias=False, name="conv1")(input_image)

x = layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name="conv1/BatchNorm")(x)

x = layers.ReLU()(x)

x = layers.MaxPool2D(pool_size=3, strides=2, padding="SAME")(x)

# 每调用一个make_layer 生成一个残差结构

x = _make_layer(block, x.shape[-1], 64, blocks_num[0], name="block1")(x)

x = _make_layer(block, x.shape[-1], 128, blocks_num[1], strides=2, name="block2")(x)

x = _make_layer(block, x.shape[-1], 256, blocks_num[2], strides=2, name="block3")(x)

x = _make_layer(block, x.shape[-1], 512, blocks_num[3], strides=2, name="block4")(x)

# 进行全局平均池化操作

if include_top:

x = layers.GlobalAvgPool2D()(x) # pool + flatten

x = layers.Dense(num_classes, name="logits")(x)

predict = layers.Softmax()(x)

else:

predict = x

model = Model(inputs=input_image, outputs=predict)

return model

def resnet18(im_width=224, im_height=224, num_classes=1000, include_top=True):

return _resnet(BasicBlock, [2, 2, 2, 2], im_width, im_height, num_classes, include_top)

def resnet34(im_width=224, im_height=224, num_classes=1000, include_top=True):

return _resnet(BasicBlock, [3, 4, 6, 3], im_width, im_height, num_classes, include_top)

def resnet50(im_width=224, im_height=224, num_classes=1000, include_top=True):

return _resnet(Bottleneck, [3, 4, 6, 3], im_width, im_height, num_classes, include_top)

def resnet101(im_width=224, im_height=224, num_classes=1000, include_top=True):

return _resnet(Bottleneck, [3, 4, 23, 3], im_width, im_height, num_classes, include_top)

(x_train, y_train), (x_test, y_test) = cifar100.load_data()

print('x_train shape:', x_train.shape)

print(x_train.shape[0], 'train samples')

print(x_test.shape[0], 'test samples')

#定义resnet 后面的网络 一层faltten 两层全连接f-c + Dropout(0.5)

import tensorflow as tf

model1 = resnet34(im_width=32, im_height=32, num_classes=100, include_top=False)

model = Sequential(model1) #model.add 只有在Sequential中才可以进行add

model.add(Flatten())

model.add(Dense(512,activation='relu'))

model.add(BatchNormalization())

model.add(Dropout(0.5))

model.add(Dense(100,activation='softmax'))

opt=tf.optimizers.Adam(learning_rate=0.01)

model.compile(optimizer=opt,loss='sparse_categorical_crossentropy',metrics=['accuracy'])

model.summary()

model.evaluate(x_test,y_test)

# Data augmentation

train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(

rotation_range=20,

width_shift_range=0.1, # Shift picture

height_shift_range=0.1,

horizontal_flip=True, # Might has flip picture but there is no upside down thing

fill_mode='nearest') # Fill missing pixels

train_gen = train_datagen.flow(x_train, y_train, batch_size=128)

print(len(train_gen))

# Train the model with the new callback

history = model.fit(train_gen,

validation_data=(x_test,y_test),

epochs=100,

# Not specify the batch_size since data is from generators (since they generate batches)

steps_per_epoch=391, # Total number of steps (batches of samples) before a epoch finished,

# default is the number of samples (50000) divided by the batch size (32)

validation_steps=x_test.shape[0])

# 绘制训练 && 验证的准确率

plt.plot(history.history['accuracy'])

plt.plot(history.history['val_accuracy'])

plt.title('Model accuracy')

plt.ylabel('Accuracy')

plt.xlabel('Epoch')

plt.legend(['Train', 'Test'], loc='upper left')

plt.show()

# 绘制训练 && 验证的损失值

plt.plot(history.history['loss'])

plt.plot(history.history['val_loss'])

plt.title('Model loss')

plt.ylabel('Loss')

plt.xlabel('Epoch')

plt.legend(['Train', 'Test'], loc='upper left')

plt.show()

三、vggnet网络简介

VGG Net由牛津大学的视觉几何组(Visual Geometry Group)和 Google DeepMind公司的研究员一起研发的的深度卷积神经网络,在 ILSVRC 2014 上取得了第二名的成绩,将 Top-5错误率降到7.3%。它主要的贡献是展示出网络的深度(depth)是算法优良性能的关键部分,其具体网络结构图如下。

a. vggnet结构图

b. vggnet的特点

1.结构非常简洁,整个网络都使用了同样大小的卷积核尺寸(3x3)和最大池化尺寸(2x2);

2.用几个小滤波器(3x3)卷积层的组合代替一个大滤波器(5x5或7x7)卷积层;

3.验证了通过不断加深网络结构可以提升性能。

c. 第三次炼丹(使用vgg16D 最高的准确率曾达到69%)

训练进程和优化进程如下图

(下面介绍最优网络结构) 网络结构: vgg16 D + Dropout(0.5) + f-c connection512 + BN + Dense(100) softmax 变化之处: 预处理:给输入数据标准化

1·转化为独热编码

2·BN和Dropout(0.4) 直接加入到vgg16D之中,两层卷积之间加Dropout,每层卷积之后加BN

3·优化器:sgd

4·学习率learning_rate = 0.1 并且学习选择动态衰减

5·训练次数training_epochs = 100

6·批大小 batch_size = 128

7·momentum = 0.9 #SGD加速动量

8·lr_decay = 1e-6 #学习衰减

9·lr_drop = 20 #衰减倍数:每经过20轮训练,学习率就衰减为原来的一半

10·kernel_regularizer=tf.keras.regularizers.l2(0.0005) 采用l2正则化 准确率:

损失变化图: 损失变化图

完整代码:

# vgg16D 实现cifar100数据集识别

import numpy as np

import tensorflow as tf

import matplotlib.pyplot as plt

from tensorflow.keras.preprocessing.image import ImageDataGenerator

#数据准备

(x_train,y_train), (x_test, y_test)=tf.keras.datasets.cifar100.load_data()

# 数据预处理部分 z-score标准化

mean = np.mean(x_train, axis=(0, 1, 2, 3))#四个维度 批数 像素x像素 通道数

std = np.std(x_train, axis=(0, 1, 2, 3))

x_train = (x_train - mean) / (std + 1e-7)#trick 加小数点 避免出现整数

x_test = (x_test - mean) / (std + 1e-7)

#转换one-hot独热映射

y_train = tf.keras.utils.to_categorical(y_train, 100)

y_test = tf.keras.utils.to_categorical(y_test, 100)

# 构建模型

def vgg16D():

model = tf.keras.Sequential()

# 第一块

#conv1

model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=[3, 3], input_shape=(32, 32, 3), strides=1,activation='relu',padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dropout(0.4))

model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

#最大池化层1

model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))

# 第二块

#conv3

model.add(tf.keras.layers.Conv2D(filters=128, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dropout(0.4))

#conv4

model.add(tf.keras.layers.Conv2D(filters=128, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

#最大池化层2

model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))

#conv5

model.add(tf.keras.layers.Conv2D(filters=256, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dropout(0.4))

#conv6

model.add(tf.keras.layers.Conv2D(filters=256, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dropout(0.4))

#conv7

model.add(tf.keras.layers.Conv2D(filters=256, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

#最大池化层3

model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))

#conv8

model.add(tf.keras.layers.Conv2D(filters=512, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dropout(0.4))

#conv9

model.add(tf.keras.layers.Conv2D(filters=512, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dropout(0.4))

#conv10

model.add(tf.keras.layers.Conv2D(filters=512, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

#最大池化层4

model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))

#conv11

model.add(tf.keras.layers.Conv2D(filters=512, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dropout(0.4))

#conv12

model.add(tf.keras.layers.Conv2D(filters=512, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dropout(0.4))

#conv13

model.add(tf.keras.layers.Conv2D(filters=512, kernel_size=[3, 3], strides=1,activation='relu',

padding='SAME', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

#最大池化层5

model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))

#f-c 三层

model.add(tf.keras.layers.Flatten())

model.add(tf.keras.layers.Dropout(rate=0.5))

model.add(tf.keras.layers.Dense(units=512,activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

model.add(tf.keras.layers.BatchNormalization())

model.add(tf.keras.layers.Dense(units=100))

model.add(tf.keras.layers.Activation('softmax'))

#查看摘要

model.summary()

return model

#超参设置

training_epochs = 100

batch_size = 128

learning_rate = 0.1

momentum = 0.9 #SGD加速动量

lr_decay = 1e-6 #学习衰减

lr_drop = 20 #衰减倍数

model = vgg16D()

# 每20个epoch 学习率缩小为原来的一半

def lr_scheduler(epoch):

return learning_rate * (0.5 ** (epoch // lr_drop))

reduce_lr = tf.keras.callbacks.LearningRateScheduler(lr_scheduler)

# 数据增强

datagen = ImageDataGenerator(

rotation_range=15, # 旋转的范围

width_shift_range=0.1,

height_shift_range=0.1,

horizontal_flip=True, # 随机水平翻转。

)

datagen.fit(x_train)

# 使用sgd优化器

optimizer = tf.keras.optimizers.SGD(learning_rate=learning_rate,

decay=1e-6, momentum=momentum, nesterov=True)

model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])

history = model.fit(datagen.flow(x_train, y_train,

batch_size=batch_size), epochs=training_epochs, callbacks=[reduce_lr],

steps_per_epoch=x_train.shape[0] // batch_size, validation_data=(x_test, y_test))

# 绘制训练 && 验证的准确率

plt.plot(history.history['accuracy'])

plt.plot(history.history['val_accuracy'])

plt.title('Model accuracy')

plt.ylabel('Accuracy')

plt.xlabel('Epoch')

plt.legend(['Train', 'Test'], loc='upper left')

plt.show()

# 绘制训练 && 验证的损失值

plt.plot(history.history['loss'])

plt.plot(history.history['val_loss'])

plt.title('Model loss')

plt.ylabel('Loss')

plt.xlabel('Epoch')

plt.legend(['Train', 'Test'], loc='upper left')

plt.show()

四、本轮学习中学到的一些小技巧:

1.过拟合问题如何处理

过拟合是指模型对训练数据拟合程度过当的情况,反映在评估指标上,就是模型在训练数据上表现很好,但是在测试数据和新数据上表现较差。换句话说,从误差=偏差+方差+噪声的角度去思考,过拟合指的就是偏差在可接受范围内而方差过高的现象,造成模型在训练数据上几乎完美,而在新数据上预测结果可能跟真实值相差过大

过拟合产生的原因:

1.训练次数epoch较多 2.数据量太小 3.数据质量很差:噪声较多 4.模型的复杂度太大,或者模型不适用 5.训练集和测试集样本的分布不同 6.特征太多,多于样本的大小

如何解决过拟合问题:

1. 模型层面 : 主要是减小模型的复杂度,减少参数个数和参数值

a. L1,L2正则化

具体内容可以参考这一篇文章 http://t.csdn.cn/pz9hK 可以直接在添加卷积层的时候加入到模型中

model.add(tf.keras.layers.Conv2D(filters=256, kernel_size=[3, 3],

strides=1,activation='relu',padding='SAME',

kernel_regularizer=tf.keras.regularizers.l2(0.0005)))

b. Dropout()

这个函数在深度学习里面也是十分常见,dropout是指在神经网络中丢弃掉一些隐藏或可见单元。通常来说,是在神经网络的训练阶段,每一次迭代时,都会随机选择一批单元,让其被暂时忽略掉,所谓的忽略是不让这些单元参与前向推理和后向传播。但是在测试阶段仍然使用这些神经元,

model.add(tf.keras.layers.Dropout(0.4)) # ()括号的是每次随机忽略神经元的比例

c. BatchNormalization()

Batch Normalization是2015年提出的一种方法,在进行深度网络训练时,大都会采取这种算法,主要解决了:深度神经网络随着网络深度加深,训练起来越困难,收敛越来越慢的问题。机器学习领域有个很重要的假设:独立同分布假设,BatchNorm就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同分布的

model.add(tf.keras.layers.BatchNormalization())

BN层的作用还有: 防止梯度消失和梯度爆炸 以及 加快网络的训练和收敛的速度

d. 权值衰减decay

权值衰减——weight_decay,简单的理解就是乘在正则项的前面的系数,目的是为了使得权值衰减到很小的值,接近如0。一般在深度学习好中,tensorflow或pytorch的提供的优化器都可以设置的。

optimizer = tf.keras.optimizers.SGD(learning_rate=learning_rate,

decay=1e-6, momentum=momentum, nesterov=True)

加入BN Dropout 激活函数 卷积的一般顺序。

2. 数据层面

a、保证数据集分布一致性

在切分数据集的时候要保证分布一致性。可以使用sklearn包中,model_selection相关train_text_split来实现数据集切割后分布的一致性。

b、增加数据集的规模

可以采用一定的数据增强策略,来实现数据集的扩充。注意的是这里可能会引入一定的噪声,噪声也会影响模型的性能的,要注意利弊的取舍。另外CV和NLP的数据增强是不一样的,NLP数据增强更难。常见的数据增强有一下几个常用的方法。

datagen = ImageDataGenerator(

rotation_range=15, # 图片可能在(-15,15)度内旋转

width_shift_range=0.05, # 图片可能在左右比例(-0.05,0.05)(百分比)内水平移动

height_shift_range=0.05, # 图片可能在上下比例(-0.05,0.05)(百分比)内水平移动

shear_range=0.05, # 图片可能以比例(-0.05,0.05)内错切变换

zoom_range=0.1, # 图片可能以比例(-0.1,0.1)内缩放

horizontal_flip=True, # 图片可能水平翻转

fill_mode='nearest') # 图片填充模式为 aaaabcdeghhhhhh

)

datagen.fit(x_train)

3. 训练层面 对模型实施early-stopping。神经网络的训练过程中我们会初始化一组较小的权值参数,随着模型的训练,这些权值也变得越来越大了.或者根据经验一次训练一小部分个epoch,然后凭借已有的loss图像考虑要不要再次训练。

注意事项

如果上述方法同时使用的情况:顺序考虑一般是:

CONV/FC -> BatchNorm -> ReLu(or other activation) -> Dropout -> CONV/FC ->

2.超参数的选取

batchsize(批大小):

当batch_size设置为2的次幂时能够充分利用矩阵运算效果会较好。当数据集的数据较多的时候一般选择64,128效果较为优秀。

a. 大的batchsize 减少训练时间,提高稳定性。 这是肯定的,同样的epoch数目,在性能允许情况下,大的batchsize需要的batch数目减少了,所以可以减少训练时间。另一方面,大的batch size梯度的计算更加稳定,因为模型训练曲线会更加平滑。在微调的时候,大的batch size可能会取得更好的结果。

b, 过大的batchsize 泛化能力下降。 在一定范围内,增加batchsize有助于收敛的稳定性,但是随着batchsize的增加,模型的性能会下降。

c, 同样是通过对训练步数的影响,小的batch_size使模型迭代次数增多,提前到达拟合点,但是epoch没结束,继续学习训练数据,容易导致过拟合于原始数据

epoch(训练次数):

从小批量开始训练,一次20次左右,观察训练集和测试集的acc&&loss图像。 如果loss一直在下降就继续训练,通常来说大的batchsize需要大的epoch达到收敛。

learning_rate(学习率):

• 学习率高的话,模型学习数据时间加快,提前到达拟合点,但是epoch没结束,继续学习训练数据,容易导致过拟合于原始数据。

• 从公式就可以看出,学习率越大,输出误差对参数的影响就越大,参数更新的就越快,但同时受到异常数据的影响也就越大,很容易发散。

• 因此选择lr,也就是不断试的过程,基本范围大概就是0.1,0.01,0.001,0.0001这样子,一个数量级一个数量级的尝试就可以了,直到找到最优的学习率。一般选择较大的学习率开始训练然后随着迭代的次数使学习率不断减小,使得模型的loss逐渐稳定。

decay的重要性:

decay 是学习率衰减率,公式如下图。

• 如果在整个梯度下降过程中,保持learning rate不变,如果learning rate设置小了,会导致梯度下降过慢,如果设置大了,对于mini-batch来说最后就很难收敛,一直在最小值附近盘旋。所以动态改变learning rate很重要,在开始的时候,设置较大的learning rate,可以保证梯度下降的速度,慢慢减小,可以使最后的cost function在最小值非常小的范围内盘旋,得到一个比较满意的值。

• 学习率过大,在算法优化的前期会加速学习,使得模型更容易接近局部或全局最优解。但是在后期会有较大波动,甚至出现损失函数的值围绕最小值徘徊,波动很大,始终难以达到最优。所以引入学习率衰减的概念,直白点说,就是在模型训练初期,会使用较大的学习率进行模型优化,随着迭代次数增加,学习率会逐渐进行减小,保证模型在训练后期不会有太大的波动,从而更加接近最优解。

3.关于不同loss&&val_loss变化的处理方法:

观察损失图像:

• train loss 下降⬇,val loss下降⬇,说明网络仍在学习;奈斯,继续训练 • train loss 下降⬇,val loss上升⬆,说明网络开始过拟合了; 赶紧停止,然后数据增强、正则、Dropout。 • train loss 不变,val loss不变,说明学习遇到瓶颈;调小学习率或批量数目 • train loss 不变,val loss下降⬇,说明数据集100%有问题;检查数据集标注有没有问题 • train loss 上升⬆,val loss上升⬆,说明网络结构设计不当,训练超参数设置不当,数据集经过清洗等问题。

loss震荡?

轻微震荡一般是正常的,在一定范围内,一般来说 Batch Size 越大,其确定的下降方向越准,引起训练震荡越小,如果震荡十分剧烈,那估计是Batch Size设置的太小。

参考文献:

1.过拟合产生的原因和方法 2.L1 L2正则化 3.BN层的原理及其作用 4.学习率和batchsize的选取 5.关于不同loss&&val_loss变化的处理方法

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: