• 【深度学习实验】前馈神经网络(final):自定义鸢尾花分类前馈神经网络模型并进行训练及评价


    目录

    一、实验介绍

     二、实验环境

    1. 配置虚拟环境

    2. 库版本介绍

    三、实验内容

    0. 导入必要的工具包

    1. 构建数据集(IrisDataset)

    2. 构建模型(FeedForward)

    a. __init__(初始化)

    b. forward(前向传播)

    3.整合训练、评估、预测过程(Runner)

    4. 模型评价(Accuracy)

    5. __main__

    6. 代码整合


     

     

     

    一、实验介绍

            鸢尾花分类(通过输入鸢尾花的特征信息,预测鸢尾花的种类)是一个常见的机器学习问题。本次实验旨在使用PyTorch构建一个简单的鸢尾花分类前馈神经网络模型,并进行训练和评价,以了解PyTorch的基本使用方法和模型训练过程。

     

     二、实验环境

        本系列实验使用了PyTorch深度学习框架,相关操作如下:

    1. 配置虚拟环境

    conda create -n DL python=3.7 
    conda activate DL
    pip install torch==1.8.1+cu102 torchvision==0.9.1+cu102 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
    
    conda install matplotlib
     conda install scikit-learn

    2. 库版本介绍

    软件包本实验版本目前最新版
    matplotlib3.5.33.8.0
    numpy1.21.61.26.0
    python3.7.16 
    scikit-learn0.22.11.3.0
    torch1.8.1+cu1022.0.1
    torchaudio0.8.12.0.2
    torchvision0.9.1+cu1020.15.2

     

    三、实验内容

    ChatGPT:

            前馈神经网络(Feedforward Neural Network)是一种常见的人工神经网络模型,也被称为多层感知器(Multilayer Perceptron,MLP)。它是一种基于前向传播的模型,主要用于解决分类和回归问题。

            前馈神经网络由多个层组成,包括输入层、隐藏层和输出层。它的名称"前馈"源于信号在网络中只能向前流动,即从输入层经过隐藏层最终到达输出层,没有反馈连接。

    以下是前馈神经网络的一般工作原理:

    1. 输入层:接收原始数据或特征向量作为网络的输入,每个输入被表示为网络的一个神经元。每个神经元将输入加权并通过激活函数进行转换,产生一个输出信号。

    2. 隐藏层:前馈神经网络可以包含一个或多个隐藏层,每个隐藏层由多个神经元组成。隐藏层的神经元接收来自上一层的输入,并将加权和经过激活函数转换后的信号传递给下一层。

    3. 输出层:最后一个隐藏层的输出被传递到输出层,输出层通常由一个或多个神经元组成。输出层的神经元根据要解决的问题类型(分类或回归)使用适当的激活函数(如Sigmoid、Softmax等)将最终结果输出。

    4. 前向传播:信号从输入层通过隐藏层传递到输出层的过程称为前向传播。在前向传播过程中,每个神经元将前一层的输出乘以相应的权重,并将结果传递给下一层。这样的计算通过网络中的每一层逐层进行,直到产生最终的输出。

    5. 损失函数和训练:前馈神经网络的训练过程通常涉及定义一个损失函数,用于衡量模型预测输出与真实标签之间的差异。常见的损失函数包括均方误差(Mean Squared Error)和交叉熵(Cross-Entropy)。通过使用反向传播算法(Backpropagation)和优化算法(如梯度下降),网络根据损失函数的梯度进行参数调整,以最小化损失函数的值。

            前馈神经网络的优点包括能够处理复杂的非线性关系,适用于各种问题类型,并且能够通过训练来自动学习特征表示。然而,它也存在一些挑战,如容易过拟合、对大规模数据和高维数据的处理较困难等。为了应对这些挑战,一些改进的网络结构和训练技术被提出,如卷积神经网络(Convolutional Neural Networks)和循环神经网络(Recurrent Neural Networks)等。

    本系列为实验内容,对理论知识不进行详细阐释

    (咳咳,其实是没时间整理,待有缘之时,回来填坑)

    977468b5ae9843c6a88005e792817cb1.png

     

    0. 导入必要的工具包

    1. import torch
    2. from torch import nn
    3. import torch.nn.functional as F
    4. # 绘画时使用的工具包
    5. import matplotlib.pyplot as plt
    6. # 导入鸢尾花数据集
    7. from sklearn.datasets import load_iris
    8. # 构建自己的数据集,继承自Dataset类
    9. from torch.utils.data import Dataset, DataLoader

     

    1. 构建数据集(IrisDataset)

    • 本实验使用了鸢尾花数据集,该数据集包含150个样本,每个样本有4个特征和1个标签。
    • load_iris函数加载数据集,并进行数据归一化处理;
    • 自定义的IrisDataset类用于数据加载,构建训练集、验证集和测试集。

    【深度学习实验】前馈神经网络(七):批量加载数据(直接加载数据→定义类封装数据)_QomolangmaH的博客-CSDN博客https://blog.csdn.net/m0_63834988/article/details/133181882?spm=1001.2014.3001.5501

     

    2. 构建模型(FeedForward)

           本实验构建了一个简单的两层前馈神经网络。这个前馈神经网络和前文实现的MLP类最大的区别在于,我们实现类中使用了自己写的激活函数,该激活函数不能通过反向传播更新参数,但深度学习框架已经帮我们完成了这个功能。(其实通过简单的改动,我们的激活函数也可以反传梯度)

    【深度学习实验】前馈神经网络(三):自定义多层感知机(激活函数logistic、线性层算Linear)_QomolangmaH的博客-CSDN博客https://blog.csdn.net/m0_63834988/article/details/133097102?spm=1001.2014.3001.5501

    1. class FeedForward(nn.Module):
    2. def __init__(self, input_size, hidden_size, output_size):
    3. super(FeedForward,self).__init__()
    4. self.fc1 = nn.Linear(input_size, hidden_size)
    5. self.fc2 = nn.Linear(hidden_size, output_size)
    6. self.act = nn.Sigmoid()
    7. def forward(self, inputs):
    8. outputs = self.fc1(inputs)
    9. outputs = self.act(outputs)
    10. outputs = self.fc2(outputs)
    11. return outputs

    a. __init__(初始化)

    • 三个参数:
      • input_size(输入大小)
      • hidden_size(隐藏层大小)
      • output_size(输出大小)
    • 调用父类nn.Module的初始化方法super(FeedForward, self).init(),确保正确地初始化该类作为一个nn.Module。
    • 两个线性层self.fc1和self.fc2:
      • self.fc1的输入大小为input_size,输出大小为hidden_size;
      • self.fc2的输入大小为hidden_size,输出大小为output_size。
    • 一个激活函数self.act,这里使用的是nn.Sigmoid(),即Sigmoid激活函数。

    b. forward(前向传播)

    • 接受一个输入张量inputs。在前向传播过程中,
      • 输入经过self.fc1线性层,
      • 然后通过self.act激活函数进行非线性变换,
      • 再经过self.fc2线性层得到最终的输出张量outputs

     

    3.整合训练、评估、预测过程(Runner)

            Runner类封装了模型训练和评价的过程。

    • 初始化函数接收模型、优化器、损失函数和评价指标等参数,并定义了一些成员变量用于记录训练过程中的损失和评价指标变化。
    • train函数进行模型的训练
    • evaluate函数进行模型的评价
    • predict函数进行模型的预测
    • save_model和load_model函数用于保存和加载模型的参数

    【深度学习实验】前馈神经网络(九):整合训练、评估、预测过程(Runner)_QomolangmaH的博客-CSDN博客https://blog.csdn.net/m0_63834988/article/details/133219448?spm=1001.2014.3001.5501

    4. 模型评价(Accuracy)

     

    【深度学习实验】前馈神经网络(八):模型评价(自定义支持分批进行评价的Accuracy类)_QomolangmaH的博客-CSDN博客https://blog.csdn.net/m0_63834988/article/details/133186305?spm=1001.2014.3001.5501

     

    5. __main__

    1. if __name__ == '__main__':
    2. batch_size = 16
    3. # 分别构建训练集、验证集和测试集
    4. train_dataset = IrisDataset(mode='train')
    5. dev_dataset = IrisDataset(mode='dev')
    6. test_dataset = IrisDataset(mode='test')
    7. train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
    8. dev_loader = DataLoader(dev_dataset, batch_size=batch_size)
    9. test_loader = DataLoader(test_dataset, batch_size=1, shuffle=True)
    10. input_size = 4
    11. output_size = 3
    12. hidden_size = 6
    13. # 定义模型
    14. model = FeedForward(input_size, hidden_size, output_size)
    15. # 定义损失函数
    16. loss_fn = F.cross_entropy
    17. # 定义优化器
    18. optimizer = torch.optim.SGD(model.parameters(), lr=0.2)
    19. # 定义评价方法
    20. metric = Accuracy(is_logist=True)
    21. # 实例化辅助runner类
    22. runner = Runner(model, optimizer, loss_fn, metric)
    23. # 模型训练
    24. runner.train(train_loader, dev_loader, num_epochs=50, log_steps=10, eval_steps=5)
    25. # 训练结束后,网络的参数会自动保存为.pth结尾的文件,且与训练文件在同一目录下
    26. model_path = 'model_25.pth'
    27. # 首先读入经过训练后的网络的参数
    28. runner.load_model(model_path)
    29. x, label = next(iter(test_loader))
    30. print(runner.predict(x.float()))
    31. print(label)

     

    • batch_size = 16 设置批大小,用于数据加载器;

    • 构建训练集、验证集和测试集的数据集对象;

    • 创建数据加载器;

    • 设置模型的输入大小、输出大小和隐藏层大小:

      • input_size = 4 输入大小为4,对应于鸢尾花数据集的特征数量。
      • output_size = 3 输出大小为3,对应于鸢尾花数据集的类别数量。
      • hidden_size = 6 隐藏层大小为6,作为前馈神经网络模型的隐藏层的单元数。
    • 定义模型、损失函数、优化器和评价指标:

      • 前馈神经网络模型:使用FeedForward类,设置输入、隐藏层和输出大小。
      • 损失函数:使用交叉熵损失函数F.cross_entropy
      • 优化器:使用随机梯度下降法(SGD),学习率为0.2。
      • 评价指标:使用Accuracy类。
    • 创建辅助的Runner类对象用于训练和评价模型。

    • 进行模型训练:

      • 设置训练数据加载器为train_loader,验证数据加载器为dev_loader,训练的轮次为50,打印日志的频率为每10步打印一次日志,评价的频率为每5个epoch进行一次评价。
    • 训练结束后,通过调用runner.load_model(model_path)方法加载模型的参数

    • 构造一个测试样本 x 和标签 label,调用 runner.predict(x.float()) 方法对样本进行预测,并输出预测结果。随后输出真实标签 label

     

    6. 代码整合

    1. # 导入必要的工具包
    2. import torch
    3. from torch import nn
    4. import torch.nn.functional as F
    5. # 绘画时使用的工具包
    6. import matplotlib.pyplot as plt
    7. # 导入鸢尾花数据集
    8. from sklearn.datasets import load_iris
    9. # 构建自己的数据集,继承自Dataset类
    10. from torch.utils.data import Dataset, DataLoader
    11. # 此函数用于加载鸢尾花数据集
    12. def load_data(shuffle=True):
    13. x = torch.tensor(load_iris().data)
    14. y = torch.tensor(load_iris().target)
    15. # 数据归一化
    16. x_min = torch.min(x, dim=0).values
    17. x_max = torch.max(x, dim=0).values
    18. x = (x - x_min) / (x_max - x_min)
    19. if shuffle:
    20. idx = torch.randperm(x.shape[0])
    21. x = x[idx]
    22. y = y[idx]
    23. return x, y
    24. class IrisDataset(Dataset):
    25. def __init__(self, mode='train', num_train=120, num_dev=15):
    26. super(IrisDataset, self).__init__()
    27. x, y = load_data(shuffle=True)
    28. if mode == 'train':
    29. self.x, self.y = x[:num_train], y[:num_train]
    30. elif mode == 'dev':
    31. self.x, self.y = x[num_train:num_train + num_dev], y[num_train:num_train + num_dev]
    32. else:
    33. self.x, self.y = x[num_train + num_dev:], y[num_train + num_dev:]
    34. def __getitem__(self, idx):
    35. return self.x[idx], self.y[idx]
    36. def __len__(self):
    37. return len(self.x)
    38. class FeedForward(nn.Module):
    39. def __init__(self, input_size, hidden_size, output_size):
    40. super(FeedForward, self).__init__()
    41. self.fc1 = nn.Linear(input_size, hidden_size)
    42. self.fc2 = nn.Linear(hidden_size, output_size)
    43. self.act = nn.Sigmoid()
    44. def forward(self, inputs):
    45. outputs = self.fc1(inputs)
    46. outputs = self.act(outputs)
    47. outputs = self.fc2(outputs)
    48. return outputs
    49. # 支持分批进行模型评价的 Accuracy 类
    50. class Accuracy:
    51. def __init__(self, is_logist=True):
    52. # 正确样本个数
    53. self.num_correct = 0
    54. # 样本总数
    55. self.num_count = 0
    56. self.is_logist = is_logist
    57. def update(self, outputs, labels):
    58. # 判断是否为二分类任务
    59. if outputs.shape[1] == 1:
    60. outputs = outputs.squeeze(-1)
    61. # 判断是否是logit形式的预测值
    62. if self.is_logist:
    63. preds = (outputs >= 0).long()
    64. else:
    65. preds = (outputs >= 0.5).long()
    66. else:
    67. # 多分类任务时,计算最大元素索引作为类别
    68. preds = torch.argmax(outputs, dim=1).long()
    69. # 获取本批数据中预测正确的样本个数
    70. labels = labels.squeeze(-1)
    71. batch_correct = (preds == labels).float().sum()
    72. batch_count = len(labels)
    73. # 更新
    74. self.num_correct += batch_correct
    75. self.num_count += batch_count
    76. def accumulate(self):
    77. # 使用累计的数据,计算总的评价指标
    78. if self.num_count == 0:
    79. return 0
    80. return self.num_correct / self.num_count
    81. def reset(self):
    82. self.num_correct = 0
    83. self.num_count = 0
    84. class Runner(object):
    85. def __init__(self, model, optimizer, loss_fn, metric, **kwargs):
    86. self.model = model
    87. self.optimizer = optimizer
    88. self.loss_fn = loss_fn
    89. # 用于计算评价指标
    90. self.metric = metric
    91. # 记录训练过程中的评价指标变化
    92. self.dev_scores = []
    93. # 记录训练过程中的损失变化
    94. self.train_epoch_losses = []
    95. self.dev_losses = []
    96. # 记录全局最优评价指标
    97. self.best_score = 0
    98. # 模型训练阶段
    99. def train(self, train_loader, dev_loader=None, **kwargs):
    100. # 将模型设置为训练模式,此时模型的参数会被更新
    101. self.model.train()
    102. num_epochs = kwargs.get('num_epochs', 0)
    103. log_steps = kwargs.get('log_steps', 100)
    104. save_path = kwargs.get('save_path', 'best_mode.pth')
    105. eval_steps = kwargs.get('eval_steps', 0)
    106. # 运行的step数,不等于epoch数
    107. global_step = 0
    108. if eval_steps:
    109. if dev_loader is None:
    110. raise RuntimeError('Error: dev_loader can not be None!')
    111. if self.metric is None:
    112. raise RuntimeError('Error: Metric can not be None')
    113. # 遍历训练的轮数
    114. for epoch in range(num_epochs):
    115. total_loss = 0
    116. # 遍历数据集
    117. for step, data in enumerate(train_loader):
    118. x, y = data
    119. logits = self.model(x.float())
    120. loss = self.loss_fn(logits, y.long())
    121. total_loss += loss
    122. if log_steps and global_step % log_steps == 0:
    123. print(f'loss:{loss.item():.5f}')
    124. loss.backward()
    125. self.optimizer.step()
    126. self.optimizer.zero_grad()
    127. # 每隔一定轮次进行一次验证,由eval_steps参数控制,可以采用不同的验证判断条件
    128. if (epoch + 1) % eval_steps == 0:
    129. dev_score, dev_loss = self.evaluate(dev_loader, global_step=global_step)
    130. print(f'[Evalute] dev score:{dev_score:.5f}, dev loss:{dev_loss:.5f}')
    131. if dev_score > self.best_score:
    132. self.save_model(f'model_{epoch + 1}.pth')
    133. print(
    134. f'[Evaluate]best accuracy performance has been updated: {self.best_score:.5f}-->{dev_score:.5f}')
    135. self.best_score = dev_score
    136. # 验证过程结束后,请记住将模型调回训练模式
    137. self.model.train()
    138. global_step += 1
    139. # 保存当前轮次训练损失的累计值
    140. train_loss = (total_loss / len(train_loader)).item()
    141. self.train_epoch_losses.append((global_step, train_loss))
    142. print('[Train] Train done')
    143. # 模型评价阶段
    144. def evaluate(self, dev_loader, **kwargs):
    145. assert self.metric is not None
    146. # 将模型设置为验证模式,此模式下,模型的参数不会更新
    147. self.model.eval()
    148. global_step = kwargs.get('global_step', -1)
    149. total_loss = 0
    150. self.metric.reset()
    151. for batch_id, data in enumerate(dev_loader):
    152. x, y = data
    153. logits = self.model(x.float())
    154. loss = self.loss_fn(logits, y.long()).item()
    155. total_loss += loss
    156. self.metric.update(logits, y)
    157. dev_loss = (total_loss / len(dev_loader))
    158. self.dev_losses.append((global_step, dev_loss))
    159. dev_score = self.metric.accumulate()
    160. self.dev_scores.append(dev_score)
    161. return dev_score, dev_loss
    162. # 模型预测阶段,
    163. def predict(self, x, **kwargs):
    164. self.model.eval()
    165. logits = self.model(x)
    166. return logits
    167. # 保存模型的参数
    168. def save_model(self, save_path):
    169. torch.save(self.model.state_dict(), save_path)
    170. # 读取模型的参数
    171. def load_model(self, model_path):
    172. self.model.load_state_dict(torch.load(model_path, map_location=torch.device('cpu')))
    173. if __name__ == '__main__':
    174. batch_size = 16
    175. # 分别构建训练集、验证集和测试集
    176. train_dataset = IrisDataset(mode='train')
    177. dev_dataset = IrisDataset(mode='dev')
    178. test_dataset = IrisDataset(mode='test')
    179. train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
    180. dev_loader = DataLoader(dev_dataset, batch_size=batch_size)
    181. test_loader = DataLoader(test_dataset, batch_size=1, shuffle=True)
    182. input_size = 4
    183. output_size = 3
    184. hidden_size = 6
    185. # 定义模型
    186. model = FeedForward(input_size, hidden_size, output_size)
    187. # 定义损失函数
    188. loss_fn = F.cross_entropy
    189. # 定义优化器
    190. optimizer = torch.optim.SGD(model.parameters(), lr=0.2)
    191. # 定义评价方法
    192. metric = Accuracy(is_logist=True)
    193. # 实例化辅助runner类
    194. runner = Runner(model, optimizer, loss_fn, metric)
    195. # 模型训练
    196. runner.train(train_loader, dev_loader, num_epochs=50, log_steps=10, eval_steps=5)
    197. # 训练结束后,网络的参数会自动保存为.pth结尾的文件,且与训练文件在同一目录下
    198. model_path = 'model_25.pth'
    199. # 首先读入经过训练后的网络的参数
    200. runner.load_model(model_path)
    201. x, label = next(iter(test_loader))
    202. print(runner.predict(x.float()))
    203. print(label)

     

     

     

  • 相关阅读:
    “蔚来杯“2022牛客暑期多校训练营9 补题题解(A)
    adb详细教程(四)-使用adb启动应用、关闭应用、清空应用数据、获取设备已安装应用列表
    BigDecimal四大误区
    OpenCV——图像细化算法
    重返ubuntu世界
    精心整理了优秀的GitHub开源项目,包含前端、后端、AI人工智能、游戏、黑客工具、网络工具、AI医疗等等,空闲的时候方便看看提高自己的视野
    Ovalbumin-tyrosine 鸡卵白蛋白修饰络氨酸,tyrosine-PEG-OVA 络氨酸-聚乙二醇-卵清蛋白
    《JavaScript悟道》——阅读笔记
    Verasity Tokenomics — 社区讨论总结与下一步计划
    javascript复习 8.从0-1 彻底理解new
  • 原文地址:https://blog.csdn.net/m0_63834988/article/details/132891131