🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

3回答

1750閲覧

Numpyの計算スピードの検証

takashim

総合スコア124

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

1グッド

4クリップ

投稿2019/09/25 02:13

編集2019/09/25 03:29

やっていること

NumPyを使った計算高速化について体感、理解するため、以下サイトを参考に勉強中です。
https://www.labri.fr/perso/nrougier/from-python-to-numpy/

サイトの事例

ここに高速化の事例として、以下のようなコードが提示されています。
イメージ説明

これら2つの関数の実行時間を比較した結果が以下だそうで、ケタ違いにNumpyが速いと言っています。
イメージ説明

自分でやってみた結果

python3

1def add_python(Z1, Z2): 2 return [z1 + z2 for (z1, z2) in zip(Z1, Z2)] 3 4def add_numpy(Z1, Z2): 5 return np.add(Z1, Z2) 6 7Z1 = random.sample(range(1000), 100) 8Z2 = random.sample(range(1000), 100)

timeitの書き方を変えて2パターン

  1. Numpyがちょっとだけ早い
%timeit ("add_python(Z1, Z2)", globals()) %timeit ("add_numpy(Z1, Z2)", globals()) 89.7 ns ± 3.92 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 87.4 ns ± 0.848 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
  1. Numpyが遅い
%timeit add_python(Z1, Z2) %timeit add_numpy(Z1, Z2) 8.06 µs ± 80.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 15.1 µs ± 288 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

知りたいこと

A. なぜNumpyの方が遅くなってしまうのでしょうか。
B. おそらく、実際はNumpyの方が速いのではと思いますが、どなたか同じ方法で検証してみて頂けないでしょうか。
C. ついでに、timeitにglobals()を付けると結果が大きく変わるのはなぜでしょうか。

ちなみに...

本件と直接関係ありませんが、他のコードにて、
np.linalg.normが異常に遅く、普通のpythonのコードに書き換えたことがあります。
これはありえない?

a1 = np.random.randint(1,10, (2)) a2 = np.random.randint(1,10, (2)) l1 = [random.randint(1,10) for i in range(2)] l2 = [random.randint(1,10) for i in range(2)] #a1=[8 9], <class 'numpy.ndarray'> #a2=[9 1], <class 'numpy.ndarray'> #l1=[1, 7], <class 'list'> #l2=[9, 8], <class 'list'> %%timeit length1 = ((l2[0] - l1[0]) ** 2 + (l2[1] - l1[1]) ** 2) ** 0.5 #1.14 µs ± 18.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) %%timeit length2 = np.linalg.norm(a2 - a1) length2 #4.99 µs ± 301 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

環境

Python 3.7.1
Numpy 1.16.4
windows10
anaconda
jupyter notebook 5.7.4

LouiS0616👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

簡単には

Python3では最適化を頑張ったのでforループが速くなった

だけです。

Python2

plain

1In [1]: import numpy as np 2 3In [2]: import random 4 5In [3]: def add_python(Z1, Z2): 6 : return [z1 + z2 for (z1, z2) in zip(Z1, Z2)] 7 : 8 : def add_numpy(Z1, Z2): 9 : return np.add(Z1, Z2) 10 : 11 : Z1 = random.sample(range(10000000), 1000000) 12 : Z2 = random.sample(range(10000000), 1000000) 13 14In [4]: %timeit add_python(Z1, Z2) 151 loop, best of 3: 616 ms per loop 16 17In [5]: %timeit add_numpy(Z1, Z2) 181 loop, best of 3: 454 ms per loop

Python3

plain

1In [1]: import numpy as np 2 3In [2]: import random 4 5In [3]: def add_python(Z1, Z2): 6 : return [z1 + z2 for (z1, z2) in zip(Z1, Z2)] 7 : 8 : def add_numpy(Z1, Z2): 9 : return np.add(Z1, Z2) 10 : 11 : Z1 = random.sample(range(10000000), 1000000) 12 : Z2 = random.sample(range(10000000), 1000000) 13 14In [4]: %timeit add_python(Z1, Z2) 15200 ms ± 4.91 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 16 17In [5]: %timeit add_numpy(Z1, Z2) 18304 ms ± 6.25 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Rob Pikeの言うところの"計測すべし"の典型例ですね。
Python3 になって、"Python2ではこっちが速かった"みたいな話はまったく当てになりません。


ではなんでPython3でnumpyが負けるのかというと、numpy.addにリストを渡しているので、中でリストをnumpy.ndarrayに変換する処理が余計に入るからだと思います。
Python2のころは、そこに余計な処理をかけてもまだ、素直なforループに比べて速かったというだけかと。

numpy.addの速度と、numpy.arrayへの変換の速度をざっくり測ると以下の様になります。(これはPython3)

plain

1In [6]: N1 = np.array(Z1) 2 3In [7]: N2 = np.array(Z2) 4 5In [8]: %timeit add_numpy(N1, N2) 62.65 ms ± 332 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 7 8In [9]: %timeit N1 = np.array(Z1) 972.1 ms ± 9.19 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

投稿2019/09/25 04:00

編集2019/09/25 04:09
quickquip

総合スコア11231

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takashim

2019/09/25 04:15

どうもありがとうございます。Python3は2に比べて改善されているのですね。 ただ、まだ気になるのは、参考サイトの事例では、 ・python3.6.0を使っている ・add_python, add_numpyの両方とも引数はリスト なのにnumpyが圧倒的に早いということです。なにか裏があるのかな。。。 まあ、quiquiさんの結果も私と同様のようですし、まずは安心しました。 「ちなみに...」にも追記しましたが、「高速化のためにnumpy」はケースバイケースで考えないと危険ですね。
guest

0

numpyの計算速度の恩恵を受けたい場合、組み込み型のlistではなくnumpy配列としてデータを作成してください。

python

1import random 2import timeit 3import numpy as np 4 5def add_python(Z1, Z2): 6 return [z1 + z2 for (z1, z2) in zip(Z1, Z2)] 7 8def add_numpy(Z1, Z2): 9 return np.add(Z1, Z2) 10 11Z1 = random.sample(range(1000), 100) 12Z2 = random.sample(range(1000), 100) 13Z1np = np.array(Z1, dtype=np.int64) 14Z2np = np.array(Z2, dtype=np.int64) 15 16for func in [add_python, add_numpy]: 17 print("func:", func.__name__) 18 t = timeit.timeit(lambda : func(Z1, Z2), number=1000) / 1000 19 print("list:") 20 print("{:.8f}".format(t)) 21 t = timeit.timeit(lambda : func(Z1np, Z2np), number=1000) / 1000 22 print("numpy array:") 23 print("{:.8f}".format(t)) 24""" => 25func: add_python 26list: 270.00001097 28numpy array: 290.00002730 30func: add_numpy 31list: 320.00002006 33numpy array: 340.00000120 35"""

投稿2019/09/25 04:02

hayataka2049

総合スコア30935

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takashim

2019/09/25 04:20

どうもありがとうございます。 >numpyの計算速度の恩恵を受けたい場合、組み込み型のlistではなくnumpy配列として >データを作成してください。 私もそう思っていたのですが、質問に載せた事例を見て、 np.addを使えばリストで渡しても高速なのか!と思い試したところ 私の環境では全然早くなく、なんでかなと思い質問した次第です。 事例ではpython3.6.0のようですが、事例がおかしいんでしょうか。
hayataka2049

2019/09/25 04:34 編集

ページ固有の疑問点は、著者に聞いてみるのが一番いいでしょう。なかなか考えづらい結果なので。そのページが間違っているだけという可能性も十分あります。
takashim

2019/09/25 05:14

おっしゃる通り、著者に聞くしかないです。。。 ただ、事例がちょっと怪しいかもということ、私の計算結果でも感覚的に間違ってはいないことが判ったので安心しました。 ありがとうございました。
takashim

2019/09/25 07:10

著者に聞いてみたところ、バージョンが違うからじゃないかとのことでした(ホントに返事が来ると思いませんでした)。 That’s interesting and I suspect some optimizations have been made from Python 3.5 and Python 3.7 that would explain the absence of difference (when I tested 2 years ago, there were some differences). If you have access to an earlier version of Python, maybe you can test if it is the case.
hayataka2049

2019/09/25 08:27

試すならnumpyのバージョンもあわせてやってみるといいかと思いました。
guest

0

numpy を使うことによる効果が出ないほどに、Z1とZ2の要素の数が少ないのでは、と思われます。
Z1とZ1がそれぞれ100万要素程度で計測してみてください。

投稿2019/09/25 02:40

hiro-k

総合スコア902

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takashim

2019/09/25 02:51

100万でやってみましたが、結果はあまり変わりませんでした。 add_python: 156 ms ± 3.57 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) add_numpy: 167 ms ± 2.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) add_python: 85.8 ns ± 1.25 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) add_numpy: 85.1 ns ± 0.518 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
hiro-k

2019/09/25 03:04

ん???1個は判ったかも %timeit ("add_python(Z1, Z2)", globals()) %timeit ("add_numpy(Z1, Z2)", globals()) 89.7 ns ± 3.92 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 87.4 ns ± 0.848 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) の方で差が出ないのは、余分な"が有るからのような気がします。 timeit (add_python(Z1, Z2), globals()) timeit (add_numpy(Z1, Z2), globals()) で測定してみてください。
takashim

2019/09/25 03:24

ダメです。。。 add_python: 155 ms ± 1.34 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) add_numpy: 167 ms ± 2.66 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) やっぱり普通はnumpyの方が速いですか?codeコピーして、hiro-kさんの環境でやってみることはできますか?厚かましいお願いですが。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問