1+
2+
3+ # Setup
4+
15library(keras )
6+ library(tensorflow )
7+
8+
9+ base_image_path = get_file(' paris.jpg' , ' https://i.imgur.com/aGBdQyK.jpg' )
10+ result_prefix = ' sky_dream'
11+
12+ # These are the names of the layers
13+ # for which we try to maximize activation,
14+ # as well as their weight in the final loss
15+ # we try to maximize.
16+ # You can tweak these setting to obtain new visual effects.
17+ layer_settings = list (
18+ ' mixed4' = 1.0 ,
19+ ' mixed5' = 1.5 ,
20+ ' mixed6' = 2.0 ,
21+ ' mixed7' = 2.5
22+ )
223
24+ # Playing with these hyperparameters will also allow you to achieve new effects
25+ step = 0.01 # Gradient ascent step size
26+ num_octave = 3 # Number of scales at which to run gradient ascent
27+ octave_scale = 1.4 # Size ratio between scales
28+ iterations = 20 # Number of ascent steps per scale
29+ max_loss = 15 .
330
4- # Utility functions -------------------------------------------------------
31+ # This is our base image:
32+ plot(magick :: image_read(base_image_path ))
533
6- # Util function to open, resize, and format pictures into tensors that Inception V3 can process
34+ # Let's set up some image preprocessing/deprocessing utilities:
735preprocess_image <- function (image_path ) {
8- image_load(image_path ) %> %
9- image_to_array() %> %
10- array_reshape(dim = c(1 , dim(. ))) %> %
11- inception_v3_preprocess_input()
12- }
13-
14- # Util function to convert a tensor into a valid image
15- deprocess_image <- function (img ) {
16- img <- array_reshape(img , dim = c(dim(img )[[2 ]], dim(img )[[3 ]], 3 ))
17- # Undoes preprocessing that was performed by `imagenet_preprocess_input`
18- img <- img / 2
19- img <- img + 0.5
20- img <- img * 255
21-
22- dims <- dim(img )
23- img <- pmax(0 , pmin(img , 255 ))
24- dim(img ) <- dims
36+ # Util function to open, resize and format pictures
37+ # into appropriate arrays.
38+ img = tf $ keras $ preprocessing $ image $ load_img(image_path )
39+ img = tf $ keras $ preprocessing $ image $ img_to_array(img )
40+ img = tf $ expand_dims(img , axis = 0L )
41+ img = inception_v3_preprocess_input(img )
2542 img
2643}
2744
28- resize_img <- function (img , size ) {
29- image_array_resize(img , size [[1 ]], size [[2 ]])
45+
46+ deprocess_image <- function (x ) {
47+ x = array_reshape(x , dim = c(dim(img )[[2 ]], dim(img )[[3 ]], 3 ))
48+ # Undo inception v3 preprocession
49+ x = x / 2 .
50+ x = x + 0.5
51+ x = x * 255 .
52+ # Convert to uint8 and clip to the valid range [0, 255]
53+ x = tf $ clip_by_value(x , 0L , 255L ) %> % tf $ cast(dtype = ' uint8' )
54+ x
3055}
3156
3257save_img <- function (img , fname ) {
@@ -35,131 +60,109 @@ save_img <- function(img, fname) {
3560}
3661
3762
38- # Model ----------------------------------------------
3963
40- # You won't be training the model, so this command disables all training-specific operations.
41- k_set_learning_phase(0 )
42-
43- # Builds the Inception V3 network, without its convolutional base. The model will be loaded with pretrained ImageNet weights.
64+ # Build an InceptionV3 model loaded with pre-trained ImageNet weights
4465model <- application_inception_v3(weights = " imagenet" ,
4566 include_top = FALSE )
4667
47- # Named list mapping layer names to a coefficient quantifying how much the layer's activation contributes to the loss you'll seek to maximize. Note that the layer names are hardcoded in the built-in Inception V3 application. You can list all layer names using `summary(model)`.
48- layer_contributions <- list (
49- mixed2 = 0.2 ,
50- mixed3 = 3 ,
51- mixed4 = 2 ,
52- mixed5 = 1.5
53- )
54-
55- # You'll define the loss by adding layer contributions to this scalar variable
56- loss <- k_variable(0 )
57- for (layer_name in names(layer_contributions )) {
58- coeff <- layer_contributions [[layer_name ]]
68+ # Get the symbolic outputs of each "key" layer (we gave them unique names).
69+ outputs_dict = list ()
70+ for (layer_name in names(layer_settings )) {
71+ coeff <- layer_settings [[layer_name ]]
5972 # Retrieves the layer's output
6073 activation <- get_layer(model , layer_name )$ output
61- scaling <- k_prod(k_cast(k_shape(activation ), " float32" ))
62- # Retrieves the layer's output
63- loss <- loss + (coeff * k_sum(k_square(activation )) / scaling )
74+ outputs_dict [[layer_name ]] <- activation
6475}
6576
66- # Retrieves the layer's output
67- dream <- model $ input
68-
69- # Computes the gradients of the dream with regard to the loss
70- grads <- k_gradients(loss , dream )[[1 ]]
71-
72- # Normalizes the gradients (important trick)
73- grads <- grads / k_maximum(k_mean(k_abs(grads )), 1e-7 )
7477
75- outputs <- list (loss , grads )
76-
77- # Sets up a Keras function to retrieve the value of the loss and gradients, given an input image
78- fetch_loss_and_grads <- k_function(list (dream ), outputs )
79-
80- eval_loss_and_grads <- function (x ) {
81- outs <- fetch_loss_and_grads(list (x ))
82- loss_value <- outs [[1 ]]
83- grad_values <- outs [[2 ]]
84- list (loss_value , grad_values )
78+ # Set up a model that returns the activation values for every target layer
79+ # (as a named list)
80+ feature_extractor = keras_model(inputs = model $ inputs ,
81+ outputs = outputs_dict )
82+
83+ compute_loss <- function (input_image ) {
84+ features = feature_extractor(input_image )
85+ names(features ) = names(layer_settings )
86+ loss = tf $ zeros(shape = list ())
87+ for (names in names(layer_settings )) {
88+ coeff = layer_settings [[names ]]
89+ activation = features [[names ]]
90+ # We avoid border artifacts by only involving non-border pixels in the loss.
91+ scaling = tf $ reduce_prod(tf $ cast(tf $ shape(activation ), ' float32' ))
92+ loss = loss + coeff * tf $ reduce_sum(tf $ square(activation )) / scaling
93+ }
94+ loss
8595}
8696
97+ # Set up the gradient ascent loop for one octave
98+ gradient_ascent_step <- function (img , learning_rate ) {
99+ with(tf $ GradientTape() %as % tape , {
100+ tape $ watch(img )
101+ loss = compute_loss(img )
102+ })
103+ # Compute gradients.
104+ grads = tape $ gradient(loss , img )
105+ # Normalize gradients.
106+ grads = grads / tf $ maximum(tf $ reduce_mean(tf $ abs(grads )), 1e-6 )
107+ img = img + learning_rate * grads
108+ list (loss , img )
109+ }
87110
88- # Run gradient ascent -----------------------------------------------------
89-
90- # This function runs gradient ascent for a number of iterations.
91- gradient_ascent <-
92- function (x , iterations , step , max_loss = NULL ) {
93- for (i in 1 : iterations ) {
94- c(loss_value , grad_values ) %<- % eval_loss_and_grads(x )
95- if (! is.null(max_loss ) && loss_value > max_loss )
96- break
97- cat(" ...Loss value at" , i , " :" , loss_value , " \n " )
98- x <- x + (step * grad_values )
99- }
100- x
111+ gradient_ascent_loop <- function (img , iterations , learning_rate , max_loss = NULL ) {
112+ for (i in 1 : iterations ) {
113+ c(loss , img ) %<- % gradient_ascent_step(img , learning_rate )
114+ if (! is.null(max_loss ) && as.array(loss ) > max_loss )
115+ break
116+ cat(" ...Loss value at step" , i , " :" , as.array(loss ), " \n " )
101117 }
118+ img
119+ }
102120
103- # Playing with these hyperparameters will let you achieve new effects.
104- # Gradient ascent step size
105- step <- 0.01
106- # Number of scales at which to run gradient ascent
107- num_octave <- 3
108- # Size ratio between scales
109- octave_scale <- 1.4
110- # Number of ascent steps to run at each scale
111- iterations <- 20
112- # If the loss grows larger than 10, we will interrupt the gradient-ascent process to avoid ugly artifacts.
113- max_loss <- 10
114-
115- # Fill this with the path to the image you want to use.
116- base_image_path <- " /tmp/mypic.jpg"
117-
118- # Loads the base image into an array
119- img <-
120- preprocess_image(base_image_path )
121+ # Run the training loop, iterating over different octaves
122+ original_img = preprocess_image(base_image_path )
121123
122124# Prepares a list of shape tuples defining the different scales at which to run gradient ascent
123- original_shape <- dim(img )[- 1 ]
124- successive_shapes <-
125- list (original_shape )
125+ original_shape <- dim(original_img )[2 : 3 ]
126+
127+ successive_shapes <- list (original_shape )
128+
126129for (i in 1 : num_octave ) {
127130 shape <- as.integer(original_shape / (octave_scale ^ i ))
128- successive_shapes [[length(successive_shapes ) + 1 ]] <-
129- shape
131+ successive_shapes [[length(successive_shapes ) + 1 ]] <- shape
130132}
131- # Reverses the list of shapes so they're in increasing order
132- successive_shapes <-
133- rev(successive_shapes )
134133
135- original_img <- img
134+ # Reverses the list of shapes so they're in increasing order
135+ successive_shapes <- rev(successive_shapes [1 : 3 ])
136136# Resizes the array of the image to the smallest scale
137- shrunk_original_img <-
138- resize_img(img , successive_shapes [[1 ]])
137+ shrunk_original_img <- tf $ image $ resize(original_img , successive_shapes [[1 ]])
138+
139+ img = tf $ identity(original_img ) # Make a copy
139140
140- for (shape in successive_shapes ) {
141- cat(" Processing image shape" , shape , " \n " )
141+ for (i in 1 : length(successive_shapes )) {
142+ shape = successive_shapes [[i ]]
143+ cat(" Processing octave" , i , " with shape" , shape , " \n " )
142144 # Scales up the dream image
143- img <- resize_img (img , shape )
145+ img <- tf $ image $ resize (img , shape )
144146 # Runs gradient ascent, altering the dream
145- img <- gradient_ascent (img ,
146- iterations = iterations ,
147- step = step ,
148- max_loss = max_loss )
147+ img <- gradient_ascent_loop (img ,
148+ iterations = iterations ,
149+ learning_rate = step ,
150+ max_loss = max_loss )
149151 # Scales up the smaller version of the original image: it will be pixellated
150152 upscaled_shrunk_original_img <-
151- resize_img (shrunk_original_img , shape )
153+ tf $ image $ resize (shrunk_original_img , shape )
152154 # Computes the high-quality version of the original image at this size
153155 same_size_original <-
154- resize_img (original_img , shape )
156+ tf $ image $ resize (original_img , shape )
155157 # The difference between the two is the detail that was lost when scaling up
156158 lost_detail <-
157159 same_size_original - upscaled_shrunk_original_img
158160 # Reinjects lost detail into the dream
159161 img <- img + lost_detail
160162 shrunk_original_img <-
161- resize_img(original_img , shape )
162- save_img(img , fname = sprintf(" dream_at_scale_%s.png" ,
163- paste(shape , collapse = " x" )))
163+ tf $ image $ resize(original_img , shape )
164+ tf $ keras $ preprocessing $ image $ save_img(paste(result_prefix ,' .png' ,sep = ' ' ), deprocess_image(img $ numpy()))
164165}
165166
167+ # Plot result
168+ plot(magick :: image_read(paste(result_prefix ,' .png' ,sep = ' ' )))
0 commit comments