Многослойная нейросеть для регрессии
Регрессия при помощи многослойной нейросети
В этой работе нам нужно будет обучить нейросеть прогнозировать выход непрерывной функции от двух параметров и поместить ее на холст Engee в качестве еще одного блока. Например, в качестве суррогатной модели на замену некоторой сложной подсистемы.
Описание данных
Наши данные порождаются процессом вида :
Pkg.add(["JLD2", "Flux"])
Nx1, Nx2 = 30, 40
x1 = Float32.( range( -3, 3, length=Nx1 ) )
x2 = Float32.( range( -3, 3, length=Nx2 ) )
Xs = [ repeat( x1, outer=Nx2) repeat( x2, inner=Nx1) ];
# Первый пример выходных данных
Ys = @. 3*(1-Xs[:,1])^2*exp(-(Xs[:,1]^2) - (Xs[:,2]+1)^2) - 10*(Xs[:,1]/5 - Xs[:,1]^3 - Xs[:,2]^5)*exp(-Xs[:,1]^2-Xs[:,2]^2) - 1/3*exp(-(Xs[:,1]+1) ^ 2 - Xs[:,2]^2);
# Второй пример выходных данных
#Ys = @. 1*(1-Xs[:,1])^2*exp(-(Xs[:,1]^2) - (Xs[:,2]+1)^2) - 15*(Xs[:,1]/5 - Xs[:,1]^3 - Xs[:,2]^6)*exp(-Xs[:,1]^2-Xs[:,2]^2) - 1/3*exp(-(Xs[:,1]+1) ^ 2 - Xs[:,2]^2);
Xs – матрица размером 1200x2 (в выборке 1200 примеров, каждый характеризуется двумя признаками: x1 и x2)
Ys – матрица размером 1200х1 (столбец прогнозов)
Процесс обучения
Зададимся параметрами для процедуры обучения и запустим достаточно простой вариант цикла.
# @markdown ## Настройка параметров нейросети
# @markdown *(двойной клик позволяет скрыть код)*
# @markdown
Параметры_по_умолчанию = false #@param {type: "boolean"}
if Параметры_по_умолчанию
Коэффициент_скорости_обучения = 0.01
Количество_циклов_обучения = 2000
else
Количество_циклов_обучения = 2001 # @param {type:"slider", min:1, max:2001, step:10}
Коэффициент_скорости_обучения = 0.08 # @param {type:"slider", min:0.001, max:0.5, step:0.001}
end
epochs = Количество_циклов_обучения;
learning_rate = Коэффициент_скорости_обучения;
Создадим нейросеть с двумя входами, двумя промежуточными состояниями (размером 20 и 5) и с одним выходом. Важно, чтобы выход нейросети имел линейную функцию активации.
using Flux
model = Chain( Dense( 2 => 20, relu ), # Структура модели, которую мы будем обучать
Dense( 20 => 5, relu ),
Dense( 5 => 1 ) )
data = [ (Xs', Ys') ] # Зададим структуру данных
loss( ỹ, y ) = Flux.mse( ỹ, y ) # и функцию потерь, в которую они будут передаваться процедурой обучения
loss_history = [] # Будем сохранять историю обучения
opt_state = Flux.setup( Adam( learning_rate ), model ); # Алгоритм оптимизации
for i in 1:epochs
Flux.train!( model, data, opt_state) do m, x, y
loss( m(x), y ) # Функция потерь - ошибка на каждом элементе датасета
end
push!( loss_history, loss( model(Xs'), Ys' ) ) # Запомним значение функции потерь
end
plot( loss_history, size=(300,200), label="loss" ) # Выведем хронику обучения
Переносим нейросеть на холст Engee
В переменной model хранится только что обученная нейросеть.
Мы воссоздали ее структуру при помощи элементов на холсте:
☝️ Нашей модели требуется переменная
model, откуда она получает параметры. Если такой переменной нет, модель попробует найти файлmodel.jld2в домашнем каталоге. Если же этого файла не обнаружится, модель создаст пустую нейросеть со случайными параметрами
Полносвязанные слои собраны из самых обычных блоков Product и Add, осуществляющих перемножение входов на матрицу и добавление вектора смещений.
Функции активации, для большей ясности, не спрятаны внутрь слоев, а вынесены наружу.
Запустим эту модель на наборе из нескольких сотен случайных точек.
if "neural_regression_multilayer" ∉ getfield.(engee.get_all_models(), :name)
engee.load( "$(@__DIR__)/neural_regression_multilayer.engee");
end
model_data = engee.run( "neural_regression_multilayer" );
Модель возвращает нам матрицу из матриц. Можно исправить это при помощи блоков холсте, а можно реализовать свой вариант функции flatten:
model_x1 = model_data["X1"].value;
model_x2 = model_data["X2"].value;
model_y = vec( hcat( model_data["Y"].value... ));
Выводим результаты
gr()
plot(
surface( Xs[:,1], Xs[:,2], vec(Ys), c=:viridis, cbar=:false, title="Обучающая выборка", titlefont=font(10)),
wireframe( x1, x2, vec(model( Xs' )), title="Прогноз от нейросети (через скрипт)", titlefont=font(10) ),
scatter( model_x1, model_x2, model_y, ms=2.5, msw=.5, leg=false, zcolor=model_y,
xlimits=(-3,3), ylimits=(-3,3), title="Прогноз от модели на холсте", titlefont=font(10) ),
layout=(1,3), size=(1000,400)
)
Сохраняем модель
Наконец, сохраним обученную нейросеть в файл, если этот файл отсутствует.
if !isfile( "model.jld2" )
using JLD2
jldsave("model.jld2"; model)
end
Заключение
Мы обучили многослойную нейросеть, состоящую из трех полносвязанных слоев. Затем перенесли все ее коэффициенты на холст для использования удобном модельно-ориентированном контексте (более удобном, чем работа с нейросетями через код).
Эту работу легко повторить с любыми табличными данными и масштабировать нейросеть под нужды проекта. Сохранение обученной сети в файл позволяет выполнять модель вместе с другими элементами диаграммы Engee, не обучая нейросеть заново.
При желании, веса и смещения можно записать в блоки Constant на холсте и получить блок с нейросетью, независимый от наличия файла model.jld2.