日々のアウトプットが変える!あなたのエンジニア・ライフ イベントレポート

2018年10月9日に開催されたこのイベントに参加してきました。 ※増枠【エンジニアのキャリアアップを語る】日々のアウトプットが変える!あなたのエンジニア・ライフ - connpass

まつもとりー(松本亮介)とKwappa(塩谷啓)さんのお二人による発表でした。 早速お二人の資料が上がっています。

資料を読めば分かることは適宜省いて、口頭で補足していたところを中心に書いています。資料と併せて読んでいただければと思います。

なぜポートフォリオが必要なのか - エンジニアと人事(評価・採用)両方向の視点から 松本亮介(まつもとりー)

なぜポートフォリオが必要なのか - エンジニアと人事(評価・採用)両方向の視点から - Speaker Deck

  1. はじめに
  2. 自分の体験
    • 6年前、28歳のとき…… 博士課程の1年目
    • OSS開発:Webではよくあるやり方
      • ビルドは通ってるけど「これ何に使うんだろう……」というあたりで公開していた
    • 博士課程に入って一番びっくりしたのは、オープンにできないことが多いこと
    • オープンに学術研究したいと思ったから大学院に入ったのに……!
    • でもオープンにやってみた
    • 批判を受けた
      • 論文ネタをオープンするなんて……!
      • 怒られたとしてもやりたいようにやっていこう
    • 指導教員の教授に相談してみた
      • 「松本さんが良いと思うやり方で好きなようにやってください。今のやり方、良いと思いますよ」
      • 迷いが消えた
    • ブログを書いても書いてもはてブ3とかしかつかない……
    • 徐々に広がり始める
      • 技術ってフェアというか平等なものなので、ネガティブなことを言われたら技術で見返そうと考えた。
      • 徐々に連鎖がつながっていって、少しずつ講演も依頼されるように
    • なんとなく自分の立ち位置が見えてくる
      • 強者の中で、みんなやってる中で自分も頑張ろう……と勉強を続けていると、どんどん成長してきた
      • 情熱が飛び火する 技術がただただ面白い
    • 頂上が見えてきた
      • 自分の得意分野、専門分野がどこか?
      • トップレベルの技術や人や企業はどこかが分かる
      • 自分を活かせる道が見えてくる
      • アウトプットというのは、自分のことを整理して外に出すこと
      • 脳内でふわっと曖昧だったものが、アウトプットすると明確化される
  3. 体験からポートフォリオの重要性を検討
    • エンジニア目線
      • 自身のスキルの明確化と再認識
        • 曖昧なままだと綺麗に整理理解できない。
        • アウトプットする中できちんと整理する
      • 周りを巻き込むことができる
        • アウトプット→スキルがつく→よりレベルの高いアウトプットができる
      • 先を予測できるようになる
        • アウトプットというものが、自分のスキルのストーリーのようなものになる
        • 自分のこの先目指す道、自分はこれが得意なんだ、などなど
    • 人事目線
      • スタンフォード助教とか取るときはまるまる3日間かけるらしい!
      • ちょっとの面接で採否を決めるのは本当に大変
      • アウトプットを永続的に残すことで、客観的な実力がわかる
    • アウトプットをまとめて、スキルのストーリーを作る

実践的アウトプット入門 塩谷啓(Kwappa)

実践的アウトプット入門 なぜ?なにを?どうやって? / Kwappa さん - ニコナレ 「日々のアウトプットが変える!あなたのエンジニア・ライフ」というイベントに登壇してきたよ #forkwell | Kwappa研究開発室

  • アウトプットとは?
  • なぜアウトプットするか?
    • データソースとして
      • 問題とその解決をコツコツログに書いて積み重ね
      • 学校の授業でノートに書くのって、後から見返す必要ないらしいですよ
    • 学びの定着と共有
      • 見せる前提で書くと理解が深まる
        • 人に見せられるようにしようと、綺麗なコード、より良い設計などを目指すから
      • 伝えることによって価値が増える
        • 誰か他のエンジニアの調べる時間を節約できる
    • コミュニケーションツールとして
      • 社内だと評価面談とか
      • 社外だと採用面接とか
  • 何をアウトプットするか?
    • 仕事メモ
      • データソースとして良い
      • 仕事中、何があって、何が困って、どうやって解決したかを書き留めたもの
      • 評価面談に使えるという面もあるけど
      • ネタ帳であるという面を意識しておく
    • 技術メモ
      • 上記の仕事メモの中から技術的な面を選り分ける、フィルターしたもの
      • 可搬性(社外に公開できるやつ)
        • 社外秘の部分を公開するわけにはいかないので 
      • 課外活動 趣味でアプリ作ってみたとか。
        • 「面白そうな技術があったので、触ってみた」でおしまいじゃなくて、困ったこと解決した方法を書いておけばネタ帳になる
    • プロダクト
  • どうやってアウトプットするか?
    • 自分用に書く
      • とにかく書く
      • ネタ帳、可搬性
      • メモツールにこだわってみよう Day Oneを使ってます
      • 「とにかく書く」といいながらも、再利用性も大事
        • 評価面談のネタにする
        • ネタ帳 などなど
    • 公開用に
      • ソースコードの場合はGitHub最強
      • 著書があると Amazonセントラルのページが作れる
        • フリーランスの人が住宅を購入するとき、融資の参考に使われたりするらしい……
      • こんな俺がこんな記事を書いてもつまらないんじゃ……って過度に萎縮しない。
        • 今の自分にできる、最高のアウトプットをし続けていればそれで良い
    • 人前でしゃべる

ポートフォリオレビュー

まずは、まつもとりーさんKwappaさんのポートフォリオを見た。その後、参加者の中から事前に選ばれた2人のポートフォリオのレビューの時間となった。

@ikedaosushi Forkwell Portfolio - エンジニア向けのポートフォリオサービス
@sachi21 Forkwell Portfolio - エンジニア向けのポートフォリオサービス

2人ともかなりの強者エンジニアで、すごいなーと思いながら見ていた。

質問:どういうことを書いたら自分の実績としてアピールできるだろうか?
書くことないなーと思ってたけど、毎日8時間仕事をやってれば何かしらあるわけで
 書き出す 自分の実績 日々やってきたこと中から紡ぎ出す

 OSSに関わる(自分でOSS製品を作る)という方法もあるよ
 OSSにするコツがだんだん出来てくる

質問:技術ブログ書いても反応がないから心が折れてきた
 Matzさんが「自分を踏み台にしてほしい」と言っていたことがあるけど
 自分のやっている分野で影響力ある人、影響力あるプロダクトとかに絡んでいく

(ikedaosushiさんに対してのコメント)
自分の強みをアピール出来ていて良い。
職歴を見ていると色々と面白そうなことをしてるのが分かる。その中に興味深いものがあって、もう少し知りたい、と思ったときに、その点の関連ブログ記事が(一般化されたとこで)ブログで書いてあったりするとさらに良い。

(sachi21さんに対してのコメント)
継続性 「いろいろやってるけどあんまり継続してないんじゃないか?」と思われがちなので、それに対して「GitHubで4年前から毎日草をはやしてます」と示せるのは素晴らしい。
手を動かすのに勝るものは無い

感想

雑感2点。

GitHubの共通言語っぷりがヤバい

最近、scounty、findy, そしてこのforkwell portfolioと、「どんなコードを書いてるか、GitHubリポジトリから分析しますね」というサービスがあまりに多い。
もはや「GitHub上にないコードは、あなたが書いたものではないと思え」という勢いだなと感じる……
(今更気づいたのかよ、とツッコまれそうだが)

ちゃんとGitHubに出すことを心がけよう。いきなり毎日やるとかは無理だけど。

課題をどう解決したか、を整理する

普段の業務でもプライベートの趣味の開発でも言えることだが。
「課題にぶつかって、いろいろ考えて解決しました」は誰しもやっていることである。
しかし大事なのはそれを明確に筋道立てて説明できること。
悲しいかな、この辺がしっかり説明できないと、「作業するとき何も考えてませんでした」に見えてしまう。
一旦整理してまとめたほうが良いな。

それでは。

機械学習モデルを解釈する方法 Permutation Importance / Partial Dependence Plot

Machine Learning for Insights Challengeで勉強した「Permutation Importance」と「Partial Dependence Plot」についてまとめる。

Machine Learning for Insights Challengeとは

9月18~21日に、kaggleの「Machine Learning for Insights Challenge」という講座が開催された。
1日に1通メールが来て、機械学習関連の話を学べる。
この記事では、4日のうち前半2日間の部分をまとめる。

なお、教材は公開されているので、今からでも同じように学習できる。
それぞれの日の説明には演習問題がついていて、kaggle kernel上で実行できる。

さてInsightsを日本語に直すと「洞察、物事の本質を見抜くこと」となるが、具体的には何なんだろうか。
「コースの始めに」に相当するUse Cases for Model Insights | Kaggleを見ると、このコースでは以下について学べると書いてある。

データのどの機能が最も重要であるとモデルは判断したか?
モデルからの単一の予測について、データ内のそれぞれの特徴量がその特定の予測にどのように影響したか?
全体的に見て、それぞれの特徴量はモデルの予測にどのような影響を与えるのか(多数の起こりうる予測を考慮した場合、典型的な効果は何か?)

以下の記事に出てくるような「解釈性(Interpretaility)」の話題と近いと考えておけば良さそうだ。
【記事更新】私のブックマーク「機械学習における解釈性(Interpretability in Machine Learning)」 - 人工知能学会 (The Japanese Society for Artificial Intelligence)

Permutation Importance

資料はこちら。下の方のリンクから演習問題に行ける。
Permutation Importance | Kaggle

機械学習のモデルを構築したときに「どの特徴量が重要なのか(feature importance)」を知る方法の一つが「Permutation Importance」である。

  1. モデルの訓練をする
  2. 1つの列の値をシャッフルして、その結果のデータセットを使用して予測を行う。 これらの予測値と真の目標値を使用して、シャッフルすることで損失関数がどれだけ悪くなったかを計算する。 そのパフォーマンス低下は、あなたが今シャッフルした変数の重要性を測定している。
  3. データを元の順序に戻す(手順2のシャッフルを元に戻す)。データセットのそれぞれの列で手順2を繰り返して、各列の重要度を計算する。

演習問題では「タクシーの料金」の予測をしていた。
乗車地点の緯度・経度、降車地点の緯度・経度、乗客の人数というデータを元に機械学習モデルを作成する。
学習用のデータの一部は以下の通り。

fare_amount pickup_longitude pickup_latitude dropoff_longitude dropoff_latitude passenger_count
2 5.7 -73.982738 40.761270 -73.991242 40.750562 2
3 7.7 -73.987130 40.733143 -73.991567 40.758092 1
4 5.3 -73.968095 40.768008 -73.956655 40.783762 1
6 7.5 -73.980002 40.751662 -73.973802 40.764842 1
7 16.5 -73.951300 40.774138 -73.990095 40.751048 1

バリデーション用データのうち、ある特徴量を互いに入れ替えてから、予測を実行して、損失関数の値がどう変わるかを観察する。
例えば、乗車地点の緯度を入れ替えてから予測をすると、予測結果は多く変わってくるから、損失関数の値も大きくなる、
一方で、乗客の人数は料金に影響しないので、乗客の人数を入れ替えても結果は殆ど変わらないだろうと予想される。

すなわち、精度が下がったらモデル構築の上で重要な特徴量、精度が下がらなかったらモデル構築の上で重要でない特徴量と判断できる。

Partial Dependence Plot (PDP)

2日目のテーマはPartial Dependence Plotである。
Partial Dependence Plotは、それぞれの特徴量が予測にどのような影響を与えるかを知るのに役に立つ。

資料はこちら。下の方のリンクから演習問題に行ける。
Partial Plots | Kaggle

モデルの訓練をしたあとに、
検証データのある点を元にして、影響を見たい1つの特徴量だけを変化させて、学習済みのモデルで予測を実施する。すると「1つの特徴量が変化すると予測はどう変わるのか?」が分かる。
検証データの中から多数の点について同様の操作を実施し、その平均を描画する。

演習問題の最後の方は、1日目のPermutation Importanceと2日目のPartial Dependence Plotを組み合わせた複合問題だった。これによって両者の挙動の違いが分かった。

問題:予測可能な特徴量が特徴量AとBの2つしかない場合を考える。どちらも最小値は-1、最大値は1である。
特徴量AのPartial Dependence Plotを描画すると、全ての範囲にわたって急激に増加する。一方、特徴量BのPartial Dependence Plotは、全ての範囲にわたってよりゆっくり(急激ではなく)増加する。
このとき、特徴量AのPermutation Importanceは必ず特徴量BのPermutation Importanceより必ず大きいと言えるか?

ちょっと考えると正しそうかなーっと思ったけど、実はこれは必ずしも正しくない。
たとえば、特徴量Aが変化する場合には大きな影響を与えるが、ほとんどのデータでは全く同じ値を取る、という場合が考えられる。この場合、Permutation Importanceを計算してもほとんどの値は変わらないままであり、特徴量AのPermutation Importanceの値はそれほど大きくならない。

pyconjp 2018 感想

PyCon JP 2018に行った。火曜日は有給休暇を取った。PyConJPは初参戦。大規模なカンファレンスってデブサミくらいしか行ったことなかったから、ある特定言語のカンファレンスは独特な感じがした。 (Rをほとんど使わないのにJapan.R 2016に行ったことはある)

資料や動画のリンクは以下の2つの記事でまとめてくれている。
PyCon_JP_2018カンファレンス資料まとめ(仮) - 筋肉で解決しないために。
【PyCon JP 2018】発表資料まとめ - Qiita

また、一部のセッションだけではあるが、ログミーTechに詳細な書き起こしが上がっている。 PyCon JP 2018の記事書き起こし - ログミーTech(テック)

【1日目】

基調講演 Argentina in Python: community, dreams, travels and learning

pythonを多くの人々に広めるために、車に乗って各地を回って講座を開催した人の話。すごい情熱だ。

東大松尾研流 実践的AI人材育成法

Twitterハッシュタグを追っている限りでは酷評されてた。 pythonの話というよりは機械学習人工知能の話をやたらと長く説明していた。普段Djangoとか触ってる人は「分からん……」だし、普段から機械学習に携わっている人は「長々と説明しなくてもええわ、退屈」になるし、加減が難しいな。 numpy力を判定する試験のシステムを開発したということなので、一般公開してほしい。

Webアプリケーションの仕組み

普段SIerのお仕事でWeb開発は全くやらないので、良い機会に勉強しとこうと思って聞いた。 まずはDjangoやFlaskのようなWebフレームワークを使わずに非常に単純なWebアプリケーションを書いてみて、少しずつ拡張する様子を実演していた。

Interpretable Machine Learning, making black box models explainable with Python!

機械学習システムで学習してみたは良いけど、そのモデルをどう解釈してよいか分からないのは問題である。モデルを解釈する主な手法を紹介する……という話。 Twitterで講演のjupyterリンクをツイートする、と講演者さんが当日は言ってたけど、結局ツイートしてないね(´・ω・`) David Low(@davidlowjw)さん | Twitter

Partial Dependence Plot (PDP)、Individual Conditional Expectation (ICE)、permutation によるFeature Importance、Local Surrogate Models (LIME)などの手法を説明していた。

この講演とは直接関係ないが、以下のサーベイ記事がまとまっていると思ったので、後で読んでおきたい。 【記事更新】私のブックマーク「機械学習における解釈性(Interpretability in Machine Learning)」 人工知能学会 (The Japanese Society for Artificial Intelligence)

Jupyterで広がるPythonの可能性

最初はメルカリのセッションに行こうとしたけど、満員で入れなかったので途中からこちらを聴講した。 内容がメチャクチャに盛りだくさんだったので追いつけなかった……これは手を実際に動かして習得したほうが良さそう。

Pythonistaの選球眼(せんきゅうがん) - エンジニアリングと野球の目利きになる技術

rettypy主催者の中川さんによる発表。私も2回ほど参加しております。 自分に馴染みのないWeb周りの話が主体だったのでちょっと感想が書きづらい。 主題となった「選球眼」すなわち「イシューからはじめよ」だが、自分は「イシューからはじめる」おとができているのかよく分からない……多分できていない気がする。 どのテーマについて何をどこまで作業するのかを明確にした上で始めましょう、手当たり次第に何でも飛びつくのはやめましょう、というやつだな。 俺自身の勉強は現在、基本的には機械学習周りだけとしているが、機械学習とひとくちに言っても、動画講座はあるわ、論文はあるわ、フレームワークは更新されるわ、と日々の更新が猛スピードで起きている。選択と集中が難しい……

【2日目】

Pythonでやってみた」:広がるプログラミングの愉しみ

気づいたら音声信号処理プログラミングになっていたので音声信号処理エンジニアの俺歓喜

  • プログラミングの動機は2種類 「面倒くさいことを簡単に」「一体どうなってるんだ? おもしろそう」
  • 作り始めるときには、最も単純な構成を考える! 最初から全部やろうとしないのがミソ。
  • 大事なのは、これらの技術を「学ぼう」とか「使おう」と思って制作をスタートしたわけではないということ。技術ドリブンじゃなくて、「何かをやりたい」と思って、あれこれ試行錯誤やってたら結果的に技術が身についた、というのが超大事。
  • 知識と学習は鶏と卵みたいな関係であり、「学習するためには知識がなきゃいけない」「知識を得るためには学習をしなきゃいけない」というジレンマがある。しかしどちらかを始めればあとはグルグル回り始める。

niconicoにおけるコンテンツレコメンドの取り組み

ニコニコの一人のユーザーとしては気づかないけど、色々なレコメンドシステムが裏側で動いているみたいだ。

Pythonistaに贈るコンテナ入門

同時刻にはGraphQLもあり、「なんだ、この時間帯はPythonに直接関係ないことも取り扱おうってセッションなのか?」と思いつつ聞いた。 Dockerは弊社内ではほとんどその名を聞かないのだが(SIだから?)、世の中的には既にかなり普及しているようなので、知っておかないとな、と思いつつ聞いた。

契約書データ関連のAI開発に伴う、前処理及び匿名化処理についての実例

センシティブな情報を扱う際の前処理や匿名化の問題。 発表者(CTO)が「まず私が10万件くらい手動でアノテーションしました」と言っていた。アノテーションがメチャクチャ大変そう。

1次元畳み込みフィルターを利用した音楽データのオートエンコーダ

ずいぶんストライドの大きい畳込みをしていたけど、あれってどういう効果があるんだろうか。音楽まわりの理論をよく分かってないまま実装しているように見受けられた。

料理写真が美味しく撮れる! 開発現場から覗くAI料理カメラの裏側

2月のデブサミでRettyの発表があり、そこでも「おいしく見える写真」を判別する機械学習システムを構築していた。Rettyでは機械学習させる際の教師データはは人力でラベルをつけたが、ヴァズ(サービス名SnapDish)では「いいねがたくさんついた投稿写真が美味しく見える写真」という手法をとっていた。あー、サービス運営してるとその手があるんだ!と思った。

ブース

アイシン精機が出てきたのが印象的で、「ずっと愛知県にいたけど、去年初めて東京都のお台場に開発拠点を立てました」って言っていて「あぁ、これは本気を出して新規事業に取り組もうとしているんだな」と感じた。 少し前に、デンソーが及川さんを技術顧問に迎えたというニュースを見たときも本気度を感じた。自動車業界の各社は、激しい変化にさらされて生き残りの道を探しているんだろう。

まとめ/おまけ

会場が1~6階にまたがっていたので結構移動が大変。 また部屋ごとに定員の数が大きく違うので、どのセッションをどの部屋に割り当てるのかは大変そう。(pyconjpの人がもしここを見ていたらお伝えしたいのですが、音声信号処理の分野は結構独特なので聞く人はそれほど多くないと思います。)

しかしそれを差し引いても、ためになる話が多く勉強になった。来年も参加しようと思う。 (あとはパーティーのときに話す相手がいなくてぼっちだったので……コミュニティに顔を出していろいろな人と知り合いになればいいのかな……?) まずは、1日目の「Jupyterで広がるPythonの可能性」の内容をもう一度見直して手を動かすことから始めようかと思う。 それでは。

画像セグメンテーション用のアルゴリズム、u-netについて簡単に説明する

きっかけ

kaggleであるコンペに参加しようとしてkernelを見たところ、みんな「u-net」ってアルゴリズムの話をしていた。聞いたことがなかったし全然分からなかったので調べてみた。

論文の著者が5分で簡単に説明している動画があるので、その内容をまとめる。
5 Minute Teaser Presentation of the U-net: Convolutional Networks for Biomedical Image Segmentation - YouTube

5 Minute Teaser Presentation of the U-net: Convolutional Networks for Biomedical Image Segmentation
youtube動画に対して、私が英語の文字起こしをして、さらに日本語訳の字幕を追加した。しかし現時点ではどちらも表示されていなくて悲しい……(誰かが字幕の審査をしてOKを出さないと表示されない。どなたか文字起こし・字幕の審査にOKを出してくれるとありがたい)

※この記事を書くにあたり、u-netの論文は読んでいません。ご注意ください。

概要、何に使うのか

u-netは画像のセグメンテーションのためのアルゴリズム。2015年に発表された。

手法のキモはどこか

u-netのアーキテクチャ(構成図)はこちら。
f:id:soratokimitonoaidani:20180924002248p:plain
これがU字型に見えるからu-netって名前なんですね、

3×3のkernelを用いて畳み込みをして、Max-poolingで縮小、という処理はCNNで一般的だ。その後、2×2のkernelを用いて拡大をして、元のサイズに戻す。
畳み込み→拡大という流れはAuto-Encoderと似ているのだが、縮小段階の途中の特徴量マップを、拡大段階の学習で再度使用するというのが最大の特徴である。

具体的に説明しよう。
図の上部に長い右向きの灰色矢印がある。矢印左側では、縮小段階の途中の568×568の特徴量マップが64枚ある。
矢印右側では、アップサンプリングしてきた392×392の特徴量マップが64枚ある(64という数字は直接登場しないが、結合結果が128枚であることから64枚と考えられる)。
縮小途中の64枚+アップサンプリングしてきた64枚 の合計128枚を結合(concatenate)したのちに入力として、次の特徴量マップ(390×390サイズ、64枚)を作る。
矢印の左右で画像サイズが異なるので、568×568から中央392×392だけを切り出して(crop)使用している。

通常のCNNは畳み込みでパディングをしているが、u-netの畳み込み時には、実行しない。
このため画像サイズが処理を経るごとに小さくなっていく。
(なんでパディングをしないのかは謎。kaggle上での実装を見てみると普通にパディングしてる例もある。)

先行研究と比べてどこがすごいか?

学習に使う画像の数が少なくても良いらしい。
あと学習および学習後の適用が高速である。(u-netは全結合の層が全く無いので、パラメータ数が小さい。それが速い理由だろうか?)

既存手法に比べて画像セグメンテーションの精度が良い。

参考

論文著者のサイト:
U-Net: Convolutional Networks for Biomedical Image Segmentation
機械学習の論文を日本語でまとめているarXivTimes上での説明:
U-Net: Convolutional Networks for Biomedical Image Segmentation - Issue #294 - arXivTimes/arXivTimes - GitHub

pythonのdillでは正規表現matchオブジェクトが保存できない

dillというのは、pickleの強化版のようなツールだ。現在使っている変数をまとめて外部のファイルに保存できる。
(バイナリにして保存することを、pickle化、シリアライズ、直列化などと呼ぶらしい)

dillについての情報はネットにあまり書いていない。下記の記事がとても参考になった。
Pythonですべての変数を保存するにはPickleよりdillが便利 - Qiita

余談ですが、「dill pickle」はピクルスの一種を指しているらしく、この語句で検索してもpythonのライブラリは出てきません。検索ワードに「python」を加えましょう。

dill pickle
《料理》ディル・ピクルス◆キュウリをディル(dill)の葉と種と共に塩漬けした物。
dill pickleの意味・用例|英辞郎 on the WEB:アルク

簡単な使い方

公式ドキュメントに使い方が載っていないので、ほぼ上記Qiitaそのままですが。

# dill_test_a.py
a = 123
b = 456
c = b - a
string = "Hello World"

import dill
dill.dump_session("session.pkl")

最後の2行で、現在の変数をすべて"session.pkl"というファイルに保存している。

# dill_test_b.py
import dill
dill.load_session("session.pkl")

print(a)
print(b)
print(c)
print(string)

'''出力:
123
456
333
Hello World
'''

保存した"session.pkl"から変数を復元し、その値を表示している。

pickleでは保存できないが、dillで保存できる例

上の例では単純な数値や文字列だけを保存(シリアライズ、直列化)した。
しかし、pythonには色々なオブジェクトがあり、pickleでは保存できないものもある。
今回の主題ではないし、私がきちんと理解していないので詳細は省くが、lambda関数、入れ子になった関数などがこれに該当する。dillではpickleを拡張子、lambda関数、入れ子になった関数なども保存できる。

公式の説明では、
12.1. pickle ? Python オブジェクトの直列化 - Python 3.6.5 ドキュメント の中の『12.1.4. pickle 化、非 pickle 化できるもの』
dillのreadme
を参照。

pickleオブジェクトがpickle化できるものの中で比較的、制限されているからです。つまり入れ子関数やlambda、スライスや、その他いろいろなものを処理できません。これらのオブジェクトを直接pickle化したいケースは、それほどはないかもしれませんが、pickle化したい他の物の中に、そういったオブジェクトが出てくることは、かなり一般的です。そのため、pickle化が失敗する原因となるのです。
私が選ぶ2015年の”新しい”Pythonモジュール トップ5 | POSTD

一部、pickle化できない種類のオブジェクトがある。
よく引っかかる例はlambda関数や、openで開いたファイルハンドラで、これが保存するオブジェクトのどこか1箇所にでも使われていると、pickle.dump()がエラーを出す。
pickle [いかたこのたこつぼ]

正規表現のマッチングオブジェクトが入っているとエラーになる

本題に入る。
色々なオブジェクトを保存できるdillだが、正規表現のmatchオブジェクトは保存できない。

import re
result = re.search("lar", "regular expression")
print(result)
print(type(result))

'''出力:
<_sre.SRE_Match object; span=(4, 7), match='lar'>
<class '_sre.SRE_Match'>
'''

"result"というmatchオブジェクトを作った。ではこれをdillで保存してみよう。

dill.dump_session("session2.pkl")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-07b38aa0950a> in <module>()
----> 1 dill.dump_session("session2.pkl")

D:\Code\Anaconda3\lib\site-packages\dill\dill.py in dump_session(filename, main, byref)
    331         pickler._recurse = False # disable pickling recursion for globals
    332         pickler._session = True  # is best indicator of when pickling a session
--> 333         pickler.dump(main)
    334     finally:
    335         f.close()

(長いので中略)

D:\Code\Anaconda3\lib\pickle.py in save(self, obj, save_persistent_id)
    494             reduce = getattr(obj, "__reduce_ex__", None)
    495             if reduce is not None:
--> 496                 rv = reduce(self.proto)
    497             else:
    498                 reduce = getattr(obj, "__reduce__", None)

TypeError: can't pickle _sre.SRE_Match objects

ダメだ。matchオブジェクトは保存できない。


解決方法

今回は、「matchオブジェクトも保存する」のではなく、「matchオブジェクトは保存できなくても良いから、その他のオブジェクトをまとめて保存する」ことを目指す。
つまり解決と言っているのは、具体的には「matchオブジェクト以外を保存する」ことである。

dump_session()関数に「指定した変数は除外する」という引数でも無いかなぁと思ったが、
公式ドキュメントを見てもそういう引数はない。
dill module documentation - dill 0.2.9.dev0 documentation

したがって、変数を削除してからdillを実行することにする。
変数の削除はdel [変数名]で実行できる。

import re
result = re.search("lar", "regular expression")
print(result)
print(type(result))

'''出力:
<_sre.SRE_Match object; span=(4, 7), match='lar'>
<class '_sre.SRE_Match'>
'''

del result
dill.dump_session("session2.pkl")

でresult以外の変数を保存できた。
保存したファイルを使用して変数を表示することもできた。(ソースコードは最初の例のファイル名を変えただけなので特に貼らない)

それでは。

Windows+Anaconda で環境切り替えしようとししてつまづいた2つのポイント

「scikit-learnとTensorFlowによる実践機械学習」の本を読み進めている。
TensorFlowのパートに入ったので、TensorFlowの環境を通常と別に構築しようとしたのでまとめておく
(なお最終的には失敗している。)

環境:
Windows 7 64bit
python --version

  • > Python 3.6.6 :: Anaconda 4.3.1 (64-bit)

Anacondaは環境管理もできる(のでvirtualenvを使う必要はない)

参考書では通常のpythonをインストールし、pipでパッケージ管理をしている。そして、隔離された環境を作成するためにvirtualenvを使うことを推奨している。
参考書に合わせてvirtualenvを使おうと思っていたが、調べてみると、Anacondaってパッケージの管理だけじゃなくて、環境の管理もできるということを知った。

condaは仮想環境管理にも使えます。: virtualenv/venvの代わり
仮想環境下でcondaやpipでパッケージをインストールできます。
condaで作る仮想環境はpythonバージョン違いまで吸収できるため、virtualenvの上位互換と言えます。
事実、anacondaでvirtualenvを使おうとすると、condaで環境を作るように警告が出ます。(virtualenvが作れはします。)
データサイエンティストを目指す人のpython環境構築 2016 - Qiita

「Condaの迷信と誤解」というタイトルの記事にはこう書かれている。

Reality: You actually can install (some) conda packages within a virtualenv, but better is to use Conda's own environment manager: it is fully-compatible with pip and has several advantages over virtualenv.
拙訳:実際には、virtualenvを通じてcondaのパッケージをインストールすることはできる。しかし、より良いのは、Conda事態の環境管理を使用することである。これはpipと完全に互換性があり、virtualenvと比べていくつかの利点がある。

Conda: Myths and Misconceptions | Pythonic Perambulations

なお、Condaで独立した環境を構築するためのコマンドに関しては、以下を参照。
Managing environments - Conda documentation


Powershellで切り替えが上手く行かない

PowerShellの上でactivateを実行すると、特にエラーなどは発生せず、正常に実行されたように見えるが、
実際には環境が切り替わっていないという不思議な事態が起きる。

解決策は
PowerShellでAnacondaの仮想環境をactivateするメモ - Qiita
に書いてあるとおり。以下のように打つとactivateが反映される。

conda install -n root -c pscondaenvs pscondaenvs

また、activateの際にエラーメッセージが表示され、実行できなかった。
activateでは「.ps1」 という拡張子のファイル(PowerShellスクリプト)を実行するが、デフォルトではスクリプトの実行は禁止されているからである。
管理者権限でPowerShellを立ち上げて、以下のように打つと「.ps1」拡張子のファイルを実行できる。

Set-ExecutionPolicy RemoteSigned

主成分分析を使って、DDRのレベル18の譜面の傾向を分析してみた

主成分分析を使って、DDRのレベル18の譜面の傾向を分析してみた

前書き

音ゲーのスコアを主成分分析して、プレイヤーや曲の傾向が求められるのではないか? と思ったので試してみた。
いま自分が一番やっている音ゲーはSOUND VOLTEXなのだが、みんなのスコアデータを取得してくる方法がないため、DDRで分析をやってみた。

注意事項:私はデータ分析についてそれほど熟練してるわけではない。しかも、DDRについてもそれほど熟練してるわけではない。(ごく少しのレベル18をギリギリクリアできる程度) 譜面についておかしな分析をしていたら、ご容赦ください。

データ収集

まずデータを収集する必要がある。すなわち、色々なプレイヤーのスコアを収集する。

有志が作成したSkill Attackというサイトがあり、そこからデータをスクレイピングした。
Skill Attackでは、個人がDDRのスコアデータを登録し、また他の人のスコアを閲覧することができる。
なお、登録しているスコアは譜面ごとの過去最高スコアである。
まずSkill Attack Difficulty Ranking (Level 18)のページを起点とした。
ここには各プレイヤーのレベル18のスコア合計・平均があるので、そこから容易にプレイ済み曲数を計算可能である。
ある程度上手い人、かつある程度曲を埋めている(=スコアを登録している)人を今回の分析対象とした。具体的には、レベル18のスコア平均値が95万点以上(満点は100万点)、プレイ済み曲数が30曲以上(曲数は全部で35曲)とした。この条件を満たした対象プレイヤーは94人である。
対象プレイヤーのスコア一覧をスクレイピングした。なお、スコアを登録していない曲については欠損値になることに注意。

データの収集(スクレイピング)は2018年8月1日の0:00頃に実施した。

データの一部は次のようになる。
f:id:soratokimitonoaidani:20180817002800j:plain
さて主成分分析……と行きたいところだが、前処理として2つの工程を実行する必要がある。
それは「不適当な曲の削除」と「欠損値の補完」である。

前処理(不適当な曲の削除)

まず「不適当な曲の削除」について。曲の特性(出現条件と出現タイミング)を考えて、データ分析するには不適当な曲があるので、その曲のデータを削除する必要がある。

「ACE FOR ACES 鬼」はレベル18なので今回の集計対象曲である。しかし、この譜面がプレイ可能になったのは7月26日と、データ収集をした僅か6日前であり、スコアデータを登録しているプレイヤーがほとんどいない。欠損値が大半であるため、この曲のデータは分析に使えないと判断し削除する。

レベル18の中で、「ACE FOR ACES 鬼」の次に新しい譜面は「ENDYMION 激」である。これは同日の7月26日から通常選曲できるようになった。それまでは4回ミスしたら即終了という条件下でプレイしなければならなかった。
この厳しい条件だとスコアは下がってしまうのか。それとも熟練のDDRプレイヤーたちはこの条件でも他のレベル18と変わらないスコアを出すのだろうか。
これを確認するため、箱ひげ図を用いて各曲のスコア分布状況を可視化する。

f:id:soratokimitonoaidani:20180817002735p:plain

明らかにENDYIONは他の曲と比べて傾向が違い、下方の外れ値も多く存在する(つまりスコアが極端に低いプレイヤーがいる)。 ENDYIONを分析に含めてしまうと、この曲の特殊条件の影響を強く受けてしまうため、分析が上手くできないと考えられる。したがって、「ENDYMION 激」も除外する。

(これによって 本当は2曲を除外したあとのスコア平均値が95万点以上、としたほうが合理的ではある。
しかしながら、この方法は集計対象プレイヤーがすぐには求まらないので複雑である。
今回は、ある程度の人数が集まればよしと思ってたので、ここについてはこだわらずに次に行く)

2曲を削除した後で箱ひげ図を再度描画してみた。

f:id:soratokimitonoaidani:20180817002741p:plain

Pluto The First 鬼」では明らかに外れ値が発生している。特に80万点を割っている人が2人いるのが気になる。しかし、この曲は最近出たというわけではない。除外せずに分析を続行する。

ちなみに、スコアが一番高い傾向にある曲は「Come to Life 鬼」だと分かる。
譜面動画見たけど結構キツくないか? 延々と16分滝を踏まされるから、クリアがギリギリの人の目線で見ると決して一番簡単には思えない。しかし、「リズムが全体的に分かりやすい」「全体BPMが180と低め」「低速部分に難解な配置がない」「同時押し絡みの強い配置がない」と考えていくと……上手い人にとってはスコアを出しやすいんだろう。多分。


前処理(欠損値の補完)

次に、欠損値の補完をする。
あるプレイヤーがある譜面をプレイしていない場合はデータがない欠損値になっている。主成分分析をするためには、これを何らかの値で埋める必要がある。
今回は単純に「その曲の平均値」で埋めることにした。
つまり、例えばあるプレイヤーAの「Elemental Creation 鬼」のスコアが無かった場合、「Elemental Creation 鬼」をプレーしているプレイヤーのスコア平均値で代用するのだ。

ただしこの手法では、そのプレイヤーAが上手いか下手かを無視している(プレイヤーAが上手ければ、スコア平均値よりももっと高いスコアを出すと推測される)。その分だけ分析の精度は悪くなる。
単に平均値を使うよりももっと複雑な処理が必要になってくる。
スコアについての何らかのモデルを考え、それに当てはめて欠損したスコアを推測するのが良いだろう。
より分析精度を上げるためにはより適切な欠損値補完の手法が必要であろう。

もともと30曲以上のスコアを登録している人に限定したのは、欠損値が数曲であればその影響も軽微であるためだ。
(35曲のうち10曲しかプレイしていない人のデータの場合、残り25曲の欠損値を埋める必要がある。この場合はデータの中身が欠損値の補完方法に大きく左右されてしまう。したがって、元々のデータの情報が少なすぎて上手く分析できないと考えた。)


主成分分析(その1)

以上の前処理をした後に、主成分分析を実行する。
下記は第1主成分のうち、それぞれの曲の成分を示したものである(値の順にソートしている)。
f:id:soratokimitonoaidani:20180818005809p:plain
第1主成分は全ての曲についてマイナス成分だ。
主成分は「ベクトルの方向」を示しているので、全体をマイナス1倍しても問題はなく、そうすると全ての曲の成分がプラスである。
どの曲についても、曲のスコアが増えれば第1主成分も増える。……すなわち、第1主成分はプレイヤーの上手さ、「実力」の成分であると解釈して問題ないだろう。


第2主成分は以下の通りである。
f:id:soratokimitonoaidani:20180818005816p:plain
絶対値が最大の曲は「Pluto The First 鬼」で、その値は-0.867である。
……他の曲と比べて値の絶対値が大きすぎる。

主成分は長さ(要素の二乗の和)が1になるようにスケーリングされている。
その中で、(-0.867)^2 = 0.751 が「Pluto The First 鬼」の成分である。……ほとんどPluto The Firstじゃねぇか!!!

一体何が起こっているのか。
それは第1・第2主成分を散布図でプロットすると判明する。
f:id:soratokimitonoaidani:20180818010221p:plain
先ほど見たように、「Pluto The First 鬼」……そろそろフルで呼ぶのが面倒になってきたので略称の「プルファス」を使う……プルファスのスコアが極端に低いプレイヤーが2人いた。
第2主成分のうちプルファスの成分は-0.867で、負である。すなわち、プルファスのスコアが低ければ第2主成分は大きくなる。
プルファスのスコアが低く、第2主成分が極端に大きい点が2つ存在する。

プルファスのスコアが低い人に合わせて主成分ベクトルを作ってしまう事態が起きているのだ。
データ分析の用語を使うと、主成分が外れ値に強く影響されている。

プルファスのスコアと第2主成分の相関係数は実に-0.752と、強い相関を示している。ほとんど「Pluto The First 鬼」のスコアだけで第2主成分が決まっているのだ。

……それでは反対側の成分が大きい曲はどうなっているだろうか。一定の傾向が出ているだろうか。

New Century 0.164
Cosy Catastrophe 0.150
888 0.133
POSSESSION 0.131
New Decade 0.130

あんまり共通点が感じられない……

第3を飛ばして、第4主成分を見てみると、なんとなく傾向があるように見える。

f:id:soratokimitonoaidani:20180818005813p:plain
★マークを付けたのは、BPM400の8分よりも速い滝を踏まなければいけない曲である。表の下のほうに固まっている。
この条件に該当する曲は

  • EGOISM 440
  • Over The Period
  • 888
  • MAX.(period)
  • Elemental Creation
  • Pluto The First
  • Astrogazer
  • 嘆きの樹

である。(正確にはNeutrino、IXも該当するが、アクセントとしてごく一部に入っているだけなので除外しておく。)

こうしてみると最近の曲が多い。もしパラハデが最近登場していたら、絶対ラストの滝に三連符(12分)が混ざってたよな……

ともかく、第4主成分のうちこれらの曲の要素がマイナスになっているので、これらの曲のスコアが高いと第4主成分が下がる。したがって、「第4主成分が低いプレイヤーは、テンポの速い曲が得意」という傾向が導けた。第4主成分には何とか意味づけができそうだ。


主成分分析(その2)

最初の主成分分析ではあまり上手い分析ができなかった。これは全てプルファスが悪い。
あんなメチャクチャなショックアローが入っていて、あんなわけの分からない停止がある譜面が悪い。
……というわけで、プルファスの存在を除外して再度分析してみよう。

第1~第4主成分は以下の通りである。(左から第1、第2の順番)
f:id:soratokimitonoaidani:20180818005805p:plain

第1主成分は先ほどと同様、全ての成分が負であり、「プレイヤーの実力」を示している。

ここまでは同じだが、第2主成分は違ってくるはずで、一定の傾向がわかる……

絶対値が最大の曲は「Neutrino 鬼」で、その値は-0.678である。
……他の曲と比べて値の絶対値が大きすぎる。

主成分は長さ(要素の二乗の和)が1になるようにスケーリングされている。
その中で、(-0.678)^2 = 0.460 が「Neutrino 鬼」の成分である。……大きな部分を「Neutrino 鬼」が占めている。「Neutrino 鬼」のスコアと第2主成分の値の相関係数は-0.615で、強い相関を示している。

……はい。さっきのプルファスがNeutrinoに変わったような結果である。プルファスほど極端ではないけれど。

こうなる仕組み(原因)も同じで、Neutrinoにも下方の外れ値がいくつか存在したからである。外れ値の外れ方がプルファスよりも弱いので、傾向もプルファスほど極端ではない、という状況である。


なおも見ていくと、不思議なことに気がついた。
主成分のうち、絶対値の大きい要素には特定の曲が出現しやすい。このあたりの曲だ。
Cosy Catastrophe
Neutrino
DEAD END (GROOVE RADAR Special)
Tohoku EVOLVED

逆に、Come To Lifeや冥などは絶対値が小さい。
この理由は考えてみれば至極当然で、Cosy Catastropheなどの曲はスコアのばらつきが大きいからである。

それを揃え、データのばらつきを一定にするのがデータの正規化であり、分析前に実施しておく必要がある。今回は「どうせどれも100万点満点の曲のスコアなんだから同じようなもんだろう」と考えてしまって正規化をしなかったが、それが裏目に出たかっこうである。


課題:さらなる分析へ向けて

今回の分析では曲やプレイヤーの傾向を示すことが目的であったが、その目的は達成されていない。
現状では3つの課題があると考える。

課題1:外れ値の除外をしていないせいで、外れ値の影響を受けやすい

先ほどはプルファスのデータを削除した。すなわち曲(=特徴量)の方を削除していたが、外れ値の除去ではサンプルを除外するほうが一般的だ。極端に周りから離れたスコアを出しているプレイヤー(=サンプル、インスタンス)を除外したほうが良さそうだ。
最初にACE FOR ACESとENDYMIONという曲を削除した(これには正当な理由があるのだが)ので、それに引きづられて曲データを削除しようとしてしまった。

機械学習/データ分析では、その手法によって「外れ値に対して安定している・安定していない」という差が合る。
今回の主成分分析はその性質上、外れ値にとても弱い。外れ値のサンプルが1つだけでも、そこに適合させようとして主成分は変化してしまうので、これは上手く除外しなければならない。

課題2:データの正規化が必要

今の状態では、Come To Lifeや冥のデータが殆ど無視されている。これは曲ごとのスコアのばらつきに差があるためなので、分散を揃える必要がある。

課題3:欠損値の補完手法に改善の余地がある

1つ目の課題よりは重要度が低いと考える。先述の通り、単に中央値で埋めるよりももっと工夫をして欠損値を埋めたほうが、分析精度が上がるはずである。

……こうしてまとめてみると、全部データの前処理じゃないか! よく言われている「データ分析の大半は前処理だよ」を身をもって思い知った。次は前処理をちゃんとやって有意義な結果を導出したい。