2018-06-21 00:46:16 Evitachan 阅读数 752
  • 【Python】第16周 数据挖掘与机器学习进阶(上)

    本周课程讲机器学习的概念,了解什么是机器学习以及机器学习的主要任务,kNN算法、线性回归算法的原理与实现,如何拆分训练数据集与测试数据集,算法中的超参数以及特征归一化等内容。实战部份,将讲解手写字体识别和房价预测。

    616 人正在学习 去看看 CSDN就业班

    KNN算法是监督学习分类方法。何为监督学习?我们用来训练的数据集应当包括数据特征和标签两个部分,通过训练建立数据特征和标签之间关系的算法模型,这样的话,将测试数据集套用算法模型,可以得到测试数据的标签。

一 KNN算法原理   

    在训练样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输人没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。选择k个最相似数据中出现次数最多的分类,作为新数据的分类。在KNN中,通过计算对象间距离作为各个对象之间的相似性指标,代替对象之间的匹配度计算。

    对于训练样本数为m,特征数为n的训练样本集,计算测试样本xm个训练样本的欧氏距离


    其中i=12,……,m。对d(x,yi)进行降序排列,选择前k个值,出现次数最多的分类作为测试样本x的分类。

    形象地理解,将训练样本和测试样本投影到n维空间上,以某个测试样本为中心,距离其最近的k个训练样本点采用投票原则,投出该训练样本的标签。

    由此可见,k值的选择会影响到标签的决策。以iris数据集为例。


数据集中共150个样本点,四个特征,为了绘制散点图,选取前两个特征进行分类。

import numpy as np
import matplotlib.pyplot as plt
from sklearn import neighbors

# import some data to play with
from sklearn import datasets
iris = datasets.load_iris()

# only take the first two features.
# we could avoid this ugly slicing by using a two-dim dataset
x = iris.data[:, :2]
y = iris.target

# k
n_neighbors = 3

# create color maps
from matplotlib.colors import ListedColormap
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])

# knn
n_neighbors = 3

for weights in ['uniform', 'distance']:
    # we create an instance of Neighbors classifier and fit the data
    clf = neighbors.KNeighborsClassifier(n_neighbors, weights = weights)
    clf.fit(x, y)

    # plot the decision boundary.
    # For that, we will assign a color to each point in the mesh[x_min, x_max] * [y_min, y_max]
    x_min, x_max = x[:, 0].min() - 1, x[:, 0].max() + 1
    y_min, y_max = x[:, 1].min() - 1, x[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, .02), np.arange(y_min, y_max, .02))

    # predict
    z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

    # put the result into a color plot
    z = z.reshape(xx.shape)
    plt.figure()
    plt.pcolormesh(xx, yy, z, cmap = cmap_light)

    # plot also the training points
    plt.scatter(x[:, 0], x[:, 1], c = y, cmap = cmap_bold, edgecolor = 'k', s = 20)
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())
    plt.title("3-Class classification (k = %i, weithts = '%s')" % (n_neighbors, weights))

plt.show()

    

    比较两个图,可以很明显看出,当k取值不同时,分类的效果不同。

二 KNN算法预测泰坦尼克沉船遇难情况

原始数据有10个特征,选取特征:‘Pclass’,‘Sex’,‘Age’,‘SibSp’,‘Parch’ 5个作为决策树特征向量。


数据预处理
>训练集中共有891位乘客的数据信息,其中277位乘客的年龄数据缺失,余下数据年龄平均值为29.7,用30补全缺失项。
>性别男/女用1/0表示。
from numpy import *
import operator
from os import listdir

def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()     
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

def createDataSet():
    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels = ['A','A','B','B']
    return group, labels

def file2matrix(filename):
    file = open(filename)
    arraylines = file.readlines()
    numberoflines = len(arraylines) - 1
    returnMat = zeros((numberoflines,3))    #创建行数为numberoflines,列数为2的矩阵
    classLabel = []
    index = 0
    for line in arraylines[1:]:
        line = line.strip()
        lis = line.split(' ')
        returnMat[index,:] = lis[0:3]
        classLabel.append(int(lis[-1]))   #是为1类,否为0类
        index += 1
    return returnMat,classLabel
    
def autoNorm(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (m,1))
    normDataSet = normDataSet/tile(ranges, (m,1))   #element wise divide
    return normDataSet, ranges, minVals
   
def datingClassTest():
    hoRatio = 0.1      #hold out 10%
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]
    numTestVecs = int(m*hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
       classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],\
          datingLabels[numTestVecs:m],3)
       
       str= "the classifier came back with: %d, the real answer is: %d"\
          % (classifierResult, datingLabels[i])
       print(str)
       if (classifierResult != datingLabels[i]): errorCount += 1.0
    str= "the total error rate is: %f" % (errorCount/float(numTestVecs))
    print(str)

    使用交叉验证方法,选取一定比例数据作为测试集,比较利用KNN算法分类所得标签与真实标签结果是否一致,若不一致,则错误样本数加一。最终利用错误样本数除以总验证样本数得到错误率。


经验证,测试数据比例与正确率关系如下。在选取10%数据作为测试数据时分类效果最好,这与最常用的十折交叉验证相符合。


三 总结

1. 在选取10%数据作为测试数据时分类效果最好,这与最常用的十折交叉验证相符合。

2. k值选择过大或过小都会影响正确率,在使用KNN模型时,我们需要不断调整k值,直到找到局部最优解,这也是KNN算法需要解决的问题。


2018-09-29 11:26:24 weixin_42137700 阅读数 203
  • 【Python】第16周 数据挖掘与机器学习进阶(上)

    本周课程讲机器学习的概念,了解什么是机器学习以及机器学习的主要任务,kNN算法、线性回归算法的原理与实现,如何拆分训练数据集与测试数据集,算法中的超参数以及特征归一化等内容。实战部份,将讲解手写字体识别和房价预测。

    616 人正在学习 去看看 CSDN就业班

KNN算法的机器学习基础

https://mp.weixin.qq.com/s/985Ym3LjFLdkmqbytIqpJQ


本文原标题 :
Machine Learning Basics with the K-Nearest Neighbors Algorithm
翻译 | 小哥哥、江舟    校对 | 吕鑫灿    整理 | 志豪
原文链接:
https://towardsdatascience.com/machine-learning-basics-with-the-k-nearest-neighbors-algorithm-6a6e71d01761

  基于K近邻算法的机器学习基础


k近邻( KNN )算法是一种简单、易于实现的监督机器学习算法,可用于解决分类和回归问题。暂停!让我们从这里入手。

  把问题分解


有监督的机器学习算法(与无监督的机器学习算法相反)是一种依靠标记的输入数据来学习函数的算法,当给定新的未标记数据时,该函数会产生适当的输出。


想象一下,一台计算机当做一个孩子,我们是它的主管(例如父母、监护人或老师),我们希望孩子(计算机)能了解猪是什么样子。我们向孩子展示几幅不同的照片,其中一些是猪,其余的可能是任何东西的照片(猫、狗等)。


当我们看到一只猪时,我们喊“猪!”!当它不是猪时,我们喊“不,不是猪!”!和孩子一起做了几次之后,我们给他们看了一张照片,然后问“猪?他们会正确地(大多数时候)说“猪!”!“或者”不,不是猪!“取决于照片是什么。这就是有监督的机器学习。


 
“猪!”


有监督机器学习算法用于解决分类或回归问题。


分类问题的输出是离散值。例如,“喜欢比萨上的菠萝”和“不喜欢比萨上的菠萝”是离散的。没有中间地带。上面教孩子识别猪的类比是分类问题的另一个例子。
 


显示随机生成数据的图像

 


这张图片展示了分类数据可能是什么样子的基本例子。我们有一个预测器(或一组预测器)和一个标签。在这张图片中,我们可能试图根据年龄(预测因子)来预测某人是(1)否(0)喜欢比萨饼上的菠萝。


标准做法是将分类算法的输出(标签)表示为整数,例如1、-1或0。在这种情况下,这些数字纯粹是代表性的。不应该对它们进行数学运算,因为这样做毫无意义。想一想。什么是“喜欢菠萝”+“不喜欢菠萝”?没错。我们不能相加它们,所以我们不应该把数字表示特征相加。


回归问题有一个实数(带有小数点的数字)作为输出。例如,我们可以使用下表中的数据来估计给定身高的人的体重。


 


图像显示了SOCR高度和权重数据集的一部分

 


回归分析中使用的数据看起来类似于上图所示的数据。我们有一个自变量(或一组自变量)和一个因变量(试图在给定自变量的情况下猜测因变量)。例如,我们可以说高度是自变量,而重量是因变量。


此外,每行通常称为示例、观察或数据点,而每列(不包括标签/因变量)通常称为预测值、维度、自变量或特征。


无监督的机器学习算法使用没有任何标签的输入数据——换句话说,没有老师(标签)告诉孩子(计算机)什么时候是正确的,什么时候是错误的,这样它就可以自我纠正。


与试图学习一种函数的监督学习不同,这种函数允许我们在给定一些新的未标记数据的情况下进行预测,无监督学习试图学习数据的基本结构,从而让我们对数据有更多的了解。

 

  最近邻
KNN算法假设相似的东西存在于很近的地方。换句话说,相似的事物彼此接近。
“物以类聚。"
 


显示相似数据点通常如何彼此靠近存在的图像

 


请注意,在上图中,大多数情况下,相似的数据点彼此接近。KNN算法就是基于这个假设以使算法有用。KNN利用与我们童年时可能学过的一些数学相似的想法(有时称为距离、接近度或接近度),即计算图上点之间的距离。


注意:在继续之前,有必要了解我们如何计算图表上的点之间的距离。如果你不熟悉或需要复习计算方法,请完整阅读“两点之间的距离”,然后再回来。


还有其他计算距离的方法,其中一种方法可能更好,这取决于我们正在解决的问题。然而,直线距离(也称为欧氏距离)是一个流行且熟悉的选择。

 

KNN算法(英文)
1. 加载数据
2. 将K初始化为你选择的邻居数量
3. 对于数据中的每个示例
3.1 根据数据计算查询示例和当前示例之间的距离。
3.2 将示例的距离和索引添加到有序集合中
4. 按距离将距离和索引的有序集合从最小到最大(按升序)排序
5. 从已排序的集合中挑选前K个条目
6. 获取所选K个条目的标签
7. 如果回归,返回K个标签的平均值
8. 如果分类,返回K个标签的模式

 

 

KNN实现(从头开始)


from collections import Counter
import math

def knn(data, query, k, distance_fn, choice_fn):
   neighbor_distances_and_indices = []
  
   # 3. For each example in the data
   for index, example in enumerate(data):
       # 3.1 Calculate the distance between the query example and the current
       # example from the data.
       distance = distance_fn(example[:-1], query)
      
       # 3.2 Add the distance and the index of the example to an ordered collection
       neighbor_distances_and_indices.append((distance, index))
  
   # 4. Sort the ordered collection of distances and indices from
   # smallest to largest (in ascending order) by the distances
   sorted_neighbor_distances_and_indices = sorted(neighbor_distances_and_indices)
  
   # 5. Pick the first K entries from the sorted collection
   k_nearest_distances_and_indices = sorted_neighbor_distances_and_indices[:k]
  
   # 6. Get the labels of the selected K entries
   k_nearest_labels = [data[i][1] for distance, i in k_nearest_distances_and_indices]

   # 7. If regression (choice_fn = mean), return the average of the K labels
   # 8. If classification (choice_fn = mode), return the mode of the K labels
   return k_nearest_distances_and_indices , choice_fn(k_nearest_labels)

def mean(labels):
   return sum(labels) / len(labels)

def mode(labels):
   return Counter(labels).most_common(1)[0][0]

def euclidean_distance(point1, point2):
   sum_squared_distance = 0
   for i in range(len(point1)):
       sum_squared_distance += math.pow(point1[i] - point2[i], 2)
   return math.sqrt(sum_squared_distance)

def main():
   '''
   # Regression Data
   #
   # Column 0: height (inches)
   # Column 1: weight (pounds)
   '''
   reg_data = [
      [65.75, 112.99],
      [71.52, 136.49],
      [69.40, 153.03],
      [68.22, 142.34],
      [67.79, 144.30],
      [68.70, 123.30],
      [69.80, 141.49],
      [70.01, 136.46],
      [67.90, 112.37],
      [66.49, 127.45],
   ]
  
   # Question:
   # Given the data we have, what's the best-guess at someone's weight if they are 60 inches tall?
   reg_query = [60]
   reg_k_nearest_neighbors, reg_prediction = knn(
       reg_data, reg_query, k=3, distance_fn=euclidean_distance, choice_fn=mean
   )
  
   '''
   # Classification Data
   #
   # Column 0: age
   # Column 1: likes pineapple
   '''
   clf_data = [
      [22, 1],
      [23, 1],
      [21, 1],
      [18, 1],
      [19, 1],
      [25, 0],
      [27, 0],
      [29, 0],
      [31, 0],
      [45, 0],
   ]
   # Question:
   # Given the data we have, does a 33 year old like pineapples on their pizza?
   clf_query = [33]
   clf_k_nearest_neighbors, clf_prediction = knn(
       clf_data, clf_query, k=3, distance_fn=euclidean_distance, choice_fn=mode
   )

if __name__ == '__main__':
   main()

 

为K选择正确的值


为了选择适合你的数据的K,我们用不同的K值运行了几次KNN算法,并选择K来减少我们遇到的错误数量,同时保持算法在给定之前从未见过的数据时准确预测的能力。


以下是一些需要记住的事情:


当我们将K值降低到1时,我们的预测会变得不稳定。试想一下,图像K = 1,我们有一个查询点,周围有几个红色和一个绿色(我在考虑上面彩色图的左上角),但是绿色是唯一最近的邻居。合理地说,我们会认为查询点很可能是红色的,但是因为K = 1,KNN错误地预测查询点是绿色的。


相反,随着K值的增加,由于多数投票/平均,我们的预测变得更加稳定,因此更有可能做出更准确的预测(直到某一点)。最终,我们开始看到越来越多的错误。正是在这一点上,我们知道我们把K的价值推得太远了。


如果我们在标签中进行多数投票(例如,在分类问题中选择模式),我们通常会将K设为奇数,以便有一个决胜局。

 

优势


该算法简单易行。
没有必要建立模型,调整多个参数,或者做额外的假设。
该算法是通用的。它可以用于分类、回归和搜索(我们将在下一节中看到)。

缺点
随着示例和/或预测器/独立变量数量的增加,算法变得非常慢。

 

 

  KNN在实践中


KNN的主要缺点是随着数据量的增加变得非常慢,这使得在需要快速做出预测的环境中,变成了一个不切实际的选择。此外,有更快的算法可以产生更准确的分类和回归结果。


然而,如果你有足够的计算资源来快速处理你用来预测的数据,KNN仍然有助于解决那些有依赖于识别相似对象的解决方案的问题。这方面的一个例子是在推荐系统中使用KNN算法,这是KNN搜索的应用。

 

推荐系统


从规模上看,这就像是在亚马逊上推荐产品,在媒体上推荐文章,在Netflix上推荐电影,或者在YouTube上推荐视频。尽管如此,我们可以肯定,由于他们处理的数据量巨大,他们都使用了更有效的方法来提出推荐。


然而,我们可以使用本文中所学的知识,以较小的规模复制其中一个推荐系统。那么让我们构建一个电影推荐系统的核心。

我们想回答什么问题?


给定我们的电影数据集,与电影查询最相似的5部电影是什么?

 

收集电影数据


如果我们在Netflix、Hulu或IMDb工作,我们可以从他们的数据仓库中获取数据。但因为我们不在这些公司工作,所以我们必须通过其他方式获取数据。我们可以使用来自UCI机器学习库、IMDb数据集的一些电影数据,或者费力地创建我们自己的数据。

探索、清理和准备数据
无论我们从哪里获得数据,都可能存在一些问题,我们需要纠正这些问题,为KNN算法做准备。例如,数据可能不是算法期望的格式,或者在将数据送入算法之前,可能缺少数据,故我们应该填充或删除它们。


我们上面的KNN实现依赖于结构化数据。它需要表格格式。此外,该实现假设所有列都包含数字数据,并且我们数据的最后一列具有可以对其执行某些功能的标签。因此,无论我们从哪里获得数据,我们都需要使其符合这些约束。


下面的数据是我们清理过的数据可能类似的一个例子。该数据包含30部电影,包括七种类型的每部电影的数据及其IMDB评级。标签的列全为零,因为我们没有使用这个数据集进行分类或回归。


 


自制电影推荐数据集


此外,在使用KNN算法时,电影之间有一些关系不会被考虑(例如演员、导演和主题),这仅仅是因为数据集中缺少捕捉这些关系的数据。因此,当我们对我们的数据运行KNN算法时,相似性将完全基于包括的类型和电影的IMDB评级。


使用算法


想象一下。我们正在浏览MoviesXb网站,这是一个虚构的IMDb衍生产品,我们遇到了《邮报》。我们不确定是否想看它,但是它的类型吸引了我们;我们对其他类似的电影很好奇。我们向下滚动到“更像这个”部分,看看MoviesXb会提出什么建议,算法齿轮开始转动。


MoviesXb网站向其后端发送了一个请求,要求获得5部最类似《华盛顿邮报》的电影。后端有一个与我们完全一样的推荐数据集。它首先为《华盛顿邮报》创建行表示(被广泛称为特征向量),然后运行类似于下面的程序来搜索与《华盛顿邮报》最相似的5部电影,最后将结果发送回MoviesXb网站。


from knn_from_scratch import knn, euclidean_distance   
def recommend_movies(movie_query, k_recommendations):   
raw_movies_data = []   
with open('movies_recommendation_data.csv', 'r') as md:   
# Discard the first line (headings)   
next(md)   
# Read the data into memory   
for line in md.readlines():   
data_row = line.strip().split(',')   
raw_movies_data.append(data_row)   
# Prepare the data for use in the knn algorithm by picking   
# the relevant columns and converting the numeric columns   
# to numbers since they were read in as strings   
movies_recommendation_data = []   
for row in raw_movies_data:   
data_row = list(map(float, row[2:]))   
movies_recommendation_data.append(data_row)   
# Use the KNN algorithm to get the 5 movies that are most   
# similar to The Post.   
recommendation_indices, _ = knn(   
movies_recommendation_data, movie_query, k=k_recommendations,   
distance_fn=euclidean_distance, choice_fn=lambda x: None   
)   
movie_recommendations = []   
for _, index in recommendation_indices:   
movie_recommendations.append(raw_movies_data[index])   
return movie_recommendations   
if __name__ == '__main__':   
the_post = [7.2, 1, 1, 0, 0, 0, 0, 1, 0] # feature vector for The Post   
recommended_movies = recommend_movies(movie_query=the_post, k_recommendations=5)   
# Print recommended movie titles   
for recommendation in recommended_movies:   
print(recommendation[1])


当我们运行这个程序时,我们看到MoviesXb推荐了《为奴十二年》,《钢锯岭》,《卡推女王》,《起风了》,还有《美丽心灵》。既然我们完全理解KNN算法是如何工作的,我们就能够准确地解释KNN算法是如何提出这些建议的。恭喜你!

 

  总结


k最近邻( KNN )算法是一种简单的有监督的机器学习算法,可用于解决分类和回归问题。这很容易实现和理解,但是有一个主要缺点,就是随着使用中数据的大小增加速度会明显变慢。


KNN通过查找查询和数据中所有示例之间的距离来工作,选择最接近查询的指定数字示例( K ),然后选择最常用的标签(在分类的情况下)或平均标签(在回归的情况下)。


在分类和回归的情况下,我们看到为我们的数据选择正确的K是通过尝试几个K并选择最有效的一个来完成的。


最后,我们看了一个KNN算法如何应用于推荐系统的例子,这是KNN搜索的一个应用。
 


KNN就像.....“让我看看你的朋友,我来告诉你谁是。”

 

  附录
[1]为了简单起见,本文中实现的KNN电影推荐器不处理电影查询有可能是推荐数据集的一部分的情况。 在生产系统中可能是不合理的,应该处理相同的问题。


如果你学到了一个新东西或者喜欢这篇文章,请点赞并且分享它,这样可以让更多的人看到。你可以自由地留下你的评论。

 

 

2018-07-16 17:07:10 gao8658 阅读数 146
  • 【Python】第16周 数据挖掘与机器学习进阶(上)

    本周课程讲机器学习的概念,了解什么是机器学习以及机器学习的主要任务,kNN算法、线性回归算法的原理与实现,如何拆分训练数据集与测试数据集,算法中的超参数以及特征归一化等内容。实战部份,将讲解手写字体识别和房价预测。

    616 人正在学习 去看看 CSDN就业班

(1) KNN算法解决什么问题?

KNN是一种机器学习算法,可以解决下面问题:

  • 分类问题
  • 回归问题
  • 离群点检测

 

(2) KNN算法流程

  • 计算测试数据与每个训练数据之间的距离;
  • 按照距离的由小到大进行排序;
  • 选取距离最小的K个点;
  • 确定前K个点所在类别的出现频率;(回归问题求K个点的均值)
  • 返回前K个点中出现频率最高的类别作为测试数据的预测分类。

Image result for KNN algorithm

(3) KNN算法实现

网络上有很多种的实现方式。

参考:https://www.cnblogs.com/ybjourney/p/4702562.html

(4) KNN拓展点

  • 如何选K值
  • 距离度量方式
  • 如何快速检索向量

(5) 距离度量

下面是几种常见的距离度量公式:

Image result for KNN algorithm

参考:KNN算法中常用的距离计算公式 https://blog.csdn.net/sanqima/article/details/51276640

(6) 如何快速检索向量?

6.1 KNN优化-KD Tree

Image result for KD tree

参考:kd-tree : k近邻查询和范围查询 https://blog.csdn.net/dylan_frank/article/details/77934272

6.2 KNN优化-Ball Tree

为何引入?

为了改进KDtree的二叉树树形结构,并且沿着笛卡尔坐标进行划分的低效率,ball tree将在一系列嵌套的超球体上分割数据。也就是说:使用超球面而不是超矩形划分区域。虽然在构建数据结构的花费上大过于KDtree,但是在高维甚至很高维的数据上都表现的很高效。

Image result for Ball Tree

参考:https://en.wikipedia.org/wiki/Ball_tree

(7) 如何选择K

依赖数据,可以通过参数调优进行K值选择。一般来说,较大的k会抑制噪声的影响,但会使分类边界变得不那么明显。

参考:https://www.quora.com/How-can-I-choose-the-best-K-in-KNN-K-nearest-neighbour-classification

 

友情推荐:ABC技术研习社

为技术人打造的专属A(AI),B(Big Data),C(Cloud)技术公众号和技术交流社群。

2019-09-03 17:44:24 bingozb 阅读数 202
  • 【Python】第16周 数据挖掘与机器学习进阶(上)

    本周课程讲机器学习的概念,了解什么是机器学习以及机器学习的主要任务,kNN算法、线性回归算法的原理与实现,如何拆分训练数据集与测试数据集,算法中的超参数以及特征归一化等内容。实战部份,将讲解手写字体识别和房价预测。

    616 人正在学习 去看看 CSDN就业班
2019-04-23 15:14:55 Mind_programmonkey 阅读数 776
  • 【Python】第16周 数据挖掘与机器学习进阶(上)

    本周课程讲机器学习的概念,了解什么是机器学习以及机器学习的主要任务,kNN算法、线性回归算法的原理与实现,如何拆分训练数据集与测试数据集,算法中的超参数以及特征归一化等内容。实战部份,将讲解手写字体识别和房价预测。

    616 人正在学习 去看看 CSDN就业班

KNN算法的学习

KNN的英文叫K-Nearest Neighbor,比较简单。
github链接:github

一、简单的例子

首先我们先从一个简单的例子入手,来体会一下KNN算法。

假设,我们想对电影的类型进行分类,统计了电影中打斗的次数、接吻的次数,当然还有其他的指标也可以统计到,这里就不列举了,如下表所示:

我们很容易的理解《战狼》《红海行动》《碟中谍6》是动作片,《前任三》《春娇救志明》《泰塔尼克号》是爱情片,但是有没有一种办法让机器也可以掌握这个分类的规则呢?当有一部新电影的时候,也可以对它的类型自动分类呢?

这里我们可以把打斗次数看成x轴,接吻次数看成y轴,然后在二维的坐标轴上,对这几部电影进行标记。

如下图所示,对于未知的电影A,坐标为(x),我们需要看下里离电影A最近的都有哪些电影,这些电影中的大多数属于哪个分类,那么电影A就属于哪个分类。

而在实际操作中,我们还需要确定一个K值,也就是我们需要观察离电影A最近的电影有多少个。

代码实现
# 导入包
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# film_train_data表示电影数据 film_train_labels表示标签
film_data =[[100,5],
            [95,3],
            [105,31],
            [2,59],
            [3,60],
            [10,80]]
film_labels = [0,0,0,1,1,1]

film_train_data = np.array(film_data)
film_train_labels = np.array(film_labels)
# 在图中展示
plt.scatter(film_train_data[film_train_labels==0,0],film_train_data[film_train_labels==0,1],color="g")

plt.scatter(film_train_data[film_train_labels==1,0],film_train_data[film_train_labels==1,1],color="r")
<matplotlib.collections.PathCollection at 0x225163dd4e0>

在这里插入图片描述

# 添加电影A
film_data_A = np.array([5,70])

# 一并在图中展示
plt.scatter(film_train_data[film_train_labels==0,0],film_train_data[film_train_labels==0,1],color="g")
plt.scatter(film_train_data[film_train_labels==1,0],film_train_data[film_train_labels==1,1],color="r")
plt.scatter(film_data_A[0],film_data_A[1],color="b")
<matplotlib.collections.PathCollection at 0x22516488198>

在这里插入图片描述

KNN的工作原理

“近朱者赤,近墨者黑”可以说是KNN的工作原理。整个计算过程分为三步:

1.计算待分类物体与其他物体之间的距离;

2.统计路基最近的k个邻居

3.对于k个最近的邻居,它们属于哪个分类最多,待分类物体就属于哪一类。

KNN的选择

我们能看出整个KNN的分类过程,K值的选择还是很重要的,那么问题来了,K值选择多少是适合的呢?

如果K值比较小,就相当于未分类物体与它的邻居分厂接近才行。这样产生的一个问题就是,如果邻居点是个噪声点,那么未分类物体的分类也会产生误差, 这样KNN分类就会产生过拟合。

如果K值比较大,相当于距离过远的点也会对未知物体的分类产生影响,虽然这种情况的好处是鲁棒性强,但是不足也很明显,会产生欠拟合情况,也就是没有把未分类物体真正分类出来。

所以K值应该是个实践出来的结果,并不是我们事先而定的。在工程上,我们一般采用交叉验证的方式选取K值。

交叉验证的思路就是,把样本集中的大部分样本作为训练集,剩余的小部分样本用于预测,来验证分类模型的准确性。所以在KNN算法中,我们一般会把K值选取在较小的范围内,同时在验证集中准确率最高的那一个最终确定作为K值。

距离如何计算

在KNN算法中,还有一个重要的计算就是关于距离的度量,两个样本点之间的距离代表了这两个样本之间的相似度。距离越大,差异性越大;距离越小,相似度越大。

关于距离的计算方式有下面五种方式:

1.欧式距离;

2.曼哈顿距离;

3.闵可夫斯基距离;

4.切比雪夫距离;

5.余弦距离。

1 欧式距离

欧式距离是我们最常用的距离公式,也叫作欧几里距离。在二维空间中,两点的欧式距离就是:

在三维空间中,两点的欧氏距离是:

同理,我们推出在n维空间中两个点之间的欧式距离是:

2 曼哈顿距离

曼哈顿距离在几何空间中用的比较多,以下图为例,绿色的直线代表两点之间的欧式距离,而红色和黄色的线为两点的曼哈顿距离,所以曼哈顿距离等于两个点在坐标系上绝对轴距总和,用公式表示就是:

d( i , j )= | xi - xj | + | yi - yj |
3 闵可夫斯基距离

闵可夫斯基不是一个距离,而是一组距离的定义。在n位向量空间中a(x11,x12,…,x1n)与b(x21,x22,…,x2n)间的闵可夫斯基距离定义为:

其中p代表空间的维数,当p=1时,就是曼哈顿距离;当p=2时,就是欧式距离;当p->∞,就是切比雪夫距离。

4 切比雪夫距离

切比雪夫距离是怎么计算的呢?两点之间的切比雪夫距离就是这两个点坐标数值差的绝对值的最大值,用数学表示就是:

max( | x1 - x2 | , | y1 - y2 | )
5 余弦距离

余弦距离实际上计算的是两个向量的夹角,是在方向上计算两者之间的差异,对绝对值不敏感。在兴趣相关性比较上,角度关系比距离的绝对值更重要,因此余弦距离可以用于衡量用户对内容兴趣的区分度。比如我们用搜索引擎搜索某个关键词,它还会给你推荐其他的相关搜素,这些推荐的关键词就是采用余弦距离计算得出的。

KNN的扩展内容
a.KD树

其实从上文我们可以看到,KNN的计算过程就是大量计算样本点之间的距离。为了减少计算距离次数,提升KNN的搜素效率,人们提出了KD树(K-dimensional的缩写)。KD树是对数据点在K维空间中划分的一种数据结构。在KD树的构造中,每个节点都是k维数值点的二叉树。既然是二叉树,就可以采用二叉树的增删改查,这样就大大提升了搜索效率。

其实,我们并不需要对KD树的数学原理了解太多,只需要知道它是一个二叉树的数据结构,方便存储K维空间的数据即可,在sklearn我们可以直接调用KD树,很方便。

b.KNN做回归

KNN不仅可以做分类,还可以做回归。

首先说下什么是回归。在开头电影这个案例中,如果想要对未知电影进行类型划分,这是一个分类问题。首先看一下要分类的未知电影,离它最近的K部电影大多数属于哪个分类,这部电影就属于哪个分类。

那么如果是一部新电影,已知它是爱情片,想要知道它的打斗次数、接吻次数可能是多少,这就是一个回归问题。

那么KNN如何做回归呢?

对于一个新点,我们需要找出这个点的K个最近邻居,然后将这些邻居的属性的平均值点赋给该点,就可以得到该点的属性。当然不同邻居的影响力权重可以设置不同的。举个例子,比如一部电影A,已知它是动作片,当K=3时,最近的3部电影是《战狼》,《红海行动》和《碟中谍6》,那么它的打斗次数和接吻次数的预估值分别为(100+95+105)/3=100次,(5+3+31)/3=13次

KNN的过程

继续刚才那个小例子,首先计算电影A到所有训练电影的距离

from math import sqrt

distance = []
for  film_data in film_train_data:
    d = sqrt(np.sum((film_data_A-film_data)**2))
    distance.append(d)

distance
[115.10864433221339,
 112.2007130102122,
 107.33592129385204,
 11.40175425099138,
 10.198039027185569,
 11.180339887498949]

对其下标进行排序输出

nearest = np.argsort(distance)
nearest
array([4, 5, 3, 2, 1, 0], dtype=int64)

选取k值,这里选取k=3

k = 3

从标签中选取k个最近的标签

topK_labels = [film_train_labels[i] for i in nearest[:k]]
topK_labels
[1, 1, 1]

统计最近的标签的次数

from collections import Counter
votes = Counter(topK_labels)
votes.most_common(1)
[(1, 3)]

输出电影A的预测值

predict_labels = votes.most_common(1)[0][0]
predict_labels
1
KNN算法封装
import numpy as np
from math import sqrt
from collections import Counter

def accuracy_score(y_true, y_predict):
    """计算y_true和y_predict之间的准确率"""
    assert len(y_true) == len(y_predict), \
        "the size of y_true must be equal to the size of y_predict"

    return np.sum(y_true == y_predict) / len(y_true)

class KNNClassifier:

    def __init__(self, k):
        """初始化kNN分类器"""
        assert k >= 1, "k must be valid"
        self.k = k
        self._x_train = None
        self._y_train = None

    def fit(self, x_train, y_train):
        """根据训练数据集X_train和y_train训练kNN分类器"""
        assert x_train.shape[0] == y_train.shape[0], \
            "the size of x_train must be equal to the size of y_train"
        assert self.k <= x_train.shape[0], \
            "the size of x_train must be at least k."

        self._x_train = x_train
        self._y_train = y_train
        return self

    def predict(self, x_predict):
        """给定待预测数据集X_predict,返回表示X_predict的结果向量"""
        assert self._x_train is not None and self._y_train is not None, \
                "must fit before predict!"
        assert x_predict.shape[1] == self._x_train.shape[1], \
                "the feature number of x_predict must be equal to x_train"

        y_predict = [self._predict(x) for x in x_predict]
        return np.array(y_predict)

    def _predict(self, x):
        """给定单个待预测数据x,返回x的预测结果值"""
        assert x.shape[0] == self._x_train.shape[1], \
            "the feature number of x must be equal to x_train"

        distances = [sqrt(np.sum((x_train - x) ** 2))
                     for x_train in self._x_train]
        nearest = np.argsort(distances)

        topK_y = [self._y_train[i] for i in nearest[:self.k]]
        votes = Counter(topK_y)

        return votes.most_common(1)[0][0]

    def score(self, x_test, y_test):
        """根据测试数据集 x_test 和 y_test 确定当前模型的准确度"""

        y_predict = self.predict(x_test)
        return accuracy_score(y_test, y_predict)

    def __repr__(self):
        return "KNN(k=%d)" % self.k

调用自己写好的KNN实现上面的小例子

knn_clf = KNNClassifier(k=3)
knn_clf.fit(film_train_data,film_train_labels)
# 将其转换为二维数据
film_data_A = film_data_A.reshape(1,-1)
predict_labels = knn_clf.predict(film_data_A)
predict_labels[0]
1

二、使用KNN对鸢尾花数据进行分类识别

# 导包
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
加载鸢尾花数据及对数据的探索
iris = datasets.load_iris()

查看鸢尾花数据的属性

iris.keys()
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])

查看对鸢尾花数据的描述

print(iris.DESCR)
.. _iris_dataset:

Iris plants dataset
--------------------

**Data Set Characteristics:**

    :Number of Instances: 150 (50 in each of three classes)
    :Number of Attributes: 4 numeric, predictive attributes and the class
    :Attribute Information:
        - sepal length in cm
        - sepal width in cm
        - petal length in cm
        - petal width in cm
        - class:
                - Iris-Setosa
                - Iris-Versicolour
                - Iris-Virginica
                
    :Summary Statistics:

    ============== ==== ==== ======= ===== ====================
                    Min  Max   Mean    SD   Class Correlation
    ============== ==== ==== ======= ===== ====================
    sepal length:   4.3  7.9   5.84   0.83    0.7826
    sepal width:    2.0  4.4   3.05   0.43   -0.4194
    petal length:   1.0  6.9   3.76   1.76    0.9490  (high!)
    petal width:    0.1  2.5   1.20   0.76    0.9565  (high!)
    ============== ==== ==== ======= ===== ====================

    :Missing Attribute Values: None
    :Class Distribution: 33.3% for each of 3 classes.
    :Creator: R.A. Fisher
    :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
    :Date: July, 1988

The famous Iris database, first used by Sir R.A. Fisher. The dataset is taken
from Fisher's paper. Note that it's the same as in R, but not as in the UCI
Machine Learning Repository, which has two wrong data points.

This is perhaps the best known database to be found in the
pattern recognition literature.  Fisher's paper is a classic in the field and
is referenced frequently to this day.  (See Duda & Hart, for example.)  The
data set contains 3 classes of 50 instances each, where each class refers to a
type of iris plant.  One class is linearly separable from the other 2; the
latter are NOT linearly separable from each other.

.. topic:: References

   - Fisher, R.A. "The use of multiple measurements in taxonomic problems"
     Annual Eugenics, 7, Part II, 179-188 (1936); also in "Contributions to
     Mathematical Statistics" (John Wiley, NY, 1950).
   - Duda, R.O., & Hart, P.E. (1973) Pattern Classification and Scene Analysis.
     (Q327.D83) John Wiley & Sons.  ISBN 0-471-22361-1.  See page 218.
   - Dasarathy, B.V. (1980) "Nosing Around the Neighborhood: A New System
     Structure and Classification Rule for Recognition in Partially Exposed
     Environments".  IEEE Transactions on Pattern Analysis and Machine
     Intelligence, Vol. PAMI-2, No. 1, 67-71.
   - Gates, G.W. (1972) "The Reduced Nearest Neighbor Rule".  IEEE Transactions
     on Information Theory, May 1972, 431-433.
   - See also: 1988 MLC Proceedings, 54-64.  Cheeseman et al"s AUTOCLASS II
     conceptual clustering system finds 3 classes in the data.
   - Many, many more ...

查看鸢尾花数据

iris.data
array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2],
       [4.7, 3.2, 1.3, 0.2],
       [4.6, 3.1, 1.5, 0.2],
       [5. , 3.6, 1.4, 0.2],
       [5.4, 3.9, 1.7, 0.4],
       [4.6, 3.4, 1.4, 0.3],
       [5. , 3.4, 1.5, 0.2],
       [4.4, 2.9, 1.4, 0.2],
       [4.9, 3.1, 1.5, 0.1],
       [5.4, 3.7, 1.5, 0.2],
       [4.8, 3.4, 1.6, 0.2],
       [4.8, 3. , 1.4, 0.1],
       [4.3, 3. , 1.1, 0.1],
       [5.8, 4. , 1.2, 0.2],
       [5.7, 4.4, 1.5, 0.4],
       [5.4, 3.9, 1.3, 0.4],
       [5.1, 3.5, 1.4, 0.3],
       [5.7, 3.8, 1.7, 0.3],
       [5.1, 3.8, 1.5, 0.3],
       [5.4, 3.4, 1.7, 0.2],
       [5.1, 3.7, 1.5, 0.4],
       [4.6, 3.6, 1. , 0.2],
       [5.1, 3.3, 1.7, 0.5],
       [4.8, 3.4, 1.9, 0.2],
       [5. , 3. , 1.6, 0.2],
       [5. , 3.4, 1.6, 0.4],
       [5.2, 3.5, 1.5, 0.2],
       [5.2, 3.4, 1.4, 0.2],
       [4.7, 3.2, 1.6, 0.2],
       [4.8, 3.1, 1.6, 0.2],
       [5.4, 3.4, 1.5, 0.4],
       [5.2, 4.1, 1.5, 0.1],
       [5.5, 4.2, 1.4, 0.2],
       [4.9, 3.1, 1.5, 0.2],
       [5. , 3.2, 1.2, 0.2],
       [5.5, 3.5, 1.3, 0.2],
       [4.9, 3.6, 1.4, 0.1],
       [4.4, 3. , 1.3, 0.2],
       [5.1, 3.4, 1.5, 0.2],
       [5. , 3.5, 1.3, 0.3],
       [4.5, 2.3, 1.3, 0.3],
       [4.4, 3.2, 1.3, 0.2],
       [5. , 3.5, 1.6, 0.6],
       [5.1, 3.8, 1.9, 0.4],
       [4.8, 3. , 1.4, 0.3],
       [5.1, 3.8, 1.6, 0.2],
       [4.6, 3.2, 1.4, 0.2],
       [5.3, 3.7, 1.5, 0.2],
       [5. , 3.3, 1.4, 0.2],
       [7. , 3.2, 4.7, 1.4],
       [6.4, 3.2, 4.5, 1.5],
       [6.9, 3.1, 4.9, 1.5],
       [5.5, 2.3, 4. , 1.3],
       [6.5, 2.8, 4.6, 1.5],
       [5.7, 2.8, 4.5, 1.3],
       [6.3, 3.3, 4.7, 1.6],
       [4.9, 2.4, 3.3, 1. ],
       [6.6, 2.9, 4.6, 1.3],
       [5.2, 2.7, 3.9, 1.4],
       [5. , 2. , 3.5, 1. ],
       [5.9, 3. , 4.2, 1.5],
       [6. , 2.2, 4. , 1. ],
       [6.1, 2.9, 4.7, 1.4],
       [5.6, 2.9, 3.6, 1.3],
       [6.7, 3.1, 4.4, 1.4],
       [5.6, 3. , 4.5, 1.5],
       [5.8, 2.7, 4.1, 1. ],
       [6.2, 2.2, 4.5, 1.5],
       [5.6, 2.5, 3.9, 1.1],
       [5.9, 3.2, 4.8, 1.8],
       [6.1, 2.8, 4. , 1.3],
       [6.3, 2.5, 4.9, 1.5],
       [6.1, 2.8, 4.7, 1.2],
       [6.4, 2.9, 4.3, 1.3],
       [6.6, 3. , 4.4, 1.4],
       [6.8, 2.8, 4.8, 1.4],
       [6.7, 3. , 5. , 1.7],
       [6. , 2.9, 4.5, 1.5],
       [5.7, 2.6, 3.5, 1. ],
       [5.5, 2.4, 3.8, 1.1],
       [5.5, 2.4, 3.7, 1. ],
       [5.8, 2.7, 3.9, 1.2],
       [6. , 2.7, 5.1, 1.6],
       [5.4, 3. , 4.5, 1.5],
       [6. , 3.4, 4.5, 1.6],
       [6.7, 3.1, 4.7, 1.5],
       [6.3, 2.3, 4.4, 1.3],
       [5.6, 3. , 4.1, 1.3],
       [5.5, 2.5, 4. , 1.3],
       [5.5, 2.6, 4.4, 1.2],
       [6.1, 3. , 4.6, 1.4],
       [5.8, 2.6, 4. , 1.2],
       [5. , 2.3, 3.3, 1. ],
       [5.6, 2.7, 4.2, 1.3],
       [5.7, 3. , 4.2, 1.2],
       [5.7, 2.9, 4.2, 1.3],
       [6.2, 2.9, 4.3, 1.3],
       [5.1, 2.5, 3. , 1.1],
       [5.7, 2.8, 4.1, 1.3],
       [6.3, 3.3, 6. , 2.5],
       [5.8, 2.7, 5.1, 1.9],
       [7.1, 3. , 5.9, 2.1],
       [6.3, 2.9, 5.6, 1.8],
       [6.5, 3. , 5.8, 2.2],
       [7.6, 3. , 6.6, 2.1],
       [4.9, 2.5, 4.5, 1.7],
       [7.3, 2.9, 6.3, 1.8],
       [6.7, 2.5, 5.8, 1.8],
       [7.2, 3.6, 6.1, 2.5],
       [6.5, 3.2, 5.1, 2. ],
       [6.4, 2.7, 5.3, 1.9],
       [6.8, 3. , 5.5, 2.1],
       [5.7, 2.5, 5. , 2. ],
       [5.8, 2.8, 5.1, 2.4],
       [6.4, 3.2, 5.3, 2.3],
       [6.5, 3. , 5.5, 1.8],
       [7.7, 3.8, 6.7, 2.2],
       [7.7, 2.6, 6.9, 2.3],
       [6. , 2.2, 5. , 1.5],
       [6.9, 3.2, 5.7, 2.3],
       [5.6, 2.8, 4.9, 2. ],
       [7.7, 2.8, 6.7, 2. ],
       [6.3, 2.7, 4.9, 1.8],
       [6.7, 3.3, 5.7, 2.1],
       [7.2, 3.2, 6. , 1.8],
       [6.2, 2.8, 4.8, 1.8],
       [6.1, 3. , 4.9, 1.8],
       [6.4, 2.8, 5.6, 2.1],
       [7.2, 3. , 5.8, 1.6],
       [7.4, 2.8, 6.1, 1.9],
       [7.9, 3.8, 6.4, 2. ],
       [6.4, 2.8, 5.6, 2.2],
       [6.3, 2.8, 5.1, 1.5],
       [6.1, 2.6, 5.6, 1.4],
       [7.7, 3. , 6.1, 2.3],
       [6.3, 3.4, 5.6, 2.4],
       [6.4, 3.1, 5.5, 1.8],
       [6. , 3. , 4.8, 1.8],
       [6.9, 3.1, 5.4, 2.1],
       [6.7, 3.1, 5.6, 2.4],
       [6.9, 3.1, 5.1, 2.3],
       [5.8, 2.7, 5.1, 1.9],
       [6.8, 3.2, 5.9, 2.3],
       [6.7, 3.3, 5.7, 2.5],
       [6.7, 3. , 5.2, 2.3],
       [6.3, 2.5, 5. , 1.9],
       [6.5, 3. , 5.2, 2. ],
       [6.2, 3.4, 5.4, 2.3],
       [5.9, 3. , 5.1, 1.8]])

查看鸢尾花数据的维度

iris.data.shape
(150, 4)

查看鸢尾花的特征

iris.feature_names
['sepal length (cm)',
 'sepal width (cm)',
 'petal length (cm)',
 'petal width (cm)']

查看鸢尾花的的标签数据

iris.target
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

查看鸢尾花的标签名字

iris.target_names
array(['setosa', 'versicolor', 'virginica'], dtype='<U10')

在图中展示鸢尾花数据,先加载鸢尾花前两列的特征

# 加载鸢尾花的前两列数据
data =  iris.data[:,:2]
# 加载鸢尾花的标签数据
labels = iris.target
plt.scatter(data[labels==0,0],data[labels==0,1],color="red",marker="o")
plt.scatter(data[labels==1,0],data[labels==1,1],color="blue",marker="+")
plt.scatter(data[labels==2,0],data[labels==2,1],color="green",marker="x")
<matplotlib.collections.PathCollection at 0x225179969b0>

在这里插入图片描述

在图中展示鸢尾花数据,加载鸢尾花后两列的特征

data = iris.data[:,2:]
labels = iris.target
plt.scatter(data[labels==0,0],data[labels==0,1],color="red",marker="o")
plt.scatter(data[labels==1,0],data[labels==1,1],color="blue",marker="+")
plt.scatter(data[labels==2,0],data[labels==2,1],color="green",marker="x")
<matplotlib.collections.PathCollection at 0x225189c3f60>

在这里插入图片描述

对数据进行切分

将数据划分为训练数据和测试数据

data = iris.data
shuffle_indexs = np.random.permutation(len(data))
shuffle_indexs
array([ 22,  94, 131,  15,  99,  62,  68,  89, 113, 114, 146, 128, 139,
        38,  50,  95,  70,  91, 123,  49, 138,  57, 117, 136,  58, 132,
        25,  60, 142,  77,  98, 141, 144,  61, 119,  40,  75,  35,   7,
        97,  16, 124,  83, 120,   6, 127,  87,  41,   0, 102, 110,  66,
       107,  84,  29,  18, 101,  21,  72, 121,  33,  14, 115,  63, 147,
        20, 116, 111,  93, 108,  52,  69, 105,  82,  39, 118,  47,  86,
        85, 137,  31,  27,  28, 140, 106,  46, 130,  80,  73,  55,  92,
        19,  88,  10, 112,  24,  36,  78,  65,  79,  74, 143, 129,  71,
       126,   9,  59,  44,   5,  45,  37,   4,  30, 125,  56,  43,  11,
       133,  51, 122, 148,  13,  81, 103, 100, 135,   3,  34,  54,  67,
        26,  53,   1,  90,  48,  32,   8,  76,  12,   2, 145,  23,  42,
       104,  64,  17, 109, 134,  96, 149])
test_ratio = 0.2
test_size = int(len(data)*test_ratio)
test_indexs = shuffle_indexs[:test_size]
train_indexs = shuffle_indexs[test_size:]

x_test = data[test_indexs]
y_test = labels[test_indexs]
x_train = data[train_indexs]
y_train = labels[train_indexs]

print(x_test.shape)
print(y_test.shape)
print(x_train.shape)
print(y_train.shape)
(30, 4)
(30,)
(120, 4)
(120,)
封装的切割函数
def train_test_split(X,y,test_ratio=0.2,seed=None):
    """将数据X和y按照test_ratio分割成X_train,X_test,y_train,y_test"""
    assert X.shape[0] == y.shape[0],"the size of X must be equal to the size of y"
    assert 0.0 <=test_ratio<=1.0,"test_ratio must be valid"
    
    if seed:
        np.random.seed(seed)
    
    shuffle_indexs = np.random.permutation(len(X))
    
    test_size = int(len(X)*test_ratio)
    test_indexs = shuffle_indexs[:test_size]
    train_indexs = shuffle_indexs[test_size:]
    
    X_train = X[train_indexs]
    y_train = y[train_indexs]
    
    X_test = X[test_indexs]
    y_test = y[test_indexs]
    
    return X_train,X_test,y_train,y_test

对data数据分成训练数据和测试数据

x_train,x_test,y_train,y_test = train_test_split(data,labels)

创建一个knn分类器

my_knn_clf = KNNClassifier(k=3)

对knn分类器进行训练

my_knn_clf.fit(x_train,y_train)
KNN(k=3)

对测试数据进行预测

y_predict = my_knn_clf.predict(x_test)
y_predict
array([1, 2, 0, 1, 1, 1, 2, 2, 0, 0, 2, 0, 1, 0, 0, 2, 2, 0, 0, 1, 0, 0,
       1, 1, 2, 2, 0, 1, 0, 0])

与测试标签进行对比

y_test
array([1, 2, 0, 1, 1, 1, 2, 2, 0, 0, 2, 0, 1, 0, 0, 2, 2, 0, 0, 1, 0, 0,
       1, 1, 2, 2, 0, 1, 0, 0])

计算预测百分比

sum(y_predict==y_test)/len(x_test)
1.0

三、KNN对手写数字进行识别

这里将直接调用sklearn调用KNN算法、使用sklearn中自带的手写数字数据集进行实战。

在sklearn中使用KNN

在Python的sklearn的工具包中有KNN算法。KNN既可以做分类器,也可以做回归。如果是做分类,你需要引用;

from sklearn.neighbors import KNeighborsClassifier

如果是做回归,那么你需要引用:

from sklearn.neighbors import KNeighborsRegressor

这里,我们看下如何在sklearn中创建KNN分类器:

使用构造函数KNeighborsClassifier(n_neighbors=5, weights=‘uniform’, algorithm=‘auto’, leaf_size=30)

1.n_neighbors:及KNN中的k值,代表的是邻居的数量。k值如果比较小,会造成过拟合。如果k值比较大,无法将未知物体分类出来。一般我们使用默认值5

2.weights:用来确定邻居的权重,有三种方式:

  weights=uniform,代表所有邻居的权重相同      
  weights=distance,代表权重是距离的倒数,即与距离成反比
  自定义函数,你可以自定义不同距离所对应的权重。大部分情况下不需要自己定义函数。

3.algorithm:用来规定计算邻居的方式,它有四种方式:

   algorithm=auto,根据数据的情况自动选择适合的算法,默认情况选择auto
   algorithm=kd_tree,也叫作KD树,是多维空间的数据结构,方便对关键数据进行检索,不过KD树适用于维度少的情况,一般维数不超过20,如果维数大于20之后,效率反而会下降;
   algorithm=ball_tree,也叫作球树,它和KD树一样都是多维空间的数据结构,不同于KD树,球树更适用于维度大的情况;
   algorithm=brute,也叫作暴力搜索,它和KD数不同的地方是在于采用的是线性扫描,而不是通过构造树结构进行快速检索。当训练集大的时候,效率很低。

4.leaf_size:代表构造KD树或球树时的叶子数,默认是30,调整leaf_size会影响到树的构造和搜索速度。

总之,创建完KNN分类器之后,我们就可以输入训练集对它进行训练,这里使用fit()函数,传入训练集中的样本特征矩阵和分类标识,会自动得到训练好的KNN分类器。然后使用predict()函数来对结果进行预测,这里传入测试集的特征矩阵,可以得到测试集的预测分类结果。

knn对手写书写识别的整体流程
整体训练过程基本上都会包括三个阶段:

   1.数据加载:
   我们可以直接从sklearn中加载自带的手写数字数据集;
   
   2.准备阶段:在这个阶段中,我们需要对数据集有个初步的了解,比如样本的个数、图像长什么样,识别结果是怎么样的。这里我们可以通过可视化的方式来查看图像的呈现。通过数据规范化可以让数据都在同一个数量级的维度。另外,因为训练集是图像,每幅图像是8*8的矩阵,我们不需要对它进行特征选取,将全部的图像数据作为特征值矩阵即可。
   
   3.分类阶段:通过训练可以得到分类器,然后用测试集进行准确率的计算。
1.数据加载
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

digits = datasets.load_digits()
2.准备阶段
# 查看digits数据的属性值
digits.keys()
dict_keys(['data', 'target', 'target_names', 'images', 'DESCR'])
# 查看digits数据
data = digits.data
# 查看维度
print(data.shape)
# 查看前三行数据
print(data[:3])
(1797, 64)
[[ 0.  0.  5. 13.  9.  1.  0.  0.  0.  0. 13. 15. 10. 15.  5.  0.  0.  3.
  15.  2.  0. 11.  8.  0.  0.  4. 12.  0.  0.  8.  8.  0.  0.  5.  8.  0.
   0.  9.  8.  0.  0.  4. 11.  0.  1. 12.  7.  0.  0.  2. 14.  5. 10. 12.
   0.  0.  0.  0.  6. 13. 10.  0.  0.  0.]
 [ 0.  0.  0. 12. 13.  5.  0.  0.  0.  0.  0. 11. 16.  9.  0.  0.  0.  0.
   3. 15. 16.  6.  0.  0.  0.  7. 15. 16. 16.  2.  0.  0.  0.  0.  1. 16.
  16.  3.  0.  0.  0.  0.  1. 16. 16.  6.  0.  0.  0.  0.  1. 16. 16.  6.
   0.  0.  0.  0.  0. 11. 16. 10.  0.  0.]
 [ 0.  0.  0.  4. 15. 12.  0.  0.  0.  0.  3. 16. 15. 14.  0.  0.  0.  0.
   8. 13.  8. 16.  0.  0.  0.  0.  1.  6. 15. 11.  0.  0.  0.  1.  8. 13.
  15.  1.  0.  0.  0.  9. 16. 16.  5.  0.  0.  0.  0.  3. 13. 16. 16. 11.
   5.  0.  0.  0.  0.  3. 11. 16.  9.  0.]]
# 查看标签数据
labels = digits.target
# 查看维度
print(labels.shape)
# 查看前三十行数据
print(labels[:30])
(1797,)
[0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9]
# 查看图像
plt.imshow(digits.images[0])
print(digits.target[0])
0

在这里插入图片描述

对原始数据集中的第一幅图进行可视化,我们可以看到图像时一8*8的像素矩阵,从上面这幅图像看出这是一个"0",从训练集的分类标注中我们也可以看到分类标注为"0"

sklearn自带的手写数字数据集一共包括了1797个样本,每幅图像都是8*8像素的矩阵。因为我们并没有专门的测试集,所以我们需要对数据集做划分,划分成测试集和训练集。因为KNN算法和距离定义相关,所以我们还需要对数据进行规范化处理,采用Z-Score规范化。

from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# 分割数据,将25%的数据作为测试集,其余作为训练集
train_x,test_x,train_y,test_y = train_test_split(data,labels,test_size=0.2,random_state=111)

# 采用Z-Score规范化
standardScaler = StandardScaler()
train_ss_x = standardScaler.fit_transform(train_x)
test_ss_x = standardScaler.transform(test_x)

然后我们构造一个KNN分类器,把训练集的数据传入构造好的knn,并通过测试集进行结果预测,与测试集的结果进行对比,得到knn分类器准确率

knn_clf = KNeighborsClassifier()
knn_clf.fit(train_ss_x,train_y)
knn_clf.score(test_ss_x,test_y)
0.9694444444444444
Gird Search寻找最佳
from sklearn.model_selection import GridSearchCV
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

digits = datasets.load_digits()
data = digits.data
labels = digits.target

x_train,x_test,y_train,y_test = train_test_split(data,labels,test_size=0.2,random_state=666)

param_grid=[
    {
        "weights":["uniform"],
        "n_neighbors":[i for i in range(1,11)]
    },
    {
        "weights":["distance"],
        "n_neighbors":[i for i in range(1,11)],
        "p":[i for i in range(1,6)]
    }
] 

knn_clf = KNeighborsClassifier()
UsageError: Line magic function `%%time` not found.
%%time
grid_search = GridSearchCV(knn_clf,param_grid,n_jobs=-1,verbose=2)
grid_search.fit(x_train,y_train)
D:\software\Anaconda\workplace\lib\site-packages\sklearn\model_selection\_split.py:2053: FutureWarning: You should specify a value for 'cv' instead of relying on the default value. The default value will change from 3 to 5 in version 0.22.
  warnings.warn(CV_WARNING, FutureWarning)
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 4 concurrent workers.


Fitting 3 folds for each of 60 candidates, totalling 180 fits


[Parallel(n_jobs=-1)]: Done  33 tasks      | elapsed:    3.5s
[Parallel(n_jobs=-1)]: Done 154 tasks      | elapsed:   39.2s


Wall time: 50.6 s


[Parallel(n_jobs=-1)]: Done 180 out of 180 | elapsed:   50.5s finished
grid_search.best_params_
{'n_neighbors': 3, 'p': 3, 'weights': 'distance'}
grid_search.best_estimator_
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=None, n_neighbors=3, p=3,
           weights='distance')
grid_search.best_score_
0.9853862212943633

总结

在这里插入图片描述

没有更多推荐了,返回首页