IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> Softmax(分类模型基础)——最全重难点解释及代码 -> 正文阅读

[人工智能]Softmax(分类模型基础)——最全重难点解释及代码


写在前面:本节内容主要参考自《动手学深度学习》,本文对其中内容进行了补充,并将可能疑问的地方进行了标注和详细的解释。完整代码实现参考【】,简洁实现参考【】。如有疑问和问题欢迎给位交流和指出。

softmax 从零开始实现

1. 图像分类数据集

1.1 数据集加载与处理

我们使用Fashion-MNIST进行softmax模型的训练。我们可以通过框架中的内置函数将Fashion-MNIST数据集下载并读取到内存中。torchvision.datasets中提供了很多常见数据集的下载与处理。torchvision.transforms中提供了各种图片类型的转换。

trans = transforms.ToTensor()
    mnist_train = torchvision.datasets.FashionMNIST(root='../data',train=True,transform=trans,download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root=r'../data',train=False,transform=trans,download=True)

在这里插入图片描述

Fashion-MNIST由10个类别的图像组成,每个输入图像的宽度和高度均为28像素,其通道数为1.

print(len(mnist_train))
>>> 60000
print(len(mnist_test))
>>> 10000

# mnist_train(test)中存放的是**转化成tensor并归一化的图片**,和类别标签
print(mnist_train[0]) 
>>> (tensor([[[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
...
          0.0000, 0.0000, 0.0000, 0.0000]]]), 9)

print(mnist_train[0][0].shape)
>>> torch.Size([1, 28, 28])

注:1)由上述代码可见,数据集的标签并不是onehot-encoding类型!!而是类别数字。

1.2 读取小批量

我们通过内置函数,创建一个数据迭代器。

def get_dataloader_works():
    "使用4个进程来读取数据"
    return 4
batch_size = 256
train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,num_workers=get_dataloader_works())
    
1.3 整合所有组件

我们定义load_data_fashion_mnist 函数,用于获取和读取Fashion-MNIST数据集。它返回训练集和验证集的数据迭代器。此外,它还接受一个可选参数,用来将图像大小调整为另一种形状。

def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]  # 定义一个变化列表
    if resize:
        trans.insert(0,transforms.Resize(resize)) # 列表插入转换方法
    trans = transforms.Compose(trans)  # 组合所有转换方法
    mnist_train = torchvision.datasets.FashionMNIST(root='../data', train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root=r'../data', train=False, transform=trans, download=True)
    return(data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_works()),
           data.DataLoader(mnist_test,batch_size,shuffle=True,num_workers=get_dataloader_works()))

注:1)trans.insert为列表在指定索引处添加数据的方法。

? 2)transforms.Compose([])可以用来定义对数据集进行多种处理。其参数需要为数据集转换方法列表

在这里插入图片描述

2. 初始化模型参数

由上文,原始数据集中的每个样本都是28*28的图像。我们将展平每个图像,把他看作长度为784的向量,这里我们认为一个像素代表一个特征,而没有考虑空间结构(cnn)。因为数据集有10个类别所以输出维度为10。因此,权重将构成一个784*10的矩阵,偏置将构成一个1*10的行向量。

num_inputs = 784
num_outputs = 10
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs),requires_grad=True)
b = torch.zeros(num_outputs,requires_grad=True)

3. 定义softmax操作

3.1 sum()方法基础

给定一个矩阵我们可以使用sum()方法,在默认情况下对所有元素求和。我们也可以通过传入参数只求同一轴上的元素,即同一列 (轴0) 或同一行 (轴1)。假设X是一个形状为(2,3)的张量,我们对列进行求和,则结果将是一个具有形状(3,)的向量。当调用sum运算符时,**我们可以令参数keepdim为True,使得求和结果保留原始张量的轴数。**这将产生一个(1,3)的二维张量。

在这里插入图片描述

3.2 softmax操作

回顾softmax公式
s o f t m a x ( X ) i j = e x p ( X i j ) ∑ k e x p ( X i k ) softmax(\mathbf{X})_{ij}=\frac{exp(\mathbf{X_{ij}})}{\sum_kexp(\mathbf{X_{ik}})} softmax(X)ij?=k?exp(Xik?)exp(Xij?)?
代码实现

def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1,keepdim=True)
    return X_exp / partition  # 运用广播机制

注:虽然在数学上看起来是正确的,但我们在代码实现中有些草率。矩阵中非常大或非常小的元素有可能造成数值上溢或下溢,但我们没有采取措施来防止这点。

4 定义模型

下面将定义输入如何通过网络映射到输出。注意,在将数据传递到我们模型之前,我们使用reshape函数将每张原始图像展平为向量。

def net(X,W,b):
    """定义模型"""
    return softmax(torch.matmul(X.reshape((-1,W.shape[0])),W)+b)

5. 定义损失函数

我们使用交叉熵损失函数,这一损失函数在深度学习中最为常见,因为分类问题要远远多于回归问题。
l ( y , y ^ ) = ? ∑ j = 1 q y i ? l o g ? y j ^ l(y,\hat{y})=-\sum_{j=1}^qy_i\ log\ \hat{y_j} l(y,y^?)=?j=1q?yi??log?yj?^?
由于 y y y为onehot-encoding格式,所以 y i y_i yi?的值只能为1和0,所以交叉熵的结果可以简化为该组数据正确类别的预测概率的负对数似然。所以为了减少for循环的使用,我们只需要通过数据集的原始onehot-encoding标签,找到该组数据的真实类别。例如我们下面定义一个y_hat,其中包含两个样本在三个类别中的预测概率,找到一个定义他们正确类别的y,根据y,我们可以知道在第一个样本中第一类是正确的预测,第二个样本中第三类是正确的预测。然后使用y作为y_hat中概率的索引,只选择出正确类别所对应的预测概率,以便计算交叉熵损失。

y = torch.tensor([0, 2])
y_hat = torch.tensor([0.1, 0.3, 0.6],[0.3, 0.2, 0.5])
y_hat[[0,1],y]  
>>> output
tensor([0.1000,0.5000])

注:列表的索引方式[[行索引数组],[列索引数组]]

根据以上分析我们可以简单的定义我们的损失函数

def cross_entropy(y_hat, y):
    """定义交叉熵损失函数"""
    return -torch.log(y_hat[range(len(y_hat)), y])

6. 分类准确率

虽然直接优化准确率可能很困难(因为准确率的计算不可导),但准确率通常是我们最关心的性能衡量标准。

为了计算准确率,我们执行以下操作:(需要学习借鉴这种思想!!!)

首先,如果 y_hat 是矩阵,那么假定第二个维度存储每个类的预测分数。我们使用 argmax 获得每行中最大元素的索引来获得预测类别。然后我们[将预测类别与真实 y 元素进行比较]。由于等式运算符 == 对数据类型很敏感,因此我们将 y_hat 的数据类型转换为与 y 的数据类型一致。结果是一个包含 0(错)和 1(对)的张量。进行求和会得到正确预测的数量。

def accuracy(y_hat,y):
    """计算预测准确率"""
    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())

注:1)argmax:返回最大值所在得索引,axis = 1表示行中最大值,axis = 0表示列中最大值。

在这里插入图片描述

? 2)**tensor张量获取和改变类型的方法:cmp.type(y.dtype) **

? 3)==也算是运算符,在代码过程中的使用。

同样,对于任意数据迭代器data_iter可访问的数据集,我们可以评估在任意模型net的准确率

def evaluate_accuracy(net, data_iter):
    """计算在指定数据集上的模型精度"""
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)
    for X,y in data_iter:
        metric.add(accuracy(net(X),y), y.numel())
    return metric[0] / metric[1]

注:1)python函数isinstance:函数isinstance()可以判断一个变量的类型,既可以用在Python内置的数据类型如str、list、dict,也可以用在我们自定义的类,它们本质上都是数据类型。参考博文

? 2)**net.eval()**会将模型设置为评估模式,即不会对以下运算建立计算图。

? 3)y.numel()返回输入张量中元素的总的个数。

扩展

这里 Accumulator 是一个实用程序类,用于对多个变量进行累加。 在上面的 evaluate_accuracy 函数中,我们在 (Accumulator 实例中创建了 2 个变量,用于分别存储正确预测的数量和预测的总数量)。当我们遍历数据集时,两者都将随着时间的推移而累加。

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, item):
        return self.data[item]

注:1)列表操作[0.0]*2 会变为变为[0.0,0.0]

在这里插入图片描述

? 2)列表操作:zip()操作,具体见博客

? 3) 类内置方法__getitem__(self,item):Python的特殊方法__getitem_() 主要作用是可以让对象实现迭代功能。同时,定义好__getitem__方法后我们可以通过直接对对象索引,可以得到相应位置的值。参考

a = Accumulator(2)
a[1] # 直接索引
for x,y in a: # 进行迭代
	pass

7. 训练

我们定义一个函数来训练一个迭代周期.其中updater是更新模型参数的常用函数,他接受批量大小作为参数。它可以是封装的d2l.sgd函数,也可以是框架的内置优化函数。

def train_epoch_ch3(net, train_iter, loss, updater):
    """训练模型一个迭代周期"""
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 训练损失总和,训练准确度总和,样本数
    metric = Accumulator(3)
    for X,y in train_iter:
        # 计算梯度并跟新参数
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            updater.zero_grad()
            l.backward()
            updater.step()
            metric.add(float(l)*len(y), accuracy(y_hat, y),y.size().numel())
        else:
            # 使用定制的优化器和损失函数
            l.sum().bachward()
            updater(X.shape[0])
            metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
        # 返回训练损失和准确率
        return metric[0] / metric[2], metric[1] / metric[2]

注:1)y.size()tensor y 的形状,y.size().numel计算元素总的个数。

接下来我们定义一个训练函数:

(此处并没有定义可视化函数,用输出进行代替。)

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter,loss,updater)
        train_loss, train_acc = train_metrics
        print(f'the loss is {train_loss:.2f}  ,  the accuracy is {train_acc:.2f}')
    test_acc = evaluate_accuracy(net, test_iter)
    print(f'the test accuracy is {test_acc}')
7.2 定义优化器

我们依旧使用之前定义好的小批量随机梯度下降来优化模型的损失函数:

lr = 0.1
def updater(batch_size,lr):
	return d2l.sgd([W,b], lr, batch_size)
  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-10-04 12:51:04  更:2021-10-04 12:54:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 10:35:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码