https://yng87.page/blog/feed.xml

テーブルデータの前処理を何でやるか

2025-02-17

最近読んだ、The Rise of Single-Node Processing: Challenging the Distributed-First Mindset という記事に最近考えていたことが書いてあったので便乗して自分の考えを書き留めておく。

元記事では、かつては大規模なデータの処理というと何はともあれ分散システムであり、Spark や BigQuery などを導入するのが当然であったが、近年は DuckDBPolars など、シングルノードでも高速にテーブルデータを処理できる技術が登場してきたことで必ずしも分散システムは必要ではないよねという風潮に変わってきた、ということが述べられている。コスト面でもクラウドを使うのであれば、小さいインスタンスをいくつも立てて分散処理するのと、合計して同程度の vCPU や RAM を持つ一つの大きなインスタンスを立てて処理するのとで料金が劇的に変わるわけではないため、オーバーヘッドの少ないシングルノードを選択するのが理にかなっているとのこと。

もちろんそもそもシングルノードには絶対に乗らないような規模のデータを扱う組織もあるとは思うが、何年か前に Big Data is Dead という BigQuery の開発者の記事で指摘されていたように、実はそういう組織はそこまで多くない。大規模データを持っている企業が限られるし、持っていたとしても大抵の分析案件は直近データだけクエリすれば十分なことが多いからだ。

以上の話はおおむね分析やそのためのデータマート構築の視点で書かれていると思うが、機械学習モデルを作る際のテーブルデータの前処理についても同様のことが当てはまると思っている。

自分がこの業界に入り最初に入社した会社では、BigQuery 上に DWH が構築されており、データサイエンティストは SQL を書いてモデルの学習に必要なデータを抽出していた。BigQuery の真っ当なユースケースとしては上に述べたような分析・データマート構築というところになると思うが、その強力なスケーラビリティから機械学習モデリングのワークフローに組み込まれ、生のログを整形し、ラベルをつけ、特徴量を計算し GCS に出力するというやり方で使われる場面も(自分の観測してきた範囲では)非常に多い。当時所属していたチームでも、テーブルデータの前処理は BigQuery でやるのがベストプラクティスとされていた。

今の会社に移ってからも当初はそのようなやり方でテーブルデータを扱っていたのだが、主に以下の二点が理由で最近は BigQuery でデータを処理しすぎないほうが良いと感じる場面が増えてきた

  1. Polars のように、シングルノードでも大規模なデータを簡単に高速に処理できるライブラリが登場した
  2. ソフトウェアエンジニアリングの経験を積んだことで、コードやMLパイプラインの品質をより重視するようになった

一点目に関しては数年前から散々話題になっていると思うが、やはり Polars が性能面で非常に優れていて、適当にクエリを書いて Pandas の 1/10 くらいの時間で処理できるのだからもう Pandas には戻れない。また Pandas のAPI は歴史的な経緯で荒れ果てていると個人的に思っているが、Polars は新興のライブラリのこともあってその辺は綺麗に保たれていると感じる。

二点目については、SQL で凝った処理を書くととても見通しが悪いし、処理を小さな関数に分けて使い回せないし、何よりユニットテストを気軽に書くということが難しい。大体は SQL をコンソールに貼って実行して結果を目で確認して意図通りっぽいかということを調べていくことになる。過去の現場でテストの書かれていない複雑なデータセット生成用 SQL をいくつも見てきて非常に辛い思いをしたが、当時はそれは仕方のないことだと思っていたし、自分もそのような SQL をいくつも生成してしまっていた。ユニットテストを書くのは大事だ。機械学習の実験フェーズにおいてもデータパイプラインに最低限のテストを書いておくと早めにバグやリークを潰せるし、本番移行のためにリファクタリングをするぞとなったときもひとまずそのテストを信頼して移植することが出来る。

以上の点を踏まえての個人的なベストプラクティスとしては、

  • DWH に SQL でクエリする際にはあまり複雑なロジックを入れず、where で指定できるフィルタや、ちょっとしたメタデータの join に留める
  • 集約特徴量などの複雑になりがちな計算は Polars で書いてユニットテストをちゃんとつける
  • シングルノードでの処理がメモリや実行時間的に厳しい場合は上の縛りを緩める

という感じだ。

それから話の前提として、チームにはメモリや CPU を柔軟に指定できるリモートのジョブ実行環境がすでにあるとしている。たとえば AWS Batch や Google Cloud の Vertex Pipelines のようなイメージで、Polars のジョブはそこに投げる。これがない場合はそもそも実験の試行錯誤がスケールしないので、まずはそちらを整備したほうが良いだろう。