Behavioral Cloning
Instead of designing rewards, record an expert and train a policy to copy each move with ordinary supervised learning. It works until one small mistake lands somewhere the expert never visited.
Why Does This Exist?
Writing a reward that produces the behavior you want is brutally hard. A driving reward that pays for speed invites tailgating, one that punishes steering jitter produces a car that never turns, and every patch adds a new exploit. When an expert can already do the task, copying them skips reward design entirely.
Behavioral cloning (Pomerleau, ALVINN, 1989) does exactly that. Log expert pairs , fit a policy with classification or regression loss, and deploy with no environment interaction and no value functions. The broader family is surveyed in imitation learning, and the method that fixes its main wound is DAgger. When demonstrations should instead reveal the hidden reward, the route is inverse RL.
The wound is covariate shift. Supervised learning assumes train and test inputs come from the same distribution. A cloned policy breaks that assumption on its first mistake: its own action selects its next input, so one error moves it onto states the expert never showed it. With no recovery examples in the log, the next prediction is worse, and the trajectory unravels.
Think of It Like This
Tracing a calligraphy master with no correction strokes
A student learns calligraphy by tracing a master's clean strokes. Every training sheet shows perfect form, centered on the page. The student copies beautifully while the brush stays where the master put it.
One day the brush slips a millimeter right. No training sheet shows a stroke starting from that wrong spot, because the master never slips. The student has learned perfect strokes but zero recovery strokes, so the correction overshoots left, the next one overshoots further right, and the character collapses off the margin.
Where the analogy stops: ink shows the error plainly and a teacher can grab the student's hand. A neural policy fails silently on unfamiliar pixel patterns and outputs confident nonsense, which is why the fix must supply the missing recovery labels rather than more perfect demonstrations.
How It Actually Works
Let be a state, the expert's action there, the learner's prediction, and a supervised loss. Training minimizes error under the expert's own state distribution :
Here is mean squared error for continuous control or cross entropy for discrete choices. At deployment the policy instead visits states from its own distribution , which is the mismatch that matters.
Why errors grow quadratically
Suppose the per step mistake rate on expert states is . Early on the learner matches the expert with probability near . But the first slip switches the input distribution from to , where the error bound no longer applies and mistakes become likely. Each step can add a fresh deviation on top of the accumulated drift, so total regret over horizon scales as (Ross and Bagnell, 2010). Interactive fixes that ask the expert about the learner's own states restore linear scaling .
Worked example
A car holds lane center with expert law and update . The log only covers in . The cloned policy carries a bias of .
- Step 0 at : expert says , learner does , next .
- Step 1 at : expert says , learner does , next .
- Step 2 at : learner does , next , the edge of the log.
- Step 3 at : learner does , next , outside everything logged.
- Step 4 at : the network saturates and outputs , next , off the lane.
Cumulative deviation is meters. With DAgger the expert labels the visited with , the car returns near center, and the trajectory totals meters. The diagram draws the straight expert line, the veering learner through these six positions, and the two totals side by side.
Code
The snippet replays both rollouts so the two totals can be checked directly.
def cumulative_deviation(path: list[float]) -> float: """Sum of absolute lateral positions after the start state.""" return round(sum(abs(x) for x in path[1:]), 2)
if __name__ == "__main__": bc_path = [0.00, 0.05, 0.10, 0.20, 0.35, 0.55] dagger_path = [0.00, 0.05, 0.01, 0.00, 0.02, 0.00] bc_total = cumulative_deviation(bc_path) dagger_total = cumulative_deviation(dagger_path) print(bc_path) print(bc_total) print(dagger_path) print(dagger_total)# -> expected output:[0.0, 0.05, 0.1, 0.2, 0.35, 0.55]1.25[0.0, 0.05, 0.01, 0.0, 0.02, 0.0]0.08Watch Out For
Copying dashboard lights instead of the road
The symptom: training loss looks excellent but the deployed policy waits for a consequence of the expert's action rather than its cause. The classic case is a brake light indicator that turns on after the expert brakes: the network learns to brake when the light is on, so at deployment it never brakes first and the light never turns on.
The fix: remove non causal signals from the policy inputs, such as internal indicators, history buffers that leak the expert's past action, or telemetry computed from the label. Then test with interventions: perturb the scene in a simulator and confirm the action follows the true cause, like obstacle distance, rather than the spurious cue.
Fixing drift with more perfect demonstrations
The symptom: the team records twice as many flawless expert runs, validation loss drops, and closed loop driving is unchanged. Every new log covers the same centered states, so the recovery region stays empty.
The fix: log the missing distribution instead. Roll out the current policy, have the expert label the visited off center states, and aggregate those pairs into training, which is the DAgger loop. A small amount of on policy correction data beats a large amount of additional perfect data.
The Quick Version
- Behavioral cloning turns imitation into supervised learning on logged expert state action pairs.
- Deployment breaks the supervised assumption because the learner's actions choose its next inputs.
- Small per step errors compound to total regret over horizon without correction.
- DAgger restores by labeling states the learner actually visits.
- In the worked example, pure cloning drifts to 0.55 for 1.25 meters of error while DAgger corrections hold the total to 0.08.