第 2 章: データ インフラストラクチャの選択¶
章の概要¶
職人は仕事をうまくやる前に、道具を研ぐ必要があります。 TB レベルまたは PB レベルの LLM トレーニング データを処理する前に、適切なインフラストラクチャを選択することが、プロジェクトの成功または失敗を決定する最初のステップです。この章では、ストレージ、コンピューティング、フォーマット、バージョン管理という 4 つの側面から最新のデータ スタック テクノロジの選択を体系的に紹介し、読者が効率的でスケーラブルで再現可能なデータ処理プラットフォームを構築できるように支援します。
シナリオの紹介¶
あなたは AI スタートアップ企業に入社し、LLM 事前トレーニング データ処理プラットフォームの構築を担当しました。チームの状況は憂慮すべきものです。データは、.txt、.json、.csv、.parquet などのさまざまな形式で、50 台のマシン上のローカル ディスクに分散しています。データが処理されるたびに、Python スクリプトを手動で作成し、完了するまでに 1 台のマシンで 3 日間実行する必要があります。先週、誰かが重要なデータセットを誤って上書きしてしまい、バックアップやバージョン記録がありませんでした。あなたの上司は、「1 か月以内にトレーニングを開始しますが、データ プラットフォームの準備はできていますか?」と尋ねます。
最初の決断: チームで使い慣れた Spark を使用するか、それとも「AI ネイティブ」Ray に切り替えるか?セルフホスト型 MinIO クラスターを構築しますか、それとも S3 を使用してクラウドに直接移行しますか?これらの質問に対する「標準的な答え」はありませんが、明確な意思決定の枠組みは存在します。この章ではそのフレームワークを提供します。
2.1 最新のデータ スタック (MDS)¶
2.1.1 最新のデータスタックとは何ですか?¶
「モダン データ スタック」(MDS) は、データ エンジニアリングにおいて近年注目されている概念であり、クラウド ネイティブでモジュール式で分離されたデータ インフラストラクチャの組み合わせを指します。従来の統合データ プラットフォームと比較して、最新のデータ スタックの中心となる哲学は、ストレージ、コンピューティング、オーケストレーション、その他の機能を独立したコンポーネントに分割し、それぞれをニーズに応じて個別に置き換えたり拡張したりできることです。
図 2-1: 最新のデータ スタック アーキテクチャ — ストレージからアプリケーション層までの 5 層の分離アーキテクチャ。各層は独立して交換可能
従来のデータ プラットフォームは、多くの場合、統合システム、ストレージとコンピューティングが緊密に結合されたローカル データ センターに展開されます。 Hadoop エコシステムを例にとると、HDFS と MapReduce の結合により、コンポーネントの置き換えが非常に困難になります。データ形式は独自のものであることが多く、深刻なベンダーロックインにつながります。スケーリングは主に垂直方向であり、より強力な単一マシンを購入することでパフォーマンスを向上させますが、初期費用は高くなります。
| 特集 | 従来のアプローチ | 最新のデータスタック |
|---|---|---|
| 展開モード | ローカルデータセンター、統合システム | クラウドネイティブで柔軟なオンデマンドのスケーリング |
| コンポーネントの結合 | ストレージとコンピューティングの境界 (HDFS + MapReduce など) | ストレージとコンピューティングの分離、各レイヤーは独立して交換可能 |
| データ形式 | 独自のフォーマット、ベンダーロックイン | オープンフォーマット (寄木細工、ORC) |
| スケーラビリティ | 主に垂直スケーリング | 水平スケーリング、ほぼ無制限 |
| コストモデル | 固定投資、高額な初期費用 | 従量課金制、柔軟なコスト |
最新のデータ スタックの出現により、この状況は一変しました。クラウドネイティブの導入により、オンデマンドでの柔軟なスケーリングが可能になります。ストレージとコンピューティングを完全に分離することで、各レイヤーを独立して進化させることができます。オープン データ形式 (Parquet、ORC など) により、ベンダー ロックインのリスクが排除されます。水平スケーリングにより、システムはほぼ無制限のデータ量を処理できるようになり、従量課金制のコスト モデルによりプロジェクトの立ち上げの障壁が大幅に低くなります。
2.1.2 ストレージ層: オブジェクト ストレージとデータ レイク¶
オブジェクト ストレージは、最新のデータ プラットフォームの事実上の標準基盤です。 AWS S3、Google Cloud Storage、Azure Blob、またはオープンソース MinIO のいずれであっても、その中心となる哲学は同じです。つまり、真のディレクトリ階層を持たず、bucket/key バイナリ構造のみを備えたフラットな名前空間です。理論的には無制限のストレージ。非常に高いデータ耐久性 (S3 は 11 ナイン、つまり 99.999999999% を主張します)。実際の使用量に応じて請求されるため、多額の先行投資は必要ありません。
オプションの中から選択する場合は、展開モード、互換性、コスト、その他の要素を考慮する必要があります。 AWS S3 は、最も成熟したエコシステムを備えたパブリック クラウド ホスティングのベンチマークであり、ほとんどの実稼働環境に適しています。 MinIO は、S3 と互換性のあるオープンソースの代替品であり、データ コンプライアンス要件のあるプライベート展開シナリオや開発/テスト環境に適しています。 Google Cloud Storage と Azure Blob はそれぞれ、すでに GCP または Azure エコシステムに深く関わっているユーザーに適しています。
| 特集 | AWS S3 | みにお | Google GCS | アズールブロブ |
|---|---|---|---|---|
| 展開モード | パブリック クラウドでホストされる | セルフホスト/プライベート クラウド | パブリック クラウドでホストされる | パブリック クラウドでホストされる |
| S3 の互換性 | ネイティブ | 100% 互換性あり | アダプター層が必要 | アダプター層が必要 |
| コールド/ホット階層化 | 氷河 | 階層化 | ニアライン/コールドライン | クール/アーカイブ |
| 最低コスト | $0.023/GB/月 | ハードウェアのコスト | $0.020/GB/月 | $0.018/GB/月 |
| 典型的な使用例 | 実稼働環境のデフォルト | プライベート展開/開発テスト | GCP エコシステム ユーザー | Azure エコシステムのユーザー |
オブジェクト ストレージは「ストレージ」の問題を解決しますが、トランザクションおよびメタデータ管理機能がありません。 S3 の Parquet ファイルを直接操作すると、多くの問題が発生します。ACID トランザクションがないため、同時書き込みによりデータが破損する可能性があります。効率的なクエリができないため、毎回すべてのファイルのメタデータをスキャンする必要があります。タイムトラベルはなく、データが上書きされると、過去のバージョンにロールバックすることはできません。
データ レイク テーブル形式は、まさにこれらの問題を解決するために登場しました。オブジェクト ストレージの上にメタデータ管理レイヤーを追加し、データ ウェアハウス レベルの機能を提供します。 Apache Iceberg、Apache Hudi、Delta Lake は、現在最も主流の 3 つのデータ レイク形式です。
図 2-2: データ レイクハウス アーキテクチャ — テーブル フォーマット レイヤーは、ACID トランザクション、タイム トラベル、スキーマ進化などの機能を提供します
Apache Iceberg は Netflix によって開発され、Apache Foundation に貢献しました。その最大の利点はエンジンの中立性です。Spark、Flink、Trino、Dremio、DuckDB、その他のコンピューティング エンジンとうまく連携します。 LLM データ エンジニアリング シナリオでは、Iceberg が最も推奨される選択肢です。 Apache Hudi は Uber によって開発され、ストリーミング バッチの統合とリアルタイムの更新に強みを持っています。実質的なリアルタイム更新のニーズがある場合 (RAG ナレッジ ベースの継続的な更新など)、Hudi を検討できます。 Delta Lake は、最も緊密な Spark 統合を使用して Databricks によって開発されました。すでに Databricks エコシステムに深く関わっている場合は、Delta Lake を選択すると最高のエクスペリエンスが得られます。
| 特集 | アパッチアイスバーグ | アパッチヒューディ | デルタ湖 |
|---|---|---|---|
| バックベンダー | Netflix → Apache | Uber → Apache | データブリック |
| オープンソースの学位 | 完全にオープンソース | 完全にオープンソース | コアのオープンソース、一部の機能は商用 |
| エンジンの互換性 | スパーク、フリンク、トリノ、ダックDB | スパーク、フリンク、プレスト | 主にスパーク |
| 典型的な使用例 | マルチエンジン混合使用、ベンダー中立 | ストリームとバッチの統合、リアルタイム更新 | Databricks エコシステムのユーザー |
実際に選択する場合は、次の決定木を使用できます。 まず、データ規模が 100TB を超えるかどうかを判断します。 「はい」の場合は、ACID トランザクションとタイムトラベルが必要かどうかをさらに検討します。「はい」でマルチエンジン アクセスのニーズがある場合は、Iceberg + S3 を推奨します。 Spark のみを使用する場合は、Delta Lake または Hudi を選択できます。 ACID 機能が必要ない場合は、S3/MinIO + Parquet を直接使用してください。データ ボリュームが 100 TB 未満のシナリオの場合、チーム サイズが小さい (5 人未満) 場合は、プロトタイプの検証にはローカル ディスク + Parquet で十分です。規模の拡大に応じて S3 + Parquet に移行します。
図 2-3: ストレージ レイヤーの選択デシジョン ツリー — データ スケール、ACID のニーズ、マルチエンジン アクセス、その他の要素に基づいて最適なソリューションを選択します
2.1.3 コンピューティング層: Spark データと Ray データ¶
これは、LLM データ エンジニアリングにおける最も一般的な「二者択一」のジレンマです。どちらも分散コンピューティング フレームワークですが、設計哲学と使用例が明らかに異なります。正しい技術的な選択を行うには、それらの違いを理解することが重要です。
Apache Spark は 2009 年にバークレーの AMPLab で誕生しました。 15 年の開発期間を経て、ビッグデータ処理の「スイス アーミー ナイフ」となりました。 Spark の中核的な強みは、その成熟度と安定性であり、非常に豊富なドキュメントとコミュニティ リソースを備え、PB 規模で実稼働検証されています。 Spark SQL を使用すると、データ アナリストも分散処理ロジックを作成できるようになり、参入障壁が低くなります。 Structured Streaming はリアルタイム データ処理をサポートし、ストリームとバッチの統合を実現します。ただし、Spark には明らかな欠点もあります。コアは JVM 実装であり、Python UDF は JVM-Python 間のシリアル化を必要とし、パフォーマンスのオーバーヘッドが大きくなります。 「AI ネイティブ」ではなく、GPU と PyTorch/TensorFlow の統合サポートが弱くなっています。演算子はそれらの間の中間結果を具体化する必要があるため、メモリに大きな負荷がかかります。
Ray は 2017 年にバークレーの RISELab で生まれ、当初は分散強化学習フレームワークでしたが、後に一般的な AI アプリケーション インフラストラクチャに進化しました。 Ray Data は、AI ワークロード専用に設計されたデータ処理モジュールです。 Ray Data の中核的な強みは、Python ネイティブであり、JVM オーバーヘッドがなく、PyTorch、HuggingFace、その他の AI エコシステムとシームレスに統合されています。高いメモリ効率によるパイプライン実行をネイティブにサポートします。内蔵 GPU スケジューリングにより、CUDA オペレーターを簡単に呼び出すことができます。アクター モデルは、ロードされた ML モデルを必要とする推論タスクなど、ステートフルで複雑な処理に適しています。ただし、Ray は比較的若いため、Spark ほど豊富なドキュメントやベスト プラクティスはありません。 SQL サポートが弱く、Spark SQL のような成熟した SQL インターフェイスはありません。従来のビッグデータ エコシステム (Hive、Iceberg) との統合には追加の作業が必要です。
| 寸法 | アパッチスパーク | レイデータ |
|---|---|---|
| 言語 | Scala/Java コア、Python API | Python ネイティブ |
| ランタイム | JVM | Python (矢印ベース) |
| データの抽象化 | DataFrame (バッチ思考) | データセット (ストリーム思考) |
| GPU サポート | RAPIDS プラグインが必要です | ネイティブサポート |
| PyTorch の統合 | 面倒 | 一級国民 |
| SQL サポート | とても成熟した | 限定 |
| 一般的なユーザー | 従来のビッグデータ チーム | AI/ML チーム |
それらの違いをより直観的に理解するには、具体的なコードの比較を検討してください。 Parquet ファイルを読み取り、短いテキストをフィルタリングし、テキストの長さを計算し、結果を保存するというタスクを想定します。
スパークの実装:
from pyspark.sql import SparkSession
from pyspark.sql.functions import length, col
# Initialize Spark Session
spark = SparkSession.builder \
.appName("TextFilter") \
.config("spark.executor.memory", "8g") \
.getOrCreate()
# Read → Filter → Compute → Save
df = spark.read.parquet("s3://my-bucket/raw_data/")
df_filtered = df.filter(length(col("text")) > 100) \
.withColumn("text_length", length(col("text")))
df_filtered.write.parquet("s3://my-bucket/processed_data/")
spark.stop()
レイデータの実装:
import ray
# Initialize Ray (auto-detect cluster resources)
ray.init()
# Define processing function
def filter_and_compute(batch):
mask = batch["text"].str.len() > 100
filtered = batch[mask].copy()
filtered["text_length"] = filtered["text"].str.len()
return filtered
# Read → Process → Save (pipeline execution)
ds = ray.data.read_parquet("s3://my-bucket/raw_data/")
ds_processed = ds.map_batches(filter_and_compute, batch_format="pandas")
ds_processed.write.parquet("s3://my-bucket/processed_data/")
ご覧のとおり、Spark は明示的な Executor メモリ構成を必要とし、宣言型 DataFrame API を使用します。 Ray はリソースを自動検出し、機能的な map_batches インターフェイスを使用します。 Spark のカスタム ロジックでは、シリアル化オーバーヘッドを備えた UDF を定義する必要があります。 Ray は通常の Python 関数を直接、より自然に使用します。
図 2-4: コンピューティング フレームワークの選択デシジョン ツリー — Spark は SQL/ETL シナリオに適し、Ray は GPU/ML シナリオに適しています
実際の意思決定を行うときは、次のロジックを使用できます。データ処理に GPU が必要な場合 (品質スコアリングのために BERT モデルを呼び出すなど)、Ray Data を選択するのがより自然です。 SQL および BI クエリの大きなニーズがある場合、Spark の SQL エコシステムはより成熟しています。すでに広範な Spark インフラストラクチャとコード資産がある場合は、移行コストを評価する必要があります。高コストの場合は Spark を維持し、低コストの場合は Ray を段階的に導入することを検討します。新しいプロジェクトの場合、チームの背景が決定的です。従来のビッグデータ チームは Spark の方が採用しやすいと考え、AI/ML チームは Ray の方がスムーズだと考えます。
言及する価値があります: 実際の大規模プロジェクトでは、Spark と Ray は相互に排他的ではなく、共存することがよくあります。一般的なハイブリッド戦略: Spark は、Iceberg/Hive テーブルの読み取り/書き込み、SQL 分析およびその他の ETL タスクの実行を含む、データ レイク/データ ウェアハウスとの対話を処理します。 Ray Data は、バッチ処理に GPU を使用して、推論のために大規模なモデルを呼び出すなど、ML 集約型の処理を処理します。この 2 つは共有オブジェクト ストレージ (S3 上の Parquet ファイル) を介してデータを交換し、それぞれの役割を果たし、相互に補完します。
2.2 データ形式と I/O の最適化¶
ストレージとコンピューティングを選択したら、次はデータのシリアル化形式を選択します。形式の選択は技術的な詳細のように見えるかもしれませんが、実際にはストレージ コスト、読み取り速度、ツールの互換性に直接影響します。フォーマット間の圧縮率の差は 10 倍に達する場合があります。列形式と行形式のクエリのパフォーマンスの差は同様に大きくなります。また、すべてのフレームワークがすべての形式をサポートしているわけではありません。
2.2.1 主流のデータ形式の比較¶
Parquet は、大規模な構造化データの事実上の標準です。これは列型ストレージを使用しており、同じ列のデータは物理的に連続しています。これにより、2 つの大きな利点がもたらされます。まず、圧縮が容易になります。同じ種類のデータをまとめて集めると、より高い圧縮率が実現します。 2 番目に、ベクトル化された読み取りが容易になり、特定の列をクエリするときにファイル全体をスキャンする必要がなくなります。 Parquet ファイルは、ファイルにスキーマが埋め込まれた自己記述型であり、外部メタデータ定義は必要ありません。また、JSON のような複雑な構造のネストされた型とネイティブ ディレクトリ パーティショニングもサポートします。 Parquet は、事前トレーニング コーパス ストレージ、特に列フィルタリングを必要とする分析クエリに推奨される形式です。 Spark、DuckDB、Pandas、その他のツールとうまく連携します。
JSONL (JSON Lines) も一般的な形式で、各行は独立した JSON オブジェクトです。その最大の利点は人間が読みやすいことです。head、cat、その他のコマンドを使用してコンテンツを直接表示できます。ストリーミング処理もサポートしており、ファイル全体をメモリにロードせずに 1 行ずつ読み取ることができます。スキーマは非常に柔軟で、各行に異なるフィールド構造を含めることができます。このタイプのデータは頻繁に手動で表示および編集する必要があるため、JSONL は特に SFT 命令データに適しています。データ交換や小規模データセット (10 GB 未満) にもよく使用されます。ただし、JSONL の欠点も明らかです。圧縮しないと容量が Parquet の 3 ~ 5 倍になり、読み取り速度が遅くなります (各行の JSON 文字列を解析する必要がある)。
WebDataset は、NVIDIA が主導する形式で、画像テキスト、ビデオ、その他のマルチモーダル データ用に特別に設計されています。その中心となるアイデアは、関連ファイル (1 つの画像とそのキャプションなど) を TAR アーカイブにパッケージ化することです。この設計はストリーミング読み取りをサポートしており、解凍せずにコンテンツを順次読み取ることができます。また、分散処理にも非常に適しており、各 TAR は独立したデータ シャードです。 WebDataset は、LAION スタイルの画像とテキストのペア データセットおよびビデオ データセットに最適な選択肢であり、複数ファイルの関連付けが必要なマルチモーダル データに適しています。
| 特集 | 寄木細工 | JSONL | ウェブデータセット |
|---|---|---|---|
| ストレージ効率 | 高 (カラム圧縮) | 低 (テキストの冗長性) | 中 (圧縮なしだがコンパクト) |
| 読み取り速度 | 高速 (ベクトル化) | 遅い (行ごとの解析) | 中 (シーケンシャル読み取り) |
| 人間が判読可能 | いいえ | はい | いいえ |
| マルチモーダル サポート | 弱い (エンコードが必要) | 弱い | 強力 (ネイティブ サポート) |
| 典型的な使用例 | トレーニング前のテキスト コーパス | SFT命令データ | 画像とテキストのペア、ビデオ データ |
2.2.2 圧縮アルゴリズムの選択¶
形式の選択に関係なく、圧縮アルゴリズムはストレージのコストと読み取り速度に大きな影響を与えます。正しい圧縮戦略には、スペース効率と時間効率のバランスを見つける必要があります。
Snappy が最も一般的なデフォルトの選択です。圧縮率は中程度ですが、圧縮速度と解凍速度はどちらも高速で、読み取りと書き込みのバランスが取れたシナリオに適しています。 LZ4 は、極限の読み取り速度を追求します。解凍パフォーマンスは Snappy よりもさらに速く、圧縮率はわずかに低く、読み取り遅延に敏感なシナリオに適しています。 Zstandard (ZSTD) は、特に高レベル (レベル 19 など) で最高の圧縮率を提供しますが、圧縮速度は遅く、ストレージ コストに敏感なアーカイブ シナリオに適しています。 Gzip は最も互換性のある選択肢であり、ほぼすべてのツールが Gzip をサポートしており、外部システムとのデータ交換が必要なシナリオに適しています。
| アルゴリズム | 圧縮率 | 圧縮速度 | 解凍速度 | 典型的な使用例 |
|---|---|---|---|---|
| キビキビ | 中 | 速い | 速い | デフォルトの選択、読み書きバランス |
| LZ4 | 下 | 非常に速い | 非常に速い | 極端な読み取り速度 |
| ZSTD | 高 | 中 | 速い | ストレージのコスト重視 |
| Gzip | 高 | 遅い | 中 | 高い互換性要件 |
実際には、階層化された戦略を採用できます。コールド データ (アーカイブ ストレージ、長期間読み取られることはほとんどありません) は、最大の圧縮率を得るために ZSTD レベル 19 を使用します。ホット データ (頻繁に読み取られ、処理されるデータ) は、解凍のオーバーヘッドを削減するために Snappy または LZ4 を使用します。ネットワーク転送シナリオでは、圧縮率と速度のバランスを取るために ZSTD レベル 3 を使用します。
2.2.3 I/O 最適化の実践的なヒント¶
大規模なデータ処理では、I/O がパフォーマンスのボトルネックになることがよくあります。次の 3 つのヒントにより、I/O 効率を大幅に向上させることができます。
適切なファイル サイズ設定が最初の重要なポイントです。よくある間違いは、多数の小さなファイル (たとえば、それぞれ 1MB のファイルを 100,000 個) を生成することです。これにより、メタデータのオーバーヘッドが大きくなり、S3 ListObjects の操作が非常に遅くなります。正しいアプローチは、データを少数の大きなファイルに統合することです。各 Parquet ファイルは 128MB ~ 1GB にする必要があります。小さすぎるとメタデータが肥大化し、並列処理が不十分になります。大きすぎるとタスクの負荷分散に影響します。
# Wrong: generate many small files
df.write.parquet("s3://bucket/data/", maxRecordsPerFile=1000)
# Correct: generate fewer large files (recommend 128MB - 1GB)
df.coalesce(100).write.parquet("s3://bucket/data/")
パーティションのプルーニングは 2 番目の重要なヒントです。書き込み時に特定の列でパーティション分割することで、読み取り時に必要なパーティションのみをスキャンするだけで済み、テーブル全体のスキャンが回避されます。パーティション列はカーディナリティを低くする必要があります (日付、言語、データ ソースなど)。カーディナリティの高い列 (ユーザー ID など) は避けてください。そうしないと、大量の小さなディレクトリが作成されます。
# Partition by date when writing
df.write.partitionBy("date").parquet("s3://bucket/data/")
# Only scan needed partitions when reading
spark.read.parquet("s3://bucket/data/date=2024-01-01/")
列の剪定は 3 番目のヒントです。列指向ストレージの最大の利点は、必要な列のみを読み取ることです。列の選択がクエリ ステートメントの早い段階で行われるようにし、すべての列を読み取ってからフィルタリングすることを避けてください。
# Wrong: read all columns
df = spark.read.parquet("s3://bucket/data/") # If 100 columns, all loaded
# Correct: only read needed columns
df = spark.read.parquet("s3://bucket/data/").select("text", "length")
図 2-5: I/O 最適化効果の比較 — パーティション プルーニング + 列プルーニングにより、クエリ時間を 91%、データ スキャン量を 92% 削減できます
これら 3 つのヒントを組み合わせて使用すると、クエリ時間を 55 秒から 5 秒に、データ スキャン量を 100 GB から 8 GB に短縮でき、非常に大きな効果が得られます。
2.3 データのバージョン管理 (DataOps)¶
コードには Git があり、機械学習モデルには MLflow があります。では、TB レベルのデータセットのバージョン管理はどのように行うのでしょうか?これは見落とされがちですが、LLM データ エンジニアリングにおいて非常に重要な問題です。
2.3.1 データにバージョン管理が必要なのはなぜですか?¶
次のシナリオを考えてみましょう。6 か月前にトレーニングされたモデルは特に優れたパフォーマンスを示し、上司は再現を望んでいます。サーバーを検索すると、トレーニング データがすでにクリーンアップされていることがわかります。「削除するように指示したのは誰ですか?」 「10TBもかかりました!」データ処理スクリプトはまだ存在しますが、依存するアップストリーム データは変更されています。処理フローを再実行すると、異なる結果が得られます。結論:再現できません。
このシナリオは実際の仕事でもよくあります。データのバージョン管理はまさにこのような問題を解決するために存在します。その中心的な価値は、次の 4 つの側面に反映されています。 再現性 - いつでもデータの状態を正確に復元します。トレーサビリティ - 生の入力から最終出力までの完全なチェーンを追跡します。コラボレーションの安全性 - 複数の人が同時にデータを変更しても競合しません。ロールバック機能 - データの問題が発見されたときに、すぐに前のバージョンに戻ります。
2.3.2 ツールの選択: DVC と LakeFS¶
現在、最も主流の 2 つのデータ バージョン管理ツールは DVC と LakeFS であり、設計理念が明らかに異なります。
DVC (データ バージョン コントロール) の設計哲学は「Git for Data」です。データ バージョン管理エクスペリエンスを可能な限り Git に近づけます。その動作原理: データ ファイル自体はリモート ストレージ (S3/GCS) に保存され、Git リポジトリにはデータ メタデータ ファイル (.dvc ファイル) のみが保存され、実際のデータは dvc push/pull コマンド経由で同期されます。
# Initialize DVC
dvc init
# Add dataset to version control
dvc add data/training_corpus.parquet
# Generates data/training_corpus.parquet.dvc and .gitignore
# Commit to Git
git add data/training_corpus.parquet.dvc .gitignore
git commit -m "Add training corpus v1"
# Push data to remote storage
dvc push
# Switch to historical version
git checkout v1_0
dvc checkout # Sync corresponding version data
DVC の利点は、既存の Git ワークフローとのシームレスな統合、緩やかな学習曲線、ML パイプライン定義のサポート (dvc.yaml 経由) であり、ファイル レベルのバージョン管理シナリオに適しています。欠点は、データセットごとに個別の .dvc ファイル管理が必要であり、きめの細かい「テーブルレベル」操作 (パーティションのロールバックなど) がサポートされていないことです。
LakeFS の設計哲学は「Git for Data Lake」です。オブジェクト ストレージ上に Git スタイルのブランチとコミットを提供します。その動作原理: LakeFS はオブジェクト ストレージ プロキシ レイヤーとして機能し、すべての読み取り/書き込みリクエストは LakeFS S3 ゲートウェイを経由し、システムはブランチ、コミット、マージ、およびその他の Git スタイルの操作をサポートします。
# Create development branch
lakectl branch create lakefs://repo/dev --source lakefs://repo/main
# Modify data on dev branch (via S3 protocol)
aws s3 cp new_data.parquet s3://lakefs-repo/dev/data/
# Commit changes
lakectl commit lakefs://repo/dev -m "Add new training data"
# Merge to main branch after validation
lakectl merge lakefs://repo/dev lakefs://repo/main
LakeFS の主な利点はゼロコピー ブランチングです。ブランチの作成ではデータはコピーされず、メタデータのみが記録されます。これは TB レベルのデータ レイクにとって重要です。 S3 と完全に互換性があります。既存のツール (Spark/Ray) は変更せずに動作します。欠点は、追加サービス (LakeFS サーバー) のデプロイメントが必要であり、DVC よりも学習曲線がわずかに急であることです。
| 特集 | DVC | レイクFS |
|---|---|---|
| 設計哲学 | データ用の Git 拡張機能 | オブジェクトストレージのバージョンレイヤー |
| 粒度 | ファイルレベル | オブジェクトレベル (より細かい) |
| ブランチのオーバーヘッド | .dvc ファイルをコピーする必要があります | ゼロコピー |
| S3 の互換性 | dvc コマンドが必要です | ネイティブ S3 API |
| 展開の複雑さ | 低 (CLI ツール) | 中 (サーバーが必要) |
| 適切なシナリオ | ML 実験管理、小規模データ | データ レイク管理、大規模データ |
図 2-6: DVC と LakeFS アーキテクチャの比較 — DVC は Git に基づいたファイル レベルのバージョン管理を提供し、LakeFS は分岐を備えたゼロコピーのオブジェクト レベルのバージョン管理を提供します
選択の推奨事項は非常に明確です。データ量が 1 TB 未満の場合、主に ML 実験管理のための Git ワークフローに精通しているチームは DVC を選択します。データ量が TB レベル以上で、データ レイク レベルのバージョン管理が必要で、複数のチームが並行して動作する場合は、LakeFS を選択します。
2.3.3 データリネージの追跡¶
バージョン管理は「データとは何か」を解決します。リネージ追跡は、「データがどこから来たのか」を解決します。系統追跡レコード: このデータはどの上流データから得られたものですか?どのような処理スクリプトとパラメータが使用されましたか?いつ、誰によって処理が実行されたのか?
リネージ追跡を実装するには複数のアプローチがあります。 Spark を使用している場合、OpenLineage 統合を通じて自動リネージ追跡を取得できます。 Airflow などのオーケストレーション ツールを使用する場合は、Marquez が適しています。エンタープライズ データ ガバナンスのニーズに対して、DataHub と Apache Atlas はより完全な機能を提供します。単純なシナリオの場合、メタデータ ファイルを生成するための手動インストルメンテーションは軽量なソリューションです。
import json
from datetime import datetime
metadata = {
"version": "v2_0",
"created_at": datetime.now().isoformat(),
"created_by": "data-pipeline-v3_2",
"inputs": [
{"path": "s3://bucket/raw/crawl_2024_01.parquet", "version": "abc123"},
{"path": "s3://bucket/raw/crawl_2024_02.parquet", "version": "def456"}
],
"processing": {
"script": "cleaning_pipeline.py",
"git_commit": "789xyz",
"params": {"min_length": 100, "dedup_threshold": 0.9}
},
"outputs": [
{"path": "s3://bucket/processed/clean_2024_q1.parquet", "records": 1000000}
]
}
with open("clean_2024_q1.metadata.json", "w") as f:
json.dump(metadata, f, indent=2)
2.4 よくある間違いと落とし穴ガイド¶
インフラストラクチャの選択プロセスでは、経験豊富なエンジニアでもいくつかの典型的な間違いを犯しやすくなります。ここでは、読者が注意していただけることを願って、最も一般的な 3 つの問題を要約します。
最初のよくある間違いは、時期尚早な最適化と過剰なエンジニアリングです。 チームによっては、5 人のメンバーと 500 GB のデータしか持たないにもかかわらず、Spark クラスター + Iceberg + Airflow + LakeFS の「フルスタック」インフラストラクチャを構築しています。結果: 時間の 80% がインフラストラクチャの保守に費やされ、実際のデータ処理には 20% のみが費やされました。正しいアプローチは、シンプルに始めて、必要に応じて進化させることです。 500 GB のデータ ボリュームの場合、単一マシン + Parquet + DVC で完全に十分です。データ量が 10TB に増加した場合は、分散ソリューションを検討してください。
2 番目によくある間違いは、エコシステムを無視して盲目的に新しいテクノロジーを追い求めることです。 一部のチームは、いくつかのブログ投稿を読んで、Ray のために Spark を完全に放棄することを決定しましたが、会社の Hive テーブルと Iceberg テーブルが直接読み取れないことに気づきました。最後に、実質的なデータ変換スクリプトを作成する必要があり、データ整合性のリスクが増大します。正しいアプローチは、技術的な選択の前に、既存のデータ資産と上流と下流の依存関係を完全に評価することです。技術的な選択は単一点の決定ではなく、システム エンジニアリングであり、エコシステム全体の互換性を考慮する必要があります。
3 番目のよくある間違いは、過度に積極的なストレージ コストの最適化です。 一部のチームは、ストレージ コストを節約するために、すべてのデータを ZSTD レベル 22 に圧縮し、S3 Glacier Deep Archive に保存します。結果: データを読み取る必要があるたびに、解凍まで 12 時間待機し、解凍にはさらに 4 時間かかります。モデルのトレーニングは 1 週間前にスケジュールする必要があります。正しいアプローチは、コールド データとホット データを区別することです。アクティブに処理されたデータは、S3 Standard + Snappy 圧縮で保存されます。 6 か月以上使用されなかったアーカイブ データは Glacier に送られます。ストレージ コストとアクセス効率のバランス ポイントを見つける必要があります。
2.5 章の概要¶
この章では、LLM データ エンジニアリング インフラストラクチャの選択について系統的に紹介し、ストレージ、コンピューティング、フォーマット、バージョン管理という 4 つの中核的な側面をカバーしました。
ストレージの選択: オブジェクト ストレージ (S3/MinIO) は最新のデータ スタックの基盤です。データ レイク フォーマット (Iceberg/Hudi/Delta) は、ACID トランザクション、タイムトラベル、その他の問題を解決します。 LLM シナリオの場合、Iceberg はエンジンの中立性が最も優れているため、S3 + Iceberg の組み合わせが推奨されます。
コンピューティングの選択: Spark は成熟度と安定性、強力な SQL エコシステムで知られており、従来のビッグ データ チームに適しています。 Ray Data は、Python ネイティブの AI フレンドリーなフレームワークであり、ML/AI チームに適しています。この 2 つは相互に排他的ではなく、混合することができます。Spark は ETL を処理し、Ray は ML 処理を処理します。
データ形式: Parquet は構造化データのデフォルトであり、JSONL は手動表示が必要な小規模データに適しており、WebDataset はマルチモーダル データに最適な形式です。圧縮アルゴリズムと I/O 最適化のヒントは、パフォーマンスとコストに大きな影響を与える可能性があります。
バージョン管理の場合: DVC は軽量で Git と緊密に統合されているため、ML 実験に適しています。 LakeFS は、大規模な運用環境に適したデータ レイク レベルのバージョン管理を提供します。
全体を通しての基本原則は、シンプルに開始し、要求に応じて進化させ、過剰なエンジニアリングを避けることです。技術的な選択は、それ自体のために技術の進歩を追求するものではなく、ビジネス目標にかなうものである必要があります。
図 2-7: データ インフラストラクチャ選択のクイック リファレンス — ストレージ、テーブル形式、コンピューティング、およびバージョン管理に関する 4 象限の意思決定ガイド
さらに読む¶
この章の内容について理解を深めたい読者は、次のリソースを参照する価値があります。
Ray Data の公式ドキュメント (docs.ray.io) には、Ray Data のベスト プラクティスと詳細な API リファレンスが記載されています。 Apache Iceberg 公式ドキュメント (iceberg.apache.org) には、テーブル形式の詳細な仕様とエンジン統合ガイドが含まれています。 DVC 公式チュートリアル (dvc.org/doc) は、クイックスタートの良い出発点です。 LakeFS の公式ドキュメント (docs.lakefs.io) では、アーキテクチャの設計と展開のオプションについて詳しく説明しています。
Databricks が公開したデータ レイク選択に関するホワイト ペーパーでは、Delta、Iceberg、Hudi 形式の詳細な比較分析が提供されています。 Uber が公開した「Scaling MLOps at Uber」記事では、PB 規模で ML データを管理する方法を紹介しています。これらの資料は、読者がより包括的な技術的観点を構築するのに役立ちます。
次の章のプレビュー¶
次の章 データの取得と収集 では、正式にトレーニング前のデータ処理フローに入ります。 Common Crawl や The Pile などのオープンソース データセットを取得して解析する方法、Trafilatura を使用して高性能 Web ページ パーサーを構築する方法、GitHub や ArXiv からコードや論文をクロールするための特殊な戦略を学びます。
この質問を次の章に取り入れてください。Common Crawl は毎月 3 ~ 5 PB のデータを追加します。そこから必要なコンテンツを効率的に抽出するにはどうすればよいですか?






