CS231n:作业1——KNN
前言
详细代码见github
问答总结:
- 实现knn算法中,我们使用了
torch.argsort
,torch.argmax
,torch.bincount
函数简化了代码,脑海中模拟他们的过程。 - 实现knn算法向量化过程中,我们使用了
+
号的传播原理(dist=d1+d2+d3
),其中d1∈RM×1,d2∈RM×N,d3∈R1×Nd_1\in R^{M \times 1},d_2 \in R^{M \times N}, d_3 \in R^{1 \times N}d1∈RM×1,d2∈RM×N,d3∈R1×N,脑海中模拟这个过程。 - 当
+
号两边是tuple
或者list
时,+
号起拼接作用。 - 在统计正确率时,如果
Y_predict
和Y_test
都是torch.uint8
的一维tensor向量,需要使用torch.sum(Y_predict == Y_test)
, 直接使用sum(Y_predict == Y_test)
会统计错误! - 基于
Tensor
的数学计算最好都用·torch.xxx
,比如torch.sum
,torch.sqrt
,torch.max
等等。 - 时间评测单元如何编写?如何调用?
- 误差条形图如何绘制?
文章目录
- 一、实验目标
- 1、实现KNN
- 一、数据集
- 1、数据集简介
- 2、加载数据集
- 3、加载结果
- 二、KNN算法
- 1、模型框架
- 2、正确率评估
- 3、两种距离计算方式时间比较
- 4、交叉验证
- 参考资料
一、实验目标
1、实现KNN
- 使用
cifar-10
数据集完成KNN
算法,使用循环
和向量化
两种方式计算距离,直接在训练集和测试集上进行实验,记录结果,并比较两种距离计算方式所花费时间。 - 使用
torch.split
函数划分划分训练集,进行交叉验证
,选取最合适的k
,并作出误差条形图
.
一、数据集
1、数据集简介
cifar-10
是有名的图像分类
数据集,其含有10
个类别,训练集50000
张图片,测试集10000
张图片。
2、加载数据集
笔者最喜欢的深度学习框架为pytorch
, 很幸运,pytorch框架中内置了cifar-10
数据集,可是省去自己寻找资源的步骤。具体使用步骤如下:
下载:
import torchvision.datasets as dset dset.CIFAR10(path, train=True, transform=None, target_transform=None, download=True) dset.CIFAR10(path, train=False, transform=None, target_transform=None, download=True)
其中,重点的是
path
参数,这是用来保存下载数据的文件目录。定义加工函数:
import torchvision.transforms as transformstransform = transforms.Compose([transforms.ToTensor(),])
此函数可以将原始图片进行转换为
tensor
、裁剪
、归一化
等操作,具体可以参看官方文档. 这里我们仅仅将图片装换为floatTensor
, 其值为[−1,1][-1,1][−1,1].得到数据:
train_set = tv.datasets.CIFAR10(root=path, train=True, download=True, transform=transform)test_set = tv.datasets.CIFAR10(root=path, train=False, download=True, transform=transform)
path
参数为保存下载数据的文件目录,transform
参数为我们定义的加工函数,这样我们就得到了所有图片的tensor
数据。封装(非必要):
train_loader = torch.utils.data.DataLoader(train_set, batch_size=50000, shuffle=True, num_workers=0)test_loader = torch.utils.data.DataLoader(test_set, batch_size=10000, shuffle=False, num_workers=0)train_data_interator = enumerate(train_loader)test_data_interator = enumerate(test_loader)train_data = next(train_data_interator)test_data = next(test_data_interator)
为了和常见的训练过程保持一致,我们使用
DataLoader
对数据进行封装,然后利用enumerate
和next
函数提取数据。
3、加载结果
经过上述步骤,最终我们得到trian_data : [flagTensor:(50000,3,32,32)]
和test_data : [floatTensor:(10000,3,32,32)]
的数据。我们此后所有实验都基于此数据集完成。
二、KNN算法
KNN算法是一种分类算法, 思想简单,不再赘述。其核心在于(1)选取距离度量。(2)确定k值。在此实验中,对于(1),我们将简单采取L2
距离进行度量。对于(2),我们将使用交叉验证方法确定k值。
1、模型框架
class KNN():def __init__(self):self.model_name = "KNN"# ---------------------------------------------------#def train(self, train_data, train_labels):self.X_trian = train_dataself.Y_train = train_labelsdef predict(self, X, k, vec=True):"""功能: 预测输入图片的标签输入: X(tensor), (M, 3*32*32): 输入的图片k(int), (1): 按k个邻居结点判断类别vec(bool), (1): 是否使用向量化距离计算方式输出:label(tensor), (M): 所有输入图片的预测类别"""if vec:dist = self.cal_dist_with_vec(X)else:dist = self.cal_dist_with_loop(X)topk = self.Y_train[torch.argsort(dist, 1)[:,:k]]labels = []for each in topk:bin_count = torch.bincount(each)label = torch.argmax(bin_count)labels.append(label.item())return torch.LongTensor(labels)# ------------------------------------------------------#def cal_dist_with_vec(self, X):"""功能:使用向量化方法,对于测试数据X, 计算其对于训练数据的L2距离。"""def cal_dist_with_loop(self, X):"""功能:使用循环,对于测试数据X, 计算其对于训练数据的L2距离。"""
(1) 模型主体
train
阶段: 即简单记录数据,不在赘述。predict
阶段: 根据k
值预测输入测试数据的类别。
值得注意的是:这里一系列函数的应用(torch.argsort
, torch.bincount
, torch.argmax
)大大简化了代码。
(2) 距离计算
向量化方法: 对于一个测试样本xxx和一个训练集样本x_trainx\_trainx_train, 我们可以这样计算他们的
l2
距离:(x−x_trian)(x−x_train)T=xxT−2xx_trainT+x_trainx_trainT=sum(x2)−2<x,x_train>+sum(x_train2)\begin{aligned} (x-x\_trian)(x-x\_train)^T &= x\ x^T-2x \ x\_train^T+x\_train \ x\_train^T \\ &= sum(x^2) - 2<x,x\_train>+sum(x\_train^2) \end{aligned}(x−x_trian)(x−x_train)T=x xT−2x x_trainT+x_train x_trainT=sum(x2)−2<x,x_train>+sum(x_train2)
其中,<><><>为内积运算,基于此,我们进行向量化:
dist=d1+d2+d3dist = d1+d2+d3dist=d1+d2+d3
d1∈RM×1d1\in R^{M \times 1}d1∈RM×1:torch.sum(pow(X,2),1).unsqueeze(1)
d2∈RM×Nd2 \in R^{M \times N}d2∈RM×N:-2*X.matmul(X_train.t())
d3∈R1×Nd3 \in R^{1\times N}d3∈R1×N:torch.sum(pow(X_train,2),1).unsqueeze(0)
注:这样的写法基于+
操作的传播机制
这样,我们就可以写出向量化代码如下:
def cal_dist_with_vec(self, X):"""功能:对于测试数据X, 计算其对于训练数据的L2距离。输入:X(tensor), (M,3*32*32): 需要预测的图片。输出:dist(tensor), (M, N): 每一行为每一个测试用例与所有训练集的L2距离。"""d1 = torch.sum(torch.pow(X,2),1).unsqueeze(1)d2 = -2*X.matmul(X, self.X_trian.t())d3 = torch.sum(torch.pow(self.X_train, 2),1).unsqueeze(0)return torch.sqrt(d1 + d2 + d3)
非向量化方法: 写一个二重循环进行计算。
def cal_dist_with_loop(self, X):"""功能:对于测试数据X, 计算其对于训练数据的L2距离。输入:X(tensor), (M,3*32*32): 需要预测的图片。输出:dist(tensor), (M, N): 每一行为每一个测试用例与所有训练集的L2距离。"""M, N = X.size(0), self.X_train.size(0)dist = torch.ones(M, N)for i in range(M):for j in range(N):delta_x = X[i] - self.X_train[j]dist[i][j] = torch.sqrt(torch.sum(pow(delta_x,2)))return dist
2、正确率评估
计算预测正确数量:
torch.sum(Y_predict == Y_test).item()
计算准确率:
Acc = torch.sum(Y_predict == Y_test).item() / len(Y_predict)
train_data_num = 500 test_data_num = 100 X_train = train_data[:train_data_num].view(train_data_num, -1) Y_train = train_labels[:train_data_num] X_test = test_data[:test_data_num].view(test_data_num,-1) Y_test = test_labels[:test_data_num]Y_predict = knnEr.predict(X_test,k=10,vec=True) print("向量化Acc:{}".format(torch.sum(Y_test == Y_predict).item() / len(Y_test))) Y_predict = knnEr.predict(X_test,k=10,vec=False) print("非向量化Acc:{}".format(torch.sum(Y_test == Y_predict).item() / len(Y_test)))>> 向量化Acc:0.22 >> 非向量化Acc:0.22
3、两种距离计算方式时间比较
编写测试程序执行时间计算单元:
def cal_time(f, *args):"""函数功能: 计算函数f执行时间输入:f(function): 所要执行的函数*args: 可变长度的函数参数输出:time(int): 函数执行时间"""import timet_st = time.time()f(*args)t_ed = time.time()return t_ed - t_st
调用:
print("向量化花费时间:{}".format(eva.cal_time(knnEr.predict,X_test,10,True))) print("非向量化花费时间:{}".format(eva.cal_time(knnEr.predict,X_test,10,False)))>> 向量化花费时间:0.008883237838745117 >> 非向量化花费时间:1.7657294273376465
可以看到,在样本非常小时,时间差距已经非常大了。因此向量化计算能够大大降低运行时间.
注:调用
cal_time
时函数不用加括号,参数以逗号隔开, 依次写在后面即可。
4、交叉验证
(1) 方法回顾
- 将训练集划分为
k
份,循环k
次,每次拿其中1
份当做验证集:cv
,其余k-1
份作为训练集:tr
. - 每组参数获得
k
个结果,以结果平均作为标准,比较选取最好的参数。 - 到测试集上测试最终结果。
(2) 编码实现
```
k_fold = 5 # 交叉验证份数
k_classes = [1, 3, 5, 8, 10, 12, 15, 20, 50, 100] # 待选的k值
train_data_num = 5000
test_data_num = 1000
fold_sample_num = int(train_data_num / k_fold)
X_train = train_data[:train_data_num].view(train_data_num, -1)
Y_train = train_labels[:train_data_num]
X_test = test_data[:test_data_num].view(test_data_num,-1)
Y_test = test_labels[:test_data_num]# ----------------------划分数据集为k_fold 份---------------------------#
X_train_folds = torch.split(X_train, fold_sample_num, 0)
Y_train_folds = torch.split(Y_train, fold_sample_num, 0)
# --------------------------------------------------------------------#knner = KNN()
k_acc = {} # 记录准确率
for k in k_classes:acc_list = []for i in range(0, k_fold):# -----------使用+号拼接tuple, 使用torch.cat拼接tuple---------------#X_tr = torch.cat(X_train_folds[:i]+X_train_folds[i+1:], 0)Y_tr = torch.cat(Y_train_folds[:i]+Y_train_folds[i+1:], 0)#----------------------------------------------------------------#X_cv = X_train_folds[i]Y_cv = Y_train_folds[i]knner.train(X_tr, Y_tr)Y_cv_predict = knner.predict(X_cv, k, True)acc = torch.sum(Y_cv_predict == Y_cv).item() / len(Y_cv)acc_list.append(acc)k_acc[k] = acc_list
```
(3) 可视化
对每个k值,我们都计算了k_fold
个准确率,更具这些准确率,我们画出误差条形图
:
```
# Plot the cross validation
for k in k_classes:plt.scatter([k] * k_fold, k_acc[k])
# plot the trend line with error bars that correspond to standard deviation
accuracies_mean = [np.mean(k_acc[k]) for k in k_acc]
accuracies_std = [np.std(k_acc[k]) for k in k_acc]
plt.errorbar(k_classes, accuracies_mean, yerr=accuracies_std)
plt.title('Cross-validation on k')
plt.xlabel('k')
plt.ylabel('Cross-validation accuracy')
plt.show()
```
(4)选择k值
可以看到,做好的k值为8
, 因此我们选择8
为最终k值,然后预测测试集:
knner.train(X_train, Y_train)
Y_test_predict = knner.predict(X_test,8,True)
print(torch.sum(Y_test_predict == Y_test).item() / len(Y_test))
>> 0.289
参考资料
- Pytorch 官方文档: torchvision transform
- 红色的石头:斯坦福CS231n项目实战(一):k最近邻(kNN)分类算法
CS231n:作业1——KNN相关推荐
- 手把手教你写CS231N作业一 KNN分类器 详细解析 作业源文件 数据集
一些准备工作 python环境 官方课程给出的源文件在这里 根据文件里的指导一步步编写答案就可.第一个实现KNN分类器. 如果是windows环境,需要首先下载数据集.The CIFAR-10 dat ...
- cs231n作业1——knn
导入数据 import random import numpy as np from cs231n.data_utils import load_CIFAR10 import matplotlib.p ...
- 【cs231n作业笔记】一:KNN分类器
安装anaconda,下载assignment作业代码 作业代码数据集等2018版基于python3.6 下载提取码4put 本课程内容参考: cs231n官方笔记地址 贺完结!CS231n官方笔记授 ...
- cs231n作业环境配置
cs231n作业的环境配置 查了很多资料,尝试了很多次,终于成功了 参考文章:http://www.manongjc.com/article/30189.html 首先我的电脑是win10(x86), ...
- Windows本地快速搭建cs231n作业环境
之前写过一个关于斯坦福cs321n课程的作业环境搭建教程,cs231n_assignment1_配置作业环境+kNN,不过好像很多人还是在搭建过程中遇到一些问题,导致做不了作业.自己动手实践是学习最快 ...
- cs231n_assignment1_配置作业环境+kNN
作业1:http://cs231n.github.io/assignments2017/assignment1/ 我是在win7电脑上做的. 准备工作: 下载anaconda:https://www. ...
- cs231n作业记录(答案)-ASS1(KNN)
学习了cs231n的课程,感觉有些困难,故做一些记录. 环境配置:jupyter ASS1 P1 KNN算法 (1)第一部分加载数据集,并输出它们的大小. 这个数据集有10个类. Training d ...
- 斯坦福cs231n作业数据集下载
刚看到斯坦福的cs231n课程,看到有课后作业,于是就想试一试, 作业网址https://cs231n.github.io/assignments2020/assignment1/#q1-k-near ...
- CS231n作业2中Cython相关问题的处理
最近磨磨蹭蹭地学着CS231n,看着满屏幕代码不禁感慨Stanford大神们当真深不可测,作业是以挖空形式给出的.这么多代码要让博主从头写,那还不得写到天荒地老-- 博主使用的是2016年的教学录像和 ...
- cs231n作业-assignment1
assignment 1 (cs231n) 文章目录 assignment 1 (cs231n) KNN基础 计算distances 方法一:双层循环 计算distances 方法二:单层循环 计算d ...
最新文章
- 由 HashMap 引发的一个面试10连炮争吵!
- 行业专业的移动广告聚合平台--KeyMob
- Python3之字典生成器结合lambda实现按key/value排序
- Java并发编程实战~Condition
- mxnet深度学习(KVS)
- React Native移动框架功能研究
- 学习机软件测试,IBM P630 POWER4 AIX小型机适合软件测试及学习机
- 计算机联锁车务仿真培训系统 casco模式,计算机联锁车务仿真培训系统简介(15页)-原创力文档...
- 几种支持动作模型格式的比较(MD2,MD5,sea3d) 【转】
- ACCP学习旅程之----- 使用Dreamweaver制作网页
- python 终止程序代码 多线程_我想问一下,tkinter 做多线程爬虫,让他停止该怎么做 quit 和 exit 都是直接退出程序...
- Linux系统下架设PPTP ×××服务器
- 【图像处理技术】 | 黑科技解读 之 PS检测、弯曲拉平、切边增强、摩尔纹
- lro gro_斯威夫特:Gro吟M
- Docker端口映射不起作用的解决办法
- 计算机网络软件系统不包括,1period;计算机软件系统一般分为lpar; A rpar;两大部分...
- 楼兰宝盒显示网络服务器无响应,捷达vs5-圈里有谁跟我一样,安装了楼兰宝盒后,用手机启动车子出现无钥匙解锁失灵时候使坏,和前部辅助系统出现故障问题,不用手机启动就没事...
- 语法高亮自定义颜色主题配置(Code::Blocks)
- 代码批量删除QQ日志和说说
- 四川省知识产权贯标申报好处条件、材料