为什么NumPy的随机函数似乎在其生成的值中显示模式?

时间:2018-05-11 13:40:22

标签: python numpy random

我正在玩NumPy和Pillow并且遇到了一个有趣的结果,显然在NumPy random.random()结果中展示了一种模式。

Image One Image Two Image Three Image Four

这里是生成和保存100个这些图像(种子0)的完整代码示例,以上是此代码生成的前四个图像。

import numpy as np
from PIL import Image

np.random.seed(0)
img_arrays = np.random.random((100, 256, 256, 3)) * 255
for i, img_array in enumerate(img_arrays):
    img = Image.fromarray(img_array, "RGB")
    img.save("{}.png".format(i))

以上是使用PIL.Image.fromarray()在使用numpy.random.random((256, 256, 3)) * 255创建的四个不同NumPy阵列上创建的四个不同图像,以在四个不同的Python实例中生成256 x 256格的RGB值(同样的事情也发生在相同的例子)。

我注意到这只发生(在我的有限测试中)当图像的宽度和高度是2的幂时,我不知道如何解释它。

虽然由于浏览器消除锯齿可能很难看到(您可以下载图像并在没有抗锯齿的图像查看器中查看它们),但每隔8列都有明显的紫褐色像素列。每张图片的第3列。为了确保这一点,我在1​​00张不同的图像上进行了测试,他们都遵循了这种模式。

这里发生了什么?我猜这样的模式是人们总是说在需要真正的随机性时使用加密安全的随机数生成器的原因,但有没有具体的解释为什么会发生这种情况?

4 个答案:

答案 0 :(得分:16)

不要责怪Numpy,责怪PIL / Pillow。 ;)你正在生成浮点数,但是PIL期望整数,并且它的浮点数转换为int并不是我们想要的。需要进一步的研究来确定PIL 正在做什么......

同时,您可以通过将值显式转换为无符号8位整数来消除这些行:

img_arrays = (np.random.random((100, 256, 256, 3)) * 255).astype(np.uint8)

正如FHTMitchell在评论中指出的那样,一种更有效的形式是

img_arrays = np.random.randint(0, 256, (100, 256, 256, 3), dtype=np.uint8) 

这是修改过的代码的典型输出:

random image made using Numpy

PIL Image.fromarray函数有一个已知错误,如here所述。您所看到的行为可能与该错误相关,但我猜它可能是一个独立的行为。 ;)

FWIW,here是我对链接问题中提到的错误所做的一些测试和解决方法。

答案 1 :(得分:2)

我很确定问题与dtype有关,但不是出于你想的原因。以下为"500 Internal Server Error" 注意,dtype为 np.random.randint(0, 256, (1, 256, 256, 3), dtype=np.uint32)

enter image description here

你能看到模式;)? PIL将8位(4字节)值(可能为4像素RGBK)与8位值(一个像素的RGB)不同地解释。 (见PM 2Ring的答案)。

最初你传递64位浮点值,这些值的解释也不同(可能与你的意图不一致)。

答案 2 :(得分:1)

正如其他人所指出的,这些模式与NumPy的随机数生成无关;问题很简单,就是PIL的“ RGB”模式期望得到dtype uint8的数组,并且在给定其他内容时,尝试将原始字节解释为好像是uint8的数组。在这里,您传递的是8字节的float64(未指定dtype时为NumPy的默认值),这将产生您看到的结果。

您正在期待数组中每个从0-255的随机数,以定义一个像素的一个颜色通道的值,但实际上,它正在定义8个连续颜色通道的值。例如,第一个随机数-您打算作为左上像素的“红色”通道的值-实际上是在定义左上像素的红色,绿色和蓝色通道位于该像素右侧的一个和该像素右侧的红色和绿色通道。糟糕!

最简单的证明实际上不是NumPy的RNG出现的模式的测试是将数组中的所有值设置为255,而不是随机数,然后显示:

>>> import numpy as np
>>> from PIL import Image
>>> img_array = np.full((256, 256, 3), 255.0)
>>> print(img_array.dtype)
float64
>>> Image.fromarray(img_array, 'RGB').show()

Image output by the above code

当然,我们仍然可以看到垂直线的图案。

答案 3 :(得分:0)

Python Docs for random()这样说:

  

Python使用Mersenne Twister作为核心生成器。它产生53位精度浮点数,周期为2 ** 19937-1。 C中的底层实现既快又线程安全。 Mersenne Twister是现存最广泛测试的随机数发生器之一。但是,它完全是确定性的,并不适用于所有目的,并且完全不适合加密目的。

最好的随机数生成器通过randomness tests,经常使用质量较差的随机数生成器,因为它们很快且被认为“足够好”。

在“Some Difficult-to-Pass Tests of Randomness”2002年1月,由Marsaglia和Tsang,他们确定“Diehard Battery of Tests”的一个子集可用于评估一系列数字的随机性,特别是gcd,大猩猩和生日间隔测试。有关熵的讨论和对这些测试的评论,请参阅“Dieharder test descriptions”。

在我们的编程拼图和高尔夫代码中,有些人开始编写代码以在此问题中通过Diehard测试:“Build a random number generator that passes the Diehard tests”。

除了最好(也可能更慢)的RNG之外,您应该会看到所有模式。

RNG统计检验的现代标准,“NIST SP 800-22 - Recommendation for Random Number Generation Using Deterministic Random Bit Generators”(Overview)提供了一系列测试,其中包括评估1的分数与½的接近程度,即序列中的1和0的数量应该大致相同。

2017年1月,由Sýs,Říha和Matyáš在ACM网站“Algorithm 970: Optimizing the NIST Statistical Test Suite and the Berlekamp-Massey Algorithm”上发表的一篇文章承诺,NIST算法的重新植入将大大加快。