机器学习实战—KNN


机器学习是人工智能领域的一个核心分支,机器学习自身又可根据训练数据有无标签分为监督学习(supervised Learning)和无监督学习(unsupervised Learning),当然还有强化学习(Reinforcement Learning),可能还有很多。

机器学习,也就是让机器去学习,核心在学习。那么如何让机器向人一样的学习呢?例如人可以很轻易的分辨一个动物是猫还是狗。这来源于我们从小开始的经验积累。这也叫经验学习。机器也如此,要想使计算机分辨出一个动物是猫还是狗,就必须喂给计算机数据,对应不同的应用,数据集少至数百,多则数万甚至更多。

就拿猫狗分辨来解释一下监督和无监督学习,它们的核心区别在于数据集的不同

监督学习:我们要喂给计算机的训练数据集包括不同的猫狗照片各500张,并给每张照片标注分类。机器通过这1000张已知答案的数据来学习。学习方式可以通过多种算法,例如KNN。

无监督学习:我们喂给计算机的训练数据集包括不同的猫狗照片各500张,每张照片的分类我们也不知道。机器通过这1000张未知答案的数据来学习。机器可以通过照片数据的内在关系将其区分开。

强化学习(Reinforcement Learning):强化学习是智能体(Agent)以“试错”的方式进行学习,通过与环境进行交互获得的奖赏指导行为,目标是使智能体获得最大的奖赏。如果Agent的某个行为策略导致环境正的奖赏(强化信号),那么Agent以后产生这个行为策略的趋势便会加强。Agent的目标是在每个离散状态发现最优策略以使期望的折扣奖赏和最大。

在这里插入图片描述

一、分类算法—KNN/K-近邻


KNN模型(K-Nearest Neighbor,K近邻算法),一个典型的非参数模型,也就是说计算机通过KNN学到的知识并不是以数值权重的方式存储下来的。(非参数可以简单理解为每个特征占预测结果的权重相等)

概述:因为属于监督学习的范畴,所以我们需要有数据集,称之为训练样本集,即我们知道训练样本集中的数据的分类。这一组数据也有特征,就是不同属性。当要预测一组新数据的分类时,用新数据的每个特征与样本集中每组数据的特征对应比较,计算距离。最后提取距离最近的(最相似)的K个数据,通常K是不大于20的整数,最后选取K个最相似数据中出现次数最多的分类,作为新数据的分类。

物以类聚,人以群分

​ —《易经》

二、简单分类

先给出算法的伪代码

(1)计算已知类别数据集中的点与当前点之间的距离

(2)按照距离递增次序排序

(3)选取与当前距离最近的K个点

(4)确定前K个点所在类别的出现频率

(5)返回前K个点出现频率最高的类别作为当前点的预测分类

下面通过一个读者推荐的案例来解释KNN算法

读者ID收藏CPU书籍收藏AI书籍收藏OS书籍收藏Java书籍收藏Python书籍收藏Js书籍收藏阅前端书籍收藏C书籍读者分类
115102011AI
216101010AI
317101011AI
462102111硬件
519101120AI
612631111软件

当一位爱好书籍方向未知的读者加入后,根据他一段时间的借书情况,预测他的分类,数据如下

读者ID收藏CPU书籍收藏AI书籍收藏OS书籍收藏Java书籍收藏Python书籍收藏Js书籍收藏阅前端书籍收藏C书籍读者分类
2326013100

下面计算ID为23的读者的各特征与已知分类读者对应特征的距离

读者ID与未知读者的距离
12.82842712
23
33.31662479
46.08276253
54.47213595
67.93725393

计算距离采用欧式距离公式
∑ i = 1 n ( x i − x 0 ) 2 2 \sqrt[2]{\sum_{i=1}^n{(x_i-x_0)^2}} 2i=1n(xix0)2
下面导入Numpy函数库,因为机器学习涉及到很多线性代数的知识,所以这个库我门以后会常用。

第一步:解析数据

这里我直接写好了上述的数据,实际过程中可以解析文本文档、excel、csv、网络格式的数据。

def createDataSet():
    group = array(
        [[1, 5, 1, 0, 2, 0, 1, 1], [1, 6, 1, 0, 1, 0, 1, 0], [1, 7, 1, 0, 1, 0, 1, 1], [6, 2, 1, 0, 2, 1, 1, 1],
         [1, 9, 1, 0, 1, 1, 2, 0], [1, 2, 6, 3, 1, 1, 1, 1]])
    labels = ['AI', 'AI', 'AI', '硬件', 'AI', '软件']
    return group, labels

接着是KNN的重点,编写分类器classify

# inx是用于分类的测试向量,dataSet是训练样本集,labels是标签
# 且labels的元素数目与数组dataSet的行数相同,K是最相邻的K个点
def classify(inX, dataSet, labels, K):
    # shape[0]表示最外围的数组的维数,这里也就是行数;
    # shape[1]表示次外围的数组的维数,这里也就是列数,数字不断增大,维数由外到内。
    dataSetSize = dataSet.shape[0]
    # tile(A,reps) tile将inX沿行复制一倍,相当于没有变化;再沿列复制dataSetSize倍
    # 如果有三个参数,第一个参数是将后面的结果复制几次
    # n维矩阵减一位矩阵,相当于每一维都减一维矩阵
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat ** 2
    # axis=0返回矩阵的所有元素相加的结果,axis=1将行向量相加返回新矩阵
    sqDistances = sqDiffMat.sum(axis=1)
    # 将距离开平方
    distances = sqDistances ** 0.5
    # 对distance升序排序,返回一个索引序列,每个数字表示数据在distances中的索引
    sortedDistIndicies = distances.argsort()
    # 定义一个空字典
    classCount = {}
    # print(sortedDistIndicies)
    for i in range(K):
        voteIlabel = labels[sortedDistIndicies[i]]
        # voteIlabel这里表示的A或B,是标签
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
        # get(voteilabel,0):原型为get(key,默认值),作用是获取key对应的值,如果不存在key,则新增key,值为默认值
        # 这里很巧妙的运用字典,将标签当做key,前K个最近的点,每一个点对应的标签出现一次,字典中标签相应的value值就加1.
    # print(classCount)
    sortedClassCount = sorted(classCount.items(),
                              key=operator.itemgetter(1), reverse=True)
    # operator.itemgetter()获取对象的哪些维的数据
    # print(sortedClassCount)
    return sortedClassCount[0][0]

未排序的classCount为{‘AI’: 3},表明前K个距离最近的数据类型全为AI,按value分类自然也为AI

if __name__ == '__main__':
    group, labels = KNN.createDataSet()
    print(KNN.classify([2, 6, 0, 1, 3, 1, 0, 0], group, labels, 3))

三、改进分类

1.归一化处理

实际应用中还会有很多其他的问题,例如数据归一化处理

例如我们增加一个特征,每年的平均阅读时间,修改后的数据表如下:

读者ID收藏CPU书籍收藏AI书籍收藏OS书籍收藏Java书籍收藏Python书籍收藏Js书籍收藏阅前端书籍收藏C书籍每年的平均阅读小时读者分类
115102011120AI
216101010300AI
31710101140AI
46210211160硬件
51910112070AI
61263111155软件

未知读者的数据如下:

读者ID收藏CPU书籍收藏AI书籍收藏OS书籍收藏Java书籍收藏Python书籍收藏Js书籍收藏阅前端书籍收藏C书籍每年的平均阅读小时读者分类
232601310060

新的距离如下:

读者ID与未知读者的距离
160.06662967
2240.01874927
320.27313493
46.08276253
510.95445115
69.38083152

得出结果则是

[ 60.06662967 240.01874927  20.27313493   6.08276253  10.95445115
   9.38083152]
{'硬件': 1, '软件': 1, 'AI': 1}
硬件

这就是由于特征的数值过大,占的权重过大,会严重影响计算结果。所以我们要对特征数值较大进行归一化处理,将取指范围处理为0-1或-1到1之间。公式如下
x n e w = ( x o l d − m i n x ) / ( m a x x − m i n x ) x_{new} = (x_{old}-min_x)/(max_x-min_x) xnew=(xoldminx)/(maxxminx)
这只是一种最简单的归一化处理公式,欧式距离、标准化欧式距离、马氏距离、余弦距离

# 归一化数值,防止因为某些数值本身值过大的原因影响
def autoNorm(dataSet):
    # min():无参-数组中所有值的最小值
    # min(0)—> axis=0 每列的最小值   1—> axis=1 每行的最小值
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    # zeros()返回给定形状和类型的新数组,用0填充。
    # shape返回元组形式的行数和列数—>(行数,列数)
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    # tile是复制矩阵,将minVals在行上复制m次,在列上复制1次;因为矩阵减法必须矩阵大小一样,除法也相同
    normDataSet = dataSet - tile(minVals, (m, 1))
    # 在某些数值处理软件包,/可能意味着矩阵除法,但在NumPy库中,矩阵除法需要使用函数linalg.solve(matA,matB)
    normDataSet = normDataSet / tile(ranges, (m, 1))
    return normDataSet, ranges, minVals
if __name__ == '__main__':
    group, labels = KNN.createDataSet()
    group, ranges, minVals = KNN.autoNorm(group)
    print(group)
    print(KNN.classify0([2, 6, 0, 1, 3, 1, 0, 0, 60], group, labels, 3))

再次执行发现结果又恢复正常

2.评估算法的正确率

通常我们只提供已有数据的90%作为训练样本来训练分类器,而使用其余的10%去测试分类器,训练即是找出使得正确率最大的K值

def test():
    hoRatio = 0.10
    DataMat, labels = KNN.createDataSet()
    normMat, ranges, minVals = KNN.autoNorm(DataMat)
    dataNumber = normMat.shape[0]
    numTestVecs = int(dataNumber * hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = KNN.classify0(normMat[i, :], normMat[numTestVecs:dataNumber, :],
                                         labels[numTestVecs:dataNumber], K=3)
        print("No.%d, the classifier came back with:%s, the real answer is: %s" % (
            i + 1, classifierResult, labels[i]))
        if classifierResult != labels[i]:
            errorCount += 1.0
    print("the total number of errors is: %d" % errorCount)
    print("the total error rate is:%.2f%%" % (errorCount / float(numTestVecs) * 100))

当然,这是我随意选择的一个案例,可能在应用数据的合理上有所不足,但窥一斑而知全豹,我们可以通过上述的几个关键点来应用KNN去解决一些实际问题。第二节将展示完整的手写识别系统的案例

四、总结

缺点

  • 保存模型需要保存全部的样本集
  • 训练过程很快,预测速度很慢
  • 无法给出任何数据的基础结构信息,无法知晓平均实例样本和典型实例样本具有什么特征

优点

  • 精度高,对异常值不敏感、无数据输入假定
  • 原理简单

很多现实中的问题都可以转化成分类/回归问题,例如:

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值