动手学习深度学习-线性神经网络

1、回归

1.1、概念理解

按照数学上的定义来看,回归分析指研究一组随机变量(Y1 ,Y2 ,…,Yi)和另一组(X1,X2,…,Xk)变量之间关系的统计分析方法,又称多重回归分析。通常Y1,Y2,…,Yi是因变量,X1、X2,…,Xk是自变量。

1.2、回归种类(了解即可)

线性回归

定义: 通过线性组合输入特征预测连续值

应用: 房价预测、销量预测等

多项式回归

定义: 通过多项式函数拟合非线性数据

应用: 经济趋势预测、生物生长模型等

岭回归(Ridge Regression)

定义: 在线性回归中加入L2正则化,防止过拟合

应用: 高维数据回归

Lasso回归

定义: 加入L1正则化,适用于特征选择

应用: 高维数据回归与特征选择

弹性网络回归(Elastic Net Regression)

定义: 结合L1和L2正则化,平衡岭回归和Lasso回归

应用: 高维数据回归与特征选择

支持向量回归(SVR)

定义: 使用支持向量机进行回归,适用于非线性数据

应用: 金融时间序列预测、生物信息学等

决策树回归

定义: 通过树结构进行回归预测

应用: 客户价值预测、风险评估等

随机森林回归

定义: 集成多个决策树进行回归

应用: 生态学、医学等领域

梯度提升回归(GBR)

定义: 通过逐步优化残差进行回归

应用: 点击率预测、推荐系统等

神经网络回归

定义: 使用神经网络进行回归预测

应用: 图像处理、自然语言处理等

贝叶斯回归

定义: 基于贝叶斯定理进行回归

应用: 医学诊断、金融风险评估等

分位数回归

定义: 预测条件分位数,适用于非对称分布数据

应用: 经济学、医学等领域

逻辑回归

定义: 虽然主要用于分类,但也可用于概率预测

应用: 信用评分、疾病预测等

非线性回归

定义: 使用非线性模型拟合数据

应用: 化学动力学、生物学等领域

2、线性回归

神经网络图:

2.1、线性回归基本元素

线性模型:建立输入特征与目标变量之间的线性关系

给定n维输入x=[x1,x2,…,xn]T

线性模型有一个n维的权重w=[w1,w2,…,wn]T 和一个标量偏差b

输出为输入的加权和

向量表示为:y=<w,x>+b

损失函数:衡量模型预测值与实际值之间的差异

其中y^为预测值,y为真实值

解析解:通过数学推导得到的模型参数的闭式解

像线性回归这样的简单问题存在解析解,但并不是所有的问题都存在解析解。 解析解可以进行很好的数学分析,但解析解对问题的限制很严格,导致它无法广泛应用在深度学习里。

梯度下降:通过最小化损失函数来找到模型参数w、b的最优值

梯度下降步骤:

步骤1:随机选取一个 w0

步骤2:计算微分,也就是当前的斜率,根据斜率来判定移动的方向

            大于0向右移动(增加w)

            小于0向左移动(减少w)

步骤3:根据学习率移动

步骤4:重复步骤2和步骤3,直到找到最低点

学习率:步长的超参数

选择学习率不能太小,否则步长很有限,需要非常多步,计算梯度很贵同时也不能太大,太大会迈过下降的地方,导致震荡。

Adagrad算法调整学习率:

小批量随机梯度下降

       在整个训练集上算梯度太贵,所以随机采样b个样本来近似损失

若每次计算量太小,不适合并行来最大利用计算资源

若每次计算量太大,内存消耗增加,浪费计算,例如如果所有样本都是相同的

矢量化加速:同时处理整个小批量的样本

3、线性回归代码实现

import random
import torch
from d2l import torch as d2l

3.1、生成数据集

目标:根据带有噪声的线性模型构造⼀个⼈造数据集。

使用线性模型参数w=[2,-3.4]T,b=4.2和噪声项ε⽣成数据集及其标签:

代码实现:

import random
import torch
from matplotlib_inline import backend_inline
from matplotlib import pyplot as plt
from d2l import torch as d2l

# 生成数据集:

def synthetic_data(w, b, num_examples):  # @save
    """生成y=Xw+b+噪声"""
    X = torch.normal(0, 1, (num_examples, len(w)))
    # torch.normal用于生成服从正态分布(高斯分布)随机数
    # num_examples为数据样本数量

    y = torch.matmul(X, w) + b
    # 计算矩阵X和向量w的乘积,再加上偏置项b。
    # torch.matmul()用于执行矩阵乘法或张量乘法。

    y += torch.normal(0, 0.01, y.shape)
    # 向y添加一个从均值为 0、标准差为 0.01 的正态分布中随机采样的噪声。
    # torch.normal用于从正态分布(也称为高斯分布)中生成随机数。

    return X, y.reshape((-1, 1))


true_w = torch.tensor([2, -3.4])
# 创建一个 PyTorch 张量

true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
# synthetic_data() 函数生成合成数据

print('features:', features[0],'\nlabel:', labels[0])
# 通过⽣成第⼆个特征features[:, 1]和labels的散点图,可以直观观察到两者之间的线性关系。
d2l.set_figsize()
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);
plt.show()

运行结果:

简洁实现:

import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l

true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)

3.2、读取数据集

训练模型时要对数据集进⾏遍历,每次抽取⼀⼩批量样本,并使⽤它们来更新我们的模型。所以要定义⼀个能打乱数据集中的样本并以⼩批量⽅式获取数据的函数。

定义一个data_iter函数, 该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。 每个小批量包含一组特征和标签。

def data_iter(batch_size, features, labels):
    num_examples = len(features)  #样本数量
    
    indices = list(range(num_examples))
    # 生成一个包含从0到num_examples-1的整数列表,并将其赋值给变量indices
    
    random.shuffle(indices)
    #将列表indices中的元素随机打乱顺序,该函数直接修改传入的列表而不会创建新的列表。
    
    for i in range(0, num_examples, batch_size):
        batch_indices = torch.tensor(
            indices[i: min(i + batch_size, num_examples)])
            #从indices列表中提取一个子列表,表示当前批次的样本索引。起始索引为i,结              束索引为min(i + batch_size,num_examples),确保即使在数据集的末尾              也不会超出范围。
            
        yield features[batch_indices], labels[batch_indices]
        #使用 batch_indices 来索引 features,从而获取当前批次的特征数据。
        #使用 batch_indices 来索引 labels,从而获取当前批次的标签数据。
        #yield 语句使得这个函数成为一个生成器函数。每次调用生成器时,它会返回当前批次          的特征和标签,然后暂停,直到下一次调用时继续执行。

简洁实现:调用框架中现有的API来读取数据,将featureslabels作为API的参数传递,并通过数据迭代器指定batch_size。 此外,布尔值is_train表示是否希望数据迭代器对象在每个迭代周期内打乱数据。

def load_array(data_arrays, batch_size, is_train=True):  #@save
    """构造一个PyTorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

batch_size = 10
data_iter = load_array((features, labels), batch_size)

3.3、初始化模型参数

在开始⽤⼩批量随机梯度下降优化我们的模型参数之前,需要先有⼀些参数。

通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重, 并将偏置初始化为0。

w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
#torch.normal:用于生成正态分布随机数的函数
#0为正态分布的均值,0.01为正态分布的标准差,size=(2,1)用于指定生成的张量的形状,这通   常用于初始化连接输入层和隐藏层(或输出层)的权重。
#requires_grad=True表示需要计算梯度

b = torch.zeros(1, requires_grad=True)

简洁实现:深度学习框架通常有预定义的方法来初始化参数。 在这里指定每个权重参数应该从均值为0、标准差为0.01的正态分布中随机采样, 偏置参数将初始化为零。

net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)

3.4、定义模型

将模型的输入和参数同模型的输出关联起来。要计算线性模型的输出,只需计算输入特征X和模型权重w的矩阵-向量乘法后加上偏置b。

def linreg(X, w, b):  #@save
    """线性回归模型"""
    return torch.matmul(X, w) + b

简洁实现:对于标准深度学习模型,可以使用框架的预定义好的层。只需关注使用哪些层来构造模型,而不必关注层的实现细节。

# nn是神经网络的缩写
from torch import nn

net = nn.Sequential(nn.Linear(2, 1))

Sequential类将多个层串联在一起。 当给定输入数据时,Sequential实例将数据传入到第一层, 然后将第一层的输出作为第二层的输入,以此类推。

3.5、定义损失函数

定义损失函数观察并通过优化器使梯度下降

def squared_loss(y_hat, y):  #@save
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

计算均方误差使用的是MSELoss类,也称为平方L2范数。 默认情况下,它返回所有样本损失的平均值。

loss = nn.MSELoss()

3.6、定义优化算法:用函数实现小批量随机梯度下降更新

该函数接受模型参数集合、学习速率和批量大小作为输入。每 一步更新的大小由学习速率lr决定。

def sgd(params, lr, batch_size):  #@save
    """小批量随机梯度下降"""
    with torch.no_grad(): #临时关闭梯度计算
        for param in params:
            param -= lr * param.grad / batch_size  
            #将归一化后的梯度乘以学习率,得到参数更新的量
            param.grad.zero_()
            #将参数的梯度清零

简洁实现:小批量随机梯度下降算法, PyTorch在optim模块中实现了该算法的许多变种。 当实例化一个SGD实例时,我们要指定优化的参数 (可通过net.parameters()从模型中获得)以及优化算法所需的超参数字典。 小批量随机梯度下降只需要设置lr值,这里设置为0.03。

trainer = torch.optim.SGD(net.parameters(), lr=0.03)

3.7、训练

lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss

for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y)  # X和y的小批量损失
        # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
        # 并以此计算关于[w,b]的梯度
        l.sum().backward()
        sgd([w, b], lr, batch_size)  # 使用参数的梯度更新参数
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

简洁实现:通过深度学习框架的高级API来实现模型只需要相对较少的代码。不必单独分配参数、不必定义损失函数,也不必手动实现小批量随机梯度下降。 当需要更复杂的模型时,高级API的优势将大大增加。

在每个迭代周期里,将完整遍历一次数据集(train_data), 不停地从中获取一个小批量的输入和相应的标签。 对于每一个小批量会进行以下步骤:

       通过调用net(X)生成预测并计算损失l(前向传播)。

       通过进行反向传播来计算梯度。

       通过调用优化器来更新模型参数。

num_epochs = 3
for epoch in range(num_epochs):
    for X, y in data_iter:
        l = loss(net(X) ,y)
        trainer.zero_grad()
        l.backward()
        trainer.step()
    l = loss(net(features), labels)
    print(f'epoch {epoch + 1}, loss {l:f}')
    
w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)
完整代码:
# 线性回归代码
import random
import torch
from matplotlib_inline import backend_inline
from matplotlib import pyplot as plt
from d2l import torch as d2l


# 生成数据集
def synthetic_data(w, b, num_examples):  # @save
    """生成y=Xw+b+噪声"""
    X = torch.normal(0, 1, (num_examples, len(w)))
    # torch.normal用于生成服从正态分布(高斯分布)随机数
    # num_examples为数据样本数量

    y = torch.matmul(X, w) + b
    # 计算矩阵X和向量w的乘积,再加上偏置项b。
    # torch.matmul()用于执行矩阵乘法或张量乘法。

    y += torch.normal(0, 0.01, y.shape)
    # 向y添加一个从均值为 0、标准差为 0.01 的正态分布中随机采样的噪声。
    # torch.normal用于从正态分布(也称为高斯分布)中生成随机数。

    return X, y.reshape((-1, 1))


true_w = torch.tensor([2, -3.4])
# 创建一个 PyTorch 张量

true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
# synthetic_data() 函数生成合成数据

print('features:', features[0],'\nlabel:', labels[0])
# 通过⽣成第⼆个特征features[:, 1]和labels的散点图,可以直观观察到两者之间的线性关系。
d2l.set_figsize()
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);
# plt.show()


# 读取数据集
def data_iter(batch_size, features, labels):
    num_examples = len(features)  # 样本数量

    indices = list(range(num_examples))
    # 生成一个包含从0到num_examples-1的整数列表,并将其赋值给变量indices

    random.shuffle(indices)
    # 将列表indices中的元素随机打乱顺序,该函数直接修改传入的列表而不会创建新的列表。

    for i in range(0, num_examples, batch_size):
        batch_indices = torch.tensor(
            indices[i: min(i + batch_size, num_examples)])
        # 从indices列表中提取一个子列表,表示当前批次的样本索引。起始索引为i,结束索引为min(i + batch_size,num_examples),确保即使在数据集的末尾也不会超出范围。

        yield features[batch_indices], labels[batch_indices]
        # 使用 batch_indices 来索引 features,从而获取当前批次的特征数据。
        # 使用 batch_indices 来索引 labels,从而获取当前批次的标签数据。
        # yield 语句使得这个函数成为一个生成器函数。每次调用生成器时,它会返回当前批次的特征和标签,然后暂停,直到下一次调用时继续执行。

batch_size = 10
# for X, y in data_iter(batch_size, features, labels):
#     print(X, '\n', y)
#     break


# 初始化模型参数
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
#torch.normal:用于生成正态分布随机数的函数
#0为正态分布的均值,0.01为正态分布的标准差,size=(2,1)用于指定生成的张量的形状,这通   常用于初始化连接输入层和隐藏层(或输出层)的权重。
#requires_grad=True表示需要计算梯度

b = torch.zeros(1, requires_grad=True)


# 模型
def linreg(X, w, b):  #@save
    """线性回归模型"""
    return torch.matmul(X, w) + b


# 损失函数
def squared_loss(y_hat, y):  #@save
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2


# 优化算法
def sgd(params, lr, batch_size):  #@save
    """小批量随机梯度下降"""
    with torch.no_grad(): #临时关闭梯度计算
        for param in params:
            param -= lr * param.grad / batch_size
            #将归一化后的梯度乘以学习率,得到参数更新的量
            param.grad.zero_()
            #将参数的梯度清零


# 训练
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss

for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y)  # X和y的小批量损失
        # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
        # 并以此计算关于[w,b]的梯度
        l.sum().backward()
        sgd([w, b], lr, batch_size)  # 使用参数的梯度更新参数
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
简洁版:
# 线性回归代码(简洁版)
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn


# 生成数据集
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)


# 读取数据集
def load_array(data_arrays, batch_size, is_train=True): #@save
    """构造⼀个PyTorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

batch_size = 10
data_iter = load_array((features, labels), batch_size)


# 定义模型
net = nn.Sequential(nn.Linear(2, 1))


# 初始化模型参数
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)


# 定义损失函数
loss = nn.MSELoss()


# 定义优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.03)


# 训练
num_epochs = 3
for epoch in range(num_epochs):
    for X, y in data_iter:
        l = loss(net(X), y)
        trainer.zero_grad()
        l.backward()
        trainer.step()
    l = loss(net(features), labels)
    print(f'epoch {epoch + 1}, loss {l:f}')
w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)

4、softmax回归

4.1、分类问题

独热编码(one-hot):一种表示分类数据的简单方法。将离散分类特征转换为二进制向量,每个类别对应一个唯一的高维稀疏向量表示。

4.2、网络架构

例⼦中,由于有4个特征和3个可能的输出类别,所以将需要12个标量来表⽰权重(带下标的w),3个标量来表⽰偏置(带下标的b),为每个输⼊计算三个未规范化的预测(logit):o1、o2和o3。

⽤神经⽹络图来描述这个计算过程:

softmax回归也是一个单层神经网络,softmax回归的输出层也是全连接层。

4.3、全连接层的参数开销

全连接层的特点是输入层每个神经元都与输出层每个神经元相连,参数数量为O(dq)。为减少参数,可以通过某种方式将输入转换为输出的成本降低到O(dq/n),其中n为可调节的超参数,用于平衡参数节约和模型有效性。

4.4、softmax运算

性质:

1、softmax函数将未规范化的预测变换为⾮负并且总和为1,同时要求模型保持可导。

2、softmax运算不会改变未规范化的预测o之间的顺序,只会确定分配给每个类别的概率。

作用:用于将一组实数转换为概率分布,常用于多分类问题的输出层。

4.5、小批量样本的矢量化

为了提高计算速度,通常会一次性处理多个样本,而不是一个接一个地处理。这种方法被称为小批量样本处理,而矢量化则是将多个样本的数据和权重转换为矩阵形式,以便进行高效的矩阵运算。

应用于softmax:

矢量化的softmax回归计算首先通过矩阵乘法 XW 和加法 +b 得到未规范化的预测结果 O

对 O 应用softmax函数,将每个样本的预测结果转换为概率分布 Y^。

在计算 XW+b 时,广播机制将偏置项 b 自动扩展到与 O 相同的形状,从而进行逐元素的加法运算。

4.6、损失函数

作用:用于量化模型预测结果与实际结果之间的差异,通过计算损失值可以评估模型在训练数据上的表现。损失函数值越小,表示模型的预测结果与实际结果越接近,模型的性能越好。

均方损失函数:

         其中y为真实值,y’为预测值。

         蓝色:当y=0时,变换预测值y’的函数

         绿色:似然函数,1-1,高斯分布

         橙色:损失函数的梯度,穿过原点的一次函数

当预测值y’与真实值y隔的比较远时,梯度比较大,参数更新比较多,随着预测值靠近真实值时,梯度的绝对值会越来越小,参数更新的幅度也越来越小。

绝对值损失函数:

         蓝色:损失函数

         绿色:似然函数

         橙色:梯度

当预测值y’与真实值y隔的比较远时,梯度永远为常数,权重更新不会特别大,稳定性高,缺点为零点处不可导,影响不平滑性(-1和1的剧烈变化),即当预测值y’与真实值y隔的比较近时(优化末期)会不太稳定。

鲁棒损失函数:

当预测值和真实值差的比较大时,为绝对值误差,当比较近时为平方误差。

5、softmax回归实现

# softmax回归实现
import torch
from torch import nn
from d2l import torch as d2l
from IPython import display
from matplotlib import pyplot as plt

# 加载数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

5.1、初始化模型参数

# PyTorch不会隐式地调整输入的形状,因此在线性层前定义了展平层(flatten)
num_inputs = 784  # 28x28像素的图像展平后的数据维度
num_outputs = 10  # 10个类别
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)

5.2、定义softmax函数

# 定义softmax函数
def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)  # 按行求和,保持维度
    return X_exp / partition  # 应用广播机制

5.3、定义模型

使用学习率为0.1的小批量随机梯度下降作为优化算法

# 定义模型
def net(X):
    # 将输入展平后进行矩阵运算,再加上偏置,最后应用softmax
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)

5.4、定义交叉熵损失函数

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

5.5、定义辅助类

# 辅助类:用于累加多个变量
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):
        self.data = [0.0] * len(self.data)

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

5.6、定义分类精度

# 定义分类精度计算函数
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)  # 返回每行中最大值的索引
    cmp = y_hat.type(y.dtype) == y
    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)  # 正确预测数、预测总数
    with torch.no_grad():  # 不计算梯度,节省内存
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]  # 正确率 = 正确预测数 / 总预测数

5.7、训练

# 训练函数
def train_epoch_ch3(net, train_iter, loss, updater):  # @save
    """训练模型一个迭代周期"""
    # 将模型设置为训练模式
    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
    """在动画中绘制数据"""

    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):
        # 向图表中添加多个数据点
        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)
        test_acc = evaluate_accuracy(net, test_iter)
        animator.add(epoch + 1, train_metrics + (test_acc,))  # 修复缩进,确保每个epoch都更新
    train_loss, train_acc = train_metrics
    # 断言检查训练结果是否合理
    assert train_loss < 0.5, f"训练损失过大: {train_loss}"
    assert train_acc <= 1 and train_acc > 0.7, f"训练精度异常: {train_acc}"
    assert test_acc <= 1 and test_acc > 0.7, f"测试精度异常: {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)
plt.show()

运行结果:

5.8、预测

# 预测
def predict_ch3(net, test_iter, n=6): #@save
    """预测标签"""
    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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值