Skip to content

Commit 74fb570

Browse files
author
Sigrid Keydana
authored
Merge pull request #1034 from henry090/master
Update deep_dream.R
2 parents 4681a67 + bb2aba2 commit 74fb570

File tree

1 file changed

+116
-113
lines changed

1 file changed

+116
-113
lines changed

vignettes/examples/deep_dream.R

Lines changed: 116 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,57 @@
1+
2+
3+
# Setup
4+
15
library(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:
735
preprocess_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

3257
save_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
4465
model <- 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+
126129
for (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

Comments
 (0)