动手学深度学习(三)线性神经网络—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 = torch.normal(0, 1, (2, 5))
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): 
    """训练模型一个迭代周期(定义见第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()即可。

  • 8
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
深度学习之卷积神经网络(CNN)详解与代码实现(一) 卷积神经网络(CNN)是深度学习领域中应用广泛的一种神经网络,它通过卷积、池化等操作,能够有效地处理图像、语音、文本等数据类型。本文将从卷积神经网络的基本原理、结构、训练方法等方面进行详细介绍,并通过代码实现,加深读者对卷积神经网络的理解。 一、卷积神经网络的基本原理 卷积神经网络(CNN)的基本原理是通过卷积操作对输入数据进行特征提取,然后通过全连接层对特征进行分类或回归。卷积操作是CNN的核心,它可以有效地减少网络中的参数数量,提高模型的泛化能力。 卷积操作是指将一个卷积核(kernel)与输入数据进行卷积运算,得到一个特征图(feature map)。卷积核是一个小的矩阵,可以通过习得到,它的作用是对输入数据进行特征提取。卷积操作可以提取出输入数据中的局部特征,不同的卷积核可以提取出不同的特征,从而实现对输入数据的特征提取。 二、卷积神经网络的结构 卷积神经网络的结构包括卷积层、池化层、全连接层等。其中卷积层和池化层是CNN的核心,全连接层用于分类或回归。 1. 卷积层 卷积层是CNN中最重要的层之一,它的作用是对输入数据进行特征提取。卷积层的参数包括卷积核的大小、深度、步长等,其中卷积核的大小和深度是最重要的参数。 卷积层的输入是一个四维张量,分别表示样本数量、图像高度、图像宽度、图像通道数。卷积核是一个维张量,分别表示卷积核的高度、宽度、深度。 卷积操作可以通过矩阵乘法实现,也可以通过FFT等方法实现,但是前者的方法在卷积核较小时速度较慢,后者在卷积核较大时速度较慢。 2. 池化层 池化层是CNN中另一个重要的层,它的作用是对卷积层的输出进行降维和特征提取。池化操作可以通过最大池化、平均池化等方法实现。最大池化是指在一个池化区域内选取最大的值作为池化结果,平均池化是指在一个池化区域内取平均值作为池化结果。 池化操作可以有效地减少数据的大小,提高模型的泛化能力。但是需要注意的是,过度的池化会导致信息的丢失,从而影响模型的性能。 3. 全连接层 全连接层是CNN中的最后一层,它的作用是将卷积层和池化层的输出进行分类或回归。全连接层是一个标准的神经网络,其中每个神经元都与前一层的所有神经元相连。 全连接层的输出可以通过softmax函数进行分类,也可以通过线性函数进行回归、卷积神经网络的训练方法 卷积神经网络的训练方法与其他神经网络类似,主要包括前向传播和反向传播两个过程。前向传播是指将输入数据通过卷积层、池化层、全连接层等一系列操作,得到最终的输出结果。反向传播是指将输出结果与真实标签进行比较,然后通过梯度下降等方法,不断调整网络参数,使得输出结果更加接近真实标签。 在训练过程中,需要注意的是,卷积神经网络通常需要较长的训练时间和大量的训练数据,以便得到更好的性能。此外,还需要注意选择适当的优化算法、习率、正则化等参数,以避免过拟合和欠拟合等问题。 四、代码实现 下面是一个简单的卷积神经网络的代码实现,用于对手写数字进行分类。 ```python import tensorflow as tf # 加载数据 mnist = tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() # 数据预处理 x_train, x_test = x_train / 255.0, x_test / 255.0 # 定义模型 model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)), tf.keras.layers.MaxPooling2

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向岸看

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值