Improving Linear Models Using Explicit Kernel Methods(使用显式内核方法改进线性模型)
在本教程中,我们将演示如何将(明确的)内核方法与线性模型结合起来,在隐性增加训练和推理时间的情况下,大幅提高后者的预测质量。与双核方法不同,显式(原始)内核方法在训练/推理时间和内存需求方面均与训练数据集的大小相适应。
目标读者: 尽管我们提供了与显式内核方法相关的概念的高级概述,但本教程主要针对已经至少具有核方法和支持向量机(SVM)基本知识的读者。 如果您不熟悉内核方法,请参阅以下任一源文件以获取简介:
- 如果你有一个强大的数学背景:机器学习中的核心方法
目前,TensorFlow仅支持密集特征的显式内核映射; TensorFlow将在以后的版本中提供对稀疏特征的支持。
本教程对我们的ML模型使用tf.contrib.learn(TensorFlow的高级机器学习API)估计器。如果你不熟悉这个API,tf.estimator Quickstart是一个很好的开始。我们将使用MNIST数据集。本教程由以下步骤组成:
- 加载并准备MNIST数据以进行分类。
- 构建一个简单的线性模型,对其进行训练,并在eval数据上进行评估。
- 用线性模型替换线性模型,重新训练并重新评估。
加载并准备MNIST数据以进行分类
运行以下实用程序命令来加载MNIST数据集:
data = tf.contrib.learn.datasets.mnist.load_mnist()
上述方法将加载整个MNIST数据集(包含70K个样本),并分别将其分成55K,5K和10K样本的训练,验证和测试数据。每个拆分包含一个用于图像的numpy数组(其形状为sample_size,784),另一个用于标签(形状为sample_size,1)。在本教程中,我们只使用训练和验证分割来分别训练和评估我们的模型。
为了将数据提供给tf.contrib.learn Estimator,将其转换为Tensors是有帮助的。为此,我们将使用一个input function
将操作添加到TensorFlow图表的程序,该程序在执行时会创建小批量的Tensors以供下层使用。有关输入函数的更多背景信息,请使用tf.contrib.learn选中构建输入函数。在这个例子中,我们将使用tf.train.shuffle_batch
除了将numpy数组转换为Tensors之外,操作还允许我们指定batch_size以及每次执行input_fn Ops时是否随机化输入(随机化通常会加快训练期间的收敛速度)。加载和准备数据的完整代码显示在下面的代码片段中。在本例中,我们使用256个小批量的训练样本和整个样本(5K个条目)进行评估。随意尝试不同的批量大小。
import numpy as np
import tensorflow as tf
def get_input_fn(dataset_split, batch_size, capacity=10000, min_after_dequeue=3000):
def _input_fn():
images_batch, labels_batch = tf.train.shuffle_batch(
tensors=[dataset_split.images, dataset_split.labels.astype(np.int32)],
batch_size=batch_size,
capacity=capacity,
min_after_dequeue=min_after_dequeue,
enqueue_many=True,
num_threads=4)
features_map = {'images': images_batch}
return features_map, labels_batch
return _input_fn
data = tf.contrib.learn.datasets.mnist.load_mnist()
train_input_fn = get_input_fn(data.train, batch_size=256)
eval_input_fn = get_input_fn(data.validation, batch_size=5000)
培训一个简单的线性模型
现在我们可以在MNIST数据集上训练一个线性模型。我们将使用tf.contrib.learn.LinearClassifier
估计器来利用10个类代表10位数。输入特征形成784维密集矢量,其可以如下指定:
image_column = tf.contrib.layers.real_valued_column('images', dimension=784)
用于构建,训练和评估LinearClassifier估算器的完整代码如下所示:
import time
# Specify the feature(s) to be used by the estimator.
image_column = tf.contrib.layers.real_valued_column('images', dimension=784)
estimator = tf.contrib.learn.LinearClassifier(feature_columns=[image_column], n_classes=10)
# Train.
start = time.time()
estimator.fit(input_fn=train_input_fn, steps=2000)
end = time.time()
print('Elapsed time: {} seconds'.format(end - start))
# Evaluate and report metrics.
eval_metrics = estimator.evaluate(input_fn=eval_input_fn, steps=1)
print(eval_metrics)
下表总结了评估数据的结果。
metric | value |
---|---|
loss | 0.25 to 0.30 |
accuracy | 92.5% |
training time | ~25 seconds on my machine |
注意:度量标准将根据各种因素而有所不同。
除了试验(训练)批量大小和训练步骤的数量之外,还有其他一些参数也可以进行调整。例如,您可以通过从可用优化器集合中明确选择另一个优化器来更改用于最小化丢失的优化方法。作为一个例子,下面的代码构建了一个LinearClassifier估计器,该估计器使用遵循正则化领导者(FTRL)优化策略,具有特定的学习速率和L2正则化。
optimizer = tf.train.FtrlOptimizer(learning_rate=5.0, l2_regularization_strength=1.0)
estimator = tf.contrib.learn.LinearClassifier(
feature_columns=[image_column], n_classes=10, optimizer=optimizer)
无论参数的值如何,线性模型在该数据集上可达到的最大准确度都在93%左右。
结合线性模型使用显式核映射。
MNIST上的线性模型相对较高的误差(〜7%)表明输入数据不是线性可分的。我们将使用显式的内核映射来减少分类错误。
直觉:高层次的想法是使用非线性映射将输入空间转换为另一个特征空间(可能更高的维度),其中(变换的)特征是(几乎)线性可分的,然后将线性模型应用于映射的功能。如下图所示:
技术细节
在这个例子中,我们将使用Rahimi和Recht 在“大规模内核机器随机特征”论文中介绍的随机傅立叶特征来映射输入数据。随机傅立叶特征通过以下映射将(\mathbf{x} \in \mathbb{R}^d) to (\mathbf{x'} \in \mathbb{R}^D) 将遵从以下映射:
$$ RFFM(\cdot): \mathbb{R}^d \to \mathbb{R}^D, \quad RFFM(\mathbf{x}) = \cos(\mathbf{\Omega} \cdot \mathbf{x}+ \mathbf{b}) $$
其中 (\mathbf{\Omega} \in \mathbb{R}^{D \times d}), (\mathbf{x} \in \mathbb{R}^d,) (\mathbf{b} \in \mathbb{R}^D) 和余弦是明智的元素应用。
在这个例子中,(\ mathbf {\ Omega})和(\ mathbf {b})的条目从分布中被抽样,使得映射满足以下属性:
$$ RFFM(\mathbf{x})^T \cdot RFFM(\mathbf{y}) \approx e^{-\frac{|\mathbf{x} - \mathbf{y}|^2}{2 \sigma^2}} $$
上面表达式的右侧量被称为RBF(或高斯)核函数。这个函数是机器学习中使用最广泛的核函数之一,并且隐含地测量了与原始空间不同的,更高维空间的相似性。有关更多详细信息,请参阅径向基函数内核。
内核分类器
tf.contrib.kernel_methods.KernelLinearClassifier
是一个预先封装的tf.contrib.learn
估计器,它将显式内核映射的功能与线性模型相结合。它的构造函数与LinearClassifier估计器的构造函数几乎相同,并带有附加选项,用于指定要应用于分类器使用的每个要素的显式内核映射列表。以下代码片段演示了如何用KernelLinearClassifier替换LinearClassifier。
# Specify the feature(s) to be used by the estimator. This is identical to the
# code used for the LinearClassifier.
image_column = tf.contrib.layers.real_valued_column('images', dimension=784)
optimizer = tf.train.FtrlOptimizer(
learning_rate=50.0, l2_regularization_strength=0.001)
kernel_mapper = tf.contrib.kernel_methods.RandomFourierFeatureMapper(
input_dim=784, output_dim=2000, stddev=5.0, name='rffm')
kernel_mappers = {image_column: [kernel_mapper]}
estimator = tf.contrib.kernel_methods.KernelLinearClassifier(
n_classes=10, optimizer=optimizer, kernel_mappers=kernel_mappers)
# Train.
start = time.time()
estimator.fit(input_fn=train_input_fn, steps=2000)
end = time.time()
print('Elapsed time: {} seconds'.format(end - start))
# Evaluate and report metrics.
eval_metrics = estimator.evaluate(input_fn=eval_input_fn, steps=1)
print(eval_metrics)
传递给KernelLinearClassifier
它的唯一附加参数是一个从feature_columns到要应用于相应特征列的内核映射列表的字典。以下几行指示分类器首先使用随机傅立叶特征将初始784维图像映射到2000维向量,然后在变换后的向量上学习线性模型:
kernel_mapper = tf.contrib.kernel_methods.RandomFourierFeatureMapper(
input_dim=784, output_dim=2000, stddev=5.0, name='rffm')
kernel_mappers = {image_column: [kernel_mapper]}
estimator = tf.contrib.kernel_methods.KernelLinearClassifier(
n_classes=10, optimizer=optimizer, kernel_mappers=kernel_mappers)
注意stddev
参数。这是近似RBF核的标准差((\ sigma)),并控制分类中使用的相似性度量。stddev
通常通过超参数调整来确定。
下表总结了运行上述代码的结果。我们可以通过增加映射的输出尺寸和调整标准偏差来进一步提高精度。
metric | value |
---|---|
loss | 0.10 |
accuracy | 97% |
training time | ~35 seconds on my machine |
stddev
分类性能对stddev的值非常敏感。下表显示了不同stddev值的eval数据上分类器的准确性。最佳值是stddev = 5.0。请注意stddev值太小或太高会显着降低分类的准确性。
stddev | eval accuracy |
---|---|
1.0 | 0.1362 |
2.0 | 0.4764 |
4.0 | 0.9654 |
5.0 | 0.9766 |
8.0 | 0.9714 |
16.0 | 0.8878 |
输出维度
直观地说,映射的输出维数越大,两个映射向量的内积越接近内核,这通常转化为更好的分类准确度。另一种考虑这种情况的方式是输出维数等于线性模型的权重数量; 该维度越大,模型的“自由度”就越大。但是,经过一定的阈值后,更高的输出尺寸会提高准确度,而使训练需要更多时间。这在以下两个图中示出,其分别描绘了作为输出尺寸和训练时间的函数的评估精度。
总结
显式内核映射将非线性模型的预测能力与线性模型的可伸缩性相结合。与传统的双内核方法不同,显式内核方法可以扩展到数百万或数亿个样本。在使用显式内核映射时,请考虑以下提示:
- 随机傅立叶特征对于具有密集特征的数据集可能特别有效。
- 内核映射的参数通常取决于数据。模型性能对这些参数非常敏感。使用超参数调整来查找最佳值。
- 如果您有多个数字特征,则将它们连接成单个多维特征并将内核映射应用于连接矢量。
本文档系腾讯云开发者社区成员共同维护,如有问题请联系 cloudcommunity@tencent.com