Aclicee 发表于 2024-9-6 00:43

《人工智能实践教程——从Python入门到机器学习》阅读报告(3)

<div class='showpostmsg'> 本帖最后由 Aclicee 于 2024-9-6 00:42 编辑

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本书的第三部分,作者深入探讨了神经网络的构建与应用,这一部分是全书的精华所在。以下是对书中核心内容的简要介绍:</p>

<ul>
        <li><strong>感知机</strong>:作者首先从感知机的原理出发,详细阐述了其从逻辑电路到多层神经网络的演变过程。书中不仅介绍了基础的激活函数,还深入探讨了如何通过这些函数构建更为复杂的神经网络结构;</li>
        <li>
        <p><strong>反向传播算法</strong>:通过链式法则和计算图,作者清晰地推导了反向传播算法的理论基础。书中不仅涵盖了常见的激活函数,还特别介绍了Softmax等复杂函数的推导过程,为读者揭示了误差反向传播在神经网络学习中的核心作用;</p>
        </li>
        <li>
        <p><strong>训练方法</strong>:在优化器的选择上,作者提供了SGD、Momentum、AdaGrad和Adam等多种算法的比较。这一部分可以参考李宏毅老师的课程,其中有一些动态图像会提供更直观的理解,帮助读者把握这些方法之间的差异;</p>
        </li>
        <li>
        <p><strong>训练的优化</strong>:书中对网络参数初始化、批量归一化、正则化以及超参数选择等训练优化技术进行了深入分析。虽然这些内容在实际操作中可能需要大量的试错,但作者的理论分析为读者提供了坚实的基础;</p>
        </li>
        <li>
        <p><strong>卷积网络(CNN)</strong>:作者介绍了卷积神经网络(CNN)的原理和优势,并以图像处理中的应用案例为例,展示了CNN的强大能力。尽管我的研究领域并非图像处理,但我仍然能够利用CNN的特性来丰富我的项目实践。</p>
        </li>
</ul>

<p><u><strong>对这一部分的总体评价:</strong></u></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;总体来说,这部分内容是全面的,尤其是在理论推导方面,作者的详细讲解极大地提升了阅读兴趣。书中对于训练过程的介绍,从优化器的选择到网络优化方法,都配有清晰的公式和示例代码,极大地方便了读者的理解和学习。然而,书中对于计算机视觉和自然语言处理两大应用领域的承诺,似乎在介绍完CNN后戛然而止,对于时间序列相关的网络如RNN、LSTM以及当前热门的Transformer模型并未涉及,这不免让人感到遗憾。</p>

<p><u><strong>对全书的测评如下:</strong></u></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;综合考虑,我认为这本书只能算是无功无过。它的内容全面,但与市场上的其他教材相比,缺乏一定的创新性和深度。虽然作为一本实践教程,它在理论深度上略显不足,但如果仅作为快速查找代码的操作手册,它的实用性又不如直接上网搜索或咨询人工智能助手来得高效。此外,书中未能涵盖自然语言处理的相关方法和网络,这在一定程度上影响了它的完整性。尽管如此,书中的代码开源是一个值得称赞的亮点。</p>

<hr />
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;回到测评的实践任务,按照测评计划,我将结合我目前正在进行的科研项目,运用神经网络技术对电池的衰退程度进行预测。专业概念和电池衰退的定义颇为复杂,我在这里就不深入讨论了,可以通过查阅相关论文来获得更多信息。</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;简单的背景就是电池在经历反复的充放电循环后,其可用容量会逐渐减少。为了量化这一衰退过程,我们引入了一个衡量指标&mdash;&mdash;电池健康状态(State of Health, SOH)。SOH是通过计算电池每个周期最大可用容量与其出厂时标称容量的比值来定义的。与之前仅进行衰退与否的二元判断不同,我们现在的目标是预测一个连续变化的值,即将问题从分类任务转变为回归任务。上一期阅读报告的链接:【<a href="https://bbs.eeworld.com.cn/thread-1291317-1-1.html">《人工智能实践教程&mdash;&mdash;从Python入门到机器学习》阅读报告(2) - 编程基础 - 电子工程世界-论坛 (eeworld.com.cn)</a>】。</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;而我们选择的特征,我选择了之前提到的马里兰大学的电池数据集(<a href="https://calce.umd.edu/data">Battery Research Data | Center for Advanced Life Cycle Engineering (umd.edu)</a>)作为研究对象。由于实际使用的数据集暂时无法公开,我在此基础上胡诌了一个数据集,但我们采用的原理和方法是相同的。我们从电池每个充放电周期中提取了5个不同充电量时的电压值作为特征,这些特征能够刻画电池的充电过程,并作为评估其SOH的依据。</p>

<p><u><strong>1. 数据的预处理和读取</strong></u></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在开始模型训练之前,我们已经对数据集进行了预处理,包括归一化等步骤。具体的预处理过程可以参考我上一次的测评报告。此外,我们根据电池的完整衰退数据,选择了两节电池的数据作为训练集,剩余一节作为测试集,训练集和测试集的样本数量比大约为2.5:1。</p>

<p>输出:<em>Training Samples: 1442&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Testing Samples: 615</em></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在之前的分类问题研究中,我们已经展示了不同特征之间的相关性。在本次回归任务中,由于预测目标是一个连续变化的值,我们直接绘制了某个特征与输出结果之间的关联图。从图中可以看出,因为我们的选取的特征还是比较和预测目标相关的,所以线性度比较强。</p>

<div style="text-align: center;"></div>

<p><u><strong>2. 线性回归模型(Linear Regression,LR)</strong></u></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 基于此,我们首先考虑实现一个基础的线性回归算法。这个算法将作为我们后续与神经网络方法进行比较的基准。通过线性回归,我们可以初步探索特征与电池衰退程度(SOH)之间的关系。</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;线性回归是一种预测连续目标值的统计方法,它假设输入特征与目标值之间存在线性关系。在模型构建过程中,我们的目标是找到一组权重,使得模型预测的输出尽可能接近实际的目标值。具体来说,模型的预测公式可以表示为:<img alt="\hat{y} = w_1x_1 + w_2x_2 + \ldots + w_nx_n + b" src="https://bbs.eeworld.com.cn/gif.latex?%5Chat%7By%7D%20%3D%20w_1x_1%20&amp;plus;%20w_2x_2%20&amp;plus;%20%5Cldots%20&amp;plus;%20w_nx_n%20&amp;plus;%20b" />,其中<img alt="\hat{y}" src="https://bbs.eeworld.com.cn/gif.latex?%5Chat%7By%7D" />是预测值,<img alt="x_1, x_2, \ldots, x_n" src="https://bbs.eeworld.com.cn/gif.latex?x_1%2C%20x_2%2C%20%5Cldots%2C%20x_n" />是输入特征,<img alt="w_1, w_2, \ldots, w_n" src="https://bbs.eeworld.com.cn/gif.latex?w_1%2C%20w_2%2C%20%5Cldots%2C%20w_n" />是权重,<img alt="b" src="https://bbs.eeworld.com.cn/gif.latex?b" />是偏置项。在之前的以及其他作者的阅读报告中已经提及,一般可以通过最小二乘等方法来求解线性回归问题。这里我就直接调用python的sklearn包了,代码如下:</p>

<pre>
<code>from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

LRmodel = LinearRegression()
LRmodel.fit(X_train_LR, y_train)
y_train_pred = LRmodel.predict(X_train_LR)
y_test_pred = LRmodel.predict(X_test_LR)

train_mse = mean_squared_error(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)
train_r2 = r2_score(y_train, y_train_pred)
test_r2 = r2_score(y_test, y_test_pred)

print(f'Training MSE: {train_mse:.6f}, R^2: {train_r2:.4f}')
print(f'Test MSE: {test_mse:.6f}, R^2: {test_r2:.4f}')</code></pre>

<p>&nbsp;输出:<em>Training MSE: 0.000126, R^2: 0.9753&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test MSE: 0.000125, R^2: 0.9641</em></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了评估模型的性能,我们使用了两个关键指标:均方误差(MSE)和拟合优度(R^2)。MSE衡量的是预测值与实际值之间差异的平方的平均值,而R^2则反映了模型预测值与实际值之间的相关程度。理想情况下,MSE应尽可能小,R^2应接近1。</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面的左右两图边是利用训练集的数据拟合的5个特征预测的输出结果和实际结果的对应关系。上面的每一个散点表示一个样本,纵坐标为其预测的目标值,横坐标为其实际的目标值。黑线代表预测结果和实际结果完全相同的情况,因此输出的散点图分布越接近黑线,准确度越高。</p>

<div style="text-align: center;"></div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在我们的实验中,由于特征与SOH之间存在较强的线性关系,线性回归模型表现出了较好的预测效果。散点图显示,大多数数据点都紧密地围绕着代表完美预测的黑线分布,这表明模型的预测精度较高。</p>

<p><u><strong>3. 引入非线性问题</strong></u></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;既然如此,我们不妨对数据进行一些干扰,来模拟采集过程中的噪声,来降低两者之间的线性相关性。这种干扰降低了特征与预测目标之间的线性相关性,从而对模型的预测能力提出了挑战。具体代码如下:</p>

<pre>
<code>import numpy as np

np.random.seed(8)

noise_level = 0.1
X_train_LR = X_train + np.random.randn(*X_train.shape) * noise_level
X_test_LR = X_test + np.random.randn(*X_test.shape) * noise_level</code></pre>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如下图所示:</p>

<div style="text-align: center;"></div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在引入噪声后,线性回归模型的预测效果受到了影响。测试集上的误差增加,拟合优度下降,这表明模型在面对非理想数据时的预测能力有所降低。如下图所示:</p>

<div style="text-align: center;"></div>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 输出如下:</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>*** Linear Regression ***</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Training MSE: 0.000797, R^2: 0.8435</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test MSE: 0.000857, R^2: 0.7535</em></p>

<p>&nbsp;</p>

<p><u><strong>4. 多层感知机(Multi-Layer Perceptron,MLP)</strong></u></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;此时因为问题的非线性特征,我们引入神经网络。神经网络相较于传统的线性回归模型,其最大的优势在于能够通过激活函数引入非线性,从而更准确地刻画数据中的复杂关系。我设计了一个包含三个层的网络。输入层接收当前周期的5个特征值,这与我们在线性回归模型中使用的特征相同。在隐层,我们将特征维度扩展到32和64,以增强模型的表达能力。最后,通过一个全连接层输出预测的SOH值。具体代码如下:</p>

<pre>
<code>import torch
import torch.nn as nn

class MLP(nn.Module):
    def __init__(self, input_dim):
      super(MLP, self).__init__()
      self.fc1 = nn.Linear(input_dim, 32)
      self.fc2 = nn.Linear(32, 64)
      self.fc3 = nn.Linear(64, 1)
      self.relu = nn.ReLU()

    def forward(self, x):
      x = self.relu(self.fc1(x))
      x = self.relu(self.fc2(x))
      x = self.fc3(x)
      return x</code></pre>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了确保训练过程的稳定性和可重复性,我们从训练集中划分出一部分数据作为验证集。这样做的目的是为了在训练过程中选择更合适的模型超参数。我们还控制了随机种子,以确保每次运行时数据的随机分配都是一致的。具体的代码如下:</p>

<pre>
<code>import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset, random_split

torch.manual_seed(8)

X_train_tensor = torch.tensor(X_train_LR, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)
X_test_tensor = torch.tensor(X_test_LR, dtype=torch.float32)
y_test_tensor = torch.tensor(y_test, dtype=torch.float32).view(-1, 1)

train_size = int(0.8 * len(X_train_tensor))
val_size = len(X_train_tensor) - train_size
train_dataset, val_dataset = random_split(TensorDataset(X_train_tensor, y_train_tensor),
                                          )

train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
val_loader = DataLoader(dataset=val_dataset, batch_size=64, shuffle=False)</code></pre>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 训练过程的代码如下:</p>

<pre>
<code>input_dim = X_train_LR.shape
model = MLP(input_dim)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.0005)

num_epochs = 200
train_losses = []
avg_val_losses = []
for epoch in range(num_epochs):
    for inputs, targets in train_loader:
      optimizer.zero_grad()
      outputs = model(inputs)
      loss = criterion(outputs, targets)
      loss.backward()
      optimizer.step()

    model.eval()
    with torch.no_grad():
      val_losses = []
      for inputs, targets in val_loader:
            outputs = model(inputs)
            val_loss = criterion(outputs, targets)
            val_losses.append(val_loss.item())
      avg_val_loss = sum(val_losses) / len(val_losses)

    train_losses.append(loss.item())
    avg_val_losses.append(avg_val_loss)

    if (epoch+1) % 10 == 0:
      print(f'Epoch [{epoch+1}/{num_epochs}], Traing Loss: {loss.item():.6f}, Validation Loss: {avg_val_loss:.6f}')</code></pre>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 测试的代码如下:</p>

<pre>
<code>model.eval()
with torch.no_grad():
    y_train_pred = model(X_train_tensor)
    y_test_pred = model(X_test_tensor)
    train_mse = mean_squared_error(y_train, y_train_pred)
    test_mse = mean_squared_error(y_test, y_test_pred)
    train_r2 = r2_score(y_train, y_train_pred)
    test_r2 = r2_score(y_test, y_test_pred)
    print(f'Training MSE: {train_mse:.6f}, R^2: {train_r2:.4f}')
    print(f'Test MSE: {test_mse:.6f}, R^2: {test_r2:.4f}')</code></pre>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在MLP模型训练完成后,我们评估了其预测效果。与直接的线性回归模型相比,MLP在预测误差和R^2值上都有所提升。尽管我还没有对模型的超参数进行细致的调整,但我相信通过进一步的优化,模型的性能还有很大的提升空间。目前,模型的准确度虽然有所提高,但仍然有待提高。最终的预测效果如下图所示:</p>

<div style="text-align: center;"></div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;输出如下:</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>*** MLP ***</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Training MSE: 0.000638, R^2: 0.8748</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test MSE: 0.000728, R^2: 0.7907</em></p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;考虑到电池的衰退不仅与当前周期有关,而且与之前的周期也有密切联系,将某一周期割裂来看确实会因为测量的噪声的问题出现预测的误差。我们将单一周期的数据扩展到连续5个周期的充电曲线特征。这样的数据重构有助于捕捉电池衰退过程中的时间序列特性。</p>

<pre>
<code>X_train_CNN = X_train.reshape(-1,5,5)
X_test_CNN = X_test.reshape(-1,5,5)
X_train_CNN = X_train_CNN + np.random.randn(*X_train_CNN.shape) * noise_level
X_test_CNN = X_test_CNN + np.random.randn(*X_test_CNN.shape) * noise_level</code></pre>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;理论上,对于时间序列数据,长短期记忆网络(LSTM)可能是更合适的选择。然而,卷积神经网络(CNN)在处理此类数据时也展现出了其独特的优势。之前有研究利用CNN对多周期的充电曲线特征进行深入的相关性和特征提取。因此,我们也尝试复现这一过程,以探索CNN在电池衰退预测中的潜力。</p>

<p><u><strong>5. 卷积神经网络(Convolutional Neural Networks,CNN)</strong></u></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们的CNN模型采用了3x3的卷积核,对输入的特征矩阵进行两次卷积运算。每次卷积之后,我们使用最大值池化(Max Pooling)来降低特征维度,同时保留重要的信息。最终,所有通道的关键特征被展平,将它们从2D结构压缩到1D结构,并通过一系列线性层和非线性激活函数进行处理,以生成最终的预测输出。具体代码如下:</p>

<pre>
<code>class CNN(nn.Module):
    def __init__(self):
      super(CNN, self).__init__()
      self.conv1 = nn.Conv2d(1, 16, kernel_size=3, padding=1)
      self.pool = nn.MaxPool2d(2, 2, padding=1)
      self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)
      self.fc1 = nn.Linear(32 * 2 * 2, 64)
      self.fc2 = nn.Linear(64, 32)
      self.fc3 = nn.Linear(32, 1)

    def forward(self, x):
      x = self.pool(torch.relu(self.conv1(x)))
      x = self.pool(torch.relu(self.conv2(x)))
      x = x.view(-1, 32 * 2 * 2)
      x = torch.relu(self.fc1(x))
      x = torch.relu(self.fc2(x))
      x = self.fc3(x)

      return x</code></pre>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CNN的训练过程与MLP相似,我们采用了相同的训练策略和验证方法。在这里,我们不再重复训练过程的详细描述。最终的预测结果如下:</p>

<div style="text-align: center;"></div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;输出如下:</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>*** CNN ***</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Training MSE: 0.000285, R^2: 0.9441</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test MSE: 0.000315, R^2: 0.9094</em></p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;经过训练,CNN模型展现出了较好的预测效果。与MLP相比,训练集和测试集的预测结果更接近理想的黑线,这表明模型的预测精度有所提高。然而,我们也观察到预测结果普遍高于实际值,这可能与模型参数的调优不足有关。</p>

<div style="text-align: center;"></div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过绘制训练集和验证集的损失下降曲线,我们发现两者都有明显的波动。为了更清晰地观察这种波动,我们将纵坐标调整为对数尺度。这种波动至少揭示了两个问题:首先,我们的学习率可能设置得过高,导致模型在训练过程中难以稳定地收敛到损失的局部最小值;其次,模型可能没有在最佳时机停止训练,因为在预设的训练周期结束时,验证集的损失已经开始上升,这可能预示着过拟合的风险。</p>

<p><u><strong>6. CNN的优化</strong></u></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对此,参考书中的介绍,一一给出解决方案:</p>

<ul>
        <li>对于学习率的设置问题,手动调整学习率并进行多次交叉验证虽然能找到最优解,但过程繁琐且耗时。书中提到了两种自适应学习率的优化器:Adagrad和Adam,我们这里选择了后者。同时引入学习率衰减策略控制具体的学习率衰退,即随着训练的进行逐渐减小学习率,这有助于模型在初期快速下降损失,在训练后期则能细致地逼近局部最小值。</li>
</ul>

<pre>
<code>import torch.optim.lr_scheduler as lr_scheduler

scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.98)</code></pre>

<ul>
        <li>对于合适的训练周期数问题,确定合适的训练周期数(epoch数)对于避免过拟合和欠拟合至关重要。我们通过监控验证集上的损失,并引入早停机制(Early Stopping)来解决这一问题。如果在设定的耐心值(patience)周期内,验证集损失没有显著下降,训练将自动停止。这样可以避免在损失尚未稳定时过早结束训练,同时防止过长时间的训练导致资源浪费和趋向过拟合。以下是实现早停机制的代码:</li>
</ul>

<pre>
<code>num_epochs = 1000
patience = 50
best_loss = float('inf')
patience_counter = 0

train_losses = []
avg_val_losses = []

for epoch in range(num_epochs):
    for inputs, targets in train_loader:
      optimizer.zero_grad()
      outputs = model_opt(inputs)
      loss = criterion(outputs, targets)
      loss.backward()
      optimizer.step()
      scheduler.step()

    model_opt.eval()
    with torch.no_grad():
      val_losses = []
      for inputs, targets in val_loader:
            outputs = model_opt(inputs)
            val_loss = criterion(outputs, targets)
            val_losses.append(val_loss.item())
      avg_val_loss = sum(val_losses) / len(val_losses)

    train_losses.append(loss.item())
    avg_val_losses.append(avg_val_loss)

    if (epoch+1) % 10 == 0:
      print(f'Epoch [{epoch+1}/{num_epochs}], Traing Loss: {loss.item():.6f}, Validation Loss: {avg_val_loss:.6f}')

    if avg_val_loss &lt; best_loss:
      best_loss = avg_val_loss
      patience_counter = 0
      torch.save(model.state_dict(), 'best_model.pth')
    else:
      patience_counter += 1
      if patience_counter &gt;= patience:
            print(f'Early stopping at epoch {epoch + 1}')
            break</code></pre>

<ul>
        <li>对于过拟合的问题,它会导致模型在训练集上表现良好,但在测试集上表现不佳。书中给出了两种比较好的解决思路。一是引入正则化,通过在损失函数中添加参数的L2范数,减少模型对特定样本的敏感度;二是使用dropout技术,在训练过程中随机&ldquo;丢弃&rdquo;一部分神经元,增加模型的泛化能力。在PyTorch中,正则化可以通过优化器的权重衰减参数来实现,它的值越大,正则化的强度就越高,对权重的惩罚也就越大。</li>
</ul>

<pre>
<code>class OptCNN(nn.Module):
    def __init__(self):
      super(OptCNN, self).__init__()
      self.conv1 = nn.Conv2d(1, 16, kernel_size=3, padding=1)
      self.pool = nn.MaxPool2d(2, 2, padding=1)
      self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)
      self.fc1 = nn.Linear(32 * 2 * 2, 64)
      self.fc2 = nn.Linear(64, 32)
      self.fc3 = nn.Linear(32, 1)
      self.dropout = nn.Dropout(p=0.3)

    def forward(self, x):
      x = self.pool(torch.relu(self.conv1(x)))
      x = self.pool(torch.relu(self.conv2(x)))
      x = x.view(-1, 32 * 2 * 2)
      x = torch.relu(self.fc1(x))
      x = self.dropout(x)
      x = torch.relu(self.fc2(x))
      x = self.fc3(x)

      return x

model_opt = OptCNN()
criterion = nn.MSELoss()
optimizer = optim.Adam(model_opt.parameters(), lr=0.001, weight_decay=1e-5)
scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.98)</code></pre>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最终结果如下:</p>

<div style="text-align: center;"></div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;输出如下:</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>&nbsp;*** Optimized CNN ***</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Early stopping at epoch 237</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Training MSE: 0.000274, R^2: 0.9462</em></p>

<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test MSE: 0.000252, R^2: 0.9276</em></p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们还监控了训练集和验证集的损失下降过程。</p>

<div style="text-align: center;"></div>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过引入正则化和dropout,验证集的损失抖动明显减少,这表明模型的稳定性得到了提升。同时,损失值稳定下降,且训练周期数减少,这得益于早停机制和学习率衰减策略的有效应用。最后所有的模型汇总如下:</p>

<table width="950px">
        <colgroup>
                <col />
                <col />
                <col />
                <col />
                <col />
        </colgroup>
        <tbody>
                <tr>
                        <td>
                        <p>&nbsp;</p>
                        </td>
                        <td>
                        <p>LR</p>
                        </td>
                        <td>
                        <p>MLP</p>
                        </td>
                        <td>
                        <p>CNN</p>
                        </td>
                        <td>
                        <p>Opt. CNN</p>
                        </td>
                </tr>
                <tr>
                        <td>
                        <p>MSE</p>
                        </td>
                        <td>
                        <p>0.000857</p>
                        </td>
                        <td>
                        <p>0.000728</p>
                        </td>
                        <td>
                        <p>0.000315</p>
                        </td>
                        <td>
                        <p>0.000252</p>
                        </td>
                </tr>
                <tr>
                        <td>
                        <p>R^2</p>
                        </td>
                        <td>
                        <p>0.7535</p>
                        </td>
                        <td>
                        <p>0.7907</p>
                        </td>
                        <td>
                        <p>0.9094</p>
                        </td>
                        <td>
                        <p>0.9276</p>
                        </td>
                </tr>
        </tbody>
</table>

<p>&nbsp;</p>

<hr />
<p><u><strong>7. 最后总结</strong></u></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;虽然书中的实例主要聚焦于计算机视觉领域,这与我的研究方向有所差异,但我依然能够将书中的理论知识与我的研究内容相结合,对具体的任务进行了深入的实践探索。通过本次测评,我不仅体验了神经网络的调用、搭建以及优化的全过程,而且对这些环节有了更深刻的理解。尽管我采取了一些措施在一定程度上提高了模型的性能,但我清楚地意识到,这些优化远非最优解。神经网络的参数调整,常被戏称为&ldquo;炼丹&rdquo;,这一比喻形象地说明了其复杂性和不确定性,后续的学习还是永无止境啊!</p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

Jacktang 发表于 2024-9-6 07:48

<p>抓住两个思路</p>

<p>一是引入正则化,通过在损失函数中添加参数的L2范数,减少模型对特定样本的敏感度;</p>

<p>二是使用dropout技术,在训练过程中随机&ldquo;丢弃&rdquo;一部分神经元,增加模型的泛化能力。</p>

通途科技 发表于 2024-10-25 11:39

页: [1]
查看完整版本: 《人工智能实践教程——从Python入门到机器学习》阅读报告(3)