Skip to content

Commit cbb8e9d

Browse files
authored
Add optional W&B logging to Atari DQN and PPO (#126)
* Add optional W&B logging to Atari PPO New --wandb CLI flag (in shared parse_args) initializes a wandb run and logs recent_mean_return, policy_loss, value_loss, and entropy keyed by global_step. Off by default so non-wandb users see no behavior change. wandb added to dependencies via uv. * Gitignore wandb run dir and document --wandb usage Add wandb/ to .gitignore so local run artifacts don't get committed, and add a short Logging section to the README covering 'wandb login' and the --wandb flag. The flag is opt-in and per-user (W&B login is tied to your own account), so contributors who skip it don't need the package at runtime. * Add optional W&B logging to Atari DQN Same --wandb pattern as PPO: logs recent_mean_return, epsilon, last loss, and buffer size every 10k frames into project rl-atari-dqn. Off by default so non-wandb users see no change. README updated to cover both Atari scripts.
1 parent 1ad59be commit cbb8e9d

7 files changed

Lines changed: 347 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ __pycache__
77
*.egg-info/
88
.python-version
99
*.pt
10+
wandb/
1011
./Code 2. Cartpole/6. A3C/Cartpole_A3C.pgy

3-atari/1-dqn.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,25 @@ def greedy_action(obs):
117117
online.load_state_dict(torch.load(SAVE_PATH, map_location=device))
118118
run_test_loop(env, greedy_action)
119119

120+
if args.wandb:
121+
import wandb
122+
wandb.init(project="rl-atari-dqn", config={
123+
"env": args.env, "total_frames": TOTAL_FRAMES,
124+
"buffer_capacity": BUFFER_CAPACITY, "batch_size": BATCH_SIZE,
125+
"gamma": GAMMA, "lr": LR, "learn_start": LEARN_START,
126+
"train_every": TRAIN_EVERY, "target_update_every": TARGET_UPDATE_EVERY,
127+
"epsilon_start": EPSILON_START, "epsilon_end": EPSILON_END,
128+
"epsilon_decay_frames": EPSILON_DECAY_FRAMES,
129+
})
130+
120131
print(f"device: {device}, env: {args.env}, actions: {n_actions}")
121132

122133
buffer = ReplayBuffer(BUFFER_CAPACITY, env.observation_space.shape)
123134
obs, _ = env.reset()
124135
ep_return = 0.0
125136
recent_returns = deque(maxlen=20)
126137
train_step = 0
138+
last_loss = 0.0
127139

128140
for frame in range(1, TOTAL_FRAMES + 1):
129141
quit_if_window_closed(env)
@@ -161,16 +173,25 @@ def greedy_action(obs):
161173
# Gradient clipping — DeepMind uses global norm 10.
162174
nn.utils.clip_grad_norm_(online.parameters(), 10.0)
163175
optimizer.step()
176+
last_loss = loss.item()
164177

165178
train_step += 1
166179
if train_step % TARGET_UPDATE_EVERY == 0:
167180
target.load_state_dict(online.state_dict())
168181

169182
# Logging.
170183
if frame % 10_000 == 0:
171-
mean = np.mean(recent_returns) if recent_returns else 0.0
184+
mean = float(np.mean(recent_returns)) if recent_returns else 0.0
172185
print(f"frame: {frame:>8} eps: {epsilon(frame):.3f} "
173186
f"recent_mean_return: {mean:.1f} buffer: {buffer.size}")
187+
if args.wandb:
188+
wandb.log({
189+
"global_step": frame,
190+
"recent_mean_return": mean,
191+
"epsilon": epsilon(frame),
192+
"loss": last_loss,
193+
"buffer_size": buffer.size,
194+
}, step=frame)
174195

175196
torch.save(online.state_dict(), SAVE_PATH)
176197
print(f"Saved trained model to {SAVE_PATH}")

3-atari/2-ppo.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ def policy_action(obs):
9393
model = ActorCritic(n_actions).to(device)
9494
optimizer = optim.Adam(model.parameters(), lr=LR, eps=1e-5)
9595

96+
if args.wandb:
97+
import wandb
98+
wandb.init(project="rl-atari-ppo", config={
99+
"env": args.env, "n_envs": N_ENVS, "rollout_steps": ROLLOUT_STEPS,
100+
"total_frames": TOTAL_FRAMES, "epochs": EPOCHS,
101+
"minibatch_size": MINIBATCH_SIZE, "clip_coef": CLIP_COEF,
102+
"gamma": GAMMA, "gae_lambda": GAE_LAMBDA, "lr": LR,
103+
"value_coef": VALUE_COEF, "entropy_coef": ENTROPY_COEF,
104+
})
105+
96106
print(f"device: {device}, env: {args.env}, actions: {n_actions}, n_envs: {N_ENVS}")
97107

98108
batch_size = ROLLOUT_STEPS * N_ENVS
@@ -152,6 +162,8 @@ def policy_action(obs):
152162

153163
# --- PPO updates ---
154164
idx = np.arange(batch_size)
165+
pl_sum = vl_sum = ent_sum = 0.0
166+
n_mb = 0
155167
for _ in range(EPOCHS):
156168
np.random.shuffle(idx)
157169
for start in range(0, batch_size, MINIBATCH_SIZE):
@@ -173,10 +185,26 @@ def policy_action(obs):
173185
nn.utils.clip_grad_norm_(model.parameters(), MAX_GRAD_NORM)
174186
optimizer.step()
175187

188+
pl_sum += policy_loss.item()
189+
vl_sum += value_loss.item()
190+
ent_sum += entropy.item()
191+
n_mb += 1
192+
193+
global_step = update * frames_per_update
176194
if ep_returns:
177195
recent = ep_returns[-20:]
178-
print(f"update: {update:>4} frames: {update * frames_per_update:>8} "
196+
print(f"update: {update:>4} frames: {global_step:>8} "
179197
f"recent_mean_return: {np.mean(recent):.1f} episodes: {len(ep_returns)}")
198+
if args.wandb:
199+
log = {
200+
"global_step": global_step,
201+
"policy_loss": pl_sum / n_mb,
202+
"value_loss": vl_sum / n_mb,
203+
"entropy": ent_sum / n_mb,
204+
}
205+
if ep_returns:
206+
log["recent_mean_return"] = float(np.mean(ep_returns[-20:]))
207+
wandb.log(log, step=global_step)
180208

181209
torch.save(model.state_dict(), SAVE_PATH)
182210
print(f"Saved trained model to {SAVE_PATH}")

3-atari/env.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def parse_args():
4545
help="load the saved checkpoint and just play (no learning)")
4646
p.add_argument("--device", choices=["auto", "cpu", "cuda", "mps"], default="auto",
4747
help="override the auto-selected torch device")
48+
p.add_argument("--wandb", action="store_true",
49+
help="log metrics to Weights & Biases")
4850
return p.parse_args()
4951

5052

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ cd 2-cartpole && uv run python 1-dqn.py --render
4545
cd 2-cartpole && uv run python 1-dqn.py --test
4646
```
4747

48+
### Logging to Weights & Biases (Atari only)
49+
50+
Both Atari scripts (`1-dqn.py`, `2-ppo.py`) can stream training metrics to your own [Weights & Biases](https://wandb.ai/) account. One-time login, then pass `--wandb`:
51+
52+
```bash
53+
uv run wandb login # paste the API key from https://wandb.ai/authorize
54+
cd 3-atari && uv run python 2-ppo.py --env breakout --wandb
55+
cd 3-atari && uv run python 1-dqn.py --env breakout --wandb
56+
```
57+
58+
Runs land in *your* `rl-atari-ppo` / `rl-atari-dqn` project — nothing is shared by default. Omit `--wandb` and the script runs without ever touching the network.
59+
4860
## Updates
4961

5062
Modernized from the 2017 original:

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ dependencies = [
1212
"matplotlib>=3.10,<3.11",
1313
"pygame>=2.6,<3",
1414
"opencv-python-headless>=4.13,<4.14",
15+
"wandb>=0.27.0",
1516
]

0 commit comments

Comments
 (0)