論文《Learning to learn without forgetting by maximizing transfer and minimizing interference》中提出了“將經驗重放與元學習相結合“的增量學習方法:Meta-Experience Replay (MER),
這里整理了一下MER的演算法流程和代碼實作,分別針對任務增量(Task-IL)和類增量(Class-IL)場景下,
論文決議可以戳這里:??????論文決議:Learning to learn without forgetting by maximizing transfer and minimizing interference
目錄
1. 演算法基礎
1.1 Reservior Sampling (蓄水池采樣)
1.2 Experience Replay (ER,經驗回放方法)
1.3 Reptile
2. Meta-Experience Replay 演算法
2.1 MER 演算法詳解
2.2 任務增量下的代碼注釋
2.3 類增量下的代碼注釋
1. 演算法基礎
1.1 Reservior Sampling (蓄水池采樣)
Reservior Sampling 是基于經驗重放的增量學習方法中常使用的等概率采樣方法,
(1) 原理
給出一個資料流,這個資料流的長度很大或者未知,并且對該資料流中資料只能訪問一次,寫出一個隨機選擇演算法,使得資料流中所有資料被選中的概率相等,
(2) 方法
假設需要采樣的數量為k,
首先構建一個可容納k個元素的資料,將序列的前k個元素放入資料,
然后對第j個元素(j>k),以k/j的概率決定該元素是否被留下(替換到陣列中,陣列中的k個元素被替換的概率相同),
(3) 證明

(4) 演算法流程

1.2 Experience Replay (ER,經驗回放方法)
(1) 學習目標
核心是保持對已經見過的exemplars的記憶
目標函式:
其中,
為 memory buffer,current size =
, maximum size =
原理:使用 Reservioe Sampling 更新 buffer,確保在每一個時間步長里,任何
個exemplars在 buffer 中被看見的概率都等于
(2) 演算法流程
ER 演算法中,每看到新的樣本,就對當前 exemplars 進行優先級排序,確保 current exemplars 與 replay buffer 中的例子交叉(因為在繼續next example前,希望確保演算法能夠對current example進行優化,特別是當它還未加入到memory中)

1.3 Reptile
Reptile是元學習中最經典和常用的演算法之一,具體的原理可以自行查閱相關文獻,
本文的MER就是在Reptile基礎上結合增量學習,Reptile基于SGD優化器和學習率,跨s批次順序優化,
在a set of s batches上的優化目標為:
演算法流程:

2. Meta-Experience Replay 演算法
這里主要介紹論文中的 Algorithm 1,是單個樣本的增量更新,(Algorithm 6 是對一個批次batch的增量更新,原理和代碼相差不大,)
2.1 MER 演算法詳解
原理:MER保持著 Experience Replay 的記憶,通過 Reservior Sampling 采樣,每次時間步提取包括從buffer中k-1個隨機樣本在內的s個batches,
流程:
1、黃色框為內部更新 inner update:
在Reptile的基礎流程,對于 s 個 batches,每個 batch 中的 k 個樣本,都進行1次Reptile批處理,2、綠色框為外部更新 outer update:
根據 inner update 后的模型引數,更新原始模型引數,使用Reservior sampling來更新 memory buffer,

2.2 任務增量下的代碼注釋
代碼鏈接:MER/meralg1.py at master · mattriemer/MER · GitHub
(1) Draw batches from buffer:
當前的新樣本為 (x,y),結合新樣本和從 memory buffer 中取出的舊樣本(經驗回放),生成該批次要訓練的樣本:
def getBatch(self, x, y, t):
# (x,y): 新看到的樣本
xi = Variable(torch.from_numpy(np.array(x))).float().view(1, -1)
yi = Variable(torch.from_numpy(np.array(y))).long().view(1)
if self.cuda:
xi = xi.cuda()
yi = yi.cuda()
# bxs, bys: 該批次要訓練的樣本
bxs = [xi]
bys = [yi]
if len(self.M) > 0:
order = [i for i in range(0, len(self.M))]
osize = min(self.batchSize, len(self.M))
for j in range(0, osize):
shuffle(order)
k = order[j]
x, y, t = self.M[k]
xi = Variable(torch.from_numpy(np.array(x))).float().view(1, -1)
yi = Variable(torch.from_numpy(np.array(y))).long().view(1)
# handle gpus if specified
if self.cuda:
xi = xi.cuda()
yi = yi.cuda()
bxs.append(xi)
bys.append(yi)
return bxs, bys
在 observe() 中呼叫:
# Draw batch from buffer
bxs,bys = self.getBatch(xi,yi,t)
(2) Inner update 中使用Reptile meta-update:
for step in range(0, self.steps):
weights_before = deepcopy(self.net.state_dict())
# Draw batch from buffer:
bxs, bys = self.getBatch(xi, yi, t)
loss = 0.0
for idx in range(len(bxs)):
# 單個樣本進行元學習
self.net.zero_grad()
bx = bxs[idx]
by = bys[idx]
prediction = self.forward(bx, 0)
loss = self.bce(prediction, by)
loss.backward()
self.opt.step()
weights_after = self.net.state_dict()
# Within batch Reptile meta-update:
# 更新內部模型的引數
self.net.load_state_dict(
{name: weights_before[name] + ((weights_after[name] - weights_before[name]) * self.beta) for name in
weights_before})
(3) Outer update 中進行 Reptile 元更新和重新采樣
第一步,將內部更新的元模型引數進行外部模型的更新:
# Across batch Reptile meta-update
self.net.load_state_dict({name : before[name] + ((after[name] - before[name]) * self.gamma) for name in before})
第二步,使用 Reservoir Sampling 更新 buffer memory:
# Reservoir sampling memory update:
if len(self.M) < self.memories:
self.M.append([xi, yi, t])
else:
p = random.randint(0, self.age)
if p < self.memories:
self.M[p] = [xi, yi, t]
2.3 類增量下的代碼注釋
代碼鏈接:La-MAML/meralg1.py at main · montrealrobotics/La-MAML · GitHub
(0) initialization初始化:
在__init__() 函式中,根據類增量的場景,重新設定了每個任務的類別數 nc_per_task
self.n_outputs = n_outputs
if self.is_cifar: # Class-IL
self.nc_per_task = n_outputs / n_tasks # 每個任務的類別不重疊
else: # Task -IL
self.nc_per_task = n_outputs # 每個任務的類別可以看作一樣
(1) Draw batches from buffer:
與2.2中的如出一轍,但是多增加了任務t,將任務t也加入到了buffer memory中,
(2) Inner update 中使用Reptile meta-update:
在這里,類增量比2.2(任務增量)新增了一個 compute_offsets() 函式,主要是因為任務增量中,驗證的是所有類別(每個任務的類別可以近似看作是一樣的)的預測結果;而類增量中,驗證的是當前任務中涉及到的類別(每個任務的類別都不重疊)的預測結果,
所以,使用 compute_offsets() 函式來框定只有在這次任務出現的類別:
def compute_offsets(self, task):
if self.is_cifar: # Class-IL
offset1 = task * self.nc_per_task
offset2 = (task + 1) * self.nc_per_task
else: # Task-IL
offset1 = 0
offset2 = self.n_outputs
return int(offset1), int(offset2)
同樣,在 forward() 中也應用了compute_offsets() 函式來框定只有在這次任務中出現的類別的預測結果:
def forward(self, x, t):
output = self.netforward(x)
if self.is_cifar:
offset1, offset2 = self.compute_offsets(t)
# 不在offset1~offset2的預測結果都剔除
if offset1 > 0:
output[:, :offset1].data.fill_(-10e10)
if offset2 < self.n_outputs:
output[:, int(offset2):self.n_outputs].data.fill_(-10e10)
return output
在observe() 函式中,inner update內部更新流程:
for step in range(0, self.steps):
weights_before = deepcopy(self.net.state_dict())
##Check for nan
if weights_before != weights_before:
ipdb.set_trace()
# Draw batch from buffer:
bxs, bys, bts = self.getBatch(xi, yi, t)
loss = 0.0
total_loss = 0.0
for idx in range(len(bxs)):
self.net.zero_grad()
bx = bxs[idx]
by = bys[idx]
bt = bts[idx]
if self.is_cifar: # Class-IL
offset1, offset2 = self.compute_offsets(bt) # 獲得當前任務的index
prediction = (self.netforward(bx)[:, offset1:offset2]) # 獲得當前任務的預測結果
loss = self.bce(prediction,
by.unsqueeze(0) - offset1)
else: # Task-IL
prediction = self.forward(bx, 0)
loss = self.bce(prediction, by.unsqueeze(0))
if torch.isnan(loss):
ipdb.set_trace()
loss.backward()
torch.nn.utils.clip_grad_norm_(self.net.parameters(), self.args.grad_clip_norm)
self.opt.step()
total_loss += loss.item()
weights_after = self.net.state_dict()
if weights_after != weights_after:
ipdb.set_trace()
# Within batch Reptile meta-update:
self.net.load_state_dict(
{name: weights_before[name] + ((weights_after[name] - weights_before[name]) * self.beta) for name in
weights_before})
(3) Outer update 中進行 Reptile 元更新和重新采樣
與2.2中的如出一轍,但是多增加了任務t,將任務t也加入到了buffer memory中,
以上是我對任務增量/類增量場景下的MER代碼的一些理解,從代碼上也可以看出任務增量和類增量的異同,如果有寫的不對的地方,歡迎指出與討論~
citation:M. Reimer, I. Cases, R. Ajemian, M. Liu, I. Rish, Y. Tu, G. Tesauro, Learning to learn without forgetting by maximizing transfer and minimizing interference, in: ICLR, 2019.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/303241.html
標籤:AI
上一篇:R語言臨床預測模型的評價指標與驗證指標實戰:C-index指標計算
下一篇:WAF繞過-漏洞發現-漏掃繞過
