-
内核寄掉了
解决方法:
在导包的时候加上这个
import os os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
画图的时候报错:
问题原因:画图的列表中有空的列表
解决方法:把训练过程的代码跑一遍, 报错了就debug就行了。
问题1:您能将上述的CNN结构,修改为SNN结构吗?
有三个卷积层
第一层 有一个输入, 32个输出, 卷积核是(3,3)的矩阵
第二层有32个输入, 64个输出, 卷积核为(3,3)的矩阵
第三层有64个输入, 64个输出, 卷积核为(3,3)的矩阵
每个卷积层后面都跟着一个ReLU的激活函数
nn.Flatten(), 扁平化, 把卷积层的输出, 变成一维的, 以便后面线性层的输入
线性层接收一维的数据, 然后映射成10个类别
为什么 -6?
因为每次卷积, 会模糊边缘的两个像素, 卷积了3层就是 2 * 3 = 6, 所以 -6
然后定义了正向传播函数
修改为SNN结构的代码如下:
class DigitClassifier(nn.Module): def __init__(self): super().__init__() self.model = nn.Sequential( nn.Linear(28 * 28, 1024), nn.ReLU(), nn.Conv2d(1024, 512), nn.ReLU(), nn.Linear(512, 10) ) def forward(self, x): return self.model(x)
clf = DigitClassifier().to(device) opt = Adam(clf.parameters(), lr=1e-3) # 指定优化算法 loss_fn = nn.CrossEntropyLoss()
问题2:您能将上述的Adam优化算法替换为其他优化算法吗?能做个对比吗?
使用SGD优化算法:
opt_sgd = torch.optim.SGD(clf.parameters(), lr=1e-3, momentum=0.9) # 使用SGD优化算法
SGD是一种经典的优化算法,它通过计算梯度并沿着梯度的负方向更新参数。SGD还可以使用动量(momentum)来帮助加速学习过程,防止陷入局部极小值。
使用RMSprop优化算法:
opt_rmsprop = torch.optim.RMSprop(clf.parameters(), lr=1e-3, alpha=0.9) # 使用RMSprop优化算法
RMSprop是一种自适应学习率的优化算法,它能够自动调整不同参数的学习率,从而加速收敛过程。RMSprop通过平均平方梯度来缩放每个参数的学习率。
对比:
-
SGD vs. Adam:
-
SGD通常比较简单,容易实现。它在学习率较小、数据较大的情况下效果较好,特别是在使用动量的情况下(momentum参数设置为一个较大的值,比如0.9)。
-
Adam结合了动量和自适应学习率的特性,通常在深度学习中表现较好。Adam适用于大多数情况,因为它能够自适应地调整学习率,并且通常比SGD更快地收敛。
-
-
RMSprop vs. Adam:
-
RMSprop也是一种自适应学习率的方法,与Adam类似。RMSprop在某些情况下可能比Adam更稳定,尤其是在处理非平稳目标函数时。
-
Adam在实践中通常表现得更好,因为它考虑了梯度的一阶矩估计(动量)和二阶矩估计(自适应学习率),这使得它在不同类型的数据和网络架构上都有很好的表现。
-
问题3:能搞定nn.CrossEntropyLoss()这个损失函数的计算公式吗?
公式如下
$$
L(x, y) = -\frac{1}{N} \sum_{n=1}^{N} \log\left(\frac{\exp(x_{n, y_{n}})}{\sum_{c=0}^{C-1} \exp(x_{n, c})}\right)
$$
问题4:你能修改上述代码,记录每一代之后的模型准确率吗?
%%time losses = [] epochs = [] total_count = 0 correct_count = 0 for epoch in range(10): # 训练10代 for batch in dataset: X,y = batch X, y = X.to(device), y.to(device) yhat = clf(X) # 正向传播 loss = loss_fn(yhat, y) # 损失函数 total_count += len(y) correct_count += (yhat.argmax(1) == y).sum().item() accuracy = correct_count / total_count # 反向传播 opt.zero_grad() # 这一步不能省,每次反向传播前,设置优化算法的梯度参数 loss.backward() # 反向传播,计算梯度 opt.step() # 基于优化算法的梯度更新 epochs.append(epoch+1) # 计算代数 losses.append(loss.item()) # 保存损失函数值 print(f"Epoch:{epoch} loss is {loss.item()} Accuracy:{accuracy}") # 显示每一代训练后的损失值 训练集和验证集上的对比曲线。
再损失函数下面加上三行代码即可
total_count += len(y) correct_count += (yhat.argmax(1) == y).sum().item() accuracy = correct_count / total_count
问题5:你能绘制出模型训练过程中的准确率曲线吗?
代码如下:
plt.plot(epochs, accuracys, 'o--') # 改成正确率的那个数组就行了 plt.xlabel('Epoch') plt.ylabel('accuracy') plt.title('Accuracy per epoch') plt.show()
问题6:如何绘制训练图形,才能有效观察模型是否过拟合?模型泛化能力如何?是否有进一步改善的余地?
绘图观察模型是否过拟合, 以及他的泛化能力
-
添加验证集数据: 在训练过程中,保留一部分数据作为验证集。通常,会将训练集分成训练集和验证集,然后使用训练集来训练模型,验证集来评估模型性能。
-
在每个epoch结束后计算验证集的损失值和准确率: 在每个epoch结束后,用模型在验证集上进行前向传播,计算损失值并记录准确率。
-
绘制损失值和准确率曲线: 使用Matplotlib或其他绘图库,绘制训练集和验证集的损失值和准确率曲线。
改善的余地:
可以添加验证集进行进一步的验证, 如果不通过验证就让他回炉重造啦
切片的时候, 先测试用什么大小的切片训练模型比较合适, 然后用合适大小的切片进行训练大模型
梯度下降的步长, 可以先做小测试, 测试出合适的步长, 然后用这个步长去训练大模型, 这样效率会高一点
绘制曲线的代码
添加验证集
test = datasets.MNIST(root="data", train=False, download=True, transform=ToTensor()) data_test = DataLoader(test, 32)
训练过程的代码修改:
添加了验证集的损失函数计算, 还有正确率的计算, 并将他们放到一个列表中, 方便下面画图使用
%%time losses = [] epochs = [] steps = [] accuracys = [] total_count = 0 correct_count = 0 val_losses = [] val_accuracies = [] for epoch in range(10): # 训练10代 for batch in dataset: X,y = batch X, y = X.to(device), y.to(device) yhat = clf(X) # 正向传播 loss = loss_fn(yhat, y) # 损失函数 total_count += len(y) correct_count += (yhat.argmax(1) == y).sum().item() accuracy = correct_count / total_count # 反向传播 opt.zero_grad() # 这一步不能省,每次反向传播前,设置优化算法的梯度参数 loss.backward() # 反向传播,计算梯度 opt.step() # 基于优化算法的梯度更新 # 在验证集上进行评估 val_total_count = 0 val_correct_count = 0 val_loss = 0.0 with torch.no_grad(): # 在验证过程中不需要计算梯度 for val_batch in data_test: val_X, val_y = val_batch val_X, val_y = val_X.to(device), val_y.to(device) val_yhat = clf(val_X) # 正向传播 val_loss += loss_fn(val_yhat, val_y).item() # 损失函数 val_total_count += len(val_y) val_correct_count += (val_yhat.argmax(1) == val_y).sum().item() val_accuracy = val_correct_count / val_total_count val_losses.append(val_loss / len(data_test)) # 平均损失值 val_accuracies.append(val_accuracy) epochs.append(epoch+1) # 计算代数 accuracys.append(accuracy) losses.append(loss.item()) # 保存损失函数值 print(f"Epoch:{epoch} loss is {loss.item()} Accuracy:{accuracy}") # 显示每一代训练后的损失值
绘制曲线的代码
朴素版本
plt.plot(epochs, losses, 'o--') plt.plot(epochs, val_losses,'o--') plt.xlabel('Epoch') plt.ylabel('Loss') plt.title('Loss per epoch') # 也可以按照每个batch绘制图形 plt.show() plt.plot(epochs, accuracys, 'o--') # 改成正确率的那个数组就行了 plt.plot(epochs, val_accuracies,'o--') plt.xlabel('Epoch') plt.ylabel('accuracy') plt.title('Accuracy per epoch') plt.show()
视觉优化:
代码:
import matplotlib.pyplot as plt plt.figure(figsize=(12, 4)) # 绘制损失值曲线 plt.subplot(1, 2, 1) plt.plot(epochs, losses, marker='o', color='b', label='Training Loss') plt.plot(epochs, val_losses, marker='o', color='r', label='Validation Loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.title('Training and Validation Loss') plt.legend() # 绘制准确率曲线 plt.subplot(1, 2, 2) plt.plot(epochs, accuracys, marker='o', color='b', label='Training Accuracy') plt.plot(epochs, val_accuracies, marker='o', color='r', label='Validation Accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.title('Training and Validation Accuracy') plt.legend() plt.tight_layout() plt.show()