AnyMath 文档
Notebook

来自卷积神经网络的嵌入式代码

让我们为玩具任务训练一个神经网络-预测几何形状,并检查它是否可以编译成C代码,然后编译成二进制库以在块中使用或作为另一个项目的一部分。

导言

在本实用指南中,我们将训练神经网络以识别玩具数据集上的几何形状,然后将训练好的模型导出为C代码,将其编译为共享库,并测试集成到第三方项目或项目中的C代码块的可能性。

在转换过程中,您会注意到精度略有下降。 这可能是由于Julia和C中的某些操作的实现差异(例如,批量归一化)或转换为代码时系数的简单舍入引起的,但它为在嵌入式系统上部署开辟了道路。

准备工作

在这个阶段,我们下载必要的库,修复随机数生成器,创建正方形,圆形和三角形的合成数据集,然后可视化每个类的样本图像。

生成具有已知属性(大小64×64,归一化到范围[-1.1])的受控平衡数据集允许您孤立地检查管道的每个阶段,而不受外部因素的影响。

让我们安装必要的库并初始化随机数生成器,以便我们的实验易于重现。:

In [ ]:
# 安装必要的软件包
neededLibs = ["Flux", "BSON", "ImageTransformations", "Random"]
for lib in neededLibs
    try
        eval(Meta.parse("using $lib"))
    catch ex
        Pkg.add(lib)
        eval(Meta.parse("using $lib"))
    end
end
In [ ]:
Random.seed!(5);

合成数据集

让我们创建一个由三个类组成的玩具数据集。 有些对象放在"未知"文件夹中,也就是说,它们的类虽然写在文件名中,但对系统来说是未知的。 您可以将其称为验证数据集。 其余的-培训和测试-安排在适当的文件夹中。

In [ ]:
include("$(@__DIR__)/_scripts/generate_shape_dataset.jl")
generate_shape_dataset(samples_per_class=200, test_samples=30, img_size=64)
Датасет сгенерирован:
  200 изображений каждого класса для обучения
  30 тестовых изображений
  Размер изображений: 64 x 64

在这个阶段,我们试图生成一个相当多样化的数据集(带有三角形旋转),但与此同时不要过分复杂代码,例如,我们在学习过程中没有做增强。 总的来说,这个阶段的问题最小。

看看训练数据集

以下是我们训练数据集中的样本对象:

In [ ]:
include("$(@__DIR__)/_scripts/show_dataset_samples.jl")
DATA_DIR = "$(@__DIR__)/учебные данные";
gr()
show_dataset_samples(DATA_DIR, samples_per_class=10)
Out[0]:
No description has been provided for this image

模型训练与分析

在这里,我们开始卷积神经网络的学习过程,保存度量的历史记录,分析准确性和损失的动态,并在测试图像上显示预测的马赛克。

按类别监控精度/召回指标并提前停止验证精度有助于及时检测过拟合并为后续导出选择最佳模型。

In [ ]:
include("$(@__DIR__)/_scripts/train_model.jl");
DATA_DIR = "$(@__DIR__)/учебные данные";
model, classes = train_model(DATA_DIR; epochs=100, imsize=64, batch_size=32, lr=0.0005, test_split=0.25, patience_limit=8);
Размер батча: 32, Скорость обучения: 0.0005
Доля тестовой выборки: 25.0%
Найдено классов: 3: ["квадрат", "круг", "треугольник"]

=== Распределение классов ===

Всего изображений: 600 (64×64)
  квадрат: 200 изображений (33.3%)
  круг: 200 изображений (33.3%)
  треугольник: 200 изображений (33.3%)

=== Разделение данных ===
  Тренировочных: 450 (75.0%)
  Тестовых: 150 (25.0%)
┌ Warning: `Flux.params(m...)` is deprecated. Use `Flux.trainable(model)` for parameter collection,
│ and the explicit `gradient(m -> loss(m, x, y), model)` for gradient computation.
└ @ Flux /opt/depot/demos/packages/Flux/c30bR/src/deprecations.jl:93
Параметров модели: 16035

=== Обучение ===
  Эпоха  1/100, Train Loss: 1.2926, Train Acc: 39.1%, Test Acc: 36.7% ★  (precision/recall по классам: квадрат: 31.7%/40.0%, круг: 46.9%/30.0%, треугольник: 43.6%/48.0%)
  Эпоха  2/100, Train Loss: 1.1301, Train Acc: 50.9%, Test Acc: 43.3% ★  (precision/recall по классам: квадрат: 51.7%/60.0%, круг: 48.8%/42.0%, треугольник: 51.0%/50.0%)
  Эпоха  3/100, Train Loss: 1.0281, Train Acc: 60.7%, Test Acc: 55.3% ★  (precision/recall по классам: квадрат: 49.2%/62.0%, круг: 40.8%/40.0%, треугольник: 52.6%/40.0%)
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
  Эпоха  4/100, Train Loss: 0.9695, Train Acc: 67.6%, Test Acc: 63.3% ★  (precision/recall по классам: квадрат: 60.4%/58.0%, круг: 51.1%/48.0%, треугольник: 60.0%/66.0%)
  Эпоха  5/100, Train Loss: 0.9029, Train Acc: 61.6%, Test Acc: 57.3%    (precision/recall по классам: квадрат: 63.0%/68.0%, круг: 37.0%/34.0%, треугольник: 58.0%/58.0%)
  Эпоха  6/100, Train Loss: 0.8281, Train Acc: 72.7%, Test Acc: 66.0% ★  (precision/recall по классам: квадрат: 67.7%/84.0%, круг: 51.1%/46.0%, треугольник: 58.1%/50.0%)
  Эпоха  7/100, Train Loss: 0.7604, Train Acc: 77.6%, Test Acc: 75.3% ★  (precision/recall по классам: квадрат: 71.4%/90.0%, круг: 65.0%/52.0%, треугольник: 74.5%/70.0%)
  Эпоха  8/100, Train Loss: 0.7174, Train Acc: 80.4%, Test Acc: 79.3% ★  (precision/recall по классам: квадрат: 72.4%/84.0%, круг: 57.5%/46.0%, треугольник: 69.2%/72.0%)
  Эпоха  9/100, Train Loss: 0.6826, Train Acc: 83.1%, Test Acc: 78.0%    (precision/recall по классам: квадрат: 73.4%/94.0%, круг: 46.8%/44.0%, треугольник: 59.0%/46.0%)
  Эпоха 10/100, Train Loss: 0.6091, Train Acc: 73.8%, Test Acc: 67.3%    (precision/recall по классам: квадрат: 76.7%/92.0%, круг: 67.6%/50.0%, треугольник: 67.9%/72.0%)
  Эпоха 11/100, Train Loss: 0.5658, Train Acc: 85.1%, Test Acc: 82.0% ★  (precision/recall по классам: квадрат: 85.7%/96.0%, круг: 72.5%/58.0%, треугольник: 74.1%/80.0%)
  Эпоха 12/100, Train Loss: 0.5198, Train Acc: 87.1%, Test Acc: 84.7% ★  (precision/recall по классам: квадрат: 88.7%/94.0%, круг: 62.2%/56.0%, треугольник: 67.3%/70.0%)
  Эпоха 13/100, Train Loss: 0.5058, Train Acc: 63.6%, Test Acc: 59.3%    (precision/recall по классам: квадрат: 94.0%/94.0%, круг: 68.8%/66.0%, треугольник: 73.1%/76.0%)
  Эпоха 14/100, Train Loss: 0.5111, Train Acc: 84.9%, Test Acc: 84.0%    (precision/recall по классам: квадрат: 90.9%/100.0%, круг: 72.0%/72.0%, треугольник: 75.6%/68.0%)
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
  Эпоха 15/100, Train Loss: 0.462, Train Acc: 74.9%, Test Acc: 69.3%    (precision/recall по классам: квадрат: 92.6%/100.0%, круг: 78.0%/78.0%, треугольник: 82.6%/76.0%)
  Эпоха 16/100, Train Loss: 0.4234, Train Acc: 95.1%, Test Acc: 94.0% ★  (precision/recall по классам: квадрат: 92.3%/96.0%, круг: 76.5%/78.0%, треугольник: 83.0%/78.0%)
  Эпоха 17/100, Train Loss: 0.3856, Train Acc: 82.9%, Test Acc: 80.0%    (precision/recall по классам: квадрат: 98.0%/100.0%, круг: 78.7%/74.0%, треугольник: 76.9%/80.0%)
  Эпоха 18/100, Train Loss: 0.3667, Train Acc: 96.7%, Test Acc: 94.7% ★  (precision/recall по классам: квадрат: 96.0%/96.0%, круг: 69.5%/82.0%, треугольник: 82.9%/68.0%)
  Эпоха 19/100, Train Loss: 0.3262, Train Acc: 97.1%, Test Acc: 95.3% ★  (precision/recall по классам: квадрат: 92.2%/94.0%, круг: 81.2%/78.0%, треугольник: 82.4%/84.0%)
  Эпоха 20/100, Train Loss: 0.2852, Train Acc: 98.4%, Test Acc: 96.7% ★  (precision/recall по классам: квадрат: 98.0%/98.0%, круг: 79.6%/86.0%, треугольник: 87.0%/80.0%)
  Эпоха 21/100, Train Loss: 0.2544, Train Acc: 96.4%, Test Acc: 92.0%    (precision/recall по классам: квадрат: 96.0%/96.0%, круг: 81.0%/94.0%, треугольник: 97.6%/82.0%)
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
  Эпоха 22/100, Train Loss: 0.2461, Train Acc: 81.6%, Test Acc: 80.7%    (precision/recall по классам: квадрат: 94.3%/100.0%, круг: 88.5%/92.0%, треугольник: 97.8%/88.0%)
  Эпоха 23/100, Train Loss: 0.2229, Train Acc: 99.3%, Test Acc: 98.0% ★  (precision/recall по классам: квадрат: 98.0%/96.0%, круг: 87.8%/86.0%, треугольник: 84.6%/88.0%)
  Эпоха 24/100, Train Loss: 0.1788, Train Acc: 99.1%, Test Acc: 97.3%    (precision/recall по классам: квадрат: 96.0%/96.0%, круг: 84.0%/84.0%, треугольник: 88.0%/88.0%)
  Эпоха 25/100, Train Loss: 0.1765, Train Acc: 99.6%, Test Acc: 97.3%    (precision/recall по классам: квадрат: 98.0%/98.0%, круг: 86.8%/92.0%, треугольник: 93.6%/88.0%)
  Эпоха 26/100, Train Loss: 0.1426, Train Acc: 96.4%, Test Acc: 92.7%    (precision/recall по классам: квадрат: 100.0%/98.0%, круг: 90.4%/94.0%, треугольник: 93.9%/92.0%)
  Эпоха 27/100, Train Loss: 0.1478, Train Acc: 97.3%, Test Acc: 94.0%    (precision/recall по классам: квадрат: 98.0%/100.0%, круг: 93.9%/92.0%, треугольник: 94.0%/94.0%)
  Эпоха 28/100, Train Loss: 0.1513, Train Acc: 99.3%, Test Acc: 97.3%    (precision/recall по классам: квадрат: 98.0%/100.0%, круг: 93.9%/92.0%, треугольник: 94.0%/94.0%)
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
  Эпоха 29/100, Train Loss: 0.1345, Train Acc: 99.6%, Test Acc: 98.0%    (precision/recall по классам: квадрат: 100.0%/98.0%, круг: 96.0%/96.0%, треугольник: 96.1%/98.0%)
  Эпоха 30/100, Train Loss: 0.0976, Train Acc: 95.1%, Test Acc: 89.3%    (precision/recall по классам: квадрат: 92.6%/100.0%, круг: 89.4%/84.0%, треугольник: 89.8%/88.0%)
  Эпоха 31/100, Train Loss: 0.1058, Train Acc: 89.6%, Test Acc: 83.3%    (precision/recall по классам: квадрат: 98.0%/100.0%, круг: 95.8%/92.0%, треугольник: 94.1%/96.0%)

  ⏹ Ранний останов после 31 эпох (нет улучшения 8 эпох)

Загружена лучшая модель (Test Acc: 98.0%)

=== Результаты ===
  Лучшая точность на тесте: 98.0%
  Точность train/test: 95.1% / 88.0%
  ✓ Нет переобучения (разрыв 7.1%)
Обучение завершено! 🚀
Модель сохранена в model.bson

让我们来看看所进行的培训的质量:

In [ ]:
include("$(@__DIR__)/_scripts/analyze_training_log.jl")
gr()
df, classes, p = analyze_training_log("training_log.txt")
display(p)
No description has been provided for this image

有趣的是分别解释每个图形。 例如,所有类的精度增长几乎相同,但对于正方形,召回分数立即变得更好,并且对于三角形总是落后,在学习过程结束时仍然不是最高的。

我们在测试中达到100%质量后没有继续培训,因为相互比较实现是没有意义的。 但我们肯定应该为数据集生成更多对象,因为平均而言,到训练结束时,模型准确地识别了正方形和圆形,但在五个建议的三角形中,平均而言,其中一个"没 虽然她标记为三角形的那些确实是三角形(网络为"圆"类显示了更多的"假阳性"错误)。

Julia(通量)神经网络的预测

In [ ]:
include("$(@__DIR__)/_scripts/simple_mosaic.jl")
UNKNOWN_DIR = "$(@__DIR__)/неизвестно";
gr()
plot(create_simple_mosaic(UNKNOWN_DIR, imsize=64))
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
Out[0]:
No description has been provided for this image

我们看到相当不错的预测,但这与其说是成功培训的结果,不如说是设计师长期工作的结果。 最耗时的是网络体系结构的选择(层数,通道,使用BatchNorm和Dropout)和超参数(学习率,批量大小,增强),以实现稳定的收敛并避免在有限的数据集上过度拟合。 因此,例如,增强被移动到数据集生成函数以简化示例,以及由于仅对三角形需要此过程的事实。

出口到C和测试

现在我们将预处理的图像转换为二进制格式,生成神经网络的C代码,将其编译为可执行文件,并将从C实现中获得的预测可视化。 我们故意假设代码将在没有PNG库的平台上工作。 因此,我们使用单独的脚本将图像转换为二进制格式。 这些二进制文件包含矩阵,其元素包括每个像素的每个颜色通道,由单个UInt8数字表示。

In [ ]:
include("$(@__DIR__)/_scripts/convert_png_to_rgb8.jl")
convert_png_to_rgb8("$(@__DIR__)/неизвестно", "$(@__DIR__)/неизвестно_rgb8", 64)

现在我们已经准备好二进制图像的数据集,我们可以下载已经训练好的模型并将其转换为C代码。 成功导出的关键要求是数据格式的完全对齐(图像的RGB8,系数的HWC顺序)以及Julia和C之间的权重遍历顺序,这是通过在所有阶段显式控制索引和标准化来

In [ ]:
include("$(@__DIR__)/_scripts/generate_cnn_code.jl")

BSON.@load "$(@__DIR__)/model.bson" model classes
model = Flux.testmode!(model)

# 生成库和主程序
generate_shared_lib(model, 64, length(classes))
generate_main_program(64, length(classes))
Generated neural_net.c and neural_net.h
Generated main.c

我们将神经网络本身编译成一个库。 我们还生成了主程序,该程序将"unknown_rgb8"文件夹中的图像馈送到神经网络并处理分类结果。

In [ ]:
;gcc -shared -fPIC neural_net.c -o libneuralnet.so -lm
In [ ]:
;gcc main.c -o classify_unknown -ldl -lm

有趣的是,要运行这个神经网络,我们不需要任何库,无论是Julia还是C。它运行在任何具有C编译器的系统上。

In [ ]:
;./classify_unknown
File                 Prediction      Confidence
------------------------------------------------
circle_001.rgb       круг        0.998
circle_002.rgb       круг        0.999
circle_003.rgb       круг        0.992
circle_004.rgb       круг        1.000
circle_005.rgb       круг        0.876
circle_007.rgb       круг        0.885
circle_009.rgb       круг        0.929
circle_010.rgb       круг        0.991
circle_011.rgb       круг        0.985
circle_012.rgb       круг        0.990
circle_014.rgb       круг        0.994
circle_015.rgb       круг        0.999
circle_016.rgb       круг        0.966
circle_017.rgb       круг        0.896
circle_018.rgb       круг        0.847
circle_020.rgb       круг        0.921
circle_021.rgb       круг        0.981
circle_022.rgb       круг        0.867
circle_023.rgb       круг        0.994
circle_024.rgb       круг        0.977
circle_025.rgb       круг        1.000
circle_026.rgb       круг        0.998
circle_028.rgb       круг        0.983
circle_029.rgb       круг        0.997
circle_030.rgb       круг        0.976
square_001.rgb       квадрат  0.525
square_002.rgb       квадрат  0.504
square_003.rgb       квадрат  0.892
square_004.rgb       квадрат  0.854
square_005.rgb       квадрат  0.877
square_007.rgb       квадрат  0.729
square_008.rgb       круг        0.577
square_009.rgb       квадрат  0.786
square_010.rgb       квадрат  0.929
square_013.rgb       квадрат  0.801
square_014.rgb       квадрат  0.942
square_015.rgb       квадрат  0.952
square_016.rgb       квадрат  0.892
square_018.rgb       квадрат  0.927
square_019.rgb       квадрат  0.974
square_020.rgb       квадрат  0.887
square_021.rgb       квадрат  0.894
square_023.rgb       квадрат  0.786
square_024.rgb       квадрат  0.984
square_026.rgb       квадрат  0.725
square_027.rgb       квадрат  0.869
square_028.rgb       квадрат  0.918
square_029.rgb       квадрат  0.925
square_030.rgb       квадрат  0.898
triangle_001.rgb     треугольник 0.550
triangle_002.rgb     круг        0.692
triangle_003.rgb     круг        0.667
triangle_004.rgb     круг        0.554
triangle_006.rgb     круг        0.770
triangle_007.rgb     круг        0.811
triangle_008.rgb     круг        0.553
triangle_009.rgb     круг        0.897
triangle_010.rgb     треугольник 0.647
triangle_011.rgb     треугольник 0.544
triangle_012.rgb     круг        0.502
triangle_013.rgb     круг        0.931
triangle_014.rgb     треугольник 0.644
triangle_016.rgb     круг        0.800
triangle_017.rgb     круг        0.546
triangle_020.rgb     круг        0.897
triangle_021.rgb     круг        0.892
triangle_022.rgb     круг        0.928
triangle_023.rgb     круг        0.546
triangle_025.rgb     треугольник 0.571
triangle_026.rgb     круг        0.552
triangle_027.rgb     круг        0.506
triangle_028.rgb     круг        0.594
triangle_029.rgb     треугольник 0.684
triangle_030.rgb     круг        0.783
circle_027.rgb       круг        0.973
square_006.rgb       квадрат  0.882
square_011.rgb       квадрат  0.784
square_012.rgb       квадрат  0.970
square_017.rgb       квадрат  0.971
square_025.rgb       круг        0.577
triangle_019.rgb     круг        0.569
triangle_024.rgb     круг        0.979

当将模型转移到C时,我们必须解决几个非平凡的任务:在没有第三方库的情况下手动实现卷积和BatchNorm,将所有操作减少到单一的HWC格式,准确地再现权重遍历的顺序(对于多通道层尤其重要),以及由于目标环境中缺少PNG库而处理二进制图像文件-所有这些困难都被成功地克服了。

C语言神经网络的预测

In [ ]:
include("$(@__DIR__)/_scripts/create_mosaic_from_c_predictions.jl")
run(pipeline(`./classify_unknown`, stdout="pred.txt"))
UNKNOWN_DIR = "$(@__DIR__)/неизвестно";
gr()
mosaic_grouped = create_mosaic_from_c_predictions("неизвестно", "pred.txt", max_images=8)
Out[0]:
No description has been provided for this image

尽管有这些困难,我们已经展示了一个完整的工作管道,证明即使目标平台的资源有限,也可以将神经网络从Julia导出到C。

In [ ]:
include("$(@__DIR__)/_scripts/predict_to_csv.jl")
UNKNOWN_DIR = "$(@__DIR__)/неизвестно";
predict_to_csv(UNKNOWN_DIR, confidence_threshold=0.4, output_csv="$(@__DIR__)/predictions.csv")
run(pipeline(`./classify_unknown`, stdout="pred.txt"))
include("$(@__DIR__)/_scripts/compare_c_and_julia.jl")
df = compare_c_and_julia()
sort(df)
Обработано файлов: 82
  квадрат: 30
  круг: 27
  треугольник: 25

=== Сравнение C и Julia ===
Всего файлов: 82
Совпадающих предсказаний: 58
Точность: 70.73%

Статистика разницы в уверенности:
  Средняя разница: 0.1468
  Макс разница: 0.4452
  Мин разница: 0.0026
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
Out[0]:
82×12 DataFrame
57 rows omitted
RowFileC_PredictionC_ConfidenceBaseNameФайлJulia_PredictionJulia_ConfidenceВероятность_квадратВероятность_кругВероятность_треугольникMatchConfidence_Diff
StringStringFloat64StringString31String31Float64Float64Float64Float64BoolFloat64
1circle_001.rgbкруг0.998circle_001circle_001.pngкруг0.9119490.05539550.9119490.0326554true0.0860508
2circle_002.rgbкруг0.999circle_002circle_002.pngкруг0.9763530.008158840.9763530.0154881true0.0226469
3circle_003.rgbкруг0.992circle_003circle_003.pngкруг0.952730.02631530.952730.0209551true0.0392704
4circle_004.rgbкруг1.0circle_004circle_004.pngкруг0.9892990.005832470.9892990.00486871true0.0107013
5circle_005.rgbкруг0.876circle_005circle_005.pngкруг0.6770330.06953770.6770330.253429true0.198967
6circle_007.rgbкруг0.885circle_007circle_007.pngкруг0.8530360.1026440.8530360.0443203true0.0319639
7circle_009.rgbкруг0.929circle_009circle_009.pngкруг0.7493890.1471960.7493890.103415true0.179612
8circle_010.rgbкруг0.991circle_010circle_010.pngкруг0.9198560.01826790.9198560.0618764true0.0711443
9circle_011.rgbкруг0.985circle_011circle_011.pngкруг0.8211480.007267880.8211480.171584true0.163852
10circle_012.rgbкруг0.99circle_012circle_012.pngкруг0.6564440.3010540.6564440.0425026true0.333556
11circle_014.rgbкруг0.994circle_014circle_014.pngкруг0.9397310.01862430.9397310.0416447true0.0542691
12circle_015.rgbкруг0.999circle_015circle_015.pngкруг0.9706860.00608770.9706860.0232258true0.0283136
13circle_016.rgbкруг0.966circle_016circle_016.pngкруг0.8432020.03315850.8432020.12364true0.122798
⋮⋮⋮⋮⋮⋮⋮⋮⋮⋮⋮⋮⋮
71triangle_019.rgbкруг0.569triangle_019triangle_019.pngтреугольник0.466660.08558060.4477590.46666false0.10234
72triangle_020.rgbкруг0.897triangle_020triangle_020.pngтреугольник0.8207940.002196020.177010.820794false0.0762059
73triangle_021.rgbкруг0.892triangle_021triangle_021.pngтреугольник0.9412310.001349090.05741970.941231false0.0492312
74triangle_022.rgbкруг0.928triangle_022triangle_022.pngтреугольник0.9777810.000122910.0220960.977781false0.0497811
75triangle_023.rgbкруг0.546triangle_023triangle_023.pngтреугольник0.8183180.01850380.1631780.818318false0.272318
76triangle_024.rgbкруг0.979triangle_024triangle_024.pngкруг0.7807410.1150860.7807410.104173true0.198259
77triangle_025.rgbтреугольник0.571triangle_025triangle_025.pngтреугольник0.8574690.01879770.1237340.857469true0.286469
78triangle_026.rgbкруг0.552triangle_026triangle_026.pngтреугольник0.9535730.002183540.0442430.953573false0.401573
79triangle_027.rgbкруг0.506triangle_027triangle_027.pngтреугольник0.9088860.006617020.08449650.908886false0.402886
80triangle_028.rgbкруг0.594triangle_028triangle_028.pngтреугольник0.7929390.01448990.1925720.792939false0.198939
81triangle_029.rgbтреугольник0.684triangle_029triangle_029.pngтреугольник0.9475950.003543110.04886240.947595true0.263594
82triangle_030.rgbкруг0.783triangle_030triangle_030.pngтреугольник0.872280.003308110.1244120.87228false0.0892798

结论

我们已经展示了如何通过创建一个包含神经网络的程序的整个周期:从创建数据集和在Julia上训练模型到导出到C并检查性能,这证实了使用生成的代码远远超出AnyMath工程平台的基本可能性。