Course outline

Deep Learning / Representation and acceptance evidence / Lesson 16

Precision, memory, and efficiency

Distinguish mathematical counts, numerical formats, and measurements from an actual runtime.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain rounding, range, and loss scaling.
  • Build an explicit training-memory estimate.
  • Evaluate efficiency changes under a fixed correctness contract.

A numeric format has range and resolution

Floating-point formats encode a sign, exponent, and significand. Exponent width affects the range of representable magnitudes, while significand precision affects spacing near a value. A smaller format can save storage but lose small differences or overflow at magnitudes a wider format represents. Different sixteen-bit formats have different tradeoffs, so bit width alone does not specify behavior. Numerical stability also depends on which operations accumulate in wider precision. Record parameter, activation, reduction, gradient, and optimizer-state formats rather than describing the entire procedure with one vague precision label.

Inspect a representable-update failure

The first fixture packs and unpacks a scalar using Python struct binary16 storage. Adding 0.0001 to one is visible in ordinary Python floating-point arithmetic, but rounding the result to binary16 loses that increment. This illustrates why a method can need wider update accumulation even when some forward values use a smaller format. The program does not execute a half-precision matrix kernel, mixed-precision training, or a GPU operation. Its result establishes a storage-format fact for the stated scalar, not a speed comparison or a claim about any framework automatic-casting policy.

python Deterministic standard-library calculation
import struct
def half(value):
    return struct.unpack("e", struct.pack("e", value))[0]
w, delta = 1.0, 0.0001
print(f"full update={w+delta:.7f}")
print(f"half update={half(w+delta):.7f}")
print("half bytes", struct.calcsize("e"))
Expected output
full update=1.0001000
half update=1.0000000
half bytes 2

Binary16 rounding loses this small increment near one; this is a storage-format demonstration, not accelerator arithmetic.

Scaling gradients has a strict algebraic boundary

Multiplying a loss by a positive scale multiplies its derivatives by that scale in exact arithmetic. Before an ordinary optimizer step, gradients must be divided back by the scale unless the algorithm intentionally defines another update. Loss scaling can help preserve small gradients in certain limited-range calculations, but it cannot repair invalid inputs or an unstable forward formula. Nonfinite gradients require a defined skip or recovery policy. Clipping a scaled gradient before unscaling changes the effective clipping bound. Adaptive scaling state and whether an update occurred also matter when advancing schedules or resuming training.

Count every named memory category

The second fixture counts weights, gradients, two assumed optimizer arrays, and one saved activation. It deliberately names omissions: actual training can store many activations, temporary workspaces, framework objects, allocator reservations, and extra precision copies. Parameter count alone therefore cannot predict peak memory. Increasing batch size generally grows many activation terms while parameter storage stays fixed. Attention score storage can grow with sequence length squared under a conventional dense implementation. Activation checkpointing trades recomputation for stored intermediates and must preserve stochastic or stateful behavior according to the chosen implementation. These are analytical tradeoffs, not measurements made by this course.

python Deterministic standard-library calculation
parameters = 1000
weight_bytes = parameters * 4
gradient_bytes = parameters * 4
optimizer_bytes = parameters * 2 * 4
activation_bytes = 8 * 16 * 4
print("weights", weight_bytes)
print("assumed training total", weight_bytes+gradient_bytes+optimizer_bytes+activation_bytes)
print("double batch activations", 2*activation_bytes)
Expected output
weights 4000
assumed training total 16512
double batch activations 1024

The count assumes float32 weights, gradients, two optimizer arrays, and one saved activation; it omits allocator and other runtime storage.

Measure improvements with the same task boundary

A lower precision, smaller batch, different kernel, or changed architecture should be checked against the required output tolerances and evaluation behavior. Measure peak memory and latency on the actual device using an appropriate synchronized procedure, and include preprocessing and transfer costs if they belong to the user-visible path. Report shapes, batch size, warmup policy, hardware, software, and the relevant distribution of times. This course performs no hardware benchmarking. Its small counts and rounding examples teach how to formulate a defensible experiment while leaving framework runtime profiling to dedicated tracks.

Common errors and fixes

A small update disappears in low-precision storage

Why it happens
The increment is smaller than the local representable spacing.
How to fix it
Use an appropriate wider accumulation policy and verify its update semantics.
Prevent it next time
Test representative parameter magnitudes and increments.

Clipping becomes stronger after enabling loss scaling

Why it happens
Scaled gradients were clipped before the intended unscale.
How to fix it
Unscale at the documented point before applying the original bound.
Prevent it next time
Check the combined precision, clipping, and step policy.

Memory usage greatly exceeds weight size

Why it happens
Activations, gradients, optimizer state, and temporary storage were omitted.
How to fix it
Build and measure a complete category budget.
Prevent it next time
Record assumptions and compare estimated versus observed peaks.

Check your understanding

1. If a loss is multiplied by 128, how should its gradient enter the same ordinary optimizer update?

Show worked solution

Divide the computed scaled gradient by 128 before that update. In exact arithmetic this restores the original derivative. Finite precision and nonfinite checks still need handling. Do not multiply the learning rate blindly without considering clipping, optimizer state, or the scaling policy.

2. Why does halving stored weight bytes not guarantee halving peak training memory?

Show worked solution

Weights are one category among gradients, optimizer arrays, saved activations, workspaces, and runtime overhead. Some remain in wider precision and some scale with batch or sequence length. Estimate each category and measure the actual runtime; a weight-file-size ratio is not a peak-memory measurement.

Key takeaways

  • Format range and precision are different properties.
  • Scaling, clipping, and update order must remain consistent.
  • Analytical counts require runtime measurements before performance claims.

Official references