經驗拾憶(純手工)=> DeepLearning+Metrics(Ng)

前言

看Andrew Ng視頻,總結的學習心得。
雖然本篇文章可能不是那麼細緻入微,甚至可能有了解偏差。
但是,我喜歡用更直白的方式去理解知識。

數據劃分

傳統機器學習數據的劃分

傳統機器學習一般都是小規模數據(幾萬條)

那麼可以  訓練集:驗證集:測試集 = 6:2:2

若是大規模深度學習一般都是大規模數據(幾百萬條)

訓練集: 驗證機:測試集 = 9:0.5:0.5

劃分 驗證集 可以過早的爲我們預測 指標和精度

偏差 與 方差

高偏差: 訓練集和測試集 loss 都比較高 (比人預測的loss高很多) (欠擬合)
高方差: 訓練集Loss低, 測試集 Loss高。 所以訓練集和測試集 loss相差太大, 也成爲(過擬合)

防止過擬合的幾種方法

損失函數 (懲罰項係數) 正則化(regularization)

可分兩種 (L1正則化懲罰 和 L2正則化懲罰)下面只以 L2爲例,L2用的也是比較多的)
正則化係數公式:

loss = ...
new_loss = loss + (λ/2m) * w^2
w = w - learning_rate * 梯度

上面公式的單調變換解釋:

求梯度的時候 λ越大, new_loss越大, 求得的梯度越大(正比)
w 減去的值就越大。   w變得就越小。
w 越小, 一定程度上特徵就衰減了許多。  就有效的放置了過擬合哦

對應API(有兩種方式):

L1 = keras.regularizers.l2(0.01)   # TF2 當作 keras 的 Layers定義來使用
L1 = tf.nn.l2_loss(w_b)            # 看到裏面傳遞 w和b了吧, 這種是偏手動方式實現的API

如果你想使用手撕實現,下面有個例子(僞代碼):

for
    with tf.GradientTape() as tape:
        ...
        loss_reg = [tf.nn.l2_loss(w_b) for w_b in model.trainable_variables] # [w1,b1,w2,b2]
        print(tf.reduce_sum(loss_reg))        # tf.Tensor(2.98585, shape=(), dtype=float32) # 就是這個形狀
        loss = loss + loss_reg

另一種正則化方式(regularization) -- DropOut

"隨機"剪枝, 略, TF框架一步到位

還有一種防止過擬合的方式(數據增強)

防止過擬合的另一種方式,就是需要的大量的數據來支撐,那麼數據就那麼點,怎麼辦?

數據增強( 其原理就是增大樣本數量,小幅度翻轉等) 某種程度上,就是增加了樣本。

最後一種防止過擬合的方法 (earlystopping )

earlystopping (tf.keras.callback模塊中,有這個 callback函數,註釋部分有解釋)

callbacks = [
    keras.callbacks.TensorBoard(logdir),
    keras.callbacks.ModelCheckpoint(output_model_file, save_best_only=True),
    # 在這裏
    keras.callbacks.EarlyStopping(patience=5, min_delta=1e-3)
    # 驗證集,每次都會提升,如果提升不動了,小於這個min_delta閾值,則會耐性等待5次。
    # 5次過後,要是還提升這麼點。就提前結束。
]

數據預處理

標準化 和 歸一化能起到什麼作用:

做了標準化 和 歸一化 可以 讓函數收斂的 更快速(梯度下降的快)
參考 (圓形下降的快, 和 橢圓下降的慢)

其次,在神經網絡中,BN層還有額外的效果。每層網絡的不同參數,可能會導致"數據分佈"散亂、變形
因此,BN可以有效 防止數據分佈變形。 (其實說白了也是"加速函數收斂" ,加速NN訓練)

注意點

訓練集 和 測試集的樣本特徵 ("要麼都做處理,要麼都不做處理")(就是相同待遇的意思。。。)
專業點叫做 :"保證訓練集 和 測試集 相同分佈"

數據 隨機分佈有什麼影響(Andrew ng解釋)?

假如訓練集的數據分佈 訓練的目標是正中靶心。
而預測時,假如你的預測集數據分佈 和 訓練集數據分佈的不同。
那麼很可能會 預測集 預測的時候(是另一個靶心)。
所以你訓練的再好,到預測集的靶子上 也是脫靶。。。
所以 訓練集 和 測試集 的 相同分佈很重要

數據預處理大體分2類:

1. 中心化處理
2. 縮放處理

zero-centered (中心化處理)

平移 --- 減去固定值

scale (縮放處理)

歸一化:除以 最大值-最小值
標準化:除以 標準差

下面的操作都是通過 中心化處理+縮放處理 聯合組成的

normalization(歸一化) sklearn那裏也提到過

目標:

將數據收斂到 [0,1]

公式

x - min(x)          # 中心化
------------------
max(x) - min(x)            # 縮放處理

StandardScaler(標準化)

目標:

將數據轉化爲標準 正態分佈(均值爲0,方差爲1)

公式:

x - 平均值
-----------------
    標準差

標準化 和 歸一化 選哪個???

視覺圖片: 歸一化
其他: 標準化較好

數據集不足怎麼辦?

我自己也遇到過這樣的問題。
我之前做一個QA聊天機器人時。 數據是百度知道的爬的。
但是(用過的應該清楚。 百度知道有很多用戶的垃圾回答,是用戶刷分的。)
問了解決這一問題。我的解決思路是:通過Pandas, 篩選答案長度至最小。
但是這樣。就可能篩選除了 大量大量的 原生數據
再加上,把(原數據中 "有問無答" 的問答對)過濾扔掉。
那麼弄下來的源數據,幾乎沒剩多少了。。(我記得我當時弄了400W+問答對)
篩選到最後(問答長度 15個漢字, 篩選掉空回答)(只剩下 幾萬條了。。。)
後來,我急中生智。在網上找了一些 中文語料庫(我用的青雲中文語料庫)
把它融合到 我自己的 語料庫中。。。

但是訓練後的結果, 全是人家 青雲語料庫的 問答內容。。。
後來也沒去繼續深究了。。。

後來正好看到 Ng。提到這一問題,記錄一下相應的應對措施!

訓練集:青雲語料+ 1/2 自己的語料
測試集: 1/4 自己的語料
驗證集:1/4 自己的語料

隨機初始化權重

隨機初始化認知

爲什麼不是初始化爲0???

因爲神經網絡中, W初始化爲0的話, 會導致反向傳播後, 所有神經元會訓練同一個網絡。一點效果沒有

爲什麼不初始化很大的值或者很小的值???

這是分情況來定的。
比如你用的 tanh 或者 sigmoid函數
由腦海中的圖像可知(求導 或 斜率) ,當 初始值過大,或者過小。
都會可能導致,y直接落在 sigmoid的  頂部和底部(就是斜率水平,近乎爲0)
落在了水平的梯度。這樣的梯度,猴年馬月也降不下去啊。。。。。
如果落在了 傾斜陡峭的梯度。 那麼梯度下降的一定很快啦。

如果做了BatchNormalization,那麼可使用 高斯 x 0.01

正態分佈 * 拉低值

np.random.randn(2,2)  * 0.01            # 2,2是形狀, 這個0.01 可以自己調節。 總之,小一點 最好, 但不要太小

如果使用了Relu激活函數,對應 初始化方法

np.random.randn(shapex, shapey) *  np.sqrt( 2/shapex )    # 係數爲2

如果使用了Tanh激活函數,對應 初始化方法(NG推薦, 也叫 Xavier)

np.random.randn(shapex, shapey) *  np.sqrt( 1/shapex ) # 係數爲1

激活函數

激活函數認知

學習Andrew Ng課更深刻了解了激活函數。

神經網絡中,爲什麼我們需要激活函數,甚至需要非線性激活函數?

首先挑明,我們使用神經網絡的目的,就是想訓練出更多,更豐富的特徵。
    所以。 一直用線性激活函數,或者不用激活函數。會使得你整個網絡訓練到頭,還是線性的。就沒意思了。
    它學不到豐富的特徵的。

因爲神經網絡多層是需要拿前一層的結果作爲下一層的 x,所以有了如下公式:
    w3 (w2 (w1x+b) +b) +b
展開後, 
    w3 * w2 * w1 * x + ......   
    很明顯它依然是線性的。  
    所以,無論你用多少層 神經網絡。  到最後它依然是線性的。。。。
    這樣倒不如 一層網絡也不用。
    直接上個 邏輯迴歸模型,效果估計也是一樣的。。。。。。

當然有一些場合也需要使用 線性激活函數,比如 房價預測。身高預測。(這些都是線性迴歸模型)

這些情況,就可以使用 線性激活函數了。

但是不妨想一想, 就像上面 身高預測這些。是線性迴歸,並且 y預測都是正數值。
某種程度上,其實我們也可以使用 relu激活函數, (因爲 relu的右半側(就是大於0的部分) 也是線性的哦)

我們NN隱層就大多數都使用非線性激活函數。

隱層: relu 或者 leakly relu 或者 tanh
輸出層: sigmoid  或者 softmax 或者 tanh  等等

sigmoid

公式

   1  
---------
1 + e**(-x)

每個out: (0, 1)  

二分類out之和爲 1

對應API:

1. tf.sigmoid(y)
2. 或函數參數 xxxxx (activations='sigmoid')
3. tf.keras.activations.sigmoid()

softmax


  e**x
---------------------------------
e**(x1) + e**(x2) + ... + e**(xn)

每個out: (0,1)

多分類 out之和爲 1

對應API:

1. tf.nn.softmax()
2. 函數參數 xxxxx (activations='softmax')
3. tf.keras.activations.softmax()

softmax特點:

輸出的是什麼形狀的張量,輸出的就是什麼形狀的張量
也是有線性決策邊界(線性 多 分類器)

tanh

coshx

e**x - e**(-x)
-------------
      2

sinhx

e**x + e**(-x)
--------------
      2

tanhx

 e**x - e**(-x)
-------------
e**x + e**(-x)
      
每個out: (0,1) * 2 -1  ===>  (-1,1)

LSTM

對應API:

1. tf.tanh(y)
2. 函數參數 xxxxx (activations='tanh')
3. tf.keras.activations.tanh()

relu

公式:

y = 0 if x < 0 else x    # 大於0,梯度爲1

對應API

1. tf.nn.relu()
2. 或函數參數 xxxxx (activations='relu')
3. tf.keras.activations.relu()

leaky_relu: (小擴展)
    y = kx if x < 0 else x    
    tf.nn.leaky_relu()

損失函數

MSE (均方誤差)

公式

Σ( (y-y_predict)**2 )
--------------------
     n

對應API

公式實現: 
    tf.reduce_mean( tf.square( y-y_predict ) )
tf.API:   
    tf.reduce_mean( tf.loss.MSE(y, y_predict) )

CrossEntropy (交叉熵)

熵公式: -Σ(plogp)
交叉熵公式:-( Σplogq ) p爲真實值One-hot, q爲預測值

p: [1,0,0]
q: [0.9, 0,0.1]
H = -( 1*log0.9 + 0*log0 + 0*log0.1) = -log0.9 = -ln0.9 ≈ 0.1053....
tf的 tf.math.log相當於 ln

交叉熵API:

交叉熵越小(y與y-predict差距越小,預測較準確)
交叉熵越大(y與y_predict差距越大,交叉相乘累加後值大,說明預測錯位了。。。所以交叉起來變大了)

tf.API: (方式1:直接是函數調用)
    loss = tf.losses.categorical_crossentropy([1,0,0], [0.9, 0, 0.1],from_logits=True)  # 第一個參數y, 第二個參數 y_predict
    loss = tf.reduce_mean(loss)

tf.API: (方式2:用類的call調用 , 這次以 二分類交叉熵爲例)
    loss = tf.losses.BinaryCrossentropy(from_logits=True)( [1], [0.1] )  # 結果爲2.+  。 因爲 真實值是1類, 而預測值概率是0.1太小了。所以肯定預測錯了。
    loss = tf.reduce_mean(loss)
    
說明:categorical_crossentropy( ) # 第一個參數必須 one_hot, (第二個參數按理來說需要做 softmax,但是你傳了 from_logigs=True,就不必softmax了)

梯度

SGD(Stochastic Gradent Descent):

解釋 各種梯度下降的區別:
Mini-Batch Gradent Descent:

指定每次 mini-batch個 來做梯度下降 (就是每次指定多少個樣本 來做GD的意思)
這種介於  1-全部樣本之間的。 是最優的

Batch gradent descent:

mini-batch 爲全部樣本

Stochastic gradent descent:

mini-batch 爲 1個樣本
缺點: 每次 1個樣本做SGD, 那麼就失去了 向量化(矩陣乘代替循環)的 加速快感。。。。。

減去梯度,代表朝着梯度方向走

w新 = w當前 - learning_rate * 梯度    

使用方式:

model.compile(..... ,optimizer=keras.optimizers.SGD(learning_rate=0.01))

再記錄其他優化器之前, 先補一個 指數加權平均 的知識

公式:

y = β * X之前 + (1-β)* X當前

圖形曲線表現:

β越小:(小到0.5) :曲線越抖動頻繁(鋸齒 越厲害)(0.5左右已經,嚴重上下跳動了)
β越大:(大至1.0) :曲線越光滑(無鋸齒)
所以 β: 越大越好 
(涉及到一個技術--偏差修正, 如果你不修正。 可能訓練會稍微慢一些。無傷大雅)

Momentum(動量)

公式大概:

dw' =  β * dw-1 + ( 1-β ) * dw        # 用 dw-1 掰彎 dw
db' =  β * db-1 + ( 1-β ) * db        # 用 db-1 掰彎 db

公式理解:

在原來的梯度基礎上, 用 上一次的梯度方向, 把當前將要計算的梯度掰彎

RMSProp

model.compile(..... ,optimizer=keras.optimizers.RMSprop(learning_rate=0.01, momentum=0.9))

Adam(強烈推薦)

TF-API: 默認原參數

model.compile(..... ,optimizer=keras.optimizers.Adam(
        learning_rate=0.001,
        beta_1=0.9,           # 學習率衰減參數
        beta_2=0.999,
        epsilon=1e-7,
    ),
)

其實這個API參數,我們只稍微調整一下 learning _ rate 即可,其他不用怎麼。

學習率衰減

其實大多數 優化器內都有 學習率衰減參數,例如:

SGD(decay)
Adam(beta_1)

當然你也可以自己實現(按照樣本已訓練的批次,動態衰減)

learning rate = learning rate * 1/(epoch輪數 * 衰減率 + 1)

其實還有更多 可調節參數,就像Adam中的 那麼多參數似。當然我壓根也沒想自己實現衰減。。

可知 decay越小, 學習率衰減的越慢, 當衰減率爲0時。  學習率壓根就不衰減
而 decay越大, 學習率衰減的越快, 當衰減率爲1時。  那衰減的就太誇張了~~

遷移學習 (我想到一個詞:移花接木)

應用場景

假如已經有現成的 狗類 識別的 神經網絡模型
那麼假如你現在想要 做一個 貓類的 識別

你完全可以把 狗識別 網絡模型拿過來
然後把最後 輸出層 扔掉,自己加一個新輸出層(當然中間你也可以加一些新的NN層)
然後 旁敲側擊,只在最後一層提供輸入,只對 新的輸出層(或者你額外加的NN)層訓練。

應用條件

當你遷移後的數據有特別特別多的時候, 那麼你完全可以把 搬過來的 模型參數 從頭到尾訓練一遍。

就像你 狗模型, 前面的網絡學到了很多  毛,特徵。 (這貓也有嘛,所以正好可以用得上)
然後你 在狗模型的基礎上 ,訓練貓模型 (我不太瞭解貓~~~, 比如說可以新學到貓的鬍鬚之類的新特徵)
總結來說:  新模型 = NN層(狗)參數 + NN層(貓)參數 + 輸出層(貓)參數

當然, 如果你遷移支持的數據,只有很少,根本不夠強大的神經網絡訓練

那麼,你就可以直接把,搬過來的模型參數固定住, 直接只在 最後輸出層,提供輸入,進行訓練
總結來說:  新模型 = NN層(狗)參數 + 輸出層(貓)參數

遷移學習的主要目的思想:

當你 有很少的小數據集A, 但是你想訓練成一個 NN 來 達到目的。
可想而知,少量數據集A 還不夠 NN 塞牙縫的。。。

所以,你需要找一些其他類似的數據集B(量多的,好收集的)
然後這些大量數據集B,足以 馳騁於 NN , 得到一個模型。(並且帶着 訓練好的參數)

數據集A說: "
    大哥,你訓練好的網絡借我用用唄。
    你用了那麼多數據,訓練出的特徵一定有我想要的。
    我把整個模型拿過來,只改一下最後一層的輸入。然後只訓練最後一層的參數。
    其他層的參數都用你的。
"。
數據集B大哥說: "可以"

遷移學習API (Tensorflow2.0)

溫馨提示: TF20的Keras Layers 是可以 用切片語法 選取具體網絡層的,舉個例子:

# from tensorflow import keras
# cut_resnet = keras.applications.DenseNet121(  # 使用現有ResNet模型
#     include_top=False,  # 不要最後一層,而是使用我們自己定義的全連接層
#     pooling='avg',
#     weights='imagenet',  # 初始化權重(從imagenet訓練好模型參數來初始化)
# )
# for layer in cut_resnet.layers[0:-3]:  # 部分可訓練(fine-tune分割)
#     trainable=False             # 0 到 倒數第三層,參數不可訓練
# 
# new_model = keras.models.Sequential()
# new_model.add(cut_resnet)
# new_model.add(其他層)

遷移學習 適用場景

  1. 統一使用領域(要麼文本遷移要文本, 要門圖像遷移到圖像。)
  2. 假如 A 遷移到 B (那麼 A的樣本最好遠大於 B的樣本)
  3. 假如 A 遷移到 B (最好A的許多特徵信息,B正好可以用得到。比如 貓狗,都有毛髮,鬍鬚,四條腿)

多任務學習(瞭解,用的少)

直接感觀:我認爲就像(類的繼承 , 或者封裝爲一個函數, 這樣的概念。。)

你想訓練 預測 各種各樣類別的圖片。
你可以首先 用一個任務 訓練一下 共有特徵 的 NN。
然後其他任務 用這個 訓練好的 共有的特徵的 NN。
Ng提示: 你需要有龐大的神經網絡支撐,不然效果不好。    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章