TODO(myenik): This is the same as the reference implementation, not actually optimized yet.
| 3596 | // TODO(myenik): This is the same as the reference implementation, not actually |
| 3597 | // optimized yet. |
| 3598 | inline void LogSoftmax(const SoftmaxParams& params, |
| 3599 | const RuntimeShape& input_shape, const float* input_data, |
| 3600 | const RuntimeShape& output_shape, float* output_data) { |
| 3601 | gemmlowp::ScopedProfilingLabel label("LogSoftmax"); |
| 3602 | const int trailing_dim = input_shape.DimensionsCount() - 1; |
| 3603 | const int outer_size = |
| 3604 | MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape); |
| 3605 | const int depth = |
| 3606 | MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); |
| 3607 | |
| 3608 | for (int i = 0; i < outer_size; ++i) { |
| 3609 | const float* block_input_data = input_data + i * depth; |
| 3610 | float* block_output_data = output_data + i * depth; |
| 3611 | // Find max element value which we'll use to ensure numerical stability |
| 3612 | // taking advantage of the following equality: |
| 3613 | // log(exp(x[i])/sum(exp(x[i]))) == log(exp(x[i]+C)/sum(exp(x[i]+C))) |
| 3614 | float max = std::numeric_limits<float>::lowest(); |
| 3615 | for (int c = 0; c < depth; ++c) { |
| 3616 | max = std::max(max, block_input_data[c]); |
| 3617 | } |
| 3618 | |
| 3619 | // Compute sum. |
| 3620 | float sum = 0.f; |
| 3621 | for (int c = 0; c < depth; ++c) { |
| 3622 | sum += std::exp(block_input_data[c] - max); |
| 3623 | } |
| 3624 | |
| 3625 | // Compute result. |
| 3626 | const float log_sum = std::log(sum); |
| 3627 | for (int c = 0; c < depth; ++c) { |
| 3628 | block_output_data[c] = block_input_data[c] - max - log_sum; |
| 3629 | } |
| 3630 | } |
| 3631 | } |
| 3632 | |
| 3633 | // Currently just a copy of the reference code. |
| 3634 | inline void LogSoftmax(const SoftmaxParams& params, |