FiftyOne 核心概念入门:从 Dataset、Sample、Field 到视图与聚合的完整实战指南
【免费下载链接】fiftyoneRefine high-quality datasets and visual AI models项目地址: https://gitcode.com/GitHub_Trending/fi/fiftyone
本篇技术指南以 FiftyOne 官方用户指南中的 FiftyOne Basics 为骨架,系统讲解 FiftyOne 数据可视与数据集管理的基本概念:Dataset(数据集)、Sample(样本)、Field(字段)、媒体类型、标签(Tag)、元数据(Metadata)、标注(Label)、DatasetView(数据集视图)与聚合框架。结合本仓库中 fiftyone/core 的实际源码实现,你将掌握如何用 Python 快速建模自己的图像/视频数据、动态扩展 schema、按需筛选与排序样本,并对整个数据集高效计算统计量,为后续的数据导入、模型评估与可视化打下基础。
总览:FiftyOne 的基本数据模型
FiftyOne 的基本数据模型由三层概念组成:
- Dataset(数据集):有序的样本集合,是核心数据结构,通过 Python 库与 FiftyOne App 共同操作;
- Sample(样本):数据集中的原子元素,存储与某一份媒体数据(如图像或视频)相关的全部信息;
- Field(字段):
Sample实例的属性,存储关于样本的可定制信息。
把Dataset想象成一张表,每一行是一个Sample,每一列就是一个Field。这种"表 + 行 + 列"的心智模型贯穿整个 FiftyOne 的使用过程。
从源码看,三者分别定义于 fiftyone/core/dataset.py(Dataset类,第 278 行)、fiftyone/core/sample.py(Sample类,第 660 行)与 fiftyone/core/view.py(DatasetView类,第 34 行)。
Dataset:数据集的创建与打印
Dataset是 FiftyOne 的核心数据结构,它允许你轻松地加载、修改、可视化与评估数据及其相关标注(分类、检测框等),并为把图像、视频、注释和模型预测加载成可在 FiftyOne App 中可视化、可与标注源同步、可与他人共享的格式提供了一致接口。对于自己的数据集合,加载为Dataset后即可方便地搜索和排序样本,识别独特样本以及标注中可能的错误;对于训练模型而言,模型的预测结果及 embeddings、logits 等关联数据都可以加载进Dataset,借助 FiftyOne App 直观地调试模型学到了什么(即使是多边形、分割掩码等复杂标注类型),并据此把更具代表性、模型难以识别的样本补充进训练集。
创建数据集只需一行代码:
import fiftyone as fo # Create an empty dataset dataset = fo.Dataset("test-dataset") print(dataset)输出如下:
Name: test-dataset Media type: None Num samples: 0 Persistent: False Tags: [] Sample fields: id: fiftyone.core.fields.ObjectIdField filepath: fiftyone.core.fields.StringField tags: fiftyone.core.fields.ListField(fiftyone.core.fields.StringField) metadata: fiftyone.core.fields.EmbeddedDocumentField(fiftyone.core.metadata.Metadata) created_at: fiftyone.core.fields.DateTimeField last_modified_at: fiftyone.core.fields.DateTimeField可见一个新建数据集自带 6 个默认样本字段:id、filepath、tags、metadata、created_at、last_modified_at。这些字段由 fiftyone/core/sample.py 中的get_default_sample_fields()定义,对应到 fiftyone/core/fields.py 中的ObjectIdField、StringField、ListField、EmbeddedDocumentField与DateTimeField等字段类型。
Dataset由多个Sample对象组成,这些Sample包含Field属性,所有属性都可以动态创建、修改和删除。FiftyOne 使用轻量级非关系型数据库存储数据集,因此可以轻松扩展到任意大小的数据集,而无需担心机器的内存(RAM)约束。数据集是有序的样本集合,当一个Sample被添加到Dataset时,它会被分配一个唯一 ID,可用于从数据集中检索该样本。这一机制体现在 fiftyone/core/dataset.py 的Dataset.__init__与_create_dataset中——数据集通过DatasetSingleton元类保证同名数据集是单例对象,且样本被赋予ObjectId形式的唯一id。
注意:
Dataset(name, persistent=False, overwrite=False)构造函数支持三个参数:name为数据集名称(默认使用get_default_dataset_name()),persistent决定会话结束后数据集是否保留在数据库中,overwrite决定是否覆盖同名数据集。更多数据加载方式参见 导入数据集指南。
数据集的切片及批量操作通过**数据集视图(DatasetView)**完成。DatasetView提供对Dataset的一个视图,可沿各种轴过滤、排序、采样等,以获得所需的样本子集。完整用法参见 使用数据集指南。
Sample:样本的创建
Sample是Dataset的原子元素,存储与给定数据(如图像或视频)相关的所有信息。所有Sample实例都在其filepath字段中存储源数据在磁盘上的路径,也可以动态添加任意数量的字段来存储关于样本的额外自定义信息。
import fiftyone as fo # An image sample sample = fo.Sample(filepath="/path/to/image.png") # A video sample sample = fo.Sample(filepath="/path/to/video.mp4")从 fiftyone/core/sample.py 的Sample.__init__可以看到,Sample接受filepath、tags、metadata、media_reference等参数,其余关键字参数会被动态设置为样本字段;当媒体类型为视频时,样本还会自动挂载一个Frames容器用于存放逐帧数据。
样本在数据集内是单例(singleton):dataset[sample_id]始终返回同一个Sample实例,这保证了在同一个进程内对样本的修改是一致的。更多用法参见 使用数据集指南。
Field:字段与动态 Schema
Field是Sample实例的属性,存储关于样本的可定制信息。所有样本都必须填充filepath字段,它指向磁盘上的源数据。默认情况下,样本还带有id、media_type、tags、metadata、created_at和last_modified_at字段,存储通用信息:
import fiftyone as fo sample = fo.Sample(filepath="/path/to/image.png") print(sample)<Sample: { 'id': None, 'media_type': 'image', 'filepath': 'path/to/image.png', 'tags': [], 'metadata': None, 'created_at': None, 'last_modified_at': None, }>自定义字段可以包含任意 Python 原始数据类型:
| 字段类型 | 存储内容 |
|---|---|
BooleanField | Pythonbool实例 |
IntField | Pythonint实例 |
FloatField | Pythonfloat实例 |
StringField | Pythonstr实例 |
DateField | Pythondate实例 |
DateTimeField | Pythondatetime实例 |
ListField | Pythonlist实例 |
DictField | Pythondict实例 |
这些字段类型都定义于 fiftyone/core/fields.py:BooleanField(第 838 行)、IntField(第 745 行)、FloatField(第 949 行)、StringField(第 994 行)、DateField(第 867 行)、DateTimeField(第 917 行)、ListField(第 1036 行)、DictField(第 1121 行)等。
列表和字典字段的元素可以是同质或异质的,甚至可以包含嵌套的列表和字典。字段还可以包含更复杂的数据类型,如 Label 标注。
字段可以动态创建、修改和删除:当新的Field被赋给Dataset中的Sample,或者带有新字段的Sample被添加到Dataset时,相应的字段会自动添加到数据集的 schema 中,从而对数据集中所有其他样本也可用。也就是说,FiftyOne 的 schema 是由数据驱动的、自动扩展的。
注意:如果某个
Field尚未在Dataset的某个Sample上设置,其值为None。
import fiftyone as fo sample = fo.Sample(filepath="/path/to/image.png") sample["quality"] = 89.7 sample["keypoints"] = [[31, 27], [63, 72]] sample["geo_json"] = { "type": "Feature", "geometry": {"type": "Point", "coordinates": [125.6, 10.1]}, "properties": {"name": "camera"}, } dataset = fo.Dataset("fields-test") dataset.add_sample(sample) print(dataset)Name: fields-test Media type: image Num samples: 1 Persistent: False Tags: [] Sample fields: id: fiftyone.core.fields.ObjectIdField filepath: fiftyone.core.fields.StringField tags: fiftyone.core.fields.ListField(fiftyone.core.fields.StringField) metadata: fiftyone.core.fields.EmbeddedDocumentField(fiftyone.core.metadata.ImageMetadata) created_at: fiftyone.core.fields.DateTimeField last_modified_at: fiftyone.core.fields.DateTimeField quality: fiftyone.core.fields.FloatField keypoints: fiftyone.core.fields.ListField geo_json: fiftyone.core.fields.DictField注意,当样本加入数据集后,metadata字段的类型从泛化的Metadata具体化为了ImageMetadata(因为媒体类型为图像),同时新增的quality、keypoints、geo_json三个自定义字段自动进入了数据集的 schema。这背后对应 fiftyone/core/dataset.py 中的_expand_schema()与 fiftyone/core/fields.py 中的create_implied_field()机制:根据样本实际值推断字段类型并合并进数据集 schema。
Media type:媒体类型的推断
创建Sample时,其媒体类型会根据filepath指向的源媒体推断出来,并通过样本的media_type属性暴露。也可以显式提供media_type,用于指定不受原生支持的媒体类型。
推断逻辑位于 fiftyone/core/media.py 的get_media_type()函数:根据 MIME 类型判断图像/视频,根据扩展名判断点云(.pcd)、3D 场景(.fo3d)、多模态数据(.mcap、.bag、.rrd),无法识别时返回unknown。该文件中还定义了全部合法媒体类型常量:image、video、point-cloud、3d、multimodal、unknown,以及group与mixed两种特殊类型(用于分组数据集)。
Tags:样本标签
所有Sample实例都有tags属性,存储一个字符串列表,可灵活地用于存储关于样本的信息。一个典型用途是标记样本所属的数据集划分(test、train、validation),但你可以按任何方式自由使用标签。
import fiftyone as fo sample = fo.Sample(filepath="/path/to/image.png", tags=["train"]) sample.tags.append("my_favorite_samples") print(sample.tags) # ["train", "my_favorite_samples"]标签在 fiftyone/core/sample.py 中由StringField列表承载(默认字段tags: ListField(StringField)),并可通过数据集上的tag_samples()、untag_samples()、count_sample_tags()等方法进行批量操作(见 fiftyone/core/collections.py)。更多信息参见 使用数据集指南。
Metadata:样本元数据
所有Sample实例都有metadata属性,存储关于样本源媒体的类型特定元数据。通过dataset.compute_metadata()可以批量填充数据集中所有样本的metadata字段:
import fiftyone as fo sample = fo.Sample(filepath="/path/to/image.png") dataset = fo.Dataset() dataset.add_sample(sample) # Populate the `metadata` field of all samples in the dataset dataset.compute_metadata() print(dataset.first())<Sample: { 'id': '60302b9dca4a8b5f74e84f16', 'media_type': 'image', 'filepath': '/path/to/image.png', 'tags': [], 'metadata': <ImageMetadata: { 'size_bytes': 544559, 'mime_type': 'image/png', 'width': 698, 'height': 664, 'num_channels': 3, }>, 'created_at': datetime.datetime(2024, 7, 22, 5, 16, 10, 701907), 'last_modified_at': datetime.datetime(2024, 7, 22, 5, 16, 10, 701907), }>元数据的类型体系定义于 fiftyone/core/metadata.py:基类Metadata提供size_bytes(文件字节数)与mime_type两个字段;ImageMetadata(第 82 行)在基类之上增加width、height、num_channels;VideoMetadata(第 172 行)则针对视频提供帧率、时长、编码等信息。compute_metadata()的实现位于 fiftyone/core/metadata.py,支持通过num_workers参数并行计算,并在 fiftyone/core/collections.py 的SampleCollection.compute_metadata()方法上暴露给数据集与视图统一调用。它同时支持本地路径与 URL(build_for对http开头路径走_build_for_url分支)。
Labels:标注类型
Label存储关于样本的语义信息,如地面真值标注(ground annotations)或模型预测。FiftyOne 为许多常见任务提供了现成的标注类,全部定义于 fiftyone/core/labels.py,包括:
| 标注类型 | 用途 |
|---|---|
Regression | 回归值 |
Classification | 单标签分类结果 |
Classifications | 分类结果列表(典型用于多标签任务) |
Detections/Detection | 目标检测框列表(可带实例掩码) |
Polylines/Polyline | 图像中的折线或多边形 |
Cuboids | 图像中的 2D 立方体框 |
| Rotated bounding boxes | 图像中的旋转框 |
Keypoints/Keypoint | 图像中的关键点列表 |
Segmentation | 图像的语义分割掩码 |
Heatmap | 图像的强度热力图 |
TemporalDetection | 视频中带时间帧支持的事件 |
| 3D detections | 场景中的 3D 检测框 |
| 3D polylines | 场景中的 3D 折线或多边形 |
GeoLocation | 地理定位点、线或面 |
使用 FiftyOne 的Label类型,即可在 FiftyOne App 中可视化你的标注。例如:
import fiftyone as fo sample = fo.Sample(filepath="/path/to/image.png") sample["weather"] = fo.Classification(label="sunny") sample["animals"] = fo.Detections( detections=[ fo.Detection(label="cat", bounding_box=[0.5, 0.5, 0.4, 0.3]), fo.Detection(label="dog", bounding_box=[0.2, 0.2, 0.2, 0.4]), ] ) print(sample)<Sample: { 'id': None, 'media_type': 'image', 'filepath': 'path/to/image.png', 'tags': [], 'metadata': None, 'created_at': None, 'last_modified_at': None, 'weather': <Classification: {'label': 'sunny', 'confidence': None, 'logits': None}>, 'animals': <Detections: { 'detections': [ <Detection: { 'label': 'cat', 'bounding_box': [0.5, 0.5, 0.4, 0.3], 'confidence': None, 'attributes': {}, }>, <Detection: { 'label': 'dog', 'bounding_box': [0.2, 0.2, 0.2, 0.4], 'confidence': None, 'attributes': {}, }>, ], }>, }>从源码看,Detection.bounding_box使用相对坐标[x, y, w, h](左上角坐标与宽高,取值 0~1),而Regression、Classification等均继承自 fiftyone/core/labels.py 中的Label动态文档基类(第 50 行),因此可以像普通字段一样动态扩展。完整的标注存储与操作指南参见 使用数据集指南。
DatasetViews:数据集视图
数据集视图是探索数据集的强大工具。你可以使用DatasetView实例来搜索、过滤、排序和操作数据集的子集,从而完成所需的分析。
import fiftyone as fo import fiftyone.zoo as foz import fiftyone.brain as fob from fiftyone import ViewField as F dataset = foz.load_zoo_dataset("cifar10", split="test") cats = dataset.match(F("ground_truth.label") == "cat") fob.compute_uniqueness(cats) similar_cats = cats.sort_by("uniqueness", reverse=False) session = fo.launch_app(view=similar_cats)上面的示例演示了一条典型的视图工作流:先用match()按ViewField表达式过滤出类别为cat的样本,再用fob.compute_uniqueness()(brain 方法)为这些样本计算"独特性"分数,最后用sort_by()按该分数升序排序,得到"最相似"的猫样本视图并交给fo.launch_app()在 App 中可视化。
DatasetView是惰性(lazy)的:视图上的过滤、排序等操作(match、sort_by、limit、skip、shuffle、select、exclude等)只是记录了一系列 view stage,真正的查询在迭代或聚合时才执行。每个 stage 在 fiftyone/core/stages.py 中都有对应实现类,并最终编译为 MongoDB 聚合管道(见 fiftyone/core/view.py 的_pipeline()与to_mongo())。ViewField(表达式 API)定义于 fiftyone/core/expressions.py,支持算术、比较、逻辑、字符串、数组、日期等丰富操作符。视图的完整讲解参见 数据集视图指南。
Aggregations:聚合统计
数据集视图用于搜索样本并过滤其内容;与之互补,我们常常需要计算数据集或视图的聚合统计量,如标签计数、分布和范围。FiftyOne 提供了一个强大的聚合框架,可高效地计算关于数据的统计信息。
import fiftyone as fo import fiftyone.zoo as foz from fiftyone import ViewField as F dataset = foz.load_zoo_dataset("quickstart") # Compute a histogram of the predicted labels in the `predictions` field print(dataset.count_values("predictions.detections.label")) # {'bicycle': 13, 'hot dog': 8, ..., 'skis': 52} # Compute the range of confidences of `cat` predictions in the dataset print( dataset .filter_labels("predictions", F("label") == "cat") .bounds("predictions.detections.confidence") ) # (0.05223553627729416, 0.9965479969978333)上述示例中,count_values("predictions.detections.label")沿点号路径逐层"解开"(unwind)标签列表,统计每个预测类别出现的次数(直方图);filter_labels()先过滤出cat预测,再由bounds()计算置信度的最小与最大值。
聚合框架的核心实现位于 fiftyone/core/aggregations.py,提供了Count、Bounds、CountValues、Distinct、HistogramValues、Sum、Mean、Std、Min、Max、Quantiles、Values、Schema等聚合类,每个聚合类实现to_mongo()将其编译为 MongoDB 聚合管道,并通过parse_result()解析结果。数据集与视图层面对应的便捷方法(count、count_values、bounds、distinct、histogram_values、sum、mean、std、min、max、quantiles、values、schema等)统一实现在 fiftyone/core/collections.py 的SampleCollection基类中,因此对Dataset和DatasetView均可用。聚合的完整讲解参见 聚合指南。
小结
本文围绕 FiftyOne 的七组基本概念展开:Dataset是有序样本集合与核心数据结构;Sample是存储单一媒体数据相关信息的原子元素;Field是可动态扩展的样本属性(schema 由数据驱动);media_type由文件路径自动推断;tags是灵活的字符串标签列表;metadata保存媒体文件的类型化元信息;Label家族为回归、分类、检测、分割、关键点、地理信息等常见任务提供了标准化标注。在此基础上,DatasetView提供惰性筛选/排序视图,aggregations框架则在数据库端高效计算各类统计量。掌握这些基本概念之后,即可顺畅地进入数据导入、数据集使用、视图操作与聚合统计等进阶主题。
【免费下载链接】fiftyoneRefine high-quality datasets and visual AI models项目地址: https://gitcode.com/GitHub_Trending/fi/fiftyone
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考