数据挖掘(二)
文章目录
- 数据挖掘(二)
- 估计器
- 近邻算法
- 距离度量
- 加载数据集
- 流水线在预处理中的应用
- 预处理实例
- 标准预处理
- 组装起来
- 流水线
- 完整实例
scikit-learn库实现了一系列数据挖掘算法,提供通用编程接口、标准化的测试和调参工具,方便用户尝试不同算法对其进行充分测试和查找最有参数值。有大量使用scikit-learn库的算法和工具。
估计器
scikit-learn把大量分类算法功能封装成了所谓的估计器。估计器用于分类任务,主要包括fit和predict函数。大多数估计器接收和输出的数据格式都是numpy数组或者类似的格式。
- fit函数用于训练算法,设置内部参数,接收训练集及其类别两个参数。
- predict函数参数是测试集,预测测试集类别并返回一个包含测试集各条数据类别的数组。
近邻算法
近邻算法可能是标准数据挖掘算法中最为直观的一种。对新个体进行分类,查找训练集,找到与新个体最相似的那些个体,看看这些个体属于哪个类别就把它分到哪个类别。近邻算法几乎可以对任何数据集进行分类,但是要计算数据集中每两个个体之间的距离计算量很大。此外在特征取离散值的数据集上表现很差。
距离度量
距离是数据挖掘的核心概念之一。我们通常需要知道两个个体之间的距离,还要解决一对个体相对另一对个体是否更相近等问题。这类问题的解决方案将直接影响分类结果。
- 欧氏距离也称真实距离,它是两个特征向量长度平方和的平方根。假如在纸上画两个点,用直尺测量这两个点的距离,得到的结果就是欧氏距离。欧氏距离很直观,如果某些特征比其他特征取值大很多,精确度就会比较差。
- 曼哈顿距离是两个特征在标准坐标系中绝对轴距之和。比如国际象棋中的车,假如每次只能走一格,那么它走到当前格子对角线那头所走的距离就是曼哈顿距离。
- 余弦距离值得是特征向量夹角的余弦值。余弦距离更适合解决异常值和数据稀疏问题,但是丢弃了向量长度所包含的在某些场景下可能会很有用的一些信息。
加载数据集
本次使用的数据集是电离层(Ionosphere),这些数据是由高频天线收集的,天线的目的是侦测在电离层和高层大气中不存在由自由电子组成的特殊结构。如果一条数据能给出特殊结构存在的证据,这条数据就属于好的那一类,否则就是坏的。在数据集中用"g"表示好的一类,"b"表示坏的哪一类。我们要做的是建立分类器,自动判断这些数据的好坏。
- 我们可以从UCI机器学习数据库下载数据集
Ionosphere
,下载好数据集后,把文件解压到数据目录。数据集每行有35个值,前34个是17座天线采集的数据,最后一个值是数据的好坏。
import os home_folder = os.path.expanduser("~")
print('当前用户主目录:%s'%home_folder)
data_folder = os.path.join(os.getcwd(), 'ionosphere')
data_filename = os.path.join(data_folder, 'ionosphere.data')
print('数据集文件的位置:%s'%data_filename)
- 读取数据集文件
import csv
import numpy as np# 已知数据集有351行34列,采用数组X和y存放数据集
# 创建一个指定形状(shape)和数据类型(dtype)的全零数组,形状可以是一个整数或者一个元组,X为351行34列的全零矩阵
X = np.zeros((351, 34), dtype='float')
y = np.zeros((351,), dtype='bool')
with open(data_filename, 'r') as input_file:#reader是可迭代的对象reader = csv.reader(input_file)for i, row in enumerate(reader):# 每行数据代表一组测量结果,成为数据集中的一个个体,最后一个数如果是g则设置对应的y值为Truedata = [float(datum) for datum in row[:-1]]X[i] = data y[i] = row[-1] == 'g'
- 实现流程标准化,用fit方法在训练集上完成模型的创建,用predict方法在测试集上评估效果。使用K近邻分类器并初始化,默认选择5个近邻作为分类依据。接着用测试集测试算法,评估在测试集上的表现。
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier# 创建训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=14)
print('在训练集中有%d个样本,测试集中有%d个样本,每个样本有%d个特征'%(X_train.shape[0], X_test.shape[0], X_train.shape[1]))
estimator = KNeighborsClassifier()
# 使用训练数据进行训练,K近邻估计器分析训练集中的数据,比较待分类的新数据点和训练集中的数据,找到新数据的近邻
estimator.fit(X_train, y_train)
y_predicted = estimator.predict(X_test)
accuracy = np.mean(y_test == y_predicted) * 100
print('准确率:{0:.1f}%'.format(accuracy))
在训练集中有263个样本,测试集中有88个样本,每个样本有34个特征
准确率:86.4%
- 我们把数据分为训练集和测试集,训练集用于训练算法,在测试集评估效果。交叉检验可以解决一次性测试带来的问题,多进行几次实验,每次切分时,都要保证这次得到的训练集和测试集与上次不一样,还要确保每条数据都只能用来测试一次。算法步骤如下:
- 把整个大数据集分为几个部分。
- 对于每部分数据集,将其中一部分作为当前测试集,用剩余部分训练算法,在当前测试集上测试算法。
- 记录每次得分及平均得分。
- 在前面的步骤中,每条数据只能在测试集中出现一次,以减少运气成分。
# scikit-learn提供了几种交叉检验方法,添加辅助函数
from sklearn.model_selection import cross_val_score
# 默认使用Stratified K Fold方法切分数据集,大体上保证切分后得到的子数据集中类别分布相同,避免某些子数据集出现类别分布失衡的情况
# 把完整的数据集X和类别值y传递给cross_val_score
scores = cross_val_score(estimator, X, y, scoring='accuracy')
average_accuracy = np.mean(scores) * 100
print('平均准确率:{0:.1f}%'.format(average_accuracy))
平均准确率:82.6%
- 设置参数,几乎所有的数据挖掘算法都允许用户设置参数,可以增强算法的泛化能力。近邻算法有多个参数,最重要的是选取多少个近邻作为预测依据。
from matplotlib import pyplot as pltavg_scores = []
all_scores = []
#测试一系列n_neighbors从1~20的值重复进行多次实验
parameter_values = list(range(1, 21))
for n_neighbors in parameter_values:#n_neighbors设置选取多少个近邻作为预测依据estimator = KNeighborsClassifier(n_neighbors=n_neighbors)scores = cross_val_score(estimator, X, y, scoring='accuracy')avg_scores.append(np.mean(scores))all_scores.append(scores)
plt.plot(parameter_values, avg_scores, '-o', linewidth=5, markersize=24)
#plt.axis([0, max(parameter_values), 0, 1.0])
流水线在预处理中的应用
对于借助数学方法来比较特征的算法而言,它们很难理解特征在规模、范围和单位上的差异。不同特征的取值范围千差万别,常见解决方法是对不同的特征进行规范化,使它们的特征值落在相同的值域或从属于某几个确定的类别,比如小、中和大。如果解决这个问题,不同特征类型对算法的影响将大大降低,分类正确率就能有大幅提升。选取最具区分度的特征、创建新特征等都属于预处理的范畴。预处理工具是转换器,接收原始数据集,返回转换后的数据集。除了处理数值型特征,转换器还能用来抽取特征。
预处理实例
- 为原数据集创建一个副本,然后搞乱数据集
Ionosphere
,破坏后的数据集上的正确率降低了,把特征值转变到0到1之间就能解决问题。
X_broken = np.array(X)
# 每隔一行把第二个特征的值除以10,数值范围变了,奇数行的第二个特征要比偶数行的大
X_broken[:, ::2] /= 10
estimator = KNeighborsClassifier()
original_scores = cross_val_score(estimator, X, y, scoring='accuracy')
print('原来的平均正确率:{0:.1f}%'.format(np.mean(original_scores) * 100))
broken_scores = cross_val_score(estimator, X_broken, y, scoring='accuracy')
print('破坏后的平均正确率:{0:.1f}%'.format(np.mean(broken_scores) * 100))
标准预处理
- 使用
MinMaxScaler
类进行基于特征的规范化,可以把每个特征的值域规范化为0到1之间。最小值用0代替,最大值用1代替,其余值介于两者之间。接下来对数据集X进行预处理,调用转换器并且不需要先训练就可以完成训练和转换。
from sklearn.preprocessing import MinMaxScaler
#X_transformed与X是同型矩阵,行列数相等,前者每列值的值域是0到1
X_transformed= MinMaxScaler().fit_transform(X)
- 使用
sklearn.preprocessing.Normalizer
可以使每条数据各特征值的和为1。 - 使用
sklearn.preprocessing.StandardScaler
可以使各特征值的均值为0,方差为1,常用作规范化的基准。 - 使用
sklearn.preprocessing.Binarizer
可以将数值型特征二值化,大于阈值的为1,反之为0。
组装起来
- 一套完整的工作流处理被破坏过的数据集实例。
X_transformed= MinMaxScaler().fit_transform(X_broken)
estimator = KNeighborsClassifier()
transformed_scores = cross_val_score(estimator, X_transformed, y, scoring='accuracy')
print('平均正确率:{0:.1f}%'.format(np.mean(transformed_scores) * 100))
流水线
流水线结构把切分数据集、对特征进行二值化处理、以特征或数据集中的个体为基础规范化数据等操作步骤保存到数据挖掘的工作流中。我们可以用流水线读入数据,做各种必要的预处理,然后给出预测结果。
from sklearn.pipeline import Pipeline
#用MinMaxScaler把特征取值范围规范到0到1,指定 KNeighborsClassifier分类器
scaling_pipeline = Pipeline([('scale', MinMaxScaler()),
('predict', KNeighborsClassifier())])
- 流水线的输入是一连串的数据挖掘步骤,最后一步必须是估计器,前几步是转换器。输入的数据集经过转换器的处理后,输出的结果作为下一步的输入。最后,用位于最后一步的估计器对数据进行分类。
- 流水线每一步都用元组(‘名称’,‘步骤’ )来表示。核心是元素为元组的列表,第一个元组规范特征取值范围(scale),第二个元组实现预测功能(predict)。
完整实例
import os
import numpy as np
import csv
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from matplotlib import pyplot as plthome_folder = os.path.expanduser("~")
print('当前用户目录:%s'%home_folder)
data_folder = os.path.join(os.getcwd(), 'ionosphere')
data_filename = os.path.join(data_folder, 'ionosphere.data')
print('数据集文件的位置:%s'%data_filename)
X = np.zeros((351, 34), dtype='float')
y = np.zeros((351,), dtype='bool')
with open(data_filename, 'r') as input_file:reader = csv.reader(input_file)for i, row in enumerate(reader):data = [float(datum) for datum in row[:-1]]X[i] = data y[i] = row[-1] == 'g'X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=14)
print('在训练集中有%d个样本,测试集中有%d个样本,每个样本有%d个特征'%(X_train.shape[0], X_test.shape[0], X_train.shape[1]))
estimator = KNeighborsClassifier()
estimator.fit(X_train, y_train)
y_predicted = estimator.predict(X_test)
accuracy = np.mean(y_test == y_predicted) * 100
print('准确率:{0:.1f}%'.format(accuracy))
scores = cross_val_score(estimator, X, y, scoring='accuracy')
average_accuracy = np.mean(scores) * 100
print('平均准确率:{0:.1f}%'.format(average_accuracy))
avg_scores = []
all_scores = []
parameter_values = list(range(1, 21))
for n_neighbors in parameter_values:estimator = KNeighborsClassifier(n_neighbors=n_neighbors)scores = cross_val_score(estimator, X, y, scoring='accuracy')avg_scores.append(np.mean(scores))all_scores.append(scores)
#plt.plot(parameter_values, avg_scores, '-o')
for parameter, scores in zip(parameter_values, all_scores):n_scores = len(scores)plt.plot([parameter] * n_scores, scores, '-o')
plt.show()