Die semantische Segmentierung ist eine Art Bilderkennungstechnologie, die Pixel für Pixel erkannt werden kann.
Ich werde die detaillierte Theorie usw. separat belassen, aber ich möchte die semantische Segmentierung mit Pytorch versuchen. Dieses Mal werden wir uns mit einem Netzwerk befassen, das flacher und einfacher ist und auch mit einem Notebook-PC ausreichend erlernt werden kann, anstatt mit einem Netzwerk mit einer tiefen und komplizierten Struktur wie Seg-Net, U-Net oder PSP-Net.
Die Umwelt ist CPU: intel(R) core(TM)i5 7200U Speicher: 8 GB OS: Windows10 python ver3.6.9 pytorch ver1.3.1 numpy ver1.17.4
Dieses Mal werde ich das Bild verwenden, das ich selbst zusammengestellt habe. Das Bild in der oberen Zeile sind die Eingabedaten, und das Bild in der unteren Zeile sind die Lehrerdaten. Mit anderen Worten, es wird automatisch ein Netzwerk erstellt, das sich wie eine Mal-Software füllt.
Erstellen Sie die zum Lernen erforderlichen Daten. imgs ist 1000 Eingabedaten imgs_ano gibt 1000 Blätter Ausgabedaten aus (Lehrerdaten) Die Quadrate und Quadrate werden nicht abgedeckt, und die Länge der Seiten und die Anzahl der Quadrate werden ebenfalls zufällig bestimmt.
import numpy as np
import matplotlib.pyplot as plt
import torch
from torch import nn, optim
from torch.nn import functional as F
from torch.utils.data import TensorDataset, DataLoader
def rectangle(img, img_ano, centers, max_side):
"""
img… Zweidimensionales Bild mit nur quadratischen Linien
img_ano… dieses Kunstbild
Zentren… Liste der Zentrumskoordinaten
max_Seite… 1 der maximalen Länge der Seite/2
"""
if max_side < 3: #max_Wenn die Seite zu klein ist
max_side = 4
#Seitenlänge 1/Definieren Sie 2
side_x = np.random.randint(3, int(max_side))
side_y = np.random.randint(3, int(max_side))
#Zentralkoordinaten,(x, y)Definieren
x = np.random.randint(max_side + 1, img.shape[0] - (max_side + 1))
y = np.random.randint(max_side + 1, img.shape[1] - (max_side + 1))
#Wenn eine Position nahe der letzten Mittelposition enthalten ist,Gibt die Eingabedaten unverändert zurück
for center in centers:
if np.abs(center[0] - x) < (2 *max_side + 1):
if np.abs(center[1] - y) < (2 * max_side + 1):
return img, img_ano, centers
img[x - side_x : x + side_x, y - side_y] = 1.0 #Oberseite
img[x - side_x : x + side_x, y + side_y] = 1.0 #Unterseite
img[x - side_x, y - side_y : y + side_y] = 1.0 #Linke Seite
img[x + side_x, y - side_y : y + side_y + 1] = 1.0 #rechte Seite
img_ano[x - side_x : x + side_x + 1, y - side_y : y + side_y + 1] = 1.0
centers.append([x, y])
return img, img_ano, centers
num_images = 1000 #Anzahl der zu generierenden Bilder
length = 64 #Bildgröße
imgs = np.zeros([num_images, 1, length, length]) #Nullmatrix generieren,Bild eingeben
imgs_ano = np.zeros([num_images, 1, length, length]) #Ausgabebild
for i in range(num_images):
centers = []
img = np.zeros([length, length])
img_ano = np.zeros([64, 64])
for j in range(6): #Generieren Sie bis zu 6 Quads
img, img_ano, centers = rectangle(img, img_ano, centers, 12)
imgs[i, 0, :, :] = img
imgs_ano[i, 0, :, :] = img_ano
imgs = torch.tensor(imgs, dtype = torch.float32) #ndarray - torch.tensor
imgs_ano = torch.tensor(imgs_ano, dtype = torch.float32) #ndarray - torch.tensor
data_set = TensorDataset(imgs, imgs_ano)
data_loader = DataLoader(data_set, batch_size = 100, shuffle = True)
Erstellen Sie dann eine Netzwerkklasse in Pytorch. Zunächst habe ich das Netzwerk verwendet, das vom Auto-Encoder von previous definiert wurde. Sowohl der Autoencoder als auch die Segmentierung erzeugen ein Bild mit der gleichen Größe wie das Eingabebild, sodass ich es verwenden kann (im Fall von Pytorch). Was ist los mit Tensorflow?
class ConvAutoencoder(nn.Module):
def __init__(self):
super(ConvAutoencoder, self).__init__()
#Encoder Layers
self.conv1 = nn.Conv2d(in_channels = 1,
out_channels = 16,
kernel_size = 3,
padding = 1)
self.conv2 = nn.Conv2d(in_channels = 16,
out_channels = 4,
kernel_size = 3,
padding = 1)
#Decoder Layers
self.t_conv1 = nn.ConvTranspose2d(in_channels = 4, out_channels = 16,
kernel_size = 2, stride = 2)
self.t_conv2 = nn.ConvTranspose2d(in_channels = 16, out_channels = 1,
kernel_size = 2, stride = 2)
self.relu = nn.ReLU()
self.pool = nn.MaxPool2d(2, 2)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
#encode#
x = self.relu(self.conv1(x))
x = self.pool(x)
x = self.relu(self.conv2(x))
x = self.pool(x)
#decode#
x = self.relu(self.t_conv1(x))
x = self.sigmoid(self.t_conv2(x))
return x
Lassen Sie uns in diesem Netzwerk lernen.
#******Wählen Sie ein Netzwerk aus******
net = ConvAutoencoder()
loss_fn = nn.MSELoss() #Definition der Verlustfunktion
optimizer = optim.Adam(net.parameters(), lr = 0.01)
losses = [] #Rekordverlust für jede Epoche
epoch_time = 30
for epoch in range(epoch_time):
running_loss = 0.0 #Berechnung des Verlustes für jede Epoche
net.train()
for i, (XX, yy) in enumerate(data_loader):
optimizer.zero_grad()
y_pred = net(XX)
loss = loss_fn(y_pred, yy)
loss.backward()
optimizer.step()
running_loss += loss.item()
print("epoch:",epoch, " loss:", running_loss/(i + 1))
losses.append(running_loss/(i + 1))
#Visualisierung des Verlustes
plt.plot(losses)
plt.ylabel("loss")
plt.xlabel("epoch time")
plt.savefig("loss_auto")
plt.show()
Es ist eine Visualisierung des Verlusts für jede Epoche. Ist die Epoche nach 30-mal einigermaßen konvergiert?
Versuchen Sie es mit einem Bild, das Sie nicht zum Lernen verwendet haben. Ich kann die grobe Position bestimmen, aber ich habe den Eindruck, dass der Bereich um die Grenze nicht gut aufgenommen ist.
net.eval() #Bewertungsmodus
#Generieren Sie ein Bild, das bisher noch nicht gelernt wurde
num_images = 1
img_test = np.zeros([num_images, 1, length, length])
imgs_test_ano = np.zeros([num_images, 1, length, length])
for i in range(num_images):
centers = []
img = np.zeros([length, length])
img_ano = np.zeros([length, length])
for j in range(6):
img, img_ano, centers = rectangle(img, img_ano, centers, 7)
img_test[i, 0, :, :] = img
img_test = img_test.reshape([1, 1, 64, 64])
img_test = torch.tensor(img_test, dtype = torch.float32)
img_test = net(img_test) #Übertragen Sie das generierte Bild in das trainierte Netzwerk
img_test = img_test.detach().numpy() #torch.tensor - ndarray
img_test = img_test[0, 0, :, :]
plt.imshow(img, cmap = "gray") #Visualisierung von Eingabedaten
plt.savefig("input_auto")
plt.show()
plt.imshow(img_test, cmap = "gray") #Visualisierung von Ausgabedaten
plt.savefig("output_auto")
plt.show()
plt.imshow(img_ano, cmap = "gray") #Richtige Antwortdaten
plt.savefig("correct_auto")
plt.plot()
Ich konnte mit dem Vorgängermodell nicht genug Leistung erzielen, daher möchte ich die Ebene vertiefen. Lassen Sie uns hier nicht nur tiefer gehen, sondern auch die Batch-Normalisierung hinzufügen, um ein Überlernen und Upsampling mit einem Decoder zu verhindern. Für eine detaillierte Erklärung des Upsamplings war dieser Artikel leicht zu verstehen.
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
#encoder
self.encoder_conv_1 = nn.Sequential(*[
nn.Conv2d(in_channels = 1,
out_channels = 6,
kernel_size = 3,
padding = 1),
nn.BatchNorm2d(6)
])
self.encoder_conv_2 = nn.Sequential(*[
nn.Conv2d(in_channels = 6,
out_channels = 16,
kernel_size = 3,
padding = 1),
nn.BatchNorm2d(16)
])
self.encoder_conv_3 = nn.Sequential(*[
nn.Conv2d(in_channels = 16,
out_channels = 32,
kernel_size = 3,
padding = 1),
nn.BatchNorm2d(32)
])
#decoder
self.decoder_convt_3 = nn.Sequential(*[
nn.ConvTranspose2d(in_channels = 32,
out_channels = 16,
kernel_size = 3,
padding = 1),
nn.BatchNorm2d(16)
])
self.decoder_convt_2 = nn.Sequential(*[
nn.ConvTranspose2d(in_channels = 16,
out_channels = 6,
kernel_size = 3,
padding = 1),
nn.BatchNorm2d(6)
])
self.decoder_convt_1 = nn.Sequential(*[
nn.ConvTranspose2d(in_channels = 6,
out_channels = 1,
kernel_size = 3,
padding = 1)
])
def forward(self, x):
#encoder
dim_0 = x.size()
x = F.relu(self.encoder_conv_1(x))
x, indices_1 = F.max_pool2d(x, kernel_size = 2,
stride = 2,
return_indices = True) #Notieren Sie die Position von maxpool mit dem Index
dim_1 = x.size()
x = F.relu(self.encoder_conv_2(x))
x, indices_2 = F.max_pool2d(x, kernel_size = 2,
stride = 2,
return_indices = True)
dim_2 = x.size()
x = F.relu(self.encoder_conv_3(x))
x, indices_3 = F.max_pool2d(x, kernel_size = 2,
stride = 2,
return_indices = True)
#decoder
x = F.max_unpool2d(x, indices_3, kernel_size = 2,
stride = 2, output_size = dim_2)
x = F.relu(self.decoder_convt_3(x))
x = F.max_unpool2d(x, indices_2, kernel_size = 2,
stride = 2, output_size = dim_1)
x = F.relu(self.decoder_convt_2(x))
x = F.max_unpool2d(x, indices_1, kernel_size = 2,
stride = 2, output_size = dim_0)
x = F.relu(self.decoder_convt_1(x))
x = torch.sigmoid(x)
return x
Es ist einfach, zu diesem Netzwerk zu wechseln
#******Wählen Sie ein Netzwerk aus******
net = ConvAutoencoder()
Ändern Sie einfach den Speicherort für die neu erstellte Klasse.
#******Wählen Sie ein Netzwerk aus******
net = Net()
Stellen Sie den Verlustübergang grafisch dar.
Geben Sie die Daten ein, die nicht für das Training verwendet werden, und vergleichen Sie sie mit dem richtigen Bild.
Sie können sehen, dass die Segmentierung abgeschlossen ist.
Diesmal habe ich eine einfache Segmentierung versucht. Es war ein einfaches Modell, das alles andere als praktisch war, aber ich habe das Gefühl, die Atmosphäre erfassen zu können.
Recommended Posts