はじめに
pythonで疑似的にデータを大量に生成する際、かなり時間がかかったので、高速化するために並列処理をしたくなりました。
Pythonでは、並列処理をするためのライブラリがいくつかありますが、実験再現性担保のため、ランダムのシード値を固定し並列処理で生成されるデータの固定化を行う必要がありました。ですが、シード値を用いた並列処理関連の記事が見つからなかったので、忘れないようにここにまとめておきます。
対象者
- pythonでCPUの並列処理したい人
- シード値使った並列処理したい人
random モジュールのシード値の固定化
pythonでは、以下のようにランダムのシード値を固定できます。
import random
random.seed(123)
逐次処理をする際は、これで十分です。しかし並列処理をする際は、ランダムモジュールの呼び出しタイミングがバラバラになるので、これだけでは不十分でした。
どうやって解決できるかなーと色々調べてみたら、このサイトでランダムモジュールのインスタンスを分けることができる、ということがわかりました!
ランダムモジュールのインスタンス化
実際に、こんな感じでランダムモジュールのインスタンスを立てることができるらしいです。インスタンス化さえしてしまえば、通常のランダムモジュールの使い方と同じようなので楽ですね。
from random import Random
random_instance_123 = Random(123)
random_instance_456 = Random(456)
print(random_instance_123.random()) # >> 0.052363598850944326
print(random_instance_456.random()) # >> 0.7482025358782363
シード値を考慮した並列処理プログラム例
上記のやつを用いてプログラムを書いてみます。並列処理をする際、各プロセスごとでインスタンス化してしまえば、他のプロセスからの干渉を防げそうです。
ここでは例として、ランダムで作成した配列をnumpy配列で保存するプログラム例を掲載しておきます。
ライブラリ
- tqdm
- numpy
コード
from random import Random
import multiprocessing
import numpy as np
from tqdm import tqdm
class MultiDatasetCreator:
def __init__(self, number):
self.number = number
self.random = Random(number)
def create(self, size):
# randomモジュールを使用してsize個の乱数リストを生成
self.data = []
for _ in range(size):
self.data.append(self.random.random())
self.data = np.array(self.data)
return
def save(self):
file_name = f"dataset/{self.number}.npy"
np.save(file_name, self.data)
def generate_dataset(number):
creator = MultiDatasetCreator(number)
creator.create(10)
creator.save()
if __name__ == "__main__":
num_dataset = 100 # 作成したいデータセットの数
num_process = multiprocessing.cpu_count() # 使用するCPUの数をカウント
random_seed_lst = [
i for i in range(num_dataset)
] # シード値のリストをあらかじめ作成しておく
# 並列処理
pool = multiprocessing.Pool(num_process)
with tqdm(total=num_dataset) as pbar:
for _ in pool.imap_unordered(generate_dataset, random_seed_lst):
pbar.update(1)
pool.close()
pool.join()
ランダムモジュールのインスタンスを別々で立ち上げることで、他のプロセスによる干渉をなくし、実験再現性の担保をすることができました。
このおかげでかなり高速化をすることができました👏
注意
今回のランダムのインスタンス化、Python標準ライブラリのrandomモジュールではできたのですが、numpyの方ではそれっぽいのが(私が見る限り)見つかりませんでした。なのでnumpyの乱数生成を実装したい際は、randomモジュールに置き換えて実装するしかなさそうです。(numpyの方は乱数生成する際の関数が多く実装されているので便利なんですけどね..)
numpyでも早く実装されるといいですね