理论
k-近邻算法,测量与测试样本之间的欧式距离,看在一定的范围(阈值)内,哪个类别占比多,就归属于哪个类别
优点
- 精度高
- 对异常值不敏感
- 无数据输入假定
缺点
- 计算复杂度高
- 空间复杂度高
适用于
- 数据量较少
- 距离明显
- 无异常值
总结:
- 需要标准化
- 异常值,缺失值影响较大
k-近邻算法分类器
def classify0(inX, dataSet, labels, k):
'''
定义一个函数,实现k-近邻算法
inX : 测试样本
dataSet : 训练样本 是ndarrat类型
labels : 训练集的目标特征
k : 范围(阈值)\邻居数目
'''
dataSetSize = dataSet.shape[0] # 获取测试集样本数
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
# np.tile(A, reps) 构造reps(几行几列)的数组每个位置用A来代替
# inX(1,n-特征数) 代入 (dataSetSize,1) 变为 (dataSetSize, n)的数组 与 dataSet 对应位置相减
sqDiffMat = diffMat ** 2 # 各特征间的距离平方
sqDistances = sqDiffMat.sum(axis=1) # 以行相加,得出与每个样本间的单特征距离平方和,得到一行dataSetSize列数组
distances = sqDistances**0.5 # 开更号得与训练样本间的欧氏距离
sortedDistIndicies = distances.argsort() # 进行排序
# array.argsort() 会对array类型的数据进行排序,得到一个列表[最小元素的索引值, ..., 最大元素的索引值]
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]] # 获取最小距离的标签
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 # 将标签进行统计
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1),reverse=True)
# operator.itemgetter(1) 定义一个函数,可以取到对象上某个地方的值
# classCount.items() 生成一个可迭代对象 dict_items([('a', 2), ('b', 3)])即将字典分解为元组列表
# sorted(iterable, /, *, key=None, reverse=False) iterable:可迭代对象,reverse:递增还是递减排序
# 按照每个类别出现的次数进行从高到低排序
return sortedClassCount[0][0]
# 调用
group = np.array([[1,1.1],
[1,1],
[0,0],
[0,0.1]])
labels = ["a","a","b","b"]
classify0([0.1,0], group, labels, 3)
案例
手写识别系统
数据集下载:
http://archive.ics.uci.edu/ml/datasets/Semeion+Handwritten+Digit
import numpy as np
import operator
def data2array(filename):
'''
转换文件为特征向量和目标向量
'''
with open(filename) as f:
datas = f.readlines()
x = []
y = []
for data in datas :
x_1 = data.split(" ")[:-11] # 转换为['0.0000', '0.0000'...]
x_2 = [ float(i) for i in x_1 ]
x.append(x_2)
y_1 = data.split(" ")[-11:-1]
y_2 = [ int(i) for i in y_1 ]
y_std = np.arange(0,10)
y.append(np.sum(y_2*y_std))
return np.array(x), np.array(y)
def classify0(inX, dataSet, labels, k):
'''
定义一个函数,实现k-近邻算法
inX : 测试样本
dataSet : 训练样本 是ndarrat类型
labels : 训练集的目标特征
k : 范围(阈值)\邻居数目
'''
dataSetSize = dataSet.shape[0] # 获取测试集样本数
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
# np.tile(A, reps) 构造reps(几行几列)的数组每个位置用A来代替
# inX(1,n-特征数) 代入 (dataSetSize,1) 变为 (dataSetSize, n)的数组 与 dataSet 对应位置相减
sqDiffMat = diffMat ** 2 # 各特征间的距离平方
sqDistances = sqDiffMat.sum(axis=1) # 以行相加,得出与每个样本间的单特征距离平方和,得到一行dataSetSize列数组
distances = sqDistances**0.5 # 开更号得与训练样本间的欧氏距离
sortedDistIndicies = distances.argsort() # 进行排序
# array.argsort() 会对array类型的数据进行排序,得到一个列表[最小元素的索引值, ..., 最大元素的索引值]
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]] # 获取最小距离的标签
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 # 将标签进行统计
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1),reverse=True)
# operator.itemgetter(1) 定义一个函数,可以取到对象上某个地方的值
# classCount.items() 生成一个可迭代对象 dict_items([('a', 2), ('b', 3)])即将字典分解为元组列表
# sorted(iterable, /, *, key=None, reverse=False) iterable:可迭代对象,reverse:递增还是递减排序
# 按照每个类别出现的次数进行从高到低排序
return sortedClassCount[0][0]
x, y = data2array("E:\XX\书本\机器学习实战\k-近邻 手写识别\semeion.data")
x_train = x[199:]
x_test = x[:199]
y_train = y[199:]
y_test = y[:199]
index = 0
sum = 0
for x in x_test:
y_predict = classify0(x, x_train, y_train, 5)
if y_predict == y_test[index]:
sum += 1
print("预测为:{},实际为:{}".format(y_predict,y_test[index]))
index += 1
print("准确率为:", sum/(index+1))
运行结果
预测为:9,实际为:9
预测为:5,实际为:9
预测为:9,实际为:9
准确率为: 0.935
kpi sklearn.neighbors
class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights='uniform', algorithm='auto', leaf_size=30, p=2, metric='minkowski', metric_params=None, n_jobs=1, **kwargs)**
"""
:param n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数
:param algorithm:{'auto','ball_tree','kd_tree','brute'},可选用于计算最近邻居的算法:'ball_tree'将会使用 BallTree,'kd_tree'将使用 KDTree,“brute”将使用强力搜索。'auto'将尝试根据传递给fit方法的值来决定最合适的算法。
:param n_jobs:int,可选(默认= 1),用于邻居搜索的并行作业数。如果-1,则将作业数设置为CPU内核数。不影响fit方法。
"""
Method-方法
fit(X, y)
使用X作为训练数据拟合模型,y作为X的类别值。X,y为数组或者矩阵
X = np.array([[1,1],[1,1.1],[0,0],[0,0.1]])
y = np.array([1,1,0,0])
neigh.fit(X,y)
kneighbors(X=None, n_neighbors=None, return_distance=True)
找到指定点集X的n_neighbors个邻居,return_distance为False的话,不返回距离
neigh.kneighbors(np.array([[1.1,1.1]]),return_distance= False)
neigh.kneighbors(np.array([[1.1,1.1]]),return_distance= False,n_neighbors=2)
predict(X)
预测提供的数据的类标签
neigh.predict(np.array([[0.1,0.1],[1.1,1.1]]))
predict_proba(X)
返回测试数据X属于某一类别的概率估计
neigh.predict_proba(np.array([[1.1,1.1]]))
案例-鸢尾花
本案例使用最著名的”鸢尾“数据集,该数据集曾经被Fisher用在经典论文中,目前作为教科书般的数据样本预存在Scikit-learn的工具包中。
from sklearn.datasets import load_iris
from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import classification_report
from sklearn.model_selection import GridSearchCV
def knniris():
"""
鸢尾花分类
:return: None
"""
# 数据集获取和分割
lr = load_iris()
x_train, x_test, y_train, y_test = train_test_split(lr.data, lr.target, test_size=0.25)
# 进行标准化
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.transform(x_test)
# estimator流程
knn = KNeighborsClassifier()
# # 得出模型
# knn.fit(x_train,y_train)
#
# # 进行预测或者得出精度
# y_predict = knn.predict(x_test)
#
# # score = knn.score(x_test,y_test)
# 通过网格搜索,n_neighbors为参数列表
param = {"n_neighbors": [3, 5, 7]}
gs = GridSearchCV(knn, param_grid=param, cv=10)
# 建立模型
gs.fit(x_train,y_train)
# print(gs)
# 预测数据
print(gs.score(x_test,y_test))
# 分类模型的精确率和召回率
# print("每个类别的精确率与召回率:",classification_report(y_test, y_predict,target_names=lr.target_names))
return None
if __name__ == "__main__":
knniris()