卷积神经网络
全连接神经网络
神经网络中全部是线性模型,是由线性模型串联起来的
全连接网络又叫全连接层
卷积神经网络
在全连接神经网络中,由于输入必须是一维向量,因此在处理图像时必须要对图像矩阵进行拉伸成一维的形式,这必然会导致损失一些空间信息
为了保证空间信息的完整性,因此我们需要使用卷积神经网络,直接出入图片,进行模型训练
基本步骤如下:
输入 – 卷积 – 下采样 – 卷积 – 下采样 – 全连接层 – 输出
其中,卷积和下采样工作称为特征提取 feature extraction
,全连接层的工作叫做分类器 classification
图像
图像分为三个维度:
- 宽
- 高
- 通道(
r
、g
、b
)
即表示为 C × H × W C×H×W C×H×W
卷积的运算
单通道卷积
在输入中,拿出和卷积核大小一致的矩阵,与卷积核进行数乘,让后放到输出的第一个单元格内
然后使用卷积核依次对输入进行遍历,最终填满所有的输出
多通道卷积
每一个通道都配有一个卷积核,对每个通道进行卷积操作,最后把所有通道的输出进行相加,得到最终的输出
原始图像几个通道,卷积核就要有几层
对于多通道图像,图像可以看作是立体的,卷积核也可以看作是立体的,经过卷积之后把各通道的输出相加,最终得到一个二维的张量
为了在训练过程中使得输出也具有多个通道,因此使用多个3维的卷积核
卷积层的设计
卷积核
三要素:
- 输入的通道数
- 输出的通道数
- 卷积核尺寸的大小
# 实例化卷积层
conv_layer = torch.nn.Conv2d(in_channels, out_channels, kernel_size = kernel_size)
# 设置卷积核权重 (batch_sie, channel, w, h) = (1, 1, 3, 3)
kernel = torch.Tensor([1, 2, 3, 4, 5, 6, 7, 8, 9]).view(1, 1, 3, 3)
conv_layer.weight.data = kernel.data
通过卷积核后输出的大小: m × n × k e r n e l _ s i z e w i d t h × k e r n e l _ s i z e h e i g h t m×n×kernel\_size_{width}×kernel\_size_{height} m×n×kernel_sizewidth×kernel_sizeheight
其中, m m m为批量 b a t c h batch batch大小, n n n为通道数
padding
填充
通过设置padding
,即对原始图像进行填充,进而控制输出的大小,填充的部分一般默认用0代替
一般来说,如果卷积核是
3
×
3
3×3
3×3,则padding
一圈,如果卷积核是
5
×
5
5×5
5×5,则padding
两圈
卷积核大小为 n × n,则
padding
n / 2 n / 2 n/2(整除)圈
conv_layer = torch.nn.Conv2d(in_channels, # 输入通道数
out_channels, # 输出通道数
kernel_size = kernel_size, # 卷积核大小
padding = 1, # 填充层数
bias = False) # 不使用偏置
stride
步长
每次遍历原始图像时的步长,可以有效地降低图像的宽度和高度
conv_layer = torch.nn.Conv2d(1, 1,
kernel_size = 3, # 卷积核大小
padding = 1, # 填充层数
stride=2, #步长
bias = False) # 不使用偏置
下采样
一般使用最大池化层,即MaxPooling
最大池化层步长默认大小为 2 × 2 2×2 2×2
先将输入按照步长大小分割,形成多个部分,然后再在各个部分中取最大值,作为输出
maxpooling_layer = torch.nn.MaxPool2d(kernel_size=2)
代码实战
使用卷积神经网络处理MNIST
构造卷积神经网络如下所示:
- 卷积层不需要知道输入输出的大小
- 但最后的全连接层(线性模型)需要定义输出输出的大小
不使用GPU
版本
import torch
import matplotlib.pyplot as plt
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
########## 准备数据集 ##########
batch_size = 64
## 实例化转换器
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST(root='./dataset/mnist/',
train=True,
download=False,
transform=transform)
train_loader = DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset = datasets.MNIST(root='./dataset/mnist/',
train=False,
download=False,
transform=transform)
test_loader = DataLoader(test_dataset, batch_size=batch_size)
########## 定义模型 ##########
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5) # 卷积层1
self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5) # 卷积层2
self.pooling = torch.nn.MaxPool2d(2) # 池化层
self.fc = torch.nn.Linear(320, 10) # 全连接层
def forward(self, x):
batch_size = x.size(0)
## 先输入卷积层 在输入池话层 最后输入到relu中做非线性变换
# 1
x = F.relu(self.pooling(self.conv1(x)))
# 2
x = F.relu(self.pooling(self.conv2(x)))
## 全连接层
# 先把输入平铺 flatten 操作
x = x.view(batch_size, -1)
x = self.fc(x)
return x
model = Net()
########## 损失函数核优化器定义 ##########
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01, momentum=0.5)
########## 模型训练 ##########
def train(epoch):
running_loss = 0.0
for batch_index, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
# forward
outputs = model(inputs)
loss = criterion(outputs, target)
# backward
loss.backward()
# update
optimizer.step()
running_loss += loss.item()
if batch_index % 300 == 299:
print('[%d %5d] loss: %.3f' % (epoch + 1, batch_index + 1, running_loss / 300))
running_loss = 0.0
########## 模型测试 ##########
def test():
correct = 0
total = 0
with torch.no_grad():
for data in test_loader:
inputs, target = data
outputs = model(inputs)
_, predicted = torch.max(outputs.data, dim=1)
total += target.size(0)
correct += (predicted == target).sum().item()
print('Accuracy on test set: %d %%' % (100 * correct / total))
return 100 * correct / total
######### main ##########
if __name__ == '__main__':
accuracy_history = []
epoch_history = []
for epoch in range(50):
train(epoch)
accuracy = test()
accuracy_history.append(accuracy)
epoch_history.append(epoch)
plt.plot(epoch_history, accuracy_history)
plt.xlabel('epoch')
plt.ylabel('accuracy(%)')
plt.show()
使用GPU
版本
-
设置模型训练设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
-
把模型放入
GPU
中model.to(device)
-
把数据放入
GPU
中inputs, target = inputs.to(device), target.to(device)
import torch
import matplotlib.pyplot as plt
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
########## 准备数据集 ##########
batch_size = 64
## 实例化转换器
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST(root='./dataset/mnist/',
train=True,
download=False,
transform=transform)
train_loader = DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset = datasets.MNIST(root='./dataset/mnist/',
train=False,
download=False,
transform=transform)
test_loader = DataLoader(test_dataset, batch_size=batch_size)
########## 定义模型 ##########
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5) # 卷积层1
self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5) # 卷积层2
self.pooling = torch.nn.MaxPool2d(2) # 池化层
self.fc = torch.nn.Linear(320, 10) # 全连接层
def forward(self, x):
batch_size = x.size(0)
## 先输入卷积层 在输入池话层 最后输入到relu中做非线性变换
# 1
x = F.relu(self.pooling(self.conv1(x)))
# 2
x = F.relu(self.pooling(self.conv2(x)))
## 全连接层
# 先把输入平铺 flatten 操作
x = x.view(batch_size, -1)
x = self.fc(x)
return x
model = Net()
## 使用gpu
if torch.cuda.is_available():
print("使用gpu训练")
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
## 将模型放入设备
model.to(device)
########## 损失函数核优化器定义 ##########
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01, momentum=0.5)
########## 模型训练 ##########
def train(epoch):
running_loss = 0.0
for batch_index, data in enumerate(train_loader, 0):
inputs, target = data
## 将数据放入设备
inputs, target = inputs.to(device), target.to(device)
optimizer.zero_grad()
# forward
outputs = model(inputs)
loss = criterion(outputs, target)
# backward
loss.backward()
# update
optimizer.step()
running_loss += loss.item()
if batch_index % 300 == 299:
print('[%d %5d] loss: %.3f' % (epoch + 1, batch_index + 1, running_loss / 300))
running_loss = 0.0
########## 模型测试 ##########
def test():
correct = 0
total = 0
with torch.no_grad():
for data in test_loader:
inputs, target = data
## 将数据放入设备
inputs, target = inputs.to(device), target.to(device)
outputs = model(inputs)
_, predicted = torch.max(outputs.data, dim=1)
total += target.size(0)
correct += (predicted == target).sum().item()
print('Accuracy on test set: %d %%' % (100 * correct / total))
return 100 * correct / total
######### main ##########
if __name__ == '__main__':
accuracy_history = []
epoch_history = []
for epoch in range(50):
train(epoch)
accuracy = test()
accuracy_history.append(accuracy)
epoch_history.append(epoch)
plt.plot(epoch_history, accuracy_history)
plt.xlabel('epoch')
plt.ylabel('accuracy(%)')
plt.show()