关于人工智能:基于关系推理的自监督学习无标记训练

38次阅读

共计 12886 个字符,预计需要花费 33 分钟才能阅读完成。

作者 |Chien Vu
编译 |Flin
起源 |towardsdatascience

背景与挑战????

在古代深度学习算法中,对未标记数据的手工标注是其次要局限性之一。为了训练一个好的模型,咱们通常须要筹备大量的标记数据。在少数类和数据的状况下,咱们能够应用带有标签的公共数据集的预训练模型,并应用你的数据微调最初几层即可。

然而,当你的数据很大时(比方商店中的产品或人的脸,..),很容易遇到问题,并且仅通过几个可训练的层就很难学习模型。此外,未标记数据(例如,文档文本,Internet 上的图像)的数量是不可数的。为工作标记所有标签简直是不可能的,然而不应用它们相对是一种节约。

在这种状况下,应用新的数据集从头开始训练深度模型是一种抉择,然而应用事后训练的深度模型时标记数据须要破费大量的工夫和精力,这仿佛不再有帮忙。这就是自监督学习诞生的起因。这背地的想法很简略,次要有两个工作:

  • 代理工作:深度模型将从没有正文的未标记数据中学习可演绎的示意,而后可能利用隐式信息自行生成监督信号。
  • 上游工作:将针对监督学习工作(如分类和图像检索)对示意进行微调,标记数据的数量更少(标记数据的数量取决于模型的性能,具体取决于你的需要)

学习这些示意有很多不同的训练方法:

  • 绝对地位[1]:模型须要了解对象的空间上下文,以判断部件之间的绝对地位;
  • 拼图游戏[2]:模型须要将 9 个打乱的补丁放回原始地位;
  • 着色[3]:模型已训练为对灰度输出图像进行着色;确切的工作是将该图像映射到量化的色彩值输入上的散布;
  • 计数特色[4]:模型利用输出图像的特色计数关系,通过缩放战争铺来学习特色编码器;
  • SimCLR[5]:该模型通过潜在空间中的比照损失来最大化同一样本不同加强视图之间的一致性,从而学习视觉输出的示意模式。

不过,我想介绍一种乏味的办法,它可能辨认像人类一样的货色。人类学习的要害是通过比拟相干实体和不同实体来获取新常识。因而,如果咱们可能通过关系推理方法在自监督机器学习中利用类似的机制,这是一个十分重要的解决方案[6]。

关系推理范式基于一个要害的设计准则:应用关系网络作为未标记数据集的可学习函数,量化同一对象视图之间的关系(外部推理)和不同场景中不同对象之间的关系(交互推理)。通过在规范数据集(CIFAR-10、CIFAR-100、CIFAR-100-20、STL-10、tiny-ImageNet、SlimageNet)、学习进度和骨干(两者)上的性能来评估通过关系推理在自监督机器学习中利用相似机制的可能性。

结果表明,关系推理方法在所有条件下都比最好的竞争对手均匀高出 14% 的准确率,而最新的办法比此文(https://arxiv.org/abs/2006.05849)的办法高出 3%。

技术亮点????

简略地说,关系推理只是一种方法论,它试图帮忙学习者了解不同对象(思维)之间的关系,而不是独自地学习对象。这有助于学习者依据本人的差别轻松地分别和记忆物体。

关系推理零碎有两个次要组成部分:骨干构造 关系头。在代理工作阶段应用关系头来反对底层神经网络骨干学习未标记数据集中的有用示意,而后将其抛弃。在代理工作训练后,将骨干构造用于后续工作,如分类或图像检索。

  • 以前的工作:关注场景内的关系,意思是同一对象中的所有元素都属于同一场景(例如,篮子中的球);在标签数据集上进行训练,次要指标是关系。
  • 新的办法:关注同一对象不同视图之间的关系(外部推理)和不同场景中不同对象之间的关系(交互推理);对未标记的数据应用关系推理,关系头是学习底层骨干中有用示意的代理工作。

让咱们来讨论一下关系推理零碎某些局部的 要点

  1. 小批量加强

如前所述,本零碎引入了外部推理和交互推理?为什么咱们须要他们?当没有给出标签时,不可能创立一对类似和不同的对象。为了解决这个问题,采纳了自举技术,造成了外部推理和交互推理,其中:

  • 外部推理 由对同一对象{A1; A2}(正对)(例如,同一篮球的不同视角)
  • 交互推理 包含耦合两个随机对象{A1; B1}(负对)(例如带随机球的篮球)

此外,还思考应用随机加强函数(如几何变换、色彩失真)使场景间的推理更加简单。这些加强性能的益处迫使学习者(骨干)留神更宽泛的特色(例如色彩、尺寸、纹理等)之间的相关性。

例如,在 {foot ball,basket ball} 对中,色彩自身就能够很好地预测类。然而,随着色彩和形态大小的随机变动,学习者当初很难辨别这两种色彩之间的差别。学习者必须思考另一个特色,因而,它能够提供更好的示意。

  1. 度量学习

度量学习的目标是应用间隔度量来靠近类似输出(正输出)的示意,同时移开不同输出(负)的示意。然而,在关系推理中,度量学习有着基本的不同:

  1. 损失函数

学习指标是一个基于示意对的二元分类问题。因而,咱们能够应用二进制穿插熵损失来最大化伯努利对数似然,其中关系分数 y 示意通过 sigmoid 激活函数诱导的示意成员的概率预计。

最初,本文 [6] 还提供了在规范数据集(CIFAR-10、CIFAR-100、CIFAR-100-20、STL-10、tiny-ImageNet、SlimageNet)、不同骨干(浅层和深层)、雷同的学习进度(epochs)上的关系推理后果。后果如下,欲了解更多信息,请查阅他的论文。

试验评估????

在本文中,我想在公共图像数据集 STL-10 上重现关系推理零碎。该数据集由 10 个类(飞机、鸟、汽车、猫、鹿、狗、马、猴、船、卡车)组成,色彩为 96×96 像素。

首先,咱们须要导入一些重要的库

import torch
import torchvision
import torchvision.transforms as transforms
from PIL import Image
import math
import time
from torch.utils.data import DataLoader
from time import sleep
from tqdm import tqdm
import numpy as np
from fastprogress.fastprogress import master_bar, progress_bar
from torchvision import models
import matplotlib.pyplot as plt
from torchvision.utils import make_grid
%config InlineBackend.figure_format = 'svg'

STL-10 数据集蕴含 1300 个标记图像(500 个用于训练,800 个用于测试)。然而,它也包含 100000 个未标记的图像,这些图像来自类似但更宽泛的散布。例如,除了标签集中的动物外,它还蕴含其余类型的动物(熊、兔子等)和车辆(火车、公共汽车等)

而后依据作者的倡议创立关系推理类

class RelationalReasoning(torch.nn.Module):
  """ 自监督关系推理。办法的根本实现,它应用“cat”聚合函数(最无效),可与任何骨干一起应用。"""
  def __init__(self, backbone, feature_size=64):
    super(RelationalReasoning, self).__init__()
    self.backbone = backbone.to(device)
    self.relation_head = torch.nn.Sequential(torch.nn.Linear(feature_size*2, 256),
                             torch.nn.BatchNorm1d(256),
                             torch.nn.LeakyReLU(),
                             torch.nn.Linear(256, 1)).to(device)

  def aggregate(self, features, K):
    relation_pairs_list = list()
    targets_list = list()
    size = int(features.shape[0] / K)
    shifts_counter=1
    for index_1 in range(0, size*K, size):
      for index_2 in range(index_1+size, size*K, size):
        # 默认状况下应用“cat”聚合函数
        pos_pair = torch.cat([features[index_1:index_1+size], 
                              features[index_2:index_2+size]], 1)
        # 通过滚动小批无碰撞的洗牌(负)
        neg_pair = torch.cat([features[index_1:index_1+size], 
                     torch.roll(features[index_2:index_2+size], 
                     shifts=shifts_counter, dims=0)], 1)
        relation_pairs_list.append(pos_pair)
        relation_pairs_list.append(neg_pair)
        targets_list.append(torch.ones(size, dtype=torch.float32))
        targets_list.append(torch.zeros(size, dtype=torch.float32))
        shifts_counter+=1
        if(shifts_counter>=size): 
            shifts_counter=1 # avoid identity pairs
    relation_pairs = torch.cat(relation_pairs_list, 0)
    targets = torch.cat(targets_list, 0)
    return relation_pairs.to(device), targets.to(device)

  def train(self, tot_epochs, train_loader):
    optimizer = torch.optim.Adam([{'params': self.backbone.parameters()},
                  {'params': self.relation_head.parameters()}])                               
    BCE = torch.nn.BCEWithLogitsLoss()
    self.backbone.train()
    self.relation_head.train()
    mb = master_bar(range(1, tot_epochs+1))

    for epoch in mb:
      # 理论指标被抛弃(无监督)train_loss = 0 
      accuracy_list = list()
      
      for data_augmented, _ in progress_bar(train_loader, parent=mb):

        K = len(data_augmented) # tot augmentations
        x = torch.cat(data_augmented, 0).to(device)
        optimizer.zero_grad()              
        # 前向流传(骨干)features = self.backbone(x)
        # 聚合函数
        relation_pairs, targets = self.aggregate(features, K)
        # 前向流传 (关系头)
        score = self.relation_head(relation_pairs).squeeze()       
        # 穿插熵损失与向后流传
        loss = BCE(score, targets)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()*K 

        predicted = torch.round(torch.sigmoid(score))
        correct = predicted.eq(targets.view_as(predicted)).sum()
        accuracy = (correct / float(len(targets))).cpu().numpy()
        accuracy_list.append(accuracy)

      epoch_loss = train_loss / len(train_loader.sampler)
      epoch_accuracy = sum(accuracy_list)/len(accuracy_list)*100
      
      mb.write(f"Epoch [{epoch}/{tot_epochs}] - Accuracy: {epoch_accuracy:.2f}% - Loss: {epoch_loss:.4f}")

为了比拟关系推理方法在浅层模型和深层模型上的性能,咱们将创立一个浅层模型(Conv4),并应用深层模型的构造(Resnet34)。

backbone = Conv4() # 浅层模型
backbone = models.resnet34(pretrained = False) # 深层模型

依据作者的倡议,设置了一些超参数和加强策略。咱们将在未标记的 STL-10 数据集上用关系头训练骨干。

# 模仿的超参数
K = 16 # tot augmentations, 论文中 K=32 
batch_size = 64 # 论文中应用 64
tot_epochs = 10 # 论文中应用 200
feature_size = 64 # Conv4 骨干的单元数
feature_size = 1000 # Resnet34 骨干的单元数 backbone
# 裁减策略
normalize = transforms.Normalize(mean=[0.4406, 0.4273, 0.3858],
                                 std=[0.2687, 0.2613, 0.2685])  
color_jitter = transforms.ColorJitter(brightness=0.8, contrast=0.8, 
                                      saturation=0.8, hue=0.2)
rnd_color_jitter = transforms.RandomApply([color_jitter], p=0.8)
rnd_gray = transforms.RandomGrayscale(p=0.2)
rnd_rcrop = transforms.RandomResizedCrop(size=96, scale=(0.08, 1.0),
                                         interpolation=2)
rnd_hflip = transforms.RandomHorizontalFlip(p=0.5)
train_transform = transforms.Compose([rnd_rcrop, rnd_hflip,
                                      rnd_color_jitter, rnd_gray, 
                                      transforms.ToTensor(), normalize])
# 加载到数据加载器
torch.manual_seed(1)
torch.cuda.manual_seed(1)
train_set = MultiSTL10(K=K, root='data', split='unlabeled', transform=train_transform, download=True)
train_loader = DataLoader(train_set,batch_size=batch_size, shuffle=True,num_workers=2, pin_memory=True)

到目前为止,咱们曾经发明了训练咱们模型所需的所有。当初咱们将在 10 个期间和 16 个加强图像(K)中训练骨干和关系头模型,应用 1 个 GPU Tesla P100-PCIE-16GB 在浅层模型(Conv4)上破费 4 个小时,在深层模型(Resnet34)上破费 6 个小时(你能够自在地更改期间数以及另一个超参数以取得更好的后果)

device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
backbone.to(device)
model = RelationalReasoning(backbone, feature_size)    
model.train(tot_epochs=tot_epochs, train_loader=train_loader)
torch.save(model.backbone.state_dict(), 'model.tar')

在训练了咱们的骨干模型之后,咱们抛弃了关系头,只将骨干用于上游工作。咱们须要应用 STL-10(500 个图像)中的标记数据来微调咱们的骨干,并在测试集中测试最终的模型(800 个图像)。训练和测试数据集将加载到 Dataloader 中,而无需进行裁减。

# set random seed
torch.manual_seed(1)
torch.cuda.manual_seed(1)

# no augmentations used for linear evaluation
transform_lineval = transforms.Compose([transforms.ToTensor(), normalize])

# Download STL10 labeled train and test dataset
train_set_lineval = torchvision.datasets.STL10('data', split='train', transform=transform_lineval)
test_set_lineval = torchvision.datasets.STL10('data', split='test', transform=transform_lineval)

# Load dataset in data loader
train_loader_lineval = DataLoader(train_set_lineval, batch_size=128, shuffle=True)
test_loader_lineval = DataLoader(test_set_lineval, batch_size=128, shuffle=False)

咱们将加载预训练的骨干模型,并应用一个简略的线性模型将输出特性与数据集中的许多类连接起来。

# linear model
linear_layer = torch.nn.Linear(64, 10) # if backbone is Conv4
linear_layer = torch.nn.Linear(1000, 10) # if backbone is Resnet34
# defining a raw backbone model
backbone_lineval = Conv4() # Conv4
backbone_lineval = models.resnet34(pretrained = False) # Resnet34
# load model
checkpoint = torch.load('model.tar') # name of pretrain weight
backbone_lineval.load_state_dict(checkpoint)

此时,只训练线性模型,解冻骨干模型。首先,咱们将看到微调 Conv4 的后果

device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
optimizer = torch.optim.Adam(linear_layer.parameters())                               
CE = torch.nn.CrossEntropyLoss()
linear_layer.to(device)
linear_layer.train()
backbone_lineval.to(device)
backbone_lineval.eval()

print('Linear evaluation')
for epoch in range(20):
    accuracy_list = list()
    for i, (data, target) in enumerate(train_loader_lineval):
        optimizer.zero_grad()
        data = data.to(device)
        target= target.to(device)
        output = backbone_lineval(data).to(device).detach()
        output = linear_layer(output)
        loss = CE(output, target)
        loss.backward()
        optimizer.step()
        # estimate the accuracy
        prediction = output.argmax(-1)
        correct = prediction.eq(target.view_as(prediction)).sum()
        accuracy = (100.0 * correct / len(target))
        accuracy_list.append(accuracy.item())
    print('Epoch [{}] loss: {:.5f}; accuracy: {:.2f}%' \
            .format(epoch+1, loss.item(), sum(accuracy_list)/len(accuracy_list)))

Linear evaluation
Epoch [1] loss: 2.24857; accuracy: 14.77%
Epoch [2] loss: 2.23015; accuracy: 24.49%
Epoch [3] loss: 2.18529; accuracy: 32.46%
Epoch [4] loss: 2.24595; accuracy: 36.45%
Epoch [5] loss: 2.09482; accuracy: 42.46%
Epoch [6] loss: 2.11192; accuracy: 43.40%
Epoch [7] loss: 2.05064; accuracy: 47.29%
Epoch [8] loss: 2.03494; accuracy: 47.38%
Epoch [9] loss: 1.91709; accuracy: 47.46%
Epoch [10] loss: 1.99181; accuracy: 48.03%
Epoch [11] loss: 1.91527; accuracy: 48.28%
Epoch [12] loss: 1.93190; accuracy: 49.55%
Epoch [13] loss: 2.00492; accuracy: 49.71%
Epoch [14] loss: 1.85328; accuracy: 49.94%
Epoch [15] loss: 1.88910; accuracy: 49.86%
Epoch [16] loss: 1.88084; accuracy: 50.76%
Epoch [17] loss: 1.63443; accuracy: 50.74%
Epoch [18] loss: 1.76303; accuracy: 50.62%
Epoch [19] loss: 1.70486; accuracy: 51.46%
Epoch [20] loss: 1.61629; accuracy: 51.84%

而后查看测试集

accuracy_list = list()
for i, (data, target) in enumerate(test_loader_lineval):
    data = data.to(device)
    target= target.to(device)
    output = backbone_lineval(data).detach()
    output = linear_layer(output)
    # estimate the accuracy
    prediction = output.argmax(-1)
    correct = prediction.eq(target.view_as(prediction)).sum()
    accuracy = (100.0 * correct / len(target))
    accuracy_list.append(accuracy.item())

print('Test accuracy: {:.2f}%'.format(sum(accuracy_list)/len(accuracy_list)))

Test accuracy: 49.98%

Conv4 在测试集上取得了 49.98% 的准确率,这意味着骨干模型能够在未标记的数据集中学习有用的特色,只需在很少的时间段内进行微调就能够达到很好的成果。当初让咱们查看深度模型的性能。

device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
optimizer = torch.optim.Adam(linear_layer.parameters())                               
CE = torch.nn.CrossEntropyLoss()
linear_layer.to(device)
linear_layer.train()
backbone_lineval.to(device)
backbone_lineval.eval()

print('Linear evaluation')
for epoch in range(20):
    accuracy_list = list()
    for i, (data, target) in enumerate(train_loader_lineval):
        optimizer.zero_grad()
        data = data.to(device)
        target= target.to(device)
        output = backbone_lineval(data).to(device).detach()
        output = linear_layer(output)
        loss = CE(output, target)
        loss.backward()
        optimizer.step()
        # estimate the accuracy
        prediction = output.argmax(-1)
        correct = prediction.eq(target.view_as(prediction)).sum()
        accuracy = (100.0 * correct / len(target))
        accuracy_list.append(accuracy.item())
    print('Epoch [{}] loss: {:.5f}; accuracy: {:.2f}%' \
            .format(epoch+1, loss.item(), sum(accuracy_list)/len(accuracy_list)))

Linear evaluation
Epoch [1] loss: 2.68060; accuracy: 47.79%
Epoch [2] loss: 1.56714; accuracy: 58.34%
Epoch [3] loss: 1.18530; accuracy: 56.50%
Epoch [4] loss: 0.94784; accuracy: 57.91%
Epoch [5] loss: 1.48861; accuracy: 57.56%
Epoch [6] loss: 0.91673; accuracy: 57.87%
Epoch [7] loss: 0.90533; accuracy: 58.96%
Epoch [8] loss: 2.10333; accuracy: 57.40%
Epoch [9] loss: 1.58732; accuracy: 55.57%
Epoch [10] loss: 0.88780; accuracy: 57.79%
Epoch [11] loss: 0.93859; accuracy: 58.44%
Epoch [12] loss: 1.15898; accuracy: 57.32%
Epoch [13] loss: 1.25100; accuracy: 57.79%
Epoch [14] loss: 0.85337; accuracy: 59.06%
Epoch [15] loss: 1.62060; accuracy: 58.91%
Epoch [16] loss: 1.30841; accuracy: 58.95%
Epoch [17] loss: 0.27441; accuracy: 58.11%
Epoch [18] loss: 1.58133; accuracy: 58.73%
Epoch [19] loss: 0.76258; accuracy: 58.81%
Epoch [20] loss: 0.62280; accuracy: 58.50%

而后评估测试数据集

accuracy_list = list()
for i, (data, target) in enumerate(test_loader_lineval):
    data = data.to(device)
    target= target.to(device)
    output = backbone_lineval(data).detach()
    output = linear_layer(output)
    # estimate the accuracy
    prediction = output.argmax(-1)
    correct = prediction.eq(target.view_as(prediction)).sum()
    accuracy = (100.0 * correct / len(target))
    accuracy_list.append(accuracy.item())

print('Test accuracy: {:.2f}%'.format(sum(accuracy_list)/len(accuracy_list)))

Test accuracy: 55.38%

这是更好的,咱们能够在测试集上取得 55.38%的精度。本文的次要目标是重现和评估关系推理方法论,以领导模型辨认无标签对象,因而,这些后果是十分有前途的。如果你感觉不称心,你能够通过扭转超参数来自在地做试验,比方减少数量,期间,或者扭转模型构造。

最初的想法????

自监督关系推理在定量和定性两方面都是无效的,并且具备从浅到深的不同大小的骨干。通过比拟学习到的示意能够很容易地从一个畛域转移到另一个畛域,它们具备细粒度和紧凑性,这可能是因为精度和裁减次数之间的相关性。在关系推理中,依据作者的试验,裁减的数量对对象簇的品质有着次要的影响[4]。自监督学习在许多方面都有很强的后劲成为机器学习的将来。

参考文献

[1] Carl Doersch et. al, Unsupervised Visual Representation Learning by Context Prediction, 2015.

[2] Mehdi Noroozi et. al, Unsupervised Learning of Visual Representations by Solving Jigsaw Puzzles, 2017.

[3] Zhang et. al, Colorful Image Colorization, 2016.

[4] Mehdi Noroozi et. al, Representation Learning by Learning to Count, 2017.

[5] Ting Chen et. al, A Simple Framework for Contrastive Learning of Visual Representations, 2020.

[6] Massimiliano Patacchiola et. al, Self-Supervised Relational Reasoning for
Representation Learning, 2020.

[7] Adam Santoro et. al, Relational recurrent neural networks, 2018.

原文链接:https://towardsdatascience.co…

欢送关注磐创 AI 博客站:
http://panchuang.net/

sklearn 机器学习中文官网文档:
http://sklearn123.com/

欢送关注磐创博客资源汇总站:
http://docs.panchuang.net/

正文完
 0