多层感知器(MLP)
前几篇博文线性回归的原理和实现 和Softmax和交叉熵的原理和实现 都是单层神经网络,但是所谓深度学习是有深度的,因此这里介绍多层感知器(Multi Layer Perceptron)。MLP在单层神经网络的基础上引入了一到多个隐藏层(hidden layer),隐藏层位于输入层和输出层之间,如下图:
线性多层神经网络可以等价变换为单层神经网络,如下:
这样无法表示复杂的映射。这个问题的根源在于全连接层只是对数据做仿射变换(affine transformation),而多个仿射变换的叠加仍然是一个仿射变换。解决问题的一个方法是引入非线性变换,例如对隐藏变量使用按元素运算的非线性函数进行变换,然后再作为下一个全连接层的输入。这个非线性函数被称为激活函数(activation function)。带隐层和激活函数的神经网络可以表示为:
激活函数
Sigmoid
即著名的Logistic函数,sigmoid函数可以将元素的值变换到0和1之间。公式和曲线如下:
导数和导函数曲线:
不足之处:
1.输出值在(0,1)之间,期望值为0.5,不符合均值为0的理想状态。当激活函数的均值不为0时,会导致收敛缓慢,会使得下一层的输入数据均值不为零。以 f=sigmoid(wx+b)为例, 假设输入均为正数(或负数),因为激活函数的输出均值大于零,那么对w的导数总是正数(或负数),这样在反向传播过程中要么都往正方向更新,要么都往负方向更新,导致有一种捆绑效果,使得收敛缓慢。
2.现有的梯度下降算法 严重依赖逐层的梯度计算值,当sigmoid函数输出值在(-inf,-5)或(5,+inf)之间时,梯度近似为0,会发生梯度消失的情况。
Tanh
Tanh(双曲正切)函数可以将元素的值变换到-1和1之间。公式和曲线如下:
导数和导函数曲线如下:
优缺点:优点:输出期望值为0。缺点:依然存在梯度消失的情况。
ReLU
有关ReLU的原理可浏览《线性修正单元(ReLU)》。公式和曲线如下:
当x=0时,设该函数导数为0,则导函数曲线如下:
Leaky ReLU
是ReLU的改进,函数如下:
优点:解决了梯度消失的问题。缺点:期望值不为0,a是固定的,合适的a值较难设定,导致实际使用时性能不稳定。
PReLU
参数化ReLU,是LeakyReLU的改进,如下:
将 Leaky Relu 函数中的超参数a设置为变量,被训练到最优,以解决a值较难设定的问题。
优点:更大自由度。缺点:更大的过拟合风险,增加了参数数量。
RReLU
随机ReLU,也是LeakyReLU的变体。斜率参数是在一个给定的范围内随机抽取的值。
CReLU
公式:CReLU(x)=[ReLU(x), ReLU(−x)] 。输出维度会自动加倍。 比如 −3→[0,3 3→[3,0]。
ELU
SELU
经过该激活函数后使得样本分布自动归一化到0均值和单位方差(自归一化,保证训练过程中梯度不会爆炸或消失,效果比Batch Normalization 要好) 。其实就是ELU乘了个lambda,关键在于这个lambda是大于1的。以前relu,prelu,elu这些激活函数,都是在负半轴坡度平缓,这样在activation的方差过大的时候可以让它减小,防止了梯度爆炸,但是正半轴坡度简单的设成了1。而selu的正半轴大于1,在方差过小的的时候可以让它增大,同时防止了梯度消失。这样激活函数就有一个不动点,网络深了以后每一层的输出都是均值为0方差为1。
MAXOUT
https://blog.csdn.net/hjimce/article/details/50414467
激活函数一览
多层感知器实现图片分类
基于tensorflow实现,使用fashion_mnist数据集。
from keras.datasets import fashion_mnist import numpy as np (x_train,y_train),(x_test,y_test)=fashion_mnist.load_data() x_train=x_train.reshape(-1,784).astype('float32')/255.0 x_test=x_test.reshape(-1,784).astype('float32')/255. y_train_onehot=np.zeros((y_train.shape[0],10)) y_test_onehot=np.zeros((y_test.shape[0],10)) for i,label in enumerate(y_train): y_train_onehot[i,label]=1 for i,label in enumerate(y_test): y_test_onehot[i,label]=1 print(x_train.shape) print(x_test.shape) print(y_train.shape) print(y_test.shape)
手动实现带感知器的多层(一个隐层)感知器。
import tensorflow as tf num_inputs=784 num_hiddens=256 num_outputs=10 #初始化模型变量 w1 = tf.Variable(initial_value=tf.random_normal(shape=(num_inputs,num_hiddens),stddev=0.1,mean=0.)) b1 = tf.Variable(initial_value=tf.zeros(num_hiddens)) w2 = tf.Variable(initial_value=tf.random_normal(shape=(num_hiddens,num_outputs),stddev=0.1,mean=0.)) b2 = tf.Variable(initial_value=tf.zeros(num_outputs)) #定义模型 x=tf.placeholder(tf.float32,shape=(None,num_inputs)) y_=tf.placeholder(tf.float32,shape=(None,num_outputs)) net=tf.matmul(x,w1)+b1 net=tf.nn.relu(net) net=tf.matmul(net,w2)+b2 #定义softmax def softmax(o): #tf.nn.softmax() y_exp=tf.exp(o) exp_sum=tf.reduce_sum(y_exp,axis=1,keep_dims=True) #求和 保持维度不变 y=tf.divide(y_exp,exp_sum) return y y=softmax(net) #定义正确率 correct_prediction=tf.equal(tf.argmax(y,1),tf.argmax(y_,1)) accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32)) #定义交叉熵损失函数 loss=-tf.reduce_mean(y_*tf.log(tf.clip_by_value(y,1e-10,1.0))) #定义优化器 sgd=tf.train.GradientDescentOptimizer(0.001).minimize(loss)
训练网络。
%%time BATCH_SIZE=64 w_history=[] b_history=[] EPOCHS=30 BATCH_SIZE=128 pred=None #生成对话,训练网络 with tf.Session() as sess: init_op=tf.global_variables_initializer() #初始化所有的Variable sess.run(init_op) for i in range(EPOCHS): xlen=x_train.shape[0] permutation = np.random.permutation(xlen) x_data = x_train[permutation] y_data = y_train_onehot[permutation] for j in range(xlen//BATCH_SIZE-1): x_batch=x_data[j*BATCH_SIZE:(j+1)*BATCH_SIZE,:] y_batch=y_data[j*BATCH_SIZE:(j+1)*BATCH_SIZE,:] sess.run(sgd,feed_dict={x:x_batch,y_:y_batch}) acc=sess.run(accuracy,feed_dict={x:x_data[:1000],y_:y_data[:1000]}) print('%d train acc:%f'%(i,acc)) acc=sess.run(accuracy,feed_dict={x:x_test,y_:y_test_onehot}) pred=sess.run(y,feed_dict={x:x_test[:30],y_:y_test_onehot[:30]}) print('test acc:',str(acc))
训练精度超过70%。
参考文献
[1]Jerry_Jin.Difference between ReLU、LReLU、PReLU、CReLU、ELU、SELU.https://www.cnblogs.com/jins-note/p/9646602.html . 2018-09-14