2.线性模型
3.梯度下降算法
4.反向传播(用pytorch算梯度)
5.用pytorch实现线性回归
6.logistic回归
7.处理多维特征的输入
8.加载数据集
9.多分类问题
10.卷积神经网络(基础篇)
11.卷积神经网络(高级篇)_哔哩哔哩_bilibili
11.1 GoogleNet
GoogleNet其创新的Inception模块而闻名,这种模块允许网络在同一层次中进行不同尺寸的卷积操作,从而捕捉不同尺度的特征。
11.1.2 Inception模块
它的主要思想是在同一层次中使用不同大小的卷积核进行特征提取,从而能够捕捉不同尺度的特征。设计神经网络时以模块为单位去组装整个网络结构,在神经网络中多次重复使用该模块,因此将其封装为类,来减少代码量。该模块构成如下图所示:
11.1.3 1*1convolution
1x1卷积是一种在卷积神经网络中使用的特殊类型的卷积操作。它的主要作用是调整特征图的维度(即通道数),同时保持特征图的空间尺寸(高度和宽度),此外,它还能跨通道的特征整合(不同通道的卷积结果最后求和)。例如,数据集为3*3*3经过3*1*1的卷积操作后,输出结果为1*3*3,如下图所示:
1x1卷积的计算量相对较小,因为卷积核的大小为1x1,只需要进行简单的点乘和加法操作。这使得1x1卷积在深层网络中非常有用,可以作为其他卷积操作的降维或升维层,提高整体网络的计算效率。如下图所示:经过1*1卷积后下方操作次数减少许多
11.1.4 Inception模块的实现
课上的Inception模块结构如下:
- 平均池化路径:
- 3x3平均池化。
- 1x1卷积降维。
- 1x1卷积:用于降维。
- 5x5卷积路径:
- 1x1卷积降维。
- 5x5卷积提取特征。
- 3x3卷积路径:
- 1x1卷积降维。
- 3x3卷积提取特征。
- 3x3卷积提取特征。
最后再将每条路径所得张量,沿着channel的方向整合成一个张量
代码演示:
import torch
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt#1.准备数据
#1.1 定义transform
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307),(0.3081))])#1.2 下载数据
trainset = datasets.MNIST('../dataset/mnist',train=True,download=True,transform=transform)
#1.3 定义dataloader
train_loader = DataLoader(trainset,batch_size=64,shuffle=True)#1.4测试集
testset=datasets.MNIST(root='../dataset/mnist',train=False,download=True,transform=transform)
test_loader = DataLoader(testset,batch_size=64,shuffle=False)#2.定义网络#2.1 定义inception模块class Inception(torch.nn.Module):def __init__(self,in_channels):super(Inception,self).__init__()#2.1.1 平均池化路径,所用到的1x1的卷积层self.branch_pool = torch.nn.Conv2d(in_channels,24,kernel_size=1)#输入通道为in_channels,输出通道为24,卷积核大小为1x1#2.1.2 1x1卷积路径,仅用到1x1的卷积层self.branch1x1 = torch.nn.Conv2d(in_channels,16,kernel_size=1)#输入通道为in_channels,输出通道为16,卷积核大小为1x1#2.1.3 5x5卷积路径 ,用到1X1和5x5的卷积层self.branch5x5_1 = torch.nn.Conv2d(in_channels,16,kernel_size=1)#输入通道为in_channels,输出通道为16,卷积核大小为1x1self.branch5x5_2 = torch.nn.Conv2d(16,24,kernel_size=5,padding=2)#输入通道为16,输出通道为24,卷积核大小为5x5,padding为2(保证输出尺寸不变)#2.1.4 3x3卷积路径,用到1x1、3x3的卷积层、3x3的卷积层self.branch3x3_1 = torch.nn.Conv2d(in_channels,16,kernel_size=1)#输入通道为in_channels,输出通道为16,卷积核大小为1x1self.branch3x3_2 = torch.nn.Conv2d(16,24,kernel_size=3,padding=1)#输入通道为16,输出通道为24,卷积核大小为3x3,padding为1(保证输出尺寸不变)self.branch3x3_3 = torch.nn.Conv2d(24,24,kernel_size=3,padding=1)#输入通道为24,输出通道为24,卷积核大小为3x3,padding为1(保证输出尺寸不变)def forward(self,x):#2.1.1 平均池化路径,先经过平均池化,再经过1x1卷积branch_pool=F.avg_pool2d(x,kernel_size=3,stride=1,padding=1)#输入为x,池化核大小为3x3,步长为1,padding为1branch_pool=self.branch_pool(branch_pool)#输入为branch_pool,卷积核大小为1x1,输出通道为24#2.1.2 1x1卷积路径,直接经过1x1卷积branch1x1=self.branch1x1(x)#输入为x,卷积核大小为1x1,输出通道为16#2.1.3 5x5卷积路径,先经过1x1卷积,再经过5x5卷积branch5x5=self.branch5x5_1(x)#输入为x,卷积核大小为1x1,输出通道为16branch5x5=self.branch5x5_2(branch5x5)#输入为branch5x5,卷积核大小为5x5,输出通道为24,padding为2#2.1.4 3x3卷积路径,先经过1x1卷积,再经过3x3卷积,再经过3x3卷积branch3x3=self.branch3x3_1(x)#输入为x,卷积核大小为1x1,输出通道为16branch3x3=self.branch3x3_2(branch3x3)#输入为branch3x3,卷积核大小为3x3,输出通道为24,padding为1branch3x3=self.branch3x3_3(branch3x3)#输入为branch3x3,卷积核大小为3x3,输出通道为24,padding为1#2.1.5 将所有路径的输出拼接起来outputs=[branch_pool,branch1x1,branch5x5,branch3x3]return torch.cat(outputs,dim=1)#将outputs中的元素沿着dim=1方向(即通道维度)拼接起来,输出为(batch_size,88,28,28)#2.2 定义网络结构
class Net(torch.nn.Module):def __init__(self):super(Net,self).__init__()self.conv1=torch.nn.Conv2d(1,10,kernel_size=5)self.conv2=torch.nn.Conv2d(88,20,kernel_size=5)self.inception1=Inception(in_channels=10)self.inception2=Inception(in_channels=20)self.maxpool=torch.nn.MaxPool2d(2)self.fc1=torch.nn.Linear(88*4*4,10)def forward(self,x):#获取batch_sizein_size=x.size(0)x=F.relu(self.maxpool(self.conv1(x)))x=self.inception1(x)x=F.relu(self.maxpool(self.conv2(x)))x=self.inception2(x)x=x.view(in_size,-1)x=self.fc1(x)return xmodel=Net()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)#3.定义损失函数和优化器
criterion=torch.nn.CrossEntropyLoss()
optimizer=optim.SGD(model.parameters(),lr=0.01,momentum=0.5)#4.训练网络
#4.1 训练函数
def train(epoch):running_loss=0.0for i,data in enumerate(train_loader,0):inputs,labels=datainputs,labels=inputs.to(device),labels.to(device)optimizer.zero_grad()#forward + backward + updateoutputs=model(inputs)loss=criterion(outputs,labels)loss.backward()optimizer.step()running_loss+=loss.item()if i%300==299:print('[%d, %5d] loss: %.3f' %(epoch+1,i+1,running_loss/300))running_loss=0.0#4.2 测试函数
acuracy_list=[]
def Net_test():correct=0total=0with torch.no_grad():for data in test_loader:inputs,targets=datainputs,targets=inputs.to(device),targets.to(device)outputs=model(inputs)_,predicted=torch.max(outputs.data,1)total+=targets.size(0)correct+=predicted.eq(targets.data).sum().item()print('Accuracy of the network test : %.2f %%' % (100.0*correct/total))acuracy_list.append(100.0*correct/total)#4.开始训练
for epoch in range(10):train(epoch)Net_test()#5.绘制准确率变化图
epochs=list(range(len(acuracy_list)))
plt.plot(epochs,acuracy_list)
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Accuracy of the network')
plt.show()
运行结果:
11.2 ResidualNet(ResNet)
11.2.1深度带来的问题
- 一是vanishing/exploding gradient,导致了训练十分难收敛,这类问题能够通过normalized initialization 和intermediate normalization layers解决;
- 另一个是被称为degradation的退化现象。对合适的深度模型继续增加层数,模型准确率会下滑(不是overfit造成),training error和test error都会很高,相应的现象在CIFAR-10和ImageNet都有出现。
当梯度小于1时,由于backword()求梯度时遵循链式法则,致使梯度相乘后不断减小最终出现梯度消失,影响所求权重(),使得训练不充分,最终导致准确率下降。下图为深度增加而导致错误率增大
11.2.2 残差结构
普通直连的卷积神经网络和 ResNet 的最大区别在于,ResNet 有很多旁路的支线将输入直接连到后面的层,使得后面的层可以直接学习残差,这种结构也被称shortcut connections。
传统的卷积层或全连接层在信息传递时,或多或少会存在信息丢失、损耗等问题。ResNet 在某种程度上解决了这个问题,通过直接将输入信息绕道传到输出,保护信息的完整性,整个网络则只需要学习输入、输出差别的那一部分,简化学习目标和难度。注意:实线部分是深度未发生变化的连接,虚线部分是深度发生变化的连接。 对应深度有变化的连接有两种解决方案:
- 使用 zero-pading 进行提升深度 parameter-free。
- 使用 1*1的卷积核提升维度 有卷积核的运算时间。
两种方法,使用下面一种方法效果更好,但是运行会更耗时,一般还是更倾向于第一种方案节约运算成本。
11.2.3 残差块
在正常的神经网络中就是一层连一层,如下图所示:
假定某段神经网络的输入是 x,期望输出是 H(x),即 H(x) 是期望的复杂潜在映射,但学习难度大;如果我们直接把输入 x 传到输出作为初始结果,通过下图“shortcut connections”,那么此时我们需要学习的目标就是 F(x)=H(x)-x,于是 ResNet 相当于将学习目标改变了,不再是学习一个完整的输出,而是最优解 H(X) 和全等映射 x 的差值,即残差 F(x) = H(x) - x。
此时,梯度的计算公式,确保梯度大于等于1,就不会造成梯度消失。
11.2.4 ResNet 结构
Residual Block 实现
值得注意的是,我们需要输入通道和输出通道一致
第一层中先做卷积在做relu(conv1(x)),第二层中做卷积conv2(y),最后返回relu(x+y)
代码演示:
import torch
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt#1.准备数据
#1.1 定义transform
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307),(0.3081))])#1.2 下载数据
trainset = datasets.MNIST('../dataset/mnist',train=True,download=True,transform=transform)
#1.3 定义dataloader
train_loader = DataLoader(trainset,batch_size=64,shuffle=True)#1.4测试集
testset=datasets.MNIST(root='../dataset/mnist',train=False,download=True,transform=transform)
test_loader = DataLoader(testset,batch_size=64,shuffle=False)#2.定义网络
#2.1 定义残差块
class ResidualBlock(torch.nn.Module):def __init__(self, channels):super(ResidualBlock, self).__init__()self.channels = channelsself.conv1 =torch.nn.Conv2d(channels, channels,kernel_size=3, padding=1)self.conv2 = torch.nn.Conv2d(channels, channels,kernel_size=3, padding=1)def forward(self, x):y = F.relu(self.conv1(x))y = self.conv2(y)return F.relu(x + y)# 注:这里的x + y是残差单元的核心,即残差单元的输出等于输入与残差单元输出的和,# 其中残差单元输出经过两次卷积后与输入相加,再经过激活函数ReLU。#2.2 定义网络结构
class Net(torch.nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = torch.nn.Conv2d(1, 16, kernel_size=5)self.conv2 = torch.nn.Conv2d(16, 32, kernel_size=5)self.mp = torch.nn.MaxPool2d(2)self.rblock1 = ResidualBlock(16)self.rblock2 = ResidualBlock(32)self.fc =torch.nn.Linear(512, 10)def forward(self, x):in_size = x.size(0)x = self.mp(F.relu(self.conv1(x)))x = self.rblock1(x)x = self.mp(F.relu(self.conv2(x)))x = self.rblock2(x)x = x.view(in_size, -1)x = self.fc(x)return xmodel=Net()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)#3.定义损失函数和优化器
criterion=torch.nn.CrossEntropyLoss()
optimizer=optim.SGD(model.parameters(),lr=0.01,momentum=0.5)#4.训练网络
#4.1 训练函数
def train(epoch):running_loss=0.0for i,data in enumerate(train_loader,0):inputs,labels=datainputs,labels=inputs.to(device),labels.to(device)optimizer.zero_grad()#forward + backward + updateoutputs=model(inputs)loss=criterion(outputs,labels)loss.backward()optimizer.step()running_loss+=loss.item()if i%300==299:print('[%d, %5d] loss: %.3f' %(epoch+1,i+1,running_loss/300))running_loss=0.0#4.2 测试函数
acuracy_list=[]
def Net_test():correct=0total=0with torch.no_grad():for data in test_loader:inputs,targets=datainputs,targets=inputs.to(device),targets.to(device)outputs=model(inputs)_,predicted=torch.max(outputs.data,1)total+=targets.size(0)correct+=predicted.eq(targets.data).sum().item()print('Accuracy of the network test : %.2f %%' % (100.0*correct/total))acuracy_list.append(100.0*correct/total)#4.开始训练
for epoch in range(10):train(epoch)Net_test()#5.绘制准确率变化图
epochs=list(range(len(acuracy_list)))
plt.plot(epochs,acuracy_list)
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Accuracy of the network')
plt.show()
运行结果: