深度學習
深度學習的缺陷
- 算法輸出不穩定,容易被“攻擊”
- 模型複雜度高,難以糾錯和調試
- 模型層級複合程度高,參數不透明
- 端到端訓練方式對資料依賴性強,模型增量性差(當樣本資料量小的時候,深度學習無法展現強大拟合能力)
- 專注直覺感覺類問題,對開放性推理問題無能為力
- 人類知識無法有效引入進行監督,機器偏見難以避免
可解釋性
2017年7月,國務院在《新一代人工智能發展規劃》中提出“實作具備高可解釋性、強泛化能力的人工智能”。
可解釋性:
- 知道哪些特征對輸出有重要影響,出了問題準确快速糾錯
- 雙向:算法能被人的知識體系了解 + 利用和結合人類知識
- 知識得到有效存儲、積累和複用——越學越聰明
淺層神經網絡
每個神經元的輸出可表示為以下函數:
激活函數
激活函數
引入非線性因素
萬有逼近定理
單隐層神經網絡可視化工具 —— A Neural Network Playground
如果一個隐層包含足夠多的神經元,三層前饋神經網絡(輸入- 隐層 - 輸出)能以任意精度逼近任意預定的連續函數。
當隐層足夠寬時,雙隐層感覺器(輸入 - 隐層1 - 隐層2 - 輸出)可以逼近任意非連續函數:可以解決任何複雜的分類問題。
神經網絡每一層的作用
神經網絡學習如何利用矩陣的線性變換加激活函數的非線性變換,将原始輸入空間投影到線性可分的空間去分類/回歸。
每層神經網絡的輸出可表示為以下函數:
該函數實作了輸入到輸出空間的變換,其中
實作升維/降維,放大/縮小和旋轉;
實作平移;
實作彎曲。
神經網絡的深度與寬度
增加節點數:增加次元,即增加線性轉換能力。
增加層數:增加激活函數的次數,即增加非線性轉換次數
在神經元總數相當的情況下,增加網絡深度可以比增加寬度帶來更強的網絡表示能力:産生更多的線性區域。
深度和寬度對函數複雜度的貢獻是不同的,深度的貢獻是指數增長的,而寬度的貢獻是線性的。
其中
表示參數每層參數對函數複雜度的貢獻,
表示參數數量,
表示深度對函數複雜度的貢獻,
和
都是一個區間即相同的參數在不同數值下仍然有不同的複雜度。
表示最大深度,
表示第
層。
前向傳播和反向傳播
局部最小值與梯度消失
假設每一層網絡的輸出為
,其中
代表第
層,
代表第
層的輸入,
是激活函數,那麼
。在梯度下降時,參數的更新為
其中
,如果此部分大于1,那麼層數增多的時候,最終的求出的梯度更新将以指數形式增加,即發生梯度爆炸;如果此部分小于1,那麼随着層數增多,求出的梯度更新将會以指數形式衰減,即發生了梯度消失。
解決方案:
Layer-wise Pre-train:2006年Hinton等提出的訓練深度網路的方法,用無監督資料作分層預訓練,再用有監督資料fine-tune(DBN, 2006)。
ReLU:新的激活函數解析性質更好,克服了sigmoid函數和tanh函數的梯度消失問題(AlexNet, 2012)。
輔助損失函數:e.g. GoogLeNet中的兩個輔助損失函數,對淺層神經元直接傳遞梯度(Inception V1, 2013)。
Batch Normalization:逐層的尺度歸一(Inception V2, 2014)。
LSTM:通過選擇記憶和遺忘機制克服RNN的梯度消失問題,進而可以模組化長時序列。
Pytorch
PyTorch 是一個 Python 庫,它主要提供了GPU加速的張量計算 (Tensor Computation) 功能和建構在反向自動求導系統上的深度神經網絡功能,使用時需導入torch包。
Pytorch使用torch.Tensor張量類定義資料,torch.autograd.Function函數類定義資料操作。
Tensor
Tensor的關鍵組成:
- data屬性,用來存資料
- grad屬性,用來存梯度
- grad_fn,用來指向創造自己的Function
定義資料
Tensor支援各種各樣類型的資料,包括:
torch.float32, torch.float64, torch.float16, torch.uint8, torch.int8, torch.int16, torch.int32, torch.int64
x = torch.tensor(666) # 一個數
x = torch.tensor([1,2,3,4,5,6]) # 一維向量
# 值全為1的任意次元的張量
# torch.ones(size=(int ...), dtype=None)
x = torch.ones(2,3)
# 對角線全1,其餘部分全0的二維數組
x = torch.eye(2,3)
# 傳回從start到end(不包括端點)步長為step的一維張量
# torch.arange(start=0, end, step=1)
a = torch.arange(0, 10, 2)
# 傳回從start到end(包括端點)的等距的steps個資料點
# torch.linspace(start, end, steps)
b = torch.linspace(0, 10, 2)
print(a) # tensor([0, 2, 4, 6, 8])
print(b) # tensor([ 0., 10.])
# 從區間[0,1)的均勻分布中抽取的一組随機數
x = torch.rand(2,3)
# 從标準正态分布(均值為0,方差為1)中抽取的一組随機數
x = torch.randn(2,3)
# 基于現有的tensor,建立一個新tensor,
# 進而可以利用原有的tensor的dtype,device,size之類的屬性資訊
y = x.new_ones(5,3) #tensor new_* 方法,利用原來tensor的dtype,device
定義操作
凡是用Tensor進行各種運算的,都是Function
- 基本運算,加減乘除,求幂求餘
- 布爾運算,大于小于,最大最小
- 線性運算,矩陣乘法,求模,求行列式
基本運算: abs/sqrt/div/exp/fmod/pow ,一些三角函數 cos/sin/asin/atan2/cosh,其他函數 ceil/round/floor/trunc
布爾運算: gt/lt/ge/le/eq/ne, topk, sort, max/min
線性計算: trace, diag, mm/bmm,t,dot/cross,inverse,svd等
螺旋資料分類
learning_rate = 1e-3
lambda_l2 = 1e-5
# nn 包用來建立線性模型
# 每一個線性模型都包含 weight 和 bias
model = nn.Sequential(
nn.Linear(D, H),
nn.ReLU(),
nn.Linear(H, H2),
nn.ReLU(),
nn.Linear(H2, C)
)
model.to(device) # 把模型放到GPU上
# nn 包含多種不同的損失函數,這裡使用的是交叉熵(cross entropy loss)損失函數
criterion = torch.nn.CrossEntropyLoss()
# 這裡使用 optim 包進行随機梯度下降(stochastic gradient descent)優化
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2)
# 開始訓練
for t in range(1000):
# 把資料輸入模型,得到預測結果
y_pred = model(X)
# 計算損失和準确率
loss = criterion(y_pred, Y)
score, predicted = torch.max(y_pred, 1)
acc = (Y == predicted).sum().float() / len(Y)
print('[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f' % (t, loss.item(), acc))
display.clear_output(wait=True)
# 反向傳播前把梯度置 0
optimizer.zero_grad()
# 反向傳播優化
loss.backward()
# 更新全部參數
optimizer.step()
線性模型 | Linear(in_features=2, out_features=100, bias=True) Linear(in_features=100, out_features=3, bias=True) epoch:1000 optimizer:SGD learning_rate = 1e-3 | [LOSS]: 0.862976 [ACCURACY]: 0.501 | |
兩層神經網絡 | Linear(in_features=2, out_features=100, bias=True) Sigmoid() Linear(in_features=100, out_features=3, bias=True) epoch:1000 optimizer:Adam learning_rate = 1e-3 | [LOSS]: 0.754807 [ACCURACY]: 0.509 | |
兩層神經網絡 | Linear(in_features=2, out_features=100, bias=True) ReLU() Linear(in_features=100, out_features=3, bias=True) epoch:1000 optimizer:Adam learning_rate = 1e-3 | [LOSS]: 0.171876 [ACCURACY]: 0.955 | |
兩層神經網絡 | Linear(in_features=2, out_features=100, bias=True) ReLU() Linear(in_features=100, out_features=3, bias=True) epoch:1000 optimizer:SGD learning_rate = 1e-1 | [LOSS]: 0.377146 [ACCURACY]: 0.813 | |
三層神經網絡 | Linear(in_features=2, out_features=100, bias=True) ReLU() Linear(in_features=100, out_features=25, bias=True) ReLU() Linear(in_features=25, out_features=3, bias=True) epoch:1000 optimizer:Adam learning_rate = 1e-3 | [LOSS]: 0.022764 [ACCURACY]: 0.998 | |
三層神經網絡 | Linear(in_features=2, out_features=100, bias=True) ReLU() Linear(in_features=100, out_features=25, bias=True) ReLU() Linear(in_features=25, out_features=3, bias=True) epoch:10000 optimizer:Adam learning_rate = 1e-3 | [LOSS]: 0.002587 [ACCURACY]: 0.999 | |
在10000輪訓練過程中,損失函數出現先減小後增大再減小的現象。該現象可能是損失函數進入局部最小值後發生震蕩,最終又跳出該局部最小值造成的。也可能是學習率過大造成震蕩,在權重衰減達到合适的學習率後震蕩消失。