卷积神经网络
全连接神经网络
神经网络中全部是线性模型,是由线性模型串联起来的
全连接网络又叫全连接层
卷积神经网络
在全连接神经网络中,由于输入必须是一维向量,因此在处理图像时必须要对图像矩阵进行拉伸成一维的形式,这必然会导致损失一些空间信息
为了保证空间信息的完整性,因此我们需要使用卷积神经网络,直接出入图片,进行模型训练
基本步骤如下:
输入 – 卷积 – 下采样 – 卷积 – 下采样 – 全连接层 – 输出
其中,卷积和下采样工作称为特征提取 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) # 卷积层1self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5) # 卷积层2self.pooling = torch.nn.MaxPool2d(2) # 池化层self.fc = torch.nn.Linear(320, 10) # 全连接层def forward(self, x):batch_size = x.size(0)## 先输入卷积层 在输入池话层 最后输入到relu中做非线性变换# 1x = F.relu(self.pooling(self.conv1(x)))# 2x = F.relu(self.pooling(self.conv2(x)))## 全连接层# 先把输入平铺 flatten 操作x = x.view(batch_size, -1)x = self.fc(x)return xmodel = Net()########## 损失函数核优化器定义 ##########
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01, momentum=0.5)########## 模型训练 ##########
def train(epoch):running_loss = 0.0for batch_index, data in enumerate(train_loader, 0):inputs, target = dataoptimizer.zero_grad()# forwardoutputs = model(inputs)loss = criterion(outputs, target)# backwardloss.backward()# updateoptimizer.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 = 0total = 0with torch.no_grad():for data in test_loader:inputs, target = dataoutputs = 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) # 卷积层1self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5) # 卷积层2self.pooling = torch.nn.MaxPool2d(2) # 池化层self.fc = torch.nn.Linear(320, 10) # 全连接层def forward(self, x):batch_size = x.size(0)## 先输入卷积层 在输入池话层 最后输入到relu中做非线性变换# 1x = F.relu(self.pooling(self.conv1(x)))# 2x = F.relu(self.pooling(self.conv2(x)))## 全连接层# 先把输入平铺 flatten 操作x = x.view(batch_size, -1)x = self.fc(x)return xmodel = 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.0for batch_index, data in enumerate(train_loader, 0):inputs, target = data## 将数据放入设备inputs, target = inputs.to(device), target.to(device)optimizer.zero_grad()# forwardoutputs = model(inputs)loss = criterion(outputs, target)# backwardloss.backward()# updateoptimizer.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 = 0total = 0with 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()