農林漁牧網

您現在的位置是:首頁 > 林業

如何利用深度學習模型實現多工學習?這裡有三點經驗

2022-05-23由 機器之心Pro 發表于 林業

兩個調節變數怎麼做模型

選自Taboola Engineering

作者:

Zohar Komarovsky

機器之心編譯

機器之心編譯

參與:Tianci LIU

Taboola 演算法開發者 Zohar Komarovsky 介紹了他們在利用深度學習模型實現多工學習(MTL)時遇到的幾個典型問題及解決方案。

在過去的一年裡,我和我的團隊一直致力於為 Taboola feed 提供個性化使用者體驗。我們運用多工學習(Multi-Task Learning,MTL),在相同的輸入特徵集上預測多個關鍵效能指標(Key Performance Indicator,KPI),然後使用 TensorFlow 實現深度學習模型。回想最初的時候,我們感覺(上手)MTL 比現在要困難很多,所以我希望在這裡分享一些經驗總結。

現在已經有很多關於利用深度學習模型實現 MTL 的文章。在本文中,我準備分享一些利用神經網路實現 MTL 時需要考慮的具體問題,同時也會展示一些基於 TensorFlow 的簡單解決方案。

、路

我們準備從引數硬共享(hard parameter sharing)的基礎方法開始。硬共享表示我們使用一個共享的子網路,下接各個任務特定的子網路。

如何利用深度學習模型實現多工學習?這裡有三點經驗

在 TensorFlow 中,實現這樣一個模型的簡單方法是使用帶有 multi_head 的 Estimator。這個模型和其他神經網路架構相比沒什麼不同,所以你可以自己想想,有哪些可能出錯的地方?

共享即關懷

我們的 MTL 模型所遇到的第一個挑戰是為多個任務定義一個損失函式。既然每個任務都有一個定義良好的損失函式,那麼多工就會有多個損失。

我們嘗試的第一個方法是將不同損失簡單相加。很快我們就發現,雖然某一個任務會收斂得到不錯的結果,其他的卻表現很差。進一步研究後,可以很容易地明白原因:不同任務損失的尺度差異非常大,導致整體損失被某一個任務所主導,最終導致其他任務的損失無法影響網路共享層的學習過程。

一個簡單的解決方案是,將損失簡單相加替換為加權和,以使所有任務損失的尺度接近。但是,這引入了另一個可能需要不時進行調節的超引數。

幸運的是,我們發現了一篇非常棒的論文《Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics》,提出引入不確定性來確定 MTL 中損失的權重:在每個任務的損失函式中學習另一個噪聲引數(noise parameter)。此方法可以接受多工(可以是迴歸和分類),並統一所有損失的尺度。這樣,我們就能像一開始那樣,直接相加得到總損失了。

與損失加權求和相比,該方法不僅得到了更好的結果,而且還可以不再理會額外的權重超引數。論文作者提供的 Keras 實現參見:https://github。com/yaringal/multi-task-learning-example/blob/master/multi-task-learning-example。ipynb。

第一點:整合損失

調節神經網路有一個通用約定:學習速率是最重要的超引數之一。所以我們嘗試調節學習速率。我們發現,對於某一個任務 A 而言,存在一個特別適合的學習速率,而對於另一個任務 B,則有不同的適合學習速率。如果選擇較高的學習速率,可能在某個任務上出現神經元死亡(由於大的負梯度,導致 Relu 函式永久關閉,即 dying ReLU),而使用較低的學習速率,則會導致其他任務收斂緩慢。應該怎麼做呢?我們可以在各個「頭部」(見上圖,即各任務的子網路)分別調節各自的學習速率,而在共享網路部分,使用另一個學習速率。

雖然聽上去很複雜,但其實非常簡單。通常,在利用 TensorFlow 訓練神經網路時,使用的是:

optimizer = tf。train。AdamOptimizer(learning_rate)。minimize(loss)

AdamOptimizer 定義如何應用梯度,而 minimize 則完成具體的計算和應用。我們可以將 minimize 替換為我們自己的實現方案,在應用梯度時,為計算圖中的各變數使用各自適合的學習速率。

all_variables = shared_vars + a_vars + b_vars

all_gradients = tf。gradients(loss, all_variables)

shared_subnet_gradients = all_gradients[:len(shared_vars)]

a_gradients = all_gradients[len(shared_vars):len(shared_vars + a_vars)]

b_gradients = all_gradients[len(shared_vars + a_vars):]

shared_subnet_optimizer = tf。train。AdamOptimizer(shared_learning_rate)

a_optimizer = tf。train。AdamOptimizer(a_learning_rate)

b_optimizer = tf。train。AdamOptimizer(b_learning_rate)

train_shared_op = shared_subnet_optimizer。apply_gradients(zip(shared_subnet_gradients, shared_vars))

train_a_op = a_optimizer。apply_gradients(zip(a_gradients, a_vars))

train_b_op = b_optimizer。apply_gradients(zip(b_gradients, b_vars))

train_op = tf。group(train_shared_op, train_a_op, train_b_op)

友情提醒:這個技巧其實在單任務網路中也很實用。

第二點:調節學習速率

當我們完成了第一階段的工作,為預測多工建立好神經網路後,我們可能希望將某一個任務得到的估計(estimate)作為另一個任務的特徵。在前向傳遞(forward-pass)中,這非常簡單。估計是一個張量,可以像任意一個神經層的輸出一樣進行傳遞。但在反向傳播中呢?

假設將任務 A 的估計作為特徵輸入給 B,我們可能並不希望將梯度從任務 B 傳回任務 A,因為我們已經有了任務 A 的標籤。對此不用擔心,TensorFlow 的 API 所提供的 tf。stop_gradient 會有所幫助。在計算梯度時,它允許你傳入一個希望作為常數的張量列表,這正是我們所需要的。

all_gradients = tf。gradients(loss, all_variables, stop_gradients=stop_tensors)

和上面一樣,這個方法對 MTL 網路很有用,但不止如此。該技術可用在任何你希望利用 TensorFlow 計算某個值並將其作為常數的場景。例如,在訓練生成對抗網路(Generative Adversarial Network,GAN)時,你不希望將對抗示例反向傳播到生成過程中。

第三點:將估計作為特徵

我們的模型已經上線執行,Taboola feed 也已經是個性化的了。但是,還有很多可以提升改進的空間,以及許多有趣的結構可以探索。在我們的應用場景中,預測多工也意味著基於多個 KPI 完成決策。這可能比基於單個 KPI 的更復雜……不過這就是另一個全新的問題了。

原文連結:https://engineering。taboola。com/deep-multi-task-learning-3-lessons-learned/

下一步