import torch
from torch import nn
from net import MyAlexNet
import numpy as np
from torch.optim import lr_scheduler
import os
from torchvision import transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 定义训练数据的根目录路径
ROOT_TRAIN = r'C:/Users/kk/PycharmProjects/pythonProject2/dataset/train/catdog1/data/train'
# 定义测试数据的根目录路径
ROOT_TEST = r'C:/Users/kk/PycharmProjects/pythonProject2/dataset/train/catdog1/data/val'
# 将图像的像素值归一化到【-1, 1】之间
normalize = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
# 定义训练数据的转换操作
train_transform = transforms.Compose([
transforms.Resize((224, 224)), # 将图像大小调整为 224x224
transforms.RandomVerticalFlip(), # 随机垂直翻转图像,增加数据多样性
transforms.ToTensor(), # 将图像转换为张量
normalize # 应用像素值归一化
])
# 定义验证数据的转换操作
val_transform = transforms.Compose([
transforms.Resize((224, 224)), # 将图像大小调整为 224x224
transforms.ToTensor(), # 将图像转换为张量
normalize # 应用像素值归一化
])
# 使用指定的根目录和转换操作创建训练数据集对象
train_dataset = ImageFolder(ROOT_TRAIN, transform=train_transform)
# 使用指定的根目录和转换操作创建验证数据集对象
val_dataset = ImageFolder(ROOT_TEST, transform=val_transform)
# 为训练数据集创建数据加载器,批量大小为 32,打乱数据顺序
train_dataloader = DataLoader(train_dataset, batch_size=32, shuffle=True)
# 为验证数据集创建数据加载器,批量大小为 32,打乱数据顺序
val_dataloader = DataLoader(val_dataset, batch_size=32, shuffle=True)
# 自动选择计算设备,如果有 CUDA 可用则使用 CUDA,否则使用 CPU
device = 'cuda' if torch.cuda.is_available() else 'cpu'
# 创建自定义的 MyAlexNet 模型,并将其移动到指定设备
model = MyAlexNet().to(device)
# 定义交叉熵损失函数
loss_fn = nn.CrossEntropyLoss()
# 使用随机梯度下降优化器来优化模型的参数,学习率为 0.01,动量为 0.9
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# 学习率每隔 10 轮变为原来的 0.5
lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)
# 定义训练函数
def train(dataloader, model, loss_fn, optimizer):
# 初始化损失、准确率累加器和样本数量
loss, current, n = 0.0, 0.0, 0
# 遍历数据加载器中的批次
for batch, (x, y) in enumerate(dataloader):
# 将图像和标签移动到指定设备
image, y = x.to(device), y.to(device)
# 模型进行前向传播得到输出
output = model(image)
# 计算当前批次的损失
cur_loss = loss_fn(output, y)
# 获取输出中的最大值索引作为预测结果
_, pred = torch.max(output, axis=1)
# 计算当前批次的准确率
cur_acc = torch.sum(y == pred) / output.shape[0]
# 反向传播前梯度清零
optimizer.zero_grad()
# 计算梯度
cur_loss.backward()
# 更新模型参数
optimizer.step()
# 累加损失和准确率
loss += cur_loss.item()
current += cur_acc.item()
n = n + 1
# 计算平均损失和准确率
train_loss = loss / n
train_acc = current / n
# 打印训练损失和准确率
print('train_loss' + str(train_loss))
print('train_acc' + str(train_acc))
# 返回训练损失和准确率
return train_loss, train_acc
# 定义验证函数
def val(dataloader, model, loss_fn):
# 将模型切换到评估模式
model.eval()
# 初始化损失、准确率累加器和样本数量
loss, current, n = 0.0, 0.0, 0
# 在不计算梯度的情况下进行计算
with torch.no_grad():
# 遍历数据加载器中的批次
for batch, (x, y) in enumerate(dataloader):
# 将图像和标签移动到指定设备
image, y = x.to(device), y.to(device)
# 模型进行前向传播得到输出
output = model(image)
# 计算当前批次的损失
cur_loss = loss_fn(output, y)
# 获取输出中的最大值索引作为预测结果
_, pred = torch.max(output, axis=1)
# 计算当前批次的准确率
cur_acc = torch.sum(y == pred) / output.shape[0]
# 累加损失和准确率
loss += cur_loss.item()
current += cur_acc.item()
n = n + 1
# 计算平均损失和准确率
val_loss = loss / n
val_acc = current / n
# 打印验证损失和准确率
print('val_loss' + str(val_loss))
print('val_acc' + str(val_acc))
# 返回验证损失和准确率
return val_loss, val_acc
# 定义绘制损失曲线的函数
def matplot_loss(train_loss, val_loss):
plt.plot(train_loss, label='train_loss') # 绘制训练集损失曲线
plt.plot(val_loss, label='val_loss') # 绘制验证集损失曲线
plt.legend(loc='best') # 显示图例,位置为最佳
plt.ylabel('loss') # 设置 y 轴标签为'loss'
plt.xlabel('epoch') # 设置 x 轴标签为'epoch'
plt.title("训练集和验证集loss值对比图") # 设置标题
plt.show() # 显示图像
# 定义绘制准确率曲线的函数
def matplot_acc(train_acc, val_acc):
plt.plot(train_acc, label='train_acc') # 绘制训练集准确率曲线
plt.plot(val_acc, label='val_acc') # 绘制验证集准确率曲线
plt.legend(loc='best') # 显示图例,位置为最佳
plt.ylabel('acc') # 设置 y 轴标签为'acc'
plt.xlabel('epoch') # 设置 x 轴标签为'epoch'
plt.title("训练集和验证集acc值对比图") # 设置标题
plt.show() # 显示图像
# 初始化用于存储训练和验证过程中的损失和准确率的列表
loss_train = []
acc_train = []
loss_val = []
acc_val = []
# 定义训练轮数
epoch = 20
min_acc = 0
# 进行训练
for t in range(epoch):
lr_scheduler.step() # 调整学习率
print(f"epoch{t + 1}\n-----------") # 打印当前轮数
# 进行训练并获取训练损失和准确率
train_loss, train_acc = train(train_dataloader, model, loss_fn, optimizer)
# 进行验证并获取验证损失和准确率
val_loss, val_acc = val(val_dataloader, model, loss_fn)
# 将损失和准确率添加到相应的列表中
loss_train.append(train_loss)
acc_train.append(train_acc)
loss_val.append(val_loss)
acc_val.append(val_acc)
# 如果当前验证准确率高于之前的最高准确率,保存模型权重
if val_acc > min_acc:
folder = 'save_model'
if not os.path.exists(folder):
os.mkdir('save_model')
min_acc = val_acc
print(f"save best model, 第{t + 1}轮")
torch.save(model.state_dict(), 'save_model/best_model.pth')
# 如果是最后一轮,保存模型权重
if t == epoch - 1:
torch.save(model.state_dict(), 'save_model/last_model.pth')
# 绘制损失曲线
matplot_loss(loss_train, loss_val)
# 绘制准确率曲线
matplot_acc(acc_train, acc_val)
print('Done!') # 打印训练完成的提示
import torch
from torch import nn
import torch.nn.functional as F
# 定义一个继承自nn.Module的类MyAlexNet
class MyAlexNet(nn.Module):
def __init__(self):
# 调用父类nn.Module的构造函数
super(MyAlexNet, self).__init__()
# 定义第一个卷积层,输入通道数为3,输出通道数为48,卷积核大小为11x11,步长为4,填充为2
self.c1 = nn.Conv2d(in_channels=3, out_channels=48, kernel_size=11, stride=4, padding=2)
# 定义ReLU激活函数
self.ReLU = nn.ReLU()
# 定义第二个卷积层,输入通道数为48,输出通道数为128,卷积核大小为5x5,步长为1,填充为2
self.c2 = nn.Conv2d(in_channels=48, out_channels=128, kernel_size=5, stride=1, padding=2)
# 定义最大池化层,池化窗口大小为2x2
self.s2 = nn.MaxPool2d(2)
# 定义第三个卷积层,输入通道数为128,输出通道数为192,卷积核大小为3x3,步长为1,填充为1
self.c3 = nn.Conv2d(in_channels=128, out_channels=192, kernel_size=3, stride=1, padding=1)
# 定义最大池化层,池化窗口大小为2x2
self.s3 = nn.MaxPool2d(2)
# 定义第四个卷积层,输入通道数为192,输出通道数为192,卷积核大小为3x3,步长为1,填充为1
self.c4 = nn.Conv2d(in_channels=192, out_channels=192, kernel_size=3, stride=1, padding=1)
# 定义第五个卷积层,输入通道数为192,输出通道数为128,卷积核大小为3x3,步长为1,填充为1
self.c5 = nn.Conv2d(in_channels=192, out_channels=128, kernel_size=3, stride=1, padding=1)
# 定义最大池化层,池化窗口大小为3x3,步长为2
self.s5 = nn.MaxPool2d(kernel_size=3, stride=2)
# 定义Flatten层,用于将多维张量展平成一维张量
self.flatten = nn.Flatten()
# 定义第一个全连接层,输入维度为4608,输出维度为2048
self.f6 = nn.Linear(4608, 2048)
# 定义第二个全连接层,输入维度为2048,输出维度为2048
self.f7 = nn.Linear(2048, 2048)
# 定义第三个全连接层,输入维度为2048,输出维度为1000
self.f8 = nn.Linear(2048, 1000)
# 定义第四个全连接层,输入维度为1000,输出维度为2
self.f9 = nn.Linear(1000, 2)
def forward(self, x):
# 通过第一个卷积层,然后应用ReLU激活函数
x = self.ReLU(self.c1(x))
# 通过第二个卷积层,然后应用ReLU激活函数
x = self.ReLU(self.c2(x))
# 通过最大池化层s2
x = self.s2(x)
# 通过第三个卷积层,然后应用ReLU激活函数
x = self.ReLU(self.c3(x))
# 通过最大池化层s3
x = self.s3(x)
# 通过第四个卷积层,然后应用ReLU激活函数
x = self.ReLU(self.c4(x))
# 通过第五个卷积层,然后应用ReLU激活函数
x = self.ReLU(self.c5(x))
# 通过最大池化层s5
x = self.s5(x)
# 将特征图展平成一维张量
x = self.flatten(x)
# 通过第一个全连接层f6,然后应用Dropout(概率为0.5)
x = self.f6(x)
x = F.dropout(x, p=0.5)
# 通过第二个全连接层f7,然后应用Dropout(概率为0.5)
x = self.f7(x)
x = F.dropout(x, p=0.5)
# 通过第三个全连接层f8,然后应用Dropout(概率为0.5)
x = self.f8(x)
x = F.dropout(x, p=0.5)
# 通过第四个全连接层f9
x = self.f9(x)
# 返回最终输出
return x
# 主函数入口
if __name__ == '__main__':
# 创建一个随机输入张量,形状为(1, 3, 224, 224),其中1表示批次大小,3表示输入通道数,224x224是图像尺寸
x = torch.rand([1, 3, 224, 224])
# 实例化MyAlexNet模型
model = MyAlexNet()
# 将随机输入通过模型
y = model(x)
# 输出模型的输出y,这通常是不需要的,这里只是为了测试
print(y)
TensorBoard 是 TensorFlow 提供的一个可视化工具,用于帮助理解、调试和优化深度学习模型的训练过程。
以下是 TensorBoard 的一些关键特点和用途:
-
监控训练指标:可以实时跟踪和展示训练过程中的各种指标,如损失值、准确率等。这使得能够直观地观察模型在训练过程中的性能变化。
- 例如,可以清晰地看到随着训练轮数的增加,损失值是如何逐渐下降,准确率是如何逐步上升的。
-
可视化模型结构:帮助理解模型的架构,包括各层的类型、连接方式和参数数量。
- 对于复杂的模型,这有助于确认模型的构建是否符合预期。
-
数据分布可视化:查看输入数据、权重等的分布情况,以便检测数据是否存在异常或不均衡。
-
图像和音频数据:如果模型处理图像或音频数据,可以直接在 TensorBoard 中展示这些数据。
-
嵌入向量可视化:对于生成嵌入向量的模型(如词嵌入),可以直观地观察向量的分布和关系。
-
直方图和分布图:展示参数(如权重、偏置)的直方图和分布变化,有助于分析模型的学习情况和是否存在梯度消失或爆炸等问题。
-
多运行比较:能够同时比较多个训练运行的结果,方便选择最优的超参数和模型结构。
使用 TensorBoard 通常需要在训练代码中添加适当的记录语句,将需要监控的数据写入特定的日志目录,然后通过命令行启动 TensorBoard 服务来查看可视化结果。
总的来说,TensorBoard 极大地增强了对深度学习模型训练过程的洞察力,有助于更高效地开发和优化模型。
明天开始 系统的听一下b站pytorch的教学,不管自己学过没,就当复习了