J'ai étudié la «segmentation sémantique», qui extrait les choses qui intéressent le deep learning qui traite des images.
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()
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».
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)
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
%%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 ...
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
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())
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()
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.
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()
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é.