ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

使用 Daft 高效读取 LeRobot 机器人学习数据集:v2/v3 布局、分幕过滤与视频帧解码实战

使用 Daft 高效读取 LeRobot 机器人学习数据集:v2/v3 布局、分幕过滤与视频帧解码实战 使用 Daft 高效读取 LeRobot 机器人学习数据集v2/v3 布局、分幕过滤与视频帧解码实战【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft本篇技术指南以 Daft 的daft.datasets.lerobot模块为核心介绍如何在 Daft 中按episode幕粒度过滤 LeRobot 机器人学习数据集再仅对需要的幕展开为frame帧级别的数据并按时间戳对齐解码各相机的 MP4 视频帧。读完本文你将掌握read、read_episodes、load_episode_frames、read_tasks四个 API 的完整用法理解 v2.0/v2.1 与 v3.0 两种数据集布局的差异以及底层按时间戳就近解码的实现原理从而在 Daft 的分布式 DataFrame 上高效完成机器人数据集的筛选、展开与多模态加载。LeRobot 数据集在 Daft 中的组织方式LeRobot 是 Hugging Face 上广泛使用的机器人学习robot learning数据集格式。一个典型的 LeRobot 数据集在磁盘 / Hub 上的目录树包含三部分meta/数据集元信息如info.json、分幕元数据、任务元数据data/每帧的传感器数据以 Parquet 格式存储如关节角度、动作、状态等videos/每台相机的原始视频以 MP4 格式存储。Daft 通过 daft/datasets/lerobot.py 将这一目录布局暴露为惰性 DataFrame API。核心设计理念是先停留在 episode 粒度进行过滤再只对保留下来的 episode 展开到 frame 粒度。这样可以用最少的 I/O 完成数据筛选避免把整个数据集的所有帧一次性读入。该模块支持的 LeRobot 数据集版本与对应布局如下这也是 docs/datasets/lerobot.md 中的布局速查表内容v2.0 / v2.1v3.0Episode 元数据meta/episodes.jsonlmeta/episodes/**/*.parquetFrame 数据data/chunk-XXX/episode_YYYYYY.parquet每幕一个文件data/chunk-XXX/file-YYY.parquet多幕打包进共享分片视频每台相机每个 episode 一个 MP4共享 MP4 分片 from_timestamp偏移任务元数据meta/tasks.jsonlmeta/tasks.parquetjsonl 兜底数据集的版本号从meta/info.json的codebase_version字段读取。从源码看daft/datasets/lerobot.py版本号会被解析为(major, minor)元组仅支持 major 为 2 或 3 的版本即 v2.0、v2.1 与 v3.x其余版本会抛出ValueError。测试 tests/datasets/test_lerobot.py 验证了传入v1.0时会报错提示当前仅支持 v2.0、v2.1 和 v3.x。Beta 提示该 API 较新后续可能随优化演进例如与 Parquet 谓词下推predicate pushdown做更深的集成使用时请注意版本兼容性。快速上手帧级读取Frame-level reads最常见的场景是直接读取整个数据集为每帧一行的惰性 DataFrame使用daft.datasets.lerobot.readimport daft from daft.datasets import lerobot df lerobot.read(your-org/your-robot-dataset, load_video_framesTrue)read的行为是读取每幕元数据与data/下的每帧传感器数据按episode_index连接并将每幕的元数据广播broadcast到该幕的所有帧上。如果传入load_video_framesTrue或指定相机 key / key 列表还会把每帧对应时刻的相机画面从 MP4 中解码出来生成图像列。从源码daft/datasets/lerobot.py可以看到read的完整签名read( dataset_uri: str, io_config: IOConfig | None None, include_stats: bool False, load_video_frames: str | list[str] | bool False, ) - DataFrame参数说明dataset_uri数据集标识支持三种形式见下文io_config远程读取时的可选 IO 配置例如对象存储的凭据与端点参考 docs/connectors/aws.md 中的对象存储配置方式include_stats为True时保留每幕的stats/*列各传感器特征的 min/max/mean/std/分位数等统计量默认False隐藏load_video_frames指定要解码为图像列的相机 key默认False不解码。传True解码全部视频特征传单个字符串如observation.image或字符串列表如[observation.image, observation.wrist]则只解码指定相机。解码依赖可选的 PyAV 与 Pillow。dataset_uri的三种合法形式dataset_uri参数在源码中由_normalize_dataset_rootdaft/datasets/lerobot.py规范化接受本地目录包含meta/、data/、videos/等子目录的本地路径如/data/lerobot/your-robot-datasethf://datasets/org/nameURIHugging Face Hub 上的数据集其仓库目录树与磁盘布局一致裸的org/name字符串会被自动解释为hf://datasets/org/name源码中通过正则[\w.-]/[\w.-]识别 Hub repo id。远程路径同样支持s3://...等对象存储 URI。测试 tests/datasets/test_lerobot.py 中的tiny_lerobot_v3/tiny_lerobot_v21fixture 展示了最小可用的本地目录布局可供搭建本地测试数据时参考。Episode 粒度读取先过滤再展开对于大规模数据集直接读取全部帧往往代价高昂。Daft 推荐的两阶段工作流是用read_episodes读取每幕一行的元数据并在这一层做过滤用load_episode_frames把过滤后的幕展开为帧。import daft from daft.datasets.lerobot import load_episode_frames, read_episodes repo hf://datasets/your-org/your-robot-dataset ep read_episodes(repo) long ep.where(daft.col(length) 100) # 只保留长度超过 100 帧的幕 frames load_episode_frames(long, repo) # 只为这些幕读取帧数据read_episodes每幕一行read_episodesdaft/datasets/lerobot.py按数据集版本读取不同的元数据文件v3读取meta/episodes/**/*.parquet可能包含多分片v2读取meta/episodes.jsonl其中任何额外的每幕字段都会被保留为 DataFrame 的列。其完整签名read_episodes( dataset_uri: str, io_config: IOConfig | None None, include_meta: bool False, include_stats: bool False, include_video_metadata: bool False, ) - DataFrame默认情况下每幕内部的meta/与stats/列会被隐藏需要时通过开关显式开启include_metaTrue保留 v3 中定位每幕自身元数据分片的内部列meta/episodes/*的 chunk/file 索引对 v2 无效果v2 没有这类簿记列include_statsTrue保留每幕统计量。v3 中它们是 episode Parquet 内的stats/*列v2.1 中则通过meta/episodes_stats.jsonl以episode_index为键 join 进来join 失败时静默跳过见 daft/datasets/lerobot.pyinclude_video_metadataTrue保留每幕的videos/{key}/*列chunk/file 索引与 from/to 时间戳用于定位该幕的影像。v2 中from_timestamp恒为 0因为每幕有独立的 MP4。测试 tests/datasets/test_lerobot.py 验证了默认隐藏meta/episode_uuid、stats/action_mean而开启对应开关后这些列会出现。load_episode_frames展开为每帧一行load_episode_frames(episodes, dataset_uri, io_configNone)daft/datasets/lerobot.py读取data/**下的逐帧 Parquetv3 的共享分片或 v2 的每幕文件并与传入的 episode DataFrame 按episode_index连接产生每帧一行、且每幕元数据广播到其帧上的结果。关键点务必先过滤再传入只有幸存下来的 episode 才会参与 join 贡献帧这正是episode 粒度过滤 → 帧粒度展开工作流的意义所在结果中会剔除data/chunk_index、data/file_index等内部定位列传入的episodesDataFrame 必须包含episode_index列通常来自read_episodes可先过滤。测试 tests/datasets/test_lerobot.py 验证了2 个 episode 共 4 帧的数据集全量展开得到 4 行只过滤episode_index 0后展开得到 2 行。任务元数据read_tasksread_tasks用于加载任务元数据如任务名与task_index的映射from daft.datasets.lerobot import read_tasks tasks read_tasks(your-org/your-robot-dataset)其行为daft/datasets/lerobot.py是优先读取meta/tasks.parquet当前 LeRobot 的默认格式文件缺失时回退到meta/tasks.jsonlv2 的默认格式。测试 tests/datasets/test_lerobot.py 与 tests/datasets/test_lerobot.py 分别验证了 v3 走 parquet、v2.1 走 jsonl 两条路径。视频帧解码按时间戳就近对齐的实现原理load_video_frames是read中最有技术含量的部分。其核心机制是按时间戳timestamp解码而非按帧序号frame_index解码Daft 将每幕在文件中的起始偏移from_timestamp与该帧在幕内的本地时间戳timestamp相加得到该帧在 MP4 中的绝对时间戳随后在解码结果中匹配与目标时间戳最接近、且误差在半个帧周期half a frame period之内的帧。两个版本在此处的差异v3一个分片shard内连续打包了多幕from_timestamp即该幕在分片内的起始位置从源码注释看daft/datasets/lerobot.py如果按frame_index取帧在 v3 中只在分片内第一幕这一特殊情况下碰巧正确因此必须按时间戳对齐v2每幕有自己独立的 MP4from_timestamp为0幕内本地时间戳在该文件中即为绝对时间戳。容差tolerance_s的计算方式为1.0 / fps / 2.0即半个帧周期源码 daft/datasets/lerobot.py任何比这更近的帧都无歧义地是那一帧。解码依赖视频帧解码需要 PyAV 与 Pillow 两个可选依赖pip install av pillow若缺失源码会在 UDF 入口显式抛出ImportError提示安装daft/datasets/lerobot.py提示文案为pip install av与pip install daft[video]或pip install pillow。底层实现批量分片解码与聚类 seek视频帧解码通过func.batch批量 UDF_decode_lerobot_video_timestamp实现daft/datasets/lerobot.py并配合几个关键常量_DECODE_BATCH_SIZE 16每批解码的行数_DECODE_FRAME_BUDGET 20_000每个分片每批最多解码的帧数兜底上限防止异常情况下解码失控_RESEEK_GAP_S 10.0目标时间戳之间的间隔超过该秒数时选择 seek 跳过而非顺解码。其优化思路对应_decode_one_sharddaft/datasets/lerobot.py值得展开说明按分片分组、一次打开同一批中共享同一 shard 文件的行被分组每个 shard 只打开一次而不是每帧打开一次时间戳聚类把同一 shard 内的目标时间戳排序后按_RESEEK_GAP_S聚成若干簇。簇内目标之间间隔小直接顺序解码比反复 seek 更划算seek 会回退到前一个关键帧重新解码簇间间隔大v3 分片中幕与幕可能相隔数分钟则 seek 跳过避免浪费解码预算seek 到前一个关键帧再前向解码与 LeRobot 官方行为一致container.seek(..., backwardTrue)回退到目标时间之前的最近关键帧再向前逐帧解码每目标只保留一帧对每个目标行保留距离最近的已解码帧内存占用有界与时间跨度无关容差校验若某个目标时间戳在容差内找不到匹配帧会抛出明确错误No frame matched timestamp ... within tolerance ...而不是静默返回错误帧。解码输出为 RGB 图像PIL.ImagemodeRGBDaft 会将其包装为DataType.image()类型的列源码预留了width/height参数传 0 表示不解码后缩放按原始分辨率解码。测试套件对这套机制有充分验证tests/datasets/test_lerobot.py解码结果与直接使用 PyAV 逐帧解码的原始结果逐像素一致np.array_equaltests/datasets/test_lerobot.py一批数据横跨两个 shard、时间戳乱序、含重复行时每行都能拿到属于自己shard 和时间戳的帧tests/datasets/test_lerobot.py同一 shard 内相距约 29 秒的两个目标在极小解码预算下仍能正确 seek 完成防止顺解码穿过大间隔耗尽预算的回归tests/datasets/test_lerobot.py超出容差的目标时间戳会抛错而非静默失败。内部列不会泄漏解码过程中会临时使用videos/{key}/video文件句柄、videos/{key}/from_timestamp等内部列但在read返回前会被exclude掉daft/datasets/lerobot.py。测试 tests/datasets/test_lerobot.py 断言最终结果中不存在任何以videos/开头的列。每个视频特征对应一列以相机 key 命名的图像列如camera.test。路径模板解析从info.json到真实文件路径LeRobot 数据集的meta/info.json中带有data_path、video_path模板如videos/chunk-{episode_chunk:03d}/{video_key}/episode_{episode_index:06d}.mp4。Daft 的_format_path_templatedaft/datasets/lerobot.py会将这些模板编译为 Daft 字符串表达式数字占位符如{episode_index:06d}用lpad补零宽度来自格式说明符06→ 补足 6 位字符串绑定如video_key直接作为字面量拼接episode_chunk由episode_index // chunks_size计算chunks_size默认 1000_DEFAULT_CHUNKS_SIZE也读取info.json中的chunks_sizev2 视频路径模板默认值为videos/chunk-{episode_chunk:03d}/{video_key}/episode_{episode_index:06d}.mp4_V2_VIDEO_PATH。测试 tests/datasets/test_lerobot.py 验证了episode_index0与episode_index1000分别生成chunk-000/.../episode_000000.mp4与chunk-001/.../episode_001000.mp4即分片按每 1000 幕滚动。实战建议与已知限制结合文档与源码给出几点实操建议善用 episode 粒度过滤面对数万幕、数十亿帧的大型机器人数据集先用read_episodes按length、task_index等字段过滤再load_episode_frames展开可大幅减少 Parquet 读取量按需加载视频只有真正需要图像时才开启load_video_frames且尽量只传需要的相机 key如observation.image而非True避免为用不到的相机浪费解码算力解码是 CPU 密集操作配合 Daft 的分布式执行可以并行摊薄成本依赖先行使用视频解码前确保pip install av pillow版本兼容当前仅支持 v2.0 / v2.1 / v3.x通过meta/info.json的codebase_version判定遇到其他版本会显式报错而非静默失败注意 Beta 状态API 可能随优化如 Parquet 谓词下推的深度集成演进升级 Daft 版本后建议回归验证。想要深入理解实现细节可以继续阅读以下仓库文件daft/datasets/lerobot.py全部四个公开 API 与批量解码 UDF 的实现tests/datasets/test_lerobot.pyv2/v3 布局、视频帧解码、路径模板、容差校验的完整测试docs/api/datasets.md数据集 API 在官方文档中的入口daft/file/video.py 与 daft/functions/file_.pyvideo_file文件句柄表达式的实现是视频解码链路的前置环节。【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表