目錄
-
起因:最近在看CL(curriculum learning)相關的文章,然后發現了SPL學習策略,簡單來說就是讓model學習的數據從
簡單
到容易
. 看SPL相關的文章必然跳不過這篇文章:Self-Paced Learning for Latent Variable Models -
困難:這篇文章寫於2010年,基於pytorch實現的代碼很難找,在我找了兩天的情況下,終於有所發現
正題:理論部分
引入公式1:基礎的公式
- 該公式優化的參數為:
w
\[\mathbf{w}_{t+1}=\underset{\mathbf{w} \in \mathbb{R}^{d}}{\operatorname{argmin}}\left(r(\mathbf{w})+\sum_{i=1}^{n} f\left(\mathbf{x}_{i}, \mathbf{y}_{i} ; \mathbf{w}\right)\right) \]
- 括號中第一項
r(w)
函數是regularization function
,下面為了方便不對該部分展開(對后續結果無影響) - 括號中第二項$$\sum_{i=1}^{n} f()$$部分就是我們常寫的部分,用
w
和x
做運算得到p_red
,並和y
用損失函數計算loss,然后參數更新...
引入公式2:基礎的公式+SPL
- 引入
v
參數控制學習,v
可以取0
,1
. 表示數據難
,易
- 該公式優化的參數為:
w
和v
\[\left(\mathbf{w}_{t+1}, \mathbf{v}_{t+1}\right)=\underset{\mathbf{w} \in \mathbb{R}^{d}, \mathbf{v} \in\{0,1\}^{n}}{\operatorname{argmin}}\left(r(\mathbf{w})+\sum_{i=1}^{n} v_{i} f\left(\mathbf{x}_{i}, \mathbf{y}_{i} ; \mathbf{w}\right)-\frac{1}{K} \sum_{i=1}^{n} v_{i}\right) \]
- 括號中第三項$$-\frac{1}{K} \sum_{i=1}^{n} v_{i}$$就是正則項,它也有許多變體
見參考[0]
,我找到的資料中正則項修改成了hard
版的,即$$-\lambda \sum_{i=1}^{n} v_{i}$$
引入公式3:基礎的公式+SPL(變體 hard
)
- 基於公式2,並修改其中的正則項
- 該公式優化的參數為:
w
和v
\[L=r(w)+\sum_{i=1}^{n} v_{i} f\left(x_{i}, y_{i}, w\right)-\lambda \sum_{i=1}^{n} v_{I} \]
- 括號中第一項
r(w)
函數是regularization function
,下面為了方便不對該部分展開(對后續結果無影響) - 括號中第二項$$\sum_{i=1}^{n}v_{i} f()$$部分新增了
v_i
,即v
. 如果取0
表示數據難
,總體為0,即該部分對后續無影響,也就達到了難
的數據不學習。如果取1
,則相反 - 括號中第三項$$-\lambda \sum_{i=1}^{n} v_{i}$$ 其中$$\lambda$$用來和loss的值對比,來確定數據的簡單與否,代碼解釋如下
def spl_loss(super_loss, lambda_a):
# 如果 模型的loss < lambda --> v=1,表示該數據集簡單
# 否則 --> v=0,表示該數據集難
v = super_loss < lambda_a
return v.int()
且需要隨着epoch的增加而增加,實現隨着epoch的增加選擇的數據集越多,代碼解釋如下
def increase_threshold(lambda_a, growing_factor):
lambda_a *= growing_factor
return lambda_a
正題:部分代碼架構 完整可運行代碼 見參考[1]
SPL-LOSS 部分
import torch
from torch import Tensor
import torch.nn as nn
class SPLLoss(nn.NLLLoss):
def __init__(self, *args, n_samples=0, **kwargs):
super(SPLLoss, self).__init__(*args, **kwargs)
self.threshold = 0.1
self.growing_factor = 1.35
self.v = torch.zeros(n_samples).int()
def forward(self, input: Tensor, target: Tensor, index: Tensor) -> Tensor:
super_loss = nn.functional.nll_loss(input, target, reduction="none")
v = self.spl_loss(super_loss)
self.v[index] = v
return (super_loss * v).mean()
# 通過增加threshold 來增加每次訓練的大小
def increase_threshold(self):
self.threshold *= self.growing_factor
def spl_loss(self, super_loss):
# 如果 模型的loss < threshold --> v=1,表示該數據集簡單
# 否則 --> v=0,表示該數據集難
v = super_loss < self.threshold
return v.int()
train部分
def train():
model = Model(2, 2)
dataloader = get_dataloader()
criterion = SPLLoss(n_samples=len(dataloader.dataset))
optimizer = optim.Adam(model.parameters())
for epoch in range(10):
for index, data, target in tqdm.tqdm(dataloader):
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target, index)
loss.backward()
optimizer.step()
criterion.increase_threshold()
plot(dataloader.dataset, model, criterion)
animation = camera.animate()
animation.save("plot.gif")
參考
[0]:束俊, 孟德宇, 徐宗本. 元自步學習. 中國科學: 信息科學, 2020, 50: 781–793, doi: 10.1360/SSI-2020-0005 Shu J, Meng D Y, Xu Z B. Meta self-paced learning (in Chinese). Sci Sin Inform, 2020, 50: 781–793, doi: 10.1360/SSI-2020-0005
[1]: GitHub