[PYTHON] J'ai créé un réseau pour convertir des images noir et blanc en images couleur (pix2pix)

1.Tout d'abord

Je souhaite utiliser le GAN (Hostile Generation Network) pour colorer automatiquement les images en niveaux de gris. Il semble être techniquement appelé "pix2pix".

Cette image en échelle de gris est
![0_gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/66e565f7-1c0b-ca9b-2a7c-aabdc47b2977 .png) J'ai pu colorier automatiquement comme suit !!
![0_fake.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/96a8572a- 3471-96ee-e2ce-6328c0d46401.png) À certains endroits, il y a des parties étranges et certaines images ne fonctionnent pas, mais la coloration est assez naturelle.

À propos, si vous affichez uniquement la ligne du bas de l'image d'origine, MSCOCO2014_557.pngMSCOCO2014_575.pngSailboat.jpgvoc2007_1963.png Comme ça. Les couleurs des trains et des lits sont différentes, mais j'ai l'impression qu'ils sont peints dans les mêmes tons dans l'ensemble.

2. Image approximative de cet apprentissage

L'image approximative de cette étude est la suivante. Puisqu'il s'agit d'un GAN, nous utilisons deux réseaux, Generator et Discriminator.
(1)pix2pix.jpg

(2)Diapositive 2.JPG

(3)Diapositive 3.JPG

(4)Diapositive 4.JPG

(5)Diapositive 5.JPG

(6)Diapositive 6.JPG

De cette manière, le Générateur et le Discriminateur sont formés pour tromper alternativement les deux réseaux.

3. À propos du réseau d'apprentissage

Cette fois, pytorch 1.1, torchvision 0.30 est utilisé. Pour le moment, importez la bibliothèque à utiliser

import glob
import os
import pickle
import torch
import torch.nn.functional as F
import torchvision
import torch.utils.data as data
import torchvision.transforms as transforms
import numpy as np                            #1.16.4
import matplotlib.pyplot as plt
from PIL import Image
from torch import nn
from skimage import io

L'environnement est windows10, Anaconda1.9.7, core-i3 8100, RAN 16.0 GB GEFORCE GTX 1060

Le GPU est recommandé car il prend beaucoup de temps d'apprentissage.

3-1.Generator

U-net utilisé pour la segmentation sémantique est utilisé pour Generator. Vous pouvez obtenir une image de sortie avec la même forme que l'image d'entrée sur le réseau Encoder-Decoder. L'image d'entrée est une image grise et l'image de sortie est une image couleur (fausse image). u-net-architecture.png La fonctionnalité de ce U-net est la partie Copie et recadrage. C'est un appareil (apparemment) pour ajouter une sortie proche du calque d'entrée à un calque proche du calque de sortie afin que la forme de l'image d'origine ne soit pas perdue.

Réaliser cette copie et recadrage avec pytorch est assez facile, -Utilisez torch.cat pour combiner les entrées. -Doublez le nombre de canaux d'entrée pour Conv2d et BatchNorm2d. seulement. Quand je l'ai vu pour la première fois, j'ai été très impressionné.

Cependant, il est nécessaire de faire correspondre la forme du tenseur à combiner avec torch.cat.

Si vous utilisez ce U-net tel quel, ce sera un réseau assez énorme. (On dirait qu'il y a environ 18 CNN) Par conséquent, réduisez la taille du réseau et réduisez la taille des images d'entrée / sortie à 3 x 128 x 128.

class Generator(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=5, stride=1, padding=2)
        self.bn1 = nn.BatchNorm2d(32)
        
        self.av2 = nn.AvgPool2d(kernel_size=4)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(64)
        
        self.av3 = nn.AvgPool2d(kernel_size=2)
        self.conv3 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
        self.bn3 = nn.BatchNorm2d(128)
        
        self.av4 = nn.AvgPool2d(kernel_size=2)
        self.conv4 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
        self.bn4 = nn.BatchNorm2d(256)
        
        self.av5 = nn.AvgPool2d(kernel_size=2)
        self.conv5 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
        self.bn5 = nn.BatchNorm2d(256)
        
        self.un6 = nn.UpsamplingNearest2d(scale_factor=2)
        self.conv6 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
        self.bn6 = nn.BatchNorm2d(256)
        
        #La sortie de conv6 et la sortie de conv4 sont envoyées à conv7.,Double canal d'entrée
        self.un7 = nn.UpsamplingNearest2d(scale_factor=2)
        self.conv7 = nn.Conv2d(256 * 2, 128, kernel_size=3, stride=1, padding=1)
        self.bn7 = nn.BatchNorm2d(128)
        
        #Envoyer la sortie de conv7 et la sortie de conv3 à conv8,Double canal d'entrée
        self.un8 = nn.UpsamplingNearest2d(scale_factor=2)
        self.conv8 = nn.Conv2d(128 * 2, 64, kernel_size=3, stride=1, padding=1)
        self.bn8 = nn.BatchNorm2d(64)
        
        #La sortie de conv8 et la sortie de conv2 sont envoyées à conv9.,Double canal d'entrée
        self.un9 = nn.UpsamplingNearest2d(scale_factor=4)
        self.conv9 = nn.Conv2d(64 * 2, 32, kernel_size=3, stride=1, padding=1)
        self.bn9 = nn.BatchNorm2d(32)
        
        self.conv10 = nn.Conv2d(32 * 2, 3, kernel_size=5, stride=1, padding=2)
        self.tanh = nn.Tanh()
    
    def forward(self, x):
        #x1-x4 est une torche.Parce que j'ai besoin de chat,Partir
        x1 = F.relu(self.bn1(self.conv1(x)), inplace=True)
        x2 = F.relu(self.bn2(self.conv2(self.av2(x1))), inplace=True)
        x3 = F.relu(self.bn3(self.conv3(self.av3(x2))), inplace=True)
        x4 = F.relu(self.bn4(self.conv4(self.av4(x3))), inplace=True)
        x = F.relu(self.bn5(self.conv5(self.av5(x4))), inplace=True)
        x = F.relu(self.bn6(self.conv6(self.un6(x))), inplace=True)
        x = torch.cat([x, x4], dim=1)
        x = F.relu(self.bn7(self.conv7(self.un7(x))), inplace=True)
        x = torch.cat([x, x3], dim=1)
        x = F.relu(self.bn8(self.conv8(self.un8(x))), inplace=True)
        x = torch.cat([x, x2], dim=1)
        x = F.relu(self.bn9(self.conv9(self.un9(x))), inplace=True)
        x = torch.cat([x, x1], dim=1)
        x = self.tanh(self.conv10(x))
        return x

3-2.Discriminator Discriminator est similaire à un réseau normal d'identification d'images. Cependant, la sortie est n x n nombres, pas unidimensionnelle. Affiche True ou False pour chacune de ces zones divisées. Dans le cas de l'image ci-dessous, c'est du 4x4.

ddd.png Cette technique est appelée patch GAN.

Après cela, la fonction d'activation est le classique Leakly Relu de GAN, InstanceNorm2d est utilisé à la place de BatchNorm2d.

J'ai essayé à la fois InstanceNorm2d et BatchNorm2d, mais je n'ai pas vraiment remarqué beaucoup de différence dans les résultats. InstanceNorm2d était bon pour Pix2Pix, donc j'utilise celui-ci cette fois.

class Discriminator(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, kernel_size=5, stride=1, padding=2)
        self.in1 = nn.InstanceNorm2d(16)
        
        self.av2 = nn.AvgPool2d(kernel_size=2)
        self.conv2_1 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
        self.in2_1 = nn.InstanceNorm2d(32)
        self.conv2_2 = nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=1)
        self.in2_2 = nn.InstanceNorm2d(32)
        
        self.av3 = nn.AvgPool2d(kernel_size=2)
        self.conv3_1 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
        self.in3_1 = nn.InstanceNorm2d(64)
        self.conv3_2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)
        self.in3_2 = nn.InstanceNorm2d(64)
        
        self.av4 = nn.AvgPool2d(kernel_size=2)
        self.conv4_1 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
        self.in4_1 = nn.InstanceNorm2d(128)
        self.conv4_2 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1)
        self.in4_2 = nn.InstanceNorm2d(128)
        
        self.av5 = nn.AvgPool2d(kernel_size=2)
        self.conv5_1 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
        self.in5_1 = nn.InstanceNorm2d(256)
        self.conv5_2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
        self.in5_2 = nn.InstanceNorm2d(256)
        
        self.av6 = nn.AvgPool2d(kernel_size=2)
        self.conv6 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)
        self.in6 = nn.InstanceNorm2d(512)
        
        self.conv7 = nn.Conv2d(512, 1, kernel_size=1)
            
    def forward(self, x):      
        x = F.leaky_relu(self.in1(self.conv1(x)), 0.2, inplace=True)
        x = F.leaky_relu(self.in2_1(self.conv2_1(self.av2(x))), 0.2, inplace=True)
        x = F.leaky_relu(self.in2_2(self.conv2_2(x)), 0.2, inplace=True)
        x = F.leaky_relu(self.in3_1(self.conv3_1(self.av3(x))), 0.2, inplace=True)
        x = F.leaky_relu(self.in3_2(self.conv3_2(x)), 0.2, inplace=True)
        x = F.leaky_relu(self.in4_1(self.conv4_1(self.av4(x))), 0.2, inplace=True)
        x = F.leaky_relu(self.in4_2(self.conv4_2(x)), 0.2, inplace=True)
        x = F.leaky_relu(self.in5_1(self.conv5_1(self.av5(x))), 0.2, inplace=True)
        x = F.leaky_relu(self.in5_2(self.conv5_2(x)), 0.2, inplace=True)
        x = F.leaky_relu(self.in6(self.conv6(self.av6(x))), 0.2, inplace=True)
        x = self.conv7(x)
        
        return x

3-3. Confirmation

Générer une pseudo image à l'aide de torch.randn Vérifiez la taille de sortie du générateur et du discriminateur.

Ici, deux images d'une taille de 3 x 128 x 128 sont générées et entrées dans le générateur et le discriminateur.

g, d = Generator(), Discriminator()

#Pseudo image par nombre aléatoire
test_imgs = torch.randn([2, 3, 128, 128])
test_imgs = g(test_imgs)
test_res = d(test_imgs)

print("Generator_output", test_imgs.size())
print("Discriminator_output",test_res.size())

La sortie ressemble à ceci:

Generator_output torch.Size([2, 3, 128, 128])  Discriminator_output torch.Size([2, 1, 4, 4])

La taille de sortie du générateur est la même que celle de l'entrée. La taille de sortie de Discriminator est de 4x4.

4. À propos du chargeur de données

Cette fois, nous allons acquérir les données selon le flux suivant. flow.png

Expansion des données de la partie b.

class DataAugment():
    #Augmentation des données de l'image PIL,Retour PIL
    def __init__(self, resize):
        self.data_transform = transforms.Compose([
                    transforms.RandomResizedCrop(resize, scale=(0.9, 1.0)),
                    transforms.RandomHorizontalFlip(),
                    transforms.RandomVerticalFlip()])
    def __call__(self, img):
        return self.data_transform(img)

Dans la partie qui se convertit en tenseur d, la normalisation des données est également effectuée en même temps.

class ImgTransform():
    #Redimensionner l'image PIL,Normaliser et retourner le tenseur
    def __init__(self, resize, mean, std):
        self.data_transform = transforms.Compose([
                    transforms.Resize(resize),
                    transforms.ToTensor(),
                    transforms.Normalize(mean, std)])
        
    def __call__(self, img):
        return self.data_transform(img)

C'est une classe qui hérite de la classe Dataset de Pytorch, et le flux jusqu'à a-d est écrit à la place de getitem. Vous pouvez facilement créer un chargeur de données en créant un flux d'entrée et de sortie pour une image dans la partie getitem.

class MonoColorDataset(data.Dataset):
    """
Hériter de la classe Dataset de Pytorch
    """
    def __init__(self, file_list, transform_tensor, augment=None):
        self.file_list = file_list
        self.augment = augment     #PIL to PIL
        self.transform_tensor = transform_tensor  #PIL to Tensor

    def __len__(self):
        return len(self.file_list)
    
    def __getitem__(self, index):
        #Obtenez le chemin du fichier du numéro d'index
        img_path = self.file_list[index]
        img = Image.open(img_path)
        img = img.convert("RGB")
        
        if self.augment is not None:
            img = self.augment(img)
        
        #Copie pour image monochrome
        img_gray = img.copy()
        #Convertir une image couleur en image monochrome
        img_gray = transforms.functional.to_grayscale(img_gray,
                                                      num_output_channels=3)
        #Convertir PIL en tenseur
        img = self.transform_tensor(img)
        img_gray = self.transform_tensor(img_gray)
        
        return img, img_gray

En définissant augment = None, les données ne seront pas développées, c'est-à-dire qu'il s'agira d'un ensemble de données pour les données de test. La fonction pour créer le chargeur de données est la suivante.

def load_train_dataloader(file_path, batch_size):
    """
    Input
     file_chemin Liste des chemins de fichiers pour l'image que vous souhaitez obtenir
     batch_size Taille du lot du chargeur de données
    return
     train_loader, RGB_images and Gray_images
    """ 
    size = 128             #Une taille de côté de l'image
    mean = (0.5, 0.5, 0.5) #Valeur moyenne pour chaque canal lorsque l'image est normalisée
    std = (0.5, 0.5, 0.5)  #Écart type pour chaque canal lorsque l'image est normalisée
    
    #base de données
    train_dataset = MonoColorDataset(file_path_train, 
                                 transform=ImgTransform(size, mean, std), 
                                 augment=DataAugment(size))
    #Chargeur de données
    train_dataloader = data.DataLoader(train_dataset,
                                    batch_size=batch_size,
                                    shuffle=True)
    return train_dataloader

5. Méthode de visualisation

5.1 Fonctions à visualiser

Il est pratique d'utiliser "torchvision.utils.make_grid" pour organiser plusieurs images dans une tuile. Après avoir généré une image en mosaïque avec tensor, convertissez-la en numpy et dessinez-la avec matplotlib.

def mat_grid_imgs(imgs, nrow, save_path = None):
    """
tenseur de pytorch(imgs)Une fonction qui dessine des tuiles
Déterminez le nombre de côtés d'une tuile avec nrow
    """
    imgs = torchvision.utils.make_grid(
                imgs[0:(nrow**2), :, :, :], nrow=nrow, padding=5)
    imgs = imgs.numpy().transpose([1,2,0])

    imgs -= np.min(imgs)   #La valeur minimale est 0
    imgs /= np.max(imgs)   #La valeur maximale est 1
    
    plt.imshow(imgs)
    plt.xticks([])
    plt.yticks([])
    plt.show()
    
    if save_path is not None:
        io.imsave(save_path, imgs)

Une fonction qui charge une image de test et dessine une image grise et une fausse image en mosaïques.

def evaluate_test(file_path_test, model_G, device="cuda:0", nrow=4):
    """
image de test de charge,Dessinez des images grises et fausses dans des carreaux
    """
    model_G = model_G.to(device)
    size = 128
    mean = (0.5, 0.5, 0.5)
    std = (0.5, 0.5, 0.5)
    test_dataset = MonoColorDataset(file_path_test, 
                                 transform=ImgTransform(size, mean, std), 
                                 augment=None)
    test_dataloader = data.DataLoader(test_dataset,
                                    batch_size=nrow**2,
                                    shuffle=False)
    #Dessinez une image pour chaque chargeur de données
    for img, img_gray in test_dataloader:
        mat_grid_imgs(img_gray, nrow=nrow)
        img = img.to(device)
        img_gray = img_gray.to(device)
        #img_Du gris à l'aide du générateur,Image RVB de faux
        img_fake = model_G(img_gray)
        img_fake = img_fake.to("cpu")
        img_fake = img_fake.detach()
        mat_grid_imgs(img_fake, nrow=nrow)

5.2 Résultat de la visualisation (avant l'apprentissage)

g = Generator()
file_path_test = glob.glob("test/*")
evaluate_test(file_path_test, g)

weew.png Bien que ce soit le résultat avant l'apprentissage, la forme de l'image d'entrée peut être vaguement comprise.

6. Comment obtenir des données d'entraînement

Pour le moment, je saisis juste une grande quantité de données d'image, donc j'entre COCO2014, PASCAL Voc2007, Labeled Faces in the Wild etc. Ces données contiennent une bonne proportion d'images grises. Je veux créer une image en noir et blanc en couleur cette fois, mais l'image qui devrait être un modèle ne peut pas être affichée dans l'image grise (?). Je voudrais donc supprimer l'image grise. Pour les images grises, les couleurs du canal R, du canal G et du canal B doivent être les mêmes, je voudrais donc l'utiliser pour les supprimer. Dans le même temps, les images trop blanches, les images trop sombres et les images qui n'ont pas beaucoup d'ombrage de couleur (l'écart type est petit) sont également extraites.

from skimage import io, color, transform

def color_mono(image, threshold=150):
    #Déterminez si l'image d'entrée de 3 canaux est en couleur
    #Si vous définissez un seuil élevé, vous pouvez définir Mono même pour les photos aux couleurs légèrement mélangées.
    image_size = image.shape[0] * image.shape[1]
    
    #La combinaison des canaux(0, 1),(0, 2),(1, 2)3 façons,Voyez la différence pour chaque canal
    diff = np.abs(np.sum(image[:,:, 0] - image[:,:, 1])) / image_size
    diff += np.abs(np.sum(image[:,:, 0] - image[:,:, 2])) / image_size
    diff += np.abs(np.sum(image[:,:, 1] - image[:,:, 2])) / image_size
    if diff > threshold:
        return "color"
    else:
        return "mono"

def bright_check(image, ave_thres = 0.15, std_thres = 0.1):
    try:
    #Image trop lumineuse,Image trop sombre,Image avec une luminosité similaire Faux
    #Convertir en noir et blanc
        image = color.rgb2gray(image)
    
        if image.shape[0] < 144:
            return False    
        #Pour les images trop lumineuses
        if np.average(image) > (1.-ave_thres):
            return False
        #Pour les images trop sombres
        if np.average(image) < ave_thres:
            return False
        #Si toute la luminosité est similaire
        if np.std(image) < std_thres:
            return False
        return True
    except:
        return False

paths = glob.glob("./test2014/*")

for i, path in enumerate(paths):
    image = io.imread(path)
    save_name = "./trans\\mscoco_" + str(i) +".png "
    
    x = image.shape[0] #Nombre de pixels le long de l'axe x
    y = image.shape[1] #Nombre de pixels dans la direction de l'axe y
    
    try:
        #Le plus court des axes x et y/2
        clip_half = min(x, y)/2
        #Découpez un carré dans l'image
        image = image[int(x/2 -clip_half): int(x/2 + clip_half),
                  int(y/2 -clip_half): int(y/2 + clip_half), :]

        if color_mono(image) == "color":
            if bright_check(image):
                image = transform.resize(image, (144, 144, 3),
                                        anti_aliasing = True)
                image = np.uint8(image*255)
                io.imsave(save_name, image)
    except:
        pass

J'ai coupé les images en carrés et les ai toutes placées dans un même dossier. L'image mesure 144x144 au lieu de 128x128 afin que les données puissent être développées. coco.png

C'est généralement correct, mais pour une raison quelconque, il y avait des omissions et des images de couleur sépia, alors je les ai supprimées manuellement.

J'ai mis environ 110 000 images dans le dossier «trans». Utilisez glob pour créer et charger une liste de chemins d'images.

7. Apprentissage

7.1 Fonctions d'apprentissage

L'apprentissage prenait environ 20 minutes par époque. Le code est long car nous formons à la fois Générateur et Discriminateur.

Le point à noter est l'étiquette de calcul de la perte, et la taille de la sortie du discriminateur est la taille de la sortie du discriminateur dans la confirmation de 4. J'ai confirmé que ce sera [batch_size, 1, 4, 4], alors faites-le correspondre Génère true_labels et false_labels.

def train(model_G, model_D, epoch, epoch_plus):
    device = "cuda:0"
    batch_size = 32
    
    model_G = model_G.to(device)
    model_D = model_D.to(device)
    
    params_G = torch.optim.Adam(model_G.parameters(),
                                lr=0.0002, betas=(0.5, 0.999))
    params_D = torch.optim.Adam(model_D.parameters(),
                                lr=0.0002, betas=(0.5, 0.999))
    
    #Étiquette pour le calcul de la perte,Faites attention à la taille du discriminateur
    true_labels = torch.ones(batch_size, 1, 4, 4).to(device)    #True
    false_labels = torch.zeros(batch_size, 1, 4, 4).to(device)  #False
    
    #loss_function
    bce_loss = nn.BCEWithLogitsLoss()
    mae_loss = nn.L1Loss()
    
    #Enregistrer la transition d'erreur
    log_loss_G_sum, log_loss_G_bce, log_loss_G_mae = list(), list(), list()
    log_loss_D = list()
    
    for i in range(epoch):
        #Enregistrer une erreur temporaire
        loss_G_sum, loss_G_bce, loss_G_mae = list(), list(), list()
        loss_D = list()
        
        train_dataloader = load_train_dataloader(file_path_train, batch_size)
        
        for real_color, input_gray in train_dataloader:
            batch_len = len(real_color)
            real_color = real_color.to(device)
            input_gray = input_gray.to(device)
            
            #Formation générateur
            #Générer une fausse image couleur
            fake_color = model_G(input_gray)
            
            #Enregistrer temporairement une fausse image
            fake_color_tensor = fake_color.detach()
            
            #Calculez la perte afin que la fausse image puisse être trompée comme la vraie chose
            LAMBD = 100.0 #Coefficients BCE et MAE
            
            #out quand une fausse image est placée dans le classificateur,D essaie de se rapprocher de 0.
            out = model_D(fake_color)
            
            #Perte pour la sortie de D,La cible est vraie parce que je veux rapprocher G de la réalité_labels
            loss_G_bce_tmp = bce_loss(out, true_labels[:batch_len])
            
            #Perte pour la sortie G
            loss_G_mae_tmp = LAMBD * mae_loss(fake_color, real_color)
            loss_G_sum_tmp = loss_G_bce_tmp + loss_G_mae_tmp
            
            loss_G_bce.append(loss_G_bce_tmp.item())
            loss_G_mae.append(loss_G_mae_tmp.item())
            loss_G_sum.append(loss_G_sum_tmp.item())
            
            #Calculer le gradient,Mise à jour du poids G
            params_D.zero_grad()
            params_G.zero_grad()
            loss_G_sum_tmp.backward()
            params_G.step()
            
            #Formation des discriminateurs
            real_out = model_D(real_color)
            fake_out = model_D(fake_color_tensor)
            
            #Calcul de la fonction de perte
            loss_D_real = bce_loss(real_out, true_labels[:batch_len])
            loss_D_fake = bce_loss(fake_out, false_labels[:batch_len])
            
            loss_D_tmp = loss_D_real + loss_D_fake
            loss_D.append(loss_D_tmp.item())
            
            #Calculer le gradient,Mise à jour du poids D
            params_D.zero_grad()
            params_G.zero_grad()
            loss_D_tmp.backward()
            params_D.step()
        
        i = i + epoch_plus
        print(i, "loss_G", np.mean(loss_G_sum), "loss_D", np.mean(loss_D))
        log_loss_G_sum.append(np.mean(loss_G_sum))
        log_loss_G_bce.append(np.mean(loss_G_bce))
        log_loss_G_mae.append(np.mean(loss_G_mae))
        log_loss_D.append(np.mean(loss_D))
        
        file_path_test = glob.glob("test/*")
        evaluate_test(file_path_test, model_G, device)
        
    return model_G, model_D, [log_loss_G_sum, log_loss_G_bce, log_loss_G_mae, log_loss_D]

Effectuer l'apprentissage.

file_path_train = glob.glob("trans/*")
model_G = Generator()
model_D = Discriminator()
model_G, model_D, logs = train(model_G, model_D, 40)

## 7.2 Résultats d'apprentissage La perte de données d'entraînement ressemble à ceci. ![loss.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/59885ca0-eb16-7c5b-4e6c-c413ed27e49c.png)

Après 2 époques 1epoch_fake.png

cette? On se sent plutôt bien sauf que l'image de l'avion n'est pas du tout peinte ??

Après 11 époque 10_fake.png

Après 21 époques 20_fake.png

Après 40 fin d'époque (image montrée au début) 0_fake.png

De manière inattendue, j'ai senti que l'image après 2 époques était bonne ...

Je publierai également d'autres images. 11 Après la fin de l'époque. J'ai sélectionné beaucoup d'images qui semblent avoir échoué. L'image terrible est vraiment terrible, avec presque aucune couleur Comme l'image du baseball, je le peins sans ignorer la frontière.

0_2gray.png

10_2_fake.png

Je sens que je suis bon pour les verts comme l'herbe et les arbres, et les bleus comme le ciel. Cela semble dépendre du biais de l'ensemble de données d'origine et de la facilité de la peinture (facilité de reconnaissance).

8. Matome, impression

L'image grise a été colorisée à l'aide de pix2pix.

Cette fois, j'ai décidé d'ajouter une image et de faire une image couleur dès que je pouvais faire quoi que ce soit. Comme le réseau est peu profond, l'expressivité est faible, donc Je pense que la réduction des types d'images fonctionne mieux.

Les références

Pour être honnête, je pense que c'est plus facile à comprendre que ce que j'ai écrit.

U-Net: Convolutional Networks for Biomedical Image Segmentation     https://lmb.informatik.uni-freiburg.de/people/ronneber/u-net/

J'ai implémenté pix2pix à partir de 1 et j'ai essayé de coloriser une image en noir et blanc (PyTorch)     https://blog.shikoan.com/pytorch_pix2pix_colorization/

pix2 je veux comprendre pix     https://qiita.com/mine820/items/36ffc3c0aea0b98027fd

image

CoCo https://cocodataset.org/#home

Labeled Faces in the Wild http://vis-www.cs.umass.edu/lfw/

The PASCAL Visual Object Classes Homepage http://host.robots.ox.ac.uk/pascal/VOC/

Recommended Posts

J'ai créé un réseau pour convertir des images noir et blanc en images couleur (pix2pix)
J'ai créé un programme pour convertir des images en art ASCII avec Python et OpenCV
J'ai créé un outil CLI pour convertir les images de chaque répertoire en PDF
J'ai créé un code pour convertir illustration2vec en modèle Keras
Convertir une vidéo en noir et blanc avec ffmpeg + python + opencv
J'ai créé une caméra réseau simple en combinant ESP32-CAM et RTSP.
J'ai fait un script pour afficher des pictogrammes
J'ai créé un script en python pour convertir des fichiers .md au format Scrapbox
J'ai créé un programme pour saisir ce que j'ai mangé et afficher les calories et les sucres
J'ai créé un outil pour convertir Jupyter py en ipynb avec VS Code
J'ai créé un outil pour informer Slack des événements Connpass et en ai fait Terraform
Introduction à la création d'IA avec Python! Partie 3 J'ai essayé de classer et de prédire les images avec un réseau de neurones convolutifs (CNN)
J'ai créé un outil pour compiler nativement Hy
Je veux exécuter et distribuer un programme qui redimensionne les images Python3 + pyinstaller
J'ai fait un module en langage C qui filtre les images chargées par Python
J'ai créé un outil pour obtenir de nouveaux articles
J'ai fait une bibliothèque pour bien séparer les phrases japonaises
J'ai fait un script pour mettre un extrait dans README.md
J'ai créé un module Python pour traduire les commentaires
J'ai essayé de faire LINE BOT avec Python et Heroku
J'ai fait une commande pour marquer le clip de la table
J'ai créé une bibliothèque python qui fait rouler le rang
〇✕ J'ai fait un jeu
J'ai créé un outil qui facilite un peu la création et l'installation d'une clé publique.
J'ai créé un script en Python pour convertir un fichier texte pour JSON (pour l'extrait d'utilisateur vscode)
Création d'une méthode pour sélectionner et visualiser automatiquement un graphique approprié pour les pandas DataFrame
J'ai essayé de mettre en œuvre le modèle de base du réseau neuronal récurrent
J'ai essayé de faire un classement en grattant l'équipe des membres de l'organisation
J'ai créé un package pour filtrer les séries chronologiques avec python
Démarrez les fichiers Bayer FITS et convertissez-les en TIFF couleur
J'ai fait une commande pour générer un commentaire pour une table dans Django
J'ai créé un outil pour créer un nuage de mots à partir de wikipedia
Convertissez des images numérisées déformées en PDF avec Pillow et PyPDF
[Titan Craft] J'ai créé un outil pour invoquer un géant sur Minecraft
J'ai créé Chatbot en utilisant l'API LINE Messaging et Python
Je vous ai fait exécuter des commandes depuis un navigateur WEB
J'ai fait un générateur de réseau neuronal qui fonctionne sur FPGA
J'ai fait un script pour dire bonjour à mon Koshien
J'ai créé mon propre réseau de neurones à propagation directe à 3 couches et j'ai essayé de comprendre le calcul en profondeur.
J'ai fait un texte Python
J'ai fait un robot discord
J'ai créé une bibliothèque qui lit facilement les fichiers de configuration avec Python
J'ai essayé de transformer un fichier Python en un EXE (erreur de récursivité prise en charge)
J'ai créé un serveur Web avec Razpai pour regarder des anime
Je voulais convertir ma photo de visage en un style Yuyu.
J'ai essayé de faire un processus d'exécution périodique avec Selenium et Python
Gratter et manger des bûches - je veux trouver un bon restaurant! ~ (Travail)
J'ai essayé de créer des taureaux et des vaches avec un programme shell
Je veux créer un fichier pip et le refléter dans le menu fixe
J'ai fait une commande pour afficher un calendrier coloré dans le terminal
J'ai créé Chatbot en utilisant l'API LINE Messaging et Python (2) ~ Server ~
J'ai écrit un script pour aider goodnotes5 et Anki à travailler ensemble
J'ai créé un chat chat bot avec Tensor2Tensor et cette fois cela a fonctionné
J'ai créé un script POST pour créer un problème sur Github et l'enregistrer dans le projet