最近在做的项目重点部分与大量生成随机数有关,维度高达[1700000,10000],需要生成 10 x 30 次左右,这里遇到内存和速度的双重瓶颈,特地研究了一下如何优化随机数。
优化时间测试所需的分析工具在另一篇博客《性能优化系列一:分析工具》中提到。
如何生成随机数
原生的python中也有随机模块生成random.randint
和random.random
等,但是速度非常慢,numpy 速度可以大幅提升。一般都采用numpy生成随机数。
from numpy import random
random.rand()#生成0-1随机float数
random.rand(n)#生成0-1 n长度arrayl
random.randn(n)#一样生成正太分布n个随机数
random.uniform(a,b)#生成(a,b)之间均匀随机数
random.randint(a,b,size,dtype)
#a必须<b,b可以不填表示无穷大,dtype表示int32 or float64等
random.choice(a,[,size,replace])#从a中随机选择随机数
random.shuffle(x[.random])#将一个列表打乱
random.permutation(x)#随机扰动一个序列
random exponential/binomial...()#各种分布的随机数
random.seed()
比较常用的就是以上几种。在需要生成大量随机数的情况下,或生成伪随机数的情况下,python 3.7 常用 RandomState
。
rng = np.random.RandomState(seed)
rng.standard_normal(size)
rng.randint(low,high,size)
...
内存问题
共享seed
直接生成大规模非稀疏矩阵如下,经常遇到MemoryError
的错误,大概是同时生成多个float64精度的大规模随机矩阵服务器内存不够,而random state 似乎也没提供调整类型的attr,
def getRandomMatrix(seed,D,d):
rng = np.random.RandomState(seed)
A = rng.standard_normal((D, d),dtype=np.double)
return A
这时最好使用即使生成即使销毁,仅保留种子作为索引,同样,多个CPU之间共享大规模矩阵涉及到共享内存或数据传输同步较慢的问题,最好也共享seed而不是直接共享矩阵。
ps. 这里注意一般我们设置time.time()为种子时,对于并发性程序是无效的,不要在并发程序中同时定义,建议生成一个seed list 列表再从中取。
分片操作
这里可以对大规模矩阵进行分片以进行后续的np 乘法,再切片赋值,以时间换内存。这种情况的麻烦在于如果设定随机数种子会导致每个分片的随机数相同。可以利用一个最初seed(爷爷种子)randint生成 一组切片组数的seed(父亲种子),再每次从中取不同的随机数。
def function(x,y,seed,s,D,d):
tmp = np.empty((D,))
for j in range(0,D,s):
if j+s > D:
s = D-1-j
tmp[j:j+s,] = np.dot(getRandomVector(seed,s,D,d),y)
x = 1.0 * x + tmp
return x
时间优化
在上述切片方法尝试之后,可以解决内存问题。但是时间非常慢,特别是采取s = 1时在standard normal 上调用170万次的时间长达3000s,line search一下搜索了大约100000为切片值仍然太慢。在文档中发现了BitGenerator
和Generator
,大约可以提速到原来的 1/3。
# RandomState
def getRandomVector2(ini_seed,idx,s,D,d):
rng = np.random.RandomState(ini_seed)
child_seed = rng.randint(0,999999,size = (D,))
rng = np.random.RandomState(child_seed[idx])
random_vector = rng.standard_normal((s,d))
return random_vector
# Generator
def getRandomVector(ini_seed,idx,s,D,d):
rng = default_rng(ini_seed)
child_seed = rng.integers(0,999999,size = (D,))
rng2 = default_rng(child_seed[idx])
return rng2.standard_normal((s,d))
其他方法
除了Numpy和基本模块之外,AES CTR 加密算法生成随机数也很快,但是并不能有比较方便的方式控制每次生成的一样。参见以下reference。
tensorflow 和 pytorch 也都有大规模生成随机tensor的方式。性能待考。
网友评论