Repository: larq/larq

Issue:
Human readable large numbers in `models.summary()`
At the moment the summary table generated by `models.summary()` has lines such as: `Number of MACs: 13228896000` where the raw integer is printed. It would be a lot more readable if this number were instead printed as `13.229 B` or `13.229 Billion`, or at least if there was a thousands separator (space or comma).


Discussion:

Resolving patch:
diff --git a/larq/models.py b/larq/models.py
--- a/larq/models.py
+++ b/larq/models.py
@@ -48,6 +48,34 @@ def _bitsize_as_str(bitsize):
         raise NotImplementedError()
 
 
+def _number_as_readable_str(num):
+    # The initial rounding here is necessary so that e.g. `999000` gets
+    # formatted as `1.000 M` rather than `1000 k`
+    num = float(f"{num:.3g}")
+
+    # For numbers less than 1000, output them directly, stripping any trailing
+    # zeros and decimal places.
+    if num < 1000:
+        return str(num).rstrip("0").rstrip(".")
+
+    # For numbers that are at least 1000 trillion (1 quadrillion) format with
+    # scientific notation (3 s.f. = 2 d.p. in scientific notation).
+    if num >= 1e15:
+        return f"{num:#.2E}"
+
+    # Count the magnitude.
+    magnitude = 0
+    while abs(num) >= 1000 and magnitude < 4:
+        magnitude += 1
+        num /= 1000.0
+
+    # ':#.3g' formats the number with 3 significant figures, without stripping
+    # trailing zeros.
+    num = f"{num:#.3g}".rstrip(".")
+    unit = ["", " k", " M", " B", " T"][magnitude]
+    return num + unit
+
+
 def _format_table_entry(x, units=1):
     try:
         assert not np.isnan(x)
@@ -295,9 +323,15 @@ def generate_table(self, include_macs=True):
 
     def generate_summary(self, include_macs=True):
         summary = [
-            ["Total params", self.weight_count()],
-            ["Trainable params", self.weight_count(trainable=True)],
-            ["Non-trainable params", self.weight_count(trainable=False)],
+            ["Total params", _number_as_readable_str(self.weight_count())],
+            [
+                "Trainable params",
+                _number_as_readable_str(self.weight_count(trainable=True)),
+            ],
+            [
+                "Non-trainable params",
+                _number_as_readable_str(self.weight_count(trainable=False)),
+            ],
             ["Model size:", f"{self.memory / (8*1024*1024):.2f} MB"],
             [
                 "Float-32 Equivalent",
@@ -309,7 +343,12 @@ def generate_summary(self, include_macs=True):
         if include_macs:
             binarization_ratio = self.op_count("mac", 1) / self.op_count(op_type="mac")
             ternarization_ratio = self.op_count("mac", 2) / self.op_count(op_type="mac")
-            summary.append(["Number of MACs", self.op_count(op_type="mac")])
+            summary.append(
+                [
+                    "Number of MACs",
+                    _number_as_readable_str(self.op_count(op_type="mac")),
+                ]
+            )
             if binarization_ratio > 0:
                 summary.append(
                     ["Ratio of MACs that are binarized", f"{binarization_ratio:.4f}"]


Test patch:
diff --git a/larq/snapshots/snap_models_test.py b/larq/snapshots/snap_models_test.py
--- a/larq/snapshots/snap_models_test.py
+++ b/larq/snapshots/snap_models_test.py
@@ -21,13 +21,13 @@
 | Total                                                     1600      288     38794  151.80       19.38        4.25       148.73 |
 +--------------------------------------------------------------------------------------------------------------------------------+
 +sequential summary--------------------------+
-| Total params                       40682   |
-| Trainable params                   1952    |
-| Non-trainable params               38730   |
+| Total params                       40.7 k  |
+| Trainable params                   1.95 k  |
+| Non-trainable params               38.7 k  |
 | Model size:                        0.15 MB |
 | Float-32 Equivalent                0.16 MB |
 | Compression Ratio of Memory        0.96    |
-| Number of MACs                     1411968 |
+| Number of MACs                     1.41 M  |
 | Ratio of MACs that are binarized   0.1124  |
 | Ratio of MACs that are ternarized  0.0247  |
 +--------------------------------------------+