反向传播:
梯度下降算法主要用于优化单个参数的取值,我在之前的文章记录过。而反向传播给出了一个高效的方式在所有的参数上使用梯度下降算法,从而使神经网络模型在训练集上的损失函数尽可能小。这里不介绍反向传播的具体实现,只知道反向传播可以计算损失函数对每一个参数的梯度,再根据梯度和学习率使用梯度下降算法更新每一个参数。
需要注意的是,反向传播算法并不能保证被优化的函数达到全局最优解,在训练网络时,参数的初始值会很大程度上影响最后得到的结果。
二:随即梯度下降,小部分的梯度下降:
由于梯度下降要在全部数据上最小化损失,所以损失函数是在所有训练数据上的损失和,这样在每一轮迭代中都要计算在全部训练数据上的损失函数。十分耗时。为了加速训练过程,可以使用随机梯度下降的算法,它随机优化某一条训练数据上的损失函数,因此它的问题也很明显:在某一条数据上损失函数更小不代表在所有数据上损失函数更小。于是使用随机梯度下降可能无法达到全局最优解。 在实际网络训练中,一般取以上两种方法的折中,每次计算一小部分训练数据的损失函数。这一小步数据被称为一个batch。实践证明,这种方式在速度上准确性上都有提升,因此应用十分广泛。
三:学习率的设计:
学习率对模型的训练有很大的影响,tf有一种更加灵活的设置学习率的方法,指数衰减法。tf.train.exponential_decay函数实现了指数衰减学习率。通过这个函数,可以先试用较大的学习率来快速得到一个比较优的解,然后随着贴袋的继续逐步减少学习率,使得模型在训练后期更加稳定。他实现了以下代码的功能:
decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)其中decayed_learning_rate为每一轮优化时使用的学习率,learning_rate为事先设好的学习率。decay_rate为衰减系数,取值小于1,decay_steps为衰减的速度,也可以说是总的迭代轮数。而global_steps是当前的迭代次数,一般初始化为0。对应于tf中的定义是这样的:
learn_rate = tf.train.exponential_decay(0.1, global_step, decay_steps, decay_rate, staircase=True)可以通过设置staircase参数选择不同的衰减方式。它的默认值是False,此时学习率的变化曲线是平滑的,但是设置为True时,是阶梯状的。在这样的设置下,decay_steps通常代表了完整的使用一遍训练数据所使用的迭代论数。这个迭代轮数就是全部训练数据除以每一个batch中的训练样本数。这种设置的场景通常是每完整的过完一遍训练数据,学习率就减小一次。这使得训练数据集中所有数据对模型训练有相同的作用。当使用连续的指数衰减学习率时,不同的训练数据有不同的学习率。而当学习率减少时,对应的训练数据对模型训练结果的影响就小了。
global_step = tf.Variable(0) learn_rate = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True)上面代码每训练100轮后学习率乘以0.96
四:过拟合问题:
解决过拟合问题我们常使用的就是L1正则化和L2正则化。都是通过限制权重的大小,使得模型不能拟合任意的训练数据中的噪音。二者的区别有:L1正则化会让参数变得更加稀疏,而L2不会。所谓参数变得更稀疏是指更多的参数变为0,这样可以达到类似特征选取的功能。其次,L1正则化的计算公式不可导,而L2正则化公式可导。所以优化时L2更加简单。实践中也可以二者一起使用。
loss=tf.reduce_mean(tf.square(y_-y)) + tf.contrib.layers.l2_regularizer(lambda)(w)
接下来介绍一个代码,来自书籍《Tensorflow实战google深度学习框架》第88页。
因为当网络变的复杂时候损失函数加正则项的定义过长,此时我们可以使用tf提供的集合。他可以在一个计算图中保存一组实体,比如张量。
#获取一层神经网络边上的权重,并将这个权重的L2正则化损失加入名称为;losses的集合中 def get_weight(shape, lam): var = tf.Variable(tf.random_normal(shape), dtype=tf.float32) #add_to_collection函数将这个新生成变量的L2损失函数加入集合 tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lam)(var)) return var x = tf.placeholder(shape=[None,2], dtype=tf.float32) y_ = tf.placeholder(shape=[None, 1], dtype=tf.float32) batch_size = 8 #定义每一层节点的个数 layer_dimension = [2, 10, 10, 10, 1] n_layers = len(layer_dimension) #这个变量维护前向传播时最深层的节点,开始的时候就是输入层 cur_layer = x #当前层的节点个数 in_dimenson = layer_dimension[0] #通过一个循环来生成5层全连接的网络结构 for i in range(1, n_layers): out_dimension = layer_dimension[i] weight = get_weight([in_dimenson, out_dimension], 0.001) bias = tf.Variable(tf.constant(0.1, shape=[out_dimension])) #使用relu激活函数 cur_layers = tf.nn.relu(tf.matmul(cur_layer, weight) + bias) in_dimenson = layer_dimension[i] #在定义神经网络前向传播的同时已经将所有的L2正则化损失加入到图上的集合中,这里只需要刻画 #模型在训练数据上的损失函数 mse_loss = tf.reduce_mean(tf.square(y_ - cur_layer)) #将均方误差损失函数加入损失集合 tf.add_to_collection('losses', mse_loss) #get_collection返回一个列表,这个列表是集合中的所有元素 loss = tf.add_n(tf.get_collection('losses'))