深度学习_Softmax回归从0开始实现

前言

        Softmax回归与常规的线性回归不同的一点是其是针对分类问题的回归,在线性回归的基础上增加了一个softmax函数层,可以将神经网络的输出压缩到0-1之间,具体的公式为:

y_{j} = \frac{\exp(o_{j})}{\sum_{k}exp(o_{k})}

        其中 j 为分类问题的第 j 类, y_{j}是指该网络的第 j 个输出经过Softmax后的值,也就是对于这个神经网络的输入,其为第 j 类的概率为y_{j};k 为分类问题的类别总数。对应的的神经网络模型如下:

图片来自:李沐老师《动手学深度学习》

代码书写流程 

        参考了第8节线性回归代码书写的内容,老师教程中的有些函数方法对于小白来说有点困难,这里的实现是非常简单的,相当于在线性回归里加了一个softmax层,然后修改了一下损失函数,代码如下:

第一步:下载训练数据集

# 下载数据
# 通过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)

 第二步:构建softmax函数

        根据前言中的公式来写的softmax函数,输入的labels对应的是神经网络的输出:

# 构建soft_max函数
def softmax(labels):
    X_exp = torch.exp(labels)
    partition = X_exp.sum(-1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制

 第三步:定义交叉熵损失函数

        交叉熵损失函数的公式如下,其中p(x_{i})指的是标签真实的概率分布,而q(x_{i})指的是神经网络输出的预测概率分布。在我们的例子中p(x_{i})只有其对应的真是标签位置 t 中的值为1,即p(x_{t}),其余值都为0,比如我们的数据集有10个类别,输入的图片是第5类,那么p(x)的分布为[0,0,0,0,1,0,0,0,0,0] ,与此同时,对应的神经网络的输出q(x)可能为[0.01, 0.09, 0.12, 0, 0.75, 0.01, 0.01, 0.01, 0.0, 0.0],这时除了两个分布中的第5个数字,其他9个乘积全为0,因此计算的时候只用计算-p(x_5)log(q(x_5))即可,其损失函数就可以写成如下图代码所示的形式,非常简单

 

def cross_entropy(y_pred, y):
    return -torch.log(y_pred[range(len(y_pred)), y])

        代码中y_pred时预测分布,y 为真实标签,y_pred[range(len(y_pred)), y]这个操作就是在求取q(x_{5}),p(x_{5})的值为1,可以省略。

第四步:定义权重更新函数(优化算法)

         这个和线性回归的优化算法是一样的,在老师的教程中,将该方法封装成了一个函数保存在了d2l包中。

def update_params(w, b, lr, batch_size):
    """"出错啦啦啦啦啦啦"""
    # 不加 with torch.no_grad():的时候会报下面的错误,不知道为什么
    # a leaf Variable that requires grad is being used in an in-place operation.
    with torch.no_grad():
        w += - lr * w.grad / batch_size
        b += - lr * b.grad / batch_size
        w.grad.zero_()
        # w并不报错b'报错:'Tensor' object has no attribute 'grad_zero_'
        # 啊,把.写成了_,服了,脑子不好使了
        b.grad.zero_()

 第五步:构建神经网络+初始化模型参数

# 构建神经网络
def linear_net(x, w, b):
    """
    其中我们的权重矩阵的形状一定是确定的,因此在这里注意要对x矩阵进行 reshape,
    (在网络中进行reshape可以提高网络的适配性)然后再进行线性操作
    """
    # return softmax(torch.matmul(x.reshape((-1, w.shape[0])), w) + b)
    # x.reshape((-1, w.shape[0])) 最里面那个,注意不是小括号是中括号
    A = x.shape[0]
    return softmax(torch.matmul(x.reshape([A, w.shape[0]]), w) + b)

# 初始化模型参数
w = torch.normal(0, 0.1, size=(784, 10), requires_grad=True)
b = torch.zeros(10, requires_grad=True)

第六步:划分数据集+训练

        这里的自己加了一个loss计算的结果输出,因为感觉老师教程上面的写的有点复杂,没有很理解,想着自己先试试写的,还是老师那种计算精确度的方法更合适一些,并且将训练和测试封装为函数更合理,慢慢学啦,先写成这样,感觉自己学的好慢~~~

# 读取小批量数据
num_epochs = 10
batch_size = 32
lr = 0.001
net = linear_net
train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=get_dataloader_workers())
A = len(mnist_train) / 32
for epoch in range(num_epochs):
    loss_all = 0
    for x, y in train_iter:
        a = x.sum(dim=1)
        y_pred = net(x.sum(dim=1), w, b)
        loss = cross_entropy(y_pred, y)
        loss.sum().backward()
        update_params(w, b, lr, batch_size)
        with torch.no_grad():
            loss_all = loss + loss_all
        loss_all = loss_all / A
    print(f'epoch {epoch + 1}, loss {float(loss_all.mean()):f}')

总结

        到这里可以说实现了softmax回归,就是略显粗糙

完整代码

import d2l.torch
import torch
import torchvision
# from d2l.mxnet import Accumulator
from torch.utils import data
import matplotlib.pylab as plt
from torchvision import transforms

# 下载数据
# 通过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)


# print(len(mnist_train))
# print(len(mnist_test))



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


# 构建soft_max函数
def softmax(labels):
    X_exp = torch.exp(labels)
    partition = X_exp.sum(-1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制



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



# 构建神经网络
def linear_net(x, w, b):
    """
    其中我们的权重矩阵的形状一定是确定的,因此在这里注意要对x矩阵进行 reshape,
    (在网络中进行reshape可以提高网络的适配性)然后再进行线性操作
    """
    # return softmax(torch.matmul(x.reshape((-1, w.shape[0])), w) + b)
    # x.reshape((-1, w.shape[0])) 最里面那个,注意不是小括号是中括号
    A = x.shape[0]
    return softmax(torch.matmul(x.reshape([A, w.shape[0]]), w) + b)


# 定义权重更新函数
def update_params(w, b, lr, batch_size):
    """"出错啦啦啦啦啦啦"""
    # 不加 with torch.no_grad():的时候会报下面的错误,不知道为什么
    # a leaf Variable that requires grad is being used in an in-place operation.
    with torch.no_grad():
        w += - lr * w.grad / batch_size
        b += - lr * b.grad / batch_size
        w.grad.zero_()
        # w并不报错b'报错:'Tensor' object has no attribute 'grad_zero_'
        # 啊,把.写成了_,服了,脑子不好使了
        b.grad.zero_()


# 初始化模型参数
w = torch.normal(0, 0.1, size=(784, 10), requires_grad=True)
b = torch.zeros(10, requires_grad=True)

# 读取小批量数据
num_epochs = 10
batch_size = 32
lr = 0.001
net = linear_net
train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=get_dataloader_workers())
A = len(mnist_train) / 32
for epoch in range(num_epochs):
    loss_all = 0
    for x, y in train_iter:
        a = x.sum(dim=1)
        y_pred = net(x.sum(dim=1), w, b)
        loss = cross_entropy(y_pred, y)
        loss.sum().backward()
        update_params(w, b, lr, batch_size)
        with torch.no_grad():
            loss_all = loss + loss_all
        loss_all = loss_all / A
    print(f'epoch {epoch + 1}, loss {float(loss_all.mean()):f}')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值