2. k近邻算法

一. k近邻算法基础(knn)

  • knn算法简单的描述,就是新的数据 与 已知的多类型的数据 进行'距离'比较, 找出距离最近的k个数据
  • k个数据中, 占比最大的是哪种类型数据, 就把这新的数据 归类到这类数据中

创建测试数据

knn算法实现


二. scikit-learn中机器学习算法封装

将前一节的knn算法封装成函数

import numpy as np
from math import sqrt
from collections import Counter


def kNN_classify(k, X_train, y_train, x):

    assert 1 <= k <= X_train.shape[0], "k must be valid"
    assert X_train.shape[0] == y_train.shape[0], \
        "the size of X_train must equal to the size of y_train"
    assert X_train.shape[1] == x.shape[0], \
        "the feature number of x must be equal to X_train"

    distances = [sqrt(np.sum((x_train - x)**2)) for x_train in X_train]
    nearest = np.argsort(distances)

    topK_y = [y_train[i] for i in nearest[:k]]
    votes = Counter(topK_y)

    return votes.most_common(1)[0][0]

  • nootbook中调用它 

疑问: 前一节的knn算法运用并没有用法模型啊?

  • k近邻算法是非常特殊的, 可以被认为是没有模型的算法
  • 为了和其他算法同一, 可以认为训练数据集就是模型本身

sklearn中的knn


按sklearn的形式封装自己的knn类

import numpy as np
from math import sqrt
from collections import Counter


class KNNClassifier:

    def __init__(self, k):
        """初始化kNN分类器"""
        assert k >= 1, "k must be valid"
        self.k = k
        self._X_train = None
        self._y_train = None

    def fit(self, X_train, y_train):
        """根据训练数据集X_train和y_train训练kNN分类器"""
        assert X_train.shape[0] == y_train.shape[0], \
            "the size of X_train must be equal to the size of y_train"
        assert self.k <= X_train.shape[0], \
            "the size of X_train must be at least k."

        self._X_train = X_train
        self._y_train = y_train
        return self

    def predict(self, X_predict):
        """给定待预测数据集X_predict,返回表示X_predict的结果向量"""
        assert self._X_train is not None and self._y_train is not None, \
                "must fit before predict!"
        assert X_predict.shape[1] == self._X_train.shape[1], \
                "the feature number of X_predict must be equal to X_train"

        y_predict = [self._predict(x) for x in X_predict]
        return np.array(y_predict)

    def _predict(self, x):
        """给定单个待预测数据x,返回x的预测结果值"""
        assert x.shape[0] == self._X_train.shape[1], \
            "the feature number of x must be equal to X_train"

        distances = [sqrt(np.sum((x_train - x) ** 2))
                     for x_train in self._X_train]
        nearest = np.argsort(distances)

        topK_y = [self._y_train[i] for i in nearest[:self.k]]
        votes = Counter(topK_y)

        return votes.most_common(1)[0][0]

    def __repr__(self):
        return "KNN(k=%d)" % self.k

  • notebook中调用自己的knn类 

三. 训练数据集, 测试数据集

判断机器学习算法的性能

  • 不将所有的数据都当训练数据,而是留一部分作为测试数据, 对训练数据得到的模型 进行测试


自己编写测试代码

import numpy as np


def train_test_split(X, y, test_ratio=0.2, seed=None):
    """将数据 X 和 y 按照test_ratio分割成X_train, X_test, y_train, y_test"""
    assert X.shape[0] == y.shape[0], \
        "the size of X must be equal to the size of y"
    assert 0.0 <= test_ratio <= 1.0, \
        "test_ration must be valid"

    if seed:
        np.random.seed(seed)

    shuffled_indexes = np.random.permutation(len(X))

    test_size = int(len(X) * test_ratio)
    test_indexes = shuffled_indexes[:test_size]
    train_indexes = shuffled_indexes[test_size:]

    X_train = X[train_indexes]
    y_train = y[train_indexes]

    X_test = X[test_indexes]
    y_test = y[test_indexes]

    return X_train, X_test, y_train, y_test

  • 此时的目录结构
 ── playML
    ├── __init__.py
    ├── kNN.py                # knn类
    └── model_selection.py    # 测试
  • notebook中调用自己的代码 

 


sklearn中的train_test_split


四.分类准确度

  • 上一节 最后查看分类准确度的代码, 可以封装成函数 metrics.py
import numpy as np


def accuracy_score(y_true, y_predict):
    '''计算y_true和y_predict之间的准确率'''
    assert y_true.shape[0] == y_predict.shape[0], \
        "the size of y_true must be equal to the size of y_predict"

    return sum(y_true == y_predict) / len(y_true)

  • 在knn类中进行调用
...
from .metrics import accuracy_score

class KNNClassifier:

   ...

    def score(self, X_test, y_test):
        """根据测试数据集 X_test 和 y_test 确定当前模型的准确度"""

        y_predict = self.predict(X_test)
        return accuracy_score(y_test, y_predict)

    def __repr__(self):
        return "KNN(k=%d)" % self.k



  • notebook中调用 

  • sklearn中的accuracy_score 


五. 超参数

超参数: 运行机器算法前需要指定的参数, 比如knn算法中的k。机器学习术语:调参一般就是指调试超参数。

模型参数: 算法过程中学习的参数,knn算法没有模型参数

寻找最好的超参数

  • 这里如果k离11很近, 我们必须把k可取的范围扩大

考虑权重的k近邻算法

  • 另外在遇到平票的问题时, 考虑权重就可以解决了.

 

明可夫斯基距离

  • 曼哈顿距离:∑i=1n(xia−xib)11∑i=1n​(xia​−xib​)11​
  • 欧拉距离:(∑i=1n(xia−xib)2)(∑i=1n​(xia​−xib​)2)21​
  • 明可夫斯基距离:(∑i=1n(xia−xib)p)(∑i=1n​(xia​−xib​)p)p1​
  • 当p=1时, 明可夫斯基距离就是曼哈顿距离
  • 当p=2时,明可夫斯基距离就是欧拉距离
  • 所以p也可以作为knn算法的一个超参数

  • 图中的k和p两重循环, 就可以看作网格搜索

六.网格搜索与k近邻算法中更多超参数

sklearn的网格搜索使用


七. 数据归一化

以肿瘤数据为例

  • 肿瘤大小和发现时间,因为单位不同,数值大小差异很大
  • 这样在求距离的时候, 会对结果的正确性造成很大影响 

解决方案

  • 将所有的数据映射到同一尺度

最值归一化

  • 最值归一化 normalization: 把所有数据映射到0-1之间, 公式如下:

 

  • 最值归一化的适用范围: 适合分布有明显边界的情况
  • 受outlier极端值,影响较大

均值方差归一化

  • 均值方差归一化: 把所有数据归一到均值为0方差为1的分布中
  • 适用于:数据分布没有明显的边界;有可能存在极端数据值的情况

实际演示


  • sklearn中也有最值归一化sklearn.preprocessing.MinMaxScaler
  • 但是均值方差归一化 是比较常用的

八. scikit-learn中的Scaler

如何对测试数据集进行归一化?

  • 假如训练数据集得到的均值和方差为: mean_train和 std_train
  • 测试数据集的归一化必须使用训练数据集的均值和方差
  • 测试数据集的归一化: (x_test-mean_train)/std_train
  • 因此需要保存训练数据集得到的均值和方差, scikit-learn中使用Scaler类

 


自己实现一下StandardScaler

preprocessing.py

import numpy as np


class StandardScaler:

    def __init__(self):
        self.mean_ = None
        self.scale_ = None

    def fit(self, X):
        """根据训练数据集X获得数据的均值和方差"""
        assert X.ndim == 2, "The dimension of X must be 2"

        self.mean_ = np.array([np.mean(X[:,i]) for i in range(X.shape[1])])
        self.scale_ = np.array([np.std(X[:,i]) for i in range(X.shape[1])])

        return self

    def transform(self, X):
        """将X根据这个StandardScaler进行均值方差归一化处理"""
        assert X.ndim == 2, "The dimension of X must be 2"
        assert self.mean_ is not None and self.scale_ is not None, \
               "must fit before transform!"
        assert X.shape[1] == len(self.mean_), \
               "the feature number of X must be equal to mean_ and std_"

        resX = np.empty(shape=X.shape, dtype=float)
        for col in range(X.shape[1]):  # 每一列 均值方差归一化
            resX[:,col] = (X[:,col] - self.mean_[col]) / self.scale_[col]
        return resX

  • notebook中调用自己的代码 


九. knn算法总结

优点

  • 可以解决分类问题
  • 天然可以解决多分类问题
  • 思想简单, 效果强大

缺点1:效率低下

  • 如果训练m个样本, n个特征, 则预测
  • 每一个新的数据, 需要O(m*n)的时间复杂度

缺点2:高度数据相关

  • 比如k=3, 则找到的3个近邻数据 中, 恰巧有错误的数据, 就会对结果造成极大的影响

缺点3:预测结果不具有可解释性

  • 很多情况下, 我们希望得到的预测结果 是可以解释的

缺点4: 容易遭受维数灾难

  • 维数灾难: 随着维度的增加,"看似相近"的两个点之间的距离越来越大
  • 实际中很多情况下, 都是非常多维的数据
  • 但也有一个解决办法:降维 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值