机器学习实战之 -- kNN

k-近邻算法

L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 / p L_p(x_i,x_j) = (\sum_{l=1}^{n} |x_i^{(l)} - x_j^{(l)}|^p)^{1/p} Lp(xi,xj)=(l=1nxi(l)xj(l)p)1/p

  • l l l:维数。

一、工作原理

存在一个样本数据集合(训练样本集),并且样本集中每个数据都存在标签,即我们知道样本集中每一行数据与所属分类对应的关系。输入没有标签的新数数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据的分类标签。

  • 优点:精度高,对异常值不敏感、无数据输入假定。

  • 缺点:计算复杂度高、空间复杂度高。

  • 适用范围:数值型和标称型。

      一组样本数据:样本行 - 分类标签。
      新数据:没有标签,求差值提取标签。
    

二、核心代码

1.创建样本数据集和标签

import numpy as np
def createDataSet():
    '''
    Desc:
    创建样本数据集和标签列
    Return:
        group -- 数据集
        labels -- 标签列
    '''
    group = np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels = ['A','A','B','B']
    return group, labels

group = createDataSet()[0]
labels = createDataSet()[1]
print(group)
print(labels)
[[1.  1.1]
 [1.  1. ]
 [0.  0. ]
 [0.  0.1]]
['A', 'A', 'B', 'B']

2.利用kNN算法返回频率最高的元素标签

  • np.tile(A, n) :数组A重复n次,构成一个新的数组。
  • axis = 1 :按行计算;axis = 0:按列计算。
  • classCount.get(voteIlabel, 0):在这里的 ‘0’ 是自己设置的,当在classCount里找不到’voteIlabel’时返回 ‘0’。
import numpy as np
import operator
def classify0(inx, dataSet, labebls, k):
    '''
    Note:dataSet与labels的行数必须相同
    Arguments:
    inx -- 待分类向量(测试集)
    dataSet -- 训练数据集
    labels -- 训练集对应分类
    k -- 选取最邻近数据的个数
    
    Return:
    sortedClassCount[0][0] -- 返回频率最高的元素标签
    '''
    ### 1.计算待分类向量与训练数据集中每一个样本的欧氏距离
    
    # 训练集的行数(样本个数)
    dataSetSize = dataSet.shape[0]
    # 待分类向量和训练集中每个样本对应的特征值的差值,保持行数相同
    diffMat = np.tile(inx,(dataSetSize, 1)) - dataSet
   
    # 特征值差值的平方
    sqDiffMat = diffMat ** 2
    
    # 特征值差值的平方和
    sqDistances = sqDiffMat.sum(axis = 1)
    # 待分类向量与训练集中每个样本的欧式距离
    distances = sqDistances ** 0.5
    # 排序后,返回按升序排列的数据的索引
    sortedDistIndicies = distances.argsort()
    # 声明字典
    classCount = {}
    
    ### 2.选距离最近的k个值
    
    for i in range(k):# 筛选k个最近的值
        # k个最近距离对应的类别
        voteIlabel = labebls[sortedDistIndicies[i]]
        # 统计不同类别的个数,
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    
    ### 3.排序返回频率最高的类别
    
    # key=operator.itemgetter(1)根据字典的值进行排序
    # key=operator.itemgetter(0)根据字典的键进行排序
    sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse=True)
    print(sortedClassCount,'--选取的k个类别--')
    return sortedClassCount[0][0]
classify0([0.,0.],group,labels,3)
[('B', 2), ('A', 1)] --类别--
'B'

三、示例

1.准备数据:从文本中解析数据,输出训练样本矩阵和类标签向量。

def file2matrix(filename):
    '''
    Desc:
    将文本记录转换为Numpy的解析程序
    Arguments:
    filename -- 文本路径
    Return:
    returnMat -- 训练样本矩阵
    classLabelVector -- 类标签向量
    
    '''
    # 读取文件返回file对象
    fr = open(filename)
    # 按行读取返回数据行列表
    arrayOLines = fr.readlines()
    # 文件行数
    numberOfLines = len(arrayOLines)
    # 初始化样本矩阵
    returnMat = np.zeros((numberOfLines,3))
    
    # 分类标签向量
    classLabelVector = []
    # 行指针
    index = 0
    # 遍历每一个样本
    for line in arrayOLines:
        # 去掉回车符
        line = line.strip()
        # 去掉分隔符
        listFromLine = line.split('\t')
        # 将数据前三列提取出来,存放到returnMat的样本矩阵中
        returnMat[index,:] = listFromLine[0:3]
        # 提取每个样本中最后一列的类别标签
        # 注意类别的数据结构
        classLabelVector.append(int(listFromLine[-1]))
        # 指向下一行
        index += 1
    return returnMat, classLabelVector
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
print(datingDataMat)
print(datingLabels[0:10])
[[4.0920000e+04 8.3269760e+00 9.5395200e-01]
 [1.4488000e+04 7.1534690e+00 1.6739040e+00]
 [2.6052000e+04 1.4418710e+00 8.0512400e-01]
 ...
 [2.6575000e+04 1.0650102e+01 8.6662700e-01]
 [4.8111000e+04 9.1345280e+00 7.2804500e-01]
 [4.3757000e+04 7.8826010e+00 1.3324460e+00]]
[3, 2, 1, 1, 1, 1, 3, 3, 1, 3]

2.分析数据:使用Matplotlib绘制散点图

import matplotlib.pyplot as plt
%matplotlib inline
# 创建图像对象
fig = plt.figure()
# 将画布分为11列的格子,图像画在第一个格子上
ax = fig.add_subplot(111)
# 指定散点图中x,y轴的数据,点的大小,点的颜色
ax.scatter(datingDataMat[:,0],datingDataMat[:,1],
10.0*np.array(datingLabels),35.0*np.array(datingLabels))
plt.show()

3.归一化数值

在处理这种不同取值范围的特征值时,我们通常采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。下面的公式可以将任意取值范围的特征值转化为0到1区间内的值:
n e w V a l u e = ( o l d V a l u e − m i n ) ( m a x − m i n ) newValue = \frac{(oldValue-min)}{(max - min)} newValue=(maxmin)(oldValuemin)
min(0):返回该矩阵中每一列的最小值
min(1):返回该矩阵中每一行的最小值

def autoNorm(dataSet):
    '''
    Desc:
    将任意取值范围的特征值转化为01区间内的值
    
    Arguments:
    dataSet -- 训练数据集
    
    Return:
    normDataSet -- 归一化后的训练集
    ranges -- 每个特征(列)的极差
    minVals -- 每个特征(列)的最小值
    
    '''
    # 数据中每个特征的最小值和最大值
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    # 每个(列)特征的极差(max−min)
    ranges = maxVals - minVals
    # shape(dataSet):返回矩阵的行列数,构造相同行列的零矩阵
    normDataSet = np.zeros(np.shape(dataSet))
    # 获得dataSet的行数
    m = dataSet.shape[0]
    # oldValue−min
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    # (oldValue−min)/(max−min)
    normDataSet = normDataSet/np.tile(ranges, (m, 1))
    return normDataSet, ranges, minVals
    
norm, ranges, minVals = autoNorm(datingDataMat)
norm,ranges,minVals
(array([[0.44832535, 0.39805139, 0.56233353],
        [0.15873259, 0.34195467, 0.98724416],
        [0.28542943, 0.06892523, 0.47449629],
        ...,
        [0.29115949, 0.50910294, 0.51079493],
        [0.52711097, 0.43665451, 0.4290048 ],
        [0.47940793, 0.3768091 , 0.78571804]]),
 array([9.1273000e+04, 2.0919349e+01, 1.6943610e+00]),
 array([0.      , 0.      , 0.001156]))

4.测试算法

def datingClassTest():
    """
    Desc:
        对约会网站的测试方法
    """
    # 设置测试数据的比例(训练数据集比例为1-hoRation)
    hoRatio = 0.1
    # 加载数据
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
    # 归一化数据
    norm, ranges, minVals = autoNorm(datingDataMat)
    # 样本个(行)数
    m = norm.shape[0]
    # 设置测试样本数量
    numTestVecs = int(m * hoRatio)
    # 记错器
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(norm[i,:], norm[numTestVecs:m, :],datingLabels[numTestVecs:m], 3)
        print('the classifier came back with: %d, the real answer is: %d' % (classifierResult, datingLabels[i]))
        if classifierResult !=datingLabels[i]:
            errorCount += 1.0
    # 错误率
    print("the total error rate is: %f" % (errorCount / float(numTestVecs)))
    # 错误个数
    print('errorCount:{}'.format(errorCount))

5.使用算法

def classifyPerson():
    #输出结果
    resultList = ['讨厌','有些喜欢','非常喜欢']
    #三维特征用户输入
    precentTats = float(input("玩视频游戏所耗时间百分比:"))
    ffMiles = float(input("每年获得的飞行常客里程数:"))
    iceCream = float(input("每周消费的冰激淋公升数:"))
    # 打开文件处理数据
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    # 归一化
    normMat, ranges, minVals = autoNorm(datingDataMat)
    # 生成NumPy数组,测试集
    inArr = np.array([precentTats, ffMiles, iceCream])
    #测试集归一化
    norminArr = (inArr - minVals) / ranges
    classifierResult = classify0(norminArr, normMat,datingLabels, 3 )
    print(classifierResult)
    print("你可能%s这个人" % (resultList[classifierResult - 1]))
classifyPerson()
玩视频游戏所耗时间百分比:10
每年获得的飞行常客里程数:10000
每周消费的冰激淋公升数:0.5
1
你可能讨厌这个人
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值