第四章:机器学习基础

机器学习的四个分支

监督学习

监督学习是目前最常见的机器学习类型。给定一组样本(通常由人工标注),它可以学会将输入数据映射到已知目标[也叫标注(annotation)]。
虽然监督学习主要包括分类回归,但还有更多的奇特变体,主要包括如下几种。

  • 序列生成(sequence generation)。给定一张图像,预测描述图像的文字。序列生成有时可以被重新表示为一系列分类问题,比如反复预测序列中的单词或标记。
  • 语法树预测(syntax tree prediction)。给定一个句子,预测其分解生成的语法树。
  • 目标检测(object detection)。给定一张图像,在图中特定目标的周围画一个边界框。这个问题也可以表示为分类问题(给定多个候选边界框,对每个框内的目标进行分类)或分类与回归联合问题(用向量回归来预测边界框的坐标)。
  • 图像分割(image segmentation)。给定一张图像,在特定物体上画一个像素级的掩模(mask)。

无监督学习

无监督学习是指在没有目标的情况下寻找输入数据的有趣变换,其目的在于数据可视化、数据压缩、数据去噪或更好地理解数据中的相关性。无监督学习是数据分析的必备技能,在解决监督学习问题之前,为了更好地了解数据集,它通常是一个必要步骤。降维(dimensionality reduction)和聚类(clustering)都是众所周知的无监督学习方法。

自监督学习

自监督学习是监督学习的一个特例,它与众不同,值得单独归为一类。自监督学习是没有人工标注的标签的监督学习,你可以将它看作没有人类参与的监督学习。标签仍然存在(因为总要有什么东西来监督学习过程),但它们是从输入数据中生成的,通常是使用启发式算法生成的。
举个例子,自编码器(autoencoder)是有名的自监督学习的例子,其生成的目标就是未经修改的输入。同样,给定视频中过去的帧来预测下一帧,或者给定文本中前面的词来预测下一个词,都是自监督学习的例子[这两个例子也属于时序监督学习(temporally supervised learning),即用未来的输入数据作为监督]。注意,监督学习、自监督学习和无监督学习之间的区别有时很模糊,这三个类别更像是没有明确界限的连续体。自监督学习可以被重新解释为监督学习或无监督学习,这取决于关注的是学习机制还是应用场景。

强化学习

强化学习一直以来被人们所忽视,但最近随着 Google 的 DeepMind 公司将其成功应用于学习玩 Atari 游戏(以及后来学习下围棋并达到最高水平),机器学习的这一分支开始受到大量关注。在强化学习中,智能体(agent)接收有关其环境的信息,并学会选择使某种奖励最大化的行动。例如,神经网络会“观察”视频游戏的屏幕并输出游戏操作,目的是尽可能得高分,这种神经网络可以通过强化学习来训练。

分类和回归术语表

分类和回归都包含很多专业术语

  • 样本(sample)或输入(input):进入模型的数据点。
  • 预测(prediction)或输出(output):从模型出来的结果。
  • 目标(target):真实值。对于外部数据源,理想情况下,模型应该能够预测出目标。
  • 预测误差(prediction error)或损失值(loss value):模型预测与目标之间的距离。
  • 类别(class):分类问题中供选择的一组标签。例如,对猫狗图像进行分类时,“狗”和“猫”就是两个类别。
  • 标签(label):分类问题中类别标注的具体例子。比如,如果 1234 号图像被标注为包含类别“狗”,那么“狗”就是 1234 号图像的标签。
  • 真值(ground-truth)或标注(annotation):数据集的所有目标,通常由人工收集。
  • 二分类(binary classification):一种分类任务,每个输入样本都应被划分到两个互斥的类别中。
  • 多分类(multiclass classification):一种分类任务,每个输入样本都应被划分到两个以上的类别中,比如手写数字分类。
  • 多标签分类(multilabel classification):一种分类任务,每个输入样本都可以分配多个标签。举个例子,如果一幅图像里可能既有猫又有狗,那么应该同时标注“猫”标签和“狗”标签。每幅图像的标签个数通常是可变的。
  • 标量回归(scalar regression):目标是连续标量值的任务。预测房价就是一个很好的例子,不同的目标价格形成一个连续的空间。
  • 向量回归(vector regression):目标是一组连续值(比如一个连续向量)的任务。如果对多个值(比如图像边界框的坐标)进行回归,那就是向量回归。
  • 小批量(mini-batch)或批量(batch):模型同时处理的一小部分样本(样本数通常为 8~128)样本数通常取 2 的幂,这样便于 GPU 上的内存分配。训练时,小批量用来为模型权重计算一次梯度下降更新。

评估机器学习模型

第 3 章介绍的三个例子中,将数据划分为训练集、验证集和测试集。没有在训练模型的相同数据上对模型进行评估,其原因很快显而易见:仅仅几轮过后,三个模型都开始过拟合。也就是说,随着训练的进行,模型在训练数据上的性能始终在提高,但在前所未见的数据上的性能则不再变化或者开始下降。
机器学习的目的是得到可以泛化(generalize)的模型,即在前所未见的数据上表现很好的模型,而过拟合则是核心难点。

训练集、验证集和测试集

评估模型的重点是将数据划分为三个集合:训练集、验证集和测试集。在训练数据上训练模型,在验证数据上评估模型。一旦找到了最佳参数,就在测试数据上最后测试一次。

为什么不是两个集合:一个训练集和一个测试集?

开发模型时需要调节模型配置,比如层数或每层大小[这叫作模型的超参数(hyperparameter),以便与模型参数(即权重)区分开]。这个调节过程需要使用模型在验证数据上的性能作为反馈信号。这个调节过程本质上就是一种学习:在某个参数空间中寻找良好的模型配置。因此,如果基于模型在验证集上的性能来调节模型配置,会很快导致模型在验证集上过拟合,即使你并没有在验证集上直接训练模型也会如此。
造成这一现象的关键在于信息泄露(information leak)。每次基于模型在验证集上的性能来调节模型超参数,都会有一些关于验证数据的信息泄露到模型中。如果对每个参数只调节一次,那么泄露的信息很少,验证集仍然可以可靠地评估模型。但如果你多次重复这一过程(运行一次实验,在验证集上评估,然后据此修改模型),那么将会有越来越多的关于验证集的信息泄露到模型中。

简单的留出验证

留出一定比例的数据作为测试集。在剩余的数据上训练模型,然后在测试集上评估模型。为了防止信息泄露,不能基于测试集来调节模型,所以还应该保留一个验证集。
在这里插入图片描述

num_validation_samples = 10000
np.random.shuffle(data)   # 通常需要打乱数据
validation_data = data[:num_validation_samples]  # 定义验证集
data = data[num_validation_samples:]
training_data = data[:]  # 定义训练集
model = get_model() 
model.train(training_data)  # 在训练数据上训练模型
validation_score = model.evaluate(validation_data) # 在验证数据上评估模型
# 现在可以调节模型、重新训练、评估,然后再次调节……
model = get_model() 
# 一旦调节好超参数,通常就在所有非测试数据上从头开始训练最终模型
model.train(np.concatenate([training_data,validation_data]))
test_score = model.evaluate(test_data)

这是最简单的评估方法,但有一个缺点:如果可用的数据很少,那么可能验证集和测试集包含的样本就太少,从而无法在统计学上代表数据。K 折验证与重复的 K 折验证,它们是解决这一问题的两种方法。

K 折验证

K 折验证(K-fold validation)将数据划分为大小相同的 K 个分区。对于每个分区 i,在剩余的 K-1 个分区上训练模型,然后在分区 i 上评估模型。最终分数等于 K 个分数的平均值。对于不同的训练集 - 测试集划分,如果模型性能的变化很大,那么这种方法很有用。与留出验证一样,这种方法也需要独立的验证集进行模型校正。
在这里插入图片描述

k = 4
num_validation_samples = len(data) // k
np.random.shuffle(data)
validation_scores = [] 
for fold in range(k):
 # 选择验证数据分区
 validation_data = data[num_validation_samples * fold: num_validation_samples * (fold + 1)]
 # 使用剩余数据作为训练数据。注意,+ 运算符是列表合并,不是求和
 training_data = data[:num_validation_samples * fold] + data[num_validation_samples * (fold + 1):] 
 model = get_model() 
 model.train(training_data)
 validation_score = model.evaluate(validation_data)
 validation_scores.append(validation_score)
validation_score = np.average(validation_scores) # 最终验证分数:K 折验证分数的平均值
model = get_model() 
model.train(data)
test_score = model.evaluate(test_data)

带有打乱数据的重复 K 折验证

如果可用的数据相对较少,而又需要尽可能精确地评估模型,那么可以选择带有打乱数据的重复 K 折验证(iterated K-fold validation with shuffling)。这种方法在 Kaggle 竞赛中特别有用。具体做法是多次使用 K 折验证,在每次将数据划分为 K 个分区之前都先将数据打乱。最终分数是每次 K 折验证分数的平均值。注意,这种方法一共要训练和评估 P×K 个模型(P是重复次数),计算代价很大。

评估模型的注意事项

  • 数据代表性(data representativeness):训练集和测试集都能够代表当前数据,因此,在将数据划分为训练集和测试集之前,通常应该随机打乱数据。
  • 时间箭头(the arrow of time):如果想要根据过去预测未来,那么在划分数据前不应该随机打乱数据,因为这么做会造成时间泄露(temporal leak),即模型将在未来的数据上得到有效训练。在这种情况下,应该始终确保测试集中所有数据的时间都晚于训练集数据。
  • 数据冗余(redundancy in your data)。如果数据中的某些数据点出现了两次,那么打乱数据并划分成训练集和验证集会导致训练集和验证集之间的数据冗余。从效果上来看,在部分训练数据上评估模型,这是极其糟糕的!一定要确保训练集和验证集之间没有交集。

数据预处理、特征工程和特征学习

神经网络的数据预处理

数据预处理的目的是使原始数据更适于用神经网络处理,包括向量化、标准化、处理缺失值和特征提取。

向量化

神经网络的所有输入和目标都必须是浮点数张量(在特定情况下可以是整数张量)。无论处理什么数据(声音、图像还是文本),都必须首先将其转换为张量,这一步叫作数据向量化(data vectorization)。例如,在前面两个文本分类的例子中,开始时文本都表示为整数列表(代表单词序列),然后我们用 one-hot 编码将其转换为 float32 格式的张量。在手写数字分类和预测房价的例子中,数据已经是向量形式,所以可以跳过这一步。

值标准化

在手写数字分类的例子中,开始时图像数据被编码为 0-255 范围内的整数,表示灰度值。将这一数据输入网络之前,需要将其转换为 float32 格式并除以 255,这样就得到 0~1 范围内的浮点数。
同样,预测房价时,开始时特征有各种不同的取值范围,有些特征是较小的浮点数,有些特征是相对较大的整数。将这一数据输入网络之前,需要对每个特征分别做标准化,使其均值为 0、标准差为 1。
一般来说,将取值相对较大的数据(比如多位整数,比网络权重的初始值大很多)或异质数据(heterogeneous data,比如数据的一个特征在 0-1 范围内,另一个特征在 100~200 范围内)输入到神经网络中是不安全的。这么做可能导致较大的梯度更新,进而导致网络无法收敛。为了让网络的学习变得更容易,输入数据应该具有以下特征。

  • 取值较小:大部分值都应该在 0~1 范围内。
  • 同质性(homogenous):所有特征的取值都应该在大致相同的范围内。

此外,下面这种更严格的标准化方法也很常见,而且很有用,虽然不一定总是必需的(例如,对于数字分类问题就不需要这么做)。

  • 将每个特征分别标准化,使其平均值为 0。
  • 将每个特征分别标准化,使其标准差为 1。

这对于 Numpy 数组很容易实现。

x -= x.mean(axis=0)  # 假设 x 是一个形状为 (samples, features) 的二维矩阵
x /= x.std(axis=0)

处理缺失值

一般来说,对于神经网络,将缺失值设置为 0 是安全的,只要 0 不是一个有意义的值。网络能够从数据中学到 0 意味着缺失数据,并且会忽略这个值。
注意,如果测试数据中可能有缺失值,而网络是在没有缺失值的数据上训练的,那么网络不可能学会忽略缺失值。在这种情况下,应该人为生成一些有缺失项的训练样本:多次复制一些训练样本,然后删除测试数据中可能缺失的某些特征。

特征工程

特征工程(feature engineering)是指将数据输入模型之前,利用你自己关于数据和机器学习算法(这里指神经网络)的知识对数据进行硬编码的变换(不是模型学到的),以改善模型的效果。多数情况下,一个机器学习模型无法从完全任意的数据中进行学习。呈现给模型的数据应该便于模型进行学习。
一个直观的例子
在这里插入图片描述
在这里插入图片描述
深度学习出现之前,特征工程曾经非常重要,因为经典的浅层算法没有足够大的假设空间来自己学习有用的表示。将数据呈现给算法的方式对解决问题至关重要。
幸运的是,对于现代深度学习,大部分特征工程都是不需要的,因为神经网络能够从原始数据中自动提取有用的特征。
但这不意味着,只要使用深度神经网络,就无须担心特征工程,原因有两点。

  • 良好的特征仍然可以使得用更少的资源更优雅地解决问题。例如,使用卷积神经网络来读取钟面上的时间是非常可笑的。
  • 良好的特征可以使得用更少的数据解决问题。深度学习模型自主学习特征的能力依赖于大量的训练数据。如果只有很少的样本,那么特征的信息价值就变得非常重要。

过拟合与欠拟合

机器学习的根本问题是优化和泛化之间的对立。优化(optimization)是指调节模型以在训练数据上得到最佳性能(即机器学习中的学习),而泛化(generalization)是指训练好的模型在前所未见的数据上的性能好坏。机器学习的目的当然是得到良好的泛化,但无法控制泛化,只能基于训练数据调节模型。
训练开始时,优化和泛化是相关的:训练数据上的损失越小,测试数据上的损失也越小。这时的模型是欠拟合(underfit)的,即仍有改进的空间,网络还没有对训练数据中所有相关模式建模。但在训练数据上迭代一定次数之后,泛化不再提高,验证指标先是不变,然后开始变差,即模型开始过拟合。这时模型开始学习仅和训练数据有关的模式,但这种模式对新数据来说是错误的或无关紧要的。
为了防止模型从训练数据中学到错误或无关紧要的模式,最优解决方法是获取更多的训练数据。模型的训练数据越多,泛化能力自然也越好。如果无法获取更多数据,次优解决方法是调节模型允许存储的信息量,或对模型允许存储的信息加以约束。如果一个网络只能记住几个模式,那么优化过程会迫使模型集中学习最重要的模式,这样更可能得到良好的泛化。这种降低过拟合的方法叫作正则化(regularization)。
下面介绍几种最常见的正则化方法

减小网络大小

防止过拟合的最简单的方法就是减小模型大小,即减少模型中可学习参数的个数(这由层数和每层的单元个数决定)。在深度学习中,模型中可学习参数的个数通常被称为模型的容量(capacity)。直观上来看,参数更多的模型拥有更大的记忆容量(memorization capacity),因此能够在训练样本和目标之间轻松地学会完美的字典式映射,这种映射没有任何泛化能力。例如,拥有 500 000 个二进制参数的模型,能够轻松学会 MNIST 训练集中所有数字对应的类别——我们只需让 50 000 个数字每个都对应 10 个二进制参数。但这种模型对于新数字样本的分类毫无用处。始终牢记:深度学习模型通常都很擅长拟合训练数据,但真正的挑战在于泛化,而不是拟合

添加权重正则化

奥卡姆剃刀(Occam’s razor)原理:如果一件事情有两种解释,那么最可能正确的解释就是最简单的那个,即假设更少的那个。这个原理也适用于神经网络学到的模型:给定一些训练数据和一种网络架构,很多组权重值(即很多模型)都可以解释这些数据。简单模型比复杂模型更不容易过拟合
这里的简单模型(simple model)是指参数值分布的熵更小的模型(或参数更少的模型)。因此,一种常见的降低过拟合的方法就是强制让模型权重只能取较小的值,从而限制模型的复杂度,这使得权重值的分布更加规则(regular)。这种方法叫作权重正则化(weight regularization),其实现方法是向网络损失函数中添加与较大权重值相关的成本(cost)。这个成本有两种形式。

  • L1 正则化(L1 regularization):添加的成本与权重系数的绝对值[权重的 L1 范数(norm)]成正比。
  • L2 正则化(L2 regularization):添加的成本与权重系数的平方(权重的 L2 范数)成正比。神经网络的 L2 正则化也叫权重衰减(weight decay)。不要被不同的名称搞混,权重衰减与 L2 正则化在数学上是完全相同的。
# 向电影评论分类网络中添加 L2 权重正则化
from keras import regularizers
model = models.Sequential()
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
 activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
 activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

regularizers.l2(0.001)的意思是该层权重矩阵的每个系数都会使网络总损失增加 0.001 * weight_coefficient_value。注意,由于这个惩罚项只在训练时添加,所以这个网络的训练损失会比测试损失大很多。
还可以用 Keras 中以下这些权重正则化项来代替 L2 正则化

# Keras 中不同的权重正则化项
from keras import regularizers
regularizers.l1(0.001)  # L1 正则化
regularizers.l1_l2(l1=0.001, l2=0.001)  # 同时做 L1 和 L2 正则化

添加 dropout 正则化

dropout 是神经网络最有效也最常用的正则化方法之一。对某一层使用 dropout,就是在训练过程中随机将该层的一些输出特征舍弃(设置为 0)。假设在训练过程中,某一层对给定输入样本的返回值应该是向量 [0.2, 0.5, 1.3, 0.8, 1.1]。使用 dropout 后,这个向量会有几个随机的元素变成 0,比如 [0, 0.5, 1.3, 0, 1.1]。dropout 比率(dropout rate)是被设为 0 的特征所占的比例,通常在 0.2~0.5范围内。测试时没有单元被舍弃,而该层的输出值需要按 dropout 比率缩小,因为这时比训练时有更多的单元被激活,需要加以平衡
假设有一个包含某层输出的 Numpy 矩 阵 layer_output,其形状为 (batch_size, features)。训练时,我们随机将矩阵中一部分值设为 0。

# 训练时,舍弃 50%的输出单元
layer_output *= np.random.randint(0, high=2, size=layer_output.shape) 

测试时,我们将输出按 dropout 比率缩小。这里我们乘以 0.5(因为前面舍弃了一半的单元)

layer_output *= 0.5 

注意,为了实现这一过程,还可以让两个运算都在训练时进行,而测试时输出保持不变。这通常也是实践中的实现方式。

layer_output *= np.random.randint(0, high=2, size=layer_output.shape) 
layer_output /= 0.5 # 注意,是成比例放大而不是成比例缩小

在这里插入图片描述
添加 dropout 正则化其核心思想是在层的输出值中引入噪声,打破不显著的偶然模式(Hinton 称之为阴谋)。如果没有噪声的话,网络将会记住这些偶然模式。
在 Keras 中,可以通过 Dropout 层向网络中引入 dropout,dropout 将被应用于前面一层的输出。

# 向 IMDB 网络中添加两个 Dropout 层,来看一下它们降低过拟合的效果
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))

机器学习的通用工作流程

  1. 定义问题,收集数据集
  2. 选择衡量成功的指标:精度、准确率(precision)和召回率(recall)等
  3. 确定评估方法:留出验证集、K 折交叉验证、重复的 K 折验证
  4. 准备数据:向量化、标准化、处理缺失值已经特征工程
  5. 开发比基准更好的模型:是获得统计功效(statistical power),即开发一个小型模型,它能够打败纯随机的基准(dumb baseline)
  6. 扩大模型规模:开发过拟合的模型
  7. 模型正则化与调节超参数:尽可能地接近理想模型,既不过拟合也不欠拟合

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值