各类神经网络学习:(一)ResNet18、34、50的详细说明和代码展示

ResNet

一般的 CNN 网络,层数太深时,会出现梯度消失或梯度爆炸的情况,以及退化问题。而引入残差模块之后,就可以解决上述问题,使得网络层数很深,并且不会有退化问题。最常用的是 ResNet34ResNet50

1)Batch Normalization, BN

“批量标准化”。就是使每个通道的特征矩阵,满足均值为 0 0 0 ,方差为 1 1 1 的正态分布。

transforms.Normalize 的作用是类似的。 transforms.Normalize 文章链接:https://blog.csdn.net/qq_51409113/article/details/144155914

pytorch 官网中 nn.BatchNorm2d() 的链接:BatchNorm2d — PyTorch 2.5 文档

可以加快网络训练,以及提升准确率。使用原因:即使原来的输入已经调整成符合均值为 0 0 0 ,方差为 1 1 1 的正态分布了,但是通过卷积层之后的输出就不一定继续满足了。所以在每个卷积层之后,都会进行 BN 操作。

①为什么可以加快网络训练,以及提升准确率??

  • BN 通过归一化处理,将每一层的输入数据的均值接近 0 ,方差接近 1 ,这有助于减少内部协变量偏移(Internal Covariate Shift),即每一层输入分布的变化。这种稳定性使得网络参数的更新更加平滑,从而允许使用更大的学习率,进而加速训练过程。
  • BN 通过归一化处理,使得每一层的输入值都落在激活函数的敏感区域内,从而避免了梯度消失的问题。同时,由于BN引入了可学习的参数 γ \gamma γ β \beta β ,这些参数可以通过训练来调整输入值的分布,使得网络在保持稳定性的同时,也能适应不同的数据分布。这种灵活性有助于减少梯度爆炸的风险。
  • BN 通过在每个 mini-batch 中对输入数据进行归一化处理时,由于每个 mini-batch 的数据都是从总体样本中随机抽样的,因此不同 mini-batch 的均值和方差会有所不同,这就为网络的学习过程中增加了随机噪音。这种随机噪音在一定程度上对模型起到了正则化的效果,有助于减少过拟合的风险。(类似于 Dropout 的效果,这有助于防止过拟合,从而提升模型的泛化能力)

②公式:

y = x − E [ x ] V a r [ x ] + ϵ ⋅ γ + β y=\frac{x-E[x]}{\sqrt{Var[x]+\epsilon}}·\gamma+\beta y=Var[x]+ϵ xE[x]γ+β

其中 x x x 为输入的 batch_size 个图像的特征矩阵; E [ x ] E[x] E[x] 为输入特征矩阵的均值; V a r [ x ] Var[x] Var[x] 为输入特征矩阵的方差; ϵ \epsilon ϵ 为一个很小的数,用来防止分母为零的; γ \gamma γ 是系数; β \beta β 是偏置。

γ \gamma γ β \beta β 是可学习的参量,但一般默认 γ = 1 \gamma=1 γ=1 β = 0 \beta=0 β=0 ϵ \epsilon ϵ 通常也默认为 0 0 0 。所以默认下的公式为:
y = x − E [ x ] V a r [ x ] y=\frac{x-E[x]}{\sqrt{Var[x]}} y=Var[x] xE[x]

③函数调用:

bn1 = torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
  • num_features :输入通道数(该函数的输出通道数 = 输入通道数);
  • eps :就是 ϵ \epsilon ϵ 的值;
  • 其余的基本上不用管。
  • 示例:bn1 = torch.nn.BatchNorm2d(64)
  • 函数的输入格式为: (N,C,H,W) ,输出格式和输入格式相同

④注意点:

  • batch size 尽可能设置大点,设置小后表现可能很糟糕,设置的越大求的均值和方差越接近整个训练集的均值和方差。
  • 建议将 bn 层放在卷积层(Conv)和激活层(例如 Relu)之间(顺序: Conv -> bn -> Relu ),且卷积层不要使用偏置 bias,因为没有用。

⑤补充:

详细介绍可以参考 CSDN 博客链接(里面有公式和代码):https://blog.csdn.net/qq_37541097/article/details/104434557

2)残差块 (ResNet Block)

残差块有两种

  • 恒等映射残差块:

    使用场景:残差块的输入和输出的 shape 是相同的,可以直接相加。

  • 卷积映射残差块。

    使用场景:残差块的输入和输出的 shape 是不相同的,不可以直接相加。

    • 下采样卷积映射残差块:改变通道数、改变尺寸(stride>1)。
    • 非下采样卷积映射残差块:只改变通道数(stride=1)。

当残差块中只有两个 3×3 的卷积层时,叫做基础残差块 basic block ,通常用于 ResNet18ResNet34

ResNet50 以上版本,基本都是三个卷积层了,并且通常是首尾为 1×1 卷积层,中间为 3×3 卷积层。因为输出的形状,又被叫做 “瓶颈残差块” 。

①恒等映射残差块

实线

在这里插入图片描述

此残差块主要包括:卷积层、非线性激活层(ReLU激活函数)、BN 层、恒等映射。

恒等映射 identity mapping 又可以叫做捷径映射

【注意】其中的卷积层一般有两种:①高宽减半卷积层(步幅2)、②高宽不变的卷积层(步幅1)。

【注意】相加的两个部分,其 shape 要相等。

②卷积映射残差块

虚线

在这里插入图片描述

这种残差块,一般是用于大残差结构的连接处,其输入和输出不是同一个 shape 的情况,不能直接相加(做恒等映射)。因此需要使用一个卷积层,使其 shape 相同,方便相加。虚线处的卷积层,采用 1×1 Conv

【注意】其中的卷积层一般有两种:①高宽减半卷积层(步幅2)、②高宽不变的卷积层(步幅1)。

【注意】在ResNet网络中,通常将包含多个连续且相同的残差块的结构叫做一个大残差结构,例如ResNet34 结构:

在这里插入图片描述

【注意】残差块中的层是可变化的,可以根据自己的想法来进行调整、增删。

3)18/34/50/101/152层ResNet一览

在这里插入图片描述

【注意点】

  • ResNet 网络模型的输入图像的像素大小默认是 224×224 ;在训练和测试时最好要将数据集的尺寸变成 224×224 !!!

4)ResNet34

①对应的残差块

  • 恒等映射残差块:

在这里插入图片描述

  • 下采样卷积映射残差块:

在这里插入图片描述

②模型结构图

在这里插入图片描述

其中的 /2 表示的是尺寸减半,此时一般 stride=2

③模型pytorch代码

文件名为 NN_models.py

from torch import nn

# 18、34版本残差块
'''
@:param
		input_channels: 输入通道数
		num_channels: 输出通道数
		use_1x1conv: 是否使用 1x1conv (使用就说明是卷积映射残差块, 要变换尺寸)
		strides: 步长
'''
class Residual_primary(nn.Module):
	def __init__(self, input_channels, num_channels, use_1x1conv=False, strides=1):
		# 默认 use_1x1conv=False, strides=1
		super(Residual_primary, self).__init__()
		self.conv1 = nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1, stride=strides)
		self.bn1 = nn.BatchNorm2d(num_channels)  # BN层
		self.relu1 = nn.ReLU(inplace=True)  # Inplace ReLU to save memory
		self.conv2 = nn.Conv2d(num_channels, num_channels, kernel_size=3, padding=1)
		self.bn2 = nn.BatchNorm2d(num_channels)
		# 如果用了 1x1conv 就说明是卷积映射残差块, 要变换尺寸
		if use_1x1conv:
			self.conv3 = nn.Conv2d(input_channels, num_channels, kernel_size=1, stride=strides)
			self.bn3 = nn.BatchNorm2d(num_channels)
		else:
			self.conv3 = None

	def forward(self, X):
		Y = self.conv1(X)
		Y = self.bn1(Y)
		Y = self.relu1(Y)
		Y = self.conv2(Y)
		Y = self.bn2(Y)
		if self.conv3:
			X = self.conv3(X)
			X = self.bn3(X)  # If using 1x1 conv, also apply BN
		Y += X
		Y = nn.ReLU(inplace=True)(Y)  # Optionally, you can move this ReLU outside the Residual class
		return Y

# 初始卷积层和池化层
b1 = nn.Sequential(
	nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False),  # Removed bias since we have BN, 输入通道为3表示彩色图像
	nn.BatchNorm2d(64),
	nn.ReLU(inplace=True),
	nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)

# 大残差结构
'''
@:param
		input_channels: 输入通道数
		num_channels: 输出通道数
		num_residuals: 残差块的个数
		first_block: 是否是第一个大残差结构
@:return
		nn.Sequential(*blk): 大残差结构
'''
def resnet_block(in_channels, out_channels, num_residuals, first_block=False):
	blk = []
	for i in range(num_residuals):
		stride = 2 if i == 0 and not first_block else 1  # 从第二个大残差结构开始, 结构中的第一个残差块一般都会尺寸减半, 即 stride=2
		use_1x1conv = i == 0 and not first_block  # use_1x1conv = False/True, 从第二个大残差结构开始, 结构中的第一个残差块都是卷积映射残差块
		if i == 0:
			blk.append(Residual_primary(in_channels, out_channels, use_1x1conv=use_1x1conv, strides=stride))
		else:
			blk.append(Residual_primary(out_channels, out_channels, strides=stride))
	return nn.Sequential(*blk)


# ResNet-18
def resnet18(num_classes, in_channels=3):
	# net = nn.Sequential(b1)
	net = nn.Sequential(
		nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3, bias=False),
		nn.BatchNorm2d(64),
		nn.ReLU(inplace=True),
		nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
	)
	net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))
	net.add_module("resnet_block2", resnet_block(64, 128, 2))
	net.add_module("resnet_block3", resnet_block(128, 256, 2))
	net.add_module("resnet_block4", resnet_block(256, 512, 2))
	net.add_module("global_avg_pool", nn.AdaptiveAvgPool2d((1, 1)))
	net.add_module("fc", nn.Sequential(nn.Flatten(), nn.Linear(512, num_classes)))
	# Optionally, initialize weights here (e.g., nn.init.kaiming_normal_(net[0].conv1.weight, mode='fan_out', nonlinearity='relu'))
	return net


# ResNet-34
def resnet34(num_classes, in_channels=3):
	# 也可以使用语句 net = nn.Sequential(b1) 来代替下方
	net = nn.Sequential(
		nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3, bias=False),
		nn.BatchNorm2d(64),
		nn.ReLU(inplace=True),
		nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
	)
	net.add_module("resnet_block1", resnet_block(64, 64, 3, first_block=True))
	net.add_module("resnet_block2", resnet_block(64, 128, 4))
	net.add_module("resnet_block3", resnet_block(128, 256, 6))
	net.add_module("resnet_block4", resnet_block(256, 512, 3))
	net.add_module("global_avg_pool", nn.AdaptiveAvgPool2d((1, 1)))
	net.add_module("fc", nn.Sequential(nn.Flatten(), nn.Linear(512, num_classes)))
	# Optionally, initialize weights here
	return net

④测试代码

import torch
from torch import nn
import torchvision
from torchvision import transforms,datasets
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from NN_models import *
from PIL import Image
import time

# 检查CUDA是否可用,并设置设备为 GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

dataclass_transform = transforms.Compose([
	transforms.ToTensor(),
	transforms.Resize(224),
	transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 标准化
])


# 加载本地训练数据集
train_data_dir = 'E:\\4_Data_sets\\respiratory waveform\\train'  # 数据集根目录路径,自定义
train_image_datasets = datasets.ImageFolder(train_data_dir, transform=dataclass_transform)
train_dataloader = DataLoader(train_image_datasets, batch_size=7, shuffle=True)
train_classes = train_image_datasets.classes  # 获取类别名称
# 加载本地验证数据集
test_data_dir = 'E:\\4_Data_sets\\respiratory waveform\\val'  # 数据集根目录路径,自定义
test_image_datasets = datasets.ImageFolder(test_data_dir, transform=dataclass_transform)
test_dataloader = DataLoader(test_image_datasets, batch_size=7, shuffle=True)
test_classes = test_image_datasets.classes  # 获取类别名称


# 创建网络模型
resnet34_Instance = resnet34(3, 3).to(device)
# 定义损失函数
loss = nn.CrossEntropyLoss()
# 定义优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(resnet34_Instance.parameters(), lr=learning_rate, momentum=0.9)

# 开始训练
total_train_step = 0
first_train_step = 0
total_test_step = 0
epoch_sum = 15  # 迭代次数

# 添加tensorboard
writer = SummaryWriter('logs')

start_time = time.time()
last_epoch_time = time.time()  # 记录开始训练的时间

for i in range(epoch_sum):
	print("----------------第 {} 轮训练开始了----------------:".format(i + 1))

	# 训练步骤开始
	for data in train_dataloader:
		imgs, labels = data
		imgs, labels = imgs.to(device), labels.to(device)  # 将数据和目标移动到GPU
		outputs = resnet34_Instance(imgs)
		loss_real = loss(outputs, labels)  # 这里的损失变量 loss_real,千万别和损失函数 loss 相同,否则会报错!
		optimizer.zero_grad()
		loss_real.backward()
		optimizer.step()  # 更新模型参数

		total_train_step += 1
		# 表示第一轮训练结束,取每一轮的第一个batch_size来看看训练效果,下面的33,由训练数据集总数÷batch_size
		if total_train_step % 33 == 0:
			first_train_step += 1
			print("训练次数为:{}, loss为:{}".format(total_train_step, loss_real))  # 此训练次数非训练轮次
			# 每轮测试结束之后,计算训练的准确率,就拿第一个batch_size来看看准确率
			outputs_B = torch.argmax(outputs, dim=1)
			outputs_C = (labels == outputs_B).sum()  # True默认为1,False默认为0
			accuracy = (outputs_C.item() / len(outputs_B)) * 100
			accuracy = round(accuracy, 2)  # 2表示保留两位小数(四舍五入)
			print(f"训练正确率为:{accuracy}%")

			writer.add_scalar('first_batch_size', loss_real.item(), first_train_step)
		writer.add_scalar('total_batch_size', loss_real.item(), total_train_step)

	# 每训练一轮,就使用测试集看看训练效果
	total_test_loss = 0
	with torch.no_grad():
		for data in test_dataloader:
			imgs, labels = data
			imgs, labels = imgs.to(device), labels.to(device)
			outputs = resnet34_Instance(imgs)
			loss_fake = loss(outputs, labels)
			total_test_loss += loss_fake.item()
	print("整体测试集上的LOSS为:{}".format(total_test_loss))

	one_epoch_time = time.time()  # 记录训练一次的时间
	one_cost_time = one_epoch_time - last_epoch_time
	print(f"训练此轮需要的时间为:{one_cost_time}")
	last_epoch_time = one_epoch_time
	one_epoch_time = 0

end_time = time.time()
total_time = end_time - start_time
print(f"训练总计需要的时间为:{total_time}")

writer.close()

训练结果:

----------------1 轮训练开始了----------------:
训练次数为:33, loss为:5.042407512664795
训练正确率为:57.14%
整体测试集上的LOSS为:52.15475556533784
训练此轮需要的时间为:3.630617618560791
----------------2 轮训练开始了----------------:
训练次数为:66, loss为:0.6747639775276184
训练正确率为:42.86%
整体测试集上的LOSS为:11.183943532407284
训练此轮需要的时间为:3.354886293411255
----------------3 轮训练开始了----------------......
----------------15 轮训练开始了----------------:
训练次数为:495, loss为:1.8968732357025146
训练正确率为:71.43%
整体测试集上的LOSS为:0.9958151796163293
训练此轮需要的时间为:2.9634268283843994
训练总计需要的时间为:47.2208309173584

5)ResNet50

①对应的残差块

  • 下采样卷积映射残差块

    第一个大残差结构的第一个是非下采样卷积映射残差块,这里就不做赘述,可以直接看后面的整体结构图。

    ResNet50 中也有恒等映射残差块,但是这里也不再次展示了。

    这些残差块又叫做“瓶颈残差块”,因为中间的 3×3 卷积层的输出维度比两端小,故而得名。

    在这里插入图片描述

    第一个卷积层的作用是:使用 1x1 的卷积核,主要作用是降低特征图的维度(即通道数);

    第二个卷积层的作用是:使用 3x3 的卷积核进行特征提取;

    第三个卷积层的作用是:再次使用 1x1 的卷积核,以便与卷积映射结果(shortcut connection)进行相加。

注意原论文中残差结构的主分支上第一个 1x1 卷积层的步距是 2 ,第二个 3x3 卷积层步距是 1 。但在 pytorch 官方实现过程中是第一个 1x1 卷积层的步距是 1 ,第二个 3x3 卷积层步距是 2 ,这样能够在 imagenet 的上提升大概 0.5% 的准确率。

②模型结构图

在这里插入图片描述

其中 conv2_xconv3_x 这些就是大残差结构,其中包含了多个残差块,每个残差块里面都有三个卷积层。从 conv1conv5_x ,再加上初始卷积层和最后的全连接层,总共 50 层。模型结构图的详细展示如下,其中 s 表示 stride 的值,p 表示 padding 的值(非常无敌之全面工整) 。

在这里插入图片描述

③模型pytorch代码

# 50版本残差块
class Bottleneck(nn.Module):
	def __init__(self, in_channels, out_channels, strides=1, downsamples=False):
		super(Bottleneck, self).__init__()
		self.conv1 = nn.Conv2d(in_channels, out_channels // 4, kernel_size=1, bias=False)
		self.bn1 = nn.BatchNorm2d(out_channels // 4)
		self.relu1 = nn.ReLU(inplace=True)
		self.conv2 = nn.Conv2d(out_channels // 4, out_channels // 4, kernel_size=3, stride=strides, padding=1,
		                       bias=False)
		self.bn2 = nn.BatchNorm2d(out_channels // 4)
		self.relu2 = nn.ReLU(inplace=True)
		self.conv3 = nn.Conv2d(out_channels // 4, out_channels, kernel_size=1, bias=False)
		self.bn3 = nn.BatchNorm2d(out_channels)
		if downsamples:
			self.conv4 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=strides)
			self.bn4 = nn.BatchNorm2d(out_channels)
		else:
			self.conv4 = None

	def forward(self, x):
		identity = x  # 映射

		out = self.conv1(x)
		out = self.bn1(out)
		out = self.relu1(out)

		out = self.conv2(out)
		out = self.bn2(out)
		out = self.relu2(out)

		out = self.conv3(out)
		out = self.bn3(out)

		if self.conv4:
			identity = self.conv4(identity)
			identity = self.bn4(identity)

		out += identity
		out = nn.ReLU(inplace=True)(out)
		return out


# 大残差结构
def resnet_Bottleneck(in_channels, out_channels, num_residuals, first_block=False):
	blk = []
	for i in range(num_residuals):
		stride = 2 if i == 0 and not first_block else 1  # 第一个残差结构中的第一个残差块,只改变通道数,不作下采样
		downsample = i == 0  # downsample = False/True, 从第二个大残差结构开始, 结构中的第一个残差块都是下采样映射残差块
		blk.append(Bottleneck(in_channels, out_channels, strides=stride, downsamples=downsample))
		in_channels = out_channels
	return nn.Sequential(*blk)


# ResNet-50
def resnet50(num_classes, in_channels=3):
	net = nn.Sequential(
		nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3, bias=False),
		nn.BatchNorm2d(64),
		nn.ReLU(inplace=True),
		nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

	net.add_module("layer1", resnet_Bottleneck(64, 256, 3, first_block=True))
	net.add_module("layer2", resnet_Bottleneck(256, 512, 4))
	net.add_module("layer3", resnet_Bottleneck(512, 1024, 6))
	net.add_module("layer4", resnet_Bottleneck(1024, 2048, 3))
	net.add_module("global_avg_pool", nn.AdaptiveAvgPool2d((1, 1)))
	net.add_module("fc", nn.Sequential(nn.Flatten(), nn.Linear(2048, num_classes)))

	return net

④测试代码

上述模型源码保存在了 NN_models.py 文件中。

import torch
from torch import nn
import torchvision

from torchvision import transforms,datasets
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from NN_models import *
from PIL import Image
import time

# 检查CUDA是否可用,并设置设备为 GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

dataclass_transform = transforms.Compose([
	transforms.ToTensor(),
	transforms.Resize(224),
	transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 标准化
])


# 加载本地训练数据集, 自定义
train_data_dir = 'E:\\4_Data_sets\\respiratory waveform\\train'  # 数据集根目录路径
train_image_datasets = datasets.ImageFolder(train_data_dir, transform=dataclass_transform)
train_dataloader = DataLoader(train_image_datasets, batch_size=7, shuffle=True)
train_classes = train_image_datasets.classes  # 获取类别名称
# 加载本地验证数据集, 自定义
test_data_dir = 'E:\\4_Data_sets\\respiratory waveform\\val'  # 数据集根目录路径
test_image_datasets = datasets.ImageFolder(test_data_dir, transform=dataclass_transform)
test_dataloader = DataLoader(test_image_datasets, batch_size=7, shuffle=True)
test_classes = test_image_datasets.classes  # 获取类别名称


# 创建网络模型
resnet50_Instance = resnet50(3, 3).to(device)
# 定义损失函数
loss = nn.CrossEntropyLoss()
# 定义优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(resnet50_Instance.parameters(), lr=learning_rate, momentum=0.9)

# 开始训练
total_train_step = 0
first_train_step = 0
total_test_step = 0
epoch_sum = 15  # 迭代次数

# 添加tensorboard
writer = SummaryWriter('logs')

start_time = time.time()
last_epoch_time = time.time()  # 记录开始训练的时间

for i in range(epoch_sum):
	print("----------------第 {} 轮训练开始了----------------:".format(i + 1))

	# 训练步骤开始
	for data in train_dataloader:
		imgs, labels = data
		imgs, labels = imgs.to(device), labels.to(device)  # 将数据和目标移动到GPU
		outputs = resnet50_Instance(imgs)
		loss_real = loss(outputs, labels)  # 这里的损失变量 loss_real,千万别和损失函数 loss 相同,否则会报错!
		optimizer.zero_grad()
		loss_real.backward()
		optimizer.step()  # 更新模型参数

		total_train_step += 1
		# 表示第一轮训练结束,取每一轮的第一个batch_size来看看训练效果,下面的33,由训练数据集总数÷batch_size
		if total_train_step % 33 == 0:
			first_train_step += 1
			print("训练次数为:{}, loss为:{}".format(total_train_step, loss_real))  # 此训练次数非训练轮次
			# 每轮测试结束之后,计算训练的准确率,就拿第一个batch_size来看看准确率
			outputs_B = torch.argmax(outputs, dim=1)
			outputs_C = (labels == outputs_B).sum()  # True默认为1,False默认为0
			accuracy = (outputs_C.item() / len(outputs_B)) * 100
			accuracy = round(accuracy, 2)  # 2表示保留两位小数(四舍五入)
			print(f"训练正确率为:{accuracy}%")

			writer.add_scalar('first_batch_size', loss_real.item(), first_train_step)
		writer.add_scalar('total_batch_size', loss_real.item(), total_train_step)

	# 每训练一轮,就使用测试集看看训练效果
	total_test_loss = 0
	with torch.no_grad():
		for data in test_dataloader:
			imgs, labels = data
			imgs, labels = imgs.to(device), labels.to(device)
			outputs = resnet50_Instance(imgs)
			loss_fake = loss(outputs, labels)
			total_test_loss += loss_fake.item()
	print("整体测试集上的LOSS为:{}".format(total_test_loss))

	one_epoch_time = time.time()  # 记录训练一次的时间
	one_cost_time = one_epoch_time - last_epoch_time
	print(f"训练此轮需要的时间为:{one_cost_time}")
	last_epoch_time = one_epoch_time
	one_epoch_time = 0

end_time = time.time()
total_time = end_time - start_time
print(f"训练总计需要的时间为:{total_time}")

writer.close()

训练结果:

----------------1 轮训练开始了----------------:
训练次数为:33, loss为:1.0290035009384155
训练正确率为:57.14%
整体测试集上的LOSS为:22.40016353689134
训练此轮需要的时间为:7.299371719360352
----------------2 轮训练开始了----------------:
训练次数为:66, loss为:19.585474014282227
训练正确率为:28.57%
整体测试集上的LOSS为:26.81494735251181
训练此轮需要的时间为:4.151764392852783
----------------3 轮训练开始了----------------......
----------------15 轮训练开始了----------------:
训练次数为:495, loss为:0.00018018828995991498
训练正确率为:100.0%
整体测试集上的LOSS为:1.971629376304918
训练此轮需要的时间为:4.134536266326904
训练总计需要的时间为:65.64659333229065

下一篇
各类神经网络学习(二)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值