Документация Engee
Notebook

Поместим нейросеть в блок Engee Function

В этом примере мы разместим обученную нейросеть на холсте Engee в качестве блока Engee Function – сгенерируем из нее код на Julia.

Описание задачи

Ранее мы обучили полносвязанную нейросеть прогнозировать величину $y$ на основе двух входных переменных: $x_1$ и $x_2$. У нас уже есть файл model.jld2, где лежит обученная нейросеть. Теперь мы переведем ее в код на Julia и создадим блок, который можно будет использовать в любой модели или сделать из таких блоков собственную библиотеку.

⚠️ Внутри блока Engee Function можно использовать только глобальную адресацию. Поэтому если у вас пример расположен в папке, отличной от /user/start/data_analysis/neural_regression_to_engee_function, нужно отредактировать код внутри блока Engee Function, прописав полный путь к файлу neural_net_code.jl.

Создание маски блока

Удобно было бы разместить на лицевой стороне блока некоторый рисунок, который возможно упростит восприятие модели. В качестве маски используем топологию нашей нейросети.

image.png

Нам понадобится библиотека ChainPlots, которая создает "рецепт" визуализации нейросети для функции plot(), после чего мы просто можем передать нейросеть в качестве аргумента функции вывода графиков.

In [ ]:
using Flux, JLD2, ChainPlots

model = load_object( "$(@__DIR__)/model.jld2" ) # Загрузим нейросеть из файла

gr()
p = plot( model, titlefontsize=10, size=(300,300),
    xticks=:none, series_annotations="", markersize=8,
    markercolor="white", markerstrokewidth=4, linewidth=1 )

savefig( p, "$(@__DIR__)/neural_net_block_mask.png"); # Сохраним график в PNG

☝️ Ссылка на картинку уже прописана в маске блока. Но маска не обновится автоматически. Чтобы иллюстрация загрузилась на лицевую сторону блока, откройте его свойства (в режиме просмотра маски) и нажмите "Обновить маску".

Создание Julia кода для нейросети

Создать код нам поможет одна особенность языка Julia.

Поскольку библиотека Flux целиком написана на Julia, каждый слой нейросети описан Julia-кодом. Что будет, если на вход подать не числа, а математические символы из библиотеки Symbolics?

In [ ]:
using Symbolics
@variables x1 x2
s = model( [x1, x2] );

# Первые 200 символов этого кода
print( string(s[1])[1:200], "..." )
2.474068ifelse((0.98925877 + 0.38023216ifelse((1.1855042 + 0.8189067x1 - 0.93786836x2) < 0, 0, 1.1855042 + 0.8189067x1 - 0.93786836x2) + 1.3171272ifelse((1.784764x1 - 0.027649183 - 1.0800585x2) < 0, 0...

Мы видим много ifelse... Действительно, нейросеть с активацией ReLU можно переписать как большую кусочно-линейную функцию.

Сохраним код в файл .jl

Если мы поместим код в текстовый файл, то его можно будет включить в блок Engee Function через команду include(), но только по абсолютному адресу. Но, при этом, в папке с моделью всегда нужно будет хранить этот файл с кодом. Есть и другой способ – поместить код и маску внутрь блока Engee Function и сделать компонент самодостаточным.

In [ ]:
open("$(@__DIR__)/neural_net_code.jl", "w") do f
    println(f, "function nn(x1, x2)\n    $(s[1])\nend")
end

# Сразу же запустим этот код и получим прогноз нейросети:
include("$(@__DIR__)/neural_net_code.jl")
nn(1,2)
Out[0]:
2.5977383718979077

Проверка: запуск нейросети из блока Engee Function

Запустим модель и визуализируем результаты.

In [ ]:
# Загрузим (если модель еще не открыта) и выполним модель
if "neural_regression_to_engee_function"  getfield.(engee.get_all_models(), :name)
    engee.load( "$(@__DIR__)/neural_regression_to_engee_function.engee");
end
model_data = engee.run( "neural_regression_to_engee_function" );

# Подготовим выходные переменные
model_x1 = model_data["X1"].value;
model_x2 = model_data["X2"].value;
model_y = vec( hcat( model_data["Y"].value... ));

# Построим график
scatter!( model_x1, model_x2, model_y, ms=2.5, msw=.5, leg=false, zcolor=model_y, c=:viridis,
         xlimits=(-3,3), ylimits=(-3,3), title="Прогноз от блока Engee Function", titlefont=font(10) )
Out[0]:

Мы видим знакомую функцию, но каждая выходная точка "предсказана" нейросетью, которая обучилась моделировать нужную нам зависимость $y = f(x_1, x_2)$.

Заключение

Пользуясь библиотекой Symbolics совсем несложно сгенерировать код из функции Julia, даже если эта функция – нейросеть из библиотеки Flux. Остается выбрать удобный способ размещения кода на холсте, и можно использовать нейросеть в качестве компонента в модельно-ориентированном проекте.

Блоки, использованные в примере