深度学习设计很多向量和多矩阵运算,比如BP , CNN 等深层模型都可以写成矩阵运算的格式,不用写成循环运算。但是CPU 上矩阵的运算会被展成循环的形式,CPU 是串行执行的。而GPU 图像处理器的众核体系结构包含几千个流处理器。可以将矩阵运算并行化执行,大幅度缩短运行时间。利用GPU 训练深度神经网络可以充分发挥其众多计算核心的能力,耗费时间大幅度缩减。
pytorch 支持GPU,通过 to(device) 函数将数据从内存中转移到GPU ,如果有多个GPU 还可以定位到哪个或那些GPU 上。一般把GPU 用在 tensor 和模型(torch.nn 的一些网络模型和自己定义的)等数据结构上。
可以通过 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 的情况:使用 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 ,,,
传统神经网络都是采用的全连接方式,这种链接方式对于图片这样的就不适合了,还容易导致过拟合。为了处理图像,视频,音频,自然语言等大数据,就采用其他的方式,比如卷积神经网络,循环神经网络等。
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等,这些滤波器可以检测图像的水平边缘,垂直边缘,增强图像中心区域权重等。
滤波器应该如何确定,滤波器类似于神经网络中的权重矩阵,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')
就是下采样,通过卷积层获取图像的特征后,理论上可以直接使用这些特征训练分类器。但是这样的计算量就很大,容易产出过拟合,就要进一步降低网络训练参数即模型的过拟合程度,就要进行池化处理。
就是在移动窗口内的池化就是局部池化。就是一般说的最大池化之类的。实际应用中,最大池化一般更常用
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])