本文的代码基于机器学习实战,然后修改了一些部分,以适应3.x的Python
首先是用到的库
import pandas as pd
import numpy as np
import heapq
from collections import Counter
import os
def file2matrix(filename):
df = pd.read_csv(filename,sep="\t",header=None)
matrix = df.to_numpy()
x = matrix[...,0:-1]
label = matrix[...,-1]
return x , label
这里以及后续更新的,读取数据方面都是使用pandas库,操作简单,且转化成numpy后容易切片操作
def classify0(inX, dataSet, labels, k):
distance = np.sum((dataSet - inX)**2,axis=1)**0.5
k_largest_ind = heapq.nsmallest(k,range(len(distance)),distance.take)
k_label = labels[k_largest_ind]
cnt_dict = Counter(k_label)
return heapq.nlargest(1,cnt_dict.items(),key = lambda x:x[1])[0][0]
注意这里,dataset的维度是nm,n是数据集个数,m是样本的特征维度,labels的维度是n1,然后inx是要被预测的样本,维度是1m
这里的dataSet-inX,numpy进行了广播操作,把inx扩展成了nm,然后再相减,得到的是一个n*m的数组,而每一行的平方和再开方就是样本和每个数据的欧氏距离了,所以这里sum时指定axis为1,
第二行就能利用堆得到前k个最小距离的数据下标,
第3行就能利用下标得到k个邻居的标签了,然后使用Python自带的Counter得到计数字典并返回最大的那个
这个不用说,对knn来说几乎是必须的一步,无量纲化能去掉某些指标太大而带来的负面影响
def autoNorm(dataSet):
minval = np.min(dataSet,axis=0)
rangeval = np.max(dataSet,axis=0)-np.min(dataSet,axis=0)
return (dataSet - minval ) / ( rangeval ),minval,rangeval
这个还要返回最小值和最大值因为后面在预测时也需要用他们来标准化数据
def dating_dataset_test():
hold_out = 0.7
dataset, labels= file2matrix("datingTestSet2.txt")
normdata,minval,rangeval = autoNorm(dataset)
n = normdata.shape[0]
test_data = normdata[:int(0.3*n)]
train_data = normdata[int(0.3*n):]
test_label = labels[:int(0.3*n)]
train_label = labels[int(0.3*n):]
error = 0
for (x,y) in zip(test_data,test_label):
if classify0(x,train_data,train_label,3)!=y:
error += 1
print("the error is {}".format(error/(n*0.3)))
这里采用了留出法的验证方法,所以实际上里面的test_data应该是valid_data,懒得改了233
把数据集化为7:3的训练集和验证集,而knn是不需要训练的,所以这里就直接验证了
zip函数是Python自带的,挺好用的一个函数,
def img2vector(filename):
data = pd.read_csv(filename,header=None).to_numpy()
vector = np.zeros((1,1024))
for i in range(32):
for j in range(32):
vector[0][32*i+j]=data[i][0][j]
return vector
这里的数据集并不是mnist那个,只不过是为了方便操作,作者自己编了一些数据集罢了
def read_data_in_file(filedir):
filelist = os.listdir("trainingDigits")
n = len(filelist)
training_x = np.zeros((n, 1024))
training_y = np.zeros((n,))
for i in range(n):
filename = "/".join(("trainingDigits", filelist[i]))
training_x[i] = img2vector(filename)
training_y[i] = int(filelist[i][0])
return training_x,training_y
使用os库,再配合上join函数,我们能容易得到所需的文件名,然后遍历下去就能得到数据集啦
def head_written_dataset_test():
training_x, training_y = read_data_in_file("trainingDigit")
test_x, test_y = read_data_in_file("testDigit")
error = 0
for (x,y) in zip(test_x,test_y):
if classify0(x,training_x,training_y,4) !=y:
error += 1
print("the error rate is {}".format(error/len(test_y)))
这儿没啥好说的啦
总的原书的代码就是这些内容,当然还有很多我们可以自己拓展实现的,比如说对k的选取可以编个函数去看看效果,比如说选取的距离度量方式可以编写成其他方式,而这2个也是knn的效果的比较重要的影响因素。