Neural Network

1.PLA

重新回顧一下一開始學(xué)的PLA,preceptron learning Algorithm。PLA適用于二維及高維的線性可分的情況,如果是非線性可分的數(shù)據(jù),如果使用PLA可能會無限循環(huán)。問題的答案只有同意或不同意:



PLA要做的其實就找到一個x映射到y(tǒng)的f,使得這個f和數(shù)據(jù)的分布一致,判斷結(jié)果一致。
如果是遇到了線性不可分的情況,就不能再要求完全正確了,所以在optimization的時候會要求只需要找到最少錯誤的分類就可以了,不再要求完全正確的分類。

2.Combine some PLA

把PLA按照aggregation model的想法結(jié)合起來:


左邊是x = {X1, X2, X3, X4......},和右邊的w = {W1, W2, W3......}做內(nèi)積之后就得到了n個值,就相當(dāng)于PLA里面WX的部分了。把得到WX的稱為是g(x),最后再乘上α,其實就是把g(x)做了一個非線性的組合,也就是aggregation的blending模型。
上圖實現(xiàn)的是g1,g2的and。如何用感知機(jī)模型實現(xiàn)and邏輯?一種方法是:


g1,g2 = {-1, +1};g1 = -1, g2 = -1,那么結(jié)果是就是-3,g1 = -1, g2 = +1, 結(jié)果就是-1,如果g1 = +1, g2 = +1,那么結(jié)果就是還是正的,符合預(yù)期。

這個例子簡單說明了在經(jīng)過一定的線性組合的情況下是可以做到非線性的分類的。除此之外,OR,XOR等等都是可以通過g(x)的線性組合得到的。
所以說,Neural Network是一種很powerful同上也是complicated的模型,另外,當(dāng)hidden層神經(jīng)元數(shù)量大的時候計算量會非常大。比如下面的一個例子,有一個圓形區(qū)域,里面的+!外面是-1,這種是沒有辦法使用一個PLA切分開的,只能使用多個PLA了,如果是8個PLA的時候,大概是可以組成一個圓形,16個PLA的時候就要更接近了,因此,使用的PLA越多,得到的結(jié)果就會和數(shù)據(jù)的分布越擬合:

之前說過,凸多邊形的VC dimension是無限大的,2的n次方,所以隨著PLA數(shù)量的增長vc 維是沒有限制的,容易造成過擬合。但是,數(shù)目較多總是可以得到一個更加平滑更加擬合的曲線,這也是aggregation model的特點。
但是,還是有單層preceptron線性組合做不到的事情,比如XOR操作:

因為XOR得到的是非線性可分的區(qū)域,如下圖所示,沒有辦法由g1和g2線性組合實現(xiàn)。所以說linear aggregation of perceptrons模型的復(fù)雜度還是有限制的。事實上,一層的perceptron其實就是一次transform,那么既然一次transform不行,我們嘗試一下多層的perceptron,多次的transform。把XOR拆分開來,第一次是使用AND,第二次就是使用OR,如下:

這樣就從簡單的單層aggregation提升到了multi-layer的多層感知機(jī),模型的復(fù)雜度增加之后就更能解決一些非線性的問題了。 第一層的wx處理后再接到下一層wx處理。這其實就是有點接近神經(jīng)網(wǎng)絡(luò)的模型了。

3.Neural Network

之前已經(jīng)介紹過三種線性模型:linear classification,linear regression,logistic regression。那么,對于OUTPUT層的分?jǐn)?shù)s,根據(jù)具體問題,可以選擇最合適的線性模型。如果是binary classification問題,可以選擇linear classification模型;如果是linear regression問題,可以選擇linear regression模型;如果是soft classification問題,則可以選擇logistic regression模型。
如果根據(jù)上面的模型,每一層都是wx,那么無論多少層疊加起來都是www....x而已,都是線性的,所以我們要在每一層結(jié)束的時候加上一個activity function。對于激活函數(shù)的選擇有很多,sigmoid,relu,tanh等。sigmoid函數(shù)由于會出現(xiàn)梯度消失的現(xiàn)象,所以現(xiàn)在已經(jīng)很少用了,relu函數(shù)是比較常用的。這里會使用tanh函數(shù):


tanh是一個平滑函數(shù),當(dāng)|s|比較大的時候近似于階梯函數(shù),|s|比較小的時候會接近于線性函數(shù)。處處連續(xù)而且可導(dǎo),計算也比較方便,而且求導(dǎo)的時候可以用上自身的值:

反向傳播計算的時候是可以用上之前計算過的緩存下來的值,如果在神經(jīng)元數(shù)量特別多的情況下是可以減少計算復(fù)雜度的。

那么下圖更新之后的Neural Network:

指的就是第幾層,再看一下權(quán)值w:
l表示第幾層,ij表示前一層輸出個數(shù)加上當(dāng)前的項。那么對于每一層的分?jǐn)?shù):

再經(jīng)過一層激活函數(shù):

每一層的輸出就是這樣了。

上圖中的每一層神經(jīng)網(wǎng)絡(luò)其實就是一種transform的過程,而每一層轉(zhuǎn)換的關(guān)鍵就在于權(quán)值w,每一層網(wǎng)絡(luò)利用輸入x和權(quán)值w的乘積,在經(jīng)過tanh函數(shù)之后,將得到改函數(shù)的輸出,從左到右,一層一層的進(jìn)行,如果乘積得到的分?jǐn)?shù)越大,那么tanh(wx)就越接近于1了,表明擬合出來的分布越接近于書記分布。所以每一層的輸入x和權(quán)重w具有模式上的相似性,比較接近平行,那么transform的效果就比較好。所以,神經(jīng)網(wǎng)絡(luò)的核心就是pattern extraction,從數(shù)據(jù)本身開始查找蘊(yùn)含的規(guī)律,通過一層一層的找到之后再擬合結(jié)果。所以,神經(jīng)網(wǎng)絡(luò)是生成模型。
根據(jù)error function,我們只要計算Ein = (y - score)^2就可利用知道這單個樣本點的error了,所以只需要建立Ein與每一個權(quán)值之間的關(guān)系就可以了,最直接的方法就是使用Gradient Descent了,不斷優(yōu)化w的值。

先來看一下如何計算他們之間的關(guān)系:

我們的對象是w,自然就是對w求偏導(dǎo):

這是輸出層的偏導(dǎo)結(jié)果,也就是delta_output。
對于其他層,根據(jù)鏈?zhǔn)椒▌t:

我們令第j層的神經(jīng)元的偏導(dǎo)數(shù)即為


后面的推導(dǎo)其實都很簡單了:

依照這種往上遞推的方式,可以把每一層的分?jǐn)?shù)梯度算出來。

上面采用的是SGD的方法,即每次迭代更新時只取一個點,這種做法一般不夠穩(wěn)定。所以通常會采用mini-batch的方法,即每次選取一些數(shù)據(jù),例如N/10,來進(jìn)行訓(xùn)練,最后求平均值更新權(quán)重w。這種做法的實際效果會比較好一些。

4.Optimization and Regularization

最終的目標(biāo)還是要Ein最小,采用什么error function只需要在推導(dǎo)上修正一下就好了。


層數(shù)越多,說明模型的復(fù)雜度就會越大,很明顯,這樣得到的model肯定不是convex的,可能有很多的山峰,可能只是走到了其中一個而已,并沒有到最低的。解決這種情況首先就是對w權(quán)值就行隨機(jī)選擇,通過random從普遍概率上選擇初始值,避免了人為干擾的因素,有更大的可能走到全局的最優(yōu)。

從理論上看神經(jīng)網(wǎng)絡(luò),dvc = O(VD)。其中,V是神經(jīng)網(wǎng)絡(luò)里面神經(jīng)元的個數(shù),D表示所有權(quán)值的數(shù)量,所有如果V很大,那么復(fù)雜度就會越大,很有可能會overfit,所以可以通過限制模型復(fù)雜度來防止過擬合。
防止overfit還有一個方法,就是regularization,L1或L2正則化。但是使用L2正則化有一個缺點,大的權(quán)值就減小很大,小權(quán)值基本不怎么變,復(fù)雜度事實上還是沒有變。而L1正則化是可以使得權(quán)值稀疏,但是在某些地方是不可以微分的,使用也不適合。使用我們需要一個可以使得大權(quán)值減小很大,小權(quán)值減小到0的一個函數(shù):


除此之外,訓(xùn)練次數(shù)減少也是可以達(dá)到限制過擬合效果的。因為訓(xùn)練時間越長,對于尋找可能性就會越多,VC dimension就會越大,當(dāng)t不大的時候是可以減低模型復(fù)雜度的。

5.代碼實現(xiàn)

首先是生成數(shù)據(jù),使用的是sklearn的make_moon:

def generator():
    np.random.seed(0)
    x, y = dataTool.make_moons(200, noise=0.2)
    plt.scatter(x[:, 0], x[:, 1], s = 40, c = y, cmap=plt.cm.Spectral)
    plt.show()
    return x, y
    pass


這是我們生成數(shù)據(jù)的分布,可以看到線性分類器是無法區(qū)分開的。
然后就是網(wǎng)絡(luò)生成訓(xùn)練的主要部分了:

class Network(object):
    def __init__(self, x, y):
        '''initialize the data'''
        self.x = x
        self.num_examples = len(x)
        self.y = y
        self.n_output = 2
        self.n_input = 2
        self.epsilon = 0.01
        self.reg_lambed = 0.01
        self.model = None
        pass

初始化數(shù)據(jù)。

    def calculate_loss(self, model):
        '''calculate the loss function'''
        w1, b1, w2, b2 = model['w1'], model['b1'], model['w2'], model['b2']
        z1 = self.x.dot(w1) + b1
        a1 = np.tanh(z1)
        z2 = a1.dot(w2) + b2
        exp_scores = np.exp(z2)
        probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
        corect_logprobs = -np.log(probs[range(self.num_examples), self.y])
        data_loss = np.sum(corect_logprobs)
        data_loss += self.reg_lambed / 2 * (np.sum(np.square(w1)) + np.sum(np.square(w2)))
        return 1.0/self.num_examples * data_loss

計算損失函數(shù),最后使用的是softmax分類器,損失函數(shù)使用的是交叉熵:



在加上regularization即可。

    def predict(self, x):
        '''according to the model,predict the consequence'''
        w1, b1, w2, b2 = self.model['w1'], self.model['b1'], self.model['w2'], self.model['b2']
        z1 = x.dot(w1) + b1
        a1 = np.tanh(z1)
        z2 = a1.dot(w2) + b2
        exp_scores = np.exp(z2)
        probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
        return np.argmax(probs, axis=1)
        pass

預(yù)測,常規(guī)操作。

    def build_Network(self, n_hinden, num_pass = 20000, print_loss = True):
        loss = []
        np.random.seed(0)
        w1 = np.random.randn(self.n_input, n_hinden) / np.sqrt(self.n_input)
        b1 = np.zeros((1, n_hinden))
        w2 = np.random.randn(n_hinden, self.n_output) / np.sqrt(n_hinden)
        b2 = np.zeros((1, self.n_output))

        self.model = {}
        for i in range(num_pass):
            z1 = self.x.dot(w1) + b1
            a1 = np.tanh(z1)
            z2 = a1.dot(w2) + b2
            exp_scores = np.exp(z2)
            probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)

            delta3 = probs
            delta3[range(self.num_examples), self.y] -= 1
            dw2 = (a1.T).dot(delta3)
            db2 = np.sum(delta3, axis=0, keepdims=True)
            delta2 = delta3.dot(w2.T)*(1 - np.power(a1, 2))
            dw1 = np.dot(self.x.T, delta2)
            db1 = np.sum(delta2, axis=0)
            dw2 += self.reg_lambed * w2
            dw1 += self.reg_lambed * w1

            w1 += -self.epsilon * dw1
            b1 += -self.epsilon * db1
            w2 += -self.epsilon * dw2
            b2 += -self.epsilon * db2

            self.model = {'w1':w1, 'b1':b1, 'w2':w2, 'b2':b2}
            if print_loss and i %200 == 0:
                print('Loss : ', (i, self.calculate_loss(model=self.model)))
                loss.append(self.calculate_loss(model=self.model))
        return loss

有使用的是softmax損失函數(shù),對于softmax函數(shù)求導(dǎo)之后就是原函數(shù)-1,求w2梯度,對w2求導(dǎo)就只有a2了,根據(jù)剛剛的公式:



可以求出delta2,最后一次更新即可。

if __name__ == '__main__':
    Accuracy = []
    losses = []
    x, y = Tool.generator()
    for i in range(1, 13):
        mlp = Network(x, y)
        loss = mlp.build_Network(n_hinden=i)
        losses.append(loss)
        Tool.plot_decision_boundary(mlp.predict, x, y, 'Neutral Network when Hidden layer size is ' + str(i))
        predicstions = mlp.predict(x)
        a = sum(1*(predicstions == y)) / len(y)
        Accuracy.append(a)

    '''draw the accuracy picture'''
    plt.plot(range(len(Accuracy)), Accuracy, c = 'blue')
    plt.title('The Accuracy of the Neural Network')
    plt.xlabel('Hinden Layer Size')
    plt.ylabel('Accuracy')
    plt.show()


    '''draw the loss function picture'''
    for i, loss in enumerate(losses):
        plt.plot(range(len(loss)), loss, c = Tool.get_colors(i), label = 'the hindden layer size '+str(i))
    plt.title('Loss Function')
    plt.xlabel('time')
    plt.ylabel('loss score')
    plt.legend()
    plt.show()

主要的運行函數(shù)。
還需要看一個畫圖函數(shù):

def plot_decision_boundary(pred_func, X, y, title):
    # Set min and max values and give it some padding
    x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    h = 0.01
    # Generate a grid of points with distance h between them
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # Predict the function value for the whole gid
    Z = pred_func(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # Plot the contour and training examples
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
    plt.title(title)
    plt.show()

接下來運行一下看效果吧(有些顏色的代碼不全,GitHub上有):








接下來的效果應(yīng)該都是類似的了。
隨著神經(jīng)元數(shù)量的增加:



準(zhǔn)確率是越來越高的。

1個神經(jīng)元和2個神經(jīng)元的時候明顯是欠擬合,后面的效果其實都變化不大。

符上所以GitHub代碼:
https://github.com/GreenArrow2017/MachineLearning/tree/master/MachineLearning/NeuralNetwork

最后編輯于
?著作權(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)容

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