基于深度学习的信道估计【附Python代码】

摘要-在本文中,我们提出了一种用于通信系统中信道估计的深度学习(DL)算法。我们认为快衰落信道的时频响应是一个二维图像。其目的是使用导频位置处的一些已知值来找到信道响应的未知值。为此,提出了一种使用深度图像处理技术、图像超分辨率(SR)和图像恢复(IR)的通用流水线。该方案将导频值视为低分辨率图像,并使用SR网络与去噪IR网络级联来估计信道。此外,建议的管道的实施。估计误差表明,该算法是可比的最小均方误差(MMSE)的信道统计的充分知识,它是优于ALMMSE(线性MMSE的近似)。结果证实,该流水线可以有效地用于信道估计。

索引词-信道估计,深度学习,图像超分辨率,图像恢复

I.介绍

正交频分复用(OFDM)是一种在通信系统中广泛使用的调制方法,用于解决无线信道中的频率选择性衰落。在通信信道中,接收信号通常会因信道特性而失真。为了恢复传输的符号,必须在接收机处估计和补偿信道效应。通常,接收机使用被称为导频的符号来估计信道,这些符号在时频中的位置和值对于发射机和接收机都是已知的。根据这些导频布置,可以考虑三种不同的结构:块型、梳型和网格型[1]。在块型布置中,在OFDM块的开始处在所有子载波处周期性地发送导频,而在梳型布置中,导频存在于几个OFDM符号的几个子载波中。在格型布置中,导频以给定周期沿时间和频率轴两者沿着插入菱形星座中。

传统的基于导频的估计方法,即,最小二乘(LS)和最小均方误差(MMSE)利用时频网格中的导频值来找到信道响应的未知值。这些算法已经在各种条件下进行了优化[2]。与不需要关于信道的统计信息的LS估计相比,MMSE估计通过利用信道和噪声方差的统计信息而导致更好的性能。为了在实际场景中使用MMSE,提出了一些方法,这些方法降低了该方案的复杂度,并且使用信道统计的估计而不是确切的信息。在文献[3]中,提出了一种在快衰落信道下的近似线性MMSE(ALMMSE),由于减小了相关矩阵和滤波矩阵的大小,其复杂度远小于原始MMSE。

最近,深度学习(DL)在通信系统中受到了广泛的关注。在基于DL的通信系统中,已经提出了一些方法来增强不同传统算法的性能,包括调制识别[4]、信号检测[5]、信道均衡[6]、信道状态信息(CSI)反馈[7]和信道估计[8]、[9]。在[8]中,通信系统被认为是黑盒子,并且端到端DL架构用于信号发送/接收。编码、解码、信道估计和通信链路的所有其他功能都隐式地嵌入在DL块中。更具体地,该方法不能明确地找到信道时频响应,因此对于需要具有完整信道响应的应用无效。在[9]中,信道矩阵被认为是一个图像,然后使用去噪网络进行信道估计。这项工作的重点是信道矩阵沿着发射机/接收机天线空间(在多天线的情况下),而不是讨论每个Tx/Rx链路的时间-频率响应。

基于此,本文提出了一种基于DL的OFDM系统信道估计框架。在该方法中,信道响应的时频网格被建模为仅在导频位置处已知的2D图像。这个信道网格与几个导频被认为是一个低分辨率(LR)的图像和估计的信道作为一个高分辨率(HR)的。提出了一种两阶段的方法来估计信道网格。首先,使用图像超分辨率(SR)算法来增强LR输入的分辨率。之后,图像恢复(IR)的方法被用来消除噪声的影响。对于SR和IR网络,我们分别使用了两种最近开发的基于CNN(卷积神经网络)的算法,SRCNN [10]和DnCNN [11]。本文的主要贡献如下:

1)将信道时频响应建模为图像。

2)将导频位置中的信道响应视为LR图像,并将估计的信道响应视为建议的HR图像。

3)使用基于DL的图像超分辨率和图像去噪技术来估计信道。

本文的其余部分组织如下。第二部分简要介绍了传统的信道估计方法。第三节介绍了所提出的DL基信道估计器的结构。在第四节中,模拟结果,最后第五节结束了本文。

二.背景

在OFDM系统中,对于第k个时隙和第i个子载波,输入输出关系表示为:

考虑大小为NS × ND的OFDM子帧,时隙索引k在[0,ND-1]之间,并且子载波索引i的范围是[0,NS-1]。在(1)中,Yi,k、Xi,k和Zi,k分别是接收信号、发送OFDM符号和白色高斯噪声。Hi,k是H ∈ CNS×ND的(i,k)元.H表示对于所有子载波和时隙的信道的时频响应。

为了估计信道,特别是在具有衰落的信道中,时域响应被表示为H = {h[1],h[2],.,h[ND]},其中每个h[k]是第k个时隙处的信道频率响应。

LS方法估计导频位置处的信道。如果我们将LS估计信道视为对角矩阵HLS p ∈ CNP×NP,则HLS p可以通过求解来估计:

为了找到导频位置以外的点处的信道值,我们必须应用二维插值方法。比LS更好的选择是MMSE估计器,其通过将导频符号位置处的LS估计与滤波矩阵AMMSE ∈ CNL×NP相乘而获得[12]:

为了找到滤波矩阵,均方误差(MSE),

必须最小化。最小化(4)导致

其中矩阵Rhdhp = E{hdhH p }表示期望子帧与导频符号之间的信道相关矩阵,并且矩阵Rhphp = E{hphH p }是导频符号处的信道相关矩阵。显然,MMSE仅在表示为R的信道的相关矩阵完全已知的情况下才有用。

图1.样本通道时频网格的归一化真实的/虚二维图像示例

B.超分辨率与图像复原

考虑到低分辨率和噪声图像,已经提出了几种技术来再现更高分辨率和更少噪声的图像。图像超分辨率(SR)是一类用于图像分辨率增强的技术。基于DL的算法,特别是具有深度和完全卷积网络的算法,在从LR图像输入恢复HR图像的问题中实现了高性能。最近,超分辨率卷积神经网络(SRCNN)[10]被提出以端到端的方式在LR/HR图像之间进行映射。除了SR技术之外,可以应用图像恢复(IR)算法来去除/减少图像上的噪声影响。在文献中已经提出了用于IR的各种模型。例如,在[11]中,提出了一种前馈去噪卷积神经网络(DnCNN)方案,该方案利用残差学习和批量归一化来加快训练过程。

图2.所提出的基于DL的信道估计流水线

三.信道网络

A.通道图像

在这项工作中,我们专注于一对Tx和Rx天线之间的一个链路,即,单输入单输出(SISO)通信链路。对于该链路,发射机和接收机之间的具有复值的信道时间-频率响应矩阵H(大小为NS ×ND)可以表示为两个2D图像(一个2D图像用于真实的值,另一个用于虚值)。图1中示出了具有ND = 14个时隙和NS = 72个子载波(基于长期演进(LTE)标准)的样本信道时频网格的归一化真实的/虚的2D图像的示例。

B.网络结构

所提出的用于基于DL的信道估计的流水线的概述,命名为DielNet,如图2所示。目标是使用所发送的导频来估计信道的整个时频。类似于LTE标准,格型导频布置已被用于导频传输。在导频位置h ∈ LS p处的信道的估计值(其可能是有噪声的)被认为是信道图像的LR和有噪声版本。为了获得完整的信道图像,提出了两阶段训练方法:

·在第一阶段中,实现SR网络,其将Lsp作为矢量化的低分辨率输入图像(once实部,然后是虚部),并估计信道响应H的未知值。

·在去除噪声影响的第二阶段中,去噪IR网络与SR网络级联。

对于SR和IR,我们分别使用SRCNN [10]和DnCNN [11]。由于篇幅所限,我们无法用图片展示它们的结构。在高层次上,SRCNN首先使用插值方案来找到高分辨率图像(通道)的近似值,然后使用三层卷积网络提高分辨率。第一个卷积层使用64个大小为9 × 9的滤波器,第二层使用32个大小为1 × 1的滤波器,两者都是ReLu激活的。最后一层仅使用一个大小为5 × 5的滤波器来重建图像。DnCNN(详见[11])是一种基于残差学习的网络,由20个卷积层组成。第一层使用64个大小为3 × 3 × 1的过滤器,然后是ReLU。随后的18个卷积层中的每一个都使用64个大小为3×3×64的过滤器,然后进行批量归一化和ReLU。最后一层使用一个3 × 3 × 64滤波器来重建输出。

C.训练

让我们用Θ = {ΘS,ΘR}表示所有网络参数的集合,其中ΘS和ΘR分别表示SR和IR网络的参数值的集合。到网络的输入是导频值向量Lsp,并且输出是估计的信道矩阵,表示为Lsp:

其中fS和fR分别是SR和IR函数。网络的总损失函数是估计的信道响应与实际信道响应之间的均方误差(MSE),计算如下:

为了简化训练过程,我们使用两阶段训练算法。其中,在第一阶段中,我们最小化SR网络的损耗C1:

在第二阶段中,我们冻结SR网络的权重,并通过定义H  = fR(Z; ΘD)并最小化损失函数C2来找到去噪网络的参数:

请注意,与基于图像的技术类似,网络的最佳权重取决于SNR的值;因此,为了获得完整的解决方案,我们必须针对每个SNR值重新训练网络。这种方法实际上不可能实现,因为SNR值是连续的。然而,幸运的是,正如第四节中的结果所证明的那样,针对几个SNR值(在我们的情况下只有两个值)的训练网络仍然可以带来良好的性能。

四.仿真结果

在本节中,我们将训练网络并在一系列SNR上评估MSE,并将结果与广泛使用的基线算法进行比较。

我们考虑在发射器和接收器处的单个天线。对于信道建模和导频传输,我们使用了维也纳大学开发的广泛使用的LTE模拟器,维也纳LTE-A模拟器[13]。Keras和使用GPU后端的Tensorflow用于实现我们提出的方案。对于SR和IR网络,训练率设置为0.001,批量大小为128,最多迭代500次。训练、测试和验证集分别由32000、4000和4000个通道组成1。与LTE一样,在我们的模拟中,每个帧由14个时隙和72个子载波组成。对于VehicularA(VehA)和SUI 5(具有长延迟扩展的模型)的无线信道模型,考虑具有2.1GHz的载波频率、1.6MHz的带宽和50 km/h的UE(用户设备)速度。

为了观察性能,我们将所提出的方法的信道估计精度与三种最先进的算法(即,理想MMSE、估计MMSE和理想ALMMSE [3])的信道估计精度进行了比较,其中在每帧中使用48个导频。估计的和实际的信道实现之间的MSE被认为是性能度量。

图3.针对VehA信道模型的根据SNR的信道估计MSE。

VehA的结果见图3。注意,理想的MMSE具有最佳性能,并且给出了可实现的MSE的下限,因为信道相关矩阵应该是完全已知的(没有任何误差),这在实际应用中不是有效的假设。估计的MMSE尝试基于接收的信号估计相关矩阵,并且理想的ALMMSE是理想MMSE的近似对应物(但仍然具有信道统计的完整知识)。

在图3中,证明了对于低SNR值,在12dB的SNR值(由深低SNR表示)下训练的所提出的MPENELNet具有与理想MMSE相当的性能,并且具有比理想ALMMSE和估计的MMSE更好的性能。另外,可以观察到,在大约中间SNR值之后,性能在SNR值为22dB(由深度高SNR表示)的情况下训练的网络的性能将优于深度低SNR。

因此,我们将SNR范围分为两个区域。当信噪比较低时,采用深度低信噪比网络进行信道估计,当信噪比超过一定阈值时,采用深度高信噪比网络进行信道估计。可以观察到,对于高于23 dB的SNR值,深度高SNR的性能将再次失败,并且必须训练另一个网络;尽管只要SNR低于20 dB,两个生成的网络就足够了。

图4.针对SUI5信道模型的根据SNR的信道估计MSE。

与SUI5模型相关的MSE结果如图4所示。一般来说,由于信道的复杂度较高,所有方案的性能都低于VehA模型。更有趣的是,在SNR值为5dB之后,我们可以观察到ALMMSE和估计MMSE等方案显着下降,而所提出的深度模型仍然可以发现底层统计数据并获得可接受的MSE。正如我们所期望的,理想MMSE具有最好的性能,但它在实际情况下是无法实现的,因为它需要正确的信道统计的充分知识。

图5.信道估计的均方误差与导频数的关系。

为了显示所提出的算法的性能,考虑到VehA信道模型,在20dB的SNR水平下针对不同数量的导频的仿真结果如图5所示。可以看出,在SNR的该特定值处训练的MSNelNet优于估计MMSE和理想ALMMSE方法,并且其与理想MMSE相当。

五.结论

在本文中,我们提出了一种基于DL的通信系统信道估计算法-。在这种方法中,我们考虑了时频响应,将衰落信道作为二维图像,并应用SR和IR算法,基于导频值来发现整个信道状态。实验结果表明,该算法的性能与MMSE算法相比具有很强的竞争力。已经提出了两步网络训练过程,并且我们还讨论了如何使用多个网络来最好地估计信道。

python代码

import numpy as np
import math
from models import interpolation , SRCNN_train , SRCNN_model, SRCNN_predict , DNCNN_train , DNCNN_model , DNCNN_predict
#from scipy.misc import imresize
from scipy.io import loadmat
import matplotlib.pyplot as plt
​
​
​
if __name__ == "__main__":
    # load datasets 
    channel_model = "VehA"
    SNR = 22
    Number_of_pilots = 48
    perfect = loadmat("Perfect_"+ channel_model.mat')['My_perfect_H']
    noisy_input = loadmat("Noisy_" + channel_model + "_" + "SNR_" + str(SNR) + ".mat") [channel_model+"_noisy_"+ str(SNR)]             
                      
    interp_noisy = interpolation(noisy_input , SNR , Number_of_pilots , 'rbf')
​
    perfect_image = numpy.zeros((len(perfect),72,14,2))
    perfect_image[:,:,:,0] = numpy.real(perfect)
    perfect_image[:,:,:,1] = numpy.imag(perfect)
    perfect_image = numpy.concatenate((perfect_image[:,:,:,0], perfect_image[:,:,:,1]), axis=0).reshape(2*len(perfect), 72, 14, 1)
    
    
    ####### ------ training SRCNN ------ #######
    idx_random = numpy.random.rand(len(perfect_image)) < (1/9)  # uses 32000 from 36000 as training and the rest as validation
    train_data, train_label = interp_noisy[idx_random,:,:,:] , perfect_image[idx_random,:,:,:]
    val_data, val_label = interp_noisy[~idx_random,:,:,:] , perfect_image[~idx_random,:,:,:]    
    SRCNN_train(train_data ,train_label, val_data , val_label , channel_model , Number_of_pilots , SNR )
    
   
    ####### ------ prediction using SRCNN ------ #######
    srcnn_pred_train = SRCNN_predict(train_data, channel_model , num_pilots , SNR)
    srcnn_pred_validation = SRCNN_predict(train_data, channel_model , num_pilots , SNR)  
                      
                      
    ####### ------ training DNCNN ------ #######
    DNCNN_train(input_data, channel_model , num_pilots , SNR):
                      
​
    
​
from keras.models import Sequential,  Model
from keras.layers import Convolution2D,Input,BatchNormalization,Conv2D,Activation,Lambda,Subtract,Conv2DTranspose, PReLU
from keras.regularizers import l2
from keras.layers import  Reshape,Dense,Flatten
# from keras.layers.advanced_activations import LeakyReLU
from keras.callbacks import ModelCheckpoint
from keras.optimizers import SGD, Adam
from scipy.io import loadmat
import keras.backend as K
# from keras.layers.advanced_activations import LeakyReLU
from keras.callbacks import ModelCheckpoint
from keras.optimizers import SGD, Adam
import numpy as np
import math
from scipy import interpolate
#from scipy.misc import imresize
​
​
def psnr(target, ref):
    # assume RGB image
    target_data = np.array(target, dtype=float)
    ref_data = np.array(ref, dtype=float)
​
    diff = ref_data - target_data
    diff = diff.flatten('C')
​
    rmse = math.sqrt(np.mean(diff ** 2.))
​
    return 20 * math.log10(255. / rmse)
​
def interpolation(noisy , SNR , Number_of_pilot , interp)
    noisy_image = np.zeros((40000,72,14,2))
​
    noisy_image[:,:,:,0] = np.real(noisy)
    noisy_image[:,:,:,1] = np.imag(noisy)
​
​
    if (Number_of_pilot == 48):
        idx = [14*i for i in range(1, 72,6)]+[4+14*(i) for i in range(4, 72,6)]+[7+14*(i) for i in range(1, 72,6)]+[11+14*(i) for i in range(4, 72,6)]
    elif (Number_of_pilot == 16):
        idx= [4+14*(i) for i in range(1, 72,9)]+[9+14*(i) for i in range(4, 72,9)]
    elif (Number_of_pilot == 24):
        idx = [14*i for i in range(1,72,9)]+ [6+14*i for i in range(4,72,9)]+ [11+14*i for i in range(1,72,9)]
    elif (Number_of_pilot == 8):
      idx = [4+14*(i) for  i in range(5,72,18)]+[9+14*(i) for i in range(8,72,18)]
    elif (Number_of_pilot == 36):
      idx = [14*(i) for  i in range(1,72,6)]+[6+14*(i) for i in range(4,72,6)] + [11+14*i for i in range(1,72,6)]
​
​
​
    r = [x//14 for x in idx]
    c = [x%14 for x in idx]
​
​
​
    interp_noisy = np.zeros((40000,72,14,2))
​
    for i in range(len(noisy)):
        z = [noisy_image[i,j,k,0] for j,k in zip(r,c)]
        if(interp == 'rbf'):
            f = interpolate.Rbf(np.array(r).astype(float), np.array(c).astype(float), z,function='gaussian')
            X , Y = np.meshgrid(range(72),range(14))
            z_intp = f(X, Y)
            interp_noisy[i,:,:,0] = z_intp.T
        elif(interp == 'spline'):
            tck = interpolate.bisplrep(np.array(r).astype(float), np.array(c).astype(float), z)
            z_intp = interpolate.bisplev(range(72),range(14),tck)
            interp_noisy[i,:,:,0] = z_intp
        z = [noisy_image[i,j,k,1] for j,k in zip(r,c)]
        if(interp == 'rbf'):
            f = interpolate.Rbf(np.array(r).astype(float), np.array(c).astype(float), z,function='gaussian')
            X , Y = np.meshgrid(range(72),range(14))
            z_intp = f(X, Y)
            interp_noisy[i,:,:,1] = z_intp.T
        elif(interp == 'spline'):
            tck = interpolate.bisplrep(np.array(r).astype(float), np.array(c).astype(float), z)
            z_intp = interpolate.bisplev(range(72),range(14),tck)
            interp_noisy[i,:,:,1] = z_intp
​
​
    interp_noisy = np.concatenate((interp_noisy[:,:,:,0], interp_noisy[:,:,:,1]), axis=0).reshape(80000, 72, 14, 1)
   
    
    return interp_noisy
​
def SRCNN_model():
​
    input_shape = (72,14,1)
    x = Input(shape = input_shape)
    c1 = Convolution2D( 64 , 9 , 9 , activation = 'relu', init = 'he_normal', border_mode='same')(x)
    c2 = Convolution2D( 32 , 1 , 1 , activation = 'relu', init = 'he_normal', border_mode='same')(c1)
    c3 = Convolution2D( 1 , 5 , 5 , init = 'he_normal', border_mode='same')(c2)
    #c4 = Input(shape = input_shape)(c3)
    model = Model(input = x, output = c3)
    ##compile
    adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-8) 
    model.compile(optimizer=adam, loss='mean_squared_error', metrics=['mean_squared_error']) 
    return model
    
def SRCNN_train(train_data ,train_label, val_data , val_label , channel_model , num_pilots , SNR ):
    srcnn_model = SRCNN_model()
    print(srcnn_model.summary())
    
    checkpoint = ModelCheckpoint("SRCNN_check.h5", monitor='val_loss', verbose=1, save_best_only=True,
                                 save_weights_only=False, mode='min'
)
    callbacks_list = [checkpoint]
​
    srcnn_model.fit(train_data, train_label, batch_size=128, validation_data=(val_data, val_label),
                    callbacks=callbacks_list, shuffle=True, epochs= 300 , verbose=0
)
    
    #srcnn_model.save_weights("drive/codes/my_srcnn/SRCNN_SUI5_weights/SRCNN_48_12.h5")
    srcnn_model.save_weights("SRCNN_" + channel_model +"_"+ str(num_pilots) + "_"  + str(SNR) + ".h5")
   
​
​
def SRCNN_predict(input_data , channel_model , num_pilots , SNR):
    srcnn_model = SRCNN_model()
    srcnn_model.load_weights("SRCNN_" + channel_model +"_"+ str(num_pilots) + "_"  + str(SNR) + ".h5")
    predicted  = srcnn_model.predict(input_data)
    return predicted
​
  
def DNCNN_model ():
  
    inpt = Input(shape=(None,None,1))
    # 1st layer, Conv+relu
    x = Conv2D(filters=64, kernel_size=(3,3), strides=(1,1), padding='same')(inpt)
    x = Activation('relu')(x)
    # 18 layers, Conv+BN+relu
    for i in range(18):
        x = Conv2D(filters=64, kernel_size=(3,3), strides=(1,1), padding='same')(x)
        x = BatchNormalization(axis=-1, epsilon=1e-3)(x)
        x = Activation('relu')(x)   
    # last layer, Conv
    x = Conv2D(filters=1, kernel_size=(3,3), strides=(1,1), padding='same')(x)
    x = Subtract()([inpt, x])   # input - noise
    model = Model(inputs=inpt, outputs=x)
    adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-8) 
    model.compile(optimizer=adam, loss='mean_squared_error', metrics=['mean_squared_error'])    
    return model
​
def DNCNN_train(train_data ,train_label, val_data , val_label, channel_model , num_pilots , SNR ):
  
  dncnn_model = DNCNN_model()
  print(dncnn_model.summary())
​
  checkpoint = ModelCheckpoint("DNCNN_check.h5", monitor='val_loss', verbose=1, save_best_only=True,
                               save_weights_only=False, mode='min'
)
  callbacks_list = [checkpoint]
​
  dncnn_model.fit(train_data, train_label, batch_size=128, validation_data=(val_data, val_label),
                  callbacks=callbacks_list, shuffle=True, epochs= 200 , verbose=0
)
  dncnn_model.save_weights("DNCNN_" + channel_model +"_"+ str(num_pilots) + "_"  + str(SNR) + ".h5")
  
  
  
def DNCNN_predict(input_data, channel_model , num_pilots , SNR):
  dncnn_model = DNCNN_model()
  dncnn_model.load_weights("DNCNN_" + channel_model +"_"+ str(num_pilots) + "_"  + str(SNR) + ".h5")
  predicted  = dncnn_model.predict(input_data)
  return predicted

                
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值