Verify Performance of 6G AI-Native Receiver Using MATLAB and PyTorch Coexecution
This example shows how to verify and test a PyTorch™ AI-native receiver and compare it with a conventional 5G receiver using both multi-process central processing unit (CPU) and graphics processing unit (GPU) hardware acceleration.
In this example, you generate a stream of data in MATLAB and perform inference using the PyTorch network in an online inference workflow. With this process, you can use deep learning models for training and inference without saving the training data.
The basic steps to validate the PyTorch network are as follows:
Set up the Python environment and verify that the required packages are installed.
Specify the test parameters.
Load the PyTorch network.
Validate the network using online data generation and testing.
Visualize and compare results with a conventional 5G receiver, which serves as a benchmark.
System Description
This example provides a testbench for AI-native convolutional receivers, featuring online data generation, inference, and hardware acceleration capabilities. The AI-native fully convolutional receiver system replaces 5G receiver operations with convolutional neural networks (CNNs) to enhance system performance under severe channel impairments and sparse pilot configurations. Traditional 5G receivers struggle to perform under such conditions. For more information about the AI-native receivers and implementation details, see the AI-Native Fully Convolutional Receiver example.
This figure shows the physical uplink shared channel (PUSCH) simulation that measures coded bit-error-ratio (BER) and throughput metrics using both the AI-native convolutional receiver and the conventional 5G receiver as a benchmark. The deeprx.py
script loads the model and performs log-likelihood-ratio (LLR) predictions to replace the channel estimation, channel equalization, and symbol demodulation functionalities of the conventional 5G receiver. The deeprx_model.py
script manages the model creation and parameter loading. The deeprx_30k.pth
file contains the model state dictionary, including model parameters (weights and biases) and optimizer states.
Step 1: Set Up Python Environment
Configure the Python environment necessary for running your trained PyTorch network. Before running this example, set up the Python environment as explained in the PyTorch Coexecution topic. Specify the full or relative path of the Python executable below. This path can also point to a virtual environment. If you are running the example on Windows®, specify the path to the pythonw.exe
file. The example runs using the OutOfProcess
execution mode of the Python interpreter. If you use the InProcess
execution mode instead, MATLAB and PyTorch might have library conflicts. To learn more about InProcess
vs. OutOfProcess
, see pyenv
.
% Configure the Python environment simParameters.PythonExecutionMode ="OutOfProcess"; % Paths for the Python executable: % ..\venv\Scripts\pythonw.exe for Windows and ../venv/bin/python3 for Linux simParameters.PythonPath = "relative_path_to_python_executable"; % Path to the REQUIREMENTS.txt file simParameters.PythonRequirements = "requirements_6GVerify.txt";
The helperSetupPyenv
function sets the Python environment in MATLAB according to the options you select and verifies that the libraries listed in the requirements_6GVerify.txt
file are installed.
currentPyEnv = helperSetupPyenv(simParameters.PythonPath, ... simParameters.PythonExecutionMode, ... simParameters.PythonRequirements);
Setting up Python environment Parsing requirements_6GVerify.txt Checking required package 'torch' Checking required package 'numpy' Required Python libraries are installed.
Step 2: Specify Test Parameters
To test the performance of your AI-native receiver network, define the carrier, uplink shared channel (UL-SCH), PUSCH, and PUSCH demodulation reference signal (DM-RS) configurations along with the channel parameters. You can also select the propagation channels in which you want to validate your model. The example supports both clustered delay line (CDL) and tapped delay line (TDL) propagation channels.
The example calculates and compares your trained network's performance with conventional 5G receiver. The signal-to-noise ratio (SNR) parameter represents the SNR per resource element (RE) and per antenna element. For further explanation of the SNR definition, see the SNR Definition Used in Link Simulations example. You can reduce the total execution time by executing each SNR point on each worker. To enable CPU-based hardware acceleration for the end-to-end uplink (UL) PUSCH simulation in test data generation, set simParameters.UseParallel
to true
. This process requires Parallel Computing Toolbox™.
% End-to-end simulation parameters for testing the AI-native and % conventional 5G receivers simParameters.UseParallel =false; % If true, enable parallel processing; otherwise, execute serially simParameters.ChannelModel =
"TDL-E"; % Channel models for testing: "CDL-A", "CDL-E", "TDL-A", "TDL-E" simParameters.DelaySpread =
100e-9; % Delay spread in seconds, range: [10e-9, 300e-9] simParameters.MaximumDopplerShift =
250; % Maximum Doppler shift in Hz, range: [0, 500] simParameters.SNRInVec =
0:10; % SNR range in dB for evaluation simParameters.ModulationType =
"QPSK"; % Modulation type: "QPSK" or "16QAM" simParameters.DMRSAdditionalPosition =
0; % DMRS additional position: 0 or 1 simParameters.DMRSConfigurationType =
2; % DMRS configuration type: 1 or 2 simParameters.NFrames =
5; % Number of frames for the simulation simParameters.PerfectChannelEstimator =
false; % Use perfect channel estimation if true % Additional UL PUSCH simulation parameters comes from [1] simParameters.CarrierFrequency = 3.5e9; % Carrier frequency in Hz simParameters.NSizeGrid = 26; % Number of resource blocks (26 RBs at 15 kHz SCS) simParameters.SubcarrierSpacing = 15; % Subcarrier spacing in kHz simParameters.CodeRate = 658/1024; % Code rate used to calculate transport block size simParameters = hGetAdditionalSystemParameters(simParameters, DisplayParameterSummary=false);
Step 3: Load Your PyTorch Network
In this section, you load and inspect your trained AI-native receiver model in the MATLAB workspace. Follow these steps to load the PyTorch network into the example workspace and inspect its properties:
Locate the model file using the
simParameters.ModelPath
parameter.Create a PyTorch model instance using the
hCreateTorchDeepRx
function and load the trained network parameters.Display the model details.
Locate the model parameters file in .pth
format in the example workspace for validation. By default, this example keeps the model's parameter dictionary file in a .zip
file, which the hCreateTorchDeepRx
function unzips.
simParameters.ModelPath = "deeprx_30k.pth";
simParameters.ModelInput = simParameters.InputSize;
simParameters.ModelOutput = [simParameters.ModelInput(1:2) 4];
Create a PyTorch DeepRx network instance in MATLAB using the hCreateTorchDeepRx
function, specifying the input and output sizes of and , respectively. The parameters , , and represent the total number of subcarriers, the total number of OFDM symbols in the input resource grid, and the number of receive antennas. The input resource grid consists of a combination of the received data grid (rxGrid
), pilot symbols grid (dmrsGrid
), and raw estimation of the channels (rawChanEstGrid
). Similarly, the parameter is the number of bits per transmitted symbol. The model inputs and outputs are fixed at and , respectively, because the example uses the same training parameters presented in the AI-Native Fully Convolutional Receiver example. For further details of the AI-native model architecture, see the Details of AI-Native Fully Convolutional Receiver Network. Once you load the network, you can test it for 16-QAM or QPSK modulations, since the bit masking supports constellation sizes less than or equal to that of the training modulation type [1].
If
Evaluate
istrue
, thehCreateTorchDeepRx
function instantiates the model in evaluation mode usingdeeprx_30k.pth
and loads the trained model and optimizer parameters. This change affects the behavior of the batch normalization layers in the DeepRx architecture.If
Evaluate
isfalse
, the model instantiates in training mode, and the model and optimizer parameters are loaded.You can also instantiate the untrained model without loading the trained model parameters by setting
simParameters.ModelPath
tostring.empty
or[]
.
model = hCreateTorchDeepRx(simParameters, Evaluate=true);
Display the network summary to show the model building blocks and the number of trainable parameters.
modelParameters = py.deeprx.info(model); displayModelSummary(modelParameters);
---------------------------------------- Neural Network Summary ---------------------------------------- - Total number of learnables : 1.2325 million - Number of layers : 13 - Model building blocks : 1) conv_in 2) resnet_block_1 3) resnet_block_2 4) resnet_block_3 5) resnet_block_4 6) resnet_block_5 7) resnet_block_6 8) resnet_block_7 9) resnet_block_8 10) resnet_block_9 11) resnet_block_10 12) resnet_block_11 13) conv_out
Step 4: Test and Compare the AI-Native Receiver Against the Benchmark
In this section, you generate a batch of test data, make predictions using the PyTorch network and conventional 5G receiver, and calculate and compare the coded BER and throughput values. The evaluateLinkPerformance
function evaluates the performance of the AI-native and conventional 5G receivers based on the simulation parameters in Step 2.
If
simParameters.UseParallel
istrue
, the example createsparfeval
future objects and schedules thehGetFeaturesAndLabels
function to execute on each worker using theparfeval
call. Each worker processes an SNR point (simParameters.SNRIn
) asynchronously, which may cause the retrieved coded BER and throughput results to be out of order. To address this, the example organizes the results before plotting.
Otherwise, the simulation generates a batch of test data for a given SNR point (
simParameters.SNRIn
) and calculates coded BER and throughput metrics for both the AI-native receiver and conventional 5G receivers consecutively in a single CPU process.
The example uses native CUDA-based GPU acceleration in PyTorch (if available) by sending the model to a GPU in Step 3. Similarly, the predict
function in the deeprx
module sends the test data tensors to a for GPU-accelerated predictions.
To simulate the end-to-end UL PUSCH link call the hCreateTorchDeepRx
function inside the hGetFeaturesAndLabels
function. The following code snippets show how the AI-native and 5G conventional receivers determine the LLR values inside the hGetFeaturesAndLabels
function with the default example parameterization.
This code snippet represents the LLR prediction and
ulschLLRs
calculation when you pass the AI-native receiver model to thehGetFeaturesAndLabels
function.
% Predict LLRs using the input resource grids % % (i) If the model is a PyTorch network, use the trained model (opts.Net) and test resource grids (X) to predict the LLRs (L). % This option uses a GPU for predictions implicitly via the predict call inside the deeprx.py module if a GPU is available. % % (ii) If the model is a dlnetwork object, use the DeepRx network and dlarray resource grids to predict the LLRs. % This option uses a GPU for predictions if a GPU is available. if opts.UsePyTorchNeuralReceiver L = single(py.deeprx.predict(opts.Net, X)); else % dlnetwork if canUseGPU L = predict(opts.Net, gpuArray(dlarray(X, 'SSCB'))); else L = predict(opts.Net, dlarray(X, 'SSCB')); end end % Extract the utilized LLR values. % Note that the LLR negation results from the difference between the LLR definition in `ldpcDecode` and the BCE loss functions. Qm = puschIndicesInfo.G / puschIndicesInfo.Gd / pusch.NumLayers; extractedLLRs = nrExtractResources(double(puschIndices), -L(:, :, 1:Qm)); ulschLLRs = reshape(gather(extractdata(extractedLLRs.')), [], 1); ulschLLRs = nrPUSCHDescramble(ulschLLRs, simLocal.PUSCH.NID, simLocal.PUSCH.RNTI, []);
Similarly, this code snippet shows the
ulschLLRs
calculation for practical channel estimation when you use the 5G conventional receiver in thehGetFeaturesAndLabels
function.
% Practical channel estimation using the PUSCH DM-RS for each layer dmrsLayerSymbols = nrPUSCHDMRS(carrier, puschNonCodebook); dmrsLayerIndices = nrPUSCHDMRSIndices(carrier, puschNonCodebook); [estChannelGrid, noiseEst] = nrChannelEstimate(carrier, rxGrid, dmrsLayerIndices, dmrsLayerSymbols, 'CDMLengths', pusch.DMRS.CDMLengths); % Extract PUSCH resource elements from the received grid [puschRx, puschHest] = nrExtractResources(puschIndices, rxGrid, estChannelGrid); % Equalization using MMSE [puschEq, csi] = nrEqualizeMMSE(puschRx, puschHest, noiseEst); % Decode PUSCH physical channel [ulschLLRs, rxSymbols] = nrPUSCHDecode(carrier, puschNonCodebook, puschEq, noiseEst);
% Link performance evaluation; results will return coded BER and throughput
[resultsRX, resultsAI] = evaluateLinkPerformance(simParameters, model);
- Test data generation: Serial execution - Inference environment: CUDA Simulating DeepRx and conventional receivers for 11 SNR points... ( 9.09%) SNRIn: +0.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 18.18%) SNRIn: +1.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 27.27%) SNRIn: +2.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 36.36%) SNRIn: +3.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 45.45%) SNRIn: +4.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 54.55%) SNRIn: +5.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 63.64%) SNRIn: +6.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 72.73%) SNRIn: +7.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 81.82%) SNRIn: +8.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 ( 90.91%) SNRIn: +9.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2 (100.00%) SNRIn: +10.00 dB | Channel: TDL-E | Delay Spread: 1.00e-07 | Max Doppler shift: 250.00 | DMRS Add. Pos. / Config. Type: 0 / 2
Step 5: Visualize Results
Use the visualizePerformance
local function to plot the coded BER and throughput values of the AI-native receiver and conventional 5G receivers against the SNR range. These figures show that the AI-native receiver significantly outperforms the conventional receiver for the given scenario parameters.
% Plot Coded BER vs. SNR and Throughput (%) vs. SNR
visualizePerformance(simParameters, resultsRX, resultsAI);
Details of AI-Native Fully Convolutional Receiver Network
The AI-native fully convolutional receiver, known as DeepRx, uses a CNN architecture that combines a Residual Network (ResNet) with full preactivation [2] and extreme Inception (Xception) architectures [3]. The network takes resource grids as input for an UL single-input-multiple-output (SIMO) system, where the grids have dimensions . The access network node receives the grids and routes them through the entry stage. After this stage, the resource grids proceed to the middle stage, which utilizes 11 ResNet blocks with various dilation factors. The architecture employs identity shortcuts between feature maps where input and output sizes match. Conversely, it uses projection shortcuts where input and output sizes differ, allowing the addition operation in each ResNet block to occur. To reduce computational complexity, each ResNet block implements depthwise separable convolutions. Finally, the processed resource block from the middle stage advances to the exit stage, which includes batch normalization, ReLU, and a final convolution stage with an output size of .
The example uses a trained model, deeprx_30k.mat
, which is compressed and included as a supporting file in the example folder under the name torch_trained_network.zip
. For training details and solver parameterization, see the AI-Native Fully Convolutional Receiver example. This diagram illustrates the architectural flow of the AI-native fully convolutional receiver model.
Local Functions
function displayModelSummary(modelParameters) % Convert model parameters to a cell array params = cell(modelParameters); % Extract and filter unique layer names layerNames = unique(string(params{2}), "stable"); layerNames = layerNames(~ismember(layerNames, ["", "bn", "relu"])); % Print the network summary border = repmat('-', 1, 40); fprintf('\n%s\n', border); fprintf(' Neural Network Summary\n'); fprintf('%s\n\n', border); fprintf(' %-30s: %.4f million\n', '- Total number of learnables', double(params{1})/1e6); fprintf(' %-30s: %d\n', '- Number of layers', length(layerNames)); fprintf('\n %-30s:\n', '- Model building blocks'); % Display unique layerNames with aligned indices indexWidth = length(num2str(length(layerNames))); arrayfun(@(i) fprintf([' %' num2str(indexWidth) 'd) %s\n'], i, layerNames{i}), 1:length(layerNames)); end function visualizePerformance(simParameters, resultsRX, resultsAIML) % Visualize the performance of the system by plotting coded BER and % throughput results against SNR for both MMSE based conventional 5G % and PyTorch AI-Native receiver % Create a figure object for the plots co = colororder; f = figure; f.Position = [0 0 1000 450]; t = tiledlayout(1, 2, "TileSpacing", "compact"); % Plot coded BER against SNR nexttile; semilogy(simParameters.SNRInVec, [resultsAIML.CodedBER], "-*", "Color", co(5,:), "LineWidth", 1.5, "MarkerSize", 7); hold on semilogy(simParameters.SNRInVec,[resultsRX.CodedBER],"-s", "LineWidth", 1.5, "MarkerSize", 7); hold off legend("DeepRx (PyTorch)","Conventional 5G Rx", "Location", "southwest"); grid on; xlabel('SNR (dB)'); ylabel('Coded BER'); % Plot throughput against SNR nexttile; plot(simParameters.SNRInVec,[resultsAIML.PercThroughput], "-*", "Color", co(5,:), "LineWidth", 1.5, "MarkerSize", 7); hold on plot(simParameters.SNRInVec,[resultsRX.PercThroughput], "-s" ,"LineWidth", 1.5, "MarkerSize", 7); hold off legend("DeepRx (PyTorch)", "Conventional 5G Rx", "Location", "southeast"); grid on; xlabel('SNR (dB)'); ylabel('Throughput (%)'); % Add a title to the tiled layout summarizing the system configuration title(t,sprintf('DelaySpread: %.2e, MaxDopplerShift: %.2f, DMRS: %d / %d, Channel: %s',... simParameters.DelaySpread,simParameters.MaximumDopplerShift,... simParameters.DMRSAdditionalPosition,simParameters.DMRSConfigurationType,... simParameters.ChannelModel)); end function [resultsRX, resultsTORCH] = evaluateLinkPerformance(simParameters, model) % ----------------------------------------------------------------- % Initialize simulation parameters % ----------------------------------------------------------------- simParameters.TrainNow = false; simParameters.DisplayInfo = true; randParameters.DelaySpread = simParameters.DelaySpread; randParameters.MaxDopplerShift = simParameters.MaximumDopplerShift; randParameters.DMRSAddPos = simParameters.DMRSAdditionalPosition; randParameters.DMRSConfigType = simParameters.DMRSConfigurationType; randParameters.ChannelModel = simParameters.ChannelModel; numOutputArgs = nargout('hGetFeaturesAndLabels'); % Check if the Python execution mode is 'InProcess' with parallelism enabled if strcmp(simParameters.PythonExecutionMode, "InProcess") && simParameters.UseParallel error("Python execution mode must be 'OutOfProcess' when `UseParallel` is true.") end % Preallocate the results structures resultsTORCH(1:length(simParameters.SNRInVec)) = struct('SNRIn', -1, 'BLER', -1, 'UncodedBER', -1, 'CodedBER', -1, ... 'SimThroughput', -1, 'MaxThroughput', -1, 'PercThroughput', -1); resultsRX(1:length(simParameters.SNRInVec)) = struct('SNRIn', -1, 'BLER', -1, 'UncodedBER', -1, 'CodedBER', -1, ... 'SimThroughput', -1, 'MaxThroughput', -1, 'PercThroughput', -1); % ----------------------------------------------------------------- % Setup parallel computing if enabled % ----------------------------------------------------------------- if simParameters.UseParallel && canUseParallelPool curPool = gcp; fprintf('\n- Test data generation: CPU acceleration on (%d) workers\n', curPool.NumWorkers); else fprintf('\n- Test data generation: Serial execution\n'); end % Inference/prediction environment device = pyrun(["import torch", "device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')"], "device"); fprintf('- Inference environment: %s\n', upper(string(device.type))); % ----------------------------------------------------------------- % Simulate conventional and AI-native receivers using parallel execution % ----------------------------------------------------------------- fprintf('\nSimulating DeepRx and conventional receivers for %d SNR points...\n', length(simParameters.SNRInVec)); if simParameters.UseParallel && canUseParallelPool futuresRX(1:length(simParameters.SNRInVec)) = parallel.FevalFuture; futuresTORCH(1:length(simParameters.SNRInVec)) = parallel.FevalFuture; % Ensure futures are canceled on function exit cleanupFuturesRX = onCleanup(@() cancel(futuresRX)); cleanupFuturesTORCH = onCleanup(@() cancel(futuresTORCH)); % Asynchronously simulate each SNR point for i = 1:length(simParameters.SNRInVec) randParameters.SNRIn = simParameters.SNRInVec(i); % Conventional 5G Receiver futuresRX(i) = parfeval(curPool, @hGetFeaturesAndLabels, numOutputArgs, simParameters, randParameters, Iteration=30e3 + i); % AI-Native Receiver (PyTorch) futuresTORCH(i) = parfeval(curPool, @hGetFeaturesAndLabels, numOutputArgs, simParameters, randParameters, Net=model, Iteration=30e3 + i); end % (Conventional Rx) Fetch the next finished results from a worker fprintf('\n'); for i = 1:length(simParameters.SNRInVec) [completedIdxRX, tmpResultRX, ~, ~, ~] = fetchNext(futuresRX); resultsRX(:, completedIdxRX) = tmpResultRX; % (Conventional Rx) Update progress status if simParameters.DisplayInfo fprintf('5G Receiver : (%6.2f%%) SNRIn: %+6.2f dB | Channel: %s | Delay Spread: %-7.2e | Max Doppler shift: %-6.2f | DMRS Add. Pos. / Config. Type: %d / %d\n', ... 100 * i / length(simParameters.SNRInVec), tmpResultRX.SNRIn, randParameters.ChannelModel, randParameters.DelaySpread, randParameters.MaxDopplerShift, ... randParameters.DMRSAddPos, randParameters.DMRSConfigType); end % (AI-native Rx) Fetch the next finished results from a worker [completedIdxTORCH, tmpResultTORCH, ~, ~, ~] = fetchNext(futuresTORCH); resultsTORCH(:, completedIdxTORCH) = tmpResultTORCH; % (AI-native Rx) Update progress status if simParameters.DisplayInfo fprintf('PyTorch DeepRx : (%6.2f%%) SNRIn: %+6.2f dB | Channel: %s | Delay Spread: %-7.2e | Max Doppler shift: %-6.2f | DMRS Add. Pos. / Config. Type: %d / %d\n', ... 100 * i / length(simParameters.SNRInVec), tmpResultTORCH.SNRIn, randParameters.ChannelModel, randParameters.DelaySpread, randParameters.MaxDopplerShift, ... randParameters.DMRSAddPos, randParameters.DMRSConfigType); end end % ----------------------------------------------------------------- % Sort results based on SNR index % ----------------------------------------------------------------- [~, idxRX] = sort([resultsRX.SNRIn]); [~, idxTORCH] = sort([resultsTORCH.SNRIn]); resultsRX = resultsRX(idxRX); resultsTORCH = resultsTORCH(idxTORCH); else % serial execution fprintf('\n'); for i = 1:length(simParameters.SNRInVec) % Calculate the performance results for the conventional 5G % receiver and PyTorch AI-native receiver randParameters.SNRIn = simParameters.SNRInVec(i); % Conventional 5G Receiver resultsRX(i) = hGetFeaturesAndLabels(simParameters, randParameters, Iteration=30e3+i); % AI-Native Receiver (PyTorch) resultsTORCH(i) = hGetFeaturesAndLabels(simParameters, randParameters, Net=model, Iteration=30e3+i); if simParameters.DisplayInfo fprintf('(%6.2f%%) SNRIn: %+6.2f dB | Channel: %s | Delay Spread: %-7.2e | Max Doppler shift: %-6.2f | DMRS Add. Pos. / Config. Type: %d / %d\n', ... 100 * i / length(simParameters.SNRInVec), randParameters.SNRIn, randParameters.ChannelModel, randParameters.DelaySpread, randParameters.MaxDopplerShift, ... randParameters.DMRSAddPos, randParameters.DMRSConfigType); end end end end
References
[1] M. Honkala, D. Korpi, and J. M. J. Huttunen, "DeepRx: Fully Convolutional Deep Learning Receiver," in IEEE Transactions on Wireless Communications, vol. 20, no. 6, pp. 3925—3940, June 2021.
[2] He, K., Zhang, X., Ren, S., and Sun, J., "Identity mappings in deep residual networks", in European Conference on Computer Vision, pp. 630—645, Springer, 2016.
[3] F. Chollet, "Xception: Deep Learning with Depthwise Separable Convolutions," in 2017 IEEE Conference on Computer Vision and Pattern Recognition (CVPR), Honolulu, HI, USA, 2017 pp. 1800—1807.