tensorflow常用技巧

本文深入讲解深度学习模型的训练与优化技巧,包括模型保存与加载、学习率衰减策略、交叉熵损失函数、参数初始化方法、梯度消失与爆炸问题及解决办法。同时,提供了TensorFlow、Slim和Keras三种框架下的CNN实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、模型的保存与加载(以继续训练)、日志保存与加载

2、模型训练时学习率衰减

3、tensorflow中的交叉熵的比较

4、tensorflow中的参数初始化方法

5、用for代替tf中的tf.data.Dataset.from_tensor_slices

6、梯度消失和梯度爆炸

7、tf中一些常用的小代码

8、tf中用tensorflow、slim(高级库)、keras写的CNN


 


1、模型的保存与加载(以继续训练)、日志保存与加载

# 模型保存
saver = tf.train.Saver(max_to_keep=5)    # max_to_keep只保存最新的5个模型,旧的模型会被覆盖
with tf.Session() as sess:
    sess.run(……)
    ……
    saver.save(sess, 'path', global_step=10)    # global_step: 在n次迭代后再保存模型

# 模型加载,以继续训练或进行测试
with tf.Session() as sess:
    # 导入模型结构(具体参考:review.ipynb的模型保存与加载)
    saver = tf.train.import_meta_graph('.meta')
    # 导入模型参数
    saver.restore(sess, tf.train.latest_checkpoint('./'))
    # 再载入一些关键的tensor,就可以进行测试或者是再训练了 
    sess.run(...)
# 【备注:华为杯中所用的保存为pb个的方式:https://github.com/gongtian1234/CV2019/blob/master/%E7%AC%AC%E5%85%AD%E8%AF%BEtensorflow/01savedModelAndLoad/11_2_savedAndLoadModel_mydata_success.ipynb】

# 日志文件的保存与加载
# 参考链接:https://blog.youkuaiyun.com/haoshan4783/article/details/89970227
tf.summary.scalar('accuracy',acc)                   #生成准确率标量图  
merge_summary = tf.summary.merge_all()  
train_writer = tf.summary.FileWriter(dir,sess.graph)#定义一个写入summary的目标文件,dir为写入文件地址  
......(交叉熵、优化器等定义)  
for step in xrange(training_step):                  #训练循环  
    train_summary = sess.run(merge_summary,feed_dict =  {...})#调用sess.run运行图,生成一步的训练过程数据  
    train_writer.add_summary(train_summary,step)#调用train_writer的add_summary方法将训练过程以及训练步数保存

# 查看命令: tensorboard --logdir=日志文件的绝对路径(只是文件所在位置,不要写上日志文件的名字)
# 【注意:路径不要加引号,不要有中文空格等特俗符号】

# 完整日志保存的的命令示例
import tensorflow as tf
def myregression():
    # 自实现一个线性回归
 
    # 指定作用域
    with tf.variable_scope('data'):
        # 1、准备数据,x目标值: [100,1]  y特征值: [100]
        x = tf.random_normal([100, 1], mean=1.75, stddev=0.5, name='x_data')
        # 矩阵相乘必须是二维的
        y_true = tf.matmul(x, [[0.7]]) + 0.8
 
    # 指定模型的作用域
    with tf.variable_scope('model'):
        # 2、建立一个线性回归模型 1个特征: 1个权重,1个偏置 y = x * w + b
        # 随机给一个权重和偏置,让它去计算损失,然后在当前状态下优化
        weight = tf.Variable(tf.random_normal([1, 1], mean=0.0, stddev=1.0), name='w')
        # 用变量定义权重才能优化
        bias = tf.Variable(0.0, name='b')
        y_predict = tf.matmul(x, weight) + bias    # 1个特征值
 
    # 指定损失的作用域
    with tf.variable_scope('loss'):
        # 3、建立损失函数:均方误差
        loss = tf.reduce_mean(tf.square(y_true - y_predict))
 
    # 指定优化损失的作用域
    with tf.variable_scope('optimizer'):
        # 4、梯度下降,优化损失 学习率learning_rate:0~1,2,3,5,7,10
        train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
 
    # 搜集tensor --->合并变量写入事件文件
    tf.summary.scalar('losses', loss)
    tf.summary.histogram('weights', weight)
    merged = tf.summary.merge_all()    # 合并变量的op
 
    # 定义一个初始化变量的op:weight,bias(不进行初始化时会报错)
    init_op = tf.global_variables_initializer()
 
    # 5、通过会话运行程序
    with tf.Session() as sess:
        # 初始化变量
        sess.run(init_op)
        # 打印随机最先初始化的权重和偏置
        print('随机初始化的参数权重为:{}, 偏置为:{}'.format(weight.eval(), bias.eval()))
 
        # 建立事件文件
        filewriter = tf.summary.FileWriter('./tmp/summary/test', graph=sess.graph)
 
        # 循环训练 运行优化
        for i in range(500):    # 【循环次数怎么确定:可以用损失函数的精确度去制定规则,如上一次较这一次值变化了10^-6,这说明此时精度较高,可以停止循环】
            sess.run(train_op)
 
            # 运行合并的tensor
            summary = sess.run(merged)
            filewriter.add_summary(summary, i)
            print('第{}次优化的参数权重为:{}, 偏置为:{}'.format(i, weight.eval(), bias.eval()))

(详见代码:06tensorflow模型的加载与保存.ipynb)

2、模型训练时学习率衰减

①学习率衰减的必要性

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

②学习率衰减的办法

(1) \alpha = \frac{1}{1+decayRate \times epochNum}\times\alpha_0, 其中decayRate为衰减率(是一个超参数),epochNum是迭代次数,\alpha_0是初始学习率;

(2) \alpha = 0.95^{epochNum}\times \alpha _{0}

(3) \alpha = \frac{k}{\sqrt{epochNum}} \times \alpha_0\alpha = \frac{k}{\sqrt{t}} \times \alpha_0, 其中t为mini-batch的数字

在实际操作中发现,在迭代50次以内,学习率的衰减速度非常快,且在100次左右,学习率衰减到非常小(如e-10),这对于迭代次数较多的模型训练即为不利,所以进行了如下研究(详见学习率分析),对传统的学习率做了一些修改,欢迎大家积极留言讨论。

参考文章:必备必考 | 调参技能之学习率衰减方案(一)—超多图直观对比

后来发现tensorflow有自己的衰减函数【这篇文章强烈推荐看一看】(咳咳)

# keras中学习率自动根据val_loss下降的函数
# tf.keras.callbacks.ReduceLROnPlateau

from keras.callbacks import ReduceLROnPlateau,EarlyStopping, ModelCheckpoint

train_datagen = image.ImageDataGenerator(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')  # rescale=1/255
val_datagen = image.ImageDataGenerator()

batch_size = 64
train_generator = train_datagen.flow_from_directory('image/train', target_size=(224,224),batch_size=batch_size)
val_generator = val_datagen.flow_from_directory('image/val', target_size=(224,224), batch_size=batch_size)

# print(train_generator.class_indices)
# print(val_generator.class_indices)

earlystoping = EarlyStopping(monitor='val_loss', patience=9, verbose=1)
# checkpointer = ModelCheckpoint()
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=3, verbose=1, 
                  mode='auto', min_delta=0.0001, min_lr=1e-7,)

model.compile(optimizer=Adam(lr=0.0001), loss='categorical_crossentropy', metrics=['accuracy'])
model.fit_generator(train_generator, steps_per_epoch=len(train_generator), epochs=201,
                   validation_data=val_generator, validation_steps=len(val_generator),shuffle=True,
                   callbacks=[earlystoping, reduce_lr])

# 参考网址有:
# 1、https://www.w3cschool.cn/tensorflow_python/tf_keras_callbacks_ReduceLROnPlateau.html(函数参数解释网址)
# 2、https://stackoverflow.com/questions/51889378/how-to-use-keras-reducelronplateau(第一次看到来源网址)
## 学习率指数衰减
with tf.name_scope('optimizer'):
    global_step = tf.Variable(0, trainable=False)
    learning_rate = tf.train.exponential_decay(learning_rate=learning_rate,global_step=global_step,
                            decay_steps=2000, decay_rate=decay_rate, staircase=True)
    optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
    train_op = optimizer.minimize(self.cost, global_step=global_step)

补充:在keras可以直接设置衰减系数,

s = Sequential()
# 初始化优化器
optimizer = Adam(lr=0.0001, decay=6e-8)    # decay学习衰减率
s.compile(optimizer=optimizer, loss='binary_crossentropy', metrics=['accuracy'])

## 以上是keras搭建神经网络的基本流程,看不懂的朋友可以找一下keras的相关流程 ##

补充2:在tensorflow中学习率可以设置分段衰减,函数为: 

tf.train.piecewise_constant(
    x,
    boundaries,
    values,
    name=None
)

3、tensorflow中的交叉熵的比较

(具体内容见链接处文章)

4、tensorflow中的参数初始化方法

但是没有找到吴恩达老师讲的He initialization的接口,这在使用slim中初始化参数有些不方便,He initialization的数学公式为:

针对tanh激活函数:w^{[l]}=np.random.randn(n^{[l-1]}, n^{[l]}) \times np.sqrt(\frac{2}{n^{[l-1]}+ n^{[l]}}) (or np.sqrt(\frac{1}{n^{[l-1]}}))

针对relu激活函数:w^{[l]}=np.random.randn(n^{[l-1]}, n^{[l]}) \times np.sqrt(\frac{1}{n^{[l-1]}})

如果一层一层的初始化权重,则可以用这种方式,但是如果用slim同一初始化参数则没有这个接口。

5、用for代替tf中的tf.data.Dataset.from_tensor_slices

一开始也挺喜欢这种mini_batch方式的,但是在华为杯中因这种方式载入模型需要初始化dataset(说实话,现在也不理解是什么鬼,总之就是在华为云上部署失败),觉得还是自己写的for循环来代替进行mini_batch操作,

for i in range(epochs):
    #### 对比mini_batch和batch #### 参考网址:https://www.jianshu.com/p/d1f469d65470
    ## mini_batch参数下降更快,更鲁棒的收敛,可以避免局部最优,总体来看,效果优于batch #    
    for j in range(int(x_train.shape[0]/batch_size)+1):
        x_batch = x_train[j*batch_size:(j+1)*batch_size]
        y_batch = y_train[j*batch_size:(j+1)*batch_size]
        _,los = sess.run([train,loss], feed_dict={x: x_batch, y: y_batch})
#     _,los = sess.run([train,loss], feed_dict={x: x_train, y: y_train})
    if i%200==0:
        print('第{}次mse为:'.format(i),los)

6、梯度消失和梯度爆炸

在训练神经网络时,非常容易遇到这种问题,这篇文章中详细的介绍了梯度爆炸1(为什么会产生~?如何判断已经~?如何改进?)、2(从激活函数图的角度解释了梯度消失、梯度爆炸;解决方案);

7、tf中一些常用的小代码

tf.identity()可以改变一个张量的类型,或者用于给其命名 

8、tf中用tensorflow、slim(高级库)、keras写的CNN

# tf中一个完整的卷积层、池化层、全连接层
wConv = tf.Variable(tf.truncated_normal([5,5,1,32],stddev=0.01))
bConv = tf.Variable(tf.zeros([32])+0.1)
aConv = tf.nn.relu(tf.nn.conv2d(net, filter=wConv, strides=[1,1,1,1], padding='SAME') + bConv)

aPool = tf.nn.max_pool(aconv, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')

aPoolFalten = tf.reshape(aPool, [-1, 7*7*64])
wFc = tf.Variable(tf.truncated_normal([7*7*64, 1024], stddev=0.01))
bFc = tf.Variable(tf.zeros([1024])+0.1)
aFc = tf.nn.relu(tf.matmul(aPoolFalten, wFc)+bFc)
keepProb = tf.placeholder(tf.float32, name='keep_prob')
aFcDropout = tf.nn.dropout(aFc, keep_prob=keepProb)
### ###
### 直接使用tensorflow每一步都得初始化参数,全连接层是一步步运算得到的

# 使用slim进行一个完整的卷积、池化、全连接层
from tensorflow.contrib import slim

with slim.arg_scope(list_ops_or_operations=[slim.conv2d, slim.fully_connected],
                    activation_fn=tf.nn.relu,
                    weights_initializer=tf.truncated_normal_initializer(stddev=0.01,seed=2020),
                    biases_initializer=tf.constant_initializer(0.1)):
    slim.conv2d(inputs=net, num_outputs=64, kernel_size=[5,5], stride=4, padding='SAME')
    slim.max_pool2d(inputs=net, kernel_size=[2,2], stride=2, padding='SAME')
    slim.flatten(inputs=net)
    slim.fully_connected(net, num_outputs=1024)
    slim.dropout(net, keep_prob=keepProb)

#使用keras进行一个完整的卷积、池化、全连接层
from keras.layers import Conv2D,MaxPool2D,Dense,Flatten
from keras.models import Sequential

model = Sequential()
model.add(Conv2D(input_shape(28,28,1),kernel_size=(5,5),filters=32,strides=1,padding='same',activation='relu',kernel_initializer='he_normal'))
model.add(MaxPool2D(pool_size=(2,2),strides=2,padding='same'))
model.add(Flatten())
mode.add(Dense(units=1024,activation='relu/softmax',kernel_initializer='he_normal'))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值