深度学习基础之单个神经元

概述

一个神经元由以下几个关键知识点组成:
·激活函数
·损失函数
·梯度下降

神经元的拟合原理

z=∑i=1nwi×xi+b=w⋅x+b
z为输出的结果;x为输入;w为权重;b为偏置值。w和b可以理解为两个变量。模型每次的学习都是为了调整w和b从而得到一个合适的值,最终由这个值配合运算公式所形成的逻辑就是神经网络的模型。

正向传播

数据是从输入到输出的流向传递过来的。当然,它是在一个假设有合适的w和b的基础上的,才可以实现对现实环境的正确拟合。但是,在实际过程中我们无法得知w和b的值具体是多少才算是正常。
于是,我们加入一个训练过程,通过反向误差传递的方法让模型自动来修正,最终得到合适的w和b。

反向传播

BP算法
正向传播的模型是清晰的,所以很容易得出一个关于由b和w组成的对于输出的表达式。接着,也可以得出一个描述损失值的表达式(将输出值与标签直接相减,或者做平方差等运算)。
为了让这个损失值最小化,我们运用数学知识,选择一个损失值的表达式,让这个表达式有最小值,接着通过对其求导的方式,找到最小值时刻的函数切线斜率(也就是梯度),让w和b沿着这个梯度来调整。
至于每次调整多少,我们引入一个叫做“学习率”的参数来控制,这样通过不断的迭代,使误差逐步接近最小值,从而达到我们的目标。

激活函数

激活函数的主要作用就是加入非线性因素,以解决线性模型表达能力不足的缺陷,在整个神经网络里起到至关重要的作用。
因为神经网络的数据基础是处处可微的,所以选取的激活函数要保证数据输入与输出也是可微的。在神经网络里常用的函数有Sigmoid、Tanh和relu等,下面逐个介绍。

Sigmoid函数

在这里插入图片描述
作用:Sigmoid函数能够把输入的值“压缩”到0~1之间。
在TensorFlow中对应的函数

tf.nn.sigmoid(x,name=name)

在这里插入图片描述
从图像上看,随着x趋近正负无穷大,y对应的值越来越接近1或0,这种情况叫做饱和。处于饱和态的激活函数意味着,当x=100和x=1000时的反映都是一样的,这样的特性转换相当于将1000大于100十倍的这个信息给丢失了。
所以,为了能有效使用Sigmoid函数,x的取值区间只能是(-6,6),而在(-3,3)之间应该会有不错的效果。

Tanh函数

Tanh函数可以说是Sigmoid函数的值域升级版,由Sigmoid函数的0~1之间升级到-1 ~ 1之间。但是Tanh函数不能完全替代Sigmoid函数,在某些输出需要大于0的情况下,还是要用到Sigmoid函数。
在这里插入图片描述
在这里插入图片描述
在TensorFlow中对应的函数
tf.nn.tanh(x,name=name)
显然,Tanh函数同样存在饱和问题。
######ReLU函数
该函数更为常用。其数学形式为f(x) = max(0,x)
解释:大于0的留下,否则一律为0。ReLU函数的应用广泛性与它的优势是分不开的,这种正向信号的重视,忽略了负向信号的特性,与我们人类神经元细胞对信号的反映极其相似。所以在神经网络中取得了很好的拟合效果。
另外,由于ReLU函数运算简单,大大地提升了机器的运行效率,也是R俄LU函数的一个很大的优点。
在这里插入图片描述
与ReLU函数类似的还有Softplus函数,两者的区别在于Softplus函数会更加平滑,但是计算量更大。公式为f(x)=ln(1+e^x)
在这里插入图片描述
虽然ReLU函数在信号响应上有很多优势,但这仅仅是在正向传播方面。由于其对负值的屏蔽,因此很容易使模型输出全零从而无法再进行训练。因此,在基于ReLU的基础上又演化了Elus函数
在这里插入图片描述
使用Elus函数时,不使用批处理比使用批处理要好。
在TensorFlow中的函数:

tf.nn.relu(features,name=None)
tf.nn.relu6(features,name=None)
tf.nn.softplus(features,name=None)
tf.nn.elu(features,name=None)
#Leaky relus公式没有专门的函数,可以用现有的函数组合而得到:
tf.maximum(x,leak*x,name = name)#leak为传入的参数,可以设置为0.01等
Swish函数

Swish函数是谷歌公司发现的一个效果更优于Relu的激活函数。经过测试,在保持所有的模型参数不变的情况下,将Relu修改为从Swish函数,模型的准确率均有提升。公式为f(x)=x*sigmoid(βx)
其中β为x的缩放参数,一般情况取默认值即可。
在TensorFlow低版本中,没有单独的Swish函数,需要手动封装。

def Swish(x,beta=1):
	return x*tf.nn.sigmoid(x*beta)
激活函数总结

神经网络中,运算特征是不断进行循环计算,所以在每代循环过程中,每个神经元的值也是在不断变化的。这就导致了Tanh函数在特征相差明显时的效果很好,在循环中不断扩大特征效果并显示出来。
但有时当计算的特征间的相差虽比较复杂却没有明显区别,或是特征间的相差不是特别大时,就需要更加细微的分类判断,这时Sigmoid函数的效果就会更好一些。
后来,出现的ReLU函数优势是,经过其处理后的数据有更好的稀疏性。即,将数据转化为只有最大值,其他都是为0。这种变换可以近似程度地最大保留数据特征,用大多数元素为0的稀疏矩阵来实现。

softmax算法——处理分类问题

softmax基本是可以算是分类任务的标配了。
对于前面讲的激活函数,其输出值只有两种,而现实生活中需要对某一问题进行多种分类,例如图片分类,这时候就需要使用softmax算法。
softmax,就是如果判断输入属于某一个类的概率大于属于其他类的概率,那么这个类对应的值就逼近于1,其他类的值就逼近于0。该算法主要应用于多分类,而且是互斥的。公式为
softmax = exp(logits)/reduce_sum(exp(logits),dim)
把所有值的e的n次方计算出来,求和后算每个值占的比率,保证总和为1,一般就可以认为softmax得出的是概率。

原理图

(这张图从id:理想几岁那里偷来的)
在这里插入图片描述

常用的分类函数
操作描述
tf.nn.softmax(logits,name=None)计算softmax
tf.nn.log_softmax(logits,name=None)对softmax取对数

在实际应用中,softmax伴随的分类标签都为one_hot编码,而且这里哟一个小技巧,在softmax时需要将目标分成几类,就在最后这层放几个节点。

损失函数

损失函数是决定网络学习质量的关键。无论什么样的网络结构,如果使用的损失函数不正确,最终都将难以训练出正确的模型。

常见的损失函数

损失函数,使用于描述模型预测值与真实值的差距大小。一般有两种比较常见的算法——均值平方差(MSE)和交叉熵。

MSE

1 n ∑ i = 1 n ( y − p ) 2 \dfrac {1}{n}\sum ^{n}_{i=1}\left( y-p\right) ^{2} n1i=1n(yp)2
解释:y为真实标签,p即predict值。均方误差的值越小,表明模型越好。类似的损失算法还有均方根误差(RMSE)(将MSE开平方),平均绝对值误差(MAD)(对一个真实值与预测值相减的绝对值取平均值)等。

交叉熵

c = 1 n ∑ x [ y ln ⁡ a + ( 1 − y ) × ln ⁡ ( 1 − a ) ] c=\dfrac {1}{n}\sum _{x}\left[ y\ln a+\left( 1-y\right) \times \ln \left( 1-a\right) \right] c=n1x[ylna+(1y)×ln(1a)]
交叉熵也是loss算法的一种,一般用于分类问题上,表达的意思为预测输入样本属于某一类的概率。公式中y代表真实值分类(0或1),a代表预测值。
交叉熵也是值越小,代表预测结果越准。
注意:这里用于计算的a也是通过分布统一化处理的(或者是经过Sigmoid函数激活尔等),取值范围在0~1之间。

损失算法的选取

损失函数的选取取决于输入标签数据的类型:如果输入的是实数、无界的值,损失函数使用MSE;如果输入标签是位矢量(分类标志),使用交叉熵会更合适。

Tensorflow中常见的loss函数
MSE

TensorFlow中没有单独的MSE函数,需要自己组合。

MSE = tf.reduce_mean(tf.pow(tf.sub(logits,outputs),2.0))
MSE = tf.reduce_mean(tf.square(tf.sub(logits,outputs)))
MSE = tf.reduce_mean(tf.square(logits,outputs))

logits代表标签值,outputs代表预测值。
同样也可以组合其他类似loss,例如:
Rmse = tf.sqrt(tf.reduce_mean(tf.pow(tf.sub(logits,outputs),2.0)))
mad = tf.reduce_mean(tf.sub(logits,output))

交叉熵

在TensorFlow中常见的交叉熵函数有:
·Sigmoid交叉熵
·softmax交叉熵
·Sparse交叉熵
·加权Sigmoid交叉熵

操作描述
tf.nn.sigmoid_cross_entropy_with_logits
(logits,targets)
计算输入logits和targets的交叉熵
tf.nn.softmax_crosss_entropy_with_logits
(logits,labels,name=None)
计算logits和labels的softmax交叉熵
Logits和labels必须为相同的shape与数据类型
tf.nn.sparse_softmax_cross_entropy_with_logits
(logits,labels,name=None)
计算logits和labels的softmax交叉熵
但不需要one-hot编码
tf.nn.weighted_cross_entropy_with_logits
(logits,targets,pos_weights,name=None)
在交叉熵的基础上给第一项
乘以一个系数(加权)
是增加或减少正样本在计算交叉熵时的损失值
softmax算法与损失函数的综合应用

背景:交叉熵这个比较生僻的术语,在深度学习领域中却是最常见的。由于其常见性,在TensorFlow中会被封装成多个版本,有的公式里直接带了交叉熵,有的需要自己单独求出,而在构建模型时,如果对这块知识不扎实,出现问题时会很难分析是模型的问题还是交叉熵的使用问题。因此有必要通过几个小实例将其弄得更明白一些。

交叉熵实验:

下面一段代码,假设有一个标签labels和一个网络输出值logits。
这个实例就是以这两个值来进行以下3次实验。
(1)两次softmax实验:将输出值logits分别进行1次和2次softmax,观察两次的区别及意义。
(2)观察交叉熵:将(1)中的两个值分别进行softmax_cross_entropy_with_logits,观察它们的区别。
(3)自建公式实验:将做两次softmax的值放到自建组合的公式里得到正确的值。

import tensorflow as tf
labels = [[0,0,1],[0,1,0]]
logits = [[2,0.5,6],
          [0.1,0,3]]
logits_scaled = tf.nn.softmax(logits)
logits_scaled2 = tf.nn.softmax(logits_scaled)

result1 = tf.nn.softmax_cross_entropy_with_logits(labels=labels,logits=logits)
result2 = tf.nn.softmax_cross_entropy_with_logits(labels=labels,logits=logits_scaled)
result3 = -tf.reduce_sum(labels*tf.log(logits_scaled),1)

with tf.Session() as sess:
    print("scaled=",sess.run(logits_scaled))
    print("scaled2=",sess.run(logits_scaled2))
    print("rel1=",sess.run(result1))
    print("rel2=", sess.run(result2))
    print("rel3=", sess.run(result3))

结果:scaled= [[0.01791432 0.00399722 0.97808844]
[0.04980332 0.04506391 0.90513283]]
scaled2= [[0.21747023 0.21446465 0.56806517]
[0.2300214 0.22893383 0.5410447 ]]
rel1= [0.02215516 3.0996735 ]
rel2= [0.56551915 1.4743223 ]
rel3= [0.02215518 3.0996735 ]

分析:可以看到logits里面的值原本加和都是大于1的,但是经过softmax之后,总和变成了1。样本中第一个是跟标签分类相符的,第二与标签分类不符,所以第一个的交叉熵比较小,是0.02215516,第二个的交叉熵比较大,是3.0996735 。
比较scaled和scaled2可以看到:经过第二次的softmax之后,分布概率会有所变化,而scaled才是我们真实转化的softmax值。
比较rel1和rel2可以看到:传入softmax_cross_entropy_with_logits的logits是不需要进行softmax的。如果将softmax后的值scaled传入softmax_cross_entropy_with_logits就相当于进行了两次softmax转化。
对于已经用了softmax转换过的scaled,在计算 loss时就不能再用TensorFlow里面的softmax_cross_entropy_with_logits了。需自行写一个loss函数,参见rel3的生成。

one_hot实验

输入的标签也可以不是标准的one-hot。下面用一组总和也是1但是数值中每个值都不等于1或0的数组来代替标签,看看效果。

labels =[[0.4,0.1,0.5],[0.3,0.6,0.1]]
logits = [[2,0.5,6],
          [0.1,0,3]]
result4 = tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=labels)
with tf.Session() as sess:
    print("re14=",sess.run(result4),"\n")

结果:
re14= [2.1721554 2.7696736]
分析:对比前面的re14发现,对于正确分类的交叉熵和错误分类的交叉熵,二者的结果差别没有标准one-hot那么明显。

sparse交叉熵的使用

因为sparse必须要求labels是正整数且从0开始,所以将labels值重新设置。这时候是不能使用one-hot编码的。如果设置了y占位符,还需要修改为y = tf.placeholder(tf.int32,[None])

labels =[2,1]
result5 = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits,labels=labels)
with tf.Session() as sess:
    print("re15=", sess.run(result5), "\n")

结果:
re15= [0.02215516 3.0996735 ]
我们发现rel5和rel1基本相同。

计算loss值

在真正的神经网络中,得到一个数组并不能满足要求,还需要对其求均值,使其最终变成一个具体的数值。
(1)对于softmax_cross_entropy_with_logits后的结果求loss直接取均值。
(2)对于softmax后的结果使用tf.reduce_mean(-tf.reduce_sum(labels*tf.log(logits_scaled)))。

梯度下降——将模型逼近最小偏差
梯度下降的作用

梯度下降法是一个最优化算法,通常也称为最速下降法,常用于机器学习和人工智能中递归性地逼近最小偏差模型,梯度下降的方法也就是用负梯度方向为搜索方向,沿着梯度下降的方法求解最小值。
在训练过程中,每次的正向传播后都会得到输出值与真实值的损失值,这个损失值越小,代表模型越好,于是梯度下降的算法就用在这里,帮助寻找最小的那个损失值,从而反推出对应的学习参数b和w,达到模型优化的效果。

TensorFlow中的梯度下降函数

在TensorFlow中是通过一个叫做Optimizer的优化器类进行训练的。对于不同算法的优化器,在tensorflow中会有不同的类。

操作描述
tf.train.GradientDescentOptimizer(learning_rate,
use_locking=False,name=‘GradientDescent’)
一般的梯度下降算法的Optimizer
tf.train.AdamOptimizer(learning_rate=0.001,
beta1=0.9,beta2=0.999,epsilon=1e-08,
use_locking=False,name=‘Adam’)
创建Adam优化器
tf.train.RMSPropOptimizer(learning_rate,dacay=0.9,
momentum=0.0,epsilon=1e-10,name=’'RMSProp)
创建RMSProp算法优化器

在训练过程中,先实例化一个优化函数如tf.train.GradientDescentOptimizer,并基于一定的学习率进行梯度优化训练。
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
接着使用一个minimize()的操作,里面传入损失值节点loss,再启动一个外层的循环,优化器就会按照循环的次数一次次沿着loss最小值的方向优化参数了。

退化学习率

背景:
设置学习率的大小,是在精度和速度之间找到一个平衡:
·如果学习率的值比较大,则训练速度会提升,但结果的精度不够;
·如果学习率的值比较小,精度虽然提升了,但训练会耗费太多时间。
因此,引出退化学习率。
退化学习率又叫做学习率衰减,当训练刚开始时使用大的学习率加快速度,训练到一定程度后使用小的学习率来提高精度。
学习率的衰减速度是由global_step和decay_steps来决定的。具体公式为:
decayed_learning_rate = learning_rate*decay_rate^(global_step/decay_steps)
代码:

learning_rate = tf.train.exponential_decay(starter_learning_rate,global_step,decay_steps=100000,decay_rate=0.96)

这种方式定义的学习率就是退化学习率,它的意思是当前迭代到global_step步,学习率每一步都按照每10万步缩小到0.96%的速度衰退。

实例:实现每10步衰减0.9

import tensorflow as tf
global_step = tf.Variable(0,trainable=False)
initial_rate = 0.1
learning_rate = tf.train.exponential_decay(initial_rate,global_step,decay_steps=10,decay_rate=0.9)
opt = tf.train.GradientDescentOptimizer(learning_rate)
add_global = global_step.assign_add(1)
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    print(sess.run(learning_rate))
    for i in range(20):
        
        g,rate = sess.run([add_global,learning_rate])
        print(g,rate)

结果
0.1
1 0.09895193
2 0.09791484
3 0.09688862
4 0.095873155
5 0.094868325
6 0.09387404
7 0.092890166
8 0.092890166
9 0.09095325
10 0.09095325
11 0.08905673
12 0.088123344
13 0.08719975
14 0.08628584
15 0.0853815
16 0.084486626
17 0.08360115
18 0.08272495
19 0.08185792
20 0.08099999
注意:使用的时候,一定要把当前迭代次数global_step传进去,否则无法实现衰减的功能。这里使用了。

初始化学习参数

在定义学习参数时可以通过get_variable和Variable两种方式,对于一个网络模型,参数不同的初始化情况,对网络的影响很大。
目前,用到的有tf.random_normal(shape)和tf.zeros(shape),之后用到新的会添加。

Maxout网络

Maxout是将激活网络变成一个网络选择器,原理就是将多个神经元并列地放在一起,让它们的输出结果中找到最大的那个,代表对特征响应最敏感,然后取这个神经元的结果参与后面的计算。

在这里插入图片描述
它的公式可以理解成:
z1 = w1x+b1
z2 = w2
x+b2
z3 = w3x+b3
z4 = w4
x+b4
z5 = w5*x+b5
……
out = max(z1,z2,z3,z4,z5…)
这样做的好处是,模仿人类的神经细胞,不同的神经元会因为输入的不同而产生不同的输出,即不同的神经细胞关心不同的信号。现在的做法就相当于同时使用多个神经元放在一起,哪个有效果就用哪个。

实例:使用maxout网络实现MNIST分类
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/")

print ('输入数据:',mnist.train.images)
print ('输入数据打shape:',mnist.train.images.shape)

import pylab
im = mnist.train.images[1]
im = im.reshape(-1,28)
pylab.imshow(im)
pylab.show()


print ('输入数据打shape:',mnist.test.images.shape)
print ('输入数据打shape:',mnist.validation.images.shape)


import tensorflow as tf #导入tensorflow库

tf.reset_default_graph()
# tf Graph Input
x = tf.placeholder(tf.float32, [None, 784]) # mnist data维度 28*28=784
y = tf.placeholder(tf.int32, [None]) # 0-9 数字=> 10 classes

# Set model weights
W = tf.Variable(tf.random_normal([784, 10]))
b = tf.Variable(tf.zeros([10]))


z= tf.matmul(x, W) + b


maxout = tf.reduce_max(z,axis= 1,keep_dims=True)
# Set model weights
W2 = tf.Variable(tf.truncated_normal([1, 10], stddev=0.1))
b2 = tf.Variable(tf.zeros([1]))
# 构建模型
pred = tf.nn.softmax(tf.matmul(maxout, W2) + b2)

# 构建模型
#pred = tf.nn.softmax(z) # Softmax分类

# Minimize error using cross entropy
#cost = tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred), reduction_indices=1))
cost = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=z))
#参数设置
learning_rate = 0.04
# 使用梯度下降优化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)

training_epochs = 200
batch_size = 100
display_step = 1


# 启动session
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())# Initializing OP

    # 启动循环开始训练
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(mnist.train.num_examples/batch_size)
        # 遍历全部数据集
        for i in range(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            # Run optimization op (backprop) and cost op (to get loss value)
            _, c = sess.run([optimizer, cost], feed_dict={x: batch_xs,
                                                          y: batch_ys})
            # Compute average loss
            avg_cost += c / total_batch
        # 显示训练中的详细信息
        if (epoch+1) % display_step == 0:
            print ("Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(avg_cost))

    print( " Finished!")


结果:
Epoch: 0200 cost= 0.290478232
 Finished!
Maxout的拟合功能很强大,但是也会有节点过多,参数过多,训练过慢的缺点。













  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值