1.7K Views
February 02, 26
スライド概要
2026年1月31日:「計測・解析基盤」×「データ科学」第2回研究交流会のチュートリアル講演「機械学習プロジェクトにおける実験管理」のスライド資料です.
東京農工大学工学部知能情報システム工学科・先進学際科学府 准教授
2026年1月31日:「計測・解析基盤」×「データ科学」第2回研究交流会 機械学習プロジェクトにおける実験管理 東京農工大学 工学研究院 准教授 山田 宏樹
2 自己紹介 山田 宏樹 (やまだ こうき) 東京農工大学工学府 修了(博士,工学) • 2022年4月 東京理科大学工学部 助教 • 2023年4月 東京農工大学工学府 特任助教 • 2024年10月 東京農工大学工学府 准教授 Computational Imaging グラフ信号処理・機械学習 コヒーレント回折イメージング コヒーレントビーム 課題 解決 Noisy data Kalofolias+ 2017 Ours (SNR = 15.45) (SNR = 17.16) (SNR = 23.03) Original 1 10 0.5 0 -0.5 6 試料厚さ上限 ・放射線損傷に起因する問題 本研究構想 : 数式駆動型深層学習 6 35 30 25 20 15 10 5 -1 試料生成 パラメータ 回折強度 パターン 計測モデル + データ同化 4 2 振幅像 試料 トレードオフ ・空間分解能 ランダムに 大量生成 8 0 研究 • 2022年4月 jF (6)j 略歴 教師データ 位相像 反復位相回復 アルゴリズム 深層学習モデル 学習 目的 ・高速位相回復 アルゴリズムの開発 ・三次元再構成手法の開発 試料を模擬した三次元データ ・解析・理解に向けた Encoder-Decoder の開発
3 今回お話しすること 機械学習プロジェクトにおける実験管理 • 実験の再現性の担保の重要性は認知されているが,うまく実行するのは難しい • 特に「計測」×「データ科学」の領域だと,実験のパイプラインが複雑化しやすい • 実験管理には確立されたベストプラクティスというものが存在せず, 体系化されていない ‣ 実験管理のプラクティスを共有することが重要 どう実験管理をすべきか? • 再現性を担保するために守るべきルールを知る • 変更に強く,拡張性の高いコードの設計をする • 実験管理のためにツール (git, GitHub, Weight and Biases, DVCなど) を活用する
4 今回お話しすること 機械学習プロジェクトにおける実験管理 • 実験の再現性の担保の重要性は認知されているが,うまく実行するのは難しい • 特に「計測」×「データ科学」の領域だと,実験のパイプラインが複雑化しやすい • 実験管理には確立されたベストプラクティスというものが存在せず, 体系化されていない ‣ 実験管理のプラクティスを共有することが重要 どう実験管理をすべきか? • 再現性を担保するために守るべきルールを知る • 変更に強く,拡張性の高いコードの設計をする ここを中心に具体例を交えつつ紹介 • 実験管理のためにツール (git, GitHub, Weight and Biases, DVCなど) を活用する
目次 • 再現性を担保するために守るべきルール - Ten Simple Rules for Reproducible Computational Research - どうやってルールを守る? • 拡張性の高いコードの設計 - 依存性の注入 (DI: Dependency Injection) - DIをどう実装に活かす? X線タイコグラフィの位相回復計算における実例 - コードと設定の分離 5
6 再現性を担保するためのルール 再現可能な研究のための10個の基本原則 • G. K. Sandve+, ``Ten Simple Rules for Reproducible Computational Research," PLOS Comput. Biol., 2013. • 「自分以外の誰か (あるいは1ヶ月後の自分)」が同じ結果を再現できるようにするために, 研究者が守るべき10個の基本原則 • 10年以上前に出た論文ですが,現在でも通用する基本原則です
7 再現性を担保するためのルール ルール1: For Every Result, Keep Track of How It Was Produced • 論文に載せる図やデータは「どのスクリプト」を「どのデータ」に対して, 「どのパラメータ」で実行したのかを完全に追跡できるようにしましょう 実現のためのヒント コードをgitで管理し,実験時のコミットを特定するためにtagをつける • 実行時のパラメータが記録されたCon gファイルを管理する (後ほど解説) • README.mdにどのスクリプトでどの実験を行ったかを記載しておく fi •
8 再現性を担保するためのルール ルール2: Avoid Manual Data Manipulation Steps • 記録が保存されず再現できなくなるので,手作業でのデータの操作はやめましょう • NG行為の例: 手作業でデータセットをtrain, validation, testに分割する データにどの前処理を加えるかを手作業で管理する 実現のためのヒント • 手作業で行わず,Pythonスクリプトで処理を行う • 複数のスクリプトを使う必要があれば,run.shなどを用意してパイプライン処理を行う • 複雑な処理はタスクランナーに登録する
9 再現性を担保するためのルール ルール3: Archive the Exact Versions of All External Programs Used • ライブラリのバージョンが変われば計算結果も変わりますし,最悪実行できなくなります • 使ったライブラリのバージョンを管理しましょう 実現方法: uvで管理しましょう (必要であればDockerも) • uvとDocker (DevContainer) の使い方についての講義資料をドクセルに公開しているので, 詳しくはそちらを参照 (講義資料のリンクはこちら)
10 再現性を担保するためのルール ルール4: Version Control All Custom Scripts • 書いたコードは全てGitで管理しましょう 実現方法 • Git・GitHubの使い方についても 公開している講義資料を参照 (講義資料のリンクはこちら)
11 再現性を担保するためのルール ルール5: Record All Intermediate Results, When Possible in Standardized Formats • 生データから最終結果までのプロセスが長い場合,途中のデータ (中間データ) も保存しましょう • 保存形式はcsv, json, yamlなどの汎用的な形式を使いましょう ルール6: For Analyses That Include Randomness, Note Underlying Random Seeds • シード値を固定・記録しない限り,同じ結果は再現できません • Pytorch, numpy, Pythonのrandomモジュールなどのシードを全て固定しましょう ルール7: Always Store Raw Data behind Plots • プロットした図だけ保存するのではなく,そのプロットに使用した生データも保存しましょう • あとから「軸を変更したい」,「別のデータと重ねたい」と思ったときに再現できなくなります
12 再現性を担保するためのルール ルール8: Generate Hierarchical Analysis Output • 実験結果はサマリー (例えば平均精度) だけでなく, サマリの計算に使った中間データも保存しましょう ルール9: Connect Textual Statements to Underlying Results • 論文に載せる結果や図に対して,その根拠となる実験データやコードがどれに対応するのかを 明確にしておきましょう • gitでtagをつけたり,README.mdに記載しましょう ルール10: Provide Public Access to Scripts, Runs, and Results • 最終的にはコードを公開できるようにしましょう 自身の開発環境以外でも適切に動作するかどうかにも気を配りましょう • コードはGitHub等で,データセットはHugging Faceで公開できます
13 再現性のルールを知れば実験管理は完璧? ルールを踏まえると... • 開発環境の再現のためのツール (uv, dockerなど) を使う • コードのバージョン管理を行い,変更の追跡をする • README.mdに実験ノートを書いておく • 適切な形式で必要なデータを保存する (実験管理ツールを使うとなお良し) ‣ これで実験管理は完璧? 実際にやってみるとそんなに簡単ではない • もちろん,ルールを知った上で運用をすると再現性は劇的に向上します • 生成AIの登場によって,ルールを遵守する運用はハードルが下がった (パイプライン処理とかもちゃんと生成してくれたりする) • しかし,実際にやってみると様々な問題に直面します
14 どういった問題に直面する? GitHub Flowによる開発 • Tagをつけているので,そのコミットにcheckoutすれば 実験の再現が可能 機能Bを追加 feature/B 機能Aを追加 feature/A exp001 実験を完了したら git tagでtagをつける main ‣ 一見,この運用で困ることはなさそうだが...
15 どういった問題に直面する? GitHub Flowによる開発 • Tagをつけているので,そのコミットにcheckoutすれば 実験の再現が可能 機能Bを追加 feature/B 機能Aを追加 ‣ 一見,この運用で困ることはなさそうだが... 課題 • この運用では,あくまでその時点での スナップショットを再現できるだけ • exp001の実験を修正した上で, 再実験しなければならない状況はよく発生する feature/A exp001 実験を完了したら git tagでtagをつける main 例えばこんなシチュエーションがありうる • 査読の指摘により,追加した機能B (e.g., 前処理) を 使った上でexp001の再実験が必要となる • しかし機能A,Bを追加する過程で破壊的変更があり, 機能Bを使った上でexp001の再実験するのが困難
16 どういった問題に直面する? GitHub Flowによる開発 • Tagをつけているので,そのコミットにcheckoutすれば 実験の再現が可能 機能Bを追加 feature/B 機能Aを追加 ‣ 一見,この運用で困ることはなさそうだが... 課題 • この運用では,あくまでその時点での スナップショットを再現できるだけ • exp001の実験を修正した上で, 再実験しなければならない状況はよく発生する feature/A exp001 実験を完了したら git tagでtagをつける main 実験管理において必要なこと • 実験の再現性を担保したうえで コードが変更容易性を備えておく必要がある • 変更に強く,拡張性の高いコードを設計する
17 (補足) Kaggleでは実験管理をどうしてる? Kaggleの場合 • 1実験1Notebook (1実験1ディレクトリ) で管理が主流 • 実験ごとにJupyter notebookを独立させ,他の実験との干渉を防ぎ, 破壊的変更の発生を抑制しつつ再現性を担保 ‣ これにより素早く多数の実験を行える 研究の場合はこの運用は非推奨 kaggleの実験管理を知りたい人は この本を読みましょう [link] • kaggleは短期間のプロジェクトであるのに対し, 研究のプロジェクトはもっと長期間 • この運用方法を長期間行うと,適切にモジュール化が行われなくなり, 属人性が高く,可読性の低いコードが生まれやすい ‣ (研究プロジェクトの期間が短く,探索的な側面が強い場合はこの方法もアリですが, 基本的にはおすすめできません)
目次 • 再現性を担保するために守るべきルール - Ten Simple Rules for Reproducible Computational Research - どうやってルールを守る? • 拡張性の高いコードの設計 - 依存性の注入 (DI: Dependency Injection) - DIをどう実装に活かす? X線タイコグラフィの位相回復計算における実例 - コードと設定の分離 18
19 拡張性の高いコードをどう設計するか? ソフトウェア設計について学ぼう • 研究者は良いコードに触れる機会が少ない • ソフトウェア設計の言葉や概念を知っていると, 生成AIへの指示が的確になり,質の良いコードが生成可能となる • とはいえ,研究者はソフトウェア設計は専門ではないので 勉強する時間を取るのはなかなか難しい... ソウトウェア設計の入門書に おすすめです [link] 「依存性の注入」を軸に拡張性の高い研究用のコードの書き方を紹介します • なんかややこしそうな名前をしていますが,そこまで難解な概念ではありません • X線タイコグラフィの位相回復を実例にDIがどう役に立つのかを紹介します
20 依存性の注入 (DI: Dependency Injection) とは? 依存性の注入の定義 さまざまなソフトウェア設計の原則やパターンを集めたものであり, コードが疎結合 (loose coupling) となるような開発を行えるようにするものです. (引用: S. V. Deursen+, なぜ依存を注入するのか DIの原理・原則とパターン, マイナビ出版, 2024.) • オブジェクトや関数が,依存する他のオブジェクトや関数を外部から受け取る デザインパターンが代表的なDI デザインパターンとは • ソフトウェア開発で繰り返し現れる問題に対して,過去のエンジニアたちが導き出した 「最適解(定石)」のカタログ • 「Strategy Pattern」や「Factory Pattern」などのように,よくあるパターンに名前をつけることで 複雑な設計意図を一言で伝えることができるようになる
21 DIのメリット 遅延バインディング (Late Binding) • コードを書いたタイミングやコンパイル時ではなく, プログラムの実行時にオブジェクトや関数の実体を決定することができる • これはコードと実験設定をうまく分離するのに役立つ 拡張容易性 (Extensibility) • 既存のコードを拡張したり,再利用したりしやすくする • DIでは実験管理に重要な拡張の容易さを担保することができる 並列開発 (Parallel Development) • 異なる機能の開発を並列に行えるようになる • 複数人で開発を進めてもバグを埋め込みにくくなる
22 DIのメリット 保守容易性 (Maintainablity) • クラスの責務が明確に定義されるため,保守が行いやすい • 破壊的変更の発生を抑制するのに役立つ テスト容易性 (Testability) テストコードについて • 単体テストがしやすくする • pytestを使ったテストについても • 実装のバグを検知するのに役立つ 公開している講義資料を参照 (講義資料のリンクはこちら) ここまでの説明でピンときてなくても大丈夫です. これから具体例を交えて説明するので,わかりやすくなると思います.
23 実例に入る前の準備 | X線タイコグラフィにおける位相回復 X線タイコグラフィ • 試料にX線を照射して計測できる回折強度パターンから, 位相回復により数十nmの空間分解能で試料像を可視化する • 回折強度パターンの計測モデルに基づく最適化問題を 反復的アルゴリズムにより解くことで位相回復が行われる 位相回復の問題設定 回折強度 I <latexit sha1_base64="6aE8wZReigkMRwfbYOSKcjdwNz0=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdKO71toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDIQI4H</latexit> プローブ関数 P 試料関数 O <latexit sha1_base64="XVT5AE+J5T46beThmEAcs8+FVso=">AAACbnichVHLSsNAFD2N7/poVRBBRLFUXJWJ+MKV6MZlH1aFKiWJ0zY0L5JpQYs/4F5cCIqCiPgZbvwBF/0EcSMouHHhTRoQFfWGyZw5c8+dM3dUx9A9wVgzIrW1d3R2dfdEe/v6B2LxwaFNz665Gs9rtmG726ricUO3eF7owuDbjssVUzX4llpd8/e36tz1dNvaEPsO3zWVsqWXdE0RRBV2TEVU1FIjfRgtxhMsxYKY/AnkECQQRtqOX2MHe7ChoQYTHBYEYQMKPPoKkMHgELeLBnEuIT3Y5zhElLQ1yuKUoRBbpX+ZVoWQtWjt1/QCtUanGDRcUk4iyR7YDXth9+yWPbL3X2s1ghq+l32a1ZaWO8XY0Wju7V+VSbNA5VP1p2eBEpYCrzp5dwLGv4XW0tcPTl5yy9lkY5pdsifyf8Ga7I5uYNVftasMz57CfwD5e7t/gs3ZlLyQms/MJVZWw6foxhimMEP9XsQK1pFGPujYMc5wHnmWRqRxaaKVKkVCzTC+hDTzAdZOjg4=</latexit> <latexit sha1_base64="SC4pGb+3gso43lDueF/wt+pkQMw=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdOPO1toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDUTI4N</latexit> R 観測された回折強度パターンの集合 {Ir }r=1 から <latexit sha1_base64="QgUbcl3KIfcaPhYEuDu8bpXfqBU=">AAACg3ichVHLSsNAFD3Gd31V3QiCFIsiCGUipYogiG50p9WqYLUkcVqDeZFMCxqyc+UPuHClIKIu9Q/c+AMu/ARxqeDGhTdpQFTUGzL3zJl77pyZUR1D9wRjjw1SY1NzS2tbe6Kjs6u7J9nbt+bZVVfjBc02bHdDVTxu6BYvCF0YfMNxuWKqBl9X9+bD9fUadz3dtlbFvsO3TKVi6WVdUwRRpeRQ0U8VTUXsqmV/MSj5bpAqhmlGDrb9fFBKplmGRZH6CeQYpBHHkp28QBE7sKGhChMcFgRhAwo8+jYhg8Ehbgs+cS4hPVrnCJAgbZWqOFUoxO7RWKHZZsxaNA97epFao10M+l1SpjDCHtgle2H37Jo9sfdfe/lRj9DLPmW1ruVOqedoYOXtX5VJWWD3U/WnZ4EypiKvOnl3IiY8hVbX1w6OX1am8yP+KDtjz+T/lD2yOzqBVXvVzpd5/gQJegD5+3X/BGsTGTmXyS1n07Nz8VO0YRDDGKP7nsQsFrCEAu17iCvc4FZqlsalCSlbL5UaYk0/voQ08wGhmZWG</latexit> • 未知の試料関数 O とプローブ関数 P を推定する問題 <latexit sha1_base64="SC4pGb+3gso43lDueF/wt+pkQMw=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdOPO1toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDUTI4N</latexit> <latexit sha1_base64="XVT5AE+J5T46beThmEAcs8+FVso=">AAACbnichVHLSsNAFD2N7/poVRBBRLFUXJWJ+MKV6MZlH1aFKiWJ0zY0L5JpQYs/4F5cCIqCiPgZbvwBF/0EcSMouHHhTRoQFfWGyZw5c8+dM3dUx9A9wVgzIrW1d3R2dfdEe/v6B2LxwaFNz665Gs9rtmG726ricUO3eF7owuDbjssVUzX4llpd8/e36tz1dNvaEPsO3zWVsqWXdE0RRBV2TEVU1FIjfRgtxhMsxYKY/AnkECQQRtqOX2MHe7ChoQYTHBYEYQMKPPoKkMHgELeLBnEuIT3Y5zhElLQ1yuKUoRBbpX+ZVoWQtWjt1/QCtUanGDRcUk4iyR7YDXth9+yWPbL3X2s1ghq+l32a1ZaWO8XY0Wju7V+VSbNA5VP1p2eBEpYCrzp5dwLGv4XW0tcPTl5yy9lkY5pdsifyf8Ga7I5uYNVftasMz57CfwD5e7t/gs3ZlLyQms/MJVZWw6foxhimMEP9XsQK1pFGPujYMc5wHnmWRqRxaaKVKkVCzTC+hDTzAdZOjg4=</latexit> 照射領域を切り出す 2 <latexit sha1_base64="NcvYhXH+EvIjUJhIAZlimZTBp9c=">AAACZHichVFNSwJBGH7cvswsLQmCICQxOskoYdFJ6tLRj/wAE9ndxlpcd5fdVTDpD9S16NCpICL6GV36Ax38A0F0NOjSodd1IUqqd5iZZ555n3eemZEMVbFsxroeYWR0bHzCO+mb8k/PBIKzcwVLb5oyz8u6qpslSbS4qmg8byu2ykuGycWGpPKiVN/u7xdb3LQUXdu12wavNMQDTakpsmgTlUlUgxEWY06Eh0HcBRG4kdaDt9jDPnTIaKIBDg02YRUiLGplxMFgEFdBhziTkOLscxzDR9omZXHKEImt03hAq7LLarTu17QctUynqNRNUoYRZU/sjvXYI7tnL+zj11odp0bfS5tmaaDlRjVwspB7/1fVoNnG4ZfqT882athwvCrk3XCY/i3kgb51dNHLbWajnRV2zV7J/xXrsge6gdZ6k28yPHsJH31A/OdzD4NCIhZPxpKZtUhqy/0KLxaxjFV673WksIM08nQuxynOcO55FvxCSJgfpAoeVxPCtxCWPgFt3om9</latexit> Or F <latexit sha1_base64="PGpAX/wn95XDwXpNsGY05O9EH14=">AAACbnichVHLSsNAFD2Nr1pfVUEEEYul0lWZiqi4Kgri0lcfUEWSOK3BvEimhRr6A+7FhaAoiIif4cYfcOEniBtBwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeIlJbe0dnV7Q71tPb1z8QHxwquFbNUXletXTLKSmyy3XN5HmhCZ2XbIfLhqLzorK/5O8X69xxNcvcFA2bbxty1dQqmioLospbhiz2VFn3lps78STLsCASP0E2BEmEsWrFr7CFXVhQUYMBDhOCsA4ZLn1lZMFgE7cNjziHkBbsczQRI22NsjhlyMTu079Kq3LImrT2a7qBWqVTdBoOKRNIsXt2zV7YHbthj+z911peUMP30qBZaWm5vTNwOLrx9q/KoFlg71P1p2eBCuYDrxp5twPGv4Xa0tcPjl82FtZT3hS7YE/k/5w9sFu6gVl/VS/X+PoJYvQA2e/t/gkK05nsbGZ2bSaZWwyfIooxTCJN/Z5DDitYRT7o2BFOcRZ5lkakcWmilSpFQs0wvoSU/gB+ko5j</latexit> = <latexit sha1_base64="S+nNq3D6qO1jpBQi/qgk3ny6Rdc=">AAACaHichVFNSwJBGH7cvu1Dq0NFF1GMTjJGVHSSunQsTQ1UZHcda2jdXXZHoaQ/0KVjRaeCiOhndOkPdOgnVEeDLh16XReipHqHmXnmmfd555kZzTaEKxl7Cig9vX39A4NDweGR0bFQeHwi51p1R+dZ3TIsZ0dTXW4Ik2elkAbfsR2u1jSD57X99fZ+vsEdV1jmtjyweamm7pqiKnRVEpUtWhVLlsMxlmBeRLpB0gcx+LFphW9QRAUWdNRRA4cJSdiACpdaAUkw2MSV0CTOISS8fY4jBElbpyxOGSqx+zTu0qrgsyat2zVdT63TKQZ1h5QRxNkju2Ut9sDu2DP7+LVW06vR9nJAs9bRcrscOp7OvP+rqtEssfel+tOzRBUrnldB3m2Pad9C7+gbh6etzGo63pxjV+yV/F+yJ3ZPNzAbb/r1Fk9fIEgfkPz53N0gt5BILiWWthZjqTX/KwYxiyjm6b2XkcIGNpGlcwVOcIbzwIsSVqaUmU6qEvA1k/gWSvQTx5qLxQ==</latexit> <latexit sha1_base64="g4GjlslrkStMDkiS7eh7Um43y6s=">AAACZHichVFNSwJBGH7cvswsLQmCICQxOskYYREEUpeOfuQHmMjuNtniurvsroJJf6CuRYdOBRHRz+jSH+jgHwiio0GXDr2uC1FSvcPMPPPM+7zzzIxkqIplM9bxCEPDI6Nj3nHfhH9yKhCcnslbesOUeU7WVd0sSqLFVUXjOVuxVV40TC7WJZUXpNp2b7/Q5Kal6Nqu3TJ4uS5WNeVAkUWbqPRmJRhhMeZEeBDEXRCBGyk9eIs97EOHjAbq4NBgE1YhwqJWQhwMBnFltIkzCSnOPscxfKRtUBanDJHYGo1VWpVcVqN1r6blqGU6RaVukjKMKHtid6zLHtk9e2Efv9ZqOzV6Xlo0S30tNyqBk7ns+7+qOs02Dr9Uf3q2cYB1x6tC3g2H6d1C7uubRxfd7EYm2l5i1+yV/F+xDnugG2jNN/kmzTOX8NEHxH8+9yDIr8TiiVgivRpJbrlf4cU8FrFM772GJHaQQo7O5TjFGc49z4JfCAmz/VTB42pC+BbCwieD3onI</latexit> Ir 2 R <latexit sha1_base64="BVeHw8xX0q6lS91qO7DgZOlTxug=">AAACj3ichVE9LwRBGH6s7/N1aCSajQsRxWVOBFHIhYZCwnFIHJfdNcfEfmV37hI2+wdUOoWKRET0WhKNP6DwE0RJolF4b28TQfBOZuaZZ97nnWdmdNcUvmTssU6pb2hsam5pTbS1d3R2Jbt7Vn2n7Bk8bzim463rms9NYfO8FNLk667HNUs3+Zq+N1vdX6twzxeOvSL3Xb5paTu2KAlDk0QVkyMFS5O7eimYD4uBF6oFYas1Sg9y4VawoBaksLivLoTFZIqlWRTqT5CJQQpxLDrJCxSwDQcGyrDAYUMSNqHBp7aBDBhc4jYREOcREtE+R4gEacuUxSlDI3aPxh1abcSsTetqTT9SG3SKSd0jpYpB9sAu2Qu7Z1fsib3/WiuIalS97NOs17TcLXYd9i2//auyaJbY/VT96VmihMnIqyDvbsRUb2HU9JWD45flqdxgMMTO2DP5P2WP7I5uYFdejfMlnjtBgj4g8/25f4LV0XRmPD2+NJbKzsRf0YJ+DGCY3nsCWcxhEXk69wjXuMGt0q1MKNNKtpaq1MWaXnwJZf4D25iaZA==</latexit> O2C <latexit sha1_base64="lR1C3nYFxQkOFa7jANLZEf8gbEc=">AAACi3ichVG7SgNBFD2u7/iK2gg2i0GxChOVKGIhimDlOyoYDbvrJA7ZF7uTgC75AbG3sFIQETtb7Wz8AQs/QSwVbCy82SyIinqXnTn3zD13zszoril8ydhjnVLf0NjU3NIaa2vv6OyKd/es+07JM3jGcEzH29Q1n5vC5hkppMk3XY9rlm7yDb04W13fKHPPF469Jvddvm1pBVvkhaFJonLxoaylyT09HyxW1Kyw1VqqB7OVnWBBzUphcV9dqOTiCZZkYag/QSoCCUSx5MQvkMUuHBgowQKHDUnYhAafvi2kwOASt42AOI+QCNc5KoiRtkRVnCo0Yos0Fijbilib8mpPP1QbtItJv0dKFYPsgV2yF3bPrtgTe/+1VxD2qHrZp1mvabmb6zrsW337V2XRLLH3qfrTs0QeE6FXQd7dkKmewqjpywfHL6uTK4PBEDtjz+T/lD2yOzqBXX41zpf5ygli9ACp79f9E6yPJFPpZHp5LDE9Ez1FC/oxgGG673FMYx5LyNC+R7jGDW6VDmVUmVSmaqVKXaTpxZdQ5j4AX1aYbA==</latexit> N ⇥N 二次元 フーリエ変換 Or 2 C <latexit sha1_base64="FNYMKhQ0IEFZirjAMavfBzhm9MI=">AAACj3ichVE9LwRBGH6s7/N1aCSajQsRxWVOBFHIhYZCfB4Sx2V3zTGxX9mdu4TN/gGVTqEiERG9lkTjDyj8BFGSaBTe29tEELyTmXnmmfd555kZ3TWFLxl7rFFq6+obGpuaEy2tbe0dyc6uVd8peQbPGY7peOu65nNT2DwnhTT5uutxzdJNvqbvTVf218rc84Vjr8h9l29a2o4tisLQJFGF5FDe0uSuXgzmw0LghWpe2GqV0oPpcCuYU/NSWNxX58JCMsXSLAr1J8jEIIU4FpzkBfLYhgMDJVjgsCEJm9DgU9tABgwucZsIiPMIiWifI0SCtCXK4pShEbtH4w6tNmLWpnWlph+pDTrFpO6RUkU/e2CX7IXdsyv2xN5/rRVENSpe9mnWq1ruFjoOe5bf/lVZNEvsfqr+9CxRxHjkVZB3N2IqtzCq+vLB8cvyxFJ/MMDO2DP5P2WP7I5uYJdfjfNFvnSCBH1A5vtz/wSrw+nMaHp0cSSVnYq/ogm96MMgvfcYspjBAnJ07hGucYNbpVMZUyaVbDVVqYk13fgSyuwHyYyaWw==</latexit> M ⇥M P2C <latexit sha1_base64="RGve++8fO3zH2zCCJlmVYtG/5fM=">AAACi3ichVFNLwNBGH66vuuruEhcNhri1EwRRByESFwkLYpEaXbXtCb2K7vTJmz6B8TdwYlERNxcubn4Aw5+gjiSuDh4u91EELybnXneZ97nnWdmdNcUvmTsMaY0NDY1t7S2xds7Oru6Ez29a75T9gyeMxzT8TZ0zeemsHlOCmnyDdfjmqWbfF3fm6+tr1e45wvHXpX7Lt+ytJItisLQJFGFxHDe0uSuXgwyVTUvbLWe6sF8dTtYUvNSWNxXl6qFRJKlWBjqT5COQBJRZJzEBfLYgQMDZVjgsCEJm9Dg07eJNBhc4rYQEOcREuE6RxVx0papilOFRuwejSXKNiPWprzW0w/VBu1i0u+RUsUQe2CX7IXdsyv2xN5/7RWEPWpe9mnW61ruFroP+1fe/lVZNEvsfqr+9CxRxFToVZB3N2RqpzDq+srB8cvK9PJQMMzO2DP5P2WP7I5OYFdejfMsXz5BnB4g/f26f4K10VR6IjWRHU/OzkVP0YoBDGKE7nsSs1hEBjna9wjXuMGt0qmMKdPKTL1UiUWaPnwJZeEDXWiYaw==</latexit> M ⇥M M ⇥M
24
実例に入る前の準備 | X線タイコグラフィにおける位相回復
位相回復の反復的アルゴリズムの基本構造
Step 1: 試料関数 Oとプローブ関数 P の初期化
<latexit sha1_base64="SC4pGb+3gso43lDueF/wt+pkQMw=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdOPO1toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDUTI4N</latexit>
<latexit sha1_base64="XVT5AE+J5T46beThmEAcs8+FVso=">AAACbnichVHLSsNAFD2N7/poVRBBRLFUXJWJ+MKV6MZlH1aFKiWJ0zY0L5JpQYs/4F5cCIqCiPgZbvwBF/0EcSMouHHhTRoQFfWGyZw5c8+dM3dUx9A9wVgzIrW1d3R2dfdEe/v6B2LxwaFNz665Gs9rtmG726ricUO3eF7owuDbjssVUzX4llpd8/e36tz1dNvaEPsO3zWVsqWXdE0RRBV2TEVU1FIjfRgtxhMsxYKY/AnkECQQRtqOX2MHe7ChoQYTHBYEYQMKPPoKkMHgELeLBnEuIT3Y5zhElLQ1yuKUoRBbpX+ZVoWQtWjt1/QCtUanGDRcUk4iyR7YDXth9+yWPbL3X2s1ghq+l32a1ZaWO8XY0Wju7V+VSbNA5VP1p2eBEpYCrzp5dwLGv4XW0tcPTl5yy9lkY5pdsifyf8Ga7I5uYNVftasMz57CfwD5e7t/gs3ZlLyQms/MJVZWw6foxhimMEP9XsQK1pFGPujYMc5wHnmWRqRxaaKVKkVCzTC+hDTzAdZOjg4=</latexit>
O
<latexit sha1_base64="SC4pGb+3gso43lDueF/wt+pkQMw=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdOPO1toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDUTI4N</latexit>
•
•
試料関数は乱数で初期化したり,
全要素を1として初期化することが多い
プローブ関数は隆起関数で初期化したり,
計測時の事前情報に基づき初期化することが多い
Step 2: 試料関数 Oとプローブ関数 P の反復更新
<latexit sha1_base64="SC4pGb+3gso43lDueF/wt+pkQMw=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdOPO1toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDUTI4N</latexit>
<latexit sha1_base64="XVT5AE+J5T46beThmEAcs8+FVso=">AAACbnichVHLSsNAFD2N7/poVRBBRLFUXJWJ+MKV6MZlH1aFKiWJ0zY0L5JpQYs/4F5cCIqCiPgZbvwBF/0EcSMouHHhTRoQFfWGyZw5c8+dM3dUx9A9wVgzIrW1d3R2dfdEe/v6B2LxwaFNz665Gs9rtmG726ricUO3eF7owuDbjssVUzX4llpd8/e36tz1dNvaEPsO3zWVsqWXdE0RRBV2TEVU1FIjfRgtxhMsxYKY/AnkECQQRtqOX2MHe7ChoQYTHBYEYQMKPPoKkMHgELeLBnEuIT3Y5zhElLQ1yuKUoRBbpX+ZVoWQtWjt1/QCtUanGDRcUk4iyR7YDXth9+yWPbL3X2s1ghq+l32a1ZaWO8XY0Wju7V+VSbNA5VP1p2eBEpYCrzp5dwLGv4XW0tcPTl5yy9lkY5pdsifyf8Ga7I5uYNVftasMz57CfwD5e7t/gs3ZlLyQms/MJVZWw6foxhimMEP9XsQK1pFGPujYMc5wHnmWRqRxaaKVKkVCzTC+hDTzAdZOjg4=</latexit>
R
• 計測された回折強度パターン {Ir }r=1 に基づき
<latexit sha1_base64="QgUbcl3KIfcaPhYEuDu8bpXfqBU=">AAACg3ichVHLSsNAFD3Gd31V3QiCFIsiCGUipYogiG50p9WqYLUkcVqDeZFMCxqyc+UPuHClIKIu9Q/c+AMu/ARxqeDGhTdpQFTUGzL3zJl77pyZUR1D9wRjjw1SY1NzS2tbe6Kjs6u7J9nbt+bZVVfjBc02bHdDVTxu6BYvCF0YfMNxuWKqBl9X9+bD9fUadz3dtlbFvsO3TKVi6WVdUwRRpeRQ0U8VTUXsqmV/MSj5bpAqhmlGDrb9fFBKplmGRZH6CeQYpBHHkp28QBE7sKGhChMcFgRhAwo8+jYhg8Ehbgs+cS4hPVrnCJAgbZWqOFUoxO7RWKHZZsxaNA97epFao10M+l1SpjDCHtgle2H37Jo9sfdfe/lRj9DLPmW1ruVOqedoYOXtX5VJWWD3U/WnZ4EypiKvOnl3IiY8hVbX1w6OX1am8yP+KDtjz+T/lD2yOzqBVXvVzpd5/gQJegD5+3X/BGsTGTmXyS1n07Nz8VO0YRDDGKP7nsQsFrCEAu17iCvc4FZqlsalCSlbL5UaYk0/voQ08wGhmZWG</latexit>
O と Pを反復的に更新
<latexit sha1_base64="XVT5AE+J5T46beThmEAcs8+FVso=">AAACbnichVHLSsNAFD2N7/poVRBBRLFUXJWJ+MKV6MZlH1aFKiWJ0zY0L5JpQYs/4F5cCIqCiPgZbvwBF/0EcSMouHHhTRoQFfWGyZw5c8+dM3dUx9A9wVgzIrW1d3R2dfdEe/v6B2LxwaFNz665Gs9rtmG726ricUO3eF7owuDbjssVUzX4llpd8/e36tz1dNvaEPsO3zWVsqWXdE0RRBV2TEVU1FIjfRgtxhMsxYKY/AnkECQQRtqOX2MHe7ChoQYTHBYEYQMKPPoKkMHgELeLBnEuIT3Y5zhElLQ1yuKUoRBbpX+ZVoWQtWjt1/QCtUanGDRcUk4iyR7YDXth9+yWPbL3X2s1ghq+l32a1ZaWO8XY0Wju7V+VSbNA5VP1p2eBEpYCrzp5dwLGv4XW0tcPTl5yy9lkY5pdsifyf8Ga7I5uYNVftasMz57CfwD5e7t/gs3ZlLyQms/MJVZWw6foxhimMEP9XsQK1pFGPujYMc5wHnmWRqRxaaKVKkVCzTC+hDTzAdZOjg4=</latexit>
<latexit sha1_base64="SC4pGb+3gso43lDueF/wt+pkQMw=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdOPO1toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDUTI4N</latexit>
•
さまざまな更新方法が提案されている
P
<latexit sha1_base64="XVT5AE+J5T46beThmEAcs8+FVso=">AAACbnichVHLSsNAFD2N7/poVRBBRLFUXJWJ+MKV6MZlH1aFKiWJ0zY0L5JpQYs/4F5cCIqCiPgZbvwBF/0EcSMouHHhTRoQFfWGyZw5c8+dM3dUx9A9wVgzIrW1d3R2dfdEe/v6B2LxwaFNz665Gs9rtmG726ricUO3eF7owuDbjssVUzX4llpd8/e36tz1dNvaEPsO3zWVsqWXdE0RRBV2TEVU1FIjfRgtxhMsxYKY/AnkECQQRtqOX2MHe7ChoQYTHBYEYQMKPPoKkMHgELeLBnEuIT3Y5zhElLQ1yuKUoRBbpX+ZVoWQtWjt1/QCtUanGDRcUk4iyR7YDXth9+yWPbL3X2s1ghq+l32a1ZaWO8XY0Wju7V+VSbNA5VP1p2eBEpYCrzp5dwLGv4XW0tcPTl5yy9lkY5pdsifyf8Ga7I5uYNVftasMz57CfwD5e7t/gs3ZlLyQms/MJVZWw6foxhimMEP9XsQK1pFGPujYMc5wHnmWRqRxaaKVKkVCzTC+hDTzAdZOjg4=</latexit>
P2C
<latexit sha1_base64="RGve++8fO3zH2zCCJlmVYtG/5fM=">AAACi3ichVFNLwNBGH66vuuruEhcNhri1EwRRByESFwkLYpEaXbXtCb2K7vTJmz6B8TdwYlERNxcubn4Aw5+gjiSuDh4u91EELybnXneZ97nnWdmdNcUvmTsMaY0NDY1t7S2xds7Oru6Ez29a75T9gyeMxzT8TZ0zeemsHlOCmnyDdfjmqWbfF3fm6+tr1e45wvHXpX7Lt+ytJItisLQJFGFxHDe0uSuXgwyVTUvbLWe6sF8dTtYUvNSWNxXl6qFRJKlWBjqT5COQBJRZJzEBfLYgQMDZVjgsCEJm9Dg07eJNBhc4rYQEOcREuE6RxVx0papilOFRuwejSXKNiPWprzW0w/VBu1i0u+RUsUQe2CX7IXdsyv2xN5/7RWEPWpe9mnW61ruFroP+1fe/lVZNEvsfqr+9CxRxFToVZB3N2RqpzDq+srB8cvK9PJQMMzO2DP5P2WP7I5OYFdejfMsXz5BnB4g/f26f4K10VR6IjWRHU/OzkVP0YoBDGKE7nsSs1hEBjna9wjXuMGt0qmMKdPKTL1UiUWaPnwJZeEDXWiYaw==</latexit>
O 2 CN ⇥N
<latexit sha1_base64="lR1C3nYFxQkOFa7jANLZEf8gbEc=">AAACi3ichVG7SgNBFD2u7/iK2gg2i0GxChOVKGIhimDlOyoYDbvrJA7ZF7uTgC75AbG3sFIQETtb7Wz8AQs/QSwVbCy82SyIinqXnTn3zD13zszoril8ydhjnVLf0NjU3NIaa2vv6OyKd/es+07JM3jGcEzH29Q1n5vC5hkppMk3XY9rlm7yDb04W13fKHPPF469Jvddvm1pBVvkhaFJonLxoaylyT09HyxW1Kyw1VqqB7OVnWBBzUphcV9dqOTiCZZkYag/QSoCCUSx5MQvkMUuHBgowQKHDUnYhAafvi2kwOASt42AOI+QCNc5KoiRtkRVnCo0Yos0Fijbilib8mpPP1QbtItJv0dKFYPsgV2yF3bPrtgTe/+1VxD2qHrZp1mvabmb6zrsW337V2XRLLH3qfrTs0QeE6FXQd7dkKmewqjpywfHL6uTK4PBEDtjz+T/lD2yOzqBXX41zpf5ygli9ACp79f9E6yPJFPpZHp5LDE9Ez1FC/oxgGG673FMYx5LyNC+R7jGDW6VDmVUmVSmaqVKXaTpxZdQ5j4AX1aYbA==</latexit>
M ⇥M
初期化には多数のパターンがある
• 有名どころのアルゴリズム
ePIE, rPIE, DM, RAARなど他多数
• 各手法は固有のハイパーパラメータや
前処理・後処理のロジックを持つ
この位相回復の反復的アルゴリズムをどう実装すべきか考えましょう
やりがちな実装① def phase_retrival( diffraction: np.ndarray, positions: np.ndarray, options: dict ) -> tuple: diffraction: 回折強度パターン positions : 照射位置の座標 options : パラメータが格納された辞書 様々な手法を内包した 汎用位相回復ソルバー関数を作る • こんな感じの入出力の関数で実装することを イメージしませんでしたか? ‣ この時点で拡張性の高い設計は失敗しています 25
やりがちな実装① 関数phase_retrivalの内部構造 # Initialize object and probe obj_initialize_type = options["O_initialize_type"] prb_initialize_type = options["P_initialize_type"] if obj_initialize_type == "ones": obj = np.ones(...) elif obj_initialize_type == "random": obj = np.random.rand(...) if prb_initialize_type == "bump": prb = make_bump_probe(options) elif prb_initialize_type == "load": prb = load_probe_from_file(...) method_name = options["method_name"] for iter in range(num_iter): # preprocessing steps ... # update O and P if method_name == "ePIE": obj, prb = ePIE(obj, prb, diffraction, positions, options) elif method_name == "DM": obj, prb = DM(obj, P prb diffraction, positions, options) elif method_name == "rPIE": obj, prb = rPIE(obj, prb, diffraction, positions, options) diffraction: 回折強度パターン positions : 照射位置の座標 options : パラメータが格納された辞書 26 様々な手法を内包した 汎用位相回復ソルバー関数を作る • こんな感じの入出力の関数で実装することを イメージしませんでしたか? ‣ この時点で拡張性の高い設計は失敗しています • 関数の内部でobjとprbの初期化・更新方法の 決定を行う実装 • for文の中でobjとprbの更新ロジックをif-elseにより 手法ごとに切り替える
やりがちな実装① 関数phase_retrivalの内部構造 # Initialize object and probe obj_initialize_type = options["O_initialize_type"] prb_initialize_type = options["P_initialize_type"] if obj_initialize_type == "ones": obj = np.ones(...) elif obj_initialize_type == "random": obj = np.random.rand(...) if prb_initialize_type == "bump": prb = make_bump_probe(options) elif prb_initialize_type == "load": prb = load_probe_from_file(...) method_name = options["method_name"] for iter in range(num_iter): # preprocessing steps ... # update O and P if method_name == "ePIE": obj, prb = ePIE(obj, prb, diffraction, positions, options) elif method_name == "DM": obj, prb = DM(obj, P prb diffraction, positions, options) elif method_name == "rPIE": obj, prb = rPIE(obj, prb, diffraction, positions, options) diffraction: 回折強度パターン positions : 照射位置の座標 options : パラメータが格納された辞書 27 欠点1: if-elseの条件分岐が氾濫する • phase_retrieval関数の内部で,objとprbの初期化, 更新方法を決定をする設計 • 初期化方法・更新方法の数以上に if-elseの条件分岐が発生する • 例えば「ePIEとrPIEには前処理Aが必要」という要件 があればまた条件分岐が増える ‣ 条件分岐の複雑化により, 可読性が低下・バグを埋め込みやすくなる
やりがちな実装① diffraction: 回折強度パターン positions : 照射位置の座標 options : パラメータが格納された辞書 28 関数phase_retrivalの内部構造 # Initialize object and probe obj_initialize_type = options["O_initialize_type"] prb_initialize_type = options["P_initialize_type"] if obj_initialize_type == "ones": obj = np.ones(...) elif obj_initialize_type == "random": obj = np.random.rand(...) if prb_initialize_type == "bump": prb = make_bump_probe(options) elif prb_initialize_type == "load": prb = load_probe_from_file(...) method_name = options["method_name"] for iter in range(num_iter): # preprocessing steps ... # update O and P if method_name == "ePIE": obj, prb = ePIE(obj, prb, diffraction, positions, options) elif method_name == "DM": obj, prb = DM(obj, P prb diffraction, positions, options) elif method_name == "rPIE": obj, prb = rPIE(obj, prb, diffraction, positions, options) 欠点2: 「全部入り」のパラメータ辞書options • objとprbの初期化・更新方法のパラメータが optionsに格納されるので,実装するロジックが増え るにつれ,パラメータが増大 • 実行時にどんなパラメータが本当に必要なのかが わかりづらくなる • 例えばePIEの実行時に,本来不必要な 他の手法のパラメータが混入しやすくなる • 実行ごとにoptionsの要素が変化するので, 変数の検証 (Validation) が複雑になる ‣ 可読性が低下・バグを埋め込みやすくなる
やりがちな実装① diffraction: 回折強度パターン positions : 照射位置の座標 options : パラメータが格納された辞書 29 関数phase_retrivalの内部構造 # Initialize object and probe obj_initialize_type = options["O_initialize_type"] prb_initialize_type = options["P_initialize_type"] if obj_initialize_type == "ones": obj = np.ones(...) elif obj_initialize_type == "random": obj = np.random.rand(...) if prb_initialize_type == "bump": prb = make_bump_probe(options) elif prb_initialize_type == "load": prb = load_probe_from_file(...) method_name = options["method_name"] for iter in range(num_iter): # preprocessing steps ... # update O and P if method_name == "ePIE": obj, prb = ePIE(obj, prb, diffraction, positions, options) elif method_name == "DM": obj, prb = DM(obj, P prb diffraction, positions, options) elif method_name == "rPIE": obj, prb = rPIE(obj, prb, diffraction, positions, options) 欠点3: 追加実装のときに既存機能に影響が及ぶ • 新しく手法を実装したときに,条件分岐にバグが あると,他の手法の動作が変わる可能性がある • 機能の追加のたびに既存機能の再現性が 脅かされる ‣ 実験の再現性を担保しづらくなる
30 やりがちな実装② main.py if obj_initialize_type == "ones": obj = np.ones((obj_size_x, obj_size_y)) elif obj_initialize_type == "random": obj = np.random.rand(obj_size_x, obj_size_y) if prb_initialize_type == "bump": prb = make_bump_probe(prb_size_x, prb_size_y) elif prb_initialize_type == "load": prb = load_probe_from_file(probe_file_path) ... if method_name == "ePIE": obj, prb = ePIE(obj, prb, diffraction, positions, alpha, beta) elif method_name == "DM": obj, prb = DM(obj, prb, diffraction, positions) elif method_name == "rPIE": obj, prb = rPIE(obj, prb, diffraction, positions, alpha, beta, gamma) phase_retrivalのような関数を作らず, mainから各手法を直接呼びだす実装にする • 各手法の関数の中で反復アルゴリズムを 実行するように実装 • if-elseの数が少し減少し, options辞書の問題は無くなっている • 反復アルゴリズム自体が各手法ごとの 関数で分離されたので,追加実装のときの 副作用が抑えられる ‣ これで解決!!と思いきや, 前回の実装とは異なる欠点が発生する
31 やりがちな実装② main.py if obj_initialize_type == "ones": obj = np.ones((obj_size_x, obj_size_y)) elif obj_initialize_type == "random": obj = np.random.rand(obj_size_x, obj_size_y) phase_retrivalのような関数を作らず, mainから各手法を直接呼びだす実装にする 欠点1: 各手法で共通のロジックを書きづらい if prb_initialize_type == "bump": prb = make_bump_probe(prb_size_x, prb_size_y) elif prb_initialize_type == "load": prb = load_probe_from_file(probe_file_path) • phase_retrievalのような関数を作らないので 各手法で共通のロジックを書ける場所がない ... • 結果的にePIE DM, rPIEのそれぞれの関数に 全く同じロジックを書く必要が出てくる • 実装する手法が増えるごとに, その手間が増大していく if method_name == "ePIE": obj, prb = ePIE(obj, prb, diffraction, positions, alpha, beta) elif method_name == "DM": obj, prb = DM(obj, prb, diffraction, positions) elif method_name == "rPIE": obj, prb = rPIE(obj, prb, diffraction, positions, alpha, beta, gamma) ‣ 実装スピードの低下,共通ロジックのコピペで バグを埋め込みやすくなる
32 やりがちな実装② main.py if obj_initialize_type == "ones": obj = np.ones((obj_size_x, obj_size_y)) elif obj_initialize_type == "random": obj = np.random.rand(obj_size_x, obj_size_y) phase_retrivalのような関数を作らず, mainから各手法を直接呼びだす実装にする 欠点2: 新規機能の追加が面倒になる if prb_initialize_type == "bump": prb = make_bump_probe(prb_size_x, prb_size_y) elif prb_initialize_type == "load": prb = load_probe_from_file(probe_file_path) • 各手法共通のロジックを新規追加したい場合, それぞれの関数にその機能を書く必要が出る ... • 例えば,各反復に対してロスをプロットする 機能を追加する場合,そのロジックをそれぞれの 関数に書く必要がある if method_name == "ePIE": obj, prb = ePIE(obj, prb, diffraction, positions, alpha, beta) elif method_name == "DM": obj, prb = DM(obj, prb, diffraction, positions) elif method_name == "rPIE": obj, prb = rPIE(obj, prb, diffraction, positions, alpha, beta, gamma) ‣ 実験管理で重要な変更容易性が担保されなくなる
33 何が悪かったのか? やりがちな実装① • コードが密結合 (Tight Coupling) していた • phase_retrival関数がobjとprbの初期化,反復アルゴリズムの決定を行なっており, この密結合があらゆる問題を引き起こしていた やりがちな実装② • 互いに依存するコードをうまく共通化・分離するのに失敗していた • 反復的位相回復アルゴリズムに共通するロジックと, 各手法に固有の更新ロジックをうまく分離することがうまくできていなかった (再掲) 依存性の注入の定義 さまざまなソフトウェア設計の原則やパターンを集めたものであり, コードが疎結合 (loose coupling) となるような開発を行えるようにするものです. DIを使った位相回復の反復的アルゴリズムの実装を見てみましょう
34 DIによる位相回復アルゴリズムの実装 位相回復ソルバークラスの実装 class PhaseRetrievalSolver: def __init__(self, updater: UpdateStrategy): self.updater = updater def run( self, diffraction: np.ndarray, positions: np.ndarray, obj: PtychoObject, probe: PtychoProbe, num_epochs: int, ) -> tuple: # iterative algorithm logic 位相回復ソルバークラスの使い方 obj = PtychoObject(...) prb = PtychoProbe(...) updater = ePIE(alpha=0.1, beta=0.1) solver = PhaseRetrievalSolver(updater) solver.run(diffraction, positions, obj, prb, num_epochs=100) 各手法の更新方法を外部から受け取る 位相回復ソルバークラスを作る • PhaseRetrievalSolverをインスタンス化するときに 更新ロジックupdaterを受け取る • 位相回復アルゴリズムの実行メソッドrunに 試料関数とプローブのインスタンスを渡す ‣ objとprbの初期化・更新方法の決定を外部に任せ, コードを疎結合にする
35 DIによる位相回復アルゴリズムの実装 抽象基底クラスUpdaterStrategyの実装 from abc import ABC, abstractmethod class UpdateStrategy(ABC): @abstractmethod def update( self, diffraction: np.ndarray, positions: np.ndarray, obj: PtychoObject, probe: PtychoProbe, ) -> tuple: pass ePIEの実装 class ePIE(UpdateStrategy): def __init__(self, alpha: float, beta: float): self.alpha = alpha self.beta = beta def update( self, diffraction: np.ndarray, positions: np.ndarray, obj: PtychoObject, probe: PtychoProbe, ) -> tuple: # ePIE update logic 抽象基底クラスを使って 各手法が備えるべきメソッドを強制する • UpdateStrategyを継承することで, ePIEやrPIEなどにupdateメソッドの実装を強制 ‣ 更新ロジック (ePIE, rPIE, DM)が 共通のインターフェイスを持つようになる • 各手法固有のパラメータはインスタンス化の タイミングで受け渡す ‣ 責務の分離がなされているので, やりがちな実装①で問題となった, 他の手法のパラメータの混入は起きない
36 DIによる位相回復アルゴリズムの実装 PhaseRetrievalSolverのrunメソッドの内部構造 PhaseRetrievalSolverのrunメソッドに def run( self, diffraction: np.ndarray, positions: np.ndarray, obj: PtychoObject, probe: PtychoProbe, num_epochs: int, ) -> tuple: 反復アルゴリズム共通の処理を書く # Iterative phase retrieval process for epoch in range(num_epochs): # Preprocessing steps (if any) ... # Update object and probe obj, probe = self.updater.update( diffraction, positions, obj, probe) # Postprocessing steps (if any) ... return obj, probe • 反復アルゴリズムで共通の前処理や後処理を 書くことができる ‣ やりがちな実装②で問題となった, 共通の処理を何度も書くことが発生しなくなる (補足): 前処理や後処理もDIで書きたい場合 • 前処理や後処理も外部から注入したい場合は Interceptor Patternが有効です • 説明が大変になるので今回は解説しませんが, 実装のヒントにはなると思います Wikipedia: Interceptor pattern
37 DIによる位相回復アルゴリズムの実装 | 特徴 位相回復ソルバークラスの実装 class PhaseRetrievalSolver: def __init__(self, updater: UpdateStrategy): self.updater = updater def run( self, diffraction: np.ndarray, positions: np.ndarray, obj: PtychoObject, probe: PtychoProbe, num_epochs: int, ) -> tuple: # iterative algorithm logic 実はこのDIによる実装は 「Strategy Pattern」というデザインパターン • Strategy Patternはアルゴリズムを実行時に 切り替えるときに使われる • 外部からStrategyを渡して動作を切り替えるので if-elseが氾濫することがなくなる • 各Strategyは分離されているので, 新しいStrategyを追加しても既存機能に影響がない ‣ 実験管理に重要な変更容易性が担保される 位相回復ソルバークラスの使い方 obj = PtychoObject(...) prb = PtychoProbe(...) updater = ePIE(alpha=0.1, beta=0.1) solver = PhaseRetrievalSolver(updater) solver.run(diffraction, positions, obj, prb, num_epochs=100) 参考: Strategy Pattern Wikipedia: Strategy パターン Zenn: 俺が一番好きなデザインパターン「Strategy Pattern」の話
38 DIにおいて外部からどうインスタンスを渡すべきか if-elseの分岐はPhaseRetrievalSolverの内部には無くなったけど 結局外部でStrategyなどをインスタンス化するときにif-elseが氾濫するのでは? • 位相回復アルゴリズムにおいて,試料関数とプローブ関数の初期化を例に説明します • con gファイルからのインスタンス化ではHydraの一部機能を使います (個人的にはHydraの挙動があまり好きではないので,インスタンス化の機能しか使いません) fi fi 「代替コンストラクタ」と「Con gファイルからのインスタンス化」で解決できます
39
DIにおいて外部からどうインスタンスを渡すべきか
位相回復ソルバークラスの実装
class PhaseRetrievalSolver:
def __init__(self,
updater: UpdateStrategy):
self.updater = updater
def run(
self,
diffraction: np.ndarray,
positions: np.ndarray,
obj: PtychoObject,
probe: PtychoProbe,
num_epochs: int,
) -> tuple:
# iterative algorithm logic
(再掲) 試料関数とプローブ関数の初期化ロジック
Step 1: 試料関数 Oとプローブ関数 P の初期化
<latexit sha1_base64="SC4pGb+3gso43lDueF/wt+pkQMw=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdOPO1toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDUTI4N</latexit>
<latexit sha1_base64="XVT5AE+J5T46beThmEAcs8+FVso=">AAACbnichVHLSsNAFD2N7/poVRBBRLFUXJWJ+MKV6MZlH1aFKiWJ0zY0L5JpQYs/4F5cCIqCiPgZbvwBF/0EcSMouHHhTRoQFfWGyZw5c8+dM3dUx9A9wVgzIrW1d3R2dfdEe/v6B2LxwaFNz665Gs9rtmG726ricUO3eF7owuDbjssVUzX4llpd8/e36tz1dNvaEPsO3zWVsqWXdE0RRBV2TEVU1FIjfRgtxhMsxYKY/AnkECQQRtqOX2MHe7ChoQYTHBYEYQMKPPoKkMHgELeLBnEuIT3Y5zhElLQ1yuKUoRBbpX+ZVoWQtWjt1/QCtUanGDRcUk4iyR7YDXth9+yWPbL3X2s1ghq+l32a1ZaWO8XY0Wju7V+VSbNA5VP1p2eBEpYCrzp5dwLGv4XW0tcPTl5yy9lkY5pdsifyf8Ga7I5uYNVftasMz57CfwD5e7t/gs3ZlLyQms/MJVZWw6foxhimMEP9XsQK1pFGPujYMc5wHnmWRqRxaaKVKkVCzTC+hDTzAdZOjg4=</latexit>
•
試料関数は乱数で初期化したり,
全要素を1として初期化することが多い
•
プローブ関数は隆起関数で初期化したり,
計測時の事前情報に基づき初期化することが多い
O
<latexit sha1_base64="SC4pGb+3gso43lDueF/wt+pkQMw=">AAACbnichVHLSsNAFD2N7/qqCiKIWCxKV2UqvnBVdOPO1toqVClJnLbBvEimBS39AffiQlAURMTPcOMPuPATxI1QwY0Lb9KAqKg3TObMmXvunLmj2LrmCsYeQ1Jbe0dnV3dPuLevf2AwMjScd62qo/KcaumWs63ILtc1k+eEJnS+bTtcNhSdbyn7q97+Vo07rmaZm+LA5ruGXDa1kqbKgqjCjiGLilKqrzfCxUiMJZgf0Z8gGYAYgkhbkWvsYA8WVFRhgMOEIKxDhktfAUkw2MTtok6cQ0jz9zkaCJO2SlmcMmRi9+lfplUhYE1aezVdX63SKToNh5RRTLMHdsOa7J7dsif2/mutul/D83JAs9LScrs4eDSWfftXZdAsUPlU/elZoIQl36tG3m2f8W6htvS1w5Nmdnljuj7DLtkz+b9gj+yObmDWXtWrDN84hfcAye/t/gnys4nkQmI+MxdLrQRP0Y1xTCFO/V5ECmtII+d37BhnOA+9SKPShDTZSpVCgWYEX0KKfwDUTI4N</latexit>
位相回復ソルバークラスの使い方
obj = PtychoObject(...)
prb = PtychoProbe(...)
updater = ePIE(alpha=0.1, beta=0.1)
solver = PhaseRetrievalSolver(updater)
solver.run(diffraction, positions,
obj, prb, num_epochs=100)
P
<latexit sha1_base64="XVT5AE+J5T46beThmEAcs8+FVso=">AAACbnichVHLSsNAFD2N7/poVRBBRLFUXJWJ+MKV6MZlH1aFKiWJ0zY0L5JpQYs/4F5cCIqCiPgZbvwBF/0EcSMouHHhTRoQFfWGyZw5c8+dM3dUx9A9wVgzIrW1d3R2dfdEe/v6B2LxwaFNz665Gs9rtmG726ricUO3eF7owuDbjssVUzX4llpd8/e36tz1dNvaEPsO3zWVsqWXdE0RRBV2TEVU1FIjfRgtxhMsxYKY/AnkECQQRtqOX2MHe7ChoQYTHBYEYQMKPPoKkMHgELeLBnEuIT3Y5zhElLQ1yuKUoRBbpX+ZVoWQtWjt1/QCtUanGDRcUk4iyR7YDXth9+yWPbL3X2s1ghq+l32a1ZaWO8XY0Wju7V+VSbNA5VP1p2eBEpYCrzp5dwLGv4XW0tcPTl5yy9lkY5pdsifyf8Ga7I5uYNVftasMz57CfwD5e7t/gs3ZlLyQms/MJVZWw6foxhimMEP9XsQK1pFGPujYMc5wHnmWRqRxaaKVKkVCzTC+hDTzAdZOjg4=</latexit>
P2C
<latexit sha1_base64="RGve++8fO3zH2zCCJlmVYtG/5fM=">AAACi3ichVFNLwNBGH66vuuruEhcNhri1EwRRByESFwkLYpEaXbXtCb2K7vTJmz6B8TdwYlERNxcubn4Aw5+gjiSuDh4u91EELybnXneZ97nnWdmdNcUvmTsMaY0NDY1t7S2xds7Oru6Ez29a75T9gyeMxzT8TZ0zeemsHlOCmnyDdfjmqWbfF3fm6+tr1e45wvHXpX7Lt+ytJItisLQJFGFxHDe0uSuXgwyVTUvbLWe6sF8dTtYUvNSWNxXl6qFRJKlWBjqT5COQBJRZJzEBfLYgQMDZVjgsCEJm9Dg07eJNBhc4rYQEOcREuE6RxVx0papilOFRuwejSXKNiPWprzW0w/VBu1i0u+RUsUQe2CX7IXdsyv2xN5/7RWEPWpe9mnW61ruFroP+1fe/lVZNEvsfqr+9CxRxFToVZB3N2RqpzDq+srB8cvK9PJQMMzO2DP5P2WP7I5OYFdejfMsXz5BnB4g/f26f4K10VR6IjWRHU/OzkVP0YoBDGKE7nsSs1hEBjna9wjXuMGt0qmMKdPKTL1UiUWaPnwJZeEDXWiYaw==</latexit>
O2C
<latexit sha1_base64="lR1C3nYFxQkOFa7jANLZEf8gbEc=">AAACi3ichVG7SgNBFD2u7/iK2gg2i0GxChOVKGIhimDlOyoYDbvrJA7ZF7uTgC75AbG3sFIQETtb7Wz8AQs/QSwVbCy82SyIinqXnTn3zD13zszoril8ydhjnVLf0NjU3NIaa2vv6OyKd/es+07JM3jGcEzH29Q1n5vC5hkppMk3XY9rlm7yDb04W13fKHPPF469Jvddvm1pBVvkhaFJonLxoaylyT09HyxW1Kyw1VqqB7OVnWBBzUphcV9dqOTiCZZkYag/QSoCCUSx5MQvkMUuHBgowQKHDUnYhAafvi2kwOASt42AOI+QCNc5KoiRtkRVnCo0Yos0Fijbilib8mpPP1QbtItJv0dKFYPsgV2yF3bPrtgTe/+1VxD2qHrZp1mvabmb6zrsW337V2XRLLH3qfrTs0QeE6FXQd7dkKmewqjpywfHL6uTK4PBEDtjz+T/lD2yOzqBXX41zpf5ygli9ACp79f9E6yPJFPpZHp5LDE9Ez1FC/oxgGG673FMYx5LyNC+R7jGDW6VDmVUmVSmaqVKXaTpxZdQ5j4AX1aYbA==</latexit>
N ⇥N
M ⇥M
初期化には多数のパターンがある
解法:PtychoObjectとPtychoProbeを工夫する
40 DIにおいて外部からどうインスタンスを渡すべきか PtychoProbeの実装 class PtychoProbe: def __init__(self, data: np.ndarray): self.data = data @classmethod def create_from_bump( cls, shape: tuple[int, int], radius_fraction: float = 0.5, ) -> "PtychoProbe": # Create a bump-shaped probe ... return cls(data) @classmethod def create_from_tiff( cls, filepath_probe_amplitude: str, filepath_probe_phase: str, shape: tuple[int, int], ) -> "PtychoProbe": # Load probe from TIFF files ... return cls(data) 注意: def __init__のコンストラクタにif文を入れたり 複雑なロジックを入れるのはアンチパターンです インスタンスの生成ロジックが複雑になり 可読性の低下や保守容易性の低下に繋がります 代替コンストラクタを実装し, 様々な初期化をできるようにする • @classmethodを使うことで, def __init__に複雑なロジックを書かなくて済む • これによりこのクラスに余計なプロパティを 追加したり,if文を使わずに済む ‣ 新しく初期化方法を追加するのも容易 参考: 代替コンストラクタ Zenn: classmethodを使った代替コンストラクタ
41 DIにおいて外部からどうインスタンスを渡すべきか con g.yamlの例 probe: __target__: PtychoProbe.create_from_bump shape: [256, 256] radius_fraction: 0.5 OmegaConfによるcon g.yamlの読み込みと hydra.utils.instantiateによるインスタンス化 from omegaconf import OmegaConf as oc from hydra.utils import instantiate # Load configuration using OmegaConf config = oc.load("config.yaml") # Instantiate the probe from config probe = instantiate(config.probe) omegaconfとhydra.utils.instantiateを使って con gファイルからインスタンス化を行う • 実験設定をyaml形式で記載し con g.yamlを用意する • con gの情報からインスタンス化が可能となる ‣ これを応用するとコードを変えずに, 様々な設定で実験を行うことが可能となる 参考: Hydraによるインスタンス化 Hydra: Instantiating objects with Hydra 事前に変数が定義できない変数をもつクラスであっても, (例えば,PtychoProbeのshapeが実行時にしかわからないケース) partial instatiation機能を使えばcon gからインスタンス化可能 fi fi fi fi fi fi Hydra: Partial Instantiation
42
深層学習の実装でDIを活用するには?
Pytorch Lightningの使用例
logger = WandbLogger(
project="training-denoiser",
name=f"{config.data_module_name}-{config.model_name}",
save_dir=log_path / "wandb",
)
checkpoint_callback = ModelCheckpoint(
monitor="val_psnr",
dirpath=log_path / "checkpoints",
mode="max",
filename="{epoch}-{val_psnr:.4f}",
save_top_k=3,
)
callbacks = [checkpoint_callback]
# Instantiate the data module, model system, and trainer
data_module: pl.LightningDataModule = ...
hydra.utils.instantiate(config.data_module)
lit_system = DenoiserSystem(config.model,
config.loss, config.optimizer)
trainer: pl.Trainer = ...
hydra.utils.instantiate(
config.trainer, logger=logger, callbacks=callbacks)
# Show the configuration
print("Experiment Configuration:")
print(OmegaConf.to_yaml(config))
# Start training
trainer.fit(model=lit_system, datamodule=data_module)
PyTorch Lightningを使いましょう!
• data, model, logger, callbackなどあらゆるものが
モジュール化されており,実装しやすい
• PyTorch Lightningの学習ループを回すためのクラス
TrainerはDIの設計になっています
‣ 外部からloggerやモデル保存機能などさまざまな
コールバックを注入することができる
• 拡張性も高く,変更容易性も兼ね備えているので,
実験管理しやすい
43
深層学習の実装でDIを活用するには?
Pytorch Lightningの使用例
logger = WandbLogger(
project="training-denoiser",
name=f"{config.data_module_name}-{config.model_name}",
save_dir=log_path / "wandb",
)
checkpoint_callback = ModelCheckpoint(
monitor="val_psnr",
dirpath=log_path / "checkpoints",
mode="max",
filename="{epoch}-{val_psnr:.4f}",
save_top_k=3,
)
callbacks = [checkpoint_callback]
PyTorch Lightningを使いましょう!
Trainerに渡すWeigh and Biasesのloggerをインスタンス化
• data, model, logger, callbackなどあらゆるものが
モジュール化されており,実装しやすい
• PyTorch Lightningの学習ループを回すためのクラス
Trainerに渡すモデル保存のためのコールバックをインスタンス化
TrainerはDIの設計になっています
# Instantiate the data module, model system, and trainer
data_module: pl.LightningDataModule = ...
hydra.utils.instantiate(config.data_module)
lit_system = DenoiserSystem(config.model,
config.loss, config.optimizer)
trainer: pl.Trainer = ...
hydra.utils.instantiate(
config.trainer, logger=logger, callbacks=callbacks)
‣ 外部からloggerやモデル保存機能などさまざまな
コールバックを注入することができる
• 拡張性も高く,変更容易性も兼ね備えているので,
実験管理しやすい
インスタンス化したloggerとcallbackをTrainerに注入
# Show the configuration
print("Experiment Configuration:")
print(OmegaConf.to_yaml(config))
# Start training
trainer.fit(model=lit_system, datamodule=data_module)
モデルとデータモジュールを注入してtrainingを開始
44 まとめ どう実験管理をすべきか? • 再現性を担保するために守るべきルールを知る • 変更に強く,拡張性の高いコードの設計をする ‣ ソフトウェア設計の重要性 ‣ 依存性の注入による柔軟な設計 ‣ con gファイルを使った実験設定とコードの分離 より良いベストプラクティスがあれば,僕にも教えてください! 共同研究のお誘いもお待ちしています! fi 連絡先: 山田宏樹 (Email: [email protected], Twitter: @KokiYamada6)