简介
![](https://i-blog.csdnimg.cn/blog_migrate/798a0b148b2b3b7b7e0d7e07463685bc.png)
图像分类的任务就是给定一个图像,正确给出该图像所属的类别。对于超级强大的人类视觉系统来说,判别出一个图像的类别是件很容易的事,但是对于计算机来说,并不能像人眼那样一下获得图像的语义信息。
计算机能看到的只是一个个像素的数值,对于一个RGB图像来说,假设图像的尺寸是32*32,那么机器看到的就是一个形状为3*32*32的矩阵,或者更正式地称其为“张量”(“张量”简单来说就是高维的矩阵),那么机器的任务其实也就是寻找一个函数关系,这个函数关系能够将这些像素的数值映射到一个具体的类别(类别可以用某个数值表示)。
算法
较为简单的算法
Nearest Neighbor
"Nearest Neighbor"是处理图像分类问题一个较为简单、直接、粗暴的方法:首先在系统中“记住”所有已经标注好类别的图像(其实这就是训练集),当遇到一个要判断的还未标注的图像(也就是测试集中的某个图像)时,就去比较这个图像与“记住的”图像的“相似性”,找到那个最相似的已经标注好的图像,用那个图像的类别作为正在分类的图像的类别,这也就是"Nearest Neighbor"名称的含义。
关于如何判断图像“最相似”,可以有多种方法,比如直接求相同位置像素值的差的总和(也就是两个图像的L1距离),也可以采用两个图像的L2距离,也就是先对相同位置像素值作差并求平方,然后对所有平方值求和,最后总体开方,准确的表述如下:记图像分别为 I 1 I_1 I1和 I 2 I_2 I2, I 1 k I^k_1 I1k表示图像 I 1 I_1 I1在k位置处的像素值,那么图像 I 1 I_1 I1和 I 2 I_2 I2的L1和L2距离可以分别表示为:
d 1 ( I 1 , I 2 ) = ∑ k ∣ I 1 k − I 2 k ∣ d_1(I_1, I_2) = \sum_k|I^k_1 - I^k_2| d1(I1,I2)=k∑∣I1k−I2k∣ d 2 ( I 1 , I 2 ) = ∑ k ( I 1 k − I 2 k ) 2 d_2(I_1, I_2) = \sqrt{\sum_k(I^k_1 - I^k_2)^2} d2(I1,I2)=k∑(I1k−I2k)2
"Nearest Neighbor"容易实现,但是存在诸多缺点:
- 预测/测试的过程太慢,给定一个图像时,需要一个个去比较训练集中的图像,每预测一次的计算量都很大,特别是图片的尺寸较大时
- 预测/测试时,仍需要较大的空间去存储训练集
- 准确率不高,实验发现,该方法在CIFAR-10上只能取得38.59%左右的正确率(其实也不算太糟糕,因为总共有10个类别,如果完全随机猜的话,正确率只有10%),其实是因为该方法没有足够的理论支撑(只有一定的道理),显然的例子是:对于一个图像,如果将其中的物体向左平移一点获得一个新的图像,则这两个图像应该是相同的类别,但是利用"Nearest Neighbor"判断时,则有可能因为相同位置的像素值不再一样而判断错误(因为L1距离和L2距离都是计算相同位置像素值的接近程度,一平移后相同位置的像素值有可能会发生很大的改变)
Nearest Neighbor的代码实现
环境:Python 3.6 + PyTorch 1.0
import torch
import torchvision
class NearestNeighbor:
def __init__(self):
self.x_train = 0
self.y_train = 0
# 训练阶段:存储训练集
def train(self, x_train, y_train):
self.x_train = x_train
self.y_train = y_train
# 测试阶段:比较待分类的图像与所有训练图像的L1距离,取最小距离的图像的类别
def test(self, x_test):
L1_distance = torch.sum(torch.abs(x_test - self.x_train), 1)
min_index = torch.argmin(L1_distance)
return self.y_train[min_index]
if __name__ == '__main__':
# 定义要对数据集的原始数据进行的变换,此处的变换是将图像转化为Tensor
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
# 加载数据集
trainset = torchvision.datasets.CIFAR10(root='./', train=True, download=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=1, shuffle=False)
testset = torchvision.datasets.CIFAR10(root='./', train=False, download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=1, shuffle=False)
# 构造一个训练集和测试集的迭代器,用于遍历
train_iterator = iter(trainloader)
test_iterator = iter(testloader)
# 训练集,x_train是所有图像像素形成的Tensor,y_train是所有图像的label
# 将每一个形状为3*32*32的图像都展开成了长度为3*32*32的行向量
x_train = torch.zeros((len(trainset), 3 * 32 * 32))
y_train = torch.zeros((len(trainset), 1))
for i, (image, label) in enumerate(train_iterator):
x_train[i] = image.reshape(-1)
y_train[i] = label
nn = NearestNeighbor()
nn.train(x_train, y_train)
# 正确预测的数目、总数目
right_num, total_num = 0, 0
for i, (image, label) in enumerate(test_iterator):
predict_label = nn.test(image.reshape(1, -1))
if predict_label.item() == label.item():
right_num += 1
total_num += 1
print('Accuracy is %.2f' % (right_num / total_num * 100) + '%')
K-Nearest Neighbor
"K-Nearest Neighbor