使用多层神经网络进行回归¶
在这项工作中,我们需要训练一个神经网络,通过两个参数来预测一个连续函数的输出,并将其作为另一个块放置在Engee画布上。例如,作为替代模型来取代某些复杂的子系统。
数据描述¶
我们的数据由$y = f(x_1, x_2)$ 形式的流程生成:
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
结论¶
我们训练了一个由三个全连接层组成的多层神经网络。然后,我们将其所有系数转移到画布上,以便在方便的面向模型的环境中使用(比通过代码处理神经网络更方便)。
我们很容易用任何表格数据重复这项工作,并根据项目需要调整神经网络的规模。将训练好的网络保存到文件中,就可以与恩吉图的其他元素一起运行模型,而无需重新训练神经网络。
如果需要,可以将权重和偏移写入画布上的Constant
块,以生成独立于model.jld2
文件的神经网络块。