Luigi で作成するワークフローの再利用性・メンテナンス性を高めるために という記事を昔書きました。
その中で、結果の一貫性を保つためにcomplete メソッドでは入出力のタイムスタンプをチェックする とか complete では依存タスクの complete がすべて True を返すかをチェックするとかいったことの重要性を書きました。
Luigi はデフォルトではタスクの終了判定が「出力が存在しているかどうか」のみとなっており、これが整合性のある計算結果を出すという観点からは不都合であることから上記のような点に注意を払う必要が生じます。
そういったことから、結果の再現性とかをより重視して「データサイエンス向けに」 Task
を拡張したものを作ってみました。以下のリポジトリに置いています。
セットアップスクリプトは書いていませんが、現状では Portalocker モジュール(とあと もちろん Luigi)に依存しているのでこれを pip なりで突っ込んでください。
使い方
使い方としては
-
hash_checking_tasks.TaskWithCheckingInputHash
を継承してタスクを作成する。 - タスクの入出力全てを
hash_checking_tasks.HashableTarget
を適切に実装したタスクにする。 - 実行する。
となります。
どうやって動いてるの?
TaskWithCheckingInputHash
は Task
を以下のように拡張しています。
- 依存タスク全ての
complete()
メソッドがTrue
を返すかチェックする。 - ターゲットが存在する場合、そのターゲットの生成に用いたタスクと入力ターゲットのハッシュ値が一致するかどうかをチェックする。
- タスクの実行が成功した場合、自分自身のクラスと入力ターゲットのハッシュ値を保存する。
具体的なハッシュ値の生成などの処理は HashableTarget
に任せてしまっています。HashableTarget
は以下のような動作を行えるように実装します。
-
hash_content()
の値の比較によってターゲットの同値性がチェックできる。 -
get_current_input_hash()
によって(もしもターゲットが存在した場合)ターゲットを生成したタスクのハッシュ値を取得できる。 -
store_input_hash()
によってターゲットを生成したタスクのハッシュ値を保存できる。
「タイムスタンプをチェックせよ」ということを以前の記事では書いたのですが、大事なのは日付ではなくて入出力の同値性です。ハッシュ値チェックの方が良いんじゃないか? ということをあの記事を書いてから思いなおしました。
ただハッシュ値チェックをするとなると必然的にハッシュ値をどこかに保持しておく必要があり、これはちょっとややこしい問題です。HashableLoaclTarget
ではターゲットとなるファイルに接尾辞 "input.pickle" をつけたパスに保存していますが、この名前が衝突した場合の処理などをちゃんとしていません。
まだまだ色々突っ込みどころのある状態ではあるかと思いますので、是非ともマサカリを投げてください。