目录
一.优化参数的三个方法
1.手动修改
2.for循环调参
3.Keras Tunner自动调参工具
介绍
1.安装
2.准备训练数据和加载的库
3.创建HyperParameters对象以及模型生成函数
4.创建Hyperband对象
4.开始优化
5.获取最佳模型
6.结果显示
二.注释
1.为什么二次调参无效,不起作用?(避坑)
2.dropout的意义
3.WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time原因
4.WARNING:tensorflow:From F:/deeplearning/dp4.py:133: Model.fit_generator (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
5.tensorflow显存占比高但是GPU利用率低的问题
下面内容是对CV4中的程序进行优化,在CV4中我们卷积层的数量、过滤器的数量、全连接层神经元的个数、学习率都需要去优化一下。在CV3中我们已经介绍过keras Tunner自动调参工具,当时这次的例子是剪子包袱锤,在这里再重复介绍下,在下文中将使用这种工具进行调参优化。注意与cv2,cv3不同的是这里我们使用了GPU加速训练,tensorflow的版本和cuda,cudnn的版本都更换了。这里要注意的就是使用gpu加速的话,再使用keras tunner调参工具可能会报错,这个时候我们的tensorflow-gpu,cudnn的版本要求是比较新的,可以参考我下面的依赖。注意使用GPU加速必须显卡能支持。
这里提供比较快的pypi源
1.使用国内源(pip比较快):
超快豆瓣源:
pip --default-timeout=100 install 库名称 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
举例:
pip --default-timeout=100 install tensorflow-gpu==2.1.0 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
清华源:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
举例:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow-gpu==2.2.0
中科大源:
举例:pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ tensorflow-gpu==2.3.0
用法:例:pip install -i https://pypi.doubanio.com/simple/ 包名
源汇总:
阿里云 http://mirrors.aliyun.com/pypi/simple/
中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban) http://pypi.douban.com/simple/
清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/
解决超时的一些其他办法:
2,设置超时时间:pip --default-timeout=100 install Pillow,
3,不使用缓存:pip --no-cache-dir install Pillow
在下面中我们将解决这个问题
有以下三个方法
我们可以有最原始的办法,就是手动修改参数,然后观察训练的效果(损失和精确度),从而判断参数的设置是否合理,但是这样很麻烦。
还有一个办法就是,那就是编程写for循环来调参,但是也有点麻烦。
在这里我们可以借助这款自动调参工具来实现我们参数的优化,优点是不容易出错,效果也比较好。
keras Tunner是一个可以自动搜索模型训练参数的库,它的基本思路是在需要调整参数的地方插入一个特殊的对象(可指定参数范围),然后调用类似训练那样的search方法。
依赖(重要)
安装命令:
pip install -U keras-tuner
#石头剪刀布(优化版)
#用GPU跑的话记得需要设置为仅在需要时申请显存。
import tensorflow as tf
config = tf.compat.v1.ConfigProto(gpu_options=tf.compat.v1.GPUOptions(allow_growth=True))
sess = tf.compat.v1.Session(config=config)
import tensorflow as tf
from keras_preprocessing.image import ImageDataGenerator
#归一化数据以及数据增强
TRAINING_DIR = "F:/deeplearning/rps/rps"
training_datagen = ImageDataGenerator(
rescale=1. / 255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
#训练集数据不用做数据增强,只归一化数据即可
VALIDATION_DIR = "F:/deeplearning/rps/rps-test-set"
validation_datagen = ImageDataGenerator(rescale=1. / 255)
#分类数据集(指向训练集数据)
train_generator = training_datagen.flow_from_directory(
TRAINING_DIR,
target_size=(150, 150),
class_mode='categorical'
)
# 分类数据集(指向测试集数据)
validation_generator = validation_datagen.flow_from_directory(
VALIDATION_DIR,
target_size=(150, 150),
class_mode='categorical'
)
from kerastuner.tuners import Hyperband
from kerastuner.engine.hyperparameters import HyperParameters
from tensorflow.keras.optimizers import RMSprop
先创建HyperParameters对象。进而我们定义一个模型,然后在模型中插入Choice以及int等调参用的对象。该函数返回一个编译好的模型。
hp = HyperParameters()#声明一个变量,类型为HyperParameters,等会通过hp来调
def build_model(hp):
#模型构建
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Conv2D(hp.Choice('num_filters_top_layer',#这个名字是和其数值对应的
values=[16,64],#取值范围
default=16),
(3, 3), activation='relu', input_shape=(150, 150, 3)))
model.add(tf.keras.layers.MaxPooling2D(2, 2))
for i in range(hp.Int('num_conv_layers',1,3)):
model.add(tf.keras.layers.Conv2D(hp.Choice(f'num_filters_layer{i}',
values=[16,64],#取值范围
default=16),
(3, 3), activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(2, 2))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dropout(0.5))#Dropout是深度学习中的一种防止过拟合手段,具体看注释2
model.add(tf.keras.layers.Dense(hp.Int('hidden_units',128,512,step=32),#给出范围和搜索步长
activation='relu'))
model.add(tf.keras.layers.Dense(3, activation='softmax'))
# 模型编译
model.compile(loss='categorical_crossentropy', optimizer=RMSprop(lr=0.001), metrics=['accuracy'])
return model
进而我们再创建Hyperband对象,这是keras tuner支持的四种方法的其中一种。
tuner = Hyperband(#生成一个tuner对象,输入Hyperband
build_model,#函数的名字,告诉他用哪个函数生成这个模型
objective='val_accuracy',#以测试集的训练效果为准
max_epochs=15,#猜最多几次可以达到会聚
directory='rpsPData',#参数数据目录
hyperparameters=hp,#告诉他用这个变量调
project_name='rps1',#调优参数工程名字
)
搜索过程:通过调用模型构建函数,使用hp跟踪的超参空间(搜索空间)中的参数配置,多次构建模型。优化器逐渐探索超参空间,记录每种配置的评估结果。
#接下来搜一下参数,search和fit用法差不多
tuner.search(train_generator,epochs=15,validation_generator=validation_generator)
搜索到最优参数之后,可通过下面的程序,用tunner对象提取最优参数构建神经元网络模型。并调用summary方法观察优化后的网络结构。
#优化完参数之后可以通过这个看到结果
best_hps=tuner.get_best_hyperparameters(1)[0]
model=tuner.hypermodel.build(best_hps)#用调优之后的参数构建模型
#查看模型网络结构
model.summary()
tuner.results_summary()#可打印结果综述
Search: Running Trial #27
Hyperparameter |Value |Best Value So Far
num_filters_top...|64 |16
num_conv_layers |2 |3
num_filters_layer0|16 |64
hidden_units |416 |160
num_filters_layer1|16 |16
num_filters_layer2|16 |64
tuner/epochs |15 |15
tuner/initial_e...|0 |5
tuner/bracket |0 |2
tuner/round |0 |2
Epoch 1/15
79/79 [==============================] - 19s 239ms/step - loss: 1.2429 - accuracy: 0.3667 - val_loss: 1.0400 - val_accuracy: 0.6022
Epoch 2/15
79/79 [==============================] - 19s 239ms/step - loss: 0.9449 - accuracy: 0.5532 - val_loss: 0.6880 - val_accuracy: 0.5968
Epoch 3/15
79/79 [==============================] - 19s 238ms/step - loss: 0.7442 - accuracy: 0.6774 - val_loss: 0.3666 - val_accuracy: 0.9247
Epoch 4/15
79/79 [==============================] - 19s 243ms/step - loss: 0.6191 - accuracy: 0.7500 - val_loss: 0.3868 - val_accuracy: 0.9247
Epoch 5/15
79/79 [==============================] - 18s 231ms/step - loss: 0.5086 - accuracy: 0.7956 - val_loss: 0.1944 - val_accuracy: 0.9570
Epoch 6/15
79/79 [==============================] - 18s 223ms/step - loss: 0.4437 - accuracy: 0.8210 - val_loss: 0.2616 - val_accuracy: 0.8817
Epoch 7/15
79/79 [==============================] - 17s 220ms/step - loss: 0.3864 - accuracy: 0.8520 - val_loss: 0.2162 - val_accuracy: 0.9194
Epoch 8/15
79/79 [==============================] - 18s 232ms/step - loss: 0.3319 - accuracy: 0.8778 - val_loss: 0.1309 - val_accuracy: 0.9624
Epoch 9/15
79/79 [==============================] - 17s 215ms/step - loss: 0.2984 - accuracy: 0.8960 - val_loss: 0.1338 - val_accuracy: 0.9624
Epoch 10/15
79/79 [==============================] - 19s 237ms/step - loss: 0.2665 - accuracy: 0.8984 - val_loss: 0.0902 - val_accuracy: 0.9758
Epoch 11/15
79/79 [==============================] - 18s 232ms/step - loss: 0.2220 - accuracy: 0.9163 - val_loss: 0.2438 - val_accuracy: 0.8898
Epoch 12/15
79/79 [==============================] - 18s 234ms/step - loss: 0.2247 - accuracy: 0.9187 - val_loss: 0.0518 - val_accuracy: 1.0000
Epoch 13/15
79/79 [==============================] - 18s 228ms/step - loss: 0.1829 - accuracy: 0.9357 - val_loss: 0.1361 - val_accuracy: 0.9301
Epoch 14/15
79/79 [==============================] - 18s 228ms/step - loss: 0.2124 - accuracy: 0.9290 - val_loss: 0.1054 - val_accuracy: 0.9651
Epoch 15/15
79/79 [==============================] - 18s 226ms/step - loss: 0.1914 - accuracy: 0.9345 - val_loss: 0.0912 - val_accuracy: 0.9758
Trial 27 Complete [00h 04m 39s]
val_accuracy: 1.0
Best val_accuracy So Far: 1.0
Total elapsed time: 00h 52m 44s
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 148, 148, 16) 448
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 74, 74, 16) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 72, 72, 64) 9280
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 36, 36, 64) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 34, 34, 16) 9232
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 17, 17, 16) 0
_________________________________________________________________
conv2d_3 (Conv2D) (None, 15, 15, 64) 9280
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 7, 7, 64) 0
_________________________________________________________________
flatten (Flatten) (None, 3136) 0
_________________________________________________________________
dropout (Dropout) (None, 3136) 0
_________________________________________________________________
dense (Dense) (None, 160) 501920
_________________________________________________________________
dense_1 (Dense) (None, 3) 483
=================================================================
Total params: 530,643
Trainable params: 530,643
Non-trainable params: 0
发现了没有我们的结果其实还不理想,在这里最有可能的原因是训练迭代的次数还不够多,所以需要增加训练迭代次数。
这个是训练迭代20次的结果,这个结果中train accuracy趋于不变,test accuracy不断下降,train accuracy大于test accuracy,这里的结果相比较上面的结果更好一些。但是也不算很好。 所以我们继续增加迭代次数。
训练迭代30次的结果
这个是训练迭代50次的结果。
这个是训练迭代80次的结果。
从中我们可以看出迭代50次的结果是最好的。证明在这里增加训练迭代的次数是能够提高模型的拟合效果,但并不是越多越好,太多了反而会造成过拟合现象.......
是因为第一次调参之后把调参之后的结果存储在directory里面了,二次调参需要把那个文件夹删了才会重新优化。
dropout
作用:避免过拟合
Dropout与L1和L2正则化区别:L1和L2正则化通过在损失函数上增加参数的惩罚项,通过对参数大小的约束,起到类似降维的作用(若高阶项参数接近0,相当于降阶)。进而简化模型,提高模型泛化力,避免过拟合。
L1和L2是正则化修改代价函数,而Dropout则是修改神经网络本身(dropout直接让一些神经元随机停止工作,其实就是比如挑选几个神经元组成小团队高效地工作)。
工作原理:dropout就是"抛弃",抛弃的是网络中隐藏层的节点(输入层和输出层是由数据类型和问题类型决定的,当然不能抛弃了),怎么抛弃呢?dropout有一个参数p,p的取值是每个节点有p概率被抛弃。抛弃这个节点有什么影响呢,影响在于一旦某个节点被选定为抛弃的节点,那么对于神经网络的forward过程这个节点的输出就被置为0,对于backward过程,这个节点的权重和偏置不参与更新,这就是说,在某次迭代中,网络中有部分节点不参与这一次的训练,整个网络结构等同于下图右侧(左侧是dropout前的)。
出现的原因是一开始verbose=1,在一个epoch中训练一个batch就会打印进度条和结果,但是打印到控制台所需时间比训练模型时间更长,所以有了警告。将verbose=0,使得一点训练过程信息也不打印,verbose=2,每一个epoch打印一次。
原因:
新版本的tensorflow已经把这个方法废弃了,我们可以直接使用fit来代替fit_generator了,fit也有fit_generator的功能了。(当然fit_generator也可以用)
这个问题其实挺重要,如果我们在做大型试验的话,train一次跑几天就比较.......
这个跟自己写的代码有关系,在读取磁盘中数据的时候,是依靠CPU在读取数据(此时磁盘读写会非常高),同时,tensorflow在运行模型训练的时候,会从内存中读取数据,GPU训练会比较快,而CPU一直在从磁盘中读数据到内存中,而跟不上GPU从内存中读取数据并训练数据的速度(就是你处理的速度反而比读写速度快很多),此时可以用两种发放来处理,一个是将磁盘中的数据转成TFRecords类型数据,让tensorflow直接读取数据,而不是让CPU读取数据后再给tensorflow读取,另一个方法就是增加线程数(num_threads),在tf.train.shuffle_batch和tf.train.batch中,有一个num_threads参数,默认为1,可以稍微调高一点。