上一篇文章用的全連接來(lái)對(duì)MNIST數(shù)據(jù)集做多分類的訓(xùn)練
全連接的缺點(diǎn)有:
全連接參數(shù)過(guò)多冰蘑,會(huì)導(dǎo)致訓(xùn)練量過(guò)大
全連接把圖像展開(kāi)成一個(gè)向量,丟失了圖像原本的位置信息
全連接限制圖像的尺寸,而卷積則不關(guān)心圖像尺寸大小,只需要接受輸入的通道數(shù)揭北,輸出的通道數(shù)和卷積核大小即可確定圖像尺寸的變換過(guò)程,即
padding:對(duì)輸入圖片進(jìn)行填充胰蝠,一般用0填充违霞,padding=1,代表填充一圈弹谁,保證卷積前后的圖像尺寸大小一致乾巧,padding計(jì)算公式如下:
stride步長(zhǎng):指的是卷積核每次滑動(dòng)的距離大小
本文采用2d卷積來(lái)構(gòu)建深度網(wǎng)絡(luò)模型
1. 數(shù)據(jù)集構(gòu)建
每個(gè)像素點(diǎn)即每條數(shù)據(jù)中的值范圍為0-255句喜,有的數(shù)字過(guò)大不利于訓(xùn)練且難以收斂,故將其歸一化到(0-1)之間
# 數(shù)據(jù)集處理
# transforms.ToTensor()---shape從(H,W,C)->(C,H,W), 每個(gè)像素點(diǎn)從(0-255)映射到(0-1):直接除以255
# transforms.Normalize()---先將輸入歸一化到(0,1),像素點(diǎn)通過(guò)"(x-mean)/std",將每個(gè)元素分布到(-1,1)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(std=(0.1307,), mean=(0.3081,))
])
# 1.準(zhǔn)備數(shù)據(jù)集
train_dataset = datasets.MNIST(root="../DataSet/mnist",
train=True,
transform=transform,
download=True)
test_dataset = datasets.MNIST(root="../DataSet/mnist",
train=False,
transform=transform,
download=True)
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
2. 用Pytorch提供的DataLoader來(lái)加載數(shù)據(jù)集
# dataset:數(shù)據(jù)集 batch_size:mini-batch的大小 shuffle:是否打亂數(shù)據(jù)集順序
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
3.采用全連接的神經(jīng)網(wǎng)絡(luò)來(lái)構(gòu)建模型沟于,最后接Softmax來(lái)處理output
# 構(gòu)建網(wǎng)絡(luò)模型
class Module(torch.nn.Module):
def __init__(self):
super(Module, self).__init__()
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5, bias=False)
self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5, bias=False)
self.maxPooling = torch.nn.MaxPool2d(2)
self.fc = torch.nn.Linear(320, 10)
def forward(self, x):
# print(x.size()) == torch.Size([64, 1, 28, 28])
# 卷積---池化---激活函數(shù)
size = x.size(0)
x = F.relu(self.maxPooling(self.conv1(x)))
# print(x.size()) == torch.Size([64, 10, 12, 12])
x = F.relu(self.maxPooling(self.conv2(x)))
# print(x.size()) == torch.Size([64, 20, 4, 4])
# 數(shù)據(jù)扁平化處理,為接下來(lái)的全連接測(cè)做準(zhǔn)備
# Flatten data from (64, 20, 4, 4) to (64,320)
x = x.view(size, -1)
x = self.fc(x)
# 全連接層之后不需要跟激活函數(shù),因?yàn)榧せ詈瘮?shù) softmax 的作用包含在 CrossEntropyLoss 中
# softmax 函數(shù)的作用包含在 CrossEntropyLoss 中
return x
4. 構(gòu)建損失函數(shù)和優(yōu)化器
損失函數(shù)采用CrossEntropyLoss
優(yōu)化器采用 SGD 隨機(jī)梯度優(yōu)化算法
# 3.構(gòu)造損失器和優(yōu)化器
criterion = torch.nn.CrossEntropyLoss() # softmax 函數(shù)的作用包含在 CrossEntropyLoss 中,交叉熵算法
opt = optim.SGD(params=model.parameters(), lr=0.01, momentum=0.5)
# 動(dòng)態(tài)更新學(xué)習(xí)率------每隔step_size : lr = lr * gamma
schedule = optim.lr_scheduler.StepLR(opt, step_size=10, gamma=0.5, last_epoch=-1)
5.完整代碼
# -*- codeing = utf-8 -*-
# @Software : PyCharm
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
# Maxpooling: 最大池化,尋找每個(gè)空間的最大值然后組成一個(gè)新的圖像
# device = torch.device("cuda:0" if torch.cuda.is_available() else "CPU")
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
batch_size = 64
# transforms.ToTensor()---shape從(H,W,C)->(C,H,W), 每個(gè)像素點(diǎn)從(0-255)映射到(0-1):直接除以255
# transforms.Normalize()---先將輸入歸一化到(0,1),像素點(diǎn)通過(guò)"(x-mean)/std",將每個(gè)元素分布到(-1,1)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(std=(0.1307,), mean=(0.3081,))
])
# 1.準(zhǔn)備數(shù)據(jù)集
train_dataset = datasets.MNIST(root="../DataSet/mnist",
train=True,
transform=transform,
download=True)
test_dataset = datasets.MNIST(root="../DataSet/mnist",
train=False,
transform=transform,
download=True)
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
# 2.構(gòu)建網(wǎng)絡(luò)模型---模型是針對(duì)批次樣本的處理情況
class Module(torch.nn.Module):
def __init__(self):
super(Module, self).__init__()
# MNIST 數(shù)據(jù)集是灰度圖-單通道咳胃,所以輸入通道為 1
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5, bias=False)
self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5, bias=False)
self.maxPooling = torch.nn.MaxPool2d(2)
self.fc = torch.nn.Linear(320, 10)
def forward(self, x):
# print(x.size()) == torch.Size([64, 1, 28, 28])
# 卷積---池化---激活函數(shù)
size = x.size(0)
x = F.relu(self.maxPooling(self.conv1(x)))
# print(x.size()) == torch.Size([64, 10, 12, 12])
x = F.relu(self.maxPooling(self.conv2(x)))
# print(x.size()) == torch.Size([64, 20, 4, 4])
# 數(shù)據(jù)扁平化處理,為接下來(lái)的全連接測(cè)做準(zhǔn)備
# Flatten data from (64, 20, 4, 4) to (64,320)
x = x.view(size, -1)
x = self.fc(x)
# 全連接層之后不需要跟激活函數(shù),因?yàn)榧せ詈瘮?shù) softmax 的作用包含在 CrossEntropyLoss 中
# softmax 函數(shù)的作用包含在 CrossEntropyLoss 中
return x
model = Module()
model.to(device)
# 3.構(gòu)造損失器和優(yōu)化器
criterion = torch.nn.CrossEntropyLoss() # softmax 函數(shù)的作用包含在 CrossEntropyLoss 中,交叉熵算法
opt = optim.SGD(params=model.parameters(), lr=0.01, momentum=0.5)
# 動(dòng)態(tài)更新學(xué)習(xí)率------每隔step_size : lr = lr * gamma
schedule = optim.lr_scheduler.StepLR(opt, step_size=10, gamma=0.5, last_epoch=-1)
# 4.訓(xùn)練數(shù)據(jù)集
def train(epoch):
runing_loss = 0
for batch_idx, (inputs, target) in enumerate(train_loader, 0):
inputs, target = inputs.to(device), target.to(device)
opt.zero_grad()
y_pred_data = model(inputs)
loss = criterion(y_pred_data, target)
loss.backward()
opt.step()
runing_loss += loss.item()
if batch_idx % 300 == 299:
print("[%5d, %5d] loss: %f" % (epoch + 1, batch_idx + 1, runing_loss / 300))
runing_loss == 0.0
# 5.測(cè)試數(shù)據(jù)集
def verify():
correct = 0
total = 0
with torch.no_grad(): # 該語(yǔ)句下的所有tensor在進(jìn)行反向傳播時(shí),不會(huì)被計(jì)算梯度
for (images, labels) in test_loader:
images, labels = images.to(device), labels.to(device)
# 數(shù)據(jù)進(jìn)入模型進(jìn)行計(jì)算
outputs = model(images)
# 沿著維度為1的方向(行方向) 尋找每行最大元素的值與其下標(biāo)
_, predicted = torch.max(outputs.data, dim=1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print("Accuracy on test set: %d%%" % (100 * correct / total))
if __name__ == '__main__':
for epoch in range(15):
train(epoch)
verify()
# AlexNet: 卷積---池化---激活函數(shù)---全連接
# 使用 卷積 + 全連接 的神經(jīng)網(wǎng)絡(luò)的準(zhǔn)確率在 98% 左右
6.結(jié)果展示
result.png