[PYTHON] Super Auflösung mit SRGAN und ESRGAN

SRGAN SRGAN ist ein Algorithmus, der ein neuronales Netzwerk verwendet, um die Auflösung von Bildern zu erhöhen, und diesmal habe ich ihn implementiert. Referenz https://qiita.com/pacifinapacific/items/ec338a500015ae8c33fe https://buildersbox.corp-sansan.com/entry/2019/04/29/110000

Vorerst implementiert

github https://github.com/AokiMasataka/Super-resolution Das Dataset verwendet dasselbe SRResNet, das ich vor langer Zeit erstellt habe. SResNet-Artikel https://qiita.com/AokiMasataka/items/3d382310d8a78f711c71 Das Netzwerk wird in PyTorch implementiert und PyTorch praktiziert. Das Generator-Netzwerk von SRGAN besteht aus ResNet + Pixcelshuffer.

Wenn Sie es in Code schreiben, sieht es so aus.

class ResidualBlock(nn.Module):
    def __init__(self, nf=64):
        super(ResidualBlock, self).__init__()
        self.Block = nn.Sequential(
            nn.Conv2d(nf, nf, kernel_size=3, padding=1),
            nn.BatchNorm2d(nf),
            nn.PReLU(),
            nn.Conv2d(nf, nf, kernel_size=3, padding=1),
            nn.BatchNorm2d(nf),
        )

    def forward(self, x):
        out = self.Block(x)
        return x + out


class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=9, padding=4)
        self.relu = nn.PReLU()

        self.residualLayer = nn.Sequential(
            ResidualBlock(),
            ResidualBlock(),
            ResidualBlock(),
            ResidualBlock(),
            ResidualBlock()
        )

        self.pixelShuffle = nn.Sequential(
            nn.Conv2d(64, 64*4, kernel_size=3, padding=1),
            nn.PReLU(),
            nn.PixelShuffle(2),
            nn.Conv2d(64, 3, kernel_size=9, padding=4),
            nn.Tanh()
        )

    def forward(self, x):
        x = self.conv1(x)
        skip = self.relu(x)

        x = self.residualLayer(skip)
        x = self.pixelShuffle(x + skip)
        return x

Der Diskriminator verwendet ein unkonventionelles Faltungsnetzwerk. Die Größe des Arguments ist die vertikale und horizontale Größe des Bildes. Diesmal beträgt die Größe des Eingabebildes 64 x 64.

class Discriminator(nn.Module):
    def __init__(self, size=64):
        super(Discriminator, self).__init__()
        size = int(size / 8) ** 2

        self.net = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, padding=1),
            nn.LeakyReLU(0.2),

            nn.Conv2d(32, 32, kernel_size=3, stride=2, padding=1),
            nn.BatchNorm2d(32),
            nn.LeakyReLU(0.2),

            nn.Conv2d(32, 64, kernel_size=3, padding=1),
            nn.BatchNorm2d(64),
            nn.LeakyReLU(0.2),

            nn.Conv2d(64, 64, kernel_size=3, stride=2, padding=1),
            nn.BatchNorm2d(64),
            nn.LeakyReLU(0.2),

            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2),

            nn.Conv2d(128, 128, kernel_size=3, stride=2, padding=1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2),

            Flatten(),
            nn.Linear(128 * size, 1024),
            nn.LeakyReLU(0.2),
            nn.Linear(1024, 1)
        )

    def forward(self, x):
        return self.net(x)

class Flatten(nn.Module):
    def forward(self, x):
        return x.view(x.shape[0], -1)

Generator loss Vggloss wird für den Verlust des Generators verwendet. Vggloss wird klarer, indem der Durchschnitt seiner Merkmale der Verlust durch die Schichten des trainierten vgg-Modells ist, während mseloss der Durchschnitt der Pixel des Bildes ist. Generieren Sie ein Bild.

class VGGLoss(nn.Module):
    def __init__(self):
        super(VGGLoss, self).__init__()
        vgg = models.vgg16(pretrained=True)
        self.contentLayers = nn.Sequential(*list(vgg.features)[:31]).cuda().eval()
        for param in self.contentLayers.parameters():
            param.requires_grad = False

    def forward(self, fakeFrame, frameY):
        MSELoss = nn.MSELoss()
        content_loss = MSELoss(self.contentLayers(fakeFrame), self.contentLayers(frameY))
        return content_loss

Der Verlust des Generators ist die Summe aus content_loss und dem vom Diskriminator ausgegebenen BCE-Verlust. Basierend auf diesen werden wir eine Zugfunktion erstellen

def train(loader):
    tensor_x, tensor_y = torch.tensor(x, dtype=torch.float), torch.tensor(y, dtype=torch.float)
    DS = TensorDataset(tensor_x, tensor_y)
    loader = DataLoader(DS, batch_size=BATCH_SIZE, shuffle=True)
    D.train()
    G.train()

    D_optimizer = torch.optim.Adam(D.parameters(), lr=DiscriminatorLR, betas=(0.9, 0.999))
    G_optimizer = torch.optim.Adam(G.parameters(), lr=GeneratorLR, betas=(0.9, 0.999))

    realLabel = torch.ones(BATCH_SIZE, 1).cuda()
    fakeLabel = torch.zeros(BATCH_SIZE, 1).cuda()
    BCE = torch.nn.BCELoss()
    VggLoss = VGGLoss()

    for batch_idx, (X, Y) in enumerate(loader):
        if X.shape[0] < BATCH_SIZE:
            break

        X = X.cuda()
        Y = Y.cuda()

        fakeFrame = G(X)

        D.zero_grad()
        DReal = D(Y)
        DFake = D(fakeFrame)

        D_loss = (BCE(DFake, fakeLabel) + BCE(DReal, realLabel)) / 2
        D_loss.backward(retain_graph=True)
        D_optimizer.step()

        G.zero_grad()
        G_label_loss= BCE(DFake, realLabel)
        G_loss = VggLoss(fakeFrame, Y) + 1e-3 * G_label_loss

        G_loss.backward()
        G_optimizer.step()

        print("G_loss :", G_loss, " D_loss :", D_loss)

Das Bild unten zeigt das Ergebnis des 32epoch-Trainings. Die Oberseite ist ein verkleinertes Bild, die Mitte ist die Ausgabe in SRGAN und die Unterseite ist das Originalbild. Sich nicht schlecht für die Genauigkeit fühlen, SRGAN.png

ESRGAN

Unterschied zu SRGAN

RRDN(Residual in Residual Dense Network) ・ Es scheint, dass die Erzeugungskapazität durch Entfernen der Chargennormalisierung erhöht wird. DenseBlock fügt allen Ebeneneingaben eine Ebenenausgabe hinzu ・ Verbinden Sie außerdem drei dichte Blöcke auf dieselbe Weise wie ResNet. residual-in-residual-dense-block-RRDB.png Wenn implementiert, sieht es so aus

class ResidualDenseBlock(nn.Module):
    def __init__(self, nf=64, gc=32, bias=True):
        super(ResidualDenseBlock, self).__init__()
        self.conv1 = nn.Conv2d(nf, gc, 3, padding=1, bias=bias)
        self.conv2 = nn.Conv2d(nf + gc, gc, 3, padding=1, bias=bias)
        self.conv3 = nn.Conv2d(nf + 2 * gc, gc, 3, padding=1, bias=bias)
        self.conv4 = nn.Conv2d(nf + 3 * gc, gc, 3, padding=1, bias=bias)
        self.conv5 = nn.Conv2d(nf + 4 * gc, nf, 3, padding=1, bias=bias)
        self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True)

    def forward(self, x):
        x1 = self.lrelu(self.conv1(x))
        x2 = self.lrelu(self.conv2(torch.cat((x, x1), dim=1)))
        x3 = self.lrelu(self.conv3(torch.cat((x, x1, x2), dim=1)))
        x4 = self.lrelu(self.conv4(torch.cat((x, x1, x2, x3), dim=1)))
        x5 = self.conv5(torch.cat((x, x1, x2, x3, x4), dim=1))
        return x5 * 0.2 + x


class Generator(nn.Module):
    def __init__(self, nf=64):
        super(Generator, self).__init__()
        self.conv1 = nn.Conv2d(3, nf, kernel_size=3, padding=1)
        self.relu = nn.LeakyReLU(negative_slope=0.2, inplace=True)

        self.blockLayer = nn.Sequential(
            ResidualDenseBlock(),
            ResidualDenseBlock(),
            ResidualDenseBlock(),
        )

        self.pixelShuffle = nn.Sequential(
            nn.Conv2d(nf, nf * 4, kernel_size=3, padding=1),
            nn.LeakyReLU(negative_slope=0.2, inplace=True),
            nn.PixelShuffle(2),
            nn.Conv2d(nf, nf, kernel_size=3, padding=1),
            nn.Conv2d(nf, 3, kernel_size=3, padding=1),
            nn.Tanh()
        )

Relativistic GAN Der SRGAN-Diskriminator trainiert den realen, um 1 Fälschung als 0 auszugeben, aber der relativistische GAN vergleicht das reale Bild mit dem gefälschten Bild, und der Unterschied und die Bezeichnung sind BC Eloss. Referenz https://github.com/Yagami360/MachineLearning-Papers_Survey/issues/51 VGG Perceptual Loss In SRGAN wurde die Merkmalsmenge mit VGG16 extrahiert, in Perceptual Loss ist die Struktur jedoch so, dass L1_loss für jede Pooling-Schicht von VGG16 hinzugefügt wird. Es sieht so aus, wenn ich es grob schreibe

class VGGPerceptualLoss(torch.nn.Module):
    def __init__(self):
        super(VGGPerceptualLoss, self).__init__()
        blocks = []
        blocks.append(models.vgg16(pretrained=True).features[:4].eval())
        blocks.append(models.vgg16(pretrained=True).features[4:9].eval())
        blocks.append(models.vgg16(pretrained=True).features[9:16].eval())
        blocks.append(models.vgg16(pretrained=True).features[16:23].eval())
        blocks.append(models.vgg16(pretrained=True).features[23:30].eval())
        for bl in blocks:
            for p in bl:
                p.requires_grad = False
        self.blocks = torch.nn.ModuleList(blocks).cuda()
        self.mean = torch.nn.Parameter(torch.tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1), requires_grad=False).cuda()
        self.std = torch.nn.Parameter(torch.tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1), requires_grad=False).cuda()

    def forward(self, fakeFrame, frameY):
        fakeFrame = (fakeFrame - self.mean) / self.std
        frameY = (frameY - self.mean) / self.std
        loss = 0.0
        x = fakeFrame
        y = frameY
        for block in self.blocks:
            x = block(x)
            y = block(y)
            loss += torch.nn.functional.l1_loss(x, y)
        return loss

Lernergebnis

Die Oberseite ist ein verkleinertes Bild, die Mitte ist die Ausgabe mit ESRGAN und die Unterseite ist das Originalbild Wie SRGAN wird 32epoch von 32px auf 64px hochskaliert. ESRGAN.png Vergleichen wir die generierten Bilder nebeneinander, die Oberseite ist SRGAN und die Unterseite ist ESRGAN. In SRGAN ist ein Geräusch zu hören, in ESRGAN jedoch weniger, und der Gesamtumriss ist klarer als in SRGAN. SRGAN.png ESRGAN.png

Recommended Posts

Super Auflösung mit SRGAN und ESRGAN
Mit und ohne WSGI
Bei mir cp und Subprocess
Programmieren mit Python und Tkinter
Arbeiten Sie mit tkinter und Maus
Python und Hardware-Verwenden von RS232C mit Python-
Group_by mit sqlalchemy und sum
Python mit Pyenv und Venv
Mit mir, NER und Flair
Funktioniert mit Python und R.
Kommunizieren Sie mit FX-5204PS mit Python und PyUSB
Leuchtendes Leben mit Python und OpenCV
Python-Maus- und Tastaturbedienung mit Pyautogui
Sortieren mit einer Mischung aus Zahlen und Buchstaben
Roboter läuft mit Arduino und Python
Installieren Sie Python 2.7.9 und Python 3.4.x mit pip.
Neuronales Netzwerk mit OpenCV 3 und Python 3
Scraping mit Node, Ruby und Python
Einfacher Slackbot mit Docker und Errbot
Bildsegmentierung mit Scikit-Image und Scikit-Learn
Authentifizierungsprozess mit gRPC- und Firebase-Authentifizierung
Scraping mit Python, Selen und Chromedriver
Spielen Sie mit Poancare-Serien und SymPy
HTTPS mit Django und Let's Encrypt
Fotosegmentierung und Clustering mit DBSCAN
Kratzen mit Python und schöner Suppe
NAS-Backup mit PHP und Rsync
JSON-Codierung und -Decodierung mit Python
Pfadverarbeitung mit takewhile und dropwhile
Basisauthentifizierung, Digest-Authentifizierung mit Flask
Hadoop-Einführung und MapReduce mit Python
[GUI in Python] PyQt5-Drag & Drop-
Vergleichen Sie DCGAN und pix2pix mit Keras
Führe errBot ein und arbeite mit Slack
Speichern und Abrufen von Dateien mit Pepper
Async / warte mit Kivy und tkinter
Ich habe mit PyQt5 und Python3 gespielt
Melden Sie sich mit PycURL an und erhalten Sie eine Antwort
Experimentiert mit Unicode, Decodierung und Codierung
Lesen und Schreiben von CSV mit Python
Mehrfachintegration mit Python und Sympy
Koexistenz von Python2 und 3 mit CircleCI (1.0)
Zeichnen Sie Figuren mit OpenCV und PIL
Sugoroku-Spiel und Zusatzspiel mit Python
Laden Sie Bilder mit Falcon hoch und laden Sie sie herunter
FM-Modulation und Demodulation mit Python
Erstellen einer Umgebung mit pyenv und pyenv-virtualenv