In this lab, we will see how to apply what we have learned about neural network.

0. Set up your workstation

As we saw during the lecture, we will use keras on top of tensorflow to train neural network. Hence we need to install it

install.packages("keras")

next we load the library and install everything required (outside of R, like a python environment, Neural nets frameworks…)

library(keras)
install_keras()

If everything went fine, you are now all set to train (deep) neural nets!

1. The basics

Fitting a neural network using keras is a 3-step procedure:

We will cover these in turn.

a) Defining the architecture

Although many architectures are available, in our (introductory) lecture, we only presented feedforward and convolutional neural nets. We will now see how to define such architectures.

## A feedforward neural net
feedforward <- keras_model_sequential() %>%
  layer_dense(input_shape = 50, units = 8, activation = "relu") %>%
  layer_dense(units = 2, activation = "relu") %>%
  layer_dense(units = 1)

## A convolutional neural net
convnet <- keras_model_sequential() %>%
  layer_conv_2d(filters = 64, kernel_size = 3, activation = "relu",
                input_shape = c(28, 28, 1)) %>%
  layer_conv_2d(filters = 32, kernel_size = 3, activation = "relu") %>%
  layer_dense(units = 10, activation = "softmax")

The code is, I think, enough verbose to understand what is going on here. Try to answer the following questions.

  • What is the architecture of thes neural nets?
  • What does it mean to set input_shape = 50 or input_shape = c(28, 28, 1)?
  • Why there is no activation in the last layer_dense call of model feedforward?
  • Why we set activation = “softmax” in the last layer_dense call of model convnet?

Have a look at the following outputs

feedforward
Model
Model: "sequential_13"
____________________________________________________________________________________________________________________
Layer (type)                                        Output Shape                                  Param #           
====================================================================================================================
dense_18 (Dense)                                    (None, 8)                                     408               
____________________________________________________________________________________________________________________
dense_19 (Dense)                                    (None, 2)                                     18                
____________________________________________________________________________________________________________________
dense_20 (Dense)                                    (None, 1)                                     3                 
====================================================================================================================
Total params: 429
Trainable params: 429
Non-trainable params: 0
____________________________________________________________________________________________________________________
convnet
Model
Model: "sequential_14"
____________________________________________________________________________________________________________________
Layer (type)                                        Output Shape                                  Param #           
====================================================================================================================
conv2d_290 (Conv2D)                                 (None, 26, 26, 64)                            640               
____________________________________________________________________________________________________________________
conv2d_291 (Conv2D)                                 (None, 24, 24, 32)                            18464             
____________________________________________________________________________________________________________________
dense_21 (Dense)                                    (None, 24, 24, 10)                            330               
====================================================================================================================
Total params: 19,434
Trainable params: 19,434
Non-trainable params: 0
____________________________________________________________________________________________________________________
  • What is printed here?
  • Try to compute “by hand” the number of parameters
## Fill in this part

b) Defining the optimization stage

The optimization stage in keras is called compiling and is very straightforward

feedforward %>% compile(optimizer = optimizer_rmsprop(),
                        loss = "mse")
  • Which optimizer are we using here?
  • Why are we using the mse loss here?
  • Which loss would you specify for the convnet model?

Possible options for the optimizer argument are:

  • optimizer_adadelta: Adaptive learning rate optimizer
  • optimizer_adagrad: Adaptive subgradient optimization
  • optimizer_adam : a kind of mix between rmsprop and momentum
  • optimizer_adamax: adam but with sup-norm
  • optimizer_nadam: adam with Nesterov acceleration
  • optimizer_rmsprop: the famaous rmsprop optimizer
  • optimizer_sgd: stochastic gradient descent

Some widely used, other are possible, options for the loss argument are:

  • ‘mse’: used mainly for regression;
  • ‘categorical_crossentropy’: used mainly for \(K\)-class classification \(K > 2\);
  • ‘binary_crossentropy’: used mainly for binary classification.

We can also add an aditional argument metrics, e.g., metrics = “accuracy” where some accuracy measure will be reported in addition to the evolution of the (generalization) error during the optimization stage.

c) Fitting the neural network

The final stage (well actually in an operational environment we will fine tune our network) consists in fitting the network. Here we supppose we have some (scaled) features X_train and a response Y_train. Training a neural net, here feedforward, is easy as 1, 2, 3:

history <- feedforward %>% fit(X_train, Y_train, epocs = 30)
plot(history)##Always a good idea to see how the optimization procedure performs

Altough the training error is of interest, it is often highly recommended to plot the test error as well. To do so, suppose we also have a X_test and Y_test data set

history <- feedforward %>% fit(X_train, Y_train, epochs = 30, verbose = 0,
                                validation_data = list(X_test, Y_test))
plot(history)

Another option would be to ask keras to split the data set

history <- feedforward %>% fit(X, Y, epochs = 30, verbose = 0,
                                validation_split = 0.2)
plot(history)

d) Making predictions

Depending on the situation, i.e., regression or classification, prediction can be done in two different ways:

feedforward %>% predict(X_new)
convnet %>% predict_classes(X_new)

1.5 Would you recognize it?

Have a look at the following neural network:

model <- keras_model_sequential() %>%
  layer_dense(input_shape = n.feat, units = 1,
              activation = "sigmoid")

model %>% compile(optimizer = optimizer_rmsprop(),
                  loss = "binary_crossentropy")

This model, rewritten within a neural network framework, is very famous. Have you recognize it?

Fit this model to a sensible data set already studied in classroom and fit it using the “conventional” way. Then compare estimates.

## Fill in this part

2. Playing with the MNIST dataset

a) Getting the data and preprocessing it

The MNIST dataset was a standard 10 years ago to benchmark various machine learning methodologies. Nowadays it is considered a bit too simple but anyway it is the perfect data set to start feeling confortable in training neural networks.

This dataset are grey-scaled images of human written digits—see an sample of this data set below.

Using the keras dedicated function we can easily retrive the data set:

mnist <- dataset_mnist()

and retrieve the training and test datasets (and scale the features to lie in \([0,1]\))

x_train <- mnist$train$x / 255
y_train <- mnist$train$y
x_test <- mnist$test$x / 255
y_test <- mnist$test$y
  1. Print the dimension of each image
## fill in this part
  1. Run the following piece of code to show a sample of these images
par(mfrow = c(2, 4), mar = rep(0, 4), bty = "n")
idx <- sample(10^4, 8)

for (i in idx)
  image(t(x_test[i,28:1,]), axes = FALSE, col = rev(grey.colors(255)))
  1. Before feeding the neural net with these data we need to reformat them to the format required by keras, i.e., the response (which is categorical) must be one-hot encoded. Learn how to use the to_categorical function to one-hot encode the response.
## Fill in this part

What is the dimension of y_test now?

## fill in this part and make sure you understand what is beyond the 'to_categorical' function
  1. Build a first (simple) feedforward neural network for the MNIST dataset. I expect you to reach at least a 99.2% accuracy on the test data set. Watch out, since it is very likely that you fine tune your model, please use the train + validation + test splitting scheme.
## Fill in this part
  1. Try to build a convolutional neural network.
## Fill in this part
  1. Add a max-pooling to your convnet model.
## Fill in this part
  1. Learn how to use the regularizer_l2 function to regularize your feedforward model.
## Fill in this part
  1. Learn how to use the layer_dropout function to regularize your convnet model.

2. Using pre-trained models

In an operational environment, it is often the case that you use pre-existing network and (possibly) slightly alter the last layers to your dataset. For instance, this can be adding another layer or estimate the weights and biases of the last layer to your dataset keeping the other ones “frozen”. Such a procedure is called using pre-trained models and parameter estimates for various famous neural nets are freely available.

a) Prediction using already fitted network

Here we just start by using the already trained neural net: the ResNet50 network

resnet <- application_resnet50()##trained on ImageNet

## Get a nice new image
url <- "cat.jpg"

img <- image_load(url, target_size = c(224, 224)) %>%## import the image in PIL format
  image_to_array() %>%## convert it to a tensor  
  array_reshape(dim = c(1, 224, 224, 3)) %>% ## store it a a 4d tensor
  imagenet_preprocess_input()## preprocessing the image from the preprocessed values of ImageNet

preds <- resnet %>% predict(img)
dim(preds)

imagenet_decode_predictions(preds)

In the code above:

  • Why do we set target_size = c(224, 224)?
  • Why the dimension of preds is 1 x 1000?

Download a picture of your choice and try to predict what it is using the pre-trained *vgg16** net.

## Fill in here

b) Slightly alter a pre-trained model

In this section we will briefly show how one can slightly alter a pre-trained model but we won’t run any code. This part is just here to show you how you can do that. Here we will fine tune the InceptionV3 neural net.

base.model <- application_inception_v3(include_top = FALSE)##trained on ImageNet

new.layers <- base.model$output %>%
  layer_dense(units = 512, activation = "relu") %>%
  layer_dense(units = 256, activation = "softmax")

altered.model <- keras_model(inputs = base.model$input,
                            outputs = new.layers$output)

## Since we want to estimate the parameters of the new layers only we have to "freeze" the original weights
freeze_weights(base_model)

## Now we compile the model
altered.model %>% compile(optimizer = optimizer_rmsprop(),
                          loss = "categorical_crossentropy")

## And finally based on new data we fit the "unfrozen" parameters
altered.model %>% fit(X_new, Y_new, epochs = 10)
LS0tCnRpdGxlOiAiTmV1cmFsIE5ldHdvcmsiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KYGBge3IgZWNobz1GQUxTRX0KbGlicmFyeShrZXJhcykKYGBgCgpJbiB0aGlzIGxhYiwgd2Ugd2lsbCBzZWUgaG93IHRvIGFwcGx5IHdoYXQgd2UgaGF2ZSBsZWFybmVkIGFib3V0IG5ldXJhbCBuZXR3b3JrLgoKIyMgMC4gU2V0IHVwIHlvdXIgd29ya3N0YXRpb24gCgpBcyB3ZSBzYXcgZHVyaW5nIHRoZSBsZWN0dXJlLCB3ZSB3aWxsIHVzZSAqa2VyYXMqIG9uIHRvcCBvZiAqdGVuc29yZmxvdyogdG8gdHJhaW4gbmV1cmFsIG5ldHdvcmsuIEhlbmNlIHdlIG5lZWQgdG8gaW5zdGFsbCBpdApgYGB7ciBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJrZXJhcyIpCmBgYApuZXh0IHdlIGxvYWQgdGhlIGxpYnJhcnkgYW5kIGluc3RhbGwgZXZlcnl0aGluZyByZXF1aXJlZCAob3V0c2lkZSBvZiBSLCBsaWtlIGEgcHl0aG9uIGVudmlyb25tZW50LCBOZXVyYWwgbmV0cyBmcmFtZXdvcmtzLi4uKQpgYGB7ciBldmFsPUZBTFNFfQpsaWJyYXJ5KGtlcmFzKQppbnN0YWxsX2tlcmFzKCkKYGBgCgpJZiBldmVyeXRoaW5nIHdlbnQgZmluZSwgeW91IGFyZSBub3cgYWxsIHNldCB0byB0cmFpbiAoZGVlcCkgbmV1cmFsIG5ldHMhCgojIyAxLiBUaGUgYmFzaWNzCgpGaXR0aW5nIGEgbmV1cmFsIG5ldHdvcmsgdXNpbmcgKmtlcmFzKiBpcyBhIDMtc3RlcCBwcm9jZWR1cmU6CgogIC0gQnVpbGQgdGhlIG5ldHdvcmsgYXJjaGl0ZWN0dXJlOwogIC0gRGVmaW5lIHRoZSBsb3NzIGZ1bmN0aW9uIGFuZCB0aGUgb3B0aW1pemF0aW9uIHNjaGVtZTsKICAtIEZpdCBpdCBiYXNlZCBvbiBzYW1lIHRyYWluaW5nIGRhdGEKICAKV2Ugd2lsbCBjb3ZlciB0aGVzZSBpbiB0dXJuLgoKIyMjIGEpIERlZmluaW5nIHRoZSBhcmNoaXRlY3R1cmUKCkFsdGhvdWdoIG1hbnkgYXJjaGl0ZWN0dXJlcyBhcmUgYXZhaWxhYmxlLCBpbiBvdXIgKGludHJvZHVjdG9yeSkgbGVjdHVyZSwgd2Ugb25seSBwcmVzZW50ZWQgZmVlZGZvcndhcmQgYW5kIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHMuIFdlIHdpbGwgbm93IHNlZSBob3cgdG8gZGVmaW5lIHN1Y2ggYXJjaGl0ZWN0dXJlcy4KCmBgYHtyfQojIyBBIGZlZWRmb3J3YXJkIG5ldXJhbCBuZXQKZmVlZGZvcndhcmQgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQogIGxheWVyX2RlbnNlKGlucHV0X3NoYXBlID0gNTAsIHVuaXRzID0gOCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAyLCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEpCgojIyBBIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldApjb252bmV0IDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9jb252XzJkKGZpbHRlcnMgPSA2NCwga2VybmVsX3NpemUgPSAzLCBhY3RpdmF0aW9uID0gInJlbHUiLAogICAgICAgICAgICAgICAgaW5wdXRfc2hhcGUgPSBjKDI4LCAyOCwgMSkpICU+JQogIGxheWVyX2NvbnZfMmQoZmlsdGVycyA9IDMyLCBrZXJuZWxfc2l6ZSA9IDMsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpCmBgYAoKVGhlIGNvZGUgaXMsIEkgdGhpbmssIGVub3VnaCB2ZXJib3NlIHRvIHVuZGVyc3RhbmQgd2hhdCBpcyBnb2luZyBvbiBoZXJlLiBUcnkgdG8gYW5zd2VyIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zLgoKICAtIFdoYXQgaXMgdGhlIGFyY2hpdGVjdHVyZSBvZiB0aGVzIG5ldXJhbCBuZXRzPwogIC0gV2hhdCBkb2VzIGl0IG1lYW4gdG8gc2V0ICppbnB1dF9zaGFwZSA9IDUwKiBvciAqaW5wdXRfc2hhcGUgPSBjKDI4LCAyOCwgMSkqPwogIC0gV2h5IHRoZXJlIGlzIG5vICphY3RpdmF0aW9uKiBpbiB0aGUgbGFzdCAqbGF5ZXJfZGVuc2UqIGNhbGwgb2YgbW9kZWwgKmZlZWRmb3J3YXJkKj8KICAtIFdoeSB3ZSBzZXQgKmFjdGl2YXRpb24gPSAic29mdG1heCIqIGluIHRoZSBsYXN0ICpsYXllcl9kZW5zZSogY2FsbCBvZiBtb2RlbCAqY29udm5ldCo/CiAgCkhhdmUgYSBsb29rIGF0IHRoZSBmb2xsb3dpbmcgb3V0cHV0cwpgYGB7cn0KZmVlZGZvcndhcmQKY29udm5ldApgYGAKICAtIFdoYXQgaXMgcHJpbnRlZCBoZXJlPwogIC0gVHJ5IHRvIGNvbXB1dGUgImJ5IGhhbmQiIHRoZSBudW1iZXIgb2YgcGFyYW1ldGVycwpgYGB7cn0KIyMgRmlsbCBpbiB0aGlzIHBhcnQKYGBgCgojIyMgYikgRGVmaW5pbmcgdGhlIG9wdGltaXphdGlvbiBzdGFnZQoKVGhlIG9wdGltaXphdGlvbiBzdGFnZSBpbiAqa2VyYXMqIGlzIGNhbGxlZCAqY29tcGlsaW5nKiBhbmQgaXMgdmVyeSBzdHJhaWdodGZvcndhcmQKYGBge3J9CmZlZWRmb3J3YXJkICU+JSBjb21waWxlKG9wdGltaXplciA9IG9wdGltaXplcl9ybXNwcm9wKCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxvc3MgPSAibXNlIikKYGBgCgogIC0gV2hpY2ggb3B0aW1pemVyIGFyZSB3ZSB1c2luZyBoZXJlPwogIC0gV2h5IGFyZSB3ZSB1c2luZyB0aGUgKm1zZSogbG9zcyBoZXJlPwogIC0gV2hpY2ggbG9zcyB3b3VsZCB5b3Ugc3BlY2lmeSBmb3IgdGhlICpjb252bmV0KiBtb2RlbD8KClBvc3NpYmxlIG9wdGlvbnMgZm9yIHRoZSAqb3B0aW1pemVyKiBhcmd1bWVudCBhcmU6CgogIC0gKm9wdGltaXplcl9hZGFkZWx0YSo6IEFkYXB0aXZlIGxlYXJuaW5nIHJhdGUgb3B0aW1pemVyCiAgLSAqb3B0aW1pemVyX2FkYWdyYWQqOiBBZGFwdGl2ZSBzdWJncmFkaWVudCBvcHRpbWl6YXRpb24KICAtICpvcHRpbWl6ZXJfYWRhbSogOiBhIGtpbmQgb2YgbWl4IGJldHdlZW4gcm1zcHJvcCBhbmQgbW9tZW50dW0KICAtICpvcHRpbWl6ZXJfYWRhbWF4KjogYWRhbSBidXQgd2l0aCBzdXAtbm9ybQogIC0gKm9wdGltaXplcl9uYWRhbSo6IGFkYW0gd2l0aCBOZXN0ZXJvdiBhY2NlbGVyYXRpb24KICAtICpvcHRpbWl6ZXJfcm1zcHJvcCo6IHRoZSBmYW1hb3VzIHJtc3Byb3Agb3B0aW1pemVyCiAgLSAqb3B0aW1pemVyX3NnZCo6IHN0b2NoYXN0aWMgZ3JhZGllbnQgZGVzY2VudAoKU29tZSB3aWRlbHkgdXNlZCwgb3RoZXIgYXJlIHBvc3NpYmxlLCBvcHRpb25zIGZvciB0aGUgKmxvc3MqIGFyZ3VtZW50IGFyZToKCiAgLSAqJ21zZScqOiB1c2VkIG1haW5seSBmb3IgcmVncmVzc2lvbjsKICAtIConY2F0ZWdvcmljYWxfY3Jvc3NlbnRyb3B5Jyo6IHVzZWQgbWFpbmx5IGZvciAkSyQtY2xhc3MgY2xhc3NpZmljYXRpb24gJEsgPiAyJDsKICAtIConYmluYXJ5X2Nyb3NzZW50cm9weScqOiB1c2VkIG1haW5seSBmb3IgYmluYXJ5IGNsYXNzaWZpY2F0aW9uLgogIApXZSBjYW4gYWxzbyBhZGQgYW4gYWRpdGlvbmFsIGFyZ3VtZW50ICptZXRyaWNzKiwgZS5nLiwgKm1ldHJpY3MgPSAiYWNjdXJhY3kiKiB3aGVyZSBzb21lIGFjY3VyYWN5IG1lYXN1cmUgd2lsbCBiZSByZXBvcnRlZCBpbiBhZGRpdGlvbiB0byB0aGUgZXZvbHV0aW9uIG9mIHRoZSAoZ2VuZXJhbGl6YXRpb24pIGVycm9yIGR1cmluZyB0aGUgb3B0aW1pemF0aW9uIHN0YWdlLgogIAojIyMgYykgRml0dGluZyB0aGUgbmV1cmFsIG5ldHdvcmsKClRoZSBmaW5hbCBzdGFnZSAod2VsbCBhY3R1YWxseSBpbiBhbiBvcGVyYXRpb25hbCBlbnZpcm9ubWVudCB3ZSB3aWxsIGZpbmUgdHVuZSBvdXIgbmV0d29yaykgY29uc2lzdHMgaW4gZml0dGluZyB0aGUgbmV0d29yay4gSGVyZSB3ZSBzdXBwcG9zZSB3ZSBoYXZlIHNvbWUgKHNjYWxlZCkgZmVhdHVyZXMgKlhfdHJhaW4qIGFuZCBhIHJlc3BvbnNlICpZX3RyYWluKi4gVHJhaW5pbmcgYSBuZXVyYWwgbmV0LCBoZXJlICpmZWVkZm9yd2FyZCosIGlzIGVhc3kgYXMgMSwgMiwgMzoKCmBgYHtyIGV2YWwgPSBGQUxTRX0KaGlzdG9yeSA8LSBmZWVkZm9yd2FyZCAlPiUgZml0KFhfdHJhaW4sIFlfdHJhaW4sIGVwb2NzID0gMzApCnBsb3QoaGlzdG9yeSkjI0Fsd2F5cyBhIGdvb2QgaWRlYSB0byBzZWUgaG93IHRoZSBvcHRpbWl6YXRpb24gcHJvY2VkdXJlIHBlcmZvcm1zCmBgYAoKQWx0b3VnaCB0aGUgKip0cmFpbmluZyoqIGVycm9yIGlzIG9mIGludGVyZXN0LCBpdCBpcyBvZnRlbiBoaWdobHkgcmVjb21tZW5kZWQgdG8gcGxvdCB0aGUgKip0ZXN0IGVycm9yKiogYXMgd2VsbC4gVG8gZG8gc28sIHN1cHBvc2Ugd2UgYWxzbyBoYXZlIGEgKlhfdGVzdCogYW5kICpZX3Rlc3QqIGRhdGEgc2V0CmBgYHtyIGV2YWw9RkFMU0V9Cmhpc3RvcnkgPC0gZmVlZGZvcndhcmQgJT4lIGZpdChYX3RyYWluLCBZX3RyYWluLCBlcG9jaHMgPSAzMCwgdmVyYm9zZSA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRhdGlvbl9kYXRhID0gbGlzdChYX3Rlc3QsIFlfdGVzdCkpCnBsb3QoaGlzdG9yeSkKYGBgCgpBbm90aGVyIG9wdGlvbiB3b3VsZCBiZSB0byBhc2sgKmtlcmFzKiB0byBzcGxpdCB0aGUgZGF0YSBzZXQKYGBge3IgZXZhbCA9IEZBTFNFfQpoaXN0b3J5IDwtIGZlZWRmb3J3YXJkICU+JSBmaXQoWCwgWSwgZXBvY2hzID0gMzAsIHZlcmJvc2UgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkYXRpb25fc3BsaXQgPSAwLjIpCnBsb3QoaGlzdG9yeSkKYGBgCgoKIyMjIGQpIE1ha2luZyBwcmVkaWN0aW9ucwoKRGVwZW5kaW5nIG9uIHRoZSBzaXR1YXRpb24sIGkuZS4sIHJlZ3Jlc3Npb24gb3IgY2xhc3NpZmljYXRpb24sIHByZWRpY3Rpb24gY2FuIGJlIGRvbmUgaW4gdHdvIGRpZmZlcmVudCB3YXlzOgpgYGB7ciBldmFsID0gRkFMU0V9CmZlZWRmb3J3YXJkICU+JSBwcmVkaWN0KFhfbmV3KQpjb252bmV0ICU+JSBwcmVkaWN0X2NsYXNzZXMoWF9uZXcpCmBgYAoKCiMjIDEuNSBXb3VsZCB5b3UgcmVjb2duaXplIGl0PwoKSGF2ZSBhIGxvb2sgYXQgdGhlIGZvbGxvd2luZyBuZXVyYWwgbmV0d29yazoKYGBge3IgZXZhbD1GQUxTRX0KbW9kZWwgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQogIGxheWVyX2RlbnNlKGlucHV0X3NoYXBlID0gbi5mZWF0LCB1bml0cyA9IDEsCiAgICAgICAgICAgICAgYWN0aXZhdGlvbiA9ICJzaWdtb2lkIikKCm1vZGVsICU+JSBjb21waWxlKG9wdGltaXplciA9IG9wdGltaXplcl9ybXNwcm9wKCksCiAgICAgICAgICAgICAgICAgIGxvc3MgPSAiYmluYXJ5X2Nyb3NzZW50cm9weSIpCmBgYAoKVGhpcyBtb2RlbCwgcmV3cml0dGVuIHdpdGhpbiBhIG5ldXJhbCBuZXR3b3JrIGZyYW1ld29yaywgaXMgdmVyeSBmYW1vdXMuIEhhdmUgeW91IHJlY29nbml6ZSBpdD8KCkZpdCB0aGlzIG1vZGVsIHRvIGEgc2Vuc2libGUgZGF0YSBzZXQgYWxyZWFkeSBzdHVkaWVkIGluIGNsYXNzcm9vbSBhbmQgZml0IGl0IHVzaW5nIHRoZSAiY29udmVudGlvbmFsIiB3YXkuIFRoZW4gY29tcGFyZSBlc3RpbWF0ZXMuCmBgYHtyfQojIyBGaWxsIGluIHRoaXMgcGFydApgYGAKCgojIyAyLiBQbGF5aW5nIHdpdGggdGhlIE1OSVNUIGRhdGFzZXQKCiMjIyBhKSBHZXR0aW5nIHRoZSBkYXRhIGFuZCBwcmVwcm9jZXNzaW5nIGl0ClRoZSAqTU5JU1QqIGRhdGFzZXQgd2FzIGEgc3RhbmRhcmQgMTAgeWVhcnMgYWdvIHRvIGJlbmNobWFyayB2YXJpb3VzIG1hY2hpbmUgbGVhcm5pbmcgbWV0aG9kb2xvZ2llcy4gTm93YWRheXMgaXQgaXMgY29uc2lkZXJlZCBhIGJpdCB0b28gc2ltcGxlIGJ1dCBhbnl3YXkgaXQgaXMgdGhlIHBlcmZlY3QgZGF0YSBzZXQgdG8gc3RhcnQgZmVlbGluZyBjb25mb3J0YWJsZSBpbiB0cmFpbmluZyBuZXVyYWwgbmV0d29ya3MuCgpUaGlzIGRhdGFzZXQgYXJlIGdyZXktc2NhbGVkIGltYWdlcyBvZiBodW1hbiB3cml0dGVuIGRpZ2l0cy0tLXNlZSBhbiBzYW1wbGUgb2YgdGhpcyBkYXRhIHNldCBiZWxvdy4KClVzaW5nIHRoZSAqa2VyYXMqIGRlZGljYXRlZCBmdW5jdGlvbiB3ZSBjYW4gZWFzaWx5IHJldHJpdmUgdGhlIGRhdGEgc2V0OgpgYGB7ciBldmFsPUZBTFNFfQptbmlzdCA8LSBkYXRhc2V0X21uaXN0KCkKYGBgCmFuZCByZXRyaWV2ZSB0aGUgKnRyYWluaW5nKiBhbmQgKnRlc3QqIGRhdGFzZXRzIChhbmQgc2NhbGUgdGhlIGZlYXR1cmVzIHRvIGxpZSBpbiAkWzAsMV0kKQpgYGB7ciBldmFsID0gRkFMU0V9CnhfdHJhaW4gPC0gbW5pc3QkdHJhaW4keCAvIDI1NQp5X3RyYWluIDwtIG1uaXN0JHRyYWluJHkKeF90ZXN0IDwtIG1uaXN0JHRlc3QkeCAvIDI1NQp5X3Rlc3QgPC0gbW5pc3QkdGVzdCR5CmBgYAoKMS4gUHJpbnQgdGhlIGRpbWVuc2lvbiBvZiBlYWNoIGltYWdlCmBgYHtyfQojIyBmaWxsIGluIHRoaXMgcGFydApgYGAKMi4gUnVuIHRoZSBmb2xsb3dpbmcgcGllY2Ugb2YgY29kZSB0byBzaG93IGEgc2FtcGxlIG9mIHRoZXNlIGltYWdlcwpgYGB7ciBldmFsPUZBTFNFfQpwYXIobWZyb3cgPSBjKDIsIDQpLCBtYXIgPSByZXAoMCwgNCksIGJ0eSA9ICJuIikKaWR4IDwtIHNhbXBsZSgxMF40LCA4KQoKZm9yIChpIGluIGlkeCkKICBpbWFnZSh0KHhfdGVzdFtpLDI4OjEsXSksIGF4ZXMgPSBGQUxTRSwgY29sID0gcmV2KGdyZXkuY29sb3JzKDI1NSkpKQpgYGAKCjMuIEJlZm9yZSBmZWVkaW5nIHRoZSBuZXVyYWwgbmV0IHdpdGggdGhlc2UgZGF0YSB3ZSBuZWVkIHRvIHJlZm9ybWF0IHRoZW0gdG8gdGhlIGZvcm1hdCByZXF1aXJlZCBieSAqa2VyYXMqLCBpLmUuLCB0aGUgcmVzcG9uc2UgKHdoaWNoIGlzIGNhdGVnb3JpY2FsKSBtdXN0IGJlICpvbmUtaG90IGVuY29kZWQqLiBMZWFybiBob3cgdG8gdXNlIHRoZSAqdG9fY2F0ZWdvcmljYWwqIGZ1bmN0aW9uIHRvICpvbmUtaG90IGVuY29kZSogdGhlIHJlc3BvbnNlLgpgYGB7cn0KIyMgRmlsbCBpbiB0aGlzIHBhcnQKYGBgCgpXaGF0IGlzIHRoZSBkaW1lbnNpb24gb2YgKnlfdGVzdCogbm93PwpgYGB7cn0KIyMgZmlsbCBpbiB0aGlzIHBhcnQgYW5kIG1ha2Ugc3VyZSB5b3UgdW5kZXJzdGFuZCB3aGF0IGlzIGJleW9uZCB0aGUgJ3RvX2NhdGVnb3JpY2FsJyBmdW5jdGlvbgpgYGAKCjQuIEJ1aWxkIGEgZmlyc3QgKHNpbXBsZSkgZmVlZGZvcndhcmQgbmV1cmFsIG5ldHdvcmsgZm9yIHRoZSBNTklTVCBkYXRhc2V0LiBJIGV4cGVjdCB5b3UgdG8gcmVhY2ggYXQgbGVhc3QgYSA5OS4yJSBhY2N1cmFjeSBvbiB0aGUgdGVzdCBkYXRhIHNldC4gV2F0Y2ggb3V0LCBzaW5jZSBpdCBpcyB2ZXJ5IGxpa2VseSB0aGF0IHlvdSBmaW5lIHR1bmUgeW91ciBtb2RlbCwgcGxlYXNlIHVzZSB0aGUgKnRyYWluICsgdmFsaWRhdGlvbiArIHRlc3QqIHNwbGl0dGluZyBzY2hlbWUuIAoKYGBge3J9CiMjIEZpbGwgaW4gdGhpcyBwYXJ0CmBgYAoKNS4gVHJ5IHRvIGJ1aWxkIGEgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29yay4KCmBgYHtyfQojIyBGaWxsIGluIHRoaXMgcGFydApgYGAKCjYuIEFkZCBhIG1heC1wb29saW5nIHRvIHlvdXIgY29udm5ldCBtb2RlbC4gCgpgYGB7cn0KIyMgRmlsbCBpbiB0aGlzIHBhcnQKYGBgCgo3LiBMZWFybiBob3cgdG8gdXNlIHRoZSAqcmVndWxhcml6ZXJfbDIqIGZ1bmN0aW9uIHRvIHJlZ3VsYXJpemUgeW91ciBmZWVkZm9yd2FyZCBtb2RlbC4KYGBge3J9CiMjIEZpbGwgaW4gdGhpcyBwYXJ0CmBgYAoKOC4gTGVhcm4gaG93IHRvIHVzZSB0aGUgKmxheWVyX2Ryb3BvdXQqIGZ1bmN0aW9uIHRvIHJlZ3VsYXJpemUgeW91ciBjb252bmV0IG1vZGVsLgoKCiMjIDIuIFVzaW5nIHByZS10cmFpbmVkIG1vZGVscwoKSW4gYW4gb3BlcmF0aW9uYWwgZW52aXJvbm1lbnQsIGl0IGlzIG9mdGVuIHRoZSBjYXNlIHRoYXQgeW91IHVzZSBwcmUtZXhpc3RpbmcgbmV0d29yayBhbmQgKHBvc3NpYmx5KSBzbGlnaHRseSBhbHRlciB0aGUgbGFzdCBsYXllcnMgdG8geW91ciBkYXRhc2V0LiBGb3IgaW5zdGFuY2UsIHRoaXMgY2FuIGJlIGFkZGluZyBhbm90aGVyIGxheWVyIG9yIGVzdGltYXRlIHRoZSB3ZWlnaHRzIGFuZCBiaWFzZXMgb2YgdGhlIGxhc3QgbGF5ZXIgdG8geW91ciBkYXRhc2V0IGtlZXBpbmcgdGhlIG90aGVyIG9uZXMgImZyb3plbiIuIFN1Y2ggYSBwcm9jZWR1cmUgaXMgY2FsbGVkICp1c2luZyBwcmUtdHJhaW5lZCBtb2RlbHMqIGFuZCBwYXJhbWV0ZXIgZXN0aW1hdGVzIGZvciB2YXJpb3VzIGZhbW91cyBuZXVyYWwgbmV0cyBhcmUgZnJlZWx5IGF2YWlsYWJsZS4KCiMjIyBhKSBQcmVkaWN0aW9uIHVzaW5nIGFscmVhZHkgZml0dGVkIG5ldHdvcmsKCkhlcmUgd2UganVzdCBzdGFydCBieSB1c2luZyB0aGUgYWxyZWFkeSB0cmFpbmVkIG5ldXJhbCBuZXQ6IHRoZSAqUmVzTmV0NTAqIG5ldHdvcmsKYGBge3IgZXZhbD1GQUxTRX0KcmVzbmV0IDwtIGFwcGxpY2F0aW9uX3Jlc25ldDUwKCkjI3RyYWluZWQgb24gSW1hZ2VOZXQKCiMjIEdldCBhIG5pY2UgbmV3IGltYWdlCnVybCA8LSAiY2F0LmpwZyIKCmltZyA8LSBpbWFnZV9sb2FkKHVybCwgdGFyZ2V0X3NpemUgPSBjKDIyNCwgMjI0KSkgJT4lIyMgaW1wb3J0IHRoZSBpbWFnZSBpbiBQSUwgZm9ybWF0CiAgaW1hZ2VfdG9fYXJyYXkoKSAlPiUjIyBjb252ZXJ0IGl0IHRvIGEgdGVuc29yICAKICBhcnJheV9yZXNoYXBlKGRpbSA9IGMoMSwgMjI0LCAyMjQsIDMpKSAlPiUgIyMgc3RvcmUgaXQgYSBhIDRkIHRlbnNvcgogIGltYWdlbmV0X3ByZXByb2Nlc3NfaW5wdXQoKSMjIHByZXByb2Nlc3NpbmcgdGhlIGltYWdlIGZyb20gdGhlIHByZXByb2Nlc3NlZCB2YWx1ZXMgb2YgSW1hZ2VOZXQKCnByZWRzIDwtIHJlc25ldCAlPiUgcHJlZGljdChpbWcpCmRpbShwcmVkcykKCmltYWdlbmV0X2RlY29kZV9wcmVkaWN0aW9ucyhwcmVkcykKYGBgCgpJbiB0aGUgY29kZSBhYm92ZToKCiAgLSBXaHkgZG8gd2Ugc2V0ICp0YXJnZXRfc2l6ZSA9IGMoMjI0LCAyMjQpKj8KICAtIFdoeSB0aGUgZGltZW5zaW9uIG9mICpwcmVkcyogaXMgKjEgeCAxMDAwKj8KCkRvd25sb2FkIGEgcGljdHVyZSBvZiB5b3VyIGNob2ljZSBhbmQgdHJ5IHRvIHByZWRpY3Qgd2hhdCBpdCBpcyB1c2luZyB0aGUgcHJlLXRyYWluZWQgKnZnZzE2KiogbmV0LgpgYGB7cn0KIyMgRmlsbCBpbiBoZXJlCmBgYAoKIyMjIGIpIFNsaWdodGx5IGFsdGVyIGEgcHJlLXRyYWluZWQgbW9kZWwKCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGJyaWVmbHkgc2hvdyBob3cgb25lIGNhbiBzbGlnaHRseSBhbHRlciBhIHByZS10cmFpbmVkIG1vZGVsIGJ1dCB3ZSB3b24ndCBydW4gYW55IGNvZGUuIFRoaXMgcGFydCBpcyBqdXN0IGhlcmUgdG8gc2hvdyB5b3UgaG93IHlvdSBjYW4gZG8gdGhhdC4gSGVyZSB3ZSB3aWxsIGZpbmUgdHVuZSB0aGUgKkluY2VwdGlvblYzKiBuZXVyYWwgbmV0LgpgYGB7ciBldmFsPUZBTFNFfQpiYXNlLm1vZGVsIDwtIGFwcGxpY2F0aW9uX2luY2VwdGlvbl92MyhpbmNsdWRlX3RvcCA9IEZBTFNFKSMjdHJhaW5lZCBvbiBJbWFnZU5ldAoKbmV3LmxheWVycyA8LSBiYXNlLm1vZGVsJG91dHB1dCAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDUxMiwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAyNTYsIGFjdGl2YXRpb24gPSAic29mdG1heCIpCgphbHRlcmVkLm1vZGVsIDwtIGtlcmFzX21vZGVsKGlucHV0cyA9IGJhc2UubW9kZWwkaW5wdXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXRzID0gbmV3LmxheWVycyRvdXRwdXQpCgojIyBTaW5jZSB3ZSB3YW50IHRvIGVzdGltYXRlIHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBuZXcgbGF5ZXJzIG9ubHkgd2UgaGF2ZSB0byAiZnJlZXplIiB0aGUgb3JpZ2luYWwgd2VpZ2h0cwpmcmVlemVfd2VpZ2h0cyhiYXNlX21vZGVsKQoKIyMgTm93IHdlIGNvbXBpbGUgdGhlIG1vZGVsCmFsdGVyZWQubW9kZWwgJT4lIGNvbXBpbGUob3B0aW1pemVyID0gb3B0aW1pemVyX3Jtc3Byb3AoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsb3NzID0gImNhdGVnb3JpY2FsX2Nyb3NzZW50cm9weSIpCgojIyBBbmQgZmluYWxseSBiYXNlZCBvbiBuZXcgZGF0YSB3ZSBmaXQgdGhlICJ1bmZyb3plbiIgcGFyYW1ldGVycwphbHRlcmVkLm1vZGVsICU+JSBmaXQoWF9uZXcsIFlfbmV3LCBlcG9jaHMgPSAxMCkKYGBgCg==