1.例子
实例,两个特征
对例子建模
举例:
上面这个图中,把小手指的这个豆子归为哪个类呢?knn的做法是看它的邻居们是什么类,然后把它归为和邻居一样的类
2.算法详述
1)步骤
为了判断未知实例的类别,以所有已知类别的实例作为参照
选择参数K(一般都选择奇数)
计算未知实例与所有已知实例的距离
选择最近K个已知实例
根据少数服从多数的投票法则(majority-voting),让未知实例归类为K个最邻近样本中最多树的类别
2)细节
关于距离的衡量方法:
1.Euclidean Distance
3.算法优缺点
1)算法优点:
简单
易于理解
容易实现
通过对K的选择可具备丢噪音数据的健壮性
2)算法缺点
需要大量空间存储所有已知实例
算法复杂度高(需要比较所有已知实例与要分类的实例)
当其样本分布不平衡时,比如其中一类样本过大(实例数量过多)占主导的时候,新的未知实例容易被归类为这个主导样本,因为这类样本实例的数量过大,但这个新的未知实例实际并没有接近目标样本
3)改进算法
考虑距离,根据距离加上权重
比如:1/d(d:距离)
离未知实例近的,则权重就大
4.介绍一种数据集
虹膜 iris
是一种花,这个数据集中一共有150个实例
萼片长度,萼片宽度,花瓣长度,花瓣宽度(sepal length,sepal width,petal length and petal width)
类别:3中label
Iris setosa, Iris versicolor, Iris virginica
这个数据集相当于有150个样本(150行),每个样本有4个特征,一个label。相当于有5列
python代码:调用sklearn库中的模块
"""调用库实现"""
from sklearn import neighbors #knn这个分类器包含在neighbors这个模块中
from sklearn import datasets #我们用到的Iris这个数据集包含在datasets这个模块中,所以要导入这个模块
knn = neighbors.KNeighborsClassifier() #将这个分类器复制给定义的变量knn
iris = datasets.load_iris() #加载要用到的数据集
print(iris) #把这个数据集打印出来
knn.fit(iris.data,iris.target) #fit函数 建立模型,然后传入参数(特征值,标签)
predictLabel = knn.predict([[0.1,0.2,0.3,0.4]])#根据建好的模型来预测新的实例
print(predictLabel)
python代码:自己实现各个模块
import csv #加载csv格式的文件用的
import random
import math #计算距离
import operator
def loadDataset(filename,split,trainingSet=[],testSet=[]):#加载数据的函数(文件名称,split将数据集分成两部分,一个训练集,一个测试集)
with open(filename,'r') as csvfile:#把文件导入成csv格式的文件
lines = csv.reader(csvfile)#读取所有的行
dataset = list(lines)#将所有行转化成list,并赋值给定义的变量名dataset
for x in range(len(dataset) - 1):
for y in range(4):
dataset[x][y] = float(dataset[x][y]) #把dataset中的数据转换成float格式
if random.random() < split: #如果产生的随机数小于split,那么就将这一行的数据加入到训练集中
trainingSet.append(dataset[x])
else:#如果产生的随机数大于split,那么就将这一行的数据加入到测试集中
testSet.append(dataset[x])
def euclideanDistance(instancel,instance2,length):#计算距离的函数(实例1(有可能是多维),实例2(有可能是多维),维度变量)
distance = 0 #初始化distance为0
for x in range(length):#对于length维度的实例,分别计算每个值得平方((x1-y1)^2 + (x2-y2)^2 + ...+(xlength - ylength)^2)
distance +=pow((instancel[x] - instance2[x]),2)
return math.sqrt(distance) #对distance开平发
def getNeighbors(trainingSet,testInstance,k):#返回最近的K个邻居的函数
distances = [] #定义一个空的容器,用来装各个距离
length = len(testInstance) - 1 #要测试实例的维度
for x in range(len(trainingSet)): #对训练集中的每一个样本,计算未知实例到每个样本的距离
dist = euclideanDistance(testInstance,trainingSet[x],length)
distances.append((trainingSet[x],dist))#把训练集中每个样本与未知实例的距离算出后加入到定义的distance容器中
distances.sort(key=operator.itemgetter(1))#用sort函数把容器中的距离按从小到大排列
neighbors = []
for x in range(k):#返回容器中距离最近的K个邻居
neighbors.append(distances[x][0])
return neighbors
def getResponse(neighbors):#在返回的个邻居中,根据少数服从多数进行归类
classVotes = {}
for x in range(len(neighbors)):#对各个邻居进行归类,
response = neighbors[x][-1]
if response in classVotes:
classVotes[response] += 1
else:
classVotes[response] = 1
sortedVotes = sorted(classVotes.items(),key=operator.itemgetter(1),reverse=True)#对每个类中同类邻居的个数按降序排列
return sortedVotes[0][0]#返回第一个值
def getAccuracy(testSet,predictions): #计算一下准确率,比较一个预测的值和真实值
correct = 0
for x in range(len(testSet)):
if testSet[x][-1] ==predictions[x]:#[-1]代表最后一个值 如果测试集中的label == 预测出来的label
correct += 1 #正确的个数加1
return (correct/float(len(testSet))) * 100.0 #返回计算的正确率
def main():
#prepare data
trainingSet = [] #创建空的训练集
testSet = [] #创建空的测试集
split = 0.67 #将加载加的数据集分为测试集和训练集的标准定为0.67(把2/3的数据集分为训练集,把1/3的数据集分为测试集)
#这里的r 是将后面的整个字符串当做路径,而忽略其中的特殊符号,比如\n这种转义符就能忽略掉
loadDataset(r'J:\python_program\machine_learning\KNN\iris.txt',split,trainingSet,testSet)
print("Train set: " + repr(len(trainingSet)))
print("Test set: " + repr(len(testSet)))
#generate predictions
predictions = []
k = 3
for x in range(len(testSet)):
neighbors = getNeighbors(trainingSet,testSet[x],k)
result = getResponse(neighbors)
predictions.append(result)
print("> predicted= " + repr(result) + ",actual = " +repr(testSet[x][-1]))
accuracy = getAccuracy(testSet,predictions)
print("Accuracy: " + repr(accuracy) + "%")
main()