"Kumantic Segumantion" pour obtenir des informations sur Kuma à partir de l'image de Kuma. Ceci est une suite de Kumantic Segmentation 2.
La dernière fois, j'ai cherché à "détecter plusieurs </ b> empreintes de pas d'ours" en utilisant "une image de multiples </ b> empreintes de pas d'ours" pour apprendre. , N'a pas fonctionné).
Cette fois, nous visons à "détecter plusieurs </ b> silhouettes d'ours" en utilisant "une image qui montre une seule </ b> silhouette d'ours" pour l'apprentissage.
Utilisé pour créer des ensembles de données pour la formation et la validation.
import numpy as np
import random
from PIL import Image, ImageDraw, ImageFilter
from itertools import product
def draw_bear(n_bear=1): #Générer de manière aléatoire une image de M. Kuma
r = g = b = 250
im = Image.new('RGB', (400, 400), (r, g, b))
draw = ImageDraw.Draw(im)
for _ in range(random.randint(-1, 0)):
r = random.randint(10, 200)
g = random.randint(10, 200)
b = random.randint(10, 200)
x1 = random.randint(0, 400)
y1 = random.randint(0, 400)
dx = random.randint(10, 50)
dy = random.randint(10, 50)
draw.ellipse((x1, y1, x1+dx, y1+dy), fill=(r, g, b))
for _ in range(n_bear):
r = g = b = 1
center_x = 200
center_y = 200
wx = 60
wy = 50
dx1 = 90
dx2 = 20
dy1 = 90
dy2 = 20
dx3 = 15
dy3 = 100
dy4 = 60
shape1 = (center_x - wx, center_y - wy, center_x + wx, center_y + wy)
shape2 = (center_x - dx1, center_y - dy1, center_x - dx2, center_y - dy2)
shape3 = (center_x + dx2, center_y - dy1, center_x + dx1, center_y - dy2)
shape4 = (center_x - dx3, center_y - dy3, center_x + dx3, center_y - dy4)
zoom = 0.2 + random.random() * 0.4
center_x = random.randint(-30, 250)
center_y = random.randint(-30, 250)
shape1 = modify(shape1, zoom=zoom, center_x=center_x, center_y=center_y)
shape2= modify(shape2, zoom=zoom, center_x=center_x, center_y=center_y)
shape3 = modify(shape3, zoom=zoom, center_x=center_x, center_y=center_y)
shape4 = modify(shape4, zoom=zoom, center_x=center_x, center_y=center_y)
draw.ellipse(shape1, fill=(r, g, b))
draw.ellipse(shape2, fill=(r, g, b))
draw.ellipse(shape3, fill=(r, g, b))
#draw.ellipse(shape4, fill=(r, g, b))
return im
def modify(shape, zoom=1, center_x=0, center_y=0):
x1, y1, x2, y2 = np.array(shape) * zoom
return (x1 + center_x, y1 + center_y, x2 + center_x, y2 + center_y)
class Noise: #Mettez du bruit sur l'image de l'ours
def __init__(self, input_image):
self.input_image = input_image
self.input_pix = self.input_image.load()
self.w, self.h = self.input_image.size
def saltpepper(self, salt=0.05, pepper=0.05):
output_image = Image.new("RGB", self.input_image.size)
output_pix = output_image.load()
for x, y in product(*map(range, (self.w, self.h))):
r = random.random()
if r < salt:
output_pix[x, y] = (255, 255, 255)
elif r > 1 - pepper:
output_pix[x, y] = ( 0, 0, 0)
else:
output_pix[x, y] = self.input_pix[x, y]
return output_image
##Traitez l'image de Kuma en données d'enseignant pour la segmentation sémantique
def getdata_for_semantic_segmentation(im):
x_im = im.filter(ImageFilter.CONTOUR)
im2 = Noise(input_image=x_im)
x_im = im2.saltpepper()
a_im = np.asarray(im)
y_im = Image.fromarray(np.where(a_im == 1, 255, 0).astype(dtype='uint8'))
return x_im, y_im
Si vous préparez la fonction ci-dessus, vous pouvez obtenir l'image x_im
de M. Kuma et les données de réponse correctes y_im
comme suit.
x_im, y_im = getdata_for_semantic_segmentation(draw_bear())
Vérifiez le contenu
x_im
y_im
Très bien, j'ai confirmé que l'image de M. Kuma avait été générée. C'est un ours, peu importe comment vous le regardez.
Générez 1000 données d'entraînement comme suit.
%%time
X_data = [] #Pour stocker des données d'image
Y_data = [] #Pour stocker des données de réponse correctes
for i in range(1000): #Générer 1000 images
x_im, y_im = getdata_for_semantic_segmentation(draw_bear())
X_data.append(x_im) #données d'image
Y_data.append(y_im) #Corriger les données de réponse
CPU times: user 1min 13s, sys: 865 ms, total: 1min 14s
Wall time: 1min 15s
Juste au cas où, vérifiez uniquement les 8 premières des données générées.
%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()
Mettez en forme les données résultantes pour PyTorch.
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)
J'ai utilisé le même réseau que la dernière fois.
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
Tout d'abord, apprenez seulement 50 époques.
%%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):
!date
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()
Tue Feb 25 12:07:52 UTC 2020
epoch: 0 loss: 1202.7168701171875
Tue Feb 25 12:10:12 UTC 2020
epoch: 1 loss: 1200.6845336914062
Tue Feb 25 12:12:28 UTC 2020
...
Tue Feb 25 13:53:40 UTC 2020
epoch: 47 loss: 1199.1316650390625
Tue Feb 25 13:55:54 UTC 2020
epoch: 48 loss: 1199.1294555664062
Tue Feb 25 13:58:08 UTC 2020
epoch: 49 loss: 1199.133544921875
CPU times: user 1h 36min 47s, sys: 2min 23s, total: 1h 39min 11s
Wall time: 1h 52min 30s
On dirait qu'il a convergé.
Générez 100 nouvelles données qui n'ont pas été utilisées pour la formation et vérifiez.
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
x_im, y_im = getdata_for_semantic_segmentation(draw_bear())
X_test.append(x_im)
Y_test.append(y_im)
#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())
Je n'illustrerai que les 10 premières données.
#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=(12,36))
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()
Il y a beaucoup de bruit, mais cela semble prévisible dans une certaine mesure.
Examinons la relation entre la taille réelle de l'ours et la taille de l'ours détecté (prédit).
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')
plt.ylabel('Predicted sizes')
#plt.xlim([0, 1700])
#plt.ylim([0, 1700])
plt.show()
La précision n'est pas bonne, mais il existe une relation linéaire vague.
Vous pouvez enregistrer le modèle entraîné et l'utiliser à nouveau à tout moment comme suit.
torch.save(kuma.state_dict(), "kuma_050_20200225.pytorch")
D'une manière ou d'une autre, j'ai essayé d'apprendre 50 époques supplémentaires. Code omis.
Je me demande si l'erreur a un peu diminué (mais ça ne change pas beaucoup)
Il ne semble y avoir aucune amélioration majeure.
torch.save(kuma.state_dict(), "kuma_100_20200225.pytorch")
Pour le moment, j'ai enregistré le modèle entraîné comme ci-dessus.
Il est enfin temps de passer en direct. Jusqu'à présent, nous avons appris des «images contenant un seul ours </ b>» et détecté des ours à partir «d'images contenant un seul ours </ b>». J'avais l'habitude d'apprendre "des images contenant un seul ours </ b>" et des "images contenant plusieurs </ b> ours" Je voudrais détecter M. Kuma.
Chargez le modèle entraîné que vous avez enregistré précédemment.
kuma = Kuma()
kuma.load_state_dict(torch.load("kuma_100_20200225.pytorch"))
<All keys matched successfully>
Génère 100 images contenant plusieurs ours. Cela génère également une image décevante qui ne montre pas l'ours.
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
x_im, y_im = getdata_for_semantic_segmentation(draw_bear(n_bear=random.randint(0, 5)))
X_test.append(x_im)
Y_test.append(y_im)
#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)
Comme il s'agit d'un modèle entraîné, la prédiction est instantanée.
#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())
#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=(12,36))
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()
C'est assez bruyant, mais j'ai pu faire une prédiction approximative.
Voyons la relation entre la taille réelle de M. Kuma et la taille prévue.
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')
plt.ylabel('Predicted sizes')
#plt.xlim([0, 1700])
#plt.ylim([0, 1700])
plt.show()
Il existe une relation linéaire assez claire, donc si vous voulez juste connaître la taille, il semble que vous puissiez corriger cette relation.
J'ai ajouté une autre couche pour créer un réseau plus profond.
import torch
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)
])
self.encode4 = nn.Sequential(
*[
nn.Conv2d(
in_channels = 32, out_channels = 64, kernel_size = 3, padding = 1),
nn.BatchNorm2d(64)
])
#Partie décodeur
self.decode4 = nn.Sequential(
*[
nn.ConvTranspose2d(
in_channels = 64, out_channels = 32, kernel_size = 3, padding = 1),
nn.BatchNorm2d(32)
])
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()
x = F.relu(self.encode1(x))
x, idx_1 = F.max_pool2d(x, kernel_size = 2, stride = 2, return_indices = True)
dim_1 = x.size()
x = F.relu(self.encode2(x))
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))
x, idx_3 = F.max_pool2d(x, kernel_size = 2, stride = 2, return_indices = True)
dim_3 = x.size()
x = F.relu(self.encode4(x))
x, idx_4 = F.max_pool2d(x, kernel_size = 2, stride = 2, return_indices = True)
#Partie décodeur
x = F.max_unpool2d(x, idx_4, kernel_size = 2, stride = 2, output_size = dim_3)
x = F.relu(self.decode4(x))
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
Le résultat est le suivant. Le code est le même que celui décrit ci-dessus (seul le nom du fichier à enregistrer est différent)
Courbe d'apprentissage
Exemple de résultat de prédiction (1 ours)
Taille de l'ours
Exemple de résultat de prédiction (beaucoup d'ours)
Taille de l'ours
Ajoutez simplement une couche et c'est assez propre!