Speee DEVELOPER BLOG

Speee開発陣による技術情報発信ブログです。 メディア開発・運用、スマートフォンアプリ開発、Webマーケティング、アドテクなどで培った技術ノウハウを発信していきます!

Pythonプロジェクト構成の落ち穂拾い

Pythonプロジェクト構成の落ち穂拾い

こんにちは。Speeeエンジニアの義田@yoppiblogです。 この記事は Speee Advent Calendar 2018 の16日目の記事です。先日は @mistral による「Looker を検証してみた」でした。

はじめに

SpeeeのUZOU事業部はアドネットワークである UZOU を開発しています。 そのため、広告のCTRを上げることが事業最大のKPIでもあるので、どの広告を配信すればクリックしてくれるかどうかを常に考えているわけです。 また、eCPMを最大化することも事業部の売上につながるので、適切にコンバージョンが発生する広告枠では、CPC単価を高めたいわけです。 そういったいい感じのCTR予測、いい感じの自動入札の話は今回はしません。

今回は、そういったいい感じの機械学習を支える地味なお話です。UZOUでは機械学習的なアプローチでタスクを解く場合、Pythonを採用しています。 この記事では、Pythonでタスクを解くプログラムをいい感じに構成する例を紹介します。

バッチ本体の話はいくつか記事を書いていますので気になる方は、バックナンバーを読んでくださいね。

Pythonプロジェクトの構成

まずは、プロジェクトのディレクトリツリーの全体像から見ていきます。

$ tree
.
├── Makefile # makeで各プロジェクトやリポジトリ全体で必要になる実行コマンドを管理
├── README.md
├── bin # 各プロジェクトのバッチのエントリポイントを定義する。複数のプロジェクトが1リポジトリに含まれることを想定してエントリポイントを分割している
│   └── <project>
│       └── main.py
├── config # プロジェクト間で共通的に使う設定ファイル。RDSといったデータウェアハウスへの接続情報など
├── data # 各プロジェクトで扱うデータの一時的な保存場所
│   └── <project>
│       └── raw
├── docker # 各プロジェクトやリポジトリで必要とするDockerfileの定義場所
│   └── <project>
├── lib # 各プロジェクトのソースコードや共通的に使うモジュールの定義場所
│   ├── __init__.py
│   ├── config # プロジェクト間で共通的に使う設定ファイルを扱う
│   ├── errors # プロジェクト間で共通的に使うエラーハンドラー
│   ├── logger # プロジェクト間で共通的に使うロガー
│   ├── models # プロジェクト間で共通的に使う各種データソース
│   ├── utils # プロジェクト間で共通的に使うutility
│   └── <project> # 各プロジェクトのエントリポイント
├── logs # 各プロジェクトのログの保存場所
├── notebooks # Jupyter notebooksの保存場所
├── requirements.txt # サードパーティライブラリの定義ファイル
├── setup.py # pip install用(公開はしないけど)
├── tests # ユニットテストの定義場所
│   ├── __init__.py
│   └── lib
│       └── <project>
└── <submodule> # UZOUで統一的に使うポジトリをgit submoduleで取り込んでいる

特別、世の中の構成と比べて大きな差はないとは思います。 個人的にこの構成で気に入っているところは、エントリポイントを明示的にわけていたり、プロジェクト間でモジュールを共有しやすくしていたり、というところです。

Jupyter notebookも共有するようにしています。 エンジニアがモデルを検証するときや、分析するときには毎回バッチを書くよりインタラクティブに実行可能、かつ、永続化してくれるのでとても便利です。 加えて、エンジニア以外でも簡単に扱えるため、誰でも参照できるようにgitで管理するようにしています。 PM等にもこういう分析したよーという共有ができるのは、プロジェクトをスムーズに進められているなと感じます。

唯一、早急に直したいのが models というネーミングです。プロジェクト配下でモデルを生成するときにかぶってしまうので、datasources あたりかなぁ。 これは単純に、Active Record脳(1テーブル1モデルとして抽象化してやり取りする)が大きく作用してしまった結果ですね。

Pythonやサードパーティライブラリの版管理

Pythonの版管理はpyenv、及び、pyenv-vertualenvを採用しています。 必要以上に複雑にしすぎない、かつ、最低限管理できるようにしたい、という意図で採用しています。

pyenv と pyenv-virtualenv で環境構築 - Qiita

プロジェクトの構成にこだわる理由

チームで開発する場合、わかりやすく整っていると「何をどこに置くか」ということを議論しすぎることもなく、スムーズに開発を進められると考えているからです。 個人で開発する場合でも、責務を明確にしておくと開発しやすいと思います。

まとめ

参考文献にあげている記事を読むと、同じように試行錯誤していることがわかって勉強になります。 今回紹介したプロジェクト構成も、このように寄せていくといい感じになりそうです。

参考文献