meowの覚え書き

write to think, create to understand

【VSCode】Pythonコードのデバッグ中に画像の入った変数を可視化できる拡張機能を試した

はじめに

この記事では、Visual Studio Code(以下VSCode)の拡張機能の1つを使った際の所感を紹介する。

私は普段、画像系の機械学習タスクに取り組む時VSCodeで開発している。 その際、画像に対して自前実装の前処理やデータオーグメンテーションがちゃんと施されているかを簡易的に確認したい場合がある。

そういう時に私はこれまでディスクに書き出して結果を確認していた1:。 しかし、もう少しスマートな方法がないかと思い、デバッグ中に画像を可視化できるVSCode拡張機能が無いか探したところ、下記の拡張機能を発見した。

github.com

VSCodeでこの名前で拡張機能を探せばヒットする。かなり長い名前なので、以下でもこのsimply-view-image-for-python-debuggingのことを「拡張機能」と呼ぶ。


スポンサードリンク

拡張機能の使用方法

公式リポジトリのgifアニメをそのまま引用するが、これを見れば使い方は一目瞭然である。

  1. 実行したいPythonスクリプトを用意する。
  2. ブレークポイントを張る。(当たり前かもしれないが)ブレークポイントの位置は、画像を可視化したいオブジェクトを変数に格納した以降の行に張ることに注意。
  3. VSCodeでデバックモードを実行し、処理がブレークポイントまで達したら、
    1. 画像のオブジェクトが格納されている変数をクリックすると、もしもその変数が拡張機能で可視化をサポートしているオブジェクトならば、電球マークが現れる。
    2. 電球マークをクリックすると、View Imageというポップアップが出る。
    3. ポップアップをクリックすると、別ウィンドウで可視化された結果が表示される。

拡張機能内部で行われている処理はとてもシンプルで、 /tmp 領域にsvifpd2いうディレクトリを作り、画像をそこに書き出して、その画像を別ウインドウで表示するというものである。

各ライブラリのオブジェクトの可視化結果結果

機械学習の画像系タスクで主に使用されるであろうPythonライブラリのオブジェクトを可視化した結果をスクショ付きで述べる。

各ライブラリごとの可視化結果を下表に示す。中には画像処理用途とは限らないものも含めてしまっているが、実用途を鑑みての判断であるのでご了承いただきたい。

ライブラリ名 可視化可能か 検証した際のバージョン
Pillow 7.1.2
NumPy(, Scipy) 1.19.5
opencv-python 4.1.1.26
imageio 2.4.1
Matplotlib 3.4.2
PyTorch 1.6.0(torchvisionは0.7.0)
TensorFlow 2.7.0

なお、その他の動作環境のバージョンは下記の通り。

Pillow・・・○

readmeにはサポートしているとは書いていないが、可視化可能。

NumPy(, Scipy)・・・○

画像ではなく単なる数値のテンソルに対して可視化をおこなってみたが、可能だった。 また、ScipyもNumPyを内部で利用しているので可視化可能。

opencv-python・・・△

NumPyがサポートされているので、もちろんopencv-pythonの可視化も可能。

ただし、opencv-pythonではChannelの順序がBGRなので、残念ながらきれいに表示されないところが玉にキズ。

imageio・・・○

私は使わないが、imageioを試してみた所、可視化可能だった。

Matplotlib・・・○

画像を扱うためのライブラリでは無いが、先程張ったgifアニメの通り、MatplotlibのFigure、Axisの可視化も可能。

PyTorch・・・○

readmeにはPyTorchのテンソルもサポートしていると書かれていたので試してみたが、確かに可視化された。PyTorch民としてはかなり嬉しい。

また、驚いたことにBatch次元を考慮して表示してくれている。

TensorFlow・・・✕

readmeにはサポートしているとは書いていないが念の為可視化を試みた。
しかし、電球マークが現れなかったので対応していない模様。なお、私はTensorFlowはほぼ使用経験がないので画像のテンソルを作る方法が正しいかは自信がない。

おわりに

デバッグ時に様々なライブラリのオブジェクトに対して画像の可視化を試みた。
その結果、おおよそのライブラリで可視化可能ということがわかった。
Python開発の生産性の向上が期待できそうである。

なお、今回テストに使用したコードと画像は↓のリポジトリに置いているので、興味を持たれた方は追試に使っていただければ幸いである。 github.com


スポンサードリンク


  1. なお、「Jupyter Notebookならば楽に画像可視化できる」という声があるかもしれないが、私は開発にJupyter Notebookは用いない。その理由はこちらの記事で書いている。

  2. 拡張機能の頭文字をとったものと思われる

Open xINT CTF 関連ページ 非公式まとめ

AV TokyoでTeam pinjaによって開催されるCTF 『Open xINT CTF』関連の情報をまとめています。
ファンサイトの位置づけです。

目次

お知らせ

(2023/11/12 1:25 更新) 2023年度のOpen xINT CTF お疲れ様でした!

ctfのサーバは下記ツイートをご確認ください。

(2023/11/10更新) 2023年度のOpen xINT CTF情報のアナウンスがありました

pinjaの公式サイト上で2023年度のOpen xINT CTFの情報が掲載されました。

去年との大きな差異は次の通りです。

  • 個人戦
  • sigint、humint問の復活
    • AVTokyo会場内で情報収集する必要があると思われます。
  • 開催時間は 14:00-19:30(日本時間) の5.5時間
    • CTF登録は 12:00(日本時間)から可能
  • オンサイト問題(sigint,humint)は配点がオンライン問題(osint)の2倍(現地とオンラインのバランス調整)

また、ルール記載は次の差分がありました。

  • すべて無償で調査可能であることを明記
  • writeup執筆時はセンシティブな情報を伏せるように明記

公式情報

有志の方々による参加報告資料など

その他のOpen xINT CTF関係のサイト

私の報告資料

Speaker DeckのPageViewをロギング&可視化する仕組みをコストを抑えて作った

f:id:meow_memow:20210915194634j:plain

この記事では、Speaker Deck上にアップロードしたスライドのページビュー(以下PV)を1日毎に収集し、日付ごとのPVをグラフとして可視化する仕組みをPythonで開発したことについて説明する。

目次


スポンサードリンク

背景

私が勉強会の発表スライドを共有する時、サービスはSpeaker Deckを使っている1

meow (@meow_noisy) on Speaker Deck

Speaker Deckを使っていて不便な点はいくつかあるが、その中の一つに1日毎のPVが記録されていない点がある。これがないために、どういうタイミングで何のスライドがどれだけ閲覧されているのかを分析できない。

したがって、1日でPVがどれだけ溜まったかを可視化するためにSpeaker Deck用のPVログ収集 & 分析する仕組みをコストを抑えて構築することにした。

アーキテクチャ

speakerdeckからPVを取得して可視化するまでの仕組みの全体像を先に示す。

f:id:meow_memow:20210915195039j:plain

区分けとしては下記の部分に分けられる

  • Speaker DeckのページからPVを取得しファイルに保存する部分
  • スクレイピングAWS Lambdaで行い、S3へ格納する部分
  • PVを記録したファイルからレコード単位に変換しDBに格納する部分
  • DBからレコードを取得し、PVをグラフとして可視化する

実装したコードは下記のリポジトリに配置している。

github.com

以下ではそれぞれの技術要素を説明する。

PVのスクレイピング部分

f:id:meow_memow:20210915195820j:plain

Speaker Deckにはスライドに関するメトリクスを取れるようなWebAPIもなければメトリクスのファイルダウンロード機能も提供されていない。したがって、ユーザがアップロードしたスライドページのスクレイピングを行い各スライドのPVを収集した。

ローカルでスクレイピングを実行するコードはリポジトリscrape_speakerdeck_pv.py に該当する。PVを取得したいSpeaker DeckのURLをこのスクリプトに書いた上で、$ python scrape_speakerdeck_pv.py を実行すると、result ディレクトリを作成し、その中にスクレイピング結果のtsvファイルを書き込む。

取得対象は、スライドのユニークID(Speaker Deck側が勝手に付与), スライドのタイトル名、PVである。

これを、次のようなフォーマットでtsvファイルで保存する。ファイル名は取得した日付にしている。

たとえば、2021-08-05.tsvは下記のような内容である。

ad7b2a026bd74e00a2c3915043c7eecb  Security Days Spring 2021のOSINTに関する講演4つの簡易まとめ/os_int_webinars2021spr    438
b9cb90f9e11845c9afe619a61c64798b    初心者向けOSINT CTF! Cyber Detective CTFに挑んだ!/cyber_detective_ctf  291
891f7fb535e14a54a85ee8792f734063    画面共有時にセンシティブな情報を意図せず公開してしまう事例の調査/sharing_screen_and_confidential_data   195
...

もちろん過去のPVは取得できないので、スクレイピングを開始した日からのロギング開始となることに注意されたい。

FaaSによるスクレイピングの定期実行

上記のスクレイピングをかつては手動のトリガーでやっていたがいい加減面倒だったので自動化した。

毎日1回定期実行といえばcrontabが第一に考えられるが、我が家には常時起動しているマシンはないためランニングコストを考慮してFaaSであるAWS Lambdaを使った。tsvのファイルの格納先はS3としている。

まず、LambdaにはS3を参照できるように権限を付与する。

次に実行するPythonの関数を登録する。Lambdaで実行する関数を含むスクリプトリポジトリlambda.py に該当する。これをコピーしてLambdaの関数として登録する。

スクレイピングに使用するrequestsBeautifulSoupはLambdaのPythonランタイムにはインストールされていないので、レイヤーを作成する。レイヤーの作成の仕方はAWS公式サイトの手順を参考にした。レイヤー作成時に気をつける点としては、python/lib/python3.8/site-package という構造でパッケージを格納する必要があることである。

この関数を1日1回定期実行するように設定する。すると、S3の指定バケットにtsvが格納される。

あとは、そのバケット$ aws s3 sync でローカルと同期すればよい。

Lambdaのエラー通知

Lambdaでエラーが起きた時にSlackに通知が飛ぶようにした。アラートの流れとしては、Lambda→CloudWatchAlarm→SNS Topic→AWS Chatbot→Slackである。これは下記のブログ記事を参考にさせていただき実装した。記事の通りSlackへのアラートをとても簡単に構築できた。

masakimisawa.com

tsvファイルをレコード単位に整形しDBへ挿入

tsvは日付ごとの各スライドのPVが記載されているが、これを各スライドごとの日付ごとのPVに変換してDBに格納する。

これらの作業はAWS GlueとRDSを使えば完全にAWS上で運用することができるが、ランニングコストがばかにならないのでローカルの方にDBを構える。

DBには手軽さを考えてSQLiteを使用した。

DBのER図は下図の通りである。slideテーブルでスライドを、slide_pb_dateテーブルでその日のスライドのPVを保持する。

f:id:meow_memow:20210915200740p:plain

このスキーマに沿うようにtsvファイルの各レコードを整形し、DBに挿入する。

この処理はリポジトリinsert_records_into_sqliteDB.py に該当する。

$ python insert_records_into_sqliteDB.py を実行すると、DBにレコードを挿入してくれる。

PlotlyでPVを可視化

この時点で、DBにはスライドに対して日付ごとのレコードが入っている。これをグラフでプロットする。インタラクティブにグラフを操作したかったため、Jupyter Notebookを立ち上げて、Plotlyで可視化する。

これはリポジトリvisualize.ipynb をJupyter Notebookを立ち上げればよい。

あとは、最後までコードを実行すると、下図のように各スライドの日付ごとのPVが可視化される。また、UIを操作すれば、スライドごとのPVを比較することもできる。

f:id:meow_memow:20210915201827p:plain

おわりに

Speaker DeckのPVをロギングし、可視化する仕組みをなるべくコストを抑えて実現する方法について説明した。
自分で溜まったログを可視化した感想としては非常に役立っている。急上昇しているスライドがあった時、何が原因でそうなったのかを調べるのが楽しい。また、今日はPVが増えていないと思っていてもグラフで見ると1つ増えているのがわかったりする。
このようにグラフを確認することで、次の発表へのモチベーションにしている。


スポンサードリンク


  1. Slide ShareではなくSpeaker Deckを使っている理由はこちらの記事で述べている。

Vue.js+Vuetifyをゼロから学習して簡単なブラウザゲームを作った

f:id:meow_memow:20210820213718j:plain

目次

Vue.jsを勉強し、数当てゲームを実装しWeb上に公開した。その開発過程を記す。

なお、開発物『16進数5桁版のhit and blow』は下記で遊べる。
『16進数5桁版のhit and blow』へのリンク


スポンサードリンク

はじめに

背景

「エンジニアたるもの専門以外の技術も身につけていくべきである。」という先人達の教えに従い、Python以外のことも学ぼうと思った。 何を勉強しようか考えた所、JavaScript(以下JS)は知っていて損はないだろうと思った。何かGUIのツールを他人に使ってもらうならセットアップは不要であったほうがいい1

私は勉強時、書籍を読んだだけだと経験上すぐ忘れるので、動くアプリを作ってリリースすることを学習目標として取り組んだ。

この記事はリリースまでの開発記の位置づけである。

私のWebアプリのフロントエンド開発経験

まず、スタートラインとなる私の開発経験は下記の通りである。

ここで言いたいのは、私はJavaScriptをまともに書いたことがないということである。

開発過程

プロジェクトスタート段階

この段階で決めたことは以下の通り。かなりいい加減である。とはいえ完璧さを求め出すと何も作れなくなるので、計画の大雑把さは意図的に心がけた。

  • 何を作るかの決定
    • 満たしたい条件
      • 最小構成で動くものがよい
      • いままでにないものがよい
    • hit and blowという数当てゲームのルールを改造したものを開発することにした。
      • 選定理由は、実装すべき機能が少なく、実際に遊んでみたかったためである。
  • どこまで作るかの決定
    • 小綺麗な見た目のUIまでは作りたいと思った。
  • 開発技術選定
    • Webフレームワーク
      • Vue.jsを主軸にWebページを作ることにした。
        • Vue.jsを選んだ理由は単純で、なんとなく流行ってそうだからである。
    • CSSフレームワーク
      • Vuetifyにした
        • Vue.jsのテンプレートと相性が良い
        • 日本語のドキュメントがあるのもありがたい。
      • ちなみに、始めた当初はVue.jsにリッチなUIが用意されていると思いこんでいたが、そうではなかった。
    • ホスティングサービス
      • Github Pagesとした
        • 理由は無料でデプロイも簡単なため。

開発期間(技術学習の期間も含む)

4月初旬から着手開始して7月末リリースなので、4ヶ月間程度かかっている。

  • 4月初旬~: Vue.js 自体の勉強
    • Vue.jsのチュートリアル
    • 他人が実装したサンプルを動かしながらVue.jsの挙動を確認
  • 6月初旬〜: Vue.jsを使わずに hit and blow の プロトタイプづくり
  • 6月下旬〜: Vue.js+Vuetifyで実装しリリース

各フェーズにやったことを以下で説明する

Vue.js 自体の勉強

Vue.js公式Webサイトにはチュートリアルが用意されていなかった。したがってガイドページの写経を行った。これを終えた段階では、スニペットのふるまいはわかるが、それが何が良くて何のためにあるのかわからないという状態である。

次に、Vue.jsのサンプル実装を写経することにした。サンプルはGoogleで「Vue サンプルページ」と検索してヒットしたもの↓を選んだ。
https://vuejs.org/v2/examples/index.html

写経をする中でtypoも発生するのでアプリが動かないということも起こった。その度にオリジナルのコードと違うところを目視diffをすることで解決した。

写経の後は、処理の流れを目視で追った。しかし、文法がわからないところがところどころ存在した。不明な箇所がVue.js起因なのか、JavaScript起因なのか分からなかったので、JavaScriptからやり直すことにした。

Vue.jsを使わずに hit and blow の プロトタイプづくり

JavaScriptの基礎の勉強にはドットインストールのはじめてのJavaScriptを見た。この時に写経はしていない。

その上で、hit and blowを1からHTML+JavaScriptで実装してみた。これはプロトタイプという位置づけである。実装したものは下記リポジトリに置いてある。

github.com

ちなみにプロトタイプもデプロイしているので下記リンクで遊べる。
プロトタイプ版『16進数5桁版のhit and blow』へのリンク
styleをほぼ指定していないため見た目がショボい。

実装にあたってはhit and blowのJavaScript実装例を探したが、ボタンで数字を入力するタイプは見つからなかったため参考にしていない。ひたすらコールバックを定義して、ボタンオブジェクトを取得して、clickイベントと対応付けることを繰り返した。

実装でこんがらがったのは、ボタンのdisableを制御することである。ゲームの状態(ゲームクリア、ゲームオーバーなど)に応じてこのステータスを更新する必要があるのだが、disableを開放するタイミングを網羅しなければならず、それをコードに落とすのが大変だった。

プロトタイプをVue.jsで書き直す

プロトタイプの実装を終えた時点で、JavaScriptの理解度は「調べればわかる」レベルまで到達したと感じたので、Vue.jsのサンプルコードを読むフェーズに戻り、Vue.jsのプロジェクトの構築の仕方を把握した。

また、辞書として『改訂2版 基礎から学ぶ Vue.js』を購入した。この本はオプション構成の説明が充実していてありがたかった。

改訂2版 基礎から学ぶVue.js [2.x対応! ]

改訂2版 基礎から学ぶVue.js [2.x対応! ]

  • 作者:mio
  • シーアンドアール研究所
Amazon

プロトタイプ開発の時点で必要な機能(正解の数の生成や、正誤判定など)は全部実装したので、後はVue.jsの機能を使えるようにコードを移植していった。

Vue.jsで書き直す中でデータバインディングは強力で感動した。プロトタイプだとボタンのdisableをイベントごとに書き換えていたが、Vue.jsならば算出プロパティでdisableの振る舞いを定義することでシンプルにボタンの状態を制御できた。

最終的にhit and blowをVue.jsで実装したプロジェクトリポジトリは下記である。

github.com

また、デプロイしたページへのリンクを再掲する。実際に遊べるので興味があればプレイいただければ幸いである。

meow-noisy.github.io

実装したかったけど、技術的に不可能だったこと

小綺麗なUIをつけられて満足しているものの、妥協した部分も勿論存在する。

  • 入力ヒストリーの初期位置がおかしい
    • 過去の入力を表示する表を右に配置しているが、なぜか最初はページの中央に配置されてしまいレイアウトのバランスがよくない印象をうける。
  • プルダウン形式のメモをつけられなかった
    • プレイ中に目星のついた桁をメモしておく場所を用意したかった。イメージとしては5桁分のプルダウンを並列に並べたかった。しかし、Vuetifyのプルダウンコンポネントであるv-selectを使用すると、1つずつ垂直に配置されてしまうのでやめた。しょうがなくテキストボックスで代替した。
  • チート防止
    • 結局、答えを格納しているオブジェクトをプレイヤーが参照できてしまう以上競技性がない。したがって、正誤判定サーバを用意して通信したかったが実装方法がわからなかったのでやめた。ここらへんはもう少しWeb開発力が上がってからチャレンジし直したい。

おわりに

開発のふりかえり

勉強のためにVue.jsでアプリを開発することにした。ロジックの単純なプロダクトをつくるという目標を達成する上で、hit and blowを題材にしたのは正しかったと思う。

プロトタイプはJSしか使わなかったこともあり、Vue.jsのありがたみを感じることができた。とはいえ、Vue.jsのコンポーネントやルーティングのような機能は使っていないので、「Vue.jsで実装できます」とは言えないであろう。今後も精進していきたい。

姉妹記事

自分で作ったゲームを遊んだ時の感想を別の記事で書いた。遊んでみてゲームデザインも大切だということがわかったのはいい経験であった。

meow-noisy.hatenablog.com


スポンサードリンク


  1. Pythonで動くGUIのツールをユーザが使おうとすると、Pythonやライブラリを自前でインストールしなければならず、導入負荷が高い。

『PyData.Tokyo Meetup #23 MLOps〜AIを社会に届ける技術』 聴講メモ

f:id:meow_memow:20210708213552j:plain

目次

5月にPyData.Tokyo Meetup #23 MLOps〜AIを社会に届ける技術がオンラインで開催されました。
https://pydatatokyo.connpass.com/event/210654/

PyData.TokyoがテーマにMLOpsを扱うのは2年ぶり1で、個人的に期待が高かったです。

今回はサイバーエージェント(CA社)のMLOpsにまつわるご発表2件でした。 トピックはML実験管理基盤、プロダクト化にあたってのパフォーマンス向上やトラブルシューティングでした。
Ops関係の直接的な話題はなかったものの、現場ならではのノウハウが豊富にふくまれていました。

私は勉強のために、スピーカーごとにメモ(個人的所感含む)を取っていました。 スライドや動画が公開されているので、そちらをご覧いただいた方がいいというのは百も承知の上、以下で共有します。


スポンサードリンク

1人目『CyberAgent AI Labを支えるCloud実験環境』

スピーカー: 岩崎 祐貴 (Yuki Iwasaki)

概要: CyberAgent AI Labでは、Computer VisionNLP・経済学・ハイパーパラメータ最適化・Human Computer Interactionなど分野の異なるResearcherが広く在籍しています。このような分野違いのデータや実験を一元管理するために、AI Labで開発・運用しているデータローダーやモデルサーバーを通じて、高速かつパワフルなCloud実験環境をご紹介します。

スライド: CyberAgent AI Labを支えるCloud実験環境 - Speaker Deck https://speakerdeck.com/chck/cyberagent-ai-labwozhi-erucloudshi-yan-huan-jing

動画: https://www.youtube.com/watch?v=E2s5NP6B8cs

本題

  • 所属組織であるAI Lab
    • 人数35人。Researcher 31名、Research Engineer 4名(岩崎さんはこちら)。リサーチャーが多い。
    • OKR: 学会発表がメイン。
    • プロダクト側ではないので、顧客データを貯めていない。しかし実験で使用したい時はプロダクト側と連携する。
  • AI Labのエコシステム
    1. 課題、仮説、アイディア
    2. データ選定し、収集する
    3. 前処理
    4. 解きたいタスクに対するモデリング
    5. 評価(実験結果のレポート作成)
    6. 論文、プロダクション化
  • 研究生産性にまつわる課題
    • 使うライブラリは、リサーチャーや分野によってばらつく。
      • → そこで岩崎さんはR&Dを加速させるために、実験の共通部分を洗い出して実験サポートツールを作った。
  • 今回の報告内容は、エコシステムにおけるサポートツール3つの紹介
    1. (ideaに関するものは無し)
    2. データ収集支援→ ailab-datasets
    3. (前処理: numpy, tfrecord, pandas, beam)
    4. モデリング支援→ ailab-model-zoo
    5. 実験支援→ ailab-mlflow
    6. (論文: overleaf, GCP, AWS)

データ選択: ailab-datasets

  • 前提知識: tensorflow-datasets
    • publicなデータセットを一元管理してくれるOSSのdata loader
      • データセット名 を指定し、ロードするだけですぐに使える。
        • 226のデータセットが提供されている。mnistとかの軽いものだけでなく Celeb AやMS COCOなどの大容量のものもカバーしている
        • 画像だけでなくテキストや音声データもカバーしている
    • 分析データを扱うためのインタフェースを統一
      • tensorflowとついているが、numpy, pandas形式で出力できる。pytorch派でも使える。
        • ただしdfはiterableではないので、メモリに展開されてしまうことに注意。
    • オリジナルのデータセットの定義方法
      • tfds cliという専用のCLIがあるので、それ経由でpythonのテンプレートを生成する。
        • テンプレート
          • メタデータにデータの型やdescription
            • データ参照時に型チェック可能
          • _info()に、データ本体ののURLや、train/val/test といったデータセットの分割方法を定義
          • _generate_example()
            • 参照したデータに対する処理を書くジェネレータ関数
        • データセット用のテストケースも書くことができる。
    • 動画のような入力データサイズが巨大な場合はapache beamの形式で並列分散も処理可能
      • もとのテンプレートをちょっとapache beam用に変えるだけで動かすことができる。
      • apache beamのおかげでクラウドでスケールしやすい
  • private(社内)なデータを扱う上での課題
    1. データの管理にルールがない
      • CA社のプロダクトは並列に大量に立ち上がるため、データ管理が難しい
        • プロダクトサイドのデータ集約基盤はリサーチャー向けではない
      • → リサーチャーごとのオレオレJupyterlab上で実験コードやデータを管理している
    2. リーダビリティの低いSQL、冪等性の無い前処理のため、リサーチャー間の再現が困難
    3. privateデータは使うのにドメイン知識が必要で、プロジェクト新規参入者の学習コストが高い
  • この課題を解決するのがailab-datasetsという社内ツール
    • 説明: private データを前処理ごと管理できるDataloader。前述のtensorflow-datasetsのラッパー
    • 利点
      • Data追加が簡単
      • プロダクトやタスクごとにデータをバージョニングできる
      • apache beamのランナーを使うことにより、高速に前処理が可能
    • post-process(共通の前処理の後の処理)を選択可能
      • 特徴としては、TensorflowTransform(TFデータのapache beam実装)が選択可能。前処理-後処理で分散処理のパイプを組める。

モデリング部分でのサポート: ailab-model-zoo

  • privateなmodelにおける課題
    • 異なるタスクで似たようなモデルの再発明が起きている
    • リサーチャー間でpretrain済みモデルのシェアしたい場合がある
      • 社内データでpretrainした重みがほしい。
      • しかし、誰がどんなモデルを作成しているか把握しづらい
  • ailab-model-zoo(WIP)
    • AI hub(GCPのマネジド版tensorflow-hub)を使用
    • 次の3つをprivateに管理可能: kubeflowパイプライン, jupyter notebook, 学習済みモデル
    • 名にtensorflowと関しているが、オブジェクトストレージとしても使える
      • メタデータやタグを打っておけば、所定のリソースをテキスト検索できて、探しやすい
    • (個人メモ; 単純にAI hub使うのと何が異なるのだろうか)

実験管理部分でのサポート: ailab-mlflow

  • 実験管理ツールにおける課題
    • MLflowはサーバ構築が面倒。セキュリティのことを考える必要がある。
    • SaaS(Nepture.ai, Comet.mlなど)は無料枠を越えると高コスト
  • MLflowでの実験管理
    • 1つの共用のMLflowサーバをたててチームで利用という案はやりたくない
      • アクセス制御ができない
      • 個人の実験管理を制限してしまう
    • ailab-mlflow
      • AILab内で共通で使えるMLflow ClusterをGCP上にGKEで構築
        • ユーザごとにMLflowのPodが立っている。podのurlをユーザに教える。urlにアクセスしたリサーチャーからは個人のMLflowのように見える。
        • アクセス制御はgoogleアカウント単位(Cloud IAP)。共同研究者はgmailをもっていればokなので楽。
        • MLflowのバックエンドDBやオブジェクトストレージは共通のものを指定
          • 区分けはテーブルやprefixで行い、ユーザごとの保存場所を用意
      • 実験バッチからどう、MLflowサーバへログを飛ばすのか
        • リサーチャーがMLflowの使用申請をailab-mlflow管理者に出す。管理者は新しいMLflow podを立ち上げる。
        • リサーチャーは予め持っているクライアントidをキーに、MLflowへアクセスするためのOAuth Tokenを発行する
        • 環境変数MLFLOW_TRACKING_TOKENにTokenをセットすると、MLflowクライアントはこれをつかって、MLFlowサーバにアクセスしてくれる。
        • あとは、普通のMLflowの使い方と一緒
      • 構築方法はAIlabのテックブログにて公開予定

おわりに

  • 今後の課題
    • 論文執筆後のコード、デモページのジェネレータの開発
  • ツールをリサーチャーにどう布教させるか
    • ツールのチュートリアルを開催
    • 地道に一人ひとり使用者を増やしていく
    • 共著で入るときに、こっそりツールが使える環境を用意してしまう
  • 研究の仕組み全体を支援するpipelineよりも、要所要所で使えるモジュールを開発をし、リサーチャーに使うモジュールを取捨選択してもらったほうがよい

質疑

  • privateなデータセットの認証周りはどうしているのか
    • private githubで管理しているため、github上で行っている
    • BigQueryからロードする場合は、BQのread権限も必要
  • ailab-model-zooをリサーチャーに使ってもらう時のインセンティブはあるか
    • ない。強いて言うならば、ailab-model-zooで実験負荷を軽減できたり、モデルを公開したことで思わぬバグを発見できたという成功体験をしてもらうこと。
  • ailab-mlflowのインフラ管理は誰が行っているのか
    • 岩崎さん+もう1名
  • ailabの研究成果をCA社のプロダクト内で使用することはあるか。使用するならば、どこの部隊がMLコードを運用管理するか。
    • 使用する。プロダクト側が実運用を担当する。理由は、AIlabの目的が研究なので、プロダクトの目的(売上を伸ばす)とギャップがあるため。

個人メモ

  • 基盤の設計。あまり意識されない部分にしっかり生産性を上げる機能を提供しているという印象でした。
    • 環境と成果の切り分け
      • 実験は個人専用の環境で、成果は全員が使えるようになっていて、研究者のニーズが考慮されています。
    • 計算資源のスケールが容易
    • 共同研究に対応した基盤
      • 外部の人がnotebookにアクセスできるようにしているのは便利。
  • 実験ツールの標準化 と 研究の自由 のトレードオフ
    • 岩崎さんは以前会社ブログやPyConJPで『小さく始めて大きく育てるMLOps2020』 https://cyberagent.ai/blog/research/12898/という発表をしていましたが、私はこれを読んだ当時これに沿って社内の実験手順を統一しているのかと思ってました。しかし、実際には研究者の個人のやりかたは自由ということでした。
    • 社内ツールを開発しても、使ってもらえないということは私にもあるので、いかに相手に使用を強制させずに普及させていくかは参考になりました。
  • プロダクト側への実験成果物(モデルなど)の引き継ぎはどのように行っているのだろう
    • 関数I/Oは共通化できるものの、内部ロジックはリサーチャーが実装しているはず。そこはおそらく実験のコードのままだと思うのでプロダクト側にわたすとき、どう品質を担保するのか、誰が今後メンテを行うのかというのは気になりました。

スポンサードリンク


2人目 『サイバーエージェントにおけるMLOpsに関する取り組み

スピーカー: 芝田 将(Masashi Shibata)

概要: サイバーエージェント社内の3つのプロダクトのMLシステム構成を紹介しながら、そのプロダクトで行ってきたMLOps周りの取り組みをお話します。

スライド https://www.slideshare.net/c-bata/mlops-248545368

動画 https://www.youtube.com/watch?v=oluIfLSsq8w

芝田さんは社内の(高度な)技術サポートの仕事を行っており、今回は社内プロダクトの改良やトラブルシューティングに関して3点報告されました。

  1. DynalystにおけるMLモデルの推論の高速化
  2. AirTrackにおけるMLモデル学習の高速化
  3. AI Messanger Voicebotにおいてシステムが固まる問題の調査

1. Dynalystにおける機械学習

  • Dynalyst:CA社のプロダクト。スマートフォンアプリに特化したダイナミックリターゲティング広告をしてくれるDemand Side Platform。
    • スマホユーザの行動ログを元に、提示する広告を決める。ユーザに広告を提示した時、コンバージョン(広告主の期待する行動をとること; 商品を購買、アプリのインストールなど)する確率を予測するのにMLを使っている。
    • 使用しているCVR予測アルゴリズム: field-aware Factorization Machine
  • C++実装でCLIのみを提供している。 そのままだとPythonで実行できない。サードパーティpythonバインディングもあるが、自前でpythonバインディングを実装した。
    • 自前のpythonバインディング 理由1
      • libffmに予測性能を上げるためのパッチを当てたいため。具体的には損失関数をカスタマイズしたかった。
      • カスタマイズすると、サードパーティpythonバインディングが想定するlibffmと違ってしまう。そのため、自前でバインディングを実装する必要がある。
    • 自前のpythonバインディング 理由2
      • 予測速度の高速化したかった。
      • ユーザに提示する広告をリアルタイムに決める。リアルタイム性が求められる。広告の表示のレスポンスタイム(100ms以内)。

以下では高速化したlibffmのpythonバインディングを開発した際のtipsを説明する。

Cythonによる推論サーバの高速化

  • Cython
    • Pythonライクなコードを書くとCの最適化したコードを生成してくれるプログラミング言語
      • Pythonのスーパセットなので、CythonのコードとPythonのコードが混ざっていてもいい
    • 変数に明示的にデータ型を与えるほど高速化する
      • 手書きのCコードより高速になることも珍しくないとのこと
      • コードアノテーションが用意されており、ハイライトされている場所(Python/C APIを読んでしまってる遅い箇所)をCythonのコードに書き直すことで処理が速くなっていく。
    • C/C++Pythonのインタフェースとしても利用可能で、バインディングを作る際にも使える
  • CythonにおけるGILの解放
    • C言語のコード部分(Python/C APIを読んでいない部分)はGILの解放が可能で、マルチスレッドの恩恵を受けられる。
  • 安全性を犠牲にした高速化もおこなっている
    • CとPythonで異なる部分に対してPython/C APIがチェックが呼ばれてしまう
      • 例えばゼロ除算チェック、配列に負値のインデックス-1を与えることなど
    • 高速化のために、Compiler directivesの設定でチェックを切る
  • 推論処理をCythonに直した効果
    • 推論時間が改善前の10%に短縮
    • 推論サーバの全体のレスポンスが元の60%に短縮
    • スループットが1.35倍なったため、稼働させておくサーバを減らしコスト削減にもつながる。

LIBFFMバインディングの実装 参照カウントとNumPy C-API

Pythonコードの高速化をしたいだけならば、Cythonに書き直せばOK。これはお手軽。しかし、Cythonを使ってPythonバインディングを作るにはCython, C/C++の深い理解が必要。その一例としてメモリ管理にまつわる話をする。

  • Cライブラリをラップするには
    • Cython側から参照したいCの関数や構造体の宣言をする
    • 関数の引数に与えるオブジェクトを生成する
      • やってることは構造体のメモリ領域の確保
    • 関数の呼び出し
    • 使用したメモリ領域の解放
  • Pythonと連動したメモリ管理がしたい
    • Cで確保したメモリ領域をPythonで触りたいというケースがある。
      • libffmの例だと、メモリ領域内にモデルの重みがあり、その重みで推論がしたい。
    • その場合、Cでメモリ領域確保→Cythonのラッパーがメモリ領域をラップしPythonで参照できるようにするnumpyオブジェクトにする
    • 問題なのは、numpyオブジェクトを破棄しても、cで定義したメモリ領域は自動的には解放されない(Cにはガーベージコレクタがない)
    • したがって自分で開放する必要があるが面倒。
    • numpyオブジェクトを破棄したら連動してメモリ領域が解放されるようにしたい。
  • CPythonのメモリ管理機構は参照カウント
    • 参照カウントが0になるとPythonオブジェクトのメモリ領域は解放される
  • どうやって連動させるか
    • ラップしたNumpy配列に対してBaseObjectを指定する
      • このBaseObjectは、Numpy配列の実体となるCの配列のポインタを所有するオブジェクト。雛形のクラスには__dealloc__メソッドを定義し、このクラスのオブジェクトが破棄されたときは、実体となるCの配列のメモリ領域を解放する処理を書く。
    • これにより、Numpy配列が破棄されると、連動してBaseObjectも破棄され、BaseObjectが破棄されるときにCの領域が解放される。
  • 以上のように、Cで確保したメモリ領域をPythonで触れるようにするのは一苦労。pythonで操作する必要がない (Cython内でメモリ領域の操作が閉じている場合は型付きメモリビューを使うことを推奨する。

2. AirTrackの事例紹介 OptunaとMLflowを使ったハイパーパラメータ最適化の転移学習

  • AirTrackとは
    • 位置情報を活用した来店計測や広告配信を行うCA社製SDK
    • ユーザやエリアの属性推定、店舗への来店予測に機械学習技術を利用
  • MLモデル学習のパイプライン
    • メトリクス、アーティファクトの管理にMLflowを利用
    • ハイパーパラメータの探索にはOptunaを使用
      • 学習バッチを実行するごとに毎回ハイパパラメータも探索しているとのこと
      • この時、ハイパラ探索の工夫をしている。それについて説明する。

Optunaの基礎知識とWarm Starting CMA-ES

  • Optunaの強み
    • 使える探索アルゴリズムが豊富
      • ただし、芝田さんの印象では殆どのユーザはデフォルトのアルゴリズムしか使っていないと思っている
  • Optunaのデフォルトのアルゴリズム: 単変量TPE
    • 欠点: ハイパラ間の相関関係を考慮しない
  • Optunaで使える、相関関係を考慮した探索アルゴリズム
    • 多変量TPE
    • CMA Evolution Strategy(CMA-ES)
      • AirTrackのモデル学習で使用
  • Warm Starting CMA-ES でハイパラ探索を高速化

3. AI Messanger Voicebotの事例紹介 軽量スレッドとWebSocket

  • AI Messanger Voicebot
    • AIによる電話自動応対サービス
  • システム構成
    • twillioを経由して、websocketで音声データをやり取り
    • エンドポイントとなる部分(WebSocketサーバ)が送られてきた音声を
      1. Google CloudのSpeech-to-Textで音声認識し、
      2. 音声認識結果を元に対話エンジンが応答内容を生成し、
      3. Google Cloud Text-to-Speechで応答内容を音声合成する
  • トラブル
    • 開発初期、WebSocketサーバ(Flaskで実装)が特定の条件下で固まって動かなくなるという相談が芝田さんに寄せられたので原因を調査した

WSGIと軽量スレッド

  • PythonでWebsocketを扱うことについて
    • WSGIに則ったデータのやり取りだと、そのままでは不可能。
    • WSGIであってもwebsocketで通信するには何らかの工夫が必要
      • 工夫の例 WSGIのcallableなオブジェクトにWebsocketオブジェクトをわたす
        • Flask-socketsを用いると、このようにしてWebsocket通信ができるようになる。
  • 工夫してWebsockets通信を行おうとする方式の問題点(WSGIの制限について)
    • WSGIアプリケーションを呼び出したスレッドは、そのアプリケーションの処理が終わるまで別の処理を行えないこと。Websocketのコネクションが張られているうちは処理を離せない。
    • 対応策としてはマルチスレッドが考えられるが、OSが管理するスレッド(threading.Thread)を使うのは避けたい
      • 理由はパフォーマンスが悪いため
        1. コンテキストスイッチが重たい
          • スレッドの切り替わりのタイミングで、レジスタの内容をメモリにダンプし別のスレッドの状態をメモリからレジスタに読み出して処理を再開する。切り替える度にこれが発生してしまう。
        2. スレッドのスタックサイズが大きく(2MBなど)、1つスレッドを作るだけでもコストがかかる
      • したがって、user landで動作するスレッドのようなもの(軽量スレッド)を使ってマルチスレッドを実現する
        • (個人メモ: アプリケーション側で生成した仮想的なスレッドだから生成が早いということだろうか)
    • 先述のFlask-socketsはGevent-websocketを内部で使用しており、これが軽量スレッドを用いたマルチスレッド処理を実現してくれる

Gevent-websocketの仕組み

  • Geventのmonkeyというモジュール
    • monkey.patch_all()を 呼び出すとPythonの標準ライブラリに対してモンキーパッチを当てる。通常ならばスレッドをブロックするような操作をGevent用のものに置き換える。置き換えると軽量スレッドベースで動作させることができる。
    • この状態でthreading.Thread(本来は、OS経由でのスレッド生成)を呼び出すと、内部的にはGeventの軽量スレッドを生成する操作(gevent.Greenlet)に置き換わっており、シングルスレッドでありながら仮想的に並列処理を行うことができる。
    • Flask-socketsはGevent-websocketを利用して1つのスレッドで複数のWebSocketのコネクションをさばいている
  • ただし、monkey.patch_all()が置換できないものがコードの中に含まれていて、そこを呼び出してしまうとブロックが発生。 Gevent(イベント駆動)のイベントループがそこで止まってしまうためプログラム全体が固まってしまう
    • 置換できないコードの例としては、サードパーティが独自実装した通信部分など。
    • CA社の例だと、GCPのText-to-SpeechAPIのSDKが内部でgRPC双方向ストリーミングを呼び出してブロックが発生
      • gRPC通信部分はC++実装のため、Geventが置き換えられなかった。
  • 【余談】ASGI(Asynchronous Server Gateway Interface)
    • 上の問題は同期処理を想定しているWSGIで無理やりWebsocket通信と非同期処理をやろうとしたことが起因
    • WebSocketもサポートできるような非同期インタフェースの仕様を策定(中)

質疑

  • Cythonのコードアノテーションのハイライト(Python/C API呼び出し)部分はどうやっているのか
    • コードアノテーションはCythonが提供している機能。コンパイルコマンドにオプションをつけるとコードハイライトしたhtmlファイルを吐いてくれる。
  • メモリ確保したい時、PyMem_Mallocで確保するのとC++のスマートポインタで確保するのでは何が違うか
    • メモリをどこで管理したいかの違い。PyMem_MallocはCythonでメモリ管理したい場合に使う。
  • (コメント) Cythonを使った実装のパートについて、話は理解できるけど自分で実装できる気がしない
    • CA社のMLエンジニアもCythonに苦手意識がある。Cythonは実際難しい。デバッグ時オブジェクトファイルにデバッグコード埋め込んでgdb起動しないといけなかったり、ハマると大変。そのあたりは頑張ってください。
  • Optunaに関して、ハイパラ探索時、過去のbestハイパラより悪くなることはないか。
    • 悪くなる場合がある。それを見越して、探索パラメータ候補に過去のbestハイパラを追加しておくことで精度の下界を保証する。
  • Warm Starting CMA-ESによって、どれだけ学習効率が上がったか
    • オフライン性能検証の結果では、単変量TPEに比べて評価回数が半減した
      • 単変量TPEだと100回評価してたどり着けるハイパパラメータがあるとして、Warm Starting CMA-ESだと50回でたどり着ける。
  • SageMakerではなくMLflowを使っている理由は
    • 自分はサポートとしてぱっと入っただけで、プロダクトの設計には関わっていない。したがってMLflowを選定した細かい背景はわからない。
    • 他のプロダクトでシステム設計のディスカッションをした時の話になるが、MLflowがむかないケースが結構あると思っている。メトリクス集めたい時、MLflowのTracking APIを使うせいで柔軟性が減ることがある。
  • AI Messanger Voicebotに関してpythonで非同期処理を扱うのは大変そうだが、何でPythonで実装したのか。
    • あくまで推察だが、対話エンジンがPythonで実装されている手前、Pythonで統一しないとシステムの複雑さがあがるからというのがあったのではないかと思う。
      • また、Webscocket通信周りを実装していたのもMLエンジニアだったというのもあるかもしれない。
    • 自分がやるとすれば、Websocketの部分はPythonより得意な言語、例えばGo langを使うだろう
      • Goにはgoroutinesという優れた軽量スレッドの実装がある

個人メモ

  • Dynalystの事例紹介に関して
    • 速度要件の求められるアドテクで、Pythonを使うことはかなりディスアドバンテージのはず。そこを技術でカバーしたのはすごい。
    • Pythonバインディング書ける人は尊敬します2
  • AirTrackの事例紹介に関して
    • 岩崎さんの方はGCPを利用していたが、こちらはAWSを利用しているようだ。どういう基準でクラウドベンダを選定しているのだろう。
    • アーキテクチャを見ると、Client(エンドユーザ)がMLflow serverにリクエストを送っているように見える。推論サーバとしてMLflowサーバが使えるのだろうかと気になりました。
  • AI Messanger Voicebotの事例紹介に関して
    • 問題は複雑。
      • 強いエンジニアが高度な技術的課題を解決して現場を救ったという見方はできるものの、最下流で尻ぬぐいした感じもする。もう少し設計段階で芝田さんが加わっていた方がトラブル解決にかかった工数が少なかったかもしれない。
        • とはいえ、GeventとgRPCの相性が起因であると予見するのはさすがに難しいか。
      • そもそもの問題はPythonでプロダクトを開発しようとしたことだが、MLエンジニアがプロダクトを全部設計しようとすると技術選定がPython中心になってしまうのは仕方がない。
        • 別にMLエンジニアもスキル不足というわけではないだろう。MLエンジニアはGoも実装できるようにならないといけないかというと違うわけで。
      • 未来に同じようなことが起きたときにまた強いエンジニアが必要になってしまう事態をどう避けるか。結論出ない。MLOpsの責任境界問題の議論のケーススタディとしては面白い。
  • その他
    • 内容が高度だった。発表内容を理解するために、数多くの参考文献にあたる必要があったが、いい勉強になった。
    • 岩崎さんはAI Labはプロダクト部隊ではないと言っていたが、今回の芝田さんの取り組みはどのような位置づけなのだろう
    • スペルを大文字含めて正確に書いていて丁寧なスライド。
      • 私は面倒なので、pythonのように 表記の正確さを若干サボって書くのですが、スライドを見る限り、全て正確だったので驚きました。

おわりに

最後になりますが、改めてPyData.Tokyoの運営の皆様、会をアレンジしていただきありがとうございました。
おかげ様でまた1つ勉強になりました。

参考文献


スポンサードリンク


  1. MLOpsという名前が定着していなかった頃だったので、SysMLというカテゴリ名でした。

  2. 私はバインディングを書けないので過去にsubprocessでrubyバインディングを実行するというなんちゃってPythonラッパーを作っていました…

初心者向けOSINT CTF『Cyber Detective CTF』のwrite-up

f:id:meow_memow:20210430232146j:plain

目次

はじめに

Cyber Detective CTFはOSINT系のCTFです。すなわち、公開情報やWebサービスを駆使してフラグを見つけるタイプのCTFです。このCTFが開催されたのは2020年ですが、常設の状態で公開されているため、現在でも解くことができます。

私は下記のブログにて初心者向けのCTFとして紹介されていた記事を読んだことをきっかけに、2ヶ月間ほど取り組んでいました。

『オススメの初級者向けCTF - 好奇心の足跡』 https://tech.kusuwada.com/entry/2020/12/02/065100

「初心者向け」と紹介されているだけあって、OSINTという言葉自体知らなくても、自力で解けるため、解いていて楽しいです。そのため、可能な限り自力で解いていただきたいです。しかし、中にはどうあがいても解けない問題もあるかもしれません1。 したがって「どうしてもわからなかった時用」という立ち位置でwrite-upを公開します。

以下よりwrite-upの内容ですが、留意事項が1点あります。解答のテキストを画像化しています
理由はOSINT問はWeb検索を多用するためです。この記事が公開されたことで、答えがWeb検索でヒットしてしまうと、後にこのCTFを解く方々の楽しみが薄れてしまいます。
したがって、そうなることを回避するために画像にしています。そのためリンクが踏めません。また、画像は自分でスクリプトを書いて生成したため、60文字で改行しています。そのため変な折返しが発生しており読みにくい部分もあるかもしれません。ご不便をおかけしますが、予めご了承ください。


スポンサードリンク

write-up

Life Online

Twitterアカウントへのインテリジェンスを行ってフラグを取得する問題です。

f:id:meow_memow:20210430234356j:plain

f:id:meow_memow:20210430234402j:plain

Evidence Investigation

様々なOSINTを行う問題です。

f:id:meow_memow:20210430234417j:plain

f:id:meow_memow:20210430234423j:plain

General Knowledge

知識問題です。知っているか否かなのでサクサク説明します。

f:id:meow_memow:20210430234440j:plain

おわりに

Cyber Detective CTFのwrite-upを共有しました。
「初心者向け」として紹介されていた通り、知識ゼロからでも始められる点がよいですね。
これを足がかりに、その他のOSINT系CTFも解いていきたいです。

姉妹記事の紹介

同内容は、勉強会にてスライド形式で発表いたしました。上で解けていない問題も解く過程を紹介しています。

初心者向けOSINT CTF! Cyber Detective CTFに挑んだ!/cyber_detective_ctf - Speaker Deck


  1. 私も解けない問題が2問ありました。

PyTorchのnn.ConvTranspose2dに与えるパラメータは畳み込みから逆算して考える

f:id:meow_memow:20201229144751j:plain

(畳み込みの画像はこちらのもの)

この記事では、転置畳み込み層のPyTorch実装であるnn.ConvTranspose2dの出力サイズを自分が狙った通りに生成できるように、パラメータを与える知見を共有する。

画像を生成するDNNモデルにおいてアップサンプリングは不可欠な要素である。アップサンプリングを行う時は転置畳み込み層が用いられることが多い。PyTorchで2次元の転置畳み込み層を扱いたい時はnn.ConvTranspose2dを使う。
しかし、いざnn.ConvTranspose2dを使って自分で画像生成のモデリングをしようとした時、公式ドキュメントを見ても引数の値に何を設定すればわからなかった。正確に言うと、動かして処理の内容を推測しようとしたが、挙動が意味不明だった。例えば出力の特徴マップの幅を増やしたいのでpaddingを増やすと逆に幅が小さくなるなどである。
そこで、調べ物をして、どうパラメータを設定すれば想定サイズの特徴マップを生成できるか調査した。

結論としては「畳み込みの逆問題を解く」なのだが、そこに至るまでの過程を省略して理解するのはおそらく難しいので、下記で導入を交えながら説明をする。


スポンサードリンク

導入: 畳み込み層における逆伝播

調査の結果、パラメータを想定通りに設定するには、畳み込み層の逆伝播で何が行われているかの理解が必要であるという結論に至った。 その根拠としては、参考文献[2]によると、TensorFlowに関して、畳み込み層の逆伝播で転置畳み込み層が使われていることを述べている。PyTorchで同様の実装が行われているかはわからなかったが、転置畳み込み層クラスの引数の意味は共通している。

「畳み込み層の逆伝播で転置畳み込みが行われる」とはどういうことか、例を交えて説明する。

畳み込み層の順伝播

例えば下図のようなCNNの一部があったとして、ある畳み込み層Conv1に着目する。このConv1はConv0の特徴マップを入力として受け取る。また、Conv1の出力の特徴マップはConv2へ渡すとする。

f:id:meow_memow:20201229145612j:plain

順伝播ではご存知の通り、特徴マップXカーネルFで畳み込みの計算を行い、新たな特徴マップOを生成するという処理である。 このConv1では、Fが2x2の時、padding=0, stride=1とする。 簡単のためにchannelはinput,outputともに1とし、biasはなく、活性化関数もないとする。Xのサイズが3x3のとき、Oのサイズは2x2となる

f:id:meow_memow:20201229150108j:plain

畳み込み層の逆伝播

f:id:meow_memow:20201229150345j:plain

次に、この畳み込み層の逆伝播を考える。
逆伝播での勾配計算というと、重み(畳み込み層ではカーネルとバイアス)に対する勾配をまずイメージするかもしれない。 しかし、勾配は重みに対してだけでなく、入力の特徴マップに対しても求める必要がある。何故ならば、Conv1にとっての畳み込み層の入力の特徴マップの勾配\frac{\partial  L}{\partial X}は、1つ手前の畳み込み層Conv0にとっての局所的な誤差として逆伝播するからである。 この、入力の特徴マップの勾配\frac{\partial  L}{\partial X}の求め方を簡単に説明する。詳しくは文献[1],[2]を参照いただきたい。

上図のように、逆伝播時はConv2層から誤差\frac{\partial  L}{\partial  O}が入ってくる。

ここで、Xの最小要素であるピクセル1X _ iが誤差Lに及ぼした影響\frac{\partial  L}{\partial X _ i}は、多変数関数の連鎖律[5]より、

\frac{\partial  L}{\partial X _ i} = \sum_{k} \frac{\partial  L}{\partial  O _ k} \cdot \frac{\partial  O _ k}{\partial  X _ i}

と計算できる。

この内、右辺の\frac{\partial  L}{\partial  O _ k} は、Conv2からの逆伝播した勾配の各ピクセルである。
また、右辺の\frac{\partial O _ k}{\partial X _ i}は、O _ kの式をX _ i偏微分したものである。
O _ kは次の式で表される。

したがって、これらを例えばX _ 1偏微分し、元の式に代入すると、

[tex: \begin{align} \frac{\partial L}{\partial X _ 1} &= \sum_{k} \frac{\partial L}{\partial O _ k} \cdot \frac{\partial O _ k}{\partial X _ 1} \ &= F _ 1 \cdot O _ 1 + 0 \cdot O _ 2 + 0 \cdot O _ 3 + 0 \cdot O _ 4 \ &= F _ 1 O _ 1 \end{align}

これを\frac{\partial  L}{\partial X _ 9} まで 求めて、shapeを整えたものが、\frac{\partial  L}{\partial X}である。

畳み込み計算で表現

この勾配\frac{\partial  L}{\partial X _ i}を求める過程は、実は畳み込み計算で表現できる。

f:id:meow_memow:20201229153537j:plain

\frac{\partial O}{\partial X _ i}が特定のフィルタor0であり、これが\frac{\partial  L}{\partial  O}の各ピクセルと掛け合わされることから大まかに把握できるかと思う。微分して0になる場所は、入力\frac{\partial  L}{\partial  O}に0をpaddingすることで対応する。
畳み込み計算と1つ違う点としては、フィルタを180°回転させることである(実際の行列計算ではフィルタの行列を転置する操作にあたる[4])。

畳み込みをしながら\frac{\partial  L}{\partial X _ i}を求めていくイメージを下図に示す

\frac{\partial  L}{\partial X _ 1}は、

\frac{\partial  L}{\partial X _ 2}は、

最後までフィルターをスライドすると、\frac{\partial  L}{\partial X _ 9} まで求まる。

このように、入力の特徴マップに対する勾配\frac{\partial  L}{\partial X}を求める上で、転置畳み込みが使用される。

パラメータの謎の解明

ここで冒頭、nn.ConvTranspose2dのパラメータ名と処理の整合性が合っていないと思った話に戻る。

実はnn.ConvTranspose2dの引数のpadding,strideは、Convの順伝播のパラメータを与えなければいけないのである。
この理由は、TransConvの入力としてConvの出力の勾配(例だと\frac{\partial  L}{\partial  O})がくることを想定しているためだと考えられる。
もともとは勾配を求める用途のものを、(Convの文脈とは独立して)単なるアップサンプリング用途に使おうとしているから私は挙動を理解できなかったのである。

パラメータをどう指定するのか

seq2seq型のモデルだったらencoderのConv層のパラメータがあると思うので、decoder側のnn.ConvTranspose2dのパラメータにはそれを与えてやればよい。
しかし、seq2seqモデルではない場合などで、アップサンプリングにnn.ConvTranspose2dを使いたい場合、出力の特徴マップを所要のサイズにするにはパラメータをどう与えるか。

公式ドキュメント[3]によると、サイズは関して下記の計算式で求まると書かれている2
H _ {out} = (H _ {in} −1)×stride[0 −2×padding[0]+dilation[0]×(kernel_size[0]−1)+output_padding[0]+1]

しかし、これを覚えるのは面倒である。

したがって、このH_outの式を覚えるよりも、畳み込みの逆問題を解いた方が早いと私は考える。
例えば、3x3の特徴マップを5x5にアップサンプリングしたい場合、逆問題として5x5の特徴マップを畳み込みで3x3にするにはどうすればいいかを考える。
答えの一例としては、カーネルを2x2、stride=2, padding=0にすればよいので、これをnn.ConvTranspose2dのパラメータに与えるといった具合である。

まとめ

nn.ConvTranspose2dで所要の出力サイズにするためのパラメータ設定方法に関して述べた。

調べ物をする中で、引数と挙動の謎が解けた。文献[1][2]にかかれている通り、畳み込みの逆伝播さえつかめれば転置畳み込みは理解できることがわかった。

逆に考えると、転置畳み込みを理解していないということは、畳み込みも理解していない、ということになる。

参考文献

ふろく: デバッグ用のコード

一応、nn.ConvTranspose2dをかけた時のshapeを確認するためのコードを用意した。


  1. 特徴マップの最小要素を正式には何と呼ぶのかわからなかった。間違っているかもしれないが、本稿ではピクセルと呼ぶことにした。

  2. これはHeightの例であるが、Widthも同様の計算が行われる。