File size: 4,913 Bytes
9d7a1f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b121006
9d7a1f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1d10707
 
9d7a1f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b121006
9d7a1f4
b121006
 
 
 
 
 
 
 
 
 
9d7a1f4
 
 
 
b121006
 
 
 
 
 
 
 
 
 
9d7a1f4
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
---
license: apache-2.0
language:
- en
library_name: lerobot
pipeline_tag: robotics
tags:
- lerobot
- robotics
- manipulation
- pi0
- tactile
- bimanual
---

# pi05_bi — task2 (dish washing), step 8000

openpi `pi05_bi` checkpoint for a bimanual dish-and-sponge task with tactile
inputs. Step 8000 (1.24 epochs), held-out validation loss
**0.0535** (best through step 12000 is step 12000 at 0.0534).

## Task

Single unified instruction for every episode:

> Use the left hand to pick up the dish, and then use the right hand to pick up
> the sponge to brush the dish. Finally, put all things back.

Only `task2_01` shipped this string; `task2_02` and `task2_03` both carried the
placeholder `"perform manipulation task"` in their `meta/tasks.jsonl`. Since
`prompt_from_task=True` feeds that string straight to the model, all three
sources were forced onto the instruction above at merge time -- otherwise 76% of
the episodes (723 of 948) would have trained against an uninformative prompt.
A trailing space in the original string was stripped.

## Data

| source | episodes | frames |
|---|---:|---:|
| `KaiyueChen/task2_01` | 225 | 222,531 |
| `KaiyueChen/task2_02` | 298 | 291,661 |
| `KaiyueChen/task2_03` | 425 | 402,110 |
| **merged** | **948** | **916,302** |

LeRobot v2.1, 30 fps, `robot_type=bimanual`, images embedded in the parquet files
(`total_videos=0`). Six camera streams: `camera0`, `camera1`, and four tactile
sensors (`tactile_left_0/1`, `tactile_right_0/1`). Mean episode length ~966
frames.

### Split

Episodes are held out **per source repo** (10%, seed 42) so the held-out set keeps
the same source mix as train:

| split | episodes | held out from |
|---|---:|---|
| `train` | 854 | |
| `val_seen` (subset of train) | 94 | |
| `val_unseen` (held out) | 94 | 22 / 30 / 42 from sources 01 / 02 / 03 |

Normalization statistics (quantile q01/q99) are computed over the `train` split
only.

## Training

| | |
|---|---|
| config | `pi05_bi` |
| hardware | 2 x A100-80GB, FSDP |
| batch size | 128 |
| this checkpoint | step 8000 (~1.24 epoch; 1 epoch = 6,444 steps) |
| planned length | 20,000 steps |
| lr | cosine decay, 1,000 warmup steps: peak 2.5e-5 -> 2.5e-6 over 30,000 steps |
| | (`CosineDecaySchedule` defaults -- `pi05_bi` does not override `lr_schedule`; the `peak_lr=2e-4 / decay_steps=100000` block in `config.py` is referenced only by `pi05_single*`) |
| LoRA | rank 16 on the LLM, rank 32 on the action expert |
| vision tower | **fully fine-tuned** -- the freeze filter matches only `.*llm.*` |

## Validation curve

Flow-matching loss, 20 batches per split, evaluated on the same leading batches
each time so successive points are comparable.

| step | train | val_seen | val_unseen | gap |
|---:|---:|---:|---:|---:|
| 0 | 0.6399 | 0.6621 | 0.6508 | -0.0113 |
| 2000 | 0.0535 | 0.0671 | 0.0662 | -0.0009 |
| 4000 | 0.0470 | 0.0574 | 0.0573 | -0.0001 |
| 6000 | 0.0448 | 0.0565 | 0.0566 | +0.0001 |
| **8000** | 0.0430 | 0.0513 | **0.0535** | +0.0022 |
| 10000 | 0.0420 | 0.0514 | 0.0552 | +0.0038 |
| 12000 | 0.0421 | 0.0493 | 0.0534 | +0.0041 |

`val_unseen` fell steeply through step 8000 (0.0535), read 0.0552 at step 10000,
then returned to 0.0534 at step 12000 -- the step-10000 reading was noise, not a
turn. Improvement has nonetheless nearly stopped: the 4,000 steps from 8000 to
12000 bought 0.0001. Over that same span `val_seen` improved 4% (0.0513 ->
0.0493) and the gap widened from 0.0022 to 0.0041.

The gap is still small in absolute terms. The sibling `two_tubes_0102` run had a
gap of 0.0135 at its step 12000 -- more than 3x larger -- and its `val_unseen` had
been flat for 6,000 steps by then. task2 is overfitting far less at the same step
count, which is why training continued past this checkpoint.

The early-step `gap` is slightly **negative** (-0.0009 at step 2000). That is not
evidence of good generalization: each validation pass covers only ~2,560 frames,
which at ~966 frames per episode is about 2-3 episodes per split, so the sign of
the gap early on is dominated by which particular episodes landed in each split.
Only the `val_unseen` absolute trend is reliable, since the same episodes and the
same rng are used at every evaluation.

Note that the training loss is measured on **augmented** images (random crop to
95%, +-5 deg rotation, colour jitter, applied to all six streams including the
four tactile ones) while validation runs with `train=False`, which skips
augmentation. The two numbers are therefore not directly comparable; on this run
`val_seen` still sits above the training loss at every step, unlike the
`two_tubes_0102` run where it dropped below after ~2.5 epochs of memorization.

## Contents

```
checkpoint/
  params/                      # inference weights
  train_state/                 # optimizer state, for resuming
  assets/task2_all/
    norm_stats.json            # computed over the train split only
```