@使用NumPy从头开始进行K最近邻分类( 二 )


假设我们的测试数据点有5个最近邻 , 其中3个属于A类 , 其中2个属于B类 。 我们不考虑neighbors之间的距离 , 因为大部分neighbors都是A类的 , 所以我们认为测试数据点属于A类 。 但是 , 如果选择权重作为距离 , 那么这意味着neighbors的距离确实很重要 。 因此 , 最接近测试数据点的任何neighbor都具有与其距离的倒数成比例的最大权重(投票) 。 因此 , 对于前面的例子 , 如果属于类A的那2个点比其他3个点更接近测试数据点 , 那么 , 这个事实本身可能会在决定数据点的类标签时起到很大的作用 。
在predict函数中 , 如果权重是一致的 , 则很容易预测数据点的标签 。 首先 , 我们获得neighbors的索引 , 然后使用这些索引从训练数据集中获取其相应的类标签 。 neighbors中的每一行对应于每个测试数据点具有的neighbors集合 。 然后 , 我们使用numpy的 bincount函数查找类标签的出现次数 , 并获取与预测的类标签对应的最大出现次数的索引 。
这里 , 我们找到neighbors距离的均值倒数 , 并为每个测试数据点计算类概率 。
def score(X_test, y_test): y_pred = predict(X_test) return float(sum(y_pred == y_test))/ float(len(y_test))score函数作为一个非常简单的准确度指标广泛用于分类问题 。 我们只是返回正确分类标签的百分比 。 这很简单!
from sklearn.datasets import make_classification X, y = make_classification(n_samples = 1000, n_features=2, n_redundant=0, n_informative=2, n_clusters_per_class=1, n_classes=3, random_state=21) mu = np.mean(X, 0) sigma = np.std(X, 0) X = (X - mu ) / sigma我们利用sklearn.dataset的make_classification函数填充数据集 。 然后 , 我们通过减去平均值然后除以标准差来对每个数据点进行归一化 。
data = http://news.hoteastday.com/a/np.hstack((X, y[:, np.newaxis])) np.random.shuffle(data) split_rate = 0.7 train, test = np.split(data, [int(split_rate*(data.shape[0]))]) X_train = train[:,:-1] y_train = train[:, -1] X_test = test[:,:-1] y_test = test[:, -1] y_train = y_train.astype(int) y_test = y_test.astype(int)创建数据集后 , 我们在训练阶段实现了随机拆分 , 从而获得了训练和测试集 。
kneighbors(X_test)
@使用NumPy从头开始进行K最近邻分类
本文插图
现在该测试我们的knn实现了 。
predict(X_test)
@使用NumPy从头开始进行K最近邻分类
本文插图
正如预期的那样 , predict函数输出测试数据的预测类标签 。
score(X_test, y_test) #0.99看起来我们的实现做得还可以 , 因为它的精确度很高 。
K最近邻的类实现
import numpy as np class KNearestNeighbors(): def __init__(self, X_train, y_train, n_neighbors=5, weights='uniform'): self.X_train = X_train self.y_train = y_train self.n_neighbors = n_neighbors self.weights = weights self.n_classes = 3 def euclidian_distance(self, a, b): return np.sqrt(np.sum((a - b)**2, axis=1)) def kneighbors(self, X_test, return_distance=False): dist = [] neigh_ind = [] point_dist = [self.euclidian_distance(x_test, self.X_train) for x_test in X_test] for row in point_dist: enum_neigh = enumerate(row) sorted_neigh = sorted(enum_neigh, key=lambda x: x[1])[:self.n_neighbors] ind_list = [tup[0] for tup in sorted_neigh] dist_list = [tup[1] for tup in sorted_neigh] dist.append(dist_list) neigh_ind.append(ind_list) if return_distance: return np.array(dist), np.array(neigh_ind) return np.array(neigh_ind) def predict(self, X_test): if self.weights == 'uniform': neighbors = self.kneighbors(X_test) y_pred = np.array([ np.argmax(np.bincount(self.y_train[neighbor])) for neighbor in neighbors ]) return y_pred if self.weights == 'distance': dist, neigh_ind = self.kneighbors(X_test, return_distance=True) inv_dist = 1 / dist mean_inv_dist = inv_dist / np.sum(inv_dist, axis=1)[:, np.newaxis] proba = [] for i, row in enumerate(mean_inv_dist): row_pred = self.y_train[neigh_ind[i]] for k in range(self.n_classes): indices = np.where(row_pred == k) prob_ind = np.sum(row[indices]) proba.append(np.array(prob_ind)) predict_proba = np.array(proba).reshape(X_test.shape[0], self.n_classes) y_pred = np.array([np.argmax(item) for item in predict_proba]) return y_pred def score(self, X_test, y_test): y_pred = self.predict(X_test) return float(sum(y_pred == y_test)) / float(len(y_test))在实现所有必要的函数之后 , 很容易创建knn的类实现 。 这里只有新添加的_init__函数 。