优秀的编程知识分享平台

网站首页 > 技术文章 正文

Kernel分类器(hear分类器)

nanyue 2024-10-14 11:32:43 技术文章 11 ℃

本教程的目的是使机器学习数据集可线性分离。本教程分为两部分:

  • 特征转换
  • 使用Tensorflow训练核分类器

在第一部分中,您将了解核分类器背后的想法,而在第二部分中,您将了解如何使用Tensorflow训练核分类器。您将使用adult 数据集。该数据集的目标是将收入分类为低于和高于50k,了解每家住户的行为。

你为什么需要Kernel?

每个机器学习分类器的目的是正确地预测类。为此,数据集应该是可分的。看看下面的图形; 很容易看出,黑线以上的所有点都属于第一类,其他点属于第二类。然而,人们很少有一个简单的数据集。大多数情况下,数据无法分离。它给像逻辑回归这样的简单分类器带来了困难。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x_lin = np.array([1,2,3,4,5,6,7,8,9,10])
y_lin = np.array([2,2,3,2,2,9,6,8,8,9])
label_lin = np.array([0,0,0,0,0,1,1,1,1,1])
fig = plt.figure()
ax=fig.add_subplot(111)
plt.scatter(x_lin, y_lin, c=label_lin, s=60)
plt.plot([-2.5, 10], [12.5, -2.5], 'k-', lw=2)
ax.set_xlim([-5,15])
ax.set_ylim([-5,15])
plt.show()
<Figure size 640x480 with 1 Axes>

在下图中,我们绘制了一个不可线性分离的数据集。如果我们绘制一条直线,大多数点将不会被归类为正确的类。

解决此问题的一种方法是获取数据集并将数据转换为另一个特征图。

x = np.array([1,1,2,3,3,6,6,6,9,9,10,11,12,13,16,18])
y = np.array([18,13,9,6,15,11,6,3,5,2,10,5,6,1,3,1])
label = np.array([1,1,1,1,0,0,0,1,0,1,0,0,0,1,0,1])
fig = plt.figure()
plt.scatter(x, y, c=label, s=60)
plt.show()

上图中的数据是二维的,不可分离。您可以尝试以三维方式转换这些数据,这意味着您可以创建一个包含3个轴的图形。

在我们的示例中,我们将应用多项式映射将数据转换为3D维度。转换数据的公式如下。

您可以在Python中定义一个函数来创建新的特征图

您可以使用numpy来编写上面的公式:

### illustration purpose
def mapping(x, y):
 x = np.c_[(x, y)]
 if len(x) > 2:
 x_1 = x[:,0]**2
 x_2 = np.sqrt(2)*x[:,0]*x[:,1]
 x_3 = x[:,1]**2 
 else: 
 x_1 = x[0]**2
 x_2 = np.sqrt(2)*x[0]*x[1]
 x_3 = x[1]**2
 
 trans_x = np.array([x_1, x_2, x_3])
 return trans_x

新的映射应该是3维,16点

x_1 = mapping(x, y)
x_1.shape
(3, 16)

我们分别用3个轴x、y和z绘制一个新图。

# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x_1[0], x_1[1], x_1[2], c=label, s=60)
ax.view_init(30, 185)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()

我们看到了改进,但如果我们改变绘图的方向,很明显数据集现在是可分离的

# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x_1[0], x_1[1], x_1[1], c=label, s=60)
ax.view_init(0, -180)
ax.set_ylim([150,-50])
ax.set_zlim([-10000,10000])
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()

要处理大型数据集,您可能必须创建2个以上的维度,使用上述方法将面临一个大问题。实际上,您需要转换所有数据点,这显然是不可行的。这会花费你很长时间,而且你的电脑可能会耗尽内存。

克服此问题的最常见方法是使用kernel 。

什么是kernel ?

这个想法是使用一个高维特征空间的数据几乎线性可分的,如上图所示。

有很多高维空间可以让数据点分离。比如,我们已经证明了多项式映射是个不错的开始。

有大量的数据,这些变换是无效的。相反,您可以使用核函数修改数据,而不需要更改新的特征计划。

kernel 的神奇之处在于找到一种避免高维计算所隐含的所有麻烦的函数。核是一个标量的结果,或者换一种说法我们回到一维空间

找到此函数后,您可以将其插入标准线性分类器。

让我们看一个例子来理解kernel 的概念。你有两个向量,x1和x2 。目标是通过使用多项式映射来创建更高维度。输出等于新特征图的点积。根据上述方法,您需要:

  • 将x1和x2变成一个新维度
  • 计算点积:所有内核都通用
  • 将x1和x2变成一个新维度

您可以使用上面创建的函数来计算更高的维度。

## Kernel
x1 = np.array([3,6])
x2 = np.array([10,10])
x_1 = mapping(x1, x2)
print(x_1)
[[ 9. 100. ]
 [ 25.45584412 141.42135624]
 [ 36. 100. ]]

计算点积

你可以用numpy的对象dot来计算在x_1中第一个和第二个向量之间的点积。

print(np.dot(x_1[:,0], x_1[:,1]))

8100.0

输出为8100.您会看到问题,您需要在内存中存储一??个新的特征图来计算点积。如果您有一个包含数百万条记录的数据集,那么它在计算上无效。

相反,你可以用多项式核来计算点积而不用改变矢量。这个函数计算x1和x2的点积,就好像这两个向量被转换成更高的维度。换句话说,核函数从另一个特征空间计算点积的结果。

您可以在Python中编写多项式核函数,如下所示。

def polynomial_kernel(x, y, p=2):
 return (np.dot(x, y)) ** p

它是两个向量的点积的幂。下面,你返回多项式核的second degree。输出等于另一种方法。这是内核的魔力。

polynomial_kernel(x1, x2, p=2)
8100

核类型

有许多不同的核可用。最简单的是线性核。此函数非常适用于文本分类。另一个核是:

  • 多项式内核
  • 高斯核

在使用TensorFlow的示例中,我们将使用随机傅立叶。TensorFlow具有用于计算新特征空间的估计器。该函数是高斯核函数的近似值。

该函数计算更高维空间中的数据点之间的相似性。

使用TensorFlow训练核分类器

该算法的目的是对家庭收入大于或小于50k进行分类。

您将评估逻辑回归以获得基准模型。之后,您将训练一个核分类器,看看是否可以获得更好的结果。

您可以使用adult数据集中的以下变量:

  • age
  • workclass
  • fnlwgt
  • education
  • education_num
  • marital
  • occupation
  • relationship
  • race
  • sex
  • capital_gain
  • capital_loss
  • hours_week
  • native_country
  • label

在训练和评估模型之前,您将按照以下步骤操作:

第1步:导入库

你需要导入tensorflow ,pandas和numpy

#import numpy as np
from sklearn.model_selection import train_test_split
import tensorflow as tf
import pandas as pd
import numpy as np

第2步:导入数据

您从以下网站下载数据(https://archive.ics.uci.edu/ml/machine-learning-databases/adult/),然后将其作为panda dataframe导入。

## Define path data
COLUMNS = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital', 'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss', 'hours_week', 'native_country', 'label']
PATH = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data"
PATH_test = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test"
## Import 
df_train = pd.read_csv(PATH, skipinitialspace=True, names = COLUMNS, index_col=False)
df_test = pd.read_csv(PATH_test,skiprows = 1, skipinitialspace=True, names = COLUMNS, index_col=False)

既然定义了训练和测试集,您可以将标签列从字符串更改为整数。tensorflow不接受标签的字符串值。

label = {'<=50K': 0,'>50K': 1}
df_train.label = [label[item] for item in df_train.label]
label_t = {'<=50K.': 0,'>50K.': 1}
df_test.label = [label_t[item] for item in df_test.label]
df_train.shape
(32561, 15)

第3步:准备数据

数据集包含连续和分类特征。一个好的做法是标准化连续变量的值。您可以使用sci-kit learn中的StandardScaler 函数。您还可以创建用户定义的函数,以便更轻松地转换训练和测试集。请注意,您将连续变量和分类变量连接到公共数据集,并且数组应该是以下类型:float32

COLUMNS_INT = ['age','fnlwgt','education_num','capital_gain', 'capital_loss', 'hours_week']
CATE_FEATURES = ['workclass', 'education', 'marital', 'occupation', 'relationship', 'race', 'sex', 'native_country']
from sklearn.preprocessing import StandardScaler
from sklearn import preprocessing
def prep_data_str(df):
 
 scaler = StandardScaler()
 le = preprocessing.LabelEncoder() 
 df_toscale = df[COLUMNS_INT]
 df_scaled = scaler.fit_transform(df_toscale.astype(np.float64))
 X_1 = df[CATE_FEATURES].apply(le.fit_transform)
 y = df['label'].astype(np.int32)
 X_conc = np.c_[df_scaled, X_1].astype(np.float32)
 return X_conc, y

transformer函数准备好了,您可以转换数据集并创建input_fn函数。

X_train, y_train = prep_data_str(df_train)
X_test, y_test = prep_data_str(df_test)
print(X_train.shape)
(32561, 14)

在下一步中,您将训练逻辑回归。它将为您提供基准准确度。目标是使用不同的算法(即核分类器)击败基线。

第4步 :构建逻辑模型:基线模型

使用对象“real_valued_column”构建特征列。它将确保所有变量都是dense 数字数据。

feat_column = tf.contrib.layers.real_valued_column('features', dimension=14)

使用TensorFlow定义估算器Estimator ,指示特征列以及保存图的位置。

estimator = tf.estimator.LinearClassifier(feature_columns=[feat_column],
 n_classes=2,
 model_dir = "kernel_log"
 )

您将使用200的小批量训练logisitc回归。

# Train the model
train_input_fn = tf.estimator.inputs.numpy_input_fn(
 x={"features": X_train},
 y=y_train,
 batch_size=200,
 num_epochs=None,
 shuffle=True)

第5步:训练机器学习模型

estimator.train(input_fn=train_input_fn, steps=1000)

第6步:评估机器学习模型

您可以定义numpy估算器来评估模型。您可以使用整个数据集进行评估

# Evaluation
test_input_fn = tf.estimator.inputs.numpy_input_fn(
 x={"features": X_test},
 y=y_test,
 batch_size=16281,
 num_epochs=1,
 shuffle=False)
estimator.evaluate(input_fn=test_input_fn, steps=1)

您的准确率为82%。在下一节中,您将尝试使用核分类器来击败逻辑分类器

第7步:构造核分类器

核估算器与传统的线性分类器没有太大区别,至少在构造方面如此。背后的想法是使用显式核与线性分类器的强大功能。

您需要在TensorFlow中使用两个预定义的估算器来训练核分类器:

  • RandomFourierFeatureMapper
  • KernelLinearClassifier

您在第一部分中了解到需要使用内核函数将低维转换为高维。更确切地说,您将使用随机傅立叶,它是高斯函数的近似值。幸运的是,Tensorflow在其库中具有以下函数:RandomFourierFeatureMapper 。可以使用估计器KernelLinearClassifier 训练模型。

要构建机器学习模型,您将按照以下步骤操作:

  • A.设置高维核函数
  • B.设置L2超参数
  • C.建立模型
  • D.训练模型
  • E.评估模型

步骤A:设置高维核函数

当前数据集包含14个要转换为5维向量的新高维的特征。您可以使用随机傅里叶特征来实现转换。如果您还记得Gaussian Kernel公式,您会注意到要定义的标准差$ \ sigma $参数。该参数控制分类期间采用的相似性度量。

你可以调整RandomFourierFeatureMapper 的所有参数

  • input_dim = 14
  • output_dim= 5000
  • stddev=4
  • Prep Kernel
  • kernel_mapper = tf.contrib.kernel_methods.RandomFourierFeatureMapper(input_dim = 14,output_dim = 5000,stddev = 4,name ='rffm')

您需要使用之前创建的特征列构建内核映射器: feat_column

### Map Kernel
kernel_mappers = {feat_column: [kernel_mapper]}

步骤B:设置L2超参数

为了防止过度拟合,您可以使用L2正则化器来惩罚损失函数。您将L2超参数设置为0.1,将学习速率设置为5

optimizer = tf.train.FtrlOptimizer(
learning_rate=5, l2_regularization_strength=0.1)

步骤C:构建模型

下一步类似于线性分类。您使用内置估算器KernelLinearClassifier 。请注意,您添加先前定义的核映射器并更改模型目录。

### Prep estimator
estimator_kernel = tf.contrib.kernel_methods.KernelLinearClassifier(
n_classes=2,
optimizer=optimizer,
kernel_mappers=kernel_mappers, 
model_dir="kernel_train")

步骤D:训练模型

现在构建了内核分类器,您就可以开始训练了

### estimate

estimator_kernel.fit(input_fn=train_input_fn, steps=2000)

步骤E:评估模型

最后,您评估模型的性能。应该能够击败逻辑回归。

# Evaluate and report metrics.
eval_metrics = estimator_kernel.evaluate(input_fn=test_input_fn, steps=1)

最终准确度为84%,与逻辑回归相比提高了2%。精度提高和计算成本之间存在权衡。您需要考虑2%的改进是否值得不同分类器消耗的时间,以及它是否会对您的业务产生重大影响。

最后

核是将非线性数据转换为(几乎)线性的一个很好的工具。这种方法的缺点是计算耗时且成本高。

Tags:

最近发表
标签列表