李宏毅机器学习课程——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()}
posted @ 2021-08-01 21:11  19376273  阅读(440)  评论(0编辑  收藏  举报