Numpyを高速化する最も簡単な方法【Python on Windows】

Numpyを高速化する最も簡単な方法【Python on Windows】 プログラミング

Numpyを使うと、listなどを使うよりも処理が速くなります。
さらには、Numpy自体を高速化することも可能です。

本記事の内容

  • Numpyの高速化
  • 高速化Numpyのダウンロード
  • 高速化Numpyのインストール
  • ノーマルNumpyと高速化Numpyの比較

それでは、上記に沿って解説していきます。

Numpyの高速化

Numpyの高速化とは、コードの改善という話ではありません。
Numpyそのものをより高速なモノにするということです。

そのための技術として、MKLが存在します。
MKLは、Intelが開発している演算用のライブラリです。
もちろん、MKLはIntelのCPUのみに対応しています。

このMKLを利用すれば、Numpyを高速化できるというわけです。
そして、MKLに対応したNumpyが用意されています。

ここで、呼び名(表記)に関して整理しておきます。
説明しやすいように、以下の二つのNumpyの呼び名を用います。

  • ノーマルNumpy(オリジナル・バニラともいう)
  • 高速化Numpy(MKL対応)

本題に戻ると、Numpyの高速化とは、高速化Numpyを使うことだと言えます。

ただし、高速化Numpyのインストールはかなり大変です。
以下の3つを試しました。

  • Numpyのビルド
  • Intelチャンネルの利用
  • ビルド済みパッケージの利用

それぞれを以下で説明します。

Numpyのビルド

MKLをインストールして、自分でNumpyをビルドする方法です。

正直、これはおススメできません。
かなりPC環境が汚れます。

MKLのダウンロードには、アカウント登録が必要です。
容量サイズも大きくて、ディスクが無駄に圧迫されます。

Intelチャンネルの利用

https://anaconda.org/intel/numpy

Intel公式だと安心できそうです。
しかし、現段階ではまだテストバージョンとのこと。

そして、pipで簡単にはインストールはできません。
依存関係を上手く管理できていないようです。

正式版になったら、再度試してみるのもいいかもしれません。

ビルド済みパッケージの利用

有志の方が、MKL+Numpyをビルドして公開してくれています。
詳細は、後述します。

結論で言うと、現在はビルド済みパッケージの利用が妥当です。
これなら、PC環境も汚れません。
また、ディスク容量も圧迫されません。

何よりも、簡単にインストールできます。
依存関係について何も考える必要がありません。

ただし、これはWindowsのみの話です。
macOSやLinuxの場合は、これ以外の方法で対応するしかありません。

まとめ

今回は、Windows上のNumpy高速化が対象です。
したがって、Numpyの高速化にはビルド済みパッケージを利用します。

あとは、Anacondaを利用するという方法もあります。
Anacondaなら、初めから高速化Numpyがインストールされているようです。

ライブラリにIDEが制限されるのは、個人的にはどうかなと思います。
なお、IDEはPyCharmを利用しています。
そもそも、Anacondaが嫌になってPyCharmを使うようになった経緯があります。

以上、Numpyの高速化についての説明でした。
次は、高速化Numpyのダウンロードを行います。

高速化Numpyのダウンロード

ダウンロードページ
https://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy

野良ページ(非公式)です。
カリフォルニア大学アーバイン校のChristoph Gohlke氏によるページとなります。

Gohlke氏は、多くのパッケージ(ビルド済み)を公開してくれています。
メンテナンスが行き届いていて、非常にありがたいです。

上記のページにアクセスすると、次のような表示を確認できます。
※Numpyはその時点の最新版が用意されている

ここから、各自の利用している環境にあったモノを選択します。
環境とは、具体的には以下となります。

  • Pythonのバージョン
  • アーキテクチャー(32bit or 64bit)

検証した環境は、以下の値となります。

Python3.9.6cp39
アーキテクチャー62bitamd64

よって、上記の環境であれば、次のファイルを選択することになります。

numpy‑1.21.0+mkl‑cp39‑cp39‑win_amd64.whl

なお、現時点において、NumpyはPython 3.6をサポートしていません。
そのため、cp36用のファイルは存在していません。

では、該当するファイルをクリックしてダウンロードします。
ファイルは、200MB以上の容量があります。

ビルド済みのDLLを含むので、それぐらいの容量にはなるのでしょう。

以上、高速化Numpyのダウンロードを説明しました。
次は、高速化Numpyをインストールしていきます。

高速化Numpyのインストール

ダウンロードしたファイルを利用します。

まずは、現状のインストール済みパッケージを確認しておきます。

>pip list
Package    Version
---------- -------
pip        21.1.3
setuptools 57.1.0

次にするべきことは、pipとsetuptoolsの更新です。
pipコマンドを使う場合、常に以下のコマンドを実行しておきましょう。

python -m pip install --upgrade pip setuptools

では、高速化Numpyのインストールです。
高速化Numpyのインストールは、以下のコマンドとなります。

pip install ダウンロードしたファイル

今回の場合だと、以下。

pip install numpy‑1.21.0+mkl‑cp39‑cp39‑win_amd64.whl

インストールには、それほど時間がかかりません。
では、どんなパッケージがインストールされたのかを確認しましょう。

>pip list
Package    Version
---------- ----------
numpy      1.21.0+mkl
pip        21.1.3
setuptools 57.1.0

numpyには、「+mkl」が追加されています。
これで、ノーマルのNumpyとは異なることがわかります。

あと、他のパッケージに依存していません。
このことにより、既存の環境にも導入しやすいと言えます。

さて、高速化Numpyの特徴はビルド不要でした。
それは、DLLをセットにしているからです。

ノーマルのNumpyには、そもそも「DLLs」というフォルダが存在していません。
それに対して、高速化Numpyには「DLLS」フォルダが存在しています。
その 「DLLs」には、以下のDLLが保存されています。

以上、高速化Numpyのインストールの説明でした。
最後に、高速化NumpyとノーマルNumpyの比較をします。

ノーマルNumpyと高速化Numpyの比較

Intel公式
https://software.intel.com/content/www/us/en/develop/articles/numpyscipy-with-intel-mkl.html

上記ページより、実行速度を計測するためのコードを拝借します。
利用マシンのCPUは、インテル Core i9 9900KFです。

import numpy as np
import time

N = 6000
M = 10000

k_list = [64, 80, 96, 104, 112, 120, 128, 144, 160, 176, 192, 200, 208, 224, 240, 256, 384]

def get_gflops(M, N, K):
    return M * N * (2.0 * K - 1.0) / 1000 ** 3

np.show_config()

for K in k_list:
    a = np.array(np.random.random((M, N)), dtype=np.double, order='C', copy=False)
    b = np.array(np.random.random((N, K)), dtype=np.double, order='C', copy=False)
    A = np.matrix(a, dtype=np.double, copy=False)
    B = np.matrix(b, dtype=np.double, copy=False)

    C = A * B

    start = time.time()

    C = A * B
    C = A * B
    C = A * B
    C = A * B
    C = A * B

    end = time.time()

    tm = (end - start) / 5.0

    print('{0:4}, {1:9.7}, {2:9.7}'.format(K, tm, get_gflops(M, N, K) / tm))

上記コードの実行結果を確認します。

ノーマルNumpy

blas_mkl_info:
  NOT AVAILABLE
blis_info:
  NOT AVAILABLE
openblas_info:
    library_dirs = ['D:\\a\\1\\s\\numpy\\build\\openblas_info']
    libraries = ['openblas_info']
    language = f77
    define_macros = [('HAVE_CBLAS', None)]
blas_opt_info:
    library_dirs = ['D:\\a\\1\\s\\numpy\\build\\openblas_info']
    libraries = ['openblas_info']
    language = f77
    define_macros = [('HAVE_CBLAS', None)]
lapack_mkl_info:
  NOT AVAILABLE
openblas_lapack_info:
    library_dirs = ['D:\\a\\1\\s\\numpy\\build\\openblas_lapack_info']
    libraries = ['openblas_lapack_info']
    language = f77
    define_macros = [('HAVE_CBLAS', None)]
lapack_opt_info:
    library_dirs = ['D:\\a\\1\\s\\numpy\\build\\openblas_lapack_info']
    libraries = ['openblas_lapack_info']
    language = f77
    define_macros = [('HAVE_CBLAS', None)]
  64, 0.08567424,  88.94156
  80, 0.0995338,  95.84684
  96, 0.1138956,  100.6185
104, 0.1161928,  106.8913
112,  0.136841,  97.77774
120, 0.1193839,  120.1167
128, 0.1126023,  135.8764
144, 0.1308503,  131.6008
160, 0.1321506,  144.8348
176, 0.1454113,  144.8306
192, 0.1550254,  148.2337
200,    0.1494,   160.241
208, 0.1654604,  150.4892
224, 0.1599717,  167.6546
240, 0.1852078,  155.1771
256,  0.174838,  175.3624
384, 0.2580142,  178.3622

高速化Numpy

blas_mkl_info:
    libraries = ['mkl_lapack95_lp64', 'mkl_blas95_lp64', 'mkl_rt']
    library_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/lib/intel64_win']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/include']
blas_opt_info:
    libraries = ['mkl_lapack95_lp64', 'mkl_blas95_lp64', 'mkl_rt']
    library_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/lib/intel64_win']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/include']
lapack_mkl_info:
    libraries = ['mkl_lapack95_lp64', 'mkl_blas95_lp64', 'mkl_rt']
    library_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/lib/intel64_win']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/include']
lapack_opt_info:
    libraries = ['mkl_lapack95_lp64', 'mkl_blas95_lp64', 'mkl_rt']
    library_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/lib/intel64_win']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/include']
  64, 0.02952104,   258.121
  80, 0.03969369,  240.3405
  96, 0.05465331,  209.6854
104, 0.05784545,  214.7101
112, 0.06303077,  212.2773
120, 0.05265942,   272.316
128, 0.05924153,  258.2648
144, 0.06642256,  259.2493
160, 0.07858987,  243.5428
176, 0.07360315,   286.129
192, 0.09325309,  246.4262
200, 0.08916178,  268.5007
208, 0.08158197,  305.2145
224, 0.09594383,  279.5386
240, 0.1022297,  281.1316
256, 0.1051193,  291.6685
384, 0.1595734,  288.3939

比較

確認するポイントは、2点。

  • blas_mkl_infoの値
  • 64, ●●●,   ●●●以降の数値

blas_mkl_infoの値

ノーマルNumpyは、「NOT AVAILABLE」となっています。
MKLは、利用できない状況ということです。

それに対して、高速化Numpyでは値がズラズラと記載されています。
「mkl_rt」は、インストールしたDLLの中にありました。

blas_mkl_infoの値を見て、MKLの利用状況を確認できます。

64, ●●●,   ●●●以降の数値

k_list = [64, 80, 96, 104, 112, 120, 128, 144, 160, 176, 192, 200, 208, 224, 240, 256, 384]

上記の値ごとに、表示されています。
数値が大きくなるほど、処理するデータが大きくなります。

その数値毎に、処理時間と処理性能が表示されています。

  64, 0.02952104,   258.121
  80, 0.03969369,  240.3405
  96, 0.05465331,  209.6854
・・・

まとめ

総じて、高速化Numpyの方が全体的に速いです。
その結果自体には、何ら驚きはありません。

ただ、興味深いのは、高速化NumpyのGFLOPS(処理性能)です。
初めからトップスピードで対応できるというところに、興味を持ちました。

そして、データのサイズによっては、2倍以上の速度が出ることが確認できました。
塵も積もればということで、大きな処理を行う場合には決して無視できない差になるでしょう。

ただ、これは推測に過ぎません。
別途、機械学習などの時間のかかる処理で検証してみたいと思います。

タイトルとURLをコピーしました