机器学习(一) 使用KNN实现车辆图像分类



前言

本文中的车辆数据集图像于集美大学拍摄。本文中记录了使用KNN算法对车辆图像进行分类的过程。

参考文章:

https://blog.csdn.net/vendetta_gg/article/details/106599718
https://blog.csdn.net/CltCj/article/details/117550045


一、车辆数据集

该数据集总共有31张图片,包括carbicycleelectric bicycle共三个分类。car类别有10张,bicycle类别有11张,electric bicycle类别有10张。

1.车辆数据集示例

car:


bicycle:
在这里插入图片描述
electric bicycle:
在这里插入图片描述
bicycle文件夹:
在这里插入图片描述

二、KNN算法

1.KNN算法介绍

KNN(K-Nearest Neighbor)算法是数据挖掘分类技术中最简单的方法之一。该算法就是使用一个样本在特征空间当中最近的k个样本的最多的类别来表示该样本的类别。

2.KNN算法步骤

1、计算出测试样本与各个训练样本之间的距离;
2、递增排序;
3、找到距离最小的K个点;
4、找到这K个点中出现最多的类别作为测试样本的类别。

3.KNN算法优点

1、简单、易于理解与实现,无估计参数;
2、预测效果好;
3、适合对稀有事件进行分类;
4、适用于多分类问题。

4.KNN算法缺点

1、样本不平衡时,预测结果可能会受到样本数多的类别影响;
2、计算量较大,每一个预测样本都需要与全体训练样本计算距离。

5.K值选取

由于K值太小容易过拟合,K值过大又会欠拟合,需要选取合适的K值。

1、根据经验选取;2、从一个较小的K值开始增加,每次计算测试集的错误率,选取错误率最小的K。

6.距离度量

6.1 欧式距离

欧式距离指在m维空间中两个点之间的真实距离。

d ( x , y ) = ∑ i = 1 n ( X i − Y i ) 2 d(x,y) = \sqrt{\sum_{i=1}^n{(X_i-Y_i)}^2} d(x,y)=i=1n(XiYi)2

6.曼哈顿距离

欧式距离的局限是只能度量两点之间的直线距离。但现实世界中,往往直走不能到达目的地。

d ( x , y ) = ∑ i = 1 n ∣ X i − Y i ∣ d(x,y) = \sum_{i=1}^n{\left|X_i-Y_i\right|} d(x,y)=i=1nXiYi

三、代码实现

1.数据集

1.1数据集随机划分

data_split.py:

import os
import random
from shutil import copy2

def data_set_split(src_data_folder, target_data_folder, train_scale=0.8, val_scale=0.0, test_scale=0.2):
	#读取源数据文件夹,生成划分好的文件夹,分为trian、val、test三个文件夹
    if os.path.isdir(target_data_folder):
        pass
    else:
        os.mkdir(target_data_folder)
    print("开始数据集划分")
    class_names = os.listdir(src_data_folder)
    split_names = ['train', 'val', 'test']
    for split_name in split_names:
        split_path = os.path.join(target_data_folder, split_name)
        if os.path.isdir(split_path):
            pass
        else:
            os.mkdir(split_path)
        for class_name in class_names:
            class_split_path = os.path.join(split_path, class_name)
            if os.path.isdir(class_split_path):
                pass
            else:
                os.mkdir(class_split_path)

    for class_name in class_names:
        current_class_data_path = os.path.join(src_data_folder, class_name)
        current_all_data = os.listdir(current_class_data_path)
        current_data_length = len(current_all_data)
        current_data_index_list = list(range(current_data_length))
        random.shuffle(current_data_index_list)

        train_folder = os.path.join(os.path.join(target_data_folder, 'train'), class_name)
        val_folder = os.path.join(os.path.join(target_data_folder, 'val'), class_name)
        test_folder = os.path.join(os.path.join(target_data_folder, 'test'), class_name)
        train_stop_flag = current_data_length * train_scale
        val_stop_flag = current_data_length * (train_scale + val_scale)
        current_idx = 0
        train_num = 0
        val_num = 0
        test_num = 0
        for i in current_data_index_list:
            src_img_path = os.path.join(current_class_data_path, current_all_data[i])
            if current_idx <= train_stop_flag:
                copy2(src_img_path, train_folder)
                train_num = train_num + 1
            elif (current_idx > train_stop_flag) and (current_idx <= val_stop_flag):
                copy2(src_img_path, val_folder)
                val_num = val_num + 1
            else:
                copy2(src_img_path, test_folder)
                test_num = test_num + 1
            
            current_idx = current_idx + 1

        print("*********************************{}*************************************".format(class_name))
        print("{}类按照{}:{}:{}的比例划分完成,一共{}张图片".format(class_name, train_scale, val_scale, test_scale, current_data_length))
        print("训练集{}:{}张".format(train_folder, train_num))
        print("验证集{}:{}张".format(val_folder, val_num))
        print("测试集{}:{}张".format(test_folder, test_num))


if __name__ == '__main__':
    src_data_folder = "dataset"
    target_data_folder = "split_dataset"
    data_set_split(src_data_folder, target_data_folder)

参照 训练集:测试集=7:3 的划分结果:
在这里插入图片描述
最终得到训练集图片总数为24,测试集图片总数为7

1.2数据集读取

由于希望一次性读出所有数据,这里的dataloader使用的batch_size就为训练集或测试集的大小。

# 加载数据集
import torch
import torchvision
import torchvision.transforms as transforms
import numpy as np
import operator

train_data_path = 'split_dataset/train'
test_data_path = 'split_dataset/test'

data_transforms ={
    'train': transforms.Compose([
        transforms.Resize(512),
        transforms.ToTensor(),
        transforms.Normalize([.5, .5, .5],[.5, .5, .5])
    ]),
    'test': transforms.Compose([
        transforms.Resize(512),
        transforms.ToTensor(),
        transforms.Normalize([.5, .5, .5],[.5, .5, .5])
    ])
}

train_dataset = torchvision.datasets.ImageFolder(train_data_path, transform=data_transforms['train'])
test_dataset = torchvision.datasets.ImageFolder(test_data_path, transform=data_transforms['test'])

train_loader=torch.utils.data.DataLoader(dataset=train_dataset,
                                         batch_size=24,
                                         shuffle=True)
test_loader=torch.utils.data.DataLoader(dataset=test_dataset,
                                        batch_size=7,
                                        shuffle=False)
for data in train_loader:
    X_train, Y_train = data
    X_train = X_train.flatten(1)

for data in test_loader:
    X_test, Y_test = data
    X_test = X_test.flatten(1)

X_train, Y_train = X_train.numpy(), Y_train.numpy()
X_test, Y_test = X_test.numpy(), Y_test.numpy()

2.KNN分类器

# KNN分类器
def KNN(k, d_type, X_train, Y_train, X_test):
    '''
    :param k: k个最近点
    :param d_type: 曼哈顿距离 or 欧几里得距离
    :param X_train: 训练样本图片
    :param X_test: 训练样本标签
    :param Y_train: 测试样本图片
    '''
    
    #判断是否属于曼哈顿距离或欧几里得距离
    assert d_type == 'M' or d_type == 'E' 
    
    Y_test = []
    num_test = X_test.shape[0]
    
    for i in range(num_test):
        if d_type == 'M':
            distance = np.sum(np.abs(X_train - np.tile(X_test[i], (X_train.shape[0],1))), 1)
        if d_type == 'E':
            distance = np.sqrt(np.sum((X_train - np.tile(X_test[i], (X_train.shape[0], 1))**2), 1))
        sorted_index = np.argsort(distance)
        top_k = sorted_index[:k]
        cnt = {}
        for j in top_k:
            cnt[Y_train[j]] = cnt.get(Y_train[j], 0) + 1
        sorted_cnt = sorted(cnt.items(), key=operator.itemgetter(1), reverse=True)
        Y_test.append(sorted_cnt[0][0])
    return np.array(Y_test)

3.训练

3.1使用曼哈顿距离进行训练

acc_list = []
best_acc = 0
best_k = 0
for k in range(1,25):
    Y_test_pred = KNN(k, 'M', X_train, Y_train, X_test)
    num_correct = np.sum(Y_test_pred == Y_test)
    accuracy = float(num_correct) / X_test.shape[0]
    acc_list.append(accuracy)
    if accuracy > best_acc:
        best_acc = accuracy 
        best_k = k

print('best accuracy:', best_acc)

from matplotlib import pyplot as plt    
plt.plot(np.arange(1,25),acc_list)

结果:
在这里插入图片描述

在这里插入图片描述

3.2使用欧式距离进行训练

acc_list = []
best_acc = 0
best_k = 0
for k in range(1,25):
    Y_test_pred = KNN(k, 'E', X_train, Y_train, X_test)
    num_correct = np.sum(Y_test_pred == Y_test)
    accuracy = float(num_correct) / X_test.shape[0]
    acc_list.append(accuracy)
    if accuracy > best_acc:
        best_acc = accuracy 
        best_k = k

print('best accuracy:', best_acc)

from matplotlib import pyplot as plt    
plt.plot(np.arange(1,25),acc_list)

结果:
在这里插入图片描述

在这里插入图片描述


总结

使用欧式距离进行训练时,最佳的k值为24,accuracy为0.857,
使用曼哈顿距离进行训练时,最佳的k值为24,accuracy为0.715。

由于训练集总共只有24张,在选取k值时就从1一直遍历到24。但可以从accuracy的记录图中发现,无论是使用曼哈顿距离还是欧式距离作为距离的衡量方法,accuracy的变化都非常不稳定。这很有可能是因为该数据集每个类别都只有10张或11张图像,样本数量过少。

用于开发,评估和比较学习方法的数据收集 066c695f141f27ea881df8af5a827c99.jpg 7fe25c5c90c4f447fe3fa10adf4232cd.jpg 6739b35e5a470fdb37862fb2bc5251ef.jpg c1adb1337eba00e584e3704af99d291c.jpg cf042dd7addc3894b78063b90016ec03.jpg 1b9471eefb6f3951f127beb69ef5a584.jpg fb1e25b446c8174cc443c366a1d0e38b.jpg 5bbe28f7cf6ee6453caad79db64ba7c0.jpg 5d7514af404266c3f92efcee8662f640.jpg 9e37ae8bf11600a30afcd592d73a4be1.jpg 63f9f7dc8298317284bf7dbf2074b3ba.jpg 356d8576837338e82cf363d2dc7747c0.jpg 45242c276cd516adb93561503a2f6ed7.jpg a67cad73fd03d2d5cef3a81bf78ed0a3.jpg b2de8a56b1fbbee32afbe72d823f8a5a.jpg eafaa5fd38df370046f8b185488e7fa5.jpg f81a39ee4b311be9eb5aac2ba1cbaffd.jpg 2aeb7856890c8dc0e0b311077d386690.jpg 2b96ccd24db32c2c47581afde8d42722.jpg 7a9027a09a9149fd901f97630a052b29.jpg 395e11b1214a0308f6a6e7834cebc379.jpg 508bc40d51bc31a77507595e528155e9.jpg 7457b846fc17229e61aea790bab3c88a.jpg b9ea06b2cdf8e72dca0f415d1525d7d5.jpg b52fbc88bc83acea1b0d374ccb460178.jpg b62c7d59cd74dc1bc2eaa1b951cfd0a7.jpg f550965cf9cc6f07184923a32d728e94.jpg ff9974e9177a989100fae4b8c505cad5.jpg 7be124ec367694c948c57d1e09181775.jpg 050aab9a8a91dca8ed1e518ebf42ee0d.jpg 253df8c5a6ed19d8dcf26ea58c40094d.jpg 802716d5a05096b02621ad4eabf57812.jpg 62352815acbebb5bc9eebaf534d8f434.jpg a2fec0c55e7c73d27fac134b9074846e.jpg b8ebfa32f6ba5c7c50b63bc1c0108361.jpg b00473a9f517bbfc5923c95288b40ff2.jpg bec04a0e1662c9c23365eb3a9e12a1d7.jpg cd0e83983e36613ec5b154d2d6ddb0d1.jpg d6086634ce334fe82400a173f0028306.jpg e7299b9187f09e190127873fb0725e1c.jpg f4f7ec6332cab9c706e12fa8aaed4a6c.jpg ff1778ed14e5ac5be5217d410841750d.jpg 1fa59ad4144c5f668e92b8ab6a0043f8.jpg 3b6365d6ab144b660c0b477442782fa5.jpg 6f93c871d9b1469bec963639ea58fe82.jpg 84c8aadbabbeb753042593faffa4e376.jpg 90d9a803b25a78b4dc2dcdf4c85a4b70.jpg 386f1418e664eab461ea243e85c19d3c.jpg 627fcb8c133d1e30c067d31a92a79f02.jpg 766f0049a40eea217328d2975312d3e0.jpg 8189cbaa92299ea536be3ad23ddf1d00.jpg 0967287cbb2b14ed9dc1ec6a1a53b21d.jpg 093
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yunggemmy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值
>