Hub documentation
Lance
Lance
Lanceは、AI向けのオープンなマルチモーダルなレイクハウステーブル形式です。Hugging Face パス(hf://)を使用して、Hub 上の Lance データセットにアクセスできます。これにより、Hugging Face Hub 上の大規模なデータセットをローカルにコピーすることなく、スキャンや検索を行うことができます。

はじめる
まずはじめに、pylanceとpyarrowをpipでインストールします:
pip install pylance pyarrow
なぜLanceなのか
- ML/AIワークロード向けに最適化: Lanceは、スキャン性能を犠牲にすることなく高速なランダムアクセスを実現するために設計されたモダンなカラムナフォーマットであり、検索、分析、学習、特徴量エンジニアリングなど、多くのユースケースで有用です。
- マルチモーダルアセットは、Lanceにおいて埋め込みベクトルや従来のスカラーデータとともに、バイト列(バイナリオブジェクト、”ファイルとしてのブロブ”)として保存されます。これにより、Hubを介して大規模なデータセットを管理、共有、配布しやすくなります。
- インデックスは第一級市民(フォーマット自体にネイティブ)です: Lanceには、高速でディスク上にスケーラブルなベクトルインデックスとFTSインデックスが同梱されており、Hub上のデータセットのすぐ隣に配置されるため、ユーザーが再計算する必要なく、データだけでなく埋め込みやインデックスも共有できます。
- 柔軟なスキーマとデータの進化により、テーブル全体を書き直す必要がなくして、新しい機能やカラム(モデレーションタグ、埋め込みなど)を段階的に追加できます。
すべてのデータを1つの場所に保存
Lanceでは、マルチモーダルなデータ資産(画像、音声、動画)が、スカラー型のメタデータや埋め込みベクトルとともに生バイトとして保存されます。これにより、複数のストレージシステムを組み合わせる必要なく、1つの場所でデータセットのスキャンとフィルタリングを簡単に実行できます。
datasetsでHubからストリーミング
load_dataset(..., streaming=True) を使用すると、データをローカルにダウンロードせずにスキャンして反復処理できます。
from datasets import load_dataset
# Return as a Hugging Face dataset
ds = load_dataset(
"lance-format/laion-1m",
split="train",
streaming=True
)
# Take first three rows
for row in ds.take(3):
print(row["caption"])ストリーミングは、データの内容を理解するためのメタデータのサンプリングに最適です。ベクトル検索や大規模なバイナリブロブの処理には、以下で説明する Lance の dataset API を使用できます。
ストリーミングは、単純なスカラーメタデータのサンプリングには高速ですが、埋め込みベクトルや大規模なマルチモーダルアセットの取得にはそれほど速くありません。大規模なデータセットを扱う場合、メタデータをスキャンして必要なサブセットを特定し、Hub のレート制限に抵触しないよう、その部分のみをローカルにダウンロードすることをお勧めします:
hf download lance-format/laion-1m --repo-type dataset --local-dir ./laion
lance.datasetでHubからストリーミング
Hugging Face Hubに保存されているLanceデータセットを、hf://パス指定子を使用してスキャンすることもできます。これにより、ローカルにダウンロードすることなく、リモートのデータセットをスキャンできます。Lanceのdataset APIを使用すると、必要なデータのみを取得するために、制限、フィルタ、および射影を設定することが非常に簡単です。
import lance
# Return as a Lance dataset
ds = lance.dataset("hf://datasets/lance-format/laion-1m/data/train.lance")
scanner = ds.scanner(
columns=["caption", "url", "similarity"],
limit=5
)
rows = scanner.to_table().to_pylist()
for row in rows:
print(row)バイナリアセットを扱う
以下の例では、Lance データセットからimageカラム内の生 JPEG バイトとして画像を取得し、下流で使用する手順を示しています。
ds.takeを使用してバイトを取得し、ディスクに書き出すことで、他の場所で利用できます。
import lance
from pathlib import Path
ds = lance.dataset("hf://datasets/lance-format/laion-1m/data/train.lance")
dir_name = "laion_samples"
Path(dir_name).mkdir(exist_ok=True)
rows = ds.take([0, 1], columns=["image", "caption"]).to_pylist()
for idx, row in enumerate(rows):
with open(f"{dir_name}/{idx}.jpg", "wb") as f:
f.write(row["image"])
print(f"Wrote image with caption: {row['caption']}")サブセットを新しいLanceデータセットに書き込む
大規模なデータセットを扱っていますか?Hubから行のサブセットを選択するためのフィルタ付きスキャンを実行し、それをローカルのLanceデータセットとしてマテリアライズするのは簡単です。
import lance
ds = lance.dataset("hf://datasets/lance-format/laion-1m/data/train.lance")
scanner = ds.scanner(
columns=["image", "caption", "width", "height"],
filter="width >= 200 AND height >= 100",
limit=10,
)
subset = scanner.to_table()
lance.write_dataset(subset, "./laion_subset")インデックスの作成
データセットにインデックスが関連付けられていない場合、ローカルにダウンロードした後にインデックスを作成できます。
# ds is a local Lance dataset
ds.create_index(
"img_emb",
index_type="IVF_PQ",
num_partitions=256,
num_sub_vectors=96,
replace=True,
)ベクトルインデックスの作成に関するより詳細な例については、Lanceのドキュメントを参照してください。ベクトルインデックスを作成した後、埋め込み(embeddings)を介してデータに対して類似度検索を実行できます。
ベクトル検索
Lanceではインデックスが第一級市民として扱われるため、データだけでなく、埋め込みベクトルやインデックスも一緒に保存し、Hub上で直接クエリを実行できます。describe_indices()メソッドを使用すると、データセットのインデックス情報を一覧表示できます。データセットにインデックスが存在しない場合は、lance.write_dataset()を使用してデータセットのローカルバージョンを書き込み、LanceDataset.create_indexを使用して必要に応じてインデックスを作成できます。
以下の例は、img_embフィールドにベクトルインデックスが既に存在するデータセットを示しています。
import lance
ds = lance.dataset("hf://datasets/lance-format/laion-1m/data/train.lance")
print(ds.list_indices())
# Returns
# [
# IndexDescription(
# name=img_emb_idx,
# type_url=/lance.table.VectorIndexDetails,
# num_rows_indexed=1209588,
# fields=[15],
# field_names=["img_emb"],
# num_segments=1
#. )
# ]リモートデータセットをダウンロードせずに、ベクトル検索クエリを直接実行できます(または、ローカルにデータセットをダウンロードして新しいインデックスを作成することもできます)。以下の例では、画像埋め込みをクエリベクトルとして使用して、ベクトルインデックス上で最近傍検索を実行する方法を示しています。
import lance
import pyarrow as pa
ds = lance.dataset("hf://datasets/lance-format/laion-1m/data/train.lance")
emb_field = ds.schema.field("img_emb")
ref = ds.take([0], columns=["img_emb"]).to_pylist()[0]["img_emb"]
query = pa.array([ref], type=emb_field.type)
neighbors = ds.scanner(
nearest={
"column": emb_field.name,
"q": query[0],
"k": 6,
"nprobes": 16,
"refine_factor": 30,
},
columns=["caption", "url", "similarity"],
).to_table().to_pylist()大きな
kまたはnprobesの値を設定したり、一度に大量のクエリを送信したりすると、Hubのレート制限に抵触する可能性があります。大量の処理を行う場合は、データセット(またはその一部)をローカルにダウンロードし、Lanceをローカルパスに向けることで、スロットリングを回避してください。
Datasetの進化
Lanceの最も強力な機能の一つは、柔軟でコストゼロのデータ進化です。これは、元のテーブルを書き換えることなく、派生カラムを容易に追加できることを意味します。非常に大規模なテーブルや、大きなブロブが多数含まれるテーブルでは、I/Oの削減効果がかなり大きくなります。ML/AIエンジニアリングのタスクでデータを試行錯誤し、新しい特徴量、埋め込み、または派生メタデータを頻繁に追加している場合、この機能は非常に重要です。
以下の例では、既存のスコアカラムに基づいて画像をNSFWとしてマークする派生カラムmoderation_labelを追加する方法を示しています。この変更を行う場合、新しいカラムのバックフィルはのみ新しいカラムのデータを書き込み、元の画像ブロブや他のカラムのデータには一切触れません。また、データのバックフィルを行わずに新しいカラムのスキーマのみを追加することも選択できます。
import lance
import pyarrow as pa
# Assumes you ran the export to Lance example above to store a local subset of the data
local_ds = lance.dataset("./laion_subset")
# schema only (data to be added later)
local_ds.add_columns(pa.field("moderation_label", pa.string()))
# with data backfill
local_ds.add_columns(
{
"moderation_label": "case WHEN \"NSFW\" > 0.5 THEN 'review' ELSE 'ok' END"
}
)Lance データセットの列を変更・削除する方法については、Lance のドキュメントの データ進化 を参照してください。
動画ブロブを扱う
Lanceテーブルは、大きなインラインの動画ブロブもサポートしています。OpenVid-1Mデータセット(この論文より)には、高品質で表現力豊かな動画とそのキャプションが含まれています。動画データは、Hub上の以下のLanceデータセットのvideo_blobカラムに保存されています。
import lance
lance_ds = lance.dataset("hf://datasets/lance-format/Openvid-1M/data/train.lance")
blob_file = lance_ds.take_blobs("video_blob", ids=[0])[0]
video_bytes = blob_file.read()他のデータ形式とは異なり、Lanceでは大規模なマルチモーダルバイナリオブジェクト(blob)が第一級市民として扱われます。blob APIは、Lanceデータセット内の大規模なblobを保存および取得するための高レベルAPIを提供します。次の例では、より重い動画blobを読み込まずにメタデータを効率的に参照し、その後、必要な動画blobをオンデマンドで取得する方法を示しています。
import lance
ds = lance.dataset("hf://datasets/lance-format/Openvid-1M/data/train.lance")
# 1. Browse metadata without loading video blobs.
metadata = ds.scanner(
columns=["caption", "aesthetic_score"],
filter="aesthetic_score >= 4.5",
limit=2,
).to_table().to_pylist()
# 2. Fetch a single video blob by row index.
selected_index = 0
blob_file = ds.take_blobs("video_blob", ids=[selected_index])[0]
with open("video_0.mp4", "wb") as f:
f.write(blob_file.read())学習用のデータを準備する
Trainingは、Lanceの高速なランダムアクセスとスキャン性能が活きるもう一つの領域です。Lanceデータセットを学習データのストレージ機構として使用し、シャッフルやバッチへの読み込みを学習パイプラインの一部として実行できます。
Lanceのblob APIはtorchcodecと互換性があるため、torchテンソルとしてビデオのblobを簡単にデコードできます。
from torchcodec.decoders import VideoDecoder
decoder = VideoDecoder(blob_file)
tensor = decoder[0] # uint8 tensor of shape [C, H, W]効率的に動画をデコードするためのその他の関数については、torchcodecのドキュメントを参照してください。
さらに、独自の画像モデルの学習のためにtorchvisionへ画像データを読み込むことに関する追加の例については、Lanceのドキュメントもご参照ください。
Lanceのデータセットをさらに探索する
Lanceは、従来のタブラーデータと並んで、マルチモーダルなブロブをネイティブでサポートするオープンフォーマットです。 Hugging Face Hubとの統合により、画像、音声、動画、テキスト、埋め込み、スカラーメタデータをすべて1つの場所で簡単に扱えます。
Explore more Lance datasets on the Hugging Face Hub, and share your own Lance datasets with others in the community! You can visit lance.org for more code snippets and examples.
Update on GitHub