pytorch 入门:GPU加速,卷积层,池化层

GPU 加速

深度学习设计很多向量和多矩阵运算,比如BP , CNN 等深层模型都可以写成矩阵运算的格式,不用写成循环运算。但是CPU 上矩阵的运算会被展成循环的形式,CPU 是串行执行的。而GPU 图像处理器的众核体系结构包含几千个流处理器。可以将矩阵运算并行化执行,大幅度缩短运行时间。利用GPU 训练深度神经网络可以充分发挥其众多计算核心的能力,耗费时间大幅度缩减。

pytorch 支持GPU,通过 to(device) 函数将数据从内存中转移到GPU ,如果有多个GPU 还可以定位到哪个或那些GPU 上。一般把GPU 用在 tensor 和模型(torch.nn 的一些网络模型和自己定义的)等数据结构上。

单 GPU 加速

可以通过 torch.cuda.is_available() 方法看看是否可以使用GPU ,torch.cuda.device_count() 查看可以使用的GPU 的数量。nvidia-smi 如果cuda 装好的化可以显示GPU 配置信息的样例。

import torch
# 数据内存转移到GPU 上
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 或者 device = torch.device("cuda:1")
for batch_idx, (img,label) in enumerate(train_loader):
	img = img.to(device)
	label = label.to(device)
# 模型也一样
model = Net()
model.to(device)

多GPU加速

单机多GPU 的情况:使用 DataParallel 函数,多机多GPU 一般使用 DistributedParallel.

使用: net = torch.nn.DataParallel(model) 这样所有的显卡都会被使用。如果你想使用一部分:

device_ids = [0,1,2,3]
input_data = input_data.to(device = device_ids[0])
# 或者,这个字符表示当前可以被Pytorch 程序检测到的GPU 。
os.environ["CUDA_VISIBLE_DEVICES"]= ','.join(map(str,[0,1,2,3])) 
net = torch.nn.DataParallel(model)

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

boston = load_boston()
X,y = (boston.data,boston.target)
dim = X.shape[1]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

#对训练数据进行标准化 , 让下降的更加平缓。
mean=X_train.mean(axis=0)
std=X_train.std(axis=0)
X_train-=mean
X_train/=std

X_test-=mean
X_test/=std

#组合训练数据及标签
myset = list(zip(X_train,y_train))

from torch.utils import data
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
dtype = torch.FloatTensor

train_loader = data.DataLoader(myset,batch_size=128,shuffle=True)

class Net(nn.Module):
    def __init__(self,in_dim,n_hidden_1,n_hidden_2,out_dim):
        super(Net,self).__init__()
        self.layer1 = torch.nn.Sequential(nn.Linear(in_dim, n_hidden_1))
        self.layer2 = torch.nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2))
        self.layer3 = torch.nn.Sequential(nn.Linear(n_hidden_2, out_dim))

    def forward(self,x):
        x1 = F.relu(self.layer1(x))
        x1 = F.relu(self.layer2(x1))
        x2 = self.layer3(x1)
        # 显示每个GPU分配的数据大小
        print("\tIn Model: input size", x.size(), "output size", x2.size())
        return x2

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#实例化网络
model = Net(13, 16, 32, 1)
if torch.cuda.device_count() > 1:
    print("Let's use", torch.cuda.device_count(), "GPUs")
    # dim = 0 [64, xxx] -> [32, ...], [32, ...] on 2GPUs
    model = nn.DataParallel(model)    # 多GPU 并发处理格式
else:
    print('只有一个GPU 哦。。')

model.to(device)

optimizer = torch.optim.Adam(model.parameters(),lr=0.01)
loss_func = torch.nn.MSELoss()

from tensorboardX import SummaryWriter
writer = SummaryWriter(log_dir='logs')
for epoch in range(100):
    model.train()
    for data,label in train_loader:
        input = data.type(dtype).to(device)
        label = label.type(dtype).to(device)
        output = model(input)
        loss = loss_func(output, label)
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        print("Outside: input size", input.size() ,"output_size", output.size())
    writer.add_scalar('train_loss_paral',loss, epoch)

然而我只有一个GPU ,,,

GPU 使用注意

  1. GPU 的数量尽量为偶数,奇数的GPU 可能出现异常中断的情况
  2. GPU 速度很快,但如果数据量很小可能效果还不如 CPU 。
  3. 如果内存不够大,使用 多GPU 训练时将 pin_memory 设置为False 。

视觉处理基础

传统神经网络都是采用的全连接方式,这种链接方式对于图片这样的就不适合了,还容易导致过拟合。为了处理图像,视频,音频,自然语言等大数据,就采用其他的方式,比如卷积神经网络,循环神经网络等。

卷积神经网络简介

CNN (Convolutional Neural Network ) 是一种前馈神经网络,由一个或多个卷积层和顶端的全连接层组成。同时包括了关联权重和池化层。

在这里插入图片描述
卷积神经网络的一般结构,包括了常用层(卷积层,池化层,全连接层和输出层)还有一些其他层(正则化层,高级层等)

class CNNNet01(nn.Module):      # 是不是很简单。。。就是搭积木。。。
    def __init__(self):
        super(CNNNet,self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3,out_channels=16,kernel_size=5,stride=1)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)
        self.conv2 = nn.Conv2d(in_channels=16,out_channels=36,kernel_size=5,stride=1)
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.dense1 = nn.Linear(900,128)
        self.dense2 = nn.Linear(128,10)
      

    def forward(self,x):
        x=self.pool1(F.relu(self.conv1(x)))
        x=self.pool2(F.relu(self.conv2(x)))
        x=x.view(-1,900)
        x=F.relu(self.dense2(F.relu(self.dense1(x))))
        return x

卷积层

这个是卷积神经网络的核心。卷积就是两个函数的一种运算(卷积运算)。就是用卷积分别乘以输入向量中的每个元素,然后输出一个代表每个输入信息的张量。其中卷积核又叫权重过滤器(过滤器 filter)。这个概念还是好理解的。
在这里插入图片描述

卷积核

比较简单的过滤器有 Horizontalfilter,Verticalfilter, SobelFilter等,这些滤波器可以检测图像的水平边缘,垂直边缘,增强图像中心区域权重等。

  1. 垂直边缘检测
    3 X 3 的矩阵(过滤器一般都是奇数阶的),这个滤波器就是将垂直边缘检测出来,真是神奇啊。。
    在这里插入图片描述
  2. 水平边缘检测
    在这里插入图片描述
    这些滤波器只是简单的,深度学习中滤波器的作用更应该是检测其他的边缘特征

滤波器应该如何确定,滤波器类似于神经网络中的权重矩阵,W 需要通过梯度下降反复迭代求得。同样深度学习中,滤波器也是通过模型训练的来的。卷积神经网络的主要目的就是计算出这些filter 的数值。得到了这些 filter 后,卷积神经网络的浅层网络就实现了对图像所有边缘特征的检测。豁然开朗啊。。

步幅

就是移动的个数。图像中就是跳过的像素个数,移动一格就是 strides = 1 。移动的过程中,卷积核是不变的,卷积核的值在整个过程中都是共享的,其值又称为共享变量,这样的共享就大大降低了参数的数量。

如果卷积核移动到了输入矩阵之外,就需要进行填充了 Padding。

填充

输入图片与卷积核不匹配时或卷积核超过图片边界时,可以采用边界填充的方法,将图片尺寸进行扩展,扩展区域补零。也可以不扩展。实际中一般时扩展的,这样不会丢失信息,

多通道上的卷积

与单通道的一样,卷积核同样也是多通道的。每个单通道与对应的卷积核进行卷积计算,然后将三通道逐元素相加
在这里插入图片描述
为了实现更多的边缘检测,可以增加更多的滤波器组,7×7×3输入,经过两个3×3×3的卷积(步幅为 2),得到了3×3×2的输出

激活函数

保证非线性,也要使用激活函数,就是在卷积运算后,将输出值另加偏移量,输入到激活函数中,然后作为下一层的输入。

卷积函数

终于来了。nn.Conv2d

torch.nn.Conv2d(
			in_channels: int,      					# 输入信号的通道
			out_channels: int, 						# 卷积产生的通道,有几个卷积核,输出就是几通道
			kernel_size: Union[T, Tuple[T, T]], 	# 卷积核的尺寸
			stride: Union[T, Tuple[T, T]] = 1,      # 卷积步长
			padding: Union[T, Tuple[T, T]] = 0, 	# 填充
			dilation: Union[T, Tuple[T, T]] = 1, 	# 卷积核元素之间的间距
			groups: int = 1, 						# 输入核输出之间的链接,group=1,输出是所 有的输入的卷积;group=2,此时相当于有并排的两个卷积层,每个卷积层 计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输 出连接起来
			bias: bool = True, 						# 是否添加偏置量
			padding_mode: str = 'zeros'
			)

groups = 1 时
conv = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=1, groups=1) conv.weight.data.size() #torch.Size([12, 6, 1, 1])    12 个6通道 1* 1 的卷积核的值

groups = 2
conv = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=1, groups=2) conv.weight.data.size() #torch.Size([12, 3, 1, 1]) 

没看懂这groups 有什么用

转置卷积

也叫反卷积,或部分跨越卷积。通过卷积的正向传播的图像一般越来越小,记为下采样。卷积的反向传播实际上就是一种转置卷积,这是上采样。

将卷积核,输入数据,输出数据都展开为矩阵,就可以认为卷积层的计算实际可以转为矩阵相乘 Y = CX 。而反卷积就是对这个矩阵运算的过程进行逆运算 X = C^T Y

转置卷积在生成式对抗网络GAN 中普遍使用。
在这里插入图片描述
这是还原图片吗。。

torch.nn.ConvTranspose2d(in_channels, out_channels,kernel_size,stride=1,padding=0,
			output_padding=0,groups=1,bias = True,dilation=1,padding_mode='zeros')

池化层

就是下采样,通过卷积层获取图像的特征后,理论上可以直接使用这些特征训练分类器。但是这样的计算量就很大,容易产出过拟合,就要进一步降低网络训练参数即模型的过拟合程度,就要进行池化处理。

  • 最大池化,选择Pooling 窗口的最大值为采样值
  • 均值池化,所有值相加取平均
  • 全局最大(平均)池化,这个是针对整个特征图的池化而不是在移动窗口的范围内。
    在这里插入图片描述
    池化层在CNN 中用来减少尺寸 ,提高运算速率减少噪声影响,让特征具有健壮性。保留显著特征,降低特征维度,增大感受野。深度网络越往后越能捕捉到物体的语义信息,这种语义信息是建立在较大的感受野的基础上的。感受野我记得是一层层卷积后,一个像素对应原图像素块的大小来着。。。

局部池化

就是在移动窗口内的池化就是局部池化。就是一般说的最大池化之类的。实际应用中,最大池化一般更常用

torch.nn.MaxPool2d(
	kernel_size: Union[T, Tuple[T, ...]], 				# 池化窗口的大小,整数就是正方形,还可以传入(h,w)
	stride: Optional[Union[T, Tuple[T, ...]]] = None,   # 滑动的步长,(w,h) or int 
	padding: Union[T, Tuple[T, ...]] = 0,   			# 填充
	dilation: Union[T, Tuple[T, ...]] = 1, 				# 空间间隔
	return_indices: bool = False, 						# 是否返回最大值对应的下标
	ceil_mode: bool = False								# 使用一些方块替代层结构
	)

# 池化窗口为正方形 size=3, stride=2
m1 = nn.MaxPool2d(3, stride=2)
# 池化窗口为非正方形
m2 = nn.MaxPool2d((3, 2), stride=(2, 1))
input = torch.randn(20, 16, 50, 32)      
output = m2(input)
print(output.shape)
>>> torch.Size([20, 16, 24, 31])      # 只有h,w 变了

全局池化

全局就是针对的是整张特征图。这个也分最大和平均池化。全剧平均池化 GAP ,以特征图为单位进行均质化,即一个特征图输出一个值。

全局池化可以看作一个全连接层然后平均输出四个分类节点,全局池化就是这两部的合并。可以为每一个特定的类别生成一个特征图。

优势在于各个类别与特征图之间的联系更加直观(比全连接的黑箱来说),特征图被转化为分类概率也更加的容易。因为在GAP 中没有参数需要调,可以避免过拟合的问题。GAP 汇总了空间信息,对输入的空间转换鲁棒性更强,所以目前卷积网络中最后几个全连接层大都用 GAP 替换。

pytorch 中虽然没有对应名称的池化层但可以使用 的自适应池化层(AdaptiveMaxPool2d(1)或 nn.AdaptiveAvgPool2d(1))来实现。

nn.AdaptiveMaxPool2d(output_size, return_indices=False)
# 输出张量的大小都是给定的output_size。
# 输出大小为5x7
m = nn.AdaptiveMaxPool2d((5,7))
input = torch.randn(1, 64, 8, 9)
output = m(input)
# t输出大小为正方形 7x7 
m = nn.AdaptiveMaxPool2d(7)
input = torch.randn(1, 64, 10, 9)
output = m(input)
# 输出大小为 10x7
m = nn.AdaptiveMaxPool2d((None, 7))
input = torch.randn(1, 64, 10, 9)
output = m(input)
# 输出大小为 1x1
m = nn.AdaptiveMaxPool2d((1))
input = torch.randn(1, 64, 10, 9)
output = m(input)
print(output.size())
>>> torch.Size([1, 64, 1, 1])
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值