Pi0.5
Pi0.5
Pi0.5는 OpenPI / LeRobot diffusion Vision-Language-Action (dVLA) 정책입니다. 카메라 이미지, 언어 지시, 로봇 상태를 소비하고 로봇 제어를 위한 연속 동작 청크(action chunk)를 반환합니다.
출처: 문서
본문
1. 모델 소개 (Model Introduction)
Pi0.5는 OpenPI / LeRobot diffusion Vision-Language-Action (dVLA) 정책입니다. 카메라 이미지, 언어 지시, 로봇 상태를 소비한 다음 로봇 제어를 위한 연속 동작 청크를 반환합니다.
SGLang은 네이티브 multimodal_gen 런타임을 통해 Pi0.5를 서빙합니다. 구현은 SigLIP/PaliGemma 프리픽스 인코더와 Gemma 동작 전문가(action expert)를 사용합니다. 프리픽스가 한 번 인코딩된 후 동작 전문가가 flow-matching 디노이징 루프를 실행합니다. 이는 토큰 디코드 워크로드가 아니므로 Pi0.5 경로는 LLM 샘플러, logits 프로세서, 토큰 스트리밍, 페이지드 디코드 KV 캐시, 별도의 SRT 서빙 엔진을 사용하지 않습니다.
SigLIP 비전 타워, PaliGemma 언어 스택, 동작 전문가는 모두 SGLang 네이티브 모듈입니다. Transformers는 체크포인트 구성과 토크나이제이션에만 사용되며 런타임 신경망에는 사용되지 않습니다.
프리픽스 인코더는 관측 인코딩의 두 단계를 모두 다룹니다. SigLIP은 리사이즈된 카메라 픽셀을 연속 패치 임베딩으로 바꾸고, PaliGemma 트랜스포머는 그 패치를 토크나이즈된 작업/상태 입력과 함께 인코딩해 레이어별 프리픽스 K/V를 생성합니다. flow 타임스텝 t에서 동작 전문가는 노이즈가 섞인 연속 동작 청크 x_t를 동작 임베딩으로 투영합니다. 그 쿼리는 고정 프리픽스 K/V와 현재 동작 K/V 모두에 어텐션하며, 타임스텝은 별도의 sinusoidal-MLP 경로를 따르고 AdaRMSNorm 게이트를 통해 모든 동작 전문가 레이어를 조건화합니다.
지원되는 공개 체크포인트:
| 체크포인트 (Checkpoint) | 카메라 (Cameras) | 상태 차원 (State Dim) | 출력 동작 차원 (Output Action Dim) | 동작 호라이즌 (Action Horizon) | 디노이즈 스텝 (Denoise Steps) |
|---|---|---|---|---|---|
lerobot/pi05_base |
base_0_rgb, left_wrist_0_rgb, right_wrist_0_rgb |
32 | 32 | 50 | 10 |
lerobot/pi05_libero_base |
image, image2, 빈 카메라 1개 |
8 | 7 | 50 | 10 |
참고 자료:
2. 설치 (Installation)
diffusion extra로 SGLang을 설치하세요. Pi0.5는 multimodal_gen에 있으며, 이 extra는 정책 서버가 사용하는 런타임 의존성을 포함합니다.
git clone https://github.com/sgl-project/sglang.git
cd sglang
pip install -e "python[diffusion]"
일반 환경 설정은 SGLang Diffusion 설치 가이드를 참조하세요.
3. 모델 배포 (Model Deployment)
기본 Pi0.5 정책을 서빙합니다:
sglang serve lerobot/pi05_base \
--model-type diffusion \
--host 127.0.0.1 \
--port 30000
LIBERO 체크포인트를 서빙합니다:
sglang serve lerobot/pi05_libero_base \
--model-type diffusion \
--host 127.0.0.1 \
--port 30000
이 등록된 LeRobot 체크포인트는 네이티브 multimodal_gen Pi0.5 파이프라인에 자동으로 디스패치됩니다. --pipeline / --pipeline-class-name 플래그는 모델 레지스트리에서 해석할 수 없는 로컬·비공개 체크포인트에 대한 고급 오버라이드일 뿐입니다. Pi0.5 서빙은 두 번째 SRT LLM 서빙 엔진을 시작하지 않습니다.
3.1 동작 요청 스키마 (Action Request Schema)
| 필드 (Field) | 타입 (Type) | 설명 (Description) |
|---|---|---|
model |
string, optional | 서빙되는 모델 이름. 생략하면 서버는 현재 로드된 정책을 사용. |
input.task 또는 input.prompt |
string | 정책용 언어 지시. |
input.observation.images |
object | 카메라 이름 → RGB 이미지 매핑. 이미지는 여기에 중첩되거나 websocket 어댑터를 통해 observation.images.base_0_rgb 같은 OpenPI 관측 키로 보낼 수 있음. |
input.observation.state |
array 또는 tensor object | 로봇 상태 벡터. OpenPI / LeRobot 체크포인트와 같은 정규화 규칙을 사용. |
input.observation.noise |
array 또는 tensor object, optional | 형태 [action_horizon, action_dim]의 초기 동작 노이즈. 결정적 디버깅에 사용. |
parameters.num_inference_steps |
integer, optional | Flow-matching 디노이즈 스텝. 기본값 10. |
parameters.action_horizon |
integer, optional | 출력 동작 호라이즌. 기본값은 체크포인트 구성. |
parameters.action_dim |
integer, optional | 내부 패딩된 동작 차원. 기본값은 체크포인트 구성. |
runtime.return_timing |
boolean, optional | 단계 타이밍 필드 반환. 기본값 true. |
runtime.prefix_cache |
boolean 또는 "auto", optional |
서버에서 enable_global_prefix_cache=true일 때 이 요청에 대한 정확한 전체 프리픽스 조회 활성화. 기본값 "auto". |
runtime.cuda_graph |
boolean 또는 "auto", optional |
이 요청에 대해 사용 가능한 프리픽스·동작-디노이즈 CUDA 그래프 경로 활성화. 기본값 "auto". |
runtime.output_format |
"list" 또는 "numpy", optional |
JSON 호환성에는 "list" 사용. msgpack 또는 Python 클라이언트에는 Python-list materialization을 피하려고 "numpy" 사용. 기본값 "list". |
runtime.response_format |
"envelope" 또는 "raw", optional |
HTTP 전용 응답 형태. "envelope"는 일반 동작 인벨로프 반환. "raw"는 정책 페이로드를 직접 반환. 기본값 "envelope". |
4. API 사용법 (API Usage)
4.1 일반 동작 HTTP API (Generic Action HTTP API)
직접 정책 호출과 디버깅에는 /v1/actions/generations를 사용하세요. /v1/actions/metadata로 현재 서빙되는 정책의 카메라 키, 상태 크기, 동작 형태, 기본값, websocket 기능을 발견하세요.
import numpy as np
import requests
image = np.zeros((224, 224, 3), dtype=np.uint8)
payload = {
"model": "lerobot/pi05_base",
"input": {
"task": "pick up the block",
"observation": {
"images": {
"base_0_rgb": image.tolist(),
"left_wrist_0_rgb": image.tolist(),
"right_wrist_0_rgb": image.tolist(),
},
"state": np.zeros(32, dtype=np.float32).tolist(),
},
},
"runtime": {
"return_timing": True,
"prefix_cache": "auto",
"cuda_graph": "auto",
},
}
response = requests.post(
"http://127.0.0.1:30000/v1/actions/generations",
json=payload,
timeout=60,
)
response.raise_for_status()
data = response.json()
actions = data["data"][0]["action"]["values"]
print(len(actions), len(actions[0]))
print(data.get("timings"))
같은 /v1/actions/generations 엔드포인트는 Content-Type: application/msgpack도 받고, Accept: application/msgpack가 설정되면 msgpack을 반환할 수 있습니다. msgpack 클라이언트의 경우 아래 websocket 예시의 pack_numpy_payload 헬퍼를 사용해 numpy 배열을 직접 보내세요. Msgpack 요청은 서버 측에서 기본적으로 numpy 동작 출력을 사용합니다. 클라이언트가 중첩된 Python 리스트를 명시적으로 필요로 할 때만 runtime.output_format을 "list"로 설정하세요.
가장 낮은 오버헤드의 일반 HTTP 경로로는 raw 정책 응답과 함께 msgpack을 사용하세요:
payload["runtime"] = {
"return_timing": True,
"prefix_cache": False,
"cuda_graph": "auto",
"response_format": "raw",
}
response = requests.post(
"http://127.0.0.1:30000/v1/actions/generations",
data=packb(payload),
headers={
"Content-Type": "application/msgpack",
"Accept": "application/msgpack",
},
timeout=60,
)
result = unpackb(response.content)
actions = result["actions"]
lerobot/pi05_libero_base의 경우 LIBERO 카메라 이름과 8차원 상태 벡터를 사용하세요:
payload = {
"input": {
"task": "pick up the object",
"observation": {
"images": {
"image": image.tolist(),
"image2": image.tolist(),
},
"state": np.zeros(8, dtype=np.float32).tolist(),
},
},
}
4.2 일반 실시간 WebSocket (Generic Realtime WebSocket)
/v1/actions/realtime은 일반 msgpack websocket 경로입니다. 연결 시 action.metadata를 보내고 각 요청에 대해 HTTP API와 같은 action.generation 인벨로프를 반환합니다.
4.3 OpenPI 호환 WebSocket (OpenPI-Compatible WebSocket)
로봇 클라이언트는 /openpi/policy의 OpenPI 호환 msgpack websocket 엔드포인트를 사용할 수 있습니다. 서버는 연결 직후 메타데이터를 보내고, 각 클라이언트 메시지는 하나의 관측을 포함해야 합니다.
import asyncio
import msgpack
import numpy as np
import websockets
def pack_array(obj):
if isinstance(obj, np.ndarray):
return {
b"__ndarray__": True,
b"data": obj.tobytes(),
b"dtype": obj.dtype.str,
b"shape": obj.shape,
}
if isinstance(obj, np.generic):
return {
b"__npgeneric__": True,
b"data": obj.item(),
b"dtype": obj.dtype.str,
}
return obj
def unpack_array(obj):
ndarray_marker = obj.get("__ndarray__") or obj.get(b"__ndarray__")
npgeneric_marker = obj.get("__npgeneric__") or obj.get(b"__npgeneric__")
data = obj.get("data", obj.get(b"data"))
dtype = obj.get("dtype", obj.get(b"dtype"))
shape = obj.get("shape", obj.get(b"shape"))
if ndarray_marker:
return np.ndarray(
buffer=data,
dtype=np.dtype(dtype),
shape=shape,
)
if npgeneric_marker:
return np.dtype(dtype).type(data)
return obj
def packb(payload):
return msgpack.packb(payload, default=pack_array, use_bin_type=True)
def unpackb(payload):
return msgpack.unpackb(payload, object_hook=unpack_array, raw=False)
async def main():
image = np.zeros((224, 224, 3), dtype=np.uint8)
observation = {
"task": "pick up the block",
"observation.images.base_0_rgb": image,
"observation.images.left_wrist_0_rgb": image,
"observation.images.right_wrist_0_rgb": image,
"observation.state": np.zeros(32, dtype=np.float32),
}
async with websockets.connect(
"ws://127.0.0.1:30000/openpi/policy",
max_size=None,
) as websocket:
metadata = unpackb(await websocket.recv())
print(metadata)
await websocket.send(packb(observation))
result = unpackb(await websocket.recv())
actions = result["actions"]
print(len(actions), len(actions[0]))
print(result.get("server_timing"))
asyncio.run(main())
5. 구성 팁 (Configuration Tips)
- 요청-로컬
PrefixContext는 한 요청의 모든 디노이즈 스텝에서 항상 재사용됩니다. 프리픽스 K/V는 스텝별로 복제되지 않습니다. - 선택적 전역 프리픽스 캐시는 제한된 정확 일치 LRU입니다. 로봇 프레임이 바뀌면 거의 히트하지 않고, 활성화하면 무관한 미스가 그룹화된 프리픽스 실행에 들어가는 것을 막기 때문에 기본적으로 비활성화되어 있습니다. 반복 관측, 재시도, 또는 같은 카메라/상태 샘플에 대한 여러 정책 호출에는
enable_global_prefix_cache=true를 설정하세요. 그러면runtime.prefix_cache로 요청별 조회를 비활성화할 수 있습니다. - Pi0.5는 전체 어텐션 아래에서 이미지와 토크나이즈된 작업/상태 입력을 결합하므로 부분 프리픽스 재사용은 지원되지 않습니다. 어떤 입력을 바꿔도 더 깊은 레이어의 프리픽스 K/V 텐서가 바뀔 수 있습니다. 정확한 키는 SigLIP 전에 리사이즈·정규화된 픽셀을 해시하고, 유효 토큰 ID, 토큰 마스크, 카메라 마스크, 모델 리비전, dtype, 병렬 레이아웃도 해시합니다. 텐서 내용 해싱은 SRT의 CPU/CUDA 구현을 재사용합니다. pre-SigLIP 입력을 해시하면 정확한 히트가 비전 인코더와 프리픽스 트랜스포머를 모두 건너뛸 수 있습니다.
- CUDA 그래프 캡처는 단일 요청 프리픽스 인코딩과 하나의 동작-디노이즈 스텝을 대상으로 합니다. 무손실 기본값은 정확한 프롬프트 길이와 하나의 상주 프리픽스 그래프를 유지합니다. 프리픽스·동작 그래프 상주성은 각각
prefix_cuda_graph_max_entries(기본1)와action_cuda_graph_max_entries(기본4)로 독립적으로 제한됩니다. 프롬프트 버킷이 없으면 보이지 않는 시그니처는 캐시가 용량에 도달한 후 eager로 실행되어 캡처 변동을 피합니다. 디노이즈 시그니처는 프리픽스 어텐션이 전체인지 마스크인지, 배치 크기, 프리픽스 길이, 동작 호라이즌, 동작 차원, dtype, 병렬 레이아웃을 포함합니다. 마스크 인지 동작 그래프는 캡처된 그래프 안에서 현재 요청 마스크로부터 위치 ID를 재구성합니다. 동작 SP가 활성화되면 디노이즈 버킷은 로컬 동작 샤드 길이와 랭크별 위치 오프셋을 사용합니다. prompt_token_buckets는 선택적으로 프롬프트 토큰을[32, 64, 128, 200]같은 고정 크기로 오른쪽 패딩해 가까운 프롬프트 길이가 두 그래프를 모두 재사용하게 합니다. 이 모드에서 제한된 그래프 캐시는 LRU 교체를 사용하고 퇴거된 CUDA 그래프를 재설정합니다. 가장 큰 구성 버킷을 초과하는 프롬프트는 정확한 길이로 eager 상태를 유지합니다. 패딩이 GPU 리덕션 형태를 바꾸므로 이는 옵트인입니다. 네 경계 모두 주변 길이의 H200 스윕이 구조적으로 정확하고 4개의 프리픽스/동작 그래프로 제한되었지만, 비경계 프롬프트는 다섯 번의 디노이즈 스텝에서 정규화된 동작 공간에서 최대0.09589만큼 정확 길이 경로와 달랐습니다. 활성화 전에 폐루프 정책 품질을 검증하세요. 빈 버킷은 수치적으로 무손실 경로를 보존합니다.
옵트인 4-버킷 서빙 구성:
{
"prompt_token_buckets": [32, 64, 128, 200],
"prefix_cuda_graph_max_entries": 4,
"action_cuda_graph_max_entries": 4
}
- Cache-DiT는 기본 Pi0.5 경로에서 사용되지 않습니다. 현재 로봇 정책 목표는 수치적으로 무손실 추론이며, Cache-DiT 스타일 재사용은 동작 제어에 권장되기 전에 별도의 정책 품질 검증이 필요한 이미지/비디오 DiT 근사이기 때문입니다.
- CFG 병렬성을 사용해 10개의 Euler 스텝을 분할하지 마세요. 여러 후보 동작이나 미래 조건/비조건 분기 같은 독립 분기에만 사용하세요.
- 프리픽스 TP는 모델 병렬 TP가 초기화되고 VLA split 브로드캐스트 그룹이 활성화되지 않았을 때 PaliGemma 언어 프리픽스 모델에 네이티브 SGLang 병렬 선형 레이어를 사용합니다. 동작 전문가는 그 TP 레이아웃을 공유하지 않습니다. v1 split 프리픽스/동작 경로는 대신 SP 그룹을 사용합니다. 프리픽스 루트가
PrefixContext를 계산/브로드캐스트하고 동작 랭크는 SP 동작 경로를 실행합니다. - split 경로는 SP 그룹을 동작 그룹으로 사용합니다. 프리픽스 루트가
PrefixContext를 계산/브로드캐스트하고, 모든 동작 랭크가 초기 동작 노이즈를 한 번 브로드캐스트하며, 동작 호라이즌을 샤딩하고, 프리픽스가 전체 어텐션이고 ring degree가 1이며 헤드가 SP 크기로 나누어지고 호라이즌이 균등하게 샤딩 가능할 때 Ulysses 어텐션을 통해 동작 전문가를 실행합니다. 그렇지 않으면 action-root 실행으로 폴백합니다. lerobot/pi05_libero_base는 내부 패딩된 동작 텐서가 32차원을 사용하더라도 7개의 동작 차원을 반환합니다.
6. VRAM 튜닝 (VRAM Tuning)
현재 공개 Pi0.5 체크포인트의 경우 안정적인 16GB 개별 GPU 대상은 로봇 워크스테이션의 합리적인 v1 배포 기준입니다. 실제로는 드라이버, 카메라 미들웨어, 로봇 프로세스, 할당자 단편화를 위한 여유를 남기세요. Jetson/Orin 통합 메모리 디바이스는 시스템 RAM과 GPU 메모리가 같은 풀을 공유하므로 특별히 주의해야 합니다.
OpenPI 추론은 전체 fp32가 아니라 혼합 정밀도입니다. 대부분의 가중치와 연산은 bf16으로 실행되고, 선택된 안정성-민감 가중치는 fp32에 유지되며, 반환되는 동작은 float32입니다. SGLang은 기본적으로 그 정책을 반영합니다. 검증된 pi05_aloha OpenPI PyTorch 체크포인트는 119,720,608 파라미터를 fp32로 유지합니다. SGLang은 동일한 fp32 안정성 세트를 보고하며, 연속 동작 추론에 사용되지 않는 LM 헤드를 건너뛴 후 3,233,713,264 bf16 런타임 파라미터를 추가로 보고합니다. fp32 세트는 SigLIP 패치/위치 임베딩, Gemma 레이어 놈/최종 놈, 동작/시간 투영 헤드를 포함합니다. 수치적 패리티를 디버깅하지 않는 한 materialize_dtype을 bf16으로 유지하세요.
현재 H100 압력 검증은 bf16 모델 경로가 레이어별 오프로드 없이 16GB 여유 예산에 맞는 것을 보여줍니다. Pi0.5 경로는 그룹화된 요청의 모든 카메라 프레임을 하나의 SigLIP forward로 배칭한 다음 임베딩을 카메라별로 다시 분할합니다. 이는 멀티 카메라 로봇 워크로드에 중요합니다. 아래 지연 숫자는 전역 프리픽스 캐시가 비활성화되고 CUDA 그래프가 활성화된 제약 없는 H100에서 Python 그룹화 API를 사용합니다. 폐루프 로봇에서 정책을 사용하기 전에 대상 서버에서 HTTP/OpenPI websocket을 다시 실행하세요.
| 모드 (Mode) | 명령 형태 (Command Shape) | 안정적 VRAM 스냅샷 (Steady VRAM Snapshot) | 참고 (Notes) |
|---|---|---|---|
| 단일 GPU bf16 (Single GPU bf16) | --num-gpus 1, 오프로드 없음 |
H100 압력 테스트에서 로드 전 16383 MiB 여유로 맞음 |
16GB급 개별 GPU를 위한 권장 첫 경로. |
| ALOHA bf16 그룹화 (ALOHA bf16 grouped) | Python API, 배치 크기 1 / 2 / 4 / 8 | 제약 없는 H100 | 52.4 ms 단일; 65.1 ms / 2; 91.9 ms / 4; 147.4 ms / 8. |
| ALOHA 16GB-여유 압력 (ALOHA 16GB-free pressure) | Python API, 오프로드 없음 | 동일 16GB-여유 압력 | bf16 수정 후 적합이 검증됨. 대상에서 지연을 다시 실행하세요. 이전 압력 지연은 최종 OpenPI 정밀도 수정 전에 수집되었기 때문. |
| 전역 프리픽스 캐시 비활성화 (Global prefix cache disabled) | enable_global_prefix_cache=false 또는 요청 runtime.prefix_cache=false |
변화하는 프레임에 걸친 캐시 성장 방지 | 반복 정확 프레임이 흔하지 않으면 타이트 엣지 예산에 권장. |
| 오프로드 폴백 (Offload fallback) | 6.2의 구성 | 대상 의존 | 기본 bf16 경로가 대상 디바이스에서 OOM일 때만 사용. |
고정 입력/노이즈에 대해 동작 수치를 바꾸지 않으므로 이 노브를 먼저 사용하세요:
6.1 16GB 엣지 구성 (16GB Edge Config)
16GB급 로봇 워크스테이션에 이 단일 GPU 구성을 먼저 사용하세요. 파라미터를 GPU에 상주시키고, CUDA 그래프를 활성화하며, 전역 프리픽스 캐시 성장을 비활성화합니다.
{
"materialize_dtype": "bf16",
"enable_global_prefix_cache": false,
"prefix_cache_max_entries": 0,
"enable_prefix_cuda_graph": true,
"prefix_cuda_graph_max_entries": 1,
"enable_action_cuda_graph": true,
"action_cuda_graph_max_entries": 1
}
오버라이드로 단일 GPU 서버를 시작합니다:
sglang serve lerobot/pi05_base \
--model-type diffusion \
--pipeline-config-path pi05_edge_16gb.json \
--num-gpus 1 \
--warmup-mode off \
--host 127.0.0.1 \
--port 30000
H100 압력 결과를 메모리 예산 검증으로 취급하고 Jetson 지연 보장으로 취급하지 마세요. Jetson/Orin 디바이스는 공유 시스템 메모리를 사용하고 H100보다 훨씬 낮은 유효 메모리 대역폭을 가지므로, 동작 청크 주기를 결정하기 전에 대상 디바이스에서 폐루프 제어 지연을 측정하세요.
6.2 오프로드 폴백 (Offload Fallback)
기본 bf16 구성이 대상에 여전히 맞지 않을 때만 오프로드를 사용하세요. 이 모드는 고정 입력/노이즈에 대해 수치적으로 무손실이지만, 가중치를 CPU와 GPU 사이로 옮기며 지연을 상당히 나쁘게 할 수 있습니다.
적당한 폴백의 경우 먼저 캐시 성장과 선택된 단계 상주 모듈을 오프로드하세요:
{
"materialize_dtype": "bf16",
"enable_global_prefix_cache": false,
"prefix_cache_max_entries": 0,
"enable_prefix_cuda_graph": false,
"enable_action_cuda_graph": false,
"offload_prefix_image_encoder_after_embed": true,
"offload_prefix_token_embedding": true,
"offload_prefix_language_layer_count_after_prefix": 2,
"offload_action_expert_after_denoise": true,
"empty_cache_after_prefix": true
}
그래도 맞지 않으면 전체 프리픽스 레이어별 CPU 오프로드가 모든 PaliGemma 언어 레이어를 CPU에 유지하고 프리픽스 계산 중에 한 번에 한 레이어를 GPU로 옮깁니다:
{
"materialize_dtype": "bf16",
"enable_global_prefix_cache": false,
"prefix_cache_max_entries": 0,
"enable_prefix_cuda_graph": false,
"enable_action_cuda_graph": false,
"offload_prefix_image_encoder": true,
"offload_prefix_token_embedding": true,
"offload_prefix_language_layers": true,
"offload_prefix_language_layers_empty_cache": true,
"empty_cache_after_prefix": true
}
dtype이나 로더를 바꾼 후에는 대상 하드웨어에서 오프로드 검증을 반복해야 합니다. 이전 fp32-런타임 오프로드 숫자는 현재 bf16 경로와 비교할 수 없습니다.
6.3 요청별 제어 (Per-Request Controls)
HTTP 호출의 경우 서버를 재시작하지 않고 캐시나 두 CUDA 그래프 경로를 모두 비활성화할 수 있습니다:
payload = {
"input": {
"task": "pick up the block",
"observation": {
"images": images,
"state": state,
},
},
"runtime": {
"prefix_cache": False,
"cuda_graph": False,
},
}
OpenPI websocket 클라이언트의 경우 요청별 호환 제어가 필요하면 각 raw msgpack 관측에 동등한 enable_prefix_cache와 enable_cuda_graph 필드를 포함하세요.
6.4 배포 선택 (Deployment Choices)
- 그룹화된 로봇 스트림이 명시적으로 검증되지 않는 한 배치 크기를 하나의 제어 스트림으로 유지하세요. 더 많은 동시 관측은 활성화와 PrefixContext 상주성을 늘립니다.
materialize_dtype을 기본bf16으로 유지하세요.fp32는 수치 문제 디버깅에만 유용하며 메모리와 지연을 늘립니다.- 여러 GPU가 있고 그 토폴로지에서 로봇 제어 지연을 검증했을 때만 split 프리픽스/동작을 사용하세요.
--sp-degree 2 --ulysses-degree 2로 2 GPU에서 동작 호라이즌을 시퀀스-샤딩하면서 프리픽스 루트는PrefixContext를 계산·브로드캐스트할 수 있습니다. 단일 16GB급 GPU는 기본 bf16 구성부터 시도해야 합니다. num_inference_steps를 줄이는 것은 지연을 낮추지만 순수 메모리 수정이 아니며 정책 동작을 바꿀 수 있습니다. 체크포인트 기본값보다 적게 사용하기 전에 폐루프 작업 성공을 검증하세요.- CPU 오프로드는 호환성 폴백이지 선호되는 16GB 경로가 아닙니다. 양자화와 더 깊은 프리픽스/비전 샤딩이 16GB 미만 디바이스의 다음 단계입니다.
6.5 로더 및 Run:ai Model Streamer (Loader And Run:ai Model Streamer)
Run:ai Model Streamer는 safetensors를 동시에 읽고 텐서를 GPU 메모리로 스트리밍해 콜드 스타트와 체크포인트 로딩을 개선할 수 있습니다. 시작 시간이 모델 파일 I/O에 지배되는 로컬 SSD, 오브젝트 스토리지, 클라우드 배포에 유용합니다.
python[diffusion] extra는 runai_model_streamer를 포함합니다. 패키지가 설치되면 SGLang은 SGLANG_USE_RUNAI_MODEL_STREAMER=true를 통해 기본적으로 활성화합니다. 일반 safetensors 로더를 강제하려면 false로 설정하세요:
SGLANG_USE_RUNAI_MODEL_STREAMER=false \
sglang serve lerobot/pi05_base \
--model-type diffusion \
--host 127.0.0.1 \
--port 30000
Pi0.5는 현재 프로세스의 모든 로드 대상이 GPU 상주일 때 직접 SSD-to-GPU Run:ai 경로를 사용합니다. 이 검사는 rank-로컬입니다. 분산 동작 랭크는 그들의 동작-전문가 하위 집합을 GPU로 직접 스트리밍할 수 있고, CPU/오프로드 대상 텐서가 있는 rank는 CPU safetensors 폴백에 머무릅니다. 단일 GPU 검증은 13.5 GiB의 safetensors를 약 1.5 s 안에 cuda:0으로 직접 스트리밍한 다음 HTTP와 OpenPI websocket 모두에서 [50, 32] 동작을 반환했습니다.
혼합 CPU 오프로드와 저-VRAM 16GB급 모드의 경우 Pi0.5는 대상 텐서가 완전히 CUDA 상주가 아닌 rank에 대해 헤더 필터링된 안전 로더를 여전히 사용합니다. 직접 GPU 스트리밍은 저메모리 경로가 피하려는 정확한 VRAM 압력을 늘릴 수 있습니다. 분산 시작을 디버깅할 때는 SGLANG_USE_RUNAI_MODEL_STREAMER=false와 비교해 스트리머 동작을 모델 실행 동작과 분리하세요.
Run:ai Model Streamer는 파라미터, 캐시, 활성화, CUDA 컨텍스트, 그래프 버퍼가 상주한 후 안정적 추론 VRAM을 줄이지 않습니다. Pi0.5 저-VRAM 작업에서는 컴포넌트 배치, 프리픽스 캐시 크기, CUDA 그래프 상주성, CPU/오프로드를 먼저 우선시하세요. Model Streamer는 안정 메모리 예산이 올바른 후의 콜드 스타트 최적화입니다.
7. OpenPI 비교 벤치마크 (OpenPI Comparison Benchmark)
OpenPI 정책 구현에 대한 나란한 지연·동작 차이 보고가 필요할 때 bench_pi05_openpi.py를 사용하세요. 먼저 HTTP 또는 websocket 모드용 SGLang Pi0.5 서버를 시작한 다음 SGLang 저장소 루트에서 벤치마크를 실행하세요:
python python/sglang/multimodal_gen/benchmarks/bench_pi05_openpi.py \
--profile aloha \
--sglang-url http://127.0.0.1:30000 \
--openpi-checkpoint gs://openpi-assets/checkpoints/pi05_base \
--num-inference-steps 10 \
--batch-size 4 \
--repeats 20 \
--warmup 3 \
--deterministic-noise \
--output pi05_openpi_compare.json
벤치마크가 보고하는 것:
- 서버가 단계 타이밍을 반환할 때 스테이지 타이밍을 포함한
/v1/actions/generations의 SGLang HTTP 지연. --sglang-api http_msgpack설정 시 SGLang msgpack HTTP 지연. 일반 HTTP 엔드포인트를 유지하되 JSON 이미지 배열 오버헤드를 피합니다.--sglang-http-response-format raw를 사용해 컴팩트한 raw 정책 응답을 벤치마크하세요.--sglang-api openpi_ws설정 시 SGLang OpenPI 호환 websocket 지연. 지속 msgpack websocket 연결을 사용하며 JSON-over-HTTP보다 로봇 클라이언트 경로에 가깝습니다.--sglang-api python설정 시 SGLang Python 인프로세스 지연. 벤치마크 프로세스에서 네이티브 Pi0.5 파이프라인을 로드하고 HTTP, websocket, 스케줄러, 직렬화 오버헤드를 피합니다.--sglang-python-batch-mode grouped로 보수적인 네이티브 그룹화-배치 경로를 시도하세요. Python 경로는 실제 SGLang 모듈 파라미터 dtype 수와 예시 파라미터 이름도 보고합니다.Policy.infer를 통한 OpenPI 단일 요청 지연.- 그룹화된 로봇 스트림의 배치 지연. SGLang은 HTTP 모드에서 동시 HTTP 요청을, websocket 모드에서 지속 다중 연결 msgpack 호출을 사용합니다. Python 백엔드는 fresh-prefix 요청에 대해 진정한 그룹화 모델 실행을 사용할 수 있습니다. 공개
Policy.inferAPI는 단일 관측이므로 OpenPI는 내부 직접 모델 배치 경로를 기본값으로 합니다. - 동일한 OpenPI 변환 모델 입력·노이즈에서 정규화된 모델 공간의 동작 차이. 검사는
--deterministic-noise가 필요하며--action-max-abs-diff또는--action-mean-abs-diff중 하나라도 초과되면 실패합니다. 이 모드는 모델 패리티를 로봇 특정 정규화·동작 후처리와 분리합니다. LIBERO 정책은 정책 후처리 후 10개 동작만 반환하지만, 그 flow-matching 모델은 여전히 50-스텝 청크를 생성합니다. 벤치마크는 OpenPI 비정규화·호라이즌 슬라이싱 전에 그 모델 출력을 SGLang과 비교합니다.
일방향 16GB급 검사의 경우 각 백엔드를 같은 VRAM 압력 아래서 개별 실행하세요. SGLang Python 경로는 서빙과 같은 파이프라인 구성 오버라이드를 받습니다:
python python/sglang/multimodal_gen/benchmarks/bench_pi05_openpi.py \
--profile aloha \
--sglang-api python \
--sglang-python-batch-mode grouped \
--skip-openpi \
--sglang-pipeline-config-path pi05_edge_16gb.json \
--num-inference-steps 10 \
--batch-size 4 \
--repeats 5 \
--warmup 2 \
--deterministic-noise \
--disable-prefix-cache \
--disable-cuda-graph
같은 환경에서 OpenPI eager 베이스라인을 측정하려면 --skip-sglang --openpi-pytorch-compile-mode none을 사용하세요. PyTorch 네이티브 OpenPI 베이스라인의 경우 --openpi-checkpoint를 model.safetensors와 OpenPI assets/ norm-stat 트리를 포함하는 체크포인트 디렉터리로 지정하세요. keep 모드는 OpenPI의 체크포인트 기본 컴파일 설정을 보존합니다. 그룹화 Python 경로는 현재 split 프리픽스/동작 워커나 유효한 프리픽스-캐시 히트 없이 호환되는 fresh-prefix 요청을 요구하며, 다른 경우에는 요청별 실행으로 폴백합니다.
8. 검증 노트 (Validation Notes)
다음 검사는 네이티브 SGLang Pi0.5 경로로 H100 GPU에서 실행되었습니다:
| 검사 (Check) | 결과 (Result) |
|---|---|
lerobot/pi05_base 직접 종단간 |
프리픽스 길이 968, 출력 형태 [1, 50, 32], 최고 할당 메모리 12.817 GiB. |
| 공식 OpenPI 패리티 | OpenPI PyTorch 리비전 15a9616 대비, 같은 LeRobot 체크포인트 리비전·관측·노이즈로: 첫 단계 속도 max/mean 절대 차이 0.02677 / 0.00344; 프로덕션 10-스텝 정규화 동작 0.00813 / 0.00092. |
| 동작 디노이즈 CUDA 그래프 | Eager 10-스텝 디노이즈 125.4 ms; 안정 그래프 재생 50.8 ms; 최대 출력 차이 0. |
| 프리픽스 CUDA 그래프 | H200에서 동작 그래프가 이미 활성화된 상태로 ALOHA batch=1 p50이 48.04 ms에서 42.98 ms로 개선(1.118x). 5·10 스텝의 두 관측은 그래프 비활성화와 비트 단위 동일. 프리픽스 형태 버킷 하나가 약 48.5 MiB 추가. batch=4는 이점 없음으로 eager 유지. |
| 정확 전체 프리픽스 캐시 | 첫 프리픽스 패스 약 203 ms; 정확 캐시 히트 프리픽스 스테이지 약 0.2 ms. |
lerobot/pi05_libero_base 직접 종단간 |
이미지 키 image, image2, empty_camera_0; 상태 차원 8; 출력 동작 차원 7; 출력 텐서 형태 [1, 50, 32]. |
| Python 그룹화 실행 | ALOHA batch=4 그룹화 경로가 현재 혼합 정밀도에서 91.9 ms / 4 측정: 프리픽스 18.4 ms, 동작 디노이즈 61.2 ms, 전처리 요청당 약 2.4 ms. 순차 Python 루프 batch=4는 211.9 ms / 4 측정. |
sglang serve HTTP |
/v1/actions/generations가 동작 형태 [50, 32] 반환. JSON-over-HTTP는 여전히 호환되지만 이미지 배열 직렬화가 지배적. 프리픽스 캐시 비활성 msgpack HTTP는 인벨로프 응답에 57.2 ms 단일·221.9 ms / 4, runtime.response_format="raw"에 56.4 ms 단일·219.7 ms / 4 측정. |
| OpenPI websocket | /openpi/policy가 동작 형태 [50, 32] 반환. 지속 msgpack 연결·프리픽스 캐시 비활성으로 ALOHA 약 77.0 ms 단일·162.0 ms / 4 측정. |
| 2-GPU 프리픽스/동작 분할 | 이전 split 검증이 동작 형태 [50, 32] 반환하고 최대 절대 차이 0로 단일 GPU HTTP와 일치. 실제 동작-SP 변경 후 --num-gpus 2 --sp-degree 2 --ulysses-degree 2로 이 검사를 다시 실행하고 두 동작 랭크가 디노이즈 커널에 들어가는지 확인하세요. |
| OpenPI/SGLang 정밀도 | 공식 OpenPI JAX 추론은 공개 GCS 체크포인트를 선택된 fp32 안정성 연산과 함께 bf16으로 복원하고 float32 동작을 반환. 변환된 OpenPI PyTorch pi05_aloha 체크포인트는 119,720,608 fp32 안정성 파라미터 유지. SGLang은 동일 fp32 세트와 사용되지 않는 LM 헤드 건너뛴 후 3,233,713,264 bf16 런타임 파라미터 보고. |
| 네이티브 어텐션 dtype | 체크포인트 소스 텐서는 fp32일 수 있지만 SGLang은 네이티브 어텐션 백엔드 선택 전에 PiGemma·SigLIP 연산 dtype을 최종화. 백엔드 로그는 이전 실행에서 PiGemma 경로에 Using fa attention backend 표시. |
| 16GB-여유 Python 압력 | 모델 로드 전 16381 MiB 여유로 인위적으로 제약된 H100로 단일 GPU bf16 무오프로드 Python 그룹화 경로가 OOM 없이 완료. 배포 크기 조정에 압력 숫자를 사용하기 전에 정밀도·로더 변경 후 지연을 다시 실행하세요. |
| 저-VRAM 스위치 | 프리픽스 캐시 비활성화가 변화하는 로봇 프레임에 걸친 캐시 성장 방지. 프리픽스 그래프 상주성 기본 1, 동작 그래프 상주성 기본 4, 둘 중 하나를 0으로 설정하면 해당 러너 비활성화. 검증된 ALOHA 프리픽스 그래프 하나가 약 48.5 MiB 사용. 동작 그래프 상주성은 이제 별도로 제한. 무손실 경로를 위해 prompt_token_buckets를 비워 두세요. |
| 오프로드 폴백 | CPU/오프로드 모드는 수치적으로 무손실 호환 폴백으로 유지되지만, 이전 fp32-런타임 오프로드 지연 숫자는 bf16 dtype 수정 후 낡았으므로 배포 결정 전에 재검증해야 함. |
| Run:ai 직접 로더 | 단일 GPU 서브가 약 1.5 s에 13.5 GiB safetensors를 cuda:0으로 스트리밍하고 [50, 32] 동작 반환. 분산 직접 스트리밍은 이제 rank-로컬이므로 대상 split 토폴로지에서 재검증해야 함. CPU 대상의 오프로드 랭크는 여전히 안전 로더 사용. |
| OpenPI 비교 상태 | 공식 OpenPI GCS pi05_base는 JAX 체크포인트. 변환된 PyTorch eager는 torch.compile 없이 검증됨. 80GB H100에서 ALOHA OpenPI PyTorch eager는 직접 모델 배치 경로에서 약 125-130 ms 단일·약 164 ms / 4. 현재 SGLang Python 그룹화는 52.4 ms 단일·91.9 ms / 4 측정; JAX OpenPI는 짧은 검사에서 53.0 ms 단일·59.5 ms / 2. |
폐루프 컨트롤러에서 정책을 사용하기 전에 로봇 배포 환경에서 전체 HTTP 또는 websocket 스모크 테스트를 실행하세요.