Transfer Learning and Generalization
A policy trained on one task can jump-start learning on a related task, but only when the two tasks share structure. Otherwise the old habits hurt.
Why Does This Exist?
Training every new task from a random policy wastes samples. A warehouse robot that already learned to pick boxes should not relearn vision, grasping, and navigation from zero when the warehouse layout changes slightly. Transfer learning reuses the old policy or value function as the starting point for the new task, then fine-tunes it.
Here is the deal: reuse helps only when the tasks share structure. This page covers how transfer works in RL (pretrained initialization plus fine-tuning), how to measure whether it helped, and how to read the generalization gap across a spread of tasks. It does not re-teach supervised transfer, which lives at transfer learning, and it stays distinct from learning-to-learn approaches such as meta-reinforcement learning and MAML in RL, which optimize for adaptability itself.
You need one prerequisite idea first. A Markov decision process (MDP) is the task description: states, actions, transition dynamics, and rewards. A policy is your rule for picking action in state . If those terms feel shaky, start with fundamentals of reinforcement learning, then come back.
Think of It Like This
The Delivery Driver in a New District
A courier spent a year delivering in one district. She knows the traffic rhythms, the parking tricks, and which apartment blocks have slow elevators. Then she moves to the next district over.
Week one, she is already fast: the driving habits, the parking instincts, and the elevator heuristics carry over. That head start is jumpstart performance. She also reaches her old delivery rate weeks earlier than a rookie would. That is time-to-threshold savings. After a year, she may settle at a slightly higher rating than a rookie ever reaches, because her bag of tricks is bigger. That is the asymptotic gain.
But suppose the new district is a beach town with ferry schedules and sand roads instead of city blocks. Her city tricks now mislead her: she queues at piers that no longer matter and spins wheels in sand. She performs worse than a rookie for a month. That is negative transfer.
Where the analogy stops: a driver notices the beach and adjusts on purpose. A policy has no such awareness. It keeps executing old weights until gradients overwrite them, so a bad initialization can cost more samples than starting fresh.
How It Actually Works
The transfer setup
Call the old task (the source MDP) and the new one (the target MDP). You already hold , a policy trained on the source, possibly with its value function .
Transfer in deep reinforcement learning usually means one of three moves:
- Policy initialization: copy the weights of into the new policy, then keep training on with your usual algorithm. Early layers (perception, locomotion primitives) are often frozen for a while; later layers adapt first.
- Value initialization: copy or so bootstrapping starts from sane estimates instead of zeros. This matters most when rewards are sparse.
- Behavior prior: keep frozen as a guide (for exploration or as a KL penalty) while a fresh policy learns. Use this when you fear the old weights will be overwritten too fast.
Fine-tuning is just continued training on from this head start, usually with a smaller learning rate than the original run.
Three numbers that tell you if transfer helped
Run two agents on : one from scratch, one fine-tuned. Compare their learning curves with three metrics:
- Jumpstart: the initial gap. That is , where is expected return at step of target training. In our worked example below it is .
- Time to threshold and total reward: how many target steps each agent needs to reach a fixed performance bar, plus the area under each curve. Faster crossing with equal final quality is a pure sample saving.
- Asymptotic performance: the final gap once both curves flatten, . Here .
A transfer can win on jumpstart yet lose asymptotically if the initialization traps the policy in a worse local optimum. Report all three, not just the pretty early curve.
Worked example: from scratch versus fine-tuned
Take a grid navigation task. has the goal in the north room; moves it behind a new door but keeps the same corridors. Returns are undiscounted episode scores. Both agents train on for 600k steps, and we checkpoint them at the same step counts:
- From scratch returns: 20 at 0, 110 at 120k, 200 at 250k, 300 at 380k, 400 at 500k, 520 at 600k.
- Fine-tuned returns: 240 at 0, 400 at 120k, 470 at 250k, 520 at 380k, 545 at 500k, 560 at 600k.
Now compute the three metrics by hand:
- Jumpstart: points of free performance before any target training.
- Time to the 400-point bar: scratch needs 500k steps, fine-tuned needs 120k. Steps saved: , which is of the scratch budget. Speedup: x.
- Asymptotic gain at 600k: points.
These are the same numbers drawn in the diagram above and recomputed in the code below.
The generalization gap across MDPs
Single-task transfer asks about one pair. Generalization asks about a spread: train on a distribution of MDPs , then test on held-out MDPs from the same spread. The generalization gap is mean test return minus mean train return. A policy can ace its training seeds yet collapse on new layouts, frictions, or goals.
A common way to shrink that gap is domain randomization: train across randomized physics (friction, mass, latency) and visual conditions so one policy covers the whole spread instead of memorizing one simulator. That technique gets its own page; what matters here is the measurement habit. Always report training return and held-out return separately. In our example, a policy averaging 545 on training mazes but 470 on held-out ones has a gap of points, and no transfer claim is complete without that second number. If your target is a robot, the same logic extends to the simulation-to-hardware step, where unmodeled physics acts as one more held-out MDP.
Code
The snippet below recomputes every number quoted above from the two checkpoint series, so you can check the arithmetic instead of trusting it.
from typing import List
def transfer_metrics( scratch: List[float], finetuned: List[float], scratch_steps_to_threshold: int, finetuned_steps_to_threshold: int, train_return: float, heldout_return: float,) -> dict: """Compute jumpstart, sample savings, asymptotic gain, and generalization gap.""" jumpstart = finetuned[0] - scratch[0] saved = scratch_steps_to_threshold - finetuned_steps_to_threshold pct_saved = 100.0 * saved / scratch_steps_to_threshold speedup = scratch_steps_to_threshold / finetuned_steps_to_threshold asymptotic_gain = finetuned[-1] - scratch[-1] gen_gap = train_return - heldout_return return { "jumpstart": jumpstart, "saved_steps": saved, "pct_saved": pct_saved, "speedup": speedup, "asymptotic_gain": asymptotic_gain, "generalization_gap": gen_gap, }
if __name__ == "__main__": scratch = [20, 110, 200, 300, 400, 520] finetuned = [240, 400, 470, 520, 545, 560] m = transfer_metrics(scratch, finetuned, 500_000, 120_000, 545.0, 470.0) print(f"jumpstart: {m['jumpstart']:.0f}") print(f"saved: {m['saved_steps']:.0f} steps ({m['pct_saved']:.1f}%), speedup: {m['speedup']:.2f}x") print(f"asymptotic gain: {m['asymptotic_gain']:.0f}") print(f"generalization gap: {m['generalization_gap']:.0f}")# -> expected output:jumpstart: 220saved: 380000 steps (76.0%), speedup: 4.17xasymptotic gain: 40generalization gap: 75Watch Out For
Negative transfer: the source policy actively hurts
The symptom: the fine-tuned curve sits below from-scratch for the first few hundred thousand steps, or never catches up. This happens when source and target dynamics disagree, for example a locomotion policy trained with low friction that keeps overshooting in a high-friction target, or a navigation policy whose memorized doorway no longer exists.
The fix: check task similarity before committing. Fine-tune with a short probe run (a tenth of your budget) and compare against scratch at that point. If the probe loses, try freezing less (or nothing), raising the learning rate back toward scratch levels, or mixing source and target experience instead of pure fine-tuning. Do not keep a losing initialization out of sunk-cost loyalty.
Overfitting the source and freezing too much
The symptom: great source return, flat target curve. The policy memorized source quirks (exact textures, exact timing) and frozen early layers prevent it from relearning. A related trap: copying the value function from a dense-reward source into a sparse-reward target, where stale value estimates mislead bootstrapping for a long time.
The fix: unfreeze from the top down and watch held-out return, not source return. Start by training only the final layers, then release earlier ones if held-out return stalls. When rewards differ between tasks, consider reinitializing the value head while keeping the policy trunk. For background on the supervised version of this habit, see the transfer learning definition.
The Quick Version
- Transfer in RL means starting target training from a pretrained policy or value function, then fine-tuning, instead of starting from random weights.
- Judge a transfer with three numbers: jumpstart (here +220), time to a fixed bar (here 120k versus 500k steps, a 380k-step saving at 4.17x), and asymptotic gain (here +40).
- The generalization gap is mean held-out return minus mean training return across an MDP distribution (here 470 versus 545, a 75-point gap), and every transfer claim needs both numbers.
- Negative transfer is real: when source and target dynamics disagree, the old policy can train slower than scratch, so run a short probe before committing the full budget.
- Freezing too much preserves source quirks; unfreeze top-down and reinitialize the value head when reward shapes change.