Predict the noise generated by airfoil blades using OpenNN

The fundamental goal of this example is to predict the noise generated by an aircraft’s airfoil blades.

Contents:

  1. Application type.
  2. Data set.
  3. Neural network.
  4. Training strategy.
  5. Testing analysis.
  6. Model deployment.
  7. Full code.

1. Application type

The variable to be predicted is continuous (sound pressure level). Therefore, this is an approximation project.

The fundamental goal is to model the sound pressure level as a function of the airfoil features and airspeed.

2. Data set

The first step is to prepare the data set, the source of information for the approximation problem. It is composed of:

  • Data source.
  • Variables.
  • Instances.

The file airfoil_self_noise.csv contains the data for this example. Here the number of variables (columns) is 6, and the number of instances (rows) is 1503. It can be loaded as:

DataSet data_set("path_to_source/airfoil_self_noise.csv",',',true);

In that way, this problem has the 6 following variables:

  • frequency, in Hertzs, used as input.
  • angle_of_attack, in degrees, used as input.
  • chord_length, in meters, used as input.
  • free_stream_velocity, in meters per second, used as input.
  • suction_side_displacement_thickness, in meters, used as input.
  • scaled_sound_pressure_level, in decibels, used as the target.

The instances are divided into training, selection, and testing subsets at random, containing 60%, 20%, and 20% of the cases, respectively. More specifically, 753 samples are used here for training, 375 for validation, and 375 for testing. They can be split into random using:

data_set.split_samples_random();

Then, there will be six numerical variables in the data set. Once we have the data ready, we will get the information of the variables, such as names and statistical descriptives

const Tensor<string, 1> inputs_names = data_set.get_input_variables_names();
const Tensor<string, 1> targets_names = data_set.get_target_variables_names();

To get the input variables number and target variables number, we use the following command

const Index input_variables_number = data_set.get_input_variables_number();
const Index target_variables_number = data_set.get_target_variables_number();

For more information about the data set methods, see DataSet class.

3. Neural network

The second step is to choose the correct neural network architecture. For approximation problems, it is usually composed by:

  • A scaling layer.
  • Two perceptron layers.
  • An unscaling layer.
  • A bounding layer.

The NeuralNetwork class is responsible for building the neural network and properly organizing the layers of neurons using the following constructor. If you need more complex architectures, you should see NeuralNetwork class.

const Index hidden_neurons_number = 12;
NeuralNetwork neural_network(NeuralNetwork::Approximation,
     {input_variables_number, hidden_neurons_number, target_variables_number});

Therefore, we have already created a good-looking model. Thus we proceed to the learning process with TrainingStrategy.

4. Training strategy

The third step is to set the training strategy, which is composed of:

  • Loss index.
  • Optimization algorithm.

Firstly, we construct the training strategy object

TrainingStrategy training_strategy(&neural_network, &data_set);

then, set the error term

training_strategy.set_loss_method(TrainingStrategy::MEAN_SQUARED_ERROR);

and finally the optimization algorithm

training_strategy.set_optimization_method(TrainingStrategy::ADAPTIVE_MOMENT_ESTIMATION);

OpenNN builds by default the training strategy object using the quasi-Newton method as the optimization algorithm and normalized squared error as the loss method. We can now start the training process by using the command

training_strategy.perform_training();

If we need to go further, OpenNN allows control of the optimization, for example

AdaptiveMomentEstimation* adam = training_strategy.get_adaptive_moment_estimation_pointer();
adam->set_loss_goal(type(1.0e-3));
adam->set_maximum_epochs_number(10000);
adam->set_display_period(1000);
training_strategy.perform_training();

For more information about the training strategy methods, see TrainingStrategy class.

5. Testing analysis

The fourth step is to evaluate our model. For that purpose, we need to use the testing analysis class, whose goal is to validate the model’s generalization performance. Here, we compare the neural network outputs to the corresponding targets in the testing instances of the data set.
As in the previous cases, we start by building the testing analysis object

TestingAnalysis testing_analysis(&neural_network, &data_set);

and perform the testing, in our case we use linear regression analysis

const TestingAnalysis::LinearRegressionAnalysis linear_regression_analysis
     = testing_analysis.perform_linear_regression_analysis()[0];
linear_regression_analysis.print();

For more information about the testing analysis methods, see TestingAnalysis class.

6. Model deployment

The model is now ready to estimate the self-noise of new airfoils with satisfactory quality over the same data range.

To generate predictions with new data, you can use

neural_network.calculate_outputs();

For instance, the new inputs are:

  • angle_of_attack: 6.782 degrees.
  • chord_length: 0.136 meters.
  • free_stream_velocity: 50.860 meters per second.
  • suction_side_displacement_thickness: 0.011 meters.

and in OpenNN we can write it as

Tensor<type, 2> inputs(1,4);
inputs.setValues({{type(6.782),type(0.136),type(50.860),type(0.011)}});
neural_network.calculate_outputs(inputs);

or save the model.

neural_network.save_expression_c("../data/expression.txt");
neural_network.save_expression_python("../data/expression.txt");

The model can be implemented in python, php, … .

7. Full Code

Joining all steps, we obtain the following code:

// DataSet
DataSet data_set("../data/airfoil_self_noise.csv", ';', true);
const Index input_variables_number = data_set.get_input_variables_number();
const Index target_variables_number = data_set.get_target_variables_number();
            
// Neural Network
const Index hidden_neurons_number = 12;
NeuralNetwork neural_network(NeuralNetwork::Approximation,
      {input_variables_number,hidden_neurons_number,target_variables_number});
ScalingLayer* scaling_layer_pointer = neural_network.get_scaling_layer_pointer();
scaling_layer_pointer->set_descriptives(inputs_descriptives);
scaling_layer_pointer->set_scalers(MinimumMaximum);
            
// Training Strategy
TrainingStrategy training_strategy(&neural_network, &data_set);
training_strategy.set_loss_method(TrainingStrategy::MEAN_SQUARED_ERROR);
training_strategy.set_optimization_method(TrainingStrategy::ADAPTIVE_MOMENT_ESTIMATION);
training_strategy.perform_training();
            
// Testing Analysis
TestingAnalysis testing_analysis(&neural_network, &data_set);
const TestingAnalysis::LinearRegressionAnalysis linear_regression_analysis
     = testing_analysis.perform_linear_regression_analysis()[0];
linear_regression_analysis.print();
            
// Model deployment
Tensor<type, 2=""> inputs(1,4);
inputs.setValues({{type(6.782),type(0.136),type(50.860),type(0.011)}});
neural_network.calculate_outputs(inputs);
            
// Save results
neural_network.save_expression_c("../data/airfoil_self_noise.txt");
neural_network.save_expression_python("../data/airfoil_self_noise.py");
            </type,>

This code can be exported to your C++ project.

References:

  • UCI Machine Learning Repository. Airfol Self-Noise Data Set.
  • Fisher, R.A. «The use of multiple measurements in taxonomic problems» Annual Eugenics, 7, Part II, 179-188 (1936); also in «Contributions to Mathematical Statistics» (John Wiley, NY, 1950).
  • T.F. Brooks, D.S. Pope, and A.M. Marcolini. Airfoil self-noise and prediction. Technical report, NASA RP-1218, July 1989.