使用pytorch和numpy? 你犯了一个错误

2021-04-11 14:43:13

ML代码中的错误难以修复 - 它们不会导致编译错误,但默默地回归准确性。一旦你忍受了痛苦并固定其中一个,就会将课程蚀刻到你的大脑中?错误的。最近,一个古老的敌人卷土重来 - 一个熟悉的虫子再次咬我!如前所述,修复后性能显着提高。

这虫子很微妙,易于制作。它有多少伤害?好奇,我从导入pytorch的github下载了超过十万个存储库,并分析了他们的源代码。我保留了定义自定义数据集的项目,使用Numpy的随机数发生器具有多过程数据加载,并且使用抽象语法树来分析更加有或更短的简单。其中,超过95%的存储库受到这个问题的困扰。它位于Pytorch的官方教程中,Openai的代码和Nvidia的项目。甚至Karpataly也承认了牺牲者。

在Pytorch中加载,预处理和增强数据的规范方式是将retch.utils.data.dataset子类分类并覆盖其__getItem__方法。要应用增强,例如随机裁剪和图像翻转,__getItem__方法通常使用numpy来生成随机数。然后将Map样式数据集传递给DataLoader以创建批处理。培训管道可能是通过数据预处理的瓶颈,因此它有意义地加载数据。这可以通过增加DataLoader对象中的Num_Workers参数来实现。

要使问题进行具体信息,这是一个示例数据集,它返回三个元素随机向量。我们使用两个和四个工人流程的批量大小。

从Torch.utils.Data导入数据集,Dataloader类随机ataSet(DataSet):def __getItem __(self,索引):返回np .random .randint(0,000,3)def __len __(self):返回16个数据集= Datraloader批处理的DramanDataset()Dataloader = Dataloader = Dataloader(DataSet,Batch_size = 2,Num_Workers = 4):打印(批量)

张量([[116,760,679],#第1批次,由过程0返回[754,897,764])张量([[116,760,679],#第2批次,通过过程返回[754, 897,764]])张量([116,760,679],第3批次,由过程2返回[754,897,764])张量([[116,760,679],#第4批次返回通过方法3 [754,897,764]])张量([[866,919,441],由过程0返回的#5批次[20,727,680])张量([[866,919,441] ,#6批次,由过程1 [20,727,680]])张量([[866,919,441],#第7批次,由过程2返回[20,727,680])张量([[第86,919,441],第8批次,由过程3返回[20,727,680]])

Pytorch使用多处理来并行加载数据。使用Fork Start方法创建工人进程。这意味着每个工作进程继承父的所有资源,包括Numpy的随机数生成器状态。

DataLoader构造函数具有可选的Worker_init_fn参数。在发生任何数据加载之前,在初始化之前调用此函数在发生任何数据加载之前。您可以在worker_init_fn中设置numpy的种子,例如:

def worker_init_fn(worker_id):np .random .seed(np .random .get_state()[1] [0] + worker_id)dataset = somandeDataset()dataloader = dataloader(数据集,batch_size = 2,num_wayers = 4,worker_init_fn = worker_init_fn )对于DataLoader中的批处理:打印(批量)

张量([[282,4,785],[35,581,521]]张量([684,17,95],[774,794,420]]张量([[939,988,37] ,[983,933,821]])张量([[832,50,453],[37,222,981])张量([[180,413,50],[894,318,729]])张量([[530,594,116],[636,468,264])张量([[142,88,429]]张量([[69,965,760] ,[360,872,22]])

时期:0tensor([282,4,785],[35,581,521])张量([684,17,95],[774,794,420]]张量([[939,988, 37],[983,933,821]])张量([[832,50,453],[37,322,981]])------------------- -------时期:1强度([282,4,785],[35,581,521])张量([[684,17,95],[774,794,420])张量([[939,988,37],[983,933,821]])张量([[832,50,453],[37,322,981]])---------- - ------------ epoch:2tensor([[282,4,785],[35,581,521])张量([[684,17,95],[774, 794,420]])张量([983,988,37]])张量([[832,50,453],[37,322,981]])---- ---------------------

在数据集上迭代三次在每个时代产生相同的随机数。这发生了,因为所有对随机状态的更改都是每个工人的本地。默认情况下,工人进程在每个时代末尾终止,并且所有工人资源都会丢失。同时,主进程中的随机状态尚未更改,它用于再次初始化每个工人进程。

因此,您需要在每个时代更改numpy的种子,例如通过np.random.seed(ini​​tial_seed + epoch)。

此外,如果使用Pytorch(例如,Torch.Randint)或Python内置随机数发生器对随机数进行样本随机数,则不会有这些问题。通过将上述种子设置为Seed + Worker_ID,Pytorch根据自动设置上述种子来照顾这些。

在Pytorch中使用自定义数据集的Go-to tutorial是他们网站上列出的tut。本教程演示了如何在面部地标数据上使用DataSet和Dataloader类。它还提到了数据增强的重要性,并提供了随机作物增强的示例。这是使用numpy的随机数生成器实现的。

通过增加Num_Workers加速数据加载的尖端,您可以获得相同的作物:

在涉及能量基模型的纸张隐含生成和建模中,用于图像的生成建模的基于能量的模型。 DataSet的__getItem__方法从磁盘读取图像和标签,损坏前者,并返回所有三个:

如果标志.DataSource =='默认&#39 ;: im_corrupt = im + 0.3 * np .random .randn(image_size,image_size)elif标志.datasource ==' rancor&#39 ;: im_corrupt = 0.5 + 0.5 * np .random .randn(image_size,image_size)return im_corrupt,im,label

Melgan的官方代码,在Neurips会议上发布的生成音频合成模型,通过使用Numpy采样随机标量来增强音频文件的响度。

数据,sampling_rate = load(full_path,sr = self .sampling_rate)数据= 0.95 *标准化(数据)如果自我。ugment:sollude = np .random .uniform(低= 0.3,high = 1.0)数据=数据*幅度

错误很容易制作。在某些情况下,它对最终性能产生最小的影响。在其他人中,相同的增强可能导致严重的降级。

基于开源Pytorch项目的分析,担心问题存在于支持真实产品的许多码名中。 我希望更好地了解陷阱,最终在Pytorch中更好地处理它,使这些产品变得更好。