动手学深度学习(三)线性神经网络—softmax回归

推荐课程:Softmax 回归_哔哩哔哩_bilibili

目录

一、softmax回归

1.1 网络架构

1.2 softmax运算

1.3 交叉熵损失函数

二、图像分类数据集

2.1 导包

2.2 创建数据集

2.3 可视化数据集函数

2.4 读取小批量

2.5 整合所有组件

三、softmax回归的从零开始实现

3.1 导包和读取数据集

3.2 定义初始化参数

3.3 实现softmax操作

3.4  定义模型

3.5 实现交叉熵损失函数

3.6 分类精度

3.7 优化器

3.8 训练

3.9 预测

四、softmax回归的简介实现

4.1 初始化模型参数

4.2 一步实现Softmax回归和损失函数的定义

4.3 定义优化算法

4.4 训练


分类任务是对离散变量预测,通过比较分类的概率来判断预测的结果。

softmax回归和线性回归一样也是将输入特征与权重做线性叠加,但是softmax回归的输出值个数等于标签中的类别数,这样就可以用于预测分类问题。

分类问题和线性回归的区别:分类任务通常有多个输出,作为不同类别的置信度。

一、softmax回归

1.1 网络架构

为了解决线性模型的分类问题,我们需要和输出一样多的仿射函数,每个输出对应它自己的仿射函数。

与线性回归一样,softmax回归也是一个单层神经网络。

在softmax回归中,输出层的输出值大小就代表其所属类别的置信度大小,置信度最大的那个类别我们将其作为预测。

1.2 softmax运算

首先,分类任务的目标是通过比较每个类别的置信度大小来判断预测的结果。但是,我们不能选择未规范化的最大输出值的 o_i 的类别作为我们的预测,原因有两点:

1. 输出值 o_i的总和不一定为1

2. 输出值 o_i有可能为负数。

这违反了概率论基本公理,很难判断所预测的类别是否真符合真实值。

softmax函数通过如下公式,解决了以上问题

softmax函数确保了输出值的非负,和为1,这一种规范手段。

1.3 交叉熵损失函数

交叉熵损失常用来衡量两个概率之间的差别

根据公式推断, 交叉熵损失函数的偏导数是我们softmax函数分配的概率与实际发生的情况之间的差距,换句话来说,其梯度是真实概率 y 和预测概率 \hat{y} 之间的差距。

二、图像分类数据集

MNIST数据集是图像分类中广泛使用的数据集之一,但作为基准数据集过于简单。我们将使用类似但更复杂的Fashion-MNIST数据集。

2.1 导包

import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

d2l.use_svg_display()  # 用SVG显示图片

2.2 创建数据集

通过框架中的内置函数将Fashion-MNIST数据集下载并读取到内存中。

# 通过ToTensor实例将图像数据从PIL类型转化成32位的浮点数格式
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(
    root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(
    root="../data", train=False, transform=trans, download=True)

查看Fashion-MNIST训练集和测试集大小,分别包含60000,10000张图片。

print(len(mnist_train), len(mnist_test))

查看图片分辨率,图片分辨率大小为[1, 28, 28]。

print(mnist_train[0][0].shape)

补充

torchvision.datasets 是Torchvision提供的标准数据集。

torchvision.transforms是包含一系列常用图像变换方法的包,可用于图像预处理、数据增强等工作。

torchvision.transforms.ToTensor()把一个取值范围是[0,255]PIL.Image或者shape(H,W,C)numpy.ndarray,转换成形状为[C,H,W],取值范围是[0,1.0]torch.FloadTensor(浮点型的tensor)。

2.3 可视化数据集函数

# 可视化数据集函数
def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]


def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    """绘制图像列表"""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize) # 创建绘制num_rows*num_cols个子图的位置区域
    axes = axes.flatten() # 降维成一维数组
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy()) # 负责对图像进行处理,并存入内存,并不显示
        else:
            # PIL图片
            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False) #不显示y轴
        ax.axes.get_yaxis().set_visible(False) #不显示x轴
        if titles:
            ax.set_title(titles[i])
    return axes

可视化展示训练集中前18个图片。

X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y))
d2l.plt.show() # 将plt.imshow()处理后的数据显示出来

plt.subplots(num_rows, num_cols, figsize):创建绘制num_rows*num_cols个子图的位置区域,其中子图大小为figsize。

enumerate():获取可迭代对象的每个元素的索引值及该元素值。

zip():用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

imshow():负责对图像进行处理,并存入内存,并不显示。

plt.show():将plt.imshow()处理后的数据显示出来。

2.4 读取小批量

使用4个进程,以批量大小为256,来读取数据集。

# 读取小批量
batch_size = 256

def get_dataloader_workers(): 
    """使用4个进程来读取数据"""
    return 4


train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=get_dataloader_workers())

2.5 整合所有组件

这个函数包含了以上所有工作。

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="../data", train=False, transform=trans, download=True)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=False,
                            num_workers=get_dataloader_workers()))

三、softmax回归的从零开始实现

3.1 导包和读取数据集

import torch
from IPython import display
from d2l import torch as d2l

batch_size = 256 # 批量大小

train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) # 读取数据集

d2l.load_data_fashion_mnist()与 “ 2.5 整合所有组件 ”完全一致。

3.2 定义初始化参数

将展平每个图像,将它们视为长度为784的向量。因为我们的数据集有10个类别,所以网络输出维度为10。因此,权重w将构成一个784x10的矩阵,偏置将构成一个1x10的行向量。与线性回归一样我们将使用正态分布初始化权重w,偏置初始化为0。

# 初始化模型参数
num_inputs = 784
num_outputs = 10
W = torch.normal(0,0.01,size=(num_inputs, num_outputs),requires_grad=True) # 从正态分布中随机选择w
b = torch.zeros(1,requires_grad=True)

3.3 实现softmax操作

# 定义softmax函数
def softmax(X):
    X_exp = torch.exp(X) # 对每个元素做指数运算
    partition = X_exp.sum(1, keepdim=True) # 对每行进行求和,保持原有张量维数
    return X_exp / partition  # 这里应用了广播机制

 观察softmax函数是否实现了:1. 所有输出值的总和为1;  2. 所有输出值都不为负数。

X_prob = softmax(X)
print(X)
print(X_prob, X_prob.sum(1))

3.4  定义模型

 X整形为[10,784]与W进行矩阵乘法,输出大小为[10,10]。

# 定义模型
def net(X):
	# 模型简单看来为:softmax(wx' + b)
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)  # X展平

reshape的参数为-1,表示根据其他维度自动计算该维度的大小 。

3.5 实现交叉熵损失函数

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

案例:创建一个数据y_hat,其中包含2个样本在3个类别的预测概率,再创建它们对应的标签y。如,y=[0,2],y_hat=[[0.1, 0.3, 0.6],[0.3, 0.2, 0.5]],根据y我们可以看出在y_hat中,第一个样本的预测概率为0.1,第二个样本的预测概率为0.5。因此,我们直接使用y作为y _hat中概率的索引

y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6],[0.3, 0.2, 0.5]])
print(y_hat[[0, 1], y])  # 查找真实标签对应的预测概率
print(cross_entropy(y_hat, y))  # 计算预测概率的交叉熵损失值

3.6 分类精度

将预测类别与真实y元素进行比较,计算评估指标accuracy。

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)    # y_hat第2个维度为预测标签,取最大元素
    cmp = y_hat.type(y.dtype) == y   	# 将y_hat转换为y的数据类型然后作比较,cmp函数存储bool类型
    
    return float(cmp.type(y.dtype).sum()) # 将正确预测的数量相加

准确率=符合条件的测定值个数/总测定值个数

print(accuracy(y_hat, y) / len(y))

2. 评估整个模型net的精确率。

def evaluate_accuracy(net, data_iter):  # @save
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module):     # 判断模型是否为深度学习模型
        net.eval()                           # 将模型设置为评估模式,阻断梯段传播,只计算精度
    metric = Accumulator(2)                  # 累加器,创建2个空间,存储预测正确数量和总预测数量

    with torch.no_grad():                    # 以下不生成计算图
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel()) #累加


    return metric[0] / metric[1]             # 预测正确数量和总预测数量相除

numel()函数:返回数组中元素的个数。

累加器:Accumulator实例中创建了2个变量,用于分别存储正确预测的数量和预测的总数量。

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

    # 初始化根据传进来n的大小来创建n个空间,全部初始化为0.0
    def __init__(self, n):
        self.data = [0.0] * n

    # 把原来类中对应位置的data和新传入的args做a + float(b)加法操作然后重新赋给该位置的data,从而达到累加器的累加效果
    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]

测试:

print(evaluate_accuracy(net, test_iter))

3.7 优化器

小批量随机梯度下降来优化损失函数。

# 小批量梯度下降
lr = 0.1

def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)

3.8 训练

训练一轮:

# 训练一轮
def train_epoch_ch3(net, train_iter, loss, updater):  # @save
    """训练模型一个迭代周期(定义见第3章)"""
    if isinstance(net, torch.nn.Module):  # 判断net模型是否为torch.nn模组,将模型设置为训练模式
        net.train()                       # 计算梯度

    metric = Accumulator(3)               # Accumulator(3)创建3个变量:训练损失总和、训练准确度总和、样本数
    for X, y in train_iter:
        # 计算梯度并更新参数
        y_hat = net(X)
        l = loss(y_hat, y)

        if isinstance(updater, torch.optim.Optimizer): # 判断优化器是否为torch.optim.Optimizer模组
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()  # 把梯度设置为0
            l.mean().backward()  # 计算梯度
            updater.step()       # 自更新,优化参数
        else:
            # 使用定制的优化器和损失函数
            l.sum().backward()   # backward只支持标量,先sum为一个标量,再反向求导
            updater(X.shape[0])  # 优化
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # 累加一个batch的损失、正确预测数和batch大小

    return metric[0] / metric[2], metric[1] / metric[2]      # 返回训练损失和训练精度

训练函数:

# 训练函数
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    """训练模型(定义见第3章)"""
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)    # 返回训练损失和训练精度
        test_acc = evaluate_accuracy(net, test_iter)    # 在测试数据集上评估精度
        train_loss, train_acc = train_metrics
        print("train_loss,train_acc,test_acc:", train_loss, train_acc, test_acc)

训练模型10个迭代周期:

num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

3.9 预测

对图像进行分类预测。

# 预测
def predict_ch3(net, test_iter, n=6):  #@save
    """预测标签(定义见第3章)"""
    for X, y in test_iter: # 拿出一个批量的样本
        break
    trues = d2l.get_fashion_mnist_labels(y) # 实际标签
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1)) # 预测,取最大概率的类别的标签
    titles = [true +'\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])
    plt.show()
predict_ch3(net, test_iter)

四、softmax回归的简介实现

通过深度学习框架的高级API能够使实现softmax回归变得更加容易。

import torch
from torch import nn
from d2l import torch as d2l

batch_size = 256 # 批量大小
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) # 读取数据集

4.1 初始化模型参数

# PyTorch不会隐式地调整输入的形状
# 因此,我们定义了展平层(flatten)在线性层前调整网络输入的形状
# Flatten()将任何维度的tensor转化为2D的tensor
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))


# 初始化权重参数
def init_weights(m):  # m就是当前的层layer
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01) # w从均值为0(默认),标准差为0.01的正态分布随机选取
        m.bias.data.fill_(0) # b设为0


net.apply(init_weights)  # 把init_weights函数apply到net上面

torch.nn.Flatten(start_dim=1, end_dim=- 1)用于设置网络中的展平层,常用于将输入张量展平为二维张量,也可以指定展平维度的范围[start_dim, end_dim]。

torch.​nn.Linear(in_features, out_features)用于设置网络中的全连接层,需要注意的是全连接层的输入与输出都是二维张量。

  • in_features指的是输入的二维张量的第二个维度的大小。
  • out_features指的是输出的二维张量的第二个维度的大小。

torch.nn.Sequential()是PyTorch中的一个类,它允许用户将多个计算层按照顺序组合成一个模型。

4.2 一步实现Softmax回归和损失函数的定义

在使用nn.CrossEntropyLoss()其内部会自动加上Softmax层。在交叉嫡损失函数中传递未归一化的预测,并同时计算softmax及其对数。

loss = nn.CrossEntropyLoss()

4.3 定义优化算法

使用学习率为0.1的小批量随机梯度下降作为优化算法。

trainer = torch.optim.SGD(net.parameters(), lr=0.03)

4.4 训练

调用之前定义的训练函数来训练模型,这里d2l模组里直接给出。

num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

如果观察不到训练结果图像,在d2l.train_ch3()函数的最后一行加上plt.show()即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/72966.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C/C++】用return返回一个函数

2023年8月13日&#xff0c;周日早上 我的第一篇使用了动态图的博客 #include<iostream> #include<windows.h>int loop(){int i0;while(1){Sleep(1000);std::cout<<i<<std::endl;}return 1; }int main(){std::cout<<"程序开始"<<…

解密Flink的状态管理:探索流处理框架的数据保留之道,释放流处理的无限潜能!

水善利万物而不争&#xff0c;处众人之所恶&#xff0c;故几于道&#x1f4a6; 文章目录 一、什么是状态二、应用场景三、Flink中状态的分类四、算子状态1. 列表状态&#xff08;List State&#xff09;2. 广播状态&#xff08;Broadcast State&#xff09; 五、键控状态1. Val…

平替 Docker - 玩转容器新利器 Podman Desktop (视频)

《OpenShift 4.x HOL教程汇总》 在 podman-desktop 1.2.1 podman 4.4 环境中验证。 文章目录 什么是 podman 和 podman-desktop安装 podman 和 podman-desktop 基本环境Image、Container 和 Pod 的基本操作拉取 Image运行 Container 将 Pod 部署到 Kubernetes安装 Kind 扩展插…

在 IntelliJ IDEA 中使用 Docker 开发指南

目录 一、IDEA安装Docker插件 二、IDEA连接Docker 1、Docker for Windows 连接 2、SSH 连接 3、Connection successful 连接成功 三、查看Docker面板 四、使用插件生成镜像 一、IDEA安装Docker插件 打开 IntelliJ IDEA&#xff0c;点击菜单栏中的 "File" -&g…

面试热题(最大子数组和)

给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 输出&#xff1a;6 解释&#xff1a;连续…

一台阿里云服务器怎么部署多个网站?以CentOS系统为例

本文阿里云百科介绍如何在CentOS 7系统的ECS实例上使用Nginx搭建多个Web站点。本教程适用于熟悉Linux操作系统&#xff0c;希望合理利用资源、统一管理站点以提高运维效率的用户。比如&#xff0c;您可以在一台云服务器上配置多个不同分类的博客平台或者搭建多个Web站点实现复杂…

二叉搜索树K和KV结构模拟

一 什么是二叉搜索树 这个的结构特性非常重要&#xff0c;是后面函数实现的结构基础&#xff0c;二叉搜索树的特性是每个根节点都比自己的左树任一节点大&#xff0c;比自己的右树任一节点小。 例如这个图&#xff0c; 41是根节点&#xff0c;要比左树大&#xff0c;比右树小&…

yo!这里是STL::list类简单模拟实现

目录 前言 重要接口实现 框架 默认成员函数 迭代器&#xff08;重点&#xff09; 1.引言 2.list迭代器类实现 3.list类中调用实现 增删查改 后记 前言 我们知道&#xff0c;stl中的vector对应数据结构中的顺序表&#xff0c;string类对应字符串&#xff0c;而今天要…

[C++ 网络协议] 套接字和地址族、数据序列

目录 1. 套接字 1.1 在Linux平台下构建套接字 1.1.1 用于接听的套接字(服务器端套接字) 1.1.2 用于发送请求的套接字(客户端套接字) 1.2 在Windows平台下构建套接字 1.2.1 Winsock的初始化 1.2.2 用于接听的套接字(服务器端套接字) 1.2.3 用于发送请求的套接字(客户端套…

Flink多流处理之coGroup(协同分组)

这篇文章主要介绍协同分组coGroup的使用,先讲解API代码模板,后面会结图解介绍coGroup是如何将流中数据进行分组的. 1 API介绍 数据源# 左流数据 ➜ ~ nc -lk 6666 101,Tom 102,小明 103,小黑 104,张强 105,Ken 106,GG小日子 107,小花 108,赵宣艺 109,明亮# 右流数据 ➜ ~ n…

【C与C++的相互调用方法】

C与C的相互调用方法 C与C为什么相互调用的方式不同C中调用CC中调用C致谢 C与C为什么相互调用的方式不同 C 和 C 之间的相互调用方式存在区别&#xff0c;主要是由于 C 和 C 语言本身的设计和特性不同。 函数调用和参数传递方式不同&#xff1a;C 和 C 在函数调用和参数传递方面…

docker — 容器网络

一、概述 Docker容器每次重启后容器ip是会发生变化的。 这也意味着如果容器间使用ip地址来进行通信的话&#xff0c;一旦有容器重启&#xff0c;重启的容器将不再能被访问到。 而Docker 网络就能够解决这个问题。 Docker 网络主要有以下两个作用&#xff1a; 容器间的互联…

docker部署springboot

基础知识 什么是docker 官网&#xff1a; Docker Docs: How to build, share, and run applications | Docker Documentation Docker 是一个基于go语言开发的开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到…

97. Interleaving String 72. Edit Distance 121. 122. 123

​​​​​​97. Interleaving String 72. Edit Distance 一个bottomup&#xff08;棋盘从右下角外围逼近[0,0]&#xff09;如果横轴是string1的index i&#xff0c;纵轴string2的index j&#xff0c;那么&#xff0c;很奇妙的是i和j一起&#xff08;从右下角的格子看&#xf…

11.Eclipse 注释模板的说明及设置

1.在eclipse中点击Window——>java——>Code Style——>CodeTemplates——>Comments 2.常用Variable 3. 我的注释模板 ①Files 文件 /** * Title: ${file_name}* Description: ${todo}* author Jeremy* date ${currentDate:date(yyyy-MM-dd hh:mm:ss)} */ ②Typ…

Kotlin入门:变量和函数——02

目录 一、Kotlin 基本数据类型 ​编辑 二、变量 val 关键字&#xff1a; var 关键字: 类型推断: 可空类型: 三、函数 基本函数语法&#xff1a; 单表达式函数&#xff1a; 默认参数值&#xff1a; 命名参数&#xff1a; 一、Kotlin 基本数据类型 Kotlin 的基本数…

树结构--介绍--二叉树遍历的递归实现

目录 树 树的学术名词 树的种类 二叉树的遍历 算法实现 遍历命名 二叉树的中序遍历 二叉树的后序遍历 二叉树的后序遍历迭代算法 二叉树的前序遍历 二叉树的前序遍历迭代算法 树 树是一种非线性的数据结构&#xff0c;它是由n(n≥0)个有限节点组成一个具有层次关系…

中电金信:ChatGPT一夜爆火,知识图谱何以应战?

随着ChatGPT的爆火出圈 人工智能再次迎来发展小高潮 那么作为此前搜索领域的主流技术 知识图谱前路又将如何呢&#xff1f; 事实上&#xff0c;ChatGPT也并非“万能”&#xff0c;作为黑箱模型&#xff0c;ChatGPT很难验证生成的知识是否准确。并且ChatGPT是通过概率模型执行推…

Django入门

Day1 django环境安装 创建虚拟环境 # step1 创建虚拟环境 python3 -m venv datawhale_django # step2 mac进入虚拟环境 source ./datawhale_django/bin/activate # step3 退出虚拟环境 deactivate安装包 pip3 install django ​pip3 install djangorestframework​​ pip3 …

Jenkins自动化打包脚本

一、背景 jenkins可以设置定时任务打包&#xff0c;也已手动点按钮打包&#xff0c;还可以通过执行http请求打包&#xff0c;今天我们就通过shell脚本&#xff0c;通过curl命令进行jenkins打包。 二、步骤 2.1 在jenkins上构建项目 设置触发器 2.2 通过shell脚本触发远程构…