SPL(Self-Paced Learning for Latent Variable Models)代碼復現-pytorch版


  1. 起因:最近在看CL(curriculum learning)相關的文章,然后發現了SPL學習策略,簡單來說就是讓model學習的數據從簡單容易. 看SPL相關的文章必然跳不過這篇文章:Self-Paced Learning for Latent Variable Models

  2. 困難:這篇文章寫於2010年,基於pytorch實現的代碼很難找,在我找了兩天的情況下,終於有所發現

正題:理論部分

引入公式1:基礎的公式

  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) \]

  1. 括號中第一項r(w)函數是regularization function,下面為了方便不對該部分展開(對后續結果無影響)
  2. 括號中第二項$$\sum_{i=1}^{n} f()$$部分就是我們常寫的部分,用wx做運算得到p_red,並和y用損失函數計算loss,然后參數更新...

引入公式2:基礎的公式+SPL

  1. 引入v參數控制學習,v可以取0,1. 表示數據,
  2. 該公式優化的參數為:wv

\[\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) \]

  1. 括號中第三項$$-\frac{1}{K} \sum_{i=1}^{n} v_{i}$$就是正則項,它也有許多變體見參考[0],我找到的資料中正則項修改成了hard版的,即$$-\lambda \sum_{i=1}^{n} v_{i}$$

引入公式3:基礎的公式+SPL(變體 hard)

  1. 基於公式2,並修改其中的正則項
  2. 該公式優化的參數為:wv

\[L=r(w)+\sum_{i=1}^{n} v_{i} f\left(x_{i}, y_{i}, w\right)-\lambda \sum_{i=1}^{n} v_{I} \]

  1. 括號中第一項r(w)函數是regularization function,下面為了方便不對該部分展開(對后續結果無影響)
  2. 括號中第二項$$\sum_{i=1}^{n}v_{i} f()$$部分新增了v_i,即v. 如果取0表示數據,總體為0,即該部分對后續無影響,也就達到了的數據不學習。如果取1,則相反
  3. 括號中第三項$$-\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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM