├── NNmodel.png ├── Rnmodel.png ├── README.md ├── A_LS_ChannelEstimationNN_keras_update.py ├── A_LS_ChannelEstimationNN_keras_updatea.py ├── C_MMSE_ChannelEstimationNN_keras_update.py ├── B_LS_ChannelEstimationNN_keras_with_vector.py ├── D_MMSE_ChannelEstimationNN_keras_with_vector.py ├── F_ChannelEstimationNN_keras_vary_noisepw_LS+MMSE.py └── E_ChannelEstimationNN_keras_LS+MMSE.py /NNmodel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agubm/ML-Channel-Estimation-with-Neural-Networks/HEAD/NNmodel.png -------------------------------------------------------------------------------- /Rnmodel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agubm/ML-Channel-Estimation-with-Neural-Networks/HEAD/Rnmodel.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ML-Channel-Estimation-with-Neural-Networks 2 | Using Neural Networks to Estimate Channel Coefficient of a Wireless Channel 3 | -------------------------------------------------------------------------------- /A_LS_ChannelEstimationNN_keras_update.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 19 10:07:41 2020 4 | @author: aguboshimec 5 | """ 6 | print ('*******MIMO Channel Estimation using Machine Learning-based Approach (LS)*******') 7 | #Chanel Estimation/Prediction with Least Square (4-layer RNeural Network, etc) 8 | import numpy as np 9 | from numpy import mean #used for computing avg. mse 10 | import matplotlib.pyplot as plt 11 | from keras.models import Sequential, Model 12 | from keras.layers import Dense, Activation, Input 13 | from keras.utils import plot_model 14 | import sys 15 | accuracy = [] #store the prediction accuracy per loop (for diff. antenna array sizes) 16 | 17 | nt = nr = 4 #number of tx_antennas #number of rx_antennas 18 | dim = nr*nt 19 | batch_size = dim 20 | noOfNodes = 25 21 | training = 6 #training sequence 22 | layer1node = noOfNodes # number of nodes in first layer 23 | layer2anode = noOfNodes # number of nodes in second layer (hidden) 24 | layer2bnode = noOfNodes # number of nodes in third layer (hidden) 25 | layer3node = dim # number of nodes in fourth layer 26 | 27 | #epoch between (188 - 195) seems cool for 4by4 ant array? 28 | epoch = 195 29 | ite = 1000 #This determines the 3rd dimension of the Tensor. With this, we can have: 40 by 4 by 4 Tensor (ie. if ite = 40) 30 | idx = int(ite/2) # the loop index will be the number of the splitted parts of the 3D tensor/array. 31 | Channel_LS_all = [] 32 | Channel_pred_all = [] 33 | Channel_all = [] #this is the true channel coefficients for every corresponding least_sq estimation. 34 | Channel_MMSE_all = [] 35 | Noise = [] 36 | y_all_N = [] #stores the output of the model with varying noise power 37 | Channel_LS_all_N = [] #stores the output of the LS Channel with varying noise power 38 | MSE_LS_all = [] # stores the MSE values for coeff. of LS solution 39 | MSE_NN_all = [] # stores the MSE values for coeff. RNN estimation 40 | 41 | #Channel model: y = Hx + n, #Assumption: Time-Invariant Channel, AWGN noise 42 | # H is the channel matrix coeffs, x is the training data, n is the awgn noise, y is the output received 43 | #Training samples or signals 44 | x = np.random.randn(nt,training) #nt by x traning samples 45 | 46 | # Generate or Adding AGWN noise: So, bascially, the noise level is what deteroritate the channel quality. the noise is the only cahning factor here. 47 | noise = np.random.randn(ite,nr,1) 48 | 49 | def Channel_dataset(): #used for testing data set 50 | # Channel (idealized without nosie or true channel coefficients) 51 | for i in range (ite): 52 | Channel = np.random.randn(nr,nt)# same channel for varying noise and constant noise power. Recall: Its LTI 53 | y = np.add(np.dot(Channel,x),noise[i]) 54 | Channel_LS = np.dot(y,(np.linalg.pinv(x))) 55 | 56 | Channel_LS_all.append ((np.reshape(Channel_LS, (dim, 1)))) 57 | Channel_all.append(np.reshape(Channel, (dim, 1))) 58 | 59 | Channel_dataset() # calls the function defined above. 60 | #splits the tensor or array into two uniques parts (not vectors this time). Comparing the training loss & verification loss curves will help me know underfitting or overfitting 61 | dataSetSplit = np.array_split(Channel_all, 2) 62 | Channel_v = np.reshape(dataSetSplit[1], (idx,dim)) 63 | Channel_t = np.reshape(dataSetSplit[0], (idx,dim)) 64 | 65 | dataSetSplit_LS = np.array_split(Channel_LS_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 66 | Channel_LS_v = np.reshape(dataSetSplit_LS[1], (idx,dim)) 67 | Channel_LS_t = np.reshape(dataSetSplit_LS[0], (idx,dim)) 68 | 69 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 70 | model = Sequential() 71 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 72 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 73 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 74 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 75 | model.compile(optimizer = 'adam', loss = 'mse') 76 | 77 | #train the model now: 78 | 79 | NN_mf = model.fit(Channel_LS_t, Channel_t, validation_data = (Channel_LS_v, Channel_v), epochs=epoch, batch_size = batch_size, verbose= 1) 80 | 81 | #Evaluting performance with varying mse vs snr: 82 | #Obtained a vector with varying noise power 83 | start = 15 84 | stop = 0.01 85 | stepsize = ((stop - start)/(idx-1)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 86 | noise_pw = np.arange(start, stop, stepsize) #Generates vector with elements used as varying noise power 87 | 88 | SNR = np.reciprocal(noise_pw) # SNR is the reciprocal of noise power 89 | print ('**'*8,'SNR is reciprocal of the noise power: see table below','**'*8) 90 | noise_pw = np.reshape(noise_pw, (-1)) 91 | SNR = np.reshape(SNR, (-1)) 92 | print(np.c_[noise_pw, SNR]) 93 | 94 | noise_= np.random.randn(nr,1) 95 | #Obtaining the overall noise vector with its varying power: 96 | #To show the noise vectors multiplied by noise powers respectively/individually 97 | for element in noise_pw: 98 | #print(i, end=', ') 99 | noise__ = [element]*noise_ # Generated Noise Vector (with varying noise level 100 | Noise.append(noise__) 101 | 102 | #Generate new Test Data/Channel Coefficient. This will help give a proof of ability of model to generalize: 103 | #transmit samples or signals, x_Test 104 | Channel_test = np.random.randn(nr,nt) 105 | # Recall: y = Hx + n 106 | for k in range(len(Noise)): 107 | y_N = np.add(np.dot(Channel_test,x),Noise[k]) 108 | y_all_N.append(y_N) 109 | 110 | #Perform Least_Square of the channel (with varying noise power). 111 | #Least Square estimation = H_ls = Channel_LS = (y*x_transpose(pinv(x*x_transpose)) 112 | Channel_LS_N = np.dot(y_all_N[k],(np.linalg.pinv(x))) 113 | Channel_LS_all_N.append((np.reshape(Channel_LS_N, (1,dim)))) 114 | #predict the trained model 115 | Channel_pred = model.predict(Channel_LS_all_N[k], batch_size = idx) 116 | Channel_pred_all.append(Channel_pred) 117 | 118 | for mse in range(idx-1): 119 | hNN_pred= np.reshape(Channel_pred_all[mse],(-1)) #reshapes or flattens the vector to allow being used for plotting 120 | hLS = np.reshape(Channel_LS_all_N[mse],(-1)) 121 | h = np.reshape(Channel_test, (-1)) 122 | MSE1 = np.mean((h - hNN_pred)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 123 | MSE2 = np.mean((h - hLS)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 124 | MSE_NN_all.append(MSE1) 125 | MSE_LS_all.append(MSE2) 126 | 127 | c_idx = idx-2 #choose channel index to view 128 | print("TrueChannel=%s, LeastSqChannel=%s, PredictedLSChannel=%s" % (np.reshape(Channel_test,(nr,nt)), Channel_LS_all_N[c_idx], Channel_pred_all[c_idx])) 129 | 130 | ite_t = 200 #the number of test channel for which i will compute the average mse 131 | ccchannel = np.random.randn(ite_t,nr,nt) 132 | y_all_nnn = [] 133 | Channel_LS_all_nn = [] 134 | Channel_pred_all_nn = [] 135 | #generate SNR with same length as the channels i have in order to correctly make a plot: #Evaluting performance with varying mse vs snr: #Obtained a vector with varying noise power 136 | start_ = 15 137 | stop_ = 0.01 138 | stepsize_ = ((stop_ - start_)/(ite_t)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 139 | noise_pw_ = np.arange(start, stop, stepsize_) #Generates vector with elements used as varying noise power 140 | SNR_ = np.reciprocal(noise_pw_) # SNR is the reciprocal of noise power 141 | noise_pw_ = np.reshape(noise_pw_, (-1)) 142 | SNR_ = np.reshape(SNR_, (-1)) 143 | #print(np.c_[noise_pw_, SNR_]) 144 | Noise_ = [] 145 | for element in noise_pw_: 146 | #print(i, end=', ') 147 | noise__cc = [element]*noise_ # Generated Noise Vector (with varying noise level 148 | Noise_.append(noise__cc) 149 | #this computes same noise, but different channel 150 | for nn in range(len(Noise_)): 151 | for cc in range (len (ccchannel)): 152 | y_nn = np.add((np.dot(ccchannel[cc],x)),Noise_[nn]) #for each iterate over all the channels 153 | y_all_nnn.append(y_nn) 154 | 155 | #next compute the ls for the output: np.dot(y,(np.linalg.pinv(x))) 156 | for lll in range (len(y_all_nnn)): 157 | Channel_LS_nn = np.dot(y_all_nnn[lll],(np.linalg.pinv(x))) #computes the LS Channel realization for y_all_nnn 158 | Channel_LS_all_nn.append(np.reshape(Channel_LS_nn, (1,dim))) 159 | #predict the trained model 160 | Channel_pred_nn = model.predict(Channel_LS_all_nn[lll], batch_size = idx) #predicts the LS Channel realizations also. 161 | Channel_pred_all_nn.append(Channel_pred_nn) 162 | 163 | #I basically, had to loop over all the channels with varying SNR individually. 164 | Channel_LS_all_nnA = [] 165 | Channel_NN_all_nnA = [] 166 | 167 | Channel_LS_all_nn_sorted = [ Channel_LS_all_nn[i:i+ite_t] for i in range(0, len(Channel_LS_all_nn), ite_t) ] ## 168 | [Channel_LS_all_nnA.extend(Channel_LS_all_nn_sorted[pp]) for pp in range(0,ite_t)] 169 | 170 | Channel_NN_all_nn_sorted = [ Channel_pred_all_nn[i:i+ite_t] for i in range(0, len(Channel_pred_all_nn), ite_t) ] ## 171 | [Channel_NN_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 172 | 173 | avgMSE_LS_all = [] 174 | avgMSE_NN_all = [] 175 | 176 | aa = 0 177 | for eee in range(len(ccchannel)): 178 | hNN_predc = np.reshape(Channel_NN_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 179 | hLS_= np.reshape(Channel_LS_all_nnA[eee+aa],(-1)) 180 | h_ = np.reshape(ccchannel[eee], (-1)) 181 | MSE1_ = np.mean((h_ - hNN_predc)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 182 | MSE2_ = np.mean((h_ - hLS_)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 183 | avgMSE_LS_all.append(MSE2_) 184 | avgMSE_NN_all.append(MSE1_) 185 | aa = aa+len(ccchannel) 186 | #Determine how close the predictions are to the real-values with some toleranace 187 | CompareResult = np.isclose(Channel_LS_all_N[c_idx], Channel_pred_all[c_idx], rtol=0.2) #toleranace of +-0.2 188 | print (CompareResult) 189 | correct_pred = np.count_nonzero(CompareResult) 190 | total_number = CompareResult.size 191 | Accuracy = (correct_pred/total_number)*100 192 | accuracy.append(Accuracy) 193 | print ('Prediction Accuracy of', Accuracy,'%') 194 | 195 | #Evaluate the performance of trained model using just one Channel matrix. 196 | plt.plot(noise_pw[::-1], MSE_LS_all) 197 | plt.plot(noise_pw[::-1], MSE_NN_all) 198 | plt.title('Graph of MSE with varying SNR (after training)') 199 | plt.ylabel('Mean Square Error') 200 | plt.xlabel('SNR') 201 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 202 | plt.grid(b=None, which='major', axis='both') 203 | plt.show() 204 | 205 | #Evaluate the performance of Average MSE from the trained model. 206 | plt.plot(noise_pw_[::-1], avgMSE_LS_all) 207 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 208 | plt.title('Graph of Average MSE with varying SNR (after training)') 209 | plt.ylabel('Avg. Mean Square Error') 210 | plt.xlabel('SNR') 211 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 212 | plt.grid(b=None, which='major', axis='both') 213 | plt.show() 214 | 215 | #Visualization after training and testing #To see the performance of the 3rd channel coefficient only 216 | #a good overlap means good performance. 217 | Channel_LS_test = np.reshape(Channel_LS_all_N[-1], (-1)) 218 | Channel_pred = np.reshape(Channel_pred_all[-1], (-1)) 219 | Channel_test = np.reshape(Channel_test, (-1)) 220 | plt.plot(Channel_LS_test, '*-') 221 | plt.plot(Channel_pred, '.-') 222 | plt.plot(Channel_test, ',-') 223 | plt.ylabel('amplitude') 224 | plt.xlabel('channel coefficient') 225 | plt.title('Plot of Test Channel Data (LS) & its Predicted Channel') 226 | plt.legend(['LeastSqChannel', 'PredictedLSChannel', 'TrueChannel'], loc='upper left') 227 | plt.grid(b=None, which='major', axis='both') 228 | plt.show() 229 | 230 | plt.plot(NN_mf.history['loss']) 231 | plt.plot(NN_mf.history['val_loss']) 232 | plt.title('Graph of Training Loss & its Validation Loss - LS') 233 | plt.ylabel('Loss') 234 | plt.xlabel('No. of Epoch') 235 | plt.legend(['Training', 'Validation'], loc='upper left') 236 | plt.grid(b=None, which='major', axis='both') 237 | plt.show() 238 | 239 | #More to visualization: show the sequential layers layers 240 | plot_model(model, show_shapes=True, show_layer_names=True, to_file='NNmodel.png') 241 | from IPython.display import Image 242 | Image(retina=True, filename='NNmodel.png') #saves the picture inot the folder-.py collocation 243 | 244 | #more to visualization of the model: #To obtain the weights and biases at each layer: 245 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 246 | summary = model.summary() 247 | TrainedWeight1 = model.layers[0].get_weights()[0] 248 | TrainedBias1 = model.layers[0].get_weights()[1] 249 | #print("trained weight of layer1 =", TrainedWeight1) 250 | #print("trained bias of layer1 =", TrainedBias1) 251 | 252 | TrainedWeight2a = model.layers[1].get_weights()[0] 253 | TrainedBias2a = model.layers[1].get_weights()[1] 254 | #print("trained weight of layer2 =", TrainedWeight2) 255 | #print("trained bias of layer2 =", TrainedBias2) 256 | 257 | TrainedWeight2b = model.layers[2].get_weights()[0] 258 | TrainedBias2b = model.layers[2].get_weights()[1] 259 | #print("trained weight of layer2 =", TrainedWeight2) 260 | #print("trained bias of layer2 =", TrainedBias2) 261 | 262 | TrainedWeight3 = model.layers[3].get_weights()[0] 263 | TrainedBias3 = model.layers[3].get_weights()[1] 264 | #print("trained weight of layer2 =", TrainedWeight3) 265 | #print("trained bias of layer2 =", TrainedBias3) 266 | 267 | #this will create the network topology or achitecture that i modelled. 268 | #so, in case you get a graphviz exectuabel error, use this llink (https://www.youtube.com/watch?v=q7PzqbKUm_4) to fix it. Cheers. 269 | #from ann_visualizer.visualize import ann_viz; 270 | #ann_viz(model, filename="RNNwithKeras", title="Neural Network Topology for Channel Estimation") 271 | 272 | #Note: If at any point the MSE curve descreases, and then increases again, this could indicate overfitting? So, in this case, i reduce my epoch value 273 | #or try to tweak other hyperparameters, number of nodes. 274 | 275 | -------------------------------------------------------------------------------- /A_LS_ChannelEstimationNN_keras_updatea.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 19 10:07:41 2020 4 | @author: aguboshimec 5 | """ 6 | print ('*******MIMO Channel Estimation using Machine Learning-based Approach (LS)*******') 7 | #Chanel Estimation/Prediction with Least Square (4-layer RNeural Network, etc) 8 | import numpy as np 9 | from numpy import mean #used for computing avg. mse 10 | import matplotlib.pyplot as plt 11 | from keras.models import Sequential, Model 12 | from keras.layers import Dense, Activation, Input 13 | from keras.utils import plot_model 14 | import sys 15 | accuracy = [] #store the prediction accuracy per loop (for diff. antenna array sizes) 16 | 17 | nt = nr = 4 #number of tx_antennas #number of rx_antennas 18 | dim = nr*nt 19 | batch_size = dim 20 | noOfNodes = 25 21 | training = 6 #training sequence 22 | layer1node = noOfNodes # number of nodes in first layer 23 | layer2anode = noOfNodes # number of nodes in second layer (hidden) 24 | layer2bnode = noOfNodes # number of nodes in third layer (hidden) 25 | layer3node = dim # number of nodes in fourth layer 26 | 27 | #epoch between (188 - 195) seems cool for 4by4 ant array? 28 | epoch = 195 29 | ite = 2000 #This determines the 3rd dimension of the Tensor. With this, we can have: 40 by 4 by 4 Tensor (ie. if ite = 40) 30 | idx = int(ite/2) # the loop index will be the number of the splitted parts of the 3D tensor/array. 31 | Channel_LS_all = [] 32 | Channel_pred_all = [] 33 | Channel_all = [] #this is the true channel coefficients for every corresponding least_sq estimation. 34 | Channel_MMSE_all = [] 35 | Noise = [] 36 | y_all_N = [] #stores the output of the model with varying noise power 37 | Channel_LS_all_N = [] #stores the output of the LS Channel with varying noise power 38 | MSE_LS_all = [] # stores the MSE values for coeff. of LS solution 39 | MSE_NN_all = [] # stores the MSE values for coeff. RNN estimation 40 | 41 | #Channel model: y = Hx + n, #Assumption: Time-Invariant Channel, AWGN noise 42 | # H is the channel matrix coeffs, x is the training data, n is the awgn noise, y is the output received 43 | #Training samples or signals 44 | x = np.random.randn(nt,training) #nt by x traning samples 45 | 46 | # Generate or Adding AGWN noise: So, bascially, the noise level is what deteroritate the channel quality. the noise is the only cahning factor here. 47 | noise = np.random.randn(ite,nr,1) 48 | 49 | def Channel_dataset(): #used for testing data set 50 | # Channel (idealized without nosie or true channel coefficients) 51 | for i in range (ite): 52 | Channel = np.random.randn(nr,nt)# same channel for varying noise and constant noise power. Recall: Its LTI 53 | y = np.add(np.dot(Channel,x),noise[i]) 54 | Channel_LS = np.dot(y,(np.linalg.pinv(x))) 55 | 56 | Channel_LS_all.append ((np.reshape(Channel_LS, (dim, 1)))) 57 | Channel_all.append(np.reshape(Channel, (dim, 1))) 58 | 59 | Channel_dataset() # calls the function defined above. 60 | #splits the tensor or array into two uniques parts (not vectors this time). Comparing the training loss & verification loss curves will help me know underfitting or overfitting 61 | dataSetSplit = np.array_split(Channel_all, 2) 62 | Channel_v = np.reshape(dataSetSplit[1], (idx,dim)) 63 | Channel_t = np.reshape(dataSetSplit[0], (idx,dim)) 64 | 65 | dataSetSplit_LS = np.array_split(Channel_LS_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 66 | Channel_LS_v = np.reshape(dataSetSplit_LS[1], (idx,dim)) 67 | Channel_LS_t = np.reshape(dataSetSplit_LS[0], (idx,dim)) 68 | 69 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 70 | model = Sequential() 71 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 72 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 73 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 74 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 75 | model.compile(optimizer = 'adam', loss = 'mse') 76 | 77 | #train the model now: 78 | 79 | NN_mf = model.fit(Channel_LS_t, Channel_t, validation_data = (Channel_LS_v, Channel_v), epochs=epoch, batch_size = batch_size, verbose= 1) 80 | 81 | #Evaluting performance with varying mse vs snr: 82 | #Obtained a vector with varying noise power 83 | start = 15 84 | stop = 0.01 85 | stepsize = ((stop - start)/(idx-1)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 86 | noise_pw = np.arange(start, stop, stepsize) #Generates vector with elements used as varying noise power 87 | 88 | SNR = np.reciprocal(noise_pw) # SNR is the reciprocal of noise power 89 | print ('**'*8,'SNR is reciprocal of the noise power: see table below','**'*8) 90 | noise_pw = np.reshape(noise_pw, (-1)) 91 | SNR = np.reshape(SNR, (-1)) 92 | print(np.c_[noise_pw, SNR]) 93 | 94 | noise_= np.random.randn(nr,1) 95 | #Obtaining the overall noise vector with its varying power: 96 | #To show the noise vectors multiplied by noise powers respectively/individually 97 | for element in noise_pw: 98 | #print(i, end=', ') 99 | noise__ = [element]*noise_ # Generated Noise Vector (with varying noise level 100 | Noise.append(noise__) 101 | 102 | #Generate new Test Data/Channel Coefficient. This will help give a proof of ability of model to generalize: 103 | #transmit samples or signals, x_Test 104 | Channel_test = np.random.randn(nr,nt) 105 | 106 | # Recall: y = Hx + n 107 | for k in range(len(Noise)): 108 | y_N = np.add(np.dot(Channel_test,x),Noise[k]) 109 | y_all_N.append(y_N) 110 | 111 | #Perform Least_Square of the channel (with varying noise power). 112 | #Least Square estimation = H_ls = Channel_LS = (y*x_transpose(pinv(x*x_transpose)) 113 | Channel_LS_N = np.dot(y_all_N[k],(np.linalg.pinv(x))) 114 | Channel_LS_all_N.append((np.reshape(Channel_LS_N, (1,dim)))) 115 | #predict the trained model 116 | Channel_pred = model.predict(Channel_LS_all_N[k], batch_size = idx) 117 | Channel_pred_all.append(Channel_pred) 118 | 119 | for mse in range(idx-1): 120 | hNN_pred= np.reshape(Channel_pred_all[mse],(-1)) #reshapes or flattens the vector to allow being used for plotting 121 | hLS = np.reshape(Channel_LS_all_N[mse],(-1)) 122 | h = np.reshape(Channel_test, (-1)) 123 | MSE1 = np.mean((h - hNN_pred)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 124 | MSE2 = np.mean((h - hLS)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 125 | MSE_NN_all.append(MSE1) 126 | MSE_LS_all.append(MSE2) 127 | 128 | c_idx = idx-2 #choose channel index to view 129 | print("TrueChannel=%s, LeastSqChannel=%s, PredictedLSChannel=%s" % (np.reshape(Channel_test,(nr,nt)), Channel_LS_all_N[c_idx], Channel_pred_all[c_idx])) 130 | 131 | ite_t = 500 #the number of test channel for which i will compute the average mse 132 | ccchannel = np.random.randn(ite_t,nr,nt) 133 | y_all_nnn = [] 134 | Channel_LS_all_nn = [] 135 | Channel_pred_all_nn = [] 136 | 137 | #generate SNR conmensurate with the channels i have in order to correct the plot: #Evaluting performance with varying mse vs snr: #Obtained a vector with varying noise power 138 | start_ = 15 139 | stop_ = 0.01 140 | stepsize_ = ((stop_ - start_)/(ite_t)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 141 | noise_pw_ = np.arange(start_, stop_, stepsize_) #Generates vector with elements used as varying noise power 142 | SNR_ = np.reciprocal(noise_pw_) # SNR is the reciprocal of noise power 143 | noise_pw_ = np.reshape(noise_pw_, (-1)) 144 | SNR_ = np.reshape(SNR_, (-1)) 145 | #print(np.c_[noise_pw_, SNR_]) 146 | Noise_ = [] 147 | for element in noise_pw_: 148 | #print(i, end=', ') 149 | noise__cc = [element]*noise_ # Generated Noise Vector (with varying noise level 150 | Noise_.append(noise__cc) 151 | #this computes same noise, but different channel 152 | for nn in range(len(Noise_)): 153 | for cc in range (len (ccchannel)): 154 | y_nn = np.add((np.dot(ccchannel[cc],x)),Noise_[nn]) #for each iterate over all the channels 155 | y_all_nnn.append(y_nn) 156 | 157 | #next compute the ls for the output: np.dot(y,(np.linalg.pinv(x))) 158 | for lll in range (len(y_all_nnn)): 159 | Channel_LS_nn = np.dot(y_all_nnn[lll],(np.linalg.pinv(x))) #computes the LS Channel realization for y_all_nnn 160 | Channel_LS_all_nn.append(np.reshape(Channel_LS_nn, (1,dim))) 161 | #predict the trained model 162 | Channel_pred_nn = model.predict(Channel_LS_all_nn[lll], batch_size = idx) #predicts the LS Channel realizations also. 163 | Channel_pred_all_nn.append(Channel_pred_nn) 164 | 165 | #I basically, had to loop over all the channels with varying SNR individually. 166 | Channel_LS_all_nnA = [] 167 | Channel_NN_all_nnA = [] 168 | 169 | Channel_LS_all_nn_sorted = [ Channel_LS_all_nn[i:i+ite_t] for i in range(0, len(Channel_LS_all_nn), ite_t) ] ## 170 | [Channel_LS_all_nnA.extend(Channel_LS_all_nn_sorted[pp]) for pp in range(0,ite_t)] 171 | 172 | Channel_NN_all_nn_sorted = [ Channel_pred_all_nn[i:i+ite_t] for i in range(0, len(Channel_pred_all_nn), ite_t) ] ## 173 | [Channel_NN_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 174 | 175 | avgMSE_LS_all = [] 176 | avgMSE_NN_all = [] 177 | 178 | aa = 0 179 | for eee in range(len(ccchannel)): 180 | hNN_pred_ = np.reshape(Channel_NN_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 181 | hLS_ = np.reshape(Channel_LS_all_nnA[eee+aa],(-1)) 182 | h_ = np.reshape(ccchannel[eee], (-1)) 183 | MSE1_ = np.mean((h_ - hNN_pred_)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 184 | MSE2_ = np.mean((h_ - hLS_)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 185 | avgMSE_LS_all.append(MSE2_) 186 | avgMSE_NN_all.append(MSE1_) 187 | aa = aa+len(ccchannel) 188 | 189 | #Determine how close the predictions are to the real-values with some toleranace 190 | CompareResult = np.isclose(Channel_LS_all_N[c_idx], Channel_pred_all[c_idx], rtol=0.2) #toleranace of +-0.2 191 | print (CompareResult) 192 | correct_pred = np.count_nonzero(CompareResult) 193 | total_number = CompareResult.size 194 | Accuracy = (correct_pred/total_number)*100 195 | accuracy.append(Accuracy) 196 | print ('Prediction Accuracy of', Accuracy,'%') 197 | 198 | #Evaluate the performance of trained model using just one Channel matrix. 199 | plt.plot(noise_pw[::-1], MSE_LS_all) 200 | plt.plot(noise_pw[::-1], MSE_NN_all) 201 | plt.title('Graph of MSE with varying SNR (after training)') 202 | plt.ylabel('Mean Square Error') 203 | plt.xlabel('SNR (linear)') 204 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 205 | plt.grid(b=None, which='major', axis='both') 206 | plt.show() 207 | 208 | #Evaluate the performance of Average MSE from the trained model. 209 | plt.plot(noise_pw_[::-1], avgMSE_LS_all) 210 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 211 | plt.title('Graph of Average MSE with varying SNR (after training)') 212 | plt.ylabel('Avg. Mean Square Error') 213 | plt.xlabel('SNR (linear)') 214 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 215 | plt.grid(b=None, which='major', axis='both') 216 | plt.show() 217 | 218 | #Visualization after training and testing #To see the performance of the 3rd channel coefficient only 219 | #a good overlap means good performance. 220 | Channel_LS_test = np.reshape(Channel_LS_all_N[-1], (-1)) 221 | Channel_pred = np.reshape(Channel_pred_all[-1], (-1)) 222 | Channel_test = np.reshape(Channel_test, (-1)) 223 | plt.plot(Channel_LS_test, '*-') 224 | plt.plot(Channel_pred, '.-') 225 | plt.plot(Channel_test, ',-') 226 | plt.ylabel('amplitude') 227 | plt.xlabel('channel coefficient') 228 | plt.title('Plot of Test Channel Data (LS) & its Predicted Channel') 229 | plt.legend(['LeastSqChannel', 'PredictedLSChannel', 'TrueChannel'], loc='upper left') 230 | plt.grid(b=None, which='major', axis='both') 231 | plt.show() 232 | 233 | 234 | plt.plot(NN_mf.history['loss']) 235 | plt.plot(NN_mf.history['val_loss']) 236 | plt.title('Graph of Training Loss & its Validation Loss - LS') 237 | plt.ylabel('Loss') 238 | plt.xlabel('No. of Epoch') 239 | plt.legend(['Training', 'Validation'], loc='upper left') 240 | plt.grid(b=None, which='major', axis='both') 241 | plt.show() 242 | 243 | #More to visualization: show the sequential layers layers 244 | plot_model(model, show_shapes=True, show_layer_names=True, to_file='NNmodel.png') 245 | from IPython.display import Image 246 | Image(retina=True, filename='NNmodel.png') #saves the picture inot the folder-.py collocation 247 | 248 | 249 | #more to visualization of the model: #To obtain the weights and biases at each layer: 250 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 251 | summary = model.summary() 252 | TrainedWeight1 = model.layers[0].get_weights()[0] 253 | TrainedBias1 = model.layers[0].get_weights()[1] 254 | #print("trained weight of layer1 =", TrainedWeight1) 255 | #print("trained bias of layer1 =", TrainedBias1) 256 | 257 | TrainedWeight2a = model.layers[1].get_weights()[0] 258 | TrainedBias2a = model.layers[1].get_weights()[1] 259 | #print("trained weight of layer2 =", TrainedWeight2) 260 | #print("trained bias of layer2 =", TrainedBias2) 261 | 262 | TrainedWeight2b = model.layers[2].get_weights()[0] 263 | TrainedBias2b = model.layers[2].get_weights()[1] 264 | #print("trained weight of layer2 =", TrainedWeight2) 265 | #print("trained bias of layer2 =", TrainedBias2) 266 | 267 | TrainedWeight3 = model.layers[3].get_weights()[0] 268 | TrainedBias3 = model.layers[3].get_weights()[1] 269 | #print("trained weight of layer2 =", TrainedWeight3) 270 | #print("trained bias of layer2 =", TrainedBias3) 271 | 272 | #this will create the network topology or achitecture that i modelled. 273 | #so, in case you get a graphviz exectuabel error, use this llink (https://www.youtube.com/watch?v=q7PzqbKUm_4) to fix it. Cheers. 274 | #from ann_visualizer.visualize import ann_viz; 275 | #ann_viz(model, filename="RNNwithKeras", title="Neural Network Topology for Channel Estimation") 276 | 277 | #Note: If at any point the MSE curve descreases, and then increases again, this could indicate overfitting? So, in this case, i reduce my epoch value 278 | #or try to tweak other hyperparameters, number of nodes. 279 | 280 | -------------------------------------------------------------------------------- /C_MMSE_ChannelEstimationNN_keras_update.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 19 10:07:41 2020 4 | @author: aguboshimec 5 | """ 6 | 7 | print ('*******MIMO Channel Estimation using Machine Learning-based Approach (MMSE)*******') 8 | #Chanel Estimation/Prediction with Least Square (4-layer RNeural Network, etc) 9 | import numpy as np 10 | from numpy import mean #used for computing avg. mse 11 | import matplotlib.pyplot as plt 12 | from keras.models import Sequential, Model 13 | from keras.layers import Dense, Activation, Input 14 | from keras.utils import plot_model 15 | import sys 16 | accuracy = [] #store the prediction accuracy per loop (for diff. antenna array sizes) 17 | 18 | nt = nr = 4 #number of tx_antennas #number of rx_antennas 19 | dim = nr*nt 20 | batch_size = dim 21 | noOfNodes = 25 22 | training = 6 #training sequence 23 | layer1node = noOfNodes # number of nodes in first layer 24 | layer2anode = noOfNodes # number of nodes in second layer (hidden) 25 | layer2bnode = noOfNodes # number of nodes in third layer (hidden) 26 | layer3node = dim # number of nodes in fourth layer 27 | 28 | #epoch between (188 - 195) seems cool for 4by4 ant array? 29 | epoch = 1850 30 | ite = 1000 #This determines the 3rd dimension of the Tensor. With this, we can have: 40 by 4 by 4 Tensor (ie. if ite = 40) 31 | idx = int(ite/2) # the loop index will be the number of the splitted parts of the 3D tensor/array. 32 | Channel_MMSE_all = [] 33 | Channel_pred_all = [] 34 | Channel_all = [] #this is the true channel coefficients for every corresponding least_sq estimation. 35 | Noise = [] 36 | y_all_N = [] #stores the output of the model with varying noise power 37 | Channel_MMSE_all_N = [] #stores the output of the MMSE Channel with varying noise power #I didnt use this again. 38 | 39 | MSE_MMSE_all = [] # stores the MSE values for coeff. of LS solution 40 | MSE_NN_all = [] # stores the MSE values for coeff. RNN estimation 41 | 42 | #Channel model: y = Hx + n, #Assumption: Time-Invariant Channel, AWGN noise 43 | # H is the channel matrix coeffs, x is the training data, n is the awgn noise, y is the output received 44 | #Training samples or signals 45 | x = np.random.randn(nt,training) #nt by x traning samples 46 | 47 | # Generate or Adding AGWN noise: So, bascially, the noise level is what deteroritate the channel quality. the noise is the only cahning factor here. 48 | noise = np.random.randn(ite,nr,1) 49 | 50 | def Channel_dataset(): #used for testing data set 51 | # Channel (idealized without nosie or true channel coefficients) 52 | for i in range (ite): 53 | Channel = np.random.randn(nr,nt)# same channel for varying noise and constant noise power. Recall: Its LTI 54 | y = np.add(np.dot(Channel,x),noise[i]) 55 | 56 | #Minimum Mean Square estimation = H_mmse = Chanel_MMSE = (Rhh(Rhh + ((1/SNR)Identity_matrix)H_ls 57 | Channel_MMSE = np.dot((np.dot(y, (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (0.01*np.identity(nr))))) 58 | Channel_MMSE_all.append(np.reshape(Channel_MMSE, (dim, 1))) 59 | Channel_all.append(np.reshape(Channel, (dim, 1))) 60 | 61 | Channel_dataset() # calls the function defined above. 62 | #splits the tensor or array into two uniques parts (not vectors this time). Comparing the training loss & verification loss curves will help me know underfitting or overfitting 63 | dataSetSplit = np.array_split(Channel_all, 2) 64 | Channel_v = np.reshape(dataSetSplit[1], (idx,dim)) 65 | Channel_t = np.reshape(dataSetSplit[0], (idx,dim)) 66 | 67 | dataSetSplit_MMSE = np.array_split(Channel_MMSE_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 68 | Channel_MMSE_v = np.reshape(dataSetSplit_MMSE[1], (idx,dim)) 69 | Channel_MMSE_t = np.reshape(dataSetSplit_MMSE[0], (idx,dim)) 70 | 71 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 72 | model = Sequential() 73 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 74 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 75 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 76 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 77 | model.compile(optimizer = 'adam', loss = 'mse') 78 | 79 | #train the model now: 80 | 81 | NN_mf = model.fit(Channel_MMSE_t, Channel_t, validation_data = (Channel_MMSE_v, Channel_v), epochs=epoch, batch_size = batch_size, verbose= 1) 82 | #Evaluting performance with varying mse vs snr: 83 | #Obtained a vector with varying noise power 84 | start = 15 85 | stop = 0.1 86 | stepsize = ((stop - start)/(idx-1)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 87 | noise_pw = np.arange(start, stop, stepsize) #Generates vector with elements used as varying noise power 88 | 89 | SNR = np.reciprocal(noise_pw) # SNR is the reciprocal of noise power 90 | print ('**'*8,'SNR is reciprocal of the noise power: see table below','**'*8) 91 | noise_pw = np.reshape(noise_pw, (-1)) 92 | SNR = np.reshape(SNR, (-1)) 93 | print(np.c_[noise_pw, SNR]) 94 | 95 | noise_= np.random.randn(nr,1) 96 | #Obtaining the overall noise vector with its varying power: 97 | #To show the noise vectors multiplied by noise powers respectively/individually 98 | for element in noise_pw: 99 | #print(i, end=', ') 100 | noise__ = [element]*noise_ # Generated Noise Vector (with varying noise level 101 | Noise.append(noise__) 102 | 103 | #Generate new Test Data/Channel Coefficient. This will help give a proof of ability of model to generalize: 104 | #transmit samples or signals, x_Test 105 | Channel_test = np.random.randn(nr,nt) 106 | # Recall: y = Hx + n 107 | for k in range(len(Noise)): 108 | y_N = np.add(np.dot(Channel_test,x),Noise[k]) 109 | y_all_N.append(y_N) 110 | 111 | #Perform MMSquareError of the channel (with varying noise power). 112 | #MMSquareError estimation 113 | Channel_MMSE_N = np.dot((np.dot(y_all_N[k], (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (3*np.identity(nr))))) 114 | Channel_MMSE_all_N.append((np.reshape(Channel_MMSE_N, (1,dim)))) 115 | #predict the trained model 116 | Channel_pred = model.predict(Channel_MMSE_all_N[k], batch_size = idx) 117 | Channel_pred_all.append(Channel_pred) 118 | 119 | for mse in range(idx-1): 120 | hNN_pred= np.reshape(Channel_pred_all[mse],(-1)) #reshapes or flattens the vector to allow being used for plotting 121 | hMMSE = np.reshape(Channel_MMSE_all_N[mse],(-1)) 122 | h = np.reshape(Channel_test, (-1)) 123 | MSE1 = np.mean((h - hNN_pred)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 124 | MSE2 = np.mean((h - hMMSE)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 125 | MSE_NN_all.append(MSE1) 126 | MSE_MMSE_all.append(MSE2) 127 | 128 | c_idx = idx-2 #choose channel index to view 129 | print("TrueChannel=%s, MMSEChannel=%s, PredictedMMSEChannel=%s" % (np.reshape(Channel_test,(nr,nt)), Channel_MMSE_all_N[c_idx], Channel_pred_all[c_idx])) 130 | 131 | ite_t = 400 #the number of test channel for which i will compute the average mse 132 | ccchannel = np.random.randn(ite_t,nr,nt) 133 | y_all_nnn = [] 134 | Channel_MMSE_all_nn = [] 135 | Channel_pred_all_nn = [] 136 | 137 | #generate SNR with same length as the channels i have in order to correctly make a plot: #Evaluting performance with varying mse vs snr: #Obtained a vector with varying noise power 138 | start_ = 15 139 | stop_ = 0.1 140 | stepsize_ = ((stop_ - start_)/(ite_t)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 141 | noise_pw_ = np.arange(start_, stop_, stepsize_) #Generates vector with elements used as varying noise power 142 | SNR_ = np.reciprocal(noise_pw_) # SNR is the reciprocal of noise power 143 | noise_pw_ = np.reshape(noise_pw_, (-1)) 144 | SNR_ = np.reshape(SNR_, (-1)) 145 | #print(np.c_[noise_pw_, SNR_]) 146 | Noise_ = [] 147 | for element in noise_pw_: 148 | #print(i, end=', ') 149 | noise__cc = [element]*noise_ # Generated Noise Vector (with varying noise level 150 | Noise_.append(noise__cc) 151 | 152 | #this computes same noise, but different channel 153 | for nn in range(len(Noise_)): 154 | for cc in range (len (ccchannel)): 155 | y_nn = np.add((np.dot(ccchannel[cc],x)),Noise_[nn])#for each iterate over all the channels 156 | y_all_nnn.append(y_nn) 157 | 158 | #next compute the ls for the output 159 | for lll in range (len(y_all_nnn)): 160 | Channel_MMSE_nn = np.dot((np.dot(y_all_nnn[lll], (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (3*np.identity(nr))))) 161 | Channel_MMSE_all_nn.append(np.reshape(Channel_MMSE_nn, (1,dim))) 162 | #predict the trained model 163 | Channel_pred_nn = model.predict(Channel_MMSE_all_nn[lll], batch_size = idx) 164 | Channel_pred_all_nn.append(Channel_pred_nn) 165 | 166 | #I basically, had to loop over all the channels with varying SNR individually. 167 | Channel_MMSE_all_nnA = [] 168 | Channel_pred_all_nnA = [] 169 | 170 | Channel_MMSE_all_nn_sorted = [ Channel_MMSE_all_nn[i:i+ite_t] for i in range(0, len(Channel_MMSE_all_nn), ite_t) ] ## 171 | [Channel_MMSE_all_nnA.extend(Channel_MMSE_all_nn_sorted[pp]) for pp in range(0,ite_t)] 172 | 173 | Channel_NN_all_nn_sorted = [ Channel_pred_all_nn[i:i+ite_t] for i in range(0, len(Channel_pred_all_nn), ite_t) ] ## 174 | [Channel_pred_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 175 | 176 | avgMSE_MMSE_all = [] 177 | avgMSE_NN_all = [] 178 | 179 | aa = 0 180 | for eee in range(len(ccchannel)): 181 | hNN_pred_ = np.reshape(Channel_pred_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 182 | hMMSE_ = np.reshape(Channel_MMSE_all_nnA[eee+aa],(-1)) 183 | hc = np.reshape(ccchannel[eee], (-1)) 184 | MSE1_ = np.mean((hc - hNN_pred_)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 185 | MSE2_ = np.mean((hc - hMMSE_)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 186 | avgMSE_MMSE_all.append(MSE2_) 187 | avgMSE_NN_all.append(MSE1_) 188 | aa = aa+len(ccchannel) 189 | 190 | #Determine how close the predictions are to the real-values with some toleranace 191 | CompareResult = np.isclose(Channel_MMSE_all_N[c_idx], Channel_pred_all[c_idx], rtol=0.2) #toleranace of +-0.2 192 | print (CompareResult) 193 | correct_pred = np.count_nonzero(CompareResult) 194 | total_number = CompareResult.size 195 | Accuracy = (correct_pred/total_number)*100 196 | accuracy.append(Accuracy) 197 | print ('Prediction Accuracy of', Accuracy,'%') 198 | 199 | #Evaluate the performance of trained model using just one Channel matrix. 200 | plt.plot(noise_pw[::-1], MSE_MMSE_all) 201 | plt.plot(noise_pw[::-1], MSE_NN_all) 202 | plt.title('Graph of MSE with varying SNR (after training)') 203 | plt.ylabel('Mean Square Error') 204 | plt.xlabel('SNR') 205 | plt.legend(['MMSE', 'NN_Pred'], loc='upper left') 206 | plt.grid(b=None, which='major', axis='both') 207 | plt.show() 208 | 209 | #Evaluate the performance of Average MSE from the trained model. 210 | plt.plot(noise_pw_[::-1], avgMSE_MMSE_all) 211 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 212 | plt.title('Graph of Average MSE with varying SNR (after training)') 213 | plt.ylabel('Avg. Mean Square Error') 214 | plt.xlabel('SNR') 215 | plt.legend(['MMSE', 'NN_Pred'], loc='upper left') 216 | plt.grid(b=None, which='major', axis='both') 217 | plt.show() 218 | 219 | #Visualization after training and testing #To see the performance of the 3rd channel coefficient only 220 | #a good overlap means good performance. 221 | Channel_LS_test = np.reshape(Channel_MMSE_all_N[-1], (-1)) 222 | Channel_pred = np.reshape(Channel_pred_all[-1], (-1)) 223 | Channel_test = np.reshape(Channel_test, (-1)) 224 | plt.plot(Channel_LS_test, '*-') 225 | plt.plot(Channel_pred, '.-') 226 | plt.plot(Channel_test, ',-') 227 | plt.ylabel('amplitude') 228 | plt.xlabel('channel coefficient') 229 | plt.title('Plot of Test Channel Data (MMSE) & its Predicted Channel') 230 | plt.legend(['MMSEChannel', 'PredictedMMSEChannel', 'TrueChannel'], loc='upper left') 231 | plt.grid(b=None, which='major', axis='both') 232 | plt.show() 233 | 234 | plt.plot(NN_mf.history['loss']) 235 | plt.plot(NN_mf.history['val_loss']) 236 | plt.title('Graph of Training Loss & its Validation Loss - MMSE') 237 | plt.ylabel('Loss') 238 | plt.xlabel('No. of Epoch') 239 | plt.legend(['Training', 'Validation'], loc='upper left') 240 | plt.grid(b=None, which='major', axis='both') 241 | plt.show() 242 | 243 | #More to visualization: show the sequential layers layers 244 | plot_model(model, show_shapes=True, show_layer_names=True, to_file='NNmodel.png') 245 | from IPython.display import Image 246 | Image(retina=True, filename='NNmodel.png') #saves the picture inot the folder-.py collocation 247 | 248 | #more to visualization of the model: #To obtain the weights and biases at each layer: 249 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 250 | summary = model.summary() 251 | TrainedWeight1 = model.layers[0].get_weights()[0] 252 | TrainedBias1 = model.layers[0].get_weights()[1] 253 | #print("trained weight of layer1 =", TrainedWeight1) 254 | #print("trained bias of layer1 =", TrainedBias1) 255 | 256 | TrainedWeight2a = model.layers[1].get_weights()[0] 257 | TrainedBias2a = model.layers[1].get_weights()[1] 258 | #print("trained weight of layer2 =", TrainedWeight2) 259 | #print("trained bias of layer2 =", TrainedBias2) 260 | 261 | TrainedWeight2b = model.layers[2].get_weights()[0] 262 | TrainedBias2b = model.layers[2].get_weights()[1] 263 | #print("trained weight of layer2 =", TrainedWeight2) 264 | #print("trained bias of layer2 =", TrainedBias2) 265 | 266 | TrainedWeight3 = model.layers[3].get_weights()[0] 267 | TrainedBias3 = model.layers[3].get_weights()[1] 268 | #print("trained weight of layer2 =", TrainedWeight3) 269 | #print("trained bias of layer2 =", TrainedBias3) 270 | 271 | #this will create the network topology or achitecture that i modelled. 272 | #so, in case you get a graphviz exectuabel error, use this llink (https://www.youtube.com/watch?v=q7PzqbKUm_4) to fix it. Cheers. 273 | #from ann_visualizer.visualize import ann_viz; 274 | #ann_viz(model, filename="RNNwithKeras", title="Neural Network Topology for Channel Estimation") 275 | 276 | #Note: If at any point the MSE curve descreases, and then increases again, this could indicate overfitting? So, in this case, i reduce my epoch value 277 | #or try to tweak other hyperparameters, number of nodes. 278 | 279 | -------------------------------------------------------------------------------- /B_LS_ChannelEstimationNN_keras_with_vector.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 19 10:07:41 2020 4 | @author: aguboshimec 5 | """ 6 | 7 | print ('*******MIMO Channel Estimation using Machine Learning-based Approach (LS) *******') 8 | #Chanel Estimation/Prediction with Least Square (4-layer RNeural Network, etc) 9 | import numpy as np 10 | from numpy import mean #used for computing avg. mse 11 | import matplotlib.pyplot as plt 12 | from keras.models import Sequential, Model 13 | from keras.layers import Dense, Activation, Input 14 | from keras.utils import plot_model 15 | import sys 16 | accuracy = [] #store the prediction accuracy per loop (for diff. antenna array sizes) 17 | 18 | def ChannelEstimation(ant_array, pilot, noOfNodes): 19 | 20 | nt = nr = ant_array #number of tx_antennas #number of rx_antennas 21 | dim = nr*nt 22 | batch_size = dim 23 | 24 | training = pilot #training sequence 25 | layer1node = noOfNodes # number of nodes in first layer 26 | layer2anode = noOfNodes # number of nodes in second layer (hidden) 27 | layer2bnode = noOfNodes # number of nodes in third layer (hidden) 28 | layer3node = dim # number of nodes in fourth layer 29 | 30 | #epoch between (188 - 195) seems cool for 4by4 ant array? 31 | epoch = 194 32 | ite = 1000 #This determines the 3rd dimension of the Tensor. With this, we can have: 40 by 4 by 4 Tensor (ie. if ite = 40) 33 | idx = int(ite/2) # the loop index will be the number of the splitted parts of the 3D tensor/array. 34 | Channel_LS_all = [] 35 | Channel_pred_all = [] 36 | Channel_all = [] #this is the true channel coefficients for every corresponding least_sq estimation. 37 | Noise = [] 38 | y_all_N = [] #stores the output of the model with varying noise power 39 | Channel_LS_all_N = [] #stores the output of the LS Channel with varying noise power 40 | 41 | MSE_LS_all = [] # stores the MSE values for coeff. of LS solution 42 | MSE_NN_all = [] # stores the MSE values for coeff. RNN estimation 43 | 44 | #Channel model: y = Hx + n, #Assumption: Time-Invariant Channel, AWGN noise 45 | # H is the channel matrix coeffs, x is the training data, n is the awgn noise, y is the output received 46 | #Training samples or signals 47 | x = np.random.randn(nt,training) #nt by x traning samples 48 | 49 | # Generate or Adding AGWN noise: So, bascially, the noise level is what deteroritate the channel quality. the noise is the only cahning factor here. 50 | noise = np.random.randn(ite,nr,1) 51 | 52 | def Channel_dataset(): #used for testing data set 53 | # Channel (idealized without nosie or true channel coefficients) 54 | for i in range (ite): 55 | Channel = np.random.randn(nr,nt)# same channel for varying noise and constant noise power. Recall: Its LTI 56 | y = np.add(np.dot(Channel,x),noise[i]) 57 | Channel_LS = np.dot(y,(np.linalg.pinv(x))) 58 | Channel_LS_all.append ((np.reshape(Channel_LS, (dim, 1)))) 59 | Channel_all.append(np.reshape(Channel, (dim, 1))) 60 | 61 | Channel_dataset() # calls the function defined above. 62 | #splits the tensor or array into two uniques parts (not vectors this time). Comparing the training loss & verification loss curves will help me know underfitting or overfitting 63 | dataSetSplit = np.array_split(Channel_all, 2) 64 | Channel_v = np.reshape(dataSetSplit[1], (idx,dim)) 65 | Channel_t = np.reshape(dataSetSplit[0], (idx,dim)) 66 | 67 | dataSetSplit_LS = np.array_split(Channel_LS_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 68 | Channel_LS_v = np.reshape(dataSetSplit_LS[1], (idx,dim)) 69 | Channel_LS_t = np.reshape(dataSetSplit_LS[0], (idx,dim)) 70 | 71 | 72 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 73 | model = Sequential() 74 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 75 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 76 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 77 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 78 | model.compile(optimizer = 'adam', loss = 'mse') 79 | 80 | #train the model now: 81 | NN_mf = model.fit(Channel_LS_t, Channel_t, validation_data = (Channel_LS_v, Channel_v), epochs=epoch, batch_size = batch_size, verbose= 1) 82 | 83 | #Evaluting performance with varying mse vs snr: 84 | #Obtained a vector with varying noise power 85 | start = 15 86 | stop = 0.1 87 | stepsize = ((stop - start)/(idx-1)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 88 | noise_pw = np.arange(start, stop, stepsize) #Generates vector with elements used as varying noise power 89 | 90 | SNR = np.reciprocal(noise_pw) # SNR is the reciprocal of noise power 91 | print ('**'*8,'SNR is reciprocal of the noise power: see table below','**'*8) 92 | noise_pw = np.reshape(noise_pw, (-1)) 93 | SNR = np.reshape(SNR, (-1)) 94 | print(np.c_[noise_pw, SNR]) 95 | 96 | noise_= np.random.randn(nr,1) 97 | #Obtaining the overall noise vector with its varying power: 98 | #To show the noise vectors multiplied by noise powers respectively/individually 99 | for element in noise_pw: 100 | #print(i, end=', ') 101 | noise__ = [element]*noise_ # Generated Noise Vector (with varying noise level 102 | Noise.append(noise__) 103 | 104 | #Generate new Test Data/Channel Coefficient. This will help give a proof of ability of model to generalize: 105 | #transmit samples or signals, x_Test 106 | Channel_test = np.random.randn(nr,nt) 107 | 108 | # Recall: y = Hx + n 109 | for k in range(len(Noise)): 110 | y_N = np.add(np.dot(Channel_test,x),Noise[k]) 111 | y_all_N.append(y_N) 112 | 113 | #Perform Least_Square of the channel (with varying noise power). 114 | #Least Square estimation = H_ls = Channel_LS = (y*x_transpose(pinv(x*x_transpose)) 115 | Channel_LS_N = np.dot(y_all_N[k],(np.linalg.pinv(x))) 116 | Channel_LS_all_N.append((np.reshape(Channel_LS_N, (1,dim)))) 117 | #predict the trained model 118 | Channel_pred = model.predict(Channel_LS_all_N[k], batch_size = idx) 119 | Channel_pred_all.append(Channel_pred) 120 | 121 | for mse in range(idx-1): 122 | hNN_pred= np.reshape(Channel_pred_all[mse],(-1)) #reshapes or flattens the vector to allow being used for plotting 123 | hLS = np.reshape(Channel_LS_all_N[mse],(-1)) 124 | h = np.reshape(Channel_test, (-1)) 125 | MSE1 = np.mean((h - hNN_pred)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 126 | MSE2 = np.mean((h - hLS)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 127 | MSE_NN_all.append(MSE1) 128 | MSE_LS_all.append(MSE2) 129 | 130 | c_idx = idx-2 #choose channel index to view 131 | print("TrueChannel=%s, LeastSqChannel=%s, PredictedLSChannel=%s" % (np.reshape(Channel_test,(nr,nt)), Channel_LS_all_N[c_idx], Channel_pred_all[c_idx])) 132 | 133 | ite_t = 300 #the number of test channel for which i will compute the average mse 134 | ccchannel = np.random.randn(ite_t,nr,nt) 135 | y_all_nnn = [] 136 | Channel_LS_all_nn = [] 137 | Channel_pred_all_nn = [] 138 | #generate SNR with same length as the channels i have in order to correctly make a plot: #Evaluting performance with varying mse vs snr: #Obtained a vector with varying noise power 139 | start_ = 15 140 | stop_ = 0.01 141 | stepsize_ = ((stop_ - start_)/(ite_t)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 142 | noise_pw_ = np.arange(start_, stop_, stepsize_) #Generates vector with elements used as varying noise power 143 | SNR_ = np.reciprocal(noise_pw_) # SNR is the reciprocal of noise power 144 | noise_pw_ = np.reshape(noise_pw_, (-1)) 145 | SNR_ = np.reshape(SNR_, (-1)) 146 | #print(np.c_[noise_pw_, SNR_]) 147 | Noise_ = [] 148 | for element in noise_pw_: 149 | #print(i, end=', ') 150 | noise__cc = [element]*noise_ # Generated Noise Vector (with varying noise level 151 | Noise_.append(noise__cc) 152 | #this computes same noise, but different channel 153 | for nn in range(len(Noise_)): 154 | for cc in range (len (ccchannel)): 155 | y_nn = np.add((np.dot(ccchannel[cc],x)),Noise_[nn]) #for each iterate over all the channels 156 | y_all_nnn.append(y_nn) 157 | 158 | #next compute the ls for the output: np.dot(y,(np.linalg.pinv(x))) 159 | for lll in range (len(y_all_nnn)): 160 | Channel_LS_nn = np.dot(y_all_nnn[lll],(np.linalg.pinv(x))) #computes the LS Channel realization for y_all_nnn 161 | Channel_LS_all_nn.append(np.reshape(Channel_LS_nn, (1,dim))) 162 | #predict the trained model 163 | Channel_pred_nn = model.predict(Channel_LS_all_nn[lll], batch_size = idx) #predicts the LS Channel realizations also. 164 | Channel_pred_all_nn.append(Channel_pred_nn) 165 | 166 | #I basically, had to loop over all the channels with varying SNR individually. 167 | Channel_LS_all_nnA = [] 168 | Channel_NN_all_nnA = [] 169 | 170 | Channel_LS_all_nn_sorted = [ Channel_LS_all_nn[i:i+ite_t] for i in range(0, len(Channel_LS_all_nn), ite_t) ] ## 171 | [Channel_LS_all_nnA.extend(Channel_LS_all_nn_sorted[pp]) for pp in range(0,ite_t)] 172 | 173 | Channel_NN_all_nn_sorted = [ Channel_pred_all_nn[i:i+ite_t] for i in range(0, len(Channel_pred_all_nn), ite_t) ] ## 174 | [Channel_NN_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 175 | 176 | avgMSE_LS_all = [] 177 | avgMSE_NN_all = [] 178 | 179 | aa = 0 180 | for eee in range(len(ccchannel)): 181 | hNN_predc = np.reshape(Channel_NN_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 182 | hLSc = np.reshape(Channel_LS_all_nnA[eee+aa],(-1)) 183 | hc = np.reshape(ccchannel[eee], (-1)) 184 | MSE1_ = np.mean((hc - hNN_predc)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 185 | MSE2_ = np.mean((hc - hLSc)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 186 | avgMSE_LS_all.append(MSE2_) 187 | avgMSE_NN_all.append(MSE1_) 188 | aa = aa+len(ccchannel) 189 | 190 | #Determine how close the predictions are to the real-values with some toleranace 191 | CompareResult = np.isclose(Channel_LS_all_N[c_idx], Channel_pred_all[c_idx], rtol=0.2) #toleranace of +-0.2 192 | print (CompareResult) 193 | correct_pred = np.count_nonzero(CompareResult) 194 | total_number = CompareResult.size 195 | Accuracy = (correct_pred/total_number)*100 196 | accuracy.append(Accuracy) 197 | print ('Prediction Accuracy of', Accuracy,'%') 198 | 199 | #Evaluate the performance of trained model using just one Channel matrix. 200 | plt.plot(noise_pw[::-1], MSE_LS_all) 201 | plt.plot(noise_pw[::-1], MSE_NN_all) 202 | plt.title('Graph of MSE with varying SNR (after training)') 203 | plt.ylabel('Mean Square Error') 204 | plt.xlabel('SNR') 205 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 206 | plt.grid(b=None, which='major', axis='both') 207 | plt.show() 208 | 209 | #Evaluate the performance of Average MSE from the trained model. 210 | plt.plot(noise_pw_[::-1], avgMSE_LS_all) 211 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 212 | plt.title('Graph of Average MSE with varying SNR (after training)') 213 | plt.ylabel('Avg. Mean Square Error') 214 | plt.xlabel('SNR') 215 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 216 | plt.grid(b=None, which='major', axis='both') 217 | plt.show() 218 | 219 | #Visualization after training and testing #To see the performance of the 3rd channel coefficient only 220 | #a good overlap means good performance. 221 | Channel_LS_test = np.reshape(Channel_LS_all_N[-1], (-1)) 222 | Channel_pred = np.reshape(Channel_pred_all[-1], (-1)) 223 | Channel_test = np.reshape(Channel_test, (-1)) 224 | plt.plot(Channel_LS_test, '*-') 225 | plt.plot(Channel_pred, '.-') 226 | plt.plot(Channel_test, ',-') 227 | plt.ylabel('amplitude') 228 | plt.xlabel('channel coefficient') 229 | plt.title('Plot of Test Channel Data (LS) & its Predicted Channel') 230 | plt.legend(['LeastSqChannel', 'PredictedLSChannel', 'TrueChannel'], loc='upper left') 231 | plt.grid(b=None, which='major', axis='both') 232 | plt.show() 233 | 234 | 235 | plt.plot(NN_mf.history['loss']) 236 | plt.plot(NN_mf.history['val_loss']) 237 | plt.title('Graph of Training Loss & its Validation Loss - LS') 238 | plt.ylabel('Loss') 239 | plt.xlabel('No. of Epoch') 240 | plt.legend(['Training', 'Validation'], loc='upper left') 241 | plt.grid(b=None, which='major', axis='both') 242 | plt.show() 243 | 244 | #More to visualization: show the sequential layers layers 245 | plot_model(model, show_shapes=True, show_layer_names=True, to_file='NNmodel.png') 246 | from IPython.display import Image 247 | Image(retina=True, filename='NNmodel.png') #saves the picture inot the folder-.py collocation 248 | 249 | 250 | #more to visualization of the model: #To obtain the weights and biases at each layer: 251 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 252 | summary = model.summary() 253 | TrainedWeight1 = model.layers[0].get_weights()[0] 254 | TrainedBias1 = model.layers[0].get_weights()[1] 255 | #print("trained weight of layer1 =", TrainedWeight1) 256 | #print("trained bias of layer1 =", TrainedBias1) 257 | 258 | TrainedWeight2a = model.layers[1].get_weights()[0] 259 | TrainedBias2a = model.layers[1].get_weights()[1] 260 | #print("trained weight of layer2 =", TrainedWeight2) 261 | #print("trained bias of layer2 =", TrainedBias2) 262 | 263 | TrainedWeight2b = model.layers[2].get_weights()[0] 264 | TrainedBias2b = model.layers[2].get_weights()[1] 265 | #print("trained weight of layer2 =", TrainedWeight2) 266 | #print("trained bias of layer2 =", TrainedBias2) 267 | 268 | TrainedWeight3 = model.layers[3].get_weights()[0] 269 | TrainedBias3 = model.layers[3].get_weights()[1] 270 | #print("trained weight of layer2 =", TrainedWeight3) 271 | #print("trained bias of layer2 =", TrainedBias3) 272 | 273 | #this will create the network topology or achitecture that i modelled. 274 | #so, in case you get a graphviz exectuabel error, use this llink (https://www.youtube.com/watch?v=q7PzqbKUm_4) to fix it. Cheers. 275 | #from ann_visualizer.visualize import ann_viz; 276 | #ann_viz(model, filename="RNNwithKeras", title="Neural Network Topology for Channel Estimation") 277 | 278 | #Note: If at any point the MSE curve descreases, and then increases again, this could indicate overfitting? So, in this case, i reduce my epoch value 279 | #or try to tweak other hyperparameters, number of nodes. 280 | # Here, I effected the idea of evaluating several antenna array numbers. 281 | ant_array = [4, 6] #so i chose this values at random. Any number or value can work too. 282 | pilot = [6, 8] # here also, i chose 150% of corresponding antenna value. 283 | noOfNodes = [25, 40] #i realized that i get better estimation using varying number of nodes for diff. antenna array sizes 284 | 285 | j = 0 286 | if (ant_array[j] < pilot[j]): 287 | print ("parameters are appropriate") #just for control measures. nothng really serious. 288 | else: 289 | print("Error! ant_array must be at least less than pilot!") #the code halts if the vlaues do not conform to what is expected. #no high condition number should be gotten. 290 | sys.exit() 291 | j = j + 1 #I am iterating over all the elements in the lists. 292 | 293 | for i in range (len(pilot)): #length of list - pilot and that of antenna array are same. 294 | print ("*** Evaluates channel estimation performance for", ant_array[i], "by", ant_array[i], "antenna array ***") # I made in such a way that it print the antenna array size under evaluation. 295 | ChannelEstimation(ant_array[i], pilot[i], noOfNodes[i]) # this is the main stuff that calls the function/ 296 | 297 | #the idea behind this visualization is this: (not so important). This helped me to know that a constant node size does not work for all antenna array sizes. 298 | #there is a possibility that the more the antenna size, the less suitable is a general neural network topology, number of nodes, epoch value, etc could be for the antenna array size. 299 | #this is just a way of seeing if the prediction preformance improved or decline per antenna array size. Has it always declined? 300 | x_axis = np.arange(len(ant_array)) 301 | y_axis = accuracy 302 | plt.bar(x_axis, y_axis, align='center', alpha=0.5) 303 | plt.xticks(x_axis, ant_array) 304 | plt.ylabel('Percentage prediction') 305 | plt.xlabel('Antenna Array size') 306 | plt.title('Visualization of prediction accuracy for different antenna array sizes') 307 | plt.show() 308 | 309 | -------------------------------------------------------------------------------- /D_MMSE_ChannelEstimationNN_keras_with_vector.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 19 10:07:41 2020 4 | @author: aguboshimec 5 | """ 6 | 7 | print ('*******MIMO Channel Estimation using Machine Learning-based Approach (MMSE)*******') 8 | #Chanel Estimation/Prediction with Least Square (4-layer RNeural Network, etc) 9 | import numpy as np 10 | from numpy import mean #used for computing avg. mse 11 | import matplotlib.pyplot as plt 12 | from keras.models import Sequential, Model 13 | from keras.layers import Dense, Activation, Input 14 | from keras.utils import plot_model 15 | import sys 16 | accuracy = [] #store the prediction accuracy per loop (for diff. antenna array sizes) 17 | 18 | def ChannelEstimationMMSE(ant_array, pilot, noOfNodes): 19 | 20 | nt = nr = ant_array #number of tx_antennas #number of rx_antennas 21 | dim = nr*nt 22 | batch_size = dim 23 | 24 | training = pilot #training sequence 25 | layer1node = noOfNodes # number of nodes in first layer 26 | layer2anode = noOfNodes # number of nodes in second layer (hidden) 27 | layer2bnode = noOfNodes # number of nodes in third layer (hidden) 28 | layer3node = dim # number of nodes in fourth layerr 29 | 30 | #epoch between (188 - 195) seems cool for 4by4 ant array? 31 | epoch = 1850 32 | ite = 1000 #This determines the 3rd dimension of the Tensor. With this, we can have: 40 by 4 by 4 Tensor (ie. if ite = 40) 33 | idx = int(ite/2) # the loop index will be the number of the splitted parts of the 3D tensor/array. 34 | Channel_MMSE_all = [] 35 | Channel_pred_all = [] 36 | Channel_all = [] #this is the true channel coefficients for every corresponding least_sq estimation. 37 | Noise = [] 38 | y_all_N = [] #stores the output of the model with varying noise power 39 | Channel_MMSE_all_N = [] #stores the output of the MMSE Channel with varying noise power #I didnt use this again. 40 | 41 | MSE_MMSE_all = [] # stores the MSE values for coeff. of LS solution 42 | MSE_NN_all = [] # stores the MSE values for coeff. RNN estimation 43 | 44 | #Channel model: y = Hx + n, #Assumption: Time-Invariant Channel, AWGN noise 45 | # H is the channel matrix coeffs, x is the training data, n is the awgn noise, y is the output received 46 | #Training samples or signals 47 | x = np.random.randn(nt,training) #nt by x traning samples 48 | 49 | # Generate or Adding AGWN noise: So, bascially, the noise level is what deteroritate the channel quality. the noise is the only cahning factor here. 50 | noise = np.random.randn(ite,nr,1) 51 | 52 | def Channel_dataset(): #used for testing data set 53 | # Channel (idealized without nosie or true channel coefficients) 54 | for i in range (ite): 55 | Channel = np.random.randn(nr,nt)# same channel for varying noise and constant noise power. Recall: Its LTI 56 | y = np.add(np.dot(Channel,x),noise[i]) 57 | 58 | #Minimum Mean Square estimation = H_mmse = Chanel_MMSE = (Rhh(Rhh + ((1/SNR)Identity_matrix)H_ls 59 | Channel_MMSE = np.dot((np.dot(y, (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (3*np.identity(nr))))) 60 | Channel_MMSE_all.append(np.reshape(Channel_MMSE, (dim, 1))) 61 | Channel_all.append(np.reshape(Channel, (dim, 1))) 62 | 63 | Channel_dataset() # calls the function defined above. 64 | #splits the tensor or array into two uniques parts (not vectors this time). Comparing the training loss & verification loss curves will help me know underfitting or overfitting 65 | dataSetSplit = np.array_split(Channel_all, 2) 66 | Channel_v = np.reshape(dataSetSplit[1], (idx,dim)) 67 | Channel_t = np.reshape(dataSetSplit[0], (idx,dim)) 68 | 69 | dataSetSplit_MMSE = np.array_split(Channel_MMSE_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 70 | Channel_MMSE_v = np.reshape(dataSetSplit_MMSE[1], (idx,dim)) 71 | Channel_MMSE_t = np.reshape(dataSetSplit_MMSE[0], (idx,dim)) 72 | 73 | 74 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 75 | model = Sequential() 76 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 77 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 78 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 79 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 80 | model.compile(optimizer = 'adam', loss = 'mse') 81 | 82 | #train the model now: 83 | 84 | NN_mf = model.fit(Channel_MMSE_t, Channel_t, validation_data = (Channel_MMSE_v, Channel_v), epochs=epoch, batch_size = batch_size, verbose= 1) 85 | 86 | #Evaluting performance with varying mse vs snr: 87 | #Obtained a vector with varying noise power 88 | start = 15 89 | stop = 0.01 90 | stepsize = ((stop - start)/(idx-1)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 91 | noise_pw = np.arange(start, stop, stepsize) #Generates vector with elements used as varying noise power 92 | 93 | SNR = np.reciprocal(noise_pw) # SNR is the reciprocal of noise power 94 | print ('**'*8,'SNR is reciprocal of the noise power: see table below','**'*8) 95 | noise_pw = np.reshape(noise_pw, (-1)) 96 | SNR = np.reshape(SNR, (-1)) 97 | print(np.c_[noise_pw, SNR]) 98 | 99 | noise_= np.random.randn(nr,1) 100 | #Obtaining the overall noise vector with its varying power: 101 | #To show the noise vectors multiplied by noise powers respectively/individually 102 | for element in noise_pw: 103 | #print(i, end=', ') 104 | noise__ = [element]*noise_ # Generated Noise Vector (with varying noise level 105 | Noise.append(noise__) 106 | 107 | #Generate new Test Data/Channel Coefficient. This will help give a proof of ability of model to generalize: 108 | #transmit samples or signals, x_Test 109 | Channel_test = np.random.randn(nr,nt) 110 | 111 | # Recall: y = Hx + n 112 | for k in range(len(Noise)): 113 | y_N = np.add(np.dot(Channel_test,x),Noise[k]) 114 | y_all_N.append(y_N) 115 | 116 | #Perform MMSquareError of the channel (with varying noise power). 117 | Channel_MMSE_N = np.dot((np.dot(y_all_N[k], (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (3*np.identity(nr))))) 118 | Channel_MMSE_all_N.append((np.reshape(Channel_MMSE_N, (1,dim)))) 119 | #predict the trained model 120 | Channel_pred = model.predict(Channel_MMSE_all_N[k], batch_size = idx) 121 | Channel_pred_all.append(Channel_pred) 122 | 123 | for mse in range(idx-1): 124 | hNN_pred= np.reshape(Channel_pred_all[mse],(-1)) #reshapes or flattens the vector to allow being used for plotting 125 | hMMSE = np.reshape(Channel_MMSE_all_N[mse],(-1)) 126 | h = np.reshape(Channel_test, (-1)) 127 | MSE1 = np.mean((h - hNN_pred)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 128 | MSE2 = np.mean((h - hMMSE)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 129 | MSE_NN_all.append(MSE1) 130 | MSE_MMSE_all.append(MSE2) 131 | 132 | c_idx = idx-2 #choose channel index to view 133 | print("TrueChannel=%s, MMSEChannel=%s, PredictedMMSEChannel=%s" % (np.reshape(Channel_test,(nr,nt)), Channel_MMSE_all_N[c_idx], Channel_pred_all[c_idx])) 134 | 135 | ite_t = 200 #the number of test channel for which i will compute the average mse 136 | ccchannel = np.random.randn(ite_t,nr,nt) 137 | y_all_nnn = [] 138 | Channel_MMSE_all_nn = [] 139 | Channel_pred_all_nn = [] 140 | 141 | #generate SNR with same length as the channels i have in order to correctly make a plot: #Evaluting performance with varying mse vs snr: #Obtained a vector with varying noise power 142 | start_ = 15 143 | stop_ = 0.1 144 | stepsize_ = ((stop_ - start_)/(ite_t)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 145 | noise_pw_ = np.arange(start_, stop_, stepsize_) #Generates vector with elements used as varying noise power 146 | SNR_ = np.reciprocal(noise_pw_) # SNR is the reciprocal of noise power 147 | noise_pw_ = np.reshape(noise_pw_, (-1)) 148 | SNR_ = np.reshape(SNR_, (-1)) 149 | #print(np.c_[noise_pw_, SNR_]) 150 | Noise_ = [] 151 | for element in noise_pw_: 152 | #print(i, end=', ') 153 | noise__cc = [element]*noise_ # Generated Noise Vector (with varying noise level 154 | Noise_.append(noise__cc) 155 | 156 | #this computes same noise, but different channel 157 | for nn in range(len(Noise_)): 158 | for cc in range (len (ccchannel)): 159 | y_nn = np.add((np.dot(ccchannel[cc],x)),Noise_[nn])#for each iterate over all the channels 160 | y_all_nnn.append(y_nn) 161 | 162 | #next compute the ls for the output 163 | for lll in range (len(y_all_nnn)): 164 | Channel_MMSE_nn = np.dot((np.dot(y_all_nnn[lll], (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (3*np.identity(nr))))) 165 | Channel_MMSE_all_nn.append(np.reshape(Channel_MMSE_nn, (1,dim))) 166 | #predict the trained model 167 | Channel_pred_nn = model.predict(Channel_MMSE_all_nn[lll], batch_size = idx) 168 | Channel_pred_all_nn.append(Channel_pred_nn) 169 | 170 | #I basically, had to loop over all the channels with varying SNR individually. 171 | Channel_MMSE_all_nnA = [] 172 | Channel_pred_all_nnA = [] 173 | 174 | Channel_MMSE_all_nn_sorted = [ Channel_MMSE_all_nn[i:i+ite_t] for i in range(0, len(Channel_MMSE_all_nn), ite_t) ] ## 175 | [Channel_MMSE_all_nnA.extend(Channel_MMSE_all_nn_sorted[pp]) for pp in range(0,ite_t)] 176 | 177 | Channel_NN_all_nn_sorted = [ Channel_pred_all_nn[i:i+ite_t] for i in range(0, len(Channel_pred_all_nn), ite_t) ] ## 178 | [Channel_pred_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 179 | 180 | avgMSE_MMSE_all = [] 181 | avgMSE_NN_all = [] 182 | 183 | aa = 0 184 | for eee in range(len(ccchannel)): 185 | hNN_pred_ = np.reshape(Channel_pred_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 186 | hMMSE_ = np.reshape(Channel_MMSE_all_nnA[eee+aa],(-1)) 187 | hc = np.reshape(ccchannel[eee], (-1)) 188 | MSE1_ = np.mean((hc - hNN_pred_)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 189 | MSE2_ = np.mean((hc - hMMSE_)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 190 | avgMSE_MMSE_all.append(MSE2_) 191 | avgMSE_NN_all.append(MSE1_) 192 | aa = aa+len(ccchannel) 193 | 194 | #Determine how close the predictions are to the real-values with some toleranace 195 | CompareResult = np.isclose(Channel_MMSE_all_N[c_idx], Channel_pred_all[c_idx], rtol=0.2) #toleranace of +-0.2 196 | print (CompareResult) 197 | correct_pred = np.count_nonzero(CompareResult) 198 | total_number = CompareResult.size 199 | Accuracy = (correct_pred/total_number)*100 200 | accuracy.append(Accuracy) 201 | print ('Prediction Accuracy of', Accuracy,'%') 202 | 203 | #Evaluate the performance of trained model using just one Channel matrix. 204 | plt.plot(noise_pw[::-1], MSE_MMSE_all) 205 | plt.plot(noise_pw[::-1], MSE_NN_all) 206 | plt.title('Graph of MSE with varying SNR (after training)') 207 | plt.ylabel('Mean Square Error') 208 | plt.xlabel('SNR') 209 | plt.legend(['MMSE', 'NN_Pred'], loc='upper left') 210 | plt.grid(b=None, which='major', axis='both') 211 | plt.show() 212 | 213 | #Evaluate the performance of Average MSE from the trained model. 214 | plt.plot(noise_pw_[::-1], avgMSE_MMSE_all) 215 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 216 | plt.title('Graph of Average MSE with varying SNR (after training)') 217 | plt.ylabel('Avg. Mean Square Error') 218 | plt.xlabel('SNR') 219 | plt.legend(['MMSE', 'NN_Pred'], loc='upper left') 220 | plt.grid(b=None, which='major', axis='both') 221 | plt.show() 222 | 223 | 224 | #Visualization after training and testing #To see the performance of the 3rd channel coefficient only 225 | #a good overlap means good performance. 226 | Channel_LS_test = np.reshape(Channel_MMSE_all_N[-1], (-1)) 227 | Channel_pred = np.reshape(Channel_pred_all[-1], (-1)) 228 | Channel_test = np.reshape(Channel_test, (-1)) 229 | plt.plot(Channel_LS_test, '*-') 230 | plt.plot(Channel_pred, '.-') 231 | plt.plot(Channel_test, ',-') 232 | plt.ylabel('amplitude') 233 | plt.xlabel('channel coefficient') 234 | plt.title('Plot of Test Channel Data (MMSE) & its Predicted Channel') 235 | plt.legend(['MMSEChannel', 'PredictedMMSEChannel', 'TrueChannel'], loc='upper left') 236 | plt.grid(b=None, which='major', axis='both') 237 | plt.show() 238 | 239 | 240 | plt.plot(NN_mf.history['loss']) 241 | plt.plot(NN_mf.history['val_loss']) 242 | plt.title('Graph of Training Loss & its Validation Loss - MMSE') 243 | plt.ylabel('Loss') 244 | plt.xlabel('No. of Epoch') 245 | plt.legend(['Training', 'Validation'], loc='upper left') 246 | plt.grid(b=None, which='major', axis='both') 247 | plt.show() 248 | 249 | #More to visualization: show the sequential layers layers 250 | plot_model(model, show_shapes=True, show_layer_names=True, to_file='NNmodel.png') 251 | from IPython.display import Image 252 | Image(retina=True, filename='NNmodel.png') #saves the picture inot the folder-.py collocation 253 | 254 | #more to visualization of the model: #To obtain the weights and biases at each layer: 255 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 256 | summary = model.summary() 257 | TrainedWeight1 = model.layers[0].get_weights()[0] 258 | TrainedBias1 = model.layers[0].get_weights()[1] 259 | #print("trained weight of layer1 =", TrainedWeight1) 260 | #print("trained bias of layer1 =", TrainedBias1) 261 | 262 | TrainedWeight2a = model.layers[1].get_weights()[0] 263 | TrainedBias2a = model.layers[1].get_weights()[1] 264 | #print("trained weight of layer2 =", TrainedWeight2) 265 | #print("trained bias of layer2 =", TrainedBias2) 266 | 267 | TrainedWeight2b = model.layers[2].get_weights()[0] 268 | TrainedBias2b = model.layers[2].get_weights()[1] 269 | #print("trained weight of layer2 =", TrainedWeight2) 270 | #print("trained bias of layer2 =", TrainedBias2) 271 | 272 | TrainedWeight3 = model.layers[3].get_weights()[0] 273 | TrainedBias3 = model.layers[3].get_weights()[1] 274 | #print("trained weight of layer2 =", TrainedWeight3) 275 | #print("trained bias of layer2 =", TrainedBias3) 276 | 277 | #this will create the network topology or achitecture that i modelled. 278 | #so, in case you get a graphviz exectuabel error, use this llink (https://www.youtube.com/watch?v=q7PzqbKUm_4) to fix it. Cheers. 279 | #from ann_visualizer.visualize import ann_viz; 280 | #ann_viz(model, filename="RNNwithKeras", title="Neural Network Topology for Channel Estimation") 281 | 282 | #Note: If at any point the MSE curve descreases, and then increases again, this could indicate overfitting? So, in this case, i reduce my epoch value 283 | #or try to tweak other hyperparameters, number of nodes. 284 | # Here, I effected the idea of evaluating several antenna array numbers. 285 | ant_array = [4, 6] #so i chose this values at random. Any number or value can work too. 286 | pilot = [6, 9] # here also, i chose 150% of corresponding antenna value. 287 | noOfNodes = [25, 40] #i realized that i get better estimation using varying number of nodes for diff. antenna array sizes 288 | 289 | j = 0 290 | if (ant_array[j] < pilot[j]): 291 | print ("parameters are appropriate") #just for control measures. nothng really serious. 292 | else: 293 | print("Error! ant_array must be at least less than pilot!") #the code halts if the vlaues do not conform to what is expected. #no high condition number should be gotten. 294 | sys.exit() 295 | j = j + 1 #I am iterating over all the elements in the lists. 296 | 297 | for i in range (len(pilot)): #length of list - pilot and that of antenna array are same. 298 | print ("*** Evaluates channel estimation performance for", ant_array[i], "by", ant_array[i], "antenna array ***") # I made in such a way that it print the antenna array size under evaluation. 299 | ChannelEstimationMMSE(ant_array[i], pilot[i], noOfNodes[i]) # this is the main stuff that calls the function/ 300 | 301 | #the idea behind this visualization is this: (not so important). This helped me to know that a constant node size does not work for all antenna array sizes. 302 | #there is a possibility that the more the antenna size, the less suitable is a general neural network topology, number of nodes, epoch value, etc could be for the antenna array size. 303 | #this is just a way of seeing if the prediction preformance improved or decline per antenna array size. Has it always declined? 304 | x_axis = np.arange(len(ant_array)) 305 | y_axis = accuracy 306 | plt.bar(x_axis, y_axis, align='center', alpha=0.5) 307 | plt.xticks(x_axis, ant_array) 308 | plt.ylabel('Percentage prediction') 309 | plt.xlabel('Antenna Array size') 310 | plt.title('Visualization of prediction accuracy for different antenna array sizes') 311 | plt.show() 312 | 313 | 314 | -------------------------------------------------------------------------------- /F_ChannelEstimationNN_keras_vary_noisepw_LS+MMSE.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 19 10:07:41 2020 4 | @author: aguboshimec 5 | """ 6 | 7 | print ('*******MIMO Channel Estimation using Machine Learning-based Approach (LS & MMSE)*******') 8 | #Channel Estimation/Prediction with Least Square (4-layer RNeural Network, etc) 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | from keras.models import Sequential 12 | from keras.layers import Dense 13 | from keras.utils import plot_model 14 | import sys 15 | 16 | accuracy = [] #store the prediction accuracy per loop (for diff. antenna array sizes). We can visualize how the accuracy decrease with high noise power, but constant epoch value and other parameters. 17 | overall_MSE_NN_all = [] #this store all the mse per noise power. I can then find a what to extract them, and plot all the values on a single graph 18 | overall_MSE_LS_all = [] #same here as above. 19 | 20 | nt = nr = 4 #number of tx_antennas #number of rx_antennas 21 | dim = nr*nt 22 | batch_size = dim 23 | #epoch between (188 - 195) seems cool for 4by4 ant array? 24 | epoch = 194 25 | ite = 10000 26 | noisepwr = list(range(1,8,2)) #here, I generated the noise power levels. 27 | # Generate or Adding AGWN noise: So, bascially, the noise level is what deteroritate the channel quality. the noise is the only cahning factor here. 28 | noise = np.random.randn(ite,nr,1) #the noise coeffs. has to be constant. What changes will be the power 29 | training = 6 #training sequence 30 | layer1node = 25 # number of nodes in first layer 31 | layer2anode = 25 # number of nodes in second layer (hidden) 32 | layer2bnode = 25 # number of nodes in third layer (hidden) 33 | layer3node = dim # number of nodes in fourth layer 34 | 35 | #with the global status, i can then reuse them for the MMSE evalation. It makes sense that way since for proper comparison, i have to use the same datasets. 36 | Chanel_v = None 37 | h = None 38 | Chanel_t = None 39 | Chanel_test = None 40 | ite_t = None 41 | stepSize = None 42 | h_ = None 43 | idx = None 44 | ccchannel = None 45 | Chanel_MMSE_v = None 46 | Chanel_MMSE_t = None 47 | #dim = None # uncomment and detach from batchsize if both are not same value 48 | Noise = None 49 | noise_pw = None 50 | noise_pw_ = None 51 | y_all_N = None 52 | y_all_nnn = None 53 | x = None 54 | summary = None 55 | ccchannel = None 56 | 57 | if (nr < training): 58 | print ("parameters are appropriate") #just for control measures. nothng really serious. 59 | else: 60 | print("Error! ant_array must be at least less than pilot!") #the code halts if the vlaues do not conform to what is expected. 61 | sys.exit() 62 | 63 | def ChannelEstimationLS(jj): 64 | global y_all_nnn, ccchannel,stepSize, ite_t, summary, ite, idx, Chanel_MMSE_v, Chanel_MMSE_t, Chanel_v, h, Chanel_t, batch_size, Chanel_test, Noise, x, y_all_N 65 | #This determines the 3rd dimension of the Tensor. With this, we can have: 40 by 4 by 4 Tensor (ie. if ite = 40) 66 | idx = int(ite/2) # the loop index will be the number of the splitted parts of the 3D tensor/array. 67 | Chanel_LS_all = [] 68 | Chanel_pred_all = [] 69 | Chanel_all = [] #this is the true chanel coefficients for every corresponding least_sq estimation. 70 | Chanel_MMSE_all = [] 71 | Noise = [] 72 | y_all_N = [] #stores the output of the model with varying noise power 73 | Chanel_LS_all_N = [] #stores the output of the LS Chanel with varying noise power 74 | 75 | MSE_LS_all = [] # stores the MSE values for coeff. of LS solution 76 | MSE_n_all = [] # stores the MSE values for coeff. Rn estimation 77 | 78 | #Chanel model: y = Hx + n, #Assumption: Time-Invariant Chanel, AWGN noise 79 | # H is the chanel matrix coeffs, x is the training data, n is the awgn noise, y is the output received 80 | #Training samples or signals 81 | x = np.random.randn(nt,training) #nt by x traning samples 82 | 83 | def Chanel_dataset(): #used for testing data set 84 | # Channel (idealized without nosie or true channel coefficients 85 | for i in range (ite): 86 | Chanel = np.random.randn(nr,nt)# same channel for varying noise and constant noise power. Recall: Its LTI 87 | y = np.add(np.dot(Chanel,x),(jj*noise[i])) 88 | 89 | #Least Square Estimation 90 | Chanel_LS = np.dot(y,(np.linalg.pinv(x))) 91 | #Minimum Mean Square estimation = 92 | Chanel_MMSE = np.dot((np.dot(y, (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (4*np.identity(nr))))) 93 | Chanel_MMSE_all.append(np.reshape(Chanel_MMSE, (dim, 1))) 94 | Chanel_LS_all.append ((np.reshape(Chanel_LS, (dim, 1)))) 95 | Chanel_all.append(np.reshape(Chanel, (dim, 1))) 96 | 97 | Chanel_dataset() # calls the function defined above. 98 | #splits the tensor or array into two uniques parts (not vectors this time). Comparing the training loss & verification loss curves will help me know underfitting or overfitting 99 | dataSetSplit = np.array_split(Chanel_all, 2) 100 | Chanel_v = np.reshape(dataSetSplit[1], (idx,dim)) 101 | Chanel_t = np.reshape(dataSetSplit[0], (idx,dim)) 102 | 103 | dataSetSplit_LS = np.array_split(Chanel_LS_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 104 | Chanel_LS_v = np.reshape(dataSetSplit_LS[1], (idx,dim)) 105 | Chanel_LS_t = np.reshape(dataSetSplit_LS[0], (idx,dim)) 106 | 107 | #i could not have splitted into 2 uneven parts 'cos the graph shows lines which has to be equal. #I decided to do the splitting here, and then call the variable from the MMSE script. 108 | dataSetSplit_MMSE = np.array_split(Chanel_MMSE_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 109 | Chanel_MMSE_v = np.reshape(dataSetSplit_MMSE[1], (idx,dim)) 110 | Chanel_MMSE_t = np.reshape(dataSetSplit_MMSE[0], (idx,dim)) 111 | 112 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 113 | model = Sequential() 114 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 115 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 116 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 117 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 118 | model.compile(optimizer = 'adam', loss = 'mse') 119 | 120 | #train the model now: 121 | n_mf = model.fit(Chanel_LS_t, Chanel_t, validation_data = (Chanel_LS_v, Chanel_v), epochs=epoch, batch_size = batch_size, verbose= 1) 122 | 123 | #Evaluting performance with varying mse vs snr: 124 | #Obtained a vector with varying noise power 125 | start = 15 126 | stop = 0.01 127 | stepsize = ((stop - start)/(idx-1)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 128 | noise_pw = np.arange(start, stop, stepsize) #Generates vector with elements used as varying noise power 129 | 130 | SNR = np.reciprocal(noise_pw) # SNR is the reciprocal of noise power 131 | print ('**'*8,'SNR is reciprocal of the noise power: see table below','**'*8) 132 | noise_pw = np.reshape(noise_pw, (-1)) 133 | SNR = np.reshape(SNR, (-1)) 134 | print(np.c_[noise_pw, SNR]) 135 | 136 | noise_= np.random.randn(nr,1) 137 | #Obtaining the overall noise vector with its varying power: 138 | #To show the noise vectors multiplied by noise powers respectively/individually 139 | for element in noise_pw: 140 | #print(i, end=', ') 141 | noise__ = [element]*noise_ # Generated Noise Vector (with varying noise level 142 | Noise.append(noise__) 143 | 144 | #Generate new Test Data/Chanel Coefficient. This will help give a proof of ability of model to generalize: 145 | #transmit samples or signals, x_Test 146 | Chanel_test = np.random.randn(nr,nt) 147 | 148 | # Recall: y = Hx + n 149 | for k in range(len(Noise)): 150 | y_N = np.add(np.dot(Chanel_test,x),Noise[k]) 151 | y_all_N.append(y_N) 152 | 153 | #Perform Least_Square of the chanel (with varying noise power). 154 | #Least Square estimation = H_ls = Chanel_LS = (y*x_transpose(pinv(x*x_transpose)) 155 | Chanel_LS_N = np.dot(y_all_N[k],(np.linalg.pinv(x))) 156 | 157 | Chanel_LS_all_N.append(np.reshape(Chanel_LS_N, (1,dim))) 158 | #predict the trained model 159 | Chanel_pred = model.predict(Chanel_LS_all_N[k], batch_size = idx) 160 | Chanel_pred_all.append(Chanel_pred) 161 | 162 | for mse in range(idx-1): 163 | hn_pred= np.reshape(Chanel_pred_all[mse],(-1)) #reshapes or flattens the vector to allow being used for plotting 164 | hLS = np.reshape(Chanel_LS_all_N[mse],(-1)) 165 | h = np.reshape(Chanel_test, (-1)) 166 | MSE1 = np.mean((h - hn_pred)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 167 | MSE2 = np.mean((h - hLS)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 168 | MSE_n_all.append(MSE1) 169 | MSE_LS_all.append(MSE2) 170 | 171 | c_idx = idx-2 #choose chanel index to view 172 | print("TrueChanel=%s, LeastSqChanel=%s, PredictedLSChanel=%s" % (np.reshape(Chanel_test,(nr,nt)), Chanel_LS_all_N[c_idx], Chanel_pred_all[c_idx])) 173 | 174 | ite_t = 300 #the number of test channel for which i will compute the average mse 175 | ccchannel = np.random.randn(ite_t,nr,nt) 176 | y_all_nnn = [] 177 | Channel_LS_all_nn = [] 178 | Channel_pred_all_nn = [] 179 | #generate SNR with same length as the channels i have in order to correctly make a plot: #Evaluting performance with varying mse vs snr: #Obtained a vector with varying noise power 180 | start_ = 15 181 | stop_ = 0.01 182 | stepsize_ = ((stop_ - start_)/(ite_t)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 183 | noise_pw_ = np.arange(start_, stop_, stepsize_) #Generates vector with elements used as varying noise power 184 | SNR_ = np.reciprocal(noise_pw_) # SNR is the reciprocal of noise power 185 | noise_pw_ = np.reshape(noise_pw_, (-1)) 186 | SNR_ = np.reshape(SNR_, (-1)) 187 | #print(np.c_[noise_pw_, SNR_]) 188 | Noise_ = [] 189 | for element in noise_pw_: 190 | #print(i, end=', ') 191 | noise__cc = [element]*noise_ # Generated Noise Vector (with varying noise level 192 | Noise_.append(noise__cc) 193 | #this computes same noise, but different channel 194 | for nn in range(len(Noise_)): 195 | for cc in range (len (ccchannel)): 196 | y_nn = np.add((np.dot(ccchannel[cc],x)),Noise_[nn]) #for each iterate over all the channels 197 | y_all_nnn.append(y_nn) 198 | 199 | #next compute the ls for the output: np.dot(y,(np.linalg.pinv(x))) 200 | for lll in range (len(y_all_nnn)): 201 | Channel_LS_nn = np.dot(y_all_nnn[lll],(np.linalg.pinv(x))) #computes the LS Channel realization for y_all_nnn 202 | Channel_LS_all_nn.append(np.reshape(Channel_LS_nn, (1,dim))) 203 | #predict the trained model 204 | Channel_pred_nn = model.predict(Channel_LS_all_nn[lll], batch_size = idx) #predicts the LS Channel realizations also. 205 | Channel_pred_all_nn.append(Channel_pred_nn) 206 | 207 | #I basically, had to loop over all the channels with varying SNR individually. 208 | Channel_LS_all_nnA = [] 209 | Channel_NN_all_nnA = [] 210 | 211 | Channel_LS_all_nn_sorted = [ Channel_LS_all_nn[i:i+ite_t] for i in range(0, len(Channel_LS_all_nn), ite_t) ] ## 212 | [Channel_LS_all_nnA.extend(Channel_LS_all_nn_sorted[pp]) for pp in range(0,ite_t)] 213 | 214 | Channel_NN_all_nn_sorted = [ Channel_pred_all_nn[i:i+ite_t] for i in range(0, len(Channel_pred_all_nn), ite_t) ] ## 215 | [Channel_NN_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 216 | 217 | avgMSE_LS_all = [] 218 | avgMSE_NN_all = [] 219 | 220 | aa = 0 221 | for eee in range(len(ccchannel)): 222 | hNN_predc = np.reshape(Channel_NN_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 223 | hLSc = np.reshape(Channel_LS_all_nnA[eee+aa],(-1)) 224 | hc = np.reshape(ccchannel[eee], (-1)) 225 | MSE1_ = np.mean((hc - hNN_predc)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 226 | MSE2_ = np.mean((hc - hLSc)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 227 | avgMSE_LS_all.append(MSE2_) 228 | avgMSE_NN_all.append(MSE1_) 229 | aa = aa+len(ccchannel) 230 | 231 | #Determine how close the predictions are to the real-values with some toleranace 232 | CompareResult = np.isclose(Chanel_LS_all_N[c_idx], Chanel_pred_all[c_idx], rtol=0.2) #toleranace of +-0.2 233 | print (CompareResult) 234 | correct_pred = np.count_nonzero(CompareResult) 235 | total_number = CompareResult.size 236 | Accuracy = (correct_pred/total_number)*100 #multiplied by 100 to get the percetage value immediately 237 | accuracy.append(Accuracy) 238 | print ('Prediction Accuracy of', Accuracy,'%') 239 | 240 | #Evaluate the performance of trained model. 241 | plt.plot(noise_pw[::-1], MSE_LS_all) 242 | plt.plot(noise_pw[::-1], MSE_n_all) 243 | plt.title('Graph of MSE with varying SNR (after training)') 244 | plt.ylabel('Mean Square Error') 245 | plt.xlabel('Signal to Noise Ratio') 246 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 247 | plt.grid(b=None, which='major', axis='both') 248 | plt.show() 249 | 250 | #Evaluate the performance of Average MSE from the trained model. 251 | plt.plot(noise_pw_[::-1], avgMSE_LS_all) 252 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 253 | plt.title('Graph of Average MSE with varying SNR (after training)') 254 | plt.ylabel('Avg. Mean Square Error') 255 | plt.xlabel('SNR') 256 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 257 | plt.grid(b=None, which='major', axis='both') 258 | plt.show() 259 | 260 | #Visualization after training and testing #To see the performance of the 3rd chanel coefficient only 261 | #a good overlap means good performance. 262 | Chanel_LS_test = np.reshape(Chanel_LS_all_N[-1], (-1)) 263 | Chanel_pred = np.reshape(Chanel_pred_all[-1], (-1)) 264 | Chanel_test = np.reshape(Chanel_test, (-1)) 265 | plt.plot(Chanel_LS_test, '*-') 266 | plt.plot(Chanel_pred, '.-') 267 | plt.plot(Chanel_test, ',-') 268 | plt.ylabel('amplitude') 269 | plt.xlabel('chanel coefficient') 270 | plt.title('Plot of Test Chanel Data (LS) & its Predicted Chanel') 271 | plt.legend(['LeastSqChanel', 'PredictedLSChanel', 'TrueChanel'], loc='upper left') 272 | plt.grid(b=None, which='major', axis='both') 273 | plt.show() 274 | 275 | 276 | plt.plot(n_mf.history['loss']) 277 | plt.plot(n_mf.history['val_loss']) 278 | plt.title('Graph of Training Loss & its Validation Loss - LS') 279 | plt.ylabel('Loss') 280 | plt.xlabel('No. of Epoch') 281 | plt.legend(['Training', 'Validation'], loc='upper left') 282 | plt.grid(b=None, which='major', axis='both') 283 | plt.show() 284 | 285 | #More to visualization: show the sequential layers layers 286 | plot_model(model, show_shapes=True, show_layer_names=True, to_file='Rnmodel.png') 287 | from IPython.display import Image 288 | Image(retina=True, filename='Rnmodel.png') #saves the picture inot the folder-.py collocation 289 | 290 | #more to visualization of the model: #To obtain the weights and biases at each layer: 291 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 292 | summary = model.summary() 293 | TrainedWeight1 = model.layers[0].get_weights()[0] 294 | TrainedBias1 = model.layers[0].get_weights()[1] 295 | #print("trained weight of layer1 =", TrainedWeight1) 296 | #print("trained bias of layer1 =", TrainedBias1) 297 | 298 | TrainedWeight2a = model.layers[1].get_weights()[0] 299 | TrainedBias2a = model.layers[1].get_weights()[1] 300 | #print("trained weight of layer2 =", TrainedWeight2a) 301 | #print("trained bias of layer2 =", TrainedBias2) 302 | 303 | TrainedWeight2b = model.layers[2].get_weights()[0] 304 | TrainedBias2b = model.layers[2].get_weights()[1] 305 | #print("trained weight of layer2 =", TrainedWeight2a) 306 | #print("trained bias of layer2 =", TrainedBias2) 307 | 308 | TrainedWeight3 = model.layers[3].get_weights()[0] 309 | TrainedBias3 = model.layers[3].get_weights()[1] 310 | #print("trained weight of layer2 =", TrainedWeight3) 311 | #print("trained bias of layer2 =", TrainedBias3) 312 | 313 | #this will create the network topology or achitecture that i modelled. 314 | #so, in case you get a graphviz exectuabel error, use this llink (https://www.youtube.com/watch?v=q7PzqbKUm_4) to fix it. Cheers. 315 | #from an_visualizer.visualize import an_viz; 316 | #an_viz(model, filename="RnwithKeras", title="Neural Network Topology for Chanel Estimation") 317 | 318 | #Note: If at any point the MSE curve descreases, and then increases again, this could indicate overfitting? So, in this case, i reduce my epoch value 319 | #or try to tweak other hyperparameters, number of nodes. 320 | 321 | accuracy_MMSE = [] 322 | def ChannelEstimationMMSE(): 323 | epoch_MMSE = 1850 #since there is a possibility of quicker convergence for MMSE than for LS, I decided to use a different epoch value here. 324 | 325 | Chanel_MMSE_all_N = [] #stores the output of the MMSE Chanel with varying noise power #I didnt use this again. 326 | Chanel_pred_MMSE_all = [] #output of the predicted MMSE Estimation 327 | MSE_n_all_mmse = [] #mean sqaure erorr of mmse estimation after training 328 | MSE_MMSE_all = [] #mean square error of mmse estmation before training 329 | 330 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 331 | model = Sequential() 332 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 333 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 334 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 335 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 336 | model.compile(optimizer = 'adam', loss = 'mse') 337 | 338 | #train the model now: 339 | n_mf_MMSE = model.fit(Chanel_MMSE_t, Chanel_t, validation_data = (Chanel_MMSE_v, Chanel_v), epochs=epoch_MMSE, batch_size = batch_size, verbose= 1) 340 | 341 | for k in range(len(Noise)): 342 | Chanel_MMSE_N = np.dot((np.dot(y_all_N[k], (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (5*np.identity(nr))))) 343 | Chanel_MMSE_all_N.append(np.reshape(Chanel_MMSE_N, (1,dim))) 344 | 345 | #predict the trained model from the MMSE data 346 | Chanel_pred_MMSE = model.predict(Chanel_MMSE_all_N[k], batch_size = idx) 347 | Chanel_pred_MMSE_all.append(Chanel_pred_MMSE) 348 | 349 | #evaluate the mean square error: 350 | for mse_ in range(idx-1): 351 | hn_pred_MMSE = np.reshape(Chanel_pred_MMSE_all[mse_], (-1)) #reshapes or flattens the vector to allow being used for plotting 352 | hMMSE = np.reshape(Chanel_MMSE_all_N[mse_],(-1)) 353 | MSE1 = np.mean((h - hn_pred_MMSE)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 354 | MSE2 = np.mean((h - hMMSE)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 355 | MSE_n_all_mmse.append(MSE1) 356 | MSE_MMSE_all.append(MSE2) 357 | 358 | c_idx = idx-2 #choose chanel index to view 359 | print("TrueChanel=%s, MMSqEChanel=%s, PredictedMMSEChanel=%s" % (np.reshape(Chanel_test,(nr,nt)), Chanel_MMSE_all_N[c_idx], Chanel_pred_MMSE_all[c_idx])) 360 | Channel_MMSE_all_nn = [] 361 | Channel_pred_all__nn = [] 362 | 363 | #next compute the ls for the output 364 | for lll in range (len(y_all_nnn)): 365 | Channel_MMSE_nn = np.dot((np.dot(y_all_nnn[lll], (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (2*np.identity(nr))))) 366 | Channel_MMSE_all_nn.append(np.reshape(Channel_MMSE_nn, (1,dim))) 367 | #predict the trained model 368 | Channel_pred__nn = model.predict(Channel_MMSE_all_nn[lll], batch_size = idx) 369 | Channel_pred_all__nn.append(Channel_pred__nn) 370 | 371 | #I basically, had to loop over all the channels with varying SNR individually. 372 | Channel_MMSE_all_nnA = [] 373 | Channel_pred_all_nnA = [] 374 | Channel_MMSE_all_nn_sorted = [ Channel_MMSE_all_nn[i:i+ite_t] for i in range(0, len(Channel_MMSE_all_nn), ite_t) ] ## 375 | [Channel_MMSE_all_nnA.extend(Channel_MMSE_all_nn_sorted[pp]) for pp in range(0,ite_t)] 376 | 377 | Channel_NN_all_nn_sorted = [ Channel_pred_all__nn[i:i+ite_t] for i in range(0, len(Channel_pred_all__nn), ite_t) ] ## 378 | [Channel_pred_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 379 | 380 | avgMSE_MMSE_all = [] 381 | avgMSE_NN_all = [] 382 | aa = 0 383 | for eee in range(len(ccchannel)): 384 | hNN_pred_ = np.reshape(Channel_pred_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 385 | hMMSE_ = np.reshape(Channel_MMSE_all_nnA[eee+aa],(-1)) 386 | hc = np.reshape(ccchannel[eee], (-1)) 387 | MSE1_ = np.mean((hc - hNN_pred_)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 388 | MSE2_ = np.mean((hc - hMMSE_)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 389 | avgMSE_MMSE_all.append(MSE2_) 390 | avgMSE_NN_all.append(MSE1_) 391 | aa = aa+len(ccchannel) 392 | 393 | #Determine how close the predictions are to the real-values with some toleranace 394 | CompareResult_MMSE = np.isclose(Chanel_MMSE_all_N[c_idx], Chanel_pred_MMSE_all[c_idx], rtol=0.2) #toleranace of +-0.2 395 | print (CompareResult_MMSE) 396 | correct_pred_mmse = np.count_nonzero(CompareResult_MMSE) 397 | total_number_mmse = CompareResult_MMSE.size 398 | Accuracy_MMSE = (correct_pred_mmse/total_number_mmse)*100 399 | print ('Prediction Accuracy of', Accuracy_MMSE,'%') 400 | accuracy_MMSE.append(Accuracy_MMSE) 401 | 402 | 403 | #more to visualization of the model: #To obtain the weights and biases at each layer: 404 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 405 | summary = model.summary() 406 | TrainedWeight1_MMSE = model.layers[0].get_weights()[0] 407 | TrainedBias1_MMSE = model.layers[0].get_weights()[1] 408 | #print("trained weight of layer1 =", TrainedWeight1_MMSE) 409 | #print("trained bias of layer1 =", TrainedBias1_MMSE) 410 | 411 | TrainedWeight2a_MMSE = model.layers[1].get_weights()[0] 412 | TrainedBias2a_MMSE = model.layers[1].get_weights()[1] 413 | #print("trained weight of layer2 =", TrainedWeight2a_MMSE) 414 | #print("trained bias of layer2 =", TrainedBias2a_MMSE) 415 | 416 | TrainedWeight2b_MMSE = model.layers[2].get_weights()[0] 417 | TrainedBias2b_MMSE = model.layers[2].get_weights()[1] 418 | #print("trained weight of layer2 =", TrainedWeight2b_MMSE) 419 | #print("trained bias of layer2 =", TrainedBias2b_MMSE) 420 | 421 | TrainedWeight3_MMSE = model.layers[3].get_weights()[0] 422 | TrainedBias3MMSE = model.layers[3].get_weights()[1] 423 | #print("trained weight of layer2 =", TrainedWeight3_MMSE) 424 | #print("trained bias of layer2 =", TrainedBias3_MMSE) 425 | 426 | #Evaluate the performance of trained model. # Display the individal plot for the MMSE performance 427 | plt.plot(noise_pw[::-1], MSE_MMSE_all) 428 | plt.plot(noise_pw[::-1], MSE_n_all_mmse) 429 | plt.title('Graph of MSE with varying SNR (after training)') 430 | plt.ylabel('Mean Square Error') 431 | plt.xlabel('Signal to Noise Ratio') 432 | plt.legend(['MMSE', 'NN_Pred'], loc='upper left') 433 | plt.grid(b=None, which='major', axis='both') 434 | plt.show() 435 | 436 | #Evaluate the performance of Average MSE from the trained model. 437 | plt.plot(noise_pw_[::-1], avgMSE_MMSE_all) 438 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 439 | plt.title('Graph of Average MSE with varying SNR (after training)') 440 | plt.ylabel('Avg. Mean Square Error') 441 | plt.xlabel('SNR') 442 | plt.legend(['MMSE', 'NN_Pred'], loc='upper left') 443 | plt.grid(b=None, which='major', axis='both') 444 | plt.show() 445 | 446 | 447 | plt.plot(n_mf_MMSE.history['loss']) 448 | plt.plot(n_mf_MMSE.history['val_loss']) 449 | plt.title('Graph of Training Loss & its Validation Loss - MMSE') 450 | plt.ylabel('Loss') 451 | plt.xlabel('No. of Epoch') 452 | plt.legend(['Training', 'Validation'], loc='upper left') 453 | plt.grid(b=None, which='major', axis='both') 454 | plt.show() 455 | 456 | 457 | #Visualization after training and testing #To see the performance of the 3rd chanel coefficient only 458 | #a good overlap means good performance. 459 | Chanel_MMSE_test = np.reshape(Chanel_MMSE_all_N[-1], (-1)) 460 | Chanel_pred_MMSE = np.reshape(Chanel_pred_MMSE_all[-1], (-1)) 461 | plt.plot(Chanel_MMSE_test, '*-') 462 | plt.plot(Chanel_pred_MMSE, '.-') 463 | plt.plot(np.reshape(Chanel_test, (-1)), ',-') 464 | plt.ylabel('amplitude') 465 | plt.xlabel('chanel coefficient') 466 | plt.title('Plot of Test Chanel Data (MMSE) & its Predicted Chanel') 467 | plt.legend(['MMSEChanel', 'PredictedMMSEChanel', 'TrueChanel'], loc='upper left') 468 | plt.grid(b=None, which='major', axis='both') 469 | plt.show() 470 | 471 | #this is the main activator: 472 | for jj in range(len(noisepwr)): 473 | print ('LS Channel Estimation for noise power of', noisepwr[jj]) 474 | ChannelEstimationLS(jj) 475 | print ('MMSE Channel Estimation for noise power of', noisepwr[jj]) 476 | ChannelEstimationMMSE() #this will not be a function of jj anymore. I am reusing the data i got from the LS computations. 477 | 478 | #make a simply plot of the varying accuracy as noise power is introduced. 479 | plt.plot(accuracy, '-*') 480 | plt.title('Graph of LS prediction accuracy with varying noise power (before training)') 481 | plt.ylabel('Predicition Accuracy') 482 | plt.xlabel('Varying Noise Power') 483 | plt.grid(b=None, which='major', axis='both') 484 | plt.show() 485 | 486 | plt.plot(accuracy_MMSE, '-*') 487 | plt.title('Graph of MMSE prediction accuracy with varying noise power (before training)') 488 | plt.ylabel('Predicition Accuracy') 489 | plt.xlabel('Varying Noise Power') 490 | plt.grid(b=None, which='major', axis='both') 491 | plt.show() -------------------------------------------------------------------------------- /E_ChannelEstimationNN_keras_LS+MMSE.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 19 10:07:41 2020 4 | @author: aguboshimec 5 | """ 6 | 7 | print ('*******MIMO Channel Estimation using Machine Learning-based Approach (LS & MMSE)*******') 8 | #Chanel Estimation/Prediction with Least Square (4-layer RNeural Network, etc) 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | from keras.models import Sequential 12 | from keras.layers import Dense 13 | from keras.utils import plot_model 14 | import sys 15 | accuracy = [] #store the prediction accuracy per loop (for diff. antena array sizes) 16 | 17 | #the next few lines with 'var1 = None' are basically to intialize the variables before I can declare them as global. 18 | #with the global status, i can then reuse them for the MMSE evalation. It makes sense that way since for proper comparison, i have to use the same datasets. 19 | layer1node = None 20 | layer2anode = None 21 | layer2bnode = None 22 | layer3node = None 23 | Chanel_v = None 24 | h = None 25 | batch_size = dim = None #if you want a different batchsize, then separate dim from batchszie, or just uncomment dim as in below 26 | Chanel_t = None 27 | Chanel_test = None 28 | ite = None 29 | ite_t = None 30 | stepSize = None 31 | idx = None 32 | ccchannel = None 33 | Chanel_MMSE_v = None 34 | Chanel_MMSE_t = None 35 | nt = nr = None 36 | #dim = None # uncomment and detach from batchsize if both are not same value 37 | Noise = None 38 | noise_pw_ = None 39 | noise_pw = None 40 | y_all_N = None 41 | y_all_nnn = None 42 | x = None 43 | summary = None 44 | 45 | 46 | def ChanelEstimation(ant_array, pilot, noOfNodes): 47 | global noise_pw_, noise_pw, layer1node, nt, dim, layer2anode, layer2bnode, layer2bnode, y_all_nnn, ccchannel,stepSize, layer3node, ite_t, summary, ite, idx, Chanel_MMSE_v, Chanel_MMSE_t, Chanel_v, h, nr, Chanel_t, batch_size, Chanel_test, Noise, x, y_all_N 48 | nt = nr = ant_array #number of tx_antenas #number of rx_antenas 49 | dim = nr*nt 50 | batch_size = dim 51 | training = pilot #training sequence 52 | layer1node = noOfNodes # number of nodes in first layer 53 | layer2anode = noOfNodes # number of nodes in second layer (hidden) 54 | layer2bnode = noOfNodes # number of nodes in third layer (hidden) 55 | layer3node = dim # number of nodes in fourth layer 56 | 57 | #epoch between (188 - 195) seems cool for 4by4 ant array? 58 | epoch = 195 59 | ite = 8000 60 | #This determines the 3rd dimension of the Tensor. With this, we can have: 40 by 4 by 4 Tensor (ie. if ite = 40) 61 | idx = int(ite/2) # the loop index will be the number of the splitted parts of the 3D tensor/array. 62 | Chanel_LS_all = [] 63 | Chanel_pred_all = [] 64 | Chanel_all = [] #this is the true chanel coefficients for every corresponding least_sq estimation. 65 | Chanel_MMSE_all = [] 66 | Noise = [] 67 | y_all_N = [] #stores the output of the model with varying noise power 68 | Chanel_LS_all_N = [] #stores the output of the LS Chanel with varying noise power 69 | 70 | MSE_LS_all = [] # stores the MSE values for coeff. of LS solution 71 | MSE_NN_all = [] # stores the MSE values for coeff. Rn estimation 72 | 73 | #Chanel model: y = Hx + n, #Assumption: Time-Invariant Chanel, AWGN noise 74 | # H is the chanel matrix coeffs, x is the training data, n is the awgn noise, y is the output received 75 | #Training samples or signals 76 | x = np.random.randn(nt,training) #nt by x traning samples 77 | 78 | # Generate or Adding AGWN noise: So, bascially, the noise level is what deteroritate the chanel quality. the noise is the only cahning factor here. 79 | noise = np.random.randn(ite,nr,1) 80 | 81 | def Chanel_dataset(): #used for testing data set 82 | # Chanel (idealized without nosie or true chanel coefficients) 83 | for i in range (ite): 84 | Chanel = np.random.randn(nr,nt)# same chanel for varying noise and constant noise power. Recall: Its LTI 85 | #Perform MMSE Estimation. (Analytical Solution is: (inverse(Rhh + (transpose(x*(transpose(x)))))*hLS) 86 | y = np.add(np.dot(Chanel,x),noise[i]) 87 | 88 | 89 | 90 | 91 | #Least Square Estimation 92 | Chanel_LS = np.dot(y,(np.linalg.pinv(x))) 93 | #Minimum Mean Square estimation = 94 | Chanel_MMSE = np.dot((np.dot(y, (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (4*np.identity(nr))))) 95 | Chanel_MMSE_all.append(np.reshape(Chanel_MMSE, (dim, 1))) 96 | Chanel_LS_all.append ((np.reshape(Chanel_LS, (dim, 1)))) 97 | Chanel_all.append(np.reshape(Chanel, (dim, 1))) 98 | 99 | Chanel_dataset() # calls the function defined above. 100 | #splits the tensor or array into two uniques parts (not vectors this time). Comparing the training loss & verification loss curves will help me know underfitting or overfitting 101 | dataSetSplit = np.array_split(Chanel_all, 2) 102 | Chanel_v = np.reshape(dataSetSplit[1], (idx,dim)) 103 | Chanel_t = np.reshape(dataSetSplit[0], (idx,dim)) 104 | 105 | dataSetSplit_LS = np.array_split(Chanel_LS_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 106 | Chanel_LS_v = np.reshape(dataSetSplit_LS[1], (idx,dim)) 107 | Chanel_LS_t = np.reshape(dataSetSplit_LS[0], (idx,dim)) 108 | 109 | #i could not have splitted into 2 uneven parts 'cos the graph shows lines which has to be equal. #I decided to do the splitting here, and then call the variable from the MMSE script. 110 | dataSetSplit_MMSE = np.array_split(Chanel_MMSE_all, 2) #splits the tensor or array into two uniques parts (not vectors this time) 111 | Chanel_MMSE_v = np.reshape(dataSetSplit_MMSE[1], (idx,dim)) 112 | Chanel_MMSE_t = np.reshape(dataSetSplit_MMSE[0], (idx,dim)) 113 | 114 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 115 | model = Sequential() 116 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 117 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 118 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 119 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 120 | model.compile(optimizer = 'adam', loss = 'mse') 121 | 122 | #train the model now: 123 | n_mf = model.fit(Chanel_LS_t, Chanel_t, validation_data = (Chanel_LS_v, Chanel_v), epochs=epoch, batch_size = batch_size, verbose= 1) 124 | 125 | #Evaluting performance with varying mse vs snr: 126 | #Obtained a vector with varying noise power 127 | start = 15 128 | stop = 0.1 129 | stepsize = ((stop - start)/(idx-1)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 130 | noise_pw = np.arange(start, stop, stepsize) #Generates vector with elements used as varying noise power 131 | 132 | SNR = np.reciprocal(noise_pw) # SNR is the reciprocal of noise power 133 | print ('**'*8,'SNR is reciprocal of the noise power: see table below','**'*8) 134 | noise_pw = np.reshape(noise_pw, (-1)) 135 | SNR = np.reshape(SNR, (-1)) 136 | print(np.c_[noise_pw, SNR]) 137 | 138 | noise_= np.random.randn(nr,1) 139 | #Obtaining the overall noise vector with its varying power: 140 | #To show the noise vectors multiplied by noise powers respectively/individually 141 | for element in noise_pw: 142 | #print(i, end=', ') 143 | noise__ = [element]*noise_ # Generated Noise Vector (with varying noise level 144 | Noise.append(noise__) 145 | 146 | #Generate new Test Data/Chanel Coefficient. This will help give a proof of ability of model to generalize: 147 | #transmit samples or signals, x_Test 148 | Chanel_test = np.random.randn(nr,nt) 149 | 150 | # Recall: y = Hx + n 151 | for k in range(len(Noise)): 152 | y_N = np.add(np.dot(Chanel_test,x),Noise[k]) 153 | y_all_N.append(y_N) 154 | 155 | #Perform Least_Square of the chanel (with varying noise power). 156 | #Least Square estimation = H_ls = Chanel_LS = (y*x_transpose(pinv(x*x_transpose)) 157 | Chanel_LS_N = np.dot(y_all_N[k],(np.linalg.pinv(x))) 158 | 159 | Chanel_LS_all_N.append(np.reshape(Chanel_LS_N, (1,dim))) 160 | #predict the trained model 161 | Chanel_pred = model.predict(Chanel_LS_all_N[k], batch_size = idx) 162 | Chanel_pred_all.append(Chanel_pred) 163 | 164 | for mse in range(idx-1): 165 | hn_pred= np.reshape(Chanel_pred_all[mse],(-1)) #reshapes or flattens the vector to allow being used for plotting 166 | hLS = np.reshape(Chanel_LS_all_N[mse],(-1)) 167 | h = np.reshape(Chanel_test, (-1)) 168 | MSE1 = np.mean((h - hn_pred)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 169 | MSE2 = np.mean((h - hLS)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 170 | MSE_NN_all.append(MSE1) 171 | MSE_LS_all.append(MSE2) 172 | 173 | c_idx = idx-2 #choose chanel index to view 174 | print("TrueChanel=%s, LeastSqChanel=%s, PredictedLSChanel=%s" % (np.reshape(Chanel_test,(nr,nt)), Chanel_LS_all_N[c_idx], Chanel_pred_all[c_idx])) 175 | 176 | ite_t = 300 #the number of test channel for which i will compute the average mse 177 | ccchannel = np.random.randn(ite_t,nr,nt) 178 | y_all_nnn = [] 179 | Channel_LS_all_nn = [] 180 | Channel_pred_all_nn = [] 181 | #generate SNR with same length as the channels i have in order to correctly make a plot: #Evaluting performance with varying mse vs snr: #Obtained a vector with varying noise power 182 | start_ = 15 183 | stop_ = 0.01 184 | stepsize_ = ((stop_ - start_)/(ite_t)) # i divided by 'cos I wanted to reduce the length of the vector. nothing really technical 185 | noise_pw_ = np.arange(start_, stop_, stepsize_) #Generates vector with elements used as varying noise power 186 | SNR_ = np.reciprocal(noise_pw_) # SNR is the reciprocal of noise power 187 | noise_pw_ = np.reshape(noise_pw_, (-1)) 188 | SNR_ = np.reshape(SNR_, (-1)) 189 | #print(np.c_[noise_pw_, SNR_]) 190 | Noise_ = [] 191 | for element in noise_pw_: 192 | #print(i, end=', ') 193 | noise__cc = [element]*noise_ # Generated Noise Vector (with varying noise level 194 | Noise_.append(noise__cc) 195 | #this computes same noise, but different channel 196 | for nn in range(len(Noise_)): 197 | for cc in range (len (ccchannel)): 198 | y_nn = np.add((np.dot(ccchannel[cc],x)),Noise_[nn]) #for each iterate over all the channels 199 | y_all_nnn.append(y_nn) 200 | 201 | #next compute the ls for the output: np.dot(y,(np.linalg.pinv(x))) 202 | for lll in range (len(y_all_nnn)): 203 | Channel_LS_nn = np.dot(y_all_nnn[lll],(np.linalg.pinv(x))) #computes the LS Channel realization for y_all_nnn 204 | Channel_LS_all_nn.append(np.reshape(Channel_LS_nn, (1,dim))) 205 | #predict the trained model 206 | Channel_pred_nn = model.predict(Channel_LS_all_nn[lll], batch_size = idx) #predicts the LS Channel realizations also. 207 | Channel_pred_all_nn.append(Channel_pred_nn) 208 | 209 | #I basically, had to loop over all the channels with varying SNR individually. 210 | Channel_LS_all_nnA = [] 211 | Channel_NN_all_nnA = [] 212 | 213 | Channel_LS_all_nn_sorted = [ Channel_LS_all_nn[i:i+ite_t] for i in range(0, len(Channel_LS_all_nn), ite_t) ] ## 214 | [Channel_LS_all_nnA.extend(Channel_LS_all_nn_sorted[pp]) for pp in range(0,ite_t)] 215 | 216 | Channel_NN_all_nn_sorted = [ Channel_pred_all_nn[i:i+ite_t] for i in range(0, len(Channel_pred_all_nn), ite_t) ] ## 217 | [Channel_NN_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 218 | 219 | avgMSE_LS_all = [] 220 | avgMSE_NN_all = [] 221 | 222 | aa = 0 223 | for eee in range(len(ccchannel)): 224 | hNN_predc = np.reshape(Channel_NN_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 225 | hLSc = np.reshape(Channel_LS_all_nnA[eee+aa],(-1)) 226 | hc = np.reshape(ccchannel[eee], (-1)) 227 | MSE1_ = np.mean((hc - hNN_predc)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 228 | MSE2_ = np.mean((hc - hLSc)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 229 | avgMSE_LS_all.append(MSE2_) 230 | avgMSE_NN_all.append(MSE1_) 231 | aa = aa+len(ccchannel) 232 | 233 | #Determine how close the predictions are to the real-values with some toleranace 234 | CompareResult = np.isclose(Chanel_LS_all_N[c_idx], Chanel_pred_all[c_idx], rtol=0.2) #toleranace of +-0.2 235 | print (CompareResult) 236 | correct_pred = np.count_nonzero(CompareResult) 237 | total_number = CompareResult.size 238 | Accuracy = (correct_pred/total_number)*100 #multiplied by 100 to get the percetage value immediately 239 | accuracy.append(Accuracy) 240 | print ('Prediction Accuracy of', Accuracy,'%') 241 | 242 | #Evaluate the performance of trained model using just one Channel matrix. 243 | plt.plot(noise_pw[::-1], MSE_LS_all) 244 | plt.plot(noise_pw[::-1], MSE_NN_all) 245 | plt.title('Graph of MSE with varying SNR (after training)') 246 | plt.ylabel('Mean Square Error') 247 | plt.xlabel('SNR') 248 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 249 | plt.grid(b=None, which='major', axis='both') 250 | plt.show() 251 | 252 | #Evaluate the performance of Average MSE from the trained model. 253 | plt.plot(noise_pw_[::-1], avgMSE_LS_all) 254 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 255 | plt.title('Graph of Average MSE with varying SNR (after training)') 256 | plt.ylabel('Avg. Mean Square Error') 257 | plt.xlabel('SNR') 258 | plt.legend(['LS', 'NN_Pred'], loc='upper left') 259 | plt.grid(b=None, which='major', axis='both') 260 | plt.show() 261 | 262 | #Visualization after training and testing #To see the performance of the 3rd chanel coefficient only 263 | #a good overlap means good performance. 264 | Chanel_LS_test = np.reshape(Chanel_LS_all_N[-1], (-1)) 265 | Chanel_pred = np.reshape(Chanel_pred_all[-1], (-1)) 266 | Chanel_test = np.reshape(Chanel_test, (-1)) 267 | plt.plot(Chanel_LS_test, '*-') 268 | plt.plot(Chanel_pred, '.-') 269 | plt.plot(Chanel_test, ',-') 270 | plt.ylabel('amplitude') 271 | plt.xlabel('chanel coefficient') 272 | plt.title('Plot of Test Chanel Data (LS) & its Predicted Chanel') 273 | plt.legend(['LeastSqChanel', 'PredictedLSChanel', 'TrueChanel'], loc='upper left') 274 | plt.grid(b=None, which='major', axis='both') 275 | plt.show() 276 | 277 | 278 | plt.plot(n_mf.history['loss']) 279 | plt.plot(n_mf.history['val_loss']) 280 | plt.title('Graph of Training Loss & its Validation Loss - LS') 281 | plt.ylabel('Loss') 282 | plt.xlabel('No. of Epoch') 283 | plt.legend(['Training', 'Validation'], loc='upper left') 284 | plt.grid(b=None, which='major', axis='both') 285 | plt.show() 286 | 287 | #More to visualization: show the sequential layers layers 288 | plot_model(model, show_shapes=True, show_layer_names=True, to_file='Rnmodel.png') 289 | from IPython.display import Image 290 | Image(retina=True, filename='Rnmodel.png') #saves the picture inot the folder-.py collocation 291 | 292 | 293 | #more to visualization of the model: #To obtain the weights and biases at each layer: 294 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 295 | summary = model.summary() 296 | TrainedWeight1 = model.layers[0].get_weights()[0] 297 | TrainedBias1 = model.layers[0].get_weights()[1] 298 | #print("trained weight of layer1 =", TrainedWeight1) 299 | #print("trained bias of layer1 =", TrainedBias1) 300 | 301 | TrainedWeight2a = model.layers[1].get_weights()[0] 302 | TrainedBias2a = model.layers[1].get_weights()[1] 303 | #print("trained weight of layer2 =", TrainedWeight2a) 304 | #print("trained bias of layer2 =", TrainedBias2) 305 | 306 | TrainedWeight2b = model.layers[2].get_weights()[0] 307 | TrainedBias2b = model.layers[2].get_weights()[1] 308 | #print("trained weight of layer2 =", TrainedWeight2a) 309 | #print("trained bias of layer2 =", TrainedBias2) 310 | 311 | TrainedWeight3 = model.layers[3].get_weights()[0] 312 | TrainedBias3 = model.layers[3].get_weights()[1] 313 | #print("trained weight of layer2 =", TrainedWeight3) 314 | #print("trained bias of layer2 =", TrainedBias3) 315 | 316 | #this will create the network topology or achitecture that i modelled. 317 | #so, in case you get a graphviz exectuabel error, use this llink (https://www.youtube.com/watch?v=q7PzqbKUm_4) to fix it. Cheers. 318 | #from an_visualizer.visualize import an_viz; 319 | #an_viz(model, filename="RnwithKeras", title="Neural Network Topology for Chanel Estimation") 320 | 321 | #Note: If at any point the MSE curve descreases, and then increases again, this could indicate overfitting? So, in this case, i reduce my epoch value 322 | #or try to tweak other hyperparameters, number of nodes. 323 | accuracy_MMSE = [] 324 | def ChanelEstimation_MMSE(ant_array, pilot, noOfNodes): 325 | epoch_MMSE = 1850 #since there is a possibility of quicker convergence for MMSE than for LS, I decided to use a different epoch value here. 326 | 327 | Chanel_MMSE_all_N = [] #stores the output of the MMSE Chanel with varying noise power #I didnt use this again. 328 | Chanel_pred_MMSE_all = [] #output of the predicted MMSE Estimation 329 | MSE_n_all_mmse = [] #mean sqaure erorr of mmse estimation after training 330 | MSE_MMSE_all = [] #mean square error of mmse estmation before training 331 | 332 | 333 | #Building the network: Setting up layers, activation functions, optimizers, and other metrics. 334 | model = Sequential() 335 | model.add(Dense(layer1node, init = 'random_uniform',activation='relu', input_shape =(dim,)))#first layer #I used dense layering for now here 336 | model.add(Dense(layer2anode , init = 'uniform', activation='relu'))# Hidden layer 337 | model.add(Dense(layer2bnode, init = 'random_uniform', activation='relu'))#Hidden layer, 338 | model.add(Dense(layer3node, init = 'uniform', activation='linear', input_shape = (dim,))) #Output layer, 339 | model.compile(optimizer = 'adam', loss = 'mse') 340 | 341 | #train the model now: 342 | n_mf_MMSE = model.fit(Chanel_MMSE_t, Chanel_t, validation_data = (Chanel_MMSE_v, Chanel_v), epochs=epoch_MMSE, batch_size = batch_size, verbose= 1) 343 | 344 | 345 | 346 | for k in range(len(Noise)): 347 | Chanel_MMSE_N = np.dot((np.dot(y_all_N[k], (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (5*np.identity(nr))))) 348 | Chanel_MMSE_all_N.append(np.reshape(Chanel_MMSE_N, (1,dim))) 349 | 350 | #predict the trained model from the MMSE data 351 | Chanel_pred_MMSE = model.predict(Chanel_MMSE_all_N[k], batch_size = idx) 352 | Chanel_pred_MMSE_all.append(Chanel_pred_MMSE) 353 | 354 | #evaluate the mean square error: 355 | for mse_ in range(idx-1): 356 | hn_pred_MMSE = np.reshape(Chanel_pred_MMSE_all[mse_], (-1)) #reshapes or flattens the vector to allow being used for plotting 357 | hMMSE = np.reshape(Chanel_MMSE_all_N[mse_],(-1)) 358 | MSE1 = np.mean((h - hn_pred_MMSE)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 359 | MSE2 = np.mean((h - hMMSE)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 360 | MSE_n_all_mmse.append(MSE1) 361 | MSE_MMSE_all.append(MSE2) 362 | 363 | c_idx = idx-2 #choose chanel index to view 364 | print("TrueChanel=%s, MMSqEChanel=%s, PredictedMMSEChanel=%s" % (np.reshape(Chanel_test,(nr,nt)), Chanel_MMSE_all_N[c_idx], Chanel_pred_MMSE_all[c_idx])) 365 | 366 | Channel_MMSE_all_nn = [] 367 | Channel_pred_all__nn = [] 368 | 369 | #next compute the ls for the output 370 | for lll in range (len(y_all_nnn)): 371 | Channel_MMSE_nn = np.dot((np.dot(y_all_nnn[lll], (np.transpose(x)))),np.linalg.pinv(np.add(np.dot(x,(np.transpose(x))), (2*np.identity(nr))))) 372 | Channel_MMSE_all_nn.append(np.reshape(Channel_MMSE_nn, (1,dim))) 373 | #predict the trained model 374 | Channel_pred__nn = model.predict(Channel_MMSE_all_nn[lll], batch_size = idx) 375 | Channel_pred_all__nn.append(Channel_pred__nn) 376 | 377 | 378 | 379 | #I basically, had to loop over all the channels with varying SNR individually. 380 | Channel_MMSE_all_nnA = [] 381 | Channel_pred_all_nnA = [] 382 | 383 | Channel_MMSE_all_nn_sorted = [ Channel_MMSE_all_nn[i:i+ite_t] for i in range(0, len(Channel_MMSE_all_nn), ite_t) ] ## 384 | [Channel_MMSE_all_nnA.extend(Channel_MMSE_all_nn_sorted[pp]) for pp in range(0,ite_t)] 385 | 386 | Channel_NN_all_nn_sorted = [ Channel_pred_all__nn[i:i+ite_t] for i in range(0, len(Channel_pred_all__nn), ite_t) ] ## 387 | [Channel_pred_all_nnA.extend(Channel_NN_all_nn_sorted[pp]) for pp in range(0,ite_t)] 388 | 389 | avgMSE_MMSE_all = [] 390 | avgMSE_NN_all = [] 391 | 392 | aa = 0 393 | for eee in range(len(ccchannel)): 394 | hNN_pred_ = np.reshape(Channel_pred_all_nnA[eee+aa],(-1)) #reshapes or flattens the vector to allow being used for plotting 395 | hMMSE_ = np.reshape(Channel_MMSE_all_nnA[eee+aa],(-1)) 396 | hc = np.reshape(ccchannel[eee], (-1)) 397 | MSE1_ = np.mean((hc - hNN_pred_)**2) #to obtain the MSE = (mean(pow(hLS - h), 2)) 398 | MSE2_ = np.mean((hc - hMMSE_)**2) #to ocompute the MSE. Same as above. Considered the LS Coeff without varying noise power 399 | avgMSE_MMSE_all.append(MSE2_) 400 | avgMSE_NN_all.append(MSE1_) 401 | aa = aa+len(ccchannel) 402 | 403 | #Determine how close the predictions are to the real-values with some toleranace 404 | CompareResult_MMSE = np.isclose(Chanel_MMSE_all_N[c_idx], Chanel_pred_MMSE_all[c_idx], rtol=0.2) #toleranace of +-0.2 405 | print (CompareResult_MMSE) 406 | correct_pred_mmse = np.count_nonzero(CompareResult_MMSE) 407 | total_number_mmse = CompareResult_MMSE.size 408 | Accuracy_MMSE = (correct_pred_mmse/total_number_mmse)*100 409 | print ('Prediction Accuracy of', Accuracy_MMSE,'%') 410 | accuracy_MMSE.append(Accuracy_MMSE) 411 | 412 | 413 | #more to visualization of the model: #To obtain the weights and biases at each layer: 414 | #Note: Layers apart from Layer 1 and Layer 3 are the hidden layers. 415 | summary = model.summary() 416 | TrainedWeight1_MMSE = model.layers[0].get_weights()[0] 417 | TrainedBias1_MMSE = model.layers[0].get_weights()[1] 418 | #print("trained weight of layer1 =", TrainedWeight1_MMSE) 419 | #print("trained bias of layer1 =", TrainedBias1_MMSE) 420 | 421 | TrainedWeight2a_MMSE = model.layers[1].get_weights()[0] 422 | TrainedBias2a_MMSE = model.layers[1].get_weights()[1] 423 | #print("trained weight of layer2 =", TrainedWeight2a_MMSE) 424 | #print("trained bias of layer2 =", TrainedBias2a_MMSE) 425 | 426 | TrainedWeight2b_MMSE = model.layers[2].get_weights()[0] 427 | TrainedBias2b_MMSE = model.layers[2].get_weights()[1] 428 | #print("trained weight of layer2 =", TrainedWeight2b_MMSE) 429 | #print("trained bias of layer2 =", TrainedBias2b_MMSE) 430 | 431 | TrainedWeight3_MMSE = model.layers[3].get_weights()[0] 432 | TrainedBias3MMSE = model.layers[3].get_weights()[1] 433 | #print("trained weight of layer2 =", TrainedWeight3_MMSE) 434 | #print("trained bias of layer2 =", TrainedBias3_MMSE) 435 | 436 | #Evaluate the performance of trained model. # Display the individal plot for the MMSE performance 437 | plt.plot(noise_pw[::-1], MSE_MMSE_all) 438 | plt.plot(noise_pw[::-1], MSE_n_all_mmse) 439 | plt.title('Graph of MSE with varying SNR (after training)') 440 | plt.ylabel('Mean Square Error') 441 | plt.xlabel('Signal to Noise Ratio') 442 | plt.legend(['MMSE', 'NN_Pred'], loc='upper left') 443 | plt.grid(b=None, which='major', axis='both') 444 | plt.show() 445 | 446 | #Evaluate the performance of Average MSE from the trained model. 447 | plt.plot(noise_pw_[::-1], avgMSE_MMSE_all) 448 | plt.plot(noise_pw_[::-1], avgMSE_NN_all) 449 | plt.title('Graph of Average MSE with varying SNR (after training)') 450 | plt.ylabel('Avg. Mean Square Error') 451 | plt.xlabel('SNR') 452 | plt.legend(['MMSE', 'NN_Pred'], loc='upper left') 453 | plt.grid(b=None, which='major', axis='both') 454 | plt.show() 455 | 456 | 457 | plt.plot(n_mf_MMSE.history['loss']) 458 | plt.plot(n_mf_MMSE.history['val_loss']) 459 | plt.title('Graph of Training Loss & its Validation Loss - MMSE') 460 | plt.ylabel('Loss') 461 | plt.xlabel('No. of Epoch') 462 | plt.legend(['Training', 'Validation'], loc='upper left') 463 | plt.grid(b=None, which='major', axis='both') 464 | plt.show() 465 | 466 | 467 | #Visualization after training and testing #To see the performance of the 3rd chanel coefficient only 468 | #a good overlap means good performance. 469 | Chanel_MMSE_test = np.reshape(Chanel_MMSE_all_N[-1], (-1)) 470 | Chanel_pred_MMSE = np.reshape(Chanel_pred_MMSE_all[-1], (-1)) 471 | plt.plot(Chanel_MMSE_test, '*-') 472 | plt.plot(Chanel_pred_MMSE, '.-') 473 | plt.plot(np.reshape(Chanel_test, (-1)), ',-') 474 | plt.ylabel('amplitude') 475 | plt.xlabel('chanel coefficient') 476 | plt.title('Plot of Test Chanel Data (MMSE) & its Predicted Chanel') 477 | plt.legend(['MMSEChanel', 'PredictedMMSEChanel', 'TrueChanel'], loc='upper left') 478 | plt.grid(b=None, which='major', axis='both') 479 | plt.show() 480 | 481 | # Here, I effected the idea of evaluating several antena array numbers. 482 | ant_array = [4, 6] #so i chose this values at random. Any number or value can work too. 483 | pilot = [6, 9] # here also, i chose 150% of corresponding antena value. 484 | noOfNodes = [25, 40] #i realized that i get better estimation using varying number of nodes for diff. antena array sizes 485 | 486 | j = 0 487 | if (ant_array[j] < pilot[j]): 488 | print ("parameters are appropriate") #just for control measures. nothng really serious. 489 | else: 490 | print("Error! ant_array must be at least less than pilot!") #the code halts if the vlaues do not conform to what is expected. 491 | sys.exit() 492 | j = j + 1 #I am iterating over all the elements in the lists. 493 | 494 | for i in range (len(pilot)): #length of list - pilot and that of antena array are same. 495 | print ("*** Evaluates LS chanel estimation performance for", ant_array[i], "by", ant_array[i], "antena array ***") # I made in such a way that it print the antena array size under evaluation. 496 | ChanelEstimation(ant_array[i], pilot[i], noOfNodes[i]) # this is the main stuff that calls the function/ 497 | print ("*** Evaluates MMSE chanel estimation performance for", ant_array[i], "by", ant_array[i], "antena array ***") # I made in such a way that it print the antena array size under evaluation. 498 | ChanelEstimation_MMSE(ant_array[i], pilot[i], noOfNodes[i]) # this is the main stuff that calls the function/ 499 | 500 | #the idea behind this visualization is this: (not so important). This helped me to know that a constant node size does not work for all antena array sizes. 501 | #there is a possibility that the more the antena size, the less suitable is a general neural network topology, number of nodes, epoch value, etc could be for the antena array size. 502 | #this is just a way of seeing if the prediction preformance improved or decline per antena array size. 503 | x_axis = np.arange(len(ant_array)) 504 | y_axis = accuracy 505 | plt.bar(x_axis, y_axis, align='center', alpha=0.5) 506 | plt.xticks(x_axis, ant_array) 507 | plt.ylabel('Percentage prediction') 508 | plt.xlabel('Antena Array size') 509 | plt.title('Visualization of LS prediction accuracy for different antena array sizes') 510 | plt.show() 511 | 512 | #this is just a way of seeing if the prediction preformance improved or decline per antena array size. 513 | x_axis_mmse = np.arange(len(ant_array)) 514 | y_axis_mmse = accuracy_MMSE 515 | plt.bar(x_axis_mmse, y_axis_mmse, align='center', alpha=0.5) 516 | plt.xticks(x_axis_mmse, ant_array) 517 | plt.ylabel('Percentage prediction') 518 | plt.xlabel('Antena Array size') 519 | plt.title('Visualization of MMSE prediction accuracy for different antena array sizes') 520 | plt.show() --------------------------------------------------------------------------------