cs231n学习笔记——图像分类及代码实现

  • 写在前面的废话
  • 1.图像分类
  • 2.数据驱动
  • 3.图形分类流程
  • 4.L1距离(曼哈顿距离)
  • 5.L2距离(欧氏距离)
  • 6. Nearest Neighbor分类器
    • 1.CIFAR-10数据集说明
    • 2.NN代码实现
  • 7. K Nearest Neighbor分类器
    • 1.确定K的值(超参数)
    • 2.KNN实现
    • 3.交叉验证
  • 8.小结

写在前面的废话

在机器学习和深度学习的道路上一直在当混子,丧了很久决定。决定从新出发,决定好好学习下经典的cs231n 2017的课程(2019英语水平的原因暂时啃不动),写博客可能更多的原因并不是想分享给别人看的,而是写给自己的,算是学后总结吧。在这里还是要感谢为cs231n课程做了精心翻译的人。在下面附上链接,我也是在看了视频后,又反过来看他做的翻译笔记重新梳理复习的。接下来的顺序也是根据课程讲解的知识顺序进行整理的,一些自己的理解和原来错误的认识我也会写出来的。

CS231n课程笔记翻译:https://zhuanlan.zhihu.com/p/20894041?refer=intelligentunit

1.图像分类

其实图像分类的原理更像是再教小孩子,你拿很多的球(也可以是别的)到孩子面前,告诉他教他哪些是篮球、哪些是足球、哪些是网球等等。之后呢,你又拿了一个其他的球,让孩子来通过刚才学习的经验分辨这个是什么球。图像分类就是这样:就是用已有固定的分类标签集合,然后对于输入的图像,从分类标签集合中找出一个分类标签,最后把分类标签分配给该输入图像。

下面来举个例子:首先科普一下,我们计算机中打开一张彩色图片,宽248像素,高400像素。这个图片包含了三个维度的信息,长x宽x3(RGB这个3代表的是红、绿和蓝3个颜色通道)。所以当我们写代码将一张图片加载后就形成了一个248X400X3=297600个元素的数组。每个元素的范围是0~255,因为每个像素点颜色范围不会超过255,0代表黑,255代表白。就是下面这个样子。

这个打开后看到的这堆乱七八糟的数字。举个类似的例子,其实就是像生物的DNA,现在图像分类要做的就是根据这些DNA来分辨,打开的这个图片是什么物种。识别的难点我就不说了,人有分不清的时候机器就更分不清了:例如角度变化、光线、变形等等。

2.数据驱动

课程讲到数据驱动的时候,我明显感觉眼前一亮,一个很恰当的词语。理解上讲,就是以往我们在做算法实现去识别某一个事物的时候,都是根据这个东西的特性量身去定制。但是我们觉得这样很麻烦,我们希望我给你什么东西,你就能自己根据我给你的变化识别。还是拿孩子的例子,就是我给你什么你就根据我给你的东西去学习分辨,你能够学到什么完全是由我给你的东西决定的。数据驱动就是这个道理。用数据驱动填充模型,也决定了模型能够识别什么东西。

3.图形分类流程

输入:输入N个图片的集合,其中每张图片标签(类别)是已知的。这个集合叫做训练集。
训练:训练其实就是学习一个分类器,使用输入的训练集来学习如何判断各个分类。
预测与评价:给定一张没见过的图片,让模型来预测是哪个分类标签。然后根据它分类的正确与否我们来评价这个分类器的质量。

4.L1距离(曼哈顿距离)


曼哈顿距离其实是一种坐标距离,表示在标准坐标轴上的轴距绝对值。在模型之间使用曼哈顿距离往往样本具有某些重要意义的特征值,更适合用到这个距离,例如1个人的工资条,其中有工龄、收入、职位等等。每个特征都代表了这个人的某种身份,而计算2个人之间的样本距离,曼哈顿是比较合适的。下面是计算公式:
d(I1,I2)=∑p∣I1P−I2P∣d(I_1,I_2)=\sum_{p}|I_1^P-I_2^P| d(I1​,I2​)=p∑​∣I1P​−I2P​∣

可以看得出L1距离取决于你选择的坐标系,一旦你转动坐标系,两点之间的距离就会随之发生变化。而相比L2距离就不存在这个问题,你可以随意转动坐标轴,两点的距离都是确定的。

5.L2距离(欧氏距离)

欧式距离我就不多解释了,初中就学过了。
d(I1,I2)=∑P(I1P−I2P)2d(I_1,I_2)=\sqrt{\sum_{P}(I_1^P-I_2^P)^2}d(I1​,I2​)=P∑​(I1P​−I2P​)2​

这里比较有趣的就是模型选择到底用哪个距离进行计算,这就要看实际情况来进行取舍了。两个距离都不存在谁更好的问题,如果把握不准的话就都试一试。

6. Nearest Neighbor分类器

首先我先讲一下这个近邻(Nearest Neighbor)算法的优缺点,下面的K近邻同样适用这个优缺点。
优点:

  • 简单,无需预训练
  • 无需调参

缺点:

  • 需要更多的训练数据才能改善模型,而且可能导致维度灾难。
  • 计算成本高,需要计算到每个样本之间的距离。

这个模型很是辣鸡,就是因为它比较简单,更适合教学讲解。而且几乎不会再实际中使用,为什么呢?而与它相反的例子是:卷积神经网络,在一般为用户提供使用的模型都是训练好的,所以往往公司会花大量的时间去提前训练好模型,在使用过程中直接进行分类即可速度非常快,不会占用用户太多空间和时间。而NN模型所谓的训练其实就是把所有的数据保存起来,进行预测的时候,要实时化大量时间去计算,这样的方式简直是不可接受的。下面在介绍模型前,先介绍两个相关概念,因为之后模型会用到,而且这两个都是可以根据实际情况搭配使用的。
———————————————————————————

1.CIFAR-10数据集说明

这里在验证Nearest Neighbor分类器的时候使用了CIFAR-10数据集。
CIFAR-10:CIFAR-10数据集包含10个类别的60000个32x32彩色图像,有50000张训练图像和10000张测试图像。这个数据集包含了10种分类标签,每个类别6000个图像。下图中你可以看见10个类的10张随机图片。

左边:从CIFAR-10数据库中的样本图像。右边:第一列是输入模型的测试图像,然后第一列的每个测试图像右边是使用Nearest Neighbor算法,从训练集中选出的10张最类似的图片。由于图不是很清楚就直接说,上面10个分类中,只有3个是准确的。

那么如何比较距离呢?就是将每张图片转化成向量(张量)然后使用L1距离进行运算。向量中的每个数值做减法。

得到的456就是,两张图片的距离,差别越大数值越大。如果数值为0那么就可以认为这两张图片是一模一样的了。
———————————————————————————
大致讲一下过程,首先我们把50000张的训练集,拿给Nearest Neighbor分类器进行训练(其实就是存起来了),之后用10000张的测试集,去挨个和训练集进行距离比较。然后每张测试图片会把,存储训练集的所有图片中和自己距离最小的那张的标签,赋给自己,这就是预测的标签。
———————————————————————————

2.NN代码实现

下面代码实现和讲解部分,课程笔记讲的很清楚,我就直接给考过来了。
下面,让我们看看如何用代码来实现这个分类器。首先,我们将CIFAR-10的数据加载到内存中,并分成4个数组:训练数据和标签,测试数据和标签。在下面的代码中,Xtr(大小是50000x32x32x3)存有训练集中所有的图像,Ytr是对应的长度为50000的1维数组,存有图像对应的分类标签(从0到9):

# CIFAR10数据读取这部分代码课程上没有,我在这里补充好了。
def load_CIFAR10(path):# path = './cifar-10-batches-py/'Xtr, Ytr, Xte, Yte = [], [], [], []for i in range(1, 6):filename = os.path.join(path, 'data_batch_%d' % i)with open(filename, 'rb') as f:data = pickle.load(f, encoding='latin1') # 读取训练集Xtr.extend(data['data'])Ytr.extend(data['labels'])with open(os.path.join(path, 'test_batch'), 'rb') as f:data = pickle.load(f, encoding='latin1') # 读取测试集Xte.extend(data['data'])Yte.extend(data['labels'])return np.array(Xtr), np.array(Ytr), np.array(Xte), np.array(Yte)Xtr, Ytr, Xte, Yte = load_CIFAR10('data/cifar10/') # a magic function we provide
# flatten out all images to be one-dimensional
Xtr_rows = Xtr.reshape(Xtr.shape[0], 32 * 32 * 3) # Xtr_rows becomes 50000 x 3072
Xte_rows = Xte.reshape(Xte.shape[0], 32 * 32 * 3) # Xte_rows becomes 10000 x 3072

现在我们得到所有的图像数据,并且把他们拉长成为行向量了。接下来展示如何训练并评价一个分类器:

nn = NearestNeighbor() # create a Nearest Neighbor classifier class
nn.train(Xtr_rows, Ytr) # train the classifier on the training images and labels
Yte_predict = nn.predict(Xte_rows) # predict labels on the test images# and now print the classification accuracy, which is the average number
# of examples that are correctly predicted (i.e. label matches)
print 'accuracy: %f' % ( np.mean(Yte_predict == Yte) )

作为评价标准,我们常常使用准确率,它描述了我们预测正确的得分。请注意以后我们实现的所有分类器都需要有这个API:train(X, y)函数。该函数使用训练集的数据和标签来进行训练。从其内部来看,类应该实现一些关于标签和标签如何被预测的模型。这里还有个predict(X)函数,它的作用是预测输入的新数据的分类标签。现在还没介绍分类器的实现,下面就是使用L1距离的Nearest Neighbor分类器的实现套路:

import numpy as np
import pickle
import osclass NearestNeighbor(object):def __init__(self):passdef train(self, X, y):""" X is N x D where each row is an example. Y is 1-dimension of size N """# the nearest neighbor classifier simply remembers all the training dataself.Xtr = Xself.ytr = ydef predict(self, X):""" X is N x D where each row is an example we wish to predict label for """num_test = X.shape[0]# lets make sure that the output type matches the input typeYpred = np.zeros(num_test, dtype = self.ytr.dtype)# loop over all test rowsfor i in xrange(num_test):# find the nearest training image to the i'th test image# using the L1 distance (sum of absolute value differences)distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)min_index = np.argmin(distances) # get the index with smallest distanceYpred[i] = self.ytr[min_index] # predict the label of the nearest examplereturn Ypred

如果你用这段代码跑CIFAR-10,你会发现准确率能达到38.6%。这比随机猜测的10%要好,但是比人类识别的水平(据研究推测是94%)和卷积神经网络能达到的95%还是差多了。点击查看基于CIFAR-10数据的Kaggle算法竞赛排行榜。

**距离选择:**计算向量间的距离有很多种方法,另一个常用的方法是L2距离,从几何学的角度,可以理解为它在计算两个向量间的欧式距离。公式在上面已经介绍过了。
换句话说,我们依旧是在计算像素间的差值,只是先求其平方,然后把这些平方全部加起来,最后对这个和开方。在Numpy中,我们只需要替换上面代码中的1行代码就行:

distances = np.sqrt(np.sum(np.square(self.Xtr - X[i,:]), axis = 1))

注意在这里使用了np.sqrt,开根号其实可有可无,因为他是个单调函数,如果开根号虽然数值变了但是整体都变大了,但是排列分布还会保持一致。

L1和L2比较。比较这两个度量方式是挺有意思的。在面对两个向量之间的差异时,L2比L1更加不能容忍这些差异。也就是说,相对于1个巨大的差异,L2距离更倾向于接受多个中等程度的差异。L1和L2都是在p-norm常用的特殊形式

7. K Nearest Neighbor分类器

Nearest Neighbor分类器如何在进行优化呢?可以看出Nearest Neighbor分类器在进行标签判别的时候是寻找离自己距离最近的一张图片,那么如果选择多张图片是不能能够带来更大的泛化能力呢?K Nearest Neighbor分类器就很自然的被想到了。道理很简单,将距离自己一张图片的判别换成通过离自己最近的K张图片,然后再通过多数投票的方式产生,标签这样能带来更好的泛化能力。

最左边的是原始数据的分布,中间的是Nearest Neighbor分类器的效果图,最右边的是5-Nearest Neighbor分类器的效果图。数据包括3类(红绿蓝)不同的颜色代表不同的决策边界。可以看得出5NN的边界效果更加的圆滑,而且NN分类器产生的不正确的数据孤岛(蓝色区域里包裹的绿色区域),5NN对它做了平滑,能够更加包容异常点,带来了更好的泛化能力。而5NN的白色区域代表着模糊的决策区域,例如投票的分类绿色2个,红色2个,蓝色1个。

1.确定K的值(超参数)

超参数简单理解就是,需要我们进行调优不断尝试变换的参数或者是不同函数例如:L1、L2距离。那么KNN中的K其实就是超参数,需要我们进行调优寻找最合适的值。嗯!机器学习和深度学习其实就是在既定的模型下不断地调超参数

这个过程肯定是要经历不断地尝试的。但是切记!!==不要使用测试集进行调优。==如果使用测试集进行调优的话,可能算法效果看起来不错,但是实际部署会效果很差。这样的方式其实就等于你把测试集当做了,训练集。通过测试集训练出来的模型使用测试集进行测试自然效果很好,但是实际上就可能过拟合。

测试数据集只使用一次,即在训练完成后评价最终的模型时使用。

为了避免这种情况,我们又设置了验证集。(其实一开始我自己看书的过程里根本分不清什么是验证集和测试集)但是在课程里讲的非常的清楚。测试集就是最后模型完成后,只使用一次进行分类器质量测试使用。超参数的调优后的测试都通过验证集来完成。

2.KNN实现

还是上面NN的那段代码,以CIFAR-10为例,我们可以用49000个图像作为训练集,用1000个图像作为验证集,下面就是划分验证集和KNN实现代码:

import numpy as np
import pickle
import os
from collections import Counterdef load_CIFAR10(path):Xtr, Ytr, Xte, Yte = [], [], [], []for i in range(1, 6):filename = os.path.join(path, 'data_batch_%d' % i)with open(filename, 'rb') as f:data = pickle.load(f, encoding='latin1')Xtr.extend(data['data'])Ytr.extend(data['labels'])with open(os.path.join(path, 'test_batch'), 'rb') as f:data = pickle.load(f, encoding='latin1')Xte.extend(data['data'])Yte.extend(data['labels'])return np.array(Xtr), np.array(Ytr), np.array(Xte), np.array(Yte)class NearestNeighbor(object):def __init__(self):passdef train(self, X, y):self.Xtr = Xself.ytr = ydef predict(self, X, k=1):num_test = X.shape[0]Ypred = np.zeros(num_test, dtype=self.ytr.dtype)Ypre_k = np.zeros(k, dtype=self.ytr.dtype)for i in range(num_test):distance = np.sum(np.abs(self.Xtr - X[i, :]), axis= 1)min_dis = np.sort(distance)[:k] # 获取前K个最小的值的索引for j in range(k):Ypre_k[j] = self.ytr[min_index[j]] # 直接获取标签Ypred[i] = Counter(Ypre_k).most_common(1)[0][0] # 对标签进行统计并取出第一个频率最高的标签return Ypred   nn = NearestNeighbor()
Xtr, ytr, Xte, yte = load_CIFAR10(r'C:\Users\Mango\Desktop\cifar-10-batches-py')
Xtr_rows = Xtr.reshape(Xtr.shape[0], 32*32*3)
Xte_rows = Xte.reshape(Xte.shape[0], 32*32*3)
Xval_row = Xtr_rows[:1000, :]
Yval = ytr[:1000]
Xtr_rows = Xtr_rows[1000:, :]
ytr = ytr[1000:]
vaildation_accuracies = []for k in [1, 3, 5, 10, 20, 50]:nn.train(Xte_rows, ytr)Yte_predict = nn.predict(Xval_row, k = k)acc = np.mean(Yte_predict == ytr)print("accuracy: %f" % acc)vaildation_accuracies.append((k, acc))

最后我们会绘出分析图,来分析那个K值表现最好。然后再用该K值跑测试集,来得到该分类器真正的性能。下面还是贴出那句重要的话。

把训练集分成训练集和验证集。使用验证集来对所有超参数调优。最后只在测试集上跑一次并报告结果。

3.交叉验证

一般当数据集较小的情况下,为了能够充分的利用数据集。我们将数据集分成5份,其中4份是训练集,一份是验证集,然后循环着其中4份做训练集使用,另一份做验证集使用。最后取五次的平均作为结果。下面是交叉验证下绘制的分析图。

上图就是交叉验证5份的K值调优图。X轴代表着在调优过程中K值的变化,Y轴代表着准确率。可以看出K=7的时候性能最佳。

交叉验证一般被分割成1份、3份、10份,常用的分割模式,大概是使用数据集50%~90%。然后训练集会被均分。图上绿色的代表训练集,黄色的是验证集,红色的是测试集。如果是交叉验证那么久循环着使用训练集和验证集。

8.小结

NN在图像分类中几乎不会应用。因为NN分类器在数据维度较低的情况下效果不错,而图像往往具有较高的维度。而高维度之间的距离通常是反直觉的(意思就是跟你常识不一样)例如下图:

右边几张图片与最左边的原始图片L2距离是一样的,所以感官上的感觉往往与像素距离是不同的。这也就是为什么NN分类器比较辣鸡的原因。

cs231n学习笔记——图像分类相关推荐

  1. CS231N学习笔记-图像分类

    仅供自己学习记录参考 注:感谢B站up主,同济子豪兄的优质课程 [子豪兄]精讲CS231N斯坦福计算机视觉公开课(2020最新)_哔哩哔哩_bilibili 第二讲 图像分类 概述 目标检测等方向本质 ...

  2. CS231n 学习笔记(1)——神经网络 part1 :图像分类与数据驱动方法

    *此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...

  3. CS231n 学习笔记(2)——神经网络 part2 :线性分类器,SVM

    *此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...

  4. CS231n 学习笔记(2)——神经网络 part2 :Softmax classifier

    *此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...

  5. CS231n 学习笔记(3)——神经网络 part3 :最优化

    stanford的course note 近日在维护中,所以换了http://cs231n.stanford.edu/slides/网页的lecture4作为最优化部分的学习资料. 训练神经网络的三要 ...

  6. 【转】cs231n学习笔记-CNN-目标检测、定位、分割

    原文链接:http://blog.csdn.net/myarrow/article/details/51878004 1. 基本概念 1)CNN:Convolutional Neural Networ ...

  7. 斯坦福大学深度学习公开课cs231n学习笔记(10)卷积神经网络

    前记:20世纪60年代,Hubel和Wiesel在研究猫脑皮层中用于局部敏感和方向选择的神经元时,发现其独特的网络结构可以有效地降低反馈神经网络的复杂性,继而提出了卷积神经网络(Convolution ...

  8. 【2017年cs231n学习笔记1】Lecture2 K近邻 距离度量 超参数 交叉验证 线性分类器

    这是2017年,斯坦福大学,李飞飞教授及其博士生讲授的CS231n课程的第二节,主要内容是KNN算法.距离度量算法.超参数的选择方法.线性分类器的简单介绍. 所有图像来自课程ppt:http://cs ...

  9. cs231n 学习笔记(5)——神经网络part1:建立神经网络架构

    引言: 学习神经网络并不是一定要了解人脑神经结构.如前所属,线性分类器可以用公式s=Wx来表示,其中X表示一张图片,是一个[3072*1]的列向量,包含了一副图像里的所有像素点.W是[10*3072] ...

最新文章

  1. 独家 | 一文读懂神经网络(附解读案例)
  2. 网站SEO优化如何保证收录量?
  3. Linux TC(Traffic Control)框架原理解析
  4. Visual Studio使用技巧,创建自己的代码片段
  5. Hibernate 一对一注释
  6. 解决ssh远程连接错误问题
  7. Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割
  8. ES6~ES12——Array Includes、Object values、Object entries、Object fromEntries、flat、flatMap、空值合并运算符、可选链等
  9. 数字系统设计学习之VHDL输入设计
  10. Buildroot根文件系统构建
  11. ZBrush常用快捷键
  12. 揭开源码的神秘面纱,让源码从此无处藏身
  13. iPhone6plus 的 iOS 11 GM和正式版安装包的 md5一模一样
  14. Adobe Premiere(Pr视频剪辑)下载安装
  15. Python的EEMD实现
  16. android别踩白块设计,别踩白块儿实例——按键精灵手机助手
  17. 股票涨或跌为什么?看懂本质,才能顺势而为!
  18. 不能拖动CAD文件到CAD窗口打开,解决方案
  19. 【Spring Security入门教学】
  20. 线程、多线程、线程池总结

热门文章

  1. [编译环境]Anaconda navigator怎么打不开了?
  2. 位图法:判断一个数是否在40亿个整数中?
  3. 2019世界机器人大会正式开幕,仿生学科技震撼网友
  4. 带后台管理的超酷jquery+ajax幻灯相册php源码,仿门户网图片频道的jQuery幻灯相册轮播代码...
  5. 2.4 Spring Framework 5.x之DI(依赖注入)
  6. svg嵌套svg_使用SVG掩盖效果
  7. 全志t3linux驱动_全志T3 Linux显示驱动分析
  8. 零磁屏蔽磁场发生系统/地磁动态模拟屏蔽系统简介
  9. SpringBoot Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration
  10. Misc 第七篇——base64stego(伪加密,base64隐写)