粗读《Python 深度学习》(3)
第四章 机器学习基础
4.1 机器学习的四个分支
4.1.1 监督学习
监督学习 是目前最常见的机器学习类型。输入一组样本(通常由人工标注),训练模型使其学会输入数据到已知目标[也叫标注(annotation)]的映射关系。该书前面的四个例子都属于监督学习。除此之外,常见的监督学习还包括:光学字符识别、语音识别、图像分类和语言翻译。
虽然监督学习主要包括分类和回归,但还有更多的奇特变体,主要包括如下几种:
1、序列生成(sequence generation)。给定一张图像,预测描述图像的文字。序列生成有时可以被重新表示为一系列分类问题,比如反复预测序列中的单词或标记。
2、语法树预测(syntax tree prediction)。给定一个句子,预测其分解生成的语法树。
3、目标检测(object detection)。给定一张图像,在图中特定目标的周围画一个边界框。这个问题也可以表示为分类问题(给定多个候选边界框,对每个框内的目标进行分类)或分类与回归联合问题(用向量回归来预测边界框的坐标)。
4、图像分割(image segmentation)。给定一张图像,在特定物体上画一个像素级的掩模(mask),像素级的分类问题。
4.1.2 无监督学习
无监督学习 是指在没有目标的情况下寻找输入数据的有趣变换,其目的在于数据可视化、数据压缩、数据去噪或更好地理解数据中的相关性。无监督学习是数据分析的必备技能,在解决监督学习问题之前,为了更好地了解数据集,它通常是一个必要步骤。降维(dimensionality reduction) 和 聚类(clustering) 都是众所周知的无监督学习方法。兴趣推送算法就是一种聚类。
4.1.3 自监督学习
自监督学习 是监督学习的一个特例。它没有人工标注的标签用来监督学习,但标签仍然存在。这些标签是从输入数据中生成的,通常是使用启发式算法生成的。例如,自编码器(autoencoder),其生成的目标就是未经修改的输入。同样,给定视频中过去的帧来预测下一帧,或者给定文本中前面的词来预测下一个词,都是自监督学习的例子(这两个例子也属于 时序监督学习(temporally supervised learning),即用未来的输入数据作为监督)
应当注意,监督学习、自监督学习和无监督学习之间的区别有时很模糊,这三个类别更像是没有明确界限的连续体。自监督学习可以被重新解释为监督学习或无监督学习,这取决于你关注的是学习机制还是应用场景。
4.1.4 强化学习
在强化学习 中, 智能体(Agent) 以“试错”的方式进行学习,通过奖惩机制,使智能体学会选择使其奖励最大化的行动。
4.2 评估机器学习模型
机器学习的目的是得到可以 泛化(generalize) 的模型,即在前所未见的数据上表现很好的模型,而过拟合则是核心难点。如何衡量泛化能力,即如何评估机器学习模型。
4.2.1 训练集、验证集和测试集
评估模型的重点是将数据划分为三个集合:训练集、验证集和测试集。在训练数据上训练模型,在验证数据上评估模型。最后将模型调整到最佳参数,就在测试数据上最后测试一次。
将数据划分为三个集合的原因在于:利用验证集评估模型并调节模型配置,比如选择层数或每层大小[这叫作模型的 超参数(hyperparameter),以便与模型参数(即权重)区分开],会造成模型在验证集上的过拟合,而模型本身的泛化程度并没有提高。调节模型配置的过程本质上是一种学习,会或多或少地造成验证集的 信息泄露,使得模型间接地获取了验证集中数据的特征。为此,需要设立测试集来最终评估模型的泛化程度。
该书介绍了三种经典的评估方法:
1、简单的留出验证
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) # 用测试集评估模型泛化程度
这是最简单的方法,但缺点也很明显。在数据样本较少时,验证集和测试集无法在统计学上代表数据。 在划分数据前进行不同的随机打乱,最终得到的模型性能差别很大。
2、K 折验证
上章已经介绍过,此处不再赘述。
3、带有打乱数据的重复 K 折验证
如果可用的数据相对较少,而你又需要尽可能精确地评估模型,那么可以选择 带有打乱数据的重复 K 折验证(iterated K-fold validation with shuffling)。具体做法是多次使用 K 折验证,在每次将数据划分为 K 个分区之前都先将数据打乱。最终分数是每次 K 折验证分数的平均值。
该方法的缺点在于训练和评估的次数较多,计算代价很大。
4.2.2 评估模型的注意事项
选择模型评估方法时,需要注意以下三点:
1、数据代表性(data representativeness)。三个数据集中数据的特征分布应该是均匀的、相似的。例如,训练数字识别的训练集需要包括 0 ~ 9。
2、时间箭头(the arrow of time)。如果想要根据过去预测未来,那么在划分数据前不应该随机打乱数据,因为这么做会造成时间泄露(temporal leak):使得模型在未来数据上得到有效训练。在这种情况下,应该始终确保测试集中所有数据的时间都晚于训练集数据。
3、数据冗余(redundancy in your data)。如果数据中的某些数据点出现了两次(这在现实中的数据里十分常见),那么打乱数据并划分成训练集和验证集会导致训练集和验证集之间的数据冗余。一定要确保训练集和验证集之间没有交集。
4.3 数据预处理、特征工程和特征学习
4.3.1 神经网络的数据预处理
数据预处理的目的是使原始数据更适于用神经网络处理,包括向量化、标准化、处理缺失值和特征提取。
1、向量化
神经网络的所有输入和目标都必须是浮点数张量(在特定情况下可以是整数张量)。无论处理什么数据(声音、图像还是文本),都必须首先将其转换为张量,这一步叫作数据向量化(data vectorization)。
2、值标准化
一般来说,将 取值相对较大的数据(比如多位整数,比网络权重的初始值大很多)或 异质数据(heterogeneous data,比如数据的一个特征在 0 ~ 1 范围内,另一个特征在 100~200 范围内)输入到神经网络中是不安全的。这么做可能导致较大的梯度更新,进而导致网络无法收敛。 为了让网络的学习变得更容易,输入数据应该具有以下特征:
取值较小: 大部分值都应该在 0~1 范围内。
同质性(homogenous): 所有特征的取值都应该在大致相同的范围内。
3、处理缺失值
获取的数据集中可能会有缺失值。一般来说,将缺失值设置为 0 是安全的,只要 0 不是一个有意义的值。网络能够从数据中学到 0 意味着缺失数据,并且会忽略这个值。
应注意的是,如果测试数据中可能有缺失值,而网络是在没有缺失值的数据上训练的,那么网络不可能学会忽略缺失值。在这种情况下,你应该人为生成一些有缺失项的训练样本:多次复制一些训练样本,然后删除测试数据中可能缺失的某些特征。
4.3.2 特征工程
特征工程(feature engineering) 是指将数据输入模型之前,利用关于数据和机器学习算法(这里指神经网络)的知识对数据进行硬编码的变换(不是模型学到的),以改善模型的效果。
特征工程的目的在于让输入数据中的特征可以更好地被网络学习,从而降低网络的复杂程度,提高训练效果。
4.4 过拟合和欠拟合
机器学习的根本问题是优化和泛化之间的对立。优化(optimization) 是指调节模型以在训练数据上得到最佳性能(即机器学习中的学习),而 泛化(generalization) 是指训练好的模型在前所未见的数据上的性能好坏。机器学习的目的当然是得到良好的泛化,但你无法控制泛化,只能基于训练数据调节模型。
训练开始时,优化和泛化是相关的:训练数据上的损失越小,测试数据上的损失也越小。这时的模型是 欠拟合(underfit) 的,即仍有改进的空间,网络还没有对训练数据中所有相关模式建模。但在训练数据上迭代一定次数之后,泛化不再提高,验证指标先是不变,然后开始变差,即模型开始 过拟合(overfit)。这时模型开始学习仅和训练数据有关的模式,但这种模式对新数据来说是错误的或无关紧要的。
对于这个问题,最优解决方法是获取更多的训练数据。模型的训练数据越多,泛化能力自然也越好。如果无法获取更多数据,次优解决方法是 调节模型允许存储的信息量,或对模型允许存储的信息加以约束。如果一个网络只能记住几个模式,那么优化过程会迫使模型集中学习最重要的模式,这样更可能得到良好的泛化。这种降低过拟合的方法叫作 正则化(regularization)。
4.4.1 减小网络大小
防止过拟合的最简单的方法就是 减小模型大小,即减少模型中可学习参数的个数(这由层数和每层的单元个数决定)。在深度学习中,模型中可学习参数的个数通常被称为 模型的容量(capacity)。直观上来看,参数更多的模型拥有更大的 记忆容量(memorization capacity),因此能够在训练样本和目标之间轻松地学会完美的字典式映射,这种映射没有任何泛化能力。深度学习模型通常都很擅长拟合训练数据,但真正的挑战在于泛化,而不是拟合。
相对应的,网络也会因为容量有限而无法学会特征映射,使模型欠拟合。所以解决问题的关键在于找到一个折中方案。
4.4.2 添加权重正则化
将奥卡姆剃刀原理应用于神经网络模型中:给定一些训练数据和一种网络架构,会有很多组权重值(即很多模型)都可以解释这些数据,而简单模型比复杂模型更不容易过拟合。因此,一种常见的降低过拟合的方法就是强制让模型权重只能取较小的值,从而限制模型的复杂度,这使得权重值的分布更加 规则(regular)。这种方法叫作 权重正则化(weight regularization),其实现方法是向网络损失函数中添加与较大权重值相关的 成本(cost)。这个成本有两种形式:
1、L1 正则化(L1 regularization): 添加的成本与权重系数的绝对值(权重的 L1 范数(norm))成正比。
2、L2 正则化(L2 regularization): 添加的成本与权重系数的平方(权重的 L2 范数)成正比。神经网络的 L2 正则化也叫 权重衰减(weight decay)。
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'))
l2(0.001)
的意思是该层权重矩阵的每个系数都会使网络总损失增加 0.001 * weight_ coefficient_value
。应注意,由于这个惩罚项只在训练时添加,所以这个网络的训练损失会比测试损失大很多。
from keras import regularizers
regularizers.l1(0.001) # L1 正则化
regularizers.l1_l2(l1=0.001, l2=0.001) # 同时做 L1 和 L2 正则化
4.4.3 添加 dropout 正则化
Dropout 是神经网络最有效也最常用的正则化方法之一。具体操作是,在训练过程中随机将使用 dropout 正则化的层中一定比例的输出特征舍弃(设置为 0)。由于测试时不会舍弃特征单元,为了平衡训练和测试的输出,可以按 dropout 比率 放大训练时的输出,或缩小测试时的输出。
dropout 正则化的核心思想是在层的输出值中引入噪声,打破不显著的偶然模式(Hinton 称之为阴谋)。如果没有噪声的话,网络将会记住这些偶然模式。
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'))
4.5 机器学习的通用工作流程
4.5.1 定义问题,收集数据集
首先,必须定义所面对的问题:
1、输入数据是什么?要预测什么(模型的输出形式)?
2、面对的是什么类型的问题?分类还是回归?
只有明确了输入、输出以及所使用的数据,才能进入下一阶段。注意你在这一阶段所做的假设。
1、假设输出是可以根据输入进行预测的。
2、假设可用数据包含足够多的信息,足以学习输入和输出之间的关系。
在开发出工作模型之前,这些只是假设,等待验证真假。并非所有问题都可以解决。这是因为机器学习只能记忆训练数据中存在的模式,只能识别出曾经见过的东西。通过在过去的数据上训练机器,学习特征,来预测未来。这里存在一个假设,就是未来的规律与过去相同,但事实往往并非如此。
4.5.2 选择衡量成功的指标
要控制一件事物,就需要能够观察它。要取得成功,就必须给出成功的定义:精度?准确率(precision) 和 召回率(recall)?客户保留率?衡量成功的指标将指引你选择损失函数,即模型要优化什么。它应该直接与你的目标保持一致。
4.5.3 确定评估方法
一旦明确了目标,你必须确定如何衡量当前的进展。前面介绍了三种常见的评估方法:
1、留出验证集;
2、K 折交叉验证;
3、重复的 K 折验证。
4.5.4 准备数据
1、将数据格式化为张量;
2、数据标准化;
3、特征工程。
4.5.5 开发比基准更好的模型
这一阶段的目标是获得 统计功效(statistical power),即开发一个小型模型,它能够打败 纯随机的基准(dumb baseline)。
如果你尝试了多种合理架构之后仍然无法打败随机基准,那么原因可能是问题的答案并不在输入数据中。即第一步中的两个假设并不满足。
如果优于随机基准,那么将接着考虑:最后一层的激活、损失函数和优化器配置。
4.5.6 扩大模型规模:开发过拟合的模型
理想的模型是刚好在欠拟合和过拟合的界线上的,要想获取理想的模型需要先开发一个过拟合的模型:
1、增加层数;
2、增加每层的单元数(参数);
3、训练更多的轮次。
4.5.7 模型正则化与调节超参数
获取过拟合的模型后,需要通过正则化和超参数调节来优化模型,提高模型泛化程度:
1、添加 dropout;
2、L1 / L2 正则化;
3、调整超参量(每层的参数或优化器的学习率);
4、改变模型结构;
5、特征工程。
应注意:每次使用验证过程的反馈来调节模型,都会将有关验证过程的信息泄露到模型中。如果系统性地迭代许多次,最终会导致模型对验证过程过拟合。这会降低验证过程的可靠性。
小结
解决深度学习问题的基本流程:
1、定义问题,收集数据集。看待问题的角度不同,可以提供不同的解决思路。输入数据与输出数据间的相关性需要一定考量。
2、选择衡量成功的指标。
3、确定评估方法。评估方法决定了模型调参的准确性。
4、准备数据。数据格式转换、预处理和特征工程。
5、开发比基准更好的模型。
6、开发过拟合的模型。
7、模型正则化与调节超参数。