神經(jīng)網(wǎng)絡(luò)的Python實現(xiàn)(二)全連接網(wǎng)絡(luò)

在上一篇 神經(jīng)網(wǎng)絡(luò)的Python實現(xiàn)(一)了解神經(jīng)網(wǎng)絡(luò) 中,我們簡單介紹了感知機模型和多層網(wǎng)絡(luò)的基礎(chǔ)結(jié)構(gòu)。在這篇博文中,我們將使用python-numpy庫搭建多層神經(jīng)網(wǎng)絡(luò)模型、介紹和實現(xiàn)BP算法。理論部分有部分參考。

全連接網(wǎng)絡(luò)

首先,簡單介紹一下全連接網(wǎng)絡(luò)(Fully-Connected Network),即在多層神經(jīng)網(wǎng)絡(luò)中,第 n 層的每個神經(jīng)元都分別與第 n-1 層的神經(jīng)元相互連接。如下圖便是一個簡單的全連接網(wǎng)絡(luò):

image

我們使用圓圈來表示神經(jīng)網(wǎng)絡(luò)的輸入,標(biāo)上 +1 的圓圈被稱為偏置節(jié)點,也就是截距項。神經(jīng)網(wǎng)絡(luò)最左邊的一層叫做輸入層,最右的一層叫做輸出層(上圖中,輸出層只有一個節(jié)點)。中間所有節(jié)點組成的一層叫做隱藏層,因為我們不能在訓(xùn)練樣本集中觀測到它們的值。同時可以看到,以上神經(jīng)網(wǎng)絡(luò)的例子中有3個輸入單元(偏置單元不計在內(nèi)),3個隱藏單元及一個輸出單元。

我們用 n_l 來表示網(wǎng)絡(luò)的層數(shù),上圖例子中 n_l=3 ,我們將第 l 層記為 L_l ,于是 L_1 是輸入層,輸出層是 L_{n_l} 。本例神經(jīng)網(wǎng)絡(luò)有參數(shù) (W,b) = (W^{(1)}, b^{(1)}, W^{(2)}, b^{(2)}) ,其中 W^{(l)}_{ij} 是第 l 層第 j 單元與第 l+1 層第 i 單元之間的聯(lián)接參數(shù)(其實就是連接線上的權(quán)重,注意標(biāo)號順序), b^{(l)}_i 是第 l+1 層第 i 單元的偏置項。因此在本例中, W^{(1)} \in \Re^{3\times 3} , W^{(2)} \in \Re^{1\times 3} 。注意,沒有其他單元連向偏置單元(即偏置單元沒有輸入),因為它們總是輸出 +1。同時,我們用 s_l 表示第 l 層的節(jié)點數(shù)(偏置單元不計在內(nèi))。

接下來詳細(xì)介紹神經(jīng)網(wǎng)絡(luò)的前向和反向的計算過程。

前向傳播

我們用 a^{(l)}_i 表示第 l 層第 i 單元的激活值(輸出值)。當(dāng) l=1 時, a^{(1)}_i = x_i ,也就是第 i 個輸入值(輸入值的第 i 個特征)。對于給定參數(shù)集合 W,b ,我們的神經(jīng)網(wǎng)絡(luò)就可以按照函數(shù) h_{W,b}(x) 來計算輸出結(jié)果。本例神經(jīng)網(wǎng)絡(luò)的計算步驟如下:

\begin{align} a_1^{(2)} &= f(W_{11}^{(1)}x_1 + W_{12}^{(1)} x_2 + W_{13}^{(1)} x_3 + b_1^{(1)}) \nonumber \\ \nonumber a_2^{(2)} &= f(W_{21}^{(1)}x_1 + W_{22}^{(1)} x_2 + W_{23}^{(1)} x_3 + b_2^{(1)}) \\ \nonumber a_3^{(2)} &= f(W_{31}^{(1)}x_1 + W_{32}^{(1)} x_2 + W_{33}^{(1)} x_3 + b_3^{(1)}) \\ \nonumber h_{W,b}(x) &= a_1^{(3)} = f(W_{11}^{(2)}a_1^{(2)} + W_{12}^{(2)} a_2^{(2)} + W_{13}^{(2)} a_3^{(2)} + b_1^{(2)}) \nonumber \end{align}

我們用 z^{(l)}_i 表示第 l 層第 i 單元輸入加權(quán)和(包括偏置單元),比如, z_i^{(2)} = \sum_{j=1}^n W^{(1)}_{ij} x_j + b^{(1)}_ia^{(l)}_i = f(z^{(l)}_i)

這樣我們就可以得到一種更簡潔的表示法。這里我們將激活函數(shù) f(\cdot) 擴(kuò)展為用向量(分量的形式)來表示,即 f([z_1, z_2, z_3]) = [f(z_1), f(z_2), f(z_3)] ,那么,上面的等式可以更簡潔地表示為:

\begin{align} z^{(2)} &= W^{(1)} x + b^{(1)}\nonumber \\ \nonumber a^{(2)} &= f(z^{(2)}) \\ \nonumber z^{(3)} &= W^{(2)} a^{(2)} + b^{(2)} \\ \nonumber h_{W,b}(x) &= a^{(3)} = f(z^{(3)}) \nonumber \end{align}
我們將上面的計算步驟叫作前向傳播?;叵胍幌?,之前我們用 a^{(1)} = x 表示輸入層的激活值,那么給定第 l 層的激活值 a^{(l)} 后,第 l+1 層的激活值 a^{(l+1)} 就可以按照下面步驟計算得到:

\begin{align} z^{(l+1)} &= W^{(l)} a^{(l)} + b^{(l)} \nonumber \\ \nonumber a^{(l+1)} &= f(z^{(l+1)}) \nonumber \end{align}
將參數(shù)矩陣化,使用矩陣-向量運算方式,我們就可以利用線性代數(shù)的優(yōu)勢對神經(jīng)網(wǎng)絡(luò)進(jìn)行快速求解。

# 在python 3 numpy 中,矩陣相乘可以使用 a @ b
z = activation(a @ w + b)

激活函數(shù)

在上面例子中 f(\cdot) 便是激活函數(shù),是神經(jīng)網(wǎng)絡(luò)中十分重要的一環(huán)。若沒有激活函數(shù),那么神經(jīng)網(wǎng)絡(luò)的輸出便始終只是各個輸入的線性組合。“深度”起不到作用。
所以激活函數(shù)的作用便是加入某種非線性的映射。早期經(jīng)常使用的是Sigmoid函數(shù),近幾年多使用ReLU函數(shù)及其變體。下面介紹一下常見的激活函數(shù)及其導(dǎo)數(shù)。

1. sigmoid

image

數(shù)學(xué)形式:

\begin{align} f(z) &= \frac{1}{1+e^{-z}} \nonumber\\ \nonumber\\ \nonumber f'(z) &= (\frac{1}{1+e^{-z}})' = \frac{e^{-z}}{(1+e^{-z})^{2}} \\ \nonumber\\ \nonumber &= \frac{1+e^{-z}-1}{(1+e^{-z})^{2}} = \frac{1}{(1+e^{-z})}(1-\frac{1}{(1+e^{-z})}) \\\nonumber \\ \nonumber &= f(z)\ (1-f(z)) \nonumber \end{align}

Sigmoid函數(shù)會將輸入映射到(0,1)的范圍,較大的值會被映射為1,較小的值會被映射為0。直觀上符合神經(jīng)元活躍與抑制狀態(tài)的區(qū)分。

缺點:

  1. 如圖,輸入值的絕對值在4以上的情況下就基本趨于飽和了,達(dá)到1或0。在反向傳播時,會造成由于梯度過小而產(chǎn)生權(quán)重更新緩慢甚至梯度消失。并且初始化權(quán)重時不可太大。
  2. Sigmoid函數(shù)的輸出分布不是以0為中心分布的,在梯度下降過程中可能會存在梯度恒正或是恒負(fù)的情況出現(xiàn)。
import numpy as np

def sigmoid(z):
  return 1.0 / (1.0 + np.exp(-x))
  
def sigmoid_prime(z):
  return sigmoid(z) * (1 - sigmoid(z))

2. tanh

![image](http://upload-images.jianshu.io/upload_images/11410045-7c709009a60414b8.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

數(shù)學(xué)形式:
\begin{align} f(z)&=\tanh(z)=\frac{\sinh(z)}{\cosh(z)}=\frac{e^z-e^{-z}}{e^z+e^{-z}}\nonumber\\ \nonumber\\ \nonumber f'(z) &= \frac{\cosh^2(z)-\sinh^2(z)}{\cosh^2(z)}=1 - f^2(z) \nonumber \end{align}

tanh函數(shù)會將輸入映射到[-1,1]的范圍,較大的值會被映射為1,較小的值會被映射為-1。

缺點:類似于Sigmoid函數(shù),也具有一定的激活飽和性。

import numpy as np

def tanh(z):
  return np.tanh(z)
  
def tanh_prime(z):
  return 1 - np.square(tanh(z))

3. relu

image

數(shù)學(xué)形式

\begin{align} f(z) &=\max (0,z)\nonumber \\ \nonumber\\ \nonumber f'(z) &= \begin{cases} 0& \text{z <= 0}\\ \nonumber 1& \text{z > 0} \nonumber \end{cases} \end{align}

優(yōu)點:

  1. 計算速度快。求導(dǎo)簡單。
  2. 不再梯度彌散。ReLU函數(shù)不像Sigmoid函數(shù),不存在梯度飽和區(qū),幾乎不會造成梯度彌散。
  3. 減少過擬合。部分神經(jīng)元輸出可能為0,加大網(wǎng)絡(luò)稀疏性,減少過擬合。

缺點:初始化不佳會造成神經(jīng)元死亡。針對此問題提出了Leaky ReLU、PReLU和RReLU等變體。

import numpy as np

def relu(z):
  return (np.abs(z) + z) / 2
  
def relu_prime(z):
  return np.where(z > 0, 1, 0)
  

損失函數(shù)

當(dāng)我們的輸入數(shù)據(jù)經(jīng)過神經(jīng)網(wǎng)絡(luò),得到了一組輸出數(shù)據(jù)。我們想去衡量我們的模型的好壞、給我們的模型一個得分或者說是我們想要優(yōu)化的最終目標(biāo),便需要定義好損失函數(shù)。將我們的輸出值與真實值通過損失函數(shù)進(jìn)行計算,得到損失值(loss),為了使得模型更好,能夠與真實情況相擬合,所以我們需要找到一個適合的網(wǎng)絡(luò)權(quán)重使得輸出的loss最小。對于回歸問題最常使用的損失函數(shù)是均方誤差(Mean-Square Error,MSE),對于分類問題最常使用的是交叉熵(Cross Entropy),這里僅簡單介紹MSE。

MSE=\frac{1}{N}\sum_{i=1}^{N}(y_i-\hat{y_i })^2

均方誤差是指參數(shù)估計值與參數(shù)真值之差平方的期望值;
MSE可以評價數(shù)據(jù)的變化程度,MSE的值越小,說明預(yù)測模型描述實驗數(shù)據(jù)具有更好的精確度。

反向傳播

現(xiàn)在我們已經(jīng)了解了全連接神經(jīng)網(wǎng)絡(luò)的前向傳播和激活函數(shù)和其導(dǎo)數(shù)的數(shù)學(xué)表達(dá),下面我們要使用反向傳播算法進(jìn)行最優(yōu)參數(shù)(采用梯度下降法,可能造成局部最優(yōu))的求解。

由于神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)十分復(fù)雜,想要直接去求得權(quán)重的最優(yōu)解是不大可能的。所以采用迭代的思想進(jìn)行一步一步的權(quán)重更新,直到找到最佳的解。最常用的便是梯度下降法(gradient descent)。如果不了解梯度下降法,推薦觀看Andrew Ng的機器學(xué)習(xí)視頻。

接下來假設(shè)你已經(jīng)了解梯度下降法,現(xiàn)在我們來一起推導(dǎo)一下反向傳播算法的公式,了解整個過程。這里采用下圖簡單的例子作為示范。很容易地可以擴(kuò)展到任意寬度,任意深度的全連接網(wǎng)絡(luò)上去。

image

假設(shè)我們的神經(jīng)網(wǎng)絡(luò)是一個輸入層,有兩個神經(jīng)元;一個隱藏層,有兩個神經(jīng)元;一個輸出層,有一個神經(jīng)元。從圖可以看到一共有6個權(quán)重需要我們計算。

說明:接下來的激活函數(shù)都是sigmoid函數(shù),均以 f(·) 表示。小寫字母表示未經(jīng)激活函數(shù)的輸出,大寫字母表示通過激活函數(shù)的輸出值。

隱藏層到輸出層

數(shù)據(jù)通過神經(jīng)網(wǎng)絡(luò)得到了一個輸出 \hat{y} ,我們定義的損失函數(shù)為MSE,所以可以計算出當(dāng)前的loss作為總誤差(這里舉例為一個輸出神經(jīng)元,如果有多個輸出,總誤差加和即可)。最后輸出層這里我們不添加激活函數(shù),所以 \hat{y} = O_1

E = \frac{1}{2}\left(y - \hat{y}\right)^2 = \frac{1}{2}\left(y - O_1\right)^2

有了總誤差,接下來我們就可以通過梯度下降法進(jìn)行權(quán)重的更新。先來看隱藏層到輸出層的權(quán)重 w_5,w_6 。

找到在前向傳播時,有關(guān) w_5,w_6 的式子:

\begin{align} O_1 &= f(o_1)\nonumber\\ o_1 &= w_5H_1+w_6H_2+b_h \nonumber \end{align}

根據(jù)鏈?zhǔn)椒▌t求出 w_5,w_6 對于總誤差的偏導(dǎo):

\begin{align} \frac{\partial E}{\partial w_5} &= \frac{\partial E}{\partial O_1}*\frac{\partial O_1}{\partial o_1}*\frac{\partial o_1}{\partial w_5} \nonumber\\ &= -(y-o_1)*f'(o_1)*H_1 \nonumber\\ &=-(y-o_1)*f(o_1)*\left(1-f(o_1)\right)*H_1 \nonumber \end{align}

同理可得:

\begin{align} \frac{\partial E}{\partial w_6} &= \frac{\partial E}{\partial O_1}*\frac{\partial O_1}{\partial o_1}*\frac{\partial o_1}{\partial w_6} \nonumber\\ &= -(y-o_1)*f'(o_1)*H_2\nonumber \\ &=-(y-o_1)*f(o_1)*\left(1-f(o_1)\right)*H_2 \nonumber \end{align}

對于偏置項 b_h

\begin{align} \frac{\partial E}{\partial b_h} &= \frac{\partial E}{\partial O_1}*\frac{\partial O_1}{\partial o_1}*\frac{\partial o_1}{\partial b_h}\nonumber \\ &= -(y-o_1)*f'(o_1)\nonumber \\ &=-(y-o_1)*f(o_1)*\left(1-f(o_1)\right) \nonumber \end{align}

為了方便表示,我們把來自 o_1 的誤差表示為 \delta o_1,即:
\delta o_1 =\frac{\partial E}{\partial O_1}*\frac{\partial O_1}{\partial o_1} = -(y-o_1)*f(o_1)*\left(1-f(o_1)\right)
整理后得到:

\begin{align} \delta_{w_5}&=\frac{\partial E}{\partial w_5} = \delta o_1 * H_1 \nonumber\\ \delta_{w_6}&=\frac{\partial E}{\partial w_6} = \delta o_1 * H_2 \nonumber\\ \delta_{b_h}&=\frac{\partial E}{\partial b_h} = \delta o_1 \nonumber \end{align}

我們計算出來 w_5.w_6,b_h 的偏導(dǎo)之后,就可以進(jìn)行權(quán)重的更新了。(這里并不立刻更新,因前層進(jìn)行反向傳播時需要此層更新前的權(quán)重,下面會講)
w_5 \leftarrow w_5 + \eta\nabla\delta_{w_5} \\w_6 \leftarrow w_6 + \eta\nabla\delta_{w_6}\\b_h \leftarrow b_h + \eta\nabla\delta_{b_h}

這里的 \eta為學(xué)習(xí)率。

輸入層到隱藏層

與隱藏層到輸出層類似,只不過有小小的差別。
對于 w_1,w_2,w_3,w_4,b_i ,只拿 w_1 作為示范,其他的類似求解。

首先列出前向傳播時與 w_1 有關(guān)的公式:

\begin{align} O_1 &= f(o_1)\nonumber\\ o_1 &= w_5H_1+w_6H_2+b_h\nonumber\\ H_1 &= f(h_1)\nonumber\\ h_1 &= w_1x_1+w_3x_2+b_i\nonumber \end{align}

從上式可以看出,我們需要先求出H_1處的誤差,進(jìn)而求得 w_1 的梯度。

\begin{align} \frac{\partial E}{\partial H_1} &= \frac{\partial E}{\partial O_1} * \frac{\partial O_1}{\partial o_1} * \frac{\partial o_1}{\partial H_1}\nonumber\\ &=\delta o_1 * w_5\nonumber \end{align}

這里便是上面提到的需要使用更新前的隱藏層到輸出層的權(quán)重值

接下來便和隱藏層到輸出層的反向傳播沒有差別了,以 w_1 為例:

\begin{align} \frac{\partial E}{\partial w_1} &= \frac{\partial E}{\partial H_1}*\frac{\partial H_1}{\partial h_1}*\frac{\partial h_1}{\partial w_1} \nonumber\\ &= (\delta o_1 * w_5) * \left(f(h_1)*\left(1-f(h_1)\right) \right)* x_1\nonumber\\ \end{align}

其他的權(quán)重和偏置項也根據(jù)公式進(jìn)行類似的計算,并進(jìn)行更新。

至此反向傳播便完成了,全部的權(quán)重得到了更新。下面我們根據(jù)上面的過程來編寫代碼。

CODE

Layer類

因為預(yù)計還要將CNN、RNN、LSTM等都采用numpy實現(xiàn)一遍,所以我們先定義一個Layer的基類。里面寫一些,所有層都需要的函數(shù),比如激活函數(shù)等。這里API形式仿照Keras。

from abc import abstractmethod
import numpy as np

class Layer(object):
    def _activation(self, name, x):
        """
        激活函數(shù)
        :param name: 激活函數(shù)的名稱。
        :param x:    激活函數(shù)的自變量。
        :return:     返回激活函數(shù)計算得到的值
        """
        if name == 'sigmoid':
            return 1.0 / (1.0 + np.exp(-x))
        elif name == 'tanh':
            return np.tanh(x)
        elif name == 'relu':
            return (np.abs(x) + x) / 2
        elif name == 'none':   # 不使用激活函數(shù)
            return x
        else:
            raise AttributeError("activation name wrong")

    def _activation_prime(self, name, x):
        if name == 'sigmoid':
            return self._activation(name, x) * (1 - self._activation(name, x))
        elif name == 'tanh':
            return 1 - np.square(self._activation(name, x))
        elif name == 'relu':
            return np.where(x > 0, 1, 0)
        elif name == 'none':
            return 1
        else:
            raise AttributeError("activation name wrong")
            
    @abstractmethod
    def forward_propagation(self, **kwargs):
        pass

    @abstractmethod
    def back_propagation(self, **kwargs):
        pass

Dense層

接下來我們開始編寫Dense層。

from Layer import Layer
import numpy as np

class DenseLayer(Layer):
    def __init__(self, shape, activation, name):
        """
        Dense層初始化。
        :param shape:       如輸入神經(jīng)元有2個,輸出神經(jīng)元有3個。那么shape = (2,3)
        :param activation:  激活函數(shù)名稱
        :param name:        當(dāng)前層的名稱
        """
        super().__init__()
        self.shape = shape
        self.activation_name = activation
        self.__name = name
        self.__w = 2 * np.random.randn(self.shape[0], self.shape[1]) # 這里采用矩陣的隨機初始化
        self.__b = np.random.randn(1, shape[1])                      

    def forward_propagation(self, _input):
        """
        Dense層的前向傳播實現(xiàn)
        :param _input: 輸入的數(shù)據(jù),即前一層的輸出
        :return:       通過激活函數(shù)后的輸出
        """
        self.__input = _input
        self.__output = self._activation(self.activation_name, self.__input.dot(self.__w) + self.__b)
        return self.__output

    def back_propagation(self, error, learning_rate):
        """
        Dense層的反向傳播
        :param error:           后一層傳播過來的誤差
        :param learning_rate:   學(xué)習(xí)率
        :return:                傳播給前一層的誤差
        """
        o_delta = np.matrix(error * self._activation_prime(self.activation_name, self.__output))
        w_delta = np.matrix(self.__input).T.dot(o_delta)
        input_delta = o_delta.dot(self.__w.T)
        self.__w -= w_delta * learning_rate
        self.__b -= o_delta * learning_rate
        return input_delta

Model類

接著寫一個Model類實現(xiàn)Keras的各種API

import numpy as np


class Model(object):
    def __init__(self):
        """
        簡單使用列表按順序存放各層
        """
        self.layers = []

    def add(self, layer):
        """
        向模型中添加一層
        :param layer:  添加的Layer
        """
        self.layers.append(layer)

    def fit(self, X, y, learning_rate, epochs):
        """
        訓(xùn)練
        :param X:                   訓(xùn)練集數(shù)據(jù)
        :param y:                   訓(xùn)練集標(biāo)簽
        :param learning_rate:       學(xué)習(xí)率
        :param epochs:              全部數(shù)據(jù)集學(xué)習(xí)的輪次
        """
        if self.__loss_function is None:
            raise Exception("compile first")
        # 前饋
        for i in range(epochs):
            loss = 0
            for num in range(len(X)):
                out = X[num]
                for layer in self.layers:
                    out = layer.forward_propagation(out)
                loss += self.__loss_function(out, y[num], True)
                error = self.__loss_function(out, y[num], False)

                for j in range(len(self.layers)):
                    index = len(self.layers) - j - 1
                    error = self.layers[index].back_propagation(error, learning_rate)
            print("epochs {} / {}  loss : {}".format(i + 1, epochs, loss/len(X)))

    def compile(self, loss_function):
        """
        編譯,目前僅設(shè)置損失函數(shù)
        :param loss_function:  損失函數(shù)的名稱
        """
        if loss_function == 'mse':
            self.__loss_function = self.__mse

    def __mse(self, output, y, forward):
        """
        :param output:      預(yù)測值
        :param y:           真實值
        :param forward:     是否是前向傳播過程
        :return:            loss值
        """
        if forward:
            return np.squeeze(0.5 * ((output - y) ** 2))
        else:
            return output - y

    def predict(self, X):
        """
        結(jié)果預(yù)測
        :param X: 測試集數(shù)據(jù)
        :return:  對測試集數(shù)據(jù)的預(yù)測
        """
        res = []
        for num in range(len(X)):
            out = X[num]
            for layer in self.layers:
                out = layer.forward_propagation(out)
            res.append(out)
        return np.np.squeeze(np.array(res))

Main

最后我們來寫一個主函數(shù)簡單擬合異或測試一下全連接網(wǎng)絡(luò)。

from Dense import DenseLayer
import Model

if __name__ == '__main__':
    model = Model.Model()
    X = np.array([
      [1, 1],
      [1, 0],
      [0, 1],
      [0, 0]
    ])
    y = np.array([0, 1, 1, 0])
    model.add(Dense((2, 3), 'sigmoid', 'dense1'))
    model.add(Dense((3, 4), 'sigmoid', 'dense2'))
    model.add(Dense((4, 1), 'none', 'output'))
    model.compile('mse')
    model.fit(X, y, 0.1, 1000)
    print(model.predict([[1, 1], [1, 0]]))

epochs 1 / 1000 loss : 1.5461586301292716
epochs 2 / 1000 loss : 1.0010336204321242
epochs 3 / 1000 loss : 0.8421754635331838
epochs 4 / 1000 loss : 0.7311597301044074
epochs 5 / 1000 loss : 0.6428097142979868
epochs 6 / 1000 loss : 0.5709843947151808
epochs 7 / 1000 loss : 0.5122654038390013
epochs 8 / 1000 loss : 0.4640985740577866
epochs 9 / 1000 loss : 0.4244527616264729
epochs 10 / 1000 loss : 0.39169518752811794
···
epochs 995 / 1000 loss : 0.0018694401858458181
epochs 996 / 1000 loss : 0.0018245697992101736
epochs 997 / 1000 loss : 0.001780665685232114
epochs 998 / 1000 loss : 0.0017377108735277388
epochs 999 / 1000 loss : 0.0016956885636446625
epochs 1000 / 1000 loss : 0.001654582127688094
···
[0.0610148 0.98877437]

可見模型是能夠收斂,并且擬合非線性映射的。

如果你有某些疑問或是改進(jìn),歡迎留下你的評論。

TODO

現(xiàn)在我們實現(xiàn)了全連接神經(jīng)網(wǎng)絡(luò),在下一篇博文我們將會繼續(xù)推導(dǎo)和實現(xiàn)最為常用而且是最為復(fù)雜的卷積神經(jīng)網(wǎng)絡(luò)(CNN)。

參考內(nèi)容

感謝以下博客和網(wǎng)站
[1] 神經(jīng)網(wǎng)絡(luò) - Ufldl
[2] 大白話講解BP算法
[3] Keras

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文地址:http://www.cnblogs.com/subconscious/p/5058741.html 神...
    Albert陳凱閱讀 5,587評論 0 48
  • (小說)引子 自從盤古開天地,三皇五帝到于今,萬物初始時,混沌未分,四面茫茫,不知過了多久,塵埃開始落定,像極了幼...
    趙載傳閱讀 333評論 0 1
  • 田英瑞,平頂山,焦點18期,持續(xù)分享912天(20180329,周四)改變思維,改變語言,帶著覺察,邁開一小步,開...
    向陽花開_田英瑞閱讀 186評論 0 1
  • 項目中我們經(jīng)常會遇到需要下載文件的功能,靜態(tài)的文件資源可以使用 a 標(biāo)簽下載,動態(tài)的則需要我們通過 js 處理,s...
    許Y1世承諾閱讀 414評論 0 0
  • 接著昨天話題的一部分,有的人是以把游戲打得更好為目的,把其中的對象可以更換成打球、做飯甚至考試等等。 因為打過網(wǎng)球...
    山人Shan閱讀 249評論 0 0

友情鏈接更多精彩內(nèi)容