今天从第四章学起-------深层神经网络。
深层神经网络是实现“多层非线性变换”最常用的一种方法:
在线性模型中,模型输出为输入的加权和。他的最大特点是任意线性模型的组合仍然是线性模型。在没有激活函数时,只进行简单的前向传播,就算网络层数再多也与单层的网络没有什么区别。而线性模型能够解决的问题是有限的,这也是为什么深度学习要强调非线性。
例如在线性可分问题中,使用liner就可以很好区分不同颜色的点。但是将liner用于线性不可分问题,则会出现右图情况,
若将激活函数换成RELU,神经网络模型就可以很好区分不同颜色的点了
下图为几种常见神经网络的激活函数图形
sigmoid
tanh
RELU
TensorFlow提供了7种不同的非线性激活函数,tf.nn.relu、tf.sigmoid、tf.tanh是比较常见的几种,也支持自己定义的激活函数。
感知器可以简单理解为单层神经网络(没有隐藏层)。从下图可以看出,感知器并不能将两种不同颜色的点分开,也就是说感知器无法模拟异或运算的功能。
但是加入隐藏层后,异或问题就可以很好解决。
神经网络模型的效果以及优化的目标就是通过损失函数来定义的。
通过神经网络解决多分类问题最常用的办法是设置n个输出节点,其中n为类别的个数。对于每一个样例,神经网络可以得到一个n维数组作为输出结果。如何判断一个输出向量和期望的向量有多接近?交叉熵是常用的评判方法之一,他是分类问题中国比较常用的损失函数。交叉熵值越小,两个概率分布越接近。
如何将神经网络前向传播得到的结果也变为概率分布呢,Softmax回归就是一个非常常用的方法。他只是一层额外的处理层,将神经网络输出变成一个概率分布。
与分类问题不同,回归问题解决的是对具体数值的预测,最常用的损失函数是均方误差(MSE)。
自定义损失函数在p79页有详细介绍。下例为自定义损失函数的例子
# -*- coding: utf-8 -*- import tensorflow as tf from numpy.random import RandomState batch_size = 8 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 = tf.reduce_sum(tf.where(tf.greater(y,y_),(y-y_)*1,(y_-y)*10)) train_step = tf.train.AdamOptimizer(0.001).minimize(loss) #通过随机数生成一个模拟数据集 rdm = RandomState(1) dataset_size = 128 X = rdm.rand(dataset_size,2)#(128,2),0~1之间 Y = [[x1+x2+rdm.rand()/10.0-0.05] for (x1,x2) in X]#回归的正确值为两个输入的和加一个随扰动(-0.05~0.05) #创建一个会话来运行tensorflow程序 with tf.Session() as sess: init_op = tf.initialize_all_variables() sess.run(init_op) #print(sess.run(w1)) # print(sess.run(w2))#未经训练的权值 STEPS = 5000 for i in range(STEPS): start = (i*batch_size) % dataset_size#每次选8个样本进行训练 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))
梯度下降算法主要用于优化单个参数的取值,而反向传播算法给出了一个高效的方式在所有参数上使用梯度下降算法,使神经网络在训练数据上的损失函数尽可能小。
梯度下降算法可能不能达到全局最优,另外一个问题就是计算时间太长。因为要在全部数据上最小化损失,所以损失函数J是所有训练数据上的损失和。
为了加速训练过程,可以使用随机梯度下降算法,这个算法优化的不是在所有训练数据上的损失和,而是在每一轮迭代中,随机优化某一条训练数据上的损失函数。
为了综合梯度下降算法和随机梯度下降算法的优缺点,在实际中一般采用这两种算法的折中------每次计算一小部分训练数据的损失函数。这一小部分数据成为一个batch。
TensorFlow提供了一种灵活的学习率设定方法,指数衰减法,通过这个函数,可以先使用较大的学习率来快速得到一个比较优的解,然后随着迭代次数的增加逐步减小学习率,使得模型在训练后期更加稳定。
为了避免过拟合问题,一个非常常用的方法是正则化,正则化的思想就是在损失函数中加入刻画模型负责程度的指标。正则化的思想都是希望通过限制权重的大小,使得模型不能任意拟合训练数据中的随机噪音。
L1正则化会让参数变得更加稀疏(会有更多的参数变为0,这样可以达到类似特征提取的功能),而L2不会。也可以在实际操作时将L1与L2同时使用。正则化加在损失函数后,为了解决损失函数定义过于长的问题,可以使用TensorFlow中提供的集合(collection)。下面代码给出了通过集合计算一个5层神经网络L2正则化的损失函数的方法。
# -*- coding: utf-8 -*- import tensorflow as tf from numpy.random import RandomState # 获得一层神经网络边上的权重,并将这个权重的L2正则化损失加入名称为'losses'的集合里 def get_weight(shape, lamada): # 生成对应一层的权重变量 var = tf.Variable(tf.random_normal(shape), dtype=tf.float32) tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lamada)(var)) return var x = tf.placeholder(tf.float32, shape=(None, 2), name='x_input') y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y_input') batch_size = 8 # 定义每层神经网络的节点个数 layer_dimension = [2, 10, 10 ,10, 1] # 获取神经网络的层数 n_layers = len(layer_dimension)#5 # 这个变量表示前向传播时最深层的节点,最开始的时候是输入层 cur_layer = x # 当前层的节点数 in_dimension = layer_dimension[0]#2 # 通过一个循环生成5层全连接的神经网络结构 for i in range(1, n_layers): # 获取下一层节点的个数 out_dimension = layer_dimension[i] # 获取当前计算层的权重并加入了L2正则化损失 weight = get_weight([in_dimension, out_dimension], 0.001) # 随机生成偏置 bias = tf.Variable(tf.constant(0.1, shape=[out_dimension])) # 计算前向传播节点,使用RELU激活函数 cur_layer = tf.nn.relu(tf.matmul(cur_layer, weight) + bias) # 进入下一层之前,更新下一层节点的输入节点数 in_dimension = layer_dimension[i] # 计算模型数据的均值化损失加入损失集合 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')) global_step = tf.Variable(0) # 学习率的设置:指数衰减法,参数:初始参数,全局步骤,每训练100轮乘以衰减速度0.96(当staircase = True的时候) learning_rate = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True) train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step) rdm = RandomState(1) dataset_size = 128 X = rdm.rand(dataset_size, 2) # 加入了一个噪声值,-0.05~0.05之间 Y = [[x1 + x2 + rdm.rand() / 10.00 - 0.05] for (x1, x2) in X] with tf.Session() as sess: init_op = tf.initialize_all_variables() sess.run(init_op) # print sess.run(w1) 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]}) if i % 100 == 0: total_loss = sess.run(loss, feed_dict={x:X, y_: Y}) print("After %d training_steps, loss on all data is %g" % (i, total_loss))
使用滑动平均模型可以让模型在测试数据上更健壮。