首页 > 编程语言 >李沐《动手学深度学习》softmax回归python代码实现

李沐《动手学深度学习》softmax回归python代码实现

时间:2024-11-06 21:18:46浏览次数:3  
标签:python self torch iter train softmax 李沐 net hat

一、手动实现softmax回归

# 手动实现softmax回归
# %matplotlib inline
import torch
from d2l import torch as d2l
import matplotlib.pyplot as plt
from IPython import display

# 参数初始化:
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

# 将28*28的图片展开成一维的784长度的向量
num_inputs = 784
num_outputs = 10

# 初始化参数w的一个784*10的矩阵,偏置项b是一个长度为10的向量
W = torch.normal(0,0.1,size=(num_inputs, num_outputs), requires_grad = True)
b = torch.zeros(num_outputs, requires_grad = True)
lr = 0.1

# 累加器
class Accumulator():
    def __init__(self, n):
        self.data = [0.0] * n
    def add(self, *args):
        # 以n=2为例,每次add(arg1, arg2),则data[0]+=arg1, data[1]+=arg2
        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]

# 定义一个动画类
class Animator:
    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, 3.5)):

        if legend is None:
            legend = []

        self.fig, self.axes = plt.subplots(nrows, ncols, figsize = figsize)
        
        if nrows * ncols == 1: # 如果只有一个子图
            self.axes = [self.axes, ] # 也处理成list,为和多子图的情况统一处理
        # 设置第一个子图的参数
        self.xlabel = xlabel
        self.ylabel = ylabel
        self.xlim = xlim
        self.ylim = ylim
        self.xscale = xscale
        self.yscale = yscale
        self.legend = legend
                
        self.X, self.Y, self.fmts = None, None, fmts
        
    def config_axes(self):
        """设置坐标轴"""
        self.axes[0].set_xlabel(self.xlabel)
        self.axes[0].set_ylabel(self.ylabel)
        if self.xlim is not None:
            self.axes[0].set_xlim(self.xlim)
        if self.ylim is not None:
            self.axes[0].set_ylim(self.ylim)
        self.axes[0].set_xscale(self.xscale)
        self.axes[0].set_yscale(self.yscale)
        if self.legend:
            self.axes[0].legend(self.legend)
        
        
    # 添加数据点,x=epoch+1, y=metric[0]~~metric[n]
    def add(self, x, y):
        if not hasattr(y, '__len__'): # 如果y不可迭代
            y = [y]
        n = len(y)
        if not hasattr(x, '__len__'):
            x = [x] * n
        
        if not self.Y:  # self.Y维度是n(代表n种需要图示的y值),每一个元素都是一个列表[第1次train的值,第2次...]
            self.Y = [ [] for _ in range(n) ]
        if not self.X:
            self.X = [ [] 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() # 清除axes[0]的参数设置           
        
        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)
    
# softmax函数,传入的是已经计算完的W*X(n*10)+b(10*1) = n*10的矩阵
# n代表样本的个数,10列对应着是10个种类的线性值
def softmax(X):
    X_exp = torch.exp(X) 
    partition = X_exp.sum(1, keepdim=True)  # 对每一行求和得到一个n*1的向量
    return X_exp / partition  # 利用广播机制对每行的10个元素除以该行的sum

# 模型函数,输入为训练数据(n*784)
def net(X):
    # 先把训练数据X的列变为784,行数自动计算,这样保证了X为n*784的矩阵
    # 再将X与W做点积得到n*10的矩阵传入给softmax
    # 这样就返回了经过softmax化的n*10的矩阵
    # 每一行的每个列就代表在这列种类下的概率
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b) 

# 交叉损失函数,输入y_hat是net得到的n*10的矩阵,y是n维度向量
# 通过遍历y_hat的1到n行,列=y[当前行号]来得到y_hat预测的概率(因为y是类别的idx)
def cross_entropy(y_hat, y):
    # 得到对每个数据的真实类别的预测概率p(n*1),再返回-log(p)
    # 因为此时我们希望每个p都越大越好,因此p越大-logp越小,反之越大
    return -torch.log(y_hat[range(len(y)), y])

# 计算y_hat中概率最大的类别预测正确的个数
def accuracy(y_hat, y):
    # 如果y_hat是矩阵
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum())

# 评估一个数据迭代器的精度
def evaluate_iter(net, data_iter):
    if isinstance(net, torch.nn.Module):
        net.eval() # 如果net是nn中的模型,我就把net设置为评估模式,防止额外构建计算图
    metric = Accumulator(2)  # 创建存储2个data的累加器
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
        return metric[0] / metric[1]

# 自定义优化器
def updater(batch_size):
    return d2l.sgd([W,b], lr, batch_size)
    
# 一个训练迭代周期
def train_epoch_ch3(net, train_iter, loss, updater): # updater是更新用的优化器
    # 如果net是nn中的神经网络就开启训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # metric[0]:训练损失和, metric[1]:预测正确数量,  metric[2]:样本总数
    metric = Accumulator(3)
    for X, y in train_iter:
        y_hat = net(X) # 先取第一批data和label,得到预测的y_hat
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer): # 如果优化器是torch框架的
            updater.zero_grad()
            l.mean().backward() # l.mean()等价于sum(l)/len(y) 牛的
            updater.step()
        else:
            l.sum().backward()
            updater(len(y))
        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):
    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)
        test_acc = evaluate_iter(net, test_iter)
        # 利用元组的拼接y=(train_tetrics, test_acc)
        animator.add(epoch+1, train_metrics + (test_acc, )) # 这让调用add的时候保证了y是个元组,这样add会把这个元组转换成为list
        train_loss, train_acc = train_metrics
        
        assert train_loss < 0.9, train_loss
        assert train_acc > 0.7 and train_acc <= 1, train_acc
        assert test_acc > 0.7 and test_acc <= 1, train_acc

def predict_ch3(net, test_iter, n=6): 
    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])
        
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

训练过程:

二、利用pytorch框架实现softmax回归

# 利用pytorch框架实现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)

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
        
# net构建了两个layer,第一层把输入的一个样本(1,28,28)展开成 (1,784)
# 第二层是一个接受(1,784)并且输出(1,10)的线性激活函数
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))
net.apply(init_weights)
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

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

训练过程:

标签:python,self,torch,iter,train,softmax,李沐,net,hat
From: https://blog.csdn.net/yuzixuan233/article/details/143518877

相关文章

  • 李沐《动手学深度学习》权重衰退(正则化)python代码实现
    一、L2正则化手动实现#权重衰退手动实现%matplotlibinlineimporttorchfromd2limporttorchasd2lfromtorchimportnn#n_train个训练样本,n_test个测试样本,输入数据维度是200维n_train,n_test,num_inputs,batch_size=20,200,200,5true_w,true_b=to......
  • 李沐《动手学深度学习》多层感知机python代码实现
    一、多层感知机手动实现#多层感知机的手动实现%matplotlibinlineimporttorchfromtorchimportnnfromd2limporttorchasd2lbatch_size=256train_iter,test_iter=d2l.load_data_fashion_mnist(batch_size)num_inputs,num_outputs,num_first_hiddens=......
  • 李沐《动手学深度学习》线性回归python代码实现
    一、手动实现线性回归#线性回归的手动实现%matplotlibinlineimporttorchimportrandomfromd2limporttorchasd2l#随机按照参数w和b外加一些噪音来创造训练数据集data和labelsdefsynthetic_data(w,b,num_examples):X=torch.normal(0,1,(num_example......
  • Python学习笔记-生成器的应用与原理
    生成器是Python中一种特殊的迭代工具,通过延迟计算的方式来逐步生成序列中的元素。这种特性使得生成器在处理大数据、无限序列或需要惰性求值的场景中十分有效。生成器的核心思想是通过yield语句逐步返回值,暂停并保留当前状态,直到下次调用继续执行,从而节省内存并优化性能......
  • Python学习笔记-断点操作结合异常处理
    在编程中,调试和错误处理是提升代码质量和开发效率的关键环节。调试能帮助识别并修复问题,异常处理则使得程序能在出现错误时有效地管理而不至于崩溃。断点与异常处理的结合应用是高级编程中不可或缺的技巧,能够帮助更高效地定位问题,提高程序的鲁棒性。本文将通过详细的断点和......
  • Python——数据结构与算法-时间复杂度&空间复杂度-链表&树状结构
    1.数据结构和算法简介程序可以理解为:程序=数据结构+算法概述/目的:都可以提高程序的效率(性能)数据结构指的是存储,组织数据的方式.算法指的是为了解决实际业务问题而思考思路和方法,就叫:算法.2.算法的5大特性介绍概述:为了解决实际业务问题,......
  • python面向对象(一)
    前言Python是一种功能强大的编程语言,因其简洁的语法和强大的库而备受开发者喜爱。而在Python中,面向对象编程(OOP)是一种核心的编程范式,它通过模拟现实世界中的对象和交互来帮助我们设计清晰、易维护的代码。在本篇博客中,我们将深入探讨Python的面向对象编程的基本概念,了解如......
  • 【PAT_Python解】1114 全素日
    原题链接:PTA|程序设计类实验辅助教学平台Tips:以下Python代码仅个人理解,非最优算法,仅供参考!多学习其他大佬的AC代码!defis_prime(n):ifn<=3:returnn>=2ifn%6notin(5,1):returnFalseforiinrange(5,int(n**0.5)+1,6):......
  • 【PAT_Python解】1110 区块反转
    原题链接:PTA|程序设计类实验辅助教学平台Tips:以下Python代码仅个人理解,非最优算法,仅供参考!多学习其他大佬的AC代码!importsys#读取输入head,n,k=map(int,sys.stdin.readline().split())#初始化数据,装入字典,最终取值data={}next={}for_inrange(n):......
  • 【PAT_Python解】1113 钱串子的加法
    原题链接:PTA|程序设计类实验辅助教学平台Tips:以下Python代码仅个人理解,非最优算法,仅供参考!多学习其他大佬的AC代码!defadd_base30(num1,num2):max_length=max(len(num1),len(num2))#在前面补零,使两个字符串长度相同num1=num1.zfill(max_lengt......