此处附上两个实践代码。
1. b站刘二大人第10讲。
'''
Convolutional NN 卷积神经网络
1. 在Feature Extraction 特征提取阶段,是直接对图像做卷积运算、转换成向量的;
然后再用Full Connected NN 全连接网络进行Classification 分类。
'''
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
# prepare dataset
batch_size=64
transform=transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,),(0.3081,))]) # ToTensor():把灰度范围从0-255变换到0-1之间; transform.Normalize():把0-1变换到(-1,1。
train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
# train=: 如果为True,则从training.pt创建数据集否则来自test.pt。 download: 如果为True,从互联网下载数据集,然后将其放在根目录中;如果数据集已经下载,则不是再次下载。
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)
# design model using class
class Net(torch.nn.Module):
def __init__(self):
super(Net,self).__init__()
# 两个卷积层,通道1 -> 10 -> 20 ; 每一个卷积核的通道数量 == 输入通道的数量。这种卷积核的总数 == 输出通道的数量。
self.conv1=torch.nn.Conv2d(1,10,kernel_size=5) # “1”:输入的Channel,灰色图像是1维的;“10”:输出的Channel,即,第一个卷积层需要10个卷积核;kernel_size=5:卷积核大小是5*5;stride=:进行卷积运算时的步长,默认为1;bias=:卷积运算是否需要偏置bias,默认为False;padding=:卷积操作是否补0。
self.conv2=torch.nn.Conv2d(10,20,kernel_size=5)
self.pooling=torch.nn.MaxPool2d(2) # 池化层。
self.fc=torch.nn.Linear(320,10) # 全连接的线性层。 “320”的获取方式:x = x.view(batch_size, -1) print(x.shape) 可得到 (64,320) ;“64”:batch,“320”:进行全连接操作时,输入的特征维度。
def forward(self,x):
batch_size=x.size(0)
x=F.relu(self.pooling(self.conv1(x))) # 做卷积 -> 池化 -> relu。
x=F.relu(self.pooling(self.conv2(x)))
x=x.view(batch_size,-1) # 先做 view 是为了变成FCNN所需要的输入。此处输出320。
x=self.fc(x)
return x
model=Net()
# construct loss and optimizer
criterion=torch.nn.CrossEntropyLoss()
optimizer=optim.SGD(model.parameeters(),lr=0.01,momentum=0.5)
# training cycle forward\backward\update
def train(epoch):
running_loss=0.0
for batch_idx,data in enumerate(train_loader,0):
inputs,target=data
optimizer.zero_grad()
outputs=model(inputs)
loss=criterion(outputs,target)
loss.backward()
optimizer.step()
running_loss+=loss.item()
if batch_idx%300==299:
print('[%d, %5d] loss: %.3f' % (epoch+1, batch_idx+1, running_loss/300))
running_loss = 0.0
def test():
correct = 0
total = 0
with torch.no_grad():
for data in test_loader:
images, labels = data
outputs = model(images)
_, 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(10):
train(epoch)
test()
2. b站刘二大人第11讲。
'''
1. 减少代码冗余:构造函数/类;
2. CNN中常见的是卷积层convolution 池化层pooling 激活函数relu 三者的叠加使用;
*卷积层:在保留数据信息的同时减少空间;重要的两个性质是 “局部连接” 和 “参数共享” 。
**局部连接:如用一个3*3的窗口对一张9*9的图进行连接操作,窗口之外未连接的部分通过滑动窗口的方法来连接;
————这个方法就称作参数(filter)共享:用滑动窗口的方式将 filter 的值共享给原图的每一块区域进行卷积运算。
*池化层:常见的方法有average pooling、max pooling;
*激活函数relu:max(0,x)只要>=0的数,<0直接改写成0。
卷积(线性变换) 激活函数(非线性变换) 池化 这个过程若干次后,view打平,进入全连接层
👇
3. 全联接层Fully-Connected:对特征图进行纬度上面的改变;要求 窗口&原图 两者坐标一一对应;
*softmax函数:分类函数,输出每个类别对应的概率值;如三个类别[0.5,0.4,0.1]那么结果肯定是第一类。
4. 卷积层要求输入输出是四维张量(Batch, Channel, Width, Height),全连接层的输入与输出都是二维张量(Batch, Input_feature)。
5. 卷积(convolution)后,C(Channels)变,W(width)和H(Height)可变可不变,取决于是否padding。
subsampling(或pooling)后,C不变,W和H变。
'''
import torch
import torch.nn as nn
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
# prepare dataset
batch_size =64 # 一次训练所选择的样本数。
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) # 归一化,均值和方差
train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)
# design model using class
class InceptionA(nn.Module): # Inception 有4个分支,将其抽象成一个类。
def __init__(self,in_channels): # 定义写在 init 里,调用写在 forward 里。
super(InceptionA, self).__init__()
self.branch_pool=nn.Conv2d(in_channels,24,kernel_size=1) # 池化卷积:“24”是 输出的通道数。
self.branch1x1=nn.Conv2d(in_channels,16,kernel_size=1) # kernel_size: 卷积核大小,此处意味卷积大小为1*1
self.branch5x5_1=nn.Conv2d(in_channels,16,kernel_size=1)
self.branch5x5_2=nn.Conv2d(16,24,kernel_size=5,padding=2) # 因为 padding!=0 所以卷积后 w 和 h 都发生了变化。
self.branch3x3_1=nn.Conv2d(in_channels,16,kernel_size=1)
self.branch3x3_2=nn.Conv2d(16,24,kernel_size=3,padding=1) # 且上下左右都要填充,如 padding=1 时,32*32 --> 34*34 。
self.branch3x3_3=nn.Conv2d(24,24,kernel_size=3,padding=1)
def forward(self,x):
branch_pool=F.avg_pool2d(x,kernel_size=3,stride=1,padding=1) # 池化层。
branch_pool=self.branch_pool(branch_pool) # 卷积层。
branch1x1 = self.branch1x1(x)
branch5x5 = self.branch5x5_1(x)
branch5x5 = self.branch5x5_2(branch5x5)
branch3x3 = self.branch3x3_1(x)
branch3x3 = self.branch3x3_2(branch3x3)
branch3x3 = self.branch3x3_3(branch3x3)
# “concatenate” 拼接运算:先放进一个数组里;再调用 cat 沿着 dim=1 的维度进行concatenate。
outputs=[branch_pool,branch1x1,branch5x5,branch3x3]
return torch.cat(outputs,dim=1)
class Net(nn.Module): # 构造网络
def __init__(self):
super(Net,self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(88, 20, kernel_size=5) # “88” 是Inception后的输出通道数; 88 = 24x3 + 16
self.incep1 = InceptionA(in_channels=10) # 初始化,将 input_channels 设为10,与 conv1 中的10对应。
self.incep2 = InceptionA(in_channels=20) # 初始化,与 conv2 中的20对应。
self.mp = nn.MaxPool2d(2)
self.fc = nn.Linear(1408, 10) # inception输出的channel一律为88,且不改变图像的w和h(只有卷积池化会改变)。所以是 88*4*4=1408 。
def forward(self,x):
in_size=x.size(0)
x=F.relu(self.mp(self.conv1(x))) # 卷积 池化 激活 三件套。这步之后,output_channels=10 。
x=self.incep1(x) # 这步之后,output_channels=88 。
x=F.relu(self.mp(self.conv2(x)))
x=self.incep2(x)
x=x.view(in_size,-1) # view是为了FCNN做准备。
x=self.fc(x)
return x
model=Net()
# construct loss and optimizer
criterion=torch.nn.CrossEntropyLoss()
optimizer=optim.SGD(model.parameters(),lr=0.01,momentum=0.5)
#training cycle
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
if batch_idx % 300 == 299:
print('[%d, %5d] loss: %.3f' % (epoch+1, batch_idx+1, running_loss/300))
running_loss = 0.0
def test():
correct = 0
total = 0
with torch.no_grad():
for data in test_loader:
images, labels = data
outputs = model(images)
_, 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(10):
train(epoch)
test()