《动手学深度学习》——3.4softmax回归

(以下使用的是jupyter)

1.基本概念

        回归可以用于预测多少,也可以用于预测“哪一类”。

        将我们需要预测的类别数据 使用独热编码(one-hot encoding)来存储,独热编码是一个向量,它的分量和类别一样多。 类别对应的分量设置为1,其他所有分量设置为0。 比如我要预测三个类别猫、鸡、狗,标签y将是一个三维向量, 其中(1,0,0)对应于“猫”、(0,1,0)对应于“鸡”、(0,0,1)对应于“狗”。

         为了解决线性模型的分类问题,我们需要和输出一样多的仿射函数(affine function)。 每个输出对应于它自己的仿射函数。假设分类的图像为2*2的灰度图,每个像素值对应4个特征x1,x2,x3,x4,则我们有4个特征和3个可能的输出类别, 我们将需要12个标量来表示权重, 3个标量来表示偏置。

softmax回归是一种单层神经网络,通过向量的形式表示o=Wx+b。

全连接层的参数开销

        对于任何具有 d 个输入和 q 个输出的全连接层, 参数开销为O(dq),该数值非常大。 幸运的是,将 d 个输入转换为 q 个输出的成本可以减少到O(dq/n), 其中超参数 n 可以由我们灵活指定,以在实际应用中平衡参数节约和模型有效性。

损失函数

        详见http://t.csdnimg.cn/bfAIx

 2、数据集

        使用Fashion-MNIST数据集,可以从torchvision中下载。

# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,
# 并除以255使得所有像素的数值均在0~1之间
# 使用的Fashion-MNIST数据集包含10(衣物)类别
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)

3、建立模型

        为了简便起见,后续将代码的解释直接放在代码当中。

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


# 0.数据预处理
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
num_inputs = 784  # 28*28=784
num_outputs = 10  # 10个类别
# 赋初值 torch.normal(means, std, out=None)
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) # w=784*10
b = torch.zeros(num_outputs, requires_grad=True)    


# 1.定义模型
def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制
def net(X):
    # 这里通过reshape函数将每张原始图像改成长度为num_inputs的向量。
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)


# 2.定义损失
# 交叉熵损失
def cross_entropy(y_hat, y):
    # y_hat[range(len(y_hat)), y],log(a,b)不写b的情况默认为e.
    # len(y_hat)为样本的数量;即可写成y_hat[range(0,2),y];
    # 表示每行中选取y列的数值,实则是每个为1的样本所在的位置。
    # 就是上面那个例子的意思。
    return - torch.log(y_hat[range(len(y_hat)), y])


# 3.计算预测正确的数量
def accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    # 如果样本数量大于1,同时特征个数大于1
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        # 找出y_hat每行的最大值所在的索引位置:
        # y_hat=tensor([2, 2])
        y_hat = y_hat.argmax(axis=1)
    # 先将y_hat的数据格式转换为y的数据类型torch.int64
    # 再逐个比较y_hat和y是否在每一个位置上的值相等
    # cmp=tensor([False,  True])
    cmp = y_hat.type(y.dtype) == y
    # 相当于返回预测正确样本的数量
    return float(cmp.type(y.dtype).sum())


# 4.计算在指定数据集上模型的精度
def evaluate_accuracy(net, data_iter):  #@save
    """计算在指定数据集上模型的精度"""
    # isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
    if isinstance(net, torch.nn.Module):
        # model.eval()可以帮我们在模型预测阶段,将Dropout层和batch normalization层设置到预测模式
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 正确预测数、预测总数
    with torch.no_grad():
        for X, y in data_iter:
            # y.numel()获取tensor中一共包含多少个元素
            # accuracy(net(X), y), y.numel()==>一个批次中正确预测数、预测总数
            # eg:第一次输入10,256,则输出10,256;下一次输入21,256,则输出31,512;在下一次输入44,256,则输出75,768.
            # 相当于就是metric.add(a,b,c),对应位置的a,b,c分别自相加。
            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, 0.0]
        self.data = [0.0] * n
        
    def add(self, *args):
        # print("add前self.data",self.data)
        # print("add前*args",*args)
        # *args接收非关键字的传参可以是元组,或者是字符串。若b = [4,5,6,7,8]
        # zip(self.data, args)==>[(0.0, 4), (0.0, 5)]
        # 第一次执行self.data =[4.0, 0.0],第二次执行self.data =[4.0,5.0]
        self.data = [a + float(b) for a, b in zip(self.data, args)]
        # print("add后self.data",self.data)

    # 重置函数,将data列表置0
    def reset(self):
        self.data = [0.0] * len(self.data)

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


# 5.训练模型
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]
class Animator:  #@save
    """在动画中绘制数据"""
    # xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], legend=['train loss', 'train acc', 'test acc']
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, 
                 xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), 
                 nrows=1, ncols=1, figsize=(3.5, 2.5)):
        # 增量地绘制多条线
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函数捕获参数
        self.config_axes = lambda: d2l.set_axes(self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # 向图表中添加多个数据点
        # hasattr用于判断对象是否具有指定的属性或方法
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    """训练模型"""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc'])
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater) # 返回训练损失和训练精度l.sum()/y.numel(), accuracy(y_hat, y)/y.numel()
        test_acc = evaluate_accuracy(net, test_iter)                    # 返回正确率accuracy(y_hat, y)/y.numel()
        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)



# 6.开始训练
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

# 7.开始测试
def predict_ch3(net, test_iter, n=6):  #@save
    """预测标签(定义见第3章)"""
    for X, y in test_iter:
        # print(X[0:n].shape)
        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.shape=([256,1,28,28]),X[0:n].shape=([6, 1, 28, 28])
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(net, test_iter)

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值