Noisy Nets for Exploration
Noisy Nets add learnable noise to network weights so one steady perturbation drives a full stretch of action, then gradients quiet the noise where values turn certain.
Why Does This Exist?
Epsilon greedy explores by rolling dice each step. With chance epsilon it picks a uniform random action, else the greedy action. That dithers. Left then right then left cancels progress, which hurts in mazes and sparse games where a distant goal needs a steady run of moves. It also treats mastered states like new ones, still blundering late in training, and its start, end, and decay need hand tuning per game.
Fortunato and coauthors introduced Noisy Nets in 2018 to move noise from actions to weights. One sampled perturbation stays fixed while the agent scores all actions in a state, so behavior stays steady across steps. The noise scales are learned, so exploration fades by itself where values turn certain. The base learner here is deep Q networks, and the natural sequel is Rainbow DQN, which ships Noisy Nets as its exploration piece. The older baseline is epsilon greedy.
This page covers the NoisyLinear split, factorised sampling that keeps cost low, and the gradient step that quiets sigma. Architecture detail for the value stream lives with dueling networks.
Think of It Like This
A violin student with shaky hands
A new student holds the bow with a slight shake. The shake is not one wrong note. It tints a whole phrase: pressure, angle, and vibrato all shift together for several bars, so the student hears how nearby sounds connect.
As a chord gets mastered, the hand steadies on that chord alone. The shake stays on unfamiliar passages. Practice does not follow a kitchen timer. Feedback sets the steadiness per passage.
The map holds part for part. Hand shake is weight noise sigma times eps. A full phrase is steady perturbed weights across action scores. Steadier mastered chords are sigma falling toward zero under gradients.
Where it stops: hands tire and ears judge tone, while Noisy Nets sample Gaussian numbers and follow Bellman error. The analogy also hides resampling. Fresh noise per mini batch is wanted, fresh noise per action score inside one state is not.
How It Actually Works
A standard layer maps input in to output in with weight and bias as . A NoisyLinear layer keeps a mean plus a scale times noise for each parameter:
Here and are learnable means, and are learnable scales, and are zero mean noise draws, and is element wise product. Sample eps once per step, hold the realized weights while scoring every action, then resample for the next step or batch.
Factorised sampling keeps cost low
Independent noise needs draws. Factorised noise needs . Draw in and in from a standard Gaussian, pass each through , then form weight noise by outer product and bias noise from the output side:
Worked example with real numbers
Take one neuron with inputs and output. Input . Means and . Scales and . Draw and .
Apply . We get , , and on the output side. So and .
Realized weights are , , and . Forward output is:
The clean mean output is . Noise shifted it by plus 1.60.
Set Bellman target with step . Error . Loss is . Gradients on means are with bias grad 1.40. Gradients on scales multiply by noise to with bias grad 1.40. The update gives , , , and . Overshoot pushed all three sigma values down, which is self annealing in one step.
Code
This snippet repeats the forward pass and the sigma update from the worked example.
import math
def f(z: float) -> float: return math.copysign(math.sqrt(abs(z)), z)
def noisy_forward(x0: float, x1: float) -> float: w0: float = 0.5 + 0.4 * f(1.0) * f(1.0) w1: float = -0.2 + 0.3 * f(1.0) * f(-4.0) b: float = 0.1 + 0.2 * f(1.0) return w0 * x0 + w1 * x1 + b
y: float = noisy_forward(2.0, -1.0)print(f"{y:.4f}") # -> 2.9000print(f"{0.5 * (y - 1.5) ** 2:.4f}") # -> 0.9800print(f"{0.4 - 0.1 * 2.8:.2f}") # -> 0.12Watch Out For
Resampling noise inside one state breaks the method
Drawing fresh eps for each action score in the same state turns weight noise back into action dither. Symptom: learning looks like epsilon greedy, with jittery Q gaps and no long exploratory runs.
Sample once per environment step and hold realized weights across all action scores for that state. Draw fresh noise per training batch. When action gaps flicker sign within one state, check the sample point first.
Sigma starts too large and training never settles
A large sigma zero makes early outputs swing so wide that targets chase noise. Symptom: loss stays high, Q values swing, and sigma gradients stay large because each draw overshoots.
Start near the paper value around for weights, watch mean sigma per layer, and lower the init when early exceeds several times the reward scale. Small steady noise beats heroic noise.
The Quick Version
- Noisy Nets move exploration from random actions to learnable weight noise held steady across action scores.
- A NoisyLinear weight equals mean plus scale times noise, with both mean and scale learned by gradient descent.
- Factorised sampling builds perturbations from draws through .
- The worked neuron outputs 2.90 noisy versus 1.30 clean, takes loss 0.98 against target 1.50, and shrinks sigma from 0.40 to 0.12.
- Overshoot lowers sigma and undershoot can sustain it, so mastered states quiet by themselves.
- Resample per step or batch, never per action score inside one state.