いくつかの実験を行ったところ、Pythonの標準のrandom
およびmath
ライブラリがnumpy
のライブラリよりも高速であるケースがいくつか見つかりました。
小規模な操作ではpythonの標準ライブラリが約10倍高速になる傾向があると思いますが、大規模な(ベクトル)操作ではnumpy
がはるかに高速です。私の推測では、numpy
にはいくつかのオーバーヘッドがあり、それが小さなケースでは支配的になると思います。
私の質問は:私の直感は正しいですか?そして一般的に、小さな(通常はスカラー)演算にはnumpy
ではなく標準ライブラリを使用することをお勧めしますか?
以下に例を示します。
import math
import random
import numpy as np
対数と指数
%timeit math.log(10)
# 158 ns ± 6.16 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit np.log(10)
# 1.64 µs ± 93.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit math.exp(3)
# 146 ns ± 8.57 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit np.exp(3)
# 1.72 µs ± 78.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
正規分布を生成
%timeit random.gauss(0, 1)
# 809 ns ± 12.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit np.random.normal()
# 2.57 µs ± 14.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
ランダムな要素を選択する
%timeit random.choices([1,2,3], k=1)
# 1.56 µs ± 55.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit np.random.choice([1,2,3], size=1)
# 23.1 µs ± 1.04 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
numpy配列と同じ
arr = np.array([1,2,3])
%timeit random.choices(arr, k=1)
# 1.72 µs ± 33.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit np.random.choice(arr, size=1)
# 18.4 µs ± 502 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
大きな配列あり
arr = np.arange(10000)
%timeit random.choices(arr, k=1000)
# 401 µs ± 6.16 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit np.random.choice(arr, size=1000)
# 41.7 µs ± 1.39 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
numpy
は、実際には大きなデータブロックのパフォーマンス向上にすぎません。 ndarray
をcコンパイル済みのnumpy
関数に注ぐ前に、メモリブロックが正しく整列することを確認するオーバーヘッドは、配列が比較的大きくない場合、通常は時間のメリットを圧倒します。これが非常に多くのnumpy
質問が基本的に「このループコードを取得して高速化するにはどうすればよいか」であり、他のほとんどすべてのタグがあなたに投げかけるこのタグで有効な質問と見なされる理由です- コードレビュー 彼らがタイトルを通過する前に。
だから、はい、あなたの観察は一般化可能です。ベクトル化はnumpy
の要点です。ベクトル化されていないnumpy
コードは、常にpython
コードよりも低速であり、おそらく、手持ち削岩機で1つのクルミを割るのと同じくらい「間違っている」でしょう。適切なツールを見つけるか、より多くのナットを取得します。
NumPyは、主にarraysでのパフォーマンスに使用されます。これは、連続したメモリブロックの使用と、より効率的な低レベルの反復に依存しています。 NumPy数学関数をスカラーに適用したり、乱数を計算したりすることは、ベクトル化可能な操作ではありません。これは、表示されている動作を説明しています。
そして、小さな(通常はスカラー)操作には、NumPyではなく標準ライブラリを使用することが一般に推奨されますか?
プログラムのボトルネックがスカラーの操作によって引き起こされることはまれです。実際には、違いはごくわずかです。したがって、どちらの方法でも問題ありません。すでにNumPyを使用している場合は、スカラーでNumPy操作を継続して使用しても害はありません。
乱数を計算する特別なケースを作成する価値があります。ご想像のとおり、random
とNumPyで選択した乱数は同じではない可能性があります。
assert random.gauss(0, 1) == np.random.normal() # AssertionError
assert random.choices(arr, k=1)[0] == np.random.choice(arr, size=1)[0] # AssertionError
NumPyには、乱数を「予測可能」にする追加機能があります。たとえば、以下のスクリプトを繰り返し実行しても、同じ結果が生成されるだけです。
np.random.seed(0)
np.random.normal()
同じことがnp.random.choice
にも当てはまります。したがって、乱数の導出方法の方法と使用可能な機能に違いがあります。テストなどの目的で、一貫した「乱数」を生成できるようにしたい場合があります。