La semaine dernière, lorsque j’ai présenté Google Deep Dream, il a fallu que je prenne une décision éditoriale afin de garder le billet dans une taille raisonnable. Configurer Docker et mettre en place le nécessaire pour « rêver » sur sa première image était déjà une bouchée raisonnable à mastiquer, à mon avis.
Alors, une fois que vous avez Deep Dream, que pouvez-vous faire de plus? DE LA SCIENCE!
Le code source
La plateforme utilisée par Deep Dream pour exécuter le code source permettant de faire fonctionner la machine à rêver est IPython. IPython permet, notamment, d’exécuter de façon interactive du code source Python à même votre navigateur web.
En particulier, avec l’exemple de Deep Dream, des explications sont fournies pour chaque extrait de code source que vous serez invité à exécuter. Parlant de code source, j’ai extrait le code de la page interactive de Deep Dream afin que vous puissiez voir une idée de ce que cela représente.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# imports and basic notebook setup | |
from cStringIO import StringIO | |
import numpy as np | |
import scipy.ndimage as nd | |
import PIL.Image | |
from IPython.display import clear_output, Image, display | |
from google.protobuf import text_format | |
import caffe | |
def showarray(a, fmt='jpeg'): | |
a = np.uint8(np.clip(a, 0, 255)) | |
f = StringIO() | |
PIL.Image.fromarray(a).save(f, fmt) | |
display(Image(data=f.getvalue())) | |
model_path = '/src/bvlc_googlenet/' # substitute your path here | |
net_fn = model_path + 'deploy.prototxt' | |
param_fn = model_path + 'bvlc_googlenet.caffemodel' | |
# Patching model to be able to compute gradients. | |
# Note that you can also manually add "force_backward: true" line to "deploy.prototxt". | |
model = caffe.io.caffe_pb2.NetParameter() | |
text_format.Merge(open(net_fn).read(), model) | |
model.force_backward = True | |
open('tmp.prototxt', 'w').write(str(model)) | |
net = caffe.Classifier('tmp.prototxt', param_fn, | |
mean = np.float32([104.0, 116.0, 122.0]), # ImageNet mean, training set dependent | |
channel_swap = (2,1,0)) # the reference model has channels in BGR order instead of RGB | |
# a couple of utility functions for converting to and from Caffe's input image layout | |
def preprocess(net, img): | |
return np.float32(np.rollaxis(img, 2)[::–1]) – net.transformer.mean['data'] | |
def deprocess(net, img): | |
return np.dstack((img + net.transformer.mean['data'])[::–1]) | |
def make_step(net, step_size=1.5, end='inception_5b/5x5_reduce', jitter=32, clip=True): | |
'''Basic gradient ascent step.''' | |
src = net.blobs['data'] # input image is stored in Net's 'data' blob | |
dst = net.blobs[end] | |
ox, oy = np.random.randint(–jitter, jitter+1, 2) | |
src.data[0] = np.roll(np.roll(src.data[0], ox, –1), oy, –2) # apply jitter shift | |
net.forward(end=end) | |
dst.diff[:] = dst.data # specify the optimization objective | |
net.backward(start=end) | |
g = src.diff[0] | |
# apply normalized ascent step to the input image | |
src.data[:] += step_size/np.abs(g).mean() * g | |
src.data[0] = np.roll(np.roll(src.data[0], –ox, –1), –oy, –2) # unshift image | |
if clip: | |
bias = net.transformer.mean['data'] | |
src.data[:] = np.clip(src.data, –bias, 255–bias) | |
def deepdream(net, base_img, iter_n=10, octave_n=4, octave_scale=1.4, end='inception_5b/5x5_reduce', clip=True, **step_params): | |
# prepare base images for all octaves | |
octaves = [preprocess(net, base_img)] | |
for i in xrange(octave_n–1): | |
octaves.append(nd.zoom(octaves[–1], (1, 1.0/octave_scale,1.0/octave_scale), order=1)) | |
src = net.blobs['data'] | |
detail = np.zeros_like(octaves[–1]) # allocate image for network-produced details | |
for octave, octave_base in enumerate(octaves[::–1]): | |
h, w = octave_base.shape[–2:] | |
if octave > 0: | |
# upscale details from the previous octave | |
h1, w1 = detail.shape[–2:] | |
detail = nd.zoom(detail, (1, 1.0*h/h1,1.0*w/w1), order=1) | |
src.reshape(1,3,h,w) # resize the network's input image size | |
src.data[0] = octave_base+detail | |
for i in xrange(iter_n): | |
make_step(net, end=end, clip=clip, **step_params) | |
# visualization | |
vis = deprocess(net, src.data[0]) | |
if not clip: # adjust image contrast if clipping is disabled | |
vis = vis*(255.0/np.percentile(vis, 99.98)) | |
showarray(vis) | |
print octave, i, end, vis.shape | |
clear_output(wait=True) | |
# extract details produced on the current octave | |
detail = src.data[0]–octave_base | |
# returning the resulting image | |
return deprocess(net, src.data[0]) | |
# Show and assign the image | |
img = np.float32(PIL.Image.open('/src/<img>.jpg')) | |
showarray(img) | |
# Dream | |
_=deepdream(net, img) | |
# Dream with layer | |
_=deepdream(net, img, end='inception_3a/output') | |
# Display keys | |
net.blobs.keys() | |
# loop dream | |
!mkdir frames | |
frame = img | |
frame_i = 0 | |
h, w = frame.shape[:2] | |
s = 0.05 # scale coefficient | |
for i in xrange(100): | |
frame = deepdream(net, frame, end='inception_4e/5×5') | |
PIL.Image.fromarray(np.uint8(frame)).save("frames/%04d.jpg"%frame_i) | |
frame = nd.affine_transform(frame, [1–s,1–s,1], [h*s/2,w*s/2,0], order=1) | |
frame_i += 1 |
Je ne crois pas que vous devez être vraiment familier avec Python pour comprendre le fonctionnement sommaire de ce code. La principale chose à savoir avec Python est que l’indentation va servir à identifier un bloc d’exécution. Alors, pas d’accolades comme en C# avec Python.
Des jeux de données supplémentaires
Lorsque vous exécutez Deep Dream dans la machine virtuelle Docker de mon billet précédent, un jeu de données nommé bvlc_googlenet est utilisé. Ce jeu de modèle, mis en place par Google, est reconnu pour sa capacité à reconnaître et classer des images.
Si vous êtes du genre à pousser l’expérience un peu plus loin, vous pouvez télécharger ces modèles de données et les exécuter sur votre instance Deep Dream. Par exemple, bvlc_googlenet et bvlc_alexnet servent des buts différents et donnent des résultants qui sont, en conséquent, différents.
Voici les étapes pour y arriver:
- Cloner le repository git https://github.com/BVLC/caffe.git
- Copier le contenu du dossier /Models/ dans /deepdream (le répertoire où vous avez mappé le répertoire /src dans la commande de démarrage du conteneur Docker dans les instructions du précédent billet)
- Pour chaque jeu de données, se référer aux instructions pour télécharger le fichier à l’extension .caffemodel et le déposer à la racine du dossier du jeu de données que vous désirez utiliser.
Pour modifier le code précédemment cité, afin de faire fonctionner des jeux de données différents, vous devez ajuster les bouts de code suivants:
- La variable model_path: afin d’y spécifier le répertoire de votre jeu de données (portez attention à l’emplacement débutant par /src
- La variable param_fn: spécifiez-y le nom de votre fichier .caffemodel
- La fonction make_step: modifiez le contenu du paramètre end pour un des layers de votre jeu de données. Pour trouver le nom d’un layer, consultez le fichier deploy.prototxt de votre jeu de données. Il s’agit d’un fichier JSON qui contient la définition des layers à votre disposition
- La fonction deepdream: idem au point précédent.
- La variable img: spécifiez le nom de l’image que vous désirez utiliser.
Faites de beaux rêves!
Bonjour, enfin des explications en langue de Voltaire ! Comment évite t-on les chiens de Google ? Comment utiliser ses propres images comme références ? Peut-on utiliser cette installation pour de la vidéo…. ? Merci pour ce beau tutoriel
Le tout est vraiment dans le guide. Cependant, il se peut qu’il ne soit plus à jour. Le billet a presqu’un an déja. Le temps passe vite… :)