计算机视觉

61 阅读8分钟

Windows常用的图形API是DirectX/Direct3d。但使用DirectX还是OpenGL不是操作系统决定的,而是GPU驱动程序决定的。

最邻近算法

L1距离(Manhattan Distance)

累加测试图片的每个像素点和训练图片的每个像素点之间差值的绝对值,以此获得L1:

L2距离(Euclidean Distance)

累加测试图片的每个像素点和训练图片的每个像素点之间差值的平方,对累加结果取平方根,以此获得L2:

  • L1常用于各个向量权重不同的情况。
  • L2常用于向量为通用向量,权重相同的情况。
  • 两者并无优劣之分,只是应用场景不同。

Minkowski Distance

总结归纳可得第三个公式——明可夫斯基距离:

  • p = 1时,为曼哈顿距离公式;
  • p = 2时,为欧拉距离公式。

K-最邻近算法

百度百科:baike.baidu.com/item/k近邻算法/…

最近邻居算法其目的可以简单理解为通过已知有色点(训练集)来绘制一幅颜色区域图(分类器),交接处尽可能平滑,且颜色内部不包含其他颜色,这样一来当新的有色点(测试集)出现时,可以知道它应该处于哪个区域。L1、L2(Distance metric)和K是KNN算法的超参数(hyperparameters)。

K=1 时,为简单的最近邻居算法但是因为单个噪点权重过高对周围颜色影响过深,会出现最右侧图中颜色交界处不平滑(或者颜色内部有其他颜色)的问题,而随着K的值逐渐升高,意味着某个白色区域的颜色取决于离它最近的 K 个已知有色点中哪种颜色最多,这就大大削弱了单个点的权重,也就削弱了噪点对正确结果的影响。【后两张图中的白色区域意味着它们没有最近邻居】

K-最邻近算法只是一种通用的训练思想,并不仅限用于接受向量、图片,它还可以接收文本等其他内容作为训练集,只要指定好“距离”指标,比如对文本而言可以是段落和段落之间的距离。如下,即使训练集一致,选择不同的距离也会导致不同的结果:

  • L1导致颜色边界的形状更跟随坐标系;
  • L2导致颜色边界的形状更跟随有色点本身。

PS:K-近邻算法交互演示的网站

实现代码:

import numpy as np

class NearestNeighbor(object):
  def __init__(self):
    pass

  def train(self, X, y):
    # X是NXD的数组,其中每一行代表一个样本,Y是N行的一维数组,对应X的标签
    # 最近邻分类器就是简单的记住所有的数据
    self.Xtr = X
    self.ytr = y

  def predict(self, X):
    # X是NXD的数组,其中每一行代表一个图片样本
    # 看一下测试数据有多少行
    num_test = X.shape[0]
    # 确认输出的结果类型符合输入的类型
    Ypred = np.zeros(num_test, dtype = self.ytr.dtype)

    # 循环每一行,也就是每一个样本
    for i in xrange(num_test):
      # 找到和第i个测试图片距离最近的训练图片
      # 计算他们的L1距离
      distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
      min_index = np.argmin(distances) # 拿到最小那个距离的索引
      Ypred[i] = self.ytr[min_index] # 预测样本的标签,其实就是跟他最近的训练数据样本的标签
    return Ypred

KNN算法不适合图像识别

  • 第二点是指无论对图像做出遮挡、偏移还是变色的更改,和原图像素在各个向量上相比没有差别。

KNN算法的维度诅咒

为了捕获10%的训练数据点:

  • 一维下涵盖的范围,任何一个方向上取0.05个单位:

  • 二维下涵盖的范围,任何一个方向取0.16个单位(0.32*0.32 ≈ 0.1):

  • 三维下涵盖的范围,任何一个方向取0.23个单位(0.460.460.46 ≈ 0.1):

总结:

  • 在单维中,只需要包括单个预测器范围的10%,就可以定义一个包括10%的均匀分布的训练数据的邻居。
  • 在两个维度上,需要两个预测器的范围各占32%。
  • 在三个维度上,需要三个预测器范围中的每一个的46%。

可以看到,随着维度的升高,捕获单点的最近邻居的成本变得越来越大,下图是1~100个维度(X轴)之间的每一个维度对应的预测器范围比例(Y轴):

  • 当达到10个维度时,几乎需要每个预测器的80%的范围;

  • 在50个维度时,需要95%的预测器范围;

  • 到了100个维度,需要98%。

  • 这意味着在高维度KNN算法的训练结果将没有可信度,生成的结果点依赖的已有点重复率过高,导致所有结果点基本一致。

换个方向思考,如果把邻居的边界限制在每个预测器范围的10%,能找到多少个邻居?

下图在X轴上显示了从1到10的维度,在Y轴上显示了覆盖每个预测器范围的10%的邻域所捕获的训练数据的比例:

如果把一个索引点的邻域定义为覆盖每个预测器范围的10%:

  • 在一个维度上,邻域覆盖了10%的训练数据。
  • 在二维,只有1%的训练数据。
  • 在10维,邻域只包括训练数据的0.00000001%
  • 这意味维度越高、邻域越空。

测试集的准确性更重要而非训练集

注意:测试集非常宝贵,只能最终的时候才能使用(所有模型的参数都确定下来之后), 不能每次训练完之后,用测试集来调一下参数。

假设训练出来的分类器可以完美契合测试集的结果,但这并不是个好的现象。因为训练集中的噪点并不是有效数据,像K=1固然让分类器预测训练集的准确性达到了100%,但是由于噪点权重过大,导致未来预测未知数据时(测试集),它不一定有好的结果,即过拟合/欠拟合现象。

好的做法是细分训练集,将一部分训练集拿出来当验证集。三者关系举例来说即:训练集相当于我们在高中时日常做卷子,验证集相当于每一次模拟考试,而测试集相当于最终的高考。我们通过训练集来提升自己的能力,但也需要模拟考试来自我验证能力是否达标,最终面向完全未知的高考,也是检验我们学习成果的时候。这套流程训练出的分类器对测试集的处理结果更加准确。

换言之,我们不在乎一个分类器能否准确识别出来所有猫的素材图片,而在乎是否能够在未来应用时别把狗识别成猫。

基于这种思想,诞生了常用于小型数据集的交叉验证方法,但这种方法在深度学习中并不常用(太耗时)。

实现过程是定义折叠训练集的次数,图中是5次,也就是将训练集五等分,一个单独的子样本被保留作为验证集,其他K-1个样本作为训练集。交叉验证重复K次,每个子样本都会作为一次验证集,平均K次的结果或者使用其它结合方式,得到一个分类器。

基于这种思想,诞生了常用于小型数据集的交叉验证方法,但这种方法在深度学习中并不常用。

线性分类器

线性分类器的公式如下所示,X是输入图片的相关信息(比如像素),W是权重矩阵,b是对应分类的偏移量。

举个例子,假设猫图像的分辨率是2X2,将其转换为列向量,并根据给出的W(权重)矩阵相乘,之后加上偏移量得到类分量(橙色对应猫,紫色对应狗,绿色对应船)。

权重矩阵可视化,每一行是一个类别,而每一列是该类别的训练数据,最终得到图中最下方一行模糊地描述类别特征的模版,这是因为线性分类器只能为一个类别提供一个模版:

线性分类器不适用的情况

线性不可分简单来说就是一个数据集不可以通过一个线性分类器(直线、平面)来实现分类,如下图中最左侧的图可以线性分类,但是后两张图无法线性分类: