[PYTHON] Ours ... pas de segmentation sémantique

J'ai étudié la «segmentation sémantique», qui extrait les choses qui intéressent le deep learning qui traite des images.

Génération d'images d'ours

Le premier problème dans l'apprentissage de la segmentation sémantique était d'obtenir des images. Je ne trouvais pas de sensation agréable, alors j'ai commencé par générer automatiquement une image pour la pratique.

"Données d'image" et "données de réponse correcte" pour la segmentation sémantique à l'aide de la fonction créée dans Générer automatiquement des images de koala et d'ours Je vais le faire moi-même.

from PIL import ImageFilter
import numpy as np
def getdata_for_semantic_segmentation(im):
    x_im = im.filter(ImageFilter.CONTOUR) #Le profilé est utilisé comme "données d'image" pour l'entrée.
    a_im = np.asarray(im) #Convertir en numpy
    #L'ours noir est changé en ours blanc et les autres sont noircis en tant que «données de réponse correcte».
    y_im = Image.fromarray(np.where(a_im == 1, 255, 0).astype(dtype='uint8'))
    return x_im, y_im

J'ai créé 2000 ensembles de données comme suit.

X_data = [] #Pour stocker des données d'image
Y_data = [] #Pour stocker des données de réponse correctes
for i in range(2000): #Générer 2000 images
    #Générer une image d'ours
    im = koala_or_bear(bear=True, rotate=True , resize=64, others=True)
    #Traité pour la segmentation sémantique
    x_im, y_im = getdata_for_semantic_segmentation(im)
    X_data.append(x_im) #données d'image
    Y_data.append(y_im) #Corriger les données de réponse

Seuls les 8 premiers éléments des données d'image créées et des données de réponse correctes sont illustrés et confirmés.

%matplotlib inline
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10,10))
for i in range(16):
    ax = fig.add_subplot(4, 4, i+1)
    ax.axis('off')
    if i < 8: #Afficher le top 8 des données d'image
        ax.set_title('input_{}'.format(i))
        ax.imshow(X_data[i],cmap=plt.cm.gray, interpolation='none')
    else: #Afficher les 8 meilleures réponses correctes
        ax.set_title('answer_{}'.format(i - 8))
        ax.imshow(Y_data[i - 8],cmap=plt.cm.gray, interpolation='none')
plt.show()

output_3_0.png

Le but de la segmentation sémantique est de construire un modèle qui extrait la partie correspondant à M. Kuma des «données d'image» ci-dessus et produit des «données de réponse correctes».

Construire un modèle de segmentation Kumantic

Mise en forme des données

import torch
from torch.utils.data import TensorDataset, DataLoader

#Convertir les données d'image et corriger les données de réponse en ndarray
X_a = np.array([[np.asarray(x).transpose((2, 0, 1))[0]] for x in X_data])
Y_a = np.array([[np.asarray(y).transpose((2, 0, 1))[0]] for y in Y_data])

#Convertir les données d'image ndarray et corriger les données de réponse en tenseur
X_t = torch.tensor(X_a, dtype = torch.float32)               
Y_t = torch.tensor(Y_a, dtype = torch.float32)

#Stocké dans le chargeur de données pour l'apprentissage avec PyTorch
data_set = TensorDataset(X_t, Y_t)
data_loader = DataLoader(data_set, batch_size = 100, shuffle = True)

Définition du modèle

Le modèle de segmentation sémantique est essentiellement le [Auto Encoder](https: // qiita.) Utilisation du Convolution Neural Network (CNN). Il s'agit de com / maskot1977 / items / 2fb459c66d49ba550db2).

from torch import nn, optim
from torch.nn import functional as F
class Kuma(nn.Module):
    def __init__(self):
        super(Kuma, self).__init__()
        #Partie codeur
        self.encode1 = nn.Sequential(
            *[
              nn.Conv2d(
                  in_channels = 1, out_channels = 6, kernel_size = 3, padding = 1),
              nn.BatchNorm2d(6)
              ])
        self.encode2 = nn.Sequential(
            *[
              nn.Conv2d(
                  in_channels = 6, out_channels = 16, kernel_size = 3, padding = 1),
              nn.BatchNorm2d(16)
              ])
        self.encode3 = nn.Sequential(
            *[
              nn.Conv2d(
                  in_channels = 16, out_channels = 32, kernel_size = 3, padding = 1),
              nn.BatchNorm2d(32)
              ])

        #Partie décodeur
        self.decode3 = nn.Sequential(
            *[
              nn.ConvTranspose2d(
                  in_channels = 32, out_channels = 16, kernel_size = 3, padding = 1),
              nn.BatchNorm2d(16)
              ])
        self.decode2 = nn.Sequential(
            *[
              nn.ConvTranspose2d(
                  in_channels = 16, out_channels = 6, kernel_size = 3, padding = 1),
              nn.BatchNorm2d(6)
              ])
        self.decode1 = nn.Sequential(
            *[
              nn.ConvTranspose2d(
                  in_channels = 6, out_channels = 1, kernel_size = 3, padding = 1),
              ])

    def forward(self, x):
        #Partie codeur
        dim_0 = x.size() #Pour restaurer la taille dans la première couche du décodeur
        x = F.relu(self.encode1(x))
        # return_indices =Réglez sur True et max dans le décodeur_Utiliser la position du pool idx
        x, idx_1 = F.max_pool2d(x, kernel_size = 2, stride = 2, return_indices = True)
        dim_1 = x.size() #Pour restaurer la taille dans la deuxième couche du décodeur
        x = F.relu(self.encode2(x))
        # return_indices =Réglez sur True et max dans le décodeur_Utiliser la position du pool idx
        x, idx_2 = F.max_pool2d(x, kernel_size = 2, stride = 2, return_indices = True)            
        dim_2 = x.size()
        x = F.relu(self.encode3(x)) #Pour restaurer la taille dans la troisième couche du décodeur
        # return_indices =Réglez sur True et max dans le décodeur_Utiliser la position du pool idx
        x, idx_3 = F.max_pool2d(x, kernel_size = 2, stride = 2, return_indices = True)

        #Partie décodeur
        x = F.max_unpool2d(x, idx_3, kernel_size = 2, stride = 2, output_size = dim_2)
        x = F.relu(self.decode3(x))
        x = F.max_unpool2d(x, idx_2, kernel_size = 2, stride = 2, output_size = dim_1)           
        x = F.relu(self.decode2(x))                           
        x = F.max_unpool2d(x, idx_1, kernel_size = 2, stride = 2, output_size = dim_0)           
        x = F.relu(self.decode1(x))                           
        x = torch.sigmoid(x)                                     

        return x

Apprentissage

%%time

kuma = Kuma()
loss_fn = nn.MSELoss()                               
optimizer = optim.Adam(kuma.parameters(), lr = 0.01)

total_loss_history = []                                     
epoch_time = 50
for epoch in range(epoch_time):
    total_loss = 0.0                          
    kuma.train()
    for i, (XX, yy) in enumerate(data_loader):
        optimizer.zero_grad()       
        y_pred = kuma(XX)
        loss = loss_fn(y_pred, yy)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print("epoch:",epoch, " loss:", total_loss/(i + 1))
    total_loss_history.append(total_loss/(i + 1))

plt.plot(total_loss_history)
plt.ylabel("loss")
plt.xlabel("epoch time")
plt.savefig("total_loss_history")
plt.show()
epoch: 0  loss: 8388.166772460938
epoch: 2  loss: 8372.164868164062
epoch: 3  loss: 8372.035913085938
...
epoch: 48  loss: 8371.781372070312
epoch: 49  loss: 8371.78125

La valeur de la fonction de perte est un nombre ridicule, est-ce que ça va ...

output_6_1.png

Il semble qu'il a convergé. Le temps de calcul est le suivant.

CPU times: user 6min 7s, sys: 8.1 s, total: 6min 16s
Wall time: 6min 16s

Annonce des résultats

Générez de nouvelles données en tant que données de test.

X_test = [] #Stocker les données d'image pour les tests
Y_test = [] #Stocke les données de réponse correctes pour les tests
Z_test = [] #Stocker les résultats de prédiction pour les tests

for i in range(100): #Générer 100 nouvelles données non utilisées pour la formation
    im = koala_or_bear(bear=True, rotate=True, resize=64, others=True)
    x_im, y_im = getdata_for_semantic_segmentation(im)
    X_test.append(x_im)
    Y_test.append(y_im)

Prédire à l'aide d'un modèle entraîné.

#Formater les données d'image de test pour PyTorch
X_test_a = np.array([[np.asarray(x).transpose((2, 0, 1))[0]] for x in X_test])
X_test_t = torch.tensor(X_test_a, dtype = torch.float32)

#Calculer des prédictions à l'aide d'un modèle entraîné
Y_pred = kuma(X_test_t)

#Stocker les valeurs prévues sous forme de ndarray
for pred in Y_pred:
    Z_test.append(pred.detach().numpy())

Dessin des 10 premières données

Tirons uniquement les 10 premières données pour voir quel est le résultat de la prédiction. De gauche à droite, saisissez les données d'image, corrigez les données de réponse et les données de prédiction.

#Dessinez des données d'image, corrigez les données de réponse et les valeurs prévues pour les 10 premiers éléments de données
fig = plt.figure(figsize=(6,18))
for i in range(10):
    ax = fig.add_subplot(10, 3, (i * 3)+1)
    ax.axis('off')
    ax.set_title('input_{}'.format(i))
    ax.imshow(X_test[i])
    ax = fig.add_subplot(10, 3, (i * 3)+2)
    ax.axis('off')
    ax.set_title('answer_{}'.format(i))
    ax.imshow(Y_test[i])
    ax = fig.add_subplot(10, 3, (i * 3)+3)
    ax.axis('off')
    ax.set_title('predicted_{}'.format(i))
    yp2 = Y_pred[i].detach().numpy()[0] * 255
    z_im = Image.fromarray(np.array([yp2, yp2, yp2]).transpose((1, 2, 0)).astype(dtype='uint8'))
    ax.imshow(z_im)
plt.show()

output_7_0.png

Je peux découper la partie ours. Même si la taille de l'ours change, vous pouvez le faire pivoter!

Cependant, il semble qu'ils ont tendance à couper un peu plus. De plus, il y a pas mal d'erreurs.

Zone de découpe

Comparons la zone découpée en blanc avec les données de réponse correctes et les données prédites.

A_ans = []
A_pred = []
for yt, zt in zip(Y_test, Z_test):
    #Zone blanche correcte (puisqu'il y a 3 vecteurs, divisez par 3)
    A_ans.append(np.where(np.asarray(yt) > 0.5, 1, 0).sum() / 3) 
    A_pred.append(np.where(np.asarray(zt) > 0.5, 1, 0).sum()) #Zone blanche prédite

plt.figure(figsize=(4, 4))
plt.scatter(A_ans, A_pred, alpha=0.5)
plt.grid()
plt.xlabel('Observed sizes of bears')
plt.ylabel('Predicted sizes of bears')
plt.xlim([0, 1700])
plt.ylim([0, 1700])
plt.show()

output_8_0.png

Il est bon que la valeur de réponse correcte et la valeur prédite aient une relation presque linéaire, mais il semble que la valeur prédite ait tendance à être grande.

Si vous voulez seulement connaître la taille de l'ours, c'est une bonne idée de faire des corrections en fonction de cette relation. Cependant, afin de découper plus précisément, il sera nécessaire de construire un modèle plus compliqué.

Recommended Posts

Ours ... pas de segmentation sémantique
Essayez la segmentation sémantique (Pytorch)
Vision par ordinateur: segmentation sémantique, partie 2 - segmentation sémantique en temps réel