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

Классификация радиолокационных целей с использованием машинного обучения и глубокого обучения

В этом примере демонстрируются подходы к классификации данных радара с использованием методов машинного и глубокого обучения. Для решения задачи применяются следующие подходы:

  1. Машинное обучение: метод опорных векторов (SVM).
  2. Глубое Обучение: SqueezeNet, LSTM

Классификация объектов — важная задача для радиолокационных систем. В данном примере рассматривается задача определения, какой именно объект отражает сигнал радара — цилиндр или конус. Хотя в примере используются синтетические данные, этот подход можно применять и для реальных радиолокационных измерений.

Обучение SVM

Первых делом необходимо импортировать используемые пакеты

В файле install_packages.jl находятся пакеты, которые необходимы для скрипта. Они добавляются в рабочее окружение. В файле import_packages все установленные пакеты импортируются для скрипта. В ячейках ниже запускаем их выполнение

In [ ]:
include("$(@__DIR__)/Install_packages.jl")
In [ ]:
include("$(@__DIR__)/import_packages.jl")
init()

Загрузна данных

Данные, которые будут использоваться для обучения моделей, были взяты из демо примера по моделированию ЭПР радарных объектов.

Пропишем путь, в которым лежат данные

In [ ]:
data_path = "$(@__DIR__)/gen_data.csv"

В CSV файле лежат данные по конусу и цилиндру. Первые 100 элементов - цилиндр, вторые 100 элементов - конус. Разобьем наши данные на тренировочный и тестовый датасет

In [ ]:
# Допустим, ваш файл называется "data.csv"
df = CSV.read(data_path, DataFrame)
data = Matrix(df)
cyl_train = data[:, 1:75]    # первые 75 элементов
cyl_test  = data[:, 76:100]  # оставшиеся 25 элементов

# Для конусов:
cone_train = data[:, 101:175]  # 75 элементов
cone_test  = data[:, 176:200]  # 25 элементов
# Объединяем в тренировочную и тестовую выборки:
train_data = hcat(cyl_train, cone_train)
test_data  = hcat(cyl_test, cone_test);
In [ ]:
TrainFeatures_T = permutedims(train_data, (2,1))
TestFeatures_T = permutedims(test_data, (2, 1))

TrainLabels = reshape(vcat(fill(1, 75), fill(2, 75)), :, 1)
TestLabels = reshape(vcat(fill(1, 25), fill(2, 25)), :, 1);

Код ниже выполняет извлечение признаков из временных рядов с использованием преобразования непрерывной вейвлет-функцией Морле. Сначала ко всем данным применяется вейвлет-преобразование с последующим расчетом абсолютных значений коэффициентов, чтобы оставить только амплитуды. Затем происходит усреднение этих амплитуд по временной оси, что позволяет снизить размерность признаков. После этого результат "сжимается" для удаления лишних измерений.

Все это делается для тренировочного и тестового наборов данных

In [ ]:
wavelet_cfg = wavelet(Morlet(π), averagingType=Dirac(), β=1.5)
train_features_cwt = dropdims(mean(abs.(cwt(TrainFeatures_T, wavelet_cfg)), dims=2), dims=2);
In [ ]:
wavelet_cfg = wavelet(Morlet(π), averagingType=Dirac(), β=1.5)
test_features_cwt = dropdims(mean(abs.(cwt(TestFeatures_T, wavelet_cfg)), dims=2), dims=2);

Инициализируем список, содержащий имена классов

In [ ]:
class_names = ["Cylinder","Cone"]

Перед обучением классификатора данные необходимо преобразовать в формат, совместимый с моделью: тренировочные признаки преобразуются в табличный вид, а метки переводятся в категориальный тип.

In [ ]:
X_train = MLJ.table(train_features_cwt)  # Преобразуем X_train в датафрейм
X_test = MLJ.table(test_features_cwt)  # Преобразуем X_test в датафрейм
y_train  = coerce(vec(TrainLabels), Multiclass)  # Преобразуем y_train в вектор и задаем тип  Multiclass
y_test = coerce(vec(TestLabels), Multiclass);  # Преобразуем y_test в вектор и задаем тип Multiclass

Инициализация модели опорных векторов и ее обучение

Далее настраиваем модель опорных векторов, инициализируя параметры и кросс-валидацию

In [ ]:
svm = (@MLJ.load SVC pkg=LIBSVM verbosity=true)()  # Загружаем и создаем модель SVM с использованием LIBSVM через MLJ

# Задаем параметры для модели SVM
svm.kernel = LIBSVM.Kernel.Polynomial         # Тип ядра: полиномиальное (Polynomial)
svm.degree = 2                                # Степень полинома для полиномиального ядра
svm.gamma = 0.1                               # Параметр γ, контролирующий влияние каждой обучающей точки
svm.cost = 1.0                                # Параметр регуляризации 

# Создаем "машину" (machine) для связывания модели с данными
mach = machine(svm, X_train, y_train)

# Настраиваем кросс-валидацию с 5 фолдами
cv = CV(nfolds=5);

Выполняем обучение модели с использованием кросс-валидации и рассчитываем точность на обучении

In [ ]:
@info "Load model config, cross-valid"

cv_results = evaluate!(mach; resampling=cv, measures=[accuracy], verbosity=0) # Выполняем кросс-валидацию модели
println("Точность модели: $(cv_results.measurement[1] * 100)" , "%")

Оценка обученной модели

Оценим обученную модель на тестовых данных

In [ ]:
@info "Predict test"

y_pred_SVM = MLJ.predict(mach, X_test)   # Выполняеем предсказания модели на тестовых данных

accuracy_score_SVM = accuracy(y_pred_SVM, y_test)  # Вычисляем точность модели на тестовом наборе

println("Точность модели на тесте: ", Int(accuracy_score_SVM * 100), "%")

Далее построим матрицу ошибок для оценки качества классификации модели - функция plot_confusion_matrix выполняет эту задачу

In [ ]:
function plot_confusion_matrix(C)
    # Создаем heatmap с большими шрифтами и контрастными цветами
    heatmap(
        C,
        title = "Confusion Matrix",
        xlabel = "Predicted",
        ylabel = "True",
        xticks = (1:length(class_names), class_names),
        yticks = (1:length(class_names), class_names),
        c = :viridis,  # Более контрастная цветовая схема
        colorbar_title = "Count",
        size = (600, 400)
    )

    # Добавим значения в ячейки для улучшенной наглядности
    for i in 1:size(C, 1)
        for j in 1:size(C, 2)
            annotate!(j, i, text(C[i, j], :white, 12, :bold)) 
        end
    end

    # Явно отображаем график
    display(current())
end;
In [ ]:
# Пример использования функции
conf_matrix = CM.confmat(y_pred_SVM, y_test, levels=[2, 1])
conf_matrix = CM.matrix(conf_matrix)
plot_confusion_matrix(conf_matrix)

Как можно увидеть из построенной матрицы ошибок, модель хорошо классифицирует конус, однако цилиндр частенько путает с конусом.

Обучение SqueezeNet

Далее обучим сеть глубокого обучения - SqueezeNet. SqueezeNet — компактная свёрточная нейронная сеть, предложенная в 2016 году, достигающая производительности AlexNet при значительно меньшем размере. Использует Fire-модули, которые включают слои squeeze (1x1 свёртки для уменьшения числа каналов) и expand (1x1 и 3x3 свёртки для восстановления размерности), что снижает количество параметров без потери качества. Подходит для встроенных устройств благодаря компактности.

Необходимые параметры

Инициализируем параметры, участвующие в обучении модели и подготовки данных

In [ ]:
batch_size = 2              # Размер батча для обучения
num_classes = 2             # Количество классов в задаче классификации
lr = 1e-4                   # Скорость обучения (learning rate)
Epochs = 15                 # Количество эпох для обучения
Classes = 1:num_classes;     # Список индексов классов, например, 1 и 2

Создание наборов данных

Прежде всего необходимо подготовить данные для обучения сети. Нужно выполнить и построить непрерывное вейвлет-преобразование для сигналов, чтобы получить его временно-частотные характеристики. Вейвлеты «сжимаются», чтобы локализовать кратковременные всплески с высокой временной точностью, и «растягиваются», чтобы уловить плавные изменения структуры сигнала.

Вспомогательная функция save_wavelet_images получает непрерывное вейвлет-преобразование (CWT) для каждого радиолокационного сигнала, преобразует результат в формат, совместимый с моделями компьютерного зрения, и сохраняет спектрограммы в виде изображений.

Инициализируем несколько вспомогательных функций

In [ ]:
# Функция для нормализации значений
function rescale(img)
    min_val = minimum(img)
    max_val = maximum(img)
    return (img .- min_val) ./ (max_val - min_val)
end

# Применение colormap jet и преобразование в RGB
function apply_colormap(data, cmap)
    h, w = size(data)
    rgb_image = [RGB(get(cmap, val)) for val in Iterators.flatten(eachrow(data))]
    return reshape(rgb_image, w, h)
end
# Преобразование непрерывного вейвлет-преобразования в изображение
function apply_image(wt)
    rescaled_data = rescale(abs.(wt))
    colored_image = apply_colormap(rescaled_data, ColorSchemes.inferno)
    resized_image = imresize(colored_image, (224, 224))
    flipped_image = reverse(resized_image, dims=1)
    return flipped_image
end;
In [ ]:
function save_wavelet_images(features_matrix, wavelet_filter, save_path, is_train=true)
    # Определение количества примеров для каждого класса
    
    class1_count = is_train ? 75 : 25  # Количество примеров для класса cone
    class2_count = size(features_matrix, 1) - class1_count  # Количество примеров для класса cylinder
    println(class1_count)
    # Создание папок для каждого класса
    cone_path = joinpath(save_path, "cone")  # cone — класс 1
    cylinder_path = joinpath(save_path, "cylinder")  # cylinder — класс 2
    mkpath(cone_path)
    mkpath(cylinder_path)

    # Обработка строк матрицы для класса cylinder
    for i in 1:class1_count
        res = cwt(features_matrix[i, :], wavelet_filter)
        image_wt = apply_image(res)
        img_filename = joinpath(cylinder_path, "sample_$(i).png")
        save(img_filename, image_wt)

    end

    # Обработка строк матрицы для класса cone
    for i in class1_count+1:class1_count+class2_count
        res = cwt(features_matrix[i, :], wavelet_filter)
        image_wt = apply_image(res)
        img_filename = joinpath(cone_path, "sample_$(i - class1_count).png")
        save(img_filename, image_wt)
    end
end;

Объект c представляет собой настраиваемый вейвлет-преобразователь на основе Морле-вейвлета

In [ ]:
c = wavelet(Morlet(π), averagingType=NoAve(), β=1);

Получим базу изображений, для обучения нейронной сети

In [ ]:
save_wavelet_images(TrainFeatures_T, c, "$(@__DIR__)/New_imgs/train")
save_wavelet_images(TestFeatures_T, c, "$(@__DIR__)/New_imgs/test", false)

Посмотрим на экземпляр полученного изображения

In [ ]:
i = Images.load("$(@__DIR__)/New_imgs/train/cylinder/sample_2.png")
Out[0]:
No description has been provided for this image

Инициализируем функцию для аугментации данных: она отвечает за приведение изображений к размеру 224x224 и преобразование данных в тензоры

In [ ]:
function Augment_func(img)
    resized_img = imresize(img, 224, 224)               #Изменение размеров изображения до 224х224
    tensor_image = channelview(resized_img);            #Представление данных в виде тензора
    permutted_tensor = permutedims(tensor_image, (2, 3, 1));        #Изменение порядка размерности до формата (H, W, C)
    permutted_tensor = Float32.(permutted_tensor)                   #Преобразование в тип Float32
    return permutted_tensor
end;

Функция Create_dataset выполняет создание обучающих наборов данных, обрабатывая директории, в которых находятся изображения.

In [ ]:
function Create_dataset(path)
    img_train = []
    img_test = []
    label_train = []
    label_test = []

    train_path = joinpath(path, "train");
    test_path = joinpath(path, "test");

    # Функция для обработки изображений в заданной директории
    function process_directory(directory, img_array, label_array, label_idx)
        for file in readdir(directory)
            if endswith(file, ".jpg") || endswith(file, ".png")
                file_path = joinpath(directory, file);
                img = Images.load(file_path);
                img = Augment_func(img);
                push!(img_array, img)
                push!(label_array, label_idx)
            end
        end
    end


    # Обработка папки train
    for (idx, label) in enumerate(readdir(train_path))
        println("Processing label in train: ", label)
        label_dir = joinpath(train_path, label)
        process_directory(label_dir, img_train, label_train, idx);
    end

    # Обработка папки test
    for (idx, label) in enumerate(readdir(test_path))
        println("Processing label in test: ", label)
        label_dir = joinpath(test_path, label)
        process_directory(label_dir, img_test, label_test, idx);
    end

    return img_train, img_test, label_train, label_test;
end;

В следующей ячейке кода выполним функцию по созданию обучающаюх и тестовых наборов

In [ ]:
path_to_data = "$(@__DIR__)/New_imgs"

img_train, img_test, label_train, label_test = Create_dataset(path_to_data);

Создаем DataLoader, которые подают изображения на вход модели пачками - батчами. Переводим их на GPU

Важное учтонение: модель обучается на GPU, поскольку это ускоряет процесс обучения многократно. Если вам необходимо использование GPU - свяжитесь с менеджерами, вам выделят доступ к GPU. В рабочей директории будут лежать веса уже предобученной сети, переведенной на CPU. После обучения основной сети вы можете посмотреть на сеть в формате CPU, подгрузив в модель соответсвующие веса

In [ ]:
train_loader_Snet = DataLoader((data=img_train, label=label_train), batchsize=batch_size, shuffle=true, collate=true)
test_loader_Snet = DataLoader((data=img_test, label=label_test), batchsize=batch_size, shuffle=true, collate=true)
train_loader_Snet = gpu.(train_loader_Snet)
test_loader_Snet = gpu.(test_loader_Snet)
@info "loading succes"
[ Info: loading succes

Подготовка к обучению

Инициализируем нашу модель, переведя ее на GPU

In [ ]:
Net = SqueezeNet(;  pretrain=false,
           nclasses = num_classes) |>gpu;

Инициализируем оптимизатор, функцию потерь

In [ ]:
optimizer_Snet = Flux.Adam(lr, (0.9, 0.99));   
lossSnet(x, y) = Flux.Losses.logitcrossentropy(Net(x), y);

Обучение SqueezeNet

Опишем функцию, которая отвечает за обучение модели на эпоху. Функция train_one_epoch выполняет обучение модели на одной эпохе, проходя по всем батчам данных из загрузчика Loader. В последствии эта функция будет использоваться для обучения модели LSTM. У этой функции есть параметр type_model, который определят, какую конкретно мы обучаем модель - сверточную или рекурентную

In [ ]:
function train_one_epoch(model, Loader, Tloss, correct_sample, TSamples, loss_function, Optimizer, type_model)
    for (i, (x, y)) in enumerate(Loader) 
         
        if type_model == "Conv"
            TSamples += length(y) 
            gs = gradient(() -> loss_function(x, onehotbatch(y, Classes)), Flux.params(model))          # Рассчитываем градиенты
        elseif type_model == "Recurrent"
            TSamples += size(y, 2) 
            gs = gradient(() -> loss_function(x, y), Flux.params(model))                                # Рассчитываем градиенты
        end
        Flux.update!(Optimizer, Flux.params(model), gs)                                                 # Обновляем оптимизатор
        y_pred = model(x)                                                                               # Делаем предсказание модели
        # Далее вычисляем точность и ошибку нашей моделе на эпохе
        if type_model == "Conv" 
            preds = onecold(y_pred, Classes)
            correct_sample += sum(preds .== y)
            Tloss += loss_function(x, onehotbatch(y, Classes))   
        elseif type_model == "Recurrent"
            Tloss += loss_function(x, y)
            predicted_classes = onecold(y_pred)  
            true_classes = onecold(y)         
            correct_sample += sum(predicted_classes .== true_classes)  
        end
    end
    return Tloss, TSamples, correct_sample
end;

Запускаем процесс обучения SqueezeNet

In [ ]:
@info "Starting training loop"

for epoch in 1:10 
    total_loss = 0.0 
    train_running_correct = 0  
    total_samples = 0   
    @info "Epoch $epoch"

    total_loss, total_samples, train_running_correct = train_one_epoch(Net, train_loader_Snet, total_loss, 
                                                train_running_correct, total_samples, lossSnet, optimizer_Snet, "Conv")



    epoch_loss = total_loss / total_samples
    epoch_acc = 100.0 * (train_running_correct / total_samples)
    println("loss: $epoch_loss, accuracy: $epoch_acc")  
end

Сохраняем нашу обученную модель

In [ ]:
mkdir("$(@__DIR__)/models")
Out[0]:
"/user/nn/radar_classification_using_ML_DL/models"
In [ ]:
cpu(Net)
@save "$(@__DIR__)/models/SNET.bson" Net

Оценка обученной модели SqueezeNet

Оценим обученную модель. Функция evaluate_model_accuracy отвечает за вычисление точности модели

In [ ]:
function evaluate_model_accuracy(loader, model, classes, loss_function, type_model)
    total_loss, correct_predictions, total_samples = 0.0, 0, 0
    all_preds = []  
    True_labels = []
    for (x, y) in loader
        # Накопление потерь
        total_loss += type_model == "Conv" ? loss_function(x, onehotbatch(y, classes)) : loss_function(x, y)

        # Предсказания и вычисление точности
        y_pred = model(x)
        
        preds = type_model == "Conv" ? onecold(y_pred, classes) : onecold(y_pred)
        true_classes = type_model == "Conv" ? y : onecold(y)
        append!(all_preds, preds)
        append!(True_labels, true_classes)
        correct_predictions += sum(preds .== true_classes)
        total_samples += type_model == "Conv" ? length(y) : size(y, 2)
    end
    # Вычисление точности
    accuracy = 100.0 * correct_predictions / total_samples
    return accuracy, all_preds, True_labels
end;
In [ ]:
accuracy_score_Snet, all_predsSnet, true_predS = evaluate_model_accuracy(test_loader_Snet, Net, Classes, lossSnet, "Conv");
println("Accuracy trained model:", accuracy_score_Snet, "%")
Accuracy trained model:100.0%

Как видно выше, точность модели - 100%. Это дает понять, что модель идеально разделяет два класса между собой. Посмотрим на конкретном примере то, что предсказывает модель

Построим матрицу ошибок, используя функцию plot_confusion_matrix

In [ ]:
preds_for_CM = map(x -> x[1], all_predsSnet);
conf_matrix = CM.confmat(preds_for_CM, true_predS, levels=[1, 2])
conf_matrix = CM.matrix(conf_matrix)
plot_confusion_matrix(conf_matrix)

Предсказание модели

Загружаем картинку из тестового набора данных

In [ ]:
path = "$(@__DIR__)/New_imgs/test/cone/sample_14.png";
img = Images.load(path)  # Загружаем изображение
img_aug = Augment_func(img);
img_res = reshape(img_aug, size(img_aug, 1), size(img_aug, 2), size(img_aug, 3), 1);

Подгружаем веса модели

In [ ]:
model_data = BSON.load("$(@__DIR__)/models/SNET.bson")
snet_cpu = model_data[:Net] |> cpu;

Делаем предсказание

In [ ]:
y_pred = (snet_cpu(img_res))
pred = onecold(y_pred, Classes)
# pred = cpu(preds)  # Переносим предсказания на CPU
predicted_class_name = class_names[pred]  # Получаем название предсказанного класса 
println("Предсказанный класс: $predicted_class_name")
Предсказанный класс: ["Cone"]

Сообственно, модель справилась со своей задачей.

LSTM

В заключительном разделе этого примера описывается рабочий процесс LSTM. Сначала определяются уровни LSTM:

Инициализация параметров

Инициализируем параметры, участвующие в обучении модели и подготовки данных

In [ ]:
MaxEpochs = 50;
BatchSize = 100;
learningrate = 0.01;
n_features = 1;
num_classes = 2;

Сбор данных

Признаки, которые подаюся на вход были определены в начале скрипта. Лэйблы несколько переопределим

In [ ]:
Trainlabels = vcat(fill(1, 75), fill(2, 75));
Testlabels = vcat(fill(1, 25), fill(2, 25));

Trainlabels = CategoricalArray(Trainlabels; levels=[1, 2]);
Testlabels = CategoricalArray(Testlabels; levels=[1, 2]);

Далее данные приводятся к виду, который требует на входе сеть LSTM

In [ ]:
train_features = reshape(train_data, 1, size(train_data, 1), size(train_data, 2))
test_features = reshape(test_data, 1, size(test_data, 1), size(test_data, 2))

# TrainFeatures = permutedims(TrainFeatures, (2, 1))
TrainLabels = onehotbatch(Trainlabels, 1:num_classes)
TestLabels = onehotbatch(Testlabels, 1:num_classes)
Out[0]:
2×50 OneHotMatrix(::Vector{UInt32}) with eltype Bool:
 1  1  1  1  1  1  1  1  1  1  1  1  1  …  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅
 ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅  ⋅     1  1  1  1  1  1  1  1  1  1  1  1

Приведем обучающие и тестовые данные в типу DataLoader и переведем их на GPU

Важное учтонение: модель обучается на GPU, поскольку это ускоряет процесс обучения многократно. Если вам необходимо использование GPU - свяжитесь с менеджерами, вам выделят доступ к GPU. В рабочей директории будут лежать веса уже предобученной сети, переведенной на CPU. После обучения основной сети вы можете посмотреть на сеть в формате CPU, подгрузив в модель соответсвующие веса

In [ ]:
train_loader_lstm = DataLoader((data=train_features, label=TrainLabels), batchsize=BatchSize, shuffle=true);
train_loader_lstm = gpu.(train_loader_lstm);
test_loader_lstm = DataLoader((data=test_features, label=TestLabels), batchsize=BatchSize, shuffle=true);
test_loader_lstm = gpu.(test_loader_lstm);

Инициализация модели

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

In [ ]:
model_lstm = Chain(
  LSTM(n_features, 100),
  x -> x[:, end, :], 
  Dense(100, num_classes),
  Flux.softmax) |> gpu;

Инициализируем оптимизатор, функцию потерь

In [ ]:
optLSTM = Flux.Adam(learningrate, (0.9, 0.99));   
lossLSTM(x, y) = Flux.Losses.crossentropy(model_lstm(x), y);

Обучение

Далее идет цикл обучения модели

In [ ]:
for epoch in 1:MaxEpochs
    total_loss = 0.0
    correct_predictions = 0
    total_samples = 0

    total_loss, total_samples, correct_predictions = train_one_epoch(model_lstm, train_loader_lstm, total_loss, 
                                                correct_predictions, total_samples, lossLSTM, optLSTM, "Recurrent")

    # Вычисление точности
    accuracy = 100.0 * correct_predictions / total_samples

    println("Epoch $epoch, Loss: $(total_loss), Accuracy: $(accuracy)%")
end

Сохранение модели

In [ ]:
cpu(model_lstm)
@save "$(@__DIR__)/models/lstm.bson" model_lstm

Оценка обученной модели

Оценим нашу модель, вычислив точность на тестовом наборе данных

In [ ]:
accuracy_score_LSTM, all_predsLSTMm, true_predS_LSTM = evaluate_model_accuracy(test_loader_lstm, model_lstm, classes, lossLSTM, "Recurrent");
println("Accuracy trained model:", accuracy_score_LSTM, "%")
Accuracy trained model:84.0%

Теперь построим матрицу ошибок для визуальной оценки модели

In [ ]:
preds_for_CM_LSTM = map(x -> x[1], all_predsLSTMm);
conf_matrix = CM.confmat(preds_for_CM_LSTM, true_predS_LSTM, levels=[1, 2])
conf_matrix = CM.matrix(conf_matrix)
plot_confusion_matrix(conf_matrix)

Протестируем модель на конкретном наблюдении

In [ ]:
random_index = rand(1:size(test_features, 3))  
random_sample = test_features[:, :, random_index] 
random_label = onecold(TestLabels[:, random_index]) 
random_sample = cpu(random_sample);
In [ ]:
model_data = BSON.load("$(@__DIR__)/models/lstm.bson")
cpu_lstm = model_data[:model_lstm] |>cpu

predicted_probs = cpu_lstm(random_sample)
predicted_class = onecold(predicted_probs) 
# Вывод результата
println("Random Sample Index: $random_index")
println("True Label: $random_label")
println("Predicted Probabilities: $predicted_probs")
println("Predicted Class: $predicted_class")
Random Sample Index: 12
True Label: 1
Predicted Probabilities: Float32[0.99999905; 9.580198f-7;;]
Predicted Class: [1]

Как видно из результатов выше, модель правильно классифицировала предоставленный ей экземпляр

Заключение

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