《机器学习实战》——kNN

1.k-近邻算法

# k近邻算法:classify0(用于分类的输入向量inX,输入的训练样本dataset,标签向量labels,选择最近邻居的数目k)
# first step:计算待分类向量和所有样本点的距离;
# second step:对该距离数据从小到大排序,确定前k个距离最小示例的标签;
# third step:返回发生频率最高的元素标签.
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]  # dataSetSize = 训练样本行数
    diffMat = tile(inX, (dataSetSize,1)) - dataSet  # 将输入inX扩展成训练样本行数*3(和训练样本大小一样)
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1) # axis = 1,对每一行求和(对列操作)
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()    # 对序列排序,并返回对应原序列索引
    classCout = {}  # 统计标签个数
    for i in range (k):
        voteIlabel = labels[sortedDistIndicies[i]]  # 索引为i的示例的标签
        classCout[voteIlabel] = classCout.get(voteIlabel,0) + 1 # 该标签个数+1
    sortedClaassCout = sorted(classCout.items(),    # 按照第2个域(即标签类别)降序排列
                              key=operator.itemgetter(1), reverse=True)
    return sortedClaassCout[0][0]

Python语法

np.argsort()

np.argsort(x, axis)

对x进行升序排列,将排序后的序列对应原序列索引赋给x。axis=0,按列排序;axis=1,按行排序。

# 一维数组
>>> x = array([0.3, 0.5, 0.8, 0.4])
>>> y = x
>>> np.argsort(x)
array([0, 3, 1, 2], dtype=int64)
>>> np.argsort(-y)
array([2, 1, 3, 0], dtype=int64)
# 二维数组
>>> x = [[0, 3], [1, 2]]
>>> y = x
>>> np.argsort(x, axis = 0)	# 升序/按列排列
array([[0, 1],
       [1, 0]], dtype=int64)
>>> np.argsort(y, axis = 1)	# 升序/按行排列
array([[0, 1],
       [0, 1]], dtype=int64)

dict.get()

dict.get(key, default=None)

返回字典中指定key对应的值,如果值不在字典中则返回default。

>>> dict = {'Name': 'Zara', 'Age': 27}
>>> print ("Value : %s" %  dict.get('Age'))
Value : 27
>>> print ("Value : %s" %  dict.get('Sex', "Never"))
Value : Never

dict.items()

dict.items()

以列表返回可遍历的(键, 值) 对/字典。

>>> dict = {'Google': 'www.google.com', 'Runoob': 'www.runoob.com', 'taobao': 'www.taobao.com'}
>>> print "字典值 : %s" %  dict.items()
字典值 : [('Google', 'www.google.com'), ('taobao', 'www.taobao.com'), ('Runoob', 'www.runoob.com')]

>>> for key,values in  dict.items():
    print (key,values)
Google www.google.com
taobao www.taobao.com
Runoob www.runoob.com   

sorted()

sorted(iterable, key=None, reverse=False)  

对可迭代对象iterable进行排序操作。key可以指定迭代对象中的某个元素进行排序。reverse=False,升序(默认);reverse=True,降序。

sorted()和sort()区别

# 1.sort()对已有list进行排序,会改变list的值,sorted()则返回一个新的list
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5] 
>>> a=[5,2,3,1,4]
>>> a.sort()
>>> a
[1,2,3,4,5]

# 2.sort()只能用于list,sorted()适用于所有可迭代对象
>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]

利用key进行特殊排序

# 1.利用key进行倒序排序
>>> example_list = [5, 0, 6, 1, 2, 7, 3, 4]
>>> result_list = sorted(example_list, key=lambda x: x*-1)	# x的相反数
>>> print(result_list)
[7, 6, 5, 4, 3, 2, 1, 0]

# 2.利用key选择排序的域/维度
>>> import operator
>>> dict = {'Lena':18, 'John':20, 'Mark':15, 'Jackson':23}
>>> sorted(dict, key=operator.itemgetter(1),reverse=True)	# 按第二个域,即数字进行降序排列
[('Jackson', 23), ('John', 20), ('Lena', 18), ('Mark', 15)]

2.准备数据:从文本文件中解析数据

# 将文本记录转换为分类器可以接受的格式
# first step:获取文本记录行数
# second step:创建一个二维数组(行数*3)
# third step:将文本每一行末尾末尾元素换成\t
# PS:注释行,源程序直接用int转换标签,出现错误,这里用一个字典进行转换
def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    returnMat = zeros((numberOfLines,3))
    classLabelVector = []
    index = 0
    labels = {'didntLike':1,'smallDoses':2,'largeDoses':3}  # 新增 这行代码可以放到for的开始,不需要每次都重新赋值
    for line in arrayOLines:
        line = line.strip() # 移除line末尾元素
        listFromLine = line.split('\t') 
        returnMat[index,:] = listFromLine[0:3]
        # classLabelVector.append(int(listFromLine[-1])) #classLabelVector储存的应为int
        classLabelVector.append(labels[listFromLine[-1]])  # 去掉了int
        index +=1
    return returnMat,classLabelVector

Python语法

file.readlines()

file.readline(size)	# 不指定size,则默认读取首行;指定size,则读取指定size字节(包括\n字符)
file.readlines()	# 读取file所有行,以list形式返回

str.strip()

str.strip([chars])

移除字符串str开头和结尾的字符或字符串chars(默认为空格),无法删除字符串中间的字符。

>>> str = "*****this is **string** example....wow!!!*****"
>>> print (str.strip( '*' ))  # 指定字符串 *
this is **string** example....wow!!!

>>> str = "123abcrunoob321"
>>> print (str.strip( '12' ))  # 字符序列为 12
3abcrunoob3

str.split()

str.split(char="", num=string.count(str))

用指定分隔符char对字符串进行切片,分割为num+1的子字符串,num默认为-1,即切割所有。

>>> str = "this is string example....wow!!!"
>>> print (str.split( ))       # 以空格为分隔符
['this', 'is', 'string', 'example....wow!!!']
>>> print (str.split('i',1))   # 以 i 为分隔符
['th', 's is string example....wow!!!']
>>> print (str.split('w'))     # 以 w 为分隔符
['this is string example....', 'o', '!!!']

list.append()

list.append(obj)

在list末尾添加新的对象。该方法无返回值,会直接改变list的值。

list.append()和list.extend()区别

>>> mylist = [10,20,30]
>>> mylist.extend([40,50,60])
>>> mylist
[10, 20, 30, 40, 50, 60]
>>> mylist.append([70,80,90])
>>> mylist
[10, 20, 30, 40, 50, 60, [70, 80, 90]]

append()向list尾部添加一个元素,而extend()只接受一个列表作为参数,并将该参数的每个元素都添加到原有的列表中。该例也说明一个list可包含不同类型。

3.准备数据:归一化数值

# 归一化数值:消除由于某一项数值过大带来的误差影响
# 将取值范围处理为0——1区间内的值
# newvalue = (oldvalue-min)/(max-min)
def autoNorm(dataSet):
    minVals = dataSet.min(0)    # 找出每列最小值,minVal为1*3矩阵
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet)) # normDataSet和dataSet为1000*3矩阵
    m = dataSet.shape[0]    # m=1000,即dataSet的行数
    normDataSet = dataSet - tile(minVals, (m,1))    #用tile,将minVal行数重复m次
    normDataSet = normDataSet/tile(ranges, (m,1))   #每个元素相除,不是矩阵除法
    return normDataSet, ranges, minVals    

Python语法

np.tile()

tile(A, reps)

A和reps均为数组,将数组A重复reps次

>>> from numpy import *
>>> A = [1,2]
>>> tile(A,(3,2))	#将A的行重复3次,列重复两次
array([[1, 2, 1, 2],
       [1, 2, 1, 2],
       [1, 2, 1, 2]])

4.测试算法:针对约会网站的测试

# 分类器针对约会网站的测试代码
# first step:读取训练样本(file2matrix函数)并归一化(autoNorm函数)
# sencond step:计算测试数据的数量,即计算哪些是测试样本(0.1),哪些是训练样本(0.9)
# third step:将测试样本和训练样本输入到classify0函数
# forth step:计算错误率(错误样本数/总样本数)并输出
def datingClassTest():
    hoRatio = 0.1
    datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)	# 调用定义的归一化函数
    m = normMat.shape[0]    # m = 1000(样本总行数)
    numTestVecs = int(m*hoRatio)    # 测试集数量占总量的0.1
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:], datingLabels[numTestVecs:m],3)	#调用定义的kNN分类函数
        print ("the classifier came back with: %d, the real answer is: %d" %(classifierResult, datingLabels[i])) # %d:整数;%s:字符串
        if(classifierResult != datingLabels[i]):
            errorCount += 1.0
    print ("the total error rate is: %f" %(errorCount/float(numTestVecs)))  # %f:错误率为浮点数

5.使用算法:约会网站预测

# 约会网站预测函数(和测试代码kNN.datingClassTest()基本相同)
# first step:获取测试用户数据(1*3)
# second step:读取训练样本(file2matrix函数)并归一化(autoNorm函数)
# third step:将测试用户数据归一化,并将其和训练样本数据输入到classify0函数
def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    percentTats = float(input("percentage of time spent playing video games?")) # 输入用户数据(py3.*用input, py2.*用raw_input)
    ffMiles = float(input("frequent flier miles earned per year?"))
    iceCream = float(input("liters of ice cream consumed per year?"))
    inArr = array([ffMiles, percentTats, iceCream])
    datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    classifierResult = classify0((inArr-minVals)/ranges, datingDataMat, datingLabels, 3)
    print("You will probably like this person: ",resultList[classifierResult - 1])

Python语句

input()

input([prompt])	# prompt为提示信息 

在 Python3.x 中 raw_input() 和 input() 进行了整合,去除了 raw_input( ),仅保留了input( )函数,其接收任意任性输入,将所有输入默认为字符串处理,并返回字符串类型。

>>>a = input("input:")
input:123                  # 输入整数
>>> type(a)
<class 'str'>              # 字符串
>>> a = input("input:")    
input:runoob              # 正确,字符串表达式
>>> type(a)
<class 'str'>             # 字符串

6.示例:手写识别系统

准备数据

# 将手写数字图像转换为向量
def img2vector(filename):
    returnVect = zeros((1,1024))    # 32*32=1024,构建1*1024向量
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0, 32*i+j] = int(lineStr[j]) #这里必须加int,lineStr[j]是字符串            
    return returnVect

测试算法

# 手写数字识别测试程序 
# first step:获取trainingDigits目录中的文件内容,存入trainingFileList
# second step:构建m*1024矩阵,该矩阵每行储存一个图像(32*32)
# third step:从文件名中解析出分类数字,存入hwLabels
# forth step:相同的方法获取测试样本,将其送入classifier分类
def handwritingClassTest():
    hwLabels = []
    # trainingFileList = open('traningDigits')
    trainingFileList = listdir('trainingDigits')
    m = len(trainingFileList)
    trainingMat = zeros((m, 1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]     # 相当于剔除后缀,e.g. 0_13.txt,fileStr=0_13
        classNumStr = int(fileStr.split('_')[0])    # 获取数字类别,e.g. 0_13.txt,fileStr=0_13,classNumStr=0
        hwLabels.append(classNumStr)
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
    # testFileList = open('testDigits')
    testFileList = listdir('testDigits')
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        # fileStr = fileNameStr.split('.')[0]     
        # classNumStr = int(fileStr.split('_')[0])    
        classNumStr = int(fileNameStr.split('_')[0])    # 可以合并成一步
        vectorUnderTest = img2vector('testDigits/%s' %fileNameStr)
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print ("the classifier came back with: %d, the real answer is: %d" %(classifierResult, classNumStr))
        if(classifierResult != classNumStr):
            errorCount += 1.0
    print("\nthe total number of error is: %d" %errorCount)
    print("\nthe total error rate is: %f" %(errorCount/float(mTest)))

测试结果

>>> import kNN
>>> kNN.handwritingClassTest()
...
...
the classifier came back with: 9, the real answer is: 9
the classifier came back with: 9, the real answer is: 9
the classifier came back with: 9, the real answer is: 9

the total number of error is: 10

the total error rate is: 0.010571

总结

k-近邻算法采用测量不同特征值之间的距离方法进行分类。
优点: 精度高,对异常值不敏感,无数据输入假定。
缺点: 计算复杂度高,空间复杂度高。如在手写数字识别示例中,2000个训练样本,那么算法要为每个测试向量做2000次距离运算,每次距离运算又包括1024维度浮点运算,也就需要为测试向量准备2MB的存储空间。
适用数据范围: 数值型和标称型。数值型数据变量 可在无尽范围内取值,如概率,主要用于回归;标称型数据变量 则在有限范围内取值,如真/假,适用于分类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值