主頁 >  其他 > 【動手學深度學習】第三章筆記:線性回歸、SoftMax 回歸、交叉熵損失

【動手學深度學習】第三章筆記:線性回歸、SoftMax 回歸、交叉熵損失

2023-04-10 08:00:29 其他

這章感覺沒什么需要特別記住的東西,感覺忘了回來翻一翻代碼就好,

3.1 線性回歸

3.1.1 線性回歸的基本元素

1. 線性模型

用符號標識的矩陣 \(\boldsymbol{X} \in \mathbb{R}^{n\times d}\) 可以很方便地參考整個資料集中的 \(n\) 個樣本,其中 \(\boldsymbol{X}\) 地每一行是一個樣本,每一列是一種特征,

對于特征集合 \(\boldsymbol{X}\),預測值 \(\hat{\boldsymbol{y}} \in \mathbb{R}^n\) 可以通過矩陣-向量乘法表示為

\[\hat{\boldsymbol{y}} = \boldsymbol{Xw} + b \]

然后求和的程序使用廣播機制,另外,即使確信特征與標簽的潛在關系是線性的,也會加入一個噪聲項以考慮觀測誤差帶來的影響,

2. 損失函式

這里采用的損失函式為平方誤差函式,當樣本 \(i\) 的預測值為 \(\hat{y}^{(i)}\),其相應的真實標簽為 \(y^{(i)}\) 時,平方誤差可以定義為:

\[l^{(i)}(\boldsymbol{w}, b) = \frac{1}{2} (\hat{y}^{(i)}-y^{(i)})^2 \]

這里的系數 \(\frac{1}{2}\) 的目的是為了求導后常數為 \(1\)

因此,整個資料集上的損失均值為:

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

最后在訓練模型時要找一組引數 \((\boldsymbol{w}^*, b^*)\) ,最小化總損失,即如:

\[\boldsymbol{w}^*, b^* = {\arg \min}_{\boldsymbol{w}, b} L(\boldsymbol{w}, b) \]

3. 決議解

這里原書寫的不是很清楚,具體合并大概是

\[\boldsymbol{X} \leftarrow \begin{bmatrix} x_{11} & x_{12} & \cdots & x_{1d} & 1\\ x_{21} & x_{22} & \cdots & x_{2d} & 1\\ \vdots & \vdots & \ddots & \vdots & \vdots \\ x_{n1} & x_{n2} & \cdots & x_{nd} & 1 \end{bmatrix} , ~~ \boldsymbol{w} \leftarrow \begin{bmatrix} w_1 \\ w_2 \\ \vdots \\ w_d \\ b \end{bmatrix} \]

是這樣合并的,然后問題就轉化為最小化 \(||\boldsymbol{y} - \boldsymbol{Xw}||^2\),然后令損失關于 \(\boldsymbol{w}\) 的導數設為 \(0\),那么有決議解:

\[\boldsymbol{w}^* = (\boldsymbol{X}^T\boldsymbol{X})^{-1} \boldsymbol{X}^T\boldsymbol{y} \]

當然了,一般的深度學習問題也沒有決議解能給你求出來(233),

4. 隨機梯度下降

在每次需要計算更新的時候隨機抽取一小批樣本,這種變體叫做小批量隨機梯度下降(minibatch stochastic gradient descent),

大致可以寫作如下公式:

\[(\boldsymbol{w},b) \leftarrow (\boldsymbol{w}, b) - \frac{\eta}{|B|} \sum_{i \in B} \partial_{(\boldsymbol{w}, b)} l^{(i)} (\boldsymbol{w}, b) \]

其中,\(|B|\) 是 batch size,\(\eta\) 是學習率,

5. 用模型進行預測

由于在統計學中,推斷(inference)更多地表示基于資料集估計引數,所以請盡量將給定特征的情況下估計目標的程序稱為預測

3.1.2 向量化加速

先定義一下 Timer 類,這個類可以丟進小本本里,

class Timer:
    def __init__(self):
        self.times = []
        self.start()
    
    def start(self):
        self.tik = time.time()
    
    def stop(self):
        self.times.append(time.time() - self.tik)
        return self.times[-1]
    
    def avg(self):
        return sum(self.times) / len(self.times)
    
    def sum(self):
        return sum(self.times)
    
    def cumsum(self):
        return np.array(self.times).cumsum().tolist()

然后用下面的 python 回圈加法和 tensor 的向量加法比較,可以發現,盡量使用 PyTorch 向量化后的 tensor 進行運算,不得不感慨一下 python 是真的慢啊,即使是 tensor 加法也還要比 C++ 慢(tensor 的基礎運算應該就是拿 C++ 實作的),這也側面證明向量加法在 CPU 環境下應該沒有涉及到應用多核,

n = 10000
a = torch.ones(n)
b = torch.ones(n)

c = torch.zeros(n)
timer = Timer()
for i in range(n):
    c[i] = a[i] + b[i]
f'{timer.stop():.5f} sec'
# '0.09908 sec'

timer.start()
d = a + b 
f'{timer.stop():.5f} sec'
'0.00035 sec'

3.1.3 正態分布與平方損失

正態分布概率密度函式如下:

\[p(x) = \frac{1}{\sqrt{2\pi \sigma^2}} \exp\left(-\frac{1}{2\sigma^2}(x-\mu)^2\right) \]

然后本書假設觀測中包含的噪聲服從正態分布,噪聲正態分布如:\(y = \boldsymbol{w}^T\boldsymbol{x} + b + \epsilon\),其中,\(\epsilon \sim N(0, \sigma^2)\)

通過給定 \(\boldsymbol{x}\) 觀測到特定的 \(y\) 的似然為:

\[P(y|\boldsymbol{x}) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp \left(- \frac{1}{2\sigma^2}(y-\boldsymbol{w}^T\boldsymbol{x}-b)^2\right) \]

那么引數 \(\boldsymbol{w}\) 和 b 的最優值是使整個資料集的似然最大的值:

\[p(\boldsymbol{y} | \boldsymbol{X}) = \prod_{i=1}^n p(y^{(i)} | \boldsymbol{x}^{(i)}) \]

等價于最小化負對數似然:

\[-\log P(\boldsymbol{y} |\boldsymbol{X}) = \sum_{i=1}^n \frac{1}{2} \log (2\pi \sigma^2) + \frac{1}{2\sigma^2}(y^{(i)} - \boldsymbol{w}^T\boldsymbol{x}^{(i)} - b)^2 \]

要讓上式最小,即讓最后一項最小,因此在有高斯噪聲的假設下,最小化均方誤差等價于對線性模型的極大似然估計,

3.2 線性回歸的從零開始實作

這節就不詳細寫了,有一些代碼里感覺有意思的點寫在這里好了,

  • 假如說有一個 numpy 陣列或者 PyTorch 的向量 \(\boldsymbol{x}\),那么可以用 y = x[torch.tensr([1, 2, 5])] 來獲得一個只包含 \(x_1, x_2, x_5\)\(\boldsymbol{y}\)

優化演算法代碼如下:

def sgd(params, lr, batch_size):
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

也就是說本質上是讓它在不計算梯度的情況下,更新 param,然后讓它的梯度更新成零,

訓練用的代碼為:

lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y)
        l.sum().backward()
        sgd([w, b], lr, batch_size)
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

這里有一大堆沒定義的東西在書中前文提到了,但是我這里僅提供訓練代碼僅供示意,

  • linreg(X, w, b) 表示用 \(\boldsymbol{X}, \boldsymbol{w}, b\) 計算 \(\hat{\boldsymbol{y}}\)
  • squared_loss(y_hat, y) 為均方損失
  • featureslabels 是資料,
  • data_iter 是一個生成器,負責迭代 batch_size 大小的資料,

練習中問了個很有趣的問題:

如果將權重初始化為零,會發生什么?演算法仍然有效嗎?

參考文章 談談神經網路權重為什么不能初始化為0,來回答這一問題,

  1. 如同書中只有一層線性層的時候:

    由于

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

    代入,有

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

    求導數,有

    \[\frac{\partial L}{\partial w_0} = \sum_{i=1}^n x_0^{(i)} (x_0^{(i)}w_0 + x_1^{(i)}w_1 + b - y^{(i)}) \\ \frac{\partial L}{\partial w_1} = \sum_{i=1}^n x_1^{(i)} (x_0^{(i)}w_0 + x_1^{(i)}w_1 + b - y^{(i)}) \\ \]

    那么在第一次求導的時候,得

    \[\frac{\partial L}{\partial w_0} = \sum_{i=1}^n x_0^{(i)} (x_0^{(i)}w_0 + x_1^{(i)}w_1 + b - y^{(i)}) = \sum_{i=1}^n x_0^{(i)} (b-y^{(i)}) \\ \frac{\partial L}{\partial w_1} = \sum_{i=1}^n x_1^{(i)} (x_0^{(i)}w_0 + x_1^{(i)}w_1 + b - y^{(i)}) = \sum_{i=1}^n x_1^{(i)} (b-y^{(i)}) \\ \]

    那么,導數不為 \(0\),就確實對于演算法沒有影響,

  2. 但是如果不是只有一層線性層的話:

    不妨假設此時有兩層線性層,第一層 \(\boldsymbol{W^{(0)} \in \mathbb{R}^{2 \times 2}}\),第二層 \(\boldsymbol{W}^{(1)} \in \mathbb{R}^2\),過第一個線性層后輸出的值就與輸入 \(\boldsymbol{X}\) 無關了,那么再過第二個線性層后得到的結果就僅與第一個線性層的偏置以及第二個線性層有關了,那么,第二個線性層的權重關于第一個線性層權重的 Jacobi 矩陣是什么樣子的呢?由于 \(y_{i} = (\sum_{j=1}^n w_{ij} x_j) + b\),因此 \(\frac{\mathrm{d}y_i}{\mathrm{d}x_j} = w_{ij} = 0\),所以該矩陣是 \(0\) 矩陣,因此無法更新第一個線性層,然后又由于過了第一個線性層就與輸入 \(\boldsymbol{X}\) 無關,所以權重不可以初始化為 \(0\)

3.3 線性回歸的簡潔實作

仍然不全抄,只寫一些有趣的代碼放在這里,

torch.utils.data.DataLoader 回傳的是一個可迭代物件(Iterable)而不是一個迭代器(Iterator),

可以用 iter() 函式構造 Python 迭代器,并使用 next() 函式從迭代器中獲取第一項,如下所示:

next(iter(torch.utils.data.DataLoader(dataset, batch_size, shuffle=is_train)))

然后用 net = nn.Sequential(nn.Linear(2, 1)) 得到模型,那么,除了新寫一個類初始化引數外,怎么初始化第一層的引數呢?通過 net[0] 選擇網路中第一層,然后使用 weight.databias.data 方法來訪問引數,

net[0].weighttorch.nn.parameter.Parameter 類的實體,這個類很有趣,一種被視為模塊引數的張量,Parameter 是 Tensor 的子類,當與 Module 類一起使用時具有非常特殊的屬性:當 Parameter 類被分配為 Module 類的屬性時,它們會自動添加到其引數串列中,并將出現在例如在 parameters() 迭代器中,但是分配一個 Tensor 就沒有這樣的效果,呼叫 net[0].weight 回傳:

net[0].weight
# Parameter containing:
# tensor([[-0.3418, -0.5904]], requires_grad=True)

呼叫 net[0].weight.data 回傳 tensor,這說明它們僅僅是張量:

net[0].weight.data
# tensor([[-0.3418, -0.5904]])

還可以使用替換方法 normal_fill_ 來重寫引數值,這兩個方法是在 torch.tensor() 里面的方法,

net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)

損失函式 nn.MSELoss() 及優化演算法 torch.optim.SGD(net.parameters(), lr=0.03),這里 net.parameters() 是個生成器,同時也是一個特殊的迭代器,輸出一下它:

loss = nn.MSELoss()
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
print(net.parameters())
# <generator object Module.parameters at 0x716c1cf61150>

下面是訓練代碼,這段代碼先把梯度清零再做反向傳播,證明把梯度清零不會把 Jacobi 矩陣之類的中間狀態清理掉,實踐中極其不推薦像書中這么寫,最好還是在求 loss 之前就清理梯度:

num_epochs = 3
for epoch in range(num_epochs):
    for X, y in data_iter:
        l = loss(net(X), y)
        trainer.zero_grad()
        l.backward()
        trainer.step()
    l = loss(net(features), labels)
    print(f'epoch {epoch + 1}, loss {l:f}')
"""
epoch 1, loss 0.000226
epoch 2, loss 0.000103
epoch 3, loss 0.000103
"""

3.4 softmax 回歸

希望在對硬性類別分類的同時使用軟性帶有概率的模型,

3.4.1 模型

本章介紹了表示分類資料的簡單方法:獨熱編碼(one-hot encoding),獨熱編碼是一個向量,它的分量和類別一樣多,類別對應的分量設定為 \(1\),其他所有分量設定為 \(0\)

本節的網絡架構仍為線性層,這里有和輸出一樣多的仿射函式,向量形式記作 \(\boldsymbol{o} = \boldsymbol{Wx} + \boldsymbol{b}\),具有 \(d\) 個輸入和 \(q\) 個輸出的全連接層,引數開銷為 \(O(dq)\),但是論文 Beyond Fully-Connected Layers with Quaternions: Parameterization of Hypercomplex Multiplications with \(\frac{1}{n}\) Parameters 提及可以將具有 \(d\) 個輸入和 \(q\) 個輸出的全連接層的成本減少到 \(O(dq/n)\),其中超引數 \(n\) 可以設定,以在實際應用中在引數節省和模型有效性之間進行平衡,(完全沒讀這論文說了啥233)

Softmax 函式可以表示為:

\[\hat{\boldsymbol{y}} = \text{softmax} (\boldsymbol{o}) \]

其中,

\[\hat{y_j} = \frac{\exp(o_j)}{\sum_k \exp(o_k)} \]

文中說,盡管 softmax 是一個非線性函式,但 softmax 回歸的輸出仍然由輸入特征的仿射變換決定,因此,softmax 回歸是一個線性模型(linear model),

3.4.2 損失函式

1. 對數似然

假設資料集 \(\{ \boldsymbol{X}, \boldsymbol{Y}\}\) 具有 \(n\) 個樣本,其中索引 \(i\) 的樣本由特征向量 \(\boldsymbol{x}^{(i)}\) 和獨熱標簽向量 \(\boldsymbol{y}^{(i)}\) 組成,因此可以將估計值與實際值比較:

\[p(\boldsymbol{Y} | \boldsymbol{X}) = \prod_{i=1}^n p(\boldsymbol{y}^{(i)} | \boldsymbol{x}^{(i)}) \]

最大化似然仍然是等價于熟悉的最小化負對數似然:

\[-\log P(\boldsymbol{Y} |\boldsymbol{X}) = \sum_{i=1}^n -\log P(\boldsymbol{y}^{(i)} | \boldsymbol{x}^{(i)}) = \sum_{i=1}^n l(\boldsymbol{y}^{(i)}, \hat{\boldsymbol{y}}^{(i)}) \]

其中,對于任何標簽 \(\boldsymbol{y}\) 和模型預測 \(\hat{\boldsymbol{y}}\),損失函式為:

\[l(\boldsymbol{y}, \hat{\boldsymbol{y}}) = - \sum_{j=1}^q y_j \log \hat{y}_j \]

上式一般被稱為交叉熵損失,

2. softmax 及其導數

利用 softmax 定義可得:

\[l(\boldsymbol{y}, \hat{\boldsymbol{y}}) = - \sum_{j=1}^q y_j \log \frac{\exp (o_j)}{\sum_{k=1}^q \exp (o_k)} \\ = \sum_{j=1}^q y_j \log \sum_{k=1}^q \exp(o_k) - \sum_{j=1}^q y_j o_j \\ = \log \sum_{k=1}^q \exp(o_k) - \sum_{j=1}^q y_j o_j \]

考慮相對于任何未規范化的預測 \(o_j\) 的導數,可以得到:

\[\partial_{o_j} l(\boldsymbol{y}, \hat{\boldsymbol{y}}) = \frac{\exp(o_j)}{\sum_{k=1}^q \exp(o_k)} - y_j = \text{softmax}(\boldsymbol{o})_j - y_j = \hat{y}_j - y_j \]

不妨設 \(s_i = \text{softmax} (\boldsymbol{o})_i\),再求二階導:

\[\frac{\partial l(\boldsymbol{y}, \hat{\boldsymbol{y}})}{\partial o_i \partial o_j} = \frac{\partial s_i}{\partial o_j} = \begin{cases} s_i (1 - s_i),& i=j \\ -s_j s_i, & i \not = j \end{cases} \]

課后題還要求 \(\text{softmax} (\boldsymbol{o})\) 給出的分布方差,并和二階導匹配起來,所以有

\[\begin{align} var(\boldsymbol{o}) &= \frac{1}{q-1} \sum_{j=1}^q (s_j - \overline{s})^2 \\ &= \frac{1}{q-1} [s_1^2 + s_2^2 + \cdots + s_q^2 + \overline{s}^2 * q - 2\overline{s}(s_1+s_2+\cdots+s_q)] \\ &= \frac{1}{q-1} \left( \sum_{j=1}^q s_j^2 - 2 \overline{s} \sum_{j=1}^q s_j\right) + \frac{q}{q-1} \overline{s}^2 \\ &= \frac{1}{q-1} \left( \sum_{j=1}^q s_j(s_j - 1) + \sum_{j=1}^q s_j - 2 \overline{s} \sum_{j=1}^q s_j \right) + \frac{q}{q-1} \overline{s}^2 \\ &= - \frac{1}{q-1} \sum_{j=1}^q \frac{\partial^2 l}{\partial o_j^2} + \frac{q}{q-1} (\overline{s} - \overline{s}^2 ) \\ &\approx - \frac{1}{q} \sum_{j=1}^q \frac{\partial^2 l}{\partial o_j^2} + (\overline{s} - \overline{s}^2 ) \end{align} \]

上面式子里除以 \(q-1\) 是符合統計學中無偏估計的做法,當然和除以 \(q\) 差別也不太大,

3. 資訊論淺談

資訊論的基本想法是一個不太可能的事件居然發生了,要比一個非常可能的事件發生,能提供更多的資訊,如果要通過這種基本想法來量化資訊,可以遵循以下三個點:

  • 非常可能發生的事件資訊量要比較少,并且極端情況下,確保能夠發生的事件應該沒有資訊量,
  • 較不可能發生的事件具有更高的資訊量,
  • 獨立事件應具有增量的資訊,例如,投擲的硬幣兩次正面朝上傳遞的資訊量,應該是投擲一次硬幣正面朝上的資訊量的兩倍,

為了滿足上述 \(3\) 個性質,因此定義一個事件 \(x\) 的自資訊(self-information)為

\[I(x) = -\log P(x) \]

這里定義的 \(I(x)\) 單位是奈特(nat),一奈特是以 \(\frac{1}{e}\) 的概率觀測到一個事件時獲得的資訊量,

自資訊只處理單個的輸出,可以用香農熵對整個概率分布中不確定性總量進行量化:

\[H(x) = \mathbb{E}_{x\sim P} [I(x)] = -\mathbb{E}_{x\sim P} [\log P(x)] \]

這個也可以記作 \(H(P)\),一個分布的香農熵是指遵循這個分布的事件所產生的期望資訊總量,

如果對同一個隨機變數 \(x\) 有兩個單獨的概率分布 \(P(x)\)\(Q(x)\),可以使用KL散度(Kullback-Leibler divergence)來衡量這兩個分布的差異:

\[D_{KL}(P||Q) = \mathbb{E}_{x \sim P} \left[\log \frac{P(x)}{Q(x)} \right] = \mathbb{E}_{x \sim P} [\log P(x) - \log Q(x)] \]

在離散型變數的情況下,KL 散度衡量的是,當使用一種被設計成能夠使得概率分布 \(Q\) 產生的訊息的長度最小的編碼,發送包含由概率分布 \(P\) 產生的符號的訊息時,所需要的額外資訊量,

KL 散度有一些有用的性質如下:

  • 非負
  • KL 散度為 \(0\),當且僅當 \(P\)\(Q\) 在離散性變數的情況下是相同的分布,或者在連續型變數的情況下是“幾乎處處”相同的,

由于上述兩個性質,因此它經常被用作分布之間的某種距離,然而,它并不滿足交換性,即 \(D_{KL}(P||Q) \not = D_{KL}(Q||P)\)

假設此時有一個分布 \(p(x)\),并且希望用另一個分布 \(q(x)\) 來近似它,那么就可以選擇最小化 \(D_{KL}(p||q)\) 或者最小化 \(D_{KL}(q||p)\),這其中選擇哪一個 KL 散度是取決于問題的,選擇 \(D_{KL}(p||q)\) 的目的是為了讓近似分布 \(q\) 在真實分布 \(p\) 放置高概率的所有地方都放置高概率,而選擇 \(D_{KL}(q||p)\) 的目的是為了讓近似分布 \(q\) 在真實分布 \(p\) 放置低概率的所有地方都很少放置高概率,

一個和 KL 散度密切聯系的量是交叉熵,即 \(H(P,Q) = H(P) + D_{KL}(P||Q) = -\mathbb{E}_{x \sim P} \log Q(x)\),因此針對 \(Q\) 最小化交叉熵等價于最小化 KL 散度,

3.5 影像分類資料集

本章其實沒啥亮點,有趣的內容稍微寫一下:

一個用來展示圖片以及標題的函式,有 num_rows 行 num_cols 列,

def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            ax.imshow(img.numpy())
        else:
            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if titles:
            ax.set_title(titles[i])
    return axes

此外,num_workers 這個引數表示了使用子行程讀取資料的個數,如果調小 batch_size 的話即使是 CPU 運行的代碼速度也會減慢,在 num_workers=4 的時候,測驗時間長度如下表:

batch_size 時間
1 117.74
4 28
256 3.11

3.6 softmax 回歸的從零開始實作

仍然是有趣的內容:

torch.normal() 能夠回傳一個其中所有值都符合正態分布的 tensor,

Accumulator 類對多個變數進行累加,

class Accumulator:
    def __init__(self, n):
        self.data = https://www.cnblogs.com/bringlu/archive/2023/04/09/[0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

還有一個可以在影片中繪制圖表的實用程式類 Animator,此函式僅能在 notebook 中使用,

import torch
from IPython import display
from d2l import torch as d2l
class Animator:  #@save
    """在影片中繪制資料"""
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        # 增量地繪制多條線
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函式捕獲引數
        self.config_axes = lambda: d2l.set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # 向圖表中添加多個資料點
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)

這個類應該怎么用呢?見下方代碼

animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                    legend=['train loss', 'train acc', 'test acc'])

for epoch in range(num_epochs):
    train_metrics = (train_loss, train_acc)
    animator.add(epoch + 1, train_metrics + (test_acc,))

3.7 softmax 回歸的簡潔實作

如何在類外給所有線性層初始化?可以使用 nn.Module.apply(fn) 可以做到,它的本來作用是遞回地對所有子模塊(包括自己)做相同的操作,如:

net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights)

即對 net 中所有 Linear 層初始化引數,

為防止由于指數函式導致的上溢位,因此再繼續 softmax 運算之前,先從所有 \(o_k\) 中減去 \(\max (o_k)\),事實上這樣不會改變 softmax 的回傳值:

\[\begin{align} \hat{y}_j &= \frac{\exp(o_j - max(o_k)) \exp(\max(o_k))}{\sum_k \exp(o_k - \max (o_k))\exp(\max(o_k))} \\ &= \frac{\exp(o_j - max(o_k))}{\sum_k \exp(o_k - \max (o_k))} \end{align} \]

又由于有些 \(\exp(o_j - max(o_k))\) 具有較大的負值,可能導致求完指數函式后直接下溢位歸零,并使得 \(\log(\hat{y}_j)\) 的值變為負無窮大,反向傳播幾步之后,可能會發現滿螢屏的 nan,因此將交叉熵和 softmax 操作結合在一起:

\[\begin{align} \log (\hat{y}_j) &= \log \left( \frac{\exp(o_j - max(o_k))}{\sum_k \exp(o_k - \max (o_k))}\right) \\ &= \log (\exp (o_j - \max (o_k))) - \log \left( \sum_k \exp (o_k - \max (o_k))\right) \\ &= o_j - \max (o_k) - \log \left( \sum_k \exp(o_k - \max (o_k))\right) \end{align} \]

這些具體落實到代碼上是模型過完最后一個線性層不要做 softmax 操作,直接往 PyTorch 的 CrossEntropyLoss 里面丟就行了,因為它已經結合好了,

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

標籤:其他

上一篇:線段樹好題! P2824 [HEOI2016/TJOI2016]排序 題解

下一篇:hack the box responder

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(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
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more