8.2卷积实战:


from torchvision import datasets,transforms
import numpy as np
import matplotlib.pyplot as plt
import torch.nn as nn
import torch
 
data_path = '../data-unversioned/p1ch7/'
cifar10 = datasets.CIFAR10(data_path, train=True, download=True,transform=transforms.ToTensor())
cifar10_val = datasets.CIFAR10(data_path, train=False, download=True,transform=transforms.ToTensor())

label_map = {0:0,2:1}
class_names = ["airplane","bird"]
cifar2 = [(img,label_map[label])for img,label in cifar10 if label in [0,2]]
cifar2_val = [(img,label_map[label])for img,label in cifar10_val if label in [0,2] ]

#8.2卷积实战

conv = nn.Conv2d(3,16,kernel_size=3)
print(conv)
print(conv.weight.shape,conv.bias.shape)

img, _ = cifar2[0]
output = conv(img.unsqueeze(0))
print(img.unsqueeze(0).shape,output.shape)

plt.imshow(output[0,0].detach(),cmap='gray')
plt.show()

#8.2.1填充边界

conv = nn.Conv2d(3,1,kernel_size=3,padding=1)
output = conv(img.unsqueeze(0))
print(img.unsqueeze(0).shape,output.shape)

#8.2.2用卷积检测特征
with torch.no_grad():
    conv.bias.zero_()

with torch.no_grad():
    conv.weight.fill_(1.0/9.0)

output = conv(img.unsqueeze(0))
plt.imshow(output[0,0].detach(),cmap='gray')
plt.show()

#8.2.3 深度和池化技术
pool = nn.MaxPool2d(2)
output = pool(img.unsqueeze(0))
print(img.unsqueeze(0).shape,output.shape)

#8.2.4 整合一切(书上原本代码就是跑不通的,因此会报错,此处仅供展示,运行时请注释掉)
model = nn.Sequential(
    nn.Conv2d(3,16,kernel_size=5,padding=2),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(16,8,kernel_size=5,padding=2),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Linear(8*8*8,32),
    nn.Tanh(),
    nn.Linear(32,2)
)

numel_list = [p.numel() for p in model.parameters()]
print(sum(numel_list),numel_list)

model(img.unsqueeze(0))#此处必定报错,报错为:RuntimeError: mat1 and mat2 shapes cannot be multiplied (64x8 and 512x32)

此代码运行时在最后一步会报错,与书上报错内容一致,具体报错原因为输入张量(64*8)与需求张量(512*32)结构不符

8.3子类化nn.Module

from torchvision import datasets,transforms
import numpy as np
import matplotlib.pyplot as plt
import torch.nn as nn
import torch

#8.3.1将网络作为一个nn.module 注此处应和8.3.3代码互斥,即运行时请注释掉8.3.3代码,否则会报重复定义Net类的错
# class Net(nn.Module):
#     def __init__(self):
#         super().__init__()
#         self.conv1 = nn.Conv2d(3,16,kernel_size=3,padding=1)
#         self.act1 = nn.Tanh()
#         self.pool1 = nn.MaxPool2d(2)
#         self.conv2 = nn.Conv2d(16,8,kernel_size=3,padding=1)
#         self.act2 = nn.Tanh()
#         self.pool2 = nn.MaxPool2d(2)
#         self.fc1 = nn.Linear(8*8*8,32)
#         self.act3 = nn.Tanh()
#         self.fc2 = nn.Linear(32,2)
    
#     def forward(self,x):
#         out = self.pool1(self.act1(self.conv1(x)))
#         out = self.pool2(self.act2(self.conv2(out)))
#         out = out.view(-1,8*8*8)
#         out = self.act3(self.fc1(out))
#         out = self.fc2(out)
#         return out


#8.3.2查看网络参数  运行时解除8.3.1代码注释
# model = Net()
# numel_list = [p.numel() for p in model.parameters()]
# print("输出网络总参数:",sum(numel_list),"网络各部分参数:",numel_list)

#8.3.3函数式API 注此处应和8.3.1代码互斥,即运行时请注释掉8.3.1代码,否则会报重复定义Net类的错
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3,16,kernel_size=3,padding=1)
        self.conv2 = nn.Conv2d(16,8,kernel_size=3,padding=1)
        self.fc1 = nn.Linear(8*8*8,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = F.max_pool2d(torch.tanh(self.conv1(x)),2)
        out = F.max_pool2d(torch.tanh(self.conv2(out)),2)
        out = out.view(-1,8*8*8)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out

data_path = '../data-unversioned/p1ch7/'
cifar10 = datasets.CIFAR10(data_path, train=True, download=True,transform=transforms.ToTensor())
cifar10_val = datasets.CIFAR10(data_path, train=False, download=True,transform=transforms.ToTensor())

label_map = {0:0,2:1}
class_names = ["airplane","bird"]
cifar2 = [(img,label_map[label])for img,label in cifar10 if label in [0,2]]
cifar2_val = [(img,label_map[label])for img,label in cifar10_val if label in [0,2] ]

img, _ = cifar2[0]

model = Net()
print(model(img.unsqueeze(0)))
    

注意注释中提及的代码互斥问题

8.4训练convnet(CPU)

from torchvision import datasets,transforms
import torch.nn as nn
import torch
import torch.nn.functional as F
import datetime

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3,16,kernel_size=3,padding=1)
        self.conv2 = nn.Conv2d(16,8,kernel_size=3,padding=1)
        self.fc1 = nn.Linear(8*8*8,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = F.max_pool2d(torch.tanh(self.conv1(x)),2)
        out = F.max_pool2d(torch.tanh(self.conv2(out)),2)
        out = out.view(-1,8*8*8)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out
    
def training_loop(n_epochs,optimizer,model,loss_fn,train_loader):
    for epoch in range(1,n_epochs+1):
        loss_train = 0.0
        for imgs,labels in train_loader:
            outputs = model(imgs)

            loss = loss_fn(outputs,labels)

            optimizer.zero_grad()
            
            loss.backward()

            optimizer.step()

            loss_train += loss.item()
        if epoch == 1 or epoch % 10 == 0:
            print('{} Epoch {}, Training loss {}'.format(datetime.datetime.now(),epoch,loss_train/len(train_loader)))


#8.4.1测量精度
def validate(model,train_loader,val_loader):
    for name,loader in [('train',train_loader),('val',val_loader)]:
        correct =0
        total = 0
        with torch.no_grad():
            for imgs,labels in loader:
                outputs  = model(imgs)
                _,predicted = torch.max(outputs,dim=1)
                total += labels.shape[0]
                correct += int((predicted == labels).sum())
        
        print('Accuracy{}:{:.2f}'.format(name,correct/total))

data_path = '../data-unversioned/p1ch7/'
cifar10 = datasets.CIFAR10(data_path, train=True, download=True,transform=transforms.ToTensor())
cifar10_val = datasets.CIFAR10(data_path, train=False, download=True,transform=transforms.ToTensor())

label_map = {0:0,2:1}
class_names = ["airplane","bird"]
cifar2 = [(img,label_map[label])for img,label in cifar10 if label in [0,2]]
cifar2_val = [(img,label_map[label])for img,label in cifar10_val if label in [0,2] ]

train_loader = torch.utils.data.DataLoader(cifar2,batch_size=64,shuffle=True)#此处shuffle设置为True时打乱数据

model = Net()
optimizer = torch.optim.SGD(model.parameters(),lr=1e-2)
loss_fn = nn.CrossEntropyLoss()

training_loop(n_epochs=100,optimizer=optimizer,model=model,loss_fn=loss_fn,train_loader=train_loader)

train_loader = torch.utils.data.DataLoader(cifar2,batch_size=64,shuffle=False)
val_loader = torch.utils.data.DataLoader(cifar2_val,batch_size=64,shuffle=False)

validate(model,train_loader,val_loader)

#8.4.2保存加载模型
torch.save(model.state_dict(),data_path + 'bird_vs_airplane.pt')#保存模型

load_model = Net()#实例化模型
load_return = load_model.load_state_dict(torch.load(data_path+'bird_vs_airplane.pt'))#加载模型参数
print(load_return)
8.4.3GPU:
from torchvision import datasets,transforms
import torch.nn as nn
import torch
import torch.nn.functional as F
import datetime

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3,16,kernel_size=3,padding=1)
        self.conv2 = nn.Conv2d(16,8,kernel_size=3,padding=1)
        self.fc1 = nn.Linear(8*8*8,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = F.max_pool2d(torch.tanh(self.conv1(x)),2)
        out = F.max_pool2d(torch.tanh(self.conv2(out)),2)
        out = out.view(-1,8*8*8)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out
    
def training_loop(n_epochs,optimizer,model,loss_fn,train_loader):
    for epoch in range(1,n_epochs+1):
        loss_train = 0.0
        for imgs,labels in train_loader:
            imgs = imgs.to(device=device)#将数据移动到GPU或CPU上
            labels = labels.to(device=device)#将数据移动到GPU或CPU上
            outputs = model(imgs)

            loss = loss_fn(outputs,labels)

            optimizer.zero_grad()
            
            loss.backward()

            optimizer.step()

            loss_train += loss.item()
        if epoch == 1 or epoch % 10 == 0:
            print('{} Epoch {}, Training loss {}'.format(datetime.datetime.now(),epoch,loss_train/len(train_loader)))


#8.4.1测量精度
def validate(model,train_loader,val_loader):
    for name,loader in [('train',train_loader),('val',val_loader)]:
        correct =0
        total = 0
        with torch.no_grad():
            for imgs,labels in loader:
                imgs = imgs.to(device=device)#将数据移动到GPU或CPU上
                labels = labels.to(device=device)#将数据移动到GPU或CPU上
                outputs  = model(imgs)
                _,predicted = torch.max(outputs,dim=1)
                total += labels.shape[0]
                correct += int((predicted == labels).sum())
        
        print('Accuracy{}:{:.2f}'.format(name,correct/total))

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print('Using device:',device)

data_path = '../data-unversioned/p1ch7/'
cifar10 = datasets.CIFAR10(data_path, train=True, download=True,transform=transforms.ToTensor())
cifar10_val = datasets.CIFAR10(data_path, train=False, download=True,transform=transforms.ToTensor())

label_map = {0:0,2:1}
class_names = ["airplane","bird"]
cifar2 = [(img,label_map[label])for img,label in cifar10 if label in [0,2]]
cifar2_val = [(img,label_map[label])for img,label in cifar10_val if label in [0,2] ]

train_loader = torch.utils.data.DataLoader(cifar2,batch_size=64,shuffle=True)#此处shuffle设置为True时打乱数据

model = Net().to(device=device)
optimizer = torch.optim.SGD(model.parameters(),lr=1e-2)
loss_fn = nn.CrossEntropyLoss()

training_loop(n_epochs=100,optimizer=optimizer,model=model,loss_fn=loss_fn,train_loader=train_loader)

train_loader = torch.utils.data.DataLoader(cifar2,batch_size=64,shuffle=False)
val_loader = torch.utils.data.DataLoader(cifar2_val,batch_size=64,shuffle=False)

validate(model,train_loader,val_loader)

#8.4.2保存加载模型
torch.save(model.state_dict(),data_path + 'bird_vs_airplane.pt')#保存模型

load_model = Net().to(device=device)#实例化模型
load_return = load_model.load_state_dict(torch.load(data_path+'bird_vs_airplane.pt'))#加载模型参数
print(load_return)

8.5模型设计:

8.5.1增加内存容量:宽度
from torchvision import datasets,transforms
import torch.nn as nn
import torch
import torch.nn.functional as F

class NetWideth(nn.Module):#硬编码数字
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3,32,kernel_size=3,padding=1)
        self.conv2 = nn.Conv2d(32,16,kernel_size=3,padding=1)
        self.fc1 = nn.Linear(16*8*8,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = F.max_pool2d(torch.tanh(self.conv1(x)),2)
        out = F.max_pool2d(torch.tanh(self.conv2(out)),2)
        out = out.view(-1,16*8*8)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out
    
model = NetWideth()
numel_list = [p.numel() for p in model.parameters()]
print("输出网络总参数:",sum(numel_list),"网络各部分参数:",numel_list)

class NetWideth2(nn.Module):#非硬编码数字
    def __init__(self,n_chans1 = 32):
        super().__init__()
        self.n_chans1 = n_chans1
        self.conv1 = nn.Conv2d(3,n_chans1,kernel_size=3,padding=1)
        self.conv2 = nn.Conv2d(n_chans1,n_chans1//2,kernel_size=3,padding=1)
        self.fc1 = nn.Linear(8*8*n_chans1//2,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = F.max_pool2d(torch.tanh(self.conv1(x)),2)
        out = F.max_pool2d(torch.tanh(self.conv2(out)),2)
        out = out.view(-1,8*8*self.n_chans1//2)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out
    
model2 = NetWideth2()
numel_list2 = [p2.numel() for p2 in model2.parameters()]
print("输出网络总参数:",sum(numel_list2),"网络各部分参数:",numel_list2)
8.5.2模型收敛和泛化:
1.检查参数:权重惩罚:
def training_loop_l2reg(n_epochs,optimizer,model,loss_fn,train_loader):
    for epoch in range(1,n_epochs+1):
        loss_train = 0.0
        for imgs,labels in train_loader:
            imgs = imgs.to(device=device)#将数据移动到GPU或CPU上
            labels = labels.to(device=device)#将数据移动到GPU或CPU上
            outputs = model(imgs)

            loss = loss_fn(outputs,labels)

            l2_lambda = 0.001
            l2_norm = sum(p.pow(2.0).sum() for p in model.parameters())
            loss = loss + l2_lambda*l2_norm

            optimizer.zero_grad()
            
            loss.backward()

            optimizer.step()

            loss_train += loss.item()
        if epoch == 1 or epoch % 10 == 0:
            print('{} Epoch {}, Training loss {}'.format(datetime.datetime.now(),epoch,loss_train/len(train_loader)))
2.不依赖单一输入:Dropout:
class NetDropout(nn.Module):
    def __init__(self,n_chans1 = 32):
        super().__init__()
        self.n_chans1 = n_chans1
        self.conv1 = nn.Conv2d(3,n_chans1,kernel_size=3,padding=1)
        self.conv1_dropout = nn.Dropout2d(p=0.4)
        self.conv2 = nn.Conv2d(n_chans1,n_chans1//2,kernel_size=3,padding=1)
        self.conv2_dropout = nn.Dropout2d(p=0.4)
        self.fc1 = nn.Linear(8*8*n_chans1//2,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = F.max_pool2d(torch.tanh(self.conv1(x)),2)
        out = self.conv1_dropout(out)
        out = F.max_pool2d(torch.tanh(self.conv2(out)),2)
        out = self.conv2_dropout(out)
        out = out.view(-1,8*8*self.n_chans1//2)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out
3.批量归一化:
class NetBatchNorm(nn.Module):
    def __init__(self,n_chans1 = 32):
        super().__init__()
        self.n_chans1 = n_chans1
        self.conv1 = nn.Conv2d(3,n_chans1,kernel_size=3,padding=1)
        self.conv1_batchnorm = nn.BatchNorm2d(num_features=n_chans1)
        self.conv2 = nn.Conv2d(n_chans1,n_chans1//2,kernel_size=3,padding=1)
        self.conv2_batchnorm = nn.BatchNorm2d(num_features=n_chans1//2)
        self.fc1 = nn.Linear(8*8*n_chans1//2,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = self.conv1_batchnorm(self.conv1(x))
        out = F.max_pool2d(torch.tanh(self.conv1(x)),2)
        out = self.conv2_batchnorm(self.conv2(out))
        out = F.max_pool2d(torch.tanh(self.conv2(out)),2)
        out = out.view(-1,8*8*self.n_chans1//2)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out
8.5.3深度:
1.跳跃连接:
class NetRes(nn.Module):
    def __init__(self,n_chans1 = 32):
        super().__init__()
        self.n_chans1 = n_chans1
        self.conv1 = nn.Conv2d(3,n_chans1,kernel_size=3,padding=1)
        self.conv2 = nn.Conv2d(n_chans1,n_chans1//2,kernel_size=3,padding=1)
        self.conv3 = nn.Conv2d(n_chans1//2,n_chans1//2,kernel_size=3,padding=1)
        self.fc1 = nn.Linear(4*4*n_chans1//2,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = F.max_pool2d(torch.relu(self.conv1(x)),2)
        out = F.max_pool2d(torch.relu(self.conv2(out)),2)
        out1 = out
        out = F.max_pool2d(torch.relu(self.conv3(out))+out1,2)
        out = out.view(-1,4*4*self.n_chans1//2)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out
2.使用pytorch建立非常深的模型:
class ResBlock(nn.Module):
    def __init__(self,n_chans):
        super(ResBlock,self).__init__()
        self.conv = nn.Conv2d(n_chans,n_chans,kernel_size=3,padding=1,bias=False)
        self.batch_norm = nn.BatchNorm2d(num_features=n_chans)
        torch.nn.init.kaiming_normal_(self.conv.weight,nonlinearity='relu')
        torch.nn.init.constant_(self.batch_norm.weight,0.5)
        torch.nn.init.zeros_(self.batch_norm.bias)
    
    def forward(self,x):
        out = self.conv(x)
        out = self.batch_norm(out)
        out = torch.relu(out)
        return out+x

class NetResDeep(nn.Module):
    def __init__(self,n_chans1 = 32,n_blocks = 10):
        super().__init__()
        self.n_chans1 = n_chans1
        self.conv1 = nn.Conv2d(3,n_chans1,kernel_size=3,padding=1)
        self.resblocks = nn.Sequential(*(n_blocks*[ResBlock(n_chans=n_chans1)]))
        self.fc1 = nn.Linear(8*8*n_chans1,32)
        self.fc2 = nn.Linear(32,2)
    
    def forward(self,x):
        out = F.max_pool2d(torch.relu(self.conv1(x)),2)
        out = self.resblocks(out)
        out =F.max_pool2d(out,2)
        out = out.view(-1,8*8*self.n_chans1)
        out = torch.relu(self.fc1(out))
        out = self.fc2(out)
        return out

更多推荐