实验2
1.构建数据集: 采用make_circle()方法生成了大圆和小圆,构成同心圆; 输出前五个样本的特征值,标签值来查看验证; 将数据转换为DataFrame格式数据集存储; 表的每一行表示随机生成的点的坐标及其标签; 数据集构建完成。 2.划分数据集: 划分数据集,划分比例训练集:测试集=8:2, 3.定义模型: 定义子类对象nn.Mdoule,使用nn.linear类定义两个层(输入层x,输出层y), 定义模型的正向传播函数forward(),初始化模型结构并挂载到device设备上 4.定义损失函数torch.nn.BCEWithLogitsLoss和优化算法torch.optim.SGD 5.训练模型:指定分类规则,创建训练循环 6.改进模型
make_circle()
1.在 2d 中创建一个包含较小圆的大圆的样本集 2.一个用于可视化聚类和分类算法的简单toy数据集 3.返回值:x(二维)生成的样本,y(一维)每个样本的标签 例:sklearn.datasets.make_circles(n_samples = 100,shuffle = True,noise = None,random_state = None,factor = 0.8) 参数含义: n_samples:生成的圆的总数 shuffe:为True时,内外圆的数据交叉出现,为False时,内外圆的数据没有出现交叉 noise:高斯噪音的标准偏差加到数据,参数比较小时,取样点比较集中,参数比较大时,取样点比较分散 random_state:确定数据重排和噪声的随机数生成 factor:内外圈之间的比例因子,当factor较大时,内圆半径较大,当factor较小时,内圆半径较小
X[0:5,0]
对于X[:,0] 取二维数组中第一维的所有数据 对于本样本:1000行2列的二维数组[[x0,y0],[x1,y1],[x2,y2]....[x999,y999]]
train_test_split()
:分割数据集为测试集与训练集 参数: train_target:要划分的样本结果 test_size:样本的占比 radom_state:随机种子 stratify:平均比例
nn.Module
在定义自己的网络时,需要继承nn.Module类,并重新实现构造函数init()和forwrad()方法 网络中具有可学习参数的层放在构造函数init()中;forward方法必须重写,它是实现模型的功能,实现各个层之间连接的关键
torch.nn.BCEWithLogitsLoss
同时结合了Sigmoid和BCELoss函数,效果比torch.nn.BCELoss()好一些
torch.optim.SGD
:pytorch中提供了torch.optim方法优化神经网络
实验3
获取数据集: 通过框架中的内置函数将Fashion-MNIST数据集下载并读取到内存中。 从torchvision中的datasets中将Fashion-MNIST数据集拿到;root是目录;train=True表示下载的是训练数据集;download=True表示确定从网上下载。 我们将Dataset作为参数传递给DataLoader。这将在我们的数据集上包装一个可迭代对象,并支持 自动批处理、采样、混洗和多进程数据加载。这里我们定义一个批处理大小为64,即每个元素 iterable将返回一批64个特征和标签。 创建模型: 确定设备为gpu还是cpu 继承nn.Module 重写 init()和forward()
__init__()
定义Flatten层,用 Sequential封装一个三层的局部结构 forward()
将数据送入神经网络中并返回网络的输出结果
激活函数(Activation Function)是一种添加到人工神经网络中的函数,旨在帮助网络学习数据中的复杂模式。类似于人类大脑中基于神经元的模型,激活函数最终决定了要发射给下一个神经元的内容。 Sigmoid函数 Tanh函数 ReLU Leaky ReLU函数
一般,回归问题可以用恒等函数,二元分类问题可以用sigmoid函数,多元分类问题可以用softmax函数
优化算法和损失函数: 在单个训练循环中,模型对训练数据集进行预测(分批馈送给它),并且 反向传播预测误差以调整模型的参数。 我们还根据测试数据集检查模型的性能,以确保它正在学习 训练过程在若干迭代(时期)上进行。在每个时期,模型学习 参数,以做出更好的预测。我们打印模型在每个时期的准确度和损失;我想去 精度随着每一个历元而增加,并且损失随着每一个历元而减小
Hyperparameters是可调参数,可用于控制模型优化过程。 不同的超参数值会影响模型训练和收敛速度 Number of Epochs迭代数据集的次数 Batch Size参数更新前通过网络传播的数据样本数 Learning Rate在每个批次/时期更新模型参数的程度。较小的值会导致学习速度较慢,而较大的值可能会导致训练期间的不可预测行为
Optimization Loop
一旦我们设置了超参数,我们就可以使用优化循环来训练和优化我们的模型。每个 优化循环的迭代被称为 epoch The Train Loop迭代训练数据集并尝试收敛到最佳参数 The Validation/Test Loop迭代测试数据集以检查模型性能是否正在改善
当提供一些训练数据时,我们未经训练的网络可能不会给予正确的结果。 Loss Function度量得到的结果与目标值的不相似程度, 这是我们在训练过程中要最小化的损失函数。为了计算我们的损失 使用给定数据样本的输入进行预测,并将其与真实数据标签值进行比较
保存模型的常用方法是序列化内部状态字典 加载模型的过程包括重新创建模型结构和加字典里的
%matplotlib inline:
IPython有一组预先定义好的所谓的魔法函数,可以通过命令行的语法形式来访问它们。 魔法函数分两种:一种是面向行(前缀“%”标注),另一种是面向单元型(两个“%%”做前缀,参数不仅是当前“%%”行后面的内容,也包括在当前行以下的行 %matplotlib inline
功能是可以内嵌绘图,并且省略掉plt.show()。
torch.utils.data.DataLoader:
PyTorch数据加载实用程序的核心是torch.utils.data.DataLoader类,它表示一个Python可迭代数据集 torchvision 主要由以下几部分构成:
torchvision.datasets : ⼀些加载数据的函数及常⽤的数据集接 torchvision.models : 包含常⽤的模型结构(含预训练模型),例如AlexNet、VGG、 ResNet等 torchvision.transforms : 常⽤的图⽚变换,例如裁剪、旋转等 torchvision.utils : 其他的⼀些有⽤的⽅法
torchvision.datasets:
torchvision.datasets模块包含许多真实世界视觉数据的Dataset对象,如 CIFAR,COCO 每个TorchVision Dataset都包含两个参数:transform和 target_transform分别修改样品和标签。
torch.utils.data.Dataset:
pytorch 提供了一个数据读取的方法,其由两个类构成:torch.utils.data.Dataset 和 DataLoader。 如果我们要自定义自己读取数据的方法,就需要继承类 torch.utils.data.Dataset ,并将其封装到DataLoader中。 torch.utils.data.Dataset 是一个 类 Dataset 。通过重写定义在该类上的方法,我们可以实现多种数据读取及数据预处理方式。 class Dataset(object):
An abstract class representing a Dataset.表示一个数据集的抽象类
All other datasets should subclass it. All subclasses should override ``__len__``, that provides the size of the dataset, and ``__getitem__``, supporting integer indexing in range from 0 to len(self) exclusive. """ 所有其他数据集都应该对其进行子类化。 所有子类都应该重写提供数据集大小的 __len__ 和 __getitem__ ,支持从 0 到 len(self) 独占的整数索引。 def __getitem__(self, index): raise NotImplementedError def __len__(self): raise NotImplementedError def __add__(self, other): return ConcatDataset([self, other])
实验4
1.数据集的加载与处理:
视觉类数据集,用 torchvision
方法:
(1) torchvision
包由流行的数据集、模型组成 架构,以及用于计算机视觉的常见图像变换。
(2) torchvision.datasets
:所有数据集都是torch.utils.data.Dataset
的子类,也就是说,它们实现了getitem
和_len_
方法。 因此它们都可以被传递给 torch.utils.data.DataLoader
可使用torch.multiprocessing
并行加载多个样品。
对数据集的加载:训练集、验证集、测试集
通过程序得到每个数据集的图片数量:例如统计训练集的投喂图片
num_files = 0 for root, dirs, files in os.walk(train_dir): for file in files: file_path = os.path.join(root, file) num_files += 1 print(f"训练集共有 {num_files} 张图片:")
得出训练集:测试集:验证集 = 6552 :819 :818 , 约为8 :1 :1
构建训练集,数据预处理变换
numpy.array(object, dtype=None) #将数据转化为矩阵 mean = np.array([0.485, 0.465, 0.406]) #均值 std = np.array([0.229, 0.224, 0.225]) #标准差
将使得RGB通道的像素均值为0,变化范围为[-1,1]
transfrom.Compose()
torchvision.transforms
是pytorch中的图像预处理包。一般用Compose把多个步骤整合到一起
例如:
train_data_transforms = transforms.Compose([ transforms.Resize((224,224)), transforms.RandomVerticalFlip(0.5), transforms.RandomHorizontalFlip(0.5), transforms.RandomRotation(90), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean = mean , std = std) ])
对训练集的图片进行:1.大小调整为224*224像素;2.随机一半的概率进行垂直翻转;3.随机一半的概率进行水平翻转;4.随机旋转,最大角度为90度;5.裁剪为224**224像素的尺寸;6.将PIL图像或Numpy数组转换为Tenor对象;7.用均值和标准差归一化图像
问:为何对训练集进行随机翻转,旋转处理?
torchvision.datasets.ImageFolder
是PyTorch的torchvision库中的一个类,它用于从文件夹中加载图像数据。这个类将文件夹中的图片按照它们的文件名顺序来排序,然后返回一个有序的图像数据集。对于每个图像,它会返回图片的路径和图片本身。在使用ImageFolder
时,保证所有的图片预处理过。
使用数字对花朵打标签
2.定义迁移学习模型:
torchvision.models
模块下定义的知名分类模型:
-
resnet18
:ResNet-18 是一种深度残差网络,它具有较少的参数和计算复杂度,适用于图像分类任务。 -
resnet50
:ResNet-50 是一种更深的 ResNet 模型,它具有更好的性能和更复杂的结构,适用于更复杂的图像分类任务。 -
resnet101
:ResNet-101 是 ResNet 的一种变体,它具有更复杂的结构,能够更好地捕捉图像的细节信息,适用于精确的图像分类任务。 -
vgg11
:VGG-11 是一种基于卷积层和池化层的网络结构,它具有较少的参数和计算复杂度,适用于基本的图像分类任务。 -
vgg16
:VGG-16 是一种更深的 VGG 模型,它具有更好的性能和更复杂的结构,适用于更复杂的图像分类任务。 -
inception_v3
:Inception-v3 是一种基于 Inception 架构的模型,它具有更复杂的结构,能够捕捉更多的特征信息,适用于高精度的图像分类任务。 -
googlenet
:GoogLeNet 是一种基于 Inception 架构的模型,它具有更复杂的结构,能够捕捉更多的特征信息,适用于高精度的图像分类任务。由于其轻量级的特点,GoogLeNet 也被广泛应用于迁移学习中。
定义New_Classifier_Layer类,重写__init__
和forword
方法,定义线性层linear1与linear2,使 用ReLU作为线性层linear1的激活函数。加载VGG16模型,base_model.classifier = new_classifier
:这一行替换了VGG16模型的分类器部分,使用你创建的新的分类器层new_classifier
for parameter in base_model.features: parameter.requires_grad = False # 固定特征提取模型的参数
问:上述代码的作用?为何固定提取模型的参数而不是进行修改?
optimizer = torch.optim.Adam(base_model.classifier.parameters(), lr = 0.0001)
: 这行代码创建了一个Adam优化器,并设置了学习率为0.0001。base_model.classifier.parameters()
表示优化器将作用于模型分类器的参数。
3.模型训练:
观察上述损失函数值的变化和准确率的变化过程,有何结论?
随着模型的不断训练,损失函数值逐渐降低,准确率不断上升,进行二代训练时,初始的损失函数值与准确率相比第一代训练都有很高的提升
4.模型测试:
使用测试集对模型验证,将模型推测的结果与标签进行对比
5.保存模型:
torch.save
保存训练好的模型
实验5
学习理解Pytorch
建模流程,在Pytorch
中掌握建模管道的分布指南
使用深度学习模型将图像分类到预定义类别
构建深度学习模型时,考虑以下问题:
-
数据可用性和结构:为每个数据集构建数据加载器
-
模型结构:定义一个模型类
-
定义建模管道:超参数、损失函数与优化器、模型度量
1.导入库
seaborn
库:seaborn
是Python中的一个可视化库,是对matplotlib
进行二次封装而成
特点:1.绘图接口更为集成,可通过少量参数实现大量封装绘图
2.多数图表具有统计学含义
3.对Pandas
和Numpy
数据类型支持很好
4.风格多样
定义一个通用函数并使用kind参数指定需要绘制的图形
termcolor
库:在终端输出彩色文字
2.数据集与数据加载器
训练机器学习模型,首先需要数据,使用MNIST
数据集,根据所提供的图像预测数字。
MNIST
数据集是NIST
数据集的一个子集,主要包括四个文件,训练集train一共包含了 60000 张图像和标签,而测试集一共包含了 10000 张图像和标签,每张图片是一个28*28像素点的0 ~ 9的灰质手写数字图片,黑底白字,图像像素值为0 ~ 255。MNIST
数据集是一个用来训练各种图像处理系统的二进制图像数据集,广泛应用于机器学习中的训练和测试
root:指定保存下载文件的位置
train:指定下载训练集(True)还是测试集(False)
download:指定是否下载数据集。请注意,如果数据已经存在于提供的根参数中,则不会再次下载数据。
值得注意的是,下载的数据集将被处理为PyTorch DataSet
。这将使输入数据能够轻松加载到训练和验证管道中。
# Train set data_train = datasets.MNIST(root=DATA_DIR, train=True, download=True) # Test set data_test = datasets.MNIST(root=DATA_DIR, train=False, download=True)
得到训练集与测试集
使用len()
来查看训练集与测试集的图像数量,使用.data.shape
方法来得到更加具体的图片大小
训练集:测试集 = 60000 :10000
每张图片大小为28*28
将图像转换为张量,随机显示几张,使用ToTensor()
将其像素值变换为(0,1)范围的值
图像大小为28 × 28像素,第一个图像应该表示数字5。让我们看看像素数据与梯度颜色更好的可视化。我们将使用pandas style
来应用渐变效果。由于我们的图像是秩3,为了将其转换为pandas数据帧,我们需要删除第一维。我们将使用numpy
squeeze()
来完成此操作,它删除长度为1的轴。下图给出了训练样本中第一个数字图像的归一化像素表示。
-
imgT.squeeze().numpy()
:这一部分把一个图像矩阵或者张量转化为NumPy数组,然后删除尺寸为1的维度。但是需要注意的是,squeeze()
函数仅移除长度为1的维度,如果图像是三维的(比如RGB图像),那么你可能需要使用imgT.squeeze(0)
或者imgT.squeeze()
来确保把所有的维度都压平。 -
dfimg = pd.DataFrame(imgT.squeeze().numpy())
:这一部分把NumPy数组转化为pandas的DataFrame。这是正确的,但是需要注意的是如果数组的形状(shape)和内容不是DataFrame能接受的,那么可能会引发错误。 -
dfimg.style.set_properties(**{'font-size':'6pt'})
和dfimg.style.background_gradient('Blues')
:这两行代码是在设置DataFrame的样式。这里需要注意的是,背景渐变功能在pandas中并没有实现,这部分代码可能不会工作。字体大小是可以设置的。 -
dfimg.format(precision=2)
:这行代码是设置数据的显示精度,也是可以正常工作的。
准备数据集训练,创建数据加载器
准备验证集来推断训练过程中是否过拟合,验证集使用训练中未使用的数据
知道标签是如何命名的是有用的。我们可以使用class_to_idx
来获取数据集中的标签字典
为了直观地检查训练和测试数据集中标签的分布,我们计算每个标签的条形图:
df = pd.DataFrame(pd.Series(data_train.targets).value_counts().reset_index()) df.columns = ['digit', 'count'] df.sort_values('digit', inplace=True) dft = pd.DataFrame(pd.Series(data_test.targets).value_counts().reset_index()) dft.columns = ['digit', 'count'] dft.sort_values('digit', inplace=True) # Define plot structure fig,_=plt.subplots(1,2, figsize=(10,3)) plt.suptitle('Target distribution label', size=15) # Plot the train set plt.subplot(1,2,1) plt.bar('digit', 'count',data=df, color=cmap(df.digit)) plt.title('Train set', size=10) plt.xlabel('digit') plt.xticks(range(len(df.digit)), df.digit) plt.ylabel('count')
训练集和测试集的目标分布是相似的,所有数字都在一个平衡的结构中相对表示,这意味着我们可以将训练数据随机分为训练集和验证集。如前所述,如果样本不平衡,则在分割样本时,我们应保持样本不平衡
我们将使用SubsetRandomSampler
来选择数据。SubsetRandomSampler
使用来自原始数据集的索引来随机地且不替换地选择数据的子集。
有了用于训练和验证的采样器,我们可以为每个数据集创建可迭代对象,这些对象将包含训练过程中使用的所有信息。这些对象称为数据加载器。
为了使用PyTorch训练模型,对于输入数据有两个基本要求: 1.数据集必须是元组的形式,其结构为(input,label),其中每个都是张量 2.输入数据应该成批存储,即,输入数据可在多个批次上迭代
为了创建可迭代的批处理,我们将使用一个名为DataLoader
的PyTorch对象,它接受一个Python集合并将其转换为基于批处理的迭代器。
损失函数
损失函数以度量的形式提供了我们的模型预测和地面实况(目标标签)之间的联系,它告诉我们我们的模型预测离目标标签有多远。它用作验证我们的权重优化过程是否按预期进行的手段,即当我们迭代学习过程时,我们的模型正在做出更好的预测
differentiable如果损失函数不可微,则没有将更新权重的梯度,在不更新权重的情况下,模型预测不会发生变化
sensitive对权重的小变化做出响应,这反过来意味着它将改变预测值。如果预测值没有变化,则训练迭代无用
只有当它满足前2个属性时,它才能与模型度量相同。
在大多数情况下,使用交叉熵损失函数
交叉熵损失函数Cross-entropy loss function 由两个分量组成:激活函数、负对数似然
绘制模型训练过程中的损失函数曲线和准确率曲线的大致步骤如下:
-
数据读取与存储:首先要得到训练时的数据,以损失值为例,网络每迭代一次都会产生相应的loss,把每一次的loss值存储下来,保存到列表,保存到.txt文件中。
-
损失曲线绘制:首先创建一张画布,然后绘制训练损失曲线和验证损失曲线。
-
准确率曲线绘制:然后绘制训练准确率和验证准确率曲线。
实验6
train = datasets.MNIST(root="data", download=True, train=True, transform=ToTensor()) dataset = DataLoader(train, 32)
这段代码将MNIST数据集加载到内存中,然后将数据集划分为32个批次
class DigitClassifier(nn.Module): def __init__(self): super().__init__() self.model = nn.Sequential( nn.Conv2d(1, 32, (3,3)), nn.ReLU(), nn.Conv2d(32, 64, (3,3)), nn.ReLU(), nn.Conv2d(64, 64, (3,3)), nn.ReLU(), nn.Flatten(), nn.Linear(64*(28-6)*(28-6), 10) ) def forward(self, x): return self.model(x)
这段代码定义了一个名为DigitClassifier的类,它是PyTorch库中nn.Module的子类。这个类主要用于构建和训练神经网络模型。
在init方法中,我们首先调用super()方法初始化父类nn.Module,然后定义了一个名为self.model的神经网络模型。这个模型是由多个层组成的序列,包括三个卷积层和两个线性层。
第一个卷积层是nn.Conv2d(1, 32, (3,3)),它接受一个输入通道(1),输出32个通道,并使用3x3的卷积核。nn.ReLU()是激活函数层,用于添加非线性。
第二个和第三个卷积层的配置与第一个类似,唯一的区别是输入和输出的通道数以及卷积核的大小。
最后一个层是nn.Flatten(),它负责将卷积层的输出(通常是一个二维或一维的张量)转换成一个可以被线性层接受的一维张量。
线性层的名称为nn.Linear(64(28-6)(28-6), 10),意味着它接受一个64(28-6)(28-6)大小的特征向量,并将其映射到10个可能的输出类别上(这通常对应于10个数字类别)。
在前向传播函数forward(x)中,输入x通过上述定义的模型进行传递,并返回模型对输入x的预测结果。
将上述的CNN结构,修改为SNN结构
class StandardNeuralNetwork(nn.Module): def __init__(self): super().__init__() self.model = nn.Sequential( nn.Linear(28*28, 1024), # 输入层,将28x28像素的图像转换为1024维的向量 nn.ReLU(), # 非线性激活函数 nn.Linear(1024, 512), nn.ReLU(), # 非线性激活函数 nn.Linear(512, 10) # 输出层,10个类别 ) def forward(self, x): return self.model(x)
clf = DigitClassifier().to(device) opt = Adam(clf.parameters(), lr=1e-3) # 指定优化算法 loss_fn = nn.CrossEntropyLoss()
clf = DigitClassifier().to(device)
: 这行代码创建了一个DigitClassifier的实例,并将其转移到了一个指定的设备上
opt = Adam(clf.parameters(), lr=1e-3)
: 这行代码定义了一个Adam优化器,并设置其学习率为1e-3。Adam是一种常用的深度学习优化算法,用于更新和调整模型的参数,以最小化损失函数。clf.parameters()
表示的是DigitClassifier模型的所有参数,这些参数会被Adam优化器用来进行更新。
loss_fn = nn.CrossEntropyLoss()
: 这行代码定义了一个损失函数,它是用于衡量模型预测与实际结果之间的差距。在这个例子中,使用的是交叉熵损失函数(Cross Entropy Loss)。在多分类问题中,这个损失函数经常被用来衡量一个模型预测的准确性。
将上述的Adam优化算法替换为其他优化算法,做个对比
opt = optim.SGD(clf.parameters(), lr=1e-3)
opt = optim.RMSprop(clf.parameters(), lr=1e-3)
Adam和SGD的对比:
-
收敛速度和稳定性:Adam通常比SGD收敛得更快,而且在处理大数据集时更稳定。这是因为Adam使用梯度的一阶矩估计和二阶矩估计来调整学习率,而SGD仅使用梯度的一阶矩估计。
-
适应性强:Adam的自适应学习率调整使其在处理具有挑战性的优化问题时更加强大。例如,当需要精细调整学习率以获得最佳性能时,Adam可以提供比SGD更好的结果。
-
计算成本:虽然Adam通常更有效,但它的计算成本比SGD稍高。这是因为Adam需要更多的内存和计算资源来存储和更新梯度的一阶矩估计和二阶矩估计。
-
参数设置:Adam的参数设置相对简单,而SGD需要对学习率和动量参数进行手动调整,这可能会更加复杂。
RMSProp和Adam都适用于深度学习模型的训练,但Adam在大多数场景中表现得更稳定、效果也更好
nn.CrossEntropyLoss()这个损失函数的计算公式
交叉熵损失的计算公式如下:
$$
L(W, Labels) = - Σ_i y_i * log(p_i)
$$
-
L(W, Labels) 是损失函数值,W是模型的参数,Labels是真实的标签向量。
-
y_i 是真实的标签,取值为0或1,表示属于第i个类别的样本的真实标签。
-
p_i 是模型预测的第i个类别的输出概率,即p(y=i|x;W),取值范围为0到1。
实际的计算公式为:
$$
L(W, Labels) = - Σ_i y_i * log(p_i + ε)
$$
使用这个损失函数时,模型的最后一层通常不需要使用激活函数(如sigmoid或softmax),因为nn.CrossEntropyLoss()
内部已经包含了这些激活函数的计算。
修改上述代码,记录每一代之后的模型准确率
%%time losses = [] epochs = [] 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) # 损失函数 # 反向传播 opt.zero_grad() # 这一步不能省,每次反向传播前,设置优化算法的梯度参数 loss.backward() # 反向传播,计算梯度 opt.step() # 基于优化算法的梯度更新 epochs.append(epoch+1) # 计算代数 losses.append(loss.item()) # 保存损失函数值 print(f"Epoch:{epoch} loss is {loss.item()}") # 显示每一代训练后的损失值
%%time losses = [] epochs = [] correct_count = 0 total_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}") # 显示每一代训练后的损失值
绘制出模型训练过程中的准确率曲线
plt.plot(epochs, accuracys, 'o--') plt.xlabel('Epoch') plt.ylabel('accuracy') plt.title('accuracy per epoch') plt.show()
如何绘制训练图形,才能有效观察模型是否过拟合?模型泛化能力如何?是否有进一步改善的余地
%%time losses = [] val_losses = [] epochs = [] correct_count = 0 total_count = 0 val_total_count = 0 val_correct_count = 0 accuracys = [] val_accuracys = [] 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() # 基于优化算法的梯度更新 # 在每个epoch之后计算验证集上的性能指标 with torch.no_grad(): # no_grad() 上下文管理器,关闭梯度计算,节省内存 for val_batch in val_dataset: # val_dataset应该是验证集 X, y = val_batch # 这里假设val_batch也是数据和标签的元组 X, y = X.to(device), y.to(device) yhat = clf(X) # 正向传播,不计算梯度 val_loss = loss_fn(yhat, y) # 损失函数 val_total_count += len(y) val_correct_count += (yhat.argmax(1) == y).sum().item() val_accuracy = val_correct_count / val_total_count epochs.append(epoch+1) # 计算代数 accuracys.append(accuracy) #计算精确度 losses.append(loss.item()) # 保存损失函数值 val_accuracys.append(val_accuracy) # 保存验证集上的精确度 val_losses.append(val_loss.item()) # 保存验证集上的损失函数值 print(f"Epoch:{epoch} loss is {loss.item()}, Accuracy: {accuracy}") # 显示每一代训练后的损失值
# 绘制对比曲线 plt.figure(figsize=(10, 6)) plt.plot(epochs, losses,'o--', label='Training loss') plt.plot(epochs, val_losses, label='Validation loss') plt.title('Training and Validation Loss') plt.xlabel('Epoch') plt.ylabel('Loss') plt.legend() # 绘制准确率曲线 plt.figure(figsize=(10, 6)) plt.plot(epochs, accuracys,'o--', label='Training accuracy') plt.plot(epochs, val_accuracys, label='Validation accuracy') plt.title('Training and Validation Accuracy') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.legend() # 显示图像 plt.show()
-
添加验证集
-
在每代结束后计算验证集的损失值与准确率
-
绘制训练集和测试集的准确率对比图:通过对比模型在训练集和测试集上的表现,可以大致判断模型是否过拟合。如果测试集上的性能远低于训练集,那么可能存在过拟合