一、KNN原理
全称K-最近邻算法,找到最近的k个邻居,在选取到的k个样本中选取出最近的且占比最高的类别作为预测类别,因此可以看做一种投票机制,依赖少数服从多数的原则,属于局部近似。
优点:1、简单训练快;
2、据周围邻近样本的标签进行分类的,所以适合于样本类别交叉或重叠较多的情况;
3、对异常值不敏感;
缺点:1、计算量大预测慢;
2、无法解决高维问题;
3、当不同类别的样本数目差异较大时,数目较大的那一类别对KNN判别结果影响较大,因此可能产生误判;
4、不存在概率评分,仅根据样本标签判别;
二、k-Nearest Neighbor (kNN) exercise
1、kNN分类器的两个阶段
- 在训练期间,分类器获取训练数据并简单地记住它
- 在测试期间,kNN通过与所有训练图像进行比较并转移k个最相似训练样本的标签来对每个测试图像进行分类
- k值是交叉验证的
2、 数据集下载
cd /assignment1/cs231n/datasets
# 自动下载
!bash get_datasets.sh
# 手动下载
wget http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz -O cifar-10-python.tar.gz
tar -xzvf cifar-10-python.tar.gz
rm cifar-10-python.tar.gz
wget http://cs231n.stanford.edu/imagenet_val_25.npz
3、Setup code
ipynb文件配置matplotlib参数
%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
在调试的过程中,如果代码发生更新,实现ipython中引用的模块也能自动更新。
%load_ext autoreload
%autoreload 2
4、加载原始cifar10数据集
cifar10_dir = 'cs231n/datasets/cifar-10-batches-py'
# 清理变量以防止多次加载数据
try:
del X_train, y_train
del X_test, y_test
print('Clear previously loaded data.')
except:
pass
X_train, y_train, X_test, y_test = load_CIFAR10(cifar10_dir)
# 打印出训练和测试数据的大小
# 训练集5w数据32*32*3,测试集1w数据32*32*3
print('Training data shape: ', X_train.shape)
print('Training labels shape: ', y_train.shape)
print('Test data shape: ', X_test.shape)
print('Test labels shape: ', y_test.shape)
输出:
Training data shape: (50000, 32, 32, 3)
Training labels shape: (50000,)
Test data shape: (10000, 32, 32, 3)
Test labels shape: (10000,)
5、可视化数据集中的一些示例
classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
num_classes = len(classes)
samples_per_class = 7
for y, cls in enumerate(classes):
# np.flatnonzero先将数组扁平化,然后将非零元素的索引以array的形式返回;这里返回y_train中值为y的索引
idxs = np.flatnonzero(y_train == y)
# np.random.choice从数组中随机抽取元素;这里随机抽取samples_per_class=7个
idxs = np.random.choice(idxs, samples_per_class, replace=False)
for i, idx in enumerate(idxs):
plt_idx = i * num_classes + y + 1
plt.subplot(samples_per_class, num_classes, plt_idx)
plt.imshow(X_train[idx].astype('uint8'))
plt.axis('off')
if i == 0:
plt.title(cls)
plt.show()
6、数据子采样提升效率
num_training = 5000
mask = list(range(num_training))
X_train = X_train[mask]
y_train = y_train[mask]
num_test = 500
mask = list(range(num_test))
X_test = X_test[mask]
y_test = y_test[mask]
# Reshape 5000*32*32*3 => 5000*3072
X_train = np.reshape(X_train, (X_train.shape[0], -1))
X_test = np.reshape(X_test, (X_test.shape[0], -1))
print(X_train.shape, X_test.shape)
输出:
(5000, 3072) (500, 3072)
7、创建一个kNN分类器
from cs231n.classifiers import KNearestNeighbor
# Create a kNN classifier instance.
# 训练一个kNN分类器是一个noop
# 分类器只记住数据,不做进一步处理
classifier = KNearestNeighbor()
classifier.train(X_train, y_train)
8、用kNN分类器对测试数据进行分类
- 首先,我们必须计算所有测试样本和所有训练样本之间的距离。
- 给定这些距离,对于每个测试示例,我们找到k个最近的示例,并让它们为标签投票
9、kNN实现
- compute_distances_two_loops
原理:双循环就是分别计算每个训练数据和每个测试数据之间的距离,第一层循环是对所有测试数据的循环,第二层循环是对所有训练数据的循环,使用np.linalg.norm()函数。
def compute_distances_two_loops(self, X):
"""
使用训练数据和测试数据上的嵌套循环来计算X中的每个测试点和self.X_train中的每个训练点之间的距离。
输入:
- X:包含测试数据的shape的numpy数组(num_test,D)
返回:
- dists:形状的numpy数组(num_test,num_train ),其中dists[i,j]
是第I个测试点和第j个训练点之间的欧几里德距离点。
"""
num_test = X.shape[0]
num_train = self.X_train.shape[0]
dists = np.zeros((num_test, num_train))
for i in range(num_test):
for j in range(num_train):
#####################################################################
# TODO: #
# Compute the l2 distance between the ith test point and the jth #
# training point, and store the result in dists[i, j]. You should #
# not use a loop over dimension, nor use np.linalg.norm(). #
#####################################################################
# *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
pass
# *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
return dists
- compute_distances_one_loops
原理:单次循环是将每个测试数据通过一次计算就得到和所有训练数据的距离,其利用了broadcast原理。注意参数axis的设置,axis=1是行相减。
def compute_distances_one_loop(self, X):
"""
使用单层循环循环来计算X中的每个测试点和self.X_train中的每个训练点之间的距离。
输入输出和compute_distances_two_loops相同
"""
num_test = X.shape[0]
num_train = self.X_train.shape[0]
dists = np.zeros((num_test, num_train))
for i in range(num_test):
#######################################################################
# TODO: #
# Compute the l2 distance between the ith test point and all training #
# points, and store the result in dists[i, :]. #
# Do not use np.linalg.norm(). #
#######################################################################
# *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
pass
# *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
return dists