杂乱的知识-PyTorch练习

本文主要受教于《Python深度学习基于PyTorch》,以下有两个相关资源:

本文PyTorch.ipynb文件分享:地址链接,提取码:kg7f


0. 从书中截取的一些重要内容

请注意,维度(dimension) 这个词在不同上下文时往往会有不同的含义,这经常会使人感到困惑。为了清楚起见,我们在此明确一下:

  • 向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。
  • 然而,张量的维度用来表示张量具有的轴数。在这个意义上,张量的某个轴的维数就是这个轴的长度。


1. 一些知识点

  1. 在深度学习训练后,需要计算每个epoch得到的模型的训练效果的时候,一般会用到Tensor.detach() item() cpu() numpy()等函数。

摘录自pytorch detach() item() cpu() numpy()理解

  • item()返回的是tensor中的值,且只能返回单个值(标量),不能返回向量,使用返回loss等。

  • detach()阻断反向传播,返回值仍为tensor。

  • cpu()将变量放在cpu上,仍为tensor。注意cuda上面的变量类型只能是tensor,不能是其他。

  • numpy()将tensor转换为numpy。

  1. 用plt画图时,要将Tensor转换为numpy。即用Tensor.numpy()转换。

  2. TensorFlow使用静态图,PyTroch使用动态图,所以PyTroch可以随时看到结果。

  3. torch.unsqueeze() 的作用是将一维变二维,torch只能处理二维的数据

  4. BN批量归一化 nn.BatchNorm1d(num_features=16)

  • BN是对隐藏层的标准化处理,它与输入的标准化处理Normalizing Inputs是有区别的。
  • Normalizing Inputs是使所有输入的均值为0,方差为1。而Batch Normalization可使各隐藏层输入的均值和方差为任意值。
  • BN应作用在非线性映射前,即对x=Wu+b做规范化时,在每一个全连接和激励函数之间。
  • 有效解决收敛速度很慢,或梯度爆炸等无法训练的情况。加快训练速度,提高模型精度。
  • 是4D的(N, C, H, W),经过 BN 输入输出的维度是一样的。
  1. 激活函数
  • 输入维度与输出维度是一样的。
  • 激活函数的输入维度一般包括批量数N,即输入数据的维度一般是4维,如(N,C,W,H)。
  • 搭建比较深的神经网络时,一般使用relu激活函数(LeakyReLu)。
  • softmax常用于多分类神经网络输出层
  1. 损失函数
  • 损失函数对每个参数的偏导数就是梯度下降中提到的梯度,防止过拟合时添加的正则化项也是加在损失函数后面。
  • 在机器学习中常用的损失函数有两种
  • 分类问题 —— 交叉熵(Cross Entropy)。反应的两个概率分布的距离(不是欧氏距离)。
    • 又称对数似然损失、对数损失(Log-likelihood Loss)
    • 二分类时还可称之为逻辑回归损失(Logistic Loss)。
    • 因为交叉熵描述了两个概率分布的差异,然而神经网络输出的是向量,并不是概率分布的形式。所以需要softmax激活函数将一个向量进行“归一化”成概率分布的形式,再采用交叉熵损失函数计算loss。
    • 多分类任务中,经常采用softmax激活函数+交叉熵损失函数
    • loss = nn.CrossEntropyLoss()
  • 回归问题 —— 均方误差(Mean squared error,MSE)。回归问题预测的不是类别,而是一个任意实数。在神经网络中一般只有一个输出节点,该输出值就是预测值。反应的预测值与实际值之间的距离可以用欧氏距离来表示。
    • loss = nn.MSELoss(reduction='mean') # reduction=‘mean’(default)、‘sum’、‘none’。如果取’sum’,则 只是差平方的和,但不会除以n。
  1. 优化器
  • RMSprop、Adadelta和Adam被认为是自适应优化算法,因为它们会自动更新学习率。而使用SGD时,必须手动选择学习率和动量参数,通常会随着时间的推移而降低学习率。
  • 可以考虑综合使用这些优化算法。Adam + (SGD+动量优化)
  • 先使用Adam优化算法来进行训练,这将大大地节省训练时间,且不必担心初始化和参数调整;
  • 一旦用Adam训练获得较好的参数后,就可以切换到SGD+动量优化,以达到最佳性能。
  1. torch.max(out,1) #按行取最大。

mlps_correct=[0 for i in range(len(mlps))] # 如果len(mlps)=3,则mlps_correct=[0, 0, 0]

  1. hsv色彩空间(色彩、饱和度、明亮度):三分钟带你快速学习RGB、HSV和HSL颜色空间

  2. 一些python语法

  • 判断一个变量是否是某个类型可以用isinstance()判断。
    • 例如resnet中有段代码是:isinstance(m, Bottleneck),这就表明是在判断选用的是1x1、3x3、1x1的Bottleneck块还是3x3、3x3的BasicBlock块。
  • nn.init.constant_(m.bias, 0) # 将张量 m.bias 就地填充常量 0
  • nn.init.normal_(m.weight, 0, 0.01) # 就地填充标准正态分布填充张量,默认为0-1分布(这里就是0-0.01分布)
  1. 靠近底层的卷积层保留了图像的很多纹理、风格等信息。越高层就是包含的那些语义信息(具体是某一个类别)。

  2. 有需要的话,再看看书上P430的风格迁移+图像修复实战。

  3. 当conv的kernel_size=3,stride=padding=1时,输入输出的尺寸相同(不会发生改变)。

conv中默认strides=1padding=0,所以有需要的时候需要自行设定padding=1。

  1. 初始化:pytorch系列 – 9 pytorch nn.init 中实现的初始化函数 uniform, normal, const, Xavier, He initialization

针对于Relu的激活函数,基本使用He initialization,pytorch也是使用kaiming 初始化卷积层参数的。

  1. 初始化及分布

  2. 当我们实例化一个模型model后,可以通过model.train()和model.val()来控制模型的状态,在model.train()模式下self.training=True,在model.eval()模式下self.training=False。



2. 实现线性回归

2.1 numpy实现线性回归

# step1.导入库
import numpy as np
%matplotlib inline
from matplotlib import pyplot as plt
# step2.生成输入数据x及目标数据y
# 设置随机数种子,生成同一份数据,以便用多种方法进行比较
np.random.seed(100)
x = np.linspace(-1,1,100).reshape(100,1)
y = 3*np.power(x,2) + 2 + 0.2*np.random.rand(x.size).reshape(100,1) # 最后一项是添加的随机噪声,噪声的形状与输入数据相同
# step3.查看x、y的分布情况
plt.scatter(x,y)
plt.show()
# step4.初始化权重
# 随机初始化参数
w1 = np.random.rand(1,1)
b1 = np.random.rand(1,1)

step5.训练模型

定义损失函数,假设批量大小为100:

Loss ⁡ = 1 2 ∑ i = 1 100 ( w x i 2 + b − y i ) 2 \operatorname{Loss}=\frac{1}{2} \sum_{i=1}^{100}\left(w x_{i}^{2}+b-y_{i}\right)^{2} Loss=21i=1100(wxi2+byi)2

对损失函数求导:

1.对w1求导

∂ Loss ⁡ ∂ w = ∑ i = 1 100 ( w x i 2 + b − y i ) x i 2 \frac{\partial \operatorname{Loss}}{\partial w}=\sum_{i=1}^{100}\left(w x_{i}^{2}+b-y_{i}\right) x_{i}^{2} wLoss=i=1100(wxi2+byi)xi2

2.对b1求导

∂ L o s s ∂ b = ∑ i = 1 100 ( w x i 2 + b − y i ) \frac{\partial \mathrm{Loss}}{\partial b}=\sum_{i=1}^{100}\left(w x_{i}^{2}+b-y_{i}\right) bLoss=i=1100(wxi2+byi)

利用梯度下降法学习参数,学习率为lr:(同时更新)

w 1 − = l r ∗ ∂ L o s s ∂ w b 1 − = l r ∗ ∂ L o s s ∂ b \begin{aligned} &w_{1^{-}}=l r^{*} \frac{\partial \mathrm{Loss}}{\partial w} \\ &b_{1^{-}}=l r^{*} \frac{\partial \mathrm{Loss}}{\partial b} \end{aligned} w1=lrwLossb1=lrbLoss

lr = 0.001 # 学习率、步长
for i in range(800):
    # 前向传播
    y_pred = np.power(x,2)*w1 + b1 # 随即定义的一个拟合曲线,慢慢的会调整
    # 定义损失函数
    loss = 0.5 * (y_pred - y) ** 2 # 是一个numpy类型,所以下面求总和
    loss = loss.sum()
    # 计算梯度
    grad_w = np.sum((y_pred - y) * np.power(x,2))
    grad_b = np.sum(y_pred - y)
    # 使用梯度下降法,使loss最小
    w1 -= lr * grad_w
    b1 -= lr * grad_b
# step6.可视化结果
plt.plot(x, y_pred, 'r-', label='predict')
plt.scatter(x, y, color='blue', marker='o', label='true') # 真值
plt.xlim(-1, 1)
plt.ylim(2, 6)
plt.legend()
plt.show()
print(w1, b1)

2.2 利用PyTorch实现线性回归

# step1.导入库
import torch
%matplotlib inline
from matplotlib import pyplot as plt
# step2.生成训练数据,并可视化数据分布情况
torch.manual_seed(100) # 设置随机数种子
# 生成x坐标数据,x为tensor,xuyaobax的形状转换为100*1
x = torch.unsqueeze(torch.linspace(-1,1,100),dim=1) # 增加一个维度
# 生成y坐标数据,y为tensor,形状为100*1,另加上一些噪声
y = 3*x.pow(2) + 2 + 0.2*torch.rand(x.size())

# 画图,把tensor数据转换为numpy数据
plt.scatter(x.numpy(),y.numpy())
plt.show()
# step3.初始化权重参数
dtype = torch.float32
w = torch.randn(1,1,dtype=dtype,requires_grad=True)
b = torch.randn(1,1,dtype=dtype,requires_grad=True)
print(w.item(), b.item())
# step4.训练模型
lr = 0.001 # 学习率
for i in range(800):
    # 前向传播,并定义损失函数loss
    y_pred = x.pow(2)*w + b 
    loss = 0.5 * (y_pred - y) ** 2
    loss = loss.sum()
    
    # 自动计算梯度,梯度存放在grad属性中
    loss.backward()
    
    if i % 100 == 0: #每100次打印一次训练结果
        print("第{}轮:损失值、权重、偏移量分别为{:.4f},{},{}".format(i, loss.item(), w.item(), b.item()))
    
    # 手动更新参数,需要用torch.no_grad(),使上下文环境中切断自动求导的计算
    with torch.no_grad():
        w -= lr * w.grad
        b -= lr * b.grad
            
    # 梯度清零
    w.grad.zero_() # 加上_表示“就地操作”,在这儿就指“就地清零”
    b.grad.zero_()   

第0轮:损失值、权重、偏移量分别为593.5973,0.3698589503765106,-0.35640448331832886
第100轮:损失值、权重、偏移量分别为2.1292,2.31864070892334,2.353914976119995
第200轮:损失值、权重、偏移量分别为0.5438,2.6827197074890137,2.219045877456665
第300轮:损失值、权重、偏移量分别为0.2392,2.842302083969116,2.1599204540252686
第400轮:损失值、权重、偏移量分别为0.1807,2.9122509956359863,2.134004592895508
第500轮:损失值、权重、偏移量分别为0.1694,2.9429121017456055,2.1226444244384766
第600轮:损失值、权重、偏移量分别为0.1673,2.9563517570495605,2.1176652908325195
第700轮:损失值、权重、偏移量分别为0.1669,2.9622421264648438,2.1154825687408447

# step5.可视化训练结果
# 注意这里是y_pred.detach().numpy(),需要先detach分离一下,因为requires_grad=True
plt.plot(x.numpy(), y_pred.detach().numpy(), 'r-', label='predict') 
plt.scatter(x.numpy(), y.numpy(), color='blue', marker='o', label='true')
plt.xlim(-1,1)
plt.ylim(2,6)
plt.legend()
plt.show()

print(w,b)	# tensor([[2.9648]], requires_grad=True) tensor([[2.1145]], requires_grad=True)

3. 实现手写数字识别MNIST

3.1 准备数据

1.导入必要的模块

import numpy as np
import torch
# 导入PyTorch内置的mnist数据
from torchvision.datasets import mnist
# 导入预处理模块
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
# 导入nn及优化器
import torch.nn.functional as F
import torch.optim as optim
from torch import nn

2.定义一些超参数

train_batch_size = 64
test_batch_size = 128
learning_rate = 0.01
num_epoches = 20
lr = 0.01
momentum = 0.5 # 动量,一般与优化函数一起使用

3.下载数据并对数据进行预处理

# 定义预处理函数,这些预处理依次放在Compose函数中
# 对张量进行归一化,这里两个0.5分别表示对张量进行归一化的全局平均值和方差。
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize([0.5],[0.5])]) 

# 下载数据,并对数据进行预处理
train_dataset = mnist.MNIST('./data', train=True, transform=transform, download=True)
test_dataset = mnist.MNIST('./data', train=False, transform=transform)
# DataLoader是一个可迭代对象,可以像迭代器一样使用
train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)

3.2 可视化数据源

import matplotlib.pyplot as plt
%matplotlib inline

examples = enumerate(test_loader) # 制造样本迭代器,下面的next()就是随机选一个样本出来
batcn_idx, (examples_data, examples_targets) = next(examples)

fig = plt.figure()
for i in range(6):
    plt.subplot(2, 3, i+1)
    plt.tight_layout()
    plt.imshow(examples_data[i][0], cmap='gray', interpolation='none')
    plt.title('Ground Truth:{}'.format(examples_targets[i]))
    plt.xticks([])
    plt.yticks([])

3.3 构建模型

1.构建模型

class Net(nn.Module):
    '''
    使用Sequential构建网络,Sequential()函数的功能是将网络的层组合到一起
    '''
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Net, self).__init__()
        self.layer1 = nn.Sequential(nn.Linear(in_dim, n_hidden_1), nn.BatchNorm1d(n_hidden_1))
        self.layer2 = nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2), nn.BatchNorm1d(n_hidden_2))
        self.layer3 = nn.Sequential(nn.Linear(n_hidden_2, out_dim))
        
    def forward(self, x):
        x = F.relu(self.layer1(x))
        x = F.relu(self.layer2(x))
        x = self.layer3(x)
        return x

2.实例化网络

# 监测是否有可用的GPU,有则使用,否则使用CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
# 实例化网络
model = Net(28 * 28, 300, 100, 10) # 输入的是每层的维度吧
model.to(device)

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)

3.4 训练模型

训练模型,这里使用for循环,进行迭代。其中包括对训练数据的训练模型,然后用测试数据的验证模型。

1.训练模型

# 开始训练
losses = []
acces = []
eval_losses = []
eval_acces = []
 
 
for epoch in range(num_epoches):
    train_loss = 0
    train_acc = 0
    
    # 设定为训练模式
    model.train()
    #动态修改参数学习率
    if epoch%5==0:
        optimizer.param_groups[0]['lr']*=0.1
    for img, label in train_loader:
        img=img.to(device)
        label = label.to(device) # 用GPU加速
        img = img.view(img.size(0), -1) # 展平送入到输入层
        # 前向传播
        out = model(img) # 模型
        loss = criterion(out, label) # 损失
        # 反向传播
        optimizer.zero_grad() # 梯度清零
        loss.backward() # 反向传播,计算梯度
        optimizer.step() # 梯度更新
        # 记录误差
        train_loss += loss.item() # item()取出误差值
        # 计算分类的准确率
        _, pred = out.max(1)
        num_correct = (pred == label).sum().item()
        acc = num_correct / img.shape[0]
        train_acc += acc
        
    losses.append(train_loss / len(train_loader))
    acces.append(train_acc / len(train_loader))
    # 在测试集上检验效果
    eval_loss = 0
    eval_acc = 0
    # 将模型改为预测模式
    model.eval()
    for img, label in test_loader:
        img=img.to(device)
        label = label.to(device)
        img = img.view(img.size(0), -1)
        out = model(img)
        loss = criterion(out, label)
        # 记录误差
        eval_loss += loss.item()
        # 记录准确率
        _, pred = out.max(1)
        num_correct = (pred == label).sum().item()
        acc = num_correct / img.shape[0]
        eval_acc += acc
        
    eval_losses.append(eval_loss / len(test_loader))
    eval_acces.append(eval_acc / len(test_loader))
    print('epoch: {}, Train Loss: {:.4f}, Train Acc: {:.4f}, Test Loss: {:.4f}, Test Acc: {:.4f}'
          .format(epoch, train_loss / len(train_loader), train_acc / len(train_loader), 
                     eval_loss / len(test_loader), eval_acc / len(test_loader)))

epoch: 0, Train Loss: 1.0369, Train Acc: 0.7917, Test Loss: 0.5618, Test Acc: 0.9002
epoch: 1, Train Loss: 0.4910, Train Acc: 0.8983, Test Loss: 0.3593, Test Acc: 0.9232
epoch: 2, Train Loss: 0.3567, Train Acc: 0.9175, Test Loss: 0.2771, Test Acc: 0.9359
epoch: 3, Train Loss: 0.2908, Train Acc: 0.9294, Test Loss: 0.2350, Test Acc: 0.9424
epoch: 4, Train Loss: 0.2488, Train Acc: 0.9375, Test Loss: 0.2010, Test Acc: 0.9494
epoch: 5, Train Loss: 0.2277, Train Acc: 0.9440, Test Loss: 0.1970, Test Acc: 0.9512
epoch: 6, Train Loss: 0.2252, Train Acc: 0.9444, Test Loss: 0.1960, Test Acc: 0.9510
epoch: 7, Train Loss: 0.2214, Train Acc: 0.9449, Test Loss: 0.1928, Test Acc: 0.9509
epoch: 8, Train Loss: 0.2181, Train Acc: 0.9457, Test Loss: 0.1915, Test Acc: 0.9519
epoch: 9, Train Loss: 0.2167, Train Acc: 0.9457, Test Loss: 0.1898, Test Acc: 0.9522
epoch: 10, Train Loss: 0.2144, Train Acc: 0.9464, Test Loss: 0.1867, Test Acc: 0.9509
epoch: 11, Train Loss: 0.2135, Train Acc: 0.9469, Test Loss: 0.1882, Test Acc: 0.9521
epoch: 12, Train Loss: 0.2146, Train Acc: 0.9464, Test Loss: 0.1883, Test Acc: 0.9519
epoch: 13, Train Loss: 0.2142, Train Acc: 0.9471, Test Loss: 0.1883, Test Acc: 0.9516
epoch: 14, Train Loss: 0.2136, Train Acc: 0.9471, Test Loss: 0.1870, Test Acc: 0.9521
epoch: 15, Train Loss: 0.2135, Train Acc: 0.9473, Test Loss: 0.1880, Test Acc: 0.9520
epoch: 16, Train Loss: 0.2133, Train Acc: 0.9475, Test Loss: 0.1879, Test Acc: 0.9518
epoch: 17, Train Loss: 0.2123, Train Acc: 0.9471, Test Loss: 0.1894, Test Acc: 0.9524
epoch: 18, Train Loss: 0.2128, Train Acc: 0.9470, Test Loss: 0.1880, Test Acc: 0.9513
epoch: 19, Train Loss: 0.2138, Train Acc: 0.9465, Test Loss: 0.1871, Test Acc: 0.9527

2.可视化训练及测试损失值

plt.title('train loss')
plt.plot(np.arange(len(losses)), losses)
plt.legend(['Train Loss'], loc='upper right')

4. 优化器比较

这个还挺重要的,说不定以后能用上

1.导入模块

import torch
import torch.utils.data as Data
import torch.nn.functional as F
import matplotlib.pyplot as plt
%matplotlib inline
 
# 超参数
LR = 0.01
BATCH_SIZE = 32
EPOCH = 12

2.生成数据

# 生成训练数据
# torch.unsqueeze() 的作用是将一维变二维,torch只能处理二维的数据
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)  
# 0.1 * torch.normal(x.size())增加噪点
y = x.pow(2) + 0.1 * torch.normal(torch.zeros(*x.size()))
 
torch_dataset = Data.TensorDataset(x,y)
#得到一个代批量的生成器
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True)

3.构建神经网络

class Net(torch.nn.Module):
    # 初始化
    def __init__(self):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(1, 20)
        self.predict = torch.nn.Linear(20, 1)
 
    # 前向传递
    def forward(self, x):
        x = F.relu(self.hidden(x))
        x = self.predict(x)
        return x

4.使用多种优化器

net_SGD = Net()
net_Momentum = Net()
net_RMSProp = Net()
net_Adam = Net()
 
nets = [net_SGD, net_Momentum, net_RMSProp, net_Adam]
 
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.9)
opt_RMSProp = torch.optim.RMSprop(net_RMSProp.parameters(), lr=LR, alpha=0.9)
opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
optimizers = [opt_SGD, opt_Momentum, opt_RMSProp, opt_Adam]

5.训练模型

loss_func = torch.nn.MSELoss() 
loss_his = [[], [], [], []]  # 记录损失 
for epoch in range(EPOCH):
    for step, (batch_x, batch_y) in enumerate(loader):
        for net, opt,l_his in zip(nets, optimizers, loss_his):
            output = net(batch_x)  # get output for every net
            loss = loss_func(output, batch_y)  # compute loss for every net
            opt.zero_grad()  # clear gradients for next train
            loss.backward()  # backpropagation, compute gradients
            opt.step()  # apply gradients
            l_his.append(loss.data.numpy())  # loss recoder

6.可视化结果

labels = ['SGD', 'Momentum', 'RMSprop', 'Adam']

for i, l_his in enumerate(loss_his):
    plt.plot(l_his, label=labels[i])
plt.legend(loc='best')
plt.xlabel('Steps')
plt.ylabel('Loss')
plt.ylim((0, 0.2))
plt.show()

5. 数据加载、预处理流程

  • 利用ImageFolder读取不同目录下的图片数据,
  • 然后使用transforms进行图像预处理,
  • 预处理有多个,我们用compose把这些操作拼接在一起。
  • 然后使用DataLoader加载。
from torchvision import transforms, utils
from torchvision import datasets
import torch
import matplotlib.pyplot as plt 
from torch.utils import data # 引入data
%matplotlib inline
 
my_trans=transforms.Compose([
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor() # 将图像的(H,W,C)改为tensor的(C,H,W)
])

data_path = r'D:\SoftWareInstallMenu\JetBrains\PycharmProjects\image-processing\data_set\flower_data'
train_data = datasets.ImageFolder(data_path, transform=my_trans)
train_loader = data.DataLoader(train_data, batch_size=8, shuffle=True) # 每次读取8章图片,并刷新
                                            
for i_batch, img in enumerate(train_loader):
    if i_batch == 0:
        print(img[1]) # 打印标签
        fig = plt.figure()
        grid = utils.make_grid(img[0]) # 将多张图片拼接在一个网格中
        plt.imshow(grid.numpy().transpose((1, 2, 0))) 
        # transpose改变维度,tensor中是(C,H,W),而图像应该是(H,W,C)所以这里将tensor中0维变为3维位置上去
        plt.show()
        utils.save_image(grid,'test01.png')
    break

tensor([0, 1, 0, 0, 1, 0, 1, 0])

from PIL import Image
Image.open('test01.png')


6. tensorboardX可视化工具

这个部分没有写完,后面可以先参考一下自己的博客:tensorboard初体验


1.导入模块

如果没有安装tensorboardX,则运行pip install tensorboardX

import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from tensorboardX import SummaryWriter

2.构建神经网络

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)
        self.bn = nn.BatchNorm2d(20)
 
    def forward(self, x):
        x = F.max_pool2d(self.conv1(x), 2)
        x = F.relu(x) + F.relu(-x)
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
        x = self.bn(x)
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        x = F.softmax(x, dim=1)
        return x

3.把模型保存为graph

#定义输入
input = torch.rand(32, 1, 28, 28)
#实例化神经网络
model = Net()
#将model保存为graph
with SummaryWriter(log_dir='logs',comment='Net') as w:
    w.add_graph(model, (input, ))


7. GPU加速

1.检查是否有可用的GPU

torch.cuda.is_available()

2.获得能够使用的GPU数量

torch.cuda.device_count()

3.查看平台GPU的配置信息

在cmd中输入命令:nvidia-smi

4.使用.to(device)或.cuda来将数据、模型放到GPU显存

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

img=img.to(device)
labels = labels.to(device)

model = Net()
model.to(device) #使用序号为0的GPU


8. 卷积神经网络CNN

由卷积层(nn.Conv2d)、池化层(nn.MaxPool2d)和全连接层(nn.Linear)等叠加而成。

  • nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1) # 步长默认为1
  • nn.MaxPool2d(kernel_size=2, stride=2) # 当之传入一个参数时,stride默认等于kernel_size
  • nn.Linear(in_dim=1296, out_dim=128)

卷积核 kernel

  • 卷积核(kernel)又称权重过滤器,简称为过滤器(filter),是整个卷积过程的核心
  • 卷积核一般是奇数阶矩阵
  • 比较简单的卷积核或过滤器有:
  • Horizontalfilter:检测图像的水平边缘。其特点是有值的是第1行和第3行,第2行为0。
  • Verticalfilter:检测图像的垂直边缘。其特点是有值的是第1列和第3列,第2列为0。
  • Sobel Filter:增强图像中心区域权重。
  • 过滤器如何确定呢?
    过滤器类似于标准神经网络中的权重矩阵W,W需要通过梯度下降算法反复迭代求得。同样,在深度学习学习中,过滤器也是需要通过模型训练来得到的。卷积神经网络主要目的就是计算出这些filter的数值。确定得到了这些filter后,卷积神经网络的浅层网络也就实现了对图像所有边缘特征的检测
  • 参数共享。卷积核的值在整个过程中都是共享的,所以又把卷积核的值称为共享变量。卷积神经网络采用参数共享的方法大大降低了参数的数量

步长 strides

  • 卷积核在窗口中每次移动的格数(无论是自左向右移动,或自上向下移动)称为步幅(strides)。
  • 在用PyTorch具体实现时,strides参数格式为单个整数或两个整数的元组

填充 Padding

  • 当输入图片与卷积核不匹配时或卷积核超过图片边界时,可以采用边界填充(Padding)的方法。根据是否扩展Padding又分为Same、Valid:

  • 采用Same方式时,对图片扩展并补0;

  • 采用Valid方式时,不对图片进行扩展。

  • 那如何选择呢?在实际训练过程中,一般选择Same方式,使用Same不会丢失信息

  • 补0的圈数为p,输入数据大小为n,过滤器大小为f,步幅大小为s,则有:

  • $ p=\frac{f-1}{2} $ (可能有这些规则:1.如果不能整除,就取整数。2.当设为valid时,p=0)

  • 卷积后的大小为:$ \frac{n-f+2p}{s}+1 $

激活函数 nn.ReLU()

常用的激活函数有:nn.Sigmoid、nn.ReLU、nn.LeakyReLU、nn.Tanh

卷积函数 nn.Conv2d()

  • nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1, padding=0) # 步长默认为1,填充默认为0.
  • 输入、输出(注意:第一个维度是批量大小,卷积过程中不会改变)
  • Input:( N N N, C i n C_{in} Cin, H i n H_{in} Hin, W i n W_{in} Win )
  • Output:( N N N, C o u t C_{out} Cout, H o u t H_{out} Hout, W o u t W_{out} Wout )

转置卷积函数 nn.ConvTranspose2d()

  • 转置卷积(Transposed Convolution)在一些文献中也称为反卷积(Deconvolution)或部分跨越卷积(Fractionally-Strided Convolution)。
  • 通过卷积的正向传播的图像一般越来越小,记为下采样(Downsampled)
  • 卷积的反向传播实际上就是一种转置卷积,它是上采样(Up-Sampling)(图像就会越来越大)
  • torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0)

池化 Pooling

  • 池化(Pooling)又称下采样。降采样:保留显著特征、降低特征维度、增大感受野、减小尺寸。

  • 最大池化比其他池化方法更常用

  • 常用的3种池化方式

  • 最大池化 nn.MaxPool2d(kernel_size, strides):选择Pooling窗口中的最大值作为采样值。

  • 均值池化 nn.AvgPool2d(kernel_size, strides):将Pooling窗口中的所有值相加取平均,以平均值作为采样值。

  • 全局最大(或均值)池化:与平常最大或最小池化相对而言,全局池化是对整个特征图(feature map)的池化而不是在移动窗口范围内的池化。PyTorch虽然没有对应名称的池化层,但可以使用PyTorch中的自适应池化层来分别实现全局平均池化、全局最大池化。

    • 全局平均池化(Global Average Pooling,GAP),不以窗口的形式取均值,而是以特征图为单位进行均值化,即一个特征图输出一个值。
      • 使用全局平均池化代替CNN中传统的全连接层。目前卷积网络中最后几个全连接层,大都用GAP替换。
      • nn.AdaptiveAvgPool2d(1)
    • 全局最大池化 AdaptiveMaxPool2d(1)


9. 清除图像中的雾霾

1.导入需要的模块

import torch
import torch.nn as nn
import torchvision
import torch.backends.cudnn as cudnn
import torch.optim
import os
import numpy as np
from torchvision import transforms
from PIL import Image
import glob

2.查看原来的图像

import matplotlib.pyplot as plt
from matplotlib.image import imread
%matplotlib inline

img=imread('./haze_img/haze1.jpg')
print(img.shape)

plt.imshow(img)
plt.show

3.定义一个神经网络

这个神经网络主要由卷积层构成,该网络将构建在预训练模型之上。

#定义一个神经网络
class model(nn.Module):
    def __init__(self):
        super(model, self).__init__()
        self.relu = nn.ReLU(inplace=True)
        
        self.e_conv1 = nn.Conv2d(3,3,1,1,0,bias=True)
        self.e_conv2 = nn.Conv2d(3,3,3,1,1,bias=True)
        self.e_conv3 = nn.Conv2d(6,3,5,1,2,bias=True)
        self.e_conv4 = nn.Conv2d(6,3,7,1,3,bias=True)
        self.e_conv5 = nn.Conv2d(12,3,3,1,1,bias=True)
        
    def forward(self, x):
        source = []
        source.append(x)
        
        x1 = self.relu(self.e_conv1(x))
        x2 = self.relu(self.e_conv2(x1))
        concat1 = torch.cat((x1,x2), 1)
        x3 = self.relu(self.e_conv3(concat1))
        
        concat2 = torch.cat((x2, x3), 1)
        x4 = self.relu(self.e_conv4(concat2))
        concat3 = torch.cat((x1,x2,x3,x4),1)
        x5 = self.relu(self.e_conv5(concat3))
        clean_image = self.relu((x5 * x) - x5 + 1)
        return clean_image

4.训练模型

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

net = model().to(device)

def cl_image(image_path):
    data = Image.open(image_path)
    data = (np.asarray(data)/255.0)
    data = torch.from_numpy(data).float()
    data = data.permute(2,0,1)
    data = data.to(device).unsqueeze(0)
    #装载预训练模型
    net.load_state_dict(torch.load('clean_photo/dehazer.pth')) # 嗨呀,并没有这个预训练权重的嘛
    clean_image = net.forward(data)
    torchvision.utils.save_image(torch.cat((data, clean_image),0), "clean_photo/results/" + image_path.split("/")[-1])
    
if __name__ == '__main__':
    test_list = glob.glob("clean_photo/test_images/*")
    for image in test_list:
        cl_image(image)
        print(image, "done!")

5.查看处理后的图像

处理后的图像与原图像拼接在一起,保存在clean_photo/results目录下。

(上面的代码并没有跑通,所以下面的代码还无法运行)

import matplotlib.pyplot as plt
from matplotlib.image import imread
%matplotlib inline
img=imread('clean_photo/results/shanghai01.jpg')
plt.imshow(img)
plt.show

10. PyTorch提供的预处理模块

1.torchvision.models模块中包括以下模型

AlexNet
VGG
ResNet
SqueezeNet
DenseNet
Inception v3
GoogLeNet
ShuffleNet v2

2.调用随机权重的模型

import torchvision.models as models
resnet18 = models.resnet18()
alexnet = models.alexnet()
vgg16 = models.vgg16()

3.获取预训练模型

在 torch.utils.model_zoo 中提供了预训练模型,通过传递参数pretrained=True来构造,具体如下代码。

如果pretrained=False,表示只需要网络结构,不需要用预训练模型的参数来初始化。

import torchvision.models as models
resnet18 = models.resnet18(pretrained=True)
alexnet = models.alexnet(pretrained=True)
squeezenet = models.squeezenet1_0(pretrained=True)
vgg16 = models.vgg16(pretrained=True)

4.注意不同模式

有些模型在训练和测试阶段用到了不同的模块,例如批标准化(BatchNormalization)、Dropout层等。

使用model.train()model.eval()可以切换到相应的模式。

5.规范化数据

所有的预训练模型都要求输入图片以相同的方式进行标准化,即:小批(Mini-Batch)3通道RGB格式(3×H×W),其中H和W应小于224。

图片加载时像素值的范围应在[0,1]内,然后通过指定mean=[0.485,0.456,0.406]和std=[0.229,0.224,0.225]进行标准化,例如:

normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

6.如何冻结某些层

如果需要冻结除最后一层之外的所有网络,可设置requires_grad==False即可,主要便可冻结参数,在backward()中不计算梯度。具体代码如下。

model = torchvision.models.resnet18(pretrained=True)
for param in model.parameters():
    param.requires_grad = False
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟孟单单

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值