最基础的分类算法-k近邻算法 kNN

01 kNN 基础

import numpy as np

import matplotlib.pyplot as plt

kNN 基础概念

k近邻算法,全称是 K Nearest Neighbors,knn算法是一个非常适合大家入门机器学习的算法。这是因为knn算法他的思想极度简单,不仅如此,knn这么一个思想简单的算法,它的效果也出奇的好。 该方法的思路是:在特征空间中,如果一个样本附近的k个最近(即特征空间中最邻近)样本的大多数属于某一个类别,则该样本也属于这个类别。 给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居), 这K个实例的多数属于某个类,就把该输入实例分类到这个类中。

这些数据点我们已经知道。其实它是分布在一个特征空间中的,与一个肿瘤患者相关的数据,横轴代表的特征是肿瘤患者发现的肿瘤的大小。纵轴是发现肿瘤的时间。对于每个患者,肿瘤的大小和发现肿瘤的时间构成了该特征平面中的一个点。所以对于这一点,恶性肿瘤用蓝色表示,良性肿瘤用红色表示,这样我们一共有8个数据点的初始信息。

有了初始信息,如果现在新来的一个病人,他在特征空间中对应的位置在绿色的点,它最有可能是良性肿瘤患者还是恶性肿瘤患者呢。首先我们必须取一个k值,在这里呢,我们假设k等于3,如果k等于3的话,对于每一个新的数据点,k近邻算法做的事情就是在所有的这些点中寻找离这个新的点最近的三个点

然后,这些最近的点以他们自己的lable自己的结果进行投票,对于这个新的点来说,离他最近的三个点都是代表恶性肿瘤的蓝色的点,所以蓝色对红色是3:0。因此,k近邻算法就说这个新的点有很高的概率是一个蓝色的点, k近邻算法它的本质其实是认为两个样本,如果他们足够的相似的话,那么它就有更高的概率属于同一个类别。当然,可能我们只看离他最近的那一个样本是不靠谱的,所以在这里我们多看几个样本,一共看k个样本。看和他最相似的k个样本中哪个类别最多,我们就认为这个新的样本最有可能属于哪个类别。在这里,我们描述两个样本是否相似,这个相似性就是靠两个样本在这个特征空间中的距离来进行描述的

实现自己的 kNN

创建一个简单的测试用例

制作了一个假的数据集,每个样本的数据特征raw_data_X,每个样本所属的类别raw_data_y

raw_data_X = [[3.393533211, 2.331273381],
              [3.110073483, 1.781539638],
              [1.343808831, 3.368360954],
              [3.582294042, 4.679179110],
              [2.280362439, 2.866990263],
              [7.423436942, 4.696522875],
              [5.745051997, 3.533989803],
              [9.172168622, 2.511101045],
              [7.792783481, 3.424088941],
              [7.939820817, 0.791637231]
             ]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]

所有原始数据都用作训练集

X_train = np.array(raw_data_X)
y_train = np.array(raw_data_y)

散点图

plt.scatter(X_train[y_train==0,0], X_train[y_train==0,1], color='g')
plt.scatter(X_train[y_train==1,0], X_train[y_train==1,1], color='r')
plt.show()

预测: 加入来了新的数据x,预测属于哪一类

x = np.array([8.093607318, 3.365731514])

plt.scatter(X_train[y_train==0,0], X_train[y_train==0,1], color='g')
plt.scatter(X_train[y_train==1,0], X_train[y_train==1,1], color='r')
plt.scatter(x[0], x[1], color='b')
plt.show()

kNN的过程

计算新的x点和原来每一个点之间的距离。欧拉距离, **2求平方

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

我们真正想知道的是离他最近的到底是哪些点,argsort是对一个数组进行排序,但是返回的是相应的排序后结果的索引 我们使用这种方式就拿到了离我们新来的这个x,从进到远相应的点是哪些。 

nearest = np.argsort(distances)
k = 6

最近是这六个点,他们所属的y作标

topK_y = [y_train[neighbor] for neighbor in nearest[:k]]

传入一个数组。得到的结果其实就是统计这个数组中的元素和这个元素出现的频率

from collections import Counter
votes = Counter(topK_y)
predict_y = votes.most_common(1)[0][0]

我们只取一个元素,这一个元素是一个列表,我们取它的第0个元素,是一个元组,对于这个元组来说,第一项是对应的,这个元素是谁,第二项对应的是它的票数有多少,可以看这个值1那么这其实就是我们的knn算法的预测结果,我们的训练数据即告诉我们新来的这个x,它的类别最有可能是1,那么是一个恶性肿瘤的患者可能性更大一些。

定义一个函数

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]

02 scikit-learn 中的 kNN

使用scikit-learn中的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

03 测试算法

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets 

iris = datasets.load_iris()
iris.keys()
X = iris.data
y = iris.target
X.shape
y.shape

 train_test_split

将一部分数据用于训练,另一部分用于测试。

array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
shuffled_indexes = np.random.permutation(len(X))
shuffled_indexes
array([ 71,  28, 149,  59,  90,  86,  77,  33,  40,  44,  65, 109, 106,
        61,  76,   3, 110,  95,  43, 105,  46,  18,   1, 120,  93, 139,
        54,  22,  91,   9, 131,  49,  31,  50,  57,  99,  12,  69,  82,
        21,  29,  78,  66, 142, 119, 103,  84,  52,  14, 127,  87,   4,
       107, 134,  72, 101,  70, 125,  45,  13,  16, 118,   5, 114,   2,
       135,  63,  67,  24,  94,  88,  75,  83, 102,  10,  51,  25,  81,
        37, 123, 113,  38,  92, 138,  68, 140,  26, 116, 145, 104, 100,
       126,   0, 144,  55,  41,  27, 124,  34,  15,  36, 112,  58,  89,
        85,  47,  23,  30,  64, 147,  56,  48,  79, 137, 136,  60, 130,
        53,  39,  42, 115,  98, 132, 133,   7,  96,  74, 122, 148, 108,
        19,  11, 117, 129,  32,   6, 143,  80,   8,  73, 111, 141, 128,
        62, 146,  17, 121,  35,  97,  20])

定义一个自己的库model_selection.py 

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

package

from playML.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y)

测试我们的算法

from playML.kNN import KNNClassifier

my_knn_clf = KNNClassifier(k=3)
my_knn_clf.fit(X_train, y_train)
y_predict = my_knn_clf.predict(X_test)

y_predict
array([1, 2, 1, 1, 0, 2, 0, 0, 2, 0, 1, 2, 0, 2, 0, 0, 2, 1, 0, 1, 1, 0, 0, 1, 1, 2, 2, 2, 0, 2])
y_test
array([1, 2, 1, 1, 0, 2, 0, 0, 2, 0, 1, 2, 0, 2, 0, 0, 2, 2, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 0, 2])
sum(y_predict == y_test)
28
sum(y_predict == y_test) / len(y_test)
0.9333333333333333

04 分类准确度

使用sklearn 中为我们提供的一个手写数字数据库。我们使用load_digits函数加载这个数据,将这个原始的数据存入digits 变量中。在这个数据库中一共有5620个这样的数据,每个数据有64个属性,或者叫做特征,这64个特征依次代表的是一个8×8这样的一个图像。8×8的图像一共有64个像素点,那么相应的每一个像素点它的颜色的深度取值范围是在0-16之间。对于每一个样本,它所属的是这10个类别之一,我们主要关注的一个是原始数据的特征矩阵,那么这个特征矩阵就是在digits.data属性里

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

digits = datasets.load_digits()
digits.keys()
 
X = digits.data
X.shape
y = digits.target
y.shape

some_digit = X[666]
some_digit_image = some_digit.reshape(8, 8)

 对some_digit进行一下可视化

import matplotlib
import matplotlib.pyplot as plt
plt.imshow(some_digit_image, cmap = matplotlib.cm.binary)
plt.show()

0.9777158774373259

首先需要对原始的数据机进行train_test_split,在这里调用我们自己的playML.model_selection库,测试数据级的比例是20%

from playML.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_ratio=0.2)

将我们的KNNClassifier这个类给加载进来 ,让k等于3,下一步就要对进行fit这个操作,predict这个函数进行具体的预测

from playML.kNN import KNNClassifier

my_knn_clf = KNNClassifier(k=3)
my_knn_clf.fit(X_train, y_train)
y_predict = my_knn_clf.predict(X_test)

sum(y_predict == y_test) / len(y_test)

要比对一下我们的这个预测结果和真正的外y_test这个预测结果,这两个预测结果是否一致。

输出   0.9777158774373259

封装我们自己的accuracy_score

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)

05 超参数

我们在之前所使用的knn算法,需要传入k值,这个值被封装成了knabersclassfair中的一个参数。那么之前我们都是直接随意的传一个值,但是对于这个参数究竟传什么样的值是最好的呢。这就涉及到机器学习领域一个非常重要的问题,就是所谓的超参数问题。

超参数,就是指在我们运行机器学习算法之前需要指定的这个参数。knn算法中的k就是一个最为典型的超参数,在这里和超参数相对应的一个概念就是的模型参数。超参数是在算法运行前决定的那些参数,而模型参数的则是在算法的过程中学习的属于这个模型的参数。不过在knn算法中是没有模型参数的,无论是现象回归法还是逻辑回归法。都包含有大量的模型参数。

机器学习算法工程师要做的一项非常重要的工作就是所谓的参数调优。一般来说,参数调优的这个参数也是一个超参数,因为它是我们在算法运行之前需要决定的参数。我们通常如何找到一个好的超参数?首先,由于机器学习算法要应用于不同的领域,领域知识在不同的领域是有意义的。通常面对不同领域的不同问题,有可能最佳的超参数是不同的,通过该领域的知识可以获得最佳的超参数。另一点是经验值。对于很多问题,会有很多比较好的经验值供人们使用。

import numpy as np
from sklearn import datasets

digits = datasets.load_digits()
X = digits.data
y = digits.target

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=666)

from sklearn.neighbors import KNeighborsClassifier

knn_clf = KNeighborsClassifier(n_neighbors=3)
knn_clf.fit(X_train, y_train)
knn_clf.score(X_test, y_test)

输出 0.9916666666666667

寻找最好的k

best_score = 0.0
best_k = -1
for k in range(1, 11):
    knn_clf = KNeighborsClassifier(n_neighbors=k)
    knn_clf.fit(X_train, y_train)
    score = knn_clf.score(X_test, y_test)
    if score > best_score:
        best_k = k
        best_score = score
        
print("best_k =", best_k)
print("best_score =", best_score)

在初始的时候,设置一个当前我们已经找到的最好的准确率的值,初始化成一个最小值就是0,其次再设置一个变量,存储已经找到的最好的一个k,初始的值为-1,比如说就来寻找从1-10认置的第二个参数传进去11那么这十个k哪个k最适合我们当前的这个手写识别的任务,在这个循环过程中,每一次传入当前循环所循环到的k值。

output

best_k = 3

best_score = 0.9916666666666667

如果我们找到的这个最好的k的值是10的话,那么此时我们有必要。对10以上的数再进行一下搜索,这是因为通常来讲,我们不同的参数决定了不同的这个分类的准确率,它们之间是呈现一个连续的变化的。如果我们找到的最好的那个参数值,他在我们寻找的这个边界上的时候就意味着有可能有更好的值在这个边界的外面,所以要稍微的拓展一下我们的搜索的范围。

考虑距离?不考虑距离?

实际上,knn这个算法中还潜藏着一个非常重要的超参数,那么这个超参数与此同时也对应着knn算法的一个可以说是不太一样的用法

比如说当k取3的时候,就找到离我们当前要判断的这个节点,比如说是绿色的这个节点,最近的三个节点。比如说是这一个红色的节点和两个蓝色的节点,然后进行投票,由于离我们的这个绿色的节点最近的三个节点中有两个蓝色的节点,一个红色的节点。所以蓝色的节点就获胜了,那么这个过程我们只考虑了离绿色的节点最近的三个节点。可是却忽略了最近的三个节点相应的距离是多少,那么可以想象一下这个绿色的节点其实离红色节点是最近的。虽然这两个蓝色的节点是相应的离这个绿色节点刺进的两个节点,可是还是离这个绿色的节点比较远,所以在这种情况下是不是红色的这个节点。他的这个票权重应该比这两个蓝色的节点的这个票相对的要重一些呢?那么这就是配件连算法的另外一个用法就是考虑了距离的权重,那么通常而言,我们考虑这个距离的权重的时候,是将这个距离的倒数作为权重,在这种情况下,这个距离越近,它的倒数相应的就越大。这个权重值相应的也就越大。     

使用普通的knn算法的话,比如说k等于3的话,那么如果我们分类的样本数最终也有三类的话,就很有可能产生平票的情况,比如说。绿色的节点,最近的三个节点分别是红,蓝,紫,这样的三个节点,这三个节点各有一票。那么我们之前的算法可能只能随机的选出一个节点来作为结果,那么这其实是不合理的,而当我们考虑了距离之后,就可以非常好的解决平票的问题。

best_score = 0.0
best_k = -1
best_method = ""
for method in ["uniform", "distance"]:
    for k in range(1, 11):
        knn_clf = KNeighborsClassifier(n_neighbors=k, weights=method)
        knn_clf.fit(X_train, y_train)
        score = knn_clf.score(X_test, y_test)
        if score > best_score:
            best_k = k
            best_score = score
            best_method = method
        
print("best_method =", best_method)
print("best_k =", best_k)
print("best_score =", best_score)
best_method = uniform
best_k = 3
best_score = 0.9916666666666667

曼哈顿距离

是指两个点在每个维度上相应的这个距离的和,在平面中对于这两个黑色的点,曼哈顿的距离就是指他们在x方向上的差值加上在y的方向上的差值

闵可夫斯基距离

在这个数字中,当p等1的时候,米可夫斯基距离就相当于是曼哈顿距离,而当p等于2的时候,米可夫斯基距离就相当于是欧拉距离,当p更大的时候。就相应的是其他距离的表示方式

在这种情况下,其实相当于又获得了一个新的超参数。这个超参数就是p,那么对于明可夫斯基距离,进而就可以在我们的算法中对这个超算数相应的进行搜索,来看对于我们的问题p这个值取谁更好。

sk_knn_clf = KNeighborsClassifier(n_neighbors=4, weights="distance", p=1)
sk_knn_clf.fit(X_train, y_train)
sk_knn_clf.score(X_test, y_test)

output

0.9833333333333333

搜索明可夫斯基距离相应的p

best_score = 0.0
best_k = -1
best_p = -1

for k in range(1, 11):
    for p in range(1, 6):
        knn_clf = KNeighborsClassifier(n_neighbors=k, weights="distance", p=p)
        knn_clf.fit(X_train, y_train)
        score = knn_clf.score(X_test, y_test)
        if score > best_score:
            best_k = k
            best_p = p
            best_score = score
        
print("best_k =", best_k)
print("best_p =", best_p)
print("best_score =", best_score)

output

best_k = 3
best_p = 2
best_score = 0.9916666666666667

07 数据归一化处理

之前使用knn这个算法来完成分类这个任务的时候,其实少做了一个非常重要的一步,就是数据规一化Feature Scaling。首先来看一看为什么要进行数据的归一化,在这里可以看到,依然是使用这个肿瘤的例子

假设有两个特征,一个特征的是肿瘤的大小的单位是厘米。另外一个特征是这个肿瘤发现的时间,单位是天,对于样本1来说,它这个肿瘤的大小可能是1厘米。发现的时间200天,对于样本2来说,肿瘤大小5厘米,但是相应的发现了100天。那么这两个样本之间的距离是怎么样的呢?如果我们使用欧拉距离的话,显然就发现了这个距离的大小,被发现的时间所主导了。这是因为我们发现的时间间隔了100天,而肿瘤的大小的这个厘米数呢只相差了4厘米。虽然在我们的这个样本数据中,5和1相差五倍之多,而200和100呢只相差了两倍。但是由于我们的量纲不同,导致了最终的距离,其实主要衡量的是发现的时间的天数之间的差值。这是因为肿瘤大小数据之间的差值太小了,很容易就发现,如果我们把这个发现时间相对应的单位调整成年的话,那么200天相当于大概是0.55年,而100天的对应大概是0.27年,此时这两个样本时间的距离一下子被肿瘤的大小这个特征所主导,这是因为我们使用年这个单位的时候,0.5-0.25,这个数值又变得特别的小,而在这个量纲下,5-1又变得非常大。那么,很显然如果我们不进行一些数据的基本处理的话,我们这样直接计算出两个样本之间的距离,很有可能是有偏差的,不能非常好的同时反映这个样本中每一个特征的重要程度,正是因为如此,我们要对数据进行规一化的处理。

它的作用就是将我们的所有的数据都映射到同一个尺度中,通常这种映射方式最简单的方式被称之为最值规一化,就是把我们的所有的数据都映射到0-1之间。

那么这个映射的方式其实也非常的简单,就是对于每一个特征,求出这个特征对应的最大值和最小值,然后对于我们的每一个特征点,用x这个特征点减去xmin,再除以xmax减去xmin就好了,我们可以。这相当于是把整个数据先映射到了从xmax减去xmin这个范围中,然后我们来找,对于我们的这个x而言,它相应的相比于我们整个这个范围所占的比例是多少,也就是用x减去xmin,在除以整个范围的长度,这样一来,对于每一个x,就都将它映射到了0-1之间了。对于这样的一个最值规一化的方法normalization 

这样其实是比较简单的一个做法,它适用于分布有明显的边界的情况。举个例子比如说我们有一组特征是学生的考试分数,那么分数就是显然有明显的边界,它的最左值可能就是0分,它的最幼稚的满分可能就是100分。再比如说一个常见的例子,就是对于我们图像的像素的每一个像素点,它的颜色范围通常也是有明显的边界的,通常是0-255之间,那么这些情况是适合使用最值归一化的,那么最值归一化它有一个缺点就是受outlier的影响比较大,如果我们的数据没有明显边界的话,那么最典型的例子就是比如说收入的分布。他不是一个明显的,所有的人都在0到多少万元的,这个收入范围中,有的人的收入是极其高的,那么一旦有了outlier对于使用我们的最值故意化来说影响就比较大。如果大多数人,比如说月收入都是1万块钱,有一个人,他的月收入是100万块钱,我们将它映射到01之间的话,那么那个月收入为100万块钱的这个特征肯定是一了,而大多数人他的收入只是1万块钱左右,就都聚集在0.01这个数值附近。那么我们说这个数据的映射的结果呢是不够好的

相应的一个改进的方式是使用均值方差归一化standerdesation。他做的事情是把所有的数据归一到均值为0方差为1的分布中,换句话说,这样做完的结果是我们的数据并不保证在01之间,但是所有的数据,它的均值是在0个位置,同时整体数据的方差为1,那么这种规一化的方式适用于我们的数据分布没有明显的边界这种情况,比如说我刚才举的这个收入的例子,如果我们的数据分布有可能存在极端值,也就是outlier的话,那么使用这种方式相对比较好。除非像之前举的例子。学生的考试分数,或者是图像的像素,这种非常明确的,我们的特征分布是有边界的情况,那么一般情况我们都使用均值方差归一化就可以。

计算方法是从每个特征值中减去该特征值对应的平均值,再除以该特征值对应的方差。

import numpy as np
import matplotlib.pyplot as plt

最值归一化 Normalization 

x = np.random.randint(0, 100, 100) 
(x - np.min(x)) / (np.max(x) - np.min(x))
array([0.70408163, 0.46938776, 0.04081633, 0.93877551, 0.37755102,
       0.42857143, 0.43877551, 0.08163265, 0.8877551 , 0.83673469,
       0.96938776, 0.26530612, 0.04081633, 0.67346939, 0.60204082,
       0.20408163, 0.31632653, 0.59183673, 0.6122449 , 0.02040816,
       0.7755102 , 0.13265306, 0.97959184, 0.3877551 , 0.55102041,
       0.7244898 , 0.66326531, 0.        , 0.91836735, 0.67346939,
       1.        , 0.25510204, 0.16326531, 0.70408163, 0.19387755,
       0.14285714, 0.52040816, 0.2755102 , 0.2244898 , 0.76530612,
       0.86734694, 0.35714286, 0.1122449 , 0.51020408, 0.6122449 ,
       0.45918367, 0.83673469, 0.69387755, 0.48979592, 0.16326531,
       0.7755102 , 0.33673469, 0.78571429, 0.97959184, 0.09183673,
       0.10204082, 0.55102041, 0.51020408, 0.48979592, 0.21428571,
       0.64285714, 0.02040816, 1.        , 0.81632653, 0.85714286,
       0.87755102, 0.58163265, 0.2755102 , 0.96938776, 0.91836735,
       0.97959184, 0.98979592, 0.66326531, 0.85714286, 0.84693878,
       0.73469388, 0.76530612, 0.05102041, 0.30612245, 0.32653061,
       0.7755102 , 0.2755102 , 0.09183673, 0.85714286, 0.75510204,
       0.07142857, 0.87755102, 0.21428571, 0.18367347, 0.17346939,
       0.89795918, 0.78571429, 0.15306122, 0.67346939, 0.89795918,
       0.84693878, 0.17346939, 0.14285714, 0.89795918, 0.78571429])
X = np.random.randint(0, 100, (50, 2))
X = np.array(X, dtype=float)
X[:,0] = (X[:,0] - np.min(X[:,0])) / (np.max(X[:,0]) - np.min(X[:,0]))
X[:,1] = (X[:,1] - np.min(X[:,1])) / (np.max(X[:,1]) - np.min(X[:,1]))
plt.scatter(X[:,0], X[:,1])
plt.show()

均值方差归一化 Standardization

X2 = np.random.randint(0, 100, (50, 2))
X2 = np.array(X2, dtype=float)
X2[:,0] = (X2[:,0] - np.mean(X2[:,0])) / np.std(X2[:,0])
X2[:,1] = (X2[:,1] - np.mean(X2[:,1])) / np.std(X2[:,1])
plt.scatter(X2[:,0], X2[:,1])
plt.show()

 08 Scikit-learn中的Scaler

对于原始数据集,我们将其拆分为训练数据集和测试数据集。如果我们想用归一化数据训练我们的模型,显然我们首先需要对训练数据进行归一化处理。例如,我们对均值方差进行归一化的方法

将这样的训练数据题用于训练模型。最终我们要使用我们获得的这个模型来预测数据的话,对于这个测试数据及我们相应的也要进行归一化处理。将测试数据集使用训练数据集得到的mean_train和std_train相应的进行归一化。

最主要的原因就是在于我们在这里划分出了一部分原始数据作为测试数据集。对于这个测试集,我们确实很容易得到它的均值和方差,但是不要忘了我们训练出这个模型是为了让它使用在真实的环境中,可是很多时候,在真实的环境中我们是无法得到所有的测试数据相应的均值和方差的,举一个简单的例子。比如说对于鸢尾花识别来说,虽然我们可以得到测试数据中所有的鸢尾花相应的平均的特征,但是在实际使用的时候,只是每次来了一朵花,那么请问来的这一朵花对应的均值是多少?方差是多少呢。我们是无法获得这样的统计数据的,因此实际上,在真正使用中来了一朵新的鸢尾花,我们要将这朵新的鸢尾花,它的特征进行归一化,只能让它减去我们训练数据集再除以训练数据集所对应的这个方差,另外一点,其实数据进行规一化其实也是我们算法本身的一部分。换句话说,我们可以理解成,我们的这个算法就包括把所有的数据减去mean_train再除以std_train。针对后面来的所有的数据,我们应该也使用同样的方式进行处理,然后来测试它的准确度,得到的才是我们真正的,我们自己做的这个算法,它对应的准确度。

我们需要保存我们的训练数据以及由此产生的均值和方差。

import numpy as np
from sklearn import datasets

iris = datasets.load_iris()
import numpy as np

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=666)

scikit-learn中的StandardScaler

from sklearn.preprocessing import StandardScaler 

standardScalar = StandardScaler() 
standardScalar.fit(X_train)
standardScalar.transform(X_train)
X_train = standardScalar.transform(X_train)
X_test_standard = standardScalar.transform(X_test) 

使用归一化后的数据进行knn分类

from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier(n_neighbors=3)
knn_clf.fit(X_train, y_train)
knn_clf.score(X_test_standard, y_test)
# 此时不能传入没有归一化的数据
knn_clf.score(X_test, y_test)

Scikit-Learn中的最值归一化

MinMaxScaler: sklearn.preprocessing.MinMaxScaler — scikit-learn 1.0.2 documentation[0]

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2022年5月6日
下一篇 2022年5月6日

相关推荐