机器学习基础
现在进入本书的第四章。本章内容包括:
- 超越分类和回归的其他机器学习方式
- 机器学习模型的正规评估程序
- 深度学习数据的预处理
- 特征工程
- 过度拟合的处理
- 处理机器学习问题的通用流程
机器学习的四个分支
- 监督学习。当前深度学习的应用几乎全是这一类型。
- 非监督学习。其中,降维和聚类是很有名的。
- 自监督学习。标签是启发算法根据输入的数据自动生成,不是人们提供。
- 强化学习。例如围棋程序 AlpahGo。
评估机器学习模型
把数据集分成训练、测试、验证三部分,是一个办法。如果数据很少,可以采用其他三个办法:hold-out 验证、K 折验证、洗牌式迭代 K 折验证。
Hold-out 验证
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 个部分。每一部分的数据 i,都用于其他 K - 1 部分训练结果的评估。模型训练最后得分,是你得到的 K 个成绩的平均分。如果你的模型在分离的训练-测试条件下表现差异很大,这个验证办法是很有用的。
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)
model = get_model()
model.train(data)
test_score = model.evaluate(test_data)
洗牌式的 K 折迭代验证
如果你的数据很少,又需要准确评估你的模型,可以用这个办法验证。我发现在 Kaggle 竞赛中,这个办法极为有用。具体做法是,多次运用 K 折验证,每次 K 折数据分割前对数据洗牌乱序。注意,模型训练评估的总次数是 P × K 。P 是迭代的次数,时间开销很大。
在选用验证办法时,要牢记以下几点:
- 在把数据分割为训练和测试两部分之前,通常应当把数据随机洗牌乱序。
- 有时间先后顺序的数据不能洗牌。应该确保训练部分的数据时间在前,预测部分的数据时间在后。
- 在测试时用到训练部分的数据,是最糟糕的事情。你要确保训练和测试数据互不交叉。
数据预处理、特征工程和特征学习
在深入模型开发之前,除了模型评估之外,还有个问题必须处理:如何预先准备输入数据和目标标签,以供神经网络使用?
数据预处理和特征工程的技术,许多有着特定领域的专业要求(例如特定的文本和图像)。首先,我们看一下对各种专业领域数据的共同基本的要求。
神经网络的数据预处理
包括数据的向量化、标准化、空白处理、特征提取。
数据的向量化
需要用到的数据,声音、图像、文本,任何一种都必须转化成张量。这种转化叫做向量化。
数值的标准化
为了便于你的网络进行学习,你的数据应当符合以下要求:
- 数值要小。一般地,大部分数值的范围应当在 0 至 1 之间。
- 值域同一。全部特征的取值应当大致在相同范围。
另外,以下是更为严格的标准化实践。它是常见常用的,但也不是非用不可。例如,数字识别的分类问题就不用它。
- 标准化每个特征独立地具有平均值 0。
- 标准化每个特征独立地具有1的标准偏差。
用 Numpy 数组很容易实现它:
x -= x.mean(axis=0)
x /= x.std(axis=0)
处理空白值
一般地,把缺失数据作为 0 输入神经网络是安全的,但前提条件是 0 不代表有意义的值。此时,网络将其忽略。
注意,如果你要测试在数据缺失条件下模型的表现,而训练数据并没有缺失数据,那么,网络并未学会忽略缺失数据。这时,你应当人为制造缺失数据的样本。
特征工程
特征工程是这样的过程:你拥有关于当前数据和机器学习算法(神经网络)的知识,你写代码编出非学习算法,转变数据后,将数据输入模型。在许多情况下,期望机器学习模型能够学习任意数据,是不合理的。输入给模型的数据,应该让模型工作更容易。
看个直观的例子。假定你要开发一个模型,它输入钟表表盘的图像,输出时间报时。
如果你用原始图像的像素作输入数据,你会遇到机器学习的麻烦问题。你要用大量的计算资源训练卷积神经网络。
如果你从更高层面理解问题,比如,你知道人们如何从表盘读知时间,那么,你的机器学习算法可以利用更好的输入特征:例如,写出五行的 Python 脚本,跟踪表针黑色像素的轨迹,输出每一时刻表针的坐标(x, y)。于是,一个简单的机器学习算法可以学会把表针坐标与恰当的时间联系起来。
你甚至可以更进一步:把坐标(x, y)改成相对于图像中心的极坐标。你的输入变成每个表针的角度 θ 。于是,你的特征使得问题如此简单,不再需要机器学习;简单的舍入运算和查对字典,足以完成报时问题。
特征工程的最大优点,是用更简单的方式表示问题,使其解决更为容易。这往往需要深刻理解要解决的问题。
在深度学习出现之前,特征工程通常是机器学习的关键,因为经典的浅层算法没有足够的假设空间自学得到有用的特征。你向算法提供数据的方式,是算法成功的必要条件。例如,在卷积神经网络成功地解决 MNIST 数字识别问题之前,典型的识别算法是对特征硬编码,如数字图像的循环次数,图像中数字的高度,柱状色素的值,等等。
很幸运,现代深度学习消除了对特征工程的大部分需求,因为,神经网络能自动地从原始数据中提取有用的特征。但是,使用深度神经网络时你仍须关注特征工程问题。有两个理由:
- 好的特征让你能用很少的资源很好地解决问题。
- 好的特征让你用很少的数据解决问题。