机器学习——KNN、SVM、MLP、CNN实现cifar100

本文介绍了使用KNN、SVM、MLP和CNN对CIFAR100数据集进行分类的实验,探讨了实验结果和遇到的问题。KNN达到29%的准确率,SVM和MLP分别为10%和25%,可能由于参数设置不当。CNN实验因长时间无结果,可能存在代码错误或需要更多计算资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文是为期一周的实验课,要求用KNN、SVM、MLP和CNN分别实现cifar100数据集的分类。该实验的目的在于对机器学习有一个直观的感受,且对前段时间所学python基础语法课、机器学习100天(前8天)做个复习。
在网上找到很多资料都是cifar10的实验。本文主要介绍实验代码和实验结果及一些分析,不讲述基础原理。代码的参考链接写在代码头部 ,本文的代码是在原来代码的基础上,将cifar10数据集加载改为cifar100。其中一些语言注释并没有改正。
实验结果:KNN准确率正常29%。SVM准确率非常低10%不正常,原因在于核函数的设置。MLP准确率也非常低25%不正常,原因在于直接使用了优化器,没有学习率,模型欠拟合。CNN运行5小时没结果。
注意:本文使用的标签是直接将100个子类标签拿出来用,更精确的做法是将10个大类标签和对应的子类标签组合起来,做成新的100个标签。否则子类之间可能有重叠。

1.KNN-cifar100

1.1KNN实验结果

实验结果:准确率:29%
结果截图:KNN-cifar100

1.2KNN实验代码

KNN 代码:

# https://github.com/luxiaohao/KNN/blob/master/datasets/data_utils.py
# python+cifar10


import os
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from sklearn import svm as s
import time
def unpickle(file):
    """
    功能:将CIFAR10中的数据转化为字典形式
    (1)加载data_batch_i(i=1,2,3,4,5)和test_batch文件返回的字典格式为:
    dict_keys([b'filenames', b'data', b'labels', b'batch_label'])
    其中每一个batch中:
    dict[b'data']为(10000,3072)的numpy array
    dict[b'labels']为长度为10000的list
    (2)加载batchs.meta文件返回的字典格式为:
    dict_keys([b'num_cases_per_batch', b'num_vis', b'label_names'])
    其中dict[b'label_names']为一个list,记录了0-9对应的类别为:
    [b'airplane', b'automobile', b'bird', b'cat', b'deer', b'dog', b'frog', b'horse', b'ship', b'truck']
    """
    import pickle
    with open(file, 'rb') as fo:
        dict = pickle.load(fo, encoding='bytes')
    return dict
# file =''r'D:\cifar-100-python\train'
# dict=unpickle(file)
# print(dict.keys())
# 运行文件可知dict_keys([b'filenames', b'batch_label', b'fine_labels', b'coarse_labels', b'data'])
def load_CIFAR10():
    """
    功能:从当前路径下读取CIFAR10数据
    输出:
    -x_train:(numpy array)训练样本数据(N,D)
    -y_train:(numpy array)训练样本数标签(N,)
    -x_test:(numpy array)测试样本数据(N,D)
    -y_test:(numpy array)测试样本数标签(N,)
    """
    x_t = []
    y_t = []
    for i in range(1, 6):
        # path_train = os.path.join('cifar-10-batches-py', 'data_batch_%d' % (i))
        path_train =''r'D:\cifar-100-python\train'
        data_dict = unpickle(path_train)
        x = data_dict[b'data'].astype('float32')
        y = np.array(data_dict[b'fine_labels'])
        x_t.append(x)
        y_t.append(y)
    # 将数据按列堆叠进行合并,默认按列进行堆叠
    x_train = np.concatenate(x_t)
    y_train = np.concatenate(y_t)
    # path_test = os.path.join('cifar-10-batches-py', 'test_batch')
    path_test = ''r'D:\cifar-100-python\test'
    data_dict = unpickle(path_test)
    x_test = data_dict[b'data'].astype('float32')
    y_test = np.array(data_dict[b'fine_labels'])
    return x_train, y_train, x_test, y_test
def data_processing():
    """
    功能:进行数据预处理
    输出:
    x_tr:(numpy array)训练集数据
    y_tr:(numpy array)训练集标签
    x_val:(numpy array)验证集数据
    y_val:(numpy array)验证集标签
    x_te:(numpy array)测试集数据
    y_te:(numpy array)测试集标签
    x_check:(numpy array)用于梯度检查的子训练集数据
    y_check:(numpy array)用于梯度检查的子训练集标签
    """
    # 加载数据
    x_train, y_train, x_test, y_test = load_CIFAR10()
    num_train = 10000
    num_test = 1000
    num_val = 1000
    num_check = 100
    # 创建训练样本
    x_tr = x_train[0:num_train]
    y_tr = y_train[0:num_train]
    # 创建验证样本
    x_val = x_train[num_train:(num_train + num_val)]
    y_val = y_train[num_train:(num_train + num_val)]
    # 创建测试样本
    x_te = x_test[0:num_test]
    y_te = y_test[0:num_test]
    # 从训练样本中取出一个子集作为梯度检查的数据
    mask = np.random.choice(num_train, num_check, replace=False)
    x_check = x_tr[mask]
    y_check = y_tr[mask]
    # 计算训练样本中图片的均值
    mean_img = np.mean(x_tr, axis=0)
    # 所有数据都减去均值做预处理
    x_tr += -mean_img
    x_val += -mean_img
    x_te += -mean_img
    x_check += -mean_img
    # 加上偏置项变成(N,3073)
    # np.hstack((a,b))等价于np.concatenate((a,b),axis=1),在横向合并
    # np.vstack((a,b))等价于np.concatenate((a,b),axis=0),在纵向合并
    x_tr = np.hstack((x_tr, np.ones((x_tr.shape[0], 1))))
    x_val = np.hstack((x_val, np.ones((x_val.shape[0], 1))))
    x_te = np.hstack((x_te, np.ones((x_te.shape[0], 1))))
    x_check = np.hstack((x_check, np.ones((x_check.shape[0], 1))))
    return x_tr, y_tr, x_val, y_val, x_te, y_te, x_check, y_check


from pandas import read_csv
import pandas as pd
import matplotlib.pyplot as plt
class KNearestNeighbor(object):
    """定义KNN分类器,用L1,L2距离计算"""
    def __init__(self):
        pass
    def train(self, X, y):
        """
        训练数据,k近邻算法训练过程只是保存训练数据。
        :param X: 输入是一个包含训练样本nun_train,和每个样本信息的维度D的二维数组
        :param y: 对应标签的一维向量,y[i] 是 X[i]的标签
        :return: 无
        """
        self.X_train = X
        self.y_train = y
    def predict(self, X, k=1, num_loops=0):
        """
        :param X:  训练数据输入值,一个二维数组
        :param k:  确定K值进行预测投票
        :param num_loops: 选择哪一种循环方式,计算训练数据和测试数据之间的距离
        :return: 返回一个测试数据预测的向量(num_test,),y[i] 是训练数据 X[i]的预测标签。
        """
        if num_loops == 0:
            dists = self.compute_distances_no_loops(X)
        elif num_loops == 1:
            dists = self.compute_distances_one_loop(X)
        elif num_loops == 2:
            dists = self.compute_distances_two_loops(X)
        else:
            raise ValueError('Invalid value %d for num_loops' % num_loops)
        return self.predict_labels(dists, k=k)
    def compute_distances_no_loops(self, X):
        """
        计算距离没有运用循环。
        只使用基本的数组操作来实现这个函数,特别是不使用SCIPY的函数。
        提示:尝试使用矩阵乘法和两个广播求和来制定L2距离。
        :param X: 输入是一个(num_test, D)的训练数据。
        :return: 返回值是一个(num_test, num_train)的二维数组,dists[i, j]对应相应位置测试数据和训练数据的距离。
        """
        num_test = X.shape[0]
        num_train = self.X_train.shape[0]
        dists = np.zeros((num_test, num_train))
        """
        M = np.dot(X, self.X_train.T)
        nrow = M.shape[0]
        ncol = M.shape[1]
        te = np.diag(np.dot(X, X.T))
        tr = np.diag(np.dot(self.X_train, self.X_train.T))
        te = np.reshape(np.repeat(te, ncol), M.shape)
        tr = np.reshape(np.repeat(tr, nrow), M.T.shape)
        sq = -2 * M + te + tr.T
        dists = np.sqrt(sq)
        #这里利用numpy的broadcasting性质,例如A = [1, 2], B = [[3], [4]], A + B = [[3 + 1, 3 + 2], [4 + 1, 4 + 2]]。
        #以及(a - b) ^ 2 = a ^ 2 + b ^ 2 - 2ab。
        """
        test_sum = np.sum(np.square(X), axis=1, keepdims=True)
        train_sum = np.sum(np.square(self.X_train), axis=1)
        test_mul_train = np.matmul(X, self.X_train.T)
        dists = test_sum + train_sum - 2 * test_mul_train
        return dists
    def compute_distances_one_loop(self, X):
        """
        应用1层循环的计算方式,计算测试数据和每个训练数据之间的距离。
        按训练数据的行索引计算,少了一个循环。
        :param X: 输入是一个(num_test, D)的训练数据。
        :return: 返回值是一个(num_test, num_train)的二维数组,dists[i, j]对应相应位置测试数据和训练数据的距离。
        """
        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):
            # axis = 1按每个行索引计算
            distances = np.sqrt(np.sum(np.square(self.X_train - X[i]), axis=1))
            # L1距离
            # distances = np.sum(np.abs(self.X_train - X[i, :]), axis=1)
            dists[i, :] = distances
        return dists
    def compute_distances_two_loops(self, X):
        '''
        应用2层循环(嵌套循环)的计算方式,计算测试数据和每个训练数据之间的距离。
        :param X: 输入是一个(num_test, D)的训练数据。
        :return: 返回值是一个(num_test, num_train)的二维数组,dists[i, j]对应相应位置测试数据和训练数据的距离。
        '''
        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):
                # 应用L2距离求解每个对应测试集数据和训练集数据的距离,并放在dists数组中,两层循环时间复杂度高
                distances = np.sqrt(np.sum(np.square(self.X_train[j] - X[i])))
                dists[i, j] = distances
        return dists
    def predict_labels(self, dists, k=1):
        '''
        输入一个测试数据和训练数据的距离矩阵,预测训练数据的标签。
        :param dists: 距离矩阵(num_test, num_train) 对应dists[i, j]
                       给出第i个测试数据和第j个训练数据的距离。
        :param k: KNN算法超参数K
        :return:返回(num_test,)向量,y[i]是X[i]对应的预测标签。
        '''
        num_test = dists.shape[0]
        y_pred = np.zeros(num_test)
        """
        这一步,要做的是:用距离矩阵找到与测试集最近的K个训练数据的距离,
        并且找到他们对应的标签,存放在closest_y中。
        函数介绍:
        numpy.argsort(a, axis=-1, kind=’quicksort’, order=None) 
        功能: 将矩阵a按照axis排序,并返回排序后的下标 
        参数: a:输入矩阵, axis:需要排序的维度 
        返回值: 输出排序后的下标,从小到大排
        """
        for i in range(num_test):
            # 创建一个长度为K的列表,用来存放与测试数据最近的K个训练数据的距离。
            closest_y = []
            distances = dists[i, :]
            indexes =
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值