为啥我使用PyTorch来生成随机数总会失败?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为啥我使用PyTorch来生成随机数总会失败?相关的知识,希望对你有一定的参考价值。
参考技术A 明敏 发自 凹非寺量子位 报道 | 公众号 QbitAI
到底是怎样的一个bug,能让95%的Pytorch库中招,就连特斯拉AI总监深受困扰?
还别说,这个bug虽小,但有够“狡猾”的。
这就是最近Reddit上热议的一个话题,是一位网友在使用再平常不过的Pytorch+Numpy组合时发现。
最主要的是,在代码能够跑通的情况下,它甚至还会影响模型的准确率!
除此之外,网友热议的另外一个点,竟然是:
而是它到底算不算一个bug?
这究竟是怎么一回事?
事情的起因是一位网友发现,在PyTorch中用NumPy来生成随机数时,受到数据预处理的限制,会多进程并行加载数据,但最后每个进程返回的随机数却是相同的。
他还举出例子证实了自己的说法。
如下是一个示例数据集,它会返回三个元素的随机向量。这里采用的批量大小分别为2,工作进程为4个。
然后神奇的事情发生了:每个进程返回的随机数都是一样的。
这个结果会着实让人有点一头雾水,就好像数学应用题求小明走一段路程需要花费多少时间,而你却算出来了负数。
发现了问题后,这位网友还在GitHub上下载了超过10万个PyTorch库,用同样的方法产生随机数。
结果更加令人震惊:居然有超过95%的库都受到这个问题的困扰!
这其中不乏PyTorch的官方教程和OpenAI的代码,连特斯拉AI总监Karpathy也承认自己“被坑过”!
但有一说一,这个bug想要解决也不难:只需要在每个epoch都重新设置seed,或者用python内置的随机数生成器就可以避免这个问题。
到底是不是bug?
如果这个问题已经可以解决,为什么还会引起如此大的讨论呢?
因为网友们的重点已经上升到了“哲学”层面:
这到底是不是一个bug?
在Reddit上有人认为:这不是一个bug。
虽然这个问题非常常见,但它并不算是一个bug,而是一个在调试时不可以忽略的点。
就是这个观点,激起了千层浪花,许多人都认为他忽略了问题的关键所在。
这不是产生伪随机数的问题,也不是numpy的问题,问题的核心是在于PyTorch中的DataLoader的实现
对于包含随机转换的数据加载pipeline,这意味着每个worker都将选择“相同”的转换。而现在NN中的许多数据加载pipeline,都使用某种类型的随机转换来进行数据增强,所以不重新初始化可能是一个预设。
另一位网友也表示这个bug其实是在预设程序下运行才出现的,应该向更多用户指出来。
并且95%以上的Pytorch库受此困扰,也绝不是危言耸听。
有人就分享出了自己此前的惨痛经历:
我认识到这一点是之前跑了许多进程来创建数据集时,然而发现其中一半的数据是重复的,之后花了很长的时间才发现哪里出了问题。
也有用户补充说,如果 95% 以上的用户使用时出现错误,那么代码就是错的。
顺便一提,这提供了Karpathy定律的另一个例子:即使你搞砸了一些非常基本代码,“neural nets want to work”。
你有踩过PyTorch的坑吗?
如上的bug并不是偶然,随着用PyTorch的人越来越多,被发现的bug也就越来越多,某乎上还有PyTorch的坑之总结,被浏览量高达49w。
其中从向量、函数到model.train(),无论是真bug还是自己出了bug,大家的血泪史还真的是各有千秋。
所以,关于PyTorch你可以分享的经验血泪史吗?
欢迎评论区留言讨论~
参考链接:
[1]https://tanelp.github.io/posts/a-bug-that-plagues-thousands-of-open-source-ml-projects/
[2]https://www.reddit.com/r/MachineLearning/comments/mocpgj/p_using_pytorch_numpy_a_bug_that_plagues/
[3]https://www.zhihu.com/question/67209417/answer/866488638
— 完 —
为啥我的随机数生成器在 C# 中看起来不是随机的?
【中文标题】为啥我的随机数生成器在 C# 中看起来不是随机的?【英文标题】:Why does it appear that my random number generator isn't random in C#?为什么我的随机数生成器在 C# 中看起来不是随机的? 【发布时间】:2010-10-30 06:31:00 【问题描述】:我正在使用 Microsoft Visual C# 2008 Express。
我找到了这个sn-p的代码:
public static int RandomNumber(int min, int max)
Random random = new Random();
return random.Next(min, max);
问题是我已经运行了超过 100 次,当我的 min = 0 和 max = 1 时,它总是给我相同的答案。我每次都得到 0。 (我创建了一个测试函数来运行它——真的——我每次都得到 0)。我很难相信这是一个巧合......我还能做些什么来检查或测试这个吗? (我确实用 min = 0 和 max = 10 重新运行了测试,前 50 次,结果总是“5”,第二次 50 次,结果总是“9”。
??我需要一些更一致的随机...
-阿迪娜
【问题讨论】:
出于某种原因,我认为“始终如一地随机”真的很有趣。 我想补充一点,根据您的应用程序的目的,使用伪随机将始终(至少在理论上)使您的应用程序可破解。 【参考方案1】:您的范围不正确。 minValue 包含在范围内,而 maxValue 在范围内不包含(意味着它不会包含在范围内)。所以这就是它只返回 0 的原因。
另一个有用的注意事项:在方法中创建一个 Random 实例并不理想,因为它可能会在调用时获得相同的种子。所以我会说使用:
static Random gen = new Random();
public static int RandomNumber(int minValue, int maxValue)
return gen.Next(minValue, maxValue);
【讨论】:
【参考方案2】:您误解了“random.Next(min, max)”这一行。 “min”代替允许随机生成的最小数字。虽然“max”是在不允许生成的最小数字的位置,但它不是在允许绘制的最大数字的位置。所以当这条线是 random.Next(0, 1) 你基本上只允许绘制 0。
【讨论】:
【参考方案3】:我找到了一种非常简单但有效的方法来生成随机数,只需获取当前日期时间毫秒的最后两位数:
int seed = Convert.ToInt32(DateTime.Now.Millisecond.ToString().Substring(1, 2));
int cnr = new Random(seed).Next(100);
它很粗糙,但它有效! :-)
当然,它会在统计上每一百次生成相同的数字。或者,您可以采用所有三位数字或与其他日期时间值(例如秒左右)连接。
【讨论】:
【参考方案4】:你总是得到 0,因为 Random.Next
返回整数。您需要调用Random.NextDouble
,它将返回一个介于 0 和 1 之间的数字。此外,您应该重用 Random 实例,如下所示:
[ThreadStatic]
static Random random;
public static Random Random
get
if (random == null) random = new Random();
return random;
public static int RandomInteger(int min, int max)
return Random.Next(min, max);
public static double RandomDouble() //Between 0 and 1
return Random.NextDouble();
如果您想要加密安全的随机数,请使用 RNGCryptoServiceProvider
类;见this article
编辑:线程安全
【讨论】:
-1 因为这段代码是危险的错误。 Random 不是线程安全的,您正在访问共享实例而不提供任何锁定或每个线程实例。【参考方案5】:几位张贴者声明 Random() 使用基于系统时钟上当前秒的种子,并且在同一秒内创建的任何其他 Random 实例将具有相同的种子。这是不正确的。 Random 的无参数构造函数的种子基于滴答计数或自启动时间以来的毫秒数。此值在大多数系统上大约每 15 毫秒更新一次,但可能会因硬件和系统设置而异。
【讨论】:
【参考方案6】:这是任何答案的附录,因为这个特定问题的答案是边界应该是 (0, 2) 而不是 (0, 1)。
但是,如果您想使用静态包装方法,那么您必须记住Random
不是线程安全的,因此您需要提供自己的同步机制或提供每个线程的实例。这是一个很大程度上非阻塞的实现,它使用一个生成器来为每个线程生成器播种:
public static class ThreadSafeRandom
private static readonly Random seed = new Random();
[ThreadStatic]
private static Random random;
public static int Next(int min, int max)
if (random == null)
lock (seed)
random = new Random(seed.Next());
return random.Next(min, max);
// etc. for other members
【讨论】:
Environment.TickCount (默认种子)对于种子来说已经足够了;制作单独的种子实例没有意义。 @SLaks - 如果你依赖 Environment.TickCount 那么你很可能会为每个线程实例获得相同的种子,这将导致它们产生相同的伪随机值序列。我非常怀疑这是你想要的。通过给他们一个明确不同的种子,你可以防止这种行为模式的发生。【参考方案7】:random = new Random();
这会以当前时间(以秒为单位)启动随机数生成器。当您在系统时钟更改之前多次调用您的函数时,随机数生成器会使用相同的值启动,因此它会返回相同的值序列。
【讨论】:
Random() 构造函数的文档几乎一字不差地描述了这个问题:msdn.microsoft.com/en-us/library/h343ddh9.aspx【参考方案8】:正如其他人所提到的,每秒构建多次的 Random 使用与种子相同的秒数,所以我将 Random 构造函数放在循环之外,并将其作为参数传递,如下所示:
public static int RandomNumber(Random random, int min, int max)
return random.Next(min, max);
正如其他人所提到的,最大值是独占的,所以如果你想要一个 0 或 1,你应该使用 [0,2] 作为你的 [min,max],或者更大的最大值,然后使用二进制与1.
public static int RandomOneOrZero(Random random)
return random.Next(0, int.MaxValue) & 1;
【讨论】:
Random对象也可以声明为静态变量,不需要每次都传递给方法。 我喜欢 RandomOneOrZero 方法。您使用它是因为您知道它比使用 [0,2] 更随机还是出于其他原因? @Matt:无论何时,随机性都是一样的。我只是把它从我的头顶上掀了起来。 @Whatsit:好点。我不确定她的用例是什么,所以我只是猜测了一下。静态或实例变量可能更合适。【参考方案9】:除了其他答案中已经提到的 0-1 问题之外,当您正在寻找 0-10 范围并连续 50 次获得相同结果时,您的问题是真正的问题。
new Random()
应该返回一个随机数,种子从计时器初始化(当前秒),但显然你每秒调用此代码 50 次。 MSDN 建议:“为了提高性能,创建一个 Random 以随着时间的推移生成许多随机数,而不是重复创建一个新的 Random 以生成一个随机数。”。如果您在方法之外创建了随机生成器,那应该可以解决您的“非随机性”问题并提高性能。
如果您需要“更高质量”的伪随机数,还可以考虑使用this post 以获得比系统提供的更好的伪随机数生成器。
【讨论】:
【参考方案10】:不要为 Next 创建包装方法。它浪费了创建 Random 类的新实例的周期。就用同一个吧!
Random myRand = new Random();
for(int i = 0; i < 10; i++)
Console.WriteLine(myRand.Next(0, 10).ToString());
这应该给你十个随机值。
如前所述——Random 是伪随机的(就像所有实现一样),如果您使用相同的种子创建 100 个实例,您将获得 100 个具有相同结果的实例。确保您正在重用该类。
另外,正如人们所说,要注意 MinValue 是包容性的,而 MaxValue 是排斥性的。对于你想要的,执行 myRand.Next(0, 2)。
【讨论】:
做 10 次或更多次只是测试......我没有意识到“max”是排他性的。 有时你会想要一个 Random 的包装器方法(或者更可能是一个包装器类),以便你可以模拟它。 @adeena, BlackWasp:如果您出于任何原因需要重载/包装 Next,那就去做吧。但是,如果您只想获得另一个随机值,请使用 Next 并使用相同的 Random 类。这样一来,您就不会冒太多风险获得一堆具有相同种子的 Random。【参考方案11】:Next() 的重载返回:
大于等于 minValue 且小于 maxValue 的 32 位有符号整数;即返回值的范围包括minValue,但不包括MaxValue。如果 minValue 等于 maxValue,则返回 minValue。
0 是它返回的唯一可能值。也许你想要 random.NextDouble(),它会返回一个介于 0 和 1 之间的双精度数。
【讨论】:
【参考方案12】:min = 0 和 max = 1 的问题是 min 是包含的,而 max 是排除的。因此,该组合的唯一可能值是 0。
【讨论】:
为了让事情更加混乱,文档指出 maxValue 必须大于或等于 minValue。因此,您可以传入 random.Next(0,0),它的功能与 random.Next(0,1) 完全相同,即使没有与指定输入参数匹配的数字。【参考方案13】:最小值包含在内,但最大值不包含在内。 Check out the API
【讨论】:
【参考方案14】:在 VB 中,我总是从 Randomize() 函数开始。只需调用 Randomize() 然后运行您的随机函数。我还执行以下操作:
Function RandomInt(ByVal lower As Integer, ByVal upper As Integer) As Integer
Return CInt(Int((upper - lower + 1) * Rnd() + lower))
End Function
希望这会有所帮助! :)
【讨论】:
你是真的吗?你知道我有多少次得到 VB 问题的 C# 答案并且能够弄清楚我做错了什么吗?这是概念,伙计,不是语法...天哪... 这些函数中的每一个仅在 VB 中可用(除非您引用 Microsoft.VisualBasic.dll) System.Random 类完全不同。以上是关于为啥我使用PyTorch来生成随机数总会失败?的主要内容,如果未能解决你的问题,请参考以下文章