C'est un thème souvent lié à l'apprentissage automatique, mais j'aimerais coloriser les photographies en noir et blanc. J'ai également mis le code sur GitHub. [GitHub]
L'autre jour, alors que je regardais les reliques de mon père, j'ai soudainement vu une vieille photo en noir et blanc. Je l'ai laissé tel quel quand je suis mort, mais maintenant je pense qu'il peut être coloré. Cela ressemble plus à un blog de passe-temps personnel qu'à un blog technique. En outre, il existe des blogs et des livres de ceux qui sont mentionnés lors de l'étude du GAN. L'article de cette personne est très éducatif, et cet article a également du contenu. Je publierai un lien ci-dessous, alors jetez un œil.
[URL de référence] Shikoan's ML Blog Apprendre de la suppression de la mosaïque, apprentissage en profondeur de pointe
GAN Discriminator est intégré à Generator, et les deux sont appris en parallèle. C'est une image de l'ajout d'une fonction de perte dynamique en utilisant la valeur prédite de Discriminateur à Perte de générateur. Il existe différents types de GAN, et la division varie en fonction de la définition, mais il existe les divisions suivantes.
type | Aperçu |
---|---|
Conditional GAN | Il existe une relation entre l'entrée et la sortie du générateur |
Non-Conditional GAN | L'entrée et la sortie du générateur ne sont pas liées |
Cette fois, pix2pix est un GAN conditionnel (CGAN) typique. DCGAN, qui est souvent utilisé dans les didacticiels GAN, génère une sortie à partir de données de bruit, il devient donc un GAN non conditionnel. Puisque pix2pix est CGAN, les informations d'entrée deviennent très importantes. Par exemple, dans DCGAN, l'apprentissage procède en utilisant la perte Adversarial qui apparaît en relation avec Discriminator, mais dans le cas de pix2pix, en plus de Adversarial Loss, la différence entre la fausse image et l'image réelle (par exemple, la perte L1 ou MSE) est également utilisée. L'apprentissage progresse. Ce faisant, l'apprentissage progresse plus rapidement que les autres GAN et les résultats sont plus stables. Au contraire, dans le cas d'une méthode d'apprentissage utilisant uniquement la perte L1, afin de réduire la perte, la sortie est vague dans son ensemble, ou le tout est peint solide avec des pixels moyens. Cela a tendance à donner l'impression d'être bâclé, et en ajoutant une perte d'adversaire, l'apprentissage a tendance à progresser de sorte que même si la perte de L1 peut être assez importante, une image plus réaliste peut être produite. Le principe selon lequel il y a un compromis entre la qualité perçue et la distorsion est l'un des facteurs très importants dans l'examen du GAN.
Référence: The Perception-Distortion Tradeoff (2017) [arXiv]
PatchGAN Ce qui suit est le papier original de pix2pix, mais je pense qu'il vaut mieux se référer ici pour plus de détails sur PatchGAN.
Image-to-Image Translation with Conditional Adversarial Networks (2016) [arXiv]
Dans PatchGAN, lorsque Discriminator juge l'exactitude d'une image, celle-ci est divisée en plusieurs zones et le jugement d'exactitude est effectué dans chaque zone.
Diviser la zone ne signifie pas que vous divisez réellement l'image et la plongez séparément dans le Discriminator. En théorie, c'est le cas, mais en termes de mise en œuvre, une image est insérée dans le Discriminator et sa sortie est transformée en un tenseur au deuxième étage. À ce moment-là, la valeur de chaque pixel du tenseur est dérivée sur la base des informations de la zone de patch de l'image d'entrée, et par conséquent, la valeur de chaque pixel du tenseur se situe entre le vrai vrai ou faux (1 ou 0). Le patch GAN est réalisé en prenant la perte de. Je ne pense pas que ce soit facile à expliquer avec des mots, je vais donc donner un exemple avec le drapeau français ci-dessus.
fig, axes = plt.subplots(1,2)
#Chargement des images
img = cv2.imread('france.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (600, 300)) #format original-> (300, 600, 3)
axes[0].imshow(img)
axes[0].set_xticks([])
axes[0].set_yticks([])
img = ToTensor()(img) # (300, 600, 3) -> (3, 300, 600)
img = img.view(1, 3, 300, 600) # (3, 300, 600) -> (1, 3, 300, 600)
img = nn.AvgPool2d(100)(img) # (1, 3, 300, 600) -> (1, 3, 3, 6)
img = nn.Conv2d(3, 1, kernel_size=1)(img) # (1, 3, 3, 6) -> (1, 1, 3, 6)
img = np.squeeze(img.detach().numpy())
axes[1].imshow(img, cmap='gray')
axes[1].set_xticks([])
axes[1].set_yticks([])
[résultat]
Dans ce qui précède, l'image d'entrée est déposée dans une carte de caractéristiques de taille (3, 6), mais il ne s'agit que de compresser toute la zone de patch de l'image d'origine en (1, 1). Dans l'exemple ci-dessus, l'authenticité est évaluée pour `` 3 × 6 = 18 '' pixels.
Unet
pix2pix utilise U-net comme générateur. U-net, qui est le même dans la segmentation, a une connexion de saut en plus de la structure encodeur-décodeur, de sorte que les informations des données d'entrée ne soient pas perdues autant que possible. Voici la méthode avancée de Generator utilisée dans l'expérience, mais vous pouvez voir que les informations d'origine sont concaténées à chaque fois avec torch.cat
.
def forward(self, x):
x1 = self.enc1(x)
x2 = self.enc2(x1)
x3 = self.enc3(x2)
x4 = self.enc4(x3)
out = self.dec1(x4)
out = self.dec2(torch.cat([out, x3], dim=1))
out = self.dec3(torch.cat([out, x2], dim=1))
out = self.dec4(torch.cat([out, x1], dim=1))
return out
À propos, l'image U-net suivante est une diapositive d'image liée à l'apprentissage automatique publiée gratuitement par Google, qui a récemment été mentionnée sur Twitter. [Visuels ML] Il y a beaucoup de belles photos, alors jetez un œil.
Google PhotoScan Utilisez MIT-Adobe FiveK Dataset comme données d'entraînement. Ceci est souvent utilisé dans les papiers d'amélioration d'image, et il s'agit d'un ensemble d'images non traitées et d'images post-traitées par un éditeur professionnel, mais cette fois, j'utiliserai cette image traitée. En ce qui concerne les photos traitées, il existe de nombreuses photos avec des couleurs plus vives que les photos couleur ordinaires, j'ai donc pensé que cela conviendrait également à cette tâche. Si la taille des données est petite, la capacité de données n'est pas grande et le téléchargement est raisonnable dans une certaine mesure. Il s'agit d'une vraie photo en noir et blanc, mais elle a été convertie en données à l'aide d'une application appelée "Google PhotoScan" sur l'iPhone. Cette application sortira en détail si vous la recherchez sur Google, mais elle est assez excellente et elle la convertira en jolies données sans aller dans un magasin photo (et en un instant). La photo originale était assez ancienne et jaunie, mais lorsque je l'ai convertie en une image en noir et blanc, elle n'avait pas l'air très différente d'une image normale en noir et blanc.
L'image en cinqk et l'image que vous souhaitez coloriser ne sont pas carrées mais rectangulaires, et les proportions sont différentes. Par conséquent, j'ai décidé d'adopter l'un des quatre modèles suivants.
Pour le moment, j'ai décidé d'utiliser la méthode 3 cette fois. En ce qui concerne 1 et 4, la raison de l'adoption est qu'il est possible que les caractéristiques de l'image changent de manière significative, et pour 2 et 3, j'ai pensé que 3 aurait plus d'informations. Bien sûr, si vous allez tout droit, c'est peut-être le numéro 5, mais je n'aime pas les cultures carrées avant les photos de production. Le résultat de la sortie sera rogné au rapport hauteur / largeur d'origine lors du post-traitement.
Apprentissage partie 1
Vous pouvez voir comment le discriminateur devient progressivement plus fort et la perte du générateur augmente, à partir de l'endroit où il a tendance à se déchaîner dans les premiers stades. Quant à la perte de L1, je ne sais pas si elle diminue. (La difficulté de l'évaluation du GAN est que `L1loss est petit = proche de la couleur réelle ≠ réelle".)
Apprentissage de la partie 2. Dans l'apprentissage 1, le discriminateur avait tendance à être plus fort, j'ai donc décidé d'expérimenter en modifiant les points suivants.
- D,Et la perte contradictoire de G est passée de BCE à Hinge Loss
-Changer la normalisation des lots de D en normalisation d'instance
-Réduire de moitié la fréquence de mise à jour du poids de D (moitié de G)
Dans la partie 2, le changement par rapport à la partie 1 consiste à changer la fonction de perte de l'entropie croisée binaire utilisée dans la partie 1 à la perte de charnière. En gros, le but est de «faire une faible perte pour éviter qu'un réseau (D ou G) ne devienne trop fort». Ce qui suit est l'entropie croisée (version D) de la plaque de fer.
D:loss = -\sum_{i=1}^{n}\bigl(t_ilogy_i - (1-t_i) log(1-y_i) \bigl)
En bref, lorsque target = 1
, la sortie doit être aussi élevée que possible (puisque la couche finale est sigmoïde, plus elle est grande, plus elle est proche de 1). Lorsque target = 0
, si vous vous entraînez à augmenter la sortie dans le sens négatif, la perte sera plus petite.
Au contraire, pour G, nous nous entraînerons à maximiser l'équation ci-dessus. De plus, dans le cas de G, seule la fausse image générée par elle-même est évaluée, donc le terme gauche de la formule ci-dessus disparaît et cela devient plus simple.
G:loss = \sum_{i=1}^{n}log\Bigl(1 - D\bigl(G(z_i))\bigl) \Bigl)(Maximiser)\\
= \sum_{i=1}^{n}log\Bigl(D\bigl(G(z_i)) - 1\bigl) \Bigl)(Minimiser)\\
= -\sum_{i=1}^{n}log\Bigl(D\bigl(G(z_i))\bigl) \Bigl) (Il est également possible de penser que cela est minimisé)
Ce qui précède donne deux modèles d'optimisation, mais il semble qu'il existe les deux méthodes de mise en œuvre.
Par contre, concernant la perte de charnière, je pense que ce site est facile à comprendre. [Référence] Le site dit qu'il n'est pas souvent utilisé pour autre chose que SVM, mais il est intéressant de noter qu'il est actuellement utilisé dans d'autres méthodes. En entropie croisée, la cible est exprimée par (0,1), mais en charnière, elle est exprimée par (-1,1).
t = ±1 \\
Loss = max(0, 1-t*y)
En ce qui concerne la formule, si vous produisez une sortie plus petite lorsque target = -1
, et inversement, produisez une sortie plus grande lorsque cible = 1
, la perte sera faible. Cependant, contrairement à l'entropie croisée, vous pouvez également voir que la perte est coupée à 0 dans une certaine mesure. Dans le cas de l'entropie croisée, la perte ne disparaîtra jamais à moins qu'elle ne soit complètement prédite par (0,1), mais ce n'est pas le cas avec les charnières. C'est la raison pour laquelle on l'appelle `` faible perte ''. L'implémentation de PyTorch est la suivante. Au niveau des charnières, il est difficile de diviser les motifs par D et G, donc je pense qu'il vaut mieux les classer tous ensemble.
# ones:Patch avec toutes les valeurs 1
# zeros:Patch avec toutes les valeurs 0
# Gloss(BCE)
loss = torch.nn.BCEWithLogitsLoss()(d_out_fake, ones)
# Gloss(Hinge)
loss = -1 * torch.mean(d_out_fake)
# Dloss(BCE)
loss_real = torch.nn.BCEWithLogitsLoss()(d_out_real, ones)
loss_fake = torch.nn.BCEWithLogitsLoss()(d_out_fake, zeros)
loss = loss_reak + loss_fake
# Dloss(Hinge)
loss_real = -1 * torch.mean(torch.min(d_out_real-1, zeros))
loss_fake = -1 * torch.mean(torch.min(-d_out_fake-1, zeros))
loss = loss_reak + loss_fake
Instance Normalization La normalisation d'instance est un dérivé de la normalisation par lots. Le contenu, y compris la normalisation par lots, est bien organisé dans les articles suivants.
[GIF] Explication de CNN pour les débutants sur la normalisation par lots et ses amis
La normalisation par lots effectue un traitement de standardisation entre les mêmes canaux de données contenus dans un mini-lot, mais la normalisation d'instance n'effectue pas le mini-lot entier mais les données seules. Le point est la normalisation des lots avec une taille de lot 1. Par exemple, il est également utilisé dans pix2pix HD, qui est un dérivé de pix2pix, mais son but est de rendre difficile la convergence de l'apprentissage en supprimant l'augmentation du gradient. L'objectif principal est d'équilibrer D et G en appliquant cela à D.
Voici les résultats. Vous pouvez voir que la convergence de Discriminator est clairement plus lente qu'auparavant. Je pense également que la diminution de la perte de L1 est plus importante qu'avant.
La partie d'apprentissage 3 a changé les points suivants de l'apprentissage 1.
-Suivre essentiellement l'apprentissage 1
-Alignez le taux d'apprentissage sur le papier d'origine.(1e-4 -> 2e-4)
-Suppression de l'ajustement du taux d'apprentissage à l'aide du planificateur
-Changement de la taille de l'image de 320 à 256 (suivant le papier)
-Changement du nombre de zones PatchGAN de 10x10 à 4x4
-Suppression du flou dans l'augmentation du train
Cependant, le résultat était presque le même que 1.
En regardant les résultats jusqu'à présent, nous pouvons voir la tendance selon laquelle «les paysages naturels peuvent être relativement colorés, mais les gens ne travaillent pas du tout». Cela n'a aucun sens pour l'objectif initial, j'ai donc décidé d'acquérir à nouveau les données moi-même. Quant à savoir comment collecter des données, j'ai écrit dans l'article précédent de Qiita, mais j'ai utilisé BingImageSearch. [Lien de l'article Qiita] De plus, bien qu'il soit basé sur la partie 2, il a été légèrement altéré. De plus, comme le nombre de données a triplé (plus de 13 000), nous avons réduit de 200 à 100 époques.
#Changements par rapport à l'apprentissage 1
-Ajouter des données d'entraînement (cinqk)-> fivek+Image de personnes)
-200 numéros d'époque-> 97
- D,Changement de la perte conflictuelle de BCE en perte charnière(Avec la partie 2)
-Changer la normalisation des lots de D en normalisation d'instance(Avec la partie 2)
-Réduire de moitié la fréquence de mise à jour du poids de D(Avec la partie 2)
-Suppression de l'ajustement du taux d'apprentissage à l'aide du planificateur(Avec la partie 3)
-Taux d'apprentissage 1e-4->2e-Changé en 4 (avec la partie 3)
#La raison pour laquelle le nombre de pixels dans la sortie était un peu insatisfaisant n'est peut-être pas vraiment bonne
-Changer la taille de l'image(320->352) (<-new)
-Parallèlement à ce qui précède, le nombre de zones PatchGAN(10,10)->(11,11)changer en (<-new)
Voici les résultats. Par rapport à la partie 2, le grand mouvement vertical dans la dernière étape semble être la première cause de suppression du planificateur.
Grâce à divers essais et erreurs, les images de paysage peuvent être colorées sans gêne à une vitesse considérable, tandis que les images de portrait et les images de contraste flashy (par exemple, des personnes, des vêtements, des fleurs, des objets artificiels, etc.) ne sont pas aussi colorées. J'ai trouvé une tendance à ne pas continuer.
(Gauche: fausse image Droite: image réelle) Au contraire, je sens que cela semble mystérieux. C'est intéressant à regarder. De plus, il me semblait que ma photo de la production réelle était bien colorée (rires). .. ..
(Finalement considéré) Bien que ce soit mon image de test (photo du père), le résultat est plus inégal que les données d'apprentissage et de vérification. Probablement, je pense qu'il y a probablement un problème de résolution en premier lieu. L'image ci-dessus est toujours bonne, et même si d'autres images en noir et blanc sont converties en données, le contour peut être vu lorsqu'il est agrandi, mais la résolution est inégale dans de petites zones et le résultat est décevant. Donc, si je veux le faire sérieusement, je ferai la tâche de super-résolution en parallèle. Alternativement, il peut être nécessaire de prendre des mesures telles que le brouillage des données d'apprentissage. Je le ferai la prochaine fois (prévu).
Recommended Posts