前言
详细代码见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_predictY_test都是torch.uint8的一维tensor向量,需要使用torch.sum(Y_predict == Y_test), 直接使用sum(Y_predict == Y_test)会统计错误!
  • 基于Tensor的数学计算最好都用·torch.xxx,比如torch.sumtorch.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对数据进行封装,然后利用enumeratenext函数提取数据。

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&lt;x,x_train&gt;+sum(x_train2)\begin{aligned} (x-x\_trian)(x-x\_train)^T &amp;= x\ x^T-2x \ x\_train^T+x\_train \ x\_train^T \\ &amp;= sum(x^2) - 2&lt;x,x\_train&gt;+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)​
    其中,&lt;&gt;&lt;&gt;<>为内积运算,基于此,我们进行向量化:
    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相关推荐

  1. 手把手教你写CS231N作业一 KNN分类器 详细解析 作业源文件 数据集

    一些准备工作 python环境 官方课程给出的源文件在这里 根据文件里的指导一步步编写答案就可.第一个实现KNN分类器. 如果是windows环境,需要首先下载数据集.The CIFAR-10 dat ...

  2. cs231n作业1——knn

    导入数据 import random import numpy as np from cs231n.data_utils import load_CIFAR10 import matplotlib.p ...

  3. 【cs231n作业笔记】一:KNN分类器

    安装anaconda,下载assignment作业代码 作业代码数据集等2018版基于python3.6 下载提取码4put 本课程内容参考: cs231n官方笔记地址 贺完结!CS231n官方笔记授 ...

  4. cs231n作业环境配置

    cs231n作业的环境配置 查了很多资料,尝试了很多次,终于成功了 参考文章:http://www.manongjc.com/article/30189.html 首先我的电脑是win10(x86), ...

  5. Windows本地快速搭建cs231n作业环境

    之前写过一个关于斯坦福cs321n课程的作业环境搭建教程,cs231n_assignment1_配置作业环境+kNN,不过好像很多人还是在搭建过程中遇到一些问题,导致做不了作业.自己动手实践是学习最快 ...

  6. cs231n_assignment1_配置作业环境+kNN

    作业1:http://cs231n.github.io/assignments2017/assignment1/ 我是在win7电脑上做的. 准备工作: 下载anaconda:https://www. ...

  7. cs231n作业记录(答案)-ASS1(KNN)

    学习了cs231n的课程,感觉有些困难,故做一些记录. 环境配置:jupyter ASS1 P1 KNN算法 (1)第一部分加载数据集,并输出它们的大小. 这个数据集有10个类. Training d ...

  8. 斯坦福cs231n作业数据集下载

    刚看到斯坦福的cs231n课程,看到有课后作业,于是就想试一试, 作业网址https://cs231n.github.io/assignments2020/assignment1/#q1-k-near ...

  9. CS231n作业2中Cython相关问题的处理

    最近磨磨蹭蹭地学着CS231n,看着满屏幕代码不禁感慨Stanford大神们当真深不可测,作业是以挖空形式给出的.这么多代码要让博主从头写,那还不得写到天荒地老-- 博主使用的是2016年的教学录像和 ...

  10. cs231n作业-assignment1

    assignment 1 (cs231n) 文章目录 assignment 1 (cs231n) KNN基础 计算distances 方法一:双层循环 计算distances 方法二:单层循环 计算d ...

最新文章

  1. 由 HashMap 引发的一个面试10连炮争吵!
  2. 行业专业的移动广告聚合平台--KeyMob
  3. Python3之字典生成器结合lambda实现按key/value排序
  4. Java并发编程实战~Condition
  5. mxnet深度学习(KVS)
  6. React Native移动框架功能研究
  7. 学习机软件测试,IBM P630 POWER4 AIX小型机适合软件测试及学习机
  8. 计算机联锁车务仿真培训系统 casco模式,计算机联锁车务仿真培训系统简介(15页)-原创力文档...
  9. 几种支持动作模型格式的比较(MD2,MD5,sea3d) 【转】
  10. ACCP学习旅程之----- 使用Dreamweaver制作网页
  11. python 终止程序代码 多线程_我想问一下,tkinter 做多线程爬虫,让他停止该怎么做 quit 和 exit 都是直接退出程序...
  12. Linux系统下架设PPTP ×××服务器
  13. 【图像处理技术】 | 黑科技解读 之 PS检测、弯曲拉平、切边增强、摩尔纹
  14. lro gro_斯威夫特:Gro吟M
  15. Docker端口映射不起作用的解决办法
  16. 计算机网络软件系统不包括,1period;计算机软件系统一般分为lpar; A rpar;两大部分...
  17. 楼兰宝盒显示网络服务器无响应,捷达vs5-圈里有谁跟我一样,安装了楼兰宝盒后,用手机启动车子出现无钥匙解锁失灵时候使坏,和前部辅助系统出现故障问题,不用手机启动就没事...
  18. 语法高亮自定义颜色主题配置(Code::Blocks)
  19. 代码批量删除QQ日志和说说
  20. 四川省知识产权贯标申报好处条件、材料

热门文章

  1. JAVA生成32位随机字符串工具
  2. 博士生“凡尔赛”大赏:全程靠自己发了篇SCI,导师发奖金拿到手软
  3. 完整BBS系统开发流程及结果展示
  4. ztree的select设置,完笔
  5. 2022年全国大学生电子设计竞赛—TI杯模拟电子系统设计专题邀请赛X题
  6. 图案设计灵感怎么写_设计理念怎么写
  7. Flash制作(简单)电子相册
  8. 联想无线键盘使用方法
  9. 今天和孝辉一起去交大买书了!
  10. Tapestry5的喜爱.