物理模型中的代理神经网络
让我们来看看如何创建一个神经网络来替换物理模型,这将允许我们简化计算,实现对控制器的可移植性或隐藏实现细节。
因此,我们可以使用掩码单元格轻松更改神经网络的参数,并根据此时间表检查预测的质量。:
模型转换
为什么需要将模型转换为神经网络? 可能有很多情况下这很有趣。:
*将模型转换为控制器的代码,
*加快模型的执行以循环运行,
*隐藏模型实现的细节,只留下计算。
神经网络是一种经过充分研究和简单的模型分配形式,特别是完全连接的模型,我们将在本项目中使用它。
我们将安装和下载几个库,以及打开一个模型,我们将转换为神经网络。
在这个例子中,我们从[燃料电池系统]项目中获得了一个模型(https://engee.com/community/ru/catalogs/projects/sistema-toplivnykh-elementov )作者shestakoviktor.
Pkg.add(["ChainPlots", "Flux"])
using DataFrames, CSV, Flux, Random, Statistics, ChainPlots
using Flux: mse
gr();
engee.open( "$(@__DIR__)/" * "FuelCell.engee");
我们的方法将允许我们分析通过以下方式获得的结果 engee.run() 于不同的输入条件。 我们将通过使用以下方法更改不同块的参数来设置条件 engee.set_param!().
我们将无法更改循环中的全局变量,只能更改块属性。 Julia不允许在局部过程中操作全局变量。 更改块的属性会减少我们在执行之前忘记使用全局计算进行一些计算的机会。
定义接口
我们的模型将有4个输出变量(4**神经网络术语中的目标):
out_vars = ["功率千瓦", "Voltage Sensor.V", "的燃料电池。thermal_port。T", "燃料电池。i_FC"]
所有这些结构都可以以不同的方式设置,例如,通过CSV表设置输入变量(两个**特征)。:
csv_text = """
block, param, units, delta, lower, upper
FuelCell/氢气压力(bar),值,,2.0,2.0,20.0
燃料箱/燃料供应,斜坡,,10/60,50/60,150/60
"""
adj_vars = CSV.read(IOBuffer(csv_text), DataFrame)
另一种选择是创建参数字典并将它们组合到数据框中。:
v1 = Dict(:block=>"燃料箱/氢气压力(bar)",:param=>"Value", :units=>"", :delta=>2.0, :lower=>2.0, :upper=>20.0)
v2 = Dict(:block=>"燃料箱/燃料供应",:param=>"slope", :units=>"", :delta=>10/60, :lower=>50/60, :upper=>150/60)
adj_vars = vcat(DataFrame.([v1, v2])...)
因此,我们已经设置了神经网络的输入和输出数量,我们将把物理模型变成它。:
n_features, n_targets= nrow(adj_vars), length(out_vars)
实验规划
让我们准备桌子 result_df. 此表中的列数等于具有不同输入参数组合的原始模型的运行次数。 它们的范围在输入变量的设置(下限)中设置 lower,上限 upper 而步骤 delta).
ranges = [collect(row.lower:row.delta:row.upper) for row in eachrow(adj_vars)]
combinations = collect(Iterators.product(ranges...))
# 包含adj_vars所有范围的值的组合的表
result_df = DataFrame()
for (i, row) in enumerate(eachrow(adj_vars))
col_name = Symbol("$(row.block)/$(row.param)")
result_df[!, col_name] = [comb[i] for comb in combinations[:]]
end
println("模型启动次数: ", nrow(result_df))
first( result_df, 4 )
启动模型
让我们浏览实验表的所有行,并用输出变量的值填充它。
作为输出变量,我们将取我们感兴趣的每个信号的最终值。
收集数据时,最好注释掉此单元格。
# 为输出变量创建新列
for var in out_vars result_df[!, Symbol(var)] = fill(Float64(NaN), nrow(result_df)); end
# 我们设置参数并运行模型
for row in eachrow(result_df)
for (i, col) in enumerate(names(result_df)[1:end-length(out_vars)])
block, param, unit = adj_vars.block[i], adj_vars.param[i], adj_vars.units[i]
unit == "" ? engee.set_param!(block, param => string(row[col])) : engee.set_param!(block, param => Dict("value" => string(row[col]), "unit" => String(unit)));
end
data = engee.run()
for var in out_vars row[Symbol(var)] = length(data[var].value) > 1 ? data[var].value[end] : NaN; end
end
# 我们将保存结果以供进一步处理。
CSV.write("$(@__DIR__)/data/outputfile.csv", result_df)
first( result_df, 4 )
我们可以看到一些发射失败了。 在这些地方,输出参数是NaN。 原因是可以改进的输入参数值或求解器设置不兼容。
以下是我们将使用神经网络重现的图表。:
result_df = DataFrame(CSV.File("$(@__DIR__)/data/outputfile.csv"))
include("$(@__DIR__)/scripts/create_plots.jl");
in_vars = names(result_df)[1:end-length(out_vars)]
p = [create_plots(result_df, :Blues, out_var, in_vars, result_df) for out_var in out_vars]
plot(p..., legend=false, size=(1500,500), titlefont=font(9), guidefont=font(7))
神经网络和训练的数据准备
由您来选择神经网络的拓扑结构和每层神经元的数量。 每个实验都需要自己的参数平衡-神经网络的能力,训练的准确性和持续时间,图形形式允许您用最少的时间对它们进行排序。
include("$(@__DIR__)/scripts/prepare_data.jl");
include("$(@__DIR__)/scripts/plot_predictions.jl"); # -如果我们想要预测和训练数据的比较图
include("$(@__DIR__)/scripts/plot_prediction_errors.jl"); # -如果我们想看到错误
l1_neurons = 16 # @param {type:"slider",min:1,max:30,step:1}
l2_neurons = 15 # @param {type:"slider",min:1,max:30,step:1}
l3_neurons = 14 # @param {type:"slider",min:1,max:30,step:1}
l4_neurons = 10 # @param {type:"slider",min:1,max:30,step:1}
l5_neurons = 10 # @param {type:"slider",min:1,max:30,step:1}
n_epochs = 5000 # @param {type:"slider",min:1,max:5000,step:1}
learning_rate_base = 1 # @param {type:"slider",min:1,max:9,step:1}
learning_rate_exp = -2 # @param {type:"slider",min:-6,max:1,step:1}
models_list = []
loss_list = []
loss_plot_list = []
for i = 1:5
include("$(@__DIR__)/scripts/prepare_and_train_net.jl")
append!(models_list, [model])
append!(loss_plot_list, [train_losses])
append!(loss_list, loss(model, X_train_norm, y_train_norm))
end
min_id = findmin(loss_list)[2]
train_losses = loss_plot_list[min_id]
model = models_list[min_id]
best_loss = loss_list[min_id]
# 图1:学习曲线
p_loss = plot(1:n_epochs, train_losses, label="Train Loss", lw=2, marker=:circle, markersize=2)
title!(p_loss, "学习动态(MSE)")
xlabel!(p_loss, "时代")
ylabel!(p_loss, "MSE")
println("接收错误值(MSE): ", best_loss)
# 所有目标变量的曲面图
surface_plots = [plot_prediction_errors(model, X_train_norm, y_train_norm,
X_train_mean, X_train_std, y_train_mean, y_train_std, i, adj_vars,
title=out_vars[i])
for i in 1:n_targets]
# 一起显示所有图形
l = @layout [a{0.3w} b{0.7w}]
plot(plot(p_loss, plot(surface_plots...),layout=l), plot(model), layout=(2,1), size=(1200,900))
让我们检查预测和初始数据是如何组合的。:
plot([plot_predictions(model, X_train_norm, y_train_norm, X_train_mean, X_train_std, y_train_mean, y_train_std, i, title=out_vars[i]) for i in 1:n_targets]...)
学习过程须知
***训练进行多次,并选择最佳模型。**每次训练运行都会为我们返回一个新的神经网络,我们需要消除对初始参数的依赖。
没有划分为训练和测试子集。**如果表中有很多训练点,最好实施这种分离,而不是冒险过度拟合。
神经网络的数据归一化。 训练过程使用非常不同尺度的数据(伏特,公里,。..),并且为了不损害预测的质量,将数据减少到单个正态分布。
***绘制图形需要最多的时间。**否则,没有必要担心学习率,这是非常高的。
*参数的任何变化都可能提高神经网络近似的质量,但增加容量将使再训练更接近。
结论
我们提出了一种基于物理模型的输入和输出创建神经网络的方法。 使用它,您可以将任何模型打包到神经网络中,并将其用于预测。
.png)
.png)
.png)