End-to-end Learning with Autoencoders

In this notebook, you will learn how to implement an end-to-end communication system as an autoencoder [1]. The implemented system is shown in the figure below. An additive white Gaussian noise (AWGN) channel is considered. On the transmitter side, joint training of the constellation geometry and bit-labeling is performed, as in [2]. On the receiver side, a neural network-based demapper that computes log-likelihood ratios (LLRs) on the transmitted bits from the received samples is optimized. Theconsidered autoencoder is benchmarked against a quadrature amplitude modulation (QAM) with Gray labeling and the optimal AWGN demapper.

System Model

Two algorithms for training the autoencoder are implemented in this notebook:

  • Conventional stochastic gradient descent (SGD) with backpropagation, which assumes a differentiable channel model and therefore optimizes the end-to-end system by backpropagating the gradients through the channel (see, e.g., [1]).

  • The training algorithm from [3], which does not assume a differentiable channel model, and which trains the end-to-end system by alternating between conventional training of the receiver and reinforcement learning (RL)-based training of the transmitter. Compared to [3], an additional step of fine-tuning of the receiver is performed after alternating training.

Note: For an introduction to the implementation of differentiable communication systems and their optimization through SGD and backpropagation with Sionna, please refer tothe Part 2 of the Sionna tutorial for Beginners.

GPU Configuration and Imports

[1]:
importosifos.getenv("CUDA_VISIBLE_DEVICES")isNone:gpu_num=0# Use "" to use the CPUos.environ["CUDA_VISIBLE_DEVICES"]=f"{gpu_num}"os.environ['TF_CPP_MIN_LOG_LEVEL']='3'# Import Sionnatry:importsionna.phyexceptImportErrorase:importsysif'google.colab'insys.modules:# Install Sionna in Google Colabprint("Installing Sionna and restarting the runtime. Please run the cell again.")os.system("pip install sionna")os.kill(os.getpid(),5)else:raisee# Configure the notebook to use only a single GPU and allocate only as much memory as needed# For more details, see https://www.tensorflow.org/guide/gpuimporttensorflowastfgpus=tf.config.list_physical_devices('GPU')ifgpus:try:tf.config.experimental.set_memory_growth(gpus[0],True)exceptRuntimeErrorase:print(e)# Avoid warnings from TensorFlowtf.get_logger().setLevel('ERROR')fromtensorflow.kerasimportModelfromtensorflow.keras.layersimportLayer,Densefromsionna.phyimportBlockfromsionna.phy.channelimportAWGNfromsionna.phy.utilsimportebnodb2no,log10,expand_to_rankfromsionna.phy.fec.ldpcimportLDPC5GEncoder,LDPC5GDecoderfromsionna.phy.mappingimportMapper,Demapper,Constellation,BinarySourcefromsionna.phy.utilsimportsim_bersionna.phy.config.seed=42# Set seed for reproducible random number generation%matplotlib inlineimportmatplotlib.pyplotaspltimportnumpyasnpimportpickle

Simulation Parameters

[2]:
################################################ SNR range for evaluation and training [dB]###############################################ebno_db_min=4.0ebno_db_max=8.0################################################ Modulation and coding configuration###############################################num_bits_per_symbol=6# Baseline is 64-QAMmodulation_order=2**num_bits_per_symbolcoderate=0.5# Coderate for the outer coden=1500# Codeword length [bit]. Must be a multiple of num_bits_per_symbolnum_symbols_per_codeword=n//num_bits_per_symbol# Number of modulated baseband symbols per codewordk=int(n*coderate)# Number of information bits per codeword################################################ Training configuration###############################################num_training_iterations_conventional=10000# Number of training iterations for conventional training# Number of training iterations with RL-based training for the alternating training phase and fine-tuning of the receiver phasenum_training_iterations_rl_alt=7000num_training_iterations_rl_finetuning=3000training_batch_size=tf.constant(128,tf.int32)# Training batch sizerl_perturbation_var=0.01# Variance of the perturbation used for RL-based training of the transmittermodel_weights_path_conventional_training="awgn_autoencoder_weights_conventional_training"# Filename to save the autoencoder weights once conventional training is donemodel_weights_path_rl_training="awgn_autoencoder_weights_rl_training"# Filename to save the autoencoder weights once RL-based training is done################################################ Evaluation configuration###############################################results_filename="awgn_autoencoder_results"# Location to save the results

Neural Demapper

The neural network-based demapper shown in the figure above is made of three dense layers with ReLU activation.

The input of the demapper consists of a received sample\(y \in \mathbb{C}\) and the noise power spectral density\(N_0\) in log-10 scale to handle different orders of magnitude for the SNR.

As the neural network can only process real-valued inputs, these values are fed as a 3-dimensional vector

\[\left[ \mathcal{R}(y), \mathcal{I}(y), \log_{10}(N_0) \right]\]

where\(\mathcal{R}(y)\) and\(\mathcal{I}(y)\) refer to the real and imaginary component of\(y\), respectively.

The output of the neural network-based demapper consists of LLRs on thenum_bits_per_symbol bits mapped to a constellation point. Therefore, the last layer consists ofnum_bits_per_symbol units.

Note: The neural network-based demapper processes the received samples\(y\) forming a block individually. Theneural receiver notebook provides an example of a more advanced neural network-based receiver that jointly processes a resource grid of received symbols.

[3]:
classNeuralDemapper(Layer):def__init__(self):super().__init__()self._dense_1=Dense(128,'relu')self._dense_2=Dense(128,'relu')self._dense_3=Dense(num_bits_per_symbol,None)# The feature correspond to the LLRs for every bits carried by a symboldefcall(self,y,no):# Using log10 scale helps with the performanceno_db=log10(no)# Stacking the real and imaginary components of the complex received samples# and the noise varianceno_db=tf.tile(no_db,[1,num_symbols_per_codeword])# [batch size, num_symbols_per_codeword]z=tf.stack([tf.math.real(y),tf.math.imag(y),no_db],axis=2)# [batch size, num_symbols_per_codeword, 3]llr=self._dense_1(z)llr=self._dense_2(llr)llr=self._dense_3(llr)# [batch size, num_symbols_per_codeword, num_bits_per_symbol]returnllr

Trainable End-to-end System: Conventional Training

The following cell defines an end-to-end communication system that transmits bits modulated using a trainable constellation over an AWGN channel.

The receiver uses the previously defined neural network-based demapper to compute LLRs on the transmitted (coded) bits.

As in [1], the constellation and neural network-based demapper are jointly trained through SGD and backpropagation using the binary cross entropy (BCE) as loss function.

Training on the BCE is known to be equivalent to maximizing an achievable information rate [2].

The following model can be instantiated either for training (training=True) or evaluation (training=False).

In the former case, the BCE is returned and no outer code is used to reduce computational complexity and as it does not impact the training of the constellation or demapper.

When settingtraining toFalse, an LDPC outer code from 5G NR is applied.

[4]:
classE2ESystemConventionalTraining(Model):def__init__(self,training):super().__init__()self._training=training################## Transmitter################self._binary_source=BinarySource()# To reduce the computational complexity of training, the outer code is not used when training,# as it is not requiredifnotself._training:# num_bits_per_symbol is required for the interleaverself._encoder=LDPC5GEncoder(k,n,num_bits_per_symbol)# Trainable constellation# We initialize a custom constellation with qam pointsqam_points=Constellation("qam",num_bits_per_symbol).pointsself.constellation=Constellation("custom",num_bits_per_symbol,points=qam_points,normalize=True,center=True)# To make the constellation trainable, we need to create seperate# variables for the real and imaginary partsself.points_r=self.add_weight(shape=qam_points.shape,initializer="zeros")self.points_i=self.add_weight(shape=qam_points.shape,initializer="zeros")self.points_r.assign(tf.math.real(qam_points))self.points_i.assign(tf.math.imag(qam_points))self._mapper=Mapper(constellation=self.constellation)################## Channel################self._channel=AWGN()################## Receiver################# We use the previously defined neural network for demappingself._demapper=NeuralDemapper()# To reduce the computational complexity of training, the outer code is not used when training,# as it is not requiredifnotself._training:self._decoder=LDPC5GDecoder(self._encoder,hard_out=True)################## Loss function#################ifself._training:self._bce=tf.keras.losses.BinaryCrossentropy(from_logits=True)defcall(self,batch_size,ebno_db):# Set the constellation points equal to a complex tensor constructed# from two real-valued variablespoints=tf.complex(self.points_r,self.points_i)self.constellation.points=points# If `ebno_db` is a scalar, a tensor with shape [batch size] is created as it is what is expected by some layersiflen(ebno_db.shape)==0:ebno_db=tf.fill([batch_size],ebno_db)no=ebnodb2no(ebno_db,num_bits_per_symbol,coderate)no=expand_to_rank(no,2)################## Transmitter################# Outer coding is only performed if not trainingifself._training:c=self._binary_source([batch_size,n])else:b=self._binary_source([batch_size,k])c=self._encoder(b)# Modulationx=self._mapper(c)# x [batch size, num_symbols_per_codeword]################## Channel################y=self._channel(x,no)# [batch size, num_symbols_per_codeword]################## Receiver################llr=self._demapper(y,no)llr=tf.reshape(llr,[batch_size,n])# If training, outer decoding is not performed and the BCE is returnedifself._training:loss=self._bce(c,llr)returnlosselse:# Outer decodingb_hat=self._decoder(llr)returnb,b_hat# Ground truth and reconstructed information bits returned for BER/BLER computation

A simple training loop is defined in the next cell, which performsnum_training_iterations_conventional training iterations of SGD. Training is done over a range of SNR, by randomly sampling a batch of SNR values at each iteration.

Note: For an introduction to the implementation of differentiable communication systems and their optimization through SGD and backpropagation with Sionna, please refer tothe Part 2 of the Sionna tutorial for Beginners.

[5]:
defconventional_training(model):# Optimizer used to apply gradientsoptimizer=tf.keras.optimizers.Adam()@tf.function(jit_compile=True)deftrain_step():# Sampling a batch of SNRsebno_db=tf.random.uniform(shape=[training_batch_size],minval=ebno_db_min,maxval=ebno_db_max)# Forward passwithtf.GradientTape()astape:loss=model(training_batch_size,ebno_db)# Computing and applying gradientsweights=model.trainable_variablesgrads=tape.gradient(loss,weights)optimizer.apply_gradients(zip(grads,weights))returnlossforiinrange(num_training_iterations_conventional):loss=train_step()# Printing periodically the progressifi%100==0:print('Iteration{}/{}  BCE:{:.4f}'.format(i,num_training_iterations_conventional,loss.numpy()),end='\r')

The next cell defines a utility function for saving the weights usingpickle.

[6]:
defsave_weights(model,model_weights_path):weights=model.get_weights()withopen(model_weights_path,'wb')asf:pickle.dump(weights,f)

In the next cell, an instance of the model defined previously is instantiated and trained.

[7]:
# Instantiate and train the end-to-end systemmodel=E2ESystemConventionalTraining(training=True)conventional_training(model)# Save weightssave_weights(model,model_weights_path_conventional_training)
Iteration 9900/10000  BCE: 0.2894

Trainable End-to-end System: RL-based Training

The following cell defines the same end-to-end system as before, but stop the gradients after the channel to simulate a non-differentiable channel.

To jointly train the transmitter and receiver over a non-differentiable channel, we follow [3], which key idea is to alternate between:

  • Training of the receiver on the BCE using conventional backpropagation and SGD.

  • Training of the transmitter by applying (known) perturbations to the transmitter output to enable estimation of the gradient of the transmitter weights with respect to an approximation of the loss function.

Whentraining is set toTrue, both losses for training the receiver and the transmitter are returned.

[8]:
classE2ESystemRLTraining(Model):def__init__(self,training):super().__init__()self._training=training################## Transmitter################self._binary_source=BinarySource()# To reduce the computational complexity of training, the outer code is not used when training,# as it is not requiredifnotself._training:self._encoder=LDPC5GEncoder(k,n,num_bits_per_symbol)# Trainable constellation# We initialize a custom constellation with qam pointsqam_points=Constellation("qam",num_bits_per_symbol).pointsself.constellation=Constellation("custom",num_bits_per_symbol,points=qam_points,normalize=True,center=True)# To make the constellation trainable, we need to create seperate# variables for the real and imaginary partsself.points_r=self.add_weight(shape=qam_points.shape,initializer="zeros")self.points_i=self.add_weight(shape=qam_points.shape,initializer="zeros")self.points_r.assign(tf.math.real(qam_points))self.points_i.assign(tf.math.imag(qam_points))self._mapper=Mapper(constellation=self.constellation)################## Channel################self._channel=AWGN()################## Receiver################# We use the previously defined neural network for demappingself._demapper=NeuralDemapper()# To reduce the computational complexity of training, the outer code is not used when training,# as it is not requiredifnotself._training:self._decoder=LDPC5GDecoder(self._encoder,hard_out=True)defcall(self,batch_size,ebno_db,perturbation_variance=tf.constant(0.0,tf.float32)):# If `ebno_db` is a scalar, a tensor with shape [batch size] is created as it is what is expected by some layersiflen(ebno_db.shape)==0:ebno_db=tf.fill([batch_size],ebno_db)no=ebnodb2no(ebno_db,num_bits_per_symbol,coderate)no=expand_to_rank(no,2)################## Transmitter################# Outer coding is only performed if not trainingifself._training:c=self._binary_source([batch_size,n])else:b=self._binary_source([batch_size,k])c=self._encoder(b)# Modulation# Set the constellation points equal to a complex tensor constructed# from two real-valued variablespoints=tf.complex(self.points_r,self.points_i)self.constellation.points=pointsx=self._mapper(c)# x [batch size, num_symbols_per_codeword]# Adding perturbation# If ``perturbation_variance`` is 0, then the added perturbation is nullepsilon_r=tf.random.normal(tf.shape(x))*tf.sqrt(0.5*perturbation_variance)epsilon_i=tf.random.normal(tf.shape(x))*tf.sqrt(0.5*perturbation_variance)epsilon=tf.complex(epsilon_r,epsilon_i)# [batch size, num_symbols_per_codeword]x_p=x+epsilon# [batch size, num_symbols_per_codeword]################## Channel################y=self._channel(x_p,no)# [batch size, num_symbols_per_codeword]y=tf.stop_gradient(y)# Stop gradient here################## Receiver################llr=self._demapper(y,no)# If training, outer decoding is not performedifself._training:# Average BCE for each baseband symbol and each batch examplec=tf.reshape(c,[-1,num_symbols_per_codeword,num_bits_per_symbol])bce=tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(c,llr),axis=2)# Avergare over the bits mapped to a same baseband symbol# The RX loss is the usual average BCErx_loss=tf.reduce_mean(bce)# From the TX side, the BCE is seen as a feedback from the RX through which backpropagation is not possiblebce=tf.stop_gradient(bce)# [batch size, num_symbols_per_codeword]x_p=tf.stop_gradient(x_p)p=x_p-x# [batch size, num_symbols_per_codeword] Gradient is backpropagated through `x`tx_loss=tf.square(tf.math.real(p))+tf.square(tf.math.imag(p))# [batch size, num_symbols_per_codeword]tx_loss=-bce*tx_loss/rl_perturbation_var# [batch size, num_symbols_per_codeword]tx_loss=tf.reduce_mean(tx_loss)returntx_loss,rx_losselse:llr=tf.reshape(llr,[-1,n])# Reshape as expected by the outer decoderb_hat=self._decoder(llr)returnb,b_hat

The next cell implements the training algorithm from [3], which alternates between conventional training of the neural network-based receiver, and RL-based training of the transmitter.

[9]:
defrl_based_training(model):# Optimizers used to apply gradientsoptimizer_tx=tf.keras.optimizers.Adam()# For training the transmitteroptimizer_rx=tf.keras.optimizers.Adam()# For training the receiver# Function that implements one transmitter training iteration using RL.@tf.function(jit_compile=True)deftrain_tx():# Sampling a batch of SNRsebno_db=tf.random.uniform(shape=[training_batch_size],minval=ebno_db_min,maxval=ebno_db_max)# Forward passwithtf.GradientTape()astape:# Keep only the TX losstx_loss,_=model(training_batch_size,ebno_db,tf.constant(rl_perturbation_var,tf.float32))# Perturbation are added to enable RL exploration## Computing and applying gradientsweights=tape.watched_variables()grads=tape.gradient(tx_loss,weights,unconnected_gradients=tf.UnconnectedGradients.ZERO)optimizer_tx.apply_gradients(zip(grads,weights))# Function that implements one receiver training iteration@tf.function(jit_compile=True)deftrain_rx():# Sampling a batch of SNRsebno_db=tf.random.uniform(shape=[training_batch_size],minval=ebno_db_min,maxval=ebno_db_max)# Forward passwithtf.GradientTape()astape:# Keep only the RX loss_,rx_loss=model(training_batch_size,ebno_db)# No perturbation is added## Computing and applying gradientsweights=tape.watched_variables()grads=tape.gradient(rx_loss,weights,unconnected_gradients=tf.UnconnectedGradients.ZERO)optimizer_rx.apply_gradients(zip(grads,weights))# The RX loss is returned to print the progressreturnrx_loss# Training loop.foriinrange(num_training_iterations_rl_alt):# 10 steps of receiver training are performed to keep it ahead of the transmitter# as it is used for computing the losses when training the transmitterfor_inrange(10):rx_loss=train_rx()# One step of transmitter trainingtrain_tx()# Printing periodically the progressifi%100==0:print('Iteration{}/{}  BCE{:.4f}'.format(i,num_training_iterations_rl_alt,rx_loss.numpy()),end='\r')print()# Line break# Once alternating training is done, the receiver is fine-tuned.print('Receiver fine-tuning... ')foriinrange(num_training_iterations_rl_finetuning):rx_loss=train_rx()ifi%100==0:print('Iteration{}/{}  BCE{:.4f}'.format(i,num_training_iterations_rl_finetuning,rx_loss.numpy()),end='\r')

In the next cell, an instance of the model defined previously is instantiated and trained.

[10]:
# Instantiate and train the end-to-end systemmodel=E2ESystemRLTraining(training=True)rl_based_training(model)# Save weightssave_weights(model,model_weights_path_rl_training)
Iteration 6900/7000  BCE 0.2767Receiver fine-tuning...Iteration 2900/3000  BCE 0.2826

Evaluation

The following cell implements a baseline which uses QAM with Gray labeling and conventional demapping for AWGN channel.

[11]:
classBaseline(Model):def__init__(self):super().__init__()################## Transmitter################self._binary_source=BinarySource()self._encoder=LDPC5GEncoder(k,n,num_bits_per_symbol)constellation=Constellation("qam",num_bits_per_symbol,trainable=False)self.constellation=constellationself._mapper=Mapper(constellation=constellation)################## Channel################self._channel=AWGN()################## Receiver################self._demapper=Demapper("app",constellation=constellation)self._decoder=LDPC5GDecoder(self._encoder,hard_out=True)defcall(self,batch_size,ebno_db):# If `ebno_db` is a scalar, a tensor with shape [batch size] is created as it is what is expected by some layersiflen(ebno_db.shape)==0:ebno_db=tf.fill([batch_size],ebno_db)no=ebnodb2no(ebno_db,num_bits_per_symbol,coderate)no=expand_to_rank(no,2)################## Transmitter################b=self._binary_source([batch_size,k])c=self._encoder(b)# Modulationx=self._mapper(c)# x [batch size, num_symbols_per_codeword]################## Channel################y=self._channel(x,no)# [batch size, num_symbols_per_codeword]################## Receiver################llr=self._demapper(y,no)# Outer decodingb_hat=self._decoder(llr)returnb,b_hat# Ground truth and reconstructed information bits returned for BER/BLER computation
[12]:
# Range of SNRs over which the systems are evaluatedebno_dbs=np.arange(ebno_db_min,# Min SNR for evaluationebno_db_max,# Max SNR for evaluation0.5)# Step
[13]:
# Utility function to load and set weights of a modeldefload_weights(model,model_weights_path):model(tf.cast(1,tf.int32),tf.constant(10.0,tf.float32))withopen(model_weights_path,'rb')asf:weights=pickle.load(f)model.set_weights(weights)points=tf.complex(model.points_r,model.points_i)model.constellation.points=points

The next cell evaluate the baseline and the two autoencoder-based communication systems, trained with different method. The results are stored in the dictionaryBLER.

[14]:
# Dictionary storing the resultsBLER={}model_baseline=Baseline()_,bler=sim_ber(model_baseline,ebno_dbs,batch_size=128,num_target_block_errors=1000,max_mc_iter=1000,graph_mode="xla")BLER['baseline']=bler.numpy()model_conventional=E2ESystemConventionalTraining(training=False)load_weights(model_conventional,model_weights_path_conventional_training)_,bler=sim_ber(model_conventional,ebno_dbs,batch_size=128,num_target_block_errors=1000,max_mc_iter=1000,graph_mode="xla")BLER['autoencoder-conv']=bler.numpy()model_rl=E2ESystemRLTraining(training=False)load_weights(model_rl,model_weights_path_rl_training)_,bler=sim_ber(model_rl,ebno_dbs,batch_size=128,num_target_block_errors=1000,max_mc_iter=1000,graph_mode="xla")BLER['autoencoder-rl']=bler.numpy()withopen(results_filename,'wb')asf:pickle.dump((ebno_dbs,BLER),f)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status---------------------------------------------------------------------------------------------------------------------------------------      4.0 | 1.2224e-01 | 1.0000e+00 |       93879 |      768000 |         1024 |        1024 |         5.3 |reached target block errors      4.5 | 9.6033e-02 | 9.9316e-01 |       73753 |      768000 |         1017 |        1024 |         0.1 |reached target block errors      5.0 | 5.8314e-02 | 9.2361e-01 |       50383 |      864000 |         1064 |        1152 |         0.1 |reached target block errors      5.5 | 1.9398e-02 | 5.1807e-01 |       29795 |     1536000 |         1061 |        2048 |         0.2 |reached target block errors      6.0 | 2.4054e-03 | 1.0841e-01 |       16857 |     7008000 |         1013 |        9344 |         1.1 |reached target block errors      6.5 | 1.2466e-04 | 9.1912e-03 |       10172 |    81600000 |         1000 |      108800 |        12.8 |reached target block errors      7.0 | 5.7812e-06 | 5.8594e-04 |         555 |    96000000 |           75 |      128000 |        15.0 |reached max iterations      7.5 | 2.5000e-07 | 4.6875e-05 |          24 |    96000000 |            6 |      128000 |        15.1 |reached max iterationsEbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status---------------------------------------------------------------------------------------------------------------------------------------      4.0 | 1.0590e-01 | 9.9902e-01 |       81333 |      768000 |         1023 |        1024 |         5.3 |reached target block errors      4.5 | 6.2444e-02 | 8.9931e-01 |       53952 |      864000 |         1036 |        1152 |         0.1 |reached target block errors      5.0 | 2.3795e-02 | 5.7310e-01 |       31981 |     1344000 |         1027 |        1792 |         0.2 |reached target block errors      5.5 | 3.7868e-03 | 1.4612e-01 |       19631 |     5184000 |         1010 |        6912 |         0.8 |reached target block errors      6.0 | 2.5252e-04 | 1.5719e-02 |       12048 |    47712000 |         1000 |       63616 |         7.4 |reached target block errors      6.5 | 1.1938e-05 | 1.0547e-03 |        1146 |    96000000 |          135 |      128000 |        15.0 |reached max iterations      7.0 | 2.6875e-06 | 1.7969e-04 |         258 |    96000000 |           23 |      128000 |        15.1 |reached max iterations      7.5 | 0.0000e+00 | 0.0000e+00 |           0 |    96000000 |            0 |      128000 |        14.9 |reached max iterationsSimulation stopped as no error occurred @ EbNo = 7.5 dB.EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status---------------------------------------------------------------------------------------------------------------------------------------      4.0 | 1.0395e-01 | 9.9316e-01 |       79833 |      768000 |         1017 |        1024 |         5.8 |reached target block errors      4.5 | 6.2806e-02 | 9.0972e-01 |       54264 |      864000 |         1048 |        1152 |         0.1 |reached target block errors      5.0 | 2.2637e-02 | 5.3542e-01 |       32597 |     1440000 |         1028 |        1920 |         0.2 |reached target block errors      5.5 | 3.5886e-03 | 1.3925e-01 |       19637 |     5472000 |         1016 |        7296 |         0.9 |reached target block errors      6.0 | 2.4070e-04 | 1.5289e-02 |       11831 |    49152000 |         1002 |       65536 |         7.7 |reached target block errors      6.5 | 1.0688e-05 | 9.8438e-04 |        1026 |    96000000 |          126 |      128000 |        15.1 |reached max iterations      7.0 | 1.3333e-06 | 1.1719e-04 |         128 |    96000000 |           15 |      128000 |        15.1 |reached max iterations      7.5 | 7.2917e-08 | 1.5625e-05 |           7 |    96000000 |            2 |      128000 |        15.1 |reached max iterations
[15]:
plt.figure(figsize=(10,8))# Baseline - Perfect CSIplt.semilogy(ebno_dbs,BLER['baseline'],'o-',c=f'C0',label=f'Baseline')# Autoencoder - conventional trainingplt.semilogy(ebno_dbs,BLER['autoencoder-conv'],'x-.',c=f'C1',label=f'Autoencoder - conventional training')# Autoencoder - RL-based trainingplt.semilogy(ebno_dbs,BLER['autoencoder-rl'],'o-.',c=f'C2',label=f'Autoencoder - RL-based training')plt.xlabel(r"$E_b/N_0$ (dB)")plt.ylabel("BLER")plt.grid(which="both")plt.ylim((1e-4,1.0))plt.legend()plt.tight_layout()
../../_images/phy_tutorials_Autoencoder_36_0.png

Visualizing the Learned Constellations

[16]:
model_conventional=E2ESystemConventionalTraining(training=True)load_weights(model_conventional,model_weights_path_conventional_training)fig=model_conventional.constellation.show()fig.suptitle('Conventional training');
../../_images/phy_tutorials_Autoencoder_38_0.png
[17]:
model_rl=E2ESystemRLTraining(training=False)load_weights(model_rl,model_weights_path_rl_training)fig=model_rl.constellation.show()fig.suptitle('RL-based training');
../../_images/phy_tutorials_Autoencoder_39_0.png
[18]:
%rm awgn_autoencoder_weights_conventional_training awgn_autoencoder_weights_rl_training awgn_autoencoder_results

References

[1] T. O’Shea and J. Hoydis, “An Introduction to Deep Learning for the Physical Layer,” in IEEE Transactions on Cognitive Communications and Networking, vol. 3, no. 4, pp. 563-575, Dec. 2017, doi: 10.1109/TCCN.2017.2758370.

[2] S. Cammerer, F. Ait Aoudia, S. Dörner, M. Stark, J. Hoydis and S. ten Brink, “Trainable Communication Systems: Concepts and Prototype,” in IEEE Transactions on Communications, vol. 68, no. 9, pp. 5489-5503, Sept. 2020, doi: 10.1109/TCOMM.2020.3002915.

[3] F. Ait Aoudia and J. Hoydis, “Model-Free Training of End-to-End Communication Systems,” in IEEE Journal on Selected Areas in Communications, vol. 37, no. 11, pp. 2503-2516, Nov. 2019, doi: 10.1109/JSAC.2019.2933891.