Pytorch实现CNN卷积神经网络

一、导入需要的包

import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
import copy
import time
import torch
import torch.nn as nn
import torch.utils.data as Data
from torchvision import transforms
from torchvision.datasets import FashionMNIST

其中,torch.nn :包含用于构建神经网络的模块和可扩展类的子包;

torchvision是pytorch中的一个图形库;torch.utils.data实现自由的数据读取。

二、数据导入

我们以FashionMNIST为例:

# 准备训练数据集
train_data = FashionMNIST(
    root="./FashionMNIST",  # 数据的路径
    train=True,  # 只使用训练数据集
    transform=transforms.ToTensor(),  # 用于对载入的图片数据进行类型转换,将之前构成 PIL 图片的数据转换成 Tensor 数据类型的变量,让 PyTorch 能够对其进行计算和处理。
    download=False
)

# 准备测试数据集
test_data = FashionMNIST(
    root="./FashionMNIST",  # 数据的路径
    train=False,  # 只使用训练数据集
    download=False
)

其中,root表示数据集保存的目录名称;train=true表示导入训练集;train=false表示导入测试集;transform=transforms.ToTensor()表示将对载入的图片数据进行类型转换,将之前构成 PIL 图片的数据转换成 Tensor数据类型的变量,让pytorch 能够对其进行计算和处理。

三、预处理

# 定义一个数据加载器
train_loader = Data.DataLoader(
    dataset=train_data,  # 使用的数据集
    batch_size=64,  # 批处理样本大小
    shuffle=False,  # 每次迭代前不打乱数据
    # num_workers=2,  # 使用两个进程
)

# 获取一个batch的数据
for step, (b_x, b_y) in enumerate(train_loader):
    if step > 0:
        break

# 一个batch里的图片可视化
batch_x = b_x.squeeze().numpy()
batch_y = b_y.numpy()
class_label = train_data.classes
plt.figure(figsize=(12, 5))
for ii in np.arange(len(batch_y)):
    plt.subplot(4, 16, ii+1)
    plt.imshow(batch_x[ii, :, :], cmap=plt.cm.gray)
    plt.title(class_label[batch_y[ii]], size=9)
    plt.axis("off")
    plt.subplots_adjust(wspace=0.05)
plt.show()

# 测试集数据预处理
test_data_x = test_data.data.type(torch.FloatTensor)/255.0
test_data_x = torch.unsqueeze(test_data_x, dim=1)  # [10000, 1, 28, 28]
test_data_y = test_data.targets  # 测试集的标签 [10000]

Data.DataLoader加载数据,batch_size为批量处理样本大小;shuffle是将列表的所有元素随机排序。

torch.unsqueeze函数将在指定维度插入新的维度;test_data.data提取训练数据;train_data.classes提取标签。

四、网络结构的定义

通过继承nn.Module父类来定义CNN子类,包含卷积层、池化层、全连接层结构和向前传播过程。

在此我们将搭建一个具有两层卷积层的网络结构,卷积后使用relu激活函数进行激活,采用平均池化,两个全连接层分别包含256和128个神经元,最后的分类器包含10个神经元。

# 定义网络结构
class MyConvNet(nn.Module):
    def __init__(self):
        super(MyConvNet, self).__init__()

        # 定义第一个卷积层
        self.conv1 = nn.Sequential(
            nn.Conv2d(  
                in_channels=1,    # 输入的图片 (1,28,28)
                out_channels=16,  # 经过一个卷积层之后 (16,28,28)
                kernel_size=3,    # 卷积核尺寸
                stride=1,  
        # 如果想要 con2d 出来的图片长宽没有变化, padding=(kernel_size-1)/2 当 stride=1
                padding=1,
            ),
            nn.ReLU(),
            nn.AvgPool2d(
                        kernel_size=2,     # 平均池化层,2*2
                        stride=2,          # 池化步长2
                        ),                 # 经过池化层处理,维度为(16,14,14)
        )

        # 定义第二个卷积层
        self.conv2 = nn.Sequential(
            nn.Conv2d(  
                in_channels=16,     # 输入(16,14,14)
                out_channels=32,    # 输出(32,12,12)
                kernel_size=3,
                stride=1,
                padding=0,
            ),  # 输出(32,12,12)
            nn.ReLU(),
            nn.AvgPool2d(
                kernel_size=2,      # 平均池化层,2*2
                stride=2,           # 池化步长2
            ),                      # 输出(32,6,6)
        )

        self.classifier = nn.Sequential(
            nn.Linear(32 * 6 * 6, 256),
            nn.ReLU(),
            nn.Linear(256,128),
            nn.ReLU(),
            nn.Linear(128, 10),
        )

    #  定义网络向前传播路径
    def forward(self, x):
        x = self.conv1(x)            # (batch_size,16,14,14)
        x = self.conv2(x)            # (batch_size,32,6,6)
        x = x.view(x.size(0), -1)    #  (batch_size,32*6*6)
        output = self.classifier(x)  #  (batch_size,10)
        return output

myconvnet = MyConvNet()

五、模型训练

定义训练函数train_model(),采用80%的batch进行训练,20%的batch进行验证。

在每一个epoch中分别有两个过程,当step < train_batch_num,进入训练模式;当step >= train_batch_num,进入验证模式。

这里的训练模式和验证模式的意义参考官方文档:

由于在验证或者测试时,我们不需要模型中的某些层起作用或者不希望某些层的参数被改变(i.e. BatchNorm层、Dropout层),这时就需要设置成model.eval()模式;但是在训练模型的时候又希望这些层起作用,这时候就需要用到model.train()模式。

其中,每一个epoch中损失函数loss需要叠加,最后除以训练总数计算平均损失。loss.item()将单个元素tensor转化为int或者float,避免了计算图中loss的叠加,从而避免显存爆炸。

# 定义网络训练过程函数
def train_model(model, traindataloader, train_rate, criterion, optimizer, num_epochs):
    
    batch_num = len(traindataloader)                   # 获取训练总batch数
    train_batch_num = round(batch_num * train_rate)    # 按照train_rate比例进行训练集的划分
    
    # 复制模型参数
    best_model_wts = copy.deepcopy(model.state_dict())
    best_acc = 0.0
    train_loss_all = []
    train_acc_all = []
    val_loss_all = []
    val_acc_all = []
    since = time.time()
    
    # 训练轮次循环
    for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs-1))

        train_loss = 0.0
        train_corrects = 0
        train_num = 0
        val_loss = 0.0
        val_corrects = 0
        val_num = 0

        # 每个epoch有两个训练阶段
        for step, (b_x, b_y) in enumerate(traindataloader):
            if step < train_batch_num:         # 训练集
                model.train()                  # 设置模型为训练模式
                output = model(b_x)
                pre_lab = torch.argmax(output, 1)
                loss = criterion(output, b_y)  # 计算损失函数
                optimizer.zero_grad()          # 清空上一层梯度
                loss.backward()                # 反向传播
                optimizer.step()               # 更新优化器的学习率
                train_loss += loss.item() * b_x.size(0)
                train_corrects += torch.sum(pre_lab == b_y.data)
                train_num += b_x.size(0)
            else:
                model.eval()                   # 设置模型为评估模式
                output = model(b_x)
                pre_lab = torch.argmax(output, 1)
                loss = criterion(output, b_y)
                val_loss += loss.item() * b_x.size(0)
                val_corrects += torch.sum(pre_lab == b_y.data)
                val_num += b_x.size(0)

        # 计算一个epoch在训练集和验证集上的损失和精度
        train_loss_all.append(train_loss / train_num)
        train_acc_all.append(train_corrects.double().item()/train_num)
        val_loss_all.append(val_loss / val_num)
        val_acc_all.append(val_corrects.double().item()/val_num)

        print('{} Train Loss: {:.4f} Train Acc: {:.4f}'.format(epoch, train_loss_all[-1], 
train_acc_all[-1]))

        print('{} Val Loss: {:.4f} Val Acc: {:.4f}'.format(epoch, val_loss_all[-1], val_acc_all[-1]))

        # 当精度创新高,复制模型最高精度下的参数
        if val_acc_all[-1] > best_acc:
            best_acc = val_acc_all[-1]
            best_model_wts = copy.deepcopy(model.state_dict())
        time_use = time.time() - since
        print("Train and val complete in {:.0f}m {:.0f}s".format(time_use // 60, time_use % 60))

        # 使用最好模型的参数
    model.load_state_dict(best_model_wts)
    train_process = pd.DataFrame(
        data={"epoch": range(num_epochs),
              "train_loss_all":train_loss_all,
              "val_loss_all":val_loss_all,
              "train_acc_all": train_acc_all,
              "val_acc_all": val_acc_all,
              }
    )
    return model, train_process

六、模型训练

# 对模型进行训练
optimizer = torch.optim.Adam(myconvnet.parameters(), lr=0.01)
criterion = nn.CrossEntropyLoss()  # 损失函数
myconvnet, train_process = train_model(
    myconvnet, train_loader, 0.8,
    criterion, optimizer, num_epochs=25
)


# 对测试集进行预测,并可视化预测效果
myconvnet.eval()
output = myconvnet(test_data_x)
pre_lab = torch.argmax(output, 1)
acc = accuracy_score(test_data_y, pre_lab)

 七、结果可视化

# 训练结果可视化
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(train_process.epoch, train_process.train_loss_all, "ro-", label="train loss")
plt.plot(train_process.epoch, train_process.val_loss_all, "bs-", label="val loss")
plt.legend()
plt.xlabel("epoch")
plt.ylabel("loss")

plt.subplot(1, 2, 2)
plt.plot(train_process.epoch, train_process.train_acc_all, "ro-", label="train acc")
plt.plot(train_process.epoch, train_process.val_acc_all, "bs-", label="val acc")
plt.legend()
plt.xlabel("epoch")
plt.ylabel("acc")
plt.show()

# 计算混淆矩阵可视化
con_mat = confusion_matrix(test_data_y, pre_lab)
con_mat_df = pd.DataFrame(con_mat, index=class_label, columns=class_label)
plt.figure(figsize=(12, 12))
heatmap = sns.heatmap(con_mat_df, annot=True, fmt="d", cmap="YlGnBu")
heatmap.yaxis.set_ticklabels(heatmap.yaxis.set_ticklabels(class_label), rotation=0, ha='right')
heatmap.yaxis.set_ticklabels(heatmap.xaxis.set_ticklabels(class_label), rotation=45, ha='right')
plt.xlabel("True label")
plt.ylabel("Predicted label")
plt.show()

文中参数以及结果无参考价值,仅供展示。

文章仅作为学习用途,侵权删。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
PyTorch可以用来实现卷积神经网络CNN)。实现CNN的一般步骤如下: 1. 导入必要的库和模块,包括torch和torchvision。 2. 定义CNN模型的结构,通常包括卷积层、池化层和全连接层。你可以使用torch.nn模块中的类来定义这些层。 3. 初始化模型实例并将其移动到所选的设备(如CPU或GPU)上。 4. 定义损失函数,常用的是交叉熵损失函数(CrossEntropyLoss)。 5. 定义优化器,通常使用随机梯度下降优化器(SGD)。 6. 在训练数据上进行多个epochs的训练,其中每个epoch包括以下步骤: - 将输入数据传递给模型以获得预测输出。 - 计算预测输出和真实标签之间的损失。 - 使用反向传播计算梯度并更新模型参数。 7. 在测试数据上评估模型的性能,通常使用准确率作为评估指标。 下面是一个示例代码,展示了如何使用PyTorch实现一个简单的卷积神经网络: ```python import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms # 定义CNN模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.pool(torch.relu(self.conv1(x))) x = self.pool(torch.relu(self.conv2(x))) x = x.view(-1, 16 * 5 * 5) x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) x = self.fc3(x) return x # 数据预处理 transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # 加载数据集 trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2) # 初始化模型 net = Net() # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) # 训练模型 for epoch in range(2): running_loss = 0.0 for i, data in enumerate(trainloader, 0): inputs, labels = data optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if i % 2000 == 1999: print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) running_loss = 0.0 print('Finished Training') # 在测试集上评估模型 testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2) correct = 0 total = 0 with torch.no_grad(): for data in testloader: images, labels = data outputs = net(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Accuracy of the network on the 10000 test images: %d %%' % ( 100 * correct / total)) # 相关问题:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值