《深度学习入门之PyTorch》书籍阅读笔记
ISBN 978-7-121-32620-2
多层全连接神经网络
Pytorch基础
Tensor张量
- 是一个多维矩阵,pytorch的tensor可以和numpy的ndarray相互转换,但numpy的ndarray只能在CPU上运行。
- 不同数据类型的Tensor,torch.Tensor默认是torch.FloatTensor。
|数据类型|形式|
| :-: | :-: |
| 32位浮点型 | torch.FloatTensor |
| 64位浮点型 | torch.DoubleTensor |
| 16位整型 | torch.ShortTensor |
| 32位整型 | torch.IntTensor |
| 64位整型 | torch.LongTensor | - Tensor的简单使用
import numpy as np
import torch
#创建一个全是0的空Tensor或者取一个正态分布作为随机初始值
a = torch.zeros((3,2))
b = torch.randn((3,2))
print(a)
print(b)
#在Tensor和numpy.ndarray之间转换
numpy_b = b.numpy()
c = np.array([[2,3],[4,5]])
torch_c = torch.from_numpy(c)
float_torch_c = torch_c.float()
#将Tensor放到GPU上
if torch.cuda.is_available():
a_cuda = a.cuda()
print(a_cuda)
Variable变量
原本需要用Variable变量用于构建计算图进行反向传播,但新版torch似乎Tensor也有这些性质,不再需要专门的torch.autograd.Variable,PyTorch Variable与Tensor 【详解】
Variable变量有三个重要组成属性:data,grad和grad_fn。grad_fn表示的是得到这个Variable的操作,比如通过加减还是乘除来得到的。使用时要注意requires_grad,默认是False。
x = torch.autograd.Variable(torch.Tensor([1]),requires_grad = True)
a = torch.zeros((3,2))
a.requires_grad = True
y = x + a
y.backward()
如果对矩阵求导则不能直接用y.backward(),而是要传入参数声明。backward没有参数的时候,默认对标量求导,有参数的时候,相当是指定了后一层传过来的梯度,根据chain rule,乘即可。可以看到使用retain_graph,接着的求导会进行累加,相当是训练了多个batch,然后梯度进行了累加,然后再一次update参数,而不是每个batch都进行update。(来自Pytorch中backward函数)
Dataset数据集
- torch.utils.data.Dataset是一个抽象类,要定义自己的数据集只需要重写__init__,__len__和__getitem__这三个函数即可,其中__len__返回数据长度,__getitem__根据索引返回图片和标签。
- 通过torch.utils.data.DataLoader定义一个新的迭代器来读取数据
dataiter = torch.utils.data.DataLoader(myDataset,batch_size=32,shuffle=True,collate_fn=default_collate)#collate_fn表示如何取样本
- 在torchvision包中还有一个更高级的关于计算机视觉的数据读取类ImageFolder,主要功能是处理图片。
nn.Module模组
在pytorch里编写神经网络,所有的层结构和损失函数都来自于torch.nn,所有的模型结构都是从基类nn.Module继承的。
#使用模板
class net_name(nn.Module):
def __init__(self,other_arguments):
super(net_name,self).__init__()
self.conv1 = nn.Conv2d(in_channels,out_channels,kernel_size)
#其他结构
def forward(self,x):
x =self.conv1(x)
return x
torch.optim优化
优化算法分为一阶优化算法和二阶优化算法(也叫Hessian方法,主要基于牛顿法),二阶导数计算成本高,故没有广泛使用。
#定义优化器,梯度归零——反向传播——通过梯度进行参数更新
optimizer = torch.optim.SGD(model.parameters(),lr=0.01,momentum=0.9)
optimizer.zero_grad()
loss.backward()
optimizer.step()
模型的保存和加载
线性模型
一维线性回归
#一维线性回归的代码实现
import torch
import numpy as np
import torch.nn as nn
#训练数据
x_train = torch.rand((10,1))
y_train = torch.rand((10,1))
#建立模型
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression,self).__init__()
self.linear = nn.Linear(1,1)
def forward(self,x):
x = self.linear(x)
return x
model = LinearRegression()
#损失函数,使用均方误差函数
criterion = nn.MSELoss()
#定义优化器
optimizer = torch.optim.SGD(model.parameters(),lr=1e-3)
#模型训练
nums_epoch = 10
for epoch in range(nums_epoch):
inputs = x_train
target = y_train
# inputs.requires_grad = True
#out Tensor(10,1)
out = model(inputs)
loss = criterion(out,target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch+1) % 2 ==0:
print(f'Epoch{epoch+1}/{nums_epoch},loss:{loss.data}')
#模型测试
model.eval()#将模型变成测试模式,因为dropout和batchnormlization等层在训练和测试时是不一样的。
predict = model(x_train)
#绘图
predict = predict.data.numpy()
plt.plot(x_train.numpy(),y_train.numpy(),'ro',label='Original data')
plt.plot(x_train.numpy(),predict,label='Fitting Line')
plt.show()
多项式回归
#多项式回归模型
#参数方程 y=b+w1*x+w2*x^2+w3*x^3
#真实方程b=0.9,w1=0.5,w2=3,w3=2.4
import torch
import torch.nn as nn
from matplotlib import pyplot as plt
#预处理数据
def make_data(x):
'''将输入的数据变成[x,x^2,x^3],方便后续处理'''
x = x.unsqueeze(1)#增加一个维度在1维度 4->(4,1)
return torch.cat([x**i for i in range(1,4)],1) #将x分别2次方3次方,将三个矩阵在维度1上拼接
#定义真实函数
w_target = torch.Tensor([0.5,3,2.4]).unsqueeze(1)#3->(3,1)
b_target = torch.Tensor([0.9])
def f(x):
return x.mm(w_target)+b_target[0] #x.mm表示做矩阵乘法
#随机生成一些数据得到训练集
def get_batch(batch_size=4):
random = torch.randn(batch_size)
x = make_data(random)
y = f(x)
return x,y
#定义多项式模型
class ploy_model(nn.Module):
def __init__(self):
super(ploy_model,self).__init__()
self.ploy = nn.Linear(3,1)
def forward(self,x):
return self.ploy(x)
model = ploy_model()
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(),lr=1e-3)
#训练模型
epoch = 0
while True:
batch_x,batch_y = get_batch()
output = model(batch_x)
loss = criterion(output,batch_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch = epoch+1
print_loss = loss.data
if print_loss < 1e-3:
break
#模型测试
model.eval()#将模型变成测试模式,因为dropout和batchnormlization等层在训练和测试时是不一样的。
x_train,y_train = get_batch()
predict = model(x_train)
#绘图
predict = predict.data.numpy()
plt.plot(x_train.numpy(),y_train.numpy(),'ro',label='Original data')
plt.plot(x_train.numpy(),predict,label='Fitting Line')
plt.show()
x.mm(w_target)
分类问题
回归问题的预测结果是连续的,而分类问题的预测结果是离散的类别。
Logistic回归——Sigmoid函数
sigmoid函数 1 1 + e − x \frac{1}{1+e^{-x}} 1+e−x1
#二分类部分代码
out = model(x)
mask = out.ge(0.5).float()#判断输出结果如果>0.5就等于1,<0.5就等于0
correct = (mask==y).sum()
acc = correct.data/x.size[0]
一般来说logistic回归也可以处理多分类问题,但最常见的还是用于处理二分类问题上。
激活函数
Sigmoid函数
Tanh函数
ReLU函数
很大的负梯度经过RELU激活函数,更新参数之后会使得这个神经元不会对任何数据有激活现象?
Leaky ReLU函数
Maxout函数
神经网络的结构
一般而言,N层神经网络并不会吧输入层算进去,因此一个一层的神经网络是指没有隐藏层,只有输入输出层的网络结构,Logistic回归就是一个一层的神经网络。输出层一般是没有激活函数的,因为输出层通常表示一个类别的得分或者回归的一个实值的目标,所以输出层可以是任意的实数?
模型的表示能力与容量
各种优化算法的变式
梯度下降法
公式推导理论证明了梯度下降法的更新公式的有效性。
SGD
Momentum
Adagrad
RMSprop
Adam
处理数据和训练模型的技巧
数据预处理
权重初始化
防止过拟合
多层全连接神经网络实现MNIST手写数字分类
#多层全连接神经网络实现MNIST手写数字分类
import torch
from torch import nn,optim
from torch.utils.data import DataLoader
from torchvision import datasets,transforms
#三层全连接网络
class SimpleNet(nn.Module):
def __init__(self,in_dim,hidden_1,hidden_2,out_dim):
super(SimpleNet,self).__init__()
self.layer1 = nn.Sequential(nn.Linear(in_dim,hidden_1),nn.BatchNorm1d(hidden_1),nn.ReLU(True))#nn.Sequential将网络的层组合在一起
self.layer2 = nn.Sequential(nn.Linear(hidden_1,hidden_2),nn.BatchNorm1d(hidden_2),nn.ReLU(True))#nn.BatchNorm1d批标准化,加快收敛速度
#批标准化一般放在全连接层的后面,激活函数的前面?
self.layer3 = nn.Linear(hidden_2,out_dim)#最后一层输出层不能加激活函数
def forward(self,x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
return x
#超参数
batch_size = 4
learning_rate = 1e-2
num_epoch = 20
#数据预处理
data_tf = transforms.Compose(
[transforms.ToTensor(),#ToTensor()在转化过程中pytorch自动将图片标准化了,也就是说Tensor的范围在0~1
transforms.Normalize([0.5],[0.5])]#将图片转化到了-1~1之间
)
#读取数据集
train_dataset = datasets.MNIST(
root='./data',train=True,transform=data_tf,download=True)
test_dataset = datasets.MNIST(
root='./data',train=False,transform=data_tf,download=True)
train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True)
test_loader = DataLoader(test_dataset,batch_size=batch_size,shuffle=False)
#定义模型,损失函数,优化器
model = SimpleNet(in_dim=28*28,hidden_1=300,hidden_2=100,out_dim=10)#图片大小是28*28,一共0-9十个数字
if torch.cuda.is_available():
model = model.cuda()
criterion = nn.CrossEntropyLoss()#交叉熵损失函数
optimizer = optim.SGD(model.parameters(),lr=learning_rate)
#训练网络
for epoch in range(num_epoch):
for data in train_loader:
img,label = data
img = img.view(img.size(0),-1)#(4,1,28,28)->(4,784)
img.requires_grad = True
out = model(img)#(4,10)
loss = criterion(out,label)#一个数,是不是需要先取出10个中最大的那个作为预测结果再算loss?
optimizer.zero_grad()
loss.backward()
optimizer.step()
#模型测试
model.eval()
eval_loss = 0
eval_acc = 0
for data in test_loader:
img, label = data
img = img.view(img.size(0), -1)
out = model(img)
loss = criterion(out,label)
eval_loss += loss.data * label.size(0)
_,pred = torch.max(out,1)#result, indices = torch.max(out, 1)
num_correct = (pred==label).sum()
eval_acc += num_correct.data[0]
print(f'Test Loss:{eval_loss/len(test_dataset)}\nAcc:{eval_acc/len(test_dataset)}')
卷积神经网络
卷积神经网络的原理和结构
目标检测的三个观点
- 局部性:对于一张图片来说,需要检测图片的特征来决定图片的类别,但这些特征通常是由一些局部区域决定的,比如检测鸟类图片的鸟喙。
- 相同性:可以用同样的检测模式去检测不同图片的相同特征。
- 不变性:对于大图片下采样,那么图片的性质基本保持不变。
卷积神经网络
主要层结构有三个:卷积层、池化层、全连接层,其中卷积层和全连接层有参数,而激活层和池化层不含参数,这些参数通过梯度下降法来更新。
卷积层
池化层
逐渐降低数据体的空间尺寸,这样能够减少网络中参数的数量,减少计算资源耗费,同时可以有效控制过拟合。
全连接层
卷积神经网络的基本形式
PyTorch卷积模块
卷积层
self.conv = nn.Conv2d(in_channels=,out_channels=,kernel_size=,stride=,padding=,dilation=,groups=,bias=)
#bias默认是True,dilation是空洞卷积的参数
#groups表示输出数据体深度上和输入数据体深度上的联系,默认为1,即所有的输出和所有的输入都是相关联的,
#如果groups=2表示输入的深度被分割成两份,输出的深度也被分割成两份,它们之间分别对应起来。
池化层
self.pool = nn.MaxPool2d(kernel_size=,stride=,padding=,dilation=,return_indices=,ceil_mode=)
#其中return_indices表示是否返回最大值所处的下标,默认为False,ceil_mode表示使用一些方格代替层结构,默认False
self.avgpool = nn.AvgPool2d(count_include_pad=)
#其中多的参数count_include_pad表示计算均值时是否包含零填充,默认True
#除此之外还有nn.LPPool2d和nn.AdaptiveAvgPool2d
#简单的多层卷积神经网络
import torch
import torch.nn as nn
class SimpleCNN(nn.Module):
def __init__(self):
super(SimpleCNN,self).__init__()
layer1 = nn.Sequential()
layer1.add_module('conv1',nn.Conv2d(3,32,3,1,padding=1))
layer1.add_module('relu1',nn.ReLU(True))
layer1.add_module('pool1',nn.MaxPool2d(2,2))
self.layer1 = layer1
layer2 = nn.Sequential()
layer2.add_module('conv2',nn.Conv2d(32,64,3,1,padding=1))
layer2.add_module('relu2',nn.ReLU(True))
layer2.add_module('pool2',nn.MaxPool2d(2,2))
self.layer2 = layer2
layer3 = nn.Sequential()
layer3.add_module('conv3',nn.Conv2d(64,128,3,1,padding=1))
layer3.add_module('relu3',nn.ReLU(True))
layer3.add_module('pool3',nn.MaxPool2d(2,2))
self.layer3 = layer3
layer4 = nn.Sequential()
layer4.add_module('fc1',nn.Linear(2048,512))
layer4.add_module('fc_relu1',nn.ReLU(True))
layer4.add_module('fc2',nn.Linear(512,64))
layer4.add_module('fc_relu2',nn.ReLU(True))
layer4.add_module('fc3',nn.Linear(64,10))
self.layer4 = layer4
def forward(self,x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
fc_input = x.view(x.size(0),-1)
fc_out = self.layer4(fc_input)
return fc_out
model = SimpleCNN()
print(model)
提取层结构
#只提取模型的前两层
new_model = nn.Sequential(*list(model.children())[:2])
print(new_model)
#提取出模型中所有的卷积层
conv_model = nn.Sequential()
for layer in model.named_modules():
if isinstance(layer[1],nn.Conv2d):
conv_model.add_module(layer[0],layer[1])#报错,因为名称为layer1.conv1包含.
print(conv_model)
#children()会返回下一级模块的迭代器(layer1),而不会返回内部的东西(layer1.conv1)
#modules()会返回模型中所有模块的迭代器
#named_children(),named_modules()返回模快的迭代器及网络层的名字
如何提取参数及自定义初始化
for param in model.named_parameters():
print(param[0])
for m in model.modules():
if isinstance(m,nn.Conv2d):
nn.init.normal_(m.weight.data)#nn.init.normal 函数通过从具有指定均值和标准差的正态分布中采样值来初始化张量中的权重。这有助于打破权重的对称性,并防止神经网络在训练开始时陷入局部极小值。
nn.init.xavier_normal_(m.weight.data)#Xavier 正态初始化是一种权重初始化方案,旨��解决神经网络中的梯度消失和梯度爆炸问题。它通过将权重的方差初始化为 2 / (n_in + n_out) 来实现,其中 n_in 是输入特征的数量,n_out 是输出特征的数量。
nn.init.kaiming_normal_(m.weight.data)#Kaiming 正态初始化是一种权重初始化方案,旨在解决神经网络中的梯度消失和梯度爆炸问题。它通过将权重的方差初始化为 2 / (n_out * (kernel_size ** 2)) 来实现,其中 n_out 是输出特征的数量,kernel_size 是卷积核的大小。
m.bias.data.fill_(0)#fill_ 函数��张量中的所有元素替换为给定的值。在本例中,我们将偏差张量中的所有元素替换为 0。这通常在初始化神经网络或重置其权重时使用。
elif isinstance(m,nn.Linear):
m.weight.data.normal_()#m.weight.data.normal_() 是 PyTorch 中的一个函数,用于将模型 m 的权重张量中的所有元素替换为从标准正态分布中采样的值。
#named_parameters()给出网络层的名字和参数的迭代器
#paramters()会给出一个网络的全部参数的迭代器
卷积神经网络案例分析
AlexNet
#AlexNet网络 8层网络,11*11的滤波器
import torch
import torch.nn as nn
class AlexNet(nn.Module):
def __init__(self,num_classes):
super(AlexNet,self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3,64,11,4,2),
nn.ReLU(inplace=True),
nn.MaxPool2d(3,2),
nn.Conv2d(64,192,5,padding=2),
nn.ReLU(True),
nn.MaxPool2d(3,2),
nn.Conv2d(192,384,3,padding=1),
nn.ReLU(True),
nn.Conv2d(384,256,3,padding=1),
nn.ReLU(True),
nn.Conv2d(256,256,3,padding=1),
nn.ReLU(True),
nn.MaxPool2d(3,2),
)
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256*6*6,4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096,4096),
nn.ReLU(True),
nn.Linear(4096,num_classes),
)
def forward(self,x):
x = self.features(x)
x = x.view(x.size(0),256*6*6)
x = self.classifier(x)
return x
self._initialize_weights()
import torch
import torch.nn as nn
import torch.nn.init as init
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
# 定义模型的层
# ...
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
init.kaiming_normal_(m.weight, mode='fan_out')
if m.bias is not None:
init.constant_(m.bias, 0)
elif isinstance(m, nn.BatchNorm2d):
init.constant_(m.weight, 1)#批归一化层(nn.BatchNorm2d)使用常量初始化(权重初始化为 1,偏差初始化为 0)
init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
init.xavier_normal_(m.weight)
if m.bias is not None:
init.constant_(m.bias, 0)
VGGNet
VGGNet有16~19层网络,只使用33的卷积滤波器和22的池化层
GooLeNet
#GooLeNet 22层
import torch
import torch.nn as nn
import torch.nn.functional as F
class BasicConv2d(nn.Module):
def __init__(self,in_channels,out_channels,**kwargs):
super(BasicConv2d,self).__init__()
self.conv = nn.Conv2d(in_channels,out_channels,bias=False,**kwargs)
self.bn = nn.BatchNorm2d(out_channels,eps=0.001)
def forward(self,x):
x = self.conv(x)
x = self.bn(x)
return F.relu(x,inplace = True)#nn.ReLU 是一个神经网络模块,而 F.relu 是一个函数。
#用途: nn.ReLU 用于创建具有 ReLU 激活的层,而 F.relu 用于对张量应用 ReLU 激活。
class Inception(nn.Module):
def __init__(self,in_channels,pool_features):
super(Inception,self).__init__()
self.branch1x1 = BasicConv2d(in_channels,64,kernel_size=1)
self.branch5x5_1 = BasicConv2d(in_channels,48,kernel_size=1)
self.branch5x5_2 = BasicConv2d(48, 64, kernel_size=5,padding=2)
self.branch3x3db1_1 = BasicConv2d(in_channels, 64, kernel_size=1)
self.branch3x3db1_2 = BasicConv2d(64, 96, kernel_size=3,padding=1)
self.branch3x3db1_3 = BasicConv2d(96, 96, kernel_size=3,padding=1)
self.branch_pool = BasicConv2d(in_channels,pool_features,kernel_size=1)
def forward(self,x):
branch1x1 = self.branch1x1(x)
branch5x5 = self.branch5x5_1(x)
branch5x5 = self.branch5x5_2(branch5x5)
branch3x3db1 = self.branch3x3db1_1(x)
branch3x3db1 = self.branch3x3db1_2(branch3x3db1)
branch3x3db1 = self.branch3x3db1_3(branch3x3db1)
branch_pool = F.avg_pool2d(x,kernel_size=3,stride=1,padding=1)
branch_pool = self.branch_pool(branch_pool)
outputs = [branch1x1,branch5x5,branch3x3db1,branch_pool]
return torch.cat(outputs,1)
ResNet
#ResNet的残差模块
import torch
import torch.nn as nn
def conv3x3(in_planes,out_planes,stride=1):
'''3x3 conv with padding=1'''
return nn.Conv2d(in_planes,out_planes,kernel_size=3,stride=stride,padding=1,bias=False)
class BasicBlock(nn.Module):
def __init__(self,inplanes,planes,stride=1,downsample=None):
super(BasicBlock,self).__init__()
self.conv1 = conv3x3(inplanes,planes,stride)
self.bn1 = nn.BatchNorm2d(planes)
self.relu = nn.ReLU(True)
self.conv2 = conv3x3(planes,planes)
self.bn2 = nn.BatchNorm2d(planes)
self.downsample = downsample#downsample 模块用于在残差块的输入和输出之间进行下采样,如果输入和输出的特征图大小不同的话
self.stride =stride
def forward(self,x):
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample is not None:
residual = self.downsample(x)
out += residual
out = self.relu(out)
return out
上述模型在torchvision.model里面,同时大部分网络都有预训练好的参数。
图像增强的方法——提高模型的准确率和泛化能力
问题:对于计算机来说,光照,物体变形(比如猫换个姿势),物体被遮蔽只呈现局部的信息,这些都会让计算机难以识别。
解决:针对这些问题,我们希望对原始图片进行增强,在一定程度上解决部分问题。
#数据增强
import torchvision.transforms as transforms
train_transform = transforms.Compose([
transforms.Scale(40),
transforms.RandomHorizontalFlip(),
transforms.RandomCrop(32),
transforms.ToTensor(),
transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
])
test_transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
])
注意:只对训练图片进行图像增强,提高其泛化能力,对于测试集,仅对其中心化,不做其他的图像增强。
循环神经网络
RNN
卷积神经网络相当于人类的视觉,但它并没有记忆能力,没办法根据以前的记忆来处理新的任务。
循环神经网络可以很好解决短时依赖问题,但对长时依赖问题的表现不好。
LSTM和GRU——暂时用不到,没细看
循环神经网络的收敛性问题
Pytorch实现RNN,LSTM,GRU
实例介绍——用LSTM识别MNIST数据集
自然语言处理的应用
词嵌入
后面还有单词预测和词性判断的pytorch代码实现。CNN+RNN可以联合在一起完成图像描述任务。
生成对抗网络
生成模型
自动编码器
用全连接网络实现自动编码器
#用全连接网络实现自动编码器
import torch.nn as nn
class autoencoder(nn.Module):
def __init__(self):
super(autoencoder,self).__init__()
self.encoder = nn.Sequential(#先对MNIST的数据集做transforms.Normalize()变化使得图片大小变为-1~1之间
nn.Linear(28*28,128),
nn.ReLU(True),
nn.Linear(128,64),
nn.ReLU(True),
nn.Linear(64,12),
nn.ReLU(True),
nn.Linear(12,3)
)
self.decoder = nn.Sequential(
nn.Linear(3,12),
nn.ReLU(True),
nn.Linear(12,64),
nn.ReLU(True),
nn.Linear(64,128),
nn.ReLU(True),
nn.Linear(128,28*28),
nn.Tanh()#这个激活函数能够将最后的输出转换到-1~1之间,因为输入的图片已经变换到-1~1之间,这里输出必须和它对应
)
def forward(self,x):
x = self.encoder(x)
x = self.decoder(x)
return x
用卷积神经网络实现自动编码器
#用卷积神经网络实现自动编码器
import torch.nn as nn
class DCautoencoder(nn.Module):
def __init__(self):
super(DCautoencoder,self).__init__()
self.encoder = nn.Sequential(
nn.Conv2d(1,16,3,3,1),
nn.ReLU(True),
nn.MaxPool2d(2,2),
nn.Conv2d(16,8,3,2,1),
nn.ReLU(True),
nn.MaxPool2d(2,1)
)
self.decoder = nn.Sequential(
nn.ConvTranspose2d(8,16,3,2),#ConvTranspose2d看作卷积的反操作,可以在某种意义上看成是反卷积
nn.ReLU(True),
nn.ConvTranspose2d(16, 8, 5, 3,1),
nn.ReLU(True),
nn.ConvTranspose2d(8, 1, 2, 2, 1),
nn.Tanh()
)
def forward(self,x):
x = self.encoder(x)
x = self.decoder(x)
return x
变分自动编码器——没细看
https://arxiv.org/pdf/1606.05908.pdf
生成对抗网络——GAN——没细看
第一部分生成模型和自动编码器的解码部分很像,第二部分对抗模型是其创新点,也是其与自动编码器最大的区别。
书里有代码实现及理论推导和一些改进方法及应用。
深度学习实战
猫狗大战——运用预训练卷积神经网络进行特征提取和预测
迁移学习
代码实现
# 猫狗大战——迁移学习的三种方式
import torch.nn as nn
import torch.optim as optim
import torchvision.models as models
#方法1——加载预训练权重,改全连接层为我们需要的层
img_classes = 7
transfer_model = models.resnet18(pretrained=True)
dim_in = transfer_model.fc.in_features#dim_in=int(512)
transfer_model.fc = nn.Linear(dim_in,img_classes)
#方法2——固定卷积层参数,只更新全连接层参数
#在1的基础上固定参数即可
for param in transfer_model.parameters():
param.requires_grad = False
optimizer = optim.SGD(transfer_model.fc.parameters(),lr=1e-3)#在优化器设置需要优化的参数只有全连接层的参数
方法3
Deep Dream——探索卷积神经网络眼中的世界
这个很有意思,不更新网络参数而更新图片像素点来可视化每层网络到底学了什么特征。
Neural-Style——使用PyTorch进行风格迁移(没细看)
https://arxiv.org/abs/1508.06576
图片内容的差异性用比较通过预训练网络提取的特征来代替直接比较图片;风格的差异性用Gram矩阵来定义