合成信号的生成网络
这个例子展示了如何使用生成神经网络(GAN)来合成真实的雷达信号。 这对于增加训练样本、建模场景或生成对实际测量访问有限的数据非常有用。
 
生成-对抗网络是两个神经网络,其中生成器将噪声信号作为输入,并试图从中生成一个似是而非的信号,而鉴别器接收这个合成信号或一个真实信号,并通过向生成器发送错误来学习区分假货和现实,以便随着时间的推移它更好地掩盖假货。
发生器从随机分布接收作为一维信号的噪声,并通过神经网络层链将其转换为合成信号,向外应努力与真实信号相似。
鉴别器接收来自发生器的合成信号或来自训练集的真实信号作为输入。 之后,它通过自己的网络来决定它是真的还是假的,并发出概率估计。
基于该估计,计算系统的两个部分的误差,并且生成器学习改进其合成以欺骗鉴别器。 鉴别器学会更准确地将假信号与真实信号区分开来. 随着他们的学习,两个Ann都提高了他们的能力,随着时间的推移,合成的信号几乎与真实的信号无法区分。
导入工作所需的软件包
include("$(@__DIR__)/InstallPackages.jl")
using Pkg
Pkg.instantiate()
using Glob
using DSP
using CUDA
using Flux 
using Statistics
using cuDNN
using BSON: @save, @load 
using Zygote
由于使用的函数具有相当多的代码行数,因此它们已被移动到单独的文件中。下面导入的jls
的模型。jl文件实现所用模型的结构。 的数据集。jl文件用于实现dataset模块的逻辑。
Dirpath = "$(@__DIR__)"
include(joinpath(Dirpath, "model.jl"))
include(joinpath(Dirpath, "dataset.jl"))
创建数据加载器
接下来,我们将创建一个数据加载器,它将批量加载数据到模型中。 在下面的块中,我们将初始化数据集。
数据集逻辑写在数据集中。jl文件
数据集将ECG和雷达信号切片到复盖的窗口上。 切片允许您减少由于使用长序列而对ANN的负载,并且使用重叠允许您考虑时间依赖性。
function mycollate(batch)
    radar = hcat(first.(batch)...)           
    ecg   = hcat(last.(batch)...)          
    return radar, ecg
end
ds = RadarECG("prepareRadarData");
N = length(ds)
train_idx = 1:N
X_train = [ ds[i][1] for i in train_idx ]  
Y_train = [ ds[i][2] for i in train_idx ]    
batch_size = 64
train_loader = Flux.DataLoader((X_train, Y_train);
    batchsize = batch_size,
    shuffle   = true,
    partial      = true,
    parallel = true,
    collate   = mycollate
);
之后,我们将确定模型将被训练的设备。 如果您可以访问GPU,训练将加速几个数量级。 我们还会将数据加载器传输到我们正在使用的设备。
device = CUDA.functional() ? gpu : cpu;
device == gpu ? train_loader = gpu.(train_loader) : nothing;
初始化模型
接下来,我们初始化ANN生成器和鉴别器,并定义使用的优化器,在这种情况下,Adam。
nz = 100
net_G = Generator(nz)
net_D = Discriminator()
    
net_G = gpu(net_G)
net_D = gpu(net_D);
η_D, η_G = 5e-5, 2e-4
optD = Adam(η_D, (0.5, 0.999))            # без WeightDecay
optG = Adam(η_G, (0.5, 0.999));
让我们定义保存训练模型的路径。
isdir("output")||mkdir("output")
Dirpath = "$(@__DIR__)"
train_path = joinpath(Dirpath, "output");
模型训练
让我们定义执行GAN训练的函数,以及每个子网(生成器和鉴别器)的损失函数。
function discr_loss(real_output, fake_output)
    real_loss = Flux.logitbinarycrossentropy(real_output, 1f0)
    fake_loss = Flux.logitbinarycrossentropy(fake_output, 0f0)
    return (real_loss + fake_loss) / 2
end
generator_loss(fake_output) = Flux.logitbinarycrossentropy(fake_output, 1f0)
function train_discr(discr, original_data, fake_data, opt_discr)
    ps = Flux.params(discr)
    loss, back = Zygote.pullback(ps) do
                      discr_loss(discr(original_data), discr(fake_data))
    end
    grads = back(1f0)
    Flux.update!(opt_discr, ps, grads)
    return loss
end
Zygote.@nograd train_discr
function train_gan(gen, discr, original_data, opt_gen, opt_discr, nz, bsz)
    noise = randn(Float32, 2, nz, bsz) |> gpu
    loss = Dict()
    ps = Flux.params(gen)
    loss["gen"], back = Zygote.pullback(ps) do
                          fake_ = gen(noise)
                          loss["discr"] = train_discr(discr, original_data, fake_, opt_discr)
                          generator_loss(discr(fake_))
    end
    grads = back(1f0)
    Flux.update!(opt_gen, ps, grads)
    return loss
end
我们开始训练吧
train_steps = 0
epochs = 100
verbose_freq = 40
for ep in 1:epochs
    @info "Epoch $ep"
    for (i, (radar, _)) in enumerate(train_loader)
        radar = reshape(radar, size(radar,1), 1, size(radar,2)) |> gpu
        radar .+= 0.05f0 .* randn(Float32, size(radar)) |> gpu 
        loss = train_gan(net_G, net_D, radar, optG, optD, nz, batch_size)
        if train_steps % verbose_freq == 0
            noiseZ = randn(Float32,2,nz, batch_size) |> gpu
            P_real = mean(sigmoid.(net_D(radar)))
            P_fake = mean(sigmoid.(net_D(net_G(noiseZ))))
            @info("[$ep/$(epochs)] step $train_steps  " *
                  "P(real)=$(round(P_real,digits=3))  " *
                  "P(fake)=$(round(P_fake,digits=3))")
        end
        train_steps += 1
    end
end
接下来,我们将保存训练好的模型
# Переносим модели на CPU
train_dir = joinpath(Dirpath, "output")
net_D_cpu = cpu(net_D)
net_G_cpu = cpu(net_G)
# Пути для сохранения
output_dir_D = joinpath(train_dir, "modelsD_bson")
output_dir_G = joinpath(train_dir, "modelsG_bson")
# создаём папки, если их нет
isdir(output_dir_D) || mkdir(output_dir_D)
isdir(output_dir_G) || mkdir(output_dir_G)
# имена файлов для лучших моделей
best_path_D = joinpath(output_dir_D, "discriminator.bson")
best_path_G = joinpath(output_dir_G, "generator.bson")
# сохраняем лучший дискриминатор
@info "Сохраняем дискриминатор в $best_path_D"
@save best_path_D net_D_cpu
# сохраняем лучший генератор
@info "Сохраняем генератор в $best_path_G"
@save best_path_G net_G_cpu
测试模型
从噪声中生成信号
noise = randn(Float32, 1, 100, 16) |> gpu
fake  = net_G(noise)
fakec = cpu(fake);
ind = 2
plot(fakec[:, 1, ind]/6)
结论
在本演示示例中,对GAN神经网络进行了训练,以根据真实数据生成合成数据。