VLA · Phase 6 · Modern VLA Models
6.2 — Octo
An open generalist robot policy built to be fine-tuned to new robots and tasks.
Octo treats a pretrained policy as a starting point for any robot. This lesson covers its reported design goals and shows three ideas behind them in small code.
Claims about papers, models and textbooks carry numbered references, listed with what each one is cited for in the References. Code marked “Toy model” is our own illustration, not the cited system.
Learning objectives
After this lesson, you should be able to:
- State Octo’s training data and instruction types.
- Explain how one policy can serve robots with different sensors.
- Explain why training only a small part can be enough to adapt.
- Place Octo relative to OpenVLA.
- Pretrain on 800k pooled trajectories
- Specify the goal as language or an image
- Tokens from whatever sensors the robot has
- Fine-tune on the new robot’s demonstrations
- Deploy with new sensors and action space
The one-sentence idea
A pretrained generalist that is meant to be adapted.
Flexible inputs, flexible goals, cheap adaptation.
A policy designed to be adapted
Octo’s goal was not the largest model but a reusable starting point. The Octo team describes a large transformer-based policy trained on 800,000 trajectories from the Open X-Embodiment dataset, which can be instructed with language commands or goal images.[1] The pitch: a useful generalist policy must handle diverse sensors and action spaces, accommodate commonly used robotic platforms, and finetune readily to new domains. The authors report that Octo can be effectively finetuned to robot setups with new sensory inputs and action spaces within a few hours on standard consumer GPUs, tested across 9 robotic platforms.[1] For a guided tour of the paper itself, with its experiments, a re-check of its tables and a critical review, see the Octo paper guide.
That is a different emphasis from OpenVLA (lesson 6.1): Octo takes language or goal images, and puts the weight on adaptation to new robots. Three ideas from that design are worth seeing in code.
Idea 1: one policy, two ways to say what you want
A goal can be a sentence (“put the spoon in the bowl”) or a picture of the finished scene. If both are turned into the same kind of goal vector, the same policy can use either. In the toy, a “goal image” is a picture of where the hand should end up and a “language goal” is a place name; both are mapped to a target position vector before the policy sees them:
import numpy as np
places = {"left shelf": np.array([-0.8, 0.5]), "right shelf": np.array([0.8, 0.5]), "table": np.array([0.0, -0.4])}
def goal_from_language(text):
return places[text] # a (very small) language encoder
def goal_from_image(image):
y, x = np.unravel_index(image.argmax(), image.shape) # a (very small) image encoder: find the bright spot
return np.array([x / 9 * 2 - 1, 1 - y / 9 * 2])
def policy(hand, goal): # the same policy for both
return 0.3 * (goal - hand)
hand = np.array([0.0, 0.0])
image = np.zeros((10, 10)); image[2, 8] = 1.0 # a picture with a bright spot at the top right
print("language goal 'left shelf' -> action", np.round(policy(hand, goal_from_language("left shelf")), 2).tolist())
print("image goal (bright spot top right) -> action", np.round(policy(hand, goal_from_image(image)), 2).tolist())Output:
language goal 'left shelf' -> action [-0.24, 0.15] image goal (bright spot top right) -> action [0.23, 0.17]
Toy model. Two tiny hand-written encoders that map into one goal vector. Real encoders are learned and the goal enters the transformer as tokens.
The point is the shared interface: whatever encodes the goal, the policy only needs the goal vector.
Idea 2: different robots have different sensors
One robot has two cameras, another has one camera and a wrist camera, a third adds touch. A transformer reads a sequence of tokens, so it can accept a different number of tokens per robot, as long as the missing ones are handled. A mask tells attention which tokens are real:
import numpy as np
rng = np.random.default_rng(0)
d = 8
def tokens_for(robot): # each robot provides its own observation tokens
return {"robot A (2 cameras)": 2, "robot B (1 camera + wrist)": 2, "robot C (1 camera)": 1}[robot]
max_tokens = 3
for robot in ("robot A (2 cameras)", "robot C (1 camera)"):
n = tokens_for(robot)
X = np.zeros((max_tokens, d)); X[:n] = rng.normal(size=(n, d)) # real tokens, then padding
mask = np.array([True] * n + [False] * (max_tokens - n))
scores = X @ X.T / np.sqrt(d)
scores[:, ~mask] = -1e9 # nobody attends to padding
w = np.exp(scores - scores.max(axis=1, keepdims=True)); w /= w.sum(axis=1, keepdims=True)
print(robot, "-> attention paid to padding:", float(np.round(w[:, ~mask].sum(), 6)))Output:
robot A (2 cameras) -> attention paid to padding: 0.0 robot C (1 camera) -> attention paid to padding: 0.0
With the mask, padding gets zero attention, so the same network can serve robots that observe different things. This is one way to build the flexibility the Octo authors ask of a generalist policy.
Idea 3: adapt cheaply, keep what was learned
A new robot has a different action space (say 3 numbers instead of 2). Adapting means keeping most of the pretrained network and training a small piece, such as a new output layer, on a few demonstrations of the new robot. In the toy, pretrained “features” compress the observation, and only a new linear output layer is trained:
import numpy as np
rng = np.random.default_rng(0)
d_in, d_feat = 40, 4
true_features = rng.normal(size=(d_in, d_feat)) # the structure that "pretraining" discovered
new_robot_map = rng.normal(size=(d_feat, 3)) # the new robot: 3 action numbers
def demos(n):
X = rng.normal(size=(n, d_in))
return X, (X @ true_features) @ new_robot_map + rng.normal(0, 0.05, (n, 3))
Xt, Yt = demos(3000)
for n in (8, 20, 100):
X, Y = demos(n)
# from scratch: learn 40 -> 3 directly
W_scratch = np.linalg.lstsq(X.T @ X + 1e-3 * np.eye(d_in), X.T @ Y, rcond=None)[0]
# adapt: reuse the pretrained 40 -> 4 features, learn only the new 4 -> 3 output layer
F = X @ true_features
W_head = np.linalg.lstsq(F.T @ F + 1e-3 * np.eye(d_feat), F.T @ Y, rcond=None)[0]
e_s = np.mean((Xt @ W_scratch - Yt) ** 2); e_h = np.mean((Xt @ true_features @ W_head - Yt) ** 2)
print(f"{n:>4} demos: from scratch {e_s:8.4f} | new head on pretrained features {e_h:.4f}")Output:
8 demos: from scratch 104.3997 | new head on pretrained features 0.0041 20 demos: from scratch 57.4402 | new head on pretrained features 0.0036 100 demos: from scratch 0.0043 | new head on pretrained features 0.0027
Toy model. The pretrained features are handed over exactly, which is optimistic. The shape of the result, a few demonstrations suffice when only a small part is learned, is the argument for adaptable policies.
In practice more than the head may be fine-tuned, and the Octo paper reports its own protocol and results; the toy only illustrates the logic.
Where Octo sits
Octo is a generalist policy, trained on robot data; unlike RT-2 and OpenVLA it is not described here as starting from a pretrained vision-language model, so its language understanding is not that of a web-scale model. That is a trade-off worth keeping in mind when you compare models in lesson 6.6.
Common misconceptions
Misconception 1 “Octo is a VLA like OpenVLA.”
Correction It takes language or goal images and focuses on adaptability; it is not described here as built on a web-pretrained vision-language model.
Misconception 2 “A generalist policy needs no fine-tuning.”
Correction Octo is designed to be fine-tuned to new setups.
Misconception 3 “All robots must have the same cameras.”
Correction Token-based inputs with masks allow different sensors.
Engineering takeaways
- 800k Open X-Embodiment trajectories.
- Language or goal images.
- Fine-tunes in hours on consumer GPUs (per the paper).
- Masks handle missing inputs.
Octo: pretrain once, adapt anywhere.
Knowledge check
Try each question first, then reveal the explanation.
Question 1
What two kinds of task specification does Octo accept?
Explanation
Language commands and goal images.
Question 2
Why use a mask for padding tokens?
Explanation
So attention ignores tokens that are not real.
Question 3
Why can a few demonstrations suffice to adapt?
Explanation
Only a small part (such as an output layer) needs to be learned.
References
Numbered notes in the text, like [1], point here. Each entry says what it is cited for and how that was checked. Papers and books can be revised or misread, so follow the links before relying on a detail. Code marked “Toy model” is our own illustration of an idea, not the code of the cited system.
(2024). Octo: An open-source generalist robot policy. RSS 2024 / arXiv. arXiv:2405.12213
Cited for:
- A large transformer-based policy trained on 800k trajectories from the Open X-Embodiment dataset, which can be instructed with language commands or goal images. Checked against the paper’s abstract.
- Can be effectively finetuned to robot setups with new sensory inputs and action spaces within a few hours on standard consumer GPUs; tested across 9 robotic platforms. Checked against the paper’s abstract.
What’s next
Next: π0 and π0.5, which generate continuous actions with flow matching.