Neural Network

#1.PLA 從新回顧一下一開始學的PLA,preceptron learning Algorithm。PLA適用於二維及高維的線性可分的狀況,若是是非線性可分的數據,若是使用PLA可能會無限循環。問題的答案只有贊成或不一樣意: git

PLA要作的其實就找到一個x映射到y的f,使得這個f和數據的分佈一致,判斷結果一致。 若是是遇到了線性不可分的狀況,就不能再要求徹底正確了,因此在optimization的時候會要求只須要找到最少錯誤的分類就能夠了,再也不要求徹底正確的分類。 #2.Combine some PLA 把PLA按照aggregation model的想法結合起來:
左邊是x = {X1, X2, X3, X4......},和右邊的w = {W1, W2, W3......}作內積以後就獲得了n個值,就至關於PLA裏面WX的部分了。把獲得WX的稱爲是g(x),最後再乘上α,其實就是把g(x)作了一個非線性的組合,也就是aggregation的blending模型。
上圖實現的是g1,g2的and。如何用感知機模型實現and邏輯?一種方法是:
g1,g2 = {-1, +1};g1 = -1, g2 = -1,那麼結果是就是-3,g1 = -1, g2 = +1, 結果就是-1,若是g1 = +1, g2 = +1,那麼結果就是仍是正的,符合預期。
這個例子簡單說明了在通過必定的線性組合的狀況下是能夠作到非線性的分類的。除此以外,OR,XOR等等都是能夠經過g(x)的線性組合獲得的。 **因此說,Neural Network是一種很powerful同上也是complicated的模型,另外,當hidden層神經元數量大的時候計算量會很是大。**好比下面的一個例子,有一個圓形區域,裏面的+!外面是-1,這種是沒有辦法使用一個PLA切分開的,只能使用多個PLA了,若是是8個PLA的時候,大概是能夠組成一個圓形,16個PLA的時候就要更接近了,所以,使用的PLA越多,獲得的結果就會和數據的分佈越擬合:
以前說過,凸多邊形的VC dimension是無限大的,2的n次方,因此隨着PLA數量的增加vc 維是沒有限制的,容易形成過擬合。可是,數目較多老是能夠獲得一個更加平滑更加擬合的曲線,這也是aggregation model的特色。 可是,仍是有單層preceptron線性組合作不到的事情,好比XOR操做:
由於XOR獲得的是非線性可分的區域,以下圖所示,沒有辦法由g1和g2線性組合實現。因此說linear aggregation of perceptrons模型的複雜度仍是有限制的。事實上,一層的perceptron其實就是一次transform,那麼既然一次transform不行,咱們嘗試一下多層的perceptron,屢次的transform。把XOR拆分開來,第一次是使用AND,第二次就是使用OR,以下:
這樣就從簡單的單層aggregation提高到了multi-layer的多層感知機,模型的複雜度增長以後就更能解決一些非線性的問題了。 第一層的wx處理後再接到下一層wx處理。這其實就是有點接近神經網絡的模型了。 #3.Neural Network 以前已經介紹過三種線性模型:linear classification,linear regression,logistic regression。那麼,對於OUTPUT層的分數s,根據具體問題,能夠選擇最合適的線性模型。若是是binary classification問題,能夠選擇linear classification模型;若是是linear regression問題,能夠選擇linear regression模型;若是是soft classification問題,則能夠選擇logistic regression模型。 若是根據上面的模型,每一層都是wx,那麼不管多少層疊加起來都是www....x而已,都是線性的,因此咱們要在每一層結束的時候加上一個activity function。對於激活函數的選擇有不少,sigmoid,relu,tanh等。sigmoid函數因爲會出現梯度消失的現象,因此如今已經不多用了,relu函數是比較經常使用的。這裏會使用tanh函數:
tanh是一個平滑函數,當|s|比較大的時候近似於階梯函數,|s|比較小的時候會接近於線性函數。到處連續並且可導,計算也比較方便,並且求導的時候能夠用上自身的值:
反向傳播計算的時候是能夠用上以前計算過的緩存下來的值,若是在神經元數量特別多的狀況下是能夠減小計算複雜度的。
那麼下圖更新以後的Neural Network:
指的就是第幾層,再看一下權值w:
l表示第幾層,ij表示前一層輸出個數加上當前的項。那麼對於每一層的分數:
再通過一層激活函數:
每一層的輸出就是這樣了。
上圖中的每一層神經網絡其實就是一種transform的過程,而每一層轉換的關鍵就在於權值w,每一層網絡利用輸入x和權值w的乘積,在通過tanh函數以後,將獲得改函數的輸出,從左到右,一層一層的進行,若是乘積獲得的分數越大,那麼tanh(wx)就越接近於1了,代表擬合出來的分佈越接近於書記分佈。因此每一層的輸入x和權重w具備模式上的類似性,比較接近平行,那麼transform的效果就比較好。因此,神經網絡的核心就是pattern extraction,從數據自己開始查找蘊含的規律,經過一層一層的找到以後再擬合結果。因此,神經網絡是生成模型。 根據error function,咱們只要計算Ein = (y - score)^2就可利用知道這單個樣本點的error了,因此只須要創建Ein與每個權值之間的關係就能夠了,最直接的方法就是使用Gradient Descent了,不斷優化w的值。
先來看一下如何計算他們之間的關係:
咱們的對象是w,天然就是對w求偏導:
這是輸出層的偏導結果,也就是delta_output。 對於其餘層,根據鏈式法則:
咱們令第j層的神經元的偏導數即爲
後面的推導其實都很簡單了:
依照這種往上遞推的方式,能夠把每一層的分數梯度算出來。
上面採用的是SGD的方法,即每次迭代更新時只取一個點,這種作法通常不夠穩定。因此一般會採用mini-batch的方法,即每次選取一些數據,例如N/10,來進行訓練,最後求平均值更新權重w。這種作法的實際效果會比較好一些。 #4.Optimization and Regularization 最終的目標仍是要Ein最小,採用什麼error function只須要在推導上修正一下就行了。
層數越多,說明模型的複雜度就會越大,很明顯,這樣獲得的model確定不是convex的,可能有不少的山峯,可能只是走到了其中一個而已,並無到最低的。解決這種狀況首先就是對w權值就行隨機選擇,經過random從廣泛機率上選擇初始值,避免了人爲干擾的因素,有更大的可能走到全局的最優。
從理論上看神經網絡,dvc = O(VD)。其中,V是神經網絡裏面神經元的個數,D表示全部權值的數量,全部若是V很大,那麼複雜度就會越大,頗有可能會overfit,因此能夠經過限制模型複雜度來防止過擬合。 防止overfit還有一個方法,就是regularization,L1或L2正則化。可是使用L2正則化有一個缺點,大的權值就減少很大,小權值基本不怎麼變,複雜度事實上仍是沒有變。而L1正則化是可使得權值稀疏,可是在某些地方是不能夠微分的,使用也不適合。使用咱們須要一個可使得大權值減少很大,小權值減少到0的一個函數:
除此以外,訓練次數減小也是能夠達到限制過擬合效果的。由於訓練時間越長,對於尋找可能性就會越多,VC dimension就會越大,當t不大的時候是能夠減低模型複雜度的。
#5.代碼實現 首先是生成數據,使用的是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

複製代碼

這是咱們生成數據的分佈,能夠看到線性分類器是沒法區分開的。 而後就是網絡生成訓練的主要部分了:

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
複製代碼

初始化數據。github

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
複製代碼

計算損失函數,最後使用的是softmax分類器,損失函數使用的是交叉熵: 緩存

在加上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
複製代碼

預測,常規操做。bash

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損失函數,對於softmax函數求導以後就是原函數-1,求w2梯度,對w2求導就只有a2了,根據剛剛的公式: 網絡

能夠求出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()
複製代碼

主要的運行函數。 還須要看一個畫圖函數:app

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上有): dom

接下來的效果應該都是相似的了。 隨着神經元數量的增長:
準確率是愈來愈高的。
1個神經元和2個神經元的時候明顯是欠擬合,後面的效果其實都變化不大。

符上因此GitHub代碼: github.com/GreenArrow2…函數