深度學習中的multi task learning——optimization strategy部分
感覺還是叫深度多工學習比較好一點。。。
看了那麼多篇理論慢慢的paper,終於找到一篇比較有工程意義的paper了。
對於應用來說,這樣比較簡單直接的survey才是王道啊!感覺之前看的多工的survey公式和定理太多,還是這樣的文章比較能夠幫助快速上手解決問題。
這篇主要是介紹optimization strategy部分,關於architectures部分,另開一篇來寫好了。
當然這裡主要還是介紹optimization strategy部分。
兩個主要的研究方向
這篇文章提到了多工學習的兩個主要研究方向:
1、多工學習的網路結構的構造;
2、多工學習對標的多目標最佳化的方法;
多目標最佳化主要劃分為兩種:
1、task balancing;
2、others。。。。
(補充:這裡主要提到的是深度學習中的多個loss的最佳化的問題,傳統的多目標最佳化問題涉及到很多其它的知識,對我暫時不是很有用所以這裡就不贅述了,多目標最佳化在運籌領域裡也有比較多的應用,不過感覺很多和深度關係不是很大,我這裡主要是為了看深度相關的多目標最佳化,置於完整的多工多目標的內容,可見這裡:
GitHub - mbs0221/Multitask-Learning: Awesome Multitask Learning Resources
太特麼多了。。很多我壓根用不上,不費勁研究了
)
multitask learning的兩大挑戰:
(1)深度學習中的多目標最佳化問題
MTL中的一個重大挑戰源於最佳化過程本身。特別是,我們需要仔細平衡所有任務的聯合訓練過程,
以避免一個或多個任務在網路權值中具有主導影響的情況
。極端情況下,當某個任務的loss非常的大而其它任務的loss非常的小,此時多工近似退化為單任務目標學習,網路的權重幾乎完全按照大loss任務來進行更新,逐漸喪失了多工學習的優勢(具體優勢可見:
馬東什麼:多工學習之非深度看起來頭大的部分
馬東什麼:多工學習之深度學習部分
)
(2)深度學習中的網路結構設計問題
目前研究的大方向是偏向於 soft parameter sharing的,主要的挑戰在於如何設計出一種合理的網路結構能夠靈活地讓nn在學習的過程中知道應該share 哪些information,不應該share 哪些information,這方面里程碑式的工作就是moe了,後續的mmoe,snr,esmm等等也都是在moe 上的基礎上衍生出來的,具體可見:
馬東什麼:深度學習中的multi task learning——model architecture部分(待續)
深度學習中的多目標最佳化問題
下文內容來自於 :
ShowMeAI知識社群
作為推薦演算法的小白,這個公眾號可以說是非常良心了!有原理解釋和程式碼,舒服的一批,建議大家可以關注一下(非廣告)
最佳化方法更多的考慮的是在已有結構下,更好地結合任務進行訓練和引數最佳化,它從loss與梯度的維度去思考不同任務之間的關係。在最佳化過程中緩解梯度衝突,引數撕扯,儘量達到多工的平衡最佳化。
目前各式各樣的多工多目標最佳化方法策略,主要集中在3個問題:
(1)
Magnitude(Loss量級)
Loss值有大有小,取值大的Loss可能會主導,如圖所示,需要處理這個問題。典型的例子是二分類任務 + 迴歸任務的多目標最佳化,L2 loss和交叉熵損失的loss大小與梯度大小的量級和幅度可能差異很大,如果不處理會對最佳化造成很大幹擾。
(2)
Velocity (Loss學習速度)
不同任務因為樣本的稀疏性、學習的難度不一致,在訓練和最佳化過程中,存在loss學習速度不一致的情況。如果不加以調整,可能會出現某個任務接近收斂甚至過擬合的時候,其他任務還是欠擬合的狀態。
(3)
Direction(Loss梯度衝突)
不同任務的Loss對共享引數進行更新,梯度存在不同的大小和方向,相同引數被多個梯度同時更新的時候,可能會出現衝突,導致相互消耗抵消,進而出現蹺蹺板、甚至負遷移現象。
這也是核心需要處理的問題
。
因為後續的一些多目標最佳化的方法內容都挺多的,所以Uncertainty Weight,gradnorm之類的就單獨來寫了。
後續看的話應該暫時先看這幾個:
1 Uncertainty Weight
2 GradNorm
3 DWA (
End-to-End Multi-Task Learning with Attention
)
4 PCGrad
5 GradVac
之前的一些內容(desperated 而又不捨得刪)
第一大類方法 Task Balancing Approaches
最佳化方法更多的考慮的是在已有結構下,更好地結合任務進行訓練和引數最佳化,它從loss與梯度的維度去思考不同任務之間的關係。在最佳化過程中緩解梯度衝突,引數撕扯,儘量達到多工的平衡最佳化。
假設任務特定權重的最佳化目標wi和任務特定損失函式Li:
當使用隨機梯度下降來儘量減少上圖方程的總目標函式值(這是深度學習時代的標準方法),對共享層Wshare中的網路權值透過以下規則進行更新:
從上圖的方程可以看出:
1、loss大的任務在share部分的梯度更新量上將佔據主導;
2、不同任務的loss差異大導致模型更新不平衡的本質原因在於
梯度大小
;
3、透過調整不同任務的loss權重wi可以改善這個問題;
4、直接對不同任務的梯度進行處理也可以改善這個問題;
所以,後續的方法大體分為兩類:
1、在權重wi上做文章;
2、在梯度上做文章
一 在權重上的改進:
在權重上做文章的方法:
1、Uncertainty Weighting
https://
arxiv。org/pdf/1705。0711
5v3。pdf
人工定義多工loss的權重是之前主要的使用方法,這種方法存在許多問題。模型效能對權重的選擇非常敏感,如圖所示。
橫軸和縱軸分別是兩個任務的權重,曲線上的點對應不同權重下多工深度學習網路最終的訓練結果。
這些權重作為超引數調整起來非常的費事費力,每次測試通常需要很多的時間。
在
貝葉斯學習(
https://
book。douban。com/subject
/26284941/
關於python機率程式設計非常推薦這本書,這本也有中文版:
貝葉斯方法:機率程式設計與貝葉斯推斷
另外,
tensorflow-probability
在google上有關於這本書完整的程式碼demo,非常淺顯易懂,上手快。
另外也有t
orch版的pyro
相關的程式碼可見:
https://
github。com/CamDavidsonP
ilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers
tensorflow/probability
pyro-ppl/pyro
)
被深度貝葉斯學習中,認為模型存在兩種不確定性:
【實驗筆記】深度學習中的兩種不確定性(上)
from
1.偶然不確定性
我們初高中學物理的時候,老師肯定提過偶然誤差這個詞。我們做小車下落測量重力加速度常數的時候,每次獲得的值都會有一個上下起伏。這是我們因為氣流擾動,測量精度不夠等原因所造成的,是無法被避免的一類誤差。在深度學習中,我們把這種誤差叫做偶然不確定性。
從深度學習的角度來舉例子,我們舉一個大家應該很比較熟悉的人臉關鍵點回歸問題[3]:
我們可以看到,對於很相似的一組資料,dataset的標註出現了比較大的誤差(見右圖的右側邊緣)。這樣的誤差並不是我們模型帶入的,而是資料本來就存在誤差。資料集裡這樣的bias越大,我們的偶然不確定性就應該越大。
2.認知不確定性
認知不確定性是我們模型中存在的不確定性。就拿我們文章一開始舉的例子來說,假設我們訓練一個分類人臉和猩猩臉的模型,訓練中沒有做任何的增強,也就是說沒有做資料集的旋轉,模糊等操作。如果我給模型一個正常的人臉,或者是正常猩猩的臉,我們的模型應該對他所產生的結果的置信度很高。但是如果我給他貓的照片,一個模糊處理過得人臉,或者旋轉90°的猩猩臉,模型的置信度應該會特別低。換句話說,
認知不確定性測量的,是我們的input data是否存在於已經見過的資料的分佈之中
。
認知不確定性可以透過增加更多的data來緩解(靠演算法不靠人),偶然不確定性則需要對資料進行統一標準的處理(靠人不靠演算法)。
偶然不確定性又存在兩種不確定性類別:
(
補充:
異方差和同方差,以經典的線性迴歸為例,我們常常假設線性迴歸的誤差項滿足同方差,即誤差項的方差是相同的,如果不相同則為異方差,一個比較形象的例子:
什麼是異方差?為什麼異方差的出現通常與模型中某個解釋變數的變化有關?
)
1 資料依賴性(異方差不確定性)依賴於輸入資料,模型預測結果的殘差的方差即隨著資料的輸入發生變化;
2、任務依賴性(同方差不確定性)是不依賴於輸入資料的任意不確定性,它與模型輸出無關,是一個在所有輸入資料保持不變的情況下,在不同任務之間變化的量,因此,它可以被描述為與任務相關的不確定性,但是作者並沒有詳細解釋在多工深度學習中的同方差不確定性的嚴格定義,
而是認為同方差不確定性是由於任務相關的權重引起的
。
下面我們定義
fW
(
x
)為nn的預測值,也就是我們熟悉的y_pred,
對於迴歸型任務,我們定義下面的不確定性:
其中
在程式碼中的體現,是一個可學習的引數,我們用這個引數服從的公式2的高斯分佈作為同方差不確定性的衡量方法,即以 y_pred為均值向量,
**2 作為方差的多元高斯分佈;
對於分類問題有:
這被稱作是Boltzmann分佈,也叫做
吉布斯分佈.
則在多目標的前提下,我們認為總的同方差不確定性可以用不同任務的不確定性的乘積來表示:
透過對公式(4)進行對數變換後可以得到:
(這個正比的公式是怎麼得到的。。。)
現在讓我們假設我們的模型輸出由兩個向量y1和y2組成,每個向量都遵循一個高斯分佈
:
(這裡作者沒有說清楚,實際上這裡作者是假設我們有兩個迴歸型的目標任務,並且損失函式使用的是mse)
然後得到多輸出模型的最小化目標函式 L(W、σ1、σ2):
因此,對於公式(7),在新的迴歸型任務中,我們可以將L1(W)和L2(W)用其它的迴歸任務對應的損失函式來代替,所以可以看到,這篇文章做的事情,其實就類似於l1,l2正則化,在總的loss function上加入了關於“不確定性”的loss function;
對於分型別任務,作者木有給出最終的化簡公式,不過對照下面的一個分型別任務+一個迴歸型任務的化簡公式:
這裡補充一下公式10的推導部分,具體的近似在上圖,將上圖帶入公式10即可。至於這個近似公式怎麼來的,我也沒看明白。。。有懂得大佬求指正一下
我們可以先推出單個迴歸型任務的不確定性度量公式,從而得到分型別任務的同方差不確定性的近似衡量公式為:
和
的和。
則也可以如法炮製,比較容易地寫出兩個分型別任務不確定性的化簡公式了,簡單來看就是分母少了2。(推導部分太頭大了就不看了)
這種構造可以簡單地擴充套件到
任意離散和連續損失函式
的組合,允許我們以一種有原則和有充分根據的方式學習每一個損失的相對權重。這種損失是平滑可微的,並且分佈形狀很好,使得任務權重不會收斂到零。相比之下,
使用直接學習權值會導致快速收斂到零的權值
。
總結一下,整體的思路就是用sigma來衡量同方差不確定性,同方差不確定性和任務有關,同方差不確定性越高的任務則意味著模任務相關的輸出的噪聲越多,任務越難以學習,因此在多工模型訓練的過程中,其對應的sigma會增大,削弱這類任務的權重使得整體的多工模型的訓練更加順暢和有效。
在程式碼實現上有個小問題,也是比較常見的實現和論文存在區別的地方:
yaringal/multi-task-learning-example
這個是原論文作者的實現:基於兩個迴歸型任務,損失函式mse為前提下得到的
def criterion(y_pred, y_true, log_vars):
loss = 0
for i in range(len(y_pred)):
precision = torch。exp(-log_vars[i])
diff = (y_pred[i]-y_true[i])**2。 ## mse loss function
loss += torch。sum(precision * diff + log_vars[i], -1)
return torch。mean(loss)
原文提到了我們直接定義變數,這個變數是
log(sigma的)(sigma表示的是方差,也就是下圖裡面的那個二次項)
,這樣可以避免loss公式中除0的問題:
看了一下才發現這是萬惡的梯度下降法靈活的變數定義導致的,無論是torch還是tf中,變數為2x還是x都沒有區別,
因為最終常數項都可以直接融合到變數的求解中
,所以之前看的很多的paper的實現裡,常數項都是直接包含在變數裡省去不寫。。真是屑,,,
首先我們定義 log(sigma)=a(a是一個可學習的變數),則 torch。exp(-a)=torch。exp(-log(sigma))=torch。exp(log(sigma**-1))=1/sigma(這裡0。5可以省去也可以包含進來,因為我們定義1/2*變數x和直接定義變數x,在梯度下降法求解的過程中沒有太大區別,然後是常數項的部分,作者在原文中提到,後面的常數項並不是很重要,放進來作為一種正則乘法太大的sigma(方差),這裡後面的常數項,按照程式碼的意思,是直接用了sigma方差來代替了標準差,其實差別也不大)
所以根據上述的設定對下面的程式碼做了一些修改:
git上對應的程式碼:
https://
github。com/Mikoto10032/
AutomaticWeightedLoss/blob/master/AutomaticWeightedLoss。py
找了幾個實現,發現程式碼都有問題,只有這個git是完全忠於原文的,並且封裝的也比較舒服。
import torch
import torch。nn as nn
class AutomaticWeightedLoss(nn。Module):
“”“automatically weighted multi-task loss
Params:
num: int,the number of loss
x: multi-task loss
Examples:
loss1=1
loss2=2
awl = AutomaticWeightedLoss(2)
loss_sum = awl(loss1, loss2)
”“”
def __init__(self, num=2):
super(AutomaticWeightedLoss, self)。__init__()
params = torch。ones(num, requires_grad=True)
self。params = torch。nn。Parameter(params) #parameters的封裝使得變數可以容易訪問到
def forward(self, *x):
loss_sum = 0
for i, loss in enumerate(x):
loss_sum += 0。5 * torch。exp(-log_vars[i]) * loss + self。params[i]
# +1避免了log 0的問題 log sigma部分對於整體loss的影響不大
return loss_sum
關於權重項部分
目前看過的三個git上都沒有對分類或者是迴歸的loss區別對待,可以設定引數用於定義分類or迴歸loss,從而給權重項部分的分佈分別賦予迴歸—2,分類—1。很多作者這部分沒有嚴格按照論文公式來預測,不過上面的code稍微改動一下就可以,
但是其實也不用改。。常數項在梯度下降的過程中都會被最佳化演算法考慮進來的
。
適配的話:
from torch import optim
from AutomaticWeightedLoss import AutomaticWeightedLoss
model = Model()
awl = AutomaticWeightedLoss(2) # we have 2 losses
loss_1 = 。。。
loss_2 = 。。。
# learnable parameters
optimizer = optim。Adam([
{‘params’: model。parameters()},
{‘params’: awl。parameters(), ‘weight_decay’: 0}
])
for i in range(epoch):
for data, label1, label2 in data_loader:
# forward
pred1, pred2 = Model(data)
# calculate losses
loss1 = loss_1(pred1, label1)
loss2 = loss_2(pred2, label2)
# weigh losses
loss_sum = awl(loss1, loss2)
# backward
optimizer。zero_grad()
loss_sum。backward()
optimizer。step()
這種方法的一個比較核心的問題也比較明顯吧:
1、同方差不確定性衡量的定義方式是否合理;
2、如果在多工學習中,我們主要是希望主任務的效果好,輔助任務的效果可能不是很care,
那麼如果恰好主任務是同方差不確定性最高的,則使用這種方法可能會削弱主任務的效果,這是最大的問題
,因為這種處理的方式針對的是整個多工模型的總體loss來設計的,無法滿足對特定任務的不同程度的需求,因為作者原始的思路是不確定性越高的任務越應該削弱權重,
但是反過來想,不確定性越高的任務越難,如果我們反而讓模型重點去學習這個任務,是否可以提高模型的能力
;
3、 這裡沒有考慮權重和為1的問題,不過我覺得作者本來也沒打算這麼做,影響不大,權重之和是否為1並不是問題其實,本來多工也不一定需要權重為1的設定,另外權重簡單做歸一化就可以得到權重為1了。。
4、實際應用的一個問題,權重可能會變成負數,導致我們最終的loss變成負數了。。。也就是部分任務對於最終總loss的貢獻是負貢獻,我認為可能是這部分任務的不確定性太大使得模型訓練困難,這個部分我們relu進行截斷就可以了
2、Grad Norm
梯度歸一化的主要目的在於希望不同任務任務對應的梯度具有相似的大小,從而控制多工網路的訓練。透過這樣做,我們鼓勵網路以相同的速度學習所有的任務。grad norm本身不focus於不同任務之間的權重,而是將所有任務等同視之,只是希望所有任務的更新能夠相對接近從而避免了某個任務收斂了,某個任務還在收斂的路上的問題,這樣會導致:
1、模型訓練的效率低,最終執行時間由最複雜的任務決定;
2、複雜任務收斂的過程中,簡單任務的區域性最優權重可能會變差;
上一篇:王憶如的悲劇誰造成的?