ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

cs231n 课程学习 一

2022-02-10 11:02:09  阅读:171  来源: 互联网

标签:分类器 cs231n 训练 课程 num 学习 np train test


cs231n 课程学习 一

cs231n 课程资源:Stanford University CS231n: Convolutional Neural Networks for Visual Recognition

我的 github 作业:FinCreWorld/cs231n: The assigments of cs231n (github.com)

第一章 图像分类——以KNN为例

一 简介

  • 什么是图像分类:给定程序一个图像,程序从预定的标签集合中选出一个标签,赋给该图像,实现图像分类

  • 举例img

    图像在计算机中以矩阵方式存储,上图猫咪像素点数为 248 × 400 248\times400 248×400,由于使用三通道RGB的表示形式,总数据点数为 248 × 400 × 3 248\times400\times3 248×400×3。标签集有 {cat、dot、hat、mug},使用分类器预测该图像,为不同标签赋予值,表示该图像属于对应标签的概率。

  • 困难

    • 角度变化:图像中对于事物的观察角度不同
    • 大小变化:同一类物体可能拥有不同的大小。如姚明和潘长江
    • 扭曲:物体的形状不是固定的,比如做瑜伽的人
    • 遮盖:物体可能被遮盖
    • 亮度:如黑底白猫与黑猫白底,只是亮度有区别
    • 背景相似度:常见于生物拟态
    • 类别内部差别:同一类物体可能形状千差万别,比如衣服
  • 数据驱动方式:对于一些问题,我们可以简单的通过算法解决,比如排序问题。但是图像分类很难通过简单的算法解决,而是需要使用大量的数据来供程序进行学习。这种方式称为数据驱动。

  • 分类流程

    • 输入:包括 N N N 个图像,每个图像都拥有 K K K 类标签中的一个,称所有这样图像的集合为训练集。
    • 学习:使用训练集训练一个分类器或模型,来学习每个标签所对应的图像基本形状
    • 评价:通过验证集评价学习器学习的质量(分类效果)

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

最近邻分类器会将测试图像与每一个训练图像进行比较,选出与测试图像距离最小的训练图像,该训练图像的标签即为预测的测试图像的标签。

  • 关于图像向量化

    由于图像大小一般为 width × height × channels \text{width}\times\text{height}\times\text{channels} width×height×channels,运算不方便,我们可以将其转化为 D × 1 D\times1 D×1 的向量,其中 D = width × height × channels D=\text{width}\times\text{height}\times\text{channels} D=width×height×channels

  • 关于距离

    考虑有两个相同维度的向量, v 1 ⃗ D × 1 \vec{v_1}_{D\times1} v1​ ​D×1​ 和 v 2 ⃗ D × 1 \vec{v_2}_{D\times1} v2​ ​D×1​,这两个向量之间的距离有

    • L1 距离: d ( v 1 ⃗ , v 2 ⃗ ) = ∑ k ∣ v 1 k ⃗ − v 2 k ⃗ ∣ d(\vec{v_1},\vec{v_2})=\sum_k|\vec{v_{1k}}-\vec{v_{2k}}| d(v1​ ​,v2​ ​)=∑k​∣v1k​ ​−v2k​ ​∣
    • L2 距离: d ( v 1 ⃗ , v 2 ⃗ ) = ∑ k ( v 1 k ⃗ − v 2 k ⃗ ) 2 d(\vec{v_1},\vec{v_2})=\sqrt{\sum_k(\vec{v_{1k}}-\vec{v_{2k}})^2} d(v1​ ​,v2​ ​)=∑k​(v1k​ ​−v2k​ ​)2

三 k-近邻分类器(k-Nearest Neighbor Classfier)

k-近邻分类器是最近邻分类器的升级版,给定测试图像,我们从训练集中找出与测试图像距离最小的前 k k k 个图像,其中出现次数最多的标签就是我们的预测值。 k = 1 k=1 k=1 时,k-近邻分类器等价于最近邻分类器。k-近邻分类器的分类效果更为平滑,可以减小异常值带来的影响。下面给出一个k-近邻分类器的简单实现

k-近邻分类器的实现实例

class KNearestNeighbor(object):
    """ a kNN classifier with L2 distance """
    
    def __init__(self):
        pass

    def train(self, X, y):
        """
        Train the classifier. For k-nearest neighbors this is just
        memorizing the training data.

        Inputs:
        - X: A numpy array of shape (num_train, D) containing the training data
          consisting of num_train samples each of dimension D.
        - y: A numpy array of shape (N,) containing the training labels, where
             y[i] is the label for X[i].
        """
        self.X_train = X
        self.y_train = y
        
    def predict(self, X, k=1, num_loops=0):
        """
        Compute the distance between each test point in X and each training point
        in self.X_train using a nested loop over both the training data and the
        test data.

        Inputs:
        - X: A numpy array of shape (num_test, D) containing test data.

        Returns:
        - dists: A numpy array of shape (num_test, num_train) where dists[i, j]
          is the Euclidean distance between the ith test point and the jth training
          point.
        """
        num_test = X.shape[0]
        num_train = self.X_train.shape[0]
        dists = np.zeros((num_test, num_train))
        for i in range(num_test):
            for j in range(num_train):
                dists[i, j] = np.sqrt(np.sum(np.square(X[i] - self.X_train[j])))
        return self.predict_labels(dists, k=k)
    
    def predict_labels(self, dists, k=1):
        """
        Given a matrix of distances between test points and training points,
        predict a label for each test point.

        Inputs:
        - dists: A numpy array of shape (num_test, num_train) where dists[i, j]
          gives the distance betwen the ith test point and the jth training point.

        Returns:
        - y: A numpy array of shape (num_test,) containing predicted labels for the
          test data, where y[i] is the predicted label for the test point X[i].
        """
        num_test = dists.shape[0]
        y_pred = np.zeros(num_test)
        for i in range(num_test):
            # A list of length k storing the labels of the k nearest neighbors to
            # the ith test point.
            closest_y = []
            closest_y = np.argsort(dists[i, :], kind='quicksort')[:k]
            closest_y = self.y_train[closest_y]
            y_pred[i] = np.argmax(np.bincount(closest_y))

        return y_pred

k-近邻分类器的向量化实现

该分类器的主要运算量在于 predict 函数中的两层循环,用于计算所有训练样例与所有测试样例之间的距离,我们可以使用 numpy 包提供的向量化计算进行加速,我们需要将原本的求和运算(循环实现)推导至矩阵运算(向量操作实现)

我们假设每一个图像都被压缩成 1 × D 1\times D 1×D 的向量,训练集 A N × D A_{N\times D} AN×D​ 包含 N N N 个训练样例,测试集 B M × D B_{M\times D} BM×D​ 包含 M M M 个测试样例,距离矩阵 P M × N P_{M\times N} PM×N​, P i j P_{ij} Pij​ 表示第 i i i 个测试样例到第 j j j 个训练样例的距离。有
P i j 2 = ∑ k ( B i k − A j k ) 2 = ∑ k B i k 2 − 2 ∑ k B i k A j k + ∑ k A j k 2 \begin{aligned} P_{ij}^2&=\sum_k(B_{ik}-A_{jk})^2\\ &= \sum_k{B_{ik}^2}-2\sum_k{B_{ik}A_{jk}}+\sum_k{A_{jk}}^2\\ \end{aligned} Pij2​​=k∑​(Bik​−Ajk​)2=k∑​Bik2​−2k∑​Bik​Ajk​+k∑​Ajk​2​
其中 Q i j = ∑ k B i k A j k Q_{ij}=\sum_k{B_{ik}A_{jk}} Qij​=∑k​Bik​Ajk​ 为向量乘积的形式,即 Q = B ⋅ A T Q=B\cdot{A^T} Q=B⋅AT,而左右两个求和式可以使用 np.sum 函数以及广播机制解决。

最后,我们可以简单的通过 numpy 来实现向量运算,有

sum_B2 = np.sum(np.square(X), axis=1).reshape(-1, 1)
sum_A2 = np.sum(np.square(self.X_train), axis=1).reshape(1, -1)
dists = np.sqrt(sum_B2 - 2 * B.dot(A.T) + sum_A2)

四 超参数调参(Hyperparameter tuning)

以上分类器实现的过程中,我们需要对一些参数进行选择,比如 k k k 值的选取以及 L1 和 L2 距离的选取,称这些参数为超参数。这些参数需要我们不断的调试,以达到最佳的性能。

我们的数据集包括训练集与测试集,如果我们在训练集上训练,在测试集上对训练效果进行评价,并以此评价为基础进行调参,那么最后我们的模型将会在测试集上取得非常好的效果,但是该模型往往无法用于实际中,因为我们最后得到的模型仅在测试集上表现很好,出现了过拟合现象,缺乏泛化能力。

img

因此,我们需要将训练集进行单独划分,再次划分出训练集验证集,我们基于训练集训练,基于验证集调参,最后通过测试集来测试模型的泛化能力,避免模型过拟合验证集。

交叉验证

如果训练集较小,那么我们可能缺乏足够数据来划分出训练集与验证集,这时我们可以采用交叉验证的方式。例如,对于 5 划分交叉验证(5-fold-cross-validation),我们将训练集等分为 5 份,使用其中的 4 份进行训练,最后一份进行验证,如此训练-验证 5 次,对最后的预测精度取平均即可。代码实现如下

def accuracy(y_test_pred, y_test):
    return float(np.sum(y_test_pred==y_test))/y_test_pred.shape[0]

X_train_folds = np.array_split(X_train, num_folds)
y_train_folds = np.array_split(y_train, num_folds)
classifier = KNearestNeighbor()
for k in k_choices:
    k_to_accuracies[k] = []
    for i in range(num_folds):
        # test data
        X_te = X_train_folds[i]
        y_te = y_train_folds[i]
        # validation data
        X_tr = np.vstack(X_train_folds[0:i] + X_train_folds[i+1:])
        y_tr = np.hstack(y_train_folds[0:i] + y_train_folds[i+1:])
        classifier.train(X_tr, y_tr)
        y_pre = classifier.predict(X_te, k, 0)
        k_to_accuracies[k].append(accuracy(y_pre, y_te))

上述代码用于调试 k 值(k-近邻)。第一层循环确定不同的 k 值,第二层循环进行交叉验证,训练集一共被划分为了 num_folds 份,遍历其中的 1 份数据进行验证,num_folds-1 份数据进行训练,将结果保存到 k_to_accuracies[k] 中。 k_to_accuracies[k] 是一个数组,保存了当前 k 值下,使用不同训练集与验证集之后的训练精度。

最后交叉验证结果如下,可以发现当 k 在 10 左右时训练精度最佳,每一列有 5 个点,表示 5 次交叉验证的精度,而曲线表示交叉验证结果的均值。

output

调参数据集选取的说明

实际中人们较少使用交叉验证,因为其运算量较大。人们常使用 50%-90% 的训练数据用于训练,剩余的进行验证,如果超参数较多,同时训练数据较多,那么我们就可以使用较多一部分的训练数据充当验证集。如果样本数据量较少,我们可以使用交叉验证。

五 近邻分类器的优缺点

  • 优点:
    • 模型简单
    • 训练时间少
  • 缺点:
    • 预测时间长。实际生活中,我们通常较少关心训练时间,而更关心预测时间。我们通常部署训练好的模型,预测时间会影响用户的体验。
    • 无法较好的应对高维数据。因为高维数据的极少量异常点就会带来数据点之间较大的距离变化。

标签:分类器,cs231n,训练,课程,num,学习,np,train,test
来源: https://blog.csdn.net/qq_45520114/article/details/122856168

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有