Instructions to use easylearning/FutureWorlds with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use easylearning/FutureWorlds with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("easylearning/FutureWorlds", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
Add files using upload-large-folder tool
Browse files- bridge/models/ordinary200/config.json +30 -0
- bridge/models/ordinary200/generation_config.json +6 -0
- bridge/preprocessing/action_ranges.pth +3 -0
- code/THIRD_PARTY_NOTICES.md +9 -0
- code/pyproject.toml +16 -0
- code/src/futureworlds/__init__.py +2 -0
- code/src/futureworlds/batch.py +9 -0
- code/src/futureworlds/checkpoints.py +76 -0
- code/src/futureworlds/decoding.py +81 -0
- code/src/futureworlds/memory.py +41 -0
- code/src/futureworlds/motion_reward.py +72 -0
- code/src/futureworlds/objective.py +203 -0
- code/src/futureworlds/ordinary_search.py +71 -0
- code/src/futureworlds/reward_core.py +98 -0
- code/src/futureworlds/rewards.py +48 -0
- code/src/futureworlds/search.py +72 -0
- code/src/futureworlds/text_conditioning.py +73 -0
- robocasa/README.md +50 -0
- robocasa/codec/config.json +40 -0
- robocasa/codec/diffusion_pytorch_model.safetensors +3 -0
- robocasa/futureworlds_config.json +77 -0
- robocasa/models/grpo200/config.json +30 -0
- robocasa/models/grpo200/generation_config.json +6 -0
- robocasa/models/grpo200/model.safetensors +3 -0
- robocasa/models/grpo200/text_adapters.safetensors +3 -0
- robocasa/models/main/config.json +30 -0
- robocasa/models/main/generation_config.json +6 -0
- robocasa/models/main/model.safetensors +3 -0
- robocasa/models/main/text_adapters.safetensors +3 -0
- robocasa/models/memspo200/config.json +30 -0
- robocasa/models/memspo200/generation_config.json +6 -0
- robocasa/models/memspo200/model.safetensors +3 -0
- robocasa/models/memspo200/text_adapters.safetensors +3 -0
- robocasa/models/ordinary200/config.json +30 -0
- robocasa/models/ordinary200/generation_config.json +6 -0
- robocasa/models/ordinary200/model.safetensors +3 -0
- robocasa/models/ordinary200/text_adapters.safetensors +3 -0
- robocasa/models/sft/config.json +30 -0
- robocasa/models/sft/generation_config.json +6 -0
- robocasa/models/sft/model.safetensors +3 -0
- robocasa/models/sft/text_adapters.safetensors +3 -0
- robocasa/preprocessing/action_ranges.pt +3 -0
- robocasa/training_recipe.json +23 -0
- rt1/models/main/text_adapters.safetensors +3 -0
- rt1/models/sft/text_adapters.safetensors +3 -0
bridge/models/ordinary200/config.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LlamaForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 9006,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 9007,
|
| 10 |
+
"head_dim": 64,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 768,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 3072,
|
| 15 |
+
"max_length": null,
|
| 16 |
+
"max_position_embeddings": 8192,
|
| 17 |
+
"mlp_bias": false,
|
| 18 |
+
"model_type": "llama",
|
| 19 |
+
"num_attention_heads": 12,
|
| 20 |
+
"num_hidden_layers": 12,
|
| 21 |
+
"num_key_value_heads": 12,
|
| 22 |
+
"pretraining_tp": 1,
|
| 23 |
+
"rms_norm_eps": 1e-06,
|
| 24 |
+
"rope_scaling": null,
|
| 25 |
+
"rope_theta": 10000.0,
|
| 26 |
+
"tie_word_embeddings": false,
|
| 27 |
+
"transformers_version": "4.57.1",
|
| 28 |
+
"use_cache": true,
|
| 29 |
+
"vocab_size": 9008
|
| 30 |
+
}
|
bridge/models/ordinary200/generation_config.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"bos_token_id": 9006,
|
| 4 |
+
"eos_token_id": 9007,
|
| 5 |
+
"transformers_version": "4.57.1"
|
| 6 |
+
}
|
bridge/preprocessing/action_ranges.pth
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4492b53758cea8b3d4cb01e01ddf32d9d4bf31095fb3ca768a2d3ff631be8b15
|
| 3 |
+
size 829
|
code/THIRD_PARTY_NOTICES.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Third-party provenance
|
| 2 |
+
|
| 3 |
+
This is a local research-code consolidation, not a public release.
|
| 4 |
+
|
| 5 |
+
- `objective.py` preserves the original ByteDance/Hugging Face Apache-2.0 header from the audited RLVR-World/verl-derived implementation.
|
| 6 |
+
- `reward_core.py` preserves its original upstream header. The audited upstream RLVR-World reference is commit `e1b8b6f40ca0696919ce9b8dbe965c4153bcf5f1`.
|
| 7 |
+
- Tokenizer and backbone loading in the archived trainers rely on iVideoGPT/RLVR-World components, Hugging Face Transformers and PyTorch. Those model assets and datasets are not included here.
|
| 8 |
+
- The local source manifest records file-level provenance. Before public distribution, consolidate the applicable full upstream licenses and choose the license for original additions; no blanket relicensing is implied here.
|
| 9 |
+
|
code/pyproject.toml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=68"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "futureworlds"
|
| 7 |
+
version = "0.1.0.dev0"
|
| 8 |
+
description = "Memory-conditioned search and post-training for robotic world models"
|
| 9 |
+
requires-python = ">=3.9"
|
| 10 |
+
dependencies = ["numpy", "torch>=2.1", "transformers>=4.45,<5", "safetensors"]
|
| 11 |
+
|
| 12 |
+
[project.optional-dependencies]
|
| 13 |
+
hub = ["huggingface_hub>=0.34"]
|
| 14 |
+
|
| 15 |
+
[tool.setuptools.packages.find]
|
| 16 |
+
where = ["src"]
|
code/src/futureworlds/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FutureWorlds research core. Training entry points are being consolidated."""
|
| 2 |
+
__version__ = "0.1.0.dev0"
|
code/src/futureworlds/batch.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Small tensor container for the upstream video reward's batch-only API."""
|
| 2 |
+
class TensorBatch:
|
| 3 |
+
def __init__(self,batch,meta_info=None):
|
| 4 |
+
self.batch=batch
|
| 5 |
+
self.meta_info=meta_info or {}
|
| 6 |
+
@classmethod
|
| 7 |
+
def from_single_dict(cls,batch,meta_info=None):return cls(batch,meta_info)
|
| 8 |
+
def __len__(self):return len(next(iter(self.batch.values())))
|
| 9 |
+
def __getitem__(self,index):return type(self)({k:v[index] for k,v in self.batch.items()},self.meta_info)
|
code/src/futureworlds/checkpoints.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Load an explicit released world-model variant; never substitute adapters."""
|
| 2 |
+
import hashlib
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path, PurePosixPath
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def safe_path(root, relative):
|
| 8 |
+
rel = PurePosixPath(relative)
|
| 9 |
+
if rel.is_absolute() or not rel.parts or '..' in rel.parts or '\\' in relative:
|
| 10 |
+
raise ValueError(f'Invalid relative path: {relative!r}')
|
| 11 |
+
root = Path(root).resolve()
|
| 12 |
+
path = (root / relative).resolve()
|
| 13 |
+
if path != root and root not in path.parents:
|
| 14 |
+
raise ValueError('Path escapes bundle root')
|
| 15 |
+
return path
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def file_sha256(path):
|
| 19 |
+
value = hashlib.sha256()
|
| 20 |
+
with Path(path).open('rb') as stream:
|
| 21 |
+
for block in iter(lambda: stream.read(8 * 1024 * 1024), b''):
|
| 22 |
+
value.update(block)
|
| 23 |
+
return value.hexdigest()
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def verify_bundle(root):
|
| 27 |
+
root = Path(root)
|
| 28 |
+
manifest = json.loads((root / 'manifest.json').read_text())
|
| 29 |
+
if manifest.get('schema_version') != 1 or not manifest.get('files'):
|
| 30 |
+
raise ValueError('Unsupported or empty manifest')
|
| 31 |
+
seen = set()
|
| 32 |
+
for item in manifest['files']:
|
| 33 |
+
name = item['path']
|
| 34 |
+
if name in seen:
|
| 35 |
+
raise ValueError(f'Duplicate file: {name}')
|
| 36 |
+
seen.add(name)
|
| 37 |
+
path = safe_path(root, name)
|
| 38 |
+
if not path.is_file() or path.stat().st_size != item['bytes']:
|
| 39 |
+
raise ValueError(f'Missing file or wrong size: {name}')
|
| 40 |
+
if file_sha256(path) != item['sha256']:
|
| 41 |
+
raise ValueError(f'Checksum mismatch: {name}')
|
| 42 |
+
return manifest
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def load_world_model(root, dataset, variant='main', device='cpu', verify=True):
|
| 46 |
+
"""Load the predictor only. Codec/actions/T5 preprocessing are separate.
|
| 47 |
+
|
| 48 |
+
FP32 and SDPA match the audited loaders. Dataset/variant selection is
|
| 49 |
+
explicit; missing text adapters are an error, never silently zero-filled.
|
| 50 |
+
"""
|
| 51 |
+
import torch
|
| 52 |
+
from safetensors.torch import load_file
|
| 53 |
+
from transformers import AutoConfig, AutoModelForCausalLM
|
| 54 |
+
from .text_conditioning import TextWorldModel
|
| 55 |
+
|
| 56 |
+
root = Path(root)
|
| 57 |
+
if verify:
|
| 58 |
+
verify_bundle(root)
|
| 59 |
+
settings = json.loads(safe_path(root, f'{dataset}/futureworlds_config.json').read_text())
|
| 60 |
+
if settings['dataset'] != dataset or variant not in settings['variants']:
|
| 61 |
+
raise ValueError('Dataset or variant does not match bundle configuration')
|
| 62 |
+
entry = settings['variants'][variant]
|
| 63 |
+
folder = safe_path(root, entry['path'])
|
| 64 |
+
cfg = AutoConfig.from_pretrained(folder, local_files_only=True, trust_remote_code=False)
|
| 65 |
+
core = AutoModelForCausalLM.from_config(cfg, torch_dtype=torch.float32, attn_implementation='sdpa', trust_remote_code=False)
|
| 66 |
+
core.load_state_dict(load_file(str(folder / 'model.safetensors')), strict=True)
|
| 67 |
+
if settings['text_conditioning']['enabled']:
|
| 68 |
+
tc = settings['text_conditioning']
|
| 69 |
+
model = TextWorldModel(core, text_width=tc['width'], layers=tc['layers'], inner=tc['inner'])
|
| 70 |
+
adapters = folder / 'text_adapters.safetensors'
|
| 71 |
+
if not adapters.is_file():
|
| 72 |
+
raise FileNotFoundError(f'Required trained text adapters missing: {adapters}')
|
| 73 |
+
model.adapters.load_state_dict(load_file(str(adapters)), strict=True)
|
| 74 |
+
else:
|
| 75 |
+
model = core
|
| 76 |
+
return model.to(device).eval()
|
code/src/futureworlds/decoding.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Fixed-length, constrained HF beam search and independent numerical checks."""
|
| 2 |
+
import torch
|
| 3 |
+
from transformers import GenerationConfig, LogitsProcessor, LogitsProcessorList
|
| 4 |
+
|
| 5 |
+
class VisualTokensOnly(LogitsProcessor):
|
| 6 |
+
def __init__(self, valid_size):
|
| 7 |
+
self.valid_size = valid_size
|
| 8 |
+
|
| 9 |
+
def __call__(self, input_ids, scores):
|
| 10 |
+
scores = scores.clone()
|
| 11 |
+
scores[:, self.valid_size:] = -torch.inf
|
| 12 |
+
return scores
|
| 13 |
+
|
| 14 |
+
@torch.inference_mode()
|
| 15 |
+
def beam_decode(model, prefix, width=4, steps=80, valid_size=4375):
|
| 16 |
+
config = GenerationConfig(
|
| 17 |
+
do_sample=False, num_beams=width, num_return_sequences=width,
|
| 18 |
+
max_new_tokens=steps, min_new_tokens=steps,
|
| 19 |
+
eos_token_id=None, bos_token_id=None, pad_token_id=0,
|
| 20 |
+
length_penalty=0.0 if width > 1 else 1.0, early_stopping=False, renormalize_logits=True,
|
| 21 |
+
repetition_penalty=1.0, no_repeat_ngram_size=0,
|
| 22 |
+
use_cache=True, return_dict_in_generate=True, output_scores=True,
|
| 23 |
+
)
|
| 24 |
+
result = model.generate(
|
| 25 |
+
input_ids=prefix, attention_mask=torch.ones_like(prefix),
|
| 26 |
+
generation_config=config,
|
| 27 |
+
logits_processor=LogitsProcessorList([VisualTokensOnly(valid_size)]),
|
| 28 |
+
eos_token_id=None, bos_token_id=None,
|
| 29 |
+
forced_bos_token_id=None, forced_eos_token_id=None,
|
| 30 |
+
)
|
| 31 |
+
candidates = result.sequences[:, prefix.shape[1]:]
|
| 32 |
+
assert candidates.shape == (width,steps)
|
| 33 |
+
assert int(candidates.min()) >= 0 and int(candidates.max()) < valid_size
|
| 34 |
+
assert len({tuple(x) for x in candidates.tolist()}) == width
|
| 35 |
+
transitions = model.compute_transition_scores(
|
| 36 |
+
result.sequences, result.scores, getattr(result,'beam_indices',None),
|
| 37 |
+
normalize_logits=False,
|
| 38 |
+
)
|
| 39 |
+
assert transitions.shape == (width,steps) and torch.isfinite(transitions).all()
|
| 40 |
+
sums = transitions.sum(-1)
|
| 41 |
+
scores = result.sequences_scores if width > 1 else sums
|
| 42 |
+
error = float((sums-scores).abs().max())
|
| 43 |
+
assert torch.allclose(sums,scores,rtol=0,atol=2e-4), error
|
| 44 |
+
assert torch.all(scores[:-1] >= scores[1:])
|
| 45 |
+
return candidates, scores, {'beam_width':width,'tokens_per_frame':steps,
|
| 46 |
+
'selected_index':0,'candidate_scores':scores.tolist(),
|
| 47 |
+
'score_reconstruction_max_error':error,
|
| 48 |
+
'score_definition':'Sum of log probabilities renormalized over valid visual tokens only; no length penalty or GT/image reward.'}
|
| 49 |
+
|
| 50 |
+
@torch.inference_mode()
|
| 51 |
+
def independent_check():
|
| 52 |
+
from transformers import LlamaConfig, LlamaForCausalLM
|
| 53 |
+
torch.manual_seed(3719)
|
| 54 |
+
model = LlamaForCausalLM(LlamaConfig(vocab_size=8,hidden_size=32,
|
| 55 |
+
intermediate_size=48,num_hidden_layers=2,num_attention_heads=4,
|
| 56 |
+
num_key_value_heads=4,max_position_embeddings=128,
|
| 57 |
+
eos_token_id=2,bos_token_id=1,pad_token_id=0,
|
| 58 |
+
attention_dropout=0.0)).float().eval()
|
| 59 |
+
prefix = torch.tensor([[6,1,3]],dtype=torch.long)
|
| 60 |
+
reports=[]
|
| 61 |
+
for width in [1,4]:
|
| 62 |
+
beams=[([],0.0)]
|
| 63 |
+
for _ in range(4):
|
| 64 |
+
expanded=[]
|
| 65 |
+
for seq,score in beams:
|
| 66 |
+
x=torch.tensor([prefix[0].tolist()+seq],dtype=torch.long)
|
| 67 |
+
probs=model(input_ids=x,use_cache=False).logits[0,-1,:5].log_softmax(-1)
|
| 68 |
+
expanded.extend((seq+[token],score+float(probs[token])) for token in range(5))
|
| 69 |
+
beams=sorted(expanded,key=lambda item:item[1],reverse=True)[:width]
|
| 70 |
+
ids,scores,meta=beam_decode(model,prefix,width=width,steps=4,valid_size=5)
|
| 71 |
+
expected=torch.tensor([seq for seq,_ in beams],dtype=torch.long)
|
| 72 |
+
expected_scores=torch.tensor([score for _,score in beams])
|
| 73 |
+
assert torch.equal(ids,expected),(width,ids,expected)
|
| 74 |
+
assert torch.allclose(scores,expected_scores,rtol=0,atol=2e-5)
|
| 75 |
+
reports.append({'width':width,'matches_independent_uncached_search':True,
|
| 76 |
+
'max_score_error':float((scores-expected_scores).abs().max())})
|
| 77 |
+
return {'tests':reports,'eos_disabled':True,'valid_token_constraint_checked':True,'complete':True}
|
| 78 |
+
|
| 79 |
+
if __name__ == '__main__':
|
| 80 |
+
import json, transformers
|
| 81 |
+
print(json.dumps({'transformers':transformers.__version__,**independent_check()}))
|
code/src/futureworlds/memory.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Causal ReCAP prompts shared by rollout and every policy log-probability pass."""
|
| 2 |
+
import torch
|
| 3 |
+
|
| 4 |
+
def retained(target,recent=6):
|
| 5 |
+
assert target>=2 and recent>=1
|
| 6 |
+
return [1]+list(range(max(2,target-recent),target))
|
| 7 |
+
|
| 8 |
+
def make_prefix(context,history,actions,target,recent=6):
|
| 9 |
+
kept=retained(target,recent)
|
| 10 |
+
assert max(kept)==target-1 and len(kept)==len(set(kept))
|
| 11 |
+
blocks=[context]
|
| 12 |
+
for t in kept:blocks += [history[t],actions[t].expand(context.shape[0],-1)]
|
| 13 |
+
return torch.cat(blocks,dim=1),kept
|
| 14 |
+
|
| 15 |
+
@torch.no_grad()
|
| 16 |
+
def rollout(model,context,anchor,actions,horizon,group,generator,visual=4375,width=80,temperature=1.0):
|
| 17 |
+
model.eval()
|
| 18 |
+
context=context.expand(group,-1);history={1:anchor.expand(group,-1)}
|
| 19 |
+
frames=[];traces=[]
|
| 20 |
+
for target in range(2,horizon+2):
|
| 21 |
+
prefix,kept=make_prefix(context,history,actions,target)
|
| 22 |
+
pending=prefix;kv=None;codes=[];lps=[]
|
| 23 |
+
for token in range(width):
|
| 24 |
+
out=model(input_ids=pending,past_key_values=kv,use_cache=True)
|
| 25 |
+
kv=out.past_key_values
|
| 26 |
+
logp=torch.log_softmax(out.logits[:,-1,:visual].float()/temperature,-1)
|
| 27 |
+
pending=torch.multinomial(logp.exp(),1,generator=generator)
|
| 28 |
+
codes.append(pending);lps.append(logp.gather(1,pending).squeeze(1))
|
| 29 |
+
response=torch.cat(codes,1)
|
| 30 |
+
frames.append({'prefix':prefix.detach(),'response':response.detach(),'sample_logp':torch.stack(lps,1).detach(),'target':target})
|
| 31 |
+
history[target]=response
|
| 32 |
+
traces.append({'target':target,'retained':kept,'prefix_length':prefix.shape[1],'positions_reset':True,'kv_reset':True,'future_observations':False})
|
| 33 |
+
return frames,traces
|
| 34 |
+
|
| 35 |
+
def frame_logp(model,prefix,response,visual=4375,temperature=1.0):
|
| 36 |
+
# Each target has its own rebased prefix. No discarded history is hidden in a cache.
|
| 37 |
+
ids=torch.cat([prefix,response[:,:-1]],1)
|
| 38 |
+
out=model(input_ids=ids,use_cache=False)
|
| 39 |
+
p=prefix.shape[1]
|
| 40 |
+
logits=out.logits[:,p-1:p+response.shape[1]-1,:visual].float()/temperature
|
| 41 |
+
return torch.log_softmax(logits,-1).gather(-1,response.unsqueeze(-1)).squeeze(-1)
|
code/src/futureworlds/motion_reward.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""GT motion-proxy ROI and GT-referenced dynamics. No actor/search sees future GT."""
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from .rewards import CodecWorker
|
| 5 |
+
from .batch import TensorBatch
|
| 6 |
+
|
| 7 |
+
@torch.no_grad()
|
| 8 |
+
def motion_regions(truth,previous):
|
| 9 |
+
"""Shared across candidates: GT-derived, never shrinks after predicted disappearance."""
|
| 10 |
+
prev=torch.cat([previous[None],truth[:-1]],0)
|
| 11 |
+
motion=(truth-prev).abs().mean(1,keepdim=True)
|
| 12 |
+
thresholds=motion.flatten(1).quantile(.90,dim=1).clamp_min(.02)
|
| 13 |
+
masks=(motion>thresholds[:,None,None,None]).float()
|
| 14 |
+
masks=F.max_pool2d(masks,11,stride=1,padding=5)
|
| 15 |
+
fallback=masks.mean((1,2,3))<.01
|
| 16 |
+
masks[fallback]=1
|
| 17 |
+
crops=[];height,width=truth.shape[-2:]
|
| 18 |
+
for mask in masks:
|
| 19 |
+
y,x=torch.where(mask[0]>0)
|
| 20 |
+
y0,y1=int(y.min()),int(y.max())+1;x0,x1=int(x.min()),int(x.max())+1
|
| 21 |
+
cy=(y0+y1)//2;cx=(x0+x1)//2
|
| 22 |
+
h=max(64,y1-y0);w=max(64,x1-x0)
|
| 23 |
+
y0=max(0,min(cy-h//2,height-h));x0=max(0,min(cx-w//2,width-w))
|
| 24 |
+
crops.append((y0,min(height,y0+h),x0,min(width,x0+w)))
|
| 25 |
+
return masks,crops,fallback
|
| 26 |
+
|
| 27 |
+
class MotionReward:
|
| 28 |
+
def __init__(self,codec,perceptual,scales=None,alpha=.5,beta=.25):
|
| 29 |
+
self.worker=CodecWorker(codec,perceptual);self.perceptual=perceptual
|
| 30 |
+
self.scales=scales or dict(global_scale=1.,roi_scale=1.,delta_scale=1.)
|
| 31 |
+
self.alpha=alpha;self.beta=beta
|
| 32 |
+
|
| 33 |
+
@torch.no_grad()
|
| 34 |
+
def components(self,pred,pixels):
|
| 35 |
+
truth=pixels[0,2:];previous=pixels[0,1]
|
| 36 |
+
n,h=pred.shape[:2];real=truth[None].expand(n,-1,-1,-1,-1)
|
| 37 |
+
mae=(pred-real).abs().mean((2,3,4))
|
| 38 |
+
percept=self.worker.perceptual_loss(TensorBatch(dict(real=real,pred=pred))).batch['perceptual_loss']
|
| 39 |
+
masks,crops,fallback=motion_regions(truth,previous)
|
| 40 |
+
roi_mae=((pred-real).abs()*masks).sum((2,3,4))/(3*masks.sum((1,2,3)))[None]
|
| 41 |
+
roi_percept=torch.empty((n,h),device=pred.device)
|
| 42 |
+
for t,(y0,y1,x0,x1) in enumerate(crops):
|
| 43 |
+
r=truth[t:t+1,:,y0:y1,x0:x1]
|
| 44 |
+
for start in range(0,n,2):
|
| 45 |
+
pp=pred[start:start+2,t,:,y0:y1,x0:x1]
|
| 46 |
+
roi_percept[start:start+len(pp),t]=self.perceptual(pp*2-1,r.expand(len(pp),-1,-1,-1)*2-1).flatten()
|
| 47 |
+
pred_prev=torch.cat([previous[None,None].expand(n,1,-1,-1,-1),pred[:,:-1]],1)
|
| 48 |
+
true_prev=torch.cat([previous[None],truth[:-1]],0)
|
| 49 |
+
delta=((pred-pred_prev)-(truth-true_prev)[None]).abs().mean((2,3,4))
|
| 50 |
+
return dict(mae=mae,lpips=percept,global_loss=mae+percept,
|
| 51 |
+
roi_loss=roi_mae+roi_percept,roi_mae=roi_mae,roi_lpips=roi_percept,
|
| 52 |
+
delta_loss=delta,roi_fallback=fallback,roi_area=masks.mean((1,2,3)))
|
| 53 |
+
|
| 54 |
+
def combine(self,c):
|
| 55 |
+
s=self.scales
|
| 56 |
+
return -(c['global_loss']+self.alpha*s['global_scale']/s['roi_scale']*c['roi_loss']+
|
| 57 |
+
self.beta*s['global_scale']/s['delta_scale']*c['delta_loss']).mean(-1)
|
| 58 |
+
|
| 59 |
+
@torch.no_grad()
|
| 60 |
+
def decode(self,frames,context):
|
| 61 |
+
codes=torch.stack([f['response'] for f in frames],1)
|
| 62 |
+
assert codes.min()>=0 and codes.max()<4375
|
| 63 |
+
return self.worker.detokenize(TensorBatch(dict(tokens=codes,ctx_tokens=context.expand(len(codes),-1,-1))),None).batch['pixels'][:,1:]
|
| 64 |
+
|
| 65 |
+
@torch.no_grad()
|
| 66 |
+
def __call__(self,frames,context,actions,pixels):
|
| 67 |
+
c=self.components(self.decode(frames,context),pixels)
|
| 68 |
+
reward=self.combine(c)
|
| 69 |
+
assert torch.isfinite(reward).all()
|
| 70 |
+
metrics={'critic/recon_loss/mean':float(c['mae'].mean()),'critic/perceptual_loss/mean':float(c['lpips'].mean())}
|
| 71 |
+
metrics.update({k:float(v.float().mean()) for k,v in c.items()})
|
| 72 |
+
return reward,metrics
|
code/src/futureworlds/objective.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 Bytedance Ltd. and/or its affiliates
|
| 2 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
import numpy as np
|
| 18 |
+
from collections import defaultdict
|
| 19 |
+
from types import SimpleNamespace
|
| 20 |
+
|
| 21 |
+
def masked_mean(values, mask, axis=None):
|
| 22 |
+
"""Compute mean of tensor with a masked values."""
|
| 23 |
+
return (values * mask).sum(axis=axis) / (mask.sum(axis=axis) + 1e-8)
|
| 24 |
+
|
| 25 |
+
verl_F=SimpleNamespace(masked_mean=masked_mean)
|
| 26 |
+
|
| 27 |
+
def compute_grpo_outcome_advantage(token_level_rewards: torch.Tensor,
|
| 28 |
+
response_mask: torch.Tensor,
|
| 29 |
+
index: np.ndarray,
|
| 30 |
+
epsilon: float = 1e-6,
|
| 31 |
+
uniform_std=False):
|
| 32 |
+
"""
|
| 33 |
+
Compute advantage for GRPO, operating only on Outcome reward
|
| 34 |
+
(with only one scalar reward for each response).
|
| 35 |
+
Args:
|
| 36 |
+
token_level_rewards: `(torch.Tensor)`
|
| 37 |
+
shape: (bs, response_length)
|
| 38 |
+
response_mask: `(torch.Tensor)`
|
| 39 |
+
shape: (bs, response_length)
|
| 40 |
+
|
| 41 |
+
Returns:
|
| 42 |
+
advantages: `(torch.Tensor)`
|
| 43 |
+
shape: (bs, response_length)
|
| 44 |
+
Returns: `(torch.Tensor)`
|
| 45 |
+
shape: (bs, response_length)
|
| 46 |
+
"""
|
| 47 |
+
scores = token_level_rewards.sum(dim=-1)
|
| 48 |
+
|
| 49 |
+
id2score = defaultdict(list)
|
| 50 |
+
id2mean = {}
|
| 51 |
+
id2std = {}
|
| 52 |
+
|
| 53 |
+
with torch.no_grad():
|
| 54 |
+
bsz = scores.shape[0]
|
| 55 |
+
for i in range(bsz):
|
| 56 |
+
id2score[index[i]].append(scores[i])
|
| 57 |
+
for idx in id2score:
|
| 58 |
+
if len(id2score[idx]) == 1:
|
| 59 |
+
id2mean[idx] = torch.tensor(0.0)
|
| 60 |
+
id2std[idx] = torch.tensor(1.0)
|
| 61 |
+
elif len(id2score[idx]) > 1:
|
| 62 |
+
id2mean[idx] = torch.mean(torch.tensor(id2score[idx]))
|
| 63 |
+
id2std[idx] = torch.std(torch.tensor([id2score[idx]]))
|
| 64 |
+
else:
|
| 65 |
+
raise ValueError(f"no score in prompt index: {idx}")
|
| 66 |
+
if uniform_std:
|
| 67 |
+
std = torch.tensor(list(id2std.values())).mean()
|
| 68 |
+
for i in range(bsz):
|
| 69 |
+
scores[i] = (scores[i] - id2mean[index[i]]) / (std + epsilon)
|
| 70 |
+
else:
|
| 71 |
+
for i in range(bsz):
|
| 72 |
+
scores[i] = (scores[i] - id2mean[index[i]]) / (id2std[index[i]] + epsilon)
|
| 73 |
+
scores = scores.unsqueeze(-1) * response_mask
|
| 74 |
+
|
| 75 |
+
return scores, scores
|
| 76 |
+
|
| 77 |
+
def agg_loss(loss_mat: torch.Tensor, loss_mask: torch.Tensor, loss_agg_mode: str):
|
| 78 |
+
"""
|
| 79 |
+
Aggregate the loss matrix into a scalar.
|
| 80 |
+
Args:
|
| 81 |
+
loss_mat: `(torch.Tensor)`
|
| 82 |
+
shape: (bs, response_length)
|
| 83 |
+
loss_mask: `(torch.Tensor)`
|
| 84 |
+
shape: (bs, response_length)
|
| 85 |
+
loss_agg_mode: (str) choices: "token-mean" / "seq-mean-token-sum" / "seq-mean-token-mean"
|
| 86 |
+
"token-mean" is the default behavior
|
| 87 |
+
Returns:
|
| 88 |
+
loss: `a scalar torch.Tensor`
|
| 89 |
+
aggregated loss
|
| 90 |
+
"""
|
| 91 |
+
if loss_agg_mode == "token-mean":
|
| 92 |
+
loss = verl_F.masked_mean(loss_mat, loss_mask)
|
| 93 |
+
elif loss_agg_mode == "seq-mean-token-sum":
|
| 94 |
+
seq_losses = torch.sum(loss_mat * loss_mask, dim=-1) # token-sum
|
| 95 |
+
loss = torch.mean(seq_losses) # seq-mean
|
| 96 |
+
elif loss_agg_mode == "seq-mean-token-mean":
|
| 97 |
+
seq_losses = torch.sum(loss_mat * loss_mask, dim=-1) / torch.sum(loss_mask, dim=-1) # token-mean
|
| 98 |
+
loss = torch.mean(seq_losses) # seq-mean
|
| 99 |
+
else:
|
| 100 |
+
raise ValueError(f"Invalid loss_agg_mode: {loss_agg_mode}")
|
| 101 |
+
|
| 102 |
+
return loss
|
| 103 |
+
|
| 104 |
+
def compute_policy_loss(old_log_prob,
|
| 105 |
+
log_prob,
|
| 106 |
+
advantages,
|
| 107 |
+
response_mask,
|
| 108 |
+
cliprange=None,
|
| 109 |
+
cliprange_low=None,
|
| 110 |
+
cliprange_high=None,
|
| 111 |
+
clip_ratio_c=3.0,
|
| 112 |
+
loss_agg_mode="token-mean"):
|
| 113 |
+
"""Adapted from https://github.com/huggingface/trl/blob/main/trl/trainer/ppo_trainer.py#L1122
|
| 114 |
+
Args:
|
| 115 |
+
old_log_prob: `(torch.Tensor)`
|
| 116 |
+
shape: (bs, response_length)
|
| 117 |
+
log_prob: `(torch.Tensor)`
|
| 118 |
+
shape: (bs, response_length)
|
| 119 |
+
advantages: `(torch.Tensor)`
|
| 120 |
+
shape: (bs, response_length)
|
| 121 |
+
response_mask: `(torch.Tensor)`
|
| 122 |
+
shape: (bs, response_length)
|
| 123 |
+
cliprange: (float)
|
| 124 |
+
The clip range used in PPO. See https://arxiv.org/abs/1707.06347
|
| 125 |
+
cliprange_low: (float)
|
| 126 |
+
The lower clip range used in PPO.
|
| 127 |
+
cliprange_high: (float)
|
| 128 |
+
The higher clip range used in PPO.
|
| 129 |
+
clip_ratio_c: (float) default: 3.0
|
| 130 |
+
The lower bound of the ratio for dual-clip PPO, See https://arxiv.org/pdf/1912.09729
|
| 131 |
+
loss_agg_mode: (str) choices: "token-mean" / "seq-mean-token-sum" / "seq-mean-token-mean"
|
| 132 |
+
"token-mean" is the default behavior
|
| 133 |
+
|
| 134 |
+
Returns:
|
| 135 |
+
pg_loss: `a scalar torch.Tensor`
|
| 136 |
+
policy gradient loss computed via PPO
|
| 137 |
+
pg_clipfrac: (float)
|
| 138 |
+
the fraction of policy gradient loss being clipped
|
| 139 |
+
ppo_kl: (float)
|
| 140 |
+
the estimated KL divergence between the latest updating policy and the old sampling policy
|
| 141 |
+
pg_clipfrac_lower: (float)
|
| 142 |
+
the fraction of policy gradient loss being clipped when the advantage is negative
|
| 143 |
+
"""
|
| 144 |
+
assert clip_ratio_c > 1.0, f"The lower bound of the clip_ratio_c for dual-clip PPO should be greater than 1.0, but get the value: {clip_ratio_c}."
|
| 145 |
+
|
| 146 |
+
negative_approx_kl = log_prob - old_log_prob
|
| 147 |
+
ratio = torch.exp(negative_approx_kl)
|
| 148 |
+
ppo_kl = verl_F.masked_mean(-negative_approx_kl, response_mask)
|
| 149 |
+
|
| 150 |
+
pg_losses1 = -advantages * ratio
|
| 151 |
+
if cliprange_low is None:
|
| 152 |
+
cliprange_low = cliprange
|
| 153 |
+
if cliprange_high is None:
|
| 154 |
+
cliprange_high = cliprange
|
| 155 |
+
pg_losses2 = -advantages * torch.clamp(ratio, 1 - cliprange_low,
|
| 156 |
+
1 + cliprange_high) # - clip(ratio, 1-cliprange, 1+cliprange) * A
|
| 157 |
+
clip_pg_losses1 = torch.maximum(pg_losses1,
|
| 158 |
+
pg_losses2) # max(-ratio * A, -clip(ratio, 1-cliprange, 1+cliprange) * A)
|
| 159 |
+
pg_clipfrac = verl_F.masked_mean(torch.gt(pg_losses2, pg_losses1).float(), response_mask)
|
| 160 |
+
|
| 161 |
+
pg_losses3 = -advantages * clip_ratio_c
|
| 162 |
+
clip_pg_losses2 = torch.min(pg_losses3, clip_pg_losses1)
|
| 163 |
+
pg_clipfrac_lower = verl_F.masked_mean(
|
| 164 |
+
torch.gt(clip_pg_losses2, pg_losses3) * (advantages < 0).float(), response_mask)
|
| 165 |
+
|
| 166 |
+
pg_losses = torch.where(advantages < 0, clip_pg_losses2, clip_pg_losses1)
|
| 167 |
+
pg_loss = agg_loss(loss_mat=pg_losses, loss_mask=response_mask, loss_agg_mode=loss_agg_mode)
|
| 168 |
+
|
| 169 |
+
return pg_loss, pg_clipfrac, ppo_kl, pg_clipfrac_lower
|
| 170 |
+
|
| 171 |
+
def kl_penalty(logprob: torch.FloatTensor, ref_logprob: torch.FloatTensor, kl_penalty) -> torch.FloatTensor:
|
| 172 |
+
"""Compute KL divergence given logprob and ref_logprob.
|
| 173 |
+
Copied from https://github.com/huggingface/trl/blob/main/trl/trainer/ppo_trainer.py#L1104
|
| 174 |
+
|
| 175 |
+
Args:
|
| 176 |
+
logprob:
|
| 177 |
+
ref_logprob:
|
| 178 |
+
|
| 179 |
+
Returns:
|
| 180 |
+
|
| 181 |
+
"""
|
| 182 |
+
if kl_penalty == "kl":
|
| 183 |
+
return logprob - ref_logprob
|
| 184 |
+
|
| 185 |
+
if kl_penalty == "abs":
|
| 186 |
+
return (logprob - ref_logprob).abs()
|
| 187 |
+
|
| 188 |
+
if kl_penalty == "mse":
|
| 189 |
+
return 0.5 * (logprob - ref_logprob).square()
|
| 190 |
+
|
| 191 |
+
# J. Schulman. Approximating kl divergence, 2020.
|
| 192 |
+
# # URL http://joschu.net/blog/kl-approx.html.
|
| 193 |
+
if kl_penalty == 'low_var_kl':
|
| 194 |
+
kl = ref_logprob - logprob
|
| 195 |
+
ratio = torch.exp(kl)
|
| 196 |
+
kld = (ratio - kl - 1).contiguous()
|
| 197 |
+
return torch.clamp(kld, min=-10, max=10)
|
| 198 |
+
|
| 199 |
+
if kl_penalty == "full":
|
| 200 |
+
# so, here logprob and ref_logprob should contain the logits for every token in vocabulary
|
| 201 |
+
raise NotImplementedError
|
| 202 |
+
|
| 203 |
+
raise NotImplementedError
|
code/src/futureworlds/ordinary_search.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Global beam width 8; return top 4 by raw cumulative model score, no diversity penalty."""
|
| 2 |
+
import torch
|
| 3 |
+
from .memory import make_prefix
|
| 4 |
+
|
| 5 |
+
@torch.no_grad()
|
| 6 |
+
def rollout_ordinary_beam(model, context, anchor, actions, horizon, penalty,
|
| 7 |
+
groups=4, beams=2, visual=4375, width=80):
|
| 8 |
+
assert groups == 4 and beams == 2
|
| 9 |
+
groups, beams, penalty = 1, 8, 0.0
|
| 10 |
+
returned = 4
|
| 11 |
+
model.eval()
|
| 12 |
+
device=context.device; count=groups*beams
|
| 13 |
+
history={1:anchor.expand(count,-1)}; frames=[]; traces=[]
|
| 14 |
+
scores=torch.full((count,),float('-inf'),device=device,dtype=torch.float64)
|
| 15 |
+
scores[::beams]=0
|
| 16 |
+
raw=torch.zeros(count,device=device,dtype=torch.float64)
|
| 17 |
+
for target in range(2,horizon+2):
|
| 18 |
+
original,kept=make_prefix(context.expand(count,-1),history,actions,target)
|
| 19 |
+
ancestors=torch.arange(count,device=device)
|
| 20 |
+
codes=torch.empty((count,0),device=device,dtype=torch.long)
|
| 21 |
+
logs=torch.empty((count,0),device=device)
|
| 22 |
+
pending=original; cache=None
|
| 23 |
+
for token in range(width):
|
| 24 |
+
out=model(input_ids=pending,past_key_values=cache,use_cache=True)
|
| 25 |
+
lp=out.logits[:,-1,:visual].float().log_softmax(-1)
|
| 26 |
+
parents=[]; choices=[]; next_scores=[]
|
| 27 |
+
for g in range(groups):
|
| 28 |
+
start=g*beams
|
| 29 |
+
if g:
|
| 30 |
+
frequency=torch.bincount(torch.cat(choices),minlength=visual).double()/(g*beams)
|
| 31 |
+
else:
|
| 32 |
+
frequency=torch.zeros(visual,device=device,dtype=torch.float64)
|
| 33 |
+
totals=scores[start:start+beams,None]+lp[start:start+beams].double()-penalty*frequency
|
| 34 |
+
score,flat=totals.flatten().topk(beams,sorted=True)
|
| 35 |
+
parents.append(start+flat//visual);choices.append(flat%visual);next_scores.append(score)
|
| 36 |
+
parent=torch.cat(parents);chosen=torch.cat(choices)
|
| 37 |
+
assert torch.equal(parent//beams,torch.arange(count,device=device)//beams)
|
| 38 |
+
raw=raw[parent]+lp[parent,chosen].double()
|
| 39 |
+
scores=torch.cat(next_scores)
|
| 40 |
+
codes=torch.cat([codes[parent],chosen[:,None]],1)
|
| 41 |
+
logs=torch.cat([logs[parent],lp[parent,chosen,None]],1)
|
| 42 |
+
ancestors=ancestors[parent]
|
| 43 |
+
cache=out.past_key_values;cache.reorder_cache(parent)
|
| 44 |
+
pending=chosen[:,None]
|
| 45 |
+
for f in frames:
|
| 46 |
+
for key in ['prefix','response','model_logp']:f[key]=f[key][ancestors]
|
| 47 |
+
history={t:x[ancestors] for t,x in history.items()}
|
| 48 |
+
history[target]=codes
|
| 49 |
+
frames.append(dict(target=target,prefix=original[ancestors],response=codes,model_logp=logs))
|
| 50 |
+
traces.append(dict(target=target,retained=kept,prefix_length=original.shape[1],
|
| 51 |
+
frame_parent_indices=ancestors.tolist(),positions_reset=True,kv_reset=True,
|
| 52 |
+
future_observations=False,random_sampling=False))
|
| 53 |
+
selected=torch.arange(returned,device=device)
|
| 54 |
+
frames=[{k:(v[selected] if torch.is_tensor(v) else v) for k,v in f.items()} for f in frames]
|
| 55 |
+
history={t:x[selected] for t,x in history.items()}
|
| 56 |
+
for f in frames:
|
| 57 |
+
rebuilt,_=make_prefix(context.expand(returned,-1),history,actions,f['target'])
|
| 58 |
+
assert torch.equal(rebuilt,f['prefix'])
|
| 59 |
+
rebuilt=sum(f['model_logp'].double().sum(-1) for f in frames)
|
| 60 |
+
assert torch.allclose(rebuilt,raw[selected],atol=1e-7,rtol=0)
|
| 61 |
+
codes=torch.stack([f['response'] for f in frames],1)
|
| 62 |
+
pair=[float((codes[i]!=codes[j]).float().mean()) for i in range(returned) for j in range(i+1,returned)]
|
| 63 |
+
perframe=(codes!=codes[:1]).any(0).sum(-1).tolist()
|
| 64 |
+
return frames,dict(strategy='ordinary_global_beam8_top4_full_video',groups=groups,beams_per_group=beams,
|
| 65 |
+
active_paths=count,returned_trajectories=returned,penalty=penalty,
|
| 66 |
+
unique_trajectories=len({tuple(c.flatten().tolist()) for c in codes}),
|
| 67 |
+
mean_pairwise_token_difference_fraction=sum(pair)/len(pair),
|
| 68 |
+
pairwise_token_difference_fraction=pair,different_token_positions=sum(perframe),
|
| 69 |
+
different_positions_per_frame=perframe,model_log_scores=raw[selected].tolist(),
|
| 70 |
+
search_scores=scores[selected].tolist(),score_is_behavior_logprob=False,
|
| 71 |
+
on_policy_grpo=False,frames=traces)
|
code/src/futureworlds/reward_core.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 Bytedance Ltd. and/or its affiliates
|
| 2 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
from .batch import TensorBatch as DataProto
|
| 18 |
+
|
| 19 |
+
def msp_reward_fn(self, batch: DataProto, pixels, return_reward_tensor=True, save_pred=False, pixels_before_repeat=None):
|
| 20 |
+
if pixels_before_repeat is None:
|
| 21 |
+
pixels_before_repeat = pixels
|
| 22 |
+
|
| 23 |
+
batch_size = batch.batch["responses"].shape[0]
|
| 24 |
+
segment_length = self.config.data.video.segment_length
|
| 25 |
+
tokens_per_frame = self.config.processor.tokens_per_frame
|
| 26 |
+
action_dim = self.config.processor.action_dim
|
| 27 |
+
|
| 28 |
+
output_tokens = batch.batch["responses"].reshape(batch_size, segment_length - 1, tokens_per_frame + action_dim)
|
| 29 |
+
output_tokens = output_tokens[:, :, :tokens_per_frame]
|
| 30 |
+
output_tokens = output_tokens.clamp(0, self.config.processor.visual_token_num - 1).long()
|
| 31 |
+
|
| 32 |
+
ctx_tokens = batch.batch["ctx_tokens"]
|
| 33 |
+
output_tokens = output_tokens.reshape(batch_size, segment_length - 1, tokens_per_frame)
|
| 34 |
+
|
| 35 |
+
detokenize_output = self.tokenizer_wg.detokenize(
|
| 36 |
+
DataProto.from_single_dict({"tokens": output_tokens, "ctx_tokens": ctx_tokens}),
|
| 37 |
+
# DataProto.from_single_dict({"real": pixels_before_repeat[:, 2:]}, meta_info={'lpips': True}),
|
| 38 |
+
DataProto.from_single_dict({"dummy": torch.zeros((batch_size, 1))}, meta_info={'lpips': True, 'recon': self.config.trainer.reward_fn}),
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
if 'recon_loss' in detokenize_output.batch.keys():
|
| 42 |
+
recon_loss = detokenize_output.batch['recon_loss']
|
| 43 |
+
else:
|
| 44 |
+
pred = detokenize_output.batch['pixels'].clamp(0.0, 1.0)[:, 1:]
|
| 45 |
+
real = pixels[:, 2:]
|
| 46 |
+
if self.config.trainer.reward_fn == 'mse':
|
| 47 |
+
recon_loss = torch.mean((real - pred)**2, dim=(2, 3, 4))
|
| 48 |
+
elif self.config.trainer.reward_fn == 'mae':
|
| 49 |
+
recon_loss = torch.mean(torch.abs(real - pred), dim=(2, 3, 4))
|
| 50 |
+
else:
|
| 51 |
+
raise NotImplementedError(f"Unsupported reward function: {self.config.trainer.reward_fn}")
|
| 52 |
+
|
| 53 |
+
if 'perceptual_loss' in detokenize_output.batch.keys():
|
| 54 |
+
perceptual_loss = detokenize_output.batch['perceptual_loss']
|
| 55 |
+
else:
|
| 56 |
+
perceptual_loss = self.tokenizer_wg.perceptual_loss(DataProto.from_single_dict({"real": real, "pred": pred}))
|
| 57 |
+
perceptual_loss = perceptual_loss.batch['perceptual_loss']
|
| 58 |
+
|
| 59 |
+
if self.config.trainer.msp_reward_aggregate == "mean":
|
| 60 |
+
loss = (recon_loss + perceptual_loss).mean(-1)
|
| 61 |
+
elif self.config.trainer.msp_reward_aggregate == "last":
|
| 62 |
+
loss = (recon_loss + perceptual_loss)[:, -1]
|
| 63 |
+
elif self.config.trainer.msp_reward_aggregate == "discount":
|
| 64 |
+
discount = 0.9
|
| 65 |
+
weight = discount ** torch.arange(recon_loss.shape[1], device=recon_loss.device)
|
| 66 |
+
loss = (recon_loss + perceptual_loss) * weight.unsqueeze(0)
|
| 67 |
+
loss = loss.sum(-1) / weight.sum()
|
| 68 |
+
if not return_reward_tensor:
|
| 69 |
+
return loss
|
| 70 |
+
|
| 71 |
+
print("loss", loss.mean().item(), flush=True)
|
| 72 |
+
print("recon_loss", recon_loss.mean().item(), flush=True)
|
| 73 |
+
print("perceptual_loss", perceptual_loss.mean().item(), flush=True)
|
| 74 |
+
if save_pred:
|
| 75 |
+
pred = detokenize_output.batch['pixels'].clamp(0.0, 1.0)[:, 1:]
|
| 76 |
+
real = pixels[:, 2:]
|
| 77 |
+
for i in range(1):
|
| 78 |
+
# for i in range(5):
|
| 79 |
+
real_traj = torch.cat([real[i:i+1, j] for j in range(real.shape[1])], dim=-1)
|
| 80 |
+
pred_traj = torch.cat([pred[i:i+1, j] for j in range(pred.shape[1])], dim=-1)
|
| 81 |
+
plot_img(torch.cat([
|
| 82 |
+
real_traj.float(),
|
| 83 |
+
pred_traj.float(),
|
| 84 |
+
torch.abs(real_traj-pred_traj).float(),
|
| 85 |
+
], dim=-2), f"{self.config.trainer.experiment_name}-{i}")
|
| 86 |
+
|
| 87 |
+
reward_tensor = torch.zeros_like(batch.batch['responses'], dtype=torch.float32)
|
| 88 |
+
for i in range(len(batch)):
|
| 89 |
+
data_item = batch[i]
|
| 90 |
+
prompt_ids = data_item.batch['prompts']
|
| 91 |
+
prompt_length = prompt_ids.shape[-1]
|
| 92 |
+
valid_response_length = data_item.batch['attention_mask'][prompt_length:].sum().long().item()
|
| 93 |
+
reward_tensor[i, valid_response_length - 1] = -loss[i].item()
|
| 94 |
+
|
| 95 |
+
return reward_tensor, {
|
| 96 |
+
"critic/recon_loss/mean": recon_loss.mean().item(),
|
| 97 |
+
"critic/perceptual_loss/mean": perceptual_loss.mean().item(),
|
| 98 |
+
}
|
code/src/futureworlds/rewards.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Use the upstream msp_reward_fn via a local frozen-tokenizer worker adapter."""
|
| 2 |
+
from types import SimpleNamespace as NS
|
| 3 |
+
import torch
|
| 4 |
+
from .batch import TensorBatch
|
| 5 |
+
from .reward_core import msp_reward_fn
|
| 6 |
+
|
| 7 |
+
class CodecWorker:
|
| 8 |
+
def __init__(self,codec,perceptual):self.codec=codec;self.perceptual=perceptual
|
| 9 |
+
@torch.no_grad()
|
| 10 |
+
def detokenize(self,batch,dummy):
|
| 11 |
+
codes=batch.batch['tokens'];ctx=batch.batch['ctx_tokens'];movies=[]
|
| 12 |
+
for b in range(codes.shape[0]):
|
| 13 |
+
parts=[];first=None
|
| 14 |
+
for start in range(0,codes.shape[1],2):
|
| 15 |
+
decoded=self.codec.detokenize(ctx[b:b+1],codes[b:b+1,start:start+2]).float().clamp(0,1)
|
| 16 |
+
if first is None:first=decoded[:,:1]
|
| 17 |
+
parts.append(decoded[:,1:])
|
| 18 |
+
movies.append(torch.cat([first]+parts,1))
|
| 19 |
+
return TensorBatch({'pixels':torch.cat(movies,0)})
|
| 20 |
+
@torch.no_grad()
|
| 21 |
+
def perceptual_loss(self,batch):
|
| 22 |
+
real,pred=batch.batch['real'],batch.batch['pred'];values=[]
|
| 23 |
+
for r,p in zip(real,pred):
|
| 24 |
+
pieces=[]
|
| 25 |
+
for i in range(0,len(p),2):pieces.append(self.perceptual(p[i:i+2]*2-1,r[i:i+2]*2-1).flatten())
|
| 26 |
+
values.append(torch.cat(pieces))
|
| 27 |
+
return TensorBatch({'perceptual_loss':torch.stack(values)})
|
| 28 |
+
|
| 29 |
+
class VideoReward:
|
| 30 |
+
msp_reward_fn=msp_reward_fn
|
| 31 |
+
def __init__(self,codec,perceptual,horizon):
|
| 32 |
+
self.config=NS(data=NS(video=NS(segment_length=horizon+1)),processor=NS(tokens_per_frame=80,action_dim=13,visual_token_num=4375),trainer=NS(reward_fn='mae',msp_reward_aggregate='mean'))
|
| 33 |
+
self.tokenizer_wg=CodecWorker(codec,perceptual)
|
| 34 |
+
@torch.no_grad()
|
| 35 |
+
def __call__(self,frames,context,actions,pixels):
|
| 36 |
+
# Only reward bookkeeping uses concatenated responses. Policy scoring uses actual per-frame prefixes.
|
| 37 |
+
codes=torch.stack([f['response'] for f in frames],1)
|
| 38 |
+
assert codes.min()>=0 and codes.max()<4375,'Illegal generated token; never hide it with upstream clamp'
|
| 39 |
+
group,horizon,_=codes.shape
|
| 40 |
+
act=actions[2:2+horizon].unsqueeze(0).expand(group,-1,-1)
|
| 41 |
+
responses=torch.cat([codes,act],-1).reshape(group,-1)
|
| 42 |
+
prompt=frames[0]['prefix']
|
| 43 |
+
batch=TensorBatch({'responses':responses,'prompts':prompt,'attention_mask':torch.ones(group,prompt.shape[1]+responses.shape[1],device=codes.device,dtype=torch.long),'ctx_tokens':context.expand(group,-1,-1)})
|
| 44 |
+
truth=pixels.expand(group,-1,-1,-1,-1)
|
| 45 |
+
tensor,metrics=self.msp_reward_fn(batch,truth)
|
| 46 |
+
reward=tensor.sum(-1)
|
| 47 |
+
assert torch.isfinite(reward).all()
|
| 48 |
+
return reward,metrics
|
code/src/futureworlds/search.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Four disjoint beam groups, with unmodified policy scores for the RL surrogate.
|
| 2 |
+
|
| 3 |
+
Search penalties only affect proposal selection. Deterministic proposals do not
|
| 4 |
+
have on-policy sampling probabilities; the beam RL objective remains biased.
|
| 5 |
+
"""
|
| 6 |
+
import torch
|
| 7 |
+
from .memory import make_prefix
|
| 8 |
+
|
| 9 |
+
@torch.no_grad()
|
| 10 |
+
def rollout_group_beam(model, context, anchor, actions, horizon, penalty,
|
| 11 |
+
groups=4, beams=2, visual=4375, width=80):
|
| 12 |
+
model.eval()
|
| 13 |
+
device=context.device; count=groups*beams
|
| 14 |
+
history={1:anchor.expand(count,-1)}; frames=[]; traces=[]
|
| 15 |
+
scores=torch.full((count,),float('-inf'),device=device,dtype=torch.float64)
|
| 16 |
+
scores[::beams]=0
|
| 17 |
+
raw=torch.zeros(count,device=device,dtype=torch.float64)
|
| 18 |
+
for target in range(2,horizon+2):
|
| 19 |
+
original,kept=make_prefix(context.expand(count,-1),history,actions,target)
|
| 20 |
+
ancestors=torch.arange(count,device=device)
|
| 21 |
+
codes=torch.empty((count,0),device=device,dtype=torch.long)
|
| 22 |
+
logs=torch.empty((count,0),device=device)
|
| 23 |
+
pending=original; cache=None
|
| 24 |
+
for token in range(width):
|
| 25 |
+
out=model(input_ids=pending,past_key_values=cache,use_cache=True)
|
| 26 |
+
lp=out.logits[:,-1,:visual].float().log_softmax(-1)
|
| 27 |
+
parents=[]; choices=[]; next_scores=[]
|
| 28 |
+
for g in range(groups):
|
| 29 |
+
start=g*beams
|
| 30 |
+
if g:
|
| 31 |
+
frequency=torch.bincount(torch.cat(choices),minlength=visual).double()/(g*beams)
|
| 32 |
+
else:
|
| 33 |
+
frequency=torch.zeros(visual,device=device,dtype=torch.float64)
|
| 34 |
+
totals=scores[start:start+beams,None]+lp[start:start+beams].double()-penalty*frequency
|
| 35 |
+
score,flat=totals.flatten().topk(beams,sorted=True)
|
| 36 |
+
parents.append(start+flat//visual);choices.append(flat%visual);next_scores.append(score)
|
| 37 |
+
parent=torch.cat(parents);chosen=torch.cat(choices)
|
| 38 |
+
assert torch.equal(parent//beams,torch.arange(count,device=device)//beams)
|
| 39 |
+
raw=raw[parent]+lp[parent,chosen].double()
|
| 40 |
+
scores=torch.cat(next_scores)
|
| 41 |
+
codes=torch.cat([codes[parent],chosen[:,None]],1)
|
| 42 |
+
logs=torch.cat([logs[parent],lp[parent,chosen,None]],1)
|
| 43 |
+
ancestors=ancestors[parent]
|
| 44 |
+
cache=out.past_key_values;cache.reorder_cache(parent)
|
| 45 |
+
pending=chosen[:,None]
|
| 46 |
+
for f in frames:
|
| 47 |
+
for key in ['prefix','response','model_logp']:f[key]=f[key][ancestors]
|
| 48 |
+
history={t:x[ancestors] for t,x in history.items()}
|
| 49 |
+
history[target]=codes
|
| 50 |
+
frames.append(dict(target=target,prefix=original[ancestors],response=codes,model_logp=logs))
|
| 51 |
+
traces.append(dict(target=target,retained=kept,prefix_length=original.shape[1],
|
| 52 |
+
frame_parent_indices=ancestors.tolist(),positions_reset=True,kv_reset=True,
|
| 53 |
+
future_observations=False,random_sampling=False))
|
| 54 |
+
selected=torch.arange(0,count,beams,device=device)
|
| 55 |
+
frames=[{k:(v[selected] if torch.is_tensor(v) else v) for k,v in f.items()} for f in frames]
|
| 56 |
+
history={t:x[selected] for t,x in history.items()}
|
| 57 |
+
for f in frames:
|
| 58 |
+
rebuilt,_=make_prefix(context.expand(groups,-1),history,actions,f['target'])
|
| 59 |
+
assert torch.equal(rebuilt,f['prefix'])
|
| 60 |
+
rebuilt=sum(f['model_logp'].double().sum(-1) for f in frames)
|
| 61 |
+
assert torch.allclose(rebuilt,raw[selected],atol=1e-7,rtol=0)
|
| 62 |
+
codes=torch.stack([f['response'] for f in frames],1)
|
| 63 |
+
pair=[float((codes[i]!=codes[j]).float().mean()) for i in range(groups) for j in range(i+1,groups)]
|
| 64 |
+
perframe=(codes!=codes[:1]).any(0).sum(-1).tolist()
|
| 65 |
+
return frames,dict(strategy='grouped_diverse_full_video_beam',groups=groups,beams_per_group=beams,
|
| 66 |
+
active_paths=count,returned_trajectories=groups,penalty=penalty,
|
| 67 |
+
unique_trajectories=len({tuple(c.flatten().tolist()) for c in codes}),
|
| 68 |
+
mean_pairwise_token_difference_fraction=sum(pair)/len(pair),
|
| 69 |
+
pairwise_token_difference_fraction=pair,different_token_positions=sum(perframe),
|
| 70 |
+
different_positions_per_frame=perframe,model_log_scores=raw[selected].tolist(),
|
| 71 |
+
search_scores=scores[selected].tolist(),score_is_behavior_logprob=False,
|
| 72 |
+
on_policy_grpo=False,frames=traces)
|
code/src/futureworlds/text_conditioning.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Pretrained-text conditioning with zero-initialized residual cross-attention.
|
| 2 |
+
|
| 3 |
+
Frozen T5 features are inputs, not vocabulary IDs. Visual/action positions and
|
| 4 |
+
vocabulary stay unchanged. No-text requests bypass the added residual exactly.
|
| 5 |
+
"""
|
| 6 |
+
import torch
|
| 7 |
+
_UNSET = object()
|
| 8 |
+
from torch import nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class TextResidual(nn.Module):
|
| 13 |
+
def __init__(self, hidden=768, text_width=768, inner=256, heads=4):
|
| 14 |
+
super().__init__();self.heads=heads;self.inner=inner
|
| 15 |
+
self.query_norm=nn.LayerNorm(hidden)
|
| 16 |
+
self.text_norm=nn.LayerNorm(text_width)
|
| 17 |
+
self.q=nn.Linear(hidden,inner,bias=False)
|
| 18 |
+
self.k=nn.Linear(text_width,inner,bias=False)
|
| 19 |
+
self.v=nn.Linear(text_width,inner,bias=False)
|
| 20 |
+
self.out=nn.Linear(inner,hidden,bias=False)
|
| 21 |
+
nn.init.zeros_(self.out.weight)
|
| 22 |
+
def forward(self, hidden, text, mask):
|
| 23 |
+
b,t,_=hidden.shape
|
| 24 |
+
if text.shape[0]!=b:
|
| 25 |
+
assert text.shape[0]==1, 'Generation expansion is valid only within a single episode'
|
| 26 |
+
text=text.expand(b,-1,-1);mask=mask.expand(b,-1)
|
| 27 |
+
dim=self.inner//self.heads
|
| 28 |
+
q=self.q(self.query_norm(hidden)).view(b,t,self.heads,dim).transpose(1,2)
|
| 29 |
+
memory=self.text_norm(text)
|
| 30 |
+
k=self.k(memory).view(b,-1,self.heads,dim).transpose(1,2)
|
| 31 |
+
v=self.v(memory).view(b,-1,self.heads,dim).transpose(1,2)
|
| 32 |
+
value=F.scaled_dot_product_attention(q,k,v,attn_mask=mask[:,None,None,:].bool(),dropout_p=0.)
|
| 33 |
+
value=value.transpose(1,2).reshape(b,t,self.inner)
|
| 34 |
+
# Explicitly suppress all-padding / deliberately dropped instruction.
|
| 35 |
+
value=self.out(value)*mask.any(-1)[:,None,None].to(value.dtype)
|
| 36 |
+
return hidden+value.to(hidden.dtype)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class TextWorldModel(nn.Module):
|
| 40 |
+
def __init__(self, core, text_width=768, layers=(3,7,11), inner=256):
|
| 41 |
+
super().__init__();self.core=core;self.layers=list(layers)
|
| 42 |
+
self.adapters=nn.ModuleDict({str(i):TextResidual(core.config.hidden_size,text_width,inner) for i in layers})
|
| 43 |
+
self._text=None;self._mask=None;self._handles=[]
|
| 44 |
+
for i in layers:
|
| 45 |
+
self._handles.append(core.model.layers[i].register_forward_pre_hook(self._hook(i),with_kwargs=True))
|
| 46 |
+
def _hook(self,index):
|
| 47 |
+
def hook(module,args,kwargs):
|
| 48 |
+
if self._text is None:return args,kwargs
|
| 49 |
+
adapter=self.adapters[str(index)]
|
| 50 |
+
if args:
|
| 51 |
+
return (adapter(args[0],self._text,self._mask),)+args[1:],kwargs
|
| 52 |
+
kw=dict(kwargs);kw['hidden_states']=adapter(kw['hidden_states'],self._text,self._mask)
|
| 53 |
+
return args,kw
|
| 54 |
+
return hook
|
| 55 |
+
def set_text(self,text,mask):
|
| 56 |
+
assert (text is None)==(mask is None)
|
| 57 |
+
if text is not None:
|
| 58 |
+
assert text.ndim==3 and mask.shape==text.shape[:2]
|
| 59 |
+
assert torch.isfinite(text).all()
|
| 60 |
+
self._text=text;self._mask=mask
|
| 61 |
+
def forward(self,text_features=_UNSET,text_mask=_UNSET,**kwargs):
|
| 62 |
+
# Generation and selected-token scoring use the explicitly bound episode.
|
| 63 |
+
# No activation checkpointing: mutable conditioning cannot change during replay.
|
| 64 |
+
if text_features is not _UNSET:
|
| 65 |
+
assert text_mask is not _UNSET
|
| 66 |
+
self.set_text(text_features,text_mask)
|
| 67 |
+
else:
|
| 68 |
+
assert text_mask is _UNSET
|
| 69 |
+
return self.core(**kwargs)
|
| 70 |
+
def generate(self,*args,**kwargs):
|
| 71 |
+
# Caller sets the same episode instruction before every generated frame.
|
| 72 |
+
return self.core.generate(*args,**kwargs)
|
| 73 |
+
def compute_transition_scores(self,*args,**kwargs):return self.core.compute_transition_scores(*args,**kwargs)
|
robocasa/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
language:
|
| 3 |
+
- en
|
| 4 |
+
tags:
|
| 5 |
+
- futureworlds
|
| 6 |
+
- world-model
|
| 7 |
+
- robotics
|
| 8 |
+
- video-prediction
|
| 9 |
+
- reinforcement-learning
|
| 10 |
+
---
|
| 11 |
+
# FutureWorlds — RoboCasa
|
| 12 |
+
|
| 13 |
+
Local release candidate, not yet published. Model weights are verified by CPU SHA-256; end-to-end inference with the portable RGB pipeline is not yet validated.
|
| 14 |
+
|
| 15 |
+
## Checkpoint variants
|
| 16 |
+
|
| 17 |
+
| Folder | Method | Updates | Use |
|
| 18 |
+
|---|---|---:|---|
|
| 19 |
+
| `models/main` | MemSPO (R0) | 400 | Paper main table |
|
| 20 |
+
| `models/sft` | SFT | 50,000 | Initialization/control |
|
| 21 |
+
| `models/memspo200` | MemSPO (R0) | 200 | Matched ablation |
|
| 22 |
+
| `models/grpo200` | GRPO (R0) | 200 | Matched ablation |
|
| 23 |
+
| `models/ordinary200` | Ordinary global beam8, top4 (R0) | 200 | Proposal-strategy ablation |
|
| 24 |
+
|
| 25 |
+
All post-training checkpoints start from their paired SFT checkpoint. `main` and `memspo200` must not be interchanged.
|
| 26 |
+
|
| 27 |
+
## Main-table evaluation
|
| 28 |
+
|
| 29 |
+
128 trajectories, 32 predicted frames, 2 initial observations, per-frame Beam4, anchor1 + recent6, no true-future refresh. Recorded split: held-out test; runtime: GPU FP32.
|
| 30 |
+
|
| 31 |
+
| PSNR ↑ | SSIM ↑ | LPIPS ↓ |
|
| 32 |
+
|---:|---:|---:|
|
| 33 |
+
| 18.4719 | 0.765948 | 0.183338 |
|
| 34 |
+
|
| 35 |
+
These are recorded paper results; staging weights does not constitute a fresh reproduction.
|
| 36 |
+
|
| 37 |
+
## Required components
|
| 38 |
+
|
| 39 |
+
- Dataset-specific `codec/` and `preprocessing/` files, paired by the manifest.
|
| 40 |
+
- Trained text adapters in each checkpoint and frozen T5-base at revision `a9723ea7f1b39c1eae772870f3b547bf6ef7e6c1`; T5 is referenced, not duplicated.
|
| 41 |
+
- 52 action tokens per predicted frame. RoboCasa uses four ordered 13-dimensional control blocks; do not flatten time in a different order.
|
| 42 |
+
- Original FP32 safetensors; no post-hoc quantization or checkpoint merging.
|
| 43 |
+
|
| 44 |
+
## Loading and scope
|
| 45 |
+
|
| 46 |
+
Use `futureworlds.checkpoints.load_world_model(bundle_root, "robocasa", variant="main")` for the predictor only. This does not decode RGB, prepare actions, or download T5 automatically. A complete RGB demo and clean-environment GPU equivalence run remain pending. These models are custom action-conditioned predictors, not generic `pipeline("text-to-video")` models.
|
| 47 |
+
|
| 48 |
+
## Provenance and limitations
|
| 49 |
+
|
| 50 |
+
Core architecture and tokenizer derive from RLVR-World/iVideoGPT. Upstream license texts are included under `licenses/`; the license for original additions is not yet selected. The raw datasets and third-party baseline weights are not included. Learned-world feedback examples are qualitative generated trajectories, not independently verified physical task-success results.
|
robocasa/codec/config.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_class_name": "CompressiveVQModelFSQ",
|
| 3 |
+
"_diffusers_version": "0.35.2",
|
| 4 |
+
"act_fn": "silu",
|
| 5 |
+
"block_out_channels": [
|
| 6 |
+
128,
|
| 7 |
+
256,
|
| 8 |
+
256,
|
| 9 |
+
512
|
| 10 |
+
],
|
| 11 |
+
"context_length": 1,
|
| 12 |
+
"down_block_types": [
|
| 13 |
+
"DownEncoderBlock2D",
|
| 14 |
+
"DownEncoderBlock2D",
|
| 15 |
+
"DownEncoderBlock2D",
|
| 16 |
+
"DownEncoderBlock2D"
|
| 17 |
+
],
|
| 18 |
+
"dyn_fsq_levels": 12,
|
| 19 |
+
"force_upcast": true,
|
| 20 |
+
"in_channels": 3,
|
| 21 |
+
"latent_channels": 64,
|
| 22 |
+
"layers_per_block": 2,
|
| 23 |
+
"lookup_from_codebook": true,
|
| 24 |
+
"max_att_resolution": 32,
|
| 25 |
+
"mid_block_add_attention": false,
|
| 26 |
+
"norm_num_groups": 32,
|
| 27 |
+
"norm_type": "group",
|
| 28 |
+
"out_channels": 3,
|
| 29 |
+
"patch_size": 4,
|
| 30 |
+
"resolution": 256,
|
| 31 |
+
"sample_size": 32,
|
| 32 |
+
"scaling_factor": 0.18215,
|
| 33 |
+
"up_block_types": [
|
| 34 |
+
"UpDecoderBlock2D",
|
| 35 |
+
"UpDecoderBlock2D",
|
| 36 |
+
"UpDecoderBlock2D",
|
| 37 |
+
"UpDecoderBlock2D"
|
| 38 |
+
],
|
| 39 |
+
"vq_fsq_levels": 12
|
| 40 |
+
}
|
robocasa/codec/diffusion_pytorch_model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4ea6133eae9d3deba4fc546ac7d0cb318363d0d79372b29a8e7d1a753023b77d
|
| 3 |
+
size 517850360
|
robocasa/futureworlds_config.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema_version": 1,
|
| 3 |
+
"dataset": "robocasa",
|
| 4 |
+
"status": "staged_research_preview",
|
| 5 |
+
"codec": "robocasa/codec",
|
| 6 |
+
"action_ranges": "robocasa/preprocessing/action_ranges.pt",
|
| 7 |
+
"action_tokens_per_step": 52,
|
| 8 |
+
"text_conditioning": {
|
| 9 |
+
"enabled": true,
|
| 10 |
+
"repo_id": "google-t5/t5-base",
|
| 11 |
+
"revision": "a9723ea7f1b39c1eae772870f3b547bf6ef7e6c1",
|
| 12 |
+
"layers": [
|
| 13 |
+
3,
|
| 14 |
+
7,
|
| 15 |
+
11
|
| 16 |
+
],
|
| 17 |
+
"inner": 256,
|
| 18 |
+
"width": 768,
|
| 19 |
+
"max_tokens": 128,
|
| 20 |
+
"bundled": false
|
| 21 |
+
},
|
| 22 |
+
"initial_observations": 2,
|
| 23 |
+
"memory": {
|
| 24 |
+
"anchor_states": 1,
|
| 25 |
+
"recent_states": 6,
|
| 26 |
+
"ground_truth_refresh": false
|
| 27 |
+
},
|
| 28 |
+
"evaluation": {
|
| 29 |
+
"strategy": "per_frame_beam",
|
| 30 |
+
"beam_width": 4,
|
| 31 |
+
"horizon": 32,
|
| 32 |
+
"visual_tokens_per_frame": 80,
|
| 33 |
+
"valid_visual_vocab": 4375
|
| 34 |
+
},
|
| 35 |
+
"variants": {
|
| 36 |
+
"main": {
|
| 37 |
+
"path": "robocasa/models/main",
|
| 38 |
+
"method": "MemSPO",
|
| 39 |
+
"updates": 400,
|
| 40 |
+
"reward": "R0",
|
| 41 |
+
"purpose": "paper_main_table",
|
| 42 |
+
"text": true
|
| 43 |
+
},
|
| 44 |
+
"sft": {
|
| 45 |
+
"path": "robocasa/models/sft",
|
| 46 |
+
"method": "SFT",
|
| 47 |
+
"updates": 50000,
|
| 48 |
+
"reward": null,
|
| 49 |
+
"purpose": "matched_ablation",
|
| 50 |
+
"text": true
|
| 51 |
+
},
|
| 52 |
+
"memspo200": {
|
| 53 |
+
"path": "robocasa/models/memspo200",
|
| 54 |
+
"method": "MemSPO",
|
| 55 |
+
"updates": 200,
|
| 56 |
+
"reward": "R0",
|
| 57 |
+
"purpose": "matched_ablation",
|
| 58 |
+
"text": true
|
| 59 |
+
},
|
| 60 |
+
"grpo200": {
|
| 61 |
+
"path": "robocasa/models/grpo200",
|
| 62 |
+
"method": "GRPO",
|
| 63 |
+
"updates": 200,
|
| 64 |
+
"reward": "R0",
|
| 65 |
+
"purpose": "matched_ablation",
|
| 66 |
+
"text": true
|
| 67 |
+
},
|
| 68 |
+
"ordinary200": {
|
| 69 |
+
"path": "robocasa/models/ordinary200",
|
| 70 |
+
"method": "OrdinaryBeam",
|
| 71 |
+
"updates": 200,
|
| 72 |
+
"reward": "R0",
|
| 73 |
+
"purpose": "matched_ablation",
|
| 74 |
+
"text": true
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
}
|
robocasa/models/grpo200/config.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LlamaForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 9006,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 9007,
|
| 10 |
+
"head_dim": 64,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 768,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 3072,
|
| 15 |
+
"max_length": null,
|
| 16 |
+
"max_position_embeddings": 8192,
|
| 17 |
+
"mlp_bias": false,
|
| 18 |
+
"model_type": "llama",
|
| 19 |
+
"num_attention_heads": 12,
|
| 20 |
+
"num_hidden_layers": 12,
|
| 21 |
+
"num_key_value_heads": 12,
|
| 22 |
+
"pretraining_tp": 1,
|
| 23 |
+
"rms_norm_eps": 1e-06,
|
| 24 |
+
"rope_scaling": null,
|
| 25 |
+
"rope_theta": 10000.0,
|
| 26 |
+
"tie_word_embeddings": false,
|
| 27 |
+
"transformers_version": "4.57.1",
|
| 28 |
+
"use_cache": true,
|
| 29 |
+
"vocab_size": 9008
|
| 30 |
+
}
|
robocasa/models/grpo200/generation_config.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"bos_token_id": 9006,
|
| 4 |
+
"eos_token_id": 9007,
|
| 5 |
+
"transformers_version": "4.57.1"
|
| 6 |
+
}
|
robocasa/models/grpo200/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:af36ba44677963a0de2753dcf933c46f616505792fe4d6668138adc2d8e59720
|
| 3 |
+
size 508419080
|
robocasa/models/grpo200/text_adapters.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:eba7ba8715d82c79f8909a8d65cf3ede5f1874b0963efcd3a351f2f8ca13633c
|
| 3 |
+
size 9476032
|
robocasa/models/main/config.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LlamaForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 9006,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 9007,
|
| 10 |
+
"head_dim": 64,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 768,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 3072,
|
| 15 |
+
"max_length": null,
|
| 16 |
+
"max_position_embeddings": 8192,
|
| 17 |
+
"mlp_bias": false,
|
| 18 |
+
"model_type": "llama",
|
| 19 |
+
"num_attention_heads": 12,
|
| 20 |
+
"num_hidden_layers": 12,
|
| 21 |
+
"num_key_value_heads": 12,
|
| 22 |
+
"pretraining_tp": 1,
|
| 23 |
+
"rms_norm_eps": 1e-06,
|
| 24 |
+
"rope_scaling": null,
|
| 25 |
+
"rope_theta": 10000.0,
|
| 26 |
+
"tie_word_embeddings": false,
|
| 27 |
+
"transformers_version": "4.57.1",
|
| 28 |
+
"use_cache": true,
|
| 29 |
+
"vocab_size": 9008
|
| 30 |
+
}
|
robocasa/models/main/generation_config.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"bos_token_id": 9006,
|
| 4 |
+
"eos_token_id": 9007,
|
| 5 |
+
"transformers_version": "4.57.1"
|
| 6 |
+
}
|
robocasa/models/main/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a0d4420b480f44f9f1f970edbfc96a95adb5e428dc6b49ca3d1b2c61fddb5807
|
| 3 |
+
size 508419080
|
robocasa/models/main/text_adapters.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:92ad8666771696ee22f6c7aaee12d9bd8bd6195569298defb1b89b3bcba11d1e
|
| 3 |
+
size 9476032
|
robocasa/models/memspo200/config.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LlamaForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 9006,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 9007,
|
| 10 |
+
"head_dim": 64,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 768,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 3072,
|
| 15 |
+
"max_length": null,
|
| 16 |
+
"max_position_embeddings": 8192,
|
| 17 |
+
"mlp_bias": false,
|
| 18 |
+
"model_type": "llama",
|
| 19 |
+
"num_attention_heads": 12,
|
| 20 |
+
"num_hidden_layers": 12,
|
| 21 |
+
"num_key_value_heads": 12,
|
| 22 |
+
"pretraining_tp": 1,
|
| 23 |
+
"rms_norm_eps": 1e-06,
|
| 24 |
+
"rope_scaling": null,
|
| 25 |
+
"rope_theta": 10000.0,
|
| 26 |
+
"tie_word_embeddings": false,
|
| 27 |
+
"transformers_version": "4.57.1",
|
| 28 |
+
"use_cache": true,
|
| 29 |
+
"vocab_size": 9008
|
| 30 |
+
}
|
robocasa/models/memspo200/generation_config.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"bos_token_id": 9006,
|
| 4 |
+
"eos_token_id": 9007,
|
| 5 |
+
"transformers_version": "4.57.1"
|
| 6 |
+
}
|
robocasa/models/memspo200/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6193281a747b6155cb32b7e4e3844f1ff05ac0e07d9df4cd3f11bba4c8711d92
|
| 3 |
+
size 508419080
|
robocasa/models/memspo200/text_adapters.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2c9f8ac2cc6c128fc29305546a29effbd99250f47905e7711a8330a397bfd7c0
|
| 3 |
+
size 9476032
|
robocasa/models/ordinary200/config.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LlamaForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 9006,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 9007,
|
| 10 |
+
"head_dim": 64,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 768,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 3072,
|
| 15 |
+
"max_length": null,
|
| 16 |
+
"max_position_embeddings": 8192,
|
| 17 |
+
"mlp_bias": false,
|
| 18 |
+
"model_type": "llama",
|
| 19 |
+
"num_attention_heads": 12,
|
| 20 |
+
"num_hidden_layers": 12,
|
| 21 |
+
"num_key_value_heads": 12,
|
| 22 |
+
"pretraining_tp": 1,
|
| 23 |
+
"rms_norm_eps": 1e-06,
|
| 24 |
+
"rope_scaling": null,
|
| 25 |
+
"rope_theta": 10000.0,
|
| 26 |
+
"tie_word_embeddings": false,
|
| 27 |
+
"transformers_version": "4.57.1",
|
| 28 |
+
"use_cache": true,
|
| 29 |
+
"vocab_size": 9008
|
| 30 |
+
}
|
robocasa/models/ordinary200/generation_config.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"bos_token_id": 9006,
|
| 4 |
+
"eos_token_id": 9007,
|
| 5 |
+
"transformers_version": "4.57.1"
|
| 6 |
+
}
|
robocasa/models/ordinary200/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:486a6c324466839121a4fb392450b1093e98d7482dbb9b6c794baf4ebb7b1519
|
| 3 |
+
size 508419080
|
robocasa/models/ordinary200/text_adapters.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c4fc6d76fe21e5687fa7dd4cb2c82da0b3c52554490be6211e22aa4fabc85900
|
| 3 |
+
size 9476032
|
robocasa/models/sft/config.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LlamaForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 9006,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 9007,
|
| 10 |
+
"head_dim": 64,
|
| 11 |
+
"hidden_act": "silu",
|
| 12 |
+
"hidden_size": 768,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"intermediate_size": 3072,
|
| 15 |
+
"max_length": null,
|
| 16 |
+
"max_position_embeddings": 8192,
|
| 17 |
+
"mlp_bias": false,
|
| 18 |
+
"model_type": "llama",
|
| 19 |
+
"num_attention_heads": 12,
|
| 20 |
+
"num_hidden_layers": 12,
|
| 21 |
+
"num_key_value_heads": 12,
|
| 22 |
+
"pretraining_tp": 1,
|
| 23 |
+
"rms_norm_eps": 1e-06,
|
| 24 |
+
"rope_scaling": null,
|
| 25 |
+
"rope_theta": 10000.0,
|
| 26 |
+
"tie_word_embeddings": false,
|
| 27 |
+
"transformers_version": "4.57.1",
|
| 28 |
+
"use_cache": true,
|
| 29 |
+
"vocab_size": 9008
|
| 30 |
+
}
|
robocasa/models/sft/generation_config.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"bos_token_id": 9006,
|
| 4 |
+
"eos_token_id": 9007,
|
| 5 |
+
"transformers_version": "4.57.1"
|
| 6 |
+
}
|
robocasa/models/sft/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a86d465e8b5c1cc824238b010aed477ea61d6d8307102135b886b32ca065463c
|
| 3 |
+
size 508419080
|
robocasa/models/sft/text_adapters.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ef8a06e8ba6451ad1873d9476400c04ae581ce8e654ca77958f7cc56d9f6b380
|
| 3 |
+
size 9476032
|
robocasa/preprocessing/action_ranges.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:55531c0e5296d01b8eb78aa941b422bde30e80e458a24d362f874671f2dbfaf4
|
| 3 |
+
size 2003
|
robocasa/training_recipe.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"max_text_tokens": 128,
|
| 3 |
+
"text_layers": [
|
| 4 |
+
3,
|
| 5 |
+
7,
|
| 6 |
+
11
|
| 7 |
+
],
|
| 8 |
+
"text_inner": 256,
|
| 9 |
+
"seed": 20260918,
|
| 10 |
+
"sft_steps": 50000,
|
| 11 |
+
"sft_global_batch": 64,
|
| 12 |
+
"core_lr": 1e-05,
|
| 13 |
+
"adapter_lr": 0.0001,
|
| 14 |
+
"warmup_steps": 500,
|
| 15 |
+
"rl_steps": 500,
|
| 16 |
+
"rl_lr": 1e-06,
|
| 17 |
+
"kl_coef": 0.001,
|
| 18 |
+
"clip": 0.2,
|
| 19 |
+
"horizon": 10,
|
| 20 |
+
"groups": 4,
|
| 21 |
+
"beams_per_group": 2,
|
| 22 |
+
"diversity_penalty": 0.1
|
| 23 |
+
}
|
rt1/models/main/text_adapters.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c426c478c690679230b613401b59f8d17bc7863b33450ba12964d42722b489cd
|
| 3 |
+
size 9476032
|
rt1/models/sft/text_adapters.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:006511fd5bbfdeb2f6975695415243b5cec4912517c7f187e65864a48ecfcf07
|
| 3 |
+
size 9476032
|