《深度学习与图像识别:原理与实践》—3.3 KNN实战

举报
华章计算机 发表于 2019/07/24 20:58:36 2019/07/24
【摘要】 本节书摘来自华章计算机《深度学习与图像识别:原理与实践》一书中的第3章,第3.3.1节,作者是魏溪含 涂铭 张修鹏。

3.3 KNN实战

3.3.1 KNN实现MNIST数据分类

我们前面使用了两节的内容来讲述KNN算法的计算逻辑以及它的Python实现思路,本节将提供两个实战案例,带领大家逐步走进图像识别。

1. MNIST数据集

为了方便大家理解,本节选择的数据集是一个比较经典的数据集—MNIST。MNIST数据集来自美国国家标准与技术研究所( National Institute of Standards and Technolo,NIST)。训练集由250个人手写的数字构成,其中50%是高中学生,50%是人口普查的工作人员。测试数据集也是同样比例的手写数字数据。MNIST数据集是一个很经典且很常用的数据集(类似于图像处理中的“Hello World!”)。为了降低学习难度,我们先从这个最简单的图像数据集开始。

我们先来看一下如何读取MNIST数据集。由于MNIST是一个基本的数据集,因此我们可以直接使用PyTorch框架进行数据的下载与读取,示例代码如下:

import torch

from torch.utils.data import DataLoader

import torchvision.datasets as dsets

import torchvision.transforms as transforms

batch_size = 100

# MNIST dataset

train_dataset = dsets.MNIST(root = '/ml/pymnist',   #选择数据的根目录

                           train = True,           #选择训练集

                           transform = None,         #不考虑使用任何数据预处理

                           download = True)          #从网络上下载图片

test_dataset = dsets.MNIST(root = '/ml/pymnist',    #选择数据的根目录

                           train = False,           #选择测试集

                           transform = None,         #不考虑使用任何数据预处理

                           download = True)          #从网络上下载图片

#加载数据

train_loader = torch.utils.data.DataLoader(dataset = train_dataset,

                                           batch_size = batch_size,

                                           shuffle = True)  #将数据打乱

test_loader = torch.utils.data.DataLoader(dataset = test_dataset,

                                          batch_size = batch_size,

                                          shuffle = True)

train_dataset与test_dataset可以返回训练集数据、训练集标签、测试集数据以及测试集标签,训练集数据以及测试集数据都是n×m维的矩阵,这里的n是样本数(行数),m是特征数(列数)。训练数据集包含60 000个样本,测试数据集包含10 000个样本。在MNIST数据集中,每张图片均由28×28个像素点构成,每个像素点使用一个灰度值表示。在这里,我们将28×28的像素展开为一个一维的行向量,这些行向量就是图片数组里的行(每行784个值,或者说每行就代表了一张图片)。训练集标签以及测试标签包含了相应的目标变量,也就是手写数字的类标签(整数0~9)。

print("train_data:", train_dataset.train_data.size())

print("train_labels:", train_dataset.train_labels.size())

print("test_data:", test_dataset.test_data.size())

print("test_labels:", test_dataset.test_labels.size())

得到的结果如下:

train_data: torch.Size([60000, 28, 28])

train_labels: torch.Size([60000])    #训练集标签的长度

test_data: torch.Size([10000, 28, 28])

test_labels: torch.Size([10000])     #测试集标签的长度

我们一般不会直接使用train_dataset与test_dataset,在训练一个算法的时候(比如,神经网络),最好是对一个batch的数据进行操作,同时还需要对数据进行shuffle和并行加速等。对此,PyTorch提供了DataLoader以帮助我们实现这些功能。我们后面用到的数据都是基于DataLoader提供的。

首先,我们先来了解下MNIST中的图片看起来到底是什么,先对它们进行可视化处理。通过Matplotlib的imshow函数进行绘制,代码如下:

import matplotlib.pyplot as plt

digit = train_loader.dataset.train_data[0]    #取第一个图片的数据

plt.imshow(digit,cmap=plt.cm.binary)

plt.show()

print(train_loader.dataset.train_labels[0])  #输出对应的标签,结果为5

标签的输出结果是5,图3-5所显示的数字也是5。

image.png

2. KNN实现MNIST数字分类

在真正使用Python实现KNN算法之前,我们先来剖析一下思想,这里我们以MNIST的60 000张图片作为训练集,我们希望对测试数据集的10 000张图片全部打上标签。KNN算法将会比较测试图片与训练集中每一张图片,然后将它认为最相似的那个训练集图片的标签赋给这张测试图片。

那么,具体应该如何比较这两张图片呢?在本例中,比较图片就是比较28×28的像素块。最简单的方法就是逐个像素进行比较,最后将差异值全部加起来,如图3-6所示。

 image.png

图3-6 两张图片曼哈顿距离的计算方法

以图3-6中的一个颜色通道为例来进行说明。两张图片使用L1距离来进行比较。逐个像素求差值,然后将所有差值加起来得到一个数值。如果两张图片一模一样,那么L1距离为0,但是如果两张图片差别很大,那么,L1的值将会非常大。

3.验证KNN在MNIST上的效果

在实现算法之后,我们需要验证MNIST数据集在KNN算法下的分类准确度,在“if __name__ == '__main__'”下添加如下代码(不要忘记缩进):

X_train = train_loader.dataset.train_data.numpy() #需要转为numpy矩阵

X_train = X_train.reshape(X_train.shape[0],28*28)#需要reshape之后才能放入knn分类器

y_train = train_loader.dataset.train_labels.numpy()

X_test = test_loader.dataset.test_data[:1000].numpy()

X_test = X_test.reshape(X_test.shape[0],28*28)

y_test = test_loader.dataset.test_labels[:1000].numpy()

num_test = y_test.shape[0]

y_test_pred = kNN_classify(5, '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))

最后,我们运行代码,由运行结果可以看到准确率只有Got 368 / 1000 correct => accuracy: 0.368000!这说明1000张图片中只有大约37张图片预测类别的结果是准确的。

先别气馁,我们之前不是刚说过可以使用数据预处理的技术吗?下面我们试一下如果在进行数据加载的时候尝试使用归一化,那么分类准确度是否会提高呢?我们稍微修改下代码,主要是在将X_train和X_test放入KNN分类器之前先调用centralized,进行归一化处理,示例代码如下:

X_train = train_loader.dataset.train_data.numpy()

mean_image = getXmean(X_train)

X_train = centralized(X_train,mean_image)

y_train = train_loader.dataset.train_labels.numpy()

X_test = test_loader.dataset.test_data[:1000].numpy()

X_test = centralized(X_test,mean_image)

y_test = test_loader.dataset.test_labels[:1000].numpy()

num_test = y_test.shape[0]

y_test_pred = kNN_classify(5, '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))

下面再来看下输出结果的准确率:Got 951 / 1000 correct => accuracy: 0.951000,95%算是不错的结果。

现在我们来看一看归一化后的图像是什么样子的,代码如下:

import matplotlib.pyplot as plt

mean_image = getXmean(X_train)

cdata = centralized(test_loader.dataset.test_data.numpy(),mean_image)

cdata = cdata.reshape(cdata.shape[0],28,28)

plt.imshow(cdata[0],cmap=plt.cm.binary)

plt.show()

print(test_loader.dataset.test_labels[0]) #输出的label为7

效果如图3-7所示。

image.png

4. KNN代码整合

现在,我们再来回顾下KNN的算法实现,对于KNN算法来说,之前的实现代码虽然可用,但并不是按照面向对象的思路来编写的,在本例中,我们将之前的代码做一下改进。代码的实现思路是:我们可以创建一个fit方法来存储所有的图片以及与它们对应的标签。伪代码如下:

def fit(self,X_train,y_train):

    return model

再创建一个predict方法,以预测输入图片最有可能匹配的标签:

def predict(self,k, dis, X_test): #其中,k的选择范围为1~20,dis代表选择的是欧拉还是曼哈顿公式,X_test表示训练数据,函数返回的是预测的类别

return test_labels

下面我们来完善下KNN算法的封装(基于面向对象的思想来实现)。我们将这个类命名为Knn(注意:这个类名的n是小写的)。

第一步,完善fit方法,fit方法主要是通过训练数据集来训练模型,在Knn类中,我们的实现思路是将训练集的数据与其对应的标签存储于内存中。代码如下:

def fit(self,X_train,y_train): #我们统一下命名规范,X_train代表的是训练数据集,而y_train代表的是对应训练集数据的标签

    self.Xtr = X_train

    self.ytr = y_train

第二步,完善predict方法,predict方法可用于预测测试集的标签。具体的实现代码与之前的代码类似,只不过输入的参数只有k(代表的是k的选值),dis代表使用的是欧拉公式还是曼哈顿公式,X_test代表的是测试数据集;predict方法返回的是预测的标签集合。代码如下(只包含了欧氏距离的实现):

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

            sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)

            labellist.append(sortedClassCount[0][0])

        return np.array(labellist)

最后,我们引入from ml.knn.demo.KnnClassify import Knn,使用MNIST数据集查看效果。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。