Hui Su | 8e15470 | 2018-03-23 16:10:57 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2016, Alliance for Open Media. All rights reserved |
| 3 | * |
| 4 | * This source code is subject to the terms of the BSD 2 Clause License and |
| 5 | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
| 6 | * was not distributed with this source code in the LICENSE file, you can |
| 7 | * obtain it at www.aomedia.org/license/software. If the Alliance for Open |
| 8 | * Media Patent License 1.0 was not distributed with this source code in the |
| 9 | * PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
| 10 | */ |
| 11 | |
| 12 | #include <assert.h> |
Alexander Bokov | 9b5fb2c | 2018-08-27 14:37:21 -0700 | [diff] [blame] | 13 | #include <math.h> |
Hui Su | 8e15470 | 2018-03-23 16:10:57 -0700 | [diff] [blame] | 14 | |
Alexander Bokov | 9b5fb2c | 2018-08-27 14:37:21 -0700 | [diff] [blame] | 15 | #include "aom_dsp/aom_dsp_common.h" |
Hui Su | 8e15470 | 2018-03-23 16:10:57 -0700 | [diff] [blame] | 16 | #include "av1/encoder/ml.h" |
| 17 | |
| 18 | void av1_nn_predict(const float *features, const NN_CONFIG *nn_config, |
| 19 | float *output) { |
| 20 | int num_input_nodes = nn_config->num_inputs; |
| 21 | int buf_index = 0; |
| 22 | float buf[2][NN_MAX_NODES_PER_LAYER]; |
| 23 | const float *input_nodes = features; |
| 24 | |
| 25 | // Propagate hidden layers. |
| 26 | const int num_layers = nn_config->num_hidden_layers; |
| 27 | assert(num_layers <= NN_MAX_HIDDEN_LAYERS); |
| 28 | for (int layer = 0; layer < num_layers; ++layer) { |
| 29 | const float *weights = nn_config->weights[layer]; |
| 30 | const float *bias = nn_config->bias[layer]; |
| 31 | float *output_nodes = buf[buf_index]; |
| 32 | const int num_output_nodes = nn_config->num_hidden_nodes[layer]; |
| 33 | assert(num_output_nodes < NN_MAX_NODES_PER_LAYER); |
| 34 | for (int node = 0; node < num_output_nodes; ++node) { |
| 35 | float val = 0.0f; |
| 36 | for (int i = 0; i < num_input_nodes; ++i) |
| 37 | val += weights[i] * input_nodes[i]; |
| 38 | val += bias[node]; |
| 39 | // ReLU as activation function. |
| 40 | val = val > 0.0f ? val : 0.0f; // Could use AOMMAX(). |
| 41 | output_nodes[node] = val; |
| 42 | weights += num_input_nodes; |
| 43 | } |
| 44 | num_input_nodes = num_output_nodes; |
| 45 | input_nodes = output_nodes; |
| 46 | buf_index = 1 - buf_index; |
| 47 | } |
| 48 | |
| 49 | // Final output layer. |
| 50 | const float *weights = nn_config->weights[num_layers]; |
| 51 | for (int node = 0; node < nn_config->num_outputs; ++node) { |
| 52 | const float *bias = nn_config->bias[num_layers]; |
| 53 | float val = 0.0f; |
| 54 | for (int i = 0; i < num_input_nodes; ++i) |
| 55 | val += weights[i] * input_nodes[i]; |
| 56 | output[node] = val + bias[node]; |
| 57 | weights += num_input_nodes; |
| 58 | } |
| 59 | } |
Alexander Bokov | 9b5fb2c | 2018-08-27 14:37:21 -0700 | [diff] [blame] | 60 | |
| 61 | void av1_nn_softmax(const float *input, float *output, int n) { |
| 62 | // Softmax function is invariant to adding the same constant |
| 63 | // to all input values, so we subtract the maximum input to avoid |
| 64 | // possible overflow. |
| 65 | float max_inp = input[0]; |
| 66 | for (int i = 1; i < n; i++) max_inp = AOMMAX(max_inp, input[i]); |
| 67 | float sum_out = 0.0f; |
| 68 | for (int i = 0; i < n; i++) { |
| 69 | output[i] = (float)exp(input[i] - max_inp); |
| 70 | sum_out += output[i]; |
| 71 | } |
| 72 | for (int i = 0; i < n; i++) output[i] /= sum_out; |
| 73 | } |