Если нас поразит столкновение, приведшее к вымиранию человечества, оно, скорее всего, произойдет весной или осенью.
Введение
Если бы вы спросили 100 человек, что, по их мнению, является самым большим риском для человеческой цивилизации, я бы поспорил, что тремя главными ответами были бы ядерная война, глобальная пандемия и глобальное потепление/изменение климата. Однако менее 10 лет назад метеор диаметром около 20 метров и массой 10 000 тонн взорвался в 30 км над городом Челябинск в России. Хотя погибших не было, по оценкам, в результате взрыва был нанесен ущерб на сумму 30 миллионов долларов и ранено 1500 человек. Около 100 лет назад, в 1908 году, над Сибирью взорвался метеор размером 50–60 метров мощностью 12 мегатонн, уничтоживший около 2200 квадратных километров леса. 66 миллионов лет назад астероид диаметром 12 км врезался в Землю со скоростью 43 000 км/ч, оставив кратер диаметром 200 км и убив по меньшей мере 80% всего живого на Земле. Возможно, это не так очевидно, как ядерная война или глобальная пандемия, учитывая текущую ситуацию на Земле, но я считаю, что разрушительное столкновение с цивилизацией продолжает представлять серьезную угрозу для выживания человечества в долгосрочной перспективе.
В этом анализе я буду использовать модели машинного обучения (ML) ETS, ARIMA и LSTM, чтобы попытаться понять тенденции сезонности и предсказать близкое сближение с Землей околоземных объектов (NEO).
Данные
Данные об ОСЗ были получены из Центра изучения околоземных объектов Лаборатории реактивного движения НАСА: https://cneos.jpl.nasa.gov/ca/. Данные были собраны с 1 января 2010 г. по 31 декабря 2021 г. и учитывали только ОСЗ с расстоянием между их центром и центром Земли ≤ 0,05 астрономических единиц (7 479 894 км).
Подготовка данных и исследование
# Load Libraries # library(forecast) library(dplyr) library(quantmod) library(tidyverse) library(glue) library(forcats) library(timetk) library(tidyquant) library(tibbletime) library(cowplot) library(recipes) library(rsample) library(yardstick) library(keras) # Read and prep Data # data <- read.csv("...NEO.csv") tail(data) dim(data)
# Convert Date column to date type # df <- mutate(data, Date= as.Date(Date, format= "%Y-%m-%d")) # Visualize # ggplot(data=df, aes(x=Date, y=Count, group=1)) + geom_line()+ geom_point()
Мы установим набор данных для обучения с 1 января 2010 г. по 31 декабря 2020 г. и набор данных для тестирования с 1 января 2021 г. по 31 декабря 2021 г.:
# Set up Training and Test Sets # train <- subset(df, Date < "2021-01-01") # 2010 to 2020 data train_count <- train$Count train.ts <- ts(train_count, frequency=12, start=c(2010,1)) test <- subset(df, Date >= "2021-01-01") # 2021 data test_count <- test$Count test.ts <- ts(test_count, frequency=12, start=c(2021,1)) seasonplot(train.ts)
СТВ
Модели ETS обозначают:
- Ошибкаошибка: может быть Aаддитивной или Ммультипликативной
- T rend: может быть Nодин, Aдобавочный или Mмногократный
- S easonality: может быть None, Additiv или Mmultiplicative.
Так, например, модель с «AAA» означает модель с аддитивной ошибкой, аддитивной тенденцией и аддитивной сезонностью. В этом примере я установлю модель «ZZZ», которая выберет для меня оптимальную модель.
# ETS # ets_fit <- ets(train.ts, model="ZZZ") autoplot(ets_fit)
Модель ETS для данных NEO — «MAM», что означает наличие Mмножественной Eошибки, Aддитивной T ренд и Mмногократная Sсезонность. Насколько хорошо эта модель работала на тестовых данных?:
ets_future <- as.data.frame(forecast(ets_fit, h=12)) df_ets <- cbind(test, ets_future) MAPE <- mean(abs(df_ets$Count - df_ets$`Point Forecast`)/df_ets$Count) MAE <- mean(abs(df_ets$Count - df_ets$`Point Forecast`)) MSE<-mean((df_ets$Count - df_ets$`Point Forecast`)^2) RMSE<-sqrt(MSE)
Модель ETS была чрезвычайно точной при прогнозировании ОСЗ на данных испытаний, отличаясь только средним абсолютным средним значением 17 (средняя абсолютная ошибка в процентах 12%).
АРИМА
ARIMA означает авторегрессивные интегрированные скользящие средние. Подобно моделям LSTM, которые мы обсудим ниже, модели ARIMA работают на основе автокорреляций, обнаруженных в данных. Другими словами, они используют корреляцию между значениями, которые предшествуют и следуют за ними, чтобы прогнозировать будущее.
# ARIMA # arima_fit <- auto.arima(train.ts) summary(arima_fit)
Модель ARIMA, сгенерированная из данных NEO, представляет собой ARIMA(0, 1, 1)(0, 1, 1), которая является наиболее часто используемой сезонной моделью ARIMA. Я не буду вдаваться во все подробности, но хотел показать уравнение:
Ŷₜ = Yₜ-₁₂ + Yₜ-₁ — Yₜ-₁₃ — (-0.8794)eₜ-₁ — (-0.5597)eₜ-₁₂ + ((-0.8794)(-0.5597)eₜ-₁₃)
На английском языке это уравнение говорит, что прогнозируемое количество ОСЗ будет зависеть от количества ОСЗ за 12 месяцев до этого, плюс число в предыдущем месяце минус число 13 месяцев назад, а затем добавляется кратность ошибок при лагах 1, 12 и 13. Стоит отметить, что коэффициент ошибки lag-13 является произведением коэффициентов MA(1) и SMA(1), показанных в сводке выше. Теперь, когда мы понимаем наше уравнение, насколько хорошо работает эта модель?:
arima_future <- as.data.frame(forecast(arima_fit, h=12)) df_arima <- cbind(test, arima_future) MAPE <- mean(abs(df_ets$Count - df_arima$`Point Forecast`)/df_ets$Count) MAE <- mean(abs(df_ets$Count - df_arima$`Point Forecast`)) MSE<-mean((df_ets$Count - df_arima$`Point Forecast`)^2) RMSE<-sqrt(MSE)
Хотя модель ARIMA была очень точной, отставая от среднего абсолютного среднего только на 20 (средняя абсолютная ошибка в процентах — 18%), модель ETS по-прежнему работала лучше.
ЛСТМ
Я не буду слишком углубляться в LSTM, но я написал статью, в которой содержится более подробная информация, которую вы можете прочитать здесь: https://medium.datadriveninvestor.com/utilizing-long-short-term-memory- сети, чтобы спрогнозировать цену биткойна в r-8a77a6512ddc
Наш первый шаг с LSTM — проверка автокорреляции:
# LSTM # tidy_acf <- function(df, Count, lags = 0:20) { value_expr <- enquo(Count) acf_values <- df %>% pull(Count) %>% acf(lag.max = tail(lags, 1), plot = FALSE) %>% .$acf %>% .[,,1] ret <- tibble(acf = acf_values) %>% rowid_to_column(var = "lag") %>% mutate(lag = lag - 1) %>% filter(lag %in% lags) na.action = na.omit return(ret) } max_lag <- 150 # Find NEO's ACF Values # df %>% tidy_acf(Count, lags = 0:max_lag) df %>% tidy_acf(Count, lags = 0:max_lag) %>% ggplot(aes(lag, acf)) + geom_segment(aes(xend = lag, yend = 0), color = palette_light()[[1]]) + geom_vline(xintercept = 120, size = 3, color = palette_light()[[2]]) + annotate("text", label = "10 Year Mark", x = 75, y = 0.8, color = palette_light()[[2]], size = 6, hjust = 0) + theme_tq() + labs(title = "Near Earth Objects")
# Train and Test Data # periods_train <- 132 periods_test <- 12 rolling_origin_resamples <- rolling_origin( df, initial = periods_train, assess = periods_test, cumulative = FALSE, skip = FALSE ) rolling_origin_resamples # Create a function that plots the Split # plot_split <- function(split, expand_y_axis = TRUE, alpha = 1, size = 1, base_size = 14) { # Manipulate data train_tbl <- training(split) %>% add_column(key = "training") test_tbl <- testing(split) %>% add_column(key = "testing") data_manipulated <- bind_rows(train_tbl, test_tbl) %>% as_tbl_time(index = Date) %>% mutate(key = fct_relevel(key, "training", "testing")) # Collect attributes train_time_summary <- train_tbl %>% tk_index() %>% tk_get_timeseries_summary() test_time_summary <- test_tbl %>% tk_index() %>% tk_get_timeseries_summary() # Visualize g <- data_manipulated %>% ggplot(aes(x = Date, y = Count, color = key)) + geom_line(size = size, alpha = alpha) + theme_tq(base_size = base_size) + scale_color_tq() + labs( title = glue("Split: {split$id}"), subtitle = glue("{train_time_summary$start} to {test_time_summary$end}"), y = "", x = "" ) + theme(legend.position = "none") if (expand_y_axis) { NEO_time_summary <- df %>% tk_index() %>% tk_get_timeseries_summary() g <- g + scale_x_date(limits = c(NEO_time_summary$start, NEO_time_summary$end)) } return(g) } rolling_origin_resamples$splits[[1]] %>% plot_split(expand_y_axis = TRUE) + theme(legend.position = "bottom") split <- rolling_origin_resamples$splits[[1]] split_id <- rolling_origin_resamples$id[[1]] df_trn <- training(split) df_tst <- testing(split) df <- bind_rows( df_trn %>% add_column(key = "training"), df_tst %>% add_column(key = "testing") ) %>% as_tbl_time(index = Date) df
# Standardize Data # rec_obj <- recipe(Count ~ ., df) %>% step_sqrt(Count) %>% step_center(Count) %>% step_scale(Count) %>% prep() df_processed_tbl <- bake(rec_obj, df) df_processed_tbl center_history <- rec_obj$steps[[2]]$means["Count"] scale_history <- rec_obj$steps[[3]]$sds["Count"] c("center" = center_history, "scale" = scale_history) nrow(df_tst) dim(df_trn)
Теперь, когда у нас настроены и стандартизированы данные для обучения и тестирования, мы можем приступить к созданию нашего LSTM. Сначала мы должны определить оптимальное количество эпох и оптимальное количество используемых узлов. Мы будем использовать двухслойный LSTM, так как этого должно быть достаточно для нашей проблемы регрессии.
# Model inputs lag_setting <- 12 # = nrow(df_tst) batch_size <- 12 train_length <- 132 tsteps <- 1 epochs <- 50 # Training Set lag_train_tbl <- df_processed_tbl %>% mutate(Count_lag = lag(Count, n = lag_setting)) %>% filter(!is.na(Count_lag)) %>% filter(key == "training") %>% tail(train_length) x_train_vec <- lag_train_tbl$Count_lag x_train_arr <- array(data = x_train_vec, dim = c(length(x_train_vec), 1, 1)) y_train_vec <- lag_train_tbl$Count y_train_arr <- array(data = y_train_vec, dim = c(length(y_train_vec), 1)) # Testing Set lag_test_tbl <- df_processed_tbl %>% mutate( Count_lag = lag(Count, n = lag_setting) ) %>% filter(!is.na(Count_lag)) %>% filter(key == "testing") x_test_vec <- lag_test_tbl$Count_lag x_test_arr <- array(data = x_test_vec, dim = c(length(x_test_vec), 1, 1)) y_test_vec <- lag_test_tbl$Count y_test_arr <- array(data = y_test_vec, dim = c(length(y_test_vec), 1)) model <- keras_model_sequential() model %>% layer_lstm(units = 88, input_shape = c(tsteps, 1), batch_size = batch_size, return_sequences = TRUE, stateful = TRUE) %>% layer_lstm(units = 88, return_sequences = FALSE, stateful = TRUE) %>% layer_dense(units = 1) model %>% compile(loss = 'mae', optimizer = 'adam') model for (i in 1:epochs) { model %>% fit(x = x_train_arr, y = y_train_arr, batch_size = batch_size, epochs = 1, verbose = 1, shuffle = FALSE) model %>% reset_states() cat("Epoch: ", i) } # Make Predictions pred_out <- model %>% predict(x_test_arr, batch_size = batch_size) %>% .[,1] # Retransform values pred_tbl <- tibble( Date = lag_test_tbl$Date, Count = (pred_out * scale_history + center_history)^2 ) # Combine actual data with predictions tbl_1 <- df_trn %>% add_column(key = "actual") tbl_2 <- df_tst %>% add_column(key = "actual") tbl_3 <- pred_tbl %>% add_column(key = "predict") # Create time_bind_rows() to solve dplyr issue time_bind_rows <- function(data_1, data_2, Date) { index_expr <- enquo(Date) bind_rows(data_1, data_2) %>% as_tbl_time(index = !! index_expr) } ret <- list(tbl_1, tbl_2, tbl_3) %>% reduce(time_bind_rows, Date = Date) %>% arrange(key, Date) %>% mutate(key = as_factor(key)) ret
Очевидно, что для создания LSTM требуется много работы, но обеспечивают ли они гораздо более точные прогнозы?
# Determining Model Performance # MSE<-mean((tbl_2$Count - tbl_3$Count)^2) RMSE<-sqrt(MSE) MAPE <- mean(abs(tbl_2$Count - tbl_3$Count)/tbl_2$Count) MAE <- mean(abs(tbl_2$Count - tbl_3$Count))
Несмотря на то, что модель LSTM работала довольно хорошо, модель ETS по-прежнему была наиболее точной в предсказании ОСЗ 2021 года.
Предсказание будущего
Поскольку модель ETS была наиболее точной, мы будем использовать ее для прогнозирования ОСЗ в 2022 году:
full_count <- df$Count full.ts <- ts(full_count, frequency=12) ets_full_fit <- ets(full.ts, model="ZZZ") ets_full_future <- as.data.frame(forecast(ets_full_fit, h=12))
Прогноз снова предсказывает большое количество ОСЗ весной и осенью 2022 года, причем наибольшее количество, 203, ожидается в октябре, затем 186 в ноябре и 174 в марте. Модель ETS прогнозирует в общей сложности 1666 ОСЗ в 2022 году, что на 72 больше, чем в 2021 году, и на 222 больше, чем в 2020 году.
Выводы
В этом эксперименте я показал, что методы прогнозирования временных рядов могут использоваться для точного прогнозирования близких сближений ОСЗ с Землей. С помощью автокорреляции я показал, что ОСЗ происходят сезонно на основе 12-месячной модели, которая, конечно же, совпадает с орбитой Земли вокруг Солнца. С помощью графиков исторических данных и прогнозов я продемонстрировал, что ожидается увеличение числа ОСЗ, что является тенденцией, которая наблюдается с 1980-х годов, когда исследователи стали лучше осознавать опасности, которые событие столкновения представляет для людей. Я также показал с помощью графиков исторических данных, графиков сезонности и с помощью прогнозов, что большинство ОСЗ происходят весной и осенью, что указывает на то, что если столкновение действительно произойдет, оно, скорее всего, произойдет в октябре/ноябре, а затем в марте/апреле. Однако я не смог найти никакой информации о том, почему существует тенденция увеличения количества NEO весной и осенью. Наконец, я показал, что глубокое обучение, в данном случае моделирование LSTM, не всегда может давать наилучший прогноз и не всегда может стоить дополнительной работы по реализации.
Запланируйте сеанс DDIChat в Data Science / AI / ML / DL:
Подайте заявку на участие в программе DDIChat Expert здесь.
Работайте с DDI: https://datadriveninvestor.com/collaborate
Подпишитесь на DDIntel здесь.