• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

实验五 前馈神经网络3鸢尾花分类

武飞扬头像
沐一mu
帮助1

一、深入研究鸢尾花数据集

画出数据集中150个数据的前两个特征的散点分布图:

  1.  
    import numpy as np
  2.  
    import torch
  3.  
    import matplotlib.pyplot as plt
  4.  
     
  5.  
    data_x,data_y = load_data()
  6.  
    iris_first = []
  7.  
    iris_second = []
  8.  
    iris_third = []
  9.  
    for i in range(0,len(data_y)):
  10.  
    if(data_y[i]==0):
  11.  
    iris_first.append(data_x[i,:].numpy())
  12.  
    elif(data_y[i]==2):
  13.  
    iris_second.append(data_x[i,:].numpy())
  14.  
    else:
  15.  
    iris_third.append(data_x[i,:].numpy())
  16.  
    iris_first = torch.tensor(iris_first)
  17.  
    iris_second = torch.tensor(iris_second)
  18.  
    iris_third = torch.tensor(iris_third)
  19.  
     
  20.  
    plt.scatter(iris_first[:,0],iris_first[:,1],c='b')
  21.  
    plt.scatter(iris_second[:,0],iris_second[:,1],c='y')
  22.  
    plt.scatter(iris_third[:,0],iris_third[:,1],c='g')
  23.  
    plt.legend(['iris versicolor','iris setosa','iris vlrglnica'])
学新通

学新通

二、实践:基于前馈神经网络完成鸢尾花分类

本次实验继续使用第三章中的鸢尾花分类任务,将Softmax分类器替换为本章介绍的前馈神经网络。使用的损失函数为交叉熵损失;优化器为随机梯度下降法;评价指标为准确率。

1.小批量梯度下降法

在梯度下降法中,目标函数是整个训练集上的风险函数,这种方式称为批量梯度下降法(Batch Gradient Descent,BGD)。 批量梯度下降法在每次迭代时需要计算每个样本上损失函数的梯度并求和。当训练集中的样本数量N NN很大时,空间复杂度比较高,每次迭代的计算开销也很大。

为了减少每次迭代的计算复杂度,我们可以在每次迭代时只采集一小部分样本,计算在这组样本上损失函数的梯度并更新参数,这种优化方式称为
小批量梯度下降法(Mini-Batch Gradient Descent,Mini-Batch GD)。

学新通次迭代时,随机选取一个包含学新通个样本的子集学新通,计算这个子集上每个样本损失函数的梯度并进行平均,然后再进行参数更新。

学新通

其中K KK为批量大小(Batch Size)。学新通通常不会设置很大,一般在1 ∼ 100 之间。在实际应用中为了提高计算效率,通常设置为2的幂学新通

在实际应用中,小批量随机梯度下降法有收敛快、计算开销小的优点,因此逐渐成为大规模的机器学习中的主要优化算法。
此外,随机梯度下降相当于在批量梯度下降的梯度上引入了随机噪声。在非凸优化问题中,随机梯度下降更容易逃离局部最优点。

小批量随机梯度下降法的训练过程如下:

学新通

 1.1数据分组

为了小批量梯度下降法,我们需要对数据进行随机分组。目前,机器学习中通常做法是构建一个数据迭代器,每个迭代过程中从全部数据集中获取一批指定数量的数据。

数据迭代器的实现原理如下图所示:

学新通

  1. 首先,将数据集封装为Dataset类,传入一组索引值,根据索引从数据集合中获取数据;
  2. 其次,构建DataLoader类,需要指定数据批量的大小和是否需要对数据进行乱序,通过该类即可批量获取数据。

2.数据处理

对鸾尾花数据集进行处理,构造IrisDataset类进行数据读取并进行继承和封装。

  1.  
    # 导入需要使用的所有包
  2.  
    import copy
  3.  
    import torch
  4.  
    import numpy as np
  5.  
    from torch import nn
  6.  
    import torch.optim as opt
  7.  
    import matplotlib.pyplot as plt
  8.  
    import torch.nn.functional as F
  9.  
    from sklearn.datasets import load_iris
  10.  
     
  11.  
    import os
  12.  
    os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
  1.  
    class IrisDataset(torch.utils.data.Dataset):
  2.  
    def __init__(self, mode='train', num_train=120, num_dev=15):
  3.  
    super(IrisDataset, self).__init__()
  4.  
    # 调用第三章中的数据读取函数,其中不需要将标签转成one-hot类型
  5.  
    X, y = load_data(shuffle=True)
  6.  
    if mode == 'train':
  7.  
    self.X, self.y = X[:num_train], y[:num_train]
  8.  
    elif mode == 'dev':
  9.  
    self.X, self.y = X[num_train:num_train num_dev], y[num_train:num_train num_dev]
  10.  
    else:
  11.  
    self.X, self.y = X[num_train num_dev:], y[num_train num_dev:]
  12.  
     
  13.  
    def __getitem__(self, idx):
  14.  
    return self.X[idx], self.y[idx]
  15.  
     
  16.  
    def __len__(self):
  17.  
    return len(self.y)
学新通
  1.  
    train_dataset = IrisDataset(mode='train')
  2.  
    dev_dataset = IrisDataset(mode='dev')
  3.  
    test_dataset = IrisDataset(mode='test')
  4.  
     
  5.  
    # 打印数据集长度
  6.  
    print("length of train set: ", len(train_dataset))
  7.  
    print("length of dev set: ", len(dev_dataset))
  8.  
    print("length of test set: ", len(test_dataset))

 执行结果:

  1.  
    length of train set: 120
  2.  
    length of dev set: 15
  3.  
    length of test set: 15

2.1用DataLoader进行封装

  1.  
    # 批量大小
  2.  
    batch_size = 16
  3.  
     
  4.  
    # 加载数据
  5.  
    train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True,num_workers=0)
  6.  
    dev_loader = torch.utils.data.DataLoader(dev_dataset, batch_size=batch_size)
  7.  
    test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size)

3.模型构建

构建一个简单的前馈神经网络进行鸢尾花分类实验。其中输入层神经元个数为4,输出层神经元个数为3,隐含层神经元个数为6。

  1.  
    from torch import nn
  2.  
     
  3.  
    # 定义前馈神经网络
  4.  
    class Model_MLP_L2_V3(nn.Module):
  5.  
    def __init__(self, input_size, output_size, hidden_size):
  6.  
    super(Model_MLP_L2_V3, self).__init__()
  7.  
    # 构建第一个全连接层
  8.  
    self.fc1 = nn.Linear(input_size,hidden_size)
  9.  
    nn.init.normal_(tensor=self.fc1.weight,mean=0.0, std=0.01)
  10.  
    nn.init.constant_(tensor=self.fc1.bias,val=1.0)
  11.  
    # 构建第二全连接层
  12.  
    self.fc2 = nn.Linear(hidden_size,output_size)
  13.  
    nn.init.normal_(tensor=self.fc2.weight,mean=0.0, std=0.01)
  14.  
    nn.init.constant_(tensor=self.fc2.bias,val=1.0)
  15.  
    # 定义网络使用的激活函数
  16.  
    self.act = nn.Sigmoid()
  17.  
     
  18.  
    def forward(self, inputs):
  19.  
    outputs = self.fc1(inputs)
  20.  
    outputs = self.act(outputs)
  21.  
    outputs = self.fc2(outputs)
  22.  
    return outputs
  23.  
     
  24.  
    fnn_model = Model_MLP_L2_V3(input_size=4, output_size=3, hidden_size=6)
学新通

4.完善Runner类

基于RunnerV2类进行完善实现了RunnerV3类。其中训练过程使用自动梯度计算,使用Dataloader加载批量数据,使用随机梯度下降法进行参数优化;模型保存时,使用state_dict方法获取模型参数;模型加载时,使用set_state_dict方法加载此参数。

由于这里使用随机梯度下降法对参数优化,所以数据以批次的形式输入到模型中进行训练,那么评价指标计算也是分别在每个批次进行的,要想获得每个epoch整体的评价结果,需要对历史评价结果进行累积。这里定义Accuracy类实现该功能。

  1.  
    class Accuracy():
  2.  
    def __init__(self, is_logist=True):
  3.  
    """
  4.  
    输入:
  5.  
    - is_logist: outputs是logist还是激活后的值
  6.  
    """
  7.  
     
  8.  
    # 用于统计正确的样本个数
  9.  
    self.num_correct = 0
  10.  
    # 用于统计样本的总数
  11.  
    self.num_count = 0
  12.  
    self.is_logist = is_logist
  13.  
     
  14.  
    def update(self, outputs, labels):
  15.  
    """
  16.  
    输入:
  17.  
    - outputs: 预测值, shape=[N,class_num]
  18.  
    - labels: 标签值, shape=[N,1]
  19.  
    """
  20.  
     
  21.  
    # 判断是二分类任务还是多分类任务,shape[1]=1时为二分类任务,shape[1]>1时为多分类任务
  22.  
    if outputs.shape[1] == 1: # 二分类
  23.  
    outputs = torch.squeeze(outputs, dim=-1)
  24.  
    if self.is_logist:
  25.  
    # logist判断是否大于0
  26.  
    preds = torch.tensor((outputs >= 0), dtype=torch.float32)
  27.  
    else:
  28.  
    # 如果不是logist,判断每个概率值是否大于0.5,当大于0.5时,类别为1,否则类别为0
  29.  
    preds = torch.tensor((outputs >= 0.5), dtype=torch.float32)
  30.  
    else:
  31.  
    # 多分类时,使用'paddle.argmax'计算最大元素索引作为类别
  32.  
    preds = torch.argmax(outputs, dim=1)
  33.  
     
  34.  
    # 获取本批数据中预测正确的样本个数
  35.  
    labels = torch.squeeze(labels, dim=-1)
  36.  
    batch_correct = torch.sum(torch.tensor(preds == labels, dtype=torch.float32)).numpy()
  37.  
    batch_count = len(labels)
  38.  
     
  39.  
    # 更新num_correct 和 num_count
  40.  
    self.num_correct = batch_correct
  41.  
    self.num_count = batch_count
  42.  
     
  43.  
    def accumulate(self):
  44.  
    # 使用累计的数据,计算总的指标
  45.  
    if self.num_count == 0:
  46.  
    return 0
  47.  
    return self.num_correct / self.num_count
  48.  
     
  49.  
    def reset(self):
  50.  
    # 重置正确的数目和总数
  51.  
    self.num_correct = 0
  52.  
    self.num_count = 0
  53.  
     
  54.  
    def name(self):
  55.  
    return "Accuracy"
学新通

RunnerV3类的代码实现如下:

  1.  
    class RunnerV3(object):
  2.  
    def __init__(self, model, optimizer, loss_fn, metric, **kwargs):
  3.  
    self.model = model
  4.  
    self.optimizer = optimizer
  5.  
    self.loss_fn = loss_fn
  6.  
    self.metric = metric # 只用于计算评价指标
  7.  
     
  8.  
    # 记录训练过程中的评价指标变化情况
  9.  
    self.dev_scores = []
  10.  
     
  11.  
    # 记录训练过程中的损失函数变化情况
  12.  
    self.train_epoch_losses = [] # 一个epoch记录一次loss
  13.  
    self.train_step_losses = [] # 一个step记录一次loss
  14.  
    self.dev_losses = []
  15.  
    # 记录全局最优指标
  16.  
    self.best_score = 0
  17.  
     
  18.  
    def train(self, train_loader, dev_loader=None, **kwargs):
  19.  
    # 将模型切换为训练模式
  20.  
    self.model.train()
  21.  
    # 传入训练轮数,如果没有传入值则默认为0
  22.  
    num_epochs = kwargs.get("num_epochs", 0)
  23.  
    # 传入log打印频率,如果没有传入值则默认为100
  24.  
    log_steps = kwargs.get("log_steps", 100)
  25.  
    # 评价频率
  26.  
    eval_steps = kwargs.get("eval_steps", 0)
  27.  
    # 传入模型保存路径,如果没有传入值则默认为"best_model.pdparams"
  28.  
    save_path = kwargs.get("save_path", "best_model.pdparams")
  29.  
    custom_print_log = kwargs.get("custom_print_log", None)
  30.  
    # 训练总的步数
  31.  
    num_training_steps = num_epochs * len(train_loader)
  32.  
     
  33.  
    if eval_steps:
  34.  
    if self.metric is None:
  35.  
    raise RuntimeError('Error: Metric can not be None!')
  36.  
    if dev_loader is None:
  37.  
    raise RuntimeError('Error: dev_loader can not be None!')
  38.  
     
  39.  
    # 运行的step数目
  40.  
    global_step = 0
  41.  
     
  42.  
    # 进行num_epochs轮训练
  43.  
    for epoch in range(num_epochs):
  44.  
    # 用于统计训练集的损失
  45.  
    total_loss = 0
  46.  
    for step, data in enumerate(train_loader):
  47.  
    X, y = data
  48.  
    # 获取模型预测
  49.  
    logits = self.model(X)
  50.  
    loss = self.loss_fn(logits, y) # 默认求mean
  51.  
    total_loss = loss
  52.  
     
  53.  
    # 训练过程中,每个step的loss进行保存
  54.  
    self.train_step_losses.append((global_step, loss.item()))
  55.  
     
  56.  
    if log_steps and global_step % log_steps == 0:
  57.  
    print(
  58.  
    f"[Train] epoch: {epoch}/{num_epochs}, step: {global_step}/{num_training_steps}, loss: {loss.item():.5f}")
  59.  
     
  60.  
    # 梯度反向传播,计算每个参数的梯度值
  61.  
    loss.backward()
  62.  
     
  63.  
    if custom_print_log:
  64.  
    custom_print_log(self)
  65.  
     
  66.  
    # 小批量梯度下降进行参数更新
  67.  
    self.optimizer.step()
  68.  
    # 梯度归零
  69.  
    self.optimizer.zero_grad()
  70.  
     
  71.  
    # 判断是否需要评价
  72.  
    if eval_steps > 0 and global_step > 0 and \
  73.  
    (global_step % eval_steps == 0 or global_step == (num_training_steps - 1)):
  74.  
     
  75.  
    dev_score, dev_loss = self.evaluate(dev_loader, global_step=global_step)
  76.  
    print(f"[Evaluate] dev score: {dev_score:.5f}, dev loss: {dev_loss:.5f}")
  77.  
     
  78.  
    # 将模型切换为训练模式
  79.  
    self.model.train()
  80.  
     
  81.  
    # 如果当前指标为最优指标,保存该模型
  82.  
    if dev_score > self.best_score:
  83.  
    self.save_model(save_path)
  84.  
    print(
  85.  
    f"[Evaluate] best accuracy performence has been updated: {self.best_score:.5f} --> {dev_score:.5f}")
  86.  
    self.best_score = dev_score
  87.  
     
  88.  
    global_step = 1
  89.  
     
  90.  
    # 当前epoch 训练loss累计值
  91.  
    trn_loss = (total_loss / len(train_loader)).item()
  92.  
    # epoch粒度的训练loss保存
  93.  
    self.train_epoch_losses.append(trn_loss)
  94.  
     
  95.  
    print("[Train] Training done!")
  96.  
     
  97.  
    # 模型评估阶段,使用'paddle.no_grad()'控制不计算和存储梯度
  98.  
    @torch.no_grad()
  99.  
    def evaluate(self, dev_loader, **kwargs):
  100.  
    assert self.metric is not None
  101.  
    # 将模型设置为评估模式
  102.  
    self.model.eval()
  103.  
    global_step = kwargs.get("global_step", -1)
  104.  
    # 用于统计训练集的损失
  105.  
    total_loss = 0
  106.  
    # 重置评价
  107.  
    self.metric.reset()
  108.  
     
  109.  
    # 遍历验证集每个批次
  110.  
    for batch_id, data in enumerate(dev_loader):
  111.  
    X, y = data
  112.  
    # 计算模型输出
  113.  
    logits = self.model(X)
  114.  
    # 计算损失函数
  115.  
    loss = self.loss_fn(logits, y).item()
  116.  
    # 累积损失
  117.  
    total_loss = loss
  118.  
    # 累积评价
  119.  
    self.metric.update(logits, y)
  120.  
     
  121.  
    dev_loss = (total_loss / len(dev_loader))
  122.  
    dev_score = self.metric.accumulate()
  123.  
     
  124.  
    # 记录验证集loss
  125.  
    if global_step != -1:
  126.  
    self.dev_losses.append((global_step, dev_loss))
  127.  
    self.dev_scores.append(dev_score)
  128.  
     
  129.  
    return dev_score, dev_loss
  130.  
     
  131.  
    # 模型评估阶段,使用'paddle.no_grad()'控制不计算和存储梯度
  132.  
    @torch.no_grad()
  133.  
    def predict(self, x, **kwargs):
  134.  
    # 将模型设置为评估模式
  135.  
    self.model.eval()
  136.  
    # 运行模型前向计算,得到预测值
  137.  
    logits = self.model(x)
  138.  
    return logits
  139.  
     
  140.  
    def save_model(self, save_path):
  141.  
    torch.save(self.model.state_dict(), save_path)
  142.  
     
  143.  
    def load_model(self, model_path):
  144.  
    model_state_dict = torch.load(model_path)
  145.  
    self.model.set_state_dict(model_state_dict)
学新通

5.模型训练

实例化RunnerV3类,并传入训练配置,代码实现如下:

  1.  
    lr = 0.2
  2.  
     
  3.  
    # 定义网络
  4.  
    model = fnn_model
  5.  
    # 定义优化器
  6.  
    optimizer = opt.SGD(lr=lr, params=model.parameters())
  7.  
    # 定义损失函数。softmax 交叉熵
  8.  
    loss_fn = F.cross_entropy
  9.  
    # 定义评价指标
  10.  
    metric = Accuracy(is_logist=True)
  11.  
     
  12.  
    runner = RunnerV3(model, optimizer, loss_fn, metric)

使用训练集和验证集进行模型训练,共训练150个epoch。在实验中,保存准确率最高的模型作为最佳模型。代码实现如下:

  1.  
    # 启动训练
  2.  
    log_steps = 100
  3.  
    eval_steps = 50
  4.  
    runner.train(train_loader, dev_loader,
  5.  
    num_epochs=150, log_steps=log_steps, eval_steps = eval_steps,
  6.  
    save_path="best_model.pdparams")

训练结果:

  1.  
    [Train] epoch: 0/150, step: 0/1200, loss: 1.10467
  2.  
    [Evaluate] dev score: 0.26667, dev loss: 1.22231
  3.  
    [Evaluate] best accuracy performence has been updated: 0.00000 --> 0.26667
  4.  
    [Train] epoch: 12/150, step: 100/1200, loss: 1.11286
  5.  
    [Evaluate] dev score: 0.26667, dev loss: 1.11308
  6.  
    [Evaluate] dev score: 0.26667, dev loss: 1.13753
  7.  
    [Train] epoch: 25/150, step: 200/1200, loss: 1.11455
  8.  
    [Evaluate] dev score: 0.26667, dev loss: 1.12687
  9.  
    [Evaluate] dev score: 0.26667, dev loss: 1.11112
  10.  
    [Train] epoch: 37/150, step: 300/1200, loss: 1.07319
  11.  
    [Evaluate] dev score: 0.26667, dev loss: 1.09228
  12.  
    [Evaluate] dev score: 0.26667, dev loss: 1.07941
  13.  
    [Train] epoch: 50/150, step: 400/1200, loss: 0.96820
  14.  
    [Evaluate] dev score: 0.53333, dev loss: 1.02344
  15.  
    [Evaluate] best accuracy performence has been updated: 0.26667 --> 0.53333
  16.  
    [Evaluate] dev score: 0.53333, dev loss: 0.93751
  17.  
    [Train] epoch: 62/150, step: 500/1200, loss: 0.72586
  18.  
    [Evaluate] dev score: 0.60000, dev loss: 0.77080
  19.  
    [Evaluate] best accuracy performence has been updated: 0.53333 --> 0.60000
  20.  
    [Evaluate] dev score: 0.66667, dev loss: 0.67423
  21.  
    [Evaluate] best accuracy performence has been updated: 0.60000 --> 0.66667
  22.  
    [Train] epoch: 75/150, step: 600/1200, loss: 0.53801
  23.  
    [Evaluate] dev score: 0.86667, dev loss: 0.57472
  24.  
    [Evaluate] best accuracy performence has been updated: 0.66667 --> 0.86667
  25.  
    [Evaluate] dev score: 0.80000, dev loss: 0.56176
  26.  
    [Train] epoch: 87/150, step: 700/1200, loss: 0.38122
  27.  
    [Evaluate] dev score: 0.86667, dev loss: 0.50781
  28.  
    [Evaluate] dev score: 0.93333, dev loss: 0.46224
  29.  
    [Evaluate] best accuracy performence has been updated: 0.86667 --> 0.93333
  30.  
    [Train] epoch: 100/150, step: 800/1200, loss: 0.43660
  31.  
    [Evaluate] dev score: 0.93333, dev loss: 0.43534
  32.  
    [Evaluate] dev score: 1.00000, dev loss: 0.40225
  33.  
    [Evaluate] best accuracy performence has been updated: 0.93333 --> 1.00000
  34.  
    [Train] epoch: 112/150, step: 900/1200, loss: 0.33995
  35.  
    [Evaluate] dev score: 0.93333, dev loss: 0.38908
  36.  
    [Evaluate] dev score: 0.93333, dev loss: 0.36191
  37.  
    [Train] epoch: 125/150, step: 1000/1200, loss: 0.28941
  38.  
    [Evaluate] dev score: 1.00000, dev loss: 0.33070
  39.  
    [Evaluate] dev score: 1.00000, dev loss: 0.31569
  40.  
    [Train] epoch: 137/150, step: 1100/1200, loss: 0.27491
  41.  
    [Evaluate] dev score: 1.00000, dev loss: 0.30810
  42.  
    [Evaluate] dev score: 1.00000, dev loss: 0.28763
  43.  
    [Evaluate] dev score: 1.00000, dev loss: 0.26681
  44.  
    [Train] Training done!
学新通

可视化观察训练集损失和训练集loss变化情况。

  1.  
    # 绘制训练集和验证集的损失变化以及验证集上的准确率变化曲线
  2.  
    def plot_training_loss_acc(runner, fig_name,
  3.  
    fig_size=(16, 6),
  4.  
    sample_step=20,
  5.  
    loss_legend_loc="upper right",
  6.  
    acc_legend_loc="lower right",
  7.  
    train_color="#8E004D",
  8.  
    dev_color='#E20079',
  9.  
    fontsize='x-large',
  10.  
    train_linestyle="-",
  11.  
    dev_linestyle='--'):
  12.  
    plt.figure(figsize=fig_size)
  13.  
    plt.subplot(1, 2, 1)
  14.  
    train_items = runner.train_step_losses[::sample_step]
  15.  
    train_steps = [x[0] for x in train_items]
  16.  
    train_losses = [x[1] for x in train_items]
  17.  
     
  18.  
    plt.plot(train_steps, train_losses, color=train_color, linestyle=train_linestyle, label="Train loss")
  19.  
    if len(runner.dev_losses) > 0:
  20.  
    dev_steps = [x[0] for x in runner.dev_losses]
  21.  
    dev_losses = [x[1] for x in runner.dev_losses]
  22.  
    plt.plot(dev_steps, dev_losses, color=dev_color, linestyle=dev_linestyle, label="Dev loss")
  23.  
    # 绘制坐标轴和图例
  24.  
    plt.ylabel("loss", fontsize=fontsize)
  25.  
    plt.xlabel("step", fontsize=fontsize)
  26.  
    plt.legend(loc=loss_legend_loc, fontsize=fontsize)
  27.  
     
  28.  
    # 绘制评价准确率变化曲线
  29.  
    if len(runner.dev_scores) > 0:
  30.  
    plt.subplot(1, 2, 2)
  31.  
    plt.plot(dev_steps, runner.dev_scores,
  32.  
    color=dev_color, linestyle=dev_linestyle, label="Dev accuracy")
  33.  
     
  34.  
    # 绘制坐标轴和图例
  35.  
    plt.ylabel("score", fontsize=fontsize)
  36.  
    plt.xlabel("step", fontsize=fontsize)
  37.  
    plt.legend(loc=acc_legend_loc, fontsize=fontsize)
  38.  
     
  39.  
    plt.savefig(fig_name)
  40.  
    plt.show()
  41.  
     
  42.  
    plot_training_loss_acc(runner, 'fw-loss.pdf')
学新通

学新通

准确率随着迭代次数增加逐渐上升,损失函数下降,最终准确率趋近于1。

6.模型评价

使用测试数据对在训练过程中保存的最佳模型进行评价,观察模型在测试集上的准确率以及Loss情况。

  1.  
    score, loss = runner.evaluate(test_loader)
  2.  
    print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))

执行结果:

[Test] accuracy/loss: 1.0000/0.1675

7.模型预测

同样地,也可以使用保存好的模型,对测试集中的某一个数据进行模型预测,观察模型效果。

  1.  
    test_loader = iter(test_loader)
  2.  
    # 获取测试集中第一条数据
  3.  
    (X, label) = next(test_loader)
  4.  
    logits = runner.predict(X)
  5.  
    pred_class = torch.argmax(logits[0]).numpy()
  6.  
    label = label.numpy()[0]
  7.  
     
  8.  
    # 输出真实类别与预测类别
  9.  
    print("The true category is {} and the predicted category is {}".format(label, pred_class))
The true category is 2 and the predicted category is 2

三、思考题

对比Softmax分类和前馈神经网络分类。

Softmax分类:

  1.  
    X = iris["data"][:, (2, 3)] # 花瓣长度, 花瓣宽度
  2.  
    y = iris["target"]
  3.  
    # 设置超参数multi_class"multinomial",指定一个支持Softmax回归的求解器,默认使用l2正则化,可以通过超参数C进行控制
  4.  
    softmax_reg = LogisticRegression(multi_class="multinomial", solver="lbfgs", C=5, random_state=42)
  5.  
    softmax_reg.fit(X, y)
  6.  
     
  7.  
    softmax_reg.predict([[5, 2]]) # 输出:array([2])
  8.  
    softmax_reg.predict_proba([[5, 2]])
  9.  
     
  10.  
    x0, x1 = np.meshgrid(
  11.  
    np.linspace(0, 8, 500).reshape(-1, 1),
  12.  
    np.linspace(0, 3.5, 200).reshape(-1, 1),
  13.  
    )
  14.  
    X_new = np.c_[x0.ravel(), x1.ravel()]
  15.  
     
  16.  
    y_proba = softmax_reg.predict_proba(X_new)
  17.  
    y_predict = softmax_reg.predict(X_new)
  18.  
     
  19.  
    zz1 = y_proba[:, 1].reshape(x0.shape)
  20.  
    zz = y_predict.reshape(x0.shape)
  21.  
     
  22.  
    plt.figure(figsize=(8, 3))
  23.  
    plt.plot(X[y == 2, 0], X[y == 2, 1], "g^", label="Iris virginica")
  24.  
    plt.plot(X[y == 1, 0], X[y == 1, 1], "bs", label="Iris versicolor")
  25.  
    plt.plot(X[y == 0, 0], X[y == 0, 1], "yo", label="Iris setosa")
  26.  
     
  27.  
    from matplotlib.colors import ListedColormap
  28.  
     
  29.  
    custom_cmap = ListedColormap(['#fafab0', '#9898ff', '#a0faa0'])
  30.  
    plt.contourf(x0, x1, zz, cmap=custom_cmap)
  31.  
    plt.clabel(contour, inline=1, fontsize=10)
  32.  
    plt.xlabel("Petal length", fontsize=13)
  33.  
    plt.ylabel("Petal width", fontsize=13)
  34.  
    plt.legend(loc="center left", fontsize=13)
  35.  
    plt.axis([0, 7, 0, 3.5])
  36.  
    plt.title('C=5')
  37.  
    plt.show()
学新通

当C=1时:

学新通

当C=10时:

学新通 当C=100时:

学新通

 刚刚的前馈神经网络分类:

学新通

       虽然显示的方式不同但对比就可以发现由于鸢尾花数据集的完美性,两个分类方法的拟合结果都很成功。其中Softmax是以误差函数最小为优化目标,是一种严格数学推导的逻辑回归分类器,相比前馈神经网络较为简单,结果更加直观。而前馈神经网络,既然是神经网络就是仿照我们人类的神经通过激活函数和各种偏置需要大量的参数,网络拓扑结构、权值和阈值的初始值等等,也就使得学习时间长,较为复杂,但由于它的这些难点也使得它的分类能力更强。

四、总结

学新通

 这是下面博客中的一张思维导图,我觉得总结得很好。

训练学习的开始就是数据,所以说数据是学习的开始就恰当,数据决定了参数的值,最后也是数据反应了模型的泛化能力,训练的准确率。损失函数也是神经网络中很重要的一部分,也是衡量模型拟合的参照,用的比较多的就是均方误差和交叉熵损失。数值微分和梯度也是计算中不可或缺的一环,有很多如权重函数的梯度更新等等。

学习到这里前馈神经网络已经掌握得差不多了,这次实验也全面分析了softmax和前馈神经网络对鸢尾花的分类情况,收获也很大。

ref:

NNDL 实验4(下) - HBU_DAVID - 博客园

2.5. 自动微分 — 动手学深度学习 2.0.0-beta1 documentation

4.7. 前向传播、反向传播和计算图 — 动手学深度学习 2.0.0-beta1 documentation

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhhkbbak
系列文章
更多 icon
同类精品
更多 icon
继续加载