Pytorch-CNN¶
Step1. 导入所需的库¶
In [1]:
import torch
import torch.nn.functional as F
import torchvision.datasets as datasets
import torchvision.transforms as transforms
from torch import optim
from torch import nn
from torch.utils.data import DataLoader
from tqdm import tqdm
Step2. 定义卷积神经网络¶
In [2]:
class CNN(nn.Module):
def __init__(self, in_channels, num_classes=10):
"""
定义神经网络的层数
参数:
in_channels : int
输入图像的通道数,对于MNIST手写数据集, 这个是1
num_classes : int
我们做预测的分类的个数,对于MNIST手写数据集,这个是10
"""
super(CNN, self).__init__()
# 第一层:卷积层 1个输入通道,8个输出通道,3*3卷积核,步长为1, 填充为1
self.conv1 = nn.Conv2d(in_channels=in_channels, out_channels=8, kernel_size=3, stride=1, padding=1)
# 第二层:最大池化层 2*2的窗口,步长为2
self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
# 第三层:卷积层 8个输入通道,16个输出通道,3*3的卷积核,步长为1, 填充为1
self.conv2 = nn.Conv2d(in_channels=8, out_channels=16, kernel_size=3, stride=1, padding=1)
# 第四层: 全连接层
self.fc1 = nn.Linear(16 * 7 * 7, num_classes)
# 前向传播
def forward(self, x):
"""
参数:
x : torch.Tensor
输入的张量
返回值:
torch.Tensor
经过网络传播后的输出张量
激活函数:
ReLU
"""
x = F.relu(self.conv1(x)) #经过第一层卷积层后再经过激活函数
x = self.pool(x) #池化层
x = F.relu(self.conv2(x)) #经过第二层卷积层后再经过激活函数
x = self.pool(x) # 池化层
x = x.reshape(x.shape[0], -1) #Flatten操作
x = self.fc1(x) #全连接层
return x
Step3. 硬件设置¶
In [3]:
device = "cuda" if torch.cuda.is_available() else "cpu"
Step4. 定义超参数¶
In [4]:
input_size = 784 # 28x28 pixels
num_classes = 10 # 0-9
learning_rate = 0.001
batch_size = 64
num_epochs = 10
Step5. 加载数据¶
In [5]:
train_dataset = datasets.MNIST(root="./Dataset/", download=True, train=True, transform=transforms.ToTensor())
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_dataset = datasets.MNIST(root="./Dataset/", download=True, train=False, transform=transforms.ToTensor())
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=True)
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz Failed to download (trying next): HTTP Error 404: Not Found Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz to ./Dataset/MNIST\raw\train-images-idx3-ubyte.gz
100%|██████████| 9.91M/9.91M [00:29<00:00, 331kB/s]
Extracting ./Dataset/MNIST\raw\train-images-idx3-ubyte.gz to ./Dataset/MNIST\raw Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz Failed to download (trying next): HTTP Error 404: Not Found Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz to ./Dataset/MNIST\raw\train-labels-idx1-ubyte.gz
100%|██████████| 28.9k/28.9k [00:00<00:00, 91.9kB/s]
Extracting ./Dataset/MNIST\raw\train-labels-idx1-ubyte.gz to ./Dataset/MNIST\raw Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz Failed to download (trying next): HTTP Error 404: Not Found Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz to ./Dataset/MNIST\raw\t10k-images-idx3-ubyte.gz
100%|██████████| 1.65M/1.65M [00:12<00:00, 137kB/s]
Extracting ./Dataset/MNIST\raw\t10k-images-idx3-ubyte.gz to ./Dataset/MNIST\raw Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz Failed to download (trying next): HTTP Error 404: Not Found Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz to ./Dataset/MNIST\raw\t10k-labels-idx1-ubyte.gz
100%|██████████| 4.54k/4.54k [00:01<00:00, 4.35kB/s]
Extracting ./Dataset/MNIST\raw\t10k-labels-idx1-ubyte.gz to ./Dataset/MNIST\raw
Step6. 初始化网络¶
In [6]:
model = CNN(in_channels=1, num_classes=num_classes).to(device)
Step7. 定义损失和优化器¶
这里使用交叉熵损失进行分类(好像涉及到分类问题的大都用的是交叉熵损失),并用Adam优化器更新模型的权重
In [7]:
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
Step8. 训练网络¶
In [8]:
for epoch in range(num_epochs):
print(f"Epoch [{epoch + 1} / N{num_epochs}]")
for batch_index, (data, targets) in enumerate(tqdm(train_loader)):
# 把数据加载到设备上
data = data.to(device)
targets = targets.to(device)
# 前向传播
scores = model(data)
loss = criterion(scores, targets)
# 反向传播
optimizer.zero_grad()
loss.backward()
# 更新模型权重
optimizer.step()
Epoch [1 / N10]
100%|██████████| 938/938 [00:13<00:00, 71.01it/s]
Epoch [2 / N10]
100%|██████████| 938/938 [00:10<00:00, 90.37it/s]
Epoch [3 / N10]
100%|██████████| 938/938 [00:11<00:00, 84.95it/s]
Epoch [4 / N10]
100%|██████████| 938/938 [00:09<00:00, 95.81it/s]
Epoch [5 / N10]
100%|██████████| 938/938 [00:11<00:00, 82.45it/s]
Epoch [6 / N10]
100%|██████████| 938/938 [00:11<00:00, 83.59it/s]
Epoch [7 / N10]
100%|██████████| 938/938 [00:11<00:00, 83.94it/s]
Epoch [8 / N10]
100%|██████████| 938/938 [00:12<00:00, 77.47it/s]
Epoch [9 / N10]
100%|██████████| 938/938 [00:12<00:00, 73.14it/s]
Epoch [10 / N10]
100%|██████████| 938/938 [00:12<00:00, 74.16it/s]
Step9. 模型评估¶
In [13]:
def check_accuracy(loader, model):
"""
参数:
loader : DataLoader
model: nn.Module
"""
if loader.dataset.train:
print("Checking accruacy on training data")
else:
print("Checking accuracy on test data")
num_correct = 0
num_samples = 0
model.eval()
with torch.no_grad():
for x, y in loader:
x = x.to(device)
y = y.to(device)
# 前向传播
scores = model(x)
_, predictions = scores.max(1)
num_correct += (predictions == y).sum()
num_samples += predictions.size(0)
# 计算准确率
accuracy = float(num_correct) / float(num_samples) * 100
print(f"Got {num_correct} / {num_samples} with accuracy {accuracy:.2f}%")
model.train()
In [14]:
check_accuracy(train_loader, model)
Checking accruacy on training data Got 59428 / 60000 with accuracy 99.05%
In [15]:
check_accuracy(test_loader, model)
Checking accuracy on test data Got 9856 / 10000 with accuracy 98.56%