《机器学习实战》第二章——k-近邻算法

理论

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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值