学习Beamforming Design for Large-Scale Antenna Arrays Using Deep Learning
contribution
本文提出了一个全新的BFNN,利用深度学习设计模拟预编码,对于能量受限以及不完美信道的时候,能够学习优化预编码器最大化频谱效率。
而通过神经网络后能够直接输出基于输入的不完美估计信道以及满足连续模约束的最优模拟预编码。
BFNN架构
BF优化问题建立
三个主要考虑的方面
-
BFNN 的输入: 估计信道以及估计的SNR。
-
Lambda 层: 为了能够保证BFNN的输出模拟预编码能够满足连续模约束,一个自定义lamada层加在BFNN的最后。
. -
损失函数:
在本文的设计中,不需要标签,BFNN用以下与目标直接相关的新的损失函数进行训练:
损失的减少正好与平均SE的增加相对应。
神经网络工作模式
- . 在线下训练的第一阶段,BFNN首先学习如何获得理想的SE,当只有实际的估计信道
- 在线上运行的第二阶段,BFNN能够自适应不完美CSI并且获得对抗信道估计误差的强健性能。
仿真参数设置及结果
仿真运行模型:
其中,输入层:imperfect_CSI +flatten 1+128
仿真代码学习(部分,个人学习向)
文件1 :utils.py
- 导入keras库
from tensorflow.python,keras import *
- 定义全局参数
定义全局参数(天线总数量Nt,传输能量等等)
Nt = 64
P =1
- 函数定义
# 首先将输入的相位转换为 complex valued 模拟预编码,采用 tf.cast(x,dytypy,name=None)
#1.定义函数 def 函数名(输入变量)
def trans_Vrf (temp) :
# 2.实部与虚部分开
v_real = tf.cos(temp) #采用的是cos 而不是matlab中的 real or imag
v_imag=tf.sin(temp)
# 3. 张量转换
vrf = tf.cast(tf.complex(v_real,v_imag),tf.complex64)
#4. retrurn 输出
return vrf
为了能够简化keras的应用,采用lambda层来计算速率
def rate_func(temp) #可以采用相同的变量名称来作为函数的变量
h,v,SNR_input = temp
#hv = kackend.batch_dot(
#tf.cast(h,tf.complex64),tf.transpose(a=v,perm=[1,0]))
#需要实现维度的匹配,所以要多加确认矩阵维度之间是否是一致的
hc = kackend.batch_dot(tf.cast(H,tf.complex64),v) #两个维度都是(Nt,:)
rate =tf.math.log(tf.cast(1+SNR_inpu/Nt*tf.pow(tf.abs(hv),2),tf.float32))/tf.math.log(2.0)
return -rate
中间有两个函数
1. kackend.batch_dot
实现矩阵之间的点乘功能,那么矩阵之前需要匹配
from keras import backend as K
a = K.ones((3,4))
b = K.ones((4,5))
c = K.dot(a, b)
print(c.shape)#(3,5)
** 2. tf.transpose **用于矩阵维度的转置功能
以perm = [0,1,2]
3个维度的数组为例, 0–代表的是最外层的一维,
1–代表外向内数第二维, 2–代表最内层的一维,
这种perm是默认的值.如果换成[1,0,2],就是把最外层的两维进行转置,
比如原来是2乘3乘4,经过[1,0,2]的转置维度将会变成3乘2乘4
- 读取matlab生成的问题件
import scipy.io as sio
def mat_load(path) :
pritn('loading data.....')
#load the pertfect csi
h = sio.loadmat(path+'/pcsi.mat')['pcsi'] #后面的[]是用来提取 读取文件中 名字为[pcsi]的变量的值,提取文件相当于一个字典。
#load the estimated csi
h_est=sio.loadmat(path+'/ecsi.mat')['ecsi']
print('the loading is completing ')
print('the shape of the CSI is ', h_est.shape )
return h , h_est
文件2 :train.py
- import 文件
from utils import *
from tensorflow.python.keras.layer import * #导入神经网络层
例如:
# tf.keras.layer.Embedding(input_dim, output_dim, #输入维度,嵌入层维度
embeddings_initializer='uniform', #嵌入初始化方式
embeddings_regularizer=None, #嵌入矩阵正则化
embeddings_constraint=None, #嵌入矩阵约束
mask_zero=False, input_length=None) #是否忽略输入中的0.
- 导入并生成仿真数据
调用utils中的mat_load函数进行读取,因此首先要定义变量 path
path = 'train_set/example/train'
H,H_est = mat_load(path)
#将数据输入到BFNN中
#将信道估计值作为输入值输入到BFNN中
H_input =np.expand_dims(np.concatenate([np.real(H_est),np.imag(H_est)],1),1)
# 第一个1表示以行进行数组连接,第二个1表示在列增加一维,相当于将行向量变为列向量
# H 表示完美信道
H =np.squeeze(H)
# 生成不同样本的SNR值 SNR_dB = 10^(SNR_linear/10)
SNR = np.pow(10,np.rand.randint(-20,20,[H.shape[0],1])/10
- np.concatenate((a1,a2…),axis=0) 用于多个数组的拼接
其中,axis=1,表示对应行的数组进行拼接,,concatenate()相较于append效率更高,适合大规模的数据拼接 - np.expand_dims(a, axis=0)是通过在指定位置插入新的轴来扩展数组形状
原先有一个2x3维的矩阵,调用axis=0后,在第0维增加一维,即1X2X3,将原先的维推到右边去。
所以说 np.expand_dims可以用于单维度的转置~! - squeeze 函数:从数组的形状中删除单维度条目,即把shape中为1的维度去掉
利用squeeze()函数将表示向量的数组转换为秩为1的数组 - power(x, y) 函数,计算 x 的 y 次方。
- 构建BFNN
信道估计值作为输入来获得vrf
完美csi用作损失函数的计算
#Input_layer 用来生成一个keras的tensor
imperfect_CSI = Input(name='imperfect_CSI', shape=(H_input.shape[1:4]), dtype=tf.float32)
#完美信道用来计算损失,并不需要估计
perfect_CSI = Input(name='perfect_CSI', shape=(H.shape[1],), dtype=tf.complex64) #是一个复数
# SNR作为输入
SNR_input = Input(name='input',shape=((1,),dtype=tf.float32)
## 批归一化+输入平坦
temp = BatchNormalization()(imperfect)
temp = Flatten()(temp)
##输入到隐藏层
temp = BatchNormalization()(temp)
temp = Dense(256,activation='relu')(temp)
temp =BatchNormalization()(temp)
temp=Dense(128,activation='relu')(temp)
#输出层 增加一个lamabda层
phase = Dense(Nt)(temp)
#利用lambda对输入的phase进行计算,trans_Vrf为utils中的方法,输出的维度为发射天线数
V_RF = Lambda(trans_Vrf, dtype=tf.complex64, output_shape=(Nt,))(phase)
rate = Lambda(Rate_func,dtype=tf.float32 ,output_shape=(1,))([perfect_CSI,V_RF,SNR_input])
#输入参数不完美信道,完美信道以及SNR,输出为速率。
model = Model(inputs=[imperfect_CSI,perfect_CSI,SNR_input],outputs =rate)
BatchNormalization
对于每个隐层神经元,把逐渐向非线性函数映射后向取值区间极限饱和区靠拢的输入分布强制拉回到均值为0方差为1的比较标准的正态分布,使得非线性变换函数的输入值落入对输入比较敏感的区域,以此避免梯度消失问题。
要对每个隐层神经元的激活值做BN,可以想象成每个隐层又加上了一层BN操作层
①不仅仅极大提升了训练速度,收敛过程大大加快;②还能增加分类效果,一种解释是这是类似于Dropout的一种防止过拟合的正则化表达方式,所以不用Dropout也能达到相当的效果;③另外调参过程也简单多了,对于初始化要求没那么高,而且可以使用大的学习率等
x = keras.layers.BatchNormalization(axis=-1,#对输入的哪个轴执行BN
momentum=0.99,#滑动平均和方差的动量
epsilon=0.001,#防止0除的较小值
center=True,#是否使用beta调整归一化后的输出均值
scale=True,#是否使用gamma调整归一化后的输出方差
trainable=True)(x)
Lambda层
本函数用以对上一层的输出施以任何Theano/TensorFlow表达式,如果你只是想对流经该层的数据做个变换,而这个变换本身没有什么需要学习的参数,那么直接用Lambda Layer是最合适的了。
function:要实现的函数,该函数仅接受一个变量,即上一层的输出
output_shape:函数应该返回的值的shape,可以是一个tuple,也可以是一个根据输入shape计算输出shape的函数
mask: 掩膜
arguments:可选,字典,用来记录向函数中传递的其他关键字参数
Model
在 Keras 中有两类主要的模型:Sequential 顺序模型 和 使用函数式 API 的 Model 类模型。
- model class
model = tf.keras.Model(inputs=inputs, outputs=outputs)
#inputs: keras.Input 目标或者keras.Input 目标列表
#outputs: model 的输出
有两种实例化model
- 使用功能API,从 开始链接层调用以指定模型的前进通道,最后从输入和输出创建模型:Input
from tensorflow.python.keras import *
from tensorflow as tf
inputs = Input(shape=(3,))
x = Dense(4, activation = 'relu')(inputs)
outputs = Dense(5,activation='softmax')(x)
model = Model(inputs = inputs, outputs = outputs)
- 通过子类:在这种情况下,应在 中定义图层,并在 中实现模型的向前传递。Model__init__call
import tensorflow as tf
class Mymodel(tf.keras.model)
def __init__(self):
super(Mymodel,self).__init__()
self.dense1 = tf.keras.layers.Dense(4,activation=tf.nn.relu)
self.dense2 =tf.keras.layers.Dense(5,activation=tf.nn.softmax)
def call(self,inputs):
x = self.densel1(inputs)
return self.dense2(x)
model = Mymodel()
创建模型后,可以使用model.compile() 将模型与损耗和指标配置 用model.fit()训练模型,或使用model.predict() 模型使用 进行预测。
keras model.compile(loss='目标函数 ', optimizer='adam', metrics=['accuracy'])
#采用自定义的loss 时,需要以(y_true, y_pred, **kwards)的格式
文件3 ,test.py
Test Your Model
for snr in range (-20,25,5):
SNR = np.powor(10,np.ones([H.shape[0],1])*snr/10)
y = model.evaluate(x = [H_input ,H, SNR], y =H ,batch_size = 10000)
rate.append(-y)
print(rate)
fit()用于使用给定输入训练模型.
predict()用于实际预测.它为输入样本生成输出预测.
evaluate()用于评估已经训练过的模型.返回损失值&模型的度量值.
总结
神经网络整体架构就是构建Model
首先输入参数,包括完美与不完美的信道信息。
input = Input (name = ’ imperfect_CSI’ ,shape= (H_input.shape [1:4],dtype =tf.float32)
然后普构建神经网络架构,包括输入层,隐藏层,以及输出层
在每一次中间加一个BatchNormalizaiton
temp = BatchNormalization()(imperfect_CSI)
输入层: 平台化数据
temp =Flatten()(temp)
隐藏层:包括三层 Dense网络
temp = BatchNormalization()(temp)
temp = Dense(256,activation='relu')(temp)
temp = BatchNormalization()(temp)
temp = Dense(128,activation ='relu')(temp)
temp = BatchNormalization()(temp)
phase = Dense(Nt)(temp) (Nt = 64)
输入层,采用Lambda 层来进行建议目标函数计算
V_RF = Lamabda(trans_Vrf,dtype=tf.complex64,output_shape=(Nt,))(phase)
rate = Lamabda(Rate_func,dtype=tf.float32,output_shape=(1,))
模型生成
model = Model(Inputs =[imerfect_CSI,perfect_CSI,SNR_input],outputs = rate)
model.compile(optimizer='adam',loss= lamabda y_ture ,y_pred:y_pred)
model.summary()
测试模型
y =model.evaluate(x=[H_input,H,SNR],y=H,batch_size = 10000)
画出结果:
for snr in range(-20,5,5):
rate.append(-y)
print(rate)
plt.plot(range(-20,25,5),rate)
plt.show()