Probablement l'une des plus grandes découvertes de l'année, alors j'ai joué avec elle en guise d'introduction à Pytorch. C'est presque la même histoire que les pionniers suivants, donc je vais me concentrer sur les difficultés d'Uwan et un petit commentaire (ce que j'ai remarqué). La référence est la suivante.
Citation
If you use this code for your research, please cite our paper:
@inproceedings{rottshaham2019singan,
title={SinGAN: Learning a Generative Model from a Single Natural Image},
author={Rott Shaham, Tamar and Dekel, Tali and Michaeli, Tomer},
booktitle={Computer Vision (ICCV), IEEE International Conference on},
year={2019}
}
【référence】 ①SinGAN: Learning a Generative Mode from a Single Natural Image@arXiv:1905.01164v2 [cs.CV] 4 Sep 2019 ②Code available at: https://github.com/tamarott/SinGAN ③ Tera était incroyable quand j'ai lu l'article de SinGAN ④ [Explication de l'article] SinGAN: Apprendre un modèle génératif à partir d'une seule image naturelle ⑤ [SinGAN] Active diverses tâches de génération d'images à partir d'une seule image
・ Environnement et exécution ・ Bref commentaire sur le papier ・ À propos de la formation ・ Animation ・ Super résolution ・ Peinture à l'image
Tout d'abord, téléchargez Zip à partir de Github dans Reference (2) ci-dessus et décompressez-le. Vous pouvez l'installer dans l'environnement Pytorch de l'autre jour avec la commande suivante.
Install dependencies
python -m pip install -r requirements.txt
This code was tested with python 3.6 Et ce que vous pouvez faire, c'est: (Traduction de Github ci-dessus)
Train Pour entraîner un modèle SinGAN avec votre propre image, placez l'image d'entraînement sous Input / Images et procédez comme suit:
python main_train.py --input_name <input_file_name>
Vous pouvez également utiliser le modèle entraîné résultant pour générer des échantillons aléatoires à partir de l'échelle la plus grossière (n = 0). Une fois formés, les modèles formés sont stockés pour chaque échelle de rugosité (n).
Pour exécuter ce code sur la machine CPU, spécifiez --not_cuda lors de l'appel de main_train.py
Random samples Pour générer un échantillon aléatoire à partir de l'échelle de rugosité, commencez par entraîner le modèle SinGAN de l'image souhaitée, puis procédez comme suit:
python random_samples.py --input_name <training_image_file_name> --mode random_samples --gen_start_scale <generation start scale number>
Remarque: indiquez 0 pour l'échelle de rugosité de départ lors de l'utilisation du modèle complet et 1 pour démarrer la génération à partir de la deuxième échelle. Il semble que la beauté du produit fini devrait être à l'échelle 0.
Random samples of arbitrery sizes Pour générer un échantillon aléatoire de n'importe quelle taille, commencez par entraîner le modèle SinGAN de l'image souhaitée (comme ci-dessus), puis procédez comme suit:
python random_samples.py --input_name <training_image_file_name> --mode random_samples_arbitrary_sizes --scale_h <horizontal scaling factor> --scale_v <vertical scaling factor>
Animation from a single image Pour générer une courte animation à partir d'une seule image, procédez comme suit:
python animation.py --input_name <input_file_name>
Cela lancera automatiquement une nouvelle phase d'entraînement en mode de remplissage de bruit. Lorsque l'exécution est terminée, une animation Gif est automatiquement générée pour chaque échelle de rugosité de départ et stockée dans chaque Dir. Le changement est le plus important lorsque start_scale = 0, et le changement devient plus petit à mesure que le start_scale augmente. Harmonization Pour harmoniser l'objet collé avec l'image (voir l'exemple de la figure 13 de l'article), commencez par entraîner le modèle SinGAN pour l'image d'arrière-plan que vous voulez (comme ci-dessus), puis la référence collée naïvement Enregistrez l'image et son masque binaire dans "Input / Harmonization" (voir l'exemple dans le répertoire des fichiers de téléchargement). Ensuite, procédez comme suit:
python harmonization.py --input_name <training_image_file_name> --ref_name <naively_pasted_reference_image_file_name> --harmonization_start_scale <scale to inject>
Notez que différentes échelles d'injection produisent différents effets d'harmonie. L'échelle d'injection la plus grossière est 1.
Editing Pour éditer l'image (voir l'exemple de la figure 12 de l'article), commencez par entraîner le modèle SinGAN avec l'image non éditée souhaitée (comme ci-dessus), puis l'image éditée simple, correspondant Enregistrez-le avec la carte binaire comme image de référence sous "Entrée / Edition" (voir Exemple d'image enregistrée). Ensuite, procédez comme suit:
python editing.py --input_name <training_image_file_name> --ref_name <edited_image_file_name> --editing_start_scale <scale to inject>
Les sorties masquées et non masquées sont enregistrées. Là encore, différentes échelles d'injection produisent différents effets d'édition. L'échelle d'injection la plus grossière est 1.
Paint to Image Pour convertir la peinture en une image réaliste (voir l'exemple de la figure 11 de l'article), commencez par entraîner le modèle SinGAN avec l'image que vous souhaitez (comme ci-dessus), puis sous "Entrée / Peinture" Enregistrez la peinture dans et procédez comme suit:
python paint2image.py --input_name <training_image_file_name> --ref_name <paint_image_file_name> --paint_start_scale <scale to inject>
Là encore, différentes échelles d'injection produisent différents effets d'édition. L'échelle d'injection la plus grossière est 1.
Advanced option: Specify quantization_flag to be True, to re-train only the injection level of the model, to get a on a color-quantized version of upsamled generated images from previous scale. For some images, this might lead to more realistic results.
Super Resolution Pour super-résolution de l'image, procédez comme suit:
python SR.py --input_name <LR_image_file_name>
Cela entraînera automatiquement le modèle SinGAN pour un facteur de suréchantillonnage 4x (s'il n'existe pas déjà). Pour différents coefficients SR, utilisez le paramètre --sr_factor lors de l'appel de la fonction. Le coefficient SR est de 4 par défaut, et plus la valeur est élevée, plus l'image finale est grande.
Les résultats SinGAN pour l'ensemble de données BSD100 peuvent être téléchargés à partir du dossier Téléchargements.
Additional Data and Functions Single Image Fréchet Inception Distance (SIFID score) Pour calculer le SIFID entre l'image réelle et le faux échantillon correspondant, procédez comme suit:
python SIFID/sifid_score.py --path2real <real images path> --path2fake <fake images path> --images_suffix <e.g. jpg, png>
Assurez-vous que chacun des faux noms de fichier image est le même que le nom de fichier image correspondant.
Les références sont des articles, etc., mais je pense que l'invention de sinGAN est la suivante.
--Un apprentissage des données --Utiliser ResGAN (perte de WGAN-GP) ――Apprentissage progressif des fonctionnalités du global au local --Bonus; Prend en charge plusieurs tâches
Les données de Learning One sont probablement devenues très populaires récemment, mais je pense que c'est la première fois que je les apprends et les utilise.
ResGAN est dans Reference ⑥, et WGAN-GP est dans Reference ⑦, et il est proposé comme méthode avec des performances de convergence élevées. 【référence】 ⑥Generative Adversarial Network based on Resnet for Conditional Image Restoration@arXiv:1707.04881v1 [cs.CV] 16 Jul 2017 ⑦Improved Training of Wasserstein GANs Tout d'abord, ResGAN dans Reference ⑥ est le générateur suivant.
D'autre part, le Générateur de chaque étape de sinGAN est composé de ResGAN en dessous du niveau de base sauf pour le premier. Autrement dit, $ (\ bar x_ {n-1}) ↑ ^ r $, qui est une image agrandie de $ z_n $ et une image plus grossière, est utilisée comme entrée de $ G_n $, et la différence est apprise pour la rendre plus claire. Il génère l'image $ \ bar x_n $. Note) Ici, $ ↑ ^ r $ indique la montée en puissance de l'image. Au fait,
min_{G_n}max_{D_n}L_{adv}(G_n,D_n)+αL_{rec}(G_n)
Le premier terme est WGAN-GP de référence ⑦, qui est exprimé par la formule suivante. Le deuxième terme est
L_{rec} = ||G_n(0,(\bar{x}^{rec}_{n+1}) ↑^r) − x_n||^2,
and for
L_{rec} = ||G_N (z^∗) − x_N||^2
"L'image du bruit d'entrée à ce moment est $ z_n (n = 0, ..., N-1) = 0 $, et seul $ z_N $ est un nombre aléatoire fixe défini au début de l'apprentissage." (Référence ④ Plus cité)
L'apprentissage se poursuit en répétant ResGAN comme indiqué dans la figure ci-dessous. Ici, l'apprentissage commence à partir de la ligne du bas, mais ici, seul $ z_N $ généré à partir de nombres aléatoires est entré. Dicriminator le compare à l'image réelle réduite $ x_N $ de l'image d'origine, qui est automatiquement déterminée lorsque le nombre de formations est déterminé. Après cela, entrez l'image $ (\ bar x_ {n-1}) ↑ ^ r $ et $ z_ {N-1} $ qui sont les images générées de cette manière à partir de l'image $ \ bar x_ {n-1} $. Faire. De cette manière, diverses applications utilisent les paramètres d'apprentissage et les images appris.
Comme mentionné ci-dessus, je pense que vous pouvez apprendre. L'environnement Pytorch de Wan utilise 1060, donc la mémoire du GPU est d'environ 3 Go. Avec cela, il y avait des images telles que vaches.png qui ne pouvaient pas être apprises jusqu'à la fin. Par conséquent, j'ai essayé de réduire la taille des images initiales (Input / images), mais la taille de l'image réduite telle que n = 0 pour l'apprentissage n'a pas changé et l'erreur Memmory n'a pas disparu facilement. Quand je l'ai réduit à la 1 / 3ème place, j'ai réussi à réduire un peu la valeur finale de n et j'ai pu apprendre en toute sécurité, mais le résultat était que l'image d'apprentissage était petite et pas très intéressante.
C'est intéressant car il bouge, mais quand vous regardez animation.py, il semble que vous vous déplaciez en changeant la localité de l'entité (en changeant la valeur de start_scale) et en agitant des nombres aléatoires dans l'espace latent. En conséquence, vous pouvez créer une animation dans laquelle une petite valeur de n fluctue considérablement et une grande valeur de n se déplace à peine. Voici quelques exemples.
start_scale=0 | start_scale=1 | start_scale=2 |
---|---|---|
Selon le tableau ci-dessous dans le document, la précision est comparable à SRGAN, qui a été introduit par Wan l'autre jour. Alors, j'ai essayé ce qui suit. Dans le tableau ci-dessous, la super résolution est augmentée vers la droite. Dans le même temps, la taille devient plus grande à mesure qu'elle va vers la droite. Vous pouvez sentir la taille réelle et la super résolution en cliquant dessus pour l'afficher indépendamment.
original | Extension 1 | Extension 2 | Extension 3 |
---|---|---|---|
Cela signifie convertir une simple image en image. L'exemple suivant est publié dans l'article, ce qui signifie que si vous entraînez l'image sur le côté gauche, placez la deuxième image simple dans "Entrée / peintures" et exécutez la commande, l'image du côté droit sera sortie. Cette figure montre également que les résultats de sinGAN sont supérieurs aux autres méthodes. Le résultat de l'exécution de ce Wan est le suivant. Pour ce faire, j'ai eu une erreur de mémoire sur le 1060 et je n'ai pas pu apprendre l'image de gauche. Donc, L'image 250x141 a été réduite à 80x46. L'image Paint est de 300x200. Le résultat est trop petit, mais plus les paramètres d'apprentissage sont grossiers, plus l'image peut être reproduite grossièrement. Par contre, lorsque n = 1, une image de vache apparaît dans une certaine mesure.
L'image originale | Paint | n=1 | n=2 | n=3 | n=4 |
---|---|---|---|---|---|
・ J'ai joué avec sinGAN ・ Pour le moment, j'ai compris le principe ・ J'ai pu réaliser la puissance de l'apprentissage local de toute la région en utilisant le nouveau ResGAN.
・ S'il s'agit de 1060, la taille de l'image sera limitée en raison du manque de mémoire GPU. ・ Je pense que c'est une découverte qui donne une impression de progrès
Les paramètres du générateur et du Dicriminator de ResGAN sont ajustés en fonction de la taille de l'image d'entrée et ont la structure suivante.
GeneratorConcatSkip2CleanAdd(
(head): ConvBlock(
(conv): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(body): Sequential(
(block1): ConvBlock(
(conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(block2): ConvBlock(
(conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(block3): ConvBlock(
(conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
)
(tail): Sequential(
(0): Conv2d(32, 3, kernel_size=(3, 3), stride=(1, 1))
(1): Tanh()
)
)
WDiscriminator(
(head): ConvBlock(
(conv): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(body): Sequential(
(block1): ConvBlock(
(conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(block2): ConvBlock(
(conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(block3): ConvBlock(
(conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
)
(tail): Conv2d(32, 1, kernel_size=(3, 3), stride=(1, 1))
...
GeneratorConcatSkip2CleanAdd(
(head): ConvBlock(
(conv): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(body): Sequential(
(block1): ConvBlock(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(block2): ConvBlock(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(block3): ConvBlock(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
)
(tail): Sequential(
(0): Conv2d(64, 3, kernel_size=(3, 3), stride=(1, 1))
(1): Tanh()
)
)
WDiscriminator(
(head): ConvBlock(
(conv): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(body): Sequential(
(block1): ConvBlock(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(block2): ConvBlock(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
(block3): ConvBlock(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))
(norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(LeakyRelu): LeakyReLU(negative_slope=0.2, inplace=True)
)
)
(tail): Conv2d(64, 1, kernel_size=(3, 3), stride=(1, 1))
)
Recommended Posts