SOCR ≫ | DSPA ≫ | DSPA2 Topics ≫ |
Review the 3D/4D MRI imaging data discussion in Chapter 2. Extract the time courses of several timeseries at different 3D spatial locations, some near-by, some farther apart (distant voxels). Then, apply time-series analyses, report findings, determine if near-by or farther-apart voxels may be more correlated.
# See examples here: https://cran.r-project.org/web/packages/oro.nifti/vignettes/nifti.pdf
library(oro.nifti) fMRIURL <- “http://socr.umich.edu/HTML5/BrainViewer/data/fMRI_FilteredData_4D.nii.gz” fMRIFile <- file.path(tempdir(), “fMRI_FilteredData_4D.nii.gz”) download.file(fMRIURL, dest=fMRIFile, quiet=TRUE) (fMRIVolume <- readNIfTI(fMRIFile, reorient=FALSE)) # dimensions: 64 x 64 x 21 x 180 ; 4mm x 4mm x 6mm x 3 sec
fMRIVolDims <- dim(fMRIVolume); fMRIVolDims time_dim <- fMRIVolDims[4]; time_dim
hist(fMRIVolume)
# To examine the time course of a specific 3D voxel (say the one at x=30, y=30, z=15): plot(fMRIVolume[30, 30, 10,], type=‘l’, main=“Time Series of 3D Voxel (x=30, y=30, z=15)”, col=“blue”)
x1 <- c(1:180) y1 <- loess(fMRIVolume[30, 30, 10,]~ x1, family = “gaussian”) lines(x1, smooth(fMRIVolume[30, 30, 10,]), col = “red”, lwd = 2) lines(ksmooth(x1, fMRIVolume[30, 30, 10,], kernel = “normal”, bandwidth = 5), col = “green”, lwd = 3)Use Google Web-Search Trends and Stock Market Data to:
Job
TTR
to smooth the original graph by monthUse Handwritten English Letters data to:
Let’s use the 2009-2017 Climate Data from the Max Planck Institute in Jena, Germany to demonstrate time-series analysis via RNN. You can find more details about this study here. This sequence data represents a time-series recorded at the Weather Station at the Max Planck Institute for Biogeochemistry in Jena, Germany. The data includes date-time and 14 climate measurements/features (e.g., atmospheric pressure, temperature and humidity), which are recorded every 10 minutes over 9 years. Suppose we choose a prediction of window 12 months (1 year) and a batch size of 4 units (evenly dividing the number of testing and training observations), time steps = 1 (specifying one lag), and epochs = 100.
# Model inputs
<- 12 # = nrow(df_test)
lag_setting <- 0
batch_size <- 10000
train_length
# the number of predictions we'll make equals the length of the hidden state
<- 1
tsteps <- 100
epochs
# how many features = predictors we have
<- 1 n_features
library("dplyr")
library("recipes")
library("tibble")
#install.packages("tibbletime")
library("tibbletime")
<- as.data.frame(cbind(df_Temp_Time$t[1:10000], df_train.std))
df_train # head(df_train)
<- as.data.frame(cbind(df_Temp_Time$t[1:10000], df_test.std))
df_test colnames(df_train) <- c("value"); colnames(df_test) <- c("value")
colnames(df_Temp_Time) <- c("Z", "index")
<- bind_rows(
df %>% add_column(key = "training"),
df_train %>% add_column(key = "testing")
df_test # %>%
) # as_tbl_time(index = as.Date(df_Temp_Time$index[1:10000]))
# df
<- as.data.frame(cbind(as.Date(df_Temp_Time$index[1:20000]),
df1 as.double(df$value), df$key))
colnames(df1) <- c("index", "value", "class")
head(df1)
<- as.Date(df_Temp_Time$index[1:20000])
dates # head(as_tbl_time(df, dates))
<- tibble::tibble(
df2 index = as.POSIXct(dates),
value = as.numeric(as.character(df1$value)),
class = df1$class
head(df2)
); <- as_tbl_time(df2, index)
df3 head(df3); tail(df3); str(df3$value); head(df3$value)
<- recipe(value ~ ., df3) %>%
rec_obj step_sqrt(value) %>%
step_center(value) %>%
step_scale(value) %>%
prep()
tail(rec_obj$template)
# define time, value and labels for 10,000 timepoints
<- climate_time[1:10000]
climate_time_10000 <- rep("training", 10000); testLabels <- rep("testing", 10000)
trainLables <- rbind(trainLables, testLabels)
TrainTestLabels <- rbind(df_train.std, df_test.std)
TrainTestValues tibble(date = as.Date(as.vector(rbind(climate_time_10000, climate_time_10000))),
value = TrainTestValues[ , 1], key=as.vector(TrainTestLabels))
# Training Set
<- rec_obj$template %>%
lag_train_tbl mutate(value_lag = lag(value, n = lag_setting)) %>%
filter(!is.na(value_lag)) %>%
filter(class == "training") %>%
tail(train_length)
if (is.na(lag_train_tbl$value)) lag_train_tbl$value <- 0
<- lag_train_tbl$value_lag
x_train_vec <- array(data = x_train_vec, dim = c(length(x_train_vec), 1, 1))
x_train_arr
<- lag_train_tbl$value
y_train_vec <- array(data = y_train_vec, dim = c(length(y_train_vec), 1))
y_train_arr
# Testing Set
<- rec_obj$template %>%
lag_test_tbl mutate(value_lag = lag(value, n = lag_setting)) %>%
filter(!is.na(value_lag)) %>%
filter(class == "testing")
<- lag_test_tbl$value_lag
x_test_vec <- array(data = x_test_vec, dim = c(length(x_test_vec), 1, 1))
x_test_arr
<- lag_test_tbl$value
y_test_vec <- array(data = y_test_vec, dim = c(length(y_test_vec), 1)) y_test_arr
keras_model_sequential()
function.layer_dense(units = 1)
provides a standard ending to the keras
sequential model. At the end, we can use compile()
with loss = "mae"
and optimizer = "adam"
..1 <- keras_model_sequential()
model
.1 %>%
modellayer_lstm(units = 50,
# batch_input_shape = c(batch_size, tsteps, n_features),
batch_input_shape=c(32, 1, 1),
return_sequences = TRUE,
stateful = TRUE) %>%
layer_lstm(units = 50,
return_sequences = FALSE,
stateful = TRUE) %>%
layer_dense(units = 1)
.1 %>%
modelcompile(loss = 'mae', optimizer = 'adam')
.1 model
for
loop (where we reset the states). To preserve sequences, we specify shuffle = FALSE
which allows us direct control over the states reset after each epoch, reset_states()
.for (i in 1:epochs) {
.1 %>% fit(x = x_train_arr[1:(166*32), ,],
modely = y_train_arr[1:(166*32), ],
batch_size = 32,
epochs = 1,
verbose = 1,
shuffle = FALSE)
.1 %>% reset_states()
modelcat("Epoch: ", i)
}
########################### RStudio Documentation
# https://cran.rstudio.com/web/packages/keras/vignettes/sequential_model.html
# define a model along with the compilation step (
.2 <- keras_model_sequential()
model.2 %>%
modellayer_dense(units = 32, input_shape = c(100)) %>%
layer_activation('relu') %>%
layer_dense(units = 10) %>%
layer_activation('softmax')
# the compile() function has appropriate arguments for a multi-class/binary classification problem
.2 %>% compile(
modeloptimizer = 'rmsprop',
# loss = 'categorical_crossentropy',
# metrics = c('accuracy')
loss = loss_binary_crossentropy,
metrics = metric_binary_accuracy
)
# Compilation for mean squared error regression problem
%>% compile(
model optimizer = optimizer_rmsprop(lr = 0.002),
loss = 'mse'
)
# Train the Keras models on R matrices or higher dimensional arrays of input data and labels using
# the fit() function, e.g., a single-input model with 2 classes (binary classification)
# Generate dummy data
<- matrix(runif(1000*100), nrow = 1000, ncol = 100)
data <- matrix(round(runif(1000, min = 0, max = 1)), nrow = 1000, ncol = 1)
labels
# Train the model, iterating on the data in batches of 32 samples
.2 %>% fit(data, labels, epochs=10, batch_size=32)
model
# constants
<- 16
data_dim <- 8
timesteps <- 10
num_classes
# define and compile model
# expected input data shape: (batch_size, timesteps, data_dim)
<- 1
data_dim <- 2
timesteps <- 2
num_classes
<- keras_model_sequential()
model %>%
model layer_lstm(units = 32, return_sequences = TRUE, input_shape = c(timesteps, data_dim)) %>%
layer_lstm(units = 32, return_sequences = TRUE) %>%
layer_lstm(units = 32) %>% # return a single vector dimension 32
layer_dense(units = 10, activation = 'softmax') %>%
compile(
loss = 'categorical_crossentropy',
optimizer = 'rmsprop',
metrics = c('accuracy')
)
# training data
<- array(runif(1000 * timesteps * data_dim), dim = c(1000, timesteps, data_dim))
x_train <- matrix(runif(1000 * num_classes), nrow = 1000, ncol = num_classes)
y_train <- array(rec_obj$template$value[1:10000], dim = c(1000, 2, 1))
x_train <- matrix(rec_obj$template$class[1:10000], nrow = 5000, ncol = 2)
y_train
# testing validation data
<- array(runif(100 * timesteps * data_dim), dim = c(100, timesteps, data_dim))
x_val <- matrix(runif(100 * num_classes), nrow = 100, ncol = num_classes)
y_val
<- array(rec_obj$template$value, dim = c(2000, 2, 1))
x_val <- matrix(rec_obj$template$class, nrow = 10000, ncol = 2)
y_val
# train
%>% fit(
model batch_size = 64, epochs = 5, validation_data = list(x_val, y_val)
x_train, y_train, )
x_test_arr
, using the predict()
function. The predictions can be inversely transformed into the original measuring units using the saved mean and SD of the standardizing transformation.reduce()
and the time_bind_rows()
function.# Make Predictions
<- model %>%
pred_out predict(x_test_arr, batch_size = batch_size) %>%
1]
.[,
# Retransform values
<- tibble(
pred_tbl index = lag_test_tbl$index,
value = (pred_out * scale_history + center_history)^2
)
# Combine actual data with predictions
<- df_trn %>%
tbl_1 add_column(key = "actual")
<- df_tst %>%
tbl_2 add_column(key = "actual")
<- pred_tbl %>%
tbl_3 add_column(key = "predict")
# Create time_bind_rows() to solve dplyr issue
<- function(data_1, data_2, index) {
time_bind_rows <- enquo(index)
index_expr bind_rows(data_1, data_2) %>%
as_tbl_time(index = !! index_expr)
}
<- list(tbl_1, tbl_2, tbl_3) %>%
ret reduce(time_bind_rows, index = index) %>%
arrange(key, index) %>%
mutate(key = as_factor(key))
ret
caret
package and yardstick::rmse()
function may be appropriate to quantify the performance. As the data is in long format we can write a wrapper function calc_rmse()
that transforms the data into the yardstick::rmse()
format.<- function(prediction_tbl) {
calc_rmse
<- function(data) {
rmse_calculation %>%
data spread(key = key, value = value) %>%
select(-index) %>%
filter(!is.na(predict)) %>%
rename(
truth = actual,
estimate = predict
%>%
) rmse(truth, estimate)
}
<- possibly(rmse_calculation, otherwise = NA)
safe_rmse
safe_rmse(prediction_tbl)
}
# assess the RMSE on the model
calc_rmse(ret)
plot_prediction()
.# Setup single plot function
<- function(data, id, alpha = 1, size = 2, base_size = 14) {
plot_prediction
<- calc_rmse(data)
rmse_val
<- data %>%
g ggplot(aes(index, value, color = key)) +
geom_point(alpha = alpha, size = size) +
theme_tq(base_size = base_size) +
scale_color_tq() +
theme(legend.position = "none") +
labs(
title = glue("{id}, RMSE: {round(rmse_val, digits = 1)}"),
x = "", y = ""
)
return(g)
}
%>%
ret plot_prediction(id = split_id, alpha = 0.65) +
theme(legend.position = "bottom")
Let’s put these ideas in practice in a Keras implementation. The first thing we need is a lot of text data that we can use to learn a language model. You could use any sufficiently large text file or set of text files – Wikipedia, the Lord of the Rings, etc. In this example we will use some of the writings of Nietzsche, the late-19th century German philosopher (translated to English). The language model we will learn will thus be specifically a model of Nietzsche’s writing style and topics of choice, rather than a more generic model of the English language.
library(keras)
library(stringr)
<- read.csv("https://umich.instructure.com/files/12554194/download?download_frd=1", header=T)
data.csv <- tolower(data.csv$text)
text cat("Corpus length:", nchar(text), "\n")
maxlen
, generate dummy indicator variables (one-hot encoding), and pack them in a 3D array x
of shape (sequences, maxlen, unique_characters)
. The array y
contains the corresponding targets - the one-hot-encoded characters that come after each extracted sequence.<- 60 # Length of extracted character sequences
maxlen <- 3 # We sample a new sequence every `step` characters
step
<- seq(1, nchar(text) - maxlen, by = step)
text_indexes # This holds our extracted sequences
<- str_sub(text, text_indexes, text_indexes + maxlen - 1)
sentences # This holds the targets (the follow-up characters)
<- str_sub(text, text_indexes + maxlen, text_indexes + maxlen)
next_chars cat("Number of sequences: ", length(sentences), "\n")
# List of unique characters in the corpus
<- unique(sort(strsplit(text, "")[[1]]))
chars cat("Unique characters:", length(chars), "\n")
# Dictionary mapping unique characters to their index in `chars`
<- 1:length(chars)
char_indices names(char_indices) <- chars
# Next, one-hot encode the characters into binary arrays.
cat("Vectorization...\n")
<- array(0L, dim = c(length(sentences), maxlen, length(chars)))
x <- array(0L, dim = c(length(sentences), length(chars)))
y for (i in 1:length(sentences)) {
<- strsplit(sentences[[i]], "")[[1]]
sentence for (t in 1:length(sentence)) {
<- sentence[[t]]
char <- 1
x[i, t, char_indices[[char]]]
}<- next_chars[[i]]
next_char <- 1
y[i, char_indices[[next_char]]] }
categorical_crossentropy
as the loss to train the model.<- keras_model_sequential() %>%
model layer_lstm(units = 128, input_shape = c(maxlen, length(chars))) %>%
layer_dense(units = length(chars), activation = "softmax")
<- optimizer_rmsprop(lr = 0.01)
optimizer %>% compile(
model loss = "categorical_crossentropy",
optimizer = optimizer
)
sample_next_char()
function re-weights the original probability distribution from the current model and draws a new character index.<- function(preds, temperature = 1.0) {
sample_next_char <- as.numeric(preds)
preds <- log(preds) / temperature
preds <- exp(preds)
exp_preds <- exp_preds / sum(exp_preds)
preds which.max(t(rmultinom(1, 1, preds)))
}
for (epoch in 1:50) {
cat("Epoch", epoch, "\n")
# Fit the LSTM model for 1 epoch on the available training data
%>% fit(x, y, batch_size = 64, epochs = 1)
model
# Select a random seed
<- sample(1:(nchar(text) - maxlen - 1), 1)
start_index <- str_sub(text, start_index, start_index + maxlen - 1)
seed_text
cat("--- Synth-Text Generation with seed:", seed_text, "\n\n")
for (temperature in c(0.2, 0.5, 1.0, 1.2)) {
cat("----------- temperature:", temperature, "\n")
cat(seed_text, "\n")
<- seed_text
generated_text
# Generate text with 500 characters
for (i in 1:500) {
<- array(0, dim = c(1, maxlen, length(chars)))
sampled <- strsplit(generated_text, "")[[1]]
generated_chars for (t in 1:length(generated_chars)) {
<- generated_chars[[t]]
char 1, t, char_indices[[char]]] <- 1
sampled[
}
<- model %>% predict(sampled, verbose = 0)
preds <- sample_next_char(preds[1,], temperature)
next_index <- chars[[next_index]]
next_char
<- paste0(generated_text, next_char)
generated_text <- substring(generated_text, 2)
generated_text
cat(next_char)
}cat("\n")
} }
temperature=0.2
, temperature=0.5
, and temperature=1.0
. Inspect the corresponding model convergence. Lower temperature results may yield extremely repetitive and predictable text with realistic English-language structure. Higher temperatures may yield more elaborate synthetic text, however, new plausible words/phrases/tokens may sometimes be invented and break the local sentence structure or include semi-random strings of characters. Experiment with multiple sampling strategies to identify optimal balance between learned structure and randomness.Training larger models is expected to be more computationally intensive and yield more realistic textual output.