Straight-through estimator — hard forward, soft backward.
Returns a tensor numerically equal to hard whose gradient is the
gradient of soft:
where is the stop-gradient operator. The forward
value is exactly , and is the identity, so a discrete or otherwise
non-differentiable hard can sit in the middle of a network while
the surrounding layers still train.
Parameters
hardTensorargmax or a
nearest-codebook lookup.softTensorhard; in practice the two have the
same shape.Returns
TensorEqual to hard in value, differentiable with respect to
whatever soft depends on.
Notes
Introduced by Bengio, Léonard, and Courville, "Estimating or
Propagating Gradients Through Stochastic Neurons for Conditional
Computation" (arXiv:1308.3432). It is the mechanism behind
gumbel_softmax with hard=True and behind
lucid.nn.VectorQuantizer, which is why it lives here rather
than being rewritten at each call site.
The estimator is biased — the returned gradient is not the gradient of the discrete function, which has none — but it is low-variance and empirically trains where the true gradient does not exist.
Examples
>>> import lucid
>>> import lucid.nn.functional as F
>>> soft = lucid.tensor([0.3, 0.7], requires_grad=True)
>>> hard = lucid.tensor([0.0, 1.0])
>>> out = F.straight_through(hard, soft)
>>> out.tolist() # the forward value is hard
[0.0, 1.0]
>>> out.sum().backward()
>>> soft.grad.tolist() # gradient passed through untouched
[1.0, 1.0]