From 2b30d4d2bfb258e0ef51dfa263b441469f5ab17c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:17:07 +0000 Subject: [PATCH] Create rule S6929: The axis argument should be specified when using TensorFlow's reduction operations (#3644) * Create rule S6929 * Create rule S6929: The axis argument should be specified when using TensorFlow's reduction operations * Fix after review --------- Co-authored-by: joke1196 <joke1196@users.noreply.github.com> Co-authored-by: David Kunzmann <david.kunzmann@sonarsource.com> --- rules/S6929/metadata.json | 2 + rules/S6929/python/metadata.json | 24 +++++++ rules/S6929/python/rule.adoc | 104 +++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 rules/S6929/metadata.json create mode 100644 rules/S6929/python/metadata.json create mode 100644 rules/S6929/python/rule.adoc diff --git a/rules/S6929/metadata.json b/rules/S6929/metadata.json new file mode 100644 index 00000000000..2c63c085104 --- /dev/null +++ b/rules/S6929/metadata.json @@ -0,0 +1,2 @@ +{ +} diff --git a/rules/S6929/python/metadata.json b/rules/S6929/python/metadata.json new file mode 100644 index 00000000000..3b898974b13 --- /dev/null +++ b/rules/S6929/python/metadata.json @@ -0,0 +1,24 @@ +{ + "title": "The axis argument should be specified when using TensorFlow's reduction operations", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-6929", + "sqKey": "S6929", + "scope": "All", + "defaultQualityProfiles": ["Sonar way"], + "quickfix": "unknown", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM", + "RELIABILITY": "HIGH" + }, + "attribute": "CLEAR" + } +} diff --git a/rules/S6929/python/rule.adoc b/rules/S6929/python/rule.adoc new file mode 100644 index 00000000000..83c46a414ef --- /dev/null +++ b/rules/S6929/python/rule.adoc @@ -0,0 +1,104 @@ +This rule raises an issue when the axis argument is not provided to TensorFlow's reduction operations. + +== Why is this an issue? + +The result of TensorFlow's reduction operations (i.e. ``tf.math.reduce_sum``, ``tf.math.reduce_std``), +highly depends on the shape of the Tensor provided. + +[source,python] +---- +import tensorflow as tf + +x = tf.constant([[1, 1, 1], [1, 1, 1]]) +tf.math.reduce_sum(x) +---- + +In the example above the reduction of the 2 dimensional array will return the value `6` as all the elements are added together. +By default TensorFlow's reduction operations are applied across all axis. When specifying an axis the result will be completely different. + +[source,python] +---- +import tensorflow as tf + +x = tf.constant([[1, 1, 1], [1, 1, 1]]) +tf.math.reduce_sum(x, axis=0) +---- + +Here the result will be `[2,2,2]` as the reduction is applied only on the axis 0. + +TensorFlow's default behavior can be confusing, especially when the reducing array of different shapes. + +Considering the following example: + +[source,python] +---- +import tensorflow as tf + +x = tf.constant([[1], [2]]) +y = tf.constant([1, 2]) +tf.math.reduce_sum(x + y) +---- + +Here the result will be `12` instead of the `6` that could be expected. This is because the implicit broadcasting reshapes the +first array to `[[1,1], [2,2]]` which is then added to the `y` array `[1,2]` resulting in ``[[2,3], [3,4]]``. As the +reduction happen across all dimensions the result is then ``2 + 3 + 3 + 4 = 12``. It is not clear by looking at the example +if this was intentional or if the user made a mistake. + +This is why a good practice is to always specify the axis on which to perform the reduction. + +For example: + +[source,python] +---- +import tensorflow as tf + +x = tf.constant([[1], [2]]) +y = tf.constant([1, 2]) +tf.math.reduce_sum(x + y, axis=0) +---- + +In the example above, specifying the axis clarifies the intent, as the result now is ``[5, 7]``. If the intent was to effectively +reduce across all dimensions the user should provide the list of axis `axis=[0,1]` +or clearly state the default behavior should be applied with ``axis=None``. + +== How to fix it + +To fix this issue provide the axis argument when using a TensorFlow reduction operation such as ``tf.math.reduce_sum``, ``tf.math.reduce_prod``, ``tf.math.reduce_mean``, etc... + +=== Code examples + +==== Noncompliant code example + +[source,python,diff-id=1,diff-type=noncompliant] +---- +import tensorflow as tf + +x = tf.constant([[1, 1, 1], [1, 1, 1]]) +tf.math.reduce_sum(x) # Noncompliant: the axis arguments defaults to None +---- + +==== Compliant solution + +[source,python,diff-id=1,diff-type=compliant] +---- +import tensorflow as tf + +x = tf.constant([[1, 1, 1], [1, 1, 1]]) +tf.math.reduce_sum(x, axis=0) # Compliant: the reduction will happen only on the axis 0, resulting in `[2,2,2]` +---- + + +== Resources +=== Documentation + +* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_max[tf.math.reduce_max reference] +* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_mean[tf.math.reduce_mean reference] +* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_min[tf.math.reduce_min reference] +* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_prod[tf.math.reduce_prod reference] +* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_std[tf.math.reduce_std reference] +* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_sum[tf.math.reduce_sum reference] +* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_variance[tf.math.reduce_variance reference] + +=== Articles & blog posts + +* Vahidk Developers Guide - https://github.com/vahidk/EffectiveTensorflow?tab=readme-ov-file#broadcasting-the-good-and-the-ugly[Broadcasting the good and the ugly]