一文开启监督学习之旅
Posted 盼小辉丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文开启监督学习之旅相关的知识,希望对你有一定的参考价值。
1. 监督学习简介
近年来,机器学习已经成为最热门的研究领域之一,机器学习通常可以分为监督学习和无监督学习。本文主要介绍监督学习,可以在博文《无监督学习》中详细了解主流无监督学习算法。
在给出正式的定义之前,我们考虑以下示例:假设存在一组人物图片,每张图片都有一个关于图中人物性别的标签,我们就可以将这些数据输入监督算法中,并使用输入变量(图片像素)来计算目标变量(图片中人物性别),更正式地说:
监督学习
是指基于标记的训练数据构建机器学习模型的过程。在监督学习中,每个数据样本都是由输入变量和所需目标变量组成的元组。例如,机器学习中常用的 Iris
数据集,该数据集包含描述鸢尾花卉的特征,其包含以下特征:
- Sepal Length (花萼长度)
- Sepal Width (花萼宽度)
- Petal Length (花瓣长度)
- Petal Width (花瓣宽度)
监督学习的目标是预测鸢尾花卉属于 (Setosa
, Versicolour
, Virginica
) 三个种类中的哪一类。
本节我们将介绍主流监督学习算法——对数据进行分类
和回归
。当然,监督学习还包含更多应用,例如目标检测、图像分割等,但是,这些应用都是分类和回归算法的高级变体应用,不在本文的介绍范围内。
2. 分类算法
2.1 分类算法基本概念
分类过程是一种用于将数据归为固定数量的类别的技术,在机器学习中,分类用于识别新数据点所属的类别。基于包含数据样本和相应标签的训练数据集构建分类模型。例如,假设我们要确定给定图像中包含的动物是猫还是狗,我们将构建一个训练数据集,其中包含两个类别:猫和狗。然后我们将根据可用的训练样本训练模型(训练阶段),然后将训练后的模型用于推测模型未见过的图片属于猫或狗(测试或推理阶段)。
一个性能优秀的分类系统可以很容易地检索数据,分类广泛用于人脸识别、垃圾邮件识别、推荐系统等。一个好的数据分类算法会自动生成正确的标准,将给定的数据分成给定数量的类别。为了获取优异的分类结果,通常需要足够多的样本用于训练分类模型,以便它可以学习到正确的分类标准。如果样本数量不足,算法将对训练数据过拟合,这意味着它将在未知数据上表现不佳,这是机器学习领域普遍存在的问题。
2.2 数据预处理
我们可以将原始数据视为机器学习算法的原料,但就像我们不能将原料直接放入机器中而必须使用电力一样,机器学习算法通常需要数据在训练开始之前进行预处理,为了准备数据以供机器学习算法学习,必须对数据进行预处理并转换为正确的格式。我们,首先介绍机器学习中常用的数据预处理方法。
为了介绍数据预处理相关技术,我们首先构造一些示例数据:
import numpy as np
from sklearn import preprocessing
input_data = np.array([[1.2, 2.5, 5.4, -8.1, 0.1],
[-5.2, 5.9, 5.4, 0.2, 4.5],
[4.5, 3.5, 4.2, 8.0, 6.4],
[-1.1, -1.5, -2.4, -6.6, -3.5],
[2.1, 6.3, 5.6, -1.1, -7.5]])
接下来,我们将要介绍经典的数据预处理技术,包括:二值化,均值移除,数据缩放,数据归一化等。
2.2.1 二值化
二值化 (Binarization
) 用于将数值转换为布尔值,使用 Scikit-learn
内置方法 Binarizer
可以完成数据的二值化。接下来,我们以 4.5
作为阈值对输入数据进行二值化:
data_binarized = preprocessing.Binarizer(threshold=4.5).transform(input_data)
print("\\nBinarized data:\\n", data_binarized)
运行代码,可以得到以下输出:
Binarized data:
[[0. 0. 1. 0. 0.]
[0. 1. 1. 0. 0.]
[0. 0. 0. 1. 1.]
[0. 0. 0. 0. 0.]
[0. 1. 1. 0. 0.]]
可以看到的,所有高于 4.5
的值都变为 1
,而其余的值则变为 0
。
2.2.2 均值移除
均值移除 (mean removal
) 是机器学习中常用的预处理技术,其通过在特征向量中减去平均值来得到新的特征向量,这样每个特征都以零为中心,从而消除了特征向量中特征的偏差。我们首先打印输入数据的平均值和标准差:
# 计算输入数据的平均值和标准差
print("\\nMean Removal Before:")
print("Mean =", input_data.mean(axis=0))
print("Std deviation =", input_data.std(axis=0))
接下来,我们对输入数据执行均值移除:
data_scaled = preprocessing.scale(input_data)
print("\\nMean Removal After:")
print("Mean =", data_scaled.mean(axis=0))
print("Std deviation =", data_scaled.std(axis=0))
运行以上代码,可以得到以下输出结果:
Mean Removal Before:
Mean = [ 0.3 3.34 3.64 -1.52 0. ]
Std deviation = [3.28329103 2.80969749 3.06045748 5.70732862 5.0935253 ]
Mean Removal After:
Mean = [-2.22044605e-17 8.88178420e-17 1.99840144e-16 -4.16333634e-17
0.00000000e+00]
Std deviation = [1. 1. 1. 1. 1.]
可以看出,预处理后的特征向量平均值非常接近 0
,而标准差为 1
。
2.2.3 数据缩放
假设我们正在使用具有与房屋价格相关特征的数据集,目标是预测这些房屋的价格,但这些特征的数值范围差别较大。例如,房屋的面积通常为在数百之上,而房间的数量通常仅数个。因此我们需要对这些特征进行处理,以赋予每个特征的大致相同的权重大致,使得离群值不会对结果产生较大干扰。其中一种方法是重新调整所有特征,使它们落在一个较小的范围内,例如 0
到 1
之间。MinMaxScaler
算法是实现这一目标的最有效方法,算法的公式如下:
n e w ( x i ) = x i − m a x ( x ) m a x ( x ) − m i n ( x ) new(x_i)=\\frac x_i - max(x) max(x)-min(x) new(xi)=max(x)−min(x)xi−max(x)
其中
m
a
x
(
x
)
max(x)
max(x) 是变量的最大值,
m
i
n
(
x
)
min(x)
min(x) 是最小值,
x
i
x_i
xi 表示
x
x
x 中的第
i
i
i 个元素。
在特征向量中,每个特征的值可以在很大范围的随机值之间变化。因此,对这些特征进行缩放,以便为机器学习算法的对训练数据集进行同等重要程度的考量十分重要。任何特征都不应该仅仅因为测量上单位属性的不同而有所区别。使用 Scikit-learn
实现数据缩放,如下所示:
data_scaler_minmax = preprocessing.MinMaxScaler(feature_range=(0, 1))
data_scaled_minmax = data_scaler_minmax.fit_transform(input_data)
print("\\nMin max scaled data:\\n", data_scaled_minmax)
运行以上代码,得到输出结果如下所示:
Min max scaled data:
[[0.65979381 0.51282051 0.975 0. 0.54676259]
[0. 0.94871795 0.975 0.51552795 0.86330935]
[1. 0.64102564 0.825 1. 1. ]
[0.42268041 0. 0. 0.0931677 0.28776978]
[0.75257732 1. 1. 0.43478261 0. ]]
可以看到,每一列数据都被缩放,以便每列的最大值为 1
,每列数据表示数据样本的一类特征,以上输入数据中,每个数据样本包含 5
列,即每个数据样本含有 5
个特征。
2.2.4 数据归一化
归一化 (normalization
) 是另一类常用数据预处理技术,它与数据缩放非常相似,它们都会对数据进行变换,但在数据缩放中,会更改变量的值范围,而在归一化中,会更改数据分布的形状。为了使机器学习模型更好的学习,特征值最好符合正态分布。但现实中的数据分布并非如此。
我们使用归一化来修改特征向量中的值,以便可以在同一分布上训练模型。在机器学习中,我们可以使用许多不同形式的归一化。一些最常见的归一化修改这些特征值以使它们总和为 1
。L1
归一化指的是最小绝对偏差,其工作原理是确保每个数据样本(每行数据)的绝对值之和为 1
。L2
归一化指的是最小二乘,通过确保每个数据样本(每行数据)的平方和为 1
。
通常,L1
归一化技术比 L2
归一化技术更具鲁棒性,L1
归一化技术可以抵抗数据中的异常值。如果我们希望在计算过程中忽略它们这些异常值,则 L1
归一化通常是最佳选择;而如果在我们需要解决的问题中,异常值较为重要,那么 L2
归一化可能会成为更好的选择。使用 Scikit-learn
实现 L1
和 L2
归一化:
data_normalized_l1 = preprocessing.normalize(input_data, norm='l1')
data_normalized_l2 = preprocessing.normalize(input_data, norm='l2')
print("\\nL1 normalized data:\\n", data_normalized_l1)
print("\\nL2 normalized data:\\n", data_normalized_l2)
运行以上代码,可以得到以下输出:
L1 normalized data:
[[ 0.06936416 0.14450867 0.31213873 -0.46820809 0.00578035]
[-0.24528302 0.27830189 0.25471698 0.00943396 0.21226415]
[ 0.16917293 0.13157895 0.15789474 0.30075188 0.2406015 ]
[-0.07284768 -0.09933775 -0.1589404 -0.43708609 -0.23178808]
[ 0.09292035 0.27876106 0.24778761 -0.04867257 -0.33185841]]
L2 normalized data:
[[ 0.1185449 0.24696854 0.53345205 -0.80017808 0.00987874]
[-0.49289653 0.55924799 0.51185409 0.01895756 0.42654507]
[ 0.36133216 0.28103612 0.33724335 0.64236828 0.51389462]
[-0.13640673 -0.18600918 -0.29761469 -0.8184404 -0.43402142]
[ 0.18214788 0.54644365 0.48572769 -0.0954108 -0.65052815]]
2.2.5 标签编码
上述预处理技术都是针对输入数据特征,而标签编码 (label encoding
) 是对目标标签进行处理的技术。在分类任务中,我们通常会处理很多标签,这些标签可以是文字、数字或其他形式,但许多机器学习算法需要数字作为输入。因此,如果它们已经是数字,则可以直接用于训练;但通常情况并非如此,标签通常是单词,因为单词更容易被人类理解。训练数据用单词作为标签,以便后续可以反向映射输出文字标签。要将单词标签转换为数字,可以使用 Scikit-learn
中的标签编码器 LabelEncoder
。标签编码是指将单词标签转换为数字的过程,使得算法能够对其进行处理。接下里,我们使用 Scikit-learn
实现标签编码:
首先,导入所需包,并定义一些标签:
import numpy as np
from sklearn import preprocessing
labels = ['apple', 'orange', 'banana', 'orange', 'banana', 'lemon', 'pear', 'mango']
创建标签编码器对象并对其进行训练:
encoder = preprocessing.LabelEncoder()
encoder.fit(labels)
打印单词和数字之间的映射:
print("\\nLabel mapping:")
for i, item in enumerate(encoder.classes_):
print(item, '-->', i)
接下来,我们对随机标签进行编码:
test_labels = ['orange', 'apple', 'mango']
encoded_values = encoder.transform(test_labels)
print("\\nLabels =", test_labels)
print("Encoded values =", list(encoded_values))
最后,我们解码一组随机数字,将其映射为相应的单词标签:
encoded_values = [2, 1, 0, 4]
decoded_list = encoder.inverse_transform(encoded_values)
print("\\nEncoded values =", encoded_values)
print("Decoded labels =", list(decoded_list))
运行以上代码,可以得到以下输出:
Label mapping:
apple --> 0
banana --> 1
lemon --> 2
mango --> 3
orange --> 4
pear --> 5
Labels = ['orange', 'apple', 'mango']
Encoded values = [4, 0, 3]
Encoded values = [2, 1, 0, 4]
Decoded labels = ['lemon', 'banana', 'apple', 'orange']
2.3 逻辑回归分类器
逻辑回归 (logistic regression
) 是用于解释输入变量和输出变量之间关系的技术,回归可用于对连续值进行预测,但逻辑回归是用于进行离散预测的算法,例如预测数据样本为真或假等。假设输入变量间是独立的,输出变量称为因变量,因变量只包含一组固定的值,这些值对应于分类问题中的类别。
我们的目标是通过使用逻辑函数估计概率来识别自变量和因变量之间的关系。本节中,我们使用 sigmoid
作为逻辑函数,在逻辑回归模型中使用 sigmoid
函数具有以下优势:
- 值域在
0
和1
之间 - 导数更容易计算
- 可以将非线性引入模型
接下来,我们使用逻辑回归构建分类器。
首先,导入所需包,并定义二维特征向量和相应的标签作为模型输入数据集:
import numpy as np
from sklearn import linear_model
import matplotlib.pyplot as plt
x = np.array([[3.1, 7.2],
[4, 6.7],
[2.9, 8],
[5.1, 4.5],
[6, 5],
[5.6, 5],
[3.3, 0.4],
[3.9, 0.9],
[2.8, 1],
[0.5, 3.4],
[1, 4],
[0.6, 4.9]])
y = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3])
使用以上带有标签的数据训练分类器,创建逻辑回归分类器对象:
classifier = linear_model.LogisticRegression(solver='liblinear', C=1)
使用定义的数据训练分类器:
classifier.fit(x, y)
通过查看分类边界来可视化分类器的性能,首先定义可视化函数 visualize_classifier
,该函数将分类器对象、输入数据和标签作为输入参数,并定义了画布的尺寸,在函数中使用分类器,根据输入数据的不同类别绘制所有数据点,并绘制分类器得到的决策边界:
def visualize_classifier(classifier, x, y):
min_x, max_x = x[:, 0].min() - 1.0, x[:, 0].max() + 1.0
min_y, max_y = x[:, 1].min() - 1.0, x[:, 1].max() + 1.0
mesh_step_size = 0.01
x_vals, y_vals = np.meshgrid(np.arange(min_x, max_x, mesh_step_size), np.arange(min_y, max_y, mesh_step_size))
output = classifier.predict(np.c_[x_vals.ravel(), y_vals.ravel()])
output = output.reshape(x_vals.shape)
plt.figure()
plt.pcolormesh(x_vals, y_vals, output, cmap=plt.cm.ocean)
plt.scatter(x[:, 0], x[:, 1], c=y, s=75, edgecolors='black', linewidth=1, cmap=plt.cm.Paired)
plt.xlim(x_vals.min(), x_vals.max())
plt.ylim(y_vals.min(), y_vals.max())
plt.xticks((np.arange(int(x[:, 0].min() - 1), int(x[:, 0].max() + 1), 1.0)))
plt.yticks((np.arange(int(x[:, 1].min() - 1), int(x[:, 1].max() + 1), 1.0)))
plt.show()
传入相应参数,调用定义的可视化函数 visualize_classifier
:
visualize_classifier(classifier, x, y)
执行以上代码,可以看到分类器的决策边界如下图所示:
通过将 LogisticRegression
对象中 C
的值修改为 100
,可以得到更加准确的决策边界:
classifier = linear_model.LogisticRegression(solver='liblinear', C=100)
classifier.fit(x, y)
visualize_classifier(classifier, x, y)
决策边界更加准确的原因是使用参数 C
可以对错误分类施加一定的惩罚,因此算法对训练数据会进行更多的微调,但如果你 C
值过大,算法将出现过拟合情况,泛化能力将大幅降低。
如果将 C
设置为 100
运行代码,可以得到以下决策边界,与 C=1
时绘制的决策边界相比较,可以看到 C=100
时决策边界更加准确:
2.4 混淆矩阵
混淆矩阵 (confusion matrix
) 是用于描述分类器性能的图表,矩阵中的每一行代表预测类中的实例,每一列代表实际类中的实例。我们很容易通过该矩阵判断模型错误标记的类别,可以看到有多少样本被正确分类或错误分类。
在构建混淆矩阵过程中,我们首先了解以下关键指标,我们以输出为 0
或 1
的二分类为例:
- 真阳性 (
True Positives
,TP
):模型预测输出为1
,真实标签也是1
的样本 - 真阴性 (
True Negatives
,TN
):模型预测输出为0
,真实标签也是0
的样本 - 假阳性 (
False Positives
,FP
):模型预测输出为1
,而真实标签为0
的样本,这也称为I
类错误 - 假阴性 (
False Negatives
,FN
):模型预测输出为0
,而真实标签为1
的样本,这也称为II
型错误
根据不同问题,我们可能需要不同的评价标准用于优化算法,可能是降低 I
类错误率,也可能是降低 I
类错误率。例如,在生物识别系统中,避免降低 I
类错误率非常重要,因为此类错误可能会造成敏感信息的泄露。接下来,我们利用 Scikit-learn
学习如何创建混淆矩阵。
首先,导入所需包,并模拟创建真实标签和预测标签数据:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
true_labels = [2, 0, 0, 2, 4, 2, 1, 0, 3, 3, 3, 5, 5]
pred_labels = [2, 1, 0, 2, 4, 5, 1, 0, 1, 3, 3, 2, 5]
使用上述定义的标签创建混淆矩阵:
confusion_mat = confusion_matrix(true_labels, pred_labels)
可视化混淆矩阵:
plt.imshow(confusion_mat, interpolation='nearest', cmap=plt.cm.hot)
plt.title('Confusion matrix')
plt.colorbar()
ticks 以上是关于一文开启监督学习之旅的主要内容,如果未能解决你的问题,请参考以下文章