機器學習實戰筆記9——深度神經網絡

任務安排

1、機器學習導論       7、邏輯迴歸
2、KNN及其實現       8、核方法
3、K-means聚類       9、深度神經網絡
4、主成分分析          10、?
5、線性判別分析       11、?
6、貝葉斯方法

      加上這次課只剩下三節課,開始進入到深度學習的範疇了,雖然已經學了不少機器學習相關的知識了,但感覺還是遠遠不夠,期待開學後的實訓和以後的課程能更深入地學習人工智能!

深度神經網絡(DNN)

      20世紀80年代以來,人們大量借用神經生理學的概念來研究人工智能,誕生了一門新興學科——人工神經網絡(artificial neural networks,ANN),簡稱神經網絡(neural netwroks,NN)
      人工神經網絡的基本思想:根據對自然神經系統構造和機理的認識,神經系統是由大量的神經細胞(神經元)構成的複雜的網絡,人們對這一網絡建立一定的數學模型(如MLP)算法(如BP),設法使它能夠實現諸如基於數據的模式識別、函數映射等帶有“智能”的功能
      在神經網絡的數學模型中,最有影響的模型是多層感知器(multi-layer perceptron,MLP),實際上,多層感知器就是深度神經網絡(deep neural networks,DNN)的模型,多層感知器屬於上次提到的 非線性分類器

Ⅰ 神經元與感知器

      (講到神經網絡,需要科普一點生物知識作爲基礎)
      一個神經元(neuron)就是一個神經細胞,是神經系統的基本組成單位。
在這裏插入圖片描述
      如圖,一個典型的神經元由這些部分組成:
      (1)細胞體(cell body),神經細胞的主體,內有細胞核和細胞質,除了實現細胞生存的各種基本功能外,還是神經細胞進行信息加工的主要場所
      (2)樹突(dendrites),細胞體外圍的大量微小分支,是細胞的“觸角”,一個神經網絡的樹突可達 10310^3 數量級,多數長度很短,主要擔負着從外界(其他細胞或體液環境)接受信息的功能
      (3)軸突(axon),細胞的輸出裝置,負責把信號傳遞給另外的神經細胞,通常每個神經元由一個軸突,有的軸突很長,如人體四肢的某些神經細胞的軸突長達 1m1m 以上
      (4)突觸(synapse),一個神經元的軸突與另一個神經元的樹突相“連接”的部位,這種連接並不是物理上的直接接觸,而是二者細胞膜的充分靠近,通過之間的微小縫隙傳遞帶電離子
      神經系統中的信號是電化學信號,靠帶電離子在細胞膜內外的濃度差來形成和維持的,這種信號以脈衝的形式沿着軸突傳播,經突觸把電荷傳遞給下一個神經元。突觸的不同狀態影響信號傳遞的效率,稱之爲突觸的連接強度。神經元之間通過突觸連接,構成複雜的神經網絡。
在這裏插入圖片描述
      一個典型的神經元簡化工作過程:外界電信號通過突觸傳遞給神經元,當細胞收到的信號總和超過一定閾值後,細胞被激活,通過軸突向下一個細胞發送電信號,完成對外界信息的加工
      如上圖 (閾值邏輯單元(threshold logic unit,TLU):McCulloch-Pitts神經元模型) 所示,x1,...,xnx_1,...,x_n 表示神經元的多個樹突接收到的信號,nn 是向量 xx 的維數,w1,...,wnw_1,...,w_n 稱作權值,反映各個輸入信號的作用強度。神經元將這些信號加權求和,當求和超過一定閾值後神經元即進入激活狀態,輸出值 y=1y=1;否則神經元處於抑制狀態,輸出值 y=0y=0;
      用公式表示該模型:y=θ(i=1nwixi+ω0)y=θ(∑^n_{i=1}w_ix_i+ω_0)      其中 θ()θ() 是單位階躍函數(也可以是符號函數 sgn()sgn()),稱爲神經元的傳遞函數(激活函數)
      幾何意義上來理解,一個感知器神經元就是一個線性分類器,用超平面i=1nwixi+ω0=0∑^n_{i=1}w_ix_i+ω_0=0      把特徵空間分爲兩個區域,y=1y=1y=0(1)y=0(-1)
      套用 Rosenblatt 的感知器準則函數及利用梯度下降法迭代求解,得到權值的迭代訓練修正函數:w(t+1)=w(t)+η(t)[d(x(t))y(t)]x(t)w(t+1)=w(t)+η(t)[d(x(t))-y(t)]x(t)      d(x)d(x) 代表訓練樣本 xx 的正確分類,tt 爲迭代次數記數,x(t)x(t) 是當前時刻考查的樣本,η(t)η(t) 是訓練步長,當兩類數據線性可分時,上述神經元權值迭代算法能夠在有限步內收斂到一個使所有訓練樣本都正確的解

★ Ⅱ 前饋網絡(FNN)

      對於單個感知器,只能解決線性分類問題(如與AND、或OR、非NOT問題),無法解決非線性分類問題(異或XOR),理論上來說,是可以像之前線性分類器的組合實現非線性分類器那樣,通過感知器的組合來實現多層學習模型,來解決非線性分類問題——多層感知器誕生了
      通常把多個神經元相互連接組成的系統稱作人工神經網絡,而把由多個感知器組成的神經網絡稱作多層感知器網絡在這裏插入圖片描述
      如圖,多層感知器實現的是從 dd 維輸入 xx 到輸出 yy(一維或多維)的一個映射。它由多個採用 SigmoidSigmoid 傳遞函數(激活函數)的神經元節點連接而成,這些神經元節點分層排列,每一層的神經元接受來自前一層的信號,經過加工後又傳遞給後一層
      多層感知器第一層是輸入層input(input layerlayer),每個節點對應於 xx 的每一維,節點本身並不完成任何處理,只是把每一維的信號“分發”到後一層的每個節點。最後一層是輸出層out(out layerlayer),如果 yy 是一維,則輸出層只有一個節點。輸入層和輸出層各層都被稱作“隱層”hidden(hidden layerlayer),其中的節點稱作“隱節點”hidden(hidden nodesnodes),因爲它們全都“隱藏”在輸入層和輸出層之內,隱層神經元的個數即爲網絡模型的寬度,層的個數即爲網絡模型的深度,故多層感知器也被稱爲深度神經網絡
      類似這種形式的神經網絡即被稱作前饋神經網絡(feedforward neural networks,FNN),是神經網絡的主要結構形式之一。在前饋神經網絡中,信號沿着輸入層到輸出層方向單向流動,輸入層把信號傳給隱層,隱層再把信號傳遞給下一個隱層,最後一個隱層把信號傳遞給輸出層。這種神經網絡實現了從輸入層到輸出層的映射。把一個樣本特徵向量的各分量分別輸入到網絡輸入層的各個對應節點上,經過網絡上從前向後一系列加工運算,在輸出端得到相應輸出向量。
      對於網絡層次的叫法,通常有兩種:①輸入輸出各一層,隱層 nn 層,如圖則是四層網絡;②輸入層不計入,則如圖是三層網絡
      對於一個實現xxyy 的映射的三層感知器,可以用函數表示爲y=g(x)=f(j=1nHwijf(i=1dwijxi))y=g(x)=f(∑^{n_H}_{j=1}w_{ij}f(∑^d_{i=1}w_{ij}x_i))      其中 f()f()SigmoidSigmoid (或其他激活函數),dd 是輸入 xx 的維數(輸入層節點個數),wijw_{ij} 是從輸入層第 ii 個節點到隱層第 jj 個節點的權值(連接強度),wjkw{jk} 是從隱層第 jj 個節點到輸出層第 kk 個節點的權值
      常見的激活函數通常有:SigmoidSigmoid 函數、tanhtanh 函數、ReluRelu 函數、 PReLU 函數(性能通常更優)ELUELU 函數、MaxOutMaxOut函數等,實際使用時一般根據模塊提供的都試一遍過去看哪個效果好…
      前饋網絡的目標是逼近於某個函數,通過學習讓映射函數逼近於分類函數,得到某些參數最優解,只是它其中一個方面的應用而已,研究發現,任何一個從 xxyy 的非線性映射,都存在一個適當結構的三層前饋神經網絡能夠以任意精度逼近它

★ Ⅲ 反向傳播算法(BP)

      對於上述提到的前饋神經網絡,會有一個疑問,爲什麼激活是 SigmoidSigmoid 函數,而不是之前常用來分類的階躍函數 θθ 或符號函數 sgnsgn 呢?
      根據前面提到的感知器學習算法,核心思想是梯度下降法,即以訓練樣本被錯分的程度爲目標函數,訓練中每次出現錯誤時便使權係數朝着目標函數相對於權係數的負梯度方向更新(使其下降),直到目標函數取得極小值即沒有訓練樣本被錯分。
      實驗表明,這種感知器學習算法無法直接應用到多層感知器上,因爲激活函數作爲階躍函數(對於權值的反饋只有0、1之分),得到輸出端的誤差只能對最後一個感知器的權值進行訓練,無法對前面感知器的權值進行調整,起初的解決辦法是提前給定其他感知器的權值,但最終模型的性能很大程度上取決於能否設計出恰當的權值,給模型優化造成了非常大的阻礙,甚至對於感知器的研究因此停滯了25年之久!?直到後來 反向傳播算法(back propagation,BP) 的提出,人工神經網絡迎來飛速發展。
      而算法突破的關鍵主要是將階躍函數 θθ 替代爲了 SigmoidSigmoid 函數SigmoidSigmoid 函數在 邏輯迴歸 有介紹過,不贅述了,其特點是單調遞增的非線性函數,無限次可微,權值較大時逼近階躍函數,權值小時逼近線性函數,用 SigmoidSigmoid 函數作爲激活函數,則數學表達式可以寫成y=f(x)=11+ei=1nwixiw0y=f(x)=\frac{1}{1+e^{-∑^n_{i=1}w_ix_i-w_0}}      至此,前饋網絡(通常以 SigmoidSigmoid 函數作爲激活函數),不管網絡結構多麼複雜,總可以通過計算梯度來考查各個參數對網絡輸出的影響,通過梯度下降法調整各個參數
{神經網絡\begin{cases}神經網絡結構 \\神經元激活函數 \\權值\end{cases}
      實際應用中,網絡結構和激活函數都是事先定好的,而權值是根據 BP 算法求得的,以下算法步驟分析以前饋網絡結構Sigmoid 激活函數爲基礎
      核心思想: 訓練開始前,隨機賦予各權值一定的初值。訓練過程中,輪流對網絡施加各個訓練樣本。當某個訓練樣本作用於神經網絡輸入後,利用當前權值計算神經網絡輸出(這是一個信號從輸入到隱層再到輸出的過程,稱爲前向過程)。考查所得到的輸出與訓練樣本的已知正確輸出之間的誤差,根據誤差對這些節點權值的偏導數修正這些權值,以此類推,直到把各層權值都修正一次。然後,從訓練集中抽出另外一個樣本進行相同的訓練過程。不斷重複,一輪訓練終止直到總誤差水平達到預先設置的閾值,或者訓練時間達到預設上限。
輸入:樣本集 {xi,yix_i,y_i}i=1n^n_{i=1},步長 ηη,小批量大小 minibatchesmini-batches,迭代次數 TT
輸出:訓練後收斂的神經網絡
      1.確定神經網絡結構,用小隨機數進行權值初始化,設訓練時間 t=0t=0
      2.從訓練集中得到一個訓練樣本 x=[x1,x2,...,xn]TRnx=[x_1,x_2,...,x_n]^T∈R^n記它的期望輸出爲 D=[d1,d2,...,dm]TRmD=[d_1,d_2,...,d_m]^T∈R^m樣本通常隨機地從訓練集中抽取
      3.計算在 xx 輸入下當前神經網絡的實際輸出y=f(s=2nL2wsrl=L1f(j=1n1wjkl=2f(i=1nwijl=1xi)))y=f(∑^{n_{L-2}}_{s=2}w^{l=L-1}_{sr}…f(∑^{n_1}_{j=1}w^{l=2}_{jk}f(∑^n_{i=1}w^{l=1}_{ij}x_i)))
      4.從輸出層開始調整權值,第l層修正公式:wijl(t+1)=wijl(t)+wijl(t)w^l_{ij}(t+1)=w^l_{ij}(t)+△w^l_{ij}(t)      上標 ll 表示從第 (l1)(l-1) 層到 ll 層的權值,wijl=ηδjlxil1△w^l_{ij}=ηδ^l_jx^{l-1}_i 爲權值修正項,對於輸出層 δjl=yj(1yj)(djyj)δ^l_j=y_j(1-y_j)(d_j-y_j),是當前輸出與期望輸出之間的差值對權值的導數;對於隱層 δjl=xjl(1xjl)k=1nl+1δkl+1wkl+1δ^l_j=x^l_j(1-x^l_j)∑^{n_{l+1}}_{k=1}δ^{l+1}_kw^{l+1}_k,是輸出誤差反向傳播到該層的誤差對權值的導數
      5.在更新全部權值後對所有訓練樣本重新計算輸出,計算更新後的網絡輸出與期望輸出的誤差。 檢查算法終止條件,如果達到則停止,否則 tt +=1+=1,返回第二步。算法終止條件通常設定爲該輪訓練中網絡實際輸出與期望輸出之間的平均平方誤差小於某一閾值,或該輪訓練中所有權值變化都小於某一閾值,或訓練次數達到上限

      手寫驗算在這裏插入圖片描述
在這裏插入圖片描述

Ⅳ 泛化能力

      泛化能力,即機器學習算法對新鮮樣本的適應能力
      神經網絡由於具有大量的參數,很強的非線性變換能力,因而也很容易導致在訓練集上過擬合,訓練集上準確率很高,損失很低,但在測試數據上效果很差,也就是缺乏泛化能力,不能適應新樣本

過擬合產生的原因:
      1)數據集有噪聲
      2)訓練數據不足
      3)訓練模型過度導致模型非常複雜

解決方法:
      1)降低模型複雜度(縮小寬度和減小深度)
      2)數據集擴增(平移,旋轉,縮放,剪切,添加噪音)
      3)正則化。
      4)加入droupout,讓神經元以一定的概率不工作。
      5)early stopping
      6)ensemble(集成學習),特徵融合。

Ⅴ 架構設計

通常有三種做法(均帶有試探性)來選擇多層感知器網絡的隱層節點數目和隱層個數
      ①根據具體問題進行試探選擇。 選擇幾個不同的隱層節點數目,分別對訓練樣本集進行試驗,採用留一法或其他方法交叉驗證,根據交叉驗證的錯誤率來選擇較好的節點數目。也有一些基本經驗,如通常隱層節點數目應該小於輸入維數訓練樣本數較小時應該適當採用少的隱層節點(如輸入節點數一半左右)
      ②根據對問題的先驗知識精心設計隱層節點數和層數,如有人設計了多層神經網絡進行手寫體數字識別,有些隱層專門爲考慮數字的旋轉不變形和某些變形不變性
      ③用算法確定隱層節點數,如**裁剪法:**初始時採用較多的隱層節點,在採用 BP 算法進行權值學習時增加一條額外目標,要求所有權值的絕對值或平方和儘可能小,這樣,一部分多餘的隱節點的權值會逐漸變小。在學習到一定階段時,檢查各個隱節點權值,將過小的隱節點刪除,剩餘神經網絡重新學習

今日任務

在這裏插入圖片描述

任務解決

1、對於 Digits 手寫字,直接用 sklearn 裏的模型就能得到非常好的分類效果了(如果要跑 MINST 數據集,就模仿後面 CIFAR-10 一樣在 PyTorch 裏自己構建神經網絡模型)

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from myModule import clustering_performance


def sklearn_MLP(*data):
    X_train, X_test, y_train, y_test = data
    mlp = MLPClassifier(solver='adam', hidden_layer_sizes=[100, 100], activation='relu',
                        random_state=62, max_iter=2000, learning_rate_init=0.0001)
    mlp.fit(X_train, y_train)
    y_predict_mlp = mlp.predict(X_test)
    print('MLP分類器')
    print('Testing Score: %.4f' % clustering_performance.clusteringMetrics1(y_test, y_predict_mlp))
    # print(mlp.loss_)
    # return clustering_performance.clusteringMetrics1(y_test, y_predict_mlp)


# Digits手寫字
digits = load_digits()
X_train_digits, X_test_digits, y_train_digits, y_test_digits = \
    train_test_split(digits.data, digits.target, test_size=0.2, random_state=22)
print('Digits')
sklearn_MLP(X_train_digits, X_test_digits, y_train_digits, y_test_digits)

在這裏插入圖片描述
★ 2、對於 CIFAR-10,需要利用 pytorch 自己構建神經網絡模型,代碼偏多、偏複雜
數據讀取之前在 邏輯迴歸 裏面有提過
深度學習步驟:
                   ①構建網絡結構
                   ②加載數據集
                   ③訓練神經網絡(優化器、損失函數)
                   ④測試神經網絡

深度學習有幾個重要概念需區分一下:

名詞 定義
Epoch 使用訓練集的全部數據對模型進行一次完整訓練,稱爲 “一代訓練”
Batch 使用訓練集中的一小部分樣本對模型權值進行一次反向傳播的參數更新,該一小部分樣本被稱爲 “一批數據”
Iteration 使用一個 Batch 數據對一次模型進行一次參數更新的過程,稱爲 “一次訓練”

Batchnumber=TraindatasizeBatchsize Batch-number=\frac{Train-data-size}{Batch-size}

例:
      CIFAR-10 數據集有 60000 張圖片作爲訓練數據,10000 張圖片作爲測試數據。假設現在選擇 Batch_Size = 100 對模型進行訓練,迭代 30000 次
● 每個 Epoch 訓練圖片數量:60000(全部)
●     每個 Epoch 需完成 Batch 個數
    = 每個 Epoch 具有 Iteration 個數
    = 每個Epoch 中模型發生權重更新次數
    = Batch_number = 60000 / 100 = 600
● 總共迭代 30000 次 = 30000 / 600 = 50 個 Epoch

梯度下降方式區別

梯度下降方式 Train-data-size Batch-size Batch-number
BGD N N 1
SGD N 1 N
Mini-Batch N B NB\frac{N}{B}(除不盡時+1)

$$
代碼

import torch
import torch.nn.functional as F  # 激勵函數的庫
from torchvision import datasets
import torchvision.transforms as transforms

path = 'C:/Users/1233/Desktop/Machine Learning/CIFAR10/'
# 定義全局變量
n_epochs = 10  # epoch 的數目
batch_size = 20  # 決定每次讀取多少圖片

# 定義訓練集個測試集
train_data = datasets.CIFAR10(root=path, train=True, transform=transforms.ToTensor())
test_data = datasets.CIFAR10(root=path, train=True, transform=transforms.ToTensor())

# 創建加載器
train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=0)
test_loader = torch.utils.data.DataLoader(test_data, batch_size=batch_size, num_workers=0)
# num_workers可以>0,是調用系統子進程來跑,但是Windows可能會因爲各種權限問題報錯


# 建立一個四層感知機網絡
class MLP(torch.nn.Module):  # 繼承 torch 的 Module
    def __init__(self):
        super(MLP, self).__init__()
        # 初始化三層神經網絡 兩個全連接的隱藏層,一個輸出層
        self.fc1 = torch.nn.Linear(3072, 512)  # 第一個隱含層
        self.fc2 = torch.nn.Linear(512, 128)  # 第二個隱含層
        self.fc3 = torch.nn.Linear(128, 10)  # 輸出層

    def forward(self, din):
        # 前向傳播, 輸入值:din, 返回值 dout
        din = din.view(din.size(0), 3072)  # .view( )是一個tensor的方法,使得tensor改變size但是元素的總數是不變的
        # din = din.view(-1, 28 * 28)  # 將一個多行的Tensor,拼接成一行
        dout = F.relu(self.fc1(din))  # 隱層激活函數 relu
        dout = F.relu(self.fc2(dout))
        dout = F.softmax(self.fc3(dout), dim=1)  # 輸出層激活函數 softmax
        # 10個數字實際上是10個類別,輸出是概率分佈,最後選取概率最大的作爲預測值輸出
        return dout


# 訓練神經網絡
def train():
    # 定義損失函數和優化器
    lossfunc = torch.nn.CrossEntropyLoss()  # 交叉熵損失函數
    # SGD隨機梯度下降法,lr學習率(步長),這裏隨機梯度和小批量隨機梯度共用.SGD
    optimizer = torch.optim.SGD(params=model.parameters(), lr=0.01)
    # 開始訓練
    for epoch in range(n_epochs):
        train_loss = 0.0
        for data, target in train_loader:
            optimizer.zero_grad()  # 清空上一步的殘餘更新參數值
            output = model(data)  # 得到預測值
            loss = lossfunc(output, target)  # 計算兩者的誤差
            loss.backward()  # 誤差反向傳播, 計算參數更新值
            optimizer.step()  # 將參數更新值施加到 net 的 parameters 上
            train_loss += loss.item() * data.size(0)
        train_loss = train_loss / len(train_loader.dataset)
        print('Epoch:  {}  \tTraining Loss: {:.6f}'.format(epoch + 1, train_loss))
        # 每訓練一epoch,測試一下性能
        test()


# 在數據集上測試神經網絡
def test():
    correct = 0
    total = 0
    with torch.no_grad():  # 訓練集中不需要反向傳播
        for data in test_loader:
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)  # 前面一個返回值數據不是我們想要的
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    print('Accuracy of the network on the test images: %d %%' % (100 * correct / total))
    return 100.0 * correct / total


# 實例化感知器網絡
model = MLP()

train()

效果圖
可以明顯看見隨着每一次epoch的參數更新以後,網絡性能在逐漸變好,但性能優化速率在下降,最終性能應該還是可以修改一些超參數提高的
在這裏插入圖片描述
參考:深度學習之BP算法
           使用 PyTorch 實現 MLP 並在 MNIST 數據集上驗證
           Pytorch中文文檔
           訓練神經網絡中最基本的三個概念:Epoch, Batch, Iteration

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章