knitr::opts_chunk$set(echo = TRUE)
library(keras)
library(ggplot2)
library(dplyr)
library(corrplot) # pour représenter la matrice des corrélations empiriques
library(factoextra) # pour avoir des représentations graphiques pour l'ACP
library(tensorflow)
#library(reticulate)
# use_miniconda("r-reticulate")
if (tensorflow::tf$executing_eagerly()) {
tensorflow::tf$compat$v1$disable_eager_execution()
}
K <- keras::backend()
Il existe deux grandes “bibliothèques logicielles” pour faire de l’apprentissage profond (deep learning).
Pytorch est une “bibliothèque logicielle” écrite en C++, Python et C, basée sur Torch, développée par les équipes d’intelligence artificielle (IA) de Facebook, déposée en open source sur GitHub en 2017. Selon les blogs, Pytorch est réputé être plutôt pour les développeurs orientés recherche, plus convivial, plus facile à adapter et debugger.
TensorFlow est une plateforme Open-Source, proposant un ensemble complet et flexible d’outils, bibliothèques et ressources communautaires pour le “machine learning”. C’est aussi une bibliothèque logicielle pour le calcul numérique utilisant des graphiques de flux de données. Les noeuds représentent des fonctions mathématiques et les arêtes des tableaux multidimensionnels (tenseurs) communiquant entre eux. TensorFlow a été développé par les équipes de Google et publié en 2015. Le “+” revendiqué est le côté écosystème complet, pas besoin de gérer CPU/GPU et facilitation de mise en production d’outils liés à l’IA.
Keras est une interface “high-level” pour construire et entraîner des modèles d’apprentissage profond, développée par F. Chollet et qui sert d’interface pour TensorFlow, mais aussi The MicroSoft Cognitive ToolKit and Theano. Keras ne se charge pas directement des opérations de bas niveau comme les produits ou les convolutions de Tensor, et s’appuie sur un moteur backend (par exemple TensorFlow). Keras inclut aussi des ensembles de données et des modèles bien connus, pré-entraînés (sur ImageNet).
Il semble qu’aujourd’hui les deux ont travaillé sur leurs points faibles.
Il existe des bibliothèques R de mêmes noms que celles Python, tensorflow, keras, torch.
torch : construction sur PyTorch C++, pas de dépendance à Python, installation plus facile.
Il existe un projet RStudio autour de TensorFlow https://tensorflow.rstudio.com/ (et donc documentation assez complète) Pour les utilisateurs de R, tensorflow et keras sont basés sur reticulate et revient à utiliser des packages Python depuis R.
L’interface Keras a été portée sous R par JJ. Allaire, et évolue avec la philosophie “make it R-like and natural, where possible and make it easy to port from Python, where necessary”. Vous pouvez lire l’article de blog ci-dessous pour plus d’infos sur les dernières évolutions. Il existe aussi le livre Deep Learning with R écrit par F. Chollet et JJ Allaire dont une deuxième version est attendue sous peu.
“Cheatsheet” pour le package R keras https://raw.githubusercontent.com/rstudio/cheatsheets/master/keras.pdf
Comprendre les commandes permettant de construire et entraîner un autoencodeur (variationnel) avec le package keras, depuis RStudio, avec un objectif de réduction de dimension.
Quelques ateliers passés autour de l’apprentissage profond :
Un atelier Happy R avait déjà été proposé autour des réseaux de neurones par Sophie Donnet et Christophe Ambroise : (https://stateofther.netlify.app/post/introduction-au-deep-learning-sous-r/)
Un autre plus récent a été proposé, avec code Python (pytorch ou keras), par Charles Ollion sur l’apprentissage profond bayésien (https://stateofther.netlify.app/post/bayesian-deep-learning/)
Pour installer tensorflow et keras et travailler depuis RStudio, suivre les consignes d’installation disponibles ici.
Sous Ubuntu, on est un peu pris par la main. Installer les packages keras et tensorflow.
Si TensorFlow n’est pas installé sur notre machine, il suffira de lancer la fonction tensorflow::install_tensorflow()
mais là encore, vous devriez avoir un message explicite. Si besoin on vous proposera d’installer Miniconda.
Pour vérifier son installation tensorflow :
library(tensorflow)
tf$constant("Hellow Tensorflow")
Possiblement on verra apparaître des messages d’erreur qui en fait ne concernent que les calculs sur GPU. Si vous n’avez pas de carte GPU ou ne souhaitez pas faire de calculs sur GPU vous pouvez ignorer les messages.
Si vous souhaitez pouvoir utiliser les fonctionnalités GPU, vous aurez sans doute besoin d’installation supplémentaire.
Remarque : pour avoir des infos sur sa carte graphique sous Ubuntu, taper lspci | egrep "3D|VGA"
dans une console.
Pour définir le modèle puis l’ajuster, on utilisera toujours les étapes suivantes.
Dans keras on assemble des couches pour construire un modèle (mode de construction le plus courant est le mode séquentiel).
layer_input(shape = NULL)
layer_dense(object, units, activation = NULL, use_bias = TRUE, input_shape = NULL)
Outre la fonction d’activation à spécifier, les schémas d’initialisation des poids (kernel_initializer
) ou de régularisation des poids (kernel_regularizer
) peuvent être importants.
compile(loss='mean_squared_error', optimizer='sgd')
optimizer
: optimiseur, souvent "rmsprop"
par défaut.
loss
: fonction de perte à minimiser.
metrics
: métriques à évaluer pendant l’entrainement et le test du modèle.
Ex : pour de la classification
optimizer = 'adam', loss = 'categorical_crossentropy', metrics = list('accuracy')
fit
)fit(object, x = NULL, y = NULL, bath_size = NULL, epochs = 10)
epochs
: l’apprentissage est structuré en ‘epochs’, ie itération sur le jeu entier de données (ce qui fait en lots (‘batches’) plus petits).
batch_size
: le modèle découpe les données en lots plus petits et itèrent sur chaque lote pendant l’entrainement. Attention si le nombre total d’échantillons n’est pas divisible par la taille du lot, le dernier batch doit être le plus petit.
predict
)Nous considérerons le jeu de données “vin rouge” de la base de données de vins portugais “Vinho Verde”. Nous disposons de 12 variables physico-chimiques et sensorielles, dont une variable de quality de vin correspondant à un score entre 0 et 10. Une courte description de chaque variable est disponible ici. Les données sont utilisées à titre purement illustratif.
wine <- read.csv("http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv", sep = ";")
#quality <- wine$quality<6
#wine2 <- read.table("http://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data", sep = ",")
# Taille du jeux de données
dim(wine)
## [1] 1599 12
# Aperçu des premières lignes
head(wine[,-12])
## fixed.acidity volatile.acidity citric.acid residual.sugar chlorides
## 1 7.4 0.70 0.00 1.9 0.076
## 2 7.8 0.88 0.00 2.6 0.098
## 3 7.8 0.76 0.04 2.3 0.092
## 4 11.2 0.28 0.56 1.9 0.075
## 5 7.4 0.70 0.00 1.9 0.076
## 6 7.4 0.66 0.00 1.8 0.075
## free.sulfur.dioxide total.sulfur.dioxide density pH sulphates alcohol
## 1 11 34 0.9978 3.51 0.56 9.4
## 2 25 67 0.9968 3.20 0.68 9.8
## 3 15 54 0.9970 3.26 0.65 9.8
## 4 17 60 0.9980 3.16 0.58 9.8
## 5 11 34 0.9978 3.51 0.56 9.4
## 6 13 40 0.9978 3.51 0.56 9.4
# Résumé
summary(wine)
## fixed.acidity volatile.acidity citric.acid residual.sugar
## Min. : 4.60 Min. :0.1200 Min. :0.000 Min. : 0.900
## 1st Qu.: 7.10 1st Qu.:0.3900 1st Qu.:0.090 1st Qu.: 1.900
## Median : 7.90 Median :0.5200 Median :0.260 Median : 2.200
## Mean : 8.32 Mean :0.5278 Mean :0.271 Mean : 2.539
## 3rd Qu.: 9.20 3rd Qu.:0.6400 3rd Qu.:0.420 3rd Qu.: 2.600
## Max. :15.90 Max. :1.5800 Max. :1.000 Max. :15.500
## chlorides free.sulfur.dioxide total.sulfur.dioxide density
## Min. :0.01200 Min. : 1.00 Min. : 6.00 Min. :0.9901
## 1st Qu.:0.07000 1st Qu.: 7.00 1st Qu.: 22.00 1st Qu.:0.9956
## Median :0.07900 Median :14.00 Median : 38.00 Median :0.9968
## Mean :0.08747 Mean :15.87 Mean : 46.47 Mean :0.9967
## 3rd Qu.:0.09000 3rd Qu.:21.00 3rd Qu.: 62.00 3rd Qu.:0.9978
## Max. :0.61100 Max. :72.00 Max. :289.00 Max. :1.0037
## pH sulphates alcohol quality
## Min. :2.740 Min. :0.3300 Min. : 8.40 Min. :3.000
## 1st Qu.:3.210 1st Qu.:0.5500 1st Qu.: 9.50 1st Qu.:5.000
## Median :3.310 Median :0.6200 Median :10.20 Median :6.000
## Mean :3.311 Mean :0.6581 Mean :10.42 Mean :5.636
## 3rd Qu.:3.400 3rd Qu.:0.7300 3rd Qu.:11.10 3rd Qu.:6.000
## Max. :4.010 Max. :2.0000 Max. :14.90 Max. :8.000
wine %>%
select_if(is.numeric) %>%
cor() %>% # Calcul de la matrice de corrélation empirique
corrplot::corrplot() # représentation graphique de la matrice
scaled_data <- wine[, -12] %>%
select_if(is.numeric) %>%
mutate_all(.funs = scale) # On applique à toutes les colonnes la fonction scale
# La fonction scale centre et réduit un vecteur
Ici, nous ne nous servirons pas de jeux de tests et d’entrainement. Exemple de code ci-dessous si on avait voulu en créer un pour prédire la qualité du vin.
set.seed(seed = 123)
dataset_size <- nrow(wine)
train_size <- as.integer(dataset_size * 0.85)
# Training dataset
train_dataset <- wine %>%
group_by(quality) %>%
sample_frac(0.85)
# Creation of the test dataset
test_dataset <- anti_join(as_tibble(wine), train_dataset)
# Scale the data
scaled_train <- train_dataset %>%
select_if(is.numeric) %>%
mutate_all(.funs = scale)
# Attention : il faudrait aussi mettre à l'echelle le jeu de test, peut-etre plutôt avec les valeurs du jeu d'entrainement
scaled_test <- test_dataset %>%
select_if(is.numeric) %>%
mutate_all(.funs = scale)
On fait une ACP sur toutes les données et en utilisant les variables hors qualité du vin.
res.pca <- prcomp(scaled_data)
head(res.pca$x)
## PC1 PC2 PC3 PC4 PC5 PC6
## [1,] -1.619023 0.4508091 1.7738992 0.04372663 -0.06699352 0.9136349
## [2,] -0.798920 1.8559724 0.9114050 0.54789457 0.01838581 -0.9294232
## [3,] -0.748245 0.8817630 1.1710279 0.41089213 0.04351739 -0.4013476
## [4,] 2.356935 -0.2698916 -0.2434125 -0.92815961 1.49868022 0.1309762
## [5,] -1.619023 0.4508091 1.7738992 0.04372663 -0.06699352 0.9136349
## [6,] -1.583212 0.5690169 1.5378050 0.02374246 0.11004154 0.9933156
## PC7 PC8 PC9 PC10 PC11
## [1,] 0.1609928 -0.2821700 -0.005096442 0.2676757 0.04861491
## [2,] 1.0095128 0.7623485 0.520543822 -0.0628132 -0.13809868
## [3,] 0.5393847 0.5977591 0.086829762 0.1873837 -0.11819169
## [4,] -0.3441828 -0.4552326 -0.091547938 0.1303517 0.31661464
## [5,] 0.1609928 -0.2821700 -0.005096442 0.2676757 0.04861491
## [6,] 0.1096249 -0.3139360 0.034336772 0.3714947 0.03660059
## plot of the eigenvalues
factoextra::fviz_eig(res.pca)
# Plot -2D
ggplot(as.data.frame(res.pca$x), aes(x = PC1, y = PC2, col =wine$quality)) + geom_point()
# Plot - 3D
#library(plotly)
#pca_plotly <- plot_ly(as.data.frame(res.pca$x), x = ~PC1, y = ~PC2, z = ~PC3, color = ~wine$quality) %>% add_markers()
#pca_plotly
On peut effectuer une classification supervisée en utilisant la variable qualité pour vérifier que la qualité de prédiction ne diminue que très peu quand on passe des données complètes aux six premières composantes de l’ACP.
library(class)
y <- wine$quality
neigh <- knn(scaled_data, scaled_data, cl = y, k = 3)
# confusion matrix
tab <- table(neigh, y)
# accuracy
sum(diag(tab)) / sum(rowSums(tab)) * 100
## Using the 6 first PC
neigh_reduced <- knn(res.pca$x[, 1:6], res.pca$x[, 1:6], cl = y, k = 3)
tab <- table(neigh_reduced, y)
sum(diag(tab)) / sum(rowSums(tab)) * 100
# si on voulait prédire des individus supplementaires :
# Centre-reduire les individus supplementaires
# ind.scaled <- scale(ind.sup, center = res.pca$center, scale = res.pca$scale)
# ind.sup.coord <- predict(res.pca, newdata = ind.sup)
# Ecriture condensée
input_size <- 11
m <- 6 # nb de composantes
ae_1 <- keras_model_sequential()
ae_1 %>%
layer_dense(units = m, input_shape = input_size, use_bias = TRUE, activation = "linear", name = "bottleneck") %>%
layer_dense(units = input_size, use_bias = TRUE, activation = "linear") %>%
summary(ae_1)
## Model: "sequential"
## ________________________________________________________________________________
## Layer (type) Output Shape Param #
## ================================================================================
## bottleneck (Dense) (None, 6) 72
##
## dense (Dense) (None, 11) 77
##
## ================================================================================
## Total params: 149
## Trainable params: 149
## Non-trainable params: 0
## ________________________________________________________________________________
On peut écrire le modèle en séparant l’encodeur et le décodeur.
# Ecriture en separant encodeur et decodeur
# Encodeur
enc_input <- layer_input(shape = input_size)
enc_output <- enc_input %>%
layer_dense(units = m, activation = "linear")
encoder <- keras_model(enc_input, enc_output)
# Decodeur
dec_input <- layer_input(shape = m)
dec_output <- dec_input %>%
layer_dense(units = input_size, activation = "linear")
decoder <- keras_model(dec_input, dec_output)
# Auto-encodeur
ae_2_input <- layer_input(shape = input_size)
ae_2_output <- ae_2_input %>%
encoder() %>%
decoder()
ae_2 <- keras_model(ae_2_input, ae_2_output)
summary(ae_2)
## Model: "model_2"
## ________________________________________________________________________________
## Layer (type) Output Shape Param #
## ================================================================================
## input_3 (InputLayer) [(None, 11)] 0
##
## model (Functional) (None, 6) 72
##
## model_1 (Functional) (None, 11) 77
##
## ================================================================================
## Total params: 149
## Trainable params: 149
## Non-trainable params: 0
## ________________________________________________________________________________
On peut récupérer la configuration du modèle à l’aide de la fonction get_config
.
# Couche encodeur : m*n poids + m terme de biais
# Couche décodeur : m*n + n termes de biais
get_config(ae_1)
## {'name': 'sequential', 'layers': [{'class_name': 'InputLayer', 'config': {'batch_input_shape': (None, 11), 'dtype': 'float32', 'sparse': False, 'ragged': False, 'name': 'bottleneck_input'}}, {'class_name': 'Dense', 'config': {'name': 'bottleneck', 'trainable': True, 'batch_input_shape': (None, 11), 'dtype': 'float32', 'units': 6, 'activation': 'linear', 'use_bias': True, 'kernel_initializer': {'class_name': 'GlorotUniform', 'config': {'seed': None}}, 'bias_initializer': {'class_name': 'Zeros', 'config': {}}, 'kernel_regularizer': None, 'bias_regularizer': None, 'activity_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}}, {'class_name': 'Dense', 'config': {'name': 'dense', 'trainable': True, 'dtype': 'float32', 'units': 11, 'activation': 'linear', 'use_bias': True, 'kernel_initializer': {'class_name': 'GlorotUniform', 'config': {'seed': None}}, 'bias_initializer': {'class_name': 'Zeros', 'config': {}}, 'kernel_regularizer': None, 'bias_regularizer': None, 'activity_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}}]}
On compile le modèle en ajoutant fonction de perte et optimiseur
Attention des modifications de valeurs de paramètres peuvent avoir des impacts importants. Exemple du taux d’apprentissage. A ce propos, cet argument a été renommé de lr
en learning_rate
(pas encore à jour sur le site de RStudio)). Le Learning rate est noté \(\rho\) dans nos slides de cours.
ae_1 %>% compile(
loss = "mean_squared_error",
optimizer = optimizer_sgd(learning_rate = 0.1) # stochastic gradient descent optimizer
)
Attention : penser à standardiser les données au préalable et à les mettre sous forme de matrice et non de dataframe. Les messages d’erreur si on oublie de passer en matrice sont parfois un peu obscures.
epochs_nb <- 50L # L spécifie que c'est un entier
batch_size <- 10L
scaled_train <- as.matrix(scaled_data)
ae_1 %>% fit(x = scaled_train, y = scaled_train, epochs = epochs_nb, batch_size = batch_size)
# evaluate the performance of the model
mse.ae <- evaluate(ae_1, scaled_train, scaled_train)
mse.ae
## loss
## 0.1455614
Si on ne précise pas la taille ds “batchs”, elle est fixée à 32. epochs est à 10 par défaut. On peut utiliser l’argument verbose = 0 pour ne plus avoir l’addiche des résultats par epoch.
On peut s’amuser à changer le nombre d’epoch, la taille des batchs et regarder ce que ça change.
On peut remarques qu’il n’y a aucune contrainte sur les poids “décodeurs” et les poids “encodeurs”.
poids <- get_weights(ae_1)
# Poids encodeurs/ decodeurs par defaut
w_encodeur <- poids[[1]] %>% print()
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 0.23468414 0.06929202 0.17294656 0.15061887 0.23833768 0.0056886827
## [2,] 0.33239827 -0.15120839 0.15678896 0.02139481 -0.45791674 -0.0717435107
## [3,] 0.03911459 0.10943296 -0.03852385 0.22915500 0.27809152 0.1401681453
## [4,] 0.12761573 0.32623178 -0.31349796 0.16859703 0.06419385 -0.6000732183
## [5,] 0.36893070 -0.18268254 -0.24759518 -0.01182475 -0.26909330 0.3475288749
## [6,] -0.15080374 -0.35679284 -0.07660953 0.25706884 0.04749735 0.0451730974
## [7,] -0.02730569 -0.45066795 0.03646427 0.32723713 -0.01812807 0.1201260686
## [8,] 0.32202190 -0.07197118 -0.02763597 -0.25654280 0.29900402 -0.3886266649
## [9,] -0.30189106 0.16621883 -0.29382595 -0.39581701 0.02554698 -0.3968795240
## [10,] -0.03883547 0.02805572 -0.53302705 -0.28257433 0.18249787 0.1098793373
## [11,] -0.28967756 0.54928517 -0.22669598 0.35186616 -0.07382300 -0.0000277053
w_decodeur <- poids[[3]] %>% print()
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 0.4803997 0.39648020 0.23494892 0.37783548 0.68008435 -0.38851345
## [2,] 0.2322183 -0.06041185 0.23661095 0.05997032 0.01200666 -0.75674850
## [3,] 0.2767902 0.08524980 -0.09072675 -0.52330399 -0.69819802 -0.36067218
## [4,] 0.1840855 0.04511364 0.31249666 0.59227073 0.01198283 0.49945936
## [5,] 0.5273888 -0.84126109 0.59490931 -0.08548960 -0.37692022 0.09208596
## [6,] 0.1343837 -0.29665691 0.27479261 -0.77625358 0.44917628 -0.26834562
## [,7] [,8] [,9] [,10] [,11]
## [1,] -0.21268399 0.5159686208 -0.5366725 0.1098122 -0.1972939
## [2,] -0.76504529 -0.1957928091 -0.1395066 -0.1072347 0.6005887
## [3,] -0.27012762 -0.0005750165 -0.2192065 -0.8509939 -0.2561342
## [4,] 0.57269549 -0.1940685809 -0.4480691 -0.2922272 0.4941948
## [5,] 0.05043474 0.5146766305 -0.2168749 0.3036602 -0.2714503
## [6,] -0.22787918 -0.3780225217 -0.4108859 0.3887524 0.1526451
# ACP : unit Norm: the weights on a layer have unit norm.
sum(w_decodeur^2) / m
## [1] 1.775577
ae_1predict <- ae_1 %>% predict(scaled_train)
# Repasser en non standardisé pour comparer...
varcol <- apply(wine[, -12], 2, var)
meancol <- colMeans(wine[, -12])
ae_1predict_or <- sapply(c(1:11), FUN = function(x) ae_1predict[, x] * sqrt(varcol[x]) + meancol[x])
# extract the bottleneck layer
intermediate_layer_model <- keras_model(inputs = ae_1$input, outputs = get_layer(ae_1, "bottleneck")$output)
intermediate_output <- predict(intermediate_layer_model, scaled_train)
ggplot(data.frame(PC1 = intermediate_output[,1], PC2 = intermediate_output[,2]), aes(x = PC1, y = PC2, col = wine$quality)) + geom_point()
# PCA reconstruction
pca.recon <- function(pca, x, k){
mu <- matrix(rep(res.pca$center, nrow(pca$x)), nrow = nrow(res.pca$x), byrow = T)
recon <- res.pca$x[,1:k] %*% t(res.pca$rotation[,1:k]) + mu
mse <- mean((recon - x)^2)
return(list(x = recon, mse = mse))
}
xhat <- rep(NA, 10)
for(k in 1:10){
xhat[k] <- pca.recon(res.pca, scaled_train, k)$mse
}
ae.mse <- rep(NA, 5)
input_size <- 11
#m <- 6 # nb de composantes
for(k in 1:10){
modelk <- keras_model_sequential()
modelk %>%
layer_dense(units = k, input_shape = input_size, use_bias = TRUE, activation = "linear", name = "bottleneck") %>%
layer_dense(units = input_size, use_bias = TRUE, activation = "linear")
modelk %>% compile(
loss = "mean_squared_error",
optimizer = optimizer_sgd(learning_rate = 0.1)
)
modelk %>% fit(
x = scaled_train,
y = scaled_train,
epochs = 50,
verbose = 0,
)
ae.mse[k] <- unname(evaluate(modelk, scaled_train, scaled_train))
}
df <- data.frame(k = c(1:10, 1:10), mse = c(xhat, ae.mse), method = c(rep("acp", 10), rep("autoencodeur", 10)))
ggplot(df, aes(x = k, y = mse, col = method)) + geom_line()
Pour construire un auto-encodeur variationnel, on effectuera deux modifications principales : une portant sur la couche intermédiaire de dimension réduite, et une sur la fonction de perte à minimiser. Vous pouvez modifier les poids \(k_1\) et \(k_2\) de deux composantes de la fonction de perte.
# Attention ecriture keras k_rendom_normal, k_shape etc.
sampling <- function(arg) {
z_mean <- arg[, 1:(latent_dim)]
z_log_var <- arg[, (latent_dim + 1):(2 * latent_dim)]
epsilon <- k_random_normal(
shape = c(k_shape(z_mean)[[1]]),
mean = 0.,
stddev = epsilon_std
)
z_mean + k_exp(z_log_var / 2) * epsilon
}
# Fonction de perte
vae_loss <- function(x, x_decoded, k1 = 1, k2 = 0.01) {
mse_loss <- k_sum(loss_mean_squared_error(x, x_decoded))
kl_loss <- -0.5 * k_sum(1 + z_log_var - k_square(z_mean) - k_exp(z_log_var))
k1*mse_loss + k2*kl_loss
}
set.seed(123)
# Parameters --------------------------------------------------------------
batch_size <- 32
latent_dim <- 6L # L spécifie que c'est un entier
epochs_nb <- 50L
epsilon_std <- 1.0
# Définition du modèle --------------------------------------------------------
x <- layer_input(shape = c(input_size))
z_mean <- layer_dense(x, latent_dim)
z_log_var <- layer_dense(x, latent_dim)
# note that "output_shape" isn't necessary with the TensorFlow backend
z <- layer_concatenate(list(z_mean, z_log_var)) %>%
layer_lambda(sampling)
# On instancie les couches séparément pour pouvoir les réutiliser plus tard
decoder <- layer_dense(units = input_size, activation = "sigmoid")
x_decoded <- decoder(z)
# end-to-end autoencoder
vae <- keras_model(x, x_decoded)
# encoder, from inputs to latent space
encoder <- keras_model(x, z_mean)
# generator, from latent space to reconstructed inputs
decoder_input <- layer_input(shape = latent_dim)
x_decoded_2 <- decoder(decoder_input)
generator <- keras_model(decoder_input, x_decoded_2)
vae %>% compile(optimizer = "sgd", loss = vae_loss) # rmsprop
vae %>% fit(x = scaled_train, y = scaled_train, epochs = epochs_nb)
x_train_encoded <- predict(encoder, scaled_train, batch_size = batch_size)
## Representation dans espace latent
x_train_encoded %>%
as_tibble() %>%
ggplot(aes(x = V1, y = V2, colour = wine$quality)) +
geom_point()
## Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if `.name_repair` is omitted as of tibble 2.0.0.
## Using compatibility `.name_repair`.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
L’auteur du blog nous dit que souvent les autoencodeurs sont sous-optimaux et qu’ils pourraient être optimisés si on les forçait à avoir les mêmes propriétés que l’ACP à savoir :
Tied Weights: equal weights on Encoder and the corresponding Decoder layer.
Orthogonal weights: each weight vector is independent of others.
Uncorrelated features: output of the encoding layer are not correlated.
Unit Norm: the weights on a layer have unit norm.
Tout n’est pas forcément facile à faire avec keras. Par exemple, pour créer des poids liés, il est nécessaire de créer une sous-classe.
reticulate::py_config()
## python: /home/juaubert/.local/share/r-miniconda/envs/r-reticulate/bin/python
## libpython: /home/juaubert/.local/share/r-miniconda/envs/r-reticulate/lib/libpython3.7m.so
## pythonhome: /home/juaubert/.local/share/r-miniconda/envs/r-reticulate:/home/juaubert/.local/share/r-miniconda/envs/r-reticulate
## version: 3.7.11 (default, Jul 27 2021, 14:32:16) [GCC 7.5.0]
## numpy: /home/juaubert/.local/share/r-miniconda/envs/r-reticulate/lib/python3.7/site-packages/numpy
## numpy_version: 1.21.4
## tensorflow: /home/juaubert/.local/share/r-miniconda/envs/r-reticulate/lib/python3.7/site-packages/tensorflow
##
## NOTE: Python version was forced by RETICULATE_PYTHON
tensorflow::tf_config()
## TensorFlow v2.7.0 (~/.local/share/r-miniconda/envs/r-reticulate/lib/python3.7/site-packages/tensorflow)
## Python v3.7 (~/.local/share/r-miniconda/envs/r-reticulate/bin/python)
Ressources autour de tensorflow, keras et rstudio : - https://tensorflow.rstudio.com/learn/resources/ - https://raw.githubusercontent.com/rstudio/cheatsheets/master/keras.pdf - https://keras.rstudio.com/articles/examples/variational_autoencoder.html
Quelques concepts-clefs : - https://www.analyticsvidhya.com/blog/2017/05/25-must-know-terms-concepts-for-beginners-in-deep-learning/
Référence du jeu de données
P. Cortez, A. Cerdeira, F. Almeida, T. Matos and J. Reis. Modeling wine preferences by data mining from physicochemical properties. In Decision Support Systems, Elsevier, 47(4):547-553, 2009.