如何使用自动编码器进行信号去噪(附教程)

332 阅读3分钟

信号

信号可以被定义为一个数量在空间或时间上的任何可观察的变化,即使它不携带信息。它们主要可以分为两种类型。

  • 模拟信号
  • 数字信号

模拟信号

一个模拟信号是一个连续的数值流。有多种可能的值。

数字信号

一个数字信号是一个离散的数值流。只有某些可能的值。

项目简介

本项目旨在生成一个正弦信号,向其添加加性白高斯噪声(AWGN),并使用自动编码器模型对其进行去噪。

库导入

import numpy as np
import matplotlib.pyplot as plt

生成正弦波信号

为了生成一个正弦信号样本,我们可以使用下面的代码

t = np.linspace(1,100,1000)

v = 10*np.sin(t/(2*np.pi))

这将产生一个信号,看起来就像

image

现在我们通过使用以下代码来计算上述生成的信号的功率

w = v ** 2

生成的信号现在看起来就像

image

现在,为了将功率从瓦特转换为分贝,我们将使用以下代码

w_db = 10 * np.log10(w)

以分贝为单位的功率图将看起来像

image

噪声生成

我们选择一个目标SnR或信噪比,计算平均功率并将其转换为dB。然后,我们计算噪声的平均值(dB),将其转换为瓦特,然后使用计算出的参数从正态分布中取样,并将其加入生成的信号中,得到噪声信号。

target_snr_db = 20
# Calculate signal power and convert to dB 
sig_avg_watts = np.mean(w)
sig_avg_db = 10 * np.log10(sig_avg_watts)
# Calculate noise according to [2] then convert to watts
noise_avg_db = sig_avg_db - target_snr_db
noise_avg_watts = 10 ** (noise_avg_db / 10)
# Generate an sample of white noise
mean_noise = 0
noise_volts = np.random.normal(mean_noise, np.sqrt(noise_avg_watts), len(w))
# Noise up the original signal
y_volts = v + noise_volts

在这之后,带有噪声的信号会看起来像

image

为了训练深度学习模型,我们需要一些数据样本。因此,我们将通过定义使用上述信号和噪声发生器逻辑的函数来随机生成这些样本。

def signal_gen():
  l = np.random.randint(1, 100)
  t = np.linspace(1,l,1000)
  v = 10*np.sin(t/(2*np.pi)) / 1000
  return v


def noise_gen(v):
  w = v ** 2
  target_snr_db = 20
  sig_avg_watts = np.mean(w)
  sig_avg_db = 10 * np.log10(sig_avg_watts)
  noise_avg_db = sig_avg_db - target_snr_db
  noise_avg_watts = 10 ** (noise_avg_db / 10)
  mean_noise = 0
  noise_volts = np.random.normal(mean_noise, np.sqrt(noise_avg_watts), len(w))
  y_volts = v + noise_volts
  return y_volts

要从生成的数据集中查看一个样本,我们可以使用下面的代码片段。

v = signal_gen()

plt.subplot(2,1,1)
plt.title("Random Signal")
plt.plot( v)
plt.show()

plt.subplot(2,1,2)
plt.title("Random Signal with noise")
plt.plot(noise_gen(v))
plt.show()

生成的结果将看起来像这样

image

为了生成数据集,我们使用下面的代码片断。

signal = []
noisy_signal = []

for i in range(1000):
  v = signal_gen()
  signal.append(v)
  noisy_signal.append(noise_gen(v))

定义深度学习模型

为了执行这个去噪,我们使用一个简单的线性自动编码器模型。这将有一个编码器层和一个解码器层。每个输入样本的大小为1000,该模型总共有1000个数据点。

import torch.nn as nn
import torch.nn.functional as F

class DeNoise(nn.Module):
  def __init__(self):
    super(DeNoise, self).__init__()
    
    self.lin1 = nn.Linear(1000, 800)
    self.lin_t1 = nn.Linear(800, 1000)


  def forward(self, x):
    x = F.tanh(self.lin1(x))
    x = self.lin_t1(x)
    return x

model = DeNoise().cuda()
print(model)

这里我们使用tanh激活函数,因为我们知道正弦函数的最大和最小边界是-1和1,具有类似的边界,我们发现它最适合这种应用。

定义损失和优化函数

import torch

criterion = nn.MSELoss()

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

定义训练环路

def train(n_epochs , model):
  training_loss = []

  for epoch in range(n_epochs):
    trainloss = 0.0
    for sig, noisig in zip(signal, noisy_signal):

      sig = torch.Tensor(sig).cuda()
      noisig = torch.Tensor(noisig).cuda()

      optimizer.zero_grad()
      output = model(noisig)
      loss = criterion(output , sig) 
      loss.backward()
      optimizer.step()
      trainloss += loss.item()  
    print("Epoch: {} , Training Loss: {}".format(epoch + 1  , trainloss / len(signal)))
    training_loss.append(trainloss / len(signal))
  plt.plot(training_loss)
        
  print("Training Completed !!!")

训练模型

train(10, model)

训练的结果看起来像

image

结果的可视化

def plot(i):

  pred = model(torch.Tensor(signal[i]).cuda()).cpu()
  plt.subplot(4,1,1)
  plt.title("Original Signal")
  plt.xlabel("Voltage")
  plt.ylabel("Time")
  plt.plot(signal[i])
  plt.show()

  plt.subplot(4,1,2)
  plt.title("Noisy Signal")
  plt.xlabel("Voltage")
  plt.ylabel("Time")
  plt.plot(noisy_signal[i])
  plt.show()

  plt.subplot(4,1,3)
  plt.title("Predicted Signal")
  plt.xlabel("Voltage")
  plt.ylabel("Time")
  plt.plot(pred.detach().numpy())
  plt.show()
 

  

上述模块将产生如下结果

image

image

image

总结

因此,在这个项目中,我们已经成功地实现了一个使用基于PyTorch的深度学习模型的信号去噪器。

代码

GitHub:github.com/srimanthten…