PyTorch で GPU 並列をちょっと詳しく

要約

PyTorch でマルチ GPU してみたけど,色々ハマったので記録に残しておく.データ並列もモデル並列(?)もやった.

メインターゲット

  • PyTorch ユーザ
  • GPU 並列したい人

前提知識

.

並列化したコード

どのようなコードを並列化したのかの説明を軽くしておく.必要なければ読み飛ばしてもらって構わない.

この論文 の自前実装.テキストを入力に画像を生成するモデル.テキスト→オブジェクト位置→オブジェクトの形→画像 の順に予測・生成していく. このモデルを訓練するスクリプトを並列化した.

モデル

主なコンポーネント
Component input output
box_generator テキスト オブジェクトの位置とラベルのペア(以後オブジェクト)の列
shape_generator テキスト,オブジェクト列 オブジェクトの形の列
image_generator テキスト,オブジェクトの形の列を一つの画像にまとめたやつ(セマンティックレイアウト) 画像
補助的なコンポーネント
Component input output
text_encoder (pretrained) テキスト(単語列) テキストベクトル
shape_discriminator テキスト,オブジェクトの形の列 そのオブジェクトが正例か shape_generator が生成したものかの 2 値
image_discriminator テキスト,画像 その画像が正例か image_generator が生成したものかの 2 値
vgg_model (pretrained) オブジェクトの形 or 画像 入力画像の内部表現(本来は画像分類モデルだが,今回は内部表現を利用する)
モデル図

f:id:ensyu3-141592653589793238:20190603172452p:plain
Hong et al., CVPR2018

特筆事項
  • box_generator は RNN な構造を持つ.shape_generator は RNN な構造を持つし,CNN も持つ.
  • vgg_model は vgg19 を利用.結構でかい.CNN.

.

並列化1:DataParallel

1 バッチを各 GPU に割り振る.例えば 4 GPU で DataParallel でバッチサイズ 256 のを回すと,一つの GPU がバッチサイズ 64 のバッチを処理する.

実装方法

PyTorch には DataParallel モジュールが用意されている.使い方はめっちゃ簡単. Document

device_ids = range(torch.cuda.device_vount())
model = DataParallel(model, device_ids=device_ids)

DataParallel が何をやってるかをちょっと詳しく

discuss.pytorch.org

同期更新型のデータ並列.以降,勾配計算のみを行う複数個の GPU を子 GPU,勾配の集約を行う唯一の GPU を親 GPU と言う. ※親 GPU 自身も勾配計算を行う.

1. Forward
  1. ミニバッチを各 GPU に均等に割り当てる.
  2. GPU に乗っているモデルのパラメータ情報等を各子 GPU にコピーする.
  3. GPU にて Forward プロセスを行う.
  4. Forward 結果を親 GPU に集約する.
2. 親 GPU にてロスを計算
3. Backward
  1. ロスを各 GPU に割り当てる.
  2. GPU にて Backward プロセスを行う.
  3. Backward 結果の勾配情報を親 GPU に集約する.
4. 親 GPU のモデルパラメータを更新

DataParallel に向かない設定

パラメータの多いモデル

イテレーションごとにモデルのコピーが発生するので,パラメータの多いモデルはコピーのオーバーヘッドが大きく,DataParallel の恩恵を受けられない.全結合層だけで構成されるモデルとかがその例.

基本的に畳み込み層で構成されていて,最終層だけ全結合層とかの場合は,全結合層を DataParallel の対象から外すというハックを行うことで大幅な高速化が期待できるそう.参考コード

自分でも全結合層を DataParallel の対象から外す処理を試してみた.イテレーションが 42秒から 40秒になるという,微妙な差.

小さいバッチサイズ

バッチサイズが小さくても,パラメータのコピーに対して Forward/Backward の計算が少なくなるので,DataParallel に向かない.十分なバッチサイズが確保できるよう,大きすぎないモデル設計を心がけるか,とてもメモリの大きい GPU を用意するかしよう.

RNN(特に LSTM)

DataParallel するには,batchFirst である必要がある.その他,色々とエラーが出て闇が深そう. さらに,LSTM はセルが複雑なことをするので速くならないらしい.説明をめちゃくちゃ端折ったので,興味がある人はこちらの議論を見てください.

.

並列化2:モデル並列 & multiprocessing

今回並列化したモデルは box_generator/shape_generator and shape_discriminator/image_generator and image_discriminator の 3 つに計算グラフを完全に分離できる.したがって,これらを別々の GPU に乗せたうえで各 GPUイテレーションを並列実行することが割と簡単にできる. 計算グラフが切れないガチのモデル並列は一つ下のレイヤからいじる必要があって闇が深いので,この世のどんな GPU にも乗り切らないめっさでかいモデルを絶対に動かさなきゃいけない事情がない限り避けたほうがいいと思う.

実装方法

1. 各モデルを別々の GPU に載せる

以下のようにすれば良い.

device0 = torch.device("cuda:0")
device1 = torch.device("cuda:1")
device2 = torch.device("cuda:2")

model.box_generator.to(device0)
model.shape_generator.to(device1)
model.image_generator.to(device2)

box_input_tensor = box_input_tensor.to(device0)
shape_input_tensor = shape_input_tensor.to(device1)
image_input_tensor = image_input_tensor.to(device2)

ただし,全てのテンソルをちゃんと正しい GPU に割り当てる必要があるので,内部で .cuda() とかで雑に初期化していると別々の GPU にあるテンソルで演算しようとしてんじゃねぇよエラーが出る.頑張って修正しよう.

2. multiprocessing

モデルを別々の GPU に乗せただけでは並列に実行されないので,並列に実行するために multiprocessing する必要がある.Python の multiprocessing の PyTroch ラッパーがあるので,それを使う.

Document にサンプル実装がある.訓練フェーズを関数化して,multiprocessing.Process に渡せば,その訓練が fork されて独立に動き出す.

CUDA initialization error が発生する場合は,mp.set_start_method('spawn')torch.manual_seed(args.seed) の前で宣言すると回避できる.参考

spawn は子プロセスを開始する方式の一種で,指定しない場合は多分 fork だった気がする.デフォルトでは子プロセスの中でも CUDA の初期化を行ってしまうせいでエラーが発生するらしい.(CUDA の初期化は 1 回しかしちゃダメ)

spawn はプロセスを一度 Pickle 漬けにするので,lambda などのPickle 漬けできない書き方を解消する必要がある.また,Pickle 漬け処理が重たいので,遅い.悲しい.

.

2 種類の並列化を試した結果と感想

どちらも lab のサーバだとそんなに速くならなくて悲しかった.それぞれ,「batch_size が小さすぎる」「spwan が遅すぎる」というネックがあり,それを解消できなかった. ABCI で試したところ,DataParallel で 1.3 倍くらいには速くなったので,それでやっていくことにした.

PyTorch の DataParallel は基本的に CV 系のモデルを想定していて,NLP 系のモデルに向いていないのが悲しかった.使う分には楽なので,使えるところで局所的に使うのが賢そう. multiprocessing はそもそも PyTorch でそこまでサポートされていなくて,エラー回避が大変だったし,効果が薄かった. DataParallel を(上手く)使うことをオススメする.

.

リファレンス

Hong et al., CVPR2018: S. Hong, D. Yang, J. Choi and H. Lee,``Inferring Semantic Layout for Hierarchical Text-to-Image Synthesis,'' In proc. of CVPR2018. pdf

flask プロジェクトの PyCharm 実行でハマった

PyCharm で flask プロジェクトを作って,PyCharm 上で実行したら,スクリプトで指定している設定が反映されなくて困った.

.

経緯

flask プロジェクトを作成

PyCharm で flask プロジェクトを作った.

pleiades.io

スクリプトを編集

app.py のメイン内で debughost の設定をいじった.

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0")

実行 & 問題発生

PyCharm で app.py を実行した.

FLASK_APP = app.py
FLASK_ENV = development
FLASK_DEBUG = 0
In folder /Users/nomotoeriko/PycharmProjects/untitled
/Users/nomotoeriko/.pyenv/versions/3.5.0/bin/python -m flask run
 * Serving Flask app "app.py"
 * Environment: development
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

明らかに host="0.0.0.0" が反映されていないことがわかる.

.

真相

どうやら app.py を直接実行しているわけではないらしく,メインが呼ばれていなかった.開発環境とかその辺のアレで PyCharm がよしなにやってくれているのかなぁ.知らんけど.

.

対処

真面目な対処法

公式によれば,

Flaskサーバーの実行/デバッグ設定の対応するパラメータを編集することにより、Flask固有の変数を変更することができます。

とのこと.以下のページから,flask run のパラメータに --host があること,Flask デバッグモードの有効化には FLASK_DEBUG にチェックをつければ良いことがわかる.

pleiades.io

ということで,Edit Configurations から設定をいじる. f:id:ensyu3-141592653589793238:20190515002358p:plain

Additional options からホストを設定.FLASK_DEBUG にチェック.

動く ✌︎('ω'✌︎)

FLASK_APP = app.py
FLASK_ENV = development
FLASK_DEBUG = 1
In folder /Users/nomotoeriko/PycharmProjects/untitled
/Users/nomotoeriko/.pyenv/versions/3.5.0/bin/python -m flask run --host 0.0.0.0
 * Serving Flask app "app.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 285-077-691

雑な対処法

PyCharm の Terminal から app.py を叩く.

あとがき

思わぬ落とし穴だった.まぁ公式をよく読みましょうってことですね.でも Hello World までを自動生成してくれるのはさすが JetBrains という感じ.便利.

一緒に原因を考えてくれた I 氏と執筆にご協力してくれた T 氏に深く感謝いたします.

RandomCrop とかを入力画像と正解画像で統一する方法

経緯

PyTorch で画像系の学習をするときに, RandomFlipRandomCrop をよく使う.最近研究関連で,オブジェクト位置,セグメンテーション,画像を順に生成していく既存モデルを構築しており,それらのデータでランダムな部分を統一する必要があった.なかなかいい感じの記事に出会えずどうやるか調べるのに苦労したので結論をまとめておく.

.

想定する読者

  • PyTorch で画像物体検知を実装しようとしている人
  • クラスの継承がわかる人

.

.

RandomCrop とかを入力画像と正解画像で統一する方法

RandomCrop とかを継承して,__call__ だけ上書きする.

from torchvision import transforms
from torchvision.transforms import functional as F


class UnitedRandomCrop(transforms.RandomCrop):

    def __call__(self, *args, **kwargs):
        img, segms, bboxes = args
        i, j, th, tw = self.get_params(img, self.size)
        cropped_img = F.crop(img, i, j, th, tw)
        cropped_segms = [F.crop(segm, i, j, th, tw) for segm in segms]
        cropped_bboxes = [self.crop_bbox(bbox, i, j, th, tw) for bbox in bboxes]
        return cropped_img, cropped_segms, cropped_bboxes

    @staticmethod
    def crop_bbox(bbox, i, j, th, tw):
        x, y, bw, bh = bbox
        start_x = max(0, x - j)
        start_y = max(0, y - i)
        stop_x = min(tw, x + bw - j)
        stop_y = min(th, y + bh - i)
        cropped_bbox = [start_x, start_y, stop_x - start_x, stop_y - start_y]
        return cropped_bbox

.

詳細

get_params でランダムなパラメータを取得している.このパラメータを使って,__call__ 内でデータに必要な変換を記述して返す.基本的には本家の記述に従えば OK.

get_paramsこの issue で導入が検討された関数.オブジェクト検知タスクにおいて,ランダム部分を入力画像と正解画像(セグメンテーション)で統一したいという欲求が発生し,様々な議論がなされていた所,fmassa さんがいい感じにまとめて問題提起を行ったのがこの issue .この議論の結論は RandomCrop とかのクラスで,パラメータ作成と実際の画像の変換を分け,また,画像変換部分は関数として提供することである.これによってユーザが自由にサブクラスや関数を作れるようになるとのこと.

全ての RandomHoge クラスに get_params が実装されているわけではないので,注意が必要.執筆時点(2019/04/08)で get_params メソッドを持つのは RandomCrop RandomResizedCrop ColorJitter RandomRotation RandomAffine.例えば RandomFlip には get_param メソッドがない.確かに単純すぎて分けるほどではない気がする.

.

あとがき

英語の issue ツリーを読むの疲れた.

もしもっといい方法があるならぜひ教えてください!

ディープラーニング分散学習ハッカソン参加報告

経緯

研究室のお金でディープラーニング分散学習ハッカソンに参加してきました.

www.cc.u-tokyo.ac.jp

 

 

想定する読者

  • 深層学習してる人
  • 分散深層学習をやってみようと思っている人

 

 

イベントの概要

要旨:参加者が持ち寄った深層学習のコードを強いメンターたちに助けられながら分散深層学習のコードに書き換えていく.

共催:

  • 東京大学情報基盤センター
  • 株式会社Preferred Networks
  • TensorFlow User Group
  • エヌビディア合同会社
  • PCクラスタコンソーシアム(実用アプリケーション部会)

期間:2019/01/24--2019/01/25 の 2 日間

場所:東京大学情報基盤センター

計算機:情報基盤センターの Reedbush-H

フレームワーク:Chainer か Tensorflow

プログラム:

  1. 主催側からのプレゼン(各組織 20 分ずつ)
  2. 参加者自己紹介
  3. 各自作業
  4. 成果報告

分散深層学習基礎講座

Nvidia さまから 分散学習基礎講座 というタイトルのプレゼンがあったので,その内容を私なりにまとめる.詳細は元スライドを参照のこと.

分散深層学習とは

深層学習を,複数の GPU を使って行うこと.データ並列/モデル並列 や 同期更新/非同期更新,パラメータサーバ方式/P2P方式 などの分類がある.

データ並列とモデル並列

データ並列

モデルをコピーして,パラメータを共有した状態でそれぞれ別のサンプルで訓練する並列化方法.GPU ごとに別々のデータで勾配を計算させるので,モデル更新時にはうまいことパラメータを一致させる必要がある.

同期更新と非同期更新

パラメータの更新方式は同期更新と非同期更新に分けられる.

同期更新 GPU の勾配計算完了を待ってモデルを更新する.GPU 性能に差がある場合無駄が出る.
非同期更新 一部の GPU の勾配計算が終わったらモデルを更新する.後述の Staleness 問題が発生する.

 

パラメータサーバ方式と P2P 方式

GPU 間の通信にはパラメータサーバ方式と P2P 方式がある.

パラメータサーバ方式 パラメータサーバのパラメータを,各 GPU がそれぞれ更新していく.
P2P 方式 ある GPU での更新を他の GPU に伝播させる.

 

モデル並列

一つのモデルを複数の GPU に置く.一つの GPU にはモデルの一部が乗っている状態.各レイヤを GPU に割り当てたり,一つのレイヤを複数の GPU に割り当てたり,いろんな切り方がある.どのような切り方がいいかはモデル依存.

各方式の弱点

データ並列:大規模化に伴う精度低下

一回の重みの更新の時に関わるサンプル数が [バッチサイズ] × [並列数] になるため,並列化しすぎると精度が低下する.

どこまで並列化できるかや,たくさん並列化しても精度を落とさないようにする研究が行われている.

  • 「8k 並列までなら精度落ちない」by 顔本
  • 「32k まではいける」by PFN

ちなみに SGD 的な最適化を行う場合,学習率を下げることとバッチサイズを大きくすることは等価 であるため,学習が進むにつれて学習率を下げる代わりに並列数を大きくするのもアリかもしれない.簡単には実装できなさそうだけれど.

同期更新:GPU の性能差で無駄が発生

パラメータの更新時に全ての GPU での勾配計算が完了するのを待つため,GPU に大きな性能差がある場合に無駄な時間が発生する.

非同期更新:Staleness 問題

非同期更新では,勾配の計算中に他の GPU がパラメータを更新するので,勾配計算に使っていたパラメータが相対的に古くなってしまう.これを Staleness 問題という.Staleness 問題は収束性の悪化の原因となるらしい.

モデル並列:通信コスト

フォワードプロセスでも GPU 間で通信が必要なので,データ並列に比べ通信コストが高い.

私たちがやったこと

弊研究室 からは K 先輩と私の 2 人で参加した.私たちは Chainer を使った seq2seq 機械翻訳モデルのコードに対して ChainerMN でデータ並列の分散化を施した.

資料 の通りに秒速 MN 化を行ったところ,悲しいことにバグが大量発生したので Chainer の中の人に助けられながらその解消を行っていった.なんとか時間ギリギリでエラーの出ない状態になり,並列化でどれくらい速くなったか測ろうとしたところ時間切れとなってしまった.ログを見る限り並列化しないほうが速かったので,多分まだどこかにバグがあるんだと思う.悲しい.

バグ取りを通して得られた知見

重複するエラーメッセージ数

並列数とエラーメッセージの重複数が同じ場合は GPU ID に関係のないところで出ているエラー,異なる場合は GPU ID によってエラーが出る場合と出ない場合があるエラー.私たちの場合は GPU に ID を割り振る操作をコードの中で複数回呼んでしまっていたことで起きていた.

単一 GPU でも同じエラーが出るかどうか

同じコードでも単一 GPU に設定した場合は動く場合,GPU 間の通信時に起きているエラーの可能性が高い.単一 GPU だと通信が行われないのでエラーが発生しない.

GPU に載せるテンソルは to_gpu とかの後に作る

ChainerMN ではデバイスをセットする操作の前後の行列を混ぜると死ぬ.

 

参加しての感想

ChainerMN 開発者をメンターに据えて ChainerMN を使ってみるという贅沢な時間でした.開発者ならではのお話が聞けて楽しかったです.

2 日間とも Nvidia さまがお弁当を提供してくれたり,1 ヶ月間 Reedbush を利用できるという特典も付いているのに参加費は無料という超絶お得なイベントでした.参加してよかったです.

分散深層学習を行うのは初めてだったのですが,難しかったです.私の研究では画像もテキストも扱うので,モデルの大きさによってはバッチサイズを小さくしないと動かないということが起こります.そんな時にデータ並列は [バッチサイズ] × [並列数] が実質のバッチサイズになるので魅力だと思いました.頑張ろうかな.頑張れるかな.

修論で忙しいにも関わらず本記事の最終確認をしてくれた T 氏に感謝いたします. 

Latex で画像位置がずれる問題へ対処した話

経緯

lab メンが某会議への論文投稿でハマっていたのをみんなで解決した.ググってもなかなかドンピシャの解決にたどり着けなかったので記事に残す.

 

想定する読者

  • 論文の投稿を dvi + eps 形式で行うように指定されている人
  • Overleaf のプレビューでは正しいのに,dvi を出力してコンパイルすると画像位置がどうしてもずれてしまう人
  • matplotlib で eps 形式の図を出力して latex に includegraphics しようとしている人

 

発生した問題

ことの経緯を以下にまとめる.

  1. モデル図はパワポで作って eps 形式で保存
  2. グラフは python の matplotlib.pyplot で描画して eps 形式で保存
  3. Overleaf でこれらの eps ファイルを includegraphics 
  4. コンパイラlatex に設定してコンパイル
  5. Overleaf のプレビューで正しくコンパイルできていることを確認
  6. Overleaf のドキュメントに従って dvi 形式でダウンロード
  7. eps ファイルたちと dvi ファイルを一つのフォルダにまとめて zip
  8. 某会議に zip ファイルを提出
  9. 某会議のシステムで pdf にコンパイルされ,その結果がこちらに届く
  10. 届いたものを確認するとグラフの表示位置がずれている

 

似たような状況として以下がある.ただしこちらの解決策は今回通用しなかった.

tex.stackexchange.com

 

私たちが dvi 形式に疎く,また某会議がわのコンパイル環境がわからなかったこともあり,対処が難航した.Overleaf で出ているエラーを解決したり,includegraphics のオプションで重複のある部分を取り除いたり,利用 OS の種類を某会議への提出時に選択するのだが,それを変えてみたり....

 

対処

elehoody.blog.fc2.com

調査の結果,上記と同じことが起きていたっぽいことがわかった.matplotlib でグラフを保存する際 pdf 形式を選択し,ImageMagic で eps に変換することで解決した.

eps にはいろんな情報が含まれているっぽい.今回は一部の余分な情報が悪さをしてて,pdf にすることでそいつを落としたら解決したのかな,と理解した.知らんけど.

 

あとがき

Overleaf の問題なのか,某会議の方の問題なのか,ビューワーの問題なのかがわからなくて苦労した.Overleaf で直接 pdf をダウンロードできるので,私含めみんなが dvi を意識したことがなかったのも足を引っ張った.

私含め 5 人でこの問題に対処したのだが,結果的に解決に繋がらなかった方法も含めて,それぞれが鋭い着想に基づいた方法で lab メンつおいってなった.eps 全てではなく,グラフだけがずれていることに着目した lab メンがこの方法を提案し,それを聞いた私が上の記事を見つけた.ちゃんとした位置に図が配置されている pdf を見たときは歓声が上がった.みんなで課題解決するの楽しい.

ちなみに今回は eps 形式という指定があったからそうしたが,選べるのなら eps 形式ではなく pdf 形式で図を入れたほうが安定だと思う.こことかにそう書いてある.

 

何はともあれ無事投稿できたようで安心.通っているといいな.

 

Amazon Mechanical Turk (MTurk) を使ってみたメモ

経緯

研究の一環で Amazon Mechanical Turk (MTurk) を使う機会があった.日本語のドキュメントや記事が少なく,ところどころ苦戦したので,使用感などをメモっておく.

私が参考にした大変よくまとまった資料を以下に紹介する.MTurk を使うための資料としては本記事よりもこちらを読むことを推奨する.

www.slideshare.net

 

想定する読者

  • MTurk を使う予定の弊 lab 学生
  • クラウドソーシングでデータを集めようと思う人
  • MTurk についてなんでもいいから情報が欲しい人

 

MTurk の概要

MTurk は Amazon が提供する,クラウドソーシングを円滑に行うためのサービスである.MTruk ユーザはワーカかリクエスタどちらかの形態をとる.ワーカは仕事を行う人,リクエスタは仕事を発行する人である.

 

MTurk の利用:ワーカ編

ワーカは仕事(MTurk では1個の仕事を HIT と呼ぶ)を行う.

HIT 選び

サービス上で現在開かれている HIT 一覧を確認することができる.HIT には仕事内容の簡単な説明,報酬,ワーカ条件,制限時間などが定められている.だいたいどれくらいの時間でできる仕事なのかを説明のところに明記しているものが多いので,それで時給換算できる.ワーカ条件とはその HIT に取り組めるワーカの条件で,『US 在住』『今までで最低 50 件の HITs をこなした』などの項目がある.HIT をクリックすることでより詳細な仕事内容を確認することができる.

HIT への取り組み

詳細な HIT 内容を確認した上で,仕事に取り組むボタン(ボタンの名前は忘れた)を押して,実際に仕事を行う.仕事は選択肢から正解をどんどん選んでいくものもあれば,画像の物体位置に矩形とオブジェクトラベルを付与するようなものもある.このような仕事を制限時間内に行い,最後に submit を押すと仕事を行なったことになる.制限時間をすぎると submit できないので,集中して取り組む必要がある.

報酬の受け取り

ワーカが submit した成果は HIT 発行者であるリクエスタによって確認される.リクエスタが承認するとワーカは報酬を得る.否認すると報酬はもらえない.また,HIT 承認率はワーカ条件として指定可能なため,否認が増えると取り組める HIT が減ることになる.仕事には誠実に取り組みましょう.

 

MTurk の利用:リクエスタ編

リクエスタは HIT の発行と提出された成果の承認を行う.

HIT の発行

HIT の発行にあたり定義するべき項目はざっと以下の通りである.

  • HIT のタイトル
  • 仕事内容の簡単な説明
  • キーワード(ワーカがHITを検索する上で利用される)
  • 報酬
  • ワーカ人数
  • 制限時間(緩めに設定しておくことが推奨されている)
  • ワーカ募集期間(もちろん人数に達した時点で募集をやめることにはなる)
  • ワーカの submit から自動承認を行うまでの期間(この期間をすぎると否認できなくなる)
  • ワーカ条件

特に気をつけるべきは『報酬』と『制限時間』と『ワーカ条件』である.以下ではそれぞれについて注意点を述べる.

気をつけるべきポイント:報酬編

報酬は適切に定められることが望ましい.だいたい 5$/1h と風の噂で聞いたため,1 時間程度の HIT だった私は 5$ と設定した.ただ,あくまで時給換算は目安であることに注意すべきである.仕事内容や仕事画面の UX 設計によっては,たとえ同じ拘束時間であってもワーカの感じる仕事量に差が出ることがあり,必ずしも時給換算した報酬が適切とは限らない.ワーカが仕事内容の説明を読んで簡単そうだと思うのなら報酬は安めに設定すべきだし,難しそう/めんどそうだと思うのなら報酬は高めに設定すべきである.

特に,報酬が安すぎると全然ワーカが集まらないといったことが起こる.大変...

(20181116追記)クラウドソーシングにおける賃金設定のガイドラインがあるらしい.Guidelines for Academic Requesters - WeAreDynamo Wiki

気をつけるべきポイント:制限時間編

制限時間は厳守される.そのため,制限時間は『普通にやっていれば絶対に超えない時間』くらいには緩めに設定しておくべきである.私の場合は 1 時間の想定に対し制限時間を 2 時間と設定したにも関わらず,1 名のワーカが制限時間をオーバーしてしまった.3 時間くらいにしておけばよかったかなぁ...

気をつけるべきポイント:ワーカ条件編

ワーカ条件には様々なものが選べる.その中で設定するときに特に注意すべきが『HIT 承認率』である.これはそのワーカが今まで submit した仕事のうちどれくらい承認されたかを表すものである.私は最初これを『greater than 99%』としていた.さて,これを和訳するとどういう意味になるだろうか.答えは『99% より上』である.サービスの実装上,この条件だと,承認率 99.3% のワーカも条件外になってしまう.正しくは『greater than or equal 99%(99%以上)』であるべきだ.こんなところで英語力のなさが露呈した...

提出された成果の承認

リクエスタは成果提出者のワーカIDと実際の成果物を確認し,成果物の質によって承認/否認を決定する.何もしなければ自動的に承認される.成果物の質の定義は様々で,私の場合はダミー問題を用意しそれへの正解率を基準に決定した.

否認することはリクエスタにとってもワーカにとってもリスクとなるため,基本的には行わないことが望ましい.よっぽど成果物の質が悪い場合にのみ否認を選択する.否認するときには否認理由を対象のワーカに通知することができる.ワーカはリクエスタのメールアドレスを見ることができるので,説明不足な否認を行うと抗議文が届いたりして面倒なことになりそう.否認理由はしっかりと書くことが望ましい.

 

その他 MTurk を利用した上で気になったこと

ワーカ-リクエスタ間のやり取りについて

ワーカからリクエスタにはメールを送ることができる.リクエスタからワーカには直接の連絡を取ることができない.リクエスタからワーカに連絡をとる唯一の方法は,成果物を否認するときのメッセージのみである.なんか不公平だよね.

仕事画面の作り方について

仕事画面は MTurk 上で作ることができる.結構様々な機能があるっぽい.ただしアップロードできるデータ量に制限があり,それを超えるような仕事の場合は自前でサイトを作成し,そこへのリンクを飛ばすという方法を取る.この場合 MTurk 上ではサイトから発行する仕事完了コードを受け取るだけになる.完了コードはワーカによって別々のものを発行することが望ましい.

ワーカは MTurk とサイトを行き来することになるので,途中で間違えてページを閉じてしまったりしても大丈夫なように自前サイトにはログイン機能を実装することをおすすめする.また,MTurk 上で受け取る情報はワーカ ID と終了コードのみになるため,成果物の確認をするならば自前サイト側でもワーカ ID をもらう必要があると思う.多分.ワーカ ID は大事なものなので,ちゃんと『もらった情報は研究にしか使いませんよ』的なことを説明書きに書いたり,最低限 https なサイトにしたりする方がいい.

 

あとがき

なかなか執筆時間が取れず,MTurk を使ってから結構時間が経っての投稿となってしまった.そのせいでところどころうろ覚えで,あまり意味のある文章にはならなかったように思われる.悲しい.弊 lab では今回,共同の MTurk リクエスタアカウントを作成し(てもらっ)た.今後この記事が弊 lab 学生の役に立つことを祈る.

 

参考にさせていただいたサイト

Amazon Mechanical Turk - Wikipedia

Amazon Mechanical Turk

実践 Amazon Mechanical Turk

 

レトリバインターン参加報告

経緯

サマーインターンで2ヶ月間,株式会社レトリバ様(以下敬称を略させていただきます)にお邪魔し,製品開発の職業体験をさせていただいたのでその記録です.

retrieva.jp

 

想定する読者

  • 来年以降インターン時期になるNLPer
  • レトリバに興味がある人
  • レトリバの人

 

 

選考

インターンに参加しようと思ったきっかけ

弊学ではインターン参加が修士の単位として認められています.毎年多くの M1 が夏休みに 2週間~2ヶ月間 のインターンに参加します.私も夏休みにどこかのインターンに参加することにしておりました.

インターンに参加するには(基本的に)受け入れ機関の審査に合格する必要があります.選考方法は受け入れ機関によって様々ですが,私が受けたところは大体 書類選考→ プログラミング課題 → 面接 の流れだったように思います.プログラミング課題はそれこそ機関によって様々で,時間内に解くものもあれば時間は自由でしっかり作り込むようなものもありました.ただし時間制限がないと言っても学業に差し支えるといけないので,そこは課題を出す側も解く側も気をつけます.

レトリバにしたきっかけ

どうせ行くならしっかりと得られるもののあるインターンに参加したいと思い,1ヶ月以上の長期のインターンを中心に選びました.また,苦学生なのでちゃんとお給料がでるところっていうのも大事です.インターン参加によって普段のバイトをお休みするので,最低でもその分は稼げるところじゃないと生活できません.

このような基準でいくつかのインターンを選び,応募しました.その中でも自然言語処理を前面に推しているレトリバインターンはトップクラスで行きたいものでした.人気が予想され,また枠も少ないので落ちる覚悟でした.なので他のインターン選考も結構ちゃんと進めていました.

結果,一度は落ちたもののご縁あって開発のインターンに参加させていただけることになりました. 

レトリバインターンについて少し詳しく

レトリバは例年,研究のインターンを1名程度受け入れています.今年からはインターン枠を少し増やして,研究2名,開発1名という枠でした.狭き門...

レトリバのインターン選考は プログラミング課題→面接 だったかと思います.プログラミング課題は研究も開発も共通です.時間制限がないタイプの課題だったので私なりに結構しっかり作りました.プログラミング技能そのもので私が他の人(特に競プロer)に勝てる気は全くしなかったので,テストや README をちゃんと書くことで差別化を図りました.ちょうどその時期テストにハマっていたんですよ〜.テスト楽しい.テスト通ると嬉しい.

プログラミング課題で通ると面接に進みます.面接は研究開発別々で,どちらも応募している場合はそれぞれ1回ずつあります.遠方にお住いの際はリモート面接OKでした.私はリモートでさせていただきました.大阪-東京 の往復で丸一日潰れてしまうので,リモートOKは助かりました.

面接が終わるとしばらくしてから,具体的には他の候補者の面接が全て終了してから,合否が返ってきます.実は私は一度落ちています.3枠が埋まり,4番手だったらしいです.だったのですが,面接をしていただいた社員さん(インターン期間中のメンターさん)が私のコードの雰囲気を気に入ってくださったようで,私をとりたいと思ってくれていたらしいです.そしてその思いが社長に届いたようで,開発の枠を一つ増やすという取り計らいをしてくださったらしいです.嬉しい.

 

インターンでやったこと

Answer Finder (レトリバが提供する FAQ 検索システム)の改善

Answer Finder ユーザからいくつかの改善してほしい事項が上がっており,これの原因究明および対応をさせていただきました.以下ざっくりと時系列でやったことをまとめます.

1~2 週目

Answer Finder やその周辺のコンポーネントの開発環境構築や修正の反映方法の確立をしました.コンパイル言語がまず初めてだったので,ビルドでこけた時にめっちゃ詰まりました.メンターさんにビルドとはなんぞやを教わりました.院試勉強で知識としては知っていたけれど,実際のコマンドとリンクしていなかった...

3~4週目

課題の原因究明と対応を2, 3件ぶん行いました.めっちゃC++読んだ.いじるコード量はそんなに多くなかったので,使い慣れている PyCharm で作業していました.それをみた CRO が不思議な顔をしていたのが印象に残っています.わかる.

5週目

中間報告資料作成とか発表練習とか発表本番とかをしました.

Answer Fidner の裏で回っているシステムのリファクタリング

中間報告までで最初に用意されていた課題を全て解決してしまいました.残りの期間は自由にしていいということだったので,Answer Finder の裏で回っているシステムのリファクタリングを強く希望しました.コードをみていてちょくちょく気になったところがあったんですよ.

リファクタリングは『レガシーコード改善ガイド』という本にしたがって進めました.大まかにはまずテストを作って,その上でコードの編集を行うという手順です.また,コードを読みやすく編集する上では『リーダブルコード』を参考にしました.どちらも良本です.

6週目

リファクタリングをする上での目標を定めました.現在は使われていない機能の切り離しを目標とすることにしました.

7週目

テスト作成をしました.テストが圧倒的に足りていなかったので,足りていないところの整備です.ただ時間がなかったのでひとまず外から見たときの挙動がざっと変わらないようにメソッドを絞って単体テストすることにしました.

8週目

リファクタリングをしました.インデントやタブのスタイルの統一という軽いところから初めて,リーダブルなコードになるようにこまごまと編集しました.機能の切り離しまで到達できました.実は機能の切り離しはすでになされていたなんて言えない

並行して最終報告資料作成なども行いました.最終報告は社外に公開されるので緊張しました.発表中に放送が落ちてしまうというハプニングがありつつもなんとか耐えました.

最終報告資料はこちらになります.

speakerdeck.com

 

業務以外の色々

歓迎会

インターン生および同時期に入社した方の歓迎会がありました.歓迎される側の人は自己紹介の一環として一発芸を披露することが慣例となっているそうです.私は腕を一回転させるっていう関節やわらかい自慢をしました. 

昼食

オフィス街に位置しているのでお昼ご飯を食べるところがたくさんあって,毎日「さて今日はどこに行きましょう...」という問答が生じていました.サクラテラス3Fの炭火の焼き魚やさんが好き.

ボドゲ

ボドゲ好きな社員さんたちが持ち寄ったボードゲームがオフィスにたくさんありました.レトリバでは歓迎会やカジュアルパーティーなどでしばしばボードゲームが行われているそうです.インターン期間中に3回ほどボードゲームに興じました.『私の世界の見方』はゲームに勝手も負けても楽しく,盛り上がるカードゲームでした.『ダイスフォージ』というボードゲームはダイスを強くしていくダイスゲームです.絵柄が綺麗で好き.

社風

レトリバは,その理念の3つ目に人材への投資を掲げるほどに社員を大切にする企業です.リモートワークや半休を気軽に取得できたり,完全週休2日だったり,休憩スペースの備品が充実していたりと働きやすい環境づくりがしっかりとなされていました.あと椅子が最高です.弊研究室の椅子も結構いいやつなのですが,それが霞んで見えるくらいにめっちゃいい椅子でした.

下の要望を上が全力で通そうとしてくれる,というのも感じました.例えば,私の「お客様が実際に製品を使っているところを見たい」という願いを受けて,インターン生の連携企業見学が実際に計画されていました.残念ながら震災の影響で中止になってしまいましたが,それがなければ実際に見学に行けていました.行きたかった...

このように風通しの良い社風で,アットホームな職場です(ホワイト)って感じでした.おかげでのびのびとインターンを楽しめました.

学び

今回のインターンを通じて,製品の保守や,保守しやすいコードとは何かを学ぶことができました.また,2ヶ月間がっつりと職業体験ができて,就職した後の生活を垣間見ることができました.

 

インターン仲間の参加記たち

www.creativ.xyz

 

専門用語抽出手法の研究と 抽出アプリケーションの開発 - Speaker Deck