0
点赞
收藏
分享

微信扫一扫

【数据挖掘】成为一名合格的scikit-learn调包侠:KNN和KMeans

这次首先要实现的是一个应用于Iris数据集上的KNN分类器,本次我们将:
1. 使用validation_curve()绘制验证曲线
2. 使用10折分层交叉验证
3. 使用准确率作为评价矩阵
4. 使用暴力算法
5. 尝试不同的K值
6. 找到最佳K值

首先进行一些必要的初始设置

from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier 
from sklearn.model_selection import validation_curve
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

iris = load_iris()
X, y = iris.data, iris.target
K_values = [1,3,6,9,12,15,18,21,24]
K_scores = []

先建一个KNN model, 并在我们的数据集上进行拟合。

model = KNeighborsClassifier(algorithm = 'brute')
model.fit(X,y)

参数说明:
algorithm:找近邻的算法。可选的值有:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, 默认为’auto’。

然后使用validation_curve进行评价。

train_scores, test_scores = validation_curve(model, X, y, param_name="n_neighbors", param_range=K_values, cv = 10, scoring = 'accuracy', n_jobs = -1)
train_scores_mean = np.mean(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)

参数说明:
n_neighbors:样本的邻近样本个数。比如K设成3,就是对每个样本点,在特征空间里找离它最近的3个点,根据这3个点的分类来对该样本进行分类。
cv:交叉验证的分段数,如这里设成10就是把整个数据集分成10份,依次选取其中一个作为测试集,其余数据集作为训练集,然后对模型进行相应的计算。这样计算得到的结果是一个1x10的矩阵(10次计算结果),所以我们要再用一次mean来求平均得分。
scoring:模型的评价标准,准确率accuracy是用的比较多的一种。
n_jobs:处理器的使用。这里设为-1则为同时使用所有处理器。

最后我们画一下不同K对应的validation curve来判断模型的最佳参数选择。

plt.figure(1, figsize=(8, 8))
plt.ylabel("Accuracy")
plt.xlabel("Number of Neighbors")
plt.title("K-fold(k=10) CV Curve for KNN on number of neighbors by validation_curve")

plt.plot(K_values, train_scores_mean,marker='o',label="Mean training Acc. from 10-fold CV",
             color="darkorange")
plt.plot(K_values, test_scores_mean,marker='o',label="Mean test Acc. from 10-fold CV",
             color="navy")
plt.grid(True)
plt.legend()
plt.show()

10折交叉验证的validation curve

接下来,我们使用KMeans来对同样的数据集进行聚类。

同样的,先进行一些必要的初始设置。

from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
iris = load_iris()
X, y = iris.data, iris.target
clusters = [2,3,4,5,6,7,8,9,10]
distortion = []

对使用不同聚类个数cluster的模型,我们使用distortion来进行评估。distortion实际上就是SSE(Sum of Squared Error),对每个样本点,我们计算它和它被分类到的cluster中心之间的距离,平方,然后求和,就得到了模型的distortion。这一计算结果可以通过调用 model.inertia_ 来获得。

for i in clusters:
    model = KMeans(n_clusters = i, n_init = 100, random_state = 0)
    model.fit(X,y)
    distortion.append(model.inertia_)

画一下图来看看模型的变化。

plt.plot(clusters, distortion, marker = 'o')

distortion随cluster的变化
可以看到模型在cluster取到8之后,distortion的下降趋于平缓,也就是根据模型的拐点elbow,我们将cluster设置为8比较合适。

最后我们自己来实现一个KNN吧~
必要设置

from sklearn.datasets import load_iris
from sklearn.neighbors import DistanceMetric
iris = load_iris()

创建一个KNN类

class KNN():
    def __init__(self, k=2):
        self.k = k
        self.dist = DistanceMetric.get_metric('euclidean')
    
    def fit(self, X, y):
        if X.shape[0] != len(y):
            print("Error: number of training features does not match labels!")
            return
        self.X = X
        self.y = y 
        
    def predict_proba(self, X_new):
        if X.shape[1] != len(X_new[0]):
            print("Error: dimension of testing features does not match training data!")
            return
        label_probs = [] 
        n = len(X)
        for j in range(len(X_new)):
            #计算数据集中每一个点和所要分类的点之间的距离
            distances = dict()
            for i in range(n):
                cur = 1/(self.dist.pairwise([X[i], X_new[j]])[0][1]**2)
                distances[i] = cur
            distances = sorted(distances.items(), key = lambda x: x[1], reverse = True)
            #计算标签和概率值
            denom = 0
            nom = 0
            label = []
            for p in range(self.k):
                denom += list(distances)[p][1]
                label.append(y[int(list(distances)[p][0])])
            check = set(label)
            cnt = dict()
            for i in check:
                cnt[i] = label.count(i)
            cnt = sorted(cnt.items(), key = lambda x: x[1], reverse = True)
            label_new = list(cnt)[0][0]
            for p in range(self.k):
                if y[int(list(distances)[p][0])] == label_new:
                    nom += list(distances)[p][1]
            prob_new = nom/denom
            zipped = (label_new, prob_new)
            label_probs.append(zipped)
    
        return  label_probs

计算标签时,仍旧选取K个近邻中占比最高的类别作为预测类别,同时输出一个预测的概率值,分母是所有近邻的权值和,分子是和所预测类别一致的近邻的权值和。注意这里我们使用欧几里得距离的平方的倒数作为计算分类概率时的权重。

来做个预测试试。

knn = KNN(k=5)
knn.fit(X,y)
print(knn.predict_proba([[1,2,3,4]]))

#[(1, 0.7857593520080711)]

这次的实践就做到这里~

可以发现KNN和KMeans虽然名字很像,但是还是有很大区别的。

  • KNN是一种有监督的分类算法,而KMeans则是一种无监督的聚类算法。
  • KNN的中心是要预测的样本点,我们从样本点出发去找和它最相邻的K个点;KMeans的中心是cluster,确定了聚类中心后我们去对所有的样本点进行distortion的计算,然后不断地重新放置cluster点来改进模型。
  • 可以发现KNN虽然很简单,但是可能会由于内存占用太多而导致速度很慢,因为我们对于每一个预测点都要计算所有样本点和它的距离。
  • 在使用KMeans时要注意cluster的选择,如果太少效果不好,太多则模型失去意义。

最后附上参考链接,两篇都讲得很清楚。
KNN
KMeans

谢谢观看~

举报

相关推荐

0 条评论