[PYTHON] Versuchen Sie es mit semantischer Segmentierung (Pytorch)

zunaechst

Die semantische Segmentierung ist eine Art Bilderkennungstechnologie, die Pixel für Pixel erkannt werden kann. seg.png

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

Erstellen eines Datensatzes

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. input_auto.png correct_auto.png

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)

Netzwerk_1

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? loss_auto.png

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. output_auto.png

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()

Versuchen Sie, das Netzwerk zu vertiefen.

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.

loss_auto.png

Geben Sie die Daten ein, die nicht für das Training verwendet werden, und vergleichen Sie sie mit dem richtigen Bild. output.png

Sie können sehen, dass die Segmentierung abgeschlossen ist.

Am Ende

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

Versuchen Sie es mit semantischer Segmentierung (Pytorch)
Bär ... keine semantische Segmentierung
Zusammenfassung der Probleme bei der semantischen Segmentierung mit Pytorch
Versuchen Sie Auto Encoder mit Pytorch
Versuchen Sie, XOR mit PyTorch zu implementieren
[PyTorch] Datenerweiterung zur Segmentierung
Computer Vision: Semantische Segmentierung Teil2 - Semantische Echtzeitsegmentierung
Probieren Sie die neue Scheduler-Verkettung in PyTorch 1.4 aus