KNN在Cifar上的应用

def getXmean(X_train):
    X_train = np.reshape(X_train, (X_train.shape[0], -1))  # 将图片从二维展开为一维
    mean_image = np.mean(X_train, axis=0)  # 求出训练集所有图片每个像素位置上的平均值
    return mean_image

def centralized(X_test,mean_image):
    X_test = np.reshape(X_test, (X_test.shape[0], -1))  # 将图片从二维展开为一维
    X_test = X_test.astype(np.float)
    X_test -= mean_image  # 减去均值图像
    return X_test

X_train = train_loader.dataset.train_data
mean_image = getXmean(X_train)
X_train = centralized(X_train,mean_image)
y_train = train_loader.dataset.train_labels
X_test = test_loader.dataset.test_data[:100]
X_test = centralized(X_test,mean_image)
y_test = test_loader.dataset.test_labels[:100]
num_test = len(y_test)
y_test_pred = kNN_classify(6, 'M', X_train, y_train, X_test)
num_correct = np.sum(y_test_pred == y_test)
accuracy = float(num_correct) / num_test
print('Got %d / %d correct => accuracy: %f' % (num_correct, num_test, accuracy))
  • X_train.shape[0]:这表示X_train的第一个维度的大小,即它的行数。在机器学习和数据处理的上下文中,这通常代表样本的数量。
  • -1:在numpyreshape函数中,-1是一个特殊的值,它告诉numpy自动计算该维度的大小,以保持数组中元素的总数不变。这意味着,如果你指定了除-1之外的其他所有维度的大小,numpy将计算出-1所在维度的大小,以确保重塑后的数组与原始数组具有相同数量的元素。

 

模型参数调优

机器学习方法(深度学习是机器学习中的一种)往往涉及很多参数甚至超参数,此实践过程中需要对这些参数进行适当地选择和调整。
对于KNN算法来说,k就是需要调整的超参数。

有一种更专业的穷举调参方法称为GridSearch,    即在所有候选的参数中,通过循环遍历,尝试每一种的可能性,表现最好的参数就是最终的结果。

那么选用哪些数据集进行调参呢?                                                                
方法一,选择整个数据集进行测试。这种方法有一个非常明显的问题,那就是设定 k=1 总是最好的,因为每个测试样本的位置总是与整个训练集中的自己最接近,

Your Dataset
方法二,将整个数据集拆分成训练集和测试集,然后在测试集中选择合适的超参数。这里也会存在一个问题,那就是不清楚这样训练出来的算法模型对于接下来的新的测试数据的表现会如何
train
test

方法三,将整个数据集拆分成训练集、验证集和测试集,然后在验证集中选择合适的超参数,最后在测试集上进行测试。这个方法相对来说比之前两种方法好很多,也是在实践中经常使用的方法
train validation test

方法四,使用交叉验证,将数据分成若干份,将其中的各份作为验证集之后给出平均准确率,最后将评估得到的合适的超参数在测试集中进行测试。这个方法更加严谨,但实
践中常在较小的数据集上使用


fold 1 fold 2 fold 3 fold 4 fold 5 test
fold 1 fold 2 fold 3 fold 4 fold 5 test
fold 1 fold 2 fold 3 fold 4 fold 5 test

class Knn:

    def __init__(self):
        pass

    def fit(self,X_train,y_train):  
        self.Xtr = X_train
        self.ytr = y_train

    def predict(self,k, dis, X_test):
        assert dis == 'E' or dis == 'M', 'dis must E or M'
        num_test = X_test.shape[0]  # 测试样本的数量
        labellist = []
        # 使用欧拉公式作为距离度量
        if (dis == 'E'):
            for i in range(num_test):
                distances = np.sqrt(np.sum(((self.Xtr - np.tile(X_test[i], (self.Xtr.shape[0], 1))) ** 2), axis=1))
                nearest_k = np.argsort(distances)   从小到大排列
                topK = nearest_k[:k]
                classCount = {}
                for i in topK:
                    classCount[self.ytr[i]] = classCount.get(self.ytr[i], 0) + 1
  1. self.ytr[i]:访问 self.ytr 列表(或数组)中索引为 i 的元素,即当前训练样本的标签。

  2. classCount.get(self.ytr[i], 0):尝试从 classCount 字典中获取键为 self.ytr[i] 的值。如果 self.ytr[i] 不在 classCount 的键中,则使用默认值 0
  3. + 1:将上一步得到的值(无论是从字典中检索到的还是默认值 0)增加 1

                sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)  #降序
sorted() 函数是 Python 的内置函数,用于对可迭代对象(如列表、元组、字典的项等)进行排序,并返回一个新的列表,其中包含已排序的元素。与列表的 .sort() 方法不同,sorted() 函数不会修改原始的可迭代对象,而是返回一个新的列表。
labellist.append(sortedClassCount[0][0])
return np.array(labellist) # 使用曼哈顿公式作为距离度量 if (dis == 'M'): for i in range(num_test): # 按照列的方向相加,其实就是行相加 distances = np.sum(np.abs(self.Xtr - np.tile(X_test[i], (self.Xtr.shape[0], 1))), axis=1) nearest_k = np.argsort(distances) topK = nearest_k[:k] classCount = {} for i in topK: classCount[self.ytr[i]] = classCount.get(self.ytr[i], 0) + 1 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) labellist.append(sortedClassCount[0][0]) return np.array(labellist)

 

sorted() 函数的基本语法如下:

sorted(iterable, *, key=None, reverse=False)
 
 
  • iterable:要排序的可迭代对象。
  • key:一个函数,用于从每个元素中提取一个用于比较的关键字。默认为 None,表示直接比较元素本身。
  • reverse:一个布尔值,指定排序是升序(False)还是降序(True)。默认为 False

准备验证集与测试集数据,使用交叉验证,在选择超参数阶段不需要使用X-TEST与Y-TEST的使用

常见的超参数包括:

  1. 学习率(Learning Rate):控制模型在训练过程中权重更新的步长大小。学习率太小会导致训练过程缓慢,而学习率太大则可能导致训练过程不稳定,甚至无法收敛。

  2. 批量大小(Batch Size):在每次权重更新时使用的样本数量。批量大小的选择会影响模型的训练速度和泛化能力。较小的批量大小可能导致训练过程更加不稳定,但有助于模型更快地适应训练数据的变化。

  3. 迭代次数/训练周期(Epochs):整个训练数据集被遍历的次数。迭代次数太少可能导致模型欠拟合,而迭代次数太多则可能导致模型过拟合。

  4. 正则化参数(Regularization Parameters):用于控制模型复杂度的参数,如L1正则化、L2正则化中的系数。正则化参数有助于防止模型过拟合。

  5. 网络架构参数:在神经网络中,超参数还包括网络的层数、每层的神经元数量、激活函数类型等。这些参数的选择对模型的性能有着重要影响。

  6. 优化器参数:如动量(Momentum)、学习率衰减(Learning Rate Decay)等,这些参数控制优化算法的行为,进而影响模型的训练过程。

  7. dropout比率:在神经网络中,dropout是一种正则化技术,用于减少神经元之间的共适应性,从而提高模型的泛化能力。dropout比率是指在训练过程中随机丢弃神经元的比例。

X_train = train_loader.dataset.train_data
X_train = X_train.reshape(X_train.shape[0],-1)   #其中每个样本被转换为一行,-1 表示自动计算列数以保持数据总量不变
mean_image = getXmean(X_train)
X_train = centralized(X_train,mean_image)   #每个训练样本中减去均值图像,从而进行中心化处理
y_train = train_loader.dataset.train_labels
y_train = np.array(y_train)
X_test = test_loader.dataset.test_data
X_test = X_test.reshape(X_test.shape[0],-1)
X_test = centralized(X_test,mean_image)
y_test = test_loader.dataset.test_labels
y_test = np.array(y_test)
print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)
#数据分为五个部分,每个部分轮流做为验证集

num_folds = 5
k_choices = [1, 3, 5, 8, 10, 12, 15, 20]#k的值一般选择1~20以内
num_training=X_train.shape[0]
X_train_folds = []
y_train_folds = []折叠训练数据
indices = np.array_split(np.arange(num_training), indices_or_sections=num_folds) # 把下标分成5个部分

indices_or_sections 指定了要分割成多少个部分,或者提供具体的索引位置。

indices_or_sections 时,表示将输入数组均匀地分割成指定数量的子数组。

np.array_split() 是 NumPy 中的一个函数,用于将输入数组分割成多个子数组。第一个参数是要分割的数组


for i in indices:
X_train_folds.append(X_train[i])
y_train_folds.append(y_train[i])
k_to_accuracies = {}
for k in k_choices:
# 进行交叉验证
acc = []
for i in range(num_folds):
x = X_train_folds[0:i] + X_train_folds[i+1:] #训练集不包括验证集
x = np.concatenate(x, axis=0) # 使用concatenate将4个训练集拼在一起
y = y_train_folds[0:i] + y_train_folds[i+1:]
y = np.concatenate(y) # 对label使用同样的操作
test_x = X_train_folds[i] #单独拿出验证集
test_y = y_train_folds[i]

classifier = Knn() # 定义model
classifier.fit(x, y) # 将训练集读入
#dist = classifier.compute_distances_no_loops(test_x) # 计算距离矩阵  

  • compute_distances_no_loops 是 classifier 类中的一个方法。这个方法的目的是计算测试样本 test_x 到训练样本的距离。在此方法名中,“no_loops” 表示该实现不会使用显式的循环来计算距离。


y_pred = classifier.predict(k,'M',test_x) # 预测结果
accuracy = np.mean(y_pred == test_y) # 计算准确率  平均值?
acc.append(accuracy)
k_to_accuracies[k] = acc # 计算交叉验证的平均准确率
# 输出准确度
for k in sorted(k_to_accuracies):
for accuracy in k_to_accuracies[k]:
print('k = %d, accuracy = %f' % (k, accuracy))

 

 

# plot the raw observations
import matplotlib.pyplot as plt
for k in k_choices:
accuracies = k_to_accuracies[k]
plt.scatter([k] * len(accuracies), accuracies)

# plot the trend line with error bars that correspond to standard deviation
accuracies_mean = np.array([np.mean(v) for k,v in sorted(k_to_accuracies.items())])
accuracies_std = np.array([np.std(v) for k,v in sorted(k_to_accuracies.items())])
plt.errorbar(k_choices, accuracies_mean, yerr=accuracies_std)

  1. accuracies_mean:这是一个数组或列表,包含与 k_choices 中每个值对应的准确率的平均值。这通常是对多次实验结果取均值,以获得更稳定的准确率估计。

  2. yerr=accuracies_std:yerr 参数指定了每个数据点的误差条长度。在这里,accuracies_std 是与 accuracies_mean 具有相同长度的数组或列表,表示每个准确率的标准差。这有助于显示每个平均准确率的不确定性。


plt.title('Cross-validation on k')
plt.xlabel('k')
plt.ylabel('Cross-validation accuracy')
plt.show()

 

posted on 2024-07-28 19:52  风起-  阅读(48)  评论(0)    收藏  举报