其他分享
首页 > 其他分享> > cs231n 课程学习 一

cs231n 课程学习 一

作者:互联网

cs231n 课程学习 一

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

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

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

一 简介

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

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

三 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