HUOXIU

百度の兆単位の特徴計算システムの実践

出典: Baidu Geek Talk
著者 | ジェイ

導入
導入
この記事では、主に、機械学習エンジニアリング、リソース スケジューリング、ストレージ最適化などの複数のトピックを網羅し、ネットワーク全体の 1 兆レベルのコンテンツを理解するための Baidu Search のエンジニアリング プラクティスを紹介します。

全文は6,648語から成り、読むのに17分かかると推定されます。



オタクトーク

01

事業背景

Baiduはインターネット上の膨大な量のコンテンツをインデックスしています。このコンテンツをインデックスするには、まずコンテンツを深く理解し、セマンティクス、品質、セキュリティなど、多角的な視点から情報を抽出する必要があります。これにより、コンテンツフィルタリングやセマンティックデータベースの構築といったニーズにも対応できます。インターネット全体にわたる膨大な量のコンテンツを深く理解することは、主にコストと効率性の面で非常に困難です。

コスト面では、計算負荷は膨大です。オンラインコンテンツデータの膨大な量(数兆データポイント)と多数の特徴量に加え、2つの傾向が計算負荷を悪化させています。第一に、インターネット上のグラフベースおよび動画ベースのコンテンツの割合が大幅に増加し続けており、画像/動画に対する計算負荷がテキストよりもはるかに大きくなっています。第二に、ディープラーニング技術の大規模な応用、特に近年の大規模モデルの台頭により、計算能力の要件が急増しています。効率性の観点から見ると、システムをよりユーザーフレンドリーにし、ビジネスの反復効率を最大化することは、あらゆるエンジニアリングシステムの中核的な目標の一つです。


オタクトーク

02

重要なアイデア

(1)コスト最適化:このような膨大な計算能力の需要を満たすには、「オープンソースを最大限に活用し、支出を削減する」ことが必要です。

1. **オープンソース:** コンピューティングリソースプールを可能な限り拡張します。調達はROIの低さを補う手段となるかもしれませんが、既存リソースの最適化が鍵となります。全社的な視点で見ると、リソースの活用は不十分です。オンラインリソースにはピークと谷があり、在庫にはかなりの量のアイドルリソースが存在します。コンピューティングの大部分はオフラインであるため、リソースの安定性に対する要件は高くありません。これら2つの側面を組み合わせることで、リソースの問題を解決するための柔軟なコンピューティングスケジューリングシステムを構築できます。

2. **コスト削減:** サービスパフォーマンスを可能な限り最適化し、単位計算コストを削減します。モデル推論の計算量は膨大ですが、最適化の余地は大きく残されています。モデル構造とGPUハードウェア特性を最適化することで、モデルサービスのシングルカードスループットを大幅に向上させることができます。さらに、CPU処理の最適化や、Baiduが独自開発したKunlunチップの採用など、様々な手法によって単位コストを削減することも可能です。

(2) 効率最適化:図に示すように、ビジネスプロセス全体はリアルタイム計算とオフライン計算の2つの部分から構成されています。新しい機能を追加するには既存データのオフライン処理が必要ですが、スパイダーによって新たに収集されたデータについては、タイムリー性の高いデータを選択してリアルタイム計算を行い、残りはオフラインで計算します。計算の大部分はオフラインで行われます。主な効率化の課題は、迅速なモデルエンジニアリングをどのようにサポートするか、オフライン計算の効率をどのように向上させるか、です。

1.モデルサービスフレームワークとプラットフォーム:モデルエンジニアリングは、統合されたモデルサービスフレームワークとそれをサポートするモデルサービスプラットフォームによって実現されます。モデルサービスフレームワークとプラットフォームは、構築、テスト、デプロイメントに至るまで、モデルサービスライフサイクルのすべての段階をサポートし、カバーします。

2. 特徴量バッチ計算プラットフォーム:オフライン特徴量計算の効率性問題を解決するため、統合バッチ計算プラットフォームを構築しました。オフラインタスク開発から計算までの各段階における効率性とパフォーマンスのボトルネックを分析し、徹底的に最適化することで、効率性を最大限に向上させました。


オタクトーク

03

技術的解決策

3.1 全体的なアーキテクチャ

全体のアーキテクチャを下図に示します。コアコンポーネントは、モデルサービスプラットフォーム、バッチコンピューティングプラットフォーム、コンピューティングスケジューリングシステム、およびモデルサービスフレームワークです。

1.モデルサービスフレームワーク:アルゴリズムチームは、サービスのカプセル化に統一されたモデルサービスフレームワークを使用しています。開発効率を高めるため、フレームワーク言語としてPythonを選択しましたが、Pythonはパフォーマンスの問題も深刻であり、多くのターゲットを絞った最適化が必要です。さらに、サービスユニットあたりの計算コストを最小限に抑えるため、様々な推論最適化手法をフレームワークに継続的に統合しています。

2. モデルサービスプラットフォーム:モデルサービスプラットフォームは、モデルサービスのDevOpsと機能出力をサポートします。このプラットフォームでは、「オペレータ」を管理粒度として用い、「オペレータ」はビデオ分類などの完全な機能を表します。ビデオ分類は通常、複数のモデルサービスの連携を必要とします。アルゴリズムエンジニアは、オペレータをプラットフォームに登録し、サービストポロジなどのメタデータを提供します。また、自動パフォーマンスチューニングと自動負荷テストを通じてパフォーマンスレポートを生成します。サービストポロジとパフォーマンスレポートは、後続のスケジューリングに不可欠な入力情報となります。このプラットフォームは、オペレータの検索と試行機能も提供し、プラットフォームベースの方法でその他のビジネスニーズをサポートします。

3. 計算スケジューリングシステム:計算スケジューリングシステムは、トラフィックとリソースの統合スケジューリングを実行します。モデルサービスへのすべてのリクエストは、計算スケジューリングシステムのゲートウェイを通過し、トラフィック制御とルーティングポリシーを実行します。また、計算スケジューリングシステムは、Baiduの複数のPaaSプラットフォームから、アイドル状態のさまざまな異種リソースをスケジュールし、適切なオペレーターを自動的に展開することで、オフラインコンピューティングのスループットを向上させます。

4. バッチコンピューティングプラットフォーム:バッチコンピューティングプラットフォームは、オフラインジョブタスク生成、タスクスケジューリング、DevOpsなどの機能をサポートします。HTAPベースのストレージソリューションを構築することで、スキャンスループットのボトルネック問題を解決し、コンピューティングスケジューリングシステムと連携することで、大規模なオフラインコンピューティングをサポートします。

3.2 主な技術的ポイント

この章では、主に、遭遇した技術的な困難、考え方やトレードオフなど、システムの主要な技術的なポイントについて詳しく説明します。また、読者の皆様がいくつかの一般的な問題について私たちとさらにコミュニケーションを取っていただけることを願っています。


3.2.1 モデルサービスフレームワーク

実際のビジネス シナリオでは、モデル サービス フレームワークは、ビジネス プログラミング モデル、Python サービス パフォーマンスの最適化、推論パフォーマンスの最適化など、いくつかの重要な問題に対処する必要があります。これらについては、以下で説明します。


3.2.1.1 ビジネスプログラミングモデル

特定の機能を実装するには、複数のモデルや様々なデータ処理ロジックを組み合わせる必要があることがよくあります。処理フローを抽象的に表現し、汎用的なロジックの再利用性を実現するために、以下のソリューションを採用しています。

  • ビジネスロジックはDAG(有向非巡回グラフ)として記述され、ノードはOpsと呼ばれます。DAGは複数のOpsで構成され、直列または並列に接続できます。Opsはモデル推論または処理ロジックの一部です。Opsはデータホワイトボードを介して互いにコンテキストを渡します。DAGを使用すると、全体的な処理フローが明確になり、コードの可読性と保守性が向上します。

  • モデル推論、ビデオフレーム抽出、ビデオ変換といった共通ロジックを汎用Opライブラリに統合し、ビジネスにおける再利用性を高めます。企業は必要に応じてOpをカスタマイズ・拡張し、フレームワークに登録して利用することも可能です。


3.2.1.2 Pythonサービスのパフォーマンス最適化

Pythonを選択すると開発コストは削減されますが、Python GIL(Global Interpreter Lock)問題が発生します。この問題により、マルチコアCPUのフル活用が妨げられ、サービスのスループットが大幅に制限されます。解決策は次のとおりです。

  • 並行処理スキームは、マルチプロセス + 非同期コルーチン + CPU/GPUコンピューティング分離アプローチを採用しています。このサービスには、RPCプロセス、DAGプロセス、モデルプロセスの3種類のプロセスが含まれており、これらは共有メモリ/GPUメモリを介して相互に連携します。

  • PRCプロセスはネットワーク通信を担い、BRPC(オープンソース版:https://github.com/apache/brpc)をベースに開発されています。Pythonマルチプロセスとコルーチンの並列モードをサポートするために、BRPCのPython実装を最適化しました。実際のビジネスシナリオテストでは、最適化後のパフォーマンスが5倍以上向上しました。

  • DAGプロセスはDAG実行(CPU処理)を担い、複数のDAGプロセスとオペレーションの非同期コルーチン実行によってCPUマルチコアを最大限に活用します。もう一つの重要なプロセスはModelOpです。これは実際には推論エージェント(RPCに類似)です。実際の推論はローカルのモデルプロセスまたはリモートサービスで実行されます。ModelOpは呼び出しの詳細を隠蔽するため、ユーザーはモデルを簡単に利用できます。

  • モデルプロセスは、モデル推論(GPU処理)を担当します。GPUメモリの制限を考慮し、モデルプロセスとDAGプロセスは独立して分離されています。モデルプロセスはPyTorchやPaddleといった複数の推論エンジンをサポートし、多くの推論最適化が行われています。テンソルデータは通常大きいため、DAGとモデルプロセスは共有GPUメモリを直接使用してテンソルを転送することで、不要なメモリコピーを回避しています。

主な最適化手法には、推論スケジューリング、推論最適化、モデル量子化、モデル圧縮などがあります。最適化後、単一のサービスカードのスループットは通常、ネイティブ実装のスループットの数倍になります。

1. 推論スケジューリング:動的バッチ処理とマルチストリーム実行。GPUバッチコンピューティングはより効率的です。サービスはリアルタイムの単一リクエストも受け入れるため、リクエストをバッチ処理することはできません。そのため、リクエストをバッチ処理するためにインサービスキャッシュが使用され、スループットのためにレイテンシが犠牲になります。ストリームはGPUタスクキューと見なすことができます。デフォルトでは単一のグローバルキューであり、タスクはシリアルに実行されます。GPU I/O操作(メモリコピー)中は、コンピューティングユニットはアイドル状態です。複数のストリームを作成することで、異なる推論リクエストは異なるストリームを使用できるため、I/Oと計算を完全に並列化できます。

2. 推論の最適化:業界ではTensorRTを使用するのが主流ですが、実際のアプリケーションでは、動的グラフの静的化の失敗やTensorRT Opsの不完全なカバレッジなどの問題があります。これらの問題を解決するために、チームはPoros(オープンソース版:https://github.com/PaddlePaddle/FastDeploy/tree/develop/poros )を開発しました。これは、TorchScript、グラフ最適化、TensorRT、vLLMなどの技術を組み合わせ、わずか数行のコードを追加するだけで、複雑なモデル変換なしに推論性能を大幅に向上させます。これにより、効率と性能の双方にメリットをもたらします。同時に、PorosはKunlunなどの異種ハードウェアもサポートしています。

3. モデルの量子化:GPUやKunlunなどのハードウェアは、低精度に対して強力な計算能力を備えています。量子化は効果の損失はわずかですが、スループットを大幅に向上させます。そのため、実稼働環境ではFP16、あるいはINT8/INT4の量子化が用いられます。この部分もPorosでサポートされています。

4. モデル圧縮:これは、モデル蒸留やモデルプルーニングなどの手法を用いてモデルパラメータを簡素化し、計算量を削減することを意味します。ただし、学習が必要であり、有効性もある程度低下します。通常はアルゴリズムエンジニアと共同で実施されます。


3.2.2 計算スケジューリングシステム

コンピューティングスケジューリングシステムの動作アーキテクチャを以下に示します。すべてのリクエストトラフィックは、様々なトラフィック制御およびルーティング戦略をサポートする統合ゲートウェイ(FeatureGateway)を通過します。オフラインジョブもゲートウェイを介してコンピューティングリクエストを送信し、ゲートウェイはこれらのリクエストをスケジューラ(SmartScheduler)に転送してスケジューリングを行います。スケジューラはBaidu内の複数のPaaSサービスに接続し、アイドル状態のリソースを継続的に監視し、要件、各種メトリック、およびアイドル状態の異種リソースの分布に基づいて、適切なオペレータを自動的にスケジューリングおよび展開します。オペレータのメタデータはサービスプラットフォームから取得されます。スケジューリングが完了すると、スケジューラはゲートウェイのフロー制御とルーティングを調整します。

システムに関する 2 つの重要な質問は、オペレータ (複雑なサービス トポロジを含む複合サービス) の自動展開をどのように実現するか、および不安定なトラフィック分散や複数の異種リソースなどの複雑な条件下でどのようにスケジューリングを実行するかです。


3.2.2.1 自動デプロイメント

スケジューラ開発を簡素化するために宣言型プログラミングを採用していますが、実際にはKubernetesコントローラーのメカニズムに基づいて開発されています。オペレーター向けの自動デプロイメントスキームは次のとおりです。

1. CRD拡張:Kubernetes CRDを利用してServiceBundle(オペレータデプロイメントパッケージ)などのオブジェクトをカスタマイズし、コントローラーメカニズムを用いてPaaSなどの外部システムへのデプロイメント操作を実行します。ServiceBundleには、オペレータが必要とするすべてのサブサービスのデプロイメント情報とトポロジが含まれています。オペレータサービスのスケジュールと作成時には、最下層から順にサブサービスが作成されます。上位層のサブサービスは、通信ホスティングメカニズムを介して下流のサブサービスのアドレスを取得できます。

2. 通信ホスティング:通信ホスティング機構は、構成センターとモデルサービスフレームワークに基づいて実装されています。サービス起動コマンドは、リモート構成アドレスとAppIDを伝達します。リモート構成をロードすることで、起動時に下流のサービスアドレスを変更できます。理想的な解決策は、ServiceMeshなどの技術を用いて、アーキテクチャ機能とビジネス戦略を分離することです。しかし、複数のPaaSプラットフォームに展開していることを考えると、各PaaSにServiceMesh SideCarコンポーネントを展開するのはコストが高く、フレームワークへの統合も負担が大きすぎます。そのため、まずは構成センターベースのソリューションを構築し、適切な時期が来たら移行を検討します。


3.2.2.2 スケジュール設計

スケジューリングは非常に複雑な問題です。私たちのシナリオでは、その複雑さは主に以下の側面に反映されています。

1. オペレータスケジューリング:オペレータ(複合サービス)が処理できるトラフィック量は、最も弱いサブサービスの容量に依存します。スケジューリングでは、最も強いサービスにリソースを無駄に費やさないように、総合的な考慮が必要です。

2. トラフィック分布の変化:一部のオペレータのパフォーマンスは、入力データの分布によって影響を受ける可能性があります。例えば、ビデオOCRはビデオの長さや画面上のテキストの割合に影響を受けるため、スケジューリング時に適応的な調整が必要になります。

3. 複数の異種ハードウェア:一部の演算子は複数の異種ハードウェア(Kunlun/GPU/CPUなど)をサポートできますが、他の演算子は1つのタイプにしかバインドできません。グローバルリソースを最も効率的に使用するために、どのようにリソースを割り当てるべきでしょうか?

4. その他の要因:タスクの優先度、リソースの優先度、リソースの変動などもスケジュールに影響します。実際のスケジュール作成では、様々な要因を考慮する必要があります。

上記の要素に基づいて、スケジュール設計スキームは次のようになります。

1. 2段階スケジューリング:スケジューリングは、トラフィックスケジューリングとリソーススケジューリングという2つの独立した段階に分かれています。トラフィックスケジューリングは、現在のオペレータサービスキャパシティを各ジョブに割り当て、結果をゲートウェイに同期し、トラフィックポリシーを調整する役割を担います。リソーススケジューリングは、リソースの可用性とオペレータキャパシティのギャップに基づいてスケジューリングを行い、最終的にオペレータサービスインスタンスのスケールアップまたはスケールダウンを行います。

2. トラフィックスケジューリング:トラフィックスケジューリングの調整フェーズでは、タスクのパフォーマンス指標に基づいて正規化係数を調整し、各タスクに必要なQpsを正規化Qpsにマッピングします。正規化Qpsは、その後のすべてのスケジューリングの基準となるため、トラフィック配分の変動による影響を軽減します。ソートフェーズでは、ジョブを優先度に基づいてソートし、割り当てフェーズでは、ソート結果と優先度に基づいて、各ジョブに既存のオペレータキャパシティを割り当てます。バインドフェーズでは、結果を実行し、ゲートウェイへのルートを同期します。

3. リソース スケジューリング: リソース スケジューリングの準備フェーズでは、まずジョブの容量ギャップが対応するサービス インスタンス ギャップに変換されます。次に、HardwareFit を実行して、拡張するサービスを適切なハードウェア リソース キューに割り当て、リソースの不足、コンピューティングの費用対効果などに基づいて Sort を実行します。次に、PreAssign を実行して各サブサービスにリソースを事前に割り当てます。最後に、GroupAssign フェーズで複合サービスの各サブサービスのスケジュールの満足度を考慮し、複合サービスの各サブサービスの容量を微調整して、リソースの無駄を回避します。


3.2.3 バッチ計算プラットフォーム

バッチコンピューティングプラットフォームが解決すべき課題は、弾性リソースが比較的豊富な時間帯(夜間など)におけるテーブル(分散テーブルシステム)のスキャンスループットのボトルネックと、オフラインタスクの効率を最大限に最適化することです。具体的な解決策については、以下で紹介します。


3.2.3.1 HTAP ストレージ設計

まず、テーブル スキャンが遅くなる主な理由を分析してみましょう。

1.読み取り/書き込みの混在:OLTP(フェッチや更新など)とOLAP(機能のバッチ計算など)の両方の要件がテーブルにアクセスするため、複数の読み取り/書き込み方法が混在することになります。基盤となるストレージはHDDを使用しているため、このような読み取り/書き込みの混在により、ディスクI/Oスループットが大幅に低下します。

2. スキャン増幅:テーブルはストレージにワイドテーブル構造を採用しています。通常、様々なタスクはスキャン時に数列しか必要としませんが、テーブル全体をスキャンする場合は、データ行全体を読み取り、フィルタリングする必要があるため、深刻なIO増幅が発生します。

3. 高い拡張コスト:OLTPとOLAPの読み取りと書き込みが混在するため、スキャン容量を個別に拡張するにはコストがかかります。また、読み取りと書き込みの比率を固定することが難しいため、拡張リソースを予測することが困難です。

上記の分析から、OLTP/OLAPテーブルの混在使用が依然として重要な課題であることが明らかになりました。業界の慣例を踏まえると、単一のストレージエンジンではOLTPとOLAPの両方のシナリオのニーズを同時に満たすには不十分です。しかし、使いやすさを考えると、単一のストレージシステムで両方のシナリオをサポートすることが望ましいです。そこで、ビジネスシナリオと業界経験に基づき、以下のHTAPストレージソリューションを実装しました。

1. OLAP/OLTP ストレージの分離: バッチ コンピューティングなどの OLAP シナリオ向けに効率的な OLAP ストレージを構築し、OLAP/OLTP テーブルの混在使用によって発生する読み取り/書き込みの混在問題を軽減します。また、必要に応じて個別に拡張することもできます。

2. 高効率OLAPストレージ設計:当社が独自に開発したOLAPストレージは、RocksDBとAFS(BaiduのようなHDFS)上に構築され、増分同期、行データパーティショニング、および動的列データマージ設計を採用しています。テーブルデータ全体をN個の物理パーティションに分割し、テーブルの増分スナップショットを使用して、OLAPストレージデータを定期的かつ効率的に同期・更新します(基礎テーブルはLSMストレージを使用しているため、増分スナップショットはフルスキャンよりもはるかに効率的です)。列ストレージは、フィールドアクセスホットスポットに基づいて再編成され、頻繁にアクセスされる列を物理層にまとめて保存することで、IO増幅を減らし、動的な調整をサポートします。このソリューションにはデータ同期のレイテンシの問題がある可能性がありますが、当社のシナリオでは、適時性の要件は高くないため、この問題は無視できます。

3. HTAP SDK : テーブルストレージとOLAPストレージの両方へのアクセスをサポートする統合SDKを提供します。ユーザーはSDKに基づいて、独自のOLAPタスクとOLTPタスクを同時に実行できます。


3.2.3.2 タスクの生成とスケジュール

バッチ コンピューティング タスクの開発を簡素化するために、プラットフォームでは現在、開発の柔軟性/コストが低いものから高いものまで、使いやすさが高いものから低いものまで、構成可能、KQL、オフライン フレームワークの 3 つのタスク開発モードを提供しています。

  • 構成可能: 一般的で頻繁に使用されるタスク タイプの場合、プラットフォームはこれらのタスクを高度にカプセル化しており、Web インターフェイスで構成するだけで生成できます。

  • KQL : KQL は、SQL に似た独自開発の言語で、様々な汎用関数を提供し、ユーザー定義関数(Spark UDF に類似)もサポートしています。ユーザーは KQL を使用してデータのクエリと処理を行うことができます。
















 Function classify = { def classify (cbytes, ids) :    unique_ids=set(ids)    classify=int.from_bytes(cbytes, byteorder= 'little' , signed=False) while classify != 0 :        tmp = classify & 0xFF if tmp in unique_ids: return True        classify = classify >> 8 return False }
declare ids = [ 2 , 8 ]; select * from my_table convert by json outlet by row filter by function@classify(@cf0 :types , @ids);
  • オフライン フレームワーク: フレームワークは、データの読み取りと書き込み、一般的な変換などの機能を提供します。ユーザーはフレームワークの仕様に従ってロジックをカスタマイズし、オフライン タスク展開パッケージを生成してプラットフォームに送信し、プラットフォームがタスクをスケジュールします。

以下に説明する手法に加え、このプラットフォームは大規模モデルの統合も検討しており、自然言語ベースのタスク生成を実現しています。実際には、どの手法を用いるかに関わらず、最終的に生成されるオフラインタスクはすべてオフラインフレームワークに基づいており、特定のシナリオに合わせてより洗練されたカプセル化が施されているだけです。

タスクが生成されると、MapReduceまたはFaaSプラットフォーム上で実行するようにスケジュールされます。スケジューリング前の前処理は、タスク生成方法によって異なります。例えば、KQLタスクは、スケジューリング用のタスクを生成する前にKQL解析が必要です。ビジネス側がフレームワークを通じて開発するタスクは、様々な予期せぬ問題が発生する可能性が高くなるため、自動受付などのDevOpsプロセスが活用されます。タスク実行中は、まず必要なオペレーターと想定されるスループットがコンピューティングスケジューリングシステムに送信されます。その後、システムはゲートウェイから利用可能なクォータを継続的に取得し、現在のタスクインスタンス数や失敗率などに基づいてリクエスト配信速度を適応的に調整します。


オタクトーク

04

要約

現在のシステムは、画像検索、動画検索、写真検索など10以上の業務領域をサポートし、数百のオペレータの開発と展開、1日あたり数千億回の計算呼び出しの処理、そしてネットワーク全体にわたる数兆ものコンテンツ機能の定期的な更新をサポートしています。大規模AIモデルの時代の到来に伴い、多くの新たなシナリオと課題が浮上し、再考に値する点も数多くあります。私たちは、大規模モデルと連携しながら、今後も更なる探求を続けていきます。