最近一直在做多任務,但是效果好象沒什麼提升,因爲都是憑自己的想法和感覺在做。於是上網查找了一些這方面的資料,尋求一些理論上的支撐和前人經驗上的幫助。
多任務學習:
故名思意,就是多個任務一起學習。爲什麼要進行多任務學習呢?因爲現實中樣本採樣的成本較高,而訓練樣本不足常常會出現過擬合的現象,而將多個相關任務同時學習,通過共享某個共同的知識可以提高各任務的泛化效果
。
分類:
基於軟共享的深度多任務學習
基於硬共享的深度多任務學習
一些問題:
1、損失的整合
爲多個任務定義一個損失函數,若將每個任務的損失進行簡單相加,由於不同任務的收斂速度不同,可能某一任務的收斂得到不錯的效果,而其他任務表現卻很差。
簡單的解決辦法是將簡單相加變爲加權相加,但這樣會不時進行調參。
論文《Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics》,提出引入不確定性來確定損失的權重:在每個任務的損失函數中學習另一個噪聲參數(noise parameter)。此方法可以接受多任務(可以是迴歸和分類),並統一所有損失的尺度。這樣就能像一開始那樣,直接相加得到總損失了。該方法不僅可以得到很好的結果而且不需要考慮額外的權重超參數。
2、調節學習速率
學習速率是最重要的超參數之一。我們發現,任務 A 和任務 B 各自合適的速率可能是不同的。這時,我們可以在各個任務的子網絡(基於硬共享的深度多任務學習)分別調節各自的學習速率,而在共享網絡部分,使用另一個學習速率。
雖然聽上去很複雜,但其實非常簡單。通常,在利用 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)
注:這個技巧其實在單任務網絡中也很實用
3、將估計作爲特徵
當完成第一階段的工作,爲預測多任務創建好神經網絡後,我們可能希望將某一個任務得到的估計(estimate)作爲另一個任務的特徵。在前向傳遞(forward-pass)中,這非常簡單。但在反向傳播中呢?
假設將任務 A 的估計作爲特徵輸入給 B,我們可能並不希望將梯度從任務 B 傳回任務 A,因爲我們已經有了任務 A 的標籤。對此,TensorFlow 的 API 所提供的 tf.stop_gradient 會有所幫助。在計算梯度時,它允許你傳入一個希望作爲常數的張量列表,這正是我們所需要的。
all_gradients = tf.gradients(loss, all_variables, stop_gradients=stop_tensors)
不止如此,該技術可用在任何你希望利用 TensorFlow 計算某個值並將其作爲常數的場景。
我的一些想法:
關於多個任務的訓練,應該也可以不統一成一個損失函數,各個任務擁有自己的損失函數即可。
這樣可以分別找到適合各個任務的學習速率,和迭代次數,然後進行次數不同迭代即可。
比如:
任務A需要迭代100次才收斂:optimizer1 = tf.train.AdamOptimizer(learning_rate1).minimize(loss1)
任務B需要迭代10次收斂:optimizer2 = tf.train.AdamOptimizer(learning_rate2).minimize(loss2)
# 訓練:
for epoch in range(100):
# Task A
sess.run([optimizer1], feed_dict1)
# Task B
if epoch % 10 == 0:
sess.run([optimizer2], feed_dict2)
【當然這部分只是我的想法啦!沒什麼科學依據】