基本定义:一类通过多层非线性变换对高复杂度性数据建模算法的合集。
激活函数实现去线性化
在前面所提到的示例中,输入输出与权值有关系:
y=xW′ ,换句话说,对于任何输入输出,都可以通过与权值进行加权求得,但这样的结果,实践证明无法解决高于一维的数据分类问题,由此提出去线性化 的概念,也即激活函数 。以下给出加入了激活函数和偏置项后的神经元结构上面的结构使用函数表达为:
A=f(xW+b) 对比之前的激活函数,其实就是在其基础上进行了一次函数运算。以下给出了几种常用的非线性函数的激活函数图像
tensorflow一共提供了7种不同的激活函数,当然,tensorflow也支持自己定义激活函数。
损失函数
损失函数刻画了神经网络的输出结果与目标之间的差距
经典损失函数
对于分类问题,一般会按照分类的数目来确定输出数目,通常n分类问题会有n个输出,比如,对于一个数字识别问题,其实就是10分类问题,这样的输出可以刻画为
[0,0,0,1,0,0,0,0,0,0] ,1代表输出的结果为真,反之为假,那么前面的例子标识的就是结果为:3。当然,为了更准确的用数学语言描述,我们需要把输出转换为概率语言表达,通过交叉熵(cross entropy) 来刻画输出向量与期望向量之间的近似程度,对于概率分布p,q ,他们的交叉熵定义为:H(p,q)=-sum_limits xp(x)log(q(x)) 。有的时候,神经网络输出的结果并不一定是概率分布,这时就需要把其转换为概率分布,通过Softmax回归实现这个目标,如下示意:Softmax的函数表达式定义为:
softmax(y)i=y′i=eyj∑nj=ieyj 对于回归问题,输出节点一般仅有一个,对于这种类别,常用的损失函数为均方误差(MSE mean squared error) ,其定义如下:
MSE(y,y′)=∑ni=1(yi−y′i)2n 自定义损失函数:损失函数的定义有时需要结合实际问题进行设定,以下给出一个使用自定义损失函数和使用MSE的例子,这里求解的问题为:
代码如下:
# TensorFlow 在模拟训练集上展示完整的训练过程 # author = yooongchun # time = 20180107 # 解决商品销量预测问题,同时引入损失函数概念,说明考察的指标不同,则定义的损失函数也应该有异 import tensorflow as tf from numpy.random import RandomState # 定义训练集大小 batch_size=8 # 输入的placeholder # None参数表示训练集数据放到了一个batch中 # 输入2个节点 x=tf.placeholder(tf.float32,shape=(None,2),name='x-input') # 输出节点,对于回归问题,一般仅有一个输出节点 y_=tf.placeholder(tf.float32,shape=(None,1),name='y-input') # 定义神经网络参数 w1=tf.Variable(tf.random_normal([2,1],stddev=1,seed=1)) # 定义神经网络前向传播过程 # 仅通过线性加权获得 y=tf.matmul(x,w1) # 定义损失函数 # 损失函数参数,分别为多预测和少预测对应的损失 loss_less=1 loss_more=10 # 使用自定义损失函数 loss=tf.reduce_sum(tf.where(tf.greater(y,y_),(y-y_)*loss_more,(y_-y)*loss_less)) # 使用MSE均方值定义损失函数 loss_mse=tf.reduce_mean(tf.square(y-y_)) train_step=tf.train.AdamOptimizer(0.001).minimize(loss_mse) # 生成随机数模拟数据集 rdm=RandomState(1) dataset_size=128 X=rdm.rand(dataset_size,2) # 定义标签,并加入噪音(为了区别不同损失函数之间的差别) Y=[[x1+x2+rdm.rand()/10.0-0.05] for (x1,x2) in X] # 创建会话进行训练 with tf.Session() as sess: tf.global_variables_initializer().run() # 设定训练轮数 STEPS=5000 for i in range(STEPS): start=(i*batch_size)%dataset_size end=min(start+batch_size,dataset_size) # 选择样本训练 sess.run(train_step,feed_dict={x:X[start:end],y_:Y[start:end]}) print(sess.run(w1))
神经网络优化算法
反向传播(backpropagation)算法和梯度下降(gradient decent)算法是用来调整神经网络参数的主要算法
梯度下降算法更新参数
θ 的公式为:θn+1=θn−η∂∂θnJ(θn) ,式中,η 代表学习率(learning rate)梯度下降算法理论上只能达到局部最优,如下示意:
另外,梯度下降算法对于大量数据收敛很慢,这时提出一种改进算法:随机梯度下降(stochastic gradient descent)算法,即对于每一次参数更新,只选择一部分(batch)数据进行更新,这样能兼顾正确性和收敛速度。
- 指数衰减学习率
过大的学习率可能会导致模型参数振荡,而太小的学习率会导致收敛太慢,鉴于此,设置一种先大后小的指数型衰减学习率,以保证模型既能较快收敛又不至于振荡。
- 过拟合问题
当模型的参数多到能够完全区分每个变量,这时数据的噪声已经被模型所包含,这样的模型失去了“预测”的功能,称之为:过拟合。如下图示:
避免过拟合的一个常用方法称之为:正则化(regularization)。正则化的思想是在损失函数中加入刻画模型复杂度的指标。当损失函数为
J(θ) 时,加入正则化后优化的目标即成为J(θ)+λR(w) ,其中,R(w) 刻画的就是模型的复杂度,而λ 代表模型复杂度在总损失中占有的比例。常用的R(w) 有两种,其一为L1 正则化,数学描述为:R(w)=||w||1=∑i|wi| 其二为
L2 正则化,数学描述为:R(w)=||w||22=∑i|w2i| 正则化的目的在于限制权重的大小,使得模型不能任意拟合训练数据中的随机噪声。这两种正则化各有特点,其中,L1正则化会使得模型参数变稀疏(更多参数变为0),而L2则不会;另外,L2正则化公式可导,而L1不可。有时,会把两种正则化方法一起使用,如:
R(w)=∑iα|wi|+(1−α)w2i - 滑动平均模型:滑动平均模型能保证模型在测试数据集上更健壮。
Tensorflow提供了tf.train.ExponentialMovingAverage来实现滑动平均模型。初始化时该函数使用一个衰减率(decay)来控制模型更新的速度。该函数对每一个变量维护一个影子变量(shadow variable),其更新规则为:
shadow_variable=decay∗shadow_variable+(1−decay)∗variable 易看出,越大的衰减率,模型更新速度越慢,Tensorflow提供了动态更新decay的机制,当提供一个
num_updates 参数时,衰减率更新公式为:decay=min{decay,1+num_updates10+num_updates} 当
num_updates=10 时,decay取值如下:一个使用滑动平均模型的例子
# TensorFlow 滑动平均模型 # author = yooongchun # time = 20180113 import tensorflow as tf # 定义变量 v1 = tf.Variable(0, dtype=tf.float32) # step 模拟训练轮数 step = tf.Variable(0, trainable=False) # 定义一个滑动平均类,初始衰减率给定0.99,控制衰减率变量为step ema = tf.train.ExponentialMovingAverage(0.99, step) # 定义一个更新变量滑动平均的操作 maintain_average_op = ema.apply([v1]) with tf.Session() as sess: tf.global_variables_initializer().run() # 获取参数的滑动平均 sess.run(maintain_average_op) print(sess.run([v1, ema.average(v1)])) # 更新变量值 sess.run(tf.assign(v1, 5)) sess.run(maintain_average_op) print(sess.run([v1, ema.average(v1)])) # 更新step sess.run(tf.assign(step, 10000)) sess.run(tf.assign(v1, 10)) sess.run(maintain_average_op) print(sess.run([v1, ema.average(v1)])) sess.run(maintain_average_op) print(sess.run([v1, ema.average(v1)]))