J'étudie la segmentation sémantique, qui est une méthode d'apprentissage en profondeur qui coupe la partie d'intérêt d'une image. Cette fois, je voudrais découper les empreintes de pas des ours. C'est une suite de Segmentation sémantique, pas ours ....
La grande différence par rapport à la dernière fois était que la dernière fois, il avait été décidé que "l'ours cible n'est qu'un par image" </ b>, mais cette fois "l'empreinte de l'ours est une image". C'est "3 par" </ b>.
D'abord et avant tout, l'auteur ne sait pas à quoi ressemble une véritable empreinte d'ours. L'image est une image.
import random
def draw_footprints(): #Générez aléatoirement des images d'empreintes d'ours
r = g = b = 250
im = Image.new('RGB', (400, 400), (r, g, b))
draw = ImageDraw.Draw(im)
for _ in range(100):
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(3):
r = g = b = 1
center_x = 200
center_y = 200
wx = 60
wy = 50
dx1 = 60
dx2 = 30
dy1 = 90
dy2 = 50
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)
from PIL import Image, ImageDraw
from itertools import product
class Noise: #Ajouter du bruit à l'image de l'empreinte 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
from PIL import ImageFilter
import numpy as np
#Traitez l'image de l'empreinte de l'ours en données de l'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
x_im, y_im = getdata_for_semantic_segmentation(draw_footprints())
Entrez la figure ci-dessous (une image qui comprend les empreintes de pas d'un ours). Savez-vous où se trouvent les empreintes de pas de l'ours là-dedans?
x_im
La réponse est dans la figure ci-dessous. Nous visons un apprentissage qui peut produire cette réponse.
y_im
Cette fois, 1000 images ont été automatiquement générées en tant que jeu d'enseignants.
%%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_footprints())
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 20s, sys: 811 ms, total: 1min 21s
Wall time: 1min 21s
%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()
Maintenant, commençons à apprendre. Tout d'abord, la conversion de 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 d'une classe pour l'apprentissage de la segmentation Kumantic
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
Commencer à apprendre
%%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()
Mon Feb 3 12:23:20 UTC 2020
epoch: 0 loss: 2685.716845703125
Mon Feb 3 12:24:47 UTC 2020
epoch: 1 loss: 2681.9998046875
Mon Feb 3 12:26:13 UTC 2020
epoch: 2 loss: 2679.750439453125
Mon Feb 3 12:27:39 UTC 2020
epoch: 3 loss: 2678.707568359375
Mon Feb 3 12:29:05 UTC 2020
...
Mon Feb 3 13:39:11 UTC 2020
epoch: 47 loss: 2677.768359375
Mon Feb 3 13:40:49 UTC 2020
epoch: 48 loss: 2677.7637939453125
Mon Feb 3 13:42:29 UTC 2020
epoch: 49 loss: 2677.7629150390626
CPU times: user 2h 15min 56s, sys: 3min, total: 2h 18min 56s
Wall time: 1h 20min 54s
Générons de nouvelles données qui n'ont pas été utilisées pour l'entraînement et faisons des prédictions.
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_footprints())
X_test.append(x_im)
Y_test.append(y_im)
Mise en forme des données
#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())
Comparons la bonne réponse et le résultat de la prédiction pour les 10 premières images.
#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. Ça ne fonctionne pas. Il semble que les empreintes ont été prises d'une manière ou d'une autre, mais il semble qu'il existe de nombreux faux positifs qui jugent les non-empreintes comme des empreintes de pas.
Comparons la zone de la bonne réponse avec la zone de la valeur prédite.
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()
Dans Kuma ... not Semantic Segmentation, il y avait une relation linéaire, mais cette fois, il semble qu'une bonne relation ne soit pas sortie.
La suite revient.
Les augmentations subséquentes du nombre de données de 1000 à 4000 et le nombre d'époques de 50 à 100 n'ont pas beaucoup changé. À propos, la courbe d'apprentissage n'a pas baissé facilement à partir d'environ 60 époques.