zoukankan      html  css  js  c++  java
  • 深度学习与计算机视觉:基于Python的神经网络的实现

    在前面两篇文章介绍了深度学习的一些基本概念,本文则使用Python实现一个简单的深度神经网络,并使用MNIST数据库进行测试。
    神经网络的实现,包括以下内容:

    • 神经网络权值的初始化
    • 正向传播
    • 误差评估
    • 反向传播
    • 更新权值

    主要是根据反向传播的4个基本方程,利用Python实现神经网络的反向传播。

    初始化

    首先定义代表神经网络的类NeuralNetwork,

    class NeuralNetwork:
        def __init__(self,layers,alpha=0.1):
            self.W = []
            self.layers = layers
            self.alpha = alpha
    

    有三个属性,

    • W存储各个层之间的权值矩阵,也是神经网络要更新学习的
    • layers 神经网络的结构,例如: [2,2,1]表示输入层有2个神经元,隐藏层2个神经元,输出层只有1个神经元。
    • alpha 学习速率

    接下来初始化各个层之间的权值矩阵

    for i in np.arange(0,len(layers) - 2):
        w = np.random.randn(layers[i] + 1,layers[i + 1] + 1)
        self.W.append(w / np.sqrt(layers[i]))
    

    注意上面生成权值矩阵的大小layers[i] + 1,layers[i + 1] + 1,都加了1。 这是将神经元的偏置和权值统一的放到了权值矩阵里面。

    [left[ egin{array}{c}w_{11} & w_{12} \ w_{21} & w_{22}end{array} ight] cdot left[egin{array}{c}x_1 \ x_2end{array} ight] + left[egin{array}{c}b_1 \ b_2end{array} ight] = left[egin{array}{c}w_{11}x_1 + w{12}x_2 + b_1 \ w_{21}x_1 + w_{22}x_2 + b_2 end{array} ight] ]

    可以将上式写成齐次的形式

    [left[ egin{array}{c}w_{11} & w_{12} & b_1 \ w_{21} & w_{22} &b_2 end{array} ight] cdot left[egin{array}{c}x_1 \ x_2 \ 1end{array} ight] ]

    使用统一的矩阵运算,在正向反向传播的时候更方便。

    在输出层的神经元并没有偏置,所以要单独初始化输出层的权值矩阵

            w = np.random.randn(layers[-2] + 1,layers[-1])
            self.W.append(w / np.sqrt(layers[-2]))
    

    下面实现Python的magic function __repr__输出神经网络结构

        def __repr__(self):
            return "NeuralNetWork:{}".format("-".join(str(l) for l in self.layers))
    

    激活函数

    在神经网络中使用sigmoid作为激活函数,实现sigmoid及其导数

        def sigmoid(self,x):
            return 1.0 / (1 + np.exp(-x))
    
        def sigmoid_deriv(self,x):
            return x * (1 - x)
    

    正向反向传播

    这一部分是神经的网络的核心了。下面实现fit方法,在方法中完成神经网络权值更新(训练)的过程。

        def fit(self,X,y,epochs=1000,displayUpdate=100):
            X = np.c_[X,np.ones((X.shape[0]))]
    
            for epoch in np.arange(0,epochs):
                for(x,target) in zip(X,y):
                    self.fit_partial(x,target)
    
                # check to see if we should display a training update
                if epoch == 0 or (epoch + 1) % displayUpdate == 0:
                    loss = self.calculate_loss(X,y)
                    print("[INFO] epoch={},loss={:.7f}".format(epoch + 1,loss))
    

    该函数有4个参数:

    • X是输入的样本数据
    • y是样本的真是值
    • epochs训练的轮数
    • displayUpdate 输出训练的loss值。

    X = np.c_[X,np.ones((X.shape[0]))]将输入训练的样本表示为齐次向量(也就是在末尾添1)。fit_partial是对输入的每个样本进行训练,包括正向传播,反向传播以及权值的更新。

        def fit_partial(self,x,y):
            A = [np.atleast_2d(x)]
            # 正向传播
            # 层层之间的数据传递
            for layer in np.arange(0,len(self.W)):
            
                # 输入经过加权以及偏置后的值
                net = A[layer].dot(self.W[layer])
    
                # 神经元的输出
                out = self.sigmoid(net)
    
                # 保存下来,反向传播的时候使用
                A.append(out)
    

    上面完成了神经玩过的正向传播过程,下面根据反向传播的4个基本方程进行反向传播。
    首先根据(BP1),

    [delta^L = frac{partial e}{partial a^L} odot sigma'(z^L) ag{BP1} ]

    计算输出层的误差(delta^L)

            error = A[-1] - y # 输出层的误差,均值方差作为损失函数
    
            D = [error * self.sigmoid_deriv(A[-1])]
    

    得到输出层的误差D后,根据(BP2)计算各个层的误差

    [delta^{L-1} = (W^L)^Tdelta^L odot sigma'(z^{L-1}) ag{BP2} ]

            for layer in np.arange(len(A) - 2,0 ,-1):
                delta = D[-1].dot(self.W[layer].T)
                delta = delta * self.sigmoid_deriv(A[layer])
                D.append(delta)
            D = D[::-1]
    

    D反转,和各个层的索引对应起来,下面根据(BP3,BP4)计算权值矩阵和偏置的导数

    [frac{partial e}{b_j^l} = delta_j^l ag{BP3} ]

    [frac{partial e}{w_{jk}^l} = delta_j^l a_k^{l-1} ag{BP4} ]

            for layer in np.arange(0,len(self.W)):
                self.W[layer] += -self.alpha * A[layer].T.dot(D[layer])
    

    首先求得权值和偏置的导数(权值和偏置统一到同一个矩阵中)A[layer].T.dot(D[layer],然后将梯度乘以学习速率alpha 每次权值减小的步长。

    上述就完成利用反向传播算法更新权值的过程。 关于反向传播四个基本方程的推导过程,可以参考文章深度学习与计算机视觉: 搞懂反向传播算法的四个基本方程

    误差评估

    上面代码已经实现了深度学习的训练过程,下面实现predict输出使用训练好的模型预测的结果,calculate_loss评估训练后模型的评估

        def predict(self,X,addBias=True):
            p = np.atleast_2d(X)
    
            if addBias:
                p = np.c_[p,np.ones((p.shape[0]))]
    
            for layer in np.arange(0,len(self.W)):
                    p = self.sigmoid(np.dot(p,self.W[layer]))
    
            return p 
        def calculate_loss(self,X,targets):
    
            targets = np.atleast_2d(targets)
            predictions = self.predict(X,addBias=False)
            loss = 0.5 * np.sum((predictions - targets) ** 2)
    
            return loss 
    

    MNIST分类识别

    使用上面实现的深度神经网络对MNIST手写体进行识别,首先导入必要的包

    import NeuralNetwork
    from sklearn.preprocessing import LabelBinarizer
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report
    from sklearn import datasets
    

    需要使用sklearn包中的一些工具,进行数据的处理。

    # load MNIST数据集,并使用min/max对数据进行归一化
    digits = datasets.load_digits()
    data = digits.data.astype("float")
    data = (data - data.min()) / (data.max() - data.min())
    
    print("[INFO] samples: {}, dim: {}".format(data.shape[0], data.shape[1]))
    

    将数据拆分为训练集和测试集,并对MNIST的类别进行编码

    (trainX, testX, trainY, testY) = train_test_split(data, digits.target, test_size=0.25)
    
    # convert the labels from integers to vectors
    trainY = LabelBinarizer().fit_transform(trainY)
    testY = LabelBinarizer().fit_transform(testY)
    

    下面构建神经网络结构,并使用训练集进行训练

    nn = NeuralNetwork([data.shape[1], 32,16, 10])
    
    print ("[INFO] {}".format(nn))
    
    nn.fit(trainX, trainY, epochs=1000)
    

    神经网络结构为:64-32-16-10,其中64为输入数据的大小,10输出类别的个数。

    最后评估训练得到的模型

    predictions = nn.predict(testX)
    
    print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1)))
    

    最终的输出结果:

    [INFO] loading MNIST (sample) dataset...
    [INFO] samples: 1797, dim: 64
    [INFO] training network...
    [INFO] NeuralNetWork:64-32-16-10
    [INFO] epoch=1,loss=607.1711647
    [INFO] epoch=100,loss=7.1082795
    [INFO] epoch=200,loss=4.0731690
    [INFO] epoch=300,loss=3.1401868
    [INFO] epoch=400,loss=2.8801101
    [INFO] epoch=500,loss=1.8738122
    [INFO] epoch=600,loss=1.7461474
    [INFO] epoch=700,loss=1.6624043
    [INFO] epoch=800,loss=1.1852884
    [INFO] epoch=900,loss=0.6710255
    [INFO] epoch=1000,loss=0.6336826
    [INFO] evaluating network...
                  precision    recall  f1-score   support
    
               0       1.00      0.95      0.97        39
               1       0.84      1.00      0.92        38
               2       1.00      0.98      0.99        41
               3       0.93      0.98      0.95        52
               4       0.91      0.97      0.94        40
               5       0.98      0.98      0.98        41
               6       1.00      0.96      0.98        51
               7       1.00      0.98      0.99        48
               8       0.98      0.89      0.93        55
               9       0.98      0.93      0.95        45
    
       micro avg       0.96      0.96      0.96       450
       macro avg       0.96      0.96      0.96       450
    weighted avg       0.96      0.96      0.96       450
    
    

    如上测试结果,在测试集的上表现还算不错。

    总结

    本文使用Python简单的实现了一个神经网络。 主要是利用反向传播的4个基本方程,实现反向传播算法,更新各个神经元的权值。 最后使用该网络,对MNIST数据进行识别分类。

    上面实现的神经网络只是“玩具”,用以加深对深度学习的训练过程以及反向传播算法的理解。后面将使用Keras和PyTorch来构建神经网络。

    本文代码在git库 https://github.com/brookicv/machineLearningSample

  • 相关阅读:
    xml的建模
    P1341 无序字母对
    P1330 封锁阳光大学
    P2661 信息传递
    P1312 Mayan游戏
    P1514 引水入城
    C. Sad powers
    P1195 口袋的天空
    P1821 [USACO07FEB]银牛派对Silver Cow Party
    P1396 营救
  • 原文地址:https://www.cnblogs.com/wangguchangqing/p/10232283.html
Copyright © 2011-2022 走看看