1. 神经元与激活函数

  • 神经元:下图有d个输入,我们可以认为当d是净输入的时候,d就是神经元的输入,让净输入加权求和并加上偏执项,并最终求和,得到一个输出,将这个输出作为激活函数的输入,其会对加权和再做一次运算最后输出a。这就是一个典型的神经元。
  •                         

  • 激活函数:对于上图右部分即激活函数,其主要作用就是加强网络的表达能力,还有学习能力。我们要求激活函数是:
  •         1. 连续的,可以允许个别点不可导,但绝大多数都是可导的,并且是非线性的。这样的函数是可以让参数是可学习的

            2. 我们还希望激活函数是尽可能简单。

            3. 还希望激活函数的治愈是在一个比较小的区间内。


            常用的激活函数有:

    1. Sigmoid:是一个s型的函数,两端是饱和的,即在负无穷和正无穷的时候,导数都趋近于0
    2. logistics:下图红色的曲线,值域在(0,1)间。可以看成一个挤压的函数,不管输入是多少,我都可以把区间挤压到(0,1)之间,当x趋近于0的时候,基本都是一个线性,导数是很明显的。可以看成是一个软性门(softGATE),控制输入的量。
    3. tanh:黑色虚线,值域在(-1,1)之间。是0中心化的。非0中心化的输出会使得其后一层神经元的输入发生偏移,会导致收敛速度降低。

                    

           

            ReLU神经元:

            好处:

    1. 只采用加乘的计算,使得训练过程变得简单。计算更加高效。
    2. 单侧抑制,款兴奋边界。
    3. 因为样本中往往只有较少的是有用的,而ReLU是一个稀疏的网络,只让少数神经元起作用。
    4. 在一定程度上缓解了梯度消失的问题。

             坏处:因为稀疏性,也会使某些神经元死亡,不再起作用,就会误杀。因此有很多优化版本

    2. 全连接神经网络(前馈神经网络/多层感知器)介绍

            前馈神经网络:各神经元分别属于不同的层,层内无连接。而是层与层之间的连接。整个网络无反馈,信号从输入层向输出层单向传播。 

                                            

             他的信息是单向的,经过每层网络提取不同的特征,最终的到结果。

            通用近似定理:因为前馈神经网络具有很强的拟合能力。只要至少有一个隐藏层,一些常见的连续的非线性函数都可以使用前馈神经网络近似。   

                    

            将通用近似定理应用到机器学习上,神经网络就可以当做一个万能函数来使用,可以用来进行复杂的特征转换,或逼近一个复杂的条件分布。

                            

             每一个函数f都是可以进行一个特征转换,直到最后经过分类器g。最后得到我们需要的参数\theta。他们都可以作为激活函数。中间的激活函数一般用ReLU。

    3. 反向传播算法

            先说训练参数的方法,再先就是确定损失函数。

                            

            其中是一个正则化项。是一个F范数,是用来减轻过拟合的。F范数是矩阵范数的一种,他是矩阵元素绝对值平方和再开方。

                                                                

             加入上图是一个神经网络,第一层有n个神经元,第二层有m层神经元。则这两层间的W有n*w个。第三个又有多少神经元……从最左到最优,所有的W就会组成一个矩阵,对于这个矩阵,我们可以用F范数作为他的正则化项。 \lambda是一个正超参数,当他越大,W越接近于0。

            在定义了损失函数后,我们就可以通过梯度下降法来优化参数。

            当有l层时,我们需要对每一层的W都有一个更新。

            

            用链式法则逐一的对参数求导是很低效率的。可以采用反向传播算法来高效的实现求导。

            链式法则:

                    

            反向传播也是在链式法则的基础上实现的。

                            

            假如现在的神经网络是上图这样,当我们按照 经过一层输入层,再经过隐藏层进行正向传播后,我们就可以开始反向传播了。

            设总体的误差为两个神经元的和。Et = E1 + E2。分别算出两个神经元的误差后,得出总体的误差。

                                                    

                    

     

                             

             反向传播算法看了很多网课,我也很难了解本质。之后再总结。

    4. 自动梯度计算

            1. 在进行数值微分的时候,为了减少舍入的误差。将deta x 扩大为两倍。

             2. 符号微分:先化简再求导。

             3. 自动微分:利用链式法则自动计算复合函数的梯度。

            比如把公式转为图的形式解释:

                    公式:  

            如果一个参数有多条路径,则要把每个路径相加。 

            因为输入到输出过程中,维度可能增加也可能减少,当减少时,适合反向传播,增加则适合正向传播,我们一般都是减少为维度的过程,因此一般都是用反向传播。

    5. Himmelblau函数求极小值(实战)

    import tensorflow as tf
    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib import  cm
    
    def himmelblau(x):
        # x : [x[0],x[1]]
        return (x[0]**2 + x[1] - 11)**2 + (x[0]+x[1]**2 - 7)**2
    
    x = np.arange(-6,6,0.1)
    y = np.arange(-6,6,0.1)
    x.shape,y.shape
    
    X,Y = np.meshgrid(x,y) #形成坐标矩阵
    
    Z = himmelblau([X,Y])
    Z
    
    fig = plt.figure('himmelblau')
    ax = fig.gca(projection='3d')
    ax.plot_surface(X,Y,Z)
    ax.view_init(60,30)
    plt.show()
    
    # 参数初始化
    x = tf.constant([4.,0.0])
    
    lr = 0.01
    for t in range(100):
        with tf.GradientTape() as tape:
            tape.watch(x)
            y = himmelblau(x)
            grads = tape.gradient(y,x)
        x = x - lr*grads
    x,y

    6. 全连接神经网络MNIST分类(实战)

            建立了三层网络模型,输入层784个神经元,第一个隐藏层256个神经元,第二个隐藏层128个神经元,输出层10。标签通过one-hot编码,转变为一个向量。

            权重和偏执的维度:

                                    

    完整代码:

    import tensorflow as tf
    import matplotlib.pyplot as plt
    
    tf.__version__
    
    #输入层 h0 784
    # w1
    #隐藏层 h1 256
    # w2
    #隐藏层 h2 128
    # w3
    #输出层 h3 10
    
    # 初始化参数
    w1 = tf.Variable(tf.random.truncated_normal([784,256] , stddev = 0.1) ) #truncated_normal 有截断的正态分布
    w2 = tf.Variable(tf.random.truncated_normal([256,128] , stddev = 0.1))
    w3 = tf.Variable(tf.random.truncated_normal([128,10] , stddev = 0.1))
    
    b1 = tf.Variable(tf.zeros([256]))
    b2 = tf.Variable(tf.zeros([128]))
    b3 = tf.Variable(tf.zeros([10]))
    b1.shape
    
    (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
    x_train.shape , y_train.shape , x_test.shape , y_test.shape
    
    #x值都是0-255之间的,我们想让他先变为0到1之间。并且想数据类型为浮点数。
    x_train = tf.convert_to_tensor(x_train , dtype = tf.float32)/255.
    y_train = tf.convert_to_tensor(y_train,dtype=tf.int32)
    
    # 把训练集拉平,并且转换为tensor类型。
    x_train = tf.reshape( x_train ,[-1,784] )
    max(x_train[0])
    
    # h1: net(z = wx+b ) out1( ReLU(z) )
    # [60000,784]@[784,256] + [256]
    net1 = x_train@w1 + tf.broadcast_to(b1 , [x_train.shape[0],256])
    # net1是输入层的输出,其作为输入再次进入第一个隐藏层的特征
    out1 = tf.nn.relu(net1)
    
    # [60000,256]@[256,128] + [128]
    net2 = out1@w2 + b2
    out2 = tf.nn.relu(net2)
    
    # [60000,128]@[128,10] + [10]
    net3 = out2@w3 + b3
    out3 = tf.nn.softmax(net3)
    
    y_train = tf.one_hot(y_train,depth=10)
    
    loss = tf.nn.softmax_cross_entropy_with_logits(labels = y_train,logits = out3)
    loss
    
    #原本是在每个值上做了个对比,我们想求出一个平均
    loss = tf.reduce_mean(loss)
    loss
    
    with tf.GradientTape() as tape:
        tape.watch([w1,b1,w2,b2,w3,b3])
        out1 = tf.nn.relu(x_train@w1 + b1)
        out2 = tf.nn.relu(out1@w2 + b2)
        out3 = tf.nn.softmax(out2@w3 + b3)
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y_train,logits = out3))
    grads = tape.gradient(loss , [w1,b1,w2,b2,w3,b3])
    
    
    lost_list = []
    
    lr = 0.1
    
    #更新参数
    #w = w - lr*grads
    w1.assign_sub(lr*grads[0])
    b1.assign_sub(lr*grads[1])
    w2.assign_sub(lr*grads[2])
    b2.assign_sub(lr*grads[3])
    w3.assign_sub(lr*grads[4])
    b3.assign_sub(lr*grads[5])
    
    for step in range(3000):
        with tf.GradientTape() as tape:
            tape.watch([w1,b1,w2,b2,w3,b3])
            out1 = tf.nn.relu(x_train@w1 + b1)
            out2 = tf.nn.relu(out1@w2 + b2)
            out3 = tf.nn.softmax(out2@w3 + b3)
            loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y_train,logits = out3))
        grads = tape.gradient(loss , [w1,b1,w2,b2,w3,b3])
        #更新梯度
        w1.assign_sub(lr*grads[0])
        b1.assign_sub(lr*grads[1])
        w2.assign_sub(lr*grads[2])
        b2.assign_sub(lr*grads[3])
        w3.assign_sub(lr*grads[4])
        b3.assign_sub(lr*grads[5])
        #输出
        lost_list.append(loss)
        if step % 100 == 1:
            print(step ,"loss: ",float(loss))
    
    plt.plot(lost_list)
    
    x_test = tf.convert_to_tensor(x_test , dtype=tf.float32)/255
    y_test = tf.convert_to_tensor(y_test , dtype=tf.int32)
    
    x_test  = tf.reshape(x_test , [-1,784])
    
    out1 = tf.nn.relu(x_test@w1 + b1)
    out2 = tf.nn.relu(out1@w2 + b2)
    out3 = tf.nn.softmax(out2@w3 + b3)
    
    y_predict =  tf.math.argmax(out3,axis=-1)
    y_predict = tf.cast(y_test,tf.int32)
    
    result = tf.equal(y_predict,y_test)
    result = tf.cast(result , dtype=tf.int32)
    true_sum = tf.reduce_sum(result)
    accuracy = true_sum/len(y_test)
    accuracy

    最后在测试集上的正确率为 100%。注意此代码用的是批量随机梯下降法。

    因此计算速度偏低

    可以使用小批量随机梯度下降法

    在tensorflow中有专门的batch方式

    # batch_size
    
    batchDataset = tf.data.Dataset.from_tensor_slices((x_train , y_train)).batch(128)
    train_iter = iter(batchDataset)
    sample = next(train_iter) #取值
    sample[0].shape, sample[1].shape

    来源:喜欢蓝喜欢白

    物联沃分享整理
    物联沃-IOTWORD物联网 » 前馈神经网络

    发表评论