前提・実現したいこと
Window10のノートPCでAnacondaをインストールして、jupyter notebook上でPython3のコードを書いています。numpyを用いています。
0と1を要素にもつm×n行列Aの列ベクトルをv1,v2,...,vnとします。viに対して、k連続する1をkに置き換えたベクトルをuiとします。u1,u2,...unを列ベクトルとしてもつ行列をBとします。
具体例を示します。
v1=[0,0,1,1,1,0,0,0,1,0,0,1,1,1,1,1,1,1,1,0,1,0]
のとき、
u1=[0,0,3,3,3,0,0,0,1,0,0,8,8,8,8,8,8,8,8,0,1,0]
です。
Aが与えられたとき、Bを得られるようなコードを書きました。しかし例えばm=n=20000だと、計算に40秒ほどかかってしまいます。この計算を数千回繰り返したいと考えておりますので、1回あたりの計算時間をより短くしたいと考えております。自分でもいくつかの方法を試しましたが、計算時間を短縮する有効な方法が分かりません。計算時間を短縮する方法について何かアドバイスを頂けたら幸いです。よろしくお願いいたします。
なお、k連続する1をkに置き換える方法については、
https://maspypy.com/numpy連続同一値の数え上げ-atcoder-abc-129-d
を参考にさせていただきました。
該当のソースコード
コード1
Python3
1import numpy as np 2from tqdm import tqdm 3 4Matrix=np.zeros((2*10**4, 2*10**4), dtype=np.int32) 5 6def f(x): 7 temp=np.arange(len(x)) 8 temp[x>0]=0 9 np.maximum.accumulate(temp, out = temp) 10 left=temp 11 12 x_reversed=x[::-1] 13 temp=np.arange(len(x)) 14 temp[x_reversed>0]=0 15 np.maximum.accumulate(temp, out = temp) 16 right=len(x)-1-temp[::-1] 17 18 y=right 19 y -= left + 1 20 y[x==0]=0 21 return y 22 23Hight=Matrix.shape[0] 24Width=Matrix.shape[1] 25 26for i in tqdm(range(Width)): 27 Matrix[0,i]=0 28 Matrix[-1,i]=0 29 Matrix[:,i]=f(Matrix[:,i])
試したこと
①Matrixのデータ型の変更
np.int16にすると計算時間は30秒ほど
np.int8にすると計算時間は22秒ほど
になりましたがデータ型はnp.int32を用いたいと考えています。
②行列に対してまとめて計算
コード1では元の行列の一つ一つの列ベクトルに対して、k連続する1をkに置き換える、という処理をしていますが、行列Aに対して、縦にk連続する1をkに置き換える、という処理をしてみました。
コード2
Python3
1import numpy as np 2 3Matrix=np.zeros((2*10**4, 2*10**4), dtype=np.int32) 4 5def f(x): 6 x[:,0]=np.zeros(len(x), dtype=np.int32) 7 x[0]=np.zeros(len(x), dtype=np.int32) 8 x[-1]=np.zeros(len(x), dtype=np.int32) 9 10 temp=np.arange(len(x)).repeat(len(x)).reshape(len(x),len(x)) 11 temp[x>0]=0 12 np.maximum.accumulate(temp, out = temp) 13 left=temp 14 15 x_reversed=x[::-1] 16 temp=np.arange(len(x)).repeat(len(x)).reshape(len(x),len(x)) 17 temp[x_reversed>0]=0 18 np.maximum.accumulate(temp, out = temp) 19 right=len(x)-1-temp[::-1] 20 21 y=right 22 y -= left + 1 23 y[x==0]=0 24 return y 25 26%time f(Matrix)
計算時間は45秒ほどとなり、効果はないことが分かりました。
③numbaを用いる
from numba import jit
を加え、
def f(x)
の1行上に
@jit
を付け加えたのですが、エラーメッセージが出たうえで、計算時間はほぼ変わりませんでした。
@jitの代わりに
@jit(nopython=True)
を付け加えたみたところ、エラーが表示され、計算が実行されませんでした。
コード1に対してnumbaを用いる方法をご存じでしたら教えていただけないでしょうか。(→hayataka2049さんの回答により解決しました)
④Cythonを用いる
Cythonというものを用いればPythonの高速化ができる、ということを知りました。そこで次のページを参考にしました。
https://qiita.com/kenmatsu4/items/7c08a85e41741e95b9ba
まず
%load_ext Cython
を実行し、次に
%%cython -n test_cython_code def fib(int n): cdef int i cdef double a=0.0, b=1.0 for i in range(n): a, b = a+b, a return a
を実行すると、
DistutilsPlatformError: Unable to find vcvarsall.bat
というエラーが出てしまいます。
このエラーを解消する方法をご存じでしたら教えていただけないでしょうか。
⑤CuPyを用いる
CuPyというものを使って、GPUを搭載したコンピューターで計算を行うと高速化できる可能性がある、ということを知りました。(自分のPCにはGPUは搭載されていません)
Google Colaboratoryを使うと、CuPyの性能を確認することができるそうです。以下のようなページを読んで、CuPyについて調べているところです。
https://qiita.com/samacoba/items/d18e6cf09f544477aff4
⑥並列処理を行う
複数のCPUがあるコンピュータでは、複数のCPUを用いることによって計算時間は短くなると思います。そこで並列処理について調べてみて、まずは以下のページを参考にしました。
http://iatlex.com/python/parallel_first
このページ内の以下のコードを実行してみました。
Python3
1######## 並列計算を使えるように ######### 2from multiprocessing import Pool 3 4##### 並列計算させる関数(処理):引数1つ ### 5##### この場合は,引数の二乗を返す関数 ### 6def nijou(x): 7 print( x*x ) 8 9###### 並列計算させてみる ######### 10if __name__ == "__main__": 11 p = Pool(4) 12 p.map( nijou, range(10) )#nijouに0,1,..のそれぞれを与えて並列演算
しかしjupyter notebookのセルの数字が*になったままになり、結果が出力されません。
コード1に対して複数のCPUを用いた並列処理をする方法をご存じでしたら教えていただけないでしょうか。
補足情報
その他に高速化する方法をご存じでしたら教えていただきたいです。
回答3件
あなたの回答
tips
プレビュー