Es completamente posible usar un CNN para hacer predicciones de series de tiempo, ya sea regresión o clasificación. Las CNN son buenas para encontrar patrones locales y, de hecho, las CNN funcionan con el supuesto de que los patrones locales son relevantes en todas partes. También la convolución es una operación bien conocida en series de tiempo y procesamiento de señales. Otra ventaja sobre los RNN es que pueden ser muy rápidos de calcular ya que pueden ser paralelos en oposición a la naturaleza secuencial de RNN.
En el siguiente código, demostraré un estudio de caso en el que es posible predecir la demanda de electricidad en R utilizando keras. Tenga en cuenta que este no es un problema de clasificación (no tenía un ejemplo a mano) pero no es difícil modificar el código para manejar un problema de clasificación (use una salida softmax en lugar de una salida lineal y una pérdida de entropía cruzada).
El conjunto de datos está disponible en la biblioteca fpp2:
library(fpp2)
library(keras)
data("elecdemand")
elec <- as.data.frame(elecdemand)
dm <- as.matrix(elec[, c("WorkDay", "Temperature", "Demand")])
A continuación creamos un generador de datos. Esto se utiliza para crear lotes de datos de capacitación y validación que se utilizarán durante el proceso de capacitación. Tenga en cuenta que este código es una versión más simple de un generador de datos que se encuentra en el libro "Aprendizaje profundo con R" (y la versión en video del mismo "Aprendizaje profundo con R en movimiento") de publicaciones de personal.
data_gen <- function(dm, batch_size, ycol, lookback, lookahead) {
num_rows <- nrow(dm) - lookback - lookahead
num_batches <- ceiling(num_rows/batch_size)
last_batch_size <- if (num_rows %% batch_size == 0) batch_size else num_rows %% batch_size
i <- 1
start_idx <- 1
return(function(){
running_batch_size <<- if (i == num_batches) last_batch_size else batch_size
end_idx <- start_idx + running_batch_size - 1
start_indices <- start_idx:end_idx
X_batch <- array(0, dim = c(running_batch_size,
lookback,
ncol(dm)))
y_batch <- array(0, dim = c(running_batch_size,
length(ycol)))
for (j in 1:running_batch_size){
row_indices <- start_indices[j]:(start_indices[j]+lookback-1)
X_batch[j,,] <- dm[row_indices,]
y_batch[j,] <- dm[start_indices[j]+lookback-1+lookahead, ycol]
}
i <<- i+1
start_idx <<- end_idx+1
if (i > num_batches){
i <<- 1
start_idx <<- 1
}
list(X_batch, y_batch)
})
}
A continuación, especificamos algunos parámetros para pasar a nuestros generadores de datos (creamos dos generadores, uno para capacitación y otro para validación).
lookback <- 72
lookahead <- 1
batch_size <- 168
ycol <- 3
El parámetro de retrospectiva es qué tan lejos en el pasado queremos mirar y qué tan lejos en el futuro queremos predecir.
Luego dividimos nuestro conjunto de datos y creamos dos generadores:
train_dm <- dm [1: 15000,]
val_dm <- dm[15001:16000,]
test_dm <- dm[16001:nrow(dm),]
train_gen <- data_gen(
train_dm,
batch_size = batch_size,
ycol = ycol,
lookback = lookback,
lookahead = lookahead
)
val_gen <- data_gen(
val_dm,
batch_size = batch_size,
ycol = ycol,
lookback = lookback,
lookahead = lookahead
)
Luego creamos una red neuronal con una capa convolucional y entrenamos el modelo:
model <- keras_model_sequential() %>%
layer_conv_1d(filters=64, kernel_size=4, activation="relu", input_shape=c(lookback, dim(dm)[[-1]])) %>%
layer_max_pooling_1d(pool_size=4) %>%
layer_flatten() %>%
layer_dense(units=lookback * dim(dm)[[-1]], activation="relu") %>%
layer_dropout(rate=0.2) %>%
layer_dense(units=1, activation="linear")
model %>% compile(
optimizer = optimizer_rmsprop(lr=0.001),
loss = "mse",
metric = "mae"
)
val_steps <- 48
history <- model %>% fit_generator(
train_gen,
steps_per_epoch = 50,
epochs = 50,
validation_data = val_gen,
validation_steps = val_steps
)
Finalmente, podemos crear un código para predecir una secuencia de 24 puntos de datos usando un procedimiento simple, explicado en los comentarios de R.
####### How to create predictions ####################
#We will create a predict_forecast function that will do the following:
#The function will be given a dataset that will contain weather forecast values and Demand values for the lookback duration. The rest of the MW values will be non-available and
#will be "filled-in" by the deep network (predicted). We will do this with the test_dm dataset.
horizon <- 24
#Store all target values in a vector
goal_predictions <- test_dm[1:(lookback+horizon),ycol]
#get a copy of the dm_test
test_set <- test_dm[1:(lookback+horizon),]
#Set all the Demand values, except the lookback values, in the test set to be equal to NA.
test_set[(lookback+1):nrow(test_set), ycol] <- NA
predict_forecast <- function(model, test_data, ycol, lookback, horizon) {
i <-1
for (i in 1:horizon){
start_idx <- i
end_idx <- start_idx + lookback - 1
predict_idx <- end_idx + 1
input_batch <- test_data[start_idx:end_idx,]
input_batch <- input_batch %>% array_reshape(dim = c(1, dim(input_batch)))
prediction <- model %>% predict_on_batch(input_batch)
test_data[predict_idx, ycol] <- prediction
}
test_data[(lookback+1):(lookback+horizon), ycol]
}
preds <- predict_forecast(model, test_set, ycol, lookback, horizon)
targets <- goal_predictions[(lookback+1):(lookback+horizon)]
pred_df <- data.frame(x = 1:horizon, y = targets, y_hat = preds)
y voilá:
No está mal.