Skip to content

Commit

Permalink
Create rule S6929: The axis argument should be specified when using T…
Browse files Browse the repository at this point in the history
…ensorFlow'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 <[email protected]>
Co-authored-by: David Kunzmann <[email protected]>
  • Loading branch information
3 people authored Apr 4, 2024
1 parent c6d7ba2 commit 2b30d4d
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
2 changes: 2 additions & 0 deletions rules/S6929/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
24 changes: 24 additions & 0 deletions rules/S6929/python/metadata.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
104 changes: 104 additions & 0 deletions rules/S6929/python/rule.adoc
Original file line number Diff line number Diff line change
@@ -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]

0 comments on commit 2b30d4d

Please sign in to comment.