主頁 >  其他 > 【動手學深度學習】第四章筆記:多層感知機、權重衰減、暫退法、數值穩定性和模型初始化、環境和分布偏移

【動手學深度學習】第四章筆記:多層感知機、權重衰減、暫退法、數值穩定性和模型初始化、環境和分布偏移

2023-04-26 07:53:03 其他

為了更好的閱讀體驗,請點擊這里

4.1 多層感知機

4.1.1 隱藏層

由于仿射變換中的線性是一個很強的假設,因此導致了線性模型可能會不適用,線性意味著單調假設:任何特征的增大都會導致模型輸出的增大或者模型輸出的減小,

但是違反單調性的例子比比皆是,除此之外,分類任務中,僅依托像素強度分類也很不合理,由于任何像素的重要性都以復雜的方式取決于該像素周圍的值,對于深度神經網路,用觀測資料來聯合學習隱藏層表示和應用于該表示的線性預測器,

因此可以在網路中加入隱藏層,把前 \(L-1\) 層看作表示,把最后一層看作線性預測器,這種架構通常稱為多層感知機,但是具有全連接層的多層感知機的引數開銷可能太過巨大,

用矩陣 \(\boldsymbol{X} \in \mathbb{R}^{n\times d}\) 來表示有 \(n\) 個樣本的小批量,其中每個樣本具有 \(d\) 個輸入特征,對于具有 \(h\) 個隱藏單元的單隱藏層多層感知機,用 \(\boldsymbol{H} \in \mathbb{R}^{n\times h}\) 表示隱藏層的輸出,稱為隱藏表示(hidden representation),在數學或代碼中,\(\boldsymbol{H}\) 也稱為隱藏層變數(hidden layer variable)或隱藏變數(hidden variable),不妨設當前有一個一層隱藏層以及一層輸出層的網路,由于隱藏層和輸出層都是全連接的,所以我們有隱藏層權重 \(\boldsymbol{W}^{(1)} \in \mathbb{R}^{d \times h}\) 和隱藏層偏置 \(\boldsymbol{b}^{(1)} \in \mathbb{R}^{1 \times h}\) 以及輸出層權重 \(\boldsymbol{W}^{(2)} \in \mathbb{R}^{h \times q}\) 和輸出層偏置 \(\boldsymbol{b}^{(2)} \in \mathbb{R}^{1 \times q}\),形式上,我們按如下方式計算單隱藏層多層感知機的輸出 \(\boldsymbol{O} \in \mathbb{R}^{n \times q}\)

\[\begin{align} \boldsymbol{H} &= \boldsymbol{XW}^{(1)} + \boldsymbol{b}^{(1)} \\ \boldsymbol{O} &= \boldsymbol{HW}^{(2)} + \boldsymbol{b}^{(2)} \end{align} \]

注意,上述模型本質上和單個線性層相同,原因在于:

\[\boldsymbol{O} = (\boldsymbol{XW}^{(1)} + \boldsymbol{b}^{(1)})\boldsymbol{W}^{(2)} + \boldsymbol{b}^{(2)} = \boldsymbol{XW}^{(1)}\boldsymbol{W}^{(2)} + \boldsymbol{b}^{(1)}\boldsymbol{W}^{(2)} + \boldsymbol{b}^{(2)} \]

所以,如果要搞多層架構的話,必須在仿射變換之后對每個隱藏單元應用非線性的激活函式(activation function)\(\sigma\),激活函式的輸出(例如,\(\sigma(\cdot)\))稱為激活值(activation),這樣就不會再退化到線性模型了,所以有:

\[\begin{align} \boldsymbol{H} &= \sigma(\boldsymbol{XW}^{(1)} + \boldsymbol{b}^{(1)}) \\ \boldsymbol{O} &= \boldsymbol{HW}^{(2)} + \boldsymbol{b}^{(2)} \end{align} \]

出于記號習慣的考量,我們定義非線性函式 \(\sigma\) 也以按行的方式作用于其輸入,即一次計算一個樣本,構建多層感知機,可以繼續堆疊這樣的隱藏層,

多層感知機可以通過隱藏神經元,捕捉到輸入之間復雜的相互作用,這些神經元依賴于每個輸入的值,我們可以很容易地設計隱藏節點來執行任意計算,而且,雖然一個單隱層網路能學習任何函式,但并不意味著我們應該嘗試使用單隱藏層網路來解決所有問題,事實上,通過使用更深(而不是更廣)的網路,可以更容易地逼近許多函式,

4.1.2 激活函式

1. ReLU

整流線性單元(rectified linear unit, ReLU)式子如下:

\[\text{ReLU}(x) = \max(x, 0) \]

ReLU 函式通過將相應的激活值設為 \(0\),僅保留正元素并丟棄所有負元素,

這里代碼在畫圖的時候很有趣,如下:

x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = torch.relu(x)
d2l.plot(x.detach(), y.detach(), 'x', 'relu(x)', figsize=(5, 2.5))

y.detach() 那里修改為 y.data 也可以過編譯,原因是 pyplot.plot(x,y) 函式中的 x,y 需要用兩個 numpy 型別的變數,而 tensor 變數如果有梯度/梯度追蹤,那么無法轉換成 numpy 型別的變數,如果這里是兩個沒有梯度/梯度追蹤的 tensor 變數,則會被型別轉化成 numpy 型別,

ReLU 函式的導數如下:

\[\frac{\mathrm{d} \text{ReLU}(x)}{\mathrm{d} x} = \begin{cases} 0,& x<0 \\ 0~或者~不可導,&x=0 \\ 1,& x>0 \end{cases} \]

當輸入值精確等于 \(0\) 時,ReLU 函式不可導,此時,默認使用左邊的導數,即當輸入為 \(0\) 時,導數為 \(0\),原書中說,可以忽略這種情況,因為輸入可能永遠都不會是 \(0\)

使用 ReLU 的原因是其求導表現特別好:要么讓引數消失,要么讓引數通過,這使得優化表現得更好,并且ReLU減輕了困擾以往神經網路的梯度消失問題,

也有一些變體,比如引數化 ReLU(parameterized ReLU, pReLU)函式,該變體為 ReLU 添加了一個線性項,因此即使引數是負的,某些資訊仍然可以通過:

\[\text{pReLU}(x) = \max(0, x) + \alpha \min(0, x) \]

2. sigmoid 函式

對于一個定義域在 \(\mathbb{R}\) 中的輸入,sigmoid 函式將輸入變換為區間 \((0,1)\) 上的輸出,因此,sigmoid 通常稱為擠壓函式(squashing function):它將范圍 \((-\infin, \infin)\) 中的任意輸入壓縮到區間 \((0,1)\) 中的某個值:

\[\text{sigmoid}(x) = \frac{1}{1+\exp(-x)} \]

sigmoid 函式是一個平滑的、可微的閾值單元近似,

它的導數為:

\[\frac{\mathrm{d}}{\mathrm{d}x} \text{sigmoid}(x) = \frac{\exp(-x)}{(1 + \exp(-x))^2} = \text{sigmoid}(x) (1 - \text{sigmoid}(x)) \]

當輸入為 \(0\) 時,sigmoid 函式的導數達到最大值 \(0.25\); 而輸入在任一方向上越遠離 \(0\) 點時,導數越接近 \(0\)

3. tanh 函式

與 sigmoid 函式類似,tanh(雙曲正切)函式也能將其輸入壓縮轉換到區間 \((-1,1)\) 上,tanh函式的公式如下:

\[\tanh(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)} \]

當輸入在 \(0\) 附近時,tanh 函式接近線性變換,函式的形狀類似于 sigmoid 函式,不同的是 tanh 函式關于坐標系原點中心對稱,

tanh 函式的導數是:

\[\frac{\mathrm{d}}{\mathrm{d}x} \tanh(x) = 1 - \tanh^2(x) \]

當輸入接近 \(0\) 時,tanh 函式的導數接近最大值 \(1\),輸入在任一方向上越遠離 \(0\) 點,導數越接近 \(0\)

課后題

第四道課后題有點意思,因為我沒看懂題意,在英文原書的網址 MLP 中,我發現原問題是這么問的:

Assume that we have a nonlinearity that applies to one minibatch at a time, such as the batch normalization (Ioffe and Szegedy, 2015). What kinds of problems do you expect this to cause?

參考別人的答案:資料可能會被劇烈地拉伸或者壓縮,可能會導致分布的偏移,并且與后面的神經元對接后可能會損失一定的特征,

4.2 多層感知機的從零開始實作

這里結合 PyTorch 的 optimizer 和自己的引數的方法很有趣,

num_inputs, num_outputs, num_hiddens = 784, 10, 256

W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))

params = [W1, b1, W2, b2]
num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)

這里是把 nn.Parameter 類打包成一個 list 丟進 SGD 的引數中,

另外在 numpy 或者是 PyTorch 中,可以用 @ 來表示矩陣乘法,

練習(6):如果想要構建多個超引數的搜索方法,請設計一個聰明的策略:

  1. 手調(Babysitting)
  2. 網格搜索(Grid Search)
  3. 隨機搜索(Random Search)

4.4 模型選擇、欠擬合和過擬合

機器學習的目標是發現模式(pattern),更正式地說,我們的目標是發現某些模式,這些模式捕捉到了我們訓練集潛在總體的規律,如果成功做到了這點,即使是對以前從未遇到過的個體,模型也可以成功地評估風險,如何發現可以泛化的模式是機器學習的根本問題,

困難在于,當我們訓練模型時,我們只能訪問資料中的小部分樣本,當我們使用有限的樣本時,可能會遇到這樣的問題: 當收集到更多的資料時,會發現之前找到的明顯關系并不成立,

將模型在訓練資料上擬合的比在潛在分布中更接近的現象稱為過擬合(overfitting),用于對抗過擬合的技術稱為正則化(regularization),

4.4.1 訓練誤差和泛化誤差

訓練誤差(training error)是指模型在訓練資料集上計算得到的誤差,泛化誤差(generalization error)是指,模型應用在同樣從原始樣本的分布中抽取的無限多資料樣本時,模型誤差的期望,問題是,我們永遠不能準確地計算出泛化誤差,這是因為無限多的資料樣本是一個虛構的物件,在實際中,我們只能通過將模型應用于一個獨立的測驗集來估計泛化誤差,該測驗集由隨機選取的、未曾在訓練集中出現的資料樣本構成,

假設訓練資料和測驗資料都是從相同的分布中獨立提取的,這通常被稱為獨立同分布假設(i.i.d. assumption),這意味著對資料進行采樣的程序沒有進行“記憶”,有時候我們即使輕微違背獨立同分布假設,模型仍將繼續運行得非常好,但同時有些違背獨立同分布假設的行為肯定會帶來麻煩,

一個模型是否能很好地泛化取決于很多因素,有以下幾個傾向于影響模型泛化的因素:

  1. 可調整引數的數量,當可調整引數的數量(有時稱為自由度)很大時,模型往往更容易過擬合,
  2. 引數采用的值,當權重的取值范圍較大時,模型可能更容易過擬合,
  3. 訓練樣本的數量,即使模型很簡單,也很容易過擬合只包含一兩個樣本的資料集,而過擬合一個有數百萬個樣本的資料集則需要一個極其靈活的模型,

4.4.2 模型選擇

為了確定候選模型中的最佳模型,我們通常會使用驗證集,不能依靠測驗資料進行模型選擇,然而,我們也不能僅僅依靠訓練資料來選擇模型,因為我們無法估計訓練資料的泛化誤差,

解決此問題的常見做法是將我們的資料分成三份,除了訓練和測驗資料集之外,還增加一個驗證資料集(validation dataset),也叫驗證集(validation set),書中每次實驗報告的準確度都是驗證集準確度,而不是測驗集準確度,

也可以用 K 折交叉驗證,原始訓練資料被分成 \(K\) 個不重疊的子集,然后執行 \(K\) 次模型訓練和驗證,每次在 \(K?1\) 個子集上進行訓練,并在剩余的一個子集(在該輪中沒有用于訓練的子集)上進行驗證,最后,通過對 \(K\) 次實驗的結果取平均來估計訓練和驗證誤差,

但是這會帶來一個問題,不存在平均誤差方差的無偏估計,于是通常會用近似來解決,

4.4.3 欠擬合還是過擬合

訓練誤差和驗證誤差都很嚴重,但它們之間僅有一點差距,如果模型不能降低訓練誤差,這可能意味著模型過于簡單(即表達能力不足),無法捕獲試圖學習的模式,此外,由于我們的訓練和驗證誤差之間的泛化誤差很小,我們有理由相信可以用一個更復雜的模型降低訓練誤差,這種現象被稱為欠擬合(underfitting),

另一方面,當我們的訓練誤差明顯低于驗證誤差時要小心,這表明嚴重的過擬合(overfitting),

是否過擬合或欠擬合可能取決于模型復雜性和可用訓練資料集的大小,

4.4.4 多項式擬合

作者用擬合一個多項式的方式來作為示范,當引數量等于原本的資料分布的引數量時擬合函式正常,但是如果模型引數量較少會發生欠擬合,而模型引數量過大會發生過擬合,

課后題(部分)

(1)多項式回歸問題可以準確地解出嗎?

直接高斯消元,或者參考前面解方程,

(2)繪制訓練損失與模型復雜度(多項式的階數)的關系圖,觀察到了什么?需要多少階的多項式才能將訓練損失減少到0?

animator = d2l.Animator(xlabel='degree', ylabel='loss', yscale='log', xlim=[1, max_degree], ylim=[1e-3, 1e2], legend=['train', 'test'])
for i in range(max_degree):
    p = train_parameter(poly_features[:n_train, :i], poly_features[n_train:, :i], labels[:n_train], labels[n_train:])
    animator.add(i + 1, p)

image-20230413204901138

訓練損失無法減少到 0.

資料量那個圖,取度數為 \(4\),在代碼中體現為 \(5\),只能說是毫無規律:

animator = d2l.Animator(xlabel='train_data', ylabel='loss', yscale='log', xlim=[100, 1000],
                       ylim=[1e-3, 1e2], legend=['train', 'test'])
for i in range(10):
    poly_features, labels = getData((i+1) * 100)
    p = train_parameter(poly_features[:n_train, :5], poly_features[n_train:, :5], labels[:n_train], labels[n_train:])
    animator.add((i + 1)*100, p)

image-20230413211648314

(3)如果不對多項式特征 \(x^i\) 進行標準化 \((1/i!)\) ,會出現什么問題?能用其他方法解決這個問題嗎?

如果有一個 \(x\) 大于 \(1\),那么這個很大的 \(i\) 就會帶來很大的值,優化的時候可能會帶來很大的梯度值,

(4)泛化誤差可能為零嗎?

幾乎不可能,

4.5 權重衰減(weight decay)

單項式(monomial)是多項式對多變數資料的自然擴展,也可以說是變數的冪的乘積,給定 \(k\) 個變數,階數 \(d\)(即選 \(k\) 個自然數的加和為 \(d\)),則共有 \(\begin{pmatrix} k-1+d \\ d-1\end{pmatrix}\) 種單項式,因此對于多變數資料在多項式上的擴展,需要更細粒度的工具來調整函式復雜度,

4.5.1 范數與權重衰減

權重衰減是使用最廣泛的正則化技術之一,它通常也稱為 \(L_2\) 正則化,這個技術通過函式與零的距離來度量函式的復雜度,要保證權重向量比較小,最常用方法是將其范數作為懲罰項加到最小化損失的問題中,將原來的訓練目標最小化訓練標簽上的預測損失,調整為最小化預測損失和懲罰項之和

上一章種線性回歸例子的損失為:

\[L(\boldsymbol{w}, b) = \frac{1}{n} \sum_{i=1}^n \frac{1}{2}(\boldsymbol{w}^T \boldsymbol{x}^{(i)} + b - y^{(i)})^2 \]

那么,可以通過正則化常數 \(\lambda\) 來描述這種權衡,這是一個非負超引數,我們使用驗證資料擬合:

\[L(\boldsymbol{w}, b) + \frac{\lambda}{2} ||\boldsymbol{w}||^2 \]

原書中關于這部分函式的設計原因寫的很詳細:

對于 \(\lambda=0\),我們恢復了原來的損失函式,對于 \(\lambda>0\),我們限制 \(\|\boldsymbol{w}\|\) 的大小,這里我們仍然除以 \(2\),當我們取一個二次函式的導數時,\(2\)\(\frac{1}{2}\) 會抵消,以確保更新運算式看起來既漂亮又簡單,為什么在這里我們使用平方范數而不是標準范數(即歐幾里得距離)?這樣做是為了便于計算,通過平方 \(L_2\) 范數,我們去掉平方根,留下權重向量每個分量的平方和,這使得懲罰的導數很容易計算:導數的和等于和的導數,

此外,為什么我們首先使用 \(L_2\) 范數,而不是 \(L_1\) 范數,事實上,這個選擇在整個統計領域中都是有效的和受歡迎的,\(L_2\) 正則化線性模型構成經典的嶺回歸(ridge regression)演算法,\(L_1\) 正則化線性回歸是統計學中類似的基本模型,通常被稱為套索回歸(lasso regression),使用 \(L_2\) 范數的一個原因是它對權重向量的大分量施加了巨大的懲罰,這使得我們的學習演算法偏向于在大量特征上均勻分布權重的模型,在實踐中,這可能使它們對單個變數中的觀測誤差更為穩定,相比之下,\(L_1\) 懲罰會導致模型將權重集中在一小部分特征上,而將其他權重清除為零,這稱為特征選擇(feature selection),這可能是其他場景下需要的,

那么 \(L_2\) 正則化回歸的小批量隨機梯度下降更新如下式:

\[\boldsymbol{w} \leftarrow (1 - \eta \lambda) \boldsymbol{w} - \frac{\eta}{|B|} \sum_{i \in B} \boldsymbol{x}^{(i)} (\boldsymbol{w}^T \boldsymbol{x}^{(i)} + b - y^{(i)}) \]

根據之前章節,我們根據估計值與觀測值之間的差異來更新 \(\boldsymbol{w}\),然而,我們同時也在試圖將 \(\boldsymbol{w}\) 的大小縮小到零,這就是為什么這種方法有時被稱為權重衰減,我們僅考慮懲罰項,優化演算法在訓練的每一步衰減權重,與特征選擇相比,權重衰減為我們提供了一種連續的機制來調整函式的復雜度,較小的 \(\lambda\) 值對應較少約束的 \(\boldsymbol{w}\),而較大的 \(\lambda\) 值對 \(\boldsymbol{w}\) 的約束更大,

是否對相應的偏置 \(b^2\) 進行懲罰在不同的實踐中會有所不同,在神經網路的不同層中也會有所不同,通常,網路輸出層的偏置項不會被正則化,

4.5.3 從零開始實作(有趣的 optim)

這一節中出現了很有趣的 Optimizer 的寫法,原文是給權重設定了權重衰減,而偏置沒有設定權重衰減,具體代碼如下:

trainer = torch.optim.SGD([{"params": net[0].weight, 'weight_decay': wd},
                           {"params": net[0].bias}], lr=lr)

下面簡單寫一下 PyTorch 原檔案中的有趣用法,

逐個引數優化方法

Optimizer 也支持逐個引數優化的選項,這里傳入的不再是 Variable(或者說是 Tensor)的一個可迭代物件,取而代之的是傳入 dict 的可迭代物件(事實上,Optimizer 中也只能傳入這兩個的可迭代物件),它們中的每一個會定義一個分離的引陣列,組中應當有一個包含了引數串列的 param 鍵,其他鍵應當匹配能夠被 Optimizer 接受的引數,

當然,與此同時也仍然可以在外面寫其他默認引數,這不會覆寫掉引陣列里面的引數,

官網中給出了這樣一個例子:

optim.SGD([
                {'params': model.base.parameters()},
                {'params': model.classifier.parameters(), 'lr': 1e-3}
            ], lr=1e-2, momentum=0.9)

這意味著 model.base 的引數會使用默認學習率 1e-2model.classifier 的引數會使用 1e-3,動量 0.9 將會應用于所有引數,

課后題

(1)繪制訓練精度和測驗精度關于 \(\lambda\) 的函式圖,可以觀察到什么?

def train_concise_many_times(max_wd):
    animator = d2l.Animator(xlabel = 'wd', ylabel = 'loss', yscale='log', xlim=[1, max_wd], legend=['train', 'test'])
    for wd in range(max_wd):
        net = nn.Sequential(nn.Linear(num_inputs, 1))
        for param in net.parameters():
            param.data.normal_()
        loss = nn.MSELoss(reduction='none')
        num_epochs, lr = 100, 0.003
        trainer = torch.optim.SGD([{"params": net[0].weight, 'weight_decay': wd},
                                  {"params": net[0].bias}], lr=lr)
        for epoch in range(num_epochs):
            for X, y in train_iter:
                trainer.zero_grad()
                l = loss(net(X), y)
                l.mean().backward()
                trainer.step()
        animator.add(wd + 1, (d2l.evaluate_loss(net, train_iter, loss), d2l.evaluate_loss(net, test_iter, loss)))
        
train_concise_many_times(10)

image-20230418112710849

可以觀察到隨著 \(\lambda\) 增加,訓練 loss 增大,測驗 loss 在 \(\lambda=4\) 的時候達到最小,然后趨于平穩,

(2)使用驗證集來找到最優值 \(\lambda\),它真的是最優值嗎?

不一定,如果驗證集與測驗集滿足獨立同分布,那么最優,

(3)如果使用 \(\sum_i |w_i|\) 作為懲罰(\(L_1\) 正則化),那么更新的公式長什么樣子?

不妨設新的損失函式為:

\[L(\boldsymbol{w}, b) + \lambda \sum_i |w_i| \]

則,更新公式為:

\[w_i \leftarrow \begin{cases} w_i + \eta \lambda - \frac{\eta}{|B|} \left(\sum_{j \in B} \boldsymbol{x}^{(j)}(\boldsymbol{w}^T \boldsymbol{x}^{(j)} + b - y^{(j)}) \right)_i, &w_j < 0 \\ w_i - \eta \lambda - \frac{\eta}{|B|} \left(\sum_{j \in B} \boldsymbol{x}^{(j)}(\boldsymbol{w}^T \boldsymbol{x}^{(j)} + b - y^{(j)}) \right)_i, &w_j \ge 0 \end{cases} \]

(4)已知 \(\|\boldsymbol{w}\|^2 = \boldsymbol{w}^T \boldsymbol{w}\),能找到類似的矩陣方程嗎?(提示:弗羅貝尼烏斯范數)

矩陣 \(X \in \mathbb{R}^{m \times n}\) 的 Frobenius norm 為:

\[\|X\|_F^2 = \sum_{i=1}^m \sum_{j=1}^n x_{ij}^2= tr (X^TX) \]

(5)處理過擬合的方法除了權重衰減、增加訓練資料、使用適當復雜度的模型,還有哪些?

還有 dropout,提前終止等,

(6)在貝葉斯統計中,使用先驗和似然的乘積,通過公式 \(P(w|x) \propto P(x|w)P(w)\) 得到后驗,如何得到正則化的 \(P(w)\)

這里題目顯然有些奇怪,英文原文為:

In Bayesian statistics we use the product of prior and likelihood to arrive at a posterior via \(P(w∣x) \propto P(x∣w)P(w)\). How can you identify P(w) with regularization?

最后一句可以翻譯為如何用正則化得到 \(P(w)\),也可以翻譯為如何得到帶有正則化的 \(P(w)\)

優化 \(w\) 的程序本質上也是最大化后驗概率 \(P(w|x)\) 的程序,也是最小化其負對數似然的程序,即:

\[\arg \max P(w|x) \implies \arg \max P(x|w) P(w) \implies \arg \min \left(-\ln P(x | w) - \ln P(w) \right) \]

可以發現上式中 \(-\ln P(w)\) 就是正則化項了,如果要讓 \(P(w|x)\) 盡量大,那么就應當讓正則化項 \(P(w)\) 盡量大,

這樣的話問題最好應當翻譯成:如何得到帶有與 \(P(w)\) 相關的正則化項,

4.6 暫退法(Dropout)

由于上一章的做法是假設了一個先驗,即權重的值取自均值為 0 的高斯分布,這可能不是特別優雅,因此希望模型深度挖掘特征,即將其權重分散到許多特征中,而不是過于依賴少數潛在的虛假關聯,

4.6.1 重新審視過擬合

線性模型沒有考慮到特征之間的互動作用,換言之,每一個僅為一次項,沒有交叉相乘的項,對于每個特征,線性模型必須指定正的或負的權重,而忽略其他特征,

泛化性和靈活性之間的這種基本權衡被描述為偏差-方差權衡(bias-variance tradeoff),線性模型有很高的偏差:它們只能表示一小類函式,然而,這些模型的方差很低:它們在不同的隨機資料樣本上可以得出相似的結果,

神經網路并不局限于單獨查看每個特征,而是學習特征之間的互動,但即使我們有位元征多得多的樣本,深度神經網路也有可能過擬合,

4.6.2 擾動的穩健性

經典泛化理論認為,為了縮小訓練和測驗性能之間的差距,應該以簡單的模型為目標,簡單性以較小維度的形式展現,此外,引數的范數也代表了一種有用的簡單性度量,簡單性的另一個角度是平滑性,即函式不應該對其輸入的微小變化敏感,1995年,克里斯托弗·畢曉普證明了 具有輸入噪聲的訓練等價于Tikhonov正則化,這項作業用數學證實了“要求函式光滑”和“要求函式對輸入的隨機噪聲具有適應性”之間確實存在聯系,

Srivastava提出了暫退法(Dropout),在訓練程序中,他們建議在計算后續層之前向網路的每一層注入噪聲,因為當訓練一個有多層的深層網路時,注入噪聲只會在輸入-輸出映射上增強平滑性,這種方法之所以被稱為暫退法,因為從表面上看是在訓練程序中丟棄(drop out)一些神經元,在整個訓練程序的每一次迭代中,標準暫退法包括在計算下一層之前將當前層中的一些節點置零,

關鍵的挑戰就是如何注入這種噪聲,一種想法是以一種無偏差(unbiased)的方式注入噪聲,這樣在固定住其他層時,每一層的期望值等于沒有噪音時的值,

  1. 在畢曉普的作業中,他將高斯噪聲添加到線性模型的輸入中,在每次訓練迭代中,他將從均值為零的分布 \(\epsilon \sim N(0, \sigma^2)\) 采樣噪聲添加到輸入 \(\boldsymbol{x}\),從而產生擾動點 \(\boldsymbol{x}' = \boldsymbol{x} + \epsilon\),預期是 \(\mathbb{E}[\boldsymbol{x}']=\boldsymbol{x}\)

  2. 在標準暫退法正則化中,通過按保留(未丟棄)的節點的分數進行規范化來消除每一層的偏差,換言之,每個中間活性值 \(h\)暫退概率 \(p\) 由隨機變數 \(h'\) 替換,如下所示:

    \[h' = \begin{cases} 0 &,\text{概率為 $p$} \\ \frac{h}{1-p} &,\text{其他情況} \end{cases} \]

    期望值保持不變,即 \(\mathbb{E}[h']=h\)

4.6.3 實踐中的暫退法

image-20230418170732248

上圖為 Dropout 前后的多層感知機,其中 Dropout 程序洗掉了 \(h_2\)\(h_5\),因此輸出的計算不依賴 \(h_2\)\(h_5\),并且它們各自的梯度在執行反向傳播時也會消失,這樣,輸出層的計算不能過度依賴于 \(h_1, h_2, \dots, h_5\) 的任何一個元素,

通常,我們在測驗時不用暫退法,給定一個訓練好的模型和一個新的樣本,我們不會丟棄任何節點,因此不需要標準化,然而也有一些例外:一些研究人員在測驗時使用暫退法,用于估計神經網路預測的“不確定性”:如果通過許多不同的暫退法遮蓋后得到的預測結果都是一致的,那么我們可以說網路發揮更穩定,

代碼中有個技巧是,在靠近輸入層的地方設定較低的暫退概率,感性理解這個原因可能是,通過第一層后如果設定的暫退概率較高/跟之后的差不多的話,會導致通過線性層后丟掉靠近源資料的特征,進而導致資料中的部分內容被丟棄,

nn.Dropout(dropout_probability) 會在訓練時,暫退層根據指定的暫退概率隨機丟棄上一層的輸出(相當于下一層的輸入),在測驗時,暫退層僅傳遞資料,

此外,作者推薦的暫退概率為 \([0.2, 0.5]\) 之間,

課后題

(1)如果更改第一層和第二層的暫退概率,會出現什么問題?具體地說,如果交換這兩個層,會出現什么問題?設計一個實驗來回答這些問題,定量描述該結果,并總結定性的結論,

# 交換前
dropout1, dropout2 = 0.2, 0.5
net = nn.Sequential(nn.Flatten(),
                   nn.Linear(784, 256),
                   nn.ReLU(),
                   nn.Dropout(dropout1),
                   nn.Linear(256, 256),
                   nn.ReLU(),
                   nn.Dropout(dropout2),
                   nn.Linear(256, 10))
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
    
net.apply(init_weights)

trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

image-20230419113624457

# 交換后
net = nn.Sequential(nn.Flatten(),
                   nn.Linear(784, 256),
                   nn.ReLU(),
                   nn.Dropout(dropout2),
                   nn.Linear(256, 256),
                   nn.ReLU(),
                   nn.Dropout(dropout1),
                   nn.Linear(256, 10))
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
    
net.apply(init_weights)
trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

image-20230419113732914

可以觀察到僅僅有微小的差距,交換后的收斂速度變慢了一點點,而精度幾乎沒變化,譯者在評論區說:

It may be hard to observe a huge loss/acc difference if the network is shallow and can converge quickly. As you can find in the original dropout paper (http://jmlr.org/papers/volume15/srivastava14a/srivastava14a.pdf) as well, the improvement with dropout on MNIST is less than 1%.

翻譯過來大概意思是,當模型比較小很難觀察到巨大的 loss/acc 的差別,并且它會快速收斂,原論文中也說,在 MNIST 上使用 dropout 的提升小于 1%,

(2)增加訓練輪數,并將使用暫退法和不使用暫退法時獲得的結果進行比較,

此處增大訓練輪數到 20 輪,當每層中間有 Dropout 層時,代碼如下:

num_epochs = 20
net = nn.Sequential(nn.Flatten(),
                   nn.Linear(784, 256),
                   nn.ReLU(),
                   nn.Dropout(dropout1),
                   nn.Linear(256, 256),
                   nn.ReLU(),
                   nn.Dropout(dropout2),
                   nn.Linear(256, 10))
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
    
net.apply(init_weights)

trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

image-20230419142625353

去掉中間的 Dropout 層時,代碼如下:

num_epochs = 20
net = nn.Sequential(nn.Flatten(),
                   nn.Linear(784, 256),
                   nn.ReLU(),
                   nn.Linear(256, 256),
                   nn.ReLU(),
                   nn.Linear(256, 10))
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
    
net.apply(init_weights)

trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

連續測了幾次都發現,影像出現了突然的 loss/acc 爆炸,剛開始以為是 Kaggle 服務器的問題,再測了幾把發現是真的會出這個問題,

image-20230419143126036

除去突然的 loss/acc 爆炸外,對比兩張圖,可以發現去掉 Dropout 層的時候擬合得更快,理論上講,帶有 dropout 的應當減少更多的泛化誤差,但是這幾張圖片中可能確實有一點,但是實在看不太出來,

(3)當使用或不使用暫退法時,每個隱藏層中激活值的方差是多少?繪制一個曲線圖,以展示這兩個模型的每個隱藏層中激活值的方差是如何隨時間變化的,

num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256

dropout1, dropout2 = 0.2, 0.5

class Net(nn.Module):
    def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2, is_training=True):
        super(Net, self).__init__()
        self.num_inputs = num_inputs
        self.training = is_training
        self.lin1 = nn.Linear(num_inputs, num_hiddens1)
        self.lin2 = nn.Linear(num_hiddens1, num_hiddens2)
        self.lin3 = nn.Linear(num_hiddens2, num_outputs)
        self.relu = nn.ReLU()
    
    def forward(self, X):
        H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs))))
        if self.training == True:
            H1 = dropout_layer(H1, dropout1)
        d1 = torch.var(H1)
        H2 = self.relu(self.lin2(H1))
        if self.training == True:
            H2 = dropout_layer(H2, dropout2)
        d2 = torch.var(H2)
        out = self.lin3(H2)
        return out, d1, d2

def train_epoch_with_variance(net, train_iter, loss, updater):
    """The training loop defined in Chapter 3.
    Defined in :numref:`sec_softmax_scratch`"""
    # Set the model to training mode
    if isinstance(net, torch.nn.Module):
        net.train()
    # Sum of training loss, sum of training accuracy, no. of examples
    metric = d2l.Accumulator(3)
    for X, y in train_iter:
        # Compute gradients and update parameters
        y_hat, v1, v2 = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # Using PyTorch in-built optimizer & loss criterion
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # Using custom built optimizer & loss criterion
            l.sum().backward()
            updater(X.shape[0])
        metric.add(v1 * (y.numel() - 1), v2 * (y.numel() - 1), y.numel())
    # Return training loss and training accuracy
    return metric[0] / metric[2], metric[1] / metric[2]

def train_with_varience(net, train_iter, test_iter, loss, num_epochs, updater):
    """Train a model (defined in Chapter 3).
    Defined in :numref:`sec_softmax_scratch`"""
    animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
                        legend=['variance 1', 'variance 2'])
    for epoch in range(num_epochs):
        train_metrics = train_epoch_with_variance(net, train_iter, loss, updater)
        print(train_metrics)
        animator.add(epoch + 1, train_metrics)
    print(train_metrics)

num_epochs, lr, batch_size = 20, 0.5, 256
loss = nn.CrossEntropyLoss(reduction = 'none')
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
trainer = torch.optim.SGD(net.parameters(), lr=lr)

net_with_dropout = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2, True)
net_with_dropout.apply(init_weights)
train_with_varience(net_with_dropout, train_iter, test_iter, loss, num_epochs, trainer)

net_without_dropout = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2, False)
net_without_dropout.apply(init_weights)
train_with_varience(net_without_dropout, train_iter, test_iter, loss, num_epochs, trainer)

image-20230419153212076image-20230419153222486

上圖(左圖)為使用暫退法的第一層方差與第二層方差,下圖(右圖)為不使用暫退法的第一層與第二層方差,可以發現加入 Dropout 層后,方差明顯變小,

(4)為什么在測驗時通常不使用暫退法?

盡量將全部函式都應用上來,暫退法本質上是給模型加噪聲,測驗的時候反而會影響效果,

(5)以本節中的模型為例,比較使用暫退法和權重衰減的效果,如果同時使用暫退法和權重衰減,會發生什么情況?結果是累加的嗎?收益是否減少(或者說更糟)?它們互相抵消了嗎?

wd = 0

trainer = torch.optim.SGD([
    {"params": net[1].weight, "weight_decay": wd},
    {"params": net[4].weight, "weight_decay": wd},
    {"params": net[7].weight, "weight_decay": wd},
    {"params": net[1].bias},
    {"params": net[4].bias},
    {"params": net[7].bias},
], lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

image-20230419162942632image-20230419164052915image-20230419163350478image-20230419163823029

圖 1 是 wd=0 的情況下,即不采用權重衰減僅采用暫退法的曲線,圖 2 是 \(10^{-4}\) 的情況下的曲線,圖 3 為 \(10^{-3}\) 的情況下的曲線,可以發現準確率有所降低,訓練損失函式也沒有降下去,最后還有奇怪的凸起,圖 4 為 \(10^{-2}\) 情況下的曲線,可以發現完全沒擬合,再大的情況甚至曲線都不會出現在這張圖上了,

由上面幾張圖可以簡單得出結論,超過 \(10^{-3}\) 的情況幾乎無法選擇,\(10^{-4}\) 目前來看情況最好,但是和 \(0\) 的情況比起來,收斂速度更慢,除此之外難以說明哪個更優,

(6)如果我們將暫退法應用到權重矩陣的各個權重,而不是激活值,會發生什么?

只使用暫退法修改前兩個線性層的權重,

dropout1, dropout2 = 0.2, 0.5

class Net(nn.Module):
    def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2, is_training=True):
        super(Net, self).__init__()
        self.num_inputs = num_inputs
        self.training = is_training
        self.lin1 = nn.Linear(num_inputs, num_hiddens1)
        self.lin2 = nn.Linear(num_hiddens1, num_hiddens2)
        self.lin3 = nn.Linear(num_hiddens2, num_outputs)
        self.relu = nn.ReLU()
    
    def forward(self, X):
        W1 = dropout_layer(self.lin1.weight, dropout1)
        W2 = dropout_layer(self.lin2.weight, dropout2)
        H1 = self.relu(torch.matmul(X.reshape((-1, self.num_inputs)), W1.t()) + self.lin1.bias)
        H2 = self.relu(torch.matmul(H1, W2.t()) + self.lin2.bias)
        out = self.lin3(H2)
        return out
    
net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)

num_epochs, lr, batch_size = 20, 0.5, 256
loss = nn.CrossEntropyLoss(reduction = 'none')
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

image-20230419170915750

訓練速度更慢了,且幾乎顯然不如對激活值應用暫退法,

(7)請提出另一個在每一層注入隨機噪聲的技術,該技術優于標準暫退法,

評論區有老哥說對每一層輸出的激活值加了個高斯噪聲(這不就是書中提的 Bishop 的做法嗎),但是由于原本的損失以及精度太好了,所以啥都看不出來,我懶得做,估計我也做不出來,

4.7 前向傳播、反向傳播和計算圖

中間作為示例的式子就跳過吧,就附個圖好了,剩下的部分太長了=,=

image-20230419212355288

前向傳播(forward propagation或forward pass)指的是:按順序(從輸入層到輸出層)計算和存盤神經網路中每層的結果,

反向傳播(backward propagation或backpropagation)指的是計算神經網路引數梯度的方法,簡言之,該方法根據微積分中的鏈式規則,按相反的順序從輸出層到輸入層遍歷網路,該演算法存盤了計算某些引數梯度時所需的任何中間變數(偏導數),

對于前向傳播,我們沿著依賴的方向遍歷計算圖并計算其路徑上的所有變數,然后將這些用于反向傳播,其中計算順序與計算圖的相反,

因此,在訓練神經網路時,在初始化模型引數后,我們交替使用前向傳播和反向傳播,利用反向傳播給出的梯度來更新模型引數,注意,反向傳播重復利用前向傳播中存盤的中間值,以避免重復計算,帶來的影響之一是我們需要保留中間值,直到反向傳播完成,這也是訓練比單純的預測需要更多的記憶體(顯存)的原因之一,此外,這些中間值的大小與網路層的數量和批量的大小大致成正比,因此,使用更大的批量來訓練更深層次的網路更容易導致記憶體不足(out of memory)錯誤,

課后題

(1)假設一些標量函式 \(\boldsymbol{X}\) 的輸入 \(\boldsymbol{X}\)\(n \times m\) 矩陣,\(f\) 相對于 \(\boldsymbol{X}\) 的梯度的維數是多少?

顯然是 \(n \times m\) 的,可以理解為 \(f = x_{11} \oplus x_{12} \oplus \cdots \oplus x_{nm}\),那么 \(f\)\(\boldsymbol{X}\) 中的每一個元素都有偏導數,

(2)向本節中描述的模型的隱藏層添加偏置項(不需要在正則化項中包含偏置項)

畫計算圖——不畫(懶得拍照了)

推導反向傳播方程:

\[\frac{\partial J}{\partial \boldsymbol{b}^{(1)}} = \frac{\partial J}{\partial \boldsymbol{h}} \frac{\partial \boldsymbol{h}}{\partial \boldsymbol{b}^{(1)}} = \frac{\partial J}{\partial \boldsymbol{h}} \odot \phi'(\boldsymbol{z}+\boldsymbol{b}^{(i)}) \\ \frac{\partial J}{\partial \boldsymbol{b}^{(2)}} = \frac{\partial J}{\partial L} \frac{\partial L}{\partial \boldsymbol{b}^{(2)}} = \frac{\partial L}{\partial\boldsymbol{b}^{(2)}} \]

上面的推導可能有一些問題,希望您們如果和我推的不一樣可以在評論區討論一下,

(3)計算本節所描述的模型用于訓練和預測的記憶體空間,

首先假定所有的變數統一使用 float32,即一個浮點數 \(4\) 個位元組,其次,訓練時由于需要中間變數,因此所需空間是全部中間變數所占空間之和,除此之外還有梯度的存在,而預測時除了輸入 \(\boldsymbol{x}\)、輸出 \(y\)、目標函式 \(J\) 這三者(除引數以外)必須要存盤外,剩下的僅需要統計占最大空間的變數即可,

如下表所示(這里有些定義的空間大小您需要回去翻一下原書):

變數/引數 特征個數
\(\boldsymbol{x}\) \(d\)
\(\boldsymbol{z}\) \(h\)
\(\boldsymbol{h}\) \(h\)
\(\boldsymbol{o}\) \(q\)
\(y\) 1
\(L\) 1
\(s\) 1
\(J\) 1
\(\boldsymbol{W}^{(1)}\) \(h \times d\)
\(\boldsymbol{W}^{(2)}\) \(q \times h\)
\(\frac{\partial \boldsymbol{z}}{\partial \boldsymbol{x}}\) \(h \times d\)
\(\frac{\partial \boldsymbol{z}}{\partial \boldsymbol{W}^{(1)}}\) \(h \times d\)
\(\frac{\partial \boldsymbol{h}}{\partial \boldsymbol{z}}\) \(h\)
\(\frac{\partial \boldsymbol{o}}{\partial \boldsymbol{h}}\) \(q \times h\)
\(\frac{\partial \boldsymbol{o}}{\partial \boldsymbol{W}^{(2)}}\) \(q \times h\)
\(\frac{\partial L}{\partial \boldsymbol{o}}\) \(q\)
\(\frac{\partial L}{\partial y}\) 1
\(\frac{\partial s}{\partial \boldsymbol{W}^{(1)}}\) \(h \times d\)
\(\frac{\partial s}{\partial \boldsymbol{W}^{(2)}}\) \(q \times h\)
\(\frac{\partial J}{\partial s}\) 1
\(\frac{\partial J}{\partial y}\) 1

因此,訓練時,除梯度外,所占空間為 \(4 \times (d + 2h + q + 4 + h \times d + q \times h)\) 位元組,

而梯度如果不優化的話,足足占了 \(4 \times 3 \times (1 + h \times d + q \times h)\) 位元組,

因此共占了 \(4 \times (d + 2h + q + 7 + 4 \times h \times d + 4 \times q \times h)\) 位元組,

預測時所占空間為 \(4 \times \left(d + 2 + h \times d + q \times h + \max (1, h, q) \right)\) 位元組,

(4)假設想計算二階導數,計算圖會發生什么變化?預計計算需要多長時間?

在使用 autograd 計算一階導數時,讓 create_graph=True 這樣就可以對一階導再求導了,

顯而易得的,計算圖中的節點數量會增加,因為梯度也變成節點進入計算圖中,假設上一次節點數為 \(n\),那么二階導的計算圖中的節點個數變為 \(2n+1\),預計計算時間會變二倍,

(5)假設計算圖對當前 GPU 來說太大了,(5.a)請嘗試把它劃分到多個 GPU 上,(5.b)這與小批量訓練相比,有哪些優點和缺點?

可以參考知乎PyTorch 81. 模型并行 Model Parallel 將模型并行到多個 GPU 的做法,

比如現在有一個包含2個 Linear layers 的模型,我們想在2塊 GPU 上 run 它,辦法可以是在每塊 GPU 上放置1個 Linear layer,并且把得到的中間結果在 GPU 之間移動,代碼可以是這樣子:

import torch
import torch.nn as nn
import torch.optim as optim


class ToyModel(nn.Module):
   def __init__(self):
       super(ToyModel, self).__init__()
       self.net1 = torch.nn.Linear(10, 10).to('cuda:0')
       self.relu = torch.nn.ReLU()
       self.net2 = torch.nn.Linear(10, 5).to('cuda:1')

   def forward(self, x):
       x = self.relu(self.net1(x.to('cuda:0')))
       return self.net2(x.to('cuda:1'))

注意,上述 ToyModel 看起來與在單個 GPU 上的實作方式非常相似,除了四個 to(device) 的呼叫,將 Linear layer 和張量放在適當的設備上,這是該模型中唯一需要改變的地方,backward() 和 torch.optim 將自動處理梯度問題,就像模型是在一個 GPU 上一樣,

你只需要確保在呼叫損失函式時,標簽和輸出是在同一個設備上,像下面這樣:

model = ToyModel()
loss_fn = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.001)

optimizer.zero_grad()
outputs = model(torch.randn(20, 10))
labels = torch.randn(20, 5).to('cuda:1')
loss_fn(outputs, labels).backward()
optimizer.step()

這里應該把標簽 labels 放在 \(1\) 號 GPU 上面,因為模型的輸出就在 \(1\) 號 GPU 上,

這樣做可以增大資料的規模,本質上和小批量訓練一樣,但是這樣可以增大批量的大小,而訓練速度略有降低,這是由于資料在不同的 GPU 上復制而導致的,

4.8 數值穩定性和模型初始化

初始化方案的選擇在神經網路學習中起著舉足輕重的作用,它對保持數值穩定性至關重要,此外,這些初始化方案的選擇可以與非線性激活函式的選擇有趣的結合在一起,我們選擇哪個函式以及如何初始化引數可以決定優化演算法收斂的速度有多快,糟糕選擇可能會導致我們在訓練時遇到梯度爆炸或梯度消失,

4.8.1 梯度消失和梯度爆炸

在鏈式法則中,梯度容易受到數值下溢問題的影響,當將太多的概率乘在一起時,這些問題經常會出現,在處理概率時,一個常見的技巧是切換到對數空間,即將數值表示的壓力從尾數轉移到指數,不幸的是,上面的問題更為嚴重:引數矩陣可能具有各種各樣的特征值,他們可能很小,也可能很大;他們的乘積可能非常大,也可能非常小,

不穩定梯度帶來的風險不止在于數值表示,也威脅到我們優化演算法的穩定性,我們可能面臨一些問題,要么是梯度爆炸(gradient exploding)問題:引數更新過大,破壞了模型的穩定收斂;要么是梯度消失(gradient vanishing)問題:引數更新過小,在每次更新時幾乎不會移動,導致模型無法學習,

1. 梯度消失

sigmoid 函式以前很流行,由于早期的人工神經網路受到生物神經網路的啟發,神經元要么完全激活要么完全不激活(就像生物神經元)的想法很有吸引力,

當sigmoid函式的輸入很大或是很小時,它的梯度都會消失,此外,當反向傳播通過許多層時,除非我們在剛剛好的地方,這些地方sigmoid函式的輸入接近于零,否則整個乘積的梯度可能會消失,當我們的網路有很多層時,除非我們很小心,否則在某一層可能會切斷梯度,事實上,這個問題曾經困擾著深度網路的訓練,因此,更穩定的ReLU系列函式已經成為從業者的默認選擇,

2. 梯度爆炸

多層神經網路通常存在像懸崖一樣斜率較大的區域,這是由于幾個較大的權重相乘導致的,書上舉的例子是一百個服從 \(N(0,1)\)\(4\times 4\) 矩陣相乘,當遇到斜率較大的懸崖結構時,梯度更新會很大程度地改變引數值,通常會完全跳過這類懸崖結構,使得引數彈射得非常遠,可能導致之前做了無用功,

3. 打破對稱性

神經網路設計中的另一個問題是其引數化所固有的對稱性,書中舉了一個一層兩個隱藏單元的多層感知機,其中兩個隱藏單元在前向傳播程序中采用相同的輸入和引數,這會導致回傳時有相同的梯度,進而導致無論怎么迭代引數都對稱,雖然小批量隨機梯度下降不會打破這種對稱性,但暫退法正則化可以,

4.8.2 引數初始化

解決(或至少減輕)上述問題的一種方法是進行引數初始化, 優化期間的注意和適當的正則化也可以進一步提高穩定性,

1. 默認初始化

如果我們不指定初始化方法, 框架將使用默認的隨機初始化方法,例如在 PyTorch 中,線性層的權重和偏置被初始化為 \(U(-\sqrt{k}, \sqrt{k})\),其中 \(k=\frac{1}{\text{in_features}}\)

2. Xavier 初始化

原書假設沒有非線性的全連接層輸出 \(o_i\) 的分布,對于該層 \(n_{\text{in}}\) 輸入 \(x_j\) 以及相關權重 \(w_{ij}\),輸出由下式給出:

\[o_i = \sum_{j=1}^{n_{\text{in}}} w_{ij} x_j \]

不妨假設 \(\mathbb{E}[w_{ij}] = 0, \text{Var}[w_{ij}] = \sigma^2\),而 \(\mathbb{E}[x_j] = 0, \text{Var}[x_j] = \gamma^2\),且這些都互相獨立,

則可以計算出 \(o_i\) 的期望和方差:

\[\begin{align} \mathbb{E}[o_i] &= \sum_{j=1}^{n_{\text{in}}} \mathbb{E}[w_{ij} x_j] \\ &= \sum_{j=1}^{n_{\text{in}}} \mathbb{E}[w_{ij}] \mathbb{E}[x_j] \\ &= 0 \\ \text{Var}[o_i] &= \mathbb{E}[o_i^2] - (\mathbb{E}[o_i])^2 \\ &= \mathbb{E}[w_{ij}^2 x_j^2] - 0 \\ &= \mathbb{E}[w_{ij}^2] \mathbb{E}[x_j^2] \\ &= n_{\text{in}} \sigma^2 \gamma^2 \end{align} \]

如果要使方差不變的方法是設定 \(n_{\text{in}} \sigma^2=1\),然而,反向傳播的程序中,也有類似的問題,如果要使反向傳播梯度的方差不變,需要設定 \(n_{\text{out}} \sigma^2=1\),但是由于一般來說全連接層維度都會有所變化,即 \(n_{\text{in}} \not = n_{\text{out}}\),因此幾乎無法同時滿足上述兩個條件,

于是 Xavier 初始化提出,要滿足這個條件:

\[\frac{1}{2}(n_{\text{in}} + n_{\text{out}}) \sigma^2=1 \iff \sigma = \sqrt{\frac{2}{n_{\text{in}} + n_{\text{out}}}} \]

通常,Xavier 初始化從均值為 \(0\),方差 \(\sigma^2 = \frac{2}{n_{\text{in}} + n_{\text{out}}}\) 的高斯分布中抽樣權重,也可以改成從均勻分布中抽取權重,那么 Xavier 初始化的均勻分布為:

\[U \left(-\sqrt{\frac{6}{n_{\text{in}} + n_{\text{out}}}}, \sqrt{\frac{6}{n_{\text{in}} + n_{\text{out}}}} \right) \]

盡管“不存在非線性”的假設在神經網路中很難實作,但是 Xavier 初始化方法在實踐中比較有效,

練習題

(1)除了多層感知機的排列對稱性之外,還能設計出其他神經網路可能會表現出對稱性且需要被打破的情況嗎?

顯然存在,如 CNN 等,

(2)我們是否可以將線性回歸或 softmax 回歸中的所有權重引數初始化為相同的值?

可以,但是并不推薦,原因在于最開始這一步會顯著體現出對稱性,如果輸入資料也具有對稱性,那么反向傳播后引數對稱性幾乎無法改變,

(3)在相關資料中查找兩個矩陣乘積特征值的決議解,這對確保梯度條件合適有什么啟示?

最大特征值與最小特征值可以決定矩陣的條件數,如果相差太大,那么條件數就也會太大,稍有擾動就會出現巨大差別,

(4)如果我們知道某些項是發散的,我們能在事后修正嗎?

參考 LARS 的原論文,可以對每一層使用不同的學習率來修正這一問題,

4.9 環境和分布偏移

機器學習的許多應用中都存在類似的問題: 通過將基于模型的決策引入環境,可能會導致破壞模型的后果,

4.9.1 分布偏移的型別

在一個經典的情景中,假設訓練資料是從某個分布 \(p_S(\boldsymbol{x}, y)\) 中采樣的, 但是測驗資料將包含從不同分布 \(p_T(\boldsymbol{x}, y)\) 中抽取的未標記樣本, 一個清醒的現實是:如果沒有任何關于 \(p_S(\boldsymbol{x})\) 和 $$p_T(\boldsymbol{x}, y)$$ 之間相互關系的假設, 學習到一個分類器是不可能的,

幸運的是,在對未來我們的資料可能發生變化的一些限制性假設下,有些演算法可以檢測這種偏移,甚至可以動態調整,以提高原始分類器的精度,

1. 協變數偏移

協變數偏移是指輸入的分布改變了,但是標簽函式(即條件分布 \(P(y|\boldsymbol{x})\))沒有改變,之所以命名為協變數偏移是因為協變數(特征)分布的變化,比如原書中給的例子:

訓練時使用下列寫實貓狗影像:

image-20230420194829696

測驗時使用下列卡通貓狗影像:

image-20230420194856351

訓練集由真實照片組成,而測驗集只包含卡通圖片,假設在一個與測驗集的特征有著本質不同的資料集上進行訓練,如果沒有方法來適應新的領域,可能會有麻煩,

2. 標簽偏移

標簽偏移(label shift)描述了與協變數偏移相反的問題,這里我們假設標簽邊緣概率 \(P(y)\) 可以改變, 但是類別條件分布 \(P(\boldsymbol{x} | y)\) 在不同的領域之間保持不變,當我們認為 \(y\) 導致 \(\boldsymbol{x}\) 時,標簽偏移是一個合理的假設,在另一些情況下,標簽偏移和協變數偏移假設可以同時成立,例如,當標簽是確定的,即使 \(y\) 導致 \(\boldsymbol{x}\),協變數偏移假設也會得到滿足,有趣的是,在這些情況下,使用基于標簽偏移假設的方法通常是有利的,這是因為這些方法傾向于包含看起來像標簽(通常是低維)的物件,而不是像輸入(通常是高維的)物件,

3. 概念偏移

概念偏移(concept shift): 當標簽的定義發生變化時,就會出現這種問題,類別會隨著不同時間、不同地理位置的用法而發生變化,例如地瓜這個概念南北方有巨大差異,這是地理位置的差異,再比如中國從文言文到白話文的程序中所有的詞伴隨著時間流逝意思已經有巨大變化,因此,最好可以利用在時間或空間上逐漸發生偏移的知識,

4.9.3 經驗風險與實際風險

1. 經驗風險與實際風險

訓練資料 \(\{(\boldsymbol{x}_1, y_1), \dots, (\boldsymbol{x}_n, y_n)\}\) 的特征和相關的標簽經過迭代,在每一個小批量之后更新模型 \(f\) 的引數,為了簡單起見,我們不考慮正則化,因此極大地降低了訓練損失:

\[\min_f \frac{1}{n} \sum_{i=1}^n l(f(\boldsymbol{x}_i), y_i) \]

其中 \(l\) 是損失函式,用來度量:給定標簽 \(y_i\),預測 \(f(\boldsymbol{x}_i)\) 的“糟糕程度”,統計學家將上面式子為經驗風險經驗風險(empirical risk)是為了近似真實風險(true risk),整個訓練資料上的平均損失,即從其真實分布 \(P(\boldsymbol{x}, y)\) 中抽取的所有資料的總體損失的期望值:

\[\mathbb{E}_{P(\boldsymbol{x}, y)} [ l(f(\boldsymbol{x}), y) ] = \iint l(f(\boldsymbol{x}), y) P(\boldsymbol{x}, y) \mathrm{d} \boldsymbol{x} \mathrm{d}y \]

然而在實踐中,我們通常無法獲得總體資料,因此可以最小化經驗風險來近似最小化真實風險,

2. 協變數偏移糾正

假設對于帶標簽的資料 \((\boldsymbol{x}_i, y_i)\),我們要評估 \(P(y|\boldsymbol{x})\),然而觀測值 \(\boldsymbol{x}_i\) 是從某些源分布 \(q(\boldsymbol{x})\) 中得出的,而不是從目標分布 \(p(\boldsymbol{x})\) 中得出的,幸運的是,依賴性假設意味著條件分布保持不變,即:\(p(y|\boldsymbol{x}) = q(y|\boldsymbol{x})\),如果源分布 \(q(\boldsymbol{x})\) 是“錯誤的”,我們可以通過在真實風險的計算中,使用以下簡單的恒等式來進行糾正:

\[\iint l(f(\boldsymbol{x}), y) p(y|\boldsymbol{x})p(\boldsymbol{x}) \mathrm{d}\boldsymbol{x} \mathrm{d}y = \iint l(f(\boldsymbol{x}), y) q(y|\boldsymbol{x}) q(\boldsymbol{x}) \frac{p(\boldsymbol{x})}{q(\boldsymbol{x})} \mathrm{d}\boldsymbol{x} \mathrm{d}y \]

換句話說,我們需要根據資料來自正確分布與來自錯誤分布的概率之比,來重新衡量每個資料樣本的權重:

\[\beta_i \overset{\text{def}}{=}\frac{p(\boldsymbol{x}_i)}{q(\boldsymbol{x}_i)} \]

將權重 \(\beta_i\) 代入到每個資料樣本 \((\boldsymbol{x}_i, y_i)\) 中,我們可以使用”加權經驗風險最小化“來訓練模型:

\[\min_f \frac{1}{n} \sum_{i=1}^n \beta_i l(f(\boldsymbol{x}_i), y_i) \]

由于不知道這個比率 \(\beta_i\),我們需要估計它,有許多方法都可以用,包括一些花哨的算子理論方法,試圖直接使用最小范數或最大熵原理重新校準期望算子,對于任意一種這樣的方法,我們都需要從兩個分布中抽取樣本:“真實”的分布 \(p\),通過訪問測驗資料獲取;訓練集 \(q\),通過人工合成的很容易獲得,請注意,我們只需要特征 \(\boldsymbol{x} \sim p(\boldsymbol{x})\),不需要訪問標簽 \(y \sim p(y)\)

在這種情況下,有一種非常有效的方法可以得到幾乎與原始方法一樣好的結果:邏輯斯蒂回歸(logistic regression),這是用于二元分類的 softmax 回歸的一個特例,綜上所述,我們學習了一個分類器來區分從 \(p(\boldsymbol{x})\) 抽取的資料和從 \(q(\boldsymbol{x})\) 抽取的資料,如果無法區分這兩個分布,則意味著相關的樣本可能來自這兩個分布中的任何一個,此外,任何可以很好區分的樣本都應該相應地顯著增加或減少權重,

為了簡單起見,假設我們分別從 \(p(\boldsymbol{x})\)\(q(\boldsymbol{x})\) 兩個分布中抽取相同數量的樣本,現在用 \(z\) 標簽表示:從 \(p\) 抽取的資料為 \(1\),從 \(q\) 抽取的資料為 \(?1\),然后,混合資料集中的概率由下式給出:

\[P(z=1|\boldsymbol{x}) = \frac{p(\boldsymbol{x})}{p(\boldsymbol{x}) + q(\boldsymbol{x})} \implies \frac{P(z=1|\boldsymbol{x})}{P(z=-1|\boldsymbol{x})} = \frac{p(\boldsymbol{x})}{q(\boldsymbol{x})} \]

因此,如果使用 logistic 回歸方法,其中 \(P(z=1|\boldsymbol{x}) = \frac{1}{1 + \exp(-h(\boldsymbol{x}))}\)(?是一個引數化函式),則很自然有:

\[\beta_i = \frac{p(\boldsymbol{x})}{q(\boldsymbol{x})} = \frac{P(z=1|\boldsymbol{x})}{P(z=-1|\boldsymbol{x})} = \frac{\frac{1}{1 + \exp(-h(\boldsymbol{x}_i))}}{1 - \frac{1}{1 + \exp(-h(\boldsymbol{x}_i))}} = \exp (h(\boldsymbol{x}_i)) \]

在得到這個式子之后,接下來就只剩下兩個問題了,第一個問題是關于區分來自兩個分布的資料;第二個問題是關于加權經驗風險的最小化問題,問題二里,要對其中的項加權 \(\beta_i\)

現在,我們來看一下完整的協變數偏移糾正演算法,假設我們有一個訓練集 \(\{(\boldsymbol{x}_1, y_1), \dots, (\boldsymbol{x}_n, y_n)\}\) 和一個未標記的測驗集 \(\{\boldsymbol{u}_1, \dots, \boldsymbol{u}_m \}\),對于協變數偏移,我們假設 \(\boldsymbol{x}_i (1 \le i \le n)\) 來自某個源分布,\(\boldsymbol{u}_i\) 來自目標分布,以下是糾正協變數偏移的典型演算法:

  1. 生成一個二元分類訓練集:\(\{(\boldsymbol{x}_1, -1), \dots, (\boldsymbol{x}_n, -1), (\boldsymbol{u}_1, 1), \dots, (\boldsymbol{u}_m, 1)\}\)
  2. 用邏輯斯蒂回歸訓練二元分類器得到函式 \(h\)
  3. 使用 \(\beta_i = \exp(h(\boldsymbol{x}_i))\) 或更好的 \(\beta_i = \min (\exp(h(\boldsymbol{x}_i)), c)\)\(c\) 為常量)對訓練資料進行加權,
  4. 使用權重 \(\beta_i\) 進行經驗風險最小化中 \(\{(\boldsymbol{x}_1, y_1), \dots, (\boldsymbol{x}_n, y_n)\}\) 的訓練,

請注意,上述演算法依賴于一個重要的假設:需要目標分布(例如,測驗分布)中的每個資料樣本在訓練時出現的概率非零,如果我們找到 \(p(\boldsymbol{x}) > 0\)\(q(\boldsymbol{x})=0\) 的點,那么相應的重要性權重會是無窮大,

3. 標簽偏移糾正

原書這段寫的非常奇怪,很難看懂,寫一點一家之言,

假設我們處理的是 \(k\) 個類別的分類任務,使用與上文中相同符號,\(q\)\(p\) 中分別是源分布(例如訓練時的分布)和目標分布(例如測驗時的分布),假設標簽的分布隨時間變化:\(q(y) \not = p(y)\),但類別條件分布保持不變:\(q(\boldsymbol{x}|y)=p(\boldsymbol{x}|y)\),如果源分布 \(q(y)\) 是“錯誤的”,我們可以根據之前定義的真實風險中的恒等式進行更正:

\[\iint l(f(\boldsymbol{x}), y) p(\boldsymbol{x} | y) p(y) \mathrm{d}\boldsymbol{x} \mathrm{d} y = \iint l(f(\boldsymbol{x}), y) q(\boldsymbol{x}|y) q(y) \frac{p(y)}{q(y)} \mathrm{d} \boldsymbol{x} \mathrm{d}y \]

這里,重要性權重將對應于標簽似然比率:

\[\beta_i \overset{\text{def}}{=} \frac{p(y_i)}{q(y_i)} \]

標簽偏移的一個好處是,如果我們在源分布上有一個相當好的模型,那么我們可以得到對這些權重的一致估計,而不需要處理周邊的其他維度,在深度學習中,輸入往往是高維物件(如影像),而標簽通常是低維(如類別),因此處理標簽偏移會容易一些,

為了估計目標標簽分布 \(p(y)\),我們首先采用性能相當好的現成的分類器(通常基于訓練資料,即 \(q\) 對應的資料進行訓練),并使用驗證集(也來自訓練分布)計算其混淆矩陣,混淆矩陣 \(\boldsymbol{C}\) 是一個 \(k \times k\) 矩陣,其中每列對應于標簽類別,每行對應于模型的預測類別,每個單元格的值 \(c_{ij}\) 是驗證集中真實標簽為 \(j\),而模型預測為 \(i\) 的樣本數量所占的比例,這個矩陣可以理解為在源分布上當真實標簽為 \(j\) 發生時,模型預測為 \(i\) 的條件概率 \(P_q(i|j)\),由于標簽偏移僅僅是標簽的邊緣概率 \(P(y)\) 改變,因此這個式子在目標分布上的條件概率 \(P_p(i | j)\) 與在源分布上的條件概率 \(P_q(i|j)\) 相等,

現在,我們不能直接計算目標資料上的混淆矩陣,因為我們無法看到真實環境下的樣本的標簽,除非我們再搭建一個復雜的實時標注流程,然而,我們所能做的是將所有模型在測驗時的預測取平均數,得到平均模型輸出 \(\mu(\hat{\boldsymbol{y}}) \in \mathbb{R}^k\),其中第 \(i\) 個元素 \(\mu (\hat{y}_i)\) 是我們模型預測測驗集中 \(i\) 的總預測分數,也可以理解為在目標分布下模型預測為 \(i\) 類別的概率之和,

結果表明,如果我們的分類器一開始就相當準確,并且目標資料只包含我們以前見過的類別,以及如果標簽偏移假設成立(這里最強的假設),我們就可以通過求解一個簡單的線性系統來估計測驗集的標簽分布:

\[\boldsymbol{C} p(\boldsymbol{y}) = \mu(\hat{\boldsymbol{y}}) \]

因為作為一個估計,\(\sum_{j=1}^k c_{ij} p(y_j) = \mu (\hat{y}_i)\) 對所有 \(1 \le i \le k\) 成立,其中 \(p(y_j)\)\(k\) 維標簽分布向量 \(p(\boldsymbol{y})\) 的第 \(j\) 個元素,如果我們的分類器一開始就足夠精確,那么混淆矩陣 \(\boldsymbol{C}\) 的對角線元素將會比較大,因此是可逆的, 進而我們可以得到一個解 \(p(\boldsymbol{y}) = \boldsymbol{C}^{-1} \mu(\hat{\boldsymbol{y}})\)

這個式子原書的跳步非常嚴重,所以展開了寫一下,

\[\begin{align} \sum_{j=1}^k c_{ij} p(y_j) &= \sum_{j=1}^k P_q(\text{model predict}=i|\text{true label}=j) P_p(\text{true label}=j) \\ &= \sum_{j=1}^k P_p(\text{model predict}=i|\text{true label}=j) P_p(\text{true label}=j) \\ &= \sum_{j=1}^k P_p(\text{model predict}=i,\text{true label}=j) \\ &= P_p(\text{model predict}=i) = \mu (\hat{y}_i) \end{align} \]

因此,上式成立,

因為我們觀測源資料上的標簽,所以很容易估計分布 \(q(y)\),那么對于標簽為 \(y_i\) 的任何訓練樣本 \(i\),我們可以使用我們估計的 \(p(y_i) / q(y_i)\) 比率來計算權重 \(\beta_i\),并將其代入加權經驗風險最小化的式子中,

4. 概念偏移糾正

概念偏移很難用原則性的方式解決,除了從零開始收集新標簽和訓練,別無妙方,幸運的是,在實踐中極端的偏移是罕見的,通常情況下,概念的變化總是緩慢的,在這種情況下,我們可以使用與訓練網路相同的方法,使其適應資料的變化,換言之,我們使用新資料更新現有的網路權重,而不是從頭開始訓練,

4.9.4 學習問題的分類法

1. 批量學習

批量學習(batch learning)中,我們可以訪問一組訓練特征和標簽 \(\{ (\boldsymbol{x}_1, y_1), \dots, (\boldsymbol{x}_n, y_n) \}\),我們使用這些特性和標簽訓練 \(f(\boldsymbol{x})\),然后,我們部署此模型來對來自同一分布的新資料 \((\boldsymbol{x},y)\) 進行評分,

2. 在線學習

除了“批量”地學習,我們還可以單個“在線”學習資料 \((\boldsymbol{x}_i, y_i)\),更具體地說,我們首先觀測到 \(\boldsymbol{x}_i\),然后我們得出一個估計值 \(f(\boldsymbol{x}_i)\),只有當我們做到這一點后,我們才觀測到 \(y_i\),然后根據我們的決定,我們會得到獎勵或損失,

在線學習(online learning)中,我們有以下的回圈,在這個回圈中,給定新的觀測結果,我們會不斷地改進我們的模型,

\[\text{模型 } f_t \rightarrow \text{資料 } \boldsymbol{x}_t \rightarrow \text{估計 } f_t(\boldsymbol{x}_t) \rightarrow \text{觀測 } y_t \rightarrow \text{損失 } l(y_t, f_t(\boldsymbol{x}_t)) \rightarrow \text{模型 } f_{t+1} \]

3. 老虎只因

老虎只因(bandits)是上述問題的一個特例,雖然在大多數學習問題中,我們有一個連續引數化的函式 \(f\)(例如,一個深度網路),但在一個老虎只因問題中,我們只有有限數量的手臂可以拉動,也就是說,我們可以采取的行動是有限的,對于這個更簡單的問題,可以獲得更強的最優性理論保證,

4. 控制

在很多情況下,環境會記住我們所做的事,不一定是以一種對抗的方式,但它會記住,而且它的反應將取決于之前發生的事情,許多這樣的演算法形成了一個環境模型,在這個模型中,他們的行為使得他們的決策看起來不那么隨機,近年來,控制理論也被用于自動調整超引數,以獲得更好的解構和重建質量,提高生成文本的多樣性和生成影像的重建質量,

5. 強化學習

強化學習(reinforcement learning)強調如何基于環境而行動,以取得最大化的預期利益,

6. 考慮到環境

上述不同情況之間的一個關鍵區別是:在靜止環境中可能一直有效的相同策略,在環境能夠改變的情況下可能不會始終有效,環境變化的速度和方式在很大程度上決定了我們可以采用的演算法型別,

4.10 Kaggle:預測房價

使用 Kaggle 跑代碼的時候發現,to_csv() 函式會出問題,我也改不動,因此本章派生實驗跳過,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/551219.html

標籤:其他

上一篇:CVE-2016-3088漏洞復現

下一篇:返回列表

標籤雲
其他(158125) Python(38106) JavaScript(25391) Java(18001) C(15217) 區塊鏈(8260) C#(7972) AI(7469) 爪哇(7425) MySQL(7144) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5870) 数组(5741) R(5409) Linux(5329) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4561) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2431) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1960) Web開發(1951) HtmlCss(1926) python-3.x(1918) 弹簧靴(1913) C++(1911) xml(1889) PostgreSQL(1874) .NETCore(1855) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 【動手學深度學習】第四章筆記:多層感知機、權重衰減、暫退法、數

    為了更好的閱讀體驗,請點擊這里 4.1 多層感知機 4.1.1 隱藏層 由于仿射變換中的線性是一個很強的假設,因此導致了線性模型可能會不適用。線性意味著單調假設:任何特征的增大都會導致模型輸出的增大或者模型輸出的減小。 但是違反單調性的例子比比皆是。除此之外,分類任務中,僅依托像素強度分類也很不合理 ......

    uj5u.com 2023-04-26 07:53:03 more
  • CVE-2016-3088漏洞復現

    1.背景介紹。 ActiveMQ的web控制臺分三個應用,admin、api和fileserver,其中admin是管理員頁面,api是介面,fileserver是儲存檔案的介面;admin和api都需要登錄后才能使用,fileserver無需登錄。 fileserver是一個RESTful API ......

    uj5u.com 2023-04-26 07:52:39 more
  • 2023年企業服務行業6大CRM客戶關系管理系統盤點

    本文首先分析了企業服務行業5大業務場景以及選型標準,盤點了目前適合企業服務行業的六大CRM系統服務商并進行了深入分析。希望幫到你 對于企業而言,今天的世界,唯一不變的就是變化,尤其是數字化時代,應對變化成為企業經營增長的必修課。當下,企業數字化轉型已經進入深水區,單一的產品和技術創新已經越來越難以滿 ......

    uj5u.com 2023-04-26 07:52:28 more
  • Java序列化和反序列化

    一、序列化和反序列化 1、含義 ? 序列化就是記憶體中的物件寫入到IO流中,保存的格式可以是二進制或者文本內容。反序列化就是IO流還原成物件。 2、用途 (1)傳輸網路物件 (2)保存Session 二、Java序列化演示 1、序列化 java.io.ObjectOutputStream代表物件輸出流 ......

    uj5u.com 2023-04-26 07:52:22 more
  • 在軟體測驗程序中如何有效的開展介面自動化測驗

    介面自動化測驗是指使用自動化測驗工具和腳本對軟體系統中的介面進行測驗的程序。其目的是在軟體開發程序中,通過對介面的自動化測驗來提高測驗效率和測驗質量,減少人工測驗的作業量和測驗成本,并且能夠快速發現和修復介面錯誤,確保軟體系統的穩定性和可靠性。介面自動化測驗可以有效地支持持續集成和持續交付,幫助團隊... ......

    uj5u.com 2023-04-26 07:52:17 more
  • 如何建設一個用于編譯 iOS App 的 macOS 云服務器集群?

    現代軟體開發一般會借助 CI/CD 來提升代碼質量、加快發版速度、自動化重復的事情,iOS App 只能在 mac 機器上編譯,CI/CD 工具因此需要有一個 macOS 云服務器集群來執行 iOS App 的編譯。今天就來談談如何建設 macOS 云服務器集群 ......

    uj5u.com 2023-04-26 07:52:08 more
  • 畢業5年的同學突然告訴我,他已經是年薪30W的自動化測驗工程師....

    作為一名程式員,都會對自己未來的職業發展而焦慮。一方面是因為IT作為知識密集型的行業,知識體系復雜且知識更新速度非常快,“一日不學就會落后”。 ?另外一方面,IT又是勞動密集型的行業,不僅業人員多,而且個人在平時的開發程序中有大量的重復勞動(如 CRUD),自己的能力沒有隨年齡的增加而增長。 這種情 ......

    uj5u.com 2023-04-26 07:51:58 more
  • 華為亮相KubeCon EU 2023 新云原生開源專案Kuasar推動“云上演進

    摘要:協力同行、擁抱開源,解放數字生產力,為社會和行業帶來更多價值。 在數字時代,如果說企業是一艘巨大的貨船,那么云原生則為企業的每一個業務、每一個應用提供了標準化的集裝箱,擺脫笨重的底層桎梏,打造新一代云作業系統,駕駛這輪“貨船”航向數字化的未來世界。 4月18日—21日,一年一度的云原生開源領域 ......

    uj5u.com 2023-04-26 07:51:49 more
  • F.binary_cross_entropy_with_logits函式與F.binary_cross_entro

    假設 1個影像樣本由神經網路處理后的輸出是 size 10×4 的tensor,隨機生成一個tensor, 使用Sigmoid對該tensor進行概率變換,tensor的每個數值的變換都是相互獨立的,下面得出預測的概率分布, 我們在這里隨機生成一個真值的概率分布y, 自己定義一下BCELoss,需要 ......

    uj5u.com 2023-04-26 07:51:35 more
  • 牛客小白月賽71

    A.貓貓與廣告 題目: 分析: 只需考慮c * d的矩陣豎著擺和橫著擺兩種情況。本題提示了考慮兩矩陣對應邊平行的情況,實際上可以證明倘若能斜著放,那么一定可以橫著放或豎著放,證明方式可已通過構造三角形來證明a* b的矩陣的長寬一定小于c * d矩陣的長寬。 code: #include <iostr ......

    uj5u.com 2023-04-26 07:51:11 more