利用机器学习和深入学习对雷达目标进行分类
本示例演示了使用机器和深度学习方法对雷达数据进行分类的方法。 以下方法用于解决问题:
- **机器学习:**支持向量机(SVM)。
- **深度学习:**SqueezeNet,LSTM
目标分类是雷达系统的一项重要任务。 在这个例子中,我们考虑确定哪个物体反射雷达信号的问题—圆柱体或圆锥体。 虽然该示例使用合成数据,但这种方法也可以应用于真实的雷达测量。
SVM培训
首先,您需要导入您使用的软件包
在文件中 install_packages.jl 有脚本所需的包。 它们被添加到工作环境中。 在文件中 import_packages 将为脚本导入所有已安装的软件包。 在下面的单元格中,我们运行它们的执行
include("$(@__DIR__)/Install_packages.jl")
include("$(@__DIR__)/import_packages.jl")
init()
加载数据
用于训练模型的数据取自雷达对象EPR建模的演示示例。
让我们写下数据所在的路径
data_path = "$(@__DIR__)/gen_data.csv"
CSV文件包含锥体和圆柱体的数据。 前100个元件是圆柱体,后100个元件是圆锥体。 让我们将数据拆分为训练和测试数据集。
# 比方说,你的文件被称为"数据。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);
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);
下面的代码使用Morlaix连续小波函数变换从时间序列中提取特征。 首先,对所有数据应用小波变换,随后计算系数的绝对值,以便仅留下振幅。 然后沿时间轴对这些幅度进行平均,这降低了特征的维数。 之后,结果被"压缩"以删除不必要的尺寸。
所有这些都是为训练和测试数据集完成的。
wavelet_cfg = wavelet(Morlet(π), averagingType=Dirac(), β=1.5)
train_features_cwt = dropdims(mean(abs.(cwt(TrainFeatures_T, wavelet_cfg)), dims=2), dims=2);
wavelet_cfg = wavelet(Morlet(π), averagingType=Dirac(), β=1.5)
test_features_cwt = dropdims(mean(abs.(cwt(TestFeatures_T, wavelet_cfg)), dims=2), dims=2);
初始化包含类名的列表
class_names = ["Cylinder","Cone"]
在训练分类器之前,必须将数据转换为与模型兼容的格式:训练特征转换为表格格式,标签转换为分类类型。
X_train = MLJ.table(train_features_cwt) # 将X_train转换为dataframe
X_test = MLJ.table(test_features_cwt) # 将X_test转换为dataframe
y_train = coerce(vec(TrainLabels), Multiclass) # 将y_train转换为向量并设置多类类型
y_test = coerce(vec(TestLabels), Multiclass); # 将y_test转换为向量并设置多类类型
支持向量模型的初始化及其训练
接下来,我们通过初始化参数和交叉验证来配置支持向量模型。
svm = (@MLJ.load SVC pkg=LIBSVM verbosity=true)() # 通过MLJ使用LIBSVM下载并创建SVM模型
# 设置SVM模型的参数
svm.kernel = LIBSVM.Kernel.Polynomial # 核类型:多项式
svm.degree = 2 # 多项式核的多项式的程度
svm.gamma = 0.1 # 控制每个训练点影响的参数γ
svm.cost = 1.0 # 正则化参数
# 创建"机器"以将模型与数据相关联
mach = machine(svm, X_train, y_train)
# 使用5个折叠设置交叉验证
cv = CV(nfolds=5);
我们使用交叉验证来训练模型,并计算训练的准确性
@info "Load model config, cross-valid"
cv_results = evaluate!(mach; resampling=cv, measures=[accuracy], verbosity=0) # 执行模型的交叉验证
println("模型精度:$(cv_results.测量[1]*100)" , "%")
训练模型的评估
让我们根据测试数据评估训练好的模型
@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 执行此任务
function plot_confusion_matrix(C)
# 使用大字体和对比色创建热图
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;
# 使用函数的示例
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是2016年提出的一个紧凑卷积神经网络,以显着更小的尺寸实现AlexNet性能。 它使用包括挤压层(1x1卷积以减少通道数量)和扩展(1x1和3x3卷积以恢复维度)的Fire模块,这减少了参数数量而不会损失质量。 由于其紧凑性,它适用于嵌入式设备。
所需参数
初始化模型训练和数据准备中涉及的参数
batch_size = 2 # 训练批次的大小
num_classes = 2 # 分类问题中的类数
lr = 1e-4 # 学习率
Epochs = 15 # 培训的时代数目
Classes = 1:num_classes; # 类索引的列表,例如,1和2
创建数据集
首先,有必要为网络训练准备数据。 为了获得信号的时频特性,需要对信号进行连续小波变换和构造。 小波被"压缩"以定位具有高时间精度的短期突发,并被"拉伸"以捕获信号结构中的平滑变化。
辅助功能 save_wavelet_images 获得连续小波变换 (CWT) 对于每个雷达信号,它将结果转换为与计算机视觉模型兼容的格式,并将频谱图保存为图像。
初始化几个辅助函数
# 用于规范化值的函数
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;
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 # 气缸类的示例数
println(class1_count)
# 为每个类创建文件夹
cone_path = joinpath(save_path, "cone") # 锥体-1级
cylinder_path = joinpath(save_path, "cylinder") # 气缸-2级
mkpath(cone_path)
mkpath(cylinder_path)
# 处理圆柱类的矩阵行
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 它是一种基于Morlaix小波的可配置小波变换器
c = wavelet(Morlet(π), averagingType=NoAve(), β=1);
我们将得到一个用于训练神经网络的图像数据库
save_wavelet_images(TrainFeatures_T, c, "$(@__DIR__)/New_imgs/train")
save_wavelet_images(TestFeatures_T, c, "$(@__DIR__)/New_imgs/test", false)
让我们来看看生成的图像的一个实例
i = Images.load("$(@__DIR__)/New_imgs/train/cylinder/sample_2.png")
初始化用于数据增广的函数:它负责将图像缩小到224x224的大小,并将数据转换为张量。
function Augment_func(img)
resized_img = imresize(img, 224, 224) # 将图像大小调整为224x224
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 通过处理图像所在的目录来创建训练数据集。
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
# 处理火车文件夹
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
# 处理测试文件夹
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;
在代码的下一个单元格中,我们将执行创建训练集和测试集的功能。
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格式的网络。
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"
培训准备
通过将模型传输到GPU来初始化模型
Net = SqueezeNet(; pretrain=false,
nclasses = num_classes) |>gpu;
初始化优化器,损失函数
optimizer_Snet = Flux.Adam(lr, (0.9, 0.99));
lossSnet(x, y) = Flux.Losses.logitcrossentropy(Net(x), y);
挤压训练
让我们描述一下负责为一个时代训练模型的函数。 功能 train_one_epoch 在单个epoch上执行模型训练,通过加载程序中的所有数据批次 Loader. 稍后,该函数将用于训练模型。 LSTM. 这个函数有一个参数 type_model 这将决定我们正在训练哪个特定模型-卷积或循环
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) # 对模型进行预测
# 接下来,我们根据epoch计算模型的精度和误差
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学习流程
@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
保存我们训练好的模型
mkdir("$(@__DIR__)/models")
cpu(Net)
@save "$(@__DIR__)/models/SNET.bson" Net
训练好的SqueezeNet模型的评估
我们来评估训练好的模型。 功能 evaluate_model_accuracy 负责计算模型的精度
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;
accuracy_score_Snet, all_predsSnet, true_predS = evaluate_model_accuracy(test_loader_Snet, Net, Classes, lossSnet, "Conv");
println("Accuracy trained model:", accuracy_score_Snet, "%")
正如您在上面看到的,模型的准确性是100%。 这清楚地表明,模型完美地将两个类彼此分开。 让我们来看一个模型预测的具体例子。
让我们使用函数构造误差矩阵 plot_confusion_matrix
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)
模型预测
从测试数据集上传图像
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);
加载模型的权重
model_data = BSON.load("$(@__DIR__)/models/SNET.bson")
snet_cpu = model_data[:Net] |> cpu;
做出预测
y_pred = (snet_cpu(img_res))
pred = onecold(y_pred, Classes)
# pred=cpu(preds)#将预测传输到CPU
predicted_class_name = class_names[pred] # 我们得到预测类的名称
println("预测类:$predictedclass_name")
因此,该模型应对其任务。
LSTM
本示例的最后一节介绍了LSTM工作流。 首先,确定LSTM水平:
参数的初始化
初始化模型训练和数据准备中涉及的参数
MaxEpochs = 50;
BatchSize = 100;
learningrate = 0.01;
n_features = 1;
num_classes = 2;
数据收集
提交给输入的符号是在脚本开始时定义的。 标签在某种程度上被重新定义
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网络在输入端所需的形式。
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)
让我们将训练和测试数据放入类型 DataLoader 并将它们传输到GPU。
**重要提示:**模型是在GPU上训练的,因为这会使学习过程加快很多倍。 如果您需要使用GPU,请联系我们的经理,您将获得GPU的访问权限。 工作目录将包含传输到CPU的已经预训练的网络的权重。 在训练主网络之后,您可以通过将适当的权重加载到模型中来查看CPU格式的网络。
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);
初始化模型
初始化我们将训练的模型。 在这个例子中,我们的模型是一个相互连接的层链。
model_lstm = Chain(
LSTM(n_features, 100),
x -> x[:, end, :],
Dense(100, num_classes),
Flux.softmax) |> gpu;
初始化优化器,损失函数
optLSTM = Flux.Adam(learningrate, (0.9, 0.99));
lossLSTM(x, y) = Flux.Losses.crossentropy(model_lstm(x), y);
培训课程
接下来是模型的训练周期
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
保存模型
cpu(model_lstm)
@save "$(@__DIR__)/models/lstm.bson" model_lstm
训练模型的评估
让我们通过计算测试数据集的准确性来评估我们的模型。
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, "%")
现在让我们构建一个误差矩阵,用于模型的可视化评估。
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)
让我们在一个特定的观测值上测试模型
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);
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")
从上面的结果可以看出,模型正确地对提供给它的实例进行了分类。
结论
此示例显示了使用机器学习和深度学习技术执行雷达目标分类的工作流程。 虽然该示例使用合成数据进行训练和测试,但可以轻松扩展以考虑雷达的实际结果。
