Tak's Notebook

Kaggle, Machine Learning, Engineering

呼び出し元の関数の変数を削除したい場合

前回の続き

前回の続き

https://takaishikawa42.hatenablog.com/entry/2019/08/19/090313

結論

関数の中で呼び出した関数内で、呼び出し元の関数の変数を削除したい場合、global 変数を宣言すればアクセスできる

サンプルコード

func 関数内で、func2 の x,y を削除したいときがある。

具体的には、train, validation が大きめなデータで、これを X_train, y_train, X_validation, y_validation に分割することがよくあるかもしれない。

このとき元の train, validation は不要になるうえメモリ的に負担になることがある場合、今回のやり方が役立つかもしれない。

>>> def func(a, b):
...     A = a ** 2
...     B = b ** 2
...     # ここで del x,y したい!
...     return A, B
...
>>> def func2():
...     x = 5
...     y = 10
...     A, B = func(x, y)
...     return A, B
...
>>> func2()
(25, 100)
>>>
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'func': <function func at 0x7ff5a023d048>, 'func2': <function func2 at 0x7ff5a023d0d0>}

グローバルな名前空間には登録されていないが、 func2 が実行されてから完了するまではメモリに乗っかっていることになる。

funcコメントアウトしている箇所で x,y を del したい場合は以下のように del globals()["x"], globals()["y"] すれば良い。

( この場合コメントアウトしている箇所で del x, y しても、UnboundLocalError: local variable 'x' referenced before assignment というエラーが出る)

>>> def func(a, b):
...     A = a ** 2
...     B = b ** 2
...     del globals()["x"], globals()["y"]
...     return A, B
...
>>> def func2():
...     global x, y
...     x = 5
...     y = 10
...     A, B = func(x,y)
...     return A, B
...
>>> func2()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'func': <function func at 0x7f90400c2048>, 'func2': <function func2 at 0x7f90400c2158>, 'x': 5, 'y': 10}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'func': <function func at 0x7f90400c2048>, 'func2': <function func2 at 0x7f90400c2158>}
(25, 100)

グローバルな名前空間からは削除されていることが分かる。

もう少し上手な方法がありそうな気がするので、このあたりのことはもう少し調べたい。

関数内でグローバル変数を削除したい場合は `del globals()["x"]` としないとダメ

関数内でグローバル変数を削除したい場合

関数内でグローバル変数を削除したい場合、 del globals()["x"] としないとダメという話。

メモリ節約の際などに使えるかもしれない。

グローバル変数

ある変数を定義すると、 globals() に辞書型で登録される。

>>> x = 1
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'x': 1}

ローカル変数

関数内で定義された変数は globals() には登録されない。

また、関数内で del しても、ローカル変数を削除するだけで、グローバルには影響を与えない。

>>> def func(x):
...     y = x * 2
...     del x
...     return y
...
>>> func(x)
2
>>> x
1
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'x': 1, 'func': <function func at 0x7ff6c81d2048>}

関数内でグローバル変数を削除したい場合、 del globals()["x"] のようにする

del globals()["x"] で関数内でグローバル変数を削除できる。

>>> def func2(x):
...     y = x * 2
...     del globals()["x"]
...     return y
...
>>> func2(x)
2
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'func': <function func at 0x7ff6c81d2048>, 'func2': <function func2 at 0x7ff6c81d20d0>}

無事 x がグローバル変数から削除された。

次回は、関数の中で呼び出した関数内で、呼び出し元の関数の変数を削除したい場合についての記事を書く。

転職ドラフト体験談

f:id:takaishikawa42:20190312232453p:plain

TL;DR

「こちらの記事は転職ドラフト体験談投稿キャンペーンに参加しています」

https://job-draft.jp/articles/251

転職活動の詳細は、こちらの記事に書いてあります。

転職ドラフトに登録したきっかけ

  • 転職エージェントやWantedlyを使う中で、転職のチャンネルは多い方がいいと感じて、転職ドラフトに登録した。

事前に審査があるのがしんどいが、これをキッカケに長めの職務経歴書を書くことができた。
多少なりとも「ここをもっと具体的に」などと指示をもらえた。人に見てもらうという緊張感を実際に企業に経歴書を提出する前に感じながら書けたのが良かった。

転職ドラフトでの指名状況

  • 流行りの機械学習をキーワードにしたため、いわゆる「文系学部卒 独学エンジニア」ながら、10を超える企業の指名を頂けた

最終的には、締め切り最終日より前に指名をいただいた企業6社ほどの指名を承諾して、ひとまず面談形式でお話を聞きに行った。
上場企業もあれば、未上場ながら成長企業もいれば、社員一桁〜数十名程度のベンチャー企業まで様々な企業のお話を聞いた。

面接で感じたこと

  • 審査制度のおかげである程度の文量がある職務経歴書が企業の手元にある成果、あまり回り道せずにスムーズに面談が進んでいったイメージがある

面談でも念の為自己紹介をお願いされたが、自分がどういうことをしてきて、どういったことに興味があるのかという大枠はどこの企業でもしっかり把握してくれていた気がする。
まあ、転職ドラフトを使わずともそのような対応をするべきではあるんだけど、特にその割合が高かった印象がある。

転職ドラフトへの改善要望

  • 過去の指名状況を確認できddるのか?
    希望年収を伏せて2回目に参加したら、そのレンジに収まった。

  • メッセンジャーのUIがやや使いづらかった
    スマホブラウザだとレイアウトが崩れることがあった。

転職ドラフトを使ったことがない方へのメッセージ

  • チャネルを増やすという意味では登録するだけでもいいのでは思った

ドラフトに参加しなければメッセージが飛んでこないので、よくある転職サイトに登録した後に大量にメールが飛んでくるみたいなことがないの良い点ではないかと思います。

P.S.

転職ドラフトは「転ドラ」と略す文化を観測した。

転職活動を振り返る:MLエンジニアとしての転職

TL;DR

@takaishikawa42 です。いわゆる転職エントリを意識して書きました。

  • 転職しました
  • こういう経歴で、こういった転職活動しました
  • 転職活動の感想と、今後の抱負について

(※ 長くなりました。長文を簡潔に書く能力が欲しいです。)


転職活動以前

経歴

  • 人文系学部卒で2016年に新卒で不動産会社に入社。ウェブマーケテイング周りに従事
  • 2017年春頃からプログラミング未経験から「データ分析エンジニア」として社内データを活用したプロジェクトに参画。業務と並行して独学で R・Python を学習。
  • 直近では機械学習モデルの構築・運用等

大学は人文系学部卒で、新卒で入社したのは上場を機にITに力を入れていこうとしている不動産会社(賃貸管理等)でした。ここに入社した経緯は紆余曲折あるんですが、決め手としては若い会社*1で色々手を出していて、自分も色々出来そうで面白そうだと感じたためでした。

1年目はウェブマーケティング周りのことをしていました。自社のサービスの LP にユーザーを誘導して資料請求につなげることを目指すのが主なミッションでした。

これを丸1年していたのですが、この仕事に対する限界を自分の中で感じていたところ、現在のCTOに声をかけてもらって、2017年の春頃からデータ分析を行うことをミッションとするチームに異動になりました。社内データを活用してアレコレやるというフワッとしたミッションでした。

そこからプログラミング未経験ながら、業務と並行して独学で R・Python を学習し始めました。業務時間にもプログラミングそのものの勉強もさせてもらえたのはありがたかった。そんなこんなで直近では機械学習モデルの作成・運用等を行ってました。AWS 使ってモデル更新の自動化のアーキテクチャを構築したりもしていました。

そういった独学の一環として、勉強になると噂に聞いて知ったのが Kaggle でした*2。人のコードを見ることもできるし、スキル向上の一助になればと始めて、今では Kaggle 中心にデータ分析/機械学習を勉強しています。

きっかけ

  • 元から3年経ったら辞めようと漠然と考えていた
  • エンジニアになって強い人が周りにいる環境のほうがいいと思った
  • 会社で色々ゴタゴタがあって実際に動き始めた

転職をしようと思った理由は色々あるのですが、直接のキッカケは会社で色々ゴタゴタがあったことです。割と大きめの「ゴタゴタ」で、元々考えていたのですぐに動き始めました。 とはいえ、初めての経験だったのでその後結構時間がかかりました。

直接のキッカケはそんなことなんですが、入社当時から3年経ったら辞めようと漠然と考えていたことに加え(月並み)、エンジニアになってからは強い人が周りにいる環境のほうがやはり良いと思うようになったこともあります。ここでいう強い人というのは、データ分析とか機械学習とかそういった文脈でのエンジニアを想定しています。社内にはこの分野の人間がほぼ皆無だったので、このあたりも独習する必要がありました。

上司にも相談したんですが、「社内の強い人がいたとして、その人に教えを乞うているだけでは結局スキルは伸びない」的なことを言われてしまった記憶があります(曖昧)。確かにその通りなんですが、その時点ですでに業務外で独学している中で「このペースでの成長速度ではヤバイ」という焦りがあったので、正しいけど自分には当てはまらないと感じました。(ただしこの方にはめちゃくちゃお世話になりました)

最終的には上述の「ゴタゴタ」があったため、人員計画の大幅な見直しによる新規の採用ストップが想定されたので、これ以上社内環境の改善はないと自分の中で見切りをつけ、転職に踏み切ったという流れもあります。


転職活動最中

初期 エージェントとの接触

  • 9月から1月まで、足掛け5ヶ月
  • エージェント 職務経歴書のチェック
  • エージェント 大企業への推薦、実力不足

そんな経緯で2018年の秋ごろから転職活動をはじめました。結局、翌年(今年)の1月までやっていたので、足掛け4ヶ月ちょい転職活動をしていたことになります。めちゃくちゃ時間がかかりました。

最初は転職エージェントを使いました。日本で某データ分析コンペを主催している企業が合わせてやっている人材サービスです。結論から言うと、このサービスは自分には合いませんでした。理由としては、いわゆるトラディショナルな大企業やその子会社の案件が多く、自分の希望に合わなかったり、先方の想定するスキルに対して「経験不足」で断られたりといったものでした。エージェントの方自体は親切かつフランクで良い人でした。結局は職務経歴書を人に確認してもらっただけになってしまいましたが。

中期 「データアナリスト」ポジションを目指す

  • Wantedly スカウトが来たところに面談、規模や業界問わず
  • 色々な企業が色々な要件で、データ基盤構築・予測モデル構築・KPI改善のためのデータ分析、自分の目指す目標を決めることが重要
  • データアナリストで応募するが、要件に合わず

そこから、当然といえば当然なんですが、案件にリーチできるチャネルの数を増やさないといけないなと考えるようになりました。ただし、大手の転職サービスはあまり評判が良くないイメージが勝手にあったので、 Wantedly を使うことにしました。スカウトが来たところに対して、規模や業界問わず面談をしていました。面談・面接をした企業数だけ見れば、15~20社くらいは話したり話を聞いたりしました。(おかげで話するのが苦手ですが、どんな質問でも淀みなく話せるようになりました。)

様々な企業と面談する中で、多くの企業がデータ分析を行おうとしていて、データ基盤構築・予測モデル構築・KPI改善のためのデータ分析など様々な求人があるなと感じました。ということで、これも当然といえば当然なんですが、「自分が何をしたいか・どういった将来像を描いているか*3」を考えて言語化することが何よりも重要だと感じました。

まあそんな感じで、そこからいわゆる「データアナリスト」を志向したのですが、これも経験不足といった理由であまりマッチしませんでした。やはり、データを使ってどのような企画を行い、実際にどのようなビジネスインパクトを残したか、その経験を強く求められました。また、SQL を実務でほとんど使ってなかったことも良い印象を与えられなかったと思いました。

後期 「エンジニア」としての採用を目指す

  • 方向転換し、既存のキャリアをベースに幅を広げられそうなポジションや企業を探す
  • 転職ドラフトでアプローチもらった企業の中から複数の最終面接・オファーをもらい、元々の希望が叶えられそうな企業のオファーを受ける
  • 広告周りでアレコレしていく

「経験不足」という壁に幾度となく阻まれたので、戦略を変えて既存のエンジニア路線を軸に置いて、それ以外のことも取り組むこともできる(そして求められる)ようなポジション・環境を求めるようになりました。

ほどなくして、転職ドラフトというサービスを通して、いくつかの企業と面談しました(ただし、オファー締切間近に応募してきた企業は除く)。結果的に、複数企業で最終面接に進んだり、オファーをもらうことができました。悩んだ挙げ句、元々の希望*4 が叶えられそうな企業のオファーを受けることにしました。今後はIT企業(?)で広告配信周りでアレコレしていく予定です。


転職活動以後

感想

転職活動をしている中で強く思ったことなんですが、他人にアドバイスを求めるのは非常に難しいと感じました。色々な人に話を聞いても、結局人それぞれが持つ性質・スキル・目標があまりに千差万別なので、ある人の助言それ自体が的を得ていても、自分に当てはまるかどうかは別問題です。なので最後には自分でよくよく考えてリスクを取って行動するしかないと思うに至りました。「どんな人でもある種のポジショントークからは逃れることができない」という言葉を聞いたことがあるのですが、まさにそれだなと。

まあ、自分の人生に責任持ってくれるのは自分だけってやつですね。

今後の抱負

Kaggle はなんとなくの興味から始めたんですが、ある時点では戦略的な動機から取り組んでいた面があります。つまり、転職する際にはある程度の(最低限の)スキルがあることを示す客観的な指標があると便利だろうと考えて、何よりもまずメダルを取って、ひとまず最低ラインの Tier になることを目的にしていたのは否めませんでした。

でも実際、Kaggle Expert は話のネタにもなって、良い印象を与えるのに寄与したかなと思っています。今後は、引き続き精進して、銀メダル以上を取得、それと Master になることを目標にしたいと思っています。


P.S.

現職は2月末日で退職、次の会社は3月1日からで、これを書いてる現在は有給消化中です。

朝っぱらからジム行ったりこまめに家事を片付けながら、Kaggle やったり Udemy で買ったままになっていた講座を見たり、それから coursera の Kaggle コースにも集中的にトライしてみようと画策しています。

朝の通勤電車に乗らず、のんびりマイペースに生活できるのはいいですが、前日に翌日のやることを決めておかないとダラダラと1日を溶かしてしまうことが分かったので、期限付きのお休みの使い方は難しいと感じています。

*1:入社前後で上場、在籍中に1部に鞍替え

*2:本格的に取り組むキッカケになったのは、メルカリコンペのクロージングイベントで登壇者や参加者の異様な熱量に触発されたことでした。「人間性を捧げる」という言葉が印象的でした。

*3:現状におけるオボロゲな将来像としては、データ分析に関して幅広いスキルを身につけることで、組織に依存しないでも生きていける人間になりたいです。そのために、機械学習モデルの開発スキル全般を伸ばしていきたいです。具体的には、ビジネス領域から逆算してデータ分析・機械学習モデルを活用できるようになる一方、データ基盤のこともちゃんと分かるようになることを当面目指しています。(トガった強みがないのでバランスよくスキルを身につけて生き抜こうという魂胆の裏返しですが)

*4:データ分析/機械学習のことがわかる人間がいること。エンジニアリング領域にとどまらずビジネス領域にも関われそうなこと。

joblib と pickle の比較

これは何か?

  • pickle でファイル保存できるのとほぼ同じ書き方で保存できる joblib.dump/joblib.load について調べた。
  • pickle と joblib の比較について。compress を使うと joblib の方がファイルサイズが小さくなる。読み込み速度も変わるかもしれない。
  • joblib の compress の値は3程度が良いと言われているが、実際調べてると大体3くらいでちょうどいいことが確認できた。

背景

pickle 以外にファイル保存する方法について調べていて、 joblib がほぼ同じ書き方で圧縮保存できて便利そうだということで興味を持った。
そこで、メモ代わりに joblib について書いてあるブログ記事を twitter でシェアしたところ、意外と反応が(自分の中では)大きかったので、色々調べてまとめてみたという話です。

詳しくはドキュメントをどうぞ。

joblib.dump — joblib 0.13.1 documentation

pickle と joblib の比較

以下、jupyter 上で実行していると思ってください。

import os
import sys
import pickle
import joblib
import numpy as np
from keras.datasets import cifar10

(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = np.array(x_train, dtype=float)  # ファイルサイズを大きくするため無駄に型変換(int->float)
x_train_size = sys.getsizeof(eval("x_train")) / (1024*1024)
print("memory size of X: {} Mb".format(round(x_train_size,1)))
memory size of X: 1171.9 Mb

まず、適当なある程度の大きさのファイルを用意したかったので、Keras にある cifar10 のデータを持ってきて、データ型を int から float に変換しました。ファイルサイズが見事(?) 1GB 程度になったので、これを用いてオブジェクトの保存等を行っていきます。

保存時間

pickle

%%time
with open("x_train.pickle", mode="wb") as f:
    pickle.dump(x_train, f)
CPU times: user 1.9 s, sys: 5.57 s, total: 7.46 s
Wall time: 13.3 s

joblib

%%time
with open("x_train.joblib", mode="wb") as f:
    joblib.dump(x_train, f, compress=3)
CPU times: user 22 s, sys: 1.2 s, total: 23.2 s
Wall time: 24.1 s

両者を比較すると圧縮している作業分、joblib.dumpの方が実行時間がかかっています。

ファイルサイズ

def getsize_filepath(path, filename):
    print( "{}: {} Mb".format(filename, round(os.path.getsize(path)/(1024*1024),1)) )

getsize_filepath("x_train.pickle", "pickle")
getsize_filepath("x_train.joblib", "joblib")
pickle: 1171.9 Mb
joblib: 225.0 Mb

compress が効いているため、このケースだと4倍以下にまで圧縮されています。

読み込み時間

pickle

%%time
with open("x_train.pickle", mode="rb") as f:
    x_train = pickle.load(f)
CPU times: user 469 ms, sys: 3.5 s, total: 3.97 s
Wall time: 6.93 s

joblib

%%time
with open("x_train.joblib", mode="rb") as f:
    x_train = joblib.load(f)
CPU times: user 3.42 s, sys: 619 ms, total: 4.04 s
Wall time: 4.36 s

ファイルサイズが小さくなると読み込み時間に差が出るのかはいまいち検証できませんでした。何回か実行してみると、それぞれ時間にバラツキがあって pickle の方が読み込み早かったりするケースもあったので。

joblib の compress について

pickle との比較の結果 joblib も悪くないなと思うに至りました。次に疑問に思ったのが、 compress の値をいくつにするかということです。
公式によると0~9の値が取りうる値で、3が推奨されているようです。(他にも compress の指定の仕方はあるが割愛)

そこで、 compress の値を変えると、dump の時間・ファイルサイズ・load の時間がどのように変化するかを調べてみました。

結果は以下のようになりました。

f:id:takaishikawa42:20190117224247p:plain
joblib の compress の値に対する dump time, file size, load time の比較

この結果を見ると、compress の値に 3 を推奨するのは納得という印象を受けました。今回のケースを見る限りでは、その値は大きくても 5 くらいが限界かなという感じです。(当然、個々のケースによって異なるかと思いますが)

まとめ

joblib は pickle と比較しても悪くなさそうで、joblib.dumpcompress の値は 3(推奨値)から 5 くらいが良さそうということがわかった。


今回分からなかったこと

  • dump/load 時のメモリの比較
  • 保存ファイルの拡張子名(pickle は .pickle とか .pkl とか使われているけど、joblib はどうするのが良いか。今回は判別しやすいので .joblib としたが、どういった名前にするのが良いか)
  • pandas の feather との比較



今回使ったコードはこちらに置いています。

notebooks/pickle_joblib.ipynb at master · takaiyuk/notebooks · GitHub

notebooks/joblib_compress.ipynb at master · takaiyuk/notebooks · GitHub

Folium: Python で地図可視化

TOC

要旨

  • folium を使うことで、Python で柔軟な地図可視化が出来る。しかもグリグリ動かせる
  • folium は日本語チュートリアルが少ないので、簡単なものを網羅的にまとめる
  • バージョンの更新で結構使い方が異なっているので、公式ドキュメント以外を参考すると上手くいかないことがある


概要

Folium は javascript ライブラリの leaflet を、Python で使えるようにしたモジュール。これを使うことで Python で柔軟な地図可視化の操作ができるようになります。

ただ、日本語版チュートリアルが少なく、またバージョンが 0.1 とか 0.2 の頃と現在とではマップにピンを置く関数名や使い方が大きく異なっています。余談だと、2017年時点では version0.5 だったのが、2018年に入ってから version0.6 となり、2018年11月に見たら version0.7 になっていました。

こういった事情で、公式ドキュメントを見る必要があるが、そうするとますます日本語ドキュメントの数が少なくなったり。そこで、簡単なものだけでも網羅的に使い方をまとめたいと思った次第です。


実装

Install

インストールは pip でも何でも。

# install if not installed
$ pip install folium

# upgrade if not latest edition
$ pip install folium -U

Getting started

ここからのコードの実行は基本的には jupyter 上で行われていると想定してください。実行後、即表示されるのでオススメです。

そうでなければ、一度htmlで保存する必要があります。

import numpy as np
import pandas as pd

import folium
from folium import plugins
print( "folium version is {}".format(folium.__version__) )

folium version is 0.7.0 と出てきます。

使用するデータ

今回使用するデータは、Kaggle のリクルートコンペの hpg_store_info.csv です。緯度経度が入っていて、適当な大きさのデータだったためです。

Recruit Restaurant Visitor Forecasting | Kaggle

中身は、以下のような感じです。

hpg_store_id hpg_genre_name hpg_area_name latitude longitude
0 hpg_6622b62385aec8bf Japanese style Tōkyō-to Setagaya-ku Taishidō 35.643675 139.668221
1 hpg_e9e068dd49c5fa00 Japanese style Tōkyō-to Setagaya-ku Taishidō 35.643675 139.668221
2 hpg_2976f7acb4b3a3bc Japanese style Tōkyō-to Setagaya-ku Taishidō 35.643675 139.668221

適当な1つの緯度経度地点が欲しいので、以下のように定義しておきます。

LAT = data.latitude.iloc[0]
LNG = data.longitude.iloc[0]

地図を表示する

まずは、地図を表示させます。

そのためには、以下のようにするだけです。

m = folium.Map(location=[LAT, LNG])
m

map_quickstart
map_quickstart

location 引数は、表示する地図の中心点となります。

HTMLで保存

ちなみに、HTML 保存する場合も以下のように簡単にできます。

filename = 'output/map.html'
m.save(filename)


Tile

Tile とは地図のベースとなるものです。

デフォルトは Open Street Map というオープンライセンスのマップタイルが利用されます。

Tile については、説明するより見比べると理解しやすいと思います。

stamen-toner

m = folium.Map(location=[LAT, LNG], tiles="Stamen Toner", zoom_start=13)
m

f:id:takaishikawa42:20190111233332j:plain
map_tile_stamen-toner

stamen-terrain

m = folium.Map(location=[LAT, LNG], zoom_start=12, tiles="Stamen Terrain")
m

f:id:takaishikawa42:20190111233357j:plain
map_tile_stamen-terrain

tile 引数で指定できます。かっこいい感じになりますね。

ちなみに、zoom_start 引数で最初のズームレベルを指定できます。こちらは指定しないと、「いい感じ」にしてくれるようです。ズームレベルは、1~20の整数値で指定し、1が最もズームアウトした状態、20が最もズームインした状態です。

あと、普段使いませんが、Mapbox も API Key を渡して使用することができるようです。


Markers

Simple Marker

最もシンプルなMarker。
ホバーやクリックで情報を表示する設定もできます。

m = folium.Map(location=[LAT, LNG])
folium.Marker(
    location=[data.latitude.iloc[100], data.longitude.iloc[100]],
    popup="This is Simple Marker",
).add_to(m)
m

引数 popup で、クリックしたときに表示する情報を追加できます。

folium.Marker() でマーカーを作るだけでは地図に表示されず、これを add_to(m) することで、地図に情報を追加できます。このあたりの書き方が最初慣れず使いにくく感じました。

f:id:takaishikawa42:20190111233426j:plain
map_simple-marker

Marker with icon

工夫したい場合は、Marker の色を変えたり、アイコンを設定できたりできます。(Leaflet.R でいうところの AwesomeMarker です)

m = folium.Map(location=[LAT, LNG])
folium.Marker(
    location=[data.latitude.iloc[100], data.longitude.iloc[100]],
    popup=data.hpg_genre_name.iloc[100],
    tooltip="<i>" + data.hpg_area_name.iloc[100] + "</i>",  # HTMLタグも使える
    icon=folium.Icon(color="red", icon="info-sign"),  # 色やマーカーのアイコンを変更できる
).add_to(m)
m

f:id:takaishikawa42:20190111233448j:plain
map_marker-icon

Circle and Circle Marker

ピンのマーカーではなく、円をマーカー代わりにできます。

m = folium.Map(
    location=[data.latitude.iloc[0], data.longitude.iloc[0]],
    tiles="Stamen Toner",
    zoom_start=10,
)
folium.Circle(
    radius=500,
    location=[data.latitude.iloc[100], data.longitude.iloc[100]],
    popup="<b>" + data.hpg_genre_name.iloc[100] + "</b>",
    color="crimson",
    fill=False,
).add_to(m)
folium.CircleMarker(
    location=[data.latitude.iloc[200], data.longitude.iloc[200]],
    radius=50,
    popup=data.hpg_genre_name.iloc[200],
    color="#3186cc",
    fill=True,
    fill_color="#3186cc",
).add_to(m)
m

f:id:takaishikawa42:20190111233509j:plain
map_circle

その他:lat/lng popovers

クリックした点の緯度経度をポップアップ表示します。

m = folium.Map(location=[46.1991, -122.1889], tiles="Stamen Terrain", zoom_start=13)
m = m.add_child(folium.LatLngPopup())
m

f:id:takaishikawa42:20190111233541j:plain
map_popover

その他:click-for-marker

クリックした点にMarkerを生成します。

m = folium.Map(location=[46.8527, -121.7649], tiles="Stamen Terrain", zoom_start=13)
folium.Marker(location=[46.8354, -121.7325], popup="Camp Muir").add_to(m)
m = m.add_child(folium.ClickForMarker(popup="Waypoint"))
m

f:id:takaishikawa42:20190111233600j:plain
map_click-for-marker

MarkerCluster

Leaflet.R と異なり、Folium.plugins.MarkerCluster で使える点に注意。ドキュメント探すときは、plugins の項目内にあるので探します。

folium.plugins.FeatureGroupSubGroup でクラスターをグループ分けできる

今までは、1点のみをマーカーを貼っていたが、複数の地点をマークしたい場合は、numpy の array 形式で渡してあげます。

locations = np.array([data.latitude, data.longitude]).T

m = folium.Map([LAT, LNG], zoom_start=4)
plugins.MarkerCluster(locations).add_to(m)
m

f:id:takaishikawa42:20190111233615j:plain
map_marker-cluster

FeatureGroup

Creation, International cuisine, Japanese style の3つのジャンルがデータ中で最も多いジャンル名だったため、これらの地図を作成することにする。

def create_feature_group(genre):
    data_genre = data[data.hpg_genre_name == genre]
    arr_latlon = np.array([data_genre.latitude, data_genre.longitude]).T
    feature_group = folium.FeatureGroup(name=genre)
    plugins.MarkerCluster(arr_latlon).add_to(feature_group)
    return feature_group


m = folium.Map(location=[LAT, LNG], zoom_start=12)
genres = ["Creation", "International cuisine", "Japanese style"]
for genre in genres:
    fg = create_feature_group(genre)
    fg.add_to(m)
folium.LayerControl().add_to(m)
m

f:id:takaishikawa42:20190111233710j:plain
map_feature-group


DataFrame 内の全要素をプロットする

m = folium.Map(location=[LAT, LNG], zoom_start=10)
data_nodup = data.drop_duplicates(subset=["latitude", "longitude"])
for lat, lon in zip(data_nodup.latitude, data_nodup.longitude):
    folium.Marker(
        location=[lat, lon],
        popup="/".join([str(lat), str(lon)]),
        tooltip=str(lat) + "_" + str(lon),
    ).add_to(m)
m

f:id:takaishikawa42:20190111233729j:plain
map_df


その他

感覚的には、ポップアップをつけたMarkerを5000以上プロットしようとすると、固まるイメージ。 Vincent/Vega and Altair/VegaLite Markers を使用する際も、渡す地図情報のサイズを重すぎないようにすることが大事かも。 (具体的にどれくらいメモリを使っているのか、どのように処理が進んでいるのかは調べていない)

LeafletRでは、マップオブジェクトにパイプ( %>% )で付加情報を足していって地図を生成する Folium の場合だと、Mapオブジェクトをはじめに定義して、その後各種要素を作って add_to(m) していく

ただ、クラスターに関しては、LeafletR ではaddMarkerの引数でオプションを指定したが、 Folium ではMarkerCluster オブジェクトを別で作成して add_to(m) する必要がある

LeafletR は、kazutan さんのサイトを参照するのがいいと思います



github.com

Kaggle Tokyo Meetup #5 レポート

編集中


概要

スケジュール

時間 タイトル 発表者
13:00 – 13:30 開場
13:30 – 13:50 Opening Talk threecourse
13:50 - 14:30 Avito 9th Place Solution & 中国Kaggle事情 senkin13
14:30 - 15:10 Home Credit 2nd place solutions ~ お通夜から始まったドラマ ~ ikiri DS
15:10 - 15:30 休憩
15:30 - 16:10 Santander Value Prediction Challenge 17th Solution keeeeei79
Santander 8th Solution~気合いでとったソロゴールド~ currypurin
16:10 - 16:50 PFDet: 2nd Place Solution to Open Images Competition PFDet
16:50 - 17:10 休憩
17:10 - 19:00 LT(飲みながら?)
スポンサーセッション Indeed
LightGBMを少し改造してみた RK
日本初の競技機械学習大会"Cpaw AI Competition"の運営 shunk031
kaggler-ja driven learning upura
RSNA Pneumonia Detection Challenge 6th Place Solution PFNeumonia
TGS Salt Identification Challenge 12th place solution catla
19:00 - 21:00 懇親会

所感

  • 会場に行って話を聞けてよかった
    • スライドの内容の理解が深まった
    • 質疑応答の学びが深かった
    • Kaggler ならではの笑いのツボを共感できた(「結果を1.1倍したら上手く行った」に対してドッと笑いが起きる感じ)
  • 自分のレベルを上げて行かないと意味がない
    • スゴいスゴいと言ってるばかりでは意味がない
    • おれもちゃんと Kaggle やって、金メダルを獲って発表する

発表

(序盤は生かせそうなところをメモ取ってたけど、ikiri_DS でキャパオーバーして、twitter に専念した。そのため、後半はほとんどメモ取っていない)

Avito 9th Place Solution & 中国Kaggle事情

www.slideshare.net

  • パイプライン
    • 問題理解
    • 実装
      • テーブルデータとテキストデータ、それぞれ分けてベースラインモデルを作成
      • パラメータチューニングは手動で(探索の実行に時間が掛かる)
    • アンサンブル
  • Feature Enginnering
    • Aggregation: 業務視点で作ると効率が良い
    • Interaction: ビジネスセンスがある加減乗除特徴量が強い(全体との差とか)
    • Inpute Null Values: 0, median, モデル予測値などで埋める
      • Magic Feature: df['price'] - df[RNN(text) -> price]
    • Word Embedding
      • Pretrain の Embeddings よりも、与えられたテキストで学習した embeddings の方が有効。おそらく、商品名などの固有名詞が目的変数に効いたため
  • Parameter Tuning
    • objective: 'xentropy'. 目的変数が2値分類の確率変数のようだったため
    • feature_fraction: 0.1. テキストベクトルがスパースなため
  • Model Ensemble

    • 様々なモデル、様々な損失関数を利用した(Diversity が精度向上に貢献する)
    • 黒魔術:アンサンブルを「各モデルの重み付き線形結合」として定式化。パラメータを LB Score から近似計算(ref: "linear quiz blending" で検索)
  • 質疑応答

    • ツリーモデルだと、線形結合を表現できない
      • そのため、加減乗除の特徴量生成が精度向上に効いてくる
      • 勾配ブースティングは加減は表現できうるが、乗除は表現できないのでそのような特徴量生成が効く


Home Credit 2nd place solutions ~ お通夜から始まったドラマ ~

  • ロバストなモデルを作成することを意識した

    • Stacking や 全てのモデルを Blending することはやめて、 CV と Public の様子から3つに分けて作ったモデルをさらに Postprocessing した
    • PRML のモデル選択の項を参照
  • 特徴量生成

    • aggregation
    • findings
    • NN(カテゴリデータは Embedding)、RNN で時系列特徴の獲得
    • DAE(Denoising AutoEncoder): Porto Segro で Michael Jahrer が使ったものを実装
      • RankGauss, Swapnoise など
      • NN への Input として利用
  • Finding

    • train/test で同一人物
    • 金利
    • 時系列でのズレ
  • 過学習を防止する

    • num_leaves を大きくしない(32くらい)
    • boosting は Dart(=Dropoutさせながら木を作る手法)が有効
    • 目的関数に対するペナルティ(Lp norm)を大きくすると改善傾向
  • blending

    • Result の CSV の相関行列を見て、似ているものは混ぜないようにして、結果の多様性を担保する


Santander Value Prediction Challenge 17th Solution

www.slideshare.net


Santander 8th Solution~気合いでとったソロゴールド~

www.dropbox.com

  • 参加するコンペを選択する戦略
  • コンペ反省会


PFDet: 2nd Place Solution to Open Images Competition

www.slideshare.net

  • PFN がコンペに出た理由:DLの進歩においてコンテストが重要な役割を果たしてきたことが多いため
  • V100(32GB) を 512 枚積んでいるのは日常


LT

スポンサーセッション

  • Indeed は Kaggle をするのにとても良い環境


LightGBMを少し改造してみた

www.slideshare.net


日本初の競技機械学習大会"Cpaw AI Competition"の運営

  • GM や Master も参加経験あり
  • 彼らでもエグい量の問題を出しているらしい(しかも環境構築から)
  • 精力的に活動しているように感じた
  • スポンサー募集中


kaggler-ja driven learning

  • kaggler-ja に 「日本のデータ分析コミュニティ」であり続けてほしい
    • 日本の R コミュニティになぞらえて?
    • タイトルを見たときに Yutani さんのスライドを思い出した
  • 参加する / 質問する / 回答する
    • 参加するだけで知見を得られる(日本語)
    • 「良い質問」をする (minimal reprex):質問は偉い
    • 回答するのが一番勉強になる
  • Win or Learn, You Never Loss

RSNA Pneumonia Detection Challenge 6th Place Solution

............................................


TGS Salt Identification Challenge 12th place solution