LoginSignup
5
3

More than 1 year has passed since last update.

numpy の dtype変更だけでパフォーマンス改善できた話

Last updated at Posted at 2020-07-12

はじめに

numpy の計算効率化に取り組む機会があったのですが、
その中で dtype の重要さを学びました。

個人の備忘録としては勿論、誰かの役に立つといいな〜と思い
書いたものが本記事となります。

パフォーマンス前後比較

ザックリ書くと、

  1. サイズにして (1万,400) @ (400,55万) 程度の行列計算を複数個行う
  2. それらの積集合を取る

という処理を行う必要があったのですが、 numpy の dtype 変更だけで
以下のようにだいぶ改善できました。(最初はどんだけ酷かったんだよ…というね)

項目 before after
処理時間 70分 10分
使用メモリ 100GB超 30GB強

※ メモリはMacアクティビティモニタの「メモリ」の値より。
(jupyter カーネルが落ちるレベルだったが、余裕で生き残るようになりました)

以下、どんな変更をしたか書いていきます。

実施したこと

高速化について

numpy.array の dtype を float32 or float64 にする

numpy の行列計算では、 BLAS というものを使っているらしい。
で、どうやらこいつが上記データ型だといい仕事をしてくれる模様。
(参考リンクの一番上の記事参照)

自分の場合、最初 int になっていたのですが、.astype(np.float32)
float32 に型を変えただけで処理時間がなんと70分 → 10分に短縮されました!!

【追記】
一晩寝て読み直したら状況説明が不十分に思ったので、少し補足します。

元はpd.get_dummies で one hot encoding した後に、 .values
numpy.ndarray を取り出し計算していました。

このやり方だとデータ型が uint8 になるのですが、これを float32 に
変えてやることで高速化を実現出来ました。

コードにすると、こんな感じです。

pd.get_dummies([PandasのSerise]).astype(np.float32).values

省メモリについて

可能なら 行列の値を bool 値にする

前述の float 化で処理は爆速になりましたが、メモリが爆食いされてしまいました。
こまめに不要なオブジェクトをdelする等、抵抗をしていたのですが大きな改善に至らず。

しかし計算結果を bool に持たせ直すことで、めちゃくちゃメモリを削減できました。
(bool型 と int, float 型で予め確保しておくメモリが違うっぽいですね)

…でも、これをやると速度は少し失われます。メモリと速度、どっちをとるか?ケースバイケースで。

【補足】
python では1/0 と True/Falseに以下のような関係が成り立ちます

true_false.png

なので、1/0 で表現出来る行列(例えば one hot encoding 行列とか) は、
bool 値でも表現できます。

そしてあわせ技

以上、2点を組みわせただけで、大幅に効率化出来ました。
最終的には、こんな処理の流れになりました。

  1. float32 型で行列計算を行う
    arrayA = float32行列 @ float32行列
  2. 得られた結果を bool 型に変換する
    arrayA = (arrayA >= 1)とかやるだけ
  3. それらの積集合を取る
    result = arrayA & arrayB & arrayC みたいな

感想

float の方は色々なケースで使えそうだけど、 bool の方は限られるかな?
結果が 0/1 で表すことが出来るケースじゃないと適用できないと思うので。

その他

  • Cpython で numpy 型指定して書いてやれば超早くなるという記事も見ました。
    が、今回そこまでは実際に試しませんでした。
    必要となる機会があれば、試してみたいと思います。
  • Intel MKL の方がパフォーマンスが良いらしい ですが、実行環境がインテルCPU
    とも限らないので、OpenBLASで自分は実行しました。

参考リンク

ついでに

2020/08/26 追記。
パフォーマンスとは微妙に違うかもだけど、
せっかくなので記録しておく。

ちょっとした理由があり、lightgbmのソース読んでいた所、
pandas DataFrame をデータに渡した時は
内部で np.float32 にキャストするのを見つけた。
https://github.com/microsoft/LightGBM/blob/877d58fac74731a5feba8d6ca3bb0cc97d154eb0/python-package/lightgbm/basic.py#L399-L400

なので lightgbm & pandas コンビを使う場合、
pandasデータ型はカテゴリ・文字列以外は何も考えず
全部 float 型にして良さそうにみえる。
(「この変数はint型にしよう」とか考えなくて良さそう)

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3