cs231n学习笔记——Lecture2 Image Classification

该博客主要用于个人学习记录,部分内容参考自 李飞飞笔记cs231n计算机视觉课程笔记图像识别算法(一)cs231n笔记2—线性分类

一、图像识别Image Classification

1、在CV中的一个核心问题

图像识别的任务就是给输入的一张图像一个标签

2、问题:语义鸿沟Semantic Gap

  • 计算机将图像看作是一个介于[0,255]之间的一个巨大的数字网格(An image is just a big grid of numbers between [0,255]. ) ,图像可能是800*600的像素,每个像素由三个值表示(红、绿、蓝)

  • 人类可以轻松地从图像中识别出目标,而计算机看到的图像只是一组0到255之间的整数。

3、挑战

  • 视角变化(viewpoint variation)—— 一张物体在不同角度的拍摄下也会有不一样的效果。

  • 光照条件(illumination conditions)—— 照明的影响在像素级别上是剧烈的。

  • 形变(deformation) —— 猫可以有很多不同的姿势和位置,我们的算法应该对这些不同类型的转换具有鲁棒性。

  • 遮挡(occlusion)—— 有时只能看到对象的一小部分(少至几个像素)。

  • 背景杂乱(background clutter) —— 感兴趣的对象可能会融入其环境,使其难以识别。如猫在外观上看起来与背景十分相似。

  • 类内变化(intraclass variation)—— 对象有许多不同的类型,每种类型都有自己的外观。如猫有不同的形状和大小、颜色和年龄。

4、数据驱动的方法Data driven approach

与其编写一个算法指定每个感兴趣的类别的样子,不如设计一个学习算法。向计算机提供每一个类别的许多例子,然后算法通过对这些例子进行学习得到每一个类别的视觉外观,这种方法被认为是data driven approach,因为它依赖于具有标记的训练数据集。

  • 收集图像和标签的数据集
  • 用机器学习训练一个分类器
  • 在新图像上评价分类器
def train(images, labels):
 #输入图片和标签,输出模型
 return model
def predict(model, test_images):
  #输入一个模型,对图片种类进行预测
  return test_labels

二、最近邻分类器(Nearest Neighbor Classifier)

  • 在最近邻分类器中,用于比较图像的距离度量是 L1(Manhattan) distance : \(d_1(I_1,I_2)=\sum_p\left|I_1^p-I_2^p\right|\)

    其在二维矩阵中的用法如下:

    将两个图像元素相减取绝对值,然后将所有差异相加为一个数字。 如果两个图像相同,则结果为零。 但如果图像非常不同,结果会很大。

  • 训练阶段train:记住所有数据和标签 —— O(1)
    预测阶段predict:拍摄新图像,尝试在训练数据中找到与新图像最相似的图像,预测最相似的图像的标签。 —— O(N)

import numpy as np

class NearestNeighbor:
  def __init__(self):
    pass
  
  def train(self, X, y):
    ‘’‘ X is N x D where each row is an example. Yis 1-d of size N ’‘’
    # the nearest neighbor classifier simply remebers all the tarining data
    self.Xtr = X
    self.ytr = y
    
  def predict(self X):
    ’‘’ X is N x D where each row is an example wish to predict label for ‘’‘
    num_test = X.shape[0]
    # lets make sure that the output type matches the input type
    Ypred = np.zeros(num_test, dtype = self.ytr.dtype)
    
    # loop over all test rows
    for 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) # using broadcast
      min_index = np.argmin(distances) # get the index with smallest distance
      Ypred[i] = self.ytr[min_index] # predict the label of the nearest example
      
    return Ypred

We want fast at prediction; slow for training is ok

三、k-最近邻分类器(k - Nearest Neighbor Classifier)

1、Take majority vote from K closest points

2、Chosing the Distance Metric:

  • L1(Manhattan) distance : \(d_1(I_1,I_2)=\sum_p\left|I_1^p-I_2^p\right|\)

  • L2(Euclidean) distance : \(d_2(I_1,I_2)=\sqrt{\sum_p(I^p_1-I^p_2)^2}\)

    • L1距离取决于所选择的坐标轴。对于输入的特征向量,如果向量中的一些值有一些重要意义的情况,最好使用L1距离。
    • L2距离不受坐标轴的影响。用于只是某个空间中的一个通用向量的情况。计算矩阵的欧氏距离(作业中会用到)

3、超参数hyperparameters

  • 参数不一定是从训练数据中学习到的,相反,这些是你提前做出的关于算法的选择,没有办法直接从数据中学习。有关算法的选择是我们设定的而不是学习的。比如K、Distance Metric。

  • 设置超参数:

    • ❌ 选择在训练集上表现最好的超参数

    • ❌ 将数据分为训练集和测试集,在训练集中设置不同的超参数训练算法,将经过训练的分类器应用于测试集中,选择在测试集上效果最好的超参数

    • ✅ 将数据分为训练集train、验证集validation、测试集test,在训练集中用许多不同的超参数选择来训练我们的算法,在验证集上进行评估,选择在验证集上表现最佳的超参数,一切操作完成后将会获得在验证集上表现最佳的分类器,然后仅在测试集上运行一次,结果表示算法在未见的新数据上表现如何。注意验证集和测试集一定要有明显的区分,我们不能将测试集用于调整超参数。每当您设计机器学习算法时,您都应该将测试集视为一种非常宝贵的资源,最好在最后一次之前永远不要接触它。最后只对测试集进行一次评估,但是我们可以通过验证集去选择最合适的超参数

    • 交叉验证Cross Validation:拿出一部分数据作为测试集在最后使用,剩余部分将训练集分为许多不同折,选择一折作为验证集。常用3折、5折、10折交叉验证。多用于小型数据集,但计算成本高,在深度学习中不常用。

      • 人们倾向于将50%-90%的训练数据用于训练,其余的用于验证。

      • 如果超参数的数量很大,可能更倾向于使用更大的验证分割。

      • 如果验证集中的例子数量很少(可能只有几百个左右),使用交叉验证会更安全

# assume we have Xtr_rows, Ytr, Xte_rows, Yte as before
# recall Xtr_rows is 50,000 x 3072 matrix
Xval_rows = Xtr_rows[:1000, :] # take first 1000 for validation
Yval = Ytr[:1000]
Xtr_rows = Xtr_rows[1000:, :] # keep last 49,000 for train
Ytr = Ytr[1000:]

# find hyperparameters that work best on the validation set
validation_accuracies = []
for k in [1, 3, 5, 10, 20, 50, 100]:

  # use a particular value of k and evaluation on validation data
  nn = NearestNeighbor()
  nn.train(Xtr_rows, Ytr)
  # here we assume a modified NearestNeighbor class that can take a k as input
  Yval_predict = nn.predict(Xval_rows, k = k)
  acc = np.mean(Yval_predict == Yval)
  print 'accuracy: %f' % (acc,)

  # keep track of what works on the validation set
  validation_accuracies.append((k, acc))

4、KNN存在的问题

在实际中基本不用。

  • 测试时间非常慢:我们通常关心的是测试时间的效率,而不是训练时间的效率。

  • 像素上的距离度量不提供有效的信息。例如下图中我们视觉上可以感觉到图像之间的差异,但4张图的L2 Distance都相同,说明了L2 Distance在捕捉图像之间的感知距离方面确实做得不好。

  • 维度灾难

    • 图像是高维对象(即它们通常包含许多像素),高维空间上的距离可能非常违反直觉。
    • 像素距离根本不对应于感知或语义相似性。

四、线性分类Linear Classification

1、参数化方法Parametric Approach

总结我们训练数据的知识,并将所有这些知识融入到参数W中,在测试阶段不再需要实际的训练数据,只需要这些参数W。这使得我们的模型更有效,并且可以在像手机这样的小型设备上运行

2、函数 f(x,W) = Wx + b

  • X: 输入数据
  • W:参数或权重,对于输入x属于哪个类的可能性有多大
  • b:偏置值,不与训练数据交互,只给一些数据独立的针对一类的偏好值,比如在数据集不平衡时,数量更多的那类偏差元素比其他的高,给一个数据独立缩放比例及每个类的偏移量
  • 只需要做一个矩阵乘法和一个矩阵加法就能对一个测试数据分类,这比 k-NN 中将测试图像和所有训练数据做比较的方法快多了。

3、W和b的合并技巧

一个常用的技巧是将W,b两组参数组合成一个单独的矩阵,同时x向量增加一个维度,这个维度的数值为常数1,这就是默认的偏差维度。由此,新的公式将简化为: f(x,W)=Wx

4、线性分类≈模板匹配方法

一张图像对应不同分类的得分,是通过使用内积(也叫点积)来比较图像和模板,然后找到和哪个模板最相似。从这个角度来看,线性分类器就是在利用学习到的模板,针对图像做模板匹配。

5、例子

线性分类器计算图像中3个颜色通道中所有像素的值与权重的矩阵乘,从而得到分类分值。根据我们对权重设置的值,对于图像中的某些位置的某些颜色,函数表现出喜好或者厌恶(根据每个权重的符号而定)。举个例子,可以想象“船”分类就是被大量的蓝色所包围(对应的就是水)。那么“船”分类器在蓝色通道上的权重就有很多的正权重(它们的出现提高了“船”分类的分值),而在绿色和红色通道上的权重为负的就比较多(它们的出现降低了“船”分类的分值)

上图是一个将图像映射到分类分值的例子。为了便于可视化,假设图像只有4个像素(都是黑白像素,这里不考虑RGB通道),有3个分类(红色代表猫,绿色代表狗,蓝色代表船,注意,这里的红、绿和蓝3种颜色仅代表分类,和RGB通道没有关系)。首先将2×2的图像像素拉伸为一个具有4个元素的列向量,与W进行矩阵乘,然后得到各个分类的分值。需要注意的是,这个W一点也不好:猫分类的分值非常低。从上图来看,算法倒是觉得这个图像是一只狗。

6、图像数据预处理

将原始像素值从[0,255]更改为[-1,1]

7、线性分类器的问题

每个类别只能学习一个模板

8、线性分类器的困境

多模态数据、奇偶问题。
蓝色类别是图像中像素的数量,这个数字大于0,并且是奇数,任何像素个数大于0的图像,都归为红色类别,如果你真的去画这些不同的决策出现这些不同的决策取悦,你能看到我们奇数像素点的蓝色类别,在平面上有两个象限,甚至是两个相反的象限,所以我们没有办法能够绘制一条单独的直线,来划分蓝色和红色。

posted @ 2022-12-01 09:49  jasf  阅读(153)  评论(0编辑  收藏  举报