[PYTHON] Bär ... keine semantische Segmentierung

Ich habe "semantische Segmentierung" studiert, die die Dinge extrahiert, die für tiefes Lernen von Interesse sind, das sich mit Bildern befasst.

Bärenbilderzeugung

Das erste Problem beim Erlernen der semantischen Segmentierung bestand darin, Bilder zu erhalten. Ich konnte kein gutes Gefühl finden und begann damit, automatisch ein Bild zum Üben zu generieren.

"Bilddaten" und "richtige Antwortdaten" für die semantische Segmentierung mit der in [Bilder von Koala und Bär automatisch generieren] erstellten Funktion (https://qiita.com/maskot1977/items/8d5fe2492e9fba2c7d90) Ich werde es selbst machen.

from PIL import ImageFilter
import numpy as np
def getdata_for_semantic_segmentation(im):
    x_im = im.filter(ImageFilter.CONTOUR) #Die konturierte wird als "Bilddaten" für die Eingabe verwendet.
    a_im = np.asarray(im) #In numpy konvertieren
    #Der Schwarzbär wird in Weißbär geändert, und die anderen werden als "richtige Antwortdaten" geschwärzt.
    y_im = Image.fromarray(np.where(a_im == 1, 255, 0).astype(dtype='uint8'))
    return x_im, y_im

Ich habe 2000 Datensätze wie folgt erstellt.

X_data = [] #Zum Speichern von Bilddaten
Y_data = [] #Zum Speichern korrekter Antwortdaten
for i in range(2000): #Generieren Sie 2000 Bilder
    #Bärenbild erzeugen
    im = koala_or_bear(bear=True, rotate=True , resize=64, others=True)
    #Für die semantische Segmentierung verarbeitet
    x_im, y_im = getdata_for_semantic_segmentation(im)
    X_data.append(x_im) #Bilddaten
    Y_data.append(y_im) #Richtige Antwortdaten

Nur die ersten 8 Elemente der erstellten Bilddaten und der richtigen Antwortdaten werden dargestellt und bestätigt.

%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: #Zeigen Sie die Top 8 der Bilddaten an
        ax.set_title('input_{}'.format(i))
        ax.imshow(X_data[i],cmap=plt.cm.gray, interpolation='none')
    else: #Zeigen Sie die Top 8 der richtigen Antwortdaten an
        ax.set_title('answer_{}'.format(i - 8))
        ax.imshow(Y_data[i - 8],cmap=plt.cm.gray, interpolation='none')
plt.show()

output_3_0.png

Der Zweck der semantischen Segmentierung besteht darin, ein Modell zu erstellen, das den Herrn Kuma entsprechenden Teil aus den obigen "Bilddaten" extrahiert und "korrekte Antwortdaten" ausgibt.

Erstellen eines kumantischen Segmentierungsmodells

Datenformung

import torch
from torch.utils.data import TensorDataset, DataLoader

#Konvertieren Sie Bilddaten und korrigieren Sie die Antwortdaten in 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])

#Konvertieren Sie ndarray-Bilddaten und korrigieren Sie die Antwortdaten in Tensor
X_t = torch.tensor(X_a, dtype = torch.float32)               
Y_t = torch.tensor(Y_a, dtype = torch.float32)

#Im Data Loader zum Lernen mit PyTorch gespeichert
data_set = TensorDataset(X_t, Y_t)
data_loader = DataLoader(data_set, batch_size = 100, shuffle = True)

Modelldefinition

Das Modell für die semantische Segmentierung ist im Grunde der [Auto Encoder](https: // qiita.) Unter Verwendung von Convolution Neural Network (CNN). Es ist 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__()
        #Encoderteil
        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)
              ])

        #Decoderteil
        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):
        #Encoderteil
        dim_0 = x.size() #Zum Wiederherstellen der Größe in der ersten Schicht des Decoders
        x = F.relu(self.encode1(x))
        # return_indices =Stellen Sie im Decoder True und Max ein_Verwenden Sie die Poolposition idx
        x, idx_1 = F.max_pool2d(x, kernel_size = 2, stride = 2, return_indices = True)
        dim_1 = x.size() #Zum Wiederherstellen der Größe in der zweiten Schicht des Decoders
        x = F.relu(self.encode2(x))
        # return_indices =Stellen Sie im Decoder True und Max ein_Verwenden Sie die Poolposition 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)) #Zum Wiederherstellen der Größe in der dritten Schicht des Decoders
        # return_indices =Stellen Sie im Decoder True und Max ein_Verwenden Sie die Poolposition idx
        x, idx_3 = F.max_pool2d(x, kernel_size = 2, stride = 2, return_indices = True)

        #Decoderteil
        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

Lernen

%%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

Der Wert der Verlustfunktion ist eine lächerliche Zahl, ist es okay ...

output_6_1.png

Es scheint, dass es konvergiert hat. Die Berechnungszeit ist wie folgt.

CPU times: user 6min 7s, sys: 8.1 s, total: 6min 16s
Wall time: 6min 16s

Ergebnisansage

Generieren Sie neue Daten als Testdaten.

X_test = [] #Speichern Sie Bilddaten zum Testen
Y_test = [] #Speichert korrekte Antwortdaten zum Testen
Z_test = [] #Speichern Sie Vorhersageergebnisse zum Testen

for i in range(100): #Generieren Sie 100 neue Daten, die nicht für das Training verwendet werden
    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)

Vorhersage mit einem trainierten Modell.

#Formatieren Sie die Testbilddaten für 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)

#Berechnen Sie Vorhersagen mit einem trainierten Modell
Y_pred = kuma(X_test_t)

#Speichern Sie die vorhergesagten Werte als ndarray
for pred in Y_pred:
    Z_test.append(pred.detach().numpy())

Zeichnung der ersten 10 Daten

Zeichnen wir nur die ersten 10 Daten, um das Vorhersageergebnis zu sehen. Geben Sie von links nach rechts Bilddaten, korrekte Antwortdaten und Vorhersagedaten ein.

#Zeichnen Sie Bilddaten, korrekte Antwortdaten und vorhergesagte Werte für die ersten 10 Daten
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()

output_7_0.png

Ich kann das Bärenteil herausschneiden. Selbst wenn sich die Größe des Bären ändert, ist es in Ordnung, ihn zu drehen!

Es scheint jedoch, dass sie dazu neigen, etwas größer auszuschneiden. Es gibt auch einige Fehler.

Ausschnittbereich

Vergleichen wir den weiß ausgeschnittenen Bereich mit den richtigen Antwortdaten und den vorhergesagten Daten.

A_ans = []
A_pred = []
for yt, zt in zip(Y_test, Z_test):
    #Korrekter weißer Bereich (Da 3 Vektoren vorhanden sind, durch 3 teilen)
    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()) #Voraussichtlicher weißer Bereich

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

output_8_0.png

Es ist gut, dass der richtige Antwortwert und der vorhergesagte Wert eine nahezu lineare Beziehung haben, aber es scheint, dass der vorhergesagte Wert tendenziell groß ist.

Wenn Sie nur die Größe des Bären wissen möchten, ist es eine gute Idee, Korrekturen basierend auf dieser Beziehung vorzunehmen. Um jedoch genauer ausschneiden zu können, muss ein komplizierteres Modell erstellt werden.

Recommended Posts

Bär ... keine semantische Segmentierung
Versuchen Sie es mit semantischer Segmentierung (Pytorch)
Computer Vision: Semantische Segmentierung Teil2 - Semantische Echtzeitsegmentierung