[PYTHON] [Didacticiel PyTorch ⑧] Didacticiel de mise au point de la détection d'objets Torch Vision

introduction

Ceci est le 8ème volet de PyTorch Tutoriel officiel après Dernière fois. Cette fois, nous allons procéder avec TorchVision Object Detection Finetuning Tutorial.

TorchVision Object Detection Finetuning Tutorial

Dans ce tutoriel, nous utiliserons le [Mask R-CNN] pré-entraîné (https://arxiv.org/abs/1703.06870) pour voir les réglages fins et le transfert d'apprentissage. Les données utilisées pour la formation sont les données de Penn-Fudan pour la détection et la segmentation des piétons. Pour ces données, 170 images avec 345 piétons (instances) sont préparées.

Tout d'abord, vous devez installer la bibliothèque pycocotools. Cette bibliothèque est utilisée pour calculer une note appelée "Intersection over Union". "Intersection over Union" est l'une des méthodes pour évaluer la condition de correspondance des zones dans la détection d'objets.

%%shell

pip install cython
#Installez pycocotools. La version par défaut de Colab est https://github.com/cocodataset/cocoapi/pull/Il y a un bug corrigé dans 354.
pip install -U 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'

Définition de l'ensemble de données

Définissez le jeu de données. L'ensemble de données nécessite certains attributs pour tirer parti du modèle entraîné par Mask R-CNN. Données requises à l'aide du script torchvision (détection d'objets, segmentation d'instance, bibliothèque de détection de points clés de personne) Vous pouvez créer un ensemble.

L'ensemble de données nécessite les attributs suivants:

(Une description approximative de l'ensemble de données est que les boîtes définissent un quadrangle contenant des objets et que les masques définissent s'il s'agit ou non d'un objet en pixels.) Si le modèle renvoie la méthode ci-dessus, le modèle fonctionne à la fois pour la formation et l'évaluation, et le script d'évaluation pycocotools est utilisé.

Écriture d'un jeu de données personnalisé pour Penn-Fudan (Création d'un jeu de données personnalisé pour Penn-Fudan)

Sortons l'ensemble de données de l'ensemble de données Penn-Fudan. Première, https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip Téléchargez et décompressez le fichier zip.

%%shell

# download the Penn-Fudan dataset
wget https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip .
# extract it in the current folder
unzip PennFudanPed.zip

Les données ont la structure suivante.

PennFudanPed/
  PedMasks/
    FudanPed00001_mask.png
    FudanPed00002_mask.png
    FudanPed00003_mask.png
    FudanPed00004_mask.png
    ...
  PNGImages/
    FudanPed00001.png
    FudanPed00002.png
    FudanPed00003.png
    FudanPed00004.png

Montrons la première image.

from PIL import Image
Image.open('PennFudanPed/PNGImages/FudanPed00001.png')

ダウンロード.png

(Comme décrit dans le fichier readme.txt décompressé, l'image de masque est une image avec un arrière-plan de "0" et une étiquette de 1 ou plus pour chaque piéton.)

mask = Image.open('PennFudanPed/PedMasks/FudanPed00001_mask.png')
#Chaque occurrence de masque a une couleur différente de zéro à N.
#Où N est le nombre d'instances (piétons). Pour faciliter la visualisation
#Ajoutons une palette de couleurs au masque.
mask.putpalette([
    0, 0, 0, # black background
    255, 0, 0, # index 1 is red
    255, 255, 0, # index 2 is yellow
    255, 153, 0, # index 3 is orange
])
mask

ダウンロード.png

Dans ces données, il y a un masque qui identifie chaque image et chaque piéton, et chaque couleur du masque correspond à chaque piéton. Créons la classe torch.utils.data.Dataset pour cet ensemble de données.

import os
import numpy as np
import torch
import torch.utils.data
from PIL import Image


class PennFudanDataset(torch.utils.data.Dataset):
    def __init__(self, root, transforms=None):
        self.root = root
        self.transforms = transforms
        #Charger et trier tous les fichiers image
        self.imgs = list(sorted(os.listdir(os.path.join(root, "PNGImages"))))
        self.masks = list(sorted(os.listdir(os.path.join(root, "PedMasks"))))

    def __getitem__(self, idx):
        #Charger des images et des masques
        img_path = os.path.join(self.root, "PNGImages", self.imgs[idx])
        mask_path = os.path.join(self.root, "PedMasks", self.masks[idx])
        img = Image.open(img_path).convert("RGB")
        #Parce que chaque couleur correspond à une instance différente et 0 est l'arrière-plan
        #Notez que nous n'avons pas converti le masque en RVB
        mask = Image.open(mask_path)

        mask = np.array(mask)
        #Les instances sont codées sous différentes couleurs
        obj_ids = np.unique(mask)
        #Le premier identifiant est l'arrière-plan, supprimez-le
        obj_ids = obj_ids[1:]

        # split the color-encoded mask into a set
        # of binary masks
        #Divisez le masque de couleur en un ensemble de masques binaires
        masks = mask == obj_ids[:, None, None]

        # get bounding box coordinates for each mask
        #Obtenez les coordonnées de la boîte englobante pour chaque masque
        num_objs = len(obj_ids)
        boxes = []
        for i in range(num_objs):
            pos = np.where(masks[i])
            xmin = np.min(pos[1])
            xmax = np.max(pos[1])
            ymin = np.min(pos[0])
            ymax = np.max(pos[0])
            boxes.append([xmin, ymin, xmax, ymax])

        boxes = torch.as_tensor(boxes, dtype=torch.float32)
        # there is only one class
        #Il n'y a qu'une seule classe
        labels = torch.ones((num_objs,), dtype=torch.int64)
        masks = torch.as_tensor(masks, dtype=torch.uint8)

        image_id = torch.tensor([idx])
        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])
        # suppose all instances are not crowd
        #Supposons que toutes les instances ne soient pas encombrées
        iscrowd = torch.zeros((num_objs,), dtype=torch.int64)

        target = {}
        target["boxes"] = boxes
        target["labels"] = labels
        target["masks"] = masks
        target["image_id"] = image_id
        target["area"] = area
        target["iscrowd"] = iscrowd

        if self.transforms is not None:
            img, target = self.transforms(img, target)

        return img, target

    def __len__(self):
        return len(self.imgs)

C'est tout pour l'ensemble de données. Voyons comment la sortie de cet ensemble de données est organisée

dataset = PennFudanDataset('PennFudanPed/')
dataset[0]

out


(<PIL.Image.Image image mode=RGB size=559x536 at 0x7FC7AC4B62E8>,
 {'area': tensor([35358., 36225.]), 'boxes': tensor([[159., 181., 301., 430.],
          [419., 170., 534., 485.]]), 'image_id': tensor([0]), 'iscrowd': tensor([0, 0]), 'labels': tensor([1, 1]), 'masks': tensor([[[0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           ...,
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0]],
  
          [[0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           ...,
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0]]], dtype=torch.uint8)})

Vous pouvez voir que l'ensemble de données renvoie PIL.Image et un dictionnaire contenant certains champs tels que boxes, labels, masks.

Bien qu'il ne soit pas dans le didacticiel, le code suivant peut illustrer des boîtes et des masques. les cases sont des carrés contenant des instances (personnes) et les masques sont les instances elles-mêmes.

import matplotlib.pyplot as plt
import matplotlib.patches as patches

fig, ax = plt.subplots()

target = dataset[0][1]

#Masques de première instance
masks_0 = target['masks'][0,:,:]

#Boîtes de première instance
boxes_0 = target['boxes'][0]

#Masque de sortie
ax.imshow(masks_0)

#Boîtes de sortie
ax.add_patch(
     patches.Rectangle(
        (boxes_0[0], boxes_0[1]),boxes_0[2] - boxes_0[0], boxes_0[3] - boxes_0[1],
        edgecolor = 'blue',
        facecolor = 'red',
        fill=True,
        alpha=0.5
     ) )

plt.show()

ダウンロード.png

Defining your model

Ce didacticiel utilise MaskR-CNN, basé sur FasterR-CNN. Faster R-CNN est un algorithme de détection d'objet qui prédit à la fois le cadre de sélection et le score de classe des objets potentiels dans une image (le carré contenant l'objet et ce qu'est l'objet). (L'image ci-dessous est une image traitée de Faster R-CNN)

Faster R-CNN

Mask R-CNN est une version améliorée de Faster R-CNN qui évalue non seulement la détection d'objets par carré (boîte), mais aussi par unité de pixel (masque). (L'image ci-dessous est une image traitée du masque R-CNN)

Mask R-CNN

Il y a deux raisons principales pour personnaliser un modèle avec Torchvision. La première consiste à tirer parti d'un modèle pré-entraîné et à modifier la dernière couche. L'autre est lorsque vous souhaitez remplacer le backbone de votre modèle par un autre backbone. (Par exemple, pour une prédiction plus rapide) Prenons un exemple concret.

1. Réglage fin à partir d'un modèle pré-entraîné

Voici comment utiliser un modèle pré-entraîné pour ajuster la classe que vous souhaitez identifier.

import torchvision
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor

#Charger des modèles pré-entraînés chez COCO
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)

#Classificateur, nombre défini par l'utilisateur_Remplacer par un nouveau classificateur avec des classes
num_classes = 2  # 1 class (person) + background :1 cours (personne)+Contexte
#Obtient le nombre de fonctionnalités d'entrée du classificateur
in_features = model.roi_heads.box_predictor.cls_score.in_features
#Remplacez la tête pré-entraînée par une nouvelle
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes) 

2. Modification du modèle pour ajouter un backbone différent (Modifiez le modèle pour ajouter un backbone différent)

L'autre cas est celui où vous souhaitez remplacer le backbone du modèle par un autre backbone. Par exemple, la dorsale par défaut actuelle (ResNet-50) peut être trop grande dans certaines situations et vous souhaiterez peut-être tirer parti d'un modèle plus petit. Ce qui suit décrit comment utiliser la vision de la torche pour changer la dorsale.

import torchvision
from torchvision.models.detection import FasterRCNN
from torchvision.models.detection.rpn import AnchorGenerator

#Charge un modèle pré-entraîné pour la classification et renvoie uniquement les fonctionnalités
backbone = torchvision.models.mobilenet_v2(pretrained=True).features
#FasterRCNN a besoin de connaître le nombre de canaux de sortie dans le backbone.
# mobilenet_Pour la v2, c'est 1280, vous devez donc l'ajouter ici
backbone.out_channels = 1280

#En RPN, avec 5 tailles différentes et 3 formats d'image différents
#Générons 5 x 3 ancres pour chaque position spatiale.
#Parce que la taille et le rapport hauteur / largeur de chaque carte de caractéristiques peuvent être différents
# Tuple [Tuple [int]]il y a.
anchor_generator = AnchorGenerator(sizes=((32, 64, 128, 256, 512),),
                                   aspect_ratios=((0.5, 1.0, 2.0),))

#Avec la carte des caractéristiques utilisée pour effectuer le découpage de la zone d'intérêt,
#Définissons la taille de la garniture après la remise à l'échelle.
#Si le backbone renvoie Tensor, featmap_les noms sont[0]Devrait être.
#Plus généralement, l'épine dorsale est OrderedDict[Tensor]Doit être retourné,
# featmap_Vous pouvez sélectionner la carte des caractéristiques à utiliser avec les noms.
roi_pooler = torchvision.ops.MultiScaleRoIAlign(featmap_names=[0],
                                                output_size=7,
                                                sampling_ratio=2)

#Assemblez les pièces dans un modèle RCNN plus rapide
model = FasterRCNN(backbone,
                   num_classes=2,
                   rpn_anchor_generator=anchor_generator,
                   box_roi_pool=roi_pooler)

Un modèle de segmentation d'instance pour l'ensemble de données PennFudan (modèle de segmentation d'instance d'ensemble de données PennFudan)

Dans ce cas, le jeu de données est très petit, nous allons donc peaufiner le modèle pré-entraîné. Par conséquent, suivez l'approche numéro 1. Nous utiliserons ici le masque R-CNN pour calculer également le masque de segmentation de l'instance (pour déterminer la zone de la personne en pixels).

import torchvision
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
from torchvision.models.detection.mask_rcnn import MaskRCNNPredictor

      
def get_instance_segmentation_model(num_classes):
    #Charger un modèle de segmentation d'instance COCO pré-entraîné
    model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=True)

    #Obtient le nombre de fonctionnalités d'entrée du classificateur
    in_features = model.roi_heads.box_predictor.cls_score.in_features
    #Remplacez la tête pré-entraînée par une nouvelle
    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)

    #mask Obtient le nombre de fonctionnalités d'entrée du classificateur
    in_features_mask = model.roi_heads.mask_predictor.conv5_mask.in_channels
    hidden_layer = 256
    # and replace the mask predictor with a new one
    #Remplacez le prédicteur de masque par un nouveau
    model.roi_heads.mask_predictor = MaskRCNNPredictor(in_features_mask,
                                                       hidden_layer,
                                                       num_classes)

    return model

Vous êtes maintenant prêt à entraîner et à évaluer votre modèle avec cet ensemble de données.

(En comparant ce modèle avec torchvision.models.detection.maskrcnn_resnet50_fpn, vous pouvez voir que les dimensions des pièces suivantes ont changé.)

  (roi_heads): RoIHeads(
・ ・ ・
    (box_predictor): FastRCNNPredictor(
      (cls_score): Linear(in_features=1024, out_features=2, bias=True)
      (bbox_pred): Linear(in_features=1024, out_features=8, bias=True)
    )
・ ・ ・
    (mask_predictor): MaskRCNNPredictor(
・ ・ ・
      (mask_fcn_logits): Conv2d(256, 2, kernel_size=(1, 1), stride=(1, 1))
    )
  )

Fonctions de formation et d'évaluation

La vision de la torche «vision / références / détection /» a un certain nombre de fonctions d'assistance pour simplifier la formation et l'évaluation des modèles de détection d'objets. Ici, nous utilisons references / detection / engine.py, references / detection / utils.py, references / detection / transforms.py.

Copiez ces fichiers (et leurs fichiers associés) pour utilisation.

%%shell

# Download TorchVision repo to use some files from
# references/detection
git clone https://github.com/pytorch/vision.git
cd vision
git checkout v0.3.0

cp references/detection/utils.py ../
cp references/detection/transforms.py ../
cp references/detection/coco_eval.py ../
cp references/detection/engine.py ../
cp references/detection/coco_utils.py ../

Utilisons les refereces / detection copiées pour créer des fonctions d'aide pour développer / transformer les données.

from engine import train_one_epoch, evaluate
import utils
import transforms as T


def get_transform(train):
    transforms = []
    #Convertir l'image en Tensor
    transforms.append(T.ToTensor())
    if train:
        #Pour la formation, l'image et les données de l'enseignant sont inversées horizontalement de manière aléatoire. (Image reflétée dans le miroir)
        transforms.append(T.RandomHorizontalFlip(0.5))
    return T.Compose(transforms)

Le code ci-dessus est la préparation des données. Convertit l'image en Tensor et l'inverse de manière aléatoire pour les données d'entraînement. Aucune normalisation des données ou redimensionnement de l'image n'est requise. Le modèle Mask R-CNN le fait en interne.

Tout rassembler

L'ensemble de données, le modèle et la préparation des données sont maintenant prêts. Instancions-les.

#Utilise un jeu de données et des transformations définies
dataset = PennFudanDataset('PennFudanPed', get_transform(train=True))
dataset_test = PennFudanDataset('PennFudanPed', get_transform(train=False))

#Divisez l'ensemble de données en données d'entraînement et données de test
torch.manual_seed(1)
indices = torch.randperm(len(dataset)).tolist()
dataset = torch.utils.data.Subset(dataset, indices[:-50])
dataset_test = torch.utils.data.Subset(dataset_test, indices[-50:])

#Définir un chargeur de données d'entraînement et de validation
data_loader = torch.utils.data.DataLoader(
    dataset, batch_size=2, shuffle=True, num_workers=4,
    collate_fn=utils.collate_fn)

data_loader_test = torch.utils.data.DataLoader(
    dataset_test, batch_size=1, shuffle=False, num_workers=4,
    collate_fn=utils.collate_fn)

Instanciez le modèle.

device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')

#Les données des enseignants ne concernent que deux classes, le parcours et la personne
num_classes = 2

#Obtenez le modèle à l'aide de la fonction d'assistance
model = get_instance_segmentation_model(num_classes)
#Déplacez le modèle vers l'appareil approprié
model.to(device)

#Construisez l'optimiseur
params = [p for p in model.parameters() if p.requires_grad]
optimizer = torch.optim.SGD(params, lr=0.005,
                            momentum=0.9, weight_decay=0.0005)

#Planificateur de taux d'apprentissage qui réduit le taux d'apprentissage à 1/10 toutes les 3 époques
lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer,
                                               step_size=3,
                                               gamma=0.1)

Entraînez-vous avec 10 époques. Évaluer avec la fonction d'évaluation à chaque époque. (Il faut environ 8 minutes pour apprendre dans l'environnement GPU Colaboratory. Une erreur d'exécution se produit dans le GPU None.)

#Formation avec 10 époques
num_epochs = 10

for epoch in range(num_epochs):
    print(epoch)
    #Formation 1 Epoch
    train_one_epoch(model, optimizer, data_loader, device, epoch, print_freq=10)
    #Mettre à jour le taux d'apprentissage
    lr_scheduler.step()
    #Évaluer avec un jeu de données de test
    evaluate(model, data_loader_test, device=device)

out


...
Averaged stats: model_time: 0.1179 (0.1174)  evaluator_time: 0.0033 (0.0051)
Accumulating evaluation results...
DONE (t=0.01s).
Accumulating evaluation results...
DONE (t=0.01s).
IoU metric: bbox
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.831
 Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.990
 Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.955
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.543
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.841
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.386
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.881
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.881
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.787
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.887
IoU metric: segm
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.760
 Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.990
 Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.921
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.492
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.771
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.345
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.808
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.808
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.725
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.814

Maintenant que la formation est terminée, voyons à quoi ressemblera l'ensemble de données de test.

#Sélectionnez une image de l'ensemble de test
img, _ = dataset_test[4]
#Mettre le modèle en mode évaluation
model.eval()
with torch.no_grad():
    prediction = model([img.to(device)])

Lorsque vous sortez la prédiction, il s'agit d'une liste de dictionnaires. Puisque nous avons spécifié une donnée de test, l'exemple ci-dessous a un élément dans la liste. Le dictionnaire contient des prédictions d'image. Dans ce cas, vous pouvez voir qu'il contient des boîtes, des étiquettes, des masques et des scores.

prediction

out


[{'boxes': tensor([[173.1167,  27.6446, 240.8375, 313.0114],
          [325.5737,  64.3967, 453.1539, 352.3020],
          [222.4494,  24.5255, 306.5306, 291.5595],
          [296.8205,  21.3736, 379.0592, 263.7513],
          [137.4137,  38.1588, 216.4886, 276.1431],
          [167.8121,  19.9211, 332.5648, 314.0146]], device='cuda:0'),
  'labels': tensor([1, 1, 1, 1, 1, 1], device='cuda:0'),
  'masks': tensor([[[[0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            ...,
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.]]],
  
  
          [[[0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            ...,
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.]]],
  
  
          [[[0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            ...,
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.]]],
  
  
          [[[0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            ...,
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.]]],
  
  
          [[[0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            ...,
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.]]],
  
  
          [[[0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            ...,
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.],
            [0., 0., 0.,  ..., 0., 0., 0.]]]], device='cuda:0'),
  'scores': tensor([0.9965, 0.9964, 0.9942, 0.9696, 0.3053, 0.1552], device='cuda:0')}]

Vérifiez l'image et le résultat de la prédiction. L'image (img) est un Tenseur de [couleur, vertical, horizontal]. La couleur étant de 0 à 1, mettez-la à l'échelle de 0 à 255 et remplacez-la par [Vertical, Horizontal, Couleur].

Image.fromarray(img.mul(255).permute(1, 2, 0).byte().numpy())

ダウンロード.png

Ensuite, visualisez le masque prédit. les masques sont attendus comme «[N, 1, H, W]». Où «N» est le nombre prévu d'instances (personnes). Chaque valeur de masque stocke la probabilité de déterminer «personne» en pixels comme 0-1.

Image.fromarray(prediction[0]['masks'][0, 0].mul(255).byte().cpu().numpy())

ダウンロード.png

(D'autres instances prédites (personnes) peuvent également être visualisées en modifiant la valeur de N comme indiqué ci-dessous.)

Image.fromarray(prediction[0]['masks'][1, 0].mul(255).byte().cpu().numpy())

ダウンロード.png

Image.fromarray(prediction[0]['masks'][2, 0].mul(255).byte().cpu().numpy())

ダウンロード.png

Image.fromarray(prediction[0]['masks'][3, 0].mul(255).byte().cpu().numpy())

ダウンロード.png

Je peux bien le prédire.

Conclusion (Résumé)

Dans ce didacticiel, vous avez appris à entraîner un modèle de détection d'objets à l'aide d'un jeu de données que vous avez défini vous-même. Pour l'ensemble de données, nous avons créé la classe torch.utils.data.Dataset qui contient la boîte et le masque pour définir l'ensemble de données spécifique à la détection d'objets. Nous avons également exploité le modèle MaskR-CNN pré-formé à COCOtrain 2017 pour effectuer l'apprentissage par transfert sur ce nouvel ensemble de données.

Pour des exemples plus détaillés de formation multi-machine / multi-GPU, voir references / detection / train sur torchvision GitHub repo Vérifiez le .py.

À la fin

Dans ce didacticiel, vous avez appris «l'apprentissage par transfert» et «l'ajustement fin» à l'aide d'un modèle pré-entraîné. (Cette fois, il s'agit apparemment de réglage fin, et la différence entre l'apprentissage par transfert et le réglage fin sera expliquée la prochaine fois.) Dans le didacticiel, j'ai essayé avec 120 données d'entraînement et 50 données de vérification, mais même avec environ 40 données d'entraînement, j'ai pu prédire assez correctement. L'apprentissage par transfert est incroyable de pouvoir apprendre avec une si petite quantité de données de test. La prochaine fois, j'aimerais continuer avec "Tutoriel de transfert d'apprentissage pour la vision par ordinateur".

Histoire

2020/11/15 Première édition publiée

Recommended Posts

[Didacticiel PyTorch ⑧] Didacticiel de mise au point de la détection d'objets Torch Vision
Vision par ordinateur: détection d'objets - Suppression non maximale
Vision par ordinateur: Détection d'objets Part2-Détecteur multi-coup unique
Computer Vision: Object Detection Part1 - Prétraitement de la boîte englobante
[Tutoriel PyTorch ①] Qu'est-ce que PyTorch?