美文网首页
笨方法学机器学习(二)全连接神经网络

笨方法学机器学习(二)全连接神经网络

作者: 寒夏凉秋 | 来源:发表于2017-09-12 15:10 被阅读0次

    网络构成

    算法假设

    我们的大脑接收眼睛观察传播来的数据后,会对其进行一层层的神经元去解析数据,然后得到我们对于所见的判断。然而我们对这个分析过程的了解以及脑部的研究较为浅,无法得知其脑部工作的原理。但是,我们是否可以对其进行部分抽象化为以下过程:


    脑部在接收一系列数据的时候进行了一个函数式(function)的抽象,得到了其认知。

    那么,我们的神经网络就是模拟这个过程,将输入信号,神经元的function处理,以及输出,都数字化。用统计与学习的方式,将这个过程一步步还原;


    全连接神经网络

    其全连接神经网络规则如下:

    • 神经元按照层来布局。最左边的层叫做输入层,负责接收输入数据;最右边的层叫输出层,我们可以从这层获取神经网络输出数据。输入层和输出层之间的层叫做隐藏层,因为它们对于外部来说是不可见的。
    • 同一层的神经元之间没有连接。
    • 第N层的每个神经元和第N-1层的所有神经元相连(这就是full connected的含义),第N-1层神经元的输出就是第N层神经元的输入。
    • 每个连接都有一个权值。

    神经元

    神经元是构成神经网络的基本单位:


    一个神经元的组成为:

    • 输 入:n维度向量x
    • 线性加权:
    • 激活函数: $H(x)$,要求非线性,容易求导数
    • 输出 a

    激活函数的选择

    (1)sigmoid函数

    (2)tanh函数

    (3)relu函数

    计算样例

    下面我们来用最简单是sigmoid函数来尝试手算一个很简单的神经网络:


    • 中间蓝色的神经元代表了隐层神经元,其左边的是W权值,b代表了其偏置项。
    • X 为输入层
    • 红色神经元代表输出,只有一个神经元代表这个神经网络只有一个输出

    x1与z1的连线权值为$0.1$,x1的输出为$0.5$,所以x1到z1的输入为$0.1$*$0.5$,同理。x2到z1的输入为$-0.06$,所以根据计算公式,z1的输入为:$0.1 0.5 +0.2-(0.3)+0.01$,再将其进行激活函数处理,得到Z1的输出:$0.50224$

    神经网络的训练

    • 我们需要知道一个神经网络的每个连接上的权值.
    • 我们可以说神经网络就是一个模型,这些权值就是模型的参数(即模型要学习的东西),
    • 对于这个神经网络的连接方式,网络层数,每层的节点数,这些,我们是事先设置的,成为超参数.

    目标函数与误差计算

    在监督学习中,对于每一个样本,我们得到其特征$x$,标记$y$.我们通过模型$h(x)$计算得到输出值:
    ,显然$y$是其真实值, 是其神经网络的预测值,我们希望预测值跟真实值更接近,
    数学中常用的办法是将两个值的差的平方的$1/2$来代表其接近程度:

    我们将$e$当做单个成本的误差,将所有的训练样本的误差值相加,得到其误差误差项$E$:


    训练的目标,就是找到合适的$w$,使误差$E$最小;

    训练方法:反向传播算法(Back Propagation)

    简单的说,反向传播算法的过程就是:

    • 给定的一个输入集$X$,取其中的一个向量$x$
    • 先正向计算$x$的神经网络,得到其输出值$y$
    • 训练向量$x$有人为标定的标签$l$(label),计算其预测值跟真实值之间的误差,
    • 将误差项每一层地反向传播,得到每一层的误差,然后利用每一层的误差去更新每一层的权值去拟合模型

    我们先给出各层的计算公式,然后再进行数学公式的推导(不擅长数学的可以跳过,等代码实现再回来看推导)

    (1) 输出层的误差计算:



    其中, 是节点i的误差值,$Yi$是节点$i$的输出值,$Ti$是对应节点$i$的目标值

    (2) 隐藏层的误差计算与传递



    我们知道:

    $ai$是节点i的输出值,$Wki$节点i到它下一层节点$i$连接的权重, 是节点$i$到下一层节点$k$的误差项

    更新权重:


    数学公式的推导:

    我们取网络所有输出层节点的误差平方和作为目标函数:


    ,
    用随机梯度下降算法对目标函数进行优化:



    我们可以从上图得到,$W84$仅影响$a4$节点到$8$节点的输入值,设:$netj$为节点$j$的加权输入:


    $Ed$是$netj$的函数,而$netj$是$Wji$的函数,根据链式求导法则:



    $xji$是节点i到节点j的输出值,即$j$的输入值

    输出层:

    netj仅影响节点j的输出值,,所以Ed是yi的函数,yi是netj的函数:



    计算第一项
    将Ed带入公式,得到:


    计算第二项:


    综合,得:


    将其推导代入 随机推导公式,得:


    隐层:

    我们定义节点j的所有直接下游节点的集合为:$Downstream(j)$,所以netj只能通过影响$Downstream(j)$来影响$Ed$,设Netk是节点J的下游输入,而$Ed$是netk的函数,而netk是netj的函数,所以我们用全导数公式:



    因为: ,代入,得:

    自此,我们完成了公式的推导

    代码:

    import numpy as np
    
    #定义tan函数以及tan函数的导数
    def tanh(x):
        return np.tanh(x)
    
    def tanh_deriv(x):
        return  1.0-np.tanh(x)*np.tanh(x)
    
    #定义logistich函数以及其导数
    def logistic(x):
        return 1/(1+np.exp(-x))
    
    def logistic_derivatrive(x):
        return logistic(x)*(1-logistic(x))
    
    class NeturalNetwork(object):
        def __init__(self,layers,activations='tanh'):
            '''
            :param layers: 一个list  包括每一层的神经元数
            :param activations:激活函数
            '''
            if activations=='tanh':
                self.activation = tanh
                self.activation_deriv = tanh_deriv
            if activations=='logistic':
                self.activation = logistic
                self.activation_deriv = logistic_derivatrive
            self.weights = []
            for i in range(1,len(layers)-1):
                #i跟前一层的权重
                self.weights.append((2*np.random.random((layers[i-1]+1,layers[i]+1))-1)*0.25)
                #i层跟i+1层进行赋值 权重
                self.weights.append((2*np.random.random((layers[i]+1,layers[i+1]))-1)*0.25)
    
        def fit(self,X,y,learning_rate = 0.2,epochs=10000):
            '''
            :param X:
            :param y:
            :param learning_rate: 学习率
            :param epochs: 学习步骤
            :return:
            '''
            #二维矩阵
            X = np.atleast_2d(X)
            #ones 矩阵全是1   shape函数返回的是行列数(返回一个List)跟X一样维度的矩阵
            temp = np.ones([X.shape[0],X.shape[1]+1])
            #temp等于第一列到最后一列跟x一样的矩阵
            temp  [:,0:-1]=X
            X= temp
            Y=np.array(y)
    
            #第几次循环
            for k in range(epochs):
                i = np.random.randint(X.shape[0])
                #随机取一个数,代表第i行,对i行数据进行更新
                a = [X[i]]
                #形成这一行数据为输入的神经网络,dot代表内积
                for l in range(len(self.weights)):
                    a.append(self.activation(np.dot(a[l],self.weights[l])))
                #误差
                error = y[i]-a[-1]
                deltas = [error * self.activation_deriv(a[-1])]
                #开始往回算每一层的误差
                #deltas是所有权重的误差列表
                for l in range(len(a)-2,0,-1):
                    deltas.append(deltas[-1].dot(self.weights[l].T)*self.activation_deriv(a[l]))
                deltas.reverse()
                for i in range(len(self.weights)):
                    layers = np.atleast_2d(a[i])
                    delta = np.atleast_2d(deltas[i])
                    self.weights[i] += learning_rate*layers.T.dot(delta)
        def predict(self,x):
            x = np.array(x)
            temp = np.ones(x.shape[0]+1)
            temp[0:-1] = x
            a =temp
            for l in range(0,len(self.weights)):
                a = self.activation(np.dot(a,self.weights[l]))
            return a
    
    用手写训练集来测试我们的神经网络:
    import numpy as np
    from sklearn.datasets import load_digits
    from sklearn.metrics import confusion_matrix,classification_report
    from sklearn.preprocessing import LabelBinarizer
    from neuralNetwork.nn import NeturalNetwork
    from sklearn.cross_validation import train_test_split
    
    digits =load_digits()
    #1797张 8*8的手写数字图片
    X = digits.data
    Y = digits.target
    #标准化
    X -=X.min()
    X /=X.max()
    
    nn = NeturalNetwork([64,100,10],'logistic')
    
    #分离测试集跟训练集
    X_train,X_test,y_train,y_test = train_test_split(X,Y)
    labels_train = LabelBinarizer().fit_transform(y_train)
    labels_test  = LabelBinarizer().fit_transform(y_test)
    
    print("start fitting")
    
    nn.fit(X_train,labels_train,epochs=3000)
    
    print("end training")
    predictions=[]
    
    for i in range(X_test.shape[0]):
        o = nn.predict(X_test[i])
        predictions.append(np.argmax(o))
    
    #10*10矩阵(分类是10) ,对角线表示预测的对,行是预测值,列是真实值
    print(confusion_matrix(y_test,predictions))
    '''
    v[[43  0  0  0  0  0  0  0  0  0]
     [ 0 37  0  0  0  0  1  0  0  8]
     [ 0  1 38  3  0  0  0  0  0  0]
     [ 0  0  1 47  0  1  0  1  0  0]
     [ 0  0  0  0 47  0  0  0  0  1]
     [ 0  0  0  0  0 48  1  0  0  0]
     [ 0  2  0  0  0  0 38  0  0  0]
     [ 0  0  0  0  1  0  0 37  0  1]
     [ 1  7  0  1  0  4  1  0 26  6]
     [ 0  0  0  4  0  0  0  0  0 43]]
    '''
    print(classification_report(y_test,predictions))
    '''统计
                 precision    recall  f1-score   support
    
              0       0.98      1.00      0.99        43
              1       0.79      0.80      0.80        46
              2       0.97      0.90      0.94        42
              3       0.85      0.94      0.90        50
              4       0.98      0.98      0.98        48
              5       0.91      0.98      0.94        49
              6       0.93      0.95      0.94        40
              7       0.97      0.95      0.96        39
              8       1.00      0.57      0.72        46
              9       0.73      0.91      0.81        47
    
    avg / total       0.91      0.90      0.90       450
    
    '''
    

    用向量式编程的思想改进我们的代码:

    # -*- coding:utf-8 -*-
    #!/usr/bin/local/bin/python
    
    import numpy as np
    
    class FullConnectedLayer(object):
        def __init__(self,input_size,
                     output_size,
                     learing_rate,
                     activator):
            self.input_size = input_size
            self.output_size = output_size
            self.activator = activator
            self.learning_rate = learing_rate
            self.W = np.random.uniform(-0.1,0.1,(output_size,input_size))
            self.b = np.zeros((output_size,1))
            self.output = np.zeros((output_size,1))
        def forward(self,input_array):
            self.input = input_array
            self.output = self.activator.forward(
                np.dot(self.W,input_array)+self.b
            )
    
        def backward(self,delta_array):
            self.delta = self.activator.backward(self.input) * np.dot(
                self.W.T,delta_array
            )
            self.W_grad = np.dot(delta_array,self.input.T)
            self.b_grad = delta_array
    
        def update(self):
            self.W += self.learning_rate * self.W_grad
            self.b += self.learning_rate * self.b_grad
    

    相关文章

      网友评论

          本文标题:笨方法学机器学习(二)全连接神经网络

          本文链接:https://www.haomeiwen.com/subject/zkmcsxtx.html