共计 4298 个字符,预计需要花费 11 分钟才能阅读完成。
在机器学习中,咱们通常致力于针对单个工作,也就是优化单个指标。然而多任务学习 (MTL) 在机器学习的许多利用中都获得了胜利,从自然语言解决和语音辨认到计算机视觉和药物发现。
MTL 最驰名的例子可能是特斯拉的主动驾驶零碎。在主动驾驶中须要同时解决大量工作,如物体检测、深度预计、3D 重建、视频剖析、跟踪等,你可能认为须要 10 个以上的深度学习模型,但事实并非如此。
HydraNet 介绍
一般来说多任务学的模型架构非常简单:一个骨干网络作为特色的提取,而后针对不同的工作创立多个头。利用繁多模型解决多个工作。
上图能够看到,特征提取模型提取图像特色。输入最初被宰割成多个头,每个头负责一个特定的状况,因为它们彼此独立能够独自进行微调!
特斯拉的讲演中具体的阐明这个模型(youtube:v=3SypMvnQT_s)
多任务学习我的项目
在本文中,咱们将介绍如何在 Pytorch 中实现一个更简略的 HydraNet。这里将应用 UTK Face 数据集,这是一个带有 3 个标签 (性别、种族、年龄) 的分类数据集。
咱们的 HydraNet 将有三个独立的头,它们都是不同的,因为年龄的预测是一个回归工作,种族的预测是一个多类分类问题,性别的预测是一个二元分类工作。
每一个 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