ysaz (ImanazaS) blog

最近はデータ分析や機械学習が中心。たまに音楽や書評。

(続)Pandasでのデータ集計

以前に以下の記事でPythonのPandasライブラリを使った集計方法をまとめていた。
nami3373.hatenablog.com

今になって、これよりもシンプルなやり方に気づいたのでメモ。

import pandas as pd

# データフレームを適当に作成
df = pd.DataFrame(data=[('2016-04-01', 'a', 10.2, 60.3), ('2016-04-02', 'a', 16.4, 71.7), ('2016-04-01', 'b', 11.1, 65.6), ('2016-04-02', 'b', 15.1, 73.2)])
df.columns = ['date', 'id', 'temp', 'humidity']

# pivot tableで集計
df1 = df.pivot_table(index='date', columns='id', values=['temp', 'humidity']).reset_index()
df1.columns = [s1 + '_' + s2 for (s1, s2) in df1.columns]
df1.rename(columns={'date_':'date'}, inplace=True)
df1.index.name = None

df1

結果は以下の通り。

date humidity_a humidity_b temp_a temp_b
0 2016-04-01 60.3 65.6 10.2 11.1
1 2016-04-02 71.7 73.2 16.4 15.1

以前のやり方は3つのステップを踏んでいたが、1ステップで同じ結果が得られるので、こちらの方が楽かと。
カラム名を補正してやる手間は残りますが)

カテゴリカル変数のEncoding手法について

Structured Data(構造化データ)の下処理をおこなう際に避けて通れないのがFeature Engineering(特徴量エンジニアリング)。
特に悩ましいのがカテゴリ変数の扱いで、どのように扱えば良いか困ることが多く、また、使った手法もすぐに忘れてしまいがちなので、自分なりに整理して記事にまとめておきたいというのが趣旨。

1.よく使われる手法

まずはよく用いられる定番の手法から。次元を増やすかどうかで大まかに次の2つに分類できる。

・次元を増やさない場合(Label, Count, LabelCount, Target)
・次元を増やす場合(One-Hot, Entity Embedding)

これらの手法について、python上の実装例とともに見ていきたい。
用いたライブラリは主にPandas、少しScikit-Learn。

2.次元を増やさない場合

Label Encoding

最もシンプルな手法で、与えられたカテゴリ変数に数字を割り当てるもの。
たとえば、「東京」、「大阪」、「名古屋」というカテゴリに対して、
0、1、2といったようにラベルをふって単純に数値化するだけ。

以下、実行例。
Method 1はPandasを使ったもので、Method 2はScikit-Learnを使ったもの。

import pandas as pd
import numpy as np

d = {'city': ['tokyo', 'nagoya', 'osaka', 'tokyo', 'nagoya', 'osaka', 'tokyo', 'osaka', 'tokyo'],
	'target': [0, 1, 0, 1, 0, 1, 0, 1, 0]}

df = pd.DataFrame(d)
df['city'] = df['city'].astype('category')
df.dtypes

# method 1
df['label_enc'] = df['city'].cat.codes

# method 2
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df['label_enc'] = le.fit_transform(df['city'])
Count Encoding

これも比較的シンプルな手法で、データに含まれるカテゴリ変数の出現回数を数えるもの。

以下、Pandasのgroupbyを使った実行例を2つ(Method 1, 2)。

# method 1
count_mean = df.groupby('city').target.count()
df['count_enc'] = df['city'].map(count_mean)

# method 2
df['count_enc'] = df.groupby('city')['target'].transform('count')
LabelCount (Count Rank) Encoding

次は上述したCount Encodingの応用編で、カテゴリ変数の出現回数が多い順に順位づけするもの。

以下、Pandasを使った実行例。

count_rank = df.groupby('city')['target'].count().rank(ascending=False)
df['count_rank'] = df['city'].map(count_rank)
Target Encoding

Mean EncodingやLikelihood Encodingとも呼ばれる手法で、目的変数(Target)に対してカテゴリ変数の処理をおこなう。
Kaggle等の機械学習コンペで頻繁に用いられている。

やり方としては、目的変数が2値のみをとるブーリアン型であればカテゴリ毎の確率を、目的変数が数値であればカテゴリ毎の平均を取るというもの。

ただし、データセットが学習データとテストデータに分かれている場合、テストデータの目的変数は未知であるため、演算は学習データに対しておこない、得られた特徴量をテストデータ内のカテゴリへ適用する。

この部分に注意しないと、Cross Validation をおこなったときにデータがリークすることになってしまう。

以下、Pandasのgroupbyを使った実行例を2つ(Method 1, 2)。

# method 1
target_mean = df.groupby('city').target.mean()
df['target_enc'] = df['city'].map(target_mean)

# method 2
df['target_enc'] = df.groupby('city')['target'].transform('mean')

3.次元を増やす場合

One hot encoding

データ中に存在するカテゴリ変数の数だけ次元(構造化データでいうと、新しい列)を用意して、各行に含まれているカテゴリ変数に対応する次元を1に、それ以外を0にする方法。
これにより、カテゴリ変数を単に連続する数としてではなく、独立する値として扱うことが可能となる。

以下、実行例。
Method 1はPandasを使ったもので、Method 2はScikit-Learnを使ったもの。
ただしMethod 2ではカラム名が自動で返ってこないため、何らかの形で指定してあげる必要がある。

# method 1
oh_enc = pd.get_dummies(df['city'])
df = pd.concat([df, oh_enc], axis=1)

# method 2
from sklearn.preprocessing import OneHotEncoder
oh_enc = OneHotEncoder(sparse=False)
df[['nagoya', 'osaka', 'tokyo']] = oh_enc.fit_transform(df.label_enc.reshape(len(df.label_enc), 1))


ここまでの実行結果は下表の通り。

city target label_end count_enc count_rank target_enc nagoya osaka tokyo
tokyo 0 2 4 1.0 0.250000 0.0 0.0 1.0
nagoya 1 0 2 3.0 0.500000 1.0 0.0 0.0
osaka 0 1 3 2.0 0.666667 0.0 1.0 0.0
tokyo 1 2 4 1.0 0.250000 0.0 0.0 1.0
nagoya 0 0 2 3.0 0.500000 1.0 0.0 0.0
Entity Embedding

KaggleのRossmannコンペで有名になった手法。
これまで挙げてきたものとはややアプローチが異なるが、ニューラルネットワークでカテゴリ変数に対応する重み行列を学習させ、その重み行列とOHEの積をとったもの(と理解している)。
最終的には数値データと合わせてニューラルネットワークの全結合層に放り込むこととなる。

KaggleのPorto Seguroコンペデータを使った実験を行い、以下Githubに公開したので、参考までに。

github.com

次のステップとして、抽出した重み行列を特徴量としてGradient Boosting Decision Tree (GBDT) にうまくまぜられないかと思案中。

4.参考記事

参考にしたのは以下のblog記事。とてもうまくまとめられており、インスパイアされるきっかけとなった。

jotkn.ciao.jp

あとは、英語だが以下の記事もEncodingについて体系的に書かれており、非常に役に立った。

pbpython.com

Entity Embeddingについてはこの記事を参照。

puyokw.hatenablog.com

最後に、最近発売されたこの本。大全というだけあって網羅的に整理されており、辞書的な使い方ができるのがよい。

機械学習や人工知能、AI関連分野を取り巻く情勢

機械学習人工知能、AI関連の記事で気になったものをピックアップし、Google Keepにメモする習慣をつけている。

 

メモに残す記事の数はだいたい一日2、3件。

(日本語英語問わずチェックしているが、ひと目見た時の情報量の多さから、日本語記事をメモに残しがち)

一ヶ月半強分のメモが貯まってきたので、どんな記事に興味を示す傾向があるのか、少し振り返ってみたい。傾向としては大きく以下の2つに分かれるようだ。

 

1.企業の活用事例

私自身が企業人であることから、世の企業がどんな風に新たな技術を取り込み、どんな商品やサービスを打ち出そうとしているのか、ビジネスを生み出そうとしているのかに関心を示すのは、自然なことと言える。

 

たとえば警察が犯罪の発生を予測するためにAI導入するとしているとか、製造業が不適合品の発見に予測モデルを適用しようとしているとか、そういう類のもの。

 

2.大企業の出資事例、ベンチャー企業の調達事例

時代の流れに乗りたい大企業は、自社で新たに技術を確立しようにも時間やお金がかかるし、そもそもそういったことができる人材が不足しているため、所謂オープン・イノベーション的なことを実現すべく、他社とのアライアンスや出資、さらにはM&Aをおこなうことが多い。

 

そもそもこうした背景には、今のAIブームのスピード感に付いていくためには、一から人を育成し事業を育てていても時間がかかるため、どんどん取り残されていくリスクが生じることがある。

他にも、世界の上場企業の中には、手元に膨大なキャッシュが積み上がっている一方で、有望な使い道が見つからないという「金余り」の状況が見受けられたりする。こうした背景から企業が投資先を積極的に探しているという事情も、ベンチャー企業への出資を後押ししていると言える。

 

一方のベンチャー企業側は、事業を拡大していく上で資金調達は必要なポイントなので、理想的なパートナーが見つかれば、出資を受け入れることを選択する。

 

日本国内でも数十億円規模の大型調達がちらほらと出ており、昨年末からの出来事を振り返っても、プリファードインフラストラクチャーの調達や、少し毛色は異なるが、FOLIOの調達案件などが話題となった。

 

こんな感じで引き続きアンテナを張って記事をチェックしていきたい。

ブログを綴るということ

去年取り組んでいたDeep LearningのMOOC (Massive Online Open Course) 、Fast.ai。

fast.ai · Making neural nets uncool again

ここではDeep Learningの概念や実装上の具体的な手法を学ぶとともに、学習を続けていく上でのマインドセットも鍛えられた。

(ちなみに、Fast.aiは昨年半ばから第二期に入っており、現在のフレームワークは、第一期に使われていたKeras (+ Theanoをbackend) から、PyTorchをベースとするfast.aiライブラリへ移行している。)

そんなFast.aiをJeremy氏と共に運営しているRachel氏のブログ記事を改めて読み、インスパイアされたので綴っておく。

medium.com

なぜブログを書くのか?

Rachel氏の投稿には、ブログ記事を書くことによって期待できる効果についてまとめられている。

その内容はというと、

  • とにかくブログを始めよう。ブログは学んだ知識を体系化し、自身の理解を促す。新しいアイデアを生み出すことに役立つ。
  • ブログは履歴書にもなり、多くの人と繋がれるきっかけをつくる。
  • 結果として、仕事のオファーや講演依頼を受けるようなこともある。
  • email等で良く飛んでくる質問に対する答えを書いておくことで、同じ質問が来たときにかかる二度手間を省くことができる。

というもので、。
 
実際にFast.aiを受講した人の中でも優れたブログ記事を書き上げている人が多数存在するらしい。
このようにブログは学習の場であると同時に、自身の成果や考えを自由に発信できる場であると言える。

とはいえ、そんなことは今更言われなくてもわかっているけれど、
書くことが思いつかなかったり、書くのが面倒くさくなったりと、続けられない理由ばかりが出てきてしまう。

ブログを書く上でのアドバイス

ブログ執筆を強く勧めるRachel氏も、書くネタもなければ、書くこと自体を苦手と捉えていて、最初はなかなか始められなかったそうだ。

そんな彼女からの、自身の経験を基にしたブログを綴っていく上でのアドバイスも、上述した記事の中で読者へのメッセージとして記されていた。

  • 自分の気に入ったブログ記事やニュース記事を集め、それらのリンクをリスト化する。
  • そして、特に重要と感じた部分を要約したり、強調したりして、サマリーを作る。
  • 学会・講演会や勉強会に参加した場合、そこで学んだことを要約する。
  • 完璧主義にならない。クオリティにこだわりすぎず、スピーディーな投稿を心がける。
  • 自分と同レベルの他の初級〜中級者が記事を読んだときに、その読者の理解を促すきっかけとなる。専門家はそのトピックに最初に触れた時、どのように理解に苦しんだのかを覚えていないので、専門家すぎる人より、ちょっと理解が進んだ程度の人が書いた記事のほうが、受け入れられやすかったりする。
  • 怒りや憤りを感じたら、なぜそう感じるかを言葉で説明してみる。


僕はこれまで自分の興味や関心を、思い立ったときに一貫性なくブログに綴ってきたが、例えば最近では機械学習ディープラーニング周辺の勉強を余暇の時間におこなっているので、学びのアウトプットの場としてもっとブログを活用していくべきと感じた。
(というか、書くことで学んだことを定着させるという学習スタイルをちゃんと確立したほうがいいと感じた。)

メモツールについて

Evernoteが1年半ほど前に無料版で2台までと利用が制限されてしまって以来、

しっくりくるメモアプリ探しを続けている。

 

ツールはあくまでツール。当たり前だけども何をやりたいのかを明確にすることは必要。

そもそもメモアプリの機能は淘汰されたり、サービスが中止になったりすることもありうるので、状況に応じてすぐに切り替えられる柔軟性も大事。フィロソフィーを持つというか。

 

とはいえ、使ってみることでアイデアが湧いてくると思うので、難しいことは考えすぎずに、色々と試している。

そんな自分のメモアプリを取り巻く状況について、まとめてみたい。

 

Evernote

以前は有料会員になるくらいのEvernoteヘビーユーザーだった。

取扱説明書や仕事に使えそうな資料などpdfは何でも送っていたし、気になったWebページのクリップ保存やToDoリスト、レシピメモに至るまで、メモというメモはEvernoteで管理していた。

残念ながら今はすっかり利用頻度が減ってしまっている。

昔はなくなったら困ると思っていたが、無くても普通に生きていけることが証明されてしまった。

 

OneNote

Evernoteの代替ツールの筆頭として、まずはEvernoteとの併用、その後の移行を試みたが失敗。

OneNoteEvernoteよりも動作が重い上、同期がうまくいかないといった使い勝手の悪さ、さらにはOffice 365の年会費を払わないとOneDriveの容量が確保できないなど、いろんな障壁を感じてしまって使いこなせなかった。

ただ、ノートを簡単に切り替えられるタブ機能は気に入っている。

 

Keep

タブなりノート間を切り替える機能が少なく不便な印象を受け、最初は少し毛嫌いしていた。

しかし、書いたメモをポストイットの様にホワイトボードへ貼っていくような感覚は視覚効果が高く、使いやすい。また、テキストメモなので、動作も軽く、同期も速い点はかなりポイントが高い。

ラベルを付けておけば簡単にフィルタリング出来るので、数が増えていっても

一番のポイントはToDoリストとGoogle Calendarとの連携がシームレスにおこなえること。

Gmail含め、Googleのエコシステムの中で完結させることが、なんだかんだ一番便利なのかもしれない。

 

Pocket

Evernoteでやっていた、気になるWebページをクリップし「あとで読む」ことについては、Pocketへ移行している。

EvernoteのWebクリップにはそこそこ時間がかかったのに対し、Pocketは一瞬なので重宝している。

また、スマホからでもストレスなくクリップできるので、最近は使用頻度がかなり高い。

 

Slack

メモツールというよりコミュニケーションツールだが、あらゆる情報を一つのプラットフォームへ集約するという点では有用と考え、一人Slackを開始。流行りのツールなので、触って慣れてみたかったというのもある。

トピックに応じてチャンネルを分ける機能や、ソースコードを埋め込める機能が気に入っている。

プログラミングや機械学習関係で気になったことはメモしたりもするが、どちらかと言うとRSSのFEEDを読み込んだり、関心の高いグループに参加して情報を取ったりという使い方がメイン。

 

最近では、下3つのアプリ・サービスを併用して使うのが自分の中で主流。

再現率と適合率

検索システムや検査などの識別問題でよく使われる「再現率」と「適合率」。

教科書に書かれている説明でなんとなく理解はしているものの、ふと思い出そうとしたときにどっちがどっちだったか曖昧になることが多い。

そこで、理解を深めることを目的に、わかりやすくまとめられている記事を探してみた。

 

mathwords.net

zellij.hatenablog.com

 

まず教科書上の定義からすると、以下の通りとなる。

  • 再現率(Recall)= 真陽性(True Positive)/ (真陽性+偽陰性(False Negative))
  • 適合率(Precision) = 真陽性(True Positive)/(真陽性+偽陽性(False Positive))

 

分子はどちらとも同じで、分母の一部が偽陰性偽陽性かが異なるだけ。ただ、数式中の記号をそのまま読み上げても、やっぱりイメージが湧かない。

そこで、参考記事の例を拝借し、病気の検査、犬の写真検索で言い表してみると、

  • 再現率:病気の人のうち、検査により本当に病気である人を発見できる確率。あるいは、犬の写真のうち、検索により犬の写真を正しく発見できる確率。
  • 適合率:検査により陽性反応が出たとき(検査結果)、それが本当に病気である確率。あるいは、検索により犬の写真が見つかったとき(検索結果)、それが本当に犬の写真である確率。

となり、実世界でこれら指標がどのような場面で適用されうるかも含め、少しクリアになった気がする。

 

ちなみに再現率と適合率はトレードオフの関係にあるので、その両方を加味して性能を評価するために、両者の調和平均(逆数の和の逆数×2)をとったF値が使われるケースもある。

Floydhubのセットアップから基本的な使い方まで

前回の投稿で、FloydHubというクラウドサービスへたどり着き、GPUを試すようになるまでの過程を綴ってみた。

nami3373.hatenablog.com

今回は、FloydHubの利用方法について、簡単にまとめてみた。

セットアップの流れ

まずは以下のサイトへアクセスし、アカウント設定をおこなう。
Floyd Zero Setup Deep Learning

アカウントが取れたら、pipを使ってCLIをインストールする。

$ pip install -U floyd-cli

その後、以下のコマンドでログインを入力するとWebブラウザでマイページへ移動する。
表示されたトークンをコピペすることでログインが完了。

$ floyd login

ローカル上でのプロジェクトソースコードの準備

使いたいソースコードがある場合は、cdで該当フォルダへ移動、
特にない場合は、チュートリアルにあるRNNなんかをgitからローカルへレポジトリする。
※以下、例としてチュートリアルから抜粋。

$ git clone https://github.com/floydhub/tensorflow-examples.git
$ cd tensorflow-examples/3_NeuralNetworks

新しいプロジェクト開始時はinitコマンドを使って定義する必要があり、以下のように入力する。

$ floyd init <プロジェクト名>

FloydHubでJupyter Notebookを使う

初期設定が終わったら、floyd runを使っていよいよ実行。
Pythonファイルをそのままサーバー上で走らせることも可能だが、
インタラクティブに作業が進められるJupyter Notebookの使い勝手が良いため、
個人的には--mode jupyterを加えてJupyterを立ち上げるのがオススメ。

具体的には、以下のようにコマンドを入力する。

$ floyd run \
   --mode jupyter \
   --env theano:py2 \
   --gpu

モードの次に--envを加えているが、これによりディープラーニングフレームワークPythonのバージョンを指定することができる。
上記の例の場合、TheanoとKeras 1.2.2をPython2環境で用意してくれる。

その他、詳しくは以下をご参照のこと。
Environments - FloydHub

また、runコマンドの最後に--gpuを加えているが、これによりGPUサーバーを使えるようになる。
GPUを必要としない作業を低コストで実行したい場合は、これを--cpuへ変更することで、CPUサーバーが利用可能。

ちなみに17年7月1日現在、GPUのコストは1時間あたり$0.4で、CPUはその10分の1の$0.04。

データセットのアップロード

データを新たにアップロードしたいときは、プロジェクトのときと同様に、initを使って初期設定をする必要がある。

$ floyd data init <ファイル名>

続けて以下を入力するとアップロードが開始される。

$ floyd data upload

アップロードが完了するとID名が発行されるため、以降呼び出すときはそのID名を使用する。

アップロードしたデータがzipファイルであるとき、
以下のコマンドを使うとJupyterを立ち上げることなく、CPUサーバー上で解凍してくれる。

$ floyd run --cpu --data <ID名> "unzip /input/<ファイル名>.zip -d /output"

なお、ローカルからアップロードするのではなく、どこかweb上のサーバーから直接Floydサーバーへダウンロードすることもできる。
この場合、floydでJupyter Notebookを立ち上げたあとTerminalを起動し、
wgetコマンドなどを使って指定したURLからデータをダウンロードすればよい。

アップロードしたデータの呼び出し

既にアップロードをし、IDを取得したデータを別のプロジェクトで使いたい場合は、
次のようにrunコマンドの中でデータのIDを指定すればよい。

$ floyd run \
  --mode jupyter \
  --data  <ID名>\
  --env tensorflow-1.0:py2 \
  --gpu

なお、プロジェクトファイルはoutputディレクトリにあるが、読み込んだデータはinputにある点に注意する必要がある。
そのため、ファイルパスを../inpu/で指定する必要がある。

プロジェクトおよびデータセットの消去について

同じプロジェクトでも立ち上げる度に新しいバージョンが作成され、
モジュール及びアウトプットの数が不必要に増えてしまうことがある。

そのため、ストレージの容量を減らしたいときは、少々面倒だが以下のようなコマンドを実行することになる。
なお、消去したい対象が複数あるときは、IDを続けて足していけば、一行でまとめて処理することが可能。

・プロジェクトを消去するとき

$ floyd delete <ID名>

・データセットを消去するとき

$ floyd data delete <ID名>

まとめ

プロジェクトの数が増えてくると管理が煩雑になることもあるが、
コマンドがシンプルだったり、ポータルサイトが使いやすかったり、
なにより100時間分の無料利用がついてきたりするので、手を動かしながらDeep Learningを学ぶ場として重宝しています。