资讯 人工智能开发者
此为临时链接,仅用于文章预览,将在时失效

黑客视角:避免神经网络训练失败,需要注意什么?

作者:skura
2019/10/04 09:59

黑客视角:避免神经网络训练失败,需要注意什么?

确保网络正常运行的关键因素之一是网络的配置。正如机器学习大师 Jason Brownle 所说,「深度学习神经网络已经变得易于定义和拟合,但仍然难以配置。」

本文分为以下几个部分:

在这一过程中,我将分享个人评论、来自资深学习实践者的故事和代码片段。享受你的学习之旅吧!

训练神经网络

让我们先来看一下可能会失败的神经网络有哪些共同点。正如 OpenAI 的 Josh Tobin 所指出的那样,深度学习模型中最常见的五个错误如下:

黑客视角:避免神经网络训练失败,需要注意什么?

实施 bug:

X_train = shuffle(X_train)

Y_train = shuffle(y_train)

而实际上它应该是:

X_train, y_train = shuffle(X_train, y_train)

黑客视角:避免神经网络训练失败,需要注意什么?

样本数据集

这里有三个分类特征:Sex, Has_Masters 和 Has_Bachelors。你可以用 one-hot 编码来更好地表示关系,或者你可以保持它们的原样。数据集中有两个连续的特征:Age 和 Bounties。它们在计量单位上有很大的不同,所以你需要将它们的比例标准化。由于所有变量都是数值型的,你可以考虑使用以下方法来标准化连续特征:

scaler = StandardScaler()

scaled_train = StandardScaler().fit_transform(data.values)

但它应该是:

scaled_train = StandardScaler().fit_transform(data[non_cat_feats].values)

有几种方法可以初始化神经网络中的权重。你可以从将所有的权重设置成零开始(这是不可取的),你可以随机初始化它们,或者你可以选择一种技术,如 Xavier 初始化或 HE 初始化。如果你使用 Xavier 或 HE 方案,则需要相应地考虑激活函数。例如,推荐 TANH 激活使用 Xavier 方案,而 RELU 激活使用 HE 方案。使用 keras 声明网络时,请考虑以下示例:

# Define a Sequential model

model = Sequential()

model.add(Dense(64, kernel_initializer='zeros', activation='relu'))

...

在上面的例子中,模型的权重初始化为零,这意味着在将输入值乘以初始化为零的权重后,结果只有零。如果要使用 ReLU 激活函数,更好的方法是:

# Define a Sequential model

model = Sequential()

model.add(Dense(64, kernel_initializer='he_normal', activation='relu'))

...

请在 PyTorch 中考虑以下训练循环:

for e in range(epochs):
   running_loss = 0
# Loop over the images and labels in the current batch
   for images, labels in trainloader:
   # Load the data to the available device and reshape the images          
       images = images.to(device)
       labels = labels.to(device)
       images = images.view(images.shape[0], -1)
             
       # Get predictions from the model and determine the loss
       log_ps = model(images)
       loss = criterion(log_ps, labels)

       # Calculate the gradients and update the parameters
       loss.backward()
       optimizer.step()

       running_loss += loss.item()

注意,在更新参数之前,代码不会将梯度归零。在使用模型进行预测之前,请尝试下面的代码行:optimizer.zero_grad()。

模型对超参数选择的敏感性:

非常高的学习速率会导致非常大的权重更新,产生 NaN 值。由于这种数值具有不稳定性,当 NaN 值开始慢慢变多时,网络变得完全无用。

学习速率是一个完整的领域,需要深入研究。如果您感兴趣,可以研究下这篇文章:https://blog.floydhub.com/ten-techniques-from-fast-ai/

数据集构造和其他:

训练组中的数据分布与测试组有很大不同。例如,你已经在猫和狗的低分辨率图像上训练了模型,并且正在高分辨率图像上测试模型。请考虑以下示例,以便更清楚地理解这一点:

黑客视角:避免神经网络训练失败,需要注意什么?

假设一个虚拟网络正在由左侧图像组成的数据集上训练。现在,如果在右边的图片上进行测试,这个训练过的网络很可能会失败,因为网络从来没有遇到过猫的图片。

以上问题是深度学习实践者在日常工作中遇到的最普遍的问题。我们非常需要完全拥有我们的深度学习模型,这样我们就可以根据需要调试它们,而不会失去理智。

使整个深度学习模型调试过程非常困难的因素是,一个深度学习模型可能会悄无声息地失败。考虑以下情况:

黑客视角:避免神经网络训练失败,需要注意什么?

数据增强的影响有时可能是残酷的!在这种情况下,数字 6 可能会在其标签仍为 6 时旋转为 9

# initialize the data generator object

datagen = ImageDataGenerator()


# specify the mean to the data generator object

# so that it can perform mean subtraction on the fly

datagen.mean = dogs_cats_mean

但在这种情况下,正确的方法是:

# initialize the data generator object

datagen = ImageDataGenerator()


# specify the mean to the data generator object

# so that it can perform mean subtraction on the fly

mean = np.array([123.68, 116.779, 103.939], dtype="float32") # ImageNet mean

datagen.mean = mean

上面的代码片段使用 Keras ImageDataGenerator 类将数据流传输到模型。

不幸的是,这些对单元测试来说也不是小事。你将需要对模型、其配置、超参数选择等有完整的命令,以了解其失败的原因和性能良好的原因。正如 Andrej Karpathy 所解释的那样:

因此,(这一点很难被过分强调)训练神经网络的「快速而激烈」的方法不起作用,只会导致痛苦。现在,痛苦是让神经网络正常工作的一个非常自然的部分,但它可以通过彻底、偏执和沉迷于基本上所有可能事情的可视化来减轻。

显然,我们需要在这方面有相当水平的专业知识,使我们能够在适当的时候发现上述各种问题。这需要经验、知识和深思熟虑的实践。

在我们练习的时候,我们需要知道一些最常见的策略,以避免让我们自己遭受遇到怪异模型的痛苦。训练神经网络需要大量的原型。在下一节中,我们将重点介绍模型原型制作过程中的一些要点和一些应用策略。

维护一个健康的原型过程

深度学习实验包括快速原型,即为给定的任务尝试新的模型架构,使用相同的模型架构尝试不同的配置,等等。Matt Gardner 等人在他们广受欢迎的 Writing Code for NLP Research 中,仔细地列出了原型设计的三个主要目标,下面我将对它们进行总结。

快速编写代码:通过重用现有的代码/框架建立一个基线(又称:不要重新发明轮子!)。尝试找到一个现有的项目来解决你正在处理的相同问题(或与问题非常相似的问题)。这里的想法是快速脱离标准位,在原型制作过程中更多地关注新的位。

小心使用别人的组件也是明智的。你应该能够阅读代码,能够在需要时绕过抽象等等。

黑客视角:避免神经网络训练失败,需要注意什么?

灵感来源

现在,我们将讨论一个非常关键全面的检查机制,它可以帮助识别模型中的许多隐藏错误,有时还可以帮助识别数据预处理步骤中的许多隐藏错误。

过拟合单批数据

黑客视角:避免神经网络训练失败,需要注意什么?

-在进行其他操作之前,请确认你的模型能够记住单个 batch 的标签,并快速将损失降到零

-这个跑起来很快,如果模型做不到,那你就知道它坏了

                                          -Tom B Brown,2019 年 7 月 30 日

这种理智的检查常常被忽视!

这种技术假设我们已经有了一个模型,并在给定的数据上运行。现在,我们希望能够在单个 batch 数据上得到任意接近于零的损失。这带来了许多问题:

以下是导致上述问题的最常见原因:

黑客视角:避免神经网络训练失败,需要注意什么?

根据我的经验,我发现我最常见的错误要么是没有按照正确的顺序加载数据和标签,要么是没有在登录时应用 softmax。

接下来,我们将讨论为什么从一个简单的实验模型体系结构开始,然后逐渐增加复杂性常常会有帮助。它不仅有助于更好的研究,而且对于模型调试也非常有效。

作为顺序函数的模型复杂性

简单的开始比我们想象的更重要。将一个浅层的完全连接的网络与我们的数据集相匹配必然会给我们带来糟糕的性能。我们应该确定这一部分,它不应该偏离太多。如果偏离的话,肯定是出问题了。在简单的模型中检测出错误比在 resnet101 模型中更容易。

在重用预先训练的模型之前,确定该模型是否真的适合当前的任务。以下是在选择网络体系结构时应考虑的一些指标:

PS:我们在上一篇文章中使用了 FashionMNIST 数据集,我们在这里也将使用这个数据集。

FashionMNIST 数据集带有预定义的训练集和测试集。我们将首先创建更小的数据子集,包括训练集中的 1000 个(每个类 100 个图像)图像和随机顺序的测试集中的 300 个(每个类 30 个图像)。我们还将确保这两个集合中类的分布没有任何偏差。

我们首先可以使用下面的 helper 函数生成一组关于标签和集合的随机索引:

def generate_random_subset(label, ds_type):
   if ds_type == 'train':
       # Extract the label indexes
       index, = np.where(y_train==label)
       index_list = np.array(index)
       # Randomly shuffle the indexes
       np.random.shuffle(index_list)
       # Return 100 indexes
       return index_list[:100]

   elif ds_type == 'test':
       # Extract the label indexes
       index, = np.where(y_test==label)
       index_list = np.array(index)
       # Randomly shuffle the indexes
       np.random.shuffle(index_list)
       # Return 30 indexes
       return index_list[:30]

然后我们可以以迭代的方式创建训练和测试,如下所示:

# Generate the training subsetindexes = []for label in np.unique(y_train):
   index = generate_random_subset(label, 'train')
   indexes.append(index)all_indexes = [ii for i in indexes for ii in i]x_train_s, y_train_s = x_train[all_indexes[:1000]],\
           y_train[all_indexes[:1000]]

现在训练子集已经被创建好啦。可以用类似的方式创建测试子集。创建测试子集时,请确保将测试传递给 generate_random_subset()函数。

这两个子集现在准备好了。我们现在可以建立一个非常简单、完全连接的网络。让我们从以下体系结构开始:

黑客视角:避免神经网络训练失败,需要注意什么?

Flatten 层将图像转换为 Flatten 向量,然后是密集层,最后是另一个密集层生成输出。让我们看看其他细节的代码:

# Baseline modelmodel = tf.keras.models.Sequential([
 tf.keras.layers.Flatten(input_shape=(28, 28)),
 tf.keras.layers.Dense(128, activation='relu',kernel_initializer='he_normal'),
 tf.keras.layers.Dense(10, activation='softmax')])model.compile(optimizer='adam',
             loss='sparse_categorical_crossentropy',
             metrics=['accuracy'])

一种直接的模型配置,由于存在 ReLU 激活,可以初始化 dense 的权重。如果你想了解更多关于神经网络中权重初始化的知识,请看这节课。现在是时候继续训练这个模型的 5 个阶段,batch 大小为 32,同时验证它:

# Train the network and validatemodel.fit(x_train_s, y_train_s,
          validation_data=(x_test_s, y_test_s),
          epochs=5,
          batch_size=32)

正如所料,它没有在社交媒体上获得一个值得一提的分数:

黑客视角:避免神经网络训练失败,需要注意什么?

模型过拟合——看看损失和值损失指标。让我们从深度学习中最常见的错误列表里面交叉检查下面几点:

这里需要考虑的几点:

明确正确的数据可视化位置就在 y_hat=model(x)(或 tf 中的 sess.run)之前。也就是说,你想把进入你的网络的东西形象化,把数据和标签的原始张力解码成具体形象。这是唯一的「真理之源」。我数不清这多少次拯救了我,也暴露了数据预处理和扩充方面的问题。

我们来谈谈第二点,并做一些预测。我已经创建了一个 helper 函数,它将以一个索引作为输入,并将返回模型对与索引对应的图像的预测以及它的真实标签。以下是 helper 函数:

def show_single_preds(index):
   pred = model.predict_classes(np.expand_dims(x_test_s[index], axis=0))
   print('Model\'s prediction: ',str(class_names[np.asscalar(pred)]))
   print('\nReality:', str(class_names[y_test_s[index]]))
   plt.imshow(x_test_s[index], cmap=plt.cm.binary)
   plt.show()

下面是一些关于函数的预测:

show_single_preds(12)

黑客视角:避免神经网络训练失败,需要注意什么?

show_single_preds(32)

黑客视角:避免神经网络训练失败,需要注意什么?

模型也做出了一些错误的预测:

show_single_preds(101)

黑客视角:避免神经网络训练失败,需要注意什么?

show_single_preds(45)

黑客视角:避免神经网络训练失败,需要注意什么?

请注意,进行这些实验时,图像可能会有所不同,因为子集是随机构造的。

从这两个错误的预测中,你可以注意到模型混淆了裤装和连衣裙。上图第一眼看上去很像一条裤子,但如果仔细看,那是一条裙子。这个简单的模型无法找到这个细粒度的细节。绘制混淆矩阵应该能说明这一点:

黑客视角:避免神经网络训练失败,需要注意什么?

这个模特把上衣和套头衫混为一谈。如果有人感兴趣,下面的代码生成一个类似于上面的混淆矩阵图:

# Plotting model's confusion matrix

import scikitplot as skplt


preds = model.predict_classes(x_test_s)


skplt.metrics.plot_confusion_matrix(y_test_s, preds, figsize=(7,7))

plt.show()

scikitplot 库使生成混淆矩阵图变得非常容易。

我们现在对基线模型很有信心,我们知道它的配置,我们知道它的失败在哪里。基线模型以展平矢量为输入。当涉及到图像时,像素的空间排列会因为它们变平而丢失。这巩固了一个基础,即在将数据传送到网络之前,数据表示部分很重要,而且这种表示因体系结构而异。尽管我们在图像示例中看到了它,但对于其他类型的数据,其一般概念也保持不变。

你可能希望保存正在工作的训练和测试集的当前子集,以便在合并更复杂的模型时看到任何进一步的改进。由于子集只不过是 numpy 数组,你可以通过以下方式轻松保存它们:

# Saving the subsets for reproducibility

np.save('tmp/x_train_s.npy', x_train_s)

np.save('tmp/y_train_s.npy', y_train_s)

np.save('tmp/x_test_s.npy', x_test_s)

np.save('tmp/y_test_s.npy', y_test_s)

save()以 .npy 格式序列化 numpy 数组。要加载并验证序列化是否正确完成,我们可以编写如下内容:

# Load and verifya = np.load('tmp/x_train_s.npy')plt.imshow(a[0], cmap=plt.cm.binary)plt.show()

你应该得到类似这样的输出:

黑客视角:避免神经网络训练失败,需要注意什么?

此时,你可以安全地使用数据集构建更复杂的模型。Josh Tobin 在他的《Troubleshooting Deep Neural Networks》中列出了常见的架构:

黑客视角:避免神经网络训练失败,需要注意什么?

一旦我们启动并运行了复杂的模型,并且验证了它产生的结果,接下来的步骤就是优化超参数。

调整旋钮:追踪超参数

黑客视角:避免神经网络训练失败,需要注意什么?

超参数对模型的性能有很大影响。与权重和偏差不同,这些是由开发人员明确指定的内容,通常不需要学习。在神经网络中,超参数的例子包括学习速率、阶段数、batch 大小、优化器(以及它的配置)等。如果你想加深对超参数和一些优化过程的理解,我强烈推荐 floydhub 的 alesio 写的这篇文章

我们将获取最重要的超参数集(根据网络类型的不同而有所不同),并讨论以有效方式对其进行优化的策略。

Declarative Configuration 简介

机器学习的代码库通常很容易出错,而神经网络可能会失败得悄无声息。深度学习实验包含大量基于试验和错误的带有超参数的手动检查。随着我们对模型的不断试验,超参数的选择变得越来越成熟。我们可能希望尝试我们最喜欢大小的 batch 或一组不同的学习速率,或两者相结合。在我们的实验中,我们经常希望能够调整超参数的任意组合。

因此,在这些情况下,最好将超参数的规范部分与训练循环分开。有许多框架遵循声明性配置,如 tensorflow 对象检测 api(tfod)、allennlp、caffe 等。下图显示了 TensorFlow 对象检测 API 中遵循的这种配置的一部分:

黑客视角:避免神经网络训练失败,需要注意什么?

请注意,tensorflow 对象检测 api 是如何允许我们指定超参数,如批处理大小、优化器的。最好设计代码库,以便使用声明性配置来处理超参数的规范。我强烈建议您查看本文以了解有关 tensorflow 对象检测 api 的更多信息。

组织超参数调整过程

如前所述,有了经验和对不同组件的算法的良好理解,你可以很好地选择正确的超参数值集。但达到那个水平需要一段时间。在你此之前,你需要依赖于超参数优化方法,比如网格搜索、随机搜索、粗到精搜索和贝叶斯超参数优化。

Josh 列出了最常见的超参数及其对模型性能的影响:

黑客视角:避免神经网络训练失败,需要注意什么?

注意,对于基于序列的模型来说,这些超参数可能会改变。

在本文的最后一部分,我们将讨论两种在提高模型精度方面非常有效的策略:模型集成、知识提炼等等。

模型集成、知识蒸馏

黑客视角:避免神经网络训练失败,需要注意什么?

在本节中,我将向你介绍模型集成,并解释它为什么工作(以及它为什么不工作),然后告诉你有关知识蒸馏的知识。但首先,让我再次引用 Andrej Karpathy 的话:

模型集成是一种几乎可以保证在任何事情上获得 2% 精度的方法。如果你在测试时负担不起计算开销,可以考虑使用暗知识将你的集成提取到一个网络中。

模型集成

模型集成的概念比听起来简单;它指的是组合来自多个模型的预测。但为什么要这么做呢?好吧,神经网络在本质上是随机的,这意味着如果你用相同的数据集进行相同的实验,你可能不会一直得到相同的结果。在生产环境中,甚至在黑客大会和个人项目中,这都会令人沮丧。

这个问题的一个非常简单的解决方案如下:

这种方法不仅允许不同模型的集成来捕获数据集的方差,它也比任何一个模型都能得到更好的分数。Goodfellow 等人在他们广受欢迎的深度学习书籍中简单地解释了为什么这样做:

模型平均有效的原因是不同的模型通常不会在测试集上产生相同的错误。

要详细了解不同的加密方法,可以查看这篇 MLWave 团队的文章

模型集成并不是最终的全部解决方案;当涉及到构建深度学习系统时,它有一个明显的缺点:

提高几乎所有机器学习算法性能的一个非常简单的方法是在相同的数据上训练许多不同的模型,然后对它们的预测进行平均。不幸的是,使用一整套模型进行预测是很麻烦的,而且计算成本可能太高,不允许部署到大量用户,特别是如果单个模型是大型神经网络时——Hinton 等人在《Distilling the Knowledge in a Neural Network》中提到。

这些沉重的模型很难部署在硬件资源有限的边缘设备上。你可能会问:「如果这个庞大的模型可以作为 rest api 在云上公开,然后根据需要在以后使用呢?」但有一个制约因素:缺乏可靠的互联网连接。还有一个因素需要考虑,那就是不太复杂的模型通常无法捕获数据的底层表示。在扩展这些模型时,还需要考虑环境成本,如果模型提供实时推理,还需要考虑对服务水平协议的依赖性。即使你有一个巨大的连接,也不太可能在云中部署模型。以特斯拉自动驾驶仪为例,当汽车在行驶时,它不能总是查询云服务以获得所需的预测。这些预测任务可以包括目标定位、车道检测、行人检测等。

记住,网络越大,占用的设备内存越多,占用的设备内存越多,部署起来就越困难。

我们希望能够将复杂(重量级)模型的知识提取为更简单的模型,我们希望那些更简单的模型能够很好地逼近复杂模型所学习的关系。因此,现在是讨论知识蒸馏的时候了。

知识蒸馏

Hinton 等人早在 2015 年就在他们的开创性论文《Distilling the Knowledge in a Neural Network》中首次提出了知识蒸馏的概念。这个想法涉及两个网络:教师网络和学生网络。

利用教师网络从原始数据中提取模式,以期生成软目标。软目标有助于我们消除在数据集的不同数据点中可能存在相似性的歧义。例如,它帮助我们了解 mnist 数据集中有多少 2 和 3 类似。这些软目标以类概率的形式出现,它们捕获的原始数据集信息比硬目标多得多。软目标也表示一种不确定性,通常被称为暗知识。

然后将这些软目标反馈给学生网络,以模拟教师网络的输出(硬目标)。通过匹配输出分布,训练学生网络以与教师相同的方式进行泛化。但这里有一个小问题:交叉熵损失被软目标(如教师网络所产生的)而不是硬目标所取代,然后转移到学生网络。

我绝对建议检查一下这个由「Hugging Face」团队完成的工作,这个团队能够将知识蒸馏的思想融入到他们的一个架构 distilbert 中,distilbert 是强大的语言模型 bert 的提炼版本。

彩票假说

神经网络的大小取决于它包含的参数数目。例如, VGG16 网络包含 1.38 亿个参数,其大小约为 528MB(keras)。现代语言模型架构,如 BERT 及其变体,甚至更重。看看下面的图表,它显示了语言模型中参数数量的逐渐增加。

黑客视角:避免神经网络训练失败,需要注意什么?

在部署模型以运行推理时,这种沉重性是模型的主要限制。这就是网络剪枝技术发挥作用的地方,它们可以将模型中的参数数量减少 90%,而不会对性能造成太大影响。彩票的概念是一个探索了这一现象的令人难以置信的研究。

Jonathan 等人在 2018 年的论文《The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks》中首次探索了深度学习彩票的思想。作者认为,删除网络中的小权重并对其进行再训练可以产生令人震惊的结果。他们提出的想法非常简单:训练一个网络,将小于某个阈值的权重设置为零,即删减权重,然后用未运行的权重重新训练网络,使其达到初始配置。这不仅比无权值剪枝的网络有更好的性能,它还表明了:

在剪除较大网络中的权重之后,我们得到了一个幸运的子网络,作者称之为中奖彩票。

要了解更多关于彩票的评论以及如何确定剪枝权重的标准,一定要查看 Uber AI 的这篇文章。

量化

如前所述,最终的网络模型通常不太可能通过云作为 api。在大多数情况下,我们希望能够在设备上服务,比如通过手机。另一种减小模型大小并使其更易于服务的方法是量化。

模型量化直接关系到数值精度,如 float64、float32、float16、int8、int16 等。深度学习模型通常在所有计算中使用 float32 精度,这代表着网络参数是最重要的参数。深度学习模型的大小取决于其权重记录的精度。精度越高,模型就越重。所以,问题是:我们能否利用较低的数值精度来表示(重)网络的权重?当然可以,但这需要更低的精度,尽管它仍然可以与较重模型的精度相媲美。这是通过量化实现的。下图显示了当更高精度的网络权重量化为更低精度时会发生什么。

黑客视角:避免神经网络训练失败,需要注意什么?

如果您想了解有关模型量化的更多信息,请从本文开始。

结论

你看到了最后:恭喜你!

整理这篇文章的主要目的是归纳不同研究者的有价值的发现,以便给社区提供一份关于神经网络及其训练的所有相关内容的详尽文档。我们已经介绍了如何训练一个神经网络,你可能会发现什么样的错误,以及如何处理它们,还有非常棒的彩票假说。当然,我的文章不可能包含关于训练和调试神经网络这一主题的所有内容,因此请查看下面的链接,以便进一步阅读,并查看这些资源以进行进一步研究,所有这些都是我对该主题的理解的基础:

via:https://blog.floydhub.com/training-neural-nets-a-hackers-perspective/

雷锋网雷锋网雷锋网

长按图片保存图片,分享给好友或朋友圈

黑客视角:避免神经网络训练失败,需要注意什么?

扫码查看文章

正在生成分享图...

取消
相关文章