Обучение полносвязной многослойной нейросети на исправленных данных
В данном примере будет рассмотрена обработка данных и обучение на их основе нейросетевой модели. Будет продемонстрирован метод скользящего окна - для разделения обучающей и тестовой выборки на наборы данных для обучения, а также будут определены параметры модели, для получения наиболее точных прогнозных значений.
Запуск необходимых библиотек:
Pkg.add(["Statistics", "CSV", "Flux", "Optimisers"])
using Statistics
using CSV
using DataFrames
using Flux
using Plots
using Flux: train!
using Optimisers
Подготовка обучающей и тестовой выборки:
Загрузка данных для обучения модели:
df = DataFrame(CSV.File("$(@__DIR__)/data.csv"));
Данные были сохранены после выполнения примера /start/examples/data_analysis/data_processing.ipynb.
Формирование обучающего набора данных:
Весь датасет был разделён на обучающую и тестовую выборку. Обучающая выборка составила 0,8 от всего датасета, а тестовая 0,2.
T = df[1:1460,3]; # определение обучающего набора данных, весь датасет 1825 строк
first(df, 5)
Разделение вектора T на батчи длиной в 100 наблюдений:
batch_starts = 1:1:1360 # определение диапазона для цикла
weather_batches = [] # определение пустого массива для записи результатов выполнения цикла
for start in batch_starts
dop = T[start:start+99] # батч на текущем временном шаге
weather_batches = vcat(weather_batches, dop) # запись батча в массив
end
Батч - небольшой набор данных, который может служить обучающим множеством для построения модели прогнозирования. Взят из первоначального обучающего набора T с помощью метода скользящего окна.
Метод скользящего окна:

где x - наблюдение, а y1 - прогнозное значение.
Преобразование полученного набора в вектор-строку:
weather_batches = weather_batches'
Изменение формы массива для соответствия длине батча, указанной выше:
weather_batches = reshape(weather_batches, (100,:))
X = weather_batches # переприсвоение
Определение массива целевых значений:
Y = (T[101:1460]) # отсчёт начинается с 101, так как предыдущие 100 наблюдений используются в качестве исходных данных
Y = Y'
Преобразование в формат приемлемый для обработки нейросетью:
X = convert(Array{Float32}, X)
Y = convert(Array{Float32}, Y)
Формирование тестового набора данных:
Разделение тестовой выборки на батчи длиной 100 наблюдений:
X_test = df[1461:1820, 3] # определение тестового набора данных
batch_starts_test = 1:1:261 # определение диапазона для цикла
test_batches = [] # определение пустого массива для записи результатов выполнения цикла
for start in batch_starts_test
dop = X_test[start:start+99] # батч на текущем временном шаге
test_batches = vcat(test_batches, dop) # запись батча в массив
end
test_batches = reshape(test_batches, (100,:)) # изменение формы массива для соответствия длине батча, указанной выше:
X_test = convert(Array{Float32}, test_batches) # преобразование в формат приемлимый для обработки нейросетью
Построение и обучение нейросети:
Определение архитектуры нейросети:
model = Flux.Chain(
Dense(100 => 50, elu),
Dense(50 => 25, elu),
Dense(25 => 5, elu),
Dense(5 => 1)
)
Определение параметров обучения:
# Инициализация оптимизатора
learning_rate = 0.001f0
opt = Optimisers.Adam(learning_rate)
state = Optimisers.setup(opt, model) # Создание начального состояния
# Функция потерь
loss(model, x, y) = Flux.mse(model(x), y)
Обучение модели:
loss_history = []
epochs = 200
for epoch in 1:epochs
# Вычисление градиентов
grads = gradient(model) do m
loss(m, X, Y)
end
# Обновление модели и состояния
state, model = Optimisers.update(state, model, grads[1])
# Расчет и сохранение потерь
current_loss = loss(model, X, Y)
push!(loss_history, current_loss)
# Вывод потерь на каждом шаге
if epoch == 1 || epoch % 10 == 0
println("Epoch $epoch: Loss = $current_loss")
end
end
Визуализация изменения функции потерь:
plot((1:epochs), loss_history, title="Изменение функции потерь", xlabel="Эпоха", ylabel="Функция потерь")
Получение прогнозных значений:
y_hat_raw = model(X_test) # загрузка тестовой выборки в модель, получение прогноза
y_pred = y_hat_raw'
y_pred = y_pred[:,1]
y_pred = convert(Vector{Float64}, y_pred)
first(y_pred, 5)
Визуализация предсказанных значений:
days = df[:,1] # формирование массива дней, начиная с первого наблюдения
first(days, 5)
Подключение бэкэнда - метода отображения графики:
plotlyjs()
Формирование набора данных из начального датасета для сравнения:
df_T = df[:, 3]#df[1471:1820, 3]
first(df_T, 5)
Построение графика зависимости температуры от времени по исходным и спрогнозированным данным:
plot(days, df_T)#plot(days, T[11:end]) #T[11:end]
plot!(days[1560:1820], y_pred)
Так как исходный датасет имеет участки, в которых пропущенные значения были заменены линейной интерполяцией, то сложно оценить работу обученной нейросетевой модели на прямой.
Для этого были загружены реальные данные без пропусков:
real_data = DataFrame(CSV.File("$(@__DIR__)/real_data.csv"));
Построение графика зависимости температуры от времени по реальным и спрогнозированным данным:
plot(real_data[1:261,2])
plot!(y_pred)
Проверим взаимосвязь полученных величин с помощью корреляции Пирсона, таким образом оценив точность полученной модели:
corr_T = cor(y_pred,real_data[1:261,2])
Коэффициент корреляции Пирсона может принимать значения от -1 до 1, где 0 будет означать отсутствие связи между переменными, а -1 и 1 - тесную связь (обратная и прямая зависимость соответственно).
Выводы:
В данном примере были предобработаны данные температурных наблюдений за последние пять лет, а также определена архитектура нейросети, параметры оптимизатора и функция потерь.
Модель была обучена и показала достаточно высокую, но не идеальную сходимость предсказанных значений с реальными данными. Для улучшения качества прогноза нейросеть может быть модифицирована путём изменения архитектуры слоёв и увеличения обучающей выборки.