您現在的位置是:首頁 > 網路遊戲首頁網路遊戲

Pytorch建立多工學習模型

簡介races[index]sample = {‘image’:img, ‘age’: age, ‘gender’: gender, ‘ethnicity’:eth}return sample簡單的做個介紹:__init__方法初始化我們的自定

matlab中絕對值怎麼表示

在機器學習中,我們通常致力於針對單個任務,也就是最佳化單個指標。但是多工學習(MTL)在機器學習的許多應用中都取得了成功,從自然語言處理和語音識別到計算機視覺和藥物發現。

MTL最著名的例子可能是特斯拉的自動駕駛系統。在自動駕駛中需要同時處理大量任務,如物體檢測、深度估計、3D重建、影片分析、跟蹤等,你可能認為需要10個以上的深度學習模型,但事實並非如此。

Pytorch建立多工學習模型

HydraNet介紹

一般來說多工學的模型架構非常簡單:一個骨幹網路作為特徵的提取,然後針對不同的任務建立多個頭。利用單一模型解決多個任務。

Pytorch建立多工學習模型

上圖可以看到,特徵提取模型提取影象特徵。輸出最後被分割成多個頭,每個頭負責一個特定的情況,由於它們彼此獨立可以單獨進行微調!

特斯拉的講演中詳細的說明這個模型(youtube:v=3SypMvnQT_s)

多工學習專案

在本文中,我們將介紹如何在Pytorch中實現一個更簡單的HydraNet。這裡將使用UTK Face資料集,這是一個帶有3個標籤(性別、種族、年齡)的分類資料集。

我們的HydraNet將有三個獨立的頭,它們都是不同的,因為年齡的預測是一個迴歸任務,種族的預測是一個多類分類問題,性別的預測是一個二元分類任務。

Pytorch建立多工學習模型

每一個Pytorch 的深度學習的專案都應該從定義Dataset和DataLoader開始。

在這個資料集中,透過影象的名稱定義了這些標籤,例如UTKFace/30_0_3_20170117145159065。jpg。chip。jpg

30歲是年齡

0為性別(0:男性,1:女性)

3是種族(0:白人,1:黑人,2:亞洲人,3:印度人,4:其他)

所以我們的自定義Dataset可以這樣寫:

class UTKFace(Dataset):

def __init__(self, image_paths):

self。transform = transforms。Compose([transforms。Resize((32, 32)), transforms。ToTensor(), transforms。Normalize([0。485, 0。456, 0。406], [0。229, 0。224, 0。225])])

self。image_paths = image_paths

self。images = []

self。ages = []

self。genders = []

self。races = []

for path in image_paths:

filename = path[8:]。split(“_”)

if len(filename)==4:

self。images。append(path)

self。ages。append(int(filename[0]))

self。genders。append(int(filename[1]))

self。races。append(int(filename[2]))

def __len__(self):

return len(self。images)

def __getitem__(self, index):

img = Image。open(self。images[index])。convert(‘RGB’)

img = self。transform(img)

age = self。ages[index]

gender = self。genders[index]

eth = self。races[index]

sample = {‘image’:img, ‘age’: age, ‘gender’: gender, ‘ethnicity’:eth}

return sample

簡單的做個介紹:

__init__方法初始化我們的自定義資料集,負責初始化各種轉換和從影象路徑中提取標籤。

__get_item__將:它將載入一張影象,應用必要的轉換,獲取標籤,並返回資料集的一個元素,也就是說這個方法會返回資料集中的單條資料(單個樣本)

然後我們定義dataloader

train_dataloader = DataLoader(UTKFace(train_dataset), shuffle=True, batch_size=BATCH_SIZE)

val_dataloader = DataLoader(UTKFace(valid_dataset), shuffle=False, batch_size=BATCH_SIZE)

下面我們定義模型,這裡使用一個預訓練的模型作為骨幹,然後建立3個頭。分別代表年齡,性別和種族。

class HydraNet(nn。Module):

def __init__(self):

super()。__init__()

self。net = models。resnet18(pretrained=True)

self。n_features = self。net。fc。in_features

self。net。fc = nn。Identity()

self。net。fc1 = nn。Sequential(OrderedDict(

[(‘linear’, nn。Linear(self。n_features,self。n_features)),

(‘relu1’, nn。ReLU()),

(‘final’, nn。Linear(self。n_features, 1))]))

self。net。fc2 = nn。Sequential(OrderedDict(

[(‘linear’, nn。Linear(self。n_features,self。n_features)),

(‘relu1’, nn。ReLU()),

(‘final’, nn。Linear(self。n_features, 1))]))

self。net。fc3 = nn。Sequential(OrderedDict(

[(‘linear’, nn。Linear(self。n_features,self。n_features)),

(‘relu1’, nn。ReLU()),

(‘final’, nn。Linear(self。n_features, 5))]))

def forward(self, x):

age_head = self。net。fc1(self。net(x))

gender_head = self。net。fc2(self。net(x))

ethnicity_head = self。net。fc3(self。net(x))

return age_head, gender_head, ethnicity_head

forward方法返回每個頭的結果。

損失作為最佳化的基礎時十分重要的,因為它將會影響到模型的效能,我們能想到的最簡單的事就是地把損失相加:

L = L1 + L2 + L3

但是我們的模型中

L1:與年齡相關的損失,如平均絕對誤差,因為它是迴歸損失。

L2:與種族相關的交叉熵,它是一個多類別的分類損失。

L3:性別有關的損失,例如二元交叉熵。

這裡損失的計算最大問題是損失的量級是不一樣的,並且損失的權重也是不相同的,這是一個一直在被深入研究的問題,我們這裡暫不做討論,我們只使用簡單的相加,所以我們的一些超引數如下:

model = HydraNet()。to(device=device)

ethnicity_loss = nn。CrossEntropyLoss()

gender_loss = nn。BCELoss()

age_loss = nn。L1Loss()

sig = nn。Sigmoid()

optimizer = torch。optim。SGD(model。parameters(), lr=1e-4, momentum=0。09)

然後我們訓練的迴圈如下:

for epoch in range(n_epochs):

model。train()

total_training_loss = 0

for i, data in enumerate(tqdm(train_dataloader)):

inputs = data[“image”]。to(device=device)

age_label = data[“age”]。to(device=device)

gender_label = data[“gender”]。to(device=device)

eth_label = data[“ethnicity”]。to(device=device)

optimizer。zero_grad()

age_output, gender_output, eth_output = model(inputs)

loss_1 = ethnicity_loss(eth_output, eth_label)

loss_2 = gender_loss(sig(gender_output), gender_label。unsqueeze(1)。float())

loss_3 = age_loss(age_output, age_label。unsqueeze(1)。float())

loss = loss_1 + loss_2 + loss_3

loss。backward()

optimizer。step()

total_training_loss += loss

這樣我們最簡單的多工學習的流程就完成了

關於損失的最佳化

多工學習的損失函式,對每個任務的損失進行權重分配,在這個過程中,必須保證所有任務同等重要,而不能讓簡單任務主導整個訓練過程。手動的設定權重是低效而且不是最優的,因此,自動的學習這些權重是十分必要的,

Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics cvpr_2018

這篇論文提出,將不同的loss拉到統一尺度下,這樣就容易統一,具體的辦法就是利用同方差的不確定性,將不確定性作為噪聲,進行訓練

End-to-End Multi-Task Learning with Attention cvpr_2019

這篇論文提出了一種可以自動調節權重的機制( Dynamic Weight Average),使得權重分配更加合理,大概的意思是每個任務首先計算前個epoch對應損失的比值,然後除以一個固定的值T,進行exp對映後,計算各個損失所佔比

最後如果你對多工學習感興趣,可以先看看這篇論文:

A Survey on Multi-Task Learning arXiv 1707.08114

從演算法建模、應用和理論分析的角度對MTL進行了調查,是入門的最好的資料。

https://avoid。overfit。cn/post/57d4e8712c634fe887247ce66e694f8f

作者:Alessandro Lamberti

Top