質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
NumPy

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

Python 3.x

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2292閲覧

Numpyブロードキャストのテクニックについて

todo_suke

総合スコア16

NumPy

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

Python 3.x

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

4クリップ

投稿2020/03/18 00:57

多次元配列のブロードキャストについて質問です。
二次元配列が多次元に並んだ2つのテンソルがあって、そのテンソルの中の行列同士の要素積の最大値を取得し、並べるという操作をしたいと思っています。
for loopで行うとわかりやすいですが計算時間が遅い、ブロードキャストできるようにrepeatで次元を増やすと早くはなるが、メモリを大量に使うためそこがネックに。
もっと賢い方法はありますか?

Python

1import numpy as np 2 3X = np.random.randint(0,9,(20,15,13,10,10)) 4Y = np.random.randint(0,9,(9,10,10)) 5 6# X.shape:(A,B,C,D,E) 7# Y.shape:(F,D,E) 8 9# for loop 10Z = np.zeros((X.shape[0],X.shape[1],X.shape[2],Y.shape[0])) 11 12for a in range(X.shape[0]): 13 for b in range(X.shape[1]): 14 for c in range(X.shape[2]): 15 for f in range(Y.shape[0]): 16 Z[a,b,c,f] = np.max(X[a,b,c,:,:]*Y[f,:,:]) 17 18 19# Z.shape:(20, 15, 13, 9) 20 21# ブロードキャスト 22X_rep = np.repeat(X[:,:,:,np.newaxis,:,:],1,axis=0) 23Y_rep = np.repeat(Y[np.newaxis,np.newaxis,np.newaxis,:,:],1,axis=0)\ 24 25Z_rep = np.max(X_rep*Y_rep,axis=(-2,-1)) 26 27# Z_rep.shape:(20, 15, 13, 9)
jun68ykt👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

np.max() を計算するときにブロードキャストするのであれば、X の axis=3 にサイズ1の次元を挿入しておくだけで十分だと思います。

ブロードキャスト時の挙動

X: (20, 15, 13, 1, 10, 10) Y: (9, 10, 10) ↓ 次元を一致させる X: (20, 15, 13, 1, 10, 10) Y: ( 1, 1, 1, 9, 10, 10) ↓ 各次元のサイズを一致させる X: (20, 15, 13, 9, 10, 10) Y: (20, 15, 13, 9, 10, 10)

python

1import numpy as np 2 3X = np.random.randint(0, 9, (20, 15, 13, 10, 10)) 4Y = np.random.randint(0, 9, (9, 10, 10)) 5Z = np.zeros((X.shape[0], X.shape[1], X.shape[2], Y.shape[0])) 6 7for a in range(X.shape[0]): 8 for b in range(X.shape[1]): 9 for c in range(X.shape[2]): 10 for f in range(Y.shape[0]): 11 Z[a, b, c, f] = np.max(X[a, b, c, :, :] * Y[f, :, :]) 12 13X = np.expand_dims(X, axis=3) 14print(X.shape) # (20, 15, 13, 1, 10, 10) 15print(Y.shape) # (9, 10, 10) 16Z2 = np.max(X * Y, axis=(-2, -1)) 17 18assert np.allclose(Z, Z2) # 一致するかチェック

ブロードキャストできるようにrepeatで次元を増やすと早くはなるが、メモリを大量に使うためそこがネックに。

要素数は変わらないので、メモリ使用量は増えないと思います。

追記

質問のループバージョンのコードを numba で最適化したところ、
218ms が 8.45 ms と25倍程度高速化できました。
ブロードキャストで計算したバージョンが 8.01 ms なのでほぼ同じぐらいの速度が出るようになりました。

python

1import timeit 2import numpy as np 3from numba import jit 4 5 6np.random.seed(0) 7X = np.random.randint(0, 9, (20, 15, 13, 10, 10)) 8Y = np.random.randint(0, 9, (9, 10, 10)) 9 10def calc1(X, Y): 11 # broadcast バージョン 12 X = np.expand_dims(X, axis=3) 13 Z = np.max(X * Y, axis=(-2, -1)) 14 15 16def calc2(X, Y): 17 # for-loop バージョン 18 Z = np.zeros((X.shape[0], X.shape[1], X.shape[2], Y.shape[0]), dtype=X.dtype) 19 for a in range(X.shape[0]): 20 for b in range(X.shape[1]): 21 for c in range(X.shape[2]): 22 for f in range(Y.shape[0]): 23 Z[a, b, c, f] = np.max(X[a, b, c] * Y[f]) 24 25 26@jit(nopython=True) 27def calc3(X, Y): 28 # for-loop バージョンに numba でコンパイルしたバージョン 29 Z = np.zeros((X.shape[0], X.shape[1], X.shape[2], Y.shape[0]), dtype=X.dtype) 30 for a in range(X.shape[0]): 31 for b in range(X.shape[1]): 32 for c in range(X.shape[2]): 33 for f in range(Y.shape[0]): 34 Z[a, b, c, f] = np.max(X[a, b, c] * Y[f]) 35 36# Jupyter Notebook 上で計測 37%timeit calc1(X, Y) 38%timeit calc2(X, Y) 39%timeit calc3(X, Y) 40# 8.01 ms ± 239 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 41# 218 ms ± 32.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 42# 8.45 ms ± 25.3 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

投稿2020/03/18 04:39

編集2020/03/19 08:00
tiitoi

総合スコア21956

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

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

todo_suke

2020/03/18 12:09 編集

ありがとうございます。 repeatは次元を増やすだけでブロードキャストしてくれたんですね。 確かにメモリ使用量は減らせますが、メモリを一番食うのは np.max(X_rep*Y_rep,axis=(-2,-1)) の要素積をとった部分なんです。 ここの部分がfor loopだと一個づつ取り出せるのでメモリー消費を抑えることができます。 一つは要素積をとる部分の一部だけにfor loopをかけることかなと思いますが、ほかにアイデアありませんか?
tiitoi

2020/03/19 06:06 編集

> 一つは要素積をとる部分の一部だけにfor loopをかけることかなと思いますが、ほかにアイデアありませんか? そのバージョンで記述して、numba を使えば高速化できるかもしれません。 https://qiita.com/hanata/items/6003b3b3506e15331ad3 それでも速度がでないようであれば、その部分だけ Cython で書くという手もあります。
todo_suke

2020/03/20 05:06

ありがとうございます。 numba初めて知りました。 コードまで記載していただき助かります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問