LeRobot documentation

Quy trình Gán nhãn

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Quy trình Gán nhãn

lerobot-annotate theo dõi video của từng episode bằng một mô hình thị giác-ngôn ngữ (VLM) và ghi chú thích ngôn ngữ tự nhiên trở lại vào dataset của bạn. Nó điền trực tiếp hai cột ngôn ngữ từ trang Các cột ngôn ngữ và công thức — language_persistent và language_events — vào data/chunk-*/file-*.parquet.

Tóm lại: chỉ cần trỏ nó đến một tập dữ liệu LeRobot, và nó sẽ thêm các tác vụ con, kế hoạch, bộ nhớ, các ngắt lời, giọng nói và hỏi đáp trực quan mà một chính sách có thể được huấn luyện trên đó.

Cách các phần kết hợp với nhau

  your dataset                  lerobot-annotate
  (LeRobotDataset v3.0)
        │
        ▼
  ┌─────────────────────────────────────────────────────┐
  │                    read episodes                     │
  └──────────────────────────┬──────────────────────────┘
                             │
        ┌────────────────────┼────────────────────┐
        ▼                    ▼                     ▼
  ┌──────────┐      ┌───────────────┐        ┌──────────┐       one shared Qwen-VL
  │   plan   │      │ interjections │        │   vqa    │  ◀──   server (vLLM, OpenAI
  └────┬─────┘      └───────┬───────┘        └────┬─────┘        API) drives all three
       └────────────────────┼─────────────────────┘
                            │   each module stages raw JSONL
                            ▼   into .annotate_staging/
                  ┌─────────────────┐
                  │    validator    │  ◀──  checks everything
                  └────────┬────────┘
                           ▼
                  ┌─────────────────┐
                  │     writer      │
                  └────────┬────────┘
                           ▼
              data/chunk-*/file-*.parquet
              (+ meta/info.json tools)

Ba module (plan, interjections, vqa) đều tương tác với một VLM dùng chung. Mỗi module lưu kết quả đầu ra ra đĩa, một trình xác thực kiểm tra chúng, và một trình ghi duy nhất ghi đè các shard của tập dữ liệu tại chỗ.

Những gì pipeline tạo ra

Mỗi module phát ra một vài kiểu chú thích (“styles”), được định tuyến đến một trong hai cột ngôn ngữ:

Phong cách / nguyên tửCộtModule
subtask (kiểu Pi0.7 “cách làm, không phải làm gì”)language_persistentplan
plan (ban đầu + làm mới khi có sự can thiệp)language_persistentplan
memory (nén kiểu MEM)language_persistentplan
task_aug (các cách diễn đạt lại của tác vụ)language_persistentplan
interjectionlanguage_eventsinterjections
speech tool-call atom (style=null, say)language_eventsinterjections
vqa (cặp user / assistant)language_eventsvqa

Cách các tác vụ con được tạo ra

Module plan không yêu cầu VLM cung cấp các tác vụ con trong một lần duy nhất. Thay vào đó, nó sử dụng luồng mô tả → phân đoạn hai bước:

  1. Mô tả — VLM chỉ mô tả những gì nó thực sự nhìn thấy trong camera được chọn (không suy đoán về tác vụ).
  2. Segment — mô tả đó được đưa trở lại, và VLM chia episode thành các subtask nguyên tử liên tiếp.

Cả hai lượt xử lý đều xem tập dữ liệu (episode) dưới dạng bảng liên hệ có mốc thời gian — các khung hình được lấy mẫu với tần số frames_per_second (mặc định là 0,5 giây) và đóng gói thành các lưới JPEG, trong đó thời gian của từng khung hình được ghi cố định vào góc, giúp VLM trích dẫn chính xác thời điểm ranh giới. Cách này tiết kiệm token thị giác hơn nhiều so với việc dùng một hình ảnh cho mỗi khung hình, nên việc lấy mẫu có thể duy trì mật độ cao; các tập dữ liệu dài hơn max_frames_per_prompt sẽ được chia thành các cửa sổ với cùng mật độ và sau đó được gộp lại. Cả hai prompt cũng đều chứa định nghĩa ranh giới sự kiện mang tính nhân quả (một sự kiện mới bắt đầu khi một vật thể được cầm / được thả / di chuyển đến một vị trí mới / nắp thay đổi trạng thái / nội dung di chuyển) để làm rõ vị trí đặt các điểm cắt.

Tùy chọn, một lượt seeded-relabel thứ ba (--plan.subtask_seeded_relabel) xem xét lại từng span với các contact sheet của đoạn trước/lần hiện tại/sau và chỉnh sửa nhãn một cách tối thiểu, sử dụng nhãn đầu tiên làm tiên nghiệm — nó giữ các ranh giới cố định và chỉ làm sắc nét hơn về mặt từ ngữ, với chi phí là một gọi bổ sung cho mỗi subtask.

Các span kết quả sau đó được ghép lại thành một lớp phủ toàn tập không có khoảng trống, đảm bảo mỗi khung hình có đúng một subtask đang hoạt động. Xem Running on Hugging Face Jobs để biết các cài đặt cho môi trường sản xuất (một camera, contact sheet có dấu thời gian, tự động tạo subtask theo cửa sổ).

Công cụ

Writer không thêm cột tools vào parquet. Danh mục công cụ nằm trong meta/info.json["tools"] (xem Tools). Sau mỗi lần chạy, pipeline đảm bảo rằng schema say chuẩn nằm trong danh sách đó, đồng thời giữ lại các công cụ bạn đã khai báo trước đó.

Muốn thêm công cụ của riêng bạn? Chỉnh sửa trực tiếp meta/info.json["tools"] — pipeline sẽ giữ nguyên những gì đã có sẵn. Điều này làm cho công cụ hiển thị với chat template, giúp mô hình có thể học cách generate lời gọi. Lớp runtime thực sự executes một lời gọi đã được generate (giao thức Tool / TOOL_REGISTRY dưới src/lerobot/tools/) hiện chưa được triển khai — tài liệu Tools đánh dấu các phần đó là chưa được triển khai.

Chạy trên Hugging Face Jobs

Việc chú thích một bộ dữ liệu thực tế đòi hỏi GPU đủ lớn để phục vụ VLM, vì vậy lerobot-annotate có thể tự triển khai lên Hugging Face Jobs — giống như lerobot-train. Thêm --job.target=<flavor> vào chính xác lệnh mà bạn sẽ chạy cục bộ và nó sẽ chạy trên phần cứng đó thay thế:

hf auth login   # once

uv run lerobot-annotate \
    --repo_id=user/my_dataset \
    --new_repo_id=user/my_dataset_annotated \
    --push_to_hub=true \
    --vlm.model_id=Qwen/Qwen3.6-27B \
    --vlm.num_gpus=1 \
    --vlm.serve_command="vllm serve Qwen/Qwen3.6-27B --tensor-parallel-size 1 \
        --max-model-len 32768 --gpu-memory-utilization 0.8 \
        --uvicorn-log-level warning --port {port}" \
    --vlm.serve_ready_timeout_s=1800 \
    --vlm.chat_template_kwargs='{"enable_thinking": false}' \
    --job.target=h200

Điều này gửi một tác vụ h200 đơn GPU mà:

  1. bắt đầu từ ảnh vllm/vllm-openai và cài đặt lerobot lên trên,
  2. khởi động một máy chủ vLLM cho mỗi GPU và điều khiển nó qua API tương thích OpenAI,
  3. chạy các module plan / interjections / vqa trên bộ dữ liệu,
  4. với --push_to_hub=true, tải kết quả lên --new_repo_id (hoặc trở lại --repo_id tại chỗ nếu bạn để trống tham số đó).

Lệnh này sẽ phát trực tiếp nhật ký của công việc; Ctrl-C sẽ ngắt kết nối mà không hủy bỏ nó. Liệt kê các loại phần cứng khả dụng cùng bảng giá của chúng bằng hf jobs hardware.

Qwen3.6 được phát hành với chế độ suy nghĩ (thinking) bật, điều này tiêu tốn ngân sách token mà bộ chú thích cần cho câu trả lời JSON của nó — --vlm.chat_template_kwargs='{"enable_thinking": false}' sẽ tắt chế độ này. Nếu không có --push_to_hub=true, bộ dữ liệu đã được chú thích sẽ bị loại bỏ khi pod thoát.

Các tùy chọn công việc

CờMặc địnhNó hoạt động như thế nào
--job.targetlocalFlavor của HF Jobs để chạy (ví dụ: h200, h200x4). Bỏ qua/local chạy tại đây.
--job.imagevllm/vllm-openai:latestẢnh runtime cho pod.
--job.timeout2hGiới hạn thời gian thực. Hãy tăng lên cho các bộ dữ liệu lớn.
--job.detachfalseGửi và thoát thay vì phát trực tiếp nhật ký.
--job.lerobot_refmainTham chiếu Git của lerobot được cài đặt trên pod — trỏ đến một nhánh để kiểm tra các thay đổi.
--job.tags[]Các thẻ bổ sung trên công việc và trên bất kỳ bộ dữ liệu nào mà nó đẩy lên (lerobot luôn được thêm vào).

Với bộ dữ liệu lớn hơn, hãy mở rộng lên h200x4 và tăng --vlm.parallel_servers / --vlm.num_gpus cho phù hợp, đồng thời dành thêm dung lượng cho công việc, ví dụ bằng --job.timeout=8h.

Chạy từ xa cần --repo_id (pod sẽ kéo dataset từ Hub; --root chỉ định một thư mục chỉ có trên máy của bạn). Dataset chỉ tồn tại trong bộ nhớ đệm cục bộ sẽ được đẩy lên một repo riêng tư trước.

Các tùy chọn chính

Đây là các cờ bạn sẽ dùng thường xuyên nhất. Chạy lerobot-annotate --help để xem các tùy chọn khác; các giá trị mặc định được điều chỉnh cho các tập hợp thao tác ngắn.

Dataset vào / ra

CờMặc địnhNó hoạt động như thế nào
--repo_id—Bộ dữ liệu Hub cần chú thích (được tải xuống nếu --root không được đặt).
--root—Thay vào đó, hãy chú thích một thư mục bộ dữ liệu cục bộ.
--new_repo_id—Đẩy kết quả đến một kho lưu trữ mới (không thay đổi kho lưu trữ nguồn).
--push_to_hubfalseTải lên sau khi chú thích (vào --new_repo_id, nếu không thì quay lại --repo_id).
--only_episodestất cảChỉ chú thích các chỉ mục tập này (tiện lợi cho một lần chạy thử).
--seed1729Khởi tạo các RNG chọn thời điểm xen kẽ + các loại câu hỏi VQA.

Các module nào được chạy

Mọi module đều được bật theo mặc định và có thể được bật/tắt độc lập (đặt thành false để bỏ qua, ví dụ để lặp lại từng module một):

CờMặc địnhTắt
--plan.enabledtruesubtasks + plan + memory + task_aug
--interjections.enabledtruecác từ cảm thán + các đơn vị ngôn ngữ
--vqa.enabledtruecác cặp VQA

VLM ( --vlm.* )

CờMặc địnhNó hoạt động như thế nào
--vlm.model_idQwen/Qwen3.6-27BMô hình cần triển khai và prompt.
--vlm.camera_keyđầu tiên images.*Camera mà mỗi prompt được dựa trên.
--vlm.serve_commandautoLệnh vllm serve … chính xác (cài đặt kích thước TP, bộ nhớ GPU, --max-model-len tại đây).
--vlm.parallel_servers1Các máy chủ độc lập cho định tuyến vòng tròn (một máy chủ cho mỗi GPU).
--vlm.num_gpus0Số GPU trên mỗi máy chủ (0 = một GPU mỗi máy).
--vlm.client_concurrency16Các yêu cầu đang xử lý trên tất cả các máy chủ.
--vlm.max_new_tokens512Giới hạn sinh mỗi lần gọi.
--vlm.temperature0.2Nhiệt độ lấy mẫu.
--vlm.reasoning_effortnullGợi ý ngân sách suy nghĩ (low/medium/high) được chuyển tiếp đến các máy chủ tương thích OpenAI.

Các tác vụ con / kế hoạch / bộ nhớ ( --plan.* )

CờMặc địnhNó hoạt động như thế nào
--plan.frames_per_second2.0Tốc độ lấy mẫu khung cho các bảng liên hệ (2.0 = một khung mỗi 0,5 giây).
--plan.max_frames_per_prompt60Ngân sách khung cho mỗi lần gọi VLM. Các episode có quá trình lấy mẫu vượt quá ngưỡng này sẽ được tự động chia cửa sổ ở cùng mật độ, sau đó được ghép lại.
--plan.contact_sheet_columns5Số cột trên lưới contact-sheet (contact_sheet_frames_per_sheet ô, hàng thời gian theo thứ tự chính).
--plan.plan_max_steps8Giới hạn trên của số lượng tác vụ con mỗi tập.
--plan.subtask_describe_firsttrueChạy bước grounding describe→segment (chất lượng subtask tốt nhất; +1 call/episode).
--plan.subtask_seeded_relabelfalseĐợt hai: dán nhãn lại từng nhiệm vụ con từ các bảng liên hệ trước/hiện tại/sau của nó, khởi tạo bằng nhãn đầu tiên (+1 cuộc gọi/nhiệm vụ con).
--plan.subtask_relabel_frames5Các khung được lấy mẫu đều trên mỗi bảng phân đoạn trong bước dán nhãn lại (chỉ được sử dụng khi subtask_seeded_relabel=true).
--plan.emit_plantrueXuất các dòng plan có đánh số (false = chỉ subtasks + bộ nhớ).
--plan.emit_memorytrueXuất các dòng memory (false = chỉ subtasks + plan); đối xứng với emit_plan.
--plan.n_task_rephrasings10Số lượng diễn đạt lại task_aug cần xuất ra (0 để tắt).
--plan.derive_task_from_videoif_shortSử dụng tác vụ của bộ dữ liệu như hiện có (off), chỉ khi nó bị thiếu/ngắn (if_short), hoặc luôn suy ra lại từ video (always).

Từ cảm thán + VQA

CờMặc địnhNó hoạt động như thế nào
--interjections.max_interjections_per_episode3Giới hạn số cặp xen ngang/lời thoại mỗi tập.
--vqa.vqa_emission_hz1.0Tần suất các cặp VQA được phát ra.
--vqa.restrict_to_default_camerafalseChỉ dựa vào --vlm.camera_key cho VQA (nếu không, mọi camera).
--vqa.coordinate_scale1000Lưới bbox/keypoint của VLM, lưu dưới dạng [0, 1] (none = px).
--executor.episode_parallelism16Số tập được xử lý đồng thời trong mỗi giai đoạn.

Đóng góp các module mới

Pipeline được thiết kế để mở rộng, và sự đóng góp luôn được chào đón — một module hoàn toàn mới (ví dụ, vết tích quỹ đạo hoặc affordances), một mẫu prompt mới, một luồng grounding thông minh hơn, hoặc các bản sửa lỗi chất lượng cho các module plan / interjections / vqa hiện có.

Mọi module đều nằm dưới src/lerobot/annotations/steerable_pipeline/modules/, chia sẻ client VLM và bộ nhớ đệm keyframe, ghi kết quả thô của nó vào cây staging, và được gắn vào executor như một phase riêng. Có ý tưởng? Hãy mở một issue hoặc PR trên kho.

Cách các công thức sử dụng kết quả đầu ra

Các chú thích được thiết kế để được đọc bởi các công thức (xem Các cột ngôn ngữ và công thức). Thông thường:

  • Các nhánh low-level / high-level / memory-update đọc subtask / plan / memory từ language_persistent.
  • một nhánh phản hồi xen ngang đọc các sự kiện interjection cùng với nguyên tử giọng nói ghép cặp (được hợp nhất thành một lượt trợ lý duy nhất thông qua tool_calls_from) và bản cập nhật plan tương ứng tại cùng một mốc thời gian.
  • Một nhánh VQA đọc các cặp (vqa, user) và (vqa, assistant) từ language_events.

Vì sao trạng thái và sự kiện được tách riêng

Hai ý tưởng định hình thiết kế:

  1. Trạng thái bền vững so với các sự kiện chính xác. Các hàng trạng thái bền vững (subtask, plan, memory) áp dụng cho toàn bộ tập và trả lời câu hỏi “điều gì đang đúng ngay bây giờ?”. Các hàng sự kiện (interjection, vqa, giọng nói) chỉ xuất hiện trên khung duy nhất có dấu thời gian khớp. Dấu thời gian được sao chép trực tiếp từ tệp parquet nguồn — không bao giờ được tính lại bằng số dấu phẩy động.
  2. Một lượt VLM. Cả ba module đều dùng chung một client VLM (client tương thích OpenAI giao tiếp với máy chủ vLLM của job), nên bạn chỉ phải trả chi phí nạp một mô hình cho mỗi dataset, thay vì ba lần.

Chạy lại một module duy nhất

Mỗi module lưu output thô của nó vào <root>/.annotate_staging/episode_{N:06d}/<module>.jsonl. Điều này giúp việc lặp lại prompt trở nên rẻ hơn: chạy lại một module chỉ ghi đè lên JSONL của chính nó, sau đó writer sẽ tổng hợp lại file parquet cuối cùng. Tắt các module không cần bằng --plan.enabled=false (tương tự với --interjections.enabled / --vqa.enabled) để kiểm thử từng cái một.

Những gì trình xác thực kiểm tra

Trước khi writer chạy, StagingValidator xác nhận:

  • mỗi hàng sự kiện khớp chính xác với một mốc thời gian khung hình thực tế;
  • không có cặp lời nói / từ cảm nào bị bỏ lại;
  • plan được làm mới tại mỗi mốc thời gian ngắt lời;
  • Các dòng memory rơi vào ranh giới của các subtask (một cảnh báo, không phải lỗi);
  • mỗi content của trợ lý VQA là JSON hợp lệ ở một trong các dạng bbox / keypoint / count / attribute / spatial;
  • mỗi hàng sẽ được đưa vào cột được chọn bởi column_for_style(style).

Bất kỳ lỗi nào cũng sẽ hủy tác vụ ghi. Truyền --skip_validation=true để ghi đè khi gỡ lỗi.

Nguồn gốc của các ý tưởng trong từng module

  • plan — các tác vụ con. Hi Robot (Shi 2025) cho mức độ chi tiết nguyên tử (“nhặt một lá xà lách”, “đặt bát vào hộp”); Pi0.7 (Physical Intelligence 2025) cho chi tiết “cách làm, không phải làm gì”.
  • plan — bộ nhớ. MEM (Torne 2026): chỉ giữ lại thông tin liên quan tối thiểu — bảo toàn kết quả, loại bỏ các thuộc tính cụ thể.
  • interjections. Phân loại kịch bản của Hi Robot: tác vụ tiêu cực, sửa lỗi theo ngữ cảnh, ràng buộc cụ thể, sở thích. Lời nói là một nguyên tử chỉ gọi công cụ (tool_calls=[{type:function, function:{name:"say", arguments:{text:...}}}]).
  • vqa. ECoT (Zawalski 2024) cho các đặc trưng có cơ sở (bounding box [x_min, y_min, x_max, y_max] và keypoints, được lưu dưới dạng phân số [0, 1] của ảnh) và Steerable VLA Policies (Zhao 2025) cho việc grounding đa mức trừu tượng. Pi0.7 cũng grounding câu trả lời xuyên suốt các mức trừu tượng.

Khi cải thiện một module, hãy điều chỉnh prompt template của nó trong src/lerobot/annotations/steerable_pipeline/prompts/ thay vì viết lại từ đầu.

Chi phí ước tính

Mỗi tập, pipeline thực hiện khoảng plan_max_steps lần gọi plan, max_interjections_per_episode lần gọi interjection và vqa_emission_hz × episode_seconds lần gọi VQA. Với các giá trị mặc định (8 subtasks, 3 interjections, 1 Hz × 3 pairs) trên một tập 30 giây, điều đó tương đương ~50 lần gọi VLM.

Bộ nhớ lưu trữ vẫn nhỏ: language_persistent chỉ tối đa vài chục KB mỗi episode (parquet mã hóa từ điển cho một mục duy nhất lặp lại qua các khung hình), và language_events trống trên hầu hết các khung hình — kích thước của nó tỷ lệ với số lượng sự kiện phát ra, chứ không phải num_frames × num_emissions.

Update on GitHub