实战图像分类模型

举报
动手学AI 发表于 2023/12/02 09:40:57 2023/12/02
【摘要】 本文是学习softmax图像分类模型的总结,主要分享softmax图像分类模型的技术原理,以及用代码实现验证,供大家参考。

本文是学习softmax图像分类模型的总结,主要分享softmax图像分类模型的技术原理,以及用代码实现验证,供大家参考。

一、图像分类问题

在日常生活中,分类问题很常见,比如下图中的动物是猫,而不是狗。人是比较很容易知道,但是要计算机知道这是猫,就需要我们训练一个图像分类模型,输入这张图片,识别结果为猫。

图片1.png

二、问题分析

1、任务建模

我们的目标就是训练一个图像分类模型,输入一张图片,输出一个类别。

首先先介绍一下one-hot编码,one-hot编码时一个向量,向量长度和类别一样多, 类别对应的位置设置为1,其他所有位置设置为0。比如我们需要分类的总类别数为3(即猫、狗和鸭),那么标签y=[1,0,0]表示猫,y=[0,1,0]表示狗,y=[0,0,1]鸭。

我们用线性回归模型来实现图像分类问题,那么整个任务可以拆解为如下流程:

图片2.png

在整个流程中,主要与前期线性回归模型不同的地方有三处:

  • 输入是一张图片,需要把图片转为一维行向量,然后作为输入。
  • 线性回归模式是一个多输出模型,即一个样本输入,输出有多个(输出个数与类别总数相等);
  • 需要把多个输出转换为对应的标签类别

下面重点说明如何把多个输出转换为对应的标签类别,比如还是之前(猫、狗、鸭)分类问题,假设一个样本经过线性回归模型之后,得到三个输出分别为Out(1)=2,Out(2)=4,Out(3)=6,则输出向量为(2,4,6)。因为我们使用的是one-hot编码,每个类别真实标签向量的分量都是0-1之间的数值,为使输出标签向量的值变换到0-1之间,在分类问题中常用softmax函数来进行处理:

图片3.png

上述输出向量为(2,4,6)经过softmax变换之后,得到的输出向量为(0.0159, 0.1173, 0.8668),该向量表示图片是猫的概率为0.0159,是狗的概率为0.1173,是鸭的概率为0.8668,我们取向量中的最大值作为分类结果,即输出向量0.0159, 0.1173, 0.8668)的分类结果为鸭。

2、损失函数

在线性回归模型中,我们用均方误差作为损失函数,但是在分类问题中,一般使用交叉熵来作为损失函数,交叉熵函数用来衡量两个概率的区别,其定义如下:

图片1.png

由上述分类任务建模分析可知,预测值和真实值表示某个类别的概率,所以每个样本预测值与真实值之间的损失函数为:

图片2.png

因为真实值y中,只有一个分量为1,其他都为0,上述损失函数可以化简为

图片3.png

比如还是上述例子:假设真实值y=(0,0,1),预测值0.0159, 0.1173, 0.8668)

图片4.png

因此,在训练多输出线性回归模型时,我们希望寻找一组参数(Wb),使得L(Wb在所有训练样本上的损失均值越小越好。

3、模型评估

在分类问题中,我们希望整个模型的分类准确度越高越好,分类准确度为正确预测数量与总预测数量之比。

三、代码验证

整个代码验证过程包括如下主要流程:


图片1.png

1、获取数据

我们选取Fashion‐MNIST图像分类数据集来进行验证,Fashion‐MNIST由10个类别[分别为t‐shirt(T恤)、trouser(裤子)、pullover(套衫)、dress(连衣裙)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴))]的图像组成,每个类别由训练数据集(train dataset)中的6000张图像和测试数据集(test dataset)中的1000张图像组成。因此,训练集和测试集分别包含60000和10000张图像。图像为灰度图像,通道数为1,,图像的高度和宽度均为28像素

"""
获取Fashion‐MNIST数据集
load_data_fashion_mnist函数,用于获取和读取Fashion‐MNIST数据集。这个函数返回训练集
和验证集,还接受一个可选参数resize,用来调整图像大小。
"""
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

我们可以查看train_itertest_iter中的数据。

图片1.png

2、定义模型

由前面的分析可知,整个分类模型分为两个层,首先要把图像转为一维向量,然后在输入到线性回归模型中。Fashion‐MNIST数据集中的每个样本都是28×28的图像展平转换784的向量。类别为10,所以模型输出维度为10

net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))  

然后初始化模型参数

# 初始化模型参数
def init_weights(m):
    if type(m) == nn.Linear:  # 只有nn.Linear网络层才有权重参数
        # 从均值为0,标准差为0.01的正态分布中取值填充张量w.weight
        nn.init.normal_(m.weight, std=0.01)


cls_net.apply(init_weights);  # 初始化nn.linear线性回归模型中的权重参数。

3、定义损失函数

pytorch中有已定义好的交叉熵损失函数可以直接使用。

"""
定义损失函数
"""
# 交叉熵损失函数,当reduction='none'时,表示每个样本的损失。
loss = nn.CrossEntropyLoss(reduction='none')

4、定义优化算法

我们采用随机梯度下降法,来迭代更新权重参数,可直接使用pytorch中已定义好的函数。

"""
定义优化算法
"""
optimization = torch.optim.SGD(cls_net.parameters(), lr=0.1)

5、定义分类准确度

分类准确度为正确预测数量与总预测数量之比。

def accuracy(y_hat, y):  # 用于计算预测正确的数量
    # y_hat是预测值,if判断条件是先判断y_hat是二维矩阵(len(y_hat.shape)获取矩阵行数,
    # y_hat.shape[1]矩阵列数,每一列表示每个类别的概率),
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum())


class Accumulator:
    """在n个变量上累加"""

    def __init__(self, n):
        self.data = [0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]


def evaluate_accuracy(net, data_iter):  # 用于计算在指定数据集上模型的精度
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    # 在Accumulator实例中创建了2个变量,分别用于存储正确预测的数量和预测的总数量。
    metric = Accumulator(2)
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel()) # y.numel()得到样本总数
    return metric[0] / metric[1]

6、训练

"""
训练
"""
if __name__ == '__main__':
    num_epochs = 5
    i = 1
    for epoch in range(num_epochs):
        metric = Accumulator(3) # 训练损失总和、训练准确度总和、样本数
        for X, y in train_iter:
            # 计算梯度并更新参数
            y_hat = cls_net(X)
            l = loss(y_hat, y)
            if isinstance(optimization, torch.optim.Optimizer):
                # 使用PyTorch内置的优化器和损失函数
                optimization.zero_grad()
                l.mean().backward()
                optimization.step()
            else:
                # 使用定制的优化器和损失函数
                l.sum().backward()  # l得到每个样本预测值与真实值的损失,要对小批量的损失进行求和,以便在l梯度下降时求平均
                print(X.shape[0])
                # X的形状是batchsize*num_input,X.shape[0]得到的是batchsize,传入下批量梯度下降参数更新函数
                optimization(X.shape[0])
            metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())  # 得到训练损失总和、训练准确度总和、样本数
        # 返回训练损失和训练精度
        train_loss = metric[0] / metric[2]
        train_acc =  metric[1] / metric[2]
        print(f'第{i}次训练平均损失为{train_loss},平均分类准确度{train_acc}')
        i = i+1

运行可以查看结果

图片1.png

7、预测

将训练得到的模型在测试集进行预测推理

图片2.png

结果如下所示:

图片3.png

至此,softmax分类模型完毕。

参考资料

1、《动手学深度学习》第二版,地址: zh.d2l.ai/index.html

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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