Skip to content

Commit 13a07cb

Browse files
committed
update: GNN+RNN
1 parent daacfff commit 13a07cb

File tree

73 files changed

+7860
-86
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+7860
-86
lines changed

A4-MLpipeline.qmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ As we have seen today, many of the machine learning algorithms are distributed o
549549

550550
Machine learning frameworks such as `mlr3` or `tidymodels` provide a general interface for the ML pipeline, in particular the training and the hyperparameter tuning with nested CV. They support most ML packages/algorithms.
551551

552-
### mlr3 {#mlr}
552+
### mlr3 {#sec-mlr}
553553

554554
The key features of mlr3 are:
555555

C2-DeepNeuralNetworks.qmd

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,12 @@ What is the "mean_squared_error" loss?
374374
```{r}
375375
model_history =
376376
model %>%
377-
fit(x = x, y = matrix(y, ncol = 1L), epochs = 100L,
377+
fit(x = x, y = as.numeric(y), epochs = 100L,
378378
batch_size = 20L, shuffle = TRUE)
379379
```
380380

381381
`r unhide()`
382382

383-
Tip: Only matrices are accepted for $\boldsymbol{X}$ and $\boldsymbol{y}$ by Keras. R often drops a one column matrix into a vector (change it back to a matrix!)
384-
385383
7. **Plot training history.**
386384

387385
`r hide("Solution")`

C3-ConvolutionalNeuralNetworks.qmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ print(test_accuracy)
609609
```
610610
:::
611611

612-
### Transfer Learning {#transfer}
612+
### Transfer Learning {#sec-transfer}
613613

614614
Another approach to reduce the necessary number of images or to speed up convergence of the models is the use of transfer learning.
615615

C4-RecurrentNeuralNetworks.qmd

Lines changed: 203 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,209 @@ editor_options:
44
chunk_output_type: console
55
---
66

7-
# Recurrent Neural Networks
8-
Artificial neural networks are biologically inspired, the idea is that inputs are processed by weights, the neurons, the signals then accumulate at hidden nodes (axioms), and only if the sum of activations of several neurons exceed a certain threshold, the signal will be passed on.
7+
# Recurrent Neural Networks (RNN)
98

109
```{r}
11-
library(cito)
10+
#| echo: false
11+
#| include: false
12+
#| results: false
13+
reticulate::use_condaenv("r-reticulate")
14+
library(tensorflow)
15+
tf
16+
tf$abs(3.)
1217
```
18+
19+
20+
Recurrent neural networks are used to model sequential data, i.e. a temporal sequence that exhibits temporal dynamic behavior. Here is a good introduction to the topic:
21+
22+
```{r chunk_chapter5_0, eval=knitr::is_html_output(excludes = "epub"), results = 'asis', echo = F}
23+
cat(
24+
'<iframe width="560" height="315"
25+
src="https://www.youtube.com/embed/SEnXr6v2ifU"
26+
frameborder="0" allow="accelerometer; autoplay; encrypted-media;
27+
gyroscope; picture-in-picture" allowfullscreen>
28+
</iframe>'
29+
)
30+
```
31+
32+
33+
## Case Study: Predicting drought
34+
We will use a subset of the data explained in [this github repository](https://github.com/Epistoteles/predicting-drought)
35+
```{r chunk_chapter5_0_Rnn, message=FALSE, warning=FALSE}
36+
utils::download.file("https://www.dropbox.com/s/radyscnl5zcf57b/weather_soil.RDS?raw=1", destfile = "weather_soil.RDS")
37+
data = readRDS("weather_soil.RDS")
38+
X = data$train # Features of the last 180 days
39+
dim(X)
40+
# 999 batches of 180 days with 21 features each
41+
Y = data$target
42+
dim(Y)
43+
# 999 batches of 6 week drought predictions
44+
45+
# let's visualize drought over 24 months:
46+
# -> We have to take 16 batches (16*6 = 96 weaks ( = 24 months) )
47+
plot(as.vector(Y[1:16,]), type = "l", xlab = "week", ylab = "Drought")
48+
```
49+
50+
51+
52+
```{r chunk_chapter5_1_Rnn, message=FALSE, warning=FALSE}
53+
library(keras)
54+
55+
holdout = 700:999
56+
X_train = X[-holdout,,]
57+
X_test = X[holdout,,]
58+
59+
Y_train = Y[-holdout,]
60+
Y_test = Y[holdout,]
61+
62+
model = keras_model_sequential()
63+
model %>%
64+
layer_rnn(cell = layer_lstm_cell(units = 60L),input_shape = dim(X)[2:3]) %>%
65+
layer_dense(units = 6L)
66+
67+
model %>% compile(loss = loss_mean_squared_error, optimizer = optimizer_adamax(learning_rate = 0.01))
68+
69+
model %>% fit(x = X_train, y = Y_train, epochs = 30L)
70+
71+
preds =
72+
model %>% predict(X_test)
73+
74+
75+
matplot(cbind(as.vector(preds[1:48,]),
76+
as.vector(Y_test[1:48,])),
77+
col = c("darkblue", "darkred"),
78+
type = "o",
79+
pch = c(15, 16),
80+
xlab = "week", ylab = "Drought")
81+
legend("topright", bty = "n",
82+
col = c("darkblue", "darkred"),
83+
pch = c(15, 16),
84+
legend = c("Prediction", "True Values"))
85+
86+
```
87+
88+
89+
90+
91+
92+
The following code snippet shows you many (technical) things you need for building more complex network structures, even with LSTM cells (the following example doesn't have any functionality, it is just an example for how to process two different inputs in different ways within one network):
93+
94+
95+
::: panel-tabset
96+
## Keras
97+
98+
```{r chunk_chapter5_1, message=FALSE, warning=FALSE}
99+
library(tensorflow)
100+
library(keras)
101+
set_random_seed(321L, disable_gpu = FALSE) # Already sets R's random seed.
102+
103+
tf$keras$backend$clear_session() # Resets especially layer counter.
104+
105+
inputDimension1 = 50L
106+
inputDimension2 = 10L
107+
108+
input1 = layer_input(shape = inputDimension1)
109+
input2 = layer_input(shape = inputDimension2)
110+
111+
modelInput2 = input2 %>%
112+
layer_dropout(rate = 0.5) %>%
113+
layer_dense(units = inputDimension2, activation = "gelu")
114+
115+
modelMemory = input1 %>%
116+
layer_embedding(input_dim = inputDimension1, output_dim = 64L) %>%
117+
layer_lstm(units = 64L) %>%
118+
layer_dropout(rate = 0.5) %>%
119+
layer_dense(units = 2L, activation = "sigmoid")
120+
121+
modelDeep = input1 %>%
122+
layer_dropout(rate = 0.5) %>%
123+
layer_dense(units = 64L, activation = "relu") %>%
124+
layer_dropout(rate = 0.3) %>%
125+
layer_dense(units = 64L, activation = "relu") %>%
126+
layer_dense(units = 64L, activation = "relu") %>%
127+
layer_dense(units = 5L, activation = "sigmoid")
128+
129+
modelMain = layer_concatenate(c(modelMemory, modelDeep, modelInput2)) %>%
130+
layer_dropout(rate = 0.25) %>%
131+
layer_dense(units = 64L, activation = "relu") %>%
132+
layer_dropout(rate = 0.3) %>%
133+
layer_dense(units = 64L, activation = "relu") %>%
134+
layer_dense(units = 2L, activation = "sigmoid")
135+
136+
model = keras_model(
137+
inputs = c(input1, input2),
138+
outputs = c(modelMain) # Use the whole modelMain (resp. its output) as output.
139+
)
140+
141+
summary(model)
142+
# model %>% plot_model()
143+
```
144+
145+
## Torch
146+
147+
```{r chunk_chapter5_1_torch, message=FALSE, warning=FALSE}
148+
library(torch)
149+
150+
model_torch = nn_module(
151+
initialize = function(type, inputDimension1 = 50L, inputDimension2 = 10L) {
152+
self$dim1 = inputDimension1
153+
self$dim2 = inputDimension2
154+
self$modelInput2 = nn_sequential(
155+
nn_dropout(0.5),
156+
nn_linear(in_features = self$dim2, out_features = self$dim2),
157+
nn_selu()
158+
)
159+
self$modelMemory = nn_sequential(
160+
nn_embedding(self$dim1, 64),
161+
nn_lstm(64, 64)
162+
)
163+
self$modelMemoryOutput = nn_sequential(
164+
nn_dropout(0.5),
165+
nn_linear(64L, 2L),
166+
nn_sigmoid()
167+
)
168+
169+
self$modelDeep = nn_sequential(
170+
nn_dropout(0.5),
171+
nn_linear(self$dim1, 64L),
172+
nn_relu(),
173+
nn_dropout(0.3),
174+
nn_linear(64, 64),
175+
nn_relu(),
176+
nn_linear(64, 64),
177+
nn_relu(),
178+
nn_linear(64, 5),
179+
nn_sigmoid()
180+
)
181+
182+
self$modelMain = nn_sequential(
183+
nn_linear(7+self$dim2, 64),
184+
nn_relu(),
185+
nn_dropout(0.5),
186+
nn_linear(64, 64),
187+
nn_relu(),
188+
nn_dropout(),
189+
nn_linear(64, 2),
190+
nn_sigmoid()
191+
)
192+
},
193+
194+
forward = function(x) {
195+
input1 = x[[1]]
196+
input2 = x[[2]]
197+
out2 = self$modelInput2(input2)
198+
out1 = self$modelMemoryOutput( self$modelMemory(input1)$view(list(dim(input1)[1], -1)) )
199+
out3 = self$modelDeep(input1)
200+
out = self$modelMain(torch_cat(list(out1, out2, out3), 2))
201+
return(out)
202+
}
203+
204+
)
205+
206+
(model_torch())
207+
208+
```
209+
210+
211+
:::
212+

C5-GNN.qmd

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
output: html_document
3+
editor_options:
4+
chunk_output_type: console
5+
---
6+
7+
# Graph Neural Networks (GNNs)
8+
9+
```{r}
10+
#| echo: false
11+
#| include: false
12+
#| results: false
13+
reticulate::use_condaenv("r-reticulate")
14+
library(tensorflow)
15+
tf
16+
tf$abs(3.)
17+
```
18+
19+
20+
Graph neural networks (GNN) is a young representative of the deep neural network family but is receiving more and more attention in the last years because of their ability to process non-Euclidean data such as graphs.
21+
22+
Currently there is no R package for GNNs available. However, we can use the 'reticulate' package to use the python packages 'torch' (python version) and 'torch_geometric'.
23+
24+
The following example was mostly adapted from the 'Node Classification with Graph Neural Networks' example from the [torch_geometric documentation](https://pytorchgeometric.readthedocs.io/en/latest/notes/colabs.html).
25+
26+
The dataset is also provided by the 'torch_geometric' package and consists of molecules presented as graphs and the task is to predict whether HIV virus replication is inhibited by the molecule or not (classification, binary classification).
27+
28+
```{r GNN_1, eval=FALSE}
29+
library(reticulate)
30+
# Load python packages torch and torch_geometric via the reticulate R package
31+
torch = import("torch")
32+
torch_geometric = import("torch_geometric")
33+
# helper functions from the torch_geometric modules
34+
GCNConv = torch_geometric$nn$GCNConv
35+
global_mean_pool = torch_geometric$nn$global_mean_pool
36+
# Download the MUTAG TUDataset
37+
dataset = torch_geometric$datasets$TUDataset(root='data/TUDataset',
38+
name='MUTAG')
39+
dataloader = torch_geometric$loader$DataLoader(dataset,
40+
batch_size=64L,
41+
shuffle=TRUE)
42+
# Create the model with a python class
43+
# There are two classes in the response variable
44+
GCN = PyClass(
45+
"GCN",
46+
inherit = torch$nn$Module,
47+
defs = list(
48+
`__init__` = function(self, hidden_channels) {
49+
super()$`__init__`()
50+
torch$manual_seed(42L)
51+
self$conv = GCNConv(dataset$num_node_features, hidden_channels)
52+
self$linear = torch$nn$Linear(hidden_channels, dataset$num_classes)
53+
NULL
54+
},
55+
forward = function(self, x, edge_index, batch) {
56+
x = self$conv(x, edge_index)
57+
x = x$relu()
58+
x = global_mean_pool(x, batch)
59+
60+
x = torch$nn$functional$dropout(x, p = 0.5, training=self$training)
61+
x = self$linear(x)
62+
return(x)
63+
}
64+
))
65+
```
66+
67+
Training loop:
68+
69+
```{r GNN_2, eval=FALSE}
70+
# create model object
71+
model = GCN(hidden_channels = 64L)
72+
# get optimizer and loss function
73+
optimizer = torch$optim$Adamax(model$parameters(), lr = 0.01)
74+
loss_func = torch$nn$CrossEntropyLoss()
75+
# set model into training mode (because of the dropout layer)
76+
model$train()
77+
# train model
78+
for(e in 1:50) {
79+
iterator = reticulate::as_iterator(dataloader)
80+
coro::loop(for (b in iterator) {
81+
pred = model(b$x, b$edge_index, b$batch)
82+
loss = loss_func(pred, b$y)
83+
loss$backward()
84+
optimizer$step()
85+
optimizer$zero_grad()
86+
})
87+
if(e %% 10 ==0) cat(paste0("Epoch: ",e," Loss: ", round(loss$item()[1], 4), "\n"))
88+
}
89+
## Epoch: 10 Loss: 0.6151
90+
## Epoch: 20 Loss: 0.6163
91+
## Epoch: 30 Loss: 0.5745
92+
## Epoch: 40 Loss: 0.5362
93+
## Epoch: 50 Loss: 0.5829
94+
```
95+
96+
Make predictions:
97+
98+
```{r GNN_3, eval = FALSE}
99+
preds = list()
100+
test = torch_geometric$loader$DataLoader(dataset, batch_size=64L,shuffle=FALSE)
101+
iterator = reticulate::as_iterator(test)
102+
model$eval()
103+
counter = 1
104+
coro::loop(for (b in iterator) {
105+
preds[[counter]] = model(b$x, b$edge_index, b$batch)
106+
counter <<- counter + 1
107+
})
108+
head(torch$concat(preds)$sigmoid()$data$cpu()$numpy(), n = 3)
109+
## [,1] [,2]
110+
## [1,] 0.3076028 0.6427078
111+
## [2,] 0.4121239 0.5515330
112+
## [3,] 0.4119514 0.5516798
113+
```

0 commit comments

Comments
 (0)