用scikit-learn估计值分类——近邻算法(KNN)

用scikit-learn估计值分类主要是为数据挖掘搭建通用的框架。有了这个框架之后,增加了算法的泛化性,减少了数据挖掘的复杂性。

用scikit-learn估计值分类有这三个方面:

                  1. 估计器(estimator):用于分类、聚类和回归分析。

                  2. 转换器(transformer):用于数据的预处理和数据的转换。

                  3. 流水线(pipeline):组合数据挖掘流程,便于再次使用。

近邻(kNN,k-NearestNeighbor)分类算法是标准数据挖掘分类技术中最为直观的一种。为了对新个体进行分类,它查找训练集,找到了与新个体最相似的那些个体,看看这些个体大多属于哪个类别,
就把新个体分到哪个类别。近邻算法几乎可以对任何数据进行分类,但是要计算数据集中每两个个体之间的距离,计算量很大。同时还有一个问题是在特征取离散值的数据集上表现很差。

距离是一个很关键的问题。欧式距离(两个点之间直线距离)、曼哈顿距离(两个特征在标准坐标系中绝对轴距之和)、余弦距离(特征向量夹角的余弦值)。

接下来我们利用一个叫电离层的数据集(http://archive.ics.uci.edu/ml/machine-learning-databases/ionosphere/)来分析近邻算法的运用。在这个网站里点击ionosphere.data,之后复制这个数据,保存在本地。然后,我们来进行近邻算法的实现吧!

import csv
import numpy as np

# 创建两个数组分别存放特征值和类别
x = np.zeros((351, 34), dtype="float")
y = np.zeros((351, ), dtype="bool")
'''
with open("ionosphere.data", 'r') as input_file:
    reader = csv.reader(input_file)
'''
input_file = open('ionosphere.data', 'r')
reader = csv.reader(input_file)

# 遍历文件中每一行数据,每一行数据相当于一个个体,用枚举函数来获取每一行的索引值,更新数据集x。
for i, row in enumerate(reader):
    data = [float(datum) for datum in row[:-1]]
    x[i] = data
    y[i] = row[-1] == "g"

# 建立测试集和训练集。
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=14)
# print "There are {0} samples in training dataset".format(x_train.shape[0])
# print "There are {0} samples in testing dataset".format(x_test.shape[0])
# print "Each sample has {0} features".format(x_train.shape[1])

# 然后导入K近邻分类器这个类,并为其初始化一个实例。
from sklearn.neighbors import KNeighborsClassifier
# 创建一个估计器
estimator = KNeighborsClassifier()
# 创建好之后,开始用训练数据进行训练。K近邻估计器会分析训练集中的数据,通过比较待分类的新数据点和训练集中的数据,找到新数据点的近邻。
estimator.fit(x_train, y_train)
# 接着用测试集测试算法。
y_predicted = estimator.predict(x_test)
accuracy = np.mean(y_test == y_predicted) * 100
print "The accuracy is {0:.1f}%".format(accuracy)
# The accuracy is 86.4%
# 正确率很高,但是仅仅是使用默认的参数,在某些具体的情况下是不能通用的,所以,要学着根据实验的实际情况,尽可能选用合适的参数值,争取达到最佳的效果。

# 我们通常用训练集训练算法,然后在测试集上评估效果。当测试集很简单,我们就会认为算法表现很出色。反之,我们可能会认为算法很糟糕。其实,只是凭一次的测试或一次的训练,是很难真正决定一个算法的好坏。所以,此时我们就要用到交叉检验了(将数据分割成很多部分,多次测试)。
'''
交叉检验:
1.将整个大数据集分成几个部分
2.对于每一部分执行以下操作:
将其中一部分作为当前测试集
用剩余部分去训练算法
在当前测试集上测试算法
3.记录每一次得分及平均得分
4.在上述过程中,每条数据只能在测试集中出现一次,以减少运气成分。
'''
# 导入cross_val_score交叉检验方式
from sklearn.model_selection import cross_val_score
scores = cross_val_score(estimator, x, y, scoring="accuracy")
average_accuracy = np.mean(scores) * 100
print "The average accuracy is {0:.1f}%".format(average_accuracy)
# The average accuracy is 82.3%
# 设置参数。参数设置对于一个算法很重要,灵活的设置参数能大大提高算法的泛化能力,所以,选取好的参数值跟数据集的特征息息相关。
# 在近邻算法中,最重要的参数(n_neighbors)是选取多少个近邻作为预测依据。它过大过小会造成不同的分类结果。
# 下面我们测试n_neighbors的值,比如1到20,可以重复进行多次实验,观察不同参数所带来的结果之间的差异。
from collections import defaultdict
avg_scores = []
all_scores = defaultdict(list)
for n_neighbors in xrange(1,21):
    estimator_ = KNeighborsClassifier(n_neighbors=n_neighbors)
    scores = cross_val_score(estimator_, x, y, scoring="accuracy")
    avg_scores.append(np.mean(scores))
    all_scores[n_neighbors].append(scores)

# 为了更直观的观察差异,我们可以用图来表示。
from matplotlib import pyplot as plt
parameter_values = list(range(1,21))
#plt.plot(parameter_values, avg_scores, "-o")
#plt.show()
plt.figure(figsize=(32,20))
plt.plot(parameter_values, avg_scores, '-o', linewidth=5, markersize=24)
plt.axis([0, max(parameter_values), 0, 1.0])
# plt.show()
# 可以看出随着近邻数的增加,正确率不断下降。


# 对数据预处理
'''
对于不同特征的取值范围千差万别,常见的解决方法是对不同的特征进行规范化,使它们的特征值落在相同的值域或从属于某几个确定的类别。
'''
# 选择最具区分度的特征、创建新特征等都属于预处理的范畴。
# 预处理示例
x_broken = np.array(x)
# 对数据的第三个特征的值除以10
x_broken[:,::2] /= 10
# print x_broken
# 再来计算正确率
original_scores = cross_val_score(estimator, x, y, scoring='accuracy')
print "The original average accuracy for is {0:.1f}%".format(np.mean(original_scores) * 100)
# The original average accuracy for is 82.3%
broken_scores = cross_val_score(estimator, x_broken, y, scoring="accuracy")
print "The broken average accuracy for is {0:.1f}%".format(np.mean(broken_scores) * 100)
# The broken average accuracy for is 71.5%
# 这次跌到71.5%。如果,把特征值转变到0到1之间就能解决这个问题。将特征值规范化,使用minmaxscaler类。
from sklearn.preprocessing import MinMaxScaler
# 不需要单独进行训练,直接调用fit_transform()函数,即可完成训练和转换。
# x_transformed = MinMaxScaler().fit_transform(x)
# 接下来将前面的broken数据拿来测试
x_transformed = MinMaxScaler().fit_transform(x_broken)
transformed_scores = cross_val_score(estimator, x_transformed, y, scoring="accuracy")
print "The average accuracy for is {0:.1f}%".format(np.mean(transformed_scores) * 100)
# The average accuracy for is 82.3%
# 正确率再次升到82.3%说明将特征值规范化有利于减少异常值对近邻算法的影响。

# 流水线
# 实现一个数据挖掘流水线,能大大提高效率。它就好比一个框架,能够有效的将转换器和估计器结合。
from sklearn.pipeline import Pipeline
# 流水线的核心是元素为元组的列表。第一个元组规范特征取值范围,第二个元组实现预测功能。
scaling_pipeline = Pipeline([("scale", MinMaxScaler()), ("predict", KNeighborsClassifier())])
scores_1 = cross_val_score(scaling_pipeline, x_broken, y, scoring="accuracy")
print "The pipeline scored_1 an average accuracy for is {0:.1f}%".format(np.mean(scores_1) * 100)
# The pipeline scored_1 an average accuracy for is 82.3%
# 运行结果与之前的一样,说明设置流水线很有用,因为它能确保代码的复杂程度不至于超出掌控范围。
# 我们再将数据规范化应用到原始数据集上,看看能不能使准确率上升,以说明数据集规范化能提升没有异常值的数据。
scores_2 = cross_val_score(scaling_pipeline, x, y, scoring="accuracy")
print "The pipeline scored_2 an average accuracy for is {0:.1f}%".format(np.mean(scores_2) * 100)
# The pipeline scored_2 an average accuracy for is 82.3%
# 这个结果说明,数据规范化只能将含有异常值的数据集的准确率提高。

 

原文地址:https://www.cnblogs.com/llhy1178/p/6876785.html