目录

  • 准备工作
  • KNN
  • 交叉验证

对于k最邻近算法(KNN):

  1. 训练时,分类器记住所有的训练数据;
  2. 测试时,每一个测试图像都要和所有的训练图像计算距离,然后选取距离最近的k个图像,最后选取k个图像中出现次数最多的类标签作为输出(预测标签)。

准备工作

为jupyter notebook运行一些准备代码

# 使python2.x也能使用print()
from __future__ import print_function
# 之后需要随机选7张图片
import random
import numpy as np
# 导入数据集
from cs231n.data_utils import load_CIFAR10
# 为画图做准备
import matplotlib.pyplot as plt# 这是使matplotlib图像出现jupyter notebook里,而不是出现在新窗口的一个小技巧
%matplotlib inline
# 设置画图的默认大小
plt.rcParams['figure.figsize'] = (10.0, 8.0)
# 最近邻差插值: 像素为正方形
plt.rcParams['image.interpolation'] = 'nearest'
# 使用灰度输出而不是彩色输出
plt.rcParams['image.cmap'] = 'gray'# 在执行用户代码前,重新装入软件的扩展和模块。autoreload意思是自动重新装入。无参:装入所有模块。
# 修改完.py文件后不需要重新从头运行,只需要重新装载修改过的函数即可
%load_ext autoreload
%autoreload 2

加载CIFAR-10数据

cifar10_dir = 'cs231n/datasets/cifar-10-batches-py'
# 清除变量,以防加载多次(可能导致内存问题)
try:del X_train, y_traindel X_test, y_testprint('Clear previously loaded data.')
except:pass# 加载数据
X_train, y_train, X_test, y_test = load_CIFAR10(cifar10_dir)
# 输出训练数据和测试数据的大小
print('Training data shape: ', X_train.shape)
# Training data shape:  (50000, 32, 32, 3)
print('Training labels shape: ', y_train.shape)
# Training labels shape:  (50000,)
print('Test data shape: ', X_test.shape)
# Test data shape:  (10000, 32, 32, 3)
print('Test labels shape: ', y_test.shape)
# Test labels shape:  (10000,)

每个类可视化一些图像

# 类标签
classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
# 类的个数(10)
num_classes = len(classes)
# 每个类的样例
samples_per_class = 7
# y是索引值(0-9),cls是名字('plane'等)
for y, cls in enumerate(classes):# 在训练数据标签y_train中记录标签为y的下标(y_train中存放的是数字)idxs = np.flatnonzero(y_train == y)# 同一标签的图片中随机选7张(记录的是下标)idxs = np.random.choice(idxs, samples_per_class, replace=False)# i代表该类选出来的第i张图片,idx是该图片在训练数据集里的下标for i, idx in enumerate(idxs):# 计算图片所在的位置plt_idx = i * num_classes + y + 1# 总共是7行10列的图片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()

可视化结果:

为了更高效地执行代码,我们只取样部分数据。选取5000张测试图片,500张测试图片。

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]

把每张32*32*3的图片变为1*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)

KNN

创建一个KNN分类器实例

from cs231n.classifiers import KNearestNeighbor
classifier = KNearestNeighbor()
# 训练就是记住所有图片和标签
classifier.train(X_train, y_train)

训练函数:

def train(self, X, y):# X[i]是图片,y[i]是X[i]的标签self.X_train = Xself.y_train = y

使用两层循环计算测试数据集和训练数据集的L2距离

dists = classifier.compute_distances_two_loops(X_test)
print(dists.shape)
# (500, 5000)(测试数据集个数,训练数据集个数)
# 对距离矩阵可视化,不进行插值
plt.imshow(dists, interpolation='none')
plt.show()

亮的地方代表距离比较远,暗的地方代表距离比较近

  1. 比较亮的横线代表,该测试图像和训练数据集里的所有图像都不像
  2. 比较亮的列代表,该训练数据集里的图片和所有测试图片都不像

两重循环的距离函数:

def compute_distances_two_loops(self, X):# X是测试数据集num_test = X.shape[0]num_train = self.X_train.shape[0]# dists[i,j]表示第i个测试数据和第j个训练数据的L2距离dists = np.zeros((num_test, num_train))for i in range(num_test):for j in range(num_train):dis = (X[i]-self.X_train[j])**2# dis是向量,np.sum是求向量的和,然后开平方dists[i,j] = np.sqrt(np.sum(dis))return dists

使用最临近(k=1)预测标签,应该得到27%左右的准确率

y_test_pred = classifier.predict_labels(dists, k=1)
# 计算准确率
num_correct = np.sum(y_test_pred == y_test)
accuracy = float(num_correct) / num_test
print('Got %d / %d correct => accuracy: %f' % (num_correct, num_test, accuracy))

预测函数:

def predict_labels(self, dists, k=1):num_test = dists.shape[0]# 预测num_test个数据的标签y_pred = np.zeros(num_test)for i in range(num_test):# 储存k个与测试数据X[i]距离最近的数据的标签closest_y = []# argsort函数返回的是数组值从小到大排序的下标    # >>> x = np.array([3, 1, 2])# >>> np.argsort(x)# array([1, 2, 0])sort_dist = np.argsort(dists[i])# 取前k个下标的标签,即距离最近的k个数据的标签    # >>> x[np.argsort(x)] #通过索引值排序后的数组# array([1, 2, 3])closest_y = self.y_train[sort_dist[:k]]# 把预测标签保存在y_pred[i],如果标签出现的数量相同,选择最小的标签# 假设k=8时closest_y结果如下。closest_y中最大的数为10——10个类别,因此bin的数量为11,那么它的索引值为0->10# closest_y = np.array([2, 1, 1, 3, 10, 3, 7, 6])# 标签0出现了0次,标签1出现了2次......标签8出现了0次......# np.bincount(closest_y)# 因此,输出结果为:array([0, 2, 1, 2, 0, 0, 1, 1, 0, 0, 1])# >>> np.argmax(np.bincount(closest_y))# 1(出现次数最多的标签为1和3,但是1比3小,所以预测的结果是1——飞机)y_pred[i] = np.argmax(np.bincount(closest_y))return y_pred

我们还可以使用L1距离来计算KNN。下列表达中L1距离不会变的是:

  • 数据减均值
  • 数据减均值后除方差
  • 旋转坐标轴
  • 以上都对

减均值和减均值除方差的情况:

旋转坐标轴的情况(橙色的是旋转后的):


k=5的时候,准确率应该比刚才稍微好一点

y_test_pred = classifier.predict_labels(dists, k=5)
num_correct = np.sum(y_test_pred == y_test)
accuracy = float(num_correct) / num_test
print('Got %d / %d correct => accuracy: %f' % (num_correct, num_test, accuracy))

使用一重循环计算距离矩阵:

dists_one = classifier.compute_distances_one_loop(X_test)
# 计算两个矩阵差的二范数
difference = np.linalg.norm(dists - dists_one, ord='fro')
print('Difference was: %f' % (difference, ))
# Difference was: 0.000000(使用两重循环和一重循环计算出来的距离是一样的)
if difference < 0.001:print('Good! The distance matrices are the same')
else:print('Uh-oh! The distance matrices are different')
# Good! The distance matrices are the same

一重循环计算L2距离的函数:

def compute_distances_one_loop(self, X):m_test = X.shape[0]num_train = self.X_train.shape[0]dists = np.zeros((num_test, num_train))for i in range(num_test):# self.X_train是(5000,3072)的矩阵,X[i]是(1,3072)的行向量。会把X[i]复制4999份,把X[i]扩充成(5000,3072)的矩阵,所以时间、空间花费都很大。dif = self.X_train - X[i]dist = np.sum(dif**2,axis = 1)#沿着行求和dists[i,:] = np.sqrt(dist)return dists

不使用循环计算距离矩阵:

dists_two = classifier.compute_distances_no_loops(X_test)
difference = np.linalg.norm(dists - dists_two, ord='fro')
# Difference was: 0.000000(使用两重循环和不使用循环计算出来的距离是一样的)
print('Difference was: %f' % (difference, ))
if difference < 0.001:print('Good! The distance matrices are the same')
else:print('Uh-oh! The distance matrices are different')
# Good! The distance matrices are the same

不使用循环计算L2距离:

def compute_distances_no_loops(self, X):m_test = X.shape[0]num_train = self.X_train.shape[0]dists = np.zeros((num_test, num_train)) # (x-y)^2=x^2+y^2-2xytest_sq = np.sum(X**2,axis=1)# x^2,(500,1)train_sq = np.sum(self.X_train**2,axis=1)# y^2,(5000,1)cross = np.multiply(np.dot(X,self.X_train.T),-2)# -2xy,(500,5000)dists = np.add(cross,test_sq)dists = np.add(dists,train_sq)dists = np.sqrt(dists)return dists

比较一下哪种循环更快:

def time_function(f, *args):# 计算函数f运行所需时间,后面输入的是函数f的参数import time# 记录开始时间tic = time.time()f(*args)# 记录结束时间toc = time.time()return toc - tic# 计算两重循环所需时间
two_loop_time = time_function(classifier.compute_distances_two_loops, X_test)
print('Two loop version took %f seconds' % two_loop_time)
# Two loop version took 43.185944 seconds# 计算一重循环所需时间
one_loop_time = time_function(classifier.compute_distances_one_loop, X_test)
print('One loop version took %f seconds' % one_loop_time)
# One loop version took 96.849304 seconds# 计算不需要循环所需的时间
no_loop_time = time_function(classifier.compute_distances_no_loops, X_test)
print('No loop version took %f seconds' % no_loop_time)
# No loop version took 0.343081 seconds

一重循环要开辟更多的空间,所以运算速度比较慢。

交叉验证

计算不同k值的准确率

# 5折交叉验证
num_folds = 5
k_choices = [1, 3, 5, 8, 10, 12, 15, 20, 50, 100]
X_train_folds = []
y_train_folds = []
y_train_ = y_train.reshape(-1, 1)#变成1列
# 分成5个小训练数据集
X_train_folds = np.array_split(X_train, num_folds)
y_train_folds = np.array_split(y_train, num_folds)
# 保存准确率
k_to_accuracies = {}
for k in k_choices:accuracies = []for i in range(num_folds):# 把第i个小训练数据集当作验证集X_test_cv = X_train_folds[i]# 除第i个以外的训练集作为训练集X_train_cv = np.vstack(X_train_folds[:i] + X_train_folds[i+1:])y_test_cv = y_train_folds[i]y_train_cv = np.hstack(y_train_folds[:i]+y_train_folds[i+1:])classifier.train(X_train_cv, y_train_cv)dists_cv = classifier.compute_distances_no_loops(X_test_cv)y_test_pred = classifier.predict_labels(dists_cv, k)num_correct = np.sum(y_test_pred == y_test_cv)accuracies.append(float(num_correct) * num_folds / num_training)k_to_accuracies[k] = accuracies
for k in sorted(k_to_accuracies):for accuracy in k_to_accuracies[k]:print('k = %d, accuracy = %f' % (k, accuracy))

画出交叉验证的图

for k in k_choices:accuracies = k_to_accuracies[k]# [k] * 5 = [k,k,k,k,k],就是画一列点plt.scatter([k] * len(accuracies), accuracies)
# 计算准确率的均值
accuracies_mean = np.array([np.mean(v) for k,v in sorted(k_to_accuracies.items())])
# 计算准确率的方差
accuracies_std = np.array([np.std(v) for k,v in sorted(k_to_accuracies.items())])
# plt.errorbar()函数用于表现有一定置信区间的带误差数据,就是蓝色的竖线和均值折线
plt.errorbar(k_choices, accuracies_mean, yerr=accuracies_std)
plt.title('Cross-validation on k')
plt.xlabel('k')
plt.ylabel('Cross-validation accuracy')
plt.show()


通过图片选择效果最好的k值(10),并计算它的准确率(应该大于28%)

best_k = 10
classifier = KNearestNeighbor()
classifier.train(X_train, y_train)
y_test_pred = classifier.predict(X_test, k=best_k)
num_correct = np.sum(y_test_pred == y_test)
accuracy = float(num_correct) / num_test
print('Got %d / %d correct => accuracy: %f' % (num_correct, num_test, accuracy))

下列哪些表达正确:

  • 训练数据在k=1时的准确率总是比k=5时要好
  • 测试数据在k=1时的准确率总是比k=5时要好
  • KNN分类器的决策边界是线性的
  • 预测一个测试样例的标签所花费的时间随着训练数据集的增大而增大
  • 以上都对
  1. 训练数据在k=1时的预测准确率为100%(因为有相同的图片,距离为0);而k=5时可能会被别的图片误导
  2. 这是不一定的,有可能更好也有可能更差
  3. KNN需要选择距离最近的k个标签所以不是线性的
  4. 因为每一个测试样例都要和所有的训练数据计算距离,而且在选择距离最近的k个标签所花的时间也和标签的总数(训练数据的个数)有关。

【深度之眼cs231n第七期】笔记(四)相关推荐

  1. 【深度之眼cs231n第七期】笔记(二十七)

    StyleTransfer-Pytorch.ipynb 预处理 计算损失 内容损失 风格损失 正则化损失 风格迁移 特征反演 style_transfer_pytorch.py StyleTransf ...

  2. 【深度之眼吴恩达机器学习第四期】笔记(七)

    目录 模型优化 损失函数 梯度函数 正则化梯度与代价函数 学习曲线 选择最优的超参数lambda 模型优化 准备数据: import numpy as np import scipy.io as si ...

  3. 【深度之眼吴恩达机器学习第四期】笔记(五)

    目录 机器学习诊断 一.无超参数时对假设进行评估 二.有超参数时对假设进行评估 三.过拟合还是欠拟合 四.增加还是减小正则化参数λ 五.应该获取更多的数据样本吗 总结一下 实现算法的推荐方法 数据偏差 ...

  4. 【深度之眼吴恩达机器学习第四期】笔记(十二)

    目录 大规模学习 小批量梯度下降 在线学习 数据并行 应用举例 人工合成数据 上限分析 总结 大规模学习 现在机器学习的性能比过去的好,其中一个原因就是现在拥有大量的数据. 而且其中一种获得高性能机器 ...

  5. 【深度之眼吴恩达机器学习第四期】笔记(九)

    目录 K均值 K均值算法 语言描述 伪代码描述 解决分离不佳的簇 K均值的损失函数 K均值初始化 如何选择K 主成分分析 用途1:去除冗余特征 用途2:可视化数据 直观来理解主成分分析 主成分分析与线 ...

  6. 【深度之眼吴恩达机器学习第四期】笔记(四)

    目录 神经网络 神经网络训练流程 我们已经有线性回归和逻辑回归了,为什么还要使用神经网络呢? 对于一个有两个输入分量(x1,x2)的分类问题,我们使用这两个分量的组合来构造假设函数(图中右上角),可能 ...

  7. 【深度之眼吴恩达机器学习第四期】笔记(二)

    目录 逻辑回归 线性回归不适合分类问题 逻辑回归函数 决策边界 损失函数 梯度下降 多类别分类 正则化 欠拟合和过拟合 线性回归正则化 正规方程的正则化 逻辑回归正则化 逻辑回归 逻辑回归虽然叫回归, ...

  8. 【深度之眼吴恩达机器学习第四期】笔记(一)

    目录 第一章:什么是机器学习 第二章:线性回归模型 第三章:矩阵运算 第四章:多变量线性回归 正规方程 第五章:操作 第一章:什么是机器学习 机器学习主要分为监督学习(我们教计算机如何学习)和无监督学 ...

  9. 【深度之眼吴恩达机器学习第四期】笔记(十一)

    目录 推荐系统 基于内容的推荐系统 协同过滤 均值归一化 编程 推荐系统 以电影推荐系统为例子,假设4个用户(nu=4)对5部电影(nm=5)作出了以下评分,其中"?"代表第j个用 ...

最新文章

  1. 全国首个!北京手机一卡通开卡费今起取消
  2. 使用Velocity(VTL)调用自定义C# .net 中的方法
  3. 如何在docker中运行MySQL实例(转载)
  4. for循环与求值 学习笔记
  5. 使用MATLAB转换图片为数据进行vga显示
  6. 注意!你手持身份证拍的照片,可能已经被炒到了上千元
  7. KHV0031-himall3.0商城异常类(一)
  8. Windows系统下快速安装、配置Aira2,及图形界面配置、度盘、B站视频下载
  9. Docker容器-------dockerfile概念简介
  10. 软件测试中的80/20原则
  11. Event-triggered MPC Design for Distributed Systems With Network Communications
  12. 谈谈小游戏加载优化及资源分配
  13. Android Studio实现百度地图定位(显示经纬度和地址)
  14. 高密度无线覆盖解决方案
  15. c#如何wmf图片转换成png图片_C#图片格式转换(支持bmp/gif/jpeg/png/tiff/wmf文件)
  16. 我的世界网易服务器修改世界,我的世界:1.16改变两个版本更新频率?网易版异常乱封大批账号?...
  17. Armijo条件,Wolfe条件,Goldstein条件
  18. ava程序员必看基础书籍
  19. nest.dll_每日新闻摘要:再见Nest.com,您好Google Store
  20. 弋江区python培训班多少钱

热门文章

  1. android 动画字体,Android字体多样式和动画TextDrawable
  2. Windows安装教程(Microsoft官方工具安装+PE安装教程,以及GPT分区、UEFI模式安装win7教程)
  3. 《多接入边缘计算(MEC)及关键技术》读书笔记 | 第3章 MEC系统架构及部署组网策略
  4. XenDesktop 连接数据库报错
  5. Objective-C Blocks Caveat
  6. 和亚马逊Amazon进行跨境电商业务,需要接入EDI吗?
  7. 解决win10 VirtualBox无法启动(VERR_NEM_VM_CREATE_FAILED)/VMware Workstation 与 Device/Credential Guard 不兼容
  8. 快手抖音上热门涨粉后怎么赚钱引流变现四大技巧-阿甘电商
  9. OSS——阿里OSS
  10. runtime.js