2020最佳AI新基建年度榜
您正在使用IE低版浏览器,为了您的雷锋网账号安全和更好的产品体验,强烈建议使用更快更安全的浏览器
此为临时链接,仅用于文章预览,将在时失效
人工智能开发者 正文
发私信给skura
发送

0

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

本文作者: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)

  • 如果将所有权重初始化为零,然后使用 ReLU 会如何?

有几种方法可以初始化神经网络中的权重。你可以从将所有的权重设置成零开始(这是不可取的),你可以随机初始化它们,或者你可以选择一种技术,如 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 吗?你忘了把梯度归零了吗?这是 PyTorch 用户特有的,因为这些梯度是在反向传播过程中累积的,不会更新。你不希望将权重以小批量混合;你希望以正确的方式更新参数。

请在 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/

  • 设置的时间段太少,无法在大型数据集上正确训练模型。你可能有一个相当大的数据集,例如 ImageNe,并且你不允许模型(不是预先训练的模型)通过数据进行足够数量的迭代。

  • 为相对较小的数据集设置过大的 batch 大小。你正在为一个只有 100 个图像的模型拟合,并将 batch 大小设置为 64。在这种情况下,有一个相对较小的 batch 更好。

数据集构造和其他:

  • 你没有以正确的方式构造验证集。训练数据集中的类分布在很大程度上不同于验证集。验证本身也可能有问题。假设你正在构建一个图像分割模型,并且数据集由从视频中捕捉的几个帧组成。创建带有随机拆分的部分验证集可能不是一个好主意,因为你可能最终在验证集中拥有一个与训练集中某个图像非常相似的图像。在这种情况下,模型很容易从验证集中对图像进行分割。一个好的验证集包含的图像本质上与训练集中的图像不连续。(该示例的灵感来自 Fastai 提供的《Practical Deep Learning for Coders v3》课程。)

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

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

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

  • 数据集中有标签噪声。这是一个非常严重的问题,很难发现。当数据点的标记不正确时,就会出现此问题。假设您正在处理狗猫数据集,有一些狗的图像被错误地标记为猫,有些猫的图像被错误地标记为狗。如果在错误未纠正的情况下训练模型,你会发现它没有按预期执行。

  • 假设你正在微调一个预先训练的模型,以便对不同品种的金鱼进行分类。如果在构造数据集时,没有使用预训练模型的原始数据集的平均值和标准偏差来规范化数据集。这样,你的网络将无法捕获正在接受训练的数据集的真实分布。

  • 数据集中有一个主要的类不平衡,但是你得到了一个很好的精度分数,你就陷入了精度悖论。部署模型时,它无法检测次要类。

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

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

  • 在数据增强过程中,你选择的超参数会增强图像,使其标签更改。

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

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

  • 在应用迁移学习时,你没有使用原始数据集的平均值,在原始数据集上训练模型(将要使用的模型)来对你的自定义数据集执行平均减法。假设您正在使用 VGG16 网络构建一个狗的图像分类器。与猫数据集相比,网络是在 ImageNet 数据集上训练的。现在假设你正在编写数据加载器,如下所示:

# 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 中,仔细地列出了原型设计的三个主要目标,下面我将对它们进行总结。

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

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

  • 运行实验并跟踪你所做的尝试:有时,如果你和你的团队没有相应地维护一个注册表,则几乎不可能跟踪原型制作过程中发生的所有事情。因此,你可能会错过原型制作过程中发生的一些不可思议的事情。总结原型制作过程中的实验,以及 floydhub 之类的平台或 git 提交的电子表格中的适当指标及其结果。下图完美地表达了这一想法:

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

灵感来源

  • 分析模型行为。它做的是你想要的吗?你的指标提高了吗?如果没有,那就找出原因。一个好的起点是分析网络内部发生的偏差-方差分解。TensorBoard 可能会很有帮助,因为它使你能够非常有效地可视化你的网络的训练行为与许多定制选项。

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

过拟合单批数据

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

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

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

                                          -Tom B Brown,2019 年 7 月 30 日

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

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

  • 损失可能上升而不是下降

  • 损失可能会持续下降一段时间,然后突然上升

  • 损失可能在一个区域内振荡

  • 损失可以降到一个标量(例如,0.01),但不会比这个更好

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

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

根据我的经验,我发现我最常见的错误要么是没有按照正确的顺序加载数据和标签,要么是没有在登录时应用 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)

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

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

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

  • 对损失函数的输入不正确:这在我们的模型中不存在,因为我们使用 CrossEntropy 作为损失函数,它隐式地处理这种情况。如果我们用的是 NegativeLogLoss,我们可以再检查一遍。

  • 数值不稳定性- inf/NaN:这可以通过观察每一层的数学运算来验证。除法、求幂、对数等运算都可能导致 inf/NaN。在我们的情况中,只有最后一层不是这样的。

这里需要考虑的几点:

  • 创建随机子集几次,并查找评估度量中的任何特殊变化。如果观察到变化,我们肯定应该进一步调查。

  • 预测几个单独的测试样本并手动验证它们。如果模型对图像的分类不正确,请进行人工评估——你是否可以对该图像进行正确分类?

  • 可视化模型的中间性能。这有助于学习模型是如何处理图像的。正如 Andrej Karpathy 在他的文章中所说的那样:

明确正确的数据可视化位置就在 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》中首次探索了深度学习彩票的思想。作者认为,删除网络中的小权重并对其进行再训练可以产生令人震惊的结果。他们提出的想法非常简单:训练一个网络,将小于某个阈值的权重设置为零,即删减权重,然后用未运行的权重重新训练网络,使其达到初始配置。这不仅比无权值剪枝的网络有更好的性能,它还表明了:

  • 与更大的、未运行的网络相比,积极剪枝的网络(85% 到 95% 的权重修剪)性能没有下降

  • 只有适度剪枝的网络(50% 到 90% 的权重修剪)通常比未剪枝的网络表现更好。

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

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

量化

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

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

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

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

结论

你看到了最后:恭喜你!

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

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

雷锋网雷锋网(公众号:雷锋网)雷锋网

雷锋网版权文章,未经授权禁止转载。详情见转载须知

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

分享:
相关文章

文章点评:

表情
当月热门文章
最新文章
请填写申请人资料
姓名
电话
邮箱
微信号
作品链接
个人简介
为了您的账户安全,请验证邮箱
您的邮箱还未验证,完成可获20积分哟!
请验证您的邮箱
立即验证
完善账号信息
您的账号已经绑定,现在您可以设置密码以方便用邮箱登录
立即设置 以后再说