动手学深度学习笔记day4

softmax回归

全连接层的参数开销

对于任何具有个d输入和q个输出的全连接层, 参数开销为o(dq)。

softmax运算

softmax函数将未规范化的预测变换为非负并且总和为1,同时要求模型保持可导。 我们首先对每个未规范化的预测求幂,这样可以确保输出非负。 为了确保最终输出的总和为1,我们再对每个求幂后的结果除以它们的总和。
动手学深度学习笔记day4_第1张图片
在预测过程中,我们可以用下式来选择最有可能的类别。
动手学深度学习笔记day4_第2张图片
损失函数交叉熵损失(cross-entropy loss)
动手学深度学习笔记day4_第3张图片

图像分类数据集

#1.导入库函数
%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l
d2l.use_svg_display()
#2.读取数据集
# 通过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)
#从数据库下载数据在上一级目录文件data中,train=bool,用于训练或测试, transform=trans把图片转化成矩阵的形式, download=bool是否从网上下载
len(mnist_train), len(mnist_test) #(60000, 10000)
#训练集和测试集分别包含60000和10000张图像。

mnist_train[0][0].shape #torch.Size([1, 28, 28])
#训练集第一个样本第一张图片的形状    mnist_train[0][1]训练集第一个样本第一张图片的标签

#以下函数用于在数字标签索引及其文本名称之间进行转换。
def get_fashion_mnist_labels(labels):  #@save
    """返回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]
#get_fashion_mnist_labels([1,3])	 ['trouser', 'dress']
batch_size = 256

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

train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=get_dataloader_workers())
#读取小批量。  num_workers使用多少个进程


整合所有组件

def load_data_fashion_mnist(batch_size, resize=None):  #@save
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
        #如果传入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回归的从零开始实现

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

import torch
from IPython import display
from d2l import torch as d2l
#1.初始化模型参数
batch_size = 256 #设置数据迭代器的批量大小为256。
train_iter, test_iter = load_data_fashion_mnist(batch_size)
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)
X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdim=True), X.sum(1, keepdim=True)
#(tensor([[5., 7., 9.]]), tensor([[ 6.], [15.]]))
#只对同一个轴上的元素求和,即同一列(轴0)或同一行(轴1)
#keepdim=False,对同一列(轴0)求和,维度减1维,由原矩阵[2,3]变成列的维度[3],对同一行(轴1)求和,维度减1维,由原矩阵[2,3]变成行的维度[2],
#keepdim=True,对同一列(轴0)求和,行维度变为1维,由原矩阵[2,3]变成列的维度[1,3],对同一行(轴1)求和,列维度变为1维,由原矩阵[2,3]变成行的维度[2,1]

回想一下,实现softmax由三个步骤组成:
对每个项求幂(使用exp);
对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数;
将每一行除以其规范化常数,确保结果的和为1。
动手学深度学习笔记day4_第4张图片

#2.定义softmax操作
def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制
y = torch.tensor([0, 2])   #假设现实中,这两张图片的标签分别是0,和1
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])  ##假设实验中,第一张图片预测的标签分别是0,1和2的概率是[0.1, 0.3, 0.6],第二张图片预测的标签分别是0,1和2的概率是[0.3, 0.2, 0.5]
y_hat[[0, 1], y]		#tensor([0.1000, 0.5000])
#y_hat中其中包含2个样本在3个类别的预测概率, 以及它们对应的标签y。使用y作为y_hat中概率的索引,我们知道在第一个样本y_hat[0]中,第一类标签y[0]是正确的预测; 而在第二个样本y_hat[1]中,第三类标签y[2]是正确的预测。
#3.定义模型
def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)

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

#分类精度
def accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)  #找到它的最大值,并且用它的Index来表示
    cmp = y_hat.type(y.dtype) == y   #就是先把y_hat换成和y一样的数据类型,然后比较y_hat和y是否在每一个位置上的值相等。而在Python里,False是0,True是1,所以可以用[0,1]表示,所以最后的求和
    return float(cmp.type(y.dtype).sum())

def evaluate_accuracy(net, data_iter):  #@save
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 正确预测数、预测总数  返回两成函数 metric[0]	 metric[1]
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]

class Accumulator:  #@save
    """在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): # reset函数即重新设置空间大小并初始化。
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx): #   __getitem__实现类似数组的取操作。
        return self.data[idx]

李沐的深度学习Accumulator怎么理解

训练

def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """训练模型一个迭代周期(定义见第3章)"""
    # 将模型设置为训练模式
    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):
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # 使用定制的优化器和损失函数
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回训练损失和训练精度
    return metric[0] / metric[2], metric[1] / metric[2]
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    """训练模型(定义见第3章)"""
   print(f'epoch {epoch + 1}, loss {l:f}')
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc
lr = 0.1

def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
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])

predict_ch3(net, test_iter)

softmax回归的简洁实现

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

#1.使用Fashion-MNIST数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

#2.初始化模型参数
# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights);

#3.Softmax的实现
loss = nn.CrossEntropyLoss(reduction='none')

#4.优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

#5.训练
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

动手学深度学习在线课程

你可能感兴趣的:(深度学习)