1. 程式人生 > 其它 >李巨集毅機器學習課程——Lifelong learning學習筆記

李巨集毅機器學習課程——Lifelong learning學習筆記

概述

lifelong learning非常直觀,意思是機器不能前邊學後邊忘。常見的方法是對前邊的task中學習出來的引數加一個保護係數,在後面的任務中,訓練引數時,對保護係數大的引數很難訓練,而保護係數小的引數則容易一些。

下面的圖非常直觀,顏色的深淺代表loss的大小,顏色越深loss越小。在task1中\(\theta_2\)的變化對loss的變化非常敏感,而\(\theta_1\)則不敏感,所以在task2中儘量只通過改變\(\theta_1\)來減小loss,而不要改變\(\theta_2\)

在lifelong learning中,loss的計算公式如下:

\(L'(\theta)=L(\theta)+\lambda\Sigma_{i}b_{i}(\theta_i-\theta_i^b)^2\)

其中\(b_i\)​就是對\(\theta\)的保護係數,\(\theta_i\)表示本次task中需要學習的引數,\(\theta_i^b\)是從之前的task中學習到的引數。

不同的方法差異就在於\(b_i\)的計算。

這裡將會結合Coding整理一下遇到的三個方法。

Coding

這部分針對\(HW14\)​​,介紹了EWCMASSCP三種方法,這裡講解一下具體的程式碼實現,並定性地分析一下這些方法是如何把哪些重要的引數保護起來。

  • EWC

EWC中不同的保護係數\(f_i\)使用如下的方法計算得到:

$ F = [ \nabla \log(p(y_n | x_n, \theta_{A}^{*}) \nabla \log(p(y_n | x_n, \theta_{A}{*})

T ] $​

\(F\)​的對角線的各個數就是各個\(\theta\)的保護係數。

\(p(y_n | x_n, \theta_{A}^{*})\)​​ 指的就是模型在給點之前 task 的 data \(x_n\)​​ 以及給定訓練完 task A (原來)存下來的模型引數 \(\theta_A^*\)​​ 得到 \(y_n\)​​(\(x_n\)​​ 對應的 label ) 的後驗概率。

其實對引數\(\theta_i\),它的保護係數就是向量\(\log(p(y_n | x_n, \theta_{A}^{*}))\)\(\theta_1\)的偏導數\(\frac{\partial \log(p(y_n|x_n,\theta_A^*))}{\partial \theta_1}\)

與自身的內積。當對這個引數敏感時,這個偏導數會變大,當預測結果正確率高時,\(p(y_n|x_n)\)​也會高,最終都會使的保護係數變大。某一個引數比較敏感,這個引數下正確率高時,這個引數就會被很好地保護起來。

for dataloader in self.dataloaders:
    for data in dataloader:
        self.model.zero_grad()
        input = data[0].to(self.device)
        output = self.model(input).view(1,-1)
        label = output.max(1)[1].view(-1)
        loss = F.nll_loss(F.log_softmax(output,dim),label)
        loss.backward()
        
        for n,p in self.model.named_parameters():
            precision_matrices[n].data += p.grad.data ** 2 / number_data
precision_matrices = {n: p for n, p in precision_matrices.items()}
  • MAS

MAS中保護係數的計算方法如下所示:

$\Omega_i = || \frac{\partial \ell_2^2(M(x_k; \theta))}{\partial \theta_i} || $

\(x_k\)​ 是來自於前面 task 的 sample data。 式子上的作法就是對最後模型的 output vector (最後一層)做2範數後取平方,再對各自的weight微分(取gradient) 並且取該 gradient 的絕對值。

for dataloader in self.dataloaders:
    for data in dataloader:
        self.model.zero_grad()
        output = self.model(data[0].to(self.device))
        output.pow_(2)
        loss = torch.sum(output,dim=1) # 2範數的平方即元素的平方和
        loss = loss.mean()
        loss.backward()
        for n, p in self.model.named_parameters():
            precision_matrices[n].data += p.grad.abs() / num_data ## difference with EWC
precision_matrices = {n: p for n, p in precision_matrices.items()}
  • SCP

SCP方法保護係數的計算方法(\(\Gamma\)​矩陣)​如下:

初始化矩陣為0矩陣。

模型的\(output\)對所有的task A的輸入\(x_A\)​取平均值:

\(\bar{\phi}_A^*=\frac{1}{N}\Sigma_{n=1}^N\phi(x_n^A;\theta_A^*)\)

\(k\)維球面依次隨機取L個單位向量,注意要與\(\bar\phi_A^*\)的維度要一致,每次取得的\(\xi_l\),依次執行如下操作:

  • 計算內積\(\rho=\xi_l * \bar{\phi}_A^*\)
  • 取梯度\(\nabla_\theta\rho\)
  • \(\Gamma += \frac{1}{L}(\nabla_\theta\rho)(\nabla_\theta\rho)^T\)
def sample_spherical(npoints, ndim=3):
    vec = np.random.randn(npoints, ndim)
    vec = (vec.T / np.linalg.norm(vec, axis=1)).T
    return vec

## main
for dataloader in self.dataloaders:
    for data in dataloader:
        self.model.zero_grad()
        output = self.model(data[0].to(self.device))
        vec_mean = output.mean(dim=0)
        L_vecs = sample_spherical(self.L,vec_mean.size()[0])
        for vec in L_vecs:
            rou = torch.dot(torch.from_numpy(vec).to(self.device),vec_mean.double())
            rou.backward(retain_graph=True)
            for n, p in self.model.named_parameters():
                precision_matrices[n].data += p.grad.data ** 2 / self.L
                self.model.zero_grad()

precision_matrices = {n: p for n, p in precision_matrices.items()}