Lecture2: Image Classification Pipeline

Lecture2 Image Classification Pipeline

1. 图片在计算机中的储存方式&图像识别

1.1 图片在计算机中的储存方式

    计算机中的图片有两种,位图和矢量图,其中矢量图储存的是该图片的计算方式,比如在这张图距离顶部10%,距离右边20%的地方有一个点,哪里有一条线,线有多长等等。从这里我们们可以看出,矢量图放大是没有损失的。但是位图不一样。位图储存的是像素点,也就是说把图片当作矩阵来存储。

    我们知道,红黄蓝三种颜色混合可以构成不同的颜色。因此我们可以用三个矩阵来储存一张图片,即RGB三色,我们用一个0-255的数字表示这个颜色的深度,比如说一个像素点可以是R110,G250,B120混合而成

1.2 图像识别

    图像识别顾名思义,就是把图片中的内容提取出来。从Image 到 Label。

    其实很多任务都可以归结为,给一个输入,给一个输出(或者多个输出)。也就是说,我们需要找到一个函数,这个函数在给定输入图片的像素矩阵之后,可以输出这张照片是什么,比如是一只猫。

    对于我们人类来说,看一眼就知道了,但是电脑毕竟需要我们来编程他不会自己想(起码现在不会)如何让电脑来识别猫呢?

    我们可以尝试自己写出一个函数来识别猫吗?这……有点困难。当然我们也不会这么做。我们想让计算机来自己学习出猫长什么样,就像人一样。我们是怎么知道一只动物是猫的呢?我们见过很多相似的动物(train_image)同时又有别人告诉我们这是猫(train_label)于是,当我们再见到一个动物的时候,我们可以根据之前的“训练”来判断我们面前的是不是猫。

    因此我们希望也能对计算机执行类似的操作,我们想要训练一个模型,基本步骤如下:

  • 输入:输入是包含N个图像的集合,每个图像的标签是K种分类标签中的一种。这个集合称为训练集(train_set)。
  • 学习:这一步的任务是使用训练集来学习每个类到底长什么样。一般该步骤叫做训练分类器或者学习一个模型。
  • 评价:让分类器来预测它未曾见过的图像(验证集 validation_set)的分类标签,并以此来评价分类器的质量。我们会把分类器预测的标签和图像真正的分类标签对比。毫无疑问,分类器预测的分类标签和图像真正的分类标签如果一致,那就是好事,这样的情况越多越好。

2. Nearest Neighbor 分类器

2.1 Nearest Neighbor 分类器

    我们知道,判断两张照片里面的是不是同一种生物,最简单的方法就是对比和两张图片的相似度,长得像,就应该是一个,Nearest Neighbor 分类器正是这么想的。我们首先要定义一个距离函数,来度量两张图片之间的距离,距离越近越有可能是同类。因此,我们对于一张图片,计算他和训练集所有图片之间的距离,取最近距离图片的标签来作为这张图片的标签。

2.2 KNN

    但是上面的那种方法有点不足,就是可能会出现个别特例,万一,有只狗像猫,我们送进去的猫的图片还就特别像怎么办?所以,我们改进上述方案,不只用一个了,用好多个,我们取K个离输入图片距离最近的训练图片,投票,那个label最多,就是哪个。


Lecture2: Image Classification Pipeline_第1张图片
分类可视化的结果

2.3 超参数

    这是我们第一次面临选择,距离?图片的距离?图片有什么距离啊。我们已经知道图片在计算机中使用像素点存储,一种比较自然的想法就是,所有像素点数值相减取绝对值加和。这种距离称为L1距离(曼哈顿距离)。

  • 曼哈顿距离 Manhattan Distance(L1)
  • 欧氏距离 Euclidean Distance (L2)

       为什么叫做超参数呢,就是因为这两种哪种好其实谁都不知道,这个是试出来的有兴趣的可以试试,我给的代码里是L2距离。

    还有一个选择是K,K取多少比较合适呢?这个……自己慢慢试去吧。

2.4 数据集

数据集可以分为以下几个类:训练集(train_set),验证集(validation_set),测试集(test_set)。

  • 训练集:用来训练模型,得出参数(过一会将出现的权重)的。
  • 验证集:用来调超参数的。
  • 测试集:评估最后结果的。
       注意,训练集只是整个数据集的一部分,因为如果训练集包括了所有的数据,那么他们可能在这个数据集上表现良好,换一个就变差了,毕竟,这是用这个数据集训练出来的(过拟合)。
       但是有时候数据较少,没有那么多可以划分,这时,可以使用交叉验证。我们将一份数据分成N份,前N-1份用于训练,第N份用于验证,循环着来,取N次验证的平均值做最后结果。

3. 线性分类

3.1 开始“学习”

   从KNN中我们可以看出去,其实这个算法是没有在“学习”的,用我的话来说,KNN只是跟过往的经验来从表层判断,没有从中抽象出一般规律。这一点表现在,KNN的“训练”只是简单的把训练集输入,没有任何参数改变。我们可以想象,这种方法在处理未知图片时正确率一定不理想。我们希望从训练集中抽象出一般规律,这样可以更好地应对未知的情况。

3.2 线性分类

    我们先想下,比较理想的模型应该是什么样的,给定输入的图片矩阵,直接输出这张图片是什么,比如这张图片是狗,那就输出一个【dog】。这显然有点困难,毕竟从图片到文字人类进化了几百万年?我们简化一下问题,输入一个图片矩阵,输出一个矩阵(或者列向量)这个向量的n维代表n种类别,我们让这个模型不管怎么样,必须归一类(或者你把其中一类定成Null)。那么最简单的模型是什么样的呢?没错,就是输出列向量,是输入列向量乘上一个矩阵,也就是输出是输入的线性组合。

Lecture2: Image Classification Pipeline_第2张图片
图示

    我们希望通过学习,定出参数矩阵。

3.3 线性回归-理解

    为什么这样可以分类呢。我们在一个二维平面上,给一堆点,我们可以使用一条直线把它分成两部分,这个时候这条直线就是我们与这个类似,线性回归,可以看作把这些图片映射到高维空间,在高维空间,他们是线性可分的。

Lecture2: Image Classification Pipeline_第3张图片
例子

4. KNN实现(Python)

4.1 环境

  • Python 3.6.6
    * Numpy == 1.14.5
  • Pycharm 2018.2 Professional
  • DataSet
    * CIFAR-10 DataSet

4.2 代码

https://github.com/lisixu1999/cs231n_programs/blob/master/Lecture%201/KNN/KNN.py

# 这是KNN分类器的示例程序
# 2018-8-15

import numpy as np

par_k = 3
#定义Knn的K
path = "E:\\2017-2018 Summer Vacation\\cs231n_program\\DataSets\\"
#这是存放数据集的路径,这个数据集我自己处理过
train_images = np.reshape(np.load(path + "cifar10_images.npy"), (10000, 3 * 32 * 32))
train_labels = np.load(path + "cifar10_labels.npy")
validation_images = np.reshape(np.load(path + "cifar10_images_validation.npy"), (10000, 3 * 32 * 32))
validation_labels = np.load(path + "cifar10_labels_validation.npy")

train_images = train_labels[:10]
train_labels = train_labels[:10]
validation_images = train_labels[:10]
validation_labels = train_labels[:10]
#不想使用整个数据集做实验的可以从中选一部分,要不然根本跑不完,
#i5-5200U下跑1000就费劲了
print(train_images.shape)
print(train_labels.shape)
print(validation_images.shape)
print(validation_labels.shape)

#L1范数
def distance_l1(a, b):
    return np.sum(np.abs(a - b), axis=1)

#L2范数
def distance_l2(a, b):
    return np.sqrt(np.sum(np.power(a - b, 2)))

#定义一个正确率的计算
def accuracy(a, b):
    total = a.shape[0]
    counter = 0
    for i in range(0, total):
        if a[i] == b[i]:
            counter = counter + 1
    return counter / total

#正儿八经的代码
class KNN(object):
    def __init__(self):
        k = par_k
        pass
    #训练代码就是把数据存进去
    def train(self, train_im, train_la):
        self.tr_data = train_im
        self.tr_label = train_la
        pass
    #预测代码就是把每个距离算出来,依次比较
    def predict(self, input_vector):
        k = par_k
        round = input_vector.shape[0]
        n_train = self.tr_data.shape[0]
        dis_array = np.zeros((round, n_train))
        label_test = np.zeros(round)
        for i in range(0, round):
            for j in range(0, n_train):
                dis_array[i, j] = distance_l2(input_vector[i], self.tr_data[j])
        for i in range(0, round):
            temp = np.argsort(dis_array[i])
            temp_label = np.zeros(par_k)
            for j in range(0, par_k):
                temp_label[j] = self.tr_label[temp[j]]
            label_test[i] = np.argmax(np.bincount(temp_label.astype(int)))
        return label_test



if __name__ == "__main__":
    myknn = KNN()
    myknn.train(train_im=train_images, train_la=train_labels)
    predict_results = myknn.predict(input_vector=validation_images)
    accuracy = accuracy(predict_results, validation_labels)
    print(accuracy)

你可能感兴趣的:(Lecture2: Image Classification Pipeline)