CONNEXION
  • RetourJeux
    • Sorties
    • Hit Parade
    • Les + populaires
    • Les + attendus
    • Soluces
    • Tous les Jeux
    • Gaming
  • RetourActu Gaming
    • News
    • Astuces
    • Tests
    • Previews
    • Toute l'actu gaming
  • RetourBons plans
    • Bons plans
    • Bons plans Smartphone
    • Bons plans Hardware
    • Bons plans Image et Son
    • Bons plans Amazon
    • Bons plans Cdiscount
    • Bons plans Decathlon
    • Bons plans Fnac
    • Tous les Bons plans
  • RetourJVTech
    • Actus High-Tech
    • Intelligence Artificielle
    • Smartphones
    • Mobilité urbaine
    • Hardware
    • Image et son
    • Tutoriels
    • Tests produits High-Tech
    • Guides d'achat High-Tech
    • JVTech
  • RetourCulture
    • Actus Culture
    • Culture
  • RetourVidéos
    • A la une
    • Gaming Live
    • Vidéos Tests
    • Vidéos Previews
    • Gameplay
    • Trailers
    • Chroniques
    • Replay Web TV
    • Toutes les vidéos
  • RetourForums
    • Hardware PC
    • PS5
    • Switch 2
    • Xbox Series
    • Switch
    • Pokemon pocket
    • FC 25 Ultimate Team
    • League of Legends
    • Tous les Forums
  • PC
  • PS5
  • Xbox Series
  • Switch 2
  • PS4
  • One
  • Switch
  • iOS
  • Android
  • MMO
  • RPG
  • FPS
En ce moment Genshin Impact Valhalla Breath of the wild Animal Crossing GTA 5 Red dead 2
Liste des sujets

Tensorflow, transfert de style

Proutozorent
Proutozorent
Niveau 7
22 mars 2021 à 21:13:52

https://www.tensorflow.org/tutorials/generative/style_transfer

Je m'intéresse à ce tutoriel pour le transfert de style qui suit la méthode proposée dans cet article : https://arxiv.org/pdf/1508.06576.pdf

Voilà en quoi consiste la méthode :
-> On choisit 2 images, une photo, une peinture, respectivement le contenu et le style
-> On utilise un réseau entraîné comme VGG16 ou VGG19
-> On utilise les couches les plus basses de la partie convolutive pour créer ce qu'on appelle l'extracteur qui renvoie des tenseurs correspondant au contenu & au style
-> On applique l'algorithme de transfert de style (c'est là où ça coince) :
En gros, via l'optimizer Adam + une loss (jusque là ça va) on va tenter de réduire la différence entre l'image générée et les target qui correspondent aux features à la fois du contenu et du style. Ce que je ne comprends pas, c'est concrètement comment l'image est générée concrètement voilà exactement le code que je ne comprends pas :

@tf.function()
def train_step(image):
  with tf.GradientTape() as tape:
    outputs = extractor(image) # Ici, on récupère les features via le réseau
    loss = style_content_loss(outputs) #On calcule la fameuse différence avec la loss

  grad = tape.gradient(loss, image) #Et là, je comprends pas comment ça marche
  opt.apply_gradients([(grad, image)]) #Idem
  image.assign(clip_0_1(image)) #On met à jour l'image, donc à chaque step, on voit l'image se styliser
Nirostreetlourd
Nirostreetlourd
Niveau 26
23 mars 2021 à 00:08:24

La comme ça je te dirais d'aller lire la doc pour voir ce que fais chaque méthode...

godrik
godrik
Niveau 30
23 mars 2021 à 01:44:30

c'est pas juste un calcul de gradient pour chaque pixel de l'entre? C'est de la propagation arriere sauf qu'au lieu de la faire sur les coefficient du modele tu le fais sur les valeures en entre.

non?

Proutozorent
Proutozorent
Niveau 7
23 mars 2021 à 18:35:39

Le 23 mars 2021 à 00:08:24 Nirostreetlourd a écrit :
La comme ça je te dirais d'aller lire la doc pour voir ce que fais chaque méthode...

Yes, c'est évidemment le premier truc que je suis allé voir, l'objet gradientTape de tf et malheureusement ça m'aide pas, la doc ne décrit que fonctionnement basique

Le 23 mars 2021 à 01:44:30 godrik a écrit :
c'est pas juste un calcul de gradient pour chaque pixel de l'entre? C'est de la propagation arriere sauf qu'au lieu de la faire sur les coefficient du modele tu le fais sur les valeures en entre.

non?

Yes je pense que c'est ça, en gros dans la variable (type tf.variable) image, à chaque step d'apprentissage cette image est bien de plus en plus "stylisée" seulement, je ne comprends pas comment ça fonctionne en dehors des lignes de code. J'entends par là que le gradient est propagé jusqu'à l'input qui est de taille 1280x720x3 (taille de l'image) puisque le gradient a effectivement cette taille mais je ne comprends pas ce qui relie tout ça. En gros, comment le gradient est calculé quoi et de quelle manière, le réseau est relié à gradientTape.

godrik
godrik
Niveau 30
23 mars 2021 à 19:33:59

Fondamentale, ton modele est une fonction mathematique f qui mappe ton image a la fonction de loss.
Donc c'est une fonction :
f(pixel1_1r, pixel1_1g, pixel1_1b, pixel1_2r, pixel1_2g, pixel1_2b, ... pixel1280_720r, pixel1280_720g, pixel1280_720b) = loss

Tu peux donc exprimer les derives partielles:
d f(pixel1_1r, pixel1_1g, pixel1_1b, pixel1_2r, pixel1_2g, pixel1_2b, ... pixel1280_720r, pixel1280_720g, pixel1280_720b)/ d pixel1_1r
d f(pixel1_1r, pixel1_1g, pixel1_1b, pixel1_2r, pixel1_2g, pixel1_2b, ... pixel1280_720r, pixel1280_720g, pixel1280_720b)/ d pixel1_1g
d f(pixel1_1r, pixel1_1g, pixel1_1b, pixel1_2r, pixel1_2g, pixel1_2b, ... pixel1280_720r, pixel1280_720g, pixel1280_720b)/ d pixel1_1b
...

C'est ca que la fonction calcule (probablement).
Apres comment elle calcul ca depend de la structure de ton reseau et des fonctions qui sont dedans. Mais typiquement toutes les fonctions qui sont la dedans sont lineaire, ou lineaire avec un max, ou un sinus. Du coup tout est differentiable. Et tu peux appliquer les regle de differentiation de f(g(x)) = f'(g(x)) g'(x).
Donc quand tu fais la phase avant, tu log les inputs de chaque fonction et leur valeure en sortie et quand tu va en arriere, tu calcules les derivees.

Et comme quasiment tous est lineaire, c'est quasiment que des produits matrices vecteurs.

Apres, je n'ai jamais implementer ca moi meme, donc je suis sur qu'il y a des aspects qui m'echappe. Quelqu'un sait comment ce truc la marche plus precisement?

Proutozorent
Proutozorent
Niveau 7
24 mars 2021 à 10:52:06

Le 23 mars 2021 à 19:33:59 godrik a écrit :
Fondamentale, ton modele est une fonction mathematique f qui mappe ton image a la fonction de loss.
Donc c'est une fonction :
f(pixel1_1r, pixel1_1g, pixel1_1b, pixel1_2r, pixel1_2g, pixel1_2b, ... pixel1280_720r, pixel1280_720g, pixel1280_720b) = loss

Tu peux donc exprimer les derives partielles:
d f(pixel1_1r, pixel1_1g, pixel1_1b, pixel1_2r, pixel1_2g, pixel1_2b, ... pixel1280_720r, pixel1280_720g, pixel1280_720b)/ d pixel1_1r
d f(pixel1_1r, pixel1_1g, pixel1_1b, pixel1_2r, pixel1_2g, pixel1_2b, ... pixel1280_720r, pixel1280_720g, pixel1280_720b)/ d pixel1_1g
d f(pixel1_1r, pixel1_1g, pixel1_1b, pixel1_2r, pixel1_2g, pixel1_2b, ... pixel1280_720r, pixel1280_720g, pixel1280_720b)/ d pixel1_1b
...

C'est ca que la fonction calcule (probablement).
Apres comment elle calcul ca depend de la structure de ton reseau et des fonctions qui sont dedans. Mais typiquement toutes les fonctions qui sont la dedans sont lineaire, ou lineaire avec un max, ou un sinus. Du coup tout est differentiable. Et tu peux appliquer les regle de differentiation de f(g(x)) = f'(g(x)) g'(x).
Donc quand tu fais la phase avant, tu log les inputs de chaque fonction et leur valeure en sortie et quand tu va en arriere, tu calcules les derivees.

Et comme quasiment tous est lineaire, c'est quasiment que des produits matrices vecteurs.

Apres, je n'ai jamais implementer ca moi meme, donc je suis sur qu'il y a des aspects qui m'echappe. Quelqu'un sait comment ce truc la marche plus precisement?

Merci pour ta réponse. Effectivement absolument toutes les fonctions d'activation sont linéaires, c'est que relu qui est utilisé, il n'y a ni sigmoïd, ni softmax ou quoi, 100% relu (x si x>0, 0 sinon) très facile à dériver ahah
En fait c'est vraiment avec ce que tensorflow fait que j'ai du mal :

En gros à la ligne grad = tape.gradient(loss, image), comment tensorflow arrive à backprog le gradient, comment il communique avec le réseau via extractor sachant que si la fonction s'appelle trainStep, ce n'est pas le réseau qui est entraîné mais l'image en soit.

Ensuite, si j'ai enfin bien compris comment la loss fonctionnait, je ne vois ce qu'elle essaye de minimiser/optimiser, elle n'entraîne pas le réseau donc aucun poids ne change vis à vis du réseau, ce qui me laisse croire que c'est à force de modifier l'image d'entrée qui tend à être 'stylisée', la loss qui prend en entrée la feed-forward de cette même image et ce à quoi elle est sensée ressembler que la loss va tendre vers 0 au fur et à mesure qu'elle ressemble de plus en plus aux deux images d'entrée.

blackapplex
blackapplex
Niveau 10
26 mai 2021 à 21:36:48

Salut, je viens de voir le post, c'est probablement trop tard mais ça servira peut être à d'autres.

- Dans un apprentissage classique, on a une entrée, un réseau, une sortie et une target. On calcule la différence entre la target et la sortie, ça donne l'erreur, et on peut appliquer la chain rule et dériver l'erreur en fonction des paramètres du réseau pour diminuer l'erreur.

- Dans l'apprentissage ici c'est différent, on a une entrée, un réseau, une sortie et une target, jusque là c'est pareil. On calcule la différence entre la target et la sortie, ça donne une erreur, sauf qu'au lieu d'appliquer la chain rule sur le réseau, on l'applique sur la donnée d'entrée pour la modifier. Ici la sortie c'est la représentation du réseau du style de l'image A, et la target c'est la représentation vue par le réseau du style de l'image B. Si le réseau en a la même représentation, c'est qu'il en a extrait les mêmes features et donc que les images ont le même style, et donc que la loss est faible, et donc objectif réussi. Donc l'objectif c'est une correspondance de styles extraits de réseau, mais on veut pas entrainer le réseau puisque c'est le réseau qui nous donne le style, ce qu'on veut c'est entrainer l'image à correspondre à ce que dit le réseau.

Pour avoir une "représentation de style", ils utilisent une matrice de gram apparemment, expliqué ici: https://www.reddit.com/r/computervision/comments/7wd7tr/gram_matrix_in_deep_learning/ , j'ai pas expérimenté cet usage mais apparemment ça permet d'extraire une information plus abstraite dans l'image (ce qu'on appelle son style).
Visiblement dans le code ils ont aussi une "content loss" dont la contrainte est de pas trop déformer l'image d'origine, c'est style_loss + content_loss, que je traduis par "rapproche moi le style avec la cible" + "garde moi le même contenu (rapproche moi le nouveau contenu avec le contenu initial = ça doit rester un chien sur l'image)" = modifie le style mais pas le contenu.

L'idée c'est de se dire qu'on apprend une modification de l'image à correspondre au style donné par un réseau fixe. Si je reprends le code:


@tf.function()
def train_step(image):
  with tf.GradientTape() as tape: #mémorise les opérations effectuées pour calculer le gradient
    outputs = extractor(image) # Ici, on récupère les features via le réseau
    loss = style_content_loss(outputs) #(1) on a calculé le style cible en amont, (2) on calcule le style prédit, (3) on fait la diff ça donne la style loss (+ la content loss)

  grad = tape.gradient(loss, image) #Et là c'est la magie du calcul des gradients. [1] Qui est qu'on sait appliquer des gradients jusque sur l'entrée d'un réseau de neurone. C'est de cette façon qu'on fait de l'apprentissage adverse ou qu'on apprend des représentations de mot pour du traitement de texte
  opt.apply_gradients([(grad, image)]) #ici on refile les données pour appliquer les gradients calculés sur l'image [2]
  image.assign(clip_0_1(image)) #On met à jour l'image, donc à chaque step, on voit l'image se styliser

[1] https://www.tensorflow.org/api_docs/python/tf/GradientTape
[2] https://www.tensorflow.org/api_docs/python/tf/compat/v1/train/AdamOptimizer

Pour la "magie du calcul des gradients sur des input", j'avoue que c'est des cours que moi même j'ai pas suivi dans le détail (au sens où je les ai pas réappliqué avec des vraies valeurs pour vérifier). Mais j'ai déjà vu les maths et ça n'a rien de très différent. On sait calculer la loss en fonction d'un paramètre du réseau mais il s'avère qu'on sait aussi très bien remonter jusqu'à calculer une loss en fonction d'une donnée d'entrée, ce qui peut se comprendre puisqu'au final si le réseau a juste comme poids " * 1 " sur toutes les entrées, on va savoir modifier ce poids en fonction de la loss, sauf qu'au lieux d'appliquer ce poids sur un réseau qui aurait que des * 1 on l'applique sur la donnée d'entrée pour monter ou diminuer une valeur comme on veut.

Tant que tout est dérivable on fait ce qu'on veut donc ça pose pas de problème d'utiliser un réseau de neurone fixe pour calculer une loss (toutes les opérations sont dérivables à travers le réseau, ça s'appelle une "perceptual loss" quand on utilise un CNN pour construire une loss). Et ça pose pas de problème d'appliquer des gradients sur une image.

Pour approfondir les réseaux de neurones j'aurai tendance à recommander Pytorch. Là c'est du vieux code tensorflow ça pique les yeux.

C'est un tuto plutôt avancé mais il n'utilise que des bases du deep learning. Si j'étais un prof sadique je mettrai ce type de réseau dans un examen de deep learning.

blackapplex
blackapplex
Niveau 10
26 mai 2021 à 22:27:47

edit: sur la derniere ligne de code

image.assign(clip_0_1(image)) #On met à jour l'image, donc à chaque step, on voit l'image se styliser

J'ai repris le texte de l'auteur du post mais en fait sauf erreur ici il ne fait que seuiller le minimum et le maximum de l'image pour éviter que le réseau modifie l'image en dehors de 0~255 (0~1 en normalisé). La modification de l'image se fait just avant, "apply_gradients" applique les gradients "grad" sur les paramètres "image" et donc modifie l'image. Si c'était un réseau de neurone qu'on cherchait à apprendre on aurait fait apply_gradients(grad, poids_du_réseau)

Message édité le 26 mai 2021 à 22:28:34 par blackapplex
Sous forums
  • Aide à l'achat Mac
  • Steam Deck
  • Création de sites web
  • Création de Jeux
  • Linux
  • Programmation
  • Internet
  • Macintosh
  • Hardware
La vidéo du moment