SkyPilot を活用し、あらゆるクラウド上で AI ワークロードを実行し、Hugging Face でゼロエグレスストレージを実現
Hugging Face は SkyPilot と連携し、任意のクラウド環境で AI ワークロードを実行しながらデータを転送コストゼロ(ゼロエグレス)のまま Hugging Face ストレージ上で保存できる新機能を発表した。
キーポイント
SkyPilot との連携による柔軟な実行環境
ユーザーは任意のクラウドプロバイダーを選択して AI ワークロードを柔軟に実行できるようになり、ベンダーロックインの解消が図られる。
転送コストゼロ(Zero-Egress)の実現
データが Hugging Face のストレージ上に残ったまま処理されるため、クラウド間のデータ移動に伴う高額なエグレス料金を完全に排除できる。
AI 開発コストの最適化と効率向上
計算リソースを最も安価または高性能な環境に動的に割り当てつつ、データ管理は一元化することで、全体のコストと運用負荷を大幅に削減する。
影響分析・編集コメントを表示
影響分析
この発表は、AI モデルの開発・運用において最大のボトルネックの一つである「データ転送料金」の課題を解決し、マルチクラウド戦略の実用性を飛躍的に高めるものです。特に大規模なトレーニングや推論を行う企業にとって、インフラコストの最適化とアーキテクチャの柔軟性が確保されるため、業界全体の運用効率に大きな影響を与える重要な進展です。
編集コメント
データ移動コストが AI プロジェクトの収益性を大きく左右する中、この機能は実務的な課題を直接解決する画期的なアプローチです。特に大規模モデル運用におけるクラウド選定とコスト管理のあり方を再定義する可能性があります。
多くのチームにとって、モデルやデータセットは単一のクラウドの特定のリージョンにあるバケット内に存在しています。開発、トレーニング、あるいは推論のために必要な GPU は、データとは異なるクラウド上に置かれることが増えています。この 2 つが分離した瞬間、自分自身のデータを自分の GPU に読み込むためにさえ、クロスクラウド転送税を支払う必要があります。
Hugging Face と共同で、私たちはこれら 2 つの要素を結びつけました:モデルやデータセットは Hub のままに保ち、SkyPilot は GPU を持つ任意のクラスター上で計算(開発、トレーニング、または推論)を実行します。既存の HF_TOKEN と hf:// URL を使用して Hugging Face Bucket または任意の Hub リポジトリを SkyPilot ジョブにマウントするだけで、容量がある場所に即座に起動できます。Hugging Face はエグレス(転送)料金を徴収しないため、どのクラウド上であっても GPU にデータを読み込むコストはゼロです。
今回の新機能は以下の通りです:
- 任意のジョブで Hub データを利用可能。store: hf コマンドにより、既存の HF_TOKEN と hf:// URL を使用して、MOUNT または COPY で Hugging Face Bucket(読み書き可能)または任意のモデル/データセット/Space リポジトリ(読み取り専用)を SkyPilot タスクにマウントできます。
- 任意のクラウド上の任意の GPU で実行可能。SkyPilot は 20 以上のクラウド、Kubernetes、Slurm、オンプレミス環境全体でジョブの計算リソースを検索するため、同じ実行において、どのベンダーでも利用可能な予約済みまたはオンデマンドの GPU を自動的に選択します。
- データ読み出しのためのエグレス(外部へのデータ転送)は不要です。Hugging Face Storage はエグレス料金や CDN 料金を徴収しないため、SkyPilot がジョブを実行する場所がどこであっても、モデルやデータセットは同じバケットから直接読み込まれ、クラウドごとのコピー作成も不要で、取り込むためのエグレス請求も発生しません。
- Xet を活用した重複排除。バケットは Xet 上で構築されているため、インクリメンタルなチェックポイントやモデルのバリアントでは、変更されたチャンクのみが保存・転送されます。
- 共同開発。Hugging Face と SkyPilot はこの機能を共同でリリースしており、Hugging Face チームは非特権コンテナでも動作するようにする hf-mount FUSE の修正を upstream(本流)に反映しました。
Hugging Face Storage が SkyPilot のファーストクラスバックエンドとして正式サポートへ
SkyPilot タスクは、ローカルパスにマウントすることで既に S3、GCS、Azure、R2 など多数のクラウドオブジェクトストアへの読み書きが可能です。Hugging Face Storage も now「store: hf」として加わり、「hf://」スキームを通じてアクセスできます:
file_mounts:
# Hugging Face バケット(チェックポイント、ログ、処理済みデータ用、読み書き可能)
/checkpoints:
source: hf://buckets/my-org/qwen-sft
store: hf
mode: MOUNT # または COPY
# モデルリポジトリ(読み取り専用でマウント)
/base-model:
source: hf://Qwen/Qwen3.5-4B
store: hf
mode: MOUNT
# データセットリポジトリ(特定のリビジョンに固定、読み取り専用)
/data:
source: hf://datasets/my-org/my-dataset@main
store: hf
mode: MOUNT
この hf:// スキームは、リポジトリからモデルとデータセットを読み取り、トレーニング中にチェックポイントをバケットに書き込み、完了したモデルを再びリポジトリへ公開し、サービス提供時に推論サーバーへプルするという、ライフサイクル全体をカバーします。ほとんどのチームはすでに Hub にモデルやデータセットを保管しているため、移行ステップも不要であり、新しいストレージアカウントを作成する必要もありません。
MOUNT は Hugging Face の hf-mount FUSE バックエンドを使用するため、バケットまたはリポジトリは SkyPilot の他の FUSE マウント(gcsfuse, blobfuse2, rclone, goofys)の隣にローカルパスとして表示されます。フェッチ処理はファイルシステム層で行われます:コードが read() を発行した際、ドライバーは Xet バックエンドから必要なバイトのみを取得するため、実際にアクセスするデータのみがネットワークを通過し、hf-mount はディスクキャッシュを保持するため、繰り返し読み込みもローカルで完結します。このディスクキャッシュの動作は、SkyPilot が他のバックエンドに対して MOUNT_CACHED の下で行う挙動と同じです。ここで通常の MOUNT はバケットからすべての読み取りをストリーミングし、ローカルに何も保持しません。しかし hf ストアにおいては、MOUNT と MOUNT_CACHED は同じ動作をするため、どちらのモードでもキャッシュが維持されます。
読み込みは遅延実行されるため、プロセスはファイル全体がダウンロードされる前に大きなファイルの処理を開始でき、まず完全なコピーを待機してブロックされることはありません。これにより、GPU はほぼ即座に稼働し、データセットやチェックポイントのコピー待ちでアイドル状態(かつ課金対象)となるのではなく、ストリーミングされるデータをトレーニングしながら使用し続けます。これはキャッシュがまだない最初のエポックにおいて最も効果が発揮されます。COPY は別のアプローチを採用しており、特別な要件なしに huggingface_hub を通じて事前にダウンロードを行います。
認証には、すでに持っているトークンを使用します。環境変数 HF_TOKEN に設定し、--secret HF_TOKEN として実行に引き渡してください。SkyPilot は、ジョブが着地するクラウド上でマウントにこのトークンを利用します。1 つのトークンで、AWS、GCP、Azure、Nebius、Lambda、または独自の Kubernetes クラスター上にジョブが着地する場合でも動作するため、クラウドごとのバケットキーを管理する必要はありません。
転送料ゼロ:ストレージが実行場所を決定するのを防ぐ
GPU の容量はもはや単一のソースから得られることは稀です。十分な数の H100 や H200 を確保するために、チームは複数のベンダーで予約済みおよびコミット済みの容量を同時に保有します(ハイパースケーラー上のブロック、ネオクラウド上のクラスター、あるいはオンプレミスラックなど)そして割り当てがある場所で実行します。SkyPilot はこのために設計されています:1 つのジョブ仕様を 20 以上のクラウド、Kubernetes、およびオンプレミスにまたいでスケジューリングし、空きのある予約済みクラスターに着地させます。
オブジェクトストレージが課題となっています。オブジェクトストアは地域ごとに、かつクラウドプロバイダーごとに独立しているため、異なるベンダーのデータセンターに置かれた GPU や推論サーバーにデータを供給するには、各ベンダーのバケットにデータの複製を保持するか、転送料金を支払う必要があります。ほとんどのクラウドでは、データがネットワークから流出した瞬間(AWS からのアウトバウンドは約 0.09 ドル/GB)にエグレス料金が発生し、同一クラウド内でもリージョン間での転送には課金されることが一般的です。ベースモデルをすべての推論ノードにダウンロードしたり、別のクラウド上のクラスターから数回のイテレーションでデータセットを更新したりすると、すでに予約済みの GPU にかかるコストに加えて、巨額の請求額が発生します。その結果、チームは各実行をデータを保持しているベンダーに固定し、残りのキャパシティをアイドル状態のまま放置することになります。
Hugging Face Storage は、このコストが最も痛手となる「読み取り側」の負担を取り除きます。エグレス料金や CDN 料金が不要であり、ストレージ料金は月あたり TB 12〜18 ドル(AWS S3 は約 23 ドル/月+エグレス料金)という価格設定のため、同じバケットをすべてのクラスターからアクセス可能にし、GPU がどこで実行されても読み取りは無料となります。書き込みについては、依然として計算クラウドの通常のエグレス料金が課金されますが、これはオフクラウドストアへの転送と同様の条件です。ただし、多くの AI ワークロードでは「読み取り」が主体を占めます:多数のイテレーションにわたってストリーミングされるデータセットや、新しいトレーニングノード・推論ノードごとにダウンロードされるモデル重みなどです。これにより、各実行をデータの複製を保持しているベンダーに固定する必要はなくなります。
簡易なベンチマーク
ベンチマーク数値を収集するために、TRL の SFTTrainer を使用し、Qwen/Qwen3.5-4B モデルを HuggingFaceH4/Multilingual-Thinking データセットで小規模なファインチューニングを行いました。モデルは Hub リポジトリから読み取り専用としてマウントし、すべてのチェックポイントは Hugging Face Bucket に書き込みました。同じ SkyPilot YAML ファイルを AWS、GCP、Lambda で実行しましたが、変更したのは --infra パラメータのみです。SkyPilot は GPU が空いている場所に各ジョブを配置し、3 つのクラウドすべてが同じバケットを読み書きしました。
qwen-sft.yaml. どこでも起動: sky launch qwen-sft.yaml --infra aws|gcp|...
resources:
accelerators: H100:1 # クラウドに何があるかによる
file_mounts:
/base-model:
source: hf://Qwen/Qwen3.5-4B # 読み取り専用、Hub から遅延マウント
store: hf
mode: MOUNT
/checkpoints:
source: hf://buckets/my-org/qwen-sft # 読み書き可能な Bucket
store: hf
mode: MOUNT
run: |
python train.py --model /base-model --output_dir /checkpoints
測定した結果:
- モデルはすべてのクラウドで無料でロードされました。遅延読み取りにより、from_pretrained がアクセスする部分のみが取得されるため、約 30 秒(最大 500 MB/s)でトレーニングの準備が整いました。Hugging Face はエグレス(転送出力)料金を徴収しないため、この取得にはコストがかかりませんでした。もしモデルが S3 に存在していた場合、別のクラウド上の GPU へのすべての読み取りに対してエグレス料金(AWS では 1GB あたり $0.09)が請求されていたはずです。
- チェックポイントは最大約 170 MB/s の速度でバケットに直接ストリーミングされ、GPU インスタンスを超えて永続化されます。
クラウド別チェックポイント書き込み速度は以下の通りです:
Cloud
GPU
Checkpoint write
AWS (us-east-2)
L40S
~168 MB/s
GCP (us-central1)
L4
~123 MB/s
Lambda (us-west-3)
H100
~112 MB/s
Xet 基盤ストレージ:チェックポイントとモデルバリアントの重複排除
Hugging Face Buckets は Xet をベースに構築されており、コンテンツ定義チャンキング を用いてファイルを約 64 KB のチャンクに分割し、各一意のチャンクを一度だけ保存します。境界がコンテンツに基づいているため、編集が行われると影響を受けるチャンクのみが変更され、残りの部分は既に保存済みとして認識されます。この仕組みは以下の点で効果を発揮します:
- 増分チェックポイントおよびアダプターチェックポイント。レイヤーを凍結したり、アダプターを訓練したり、あるいは保存間隔で重みの大部分を変更しない場合でも、変更されたチャンクのみがアップロードされ、チェックポイント全体が転送されることはありません。
- ベースモデルを共有するモデルバリアント。1 つのベースモデルに対するファインチューニングや量子化は大幅に重複するため、共通のチャンクはそれらすべてに対して一度だけ保存されます。
- 追記するデータセット。会話のトレースや推論出力のようなログは、大規模な Parquet ファイルに行を追加することで成長します。既存の行グループはバイト単位で同一のままなので、転送されるのは新しい行のみです:Hugging Face のテストでは、10 万行のテーブルに 1 万行を追加しても、完全な約 106 MB ではなく約 10 MB しか移動しませんでした。(行をその場で編集または削除する場合は、変更をローカルに保つために use_content_defined_chunking=True を指定して書き込んでください。)
- 再アップロードでは、既に保存されている部分はスキップされます。テストでは、バケット内に既に存在する 8.43 GB のブロブを再アップロードするのに約 8 秒かかりましたが、初回アップロードは 24 秒でした。これはチャンクハッシュのみが移動するためです。同じメカニズムにより、サーバーサイドの hf バケット間での cp コマンドやリポジトリ間のコピーも、バイトの再アップロードではなく参照によるコピーとして実行されます。
節約できる量はアーティファクトの重複度合いによりますが、重複排除は自動で行われます:チェックポイントは通常通り書き込み、新しいチャンクのみがマシンから送信されます。
始め方
pip install "skypilot[huggingface]"
hf auth login # または: export HF_TOKEN=<your-token>
hf:// マウントを任意の SkyPilot タスクに追加して起動します。MOUNT には、glibc 2.34+ と /dev/fuse を備えたベースイメージが必要です。
Hugging Face と SkyPilot が共に構築
初期の hf へのストアサポートは、Nikhil Jha による貢献 [https://github.com/skypilot-org/skypilot/pull/9418] から始まりました。Hugging Face チームはこの取り組みを引き継ぎ [https://github.com/skypilot-org/skypilot/pull/9698]、多くの Kubernetes クラスターでデフォルトとなっている非特権コンテナでもマウントできるようにする hf-mount の FUSE 修正を本流に統合しました。SkyPilot チームはこれをストレージバックエンドに組み込みました。この全体のプロセスはオープンソースです:SkyPilot、Hugging Face の hf-mount、および huggingface_hub クライアント。
リソース
- SkyPilot ストレージドキュメント
- Hugging Face Storage Buckets ガイド
- hf-mount
- Xet: コンテンツ定義チャンキングと重複排除
- SkyPilot Slack コミュニティ
原文を表示
For most teams, models and datasets live in a bucket in one region of one cloud. The GPUs you can get, whether for development, training, or serving, increasingly sit on a different cloud than your data. The moment those two come apart, you pay a cross-cloud transfer tax just to read your own data onto your own GPUs.
Together with Hugging Face, we've joined the two halves: your models and datasets stay on the Hub, and SkyPilot runs the compute (dev, training, or serving) on whatever cluster has the GPUs. Mount a Hugging Face Bucket or any Hub repo into a SkyPilot job with one hf:// URL and the HF_TOKEN you already have, then launch it wherever capacity is. Hugging Face charges no egress, so reading your data onto those GPUs costs nothing, on any cloud.
Here's what's new:
- Your Hub data in any job. store: hf mounts a Hugging Face Bucket (read-write) or any model / dataset / Space repo (read-only) into a SkyPilot task with one hf:// URL and your existing HF_TOKEN, via MOUNT or COPY.
- Run it on any GPU, on any cloud. SkyPilot finds that job compute across 20+ clouds, Kubernetes, Slurm, and on-prem, so the same run uses whichever of your reserved or on-demand GPUs is available, on any vendor.
- No egress to read your data. Hugging Face Storage charges no egress or CDN fees, so wherever SkyPilot lands the job, it reads your models and datasets straight from the same bucket, with no per-cloud copies and no egress bill to pull them in.
- Xet-backed dedup. Buckets are built on Xet, so incremental checkpoints and model variants only store and transfer the chunks that changed.
- Built together. Hugging Face and SkyPilot shipped this jointly, and the Hugging Face team upstreamed the hf-mount FUSE fixes that make it work in unprivileged containers.
Hugging Face Storage is now a first-class SkyPilot backend
SkyPilot tasks already read and write cloud object stores (S3, GCS, Azure, R2, and many more) by mounting them at a local path. Hugging Face Storage now joins that list as store: hf, reached through the hf:// scheme:
file_mounts:
# A Hugging Face Bucket, read-write, for checkpoints, logs, processed data.
/checkpoints:
source: hf://buckets/my-org/qwen-sft
store: hf
mode: MOUNT # or COPY
# A model repo, mounted read-only.
/base-model:
source: hf://Qwen/Qwen3.5-4B
store: hf
mode: MOUNT
# A dataset repo, pinned to a revision, read-only.
/data:
source: hf://datasets/my-org/my-dataset@main
store: hf
mode: MOUNT
That one hf:// scheme covers the whole lifecycle: read the model and dataset from their repos, write checkpoints to a Bucket while you train, publish the finished model back to a repo, and pull it onto inference servers when you serve. Most teams already keep their models and datasets on the Hub, so there is no migration step and no new storage account to create.
MOUNT uses Hugging Face's hf-mount FUSE backend, so a bucket or repo shows up as a local path next to SkyPilot's other FUSE mounts (gcsfuse, blobfuse2, rclone, goofys). The fetching happens at the filesystem layer: when your code issues a read(), the driver pulls just those bytes from the Xet backend, so only the data you actually touch crosses the network, and hf-mount keeps an on-disk cache so repeat reads stay local. That on-disk cache is the behavior SkyPilot gives its other backends under MOUNT_CACHED, where a plain MOUNT instead streams every read from the bucket with nothing kept locally. For the hf store, MOUNT and MOUNT_CACHED behave the same, so either mode keeps the cache.
Because reads are lazy, a process can start working through a large file before the whole file has downloaded, instead of blocking on a full copy first. That keeps the GPU busy almost immediately, training on data as it streams in rather than sitting idle (and billing) while a dataset or checkpoint copies down. It pays off most on the first epoch, when nothing is cached yet. COPY takes the other route and downloads through huggingface_hub up front, with no special requirements.
Authentication is the token you already have. Set HF_TOKEN in your environment and hand it to a run with --secret HF_TOKEN; SkyPilot uses it for the mount on whatever cloud the job lands. One token works whether the job lands on AWS, GCP, Azure, Nebius, Lambda, or your own Kubernetes cluster, so there are no per-cloud bucket keys to juggle.
No egress: storage stops deciding where you run
GPU capacity rarely comes from one place anymore. To get enough H100s and H200s, teams hold reserved and committed capacity across several vendors at once (a block on a hyperscaler, a cluster on a neocloud, maybe an on-prem rack) and run wherever they have allocation. SkyPilot is built for this: one job spec, scheduled across 20+ clouds, Kubernetes, and on-prem, landing on whichever reserved cluster is free.
Object storage has been the catch. Object stores are regional and per-cloud, so feeding a GPU or an inference server that sits in a different vendor's data center means either keeping a copy of your data in every vendor's bucket or paying to pull it across. Most clouds charge egress (around $0.09/GB out of AWS) the moment data leaves their network, and often between regions inside one cloud. Pulling a base model onto every inference node, or iterating a dataset for several epochs from a cluster on another cloud, adds a hefty bill on top of GPUs you have already reserved. Teams end up pinning each run to whichever vendor holds the data and leaving the rest of their capacity idle.
Hugging Face Storage takes that cost off the table where it bites: the read side. With no egress or CDN fees and storage at $12-18/TB/month (versus AWS S3 at roughly $23/TB plus egress), the same bucket is reachable from every one of those clusters, and reading from it is free no matter where the GPUs run. Writing back still costs your compute cloud's usual egress, the same as it would to any off-cloud store, but for most AI work the reads dominate: a dataset streamed over many epochs, or model weights pulled onto every new training or inference node. So you stop pinning each run to whichever vendor holds a copy of the data.
A quick benchmark
To collect some benchmark numbers, we ran a small fine-tune: Qwen/Qwen3.5-4B on the HuggingFaceH4/Multilingual-Thinking dataset with TRL's SFTTrainer, mounting the model read-only from its Hub repo and writing every checkpoint to a Hugging Face Bucket. The same SkyPilot YAML ran on AWS, GCP, and Lambda, changing only --infra. SkyPilot placed each job wherever GPUs were free, and all three read and wrote the same bucket.
# qwen-sft.yaml. Launch anywhere: sky launch qwen-sft.yaml --infra aws|gcp|...
resources:
accelerators: H100:1 # or whatever the cloud has
file_mounts:
/base-model:
source: hf://Qwen/Qwen3.5-4B # read-only, lazy-mounted from the Hub
store: hf
mode: MOUNT
/checkpoints:
source: hf://buckets/my-org/qwen-sft # read-write Bucket
store: hf
mode: MOUNT
run: |
python train.py --model /base-model --output_dir /checkpoints
What we measured:
- The model loaded free on every cloud. Lazy reads pull only what from_pretrained touches, so it was ready to train in about 30 seconds (up to 500 MB/s). Because Hugging Face charges no egress, that pull cost nothing; had the model lived in S3, every read to a GPU on another cloud would have been billed egress ($0.09/GB on AWS).
- Checkpoints streamed straight to the bucket at up to ~170 MB/s (8.43 GB of weights each) and persisted past the GPU instance.
Per cloud, checkpoints wrote to the bucket at:
Cloud
GPU
Checkpoint write
AWS (us-east-2)
L40S
~168 MB/s
GCP (us-central1)
L4
~123 MB/s
Lambda (us-west-3)
H100
~112 MB/s
Xet-backed storage: dedup for checkpoints and model variants
Hugging Face Buckets are built on Xet, which uses content-defined chunking to split files into ~64 KB chunks and store each unique chunk once. Because the boundaries follow the content, an edit changes only the chunks it touches and the rest are recognized as already stored. This pays off in a few places:
- Incremental and adapter checkpoints. When you freeze layers, train adapters, or otherwise leave most weights untouched between saves, only the changed chunks upload instead of the whole checkpoint.
- Model variants that share a base. Fine-tunes and quantizations of one base model overlap heavily, so the shared chunks are stored once across all of them.
- Datasets you append to. Logs like conversation traces or inference outputs grow by appending rows to large Parquet files. The existing row groups stay byte-identical, so only the new rows transfer: in Hugging Face's test, appending 10K rows to a 100K-row table moved about 10 MB instead of the full ~106 MB. (If you edit or delete rows in place, write with use_content_defined_chunking=True to keep changes local.)
- Re-uploads skip what's already stored. In our test, re-uploading an 8.43 GB blob already in the bucket took about 8 seconds, versus 24 seconds for the first upload, because only chunk hashes move. The same mechanism lets server-side hf buckets cp between repos and buckets copy by reference instead of re-uploading bytes.
How much you save depends on how much your artifacts overlap, but the deduplication is automatic: you write a checkpoint as usual, and only the new chunks leave the machine.
Get started
pip install "skypilot[huggingface]"
hf auth login # or: export HF_TOKEN=<your-token>
Add an hf:// mount to any SkyPilot task and launch. MOUNT needs a base image with glibc 2.34+ and /dev/fuse.
Built together: Hugging Face and SkyPilot
The initial store: hf support started as a contribution from Nikhil Jha. The Hugging Face team carried it forward and upstreamed the hf-mount FUSE fixes that let it mount in unprivileged containers, the default on many Kubernetes clusters. The SkyPilot team wired it into the storage backend. The whole path is open source: SkyPilot, Hugging Face's hf-mount, and the huggingface_hub client.
Resources
- SkyPilot storage docs
- Hugging Face Storage Buckets guide
- hf-mount
- Xet: content-defined chunking and deduplication
- SkyPilot Slack community
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み