Pytorch卷积神经网络及Code实现

GPU
  • 计算设备
import torch
from torch import nn
torch.device('cpu'), torch.cuda.device('cuda'), torch.cuda.device('cuda:1')
  • 查询GPU个数
torch.cuda.device_count()
  • 这两个函数允许我们在请求的GPU不存在的情况下运行代码
def try_gpu(i=0):  
    """如果存在,则返回gpu(i),否则返回cpu()。"""
    if torch.cuda.device_count() >= i + 1:
        return torch.device(f'cuda:{i}')
    return torch.device('cpu')
def try_all_gpus():  
    """返回所有可用的GPU,如果没有GPU,则返回[cpu(),]。"""
    devices = [
        torch.device(f'cuda:{i}') for i in range(torch.cuda.device_count())]
    return devices if devices else [torch.device('cpu')]
try_gpu(), try_gpu(10), try_all_gpus()
  • 查询张量所存储的设备
x = torch.tensor([1, 2, 3])
x.device # 观察是在cpu还时gpu上
X = torch.ones(2, 3, device=try_gpu())# 指定存储在GPU上 
Y = torch.rand(2, 3, device=try_gpu(1))# 第二个GPU上创建一个随机张量
Z = X.cuda(1)# 将X的数据从0copy到1GPU
print(X)
print(Z)
# 要计算X + Y,我们需要决定在哪里执行这个操作
Y + Z # 现在数据在同一个GPU上(Z和Y都在),我们可以将它们相加
  • 神经网络与GPU
net = nn.Sequential(nn.Linear(3, 1))
net = net.to(device=try_gpu())
net(X)
# 需要copy数据、权重、输入进入GPU
卷积神经网络
  • 卷积
    • 定义
      ∫ 0 t f ( x ) g ( t − x ) d x \int_0^t f(x)g(t-x) dx 0tf(x)g(tx)dx
    • 意义
      • 一个系统输入不稳定,输出稳定,则用卷积求解系统存量
      • 更广泛的理解应该是在某时某处发生的一件事情,是受到之前很多事的影响的,而这些事情对该事物的影响是随着时间发生变化的,变化规律就是g(x)
  • 图像的卷积操作
    • 其实就是很多像素点对某一个像素点产生何种影响
    • 卷积核恰恰就是定义在一定区间范围内其他像素点对某一像素点的影响
    • 但是g(x)并不是卷积核,卷积核是g(x)旋转180度后得到的
    • 卷积核的选择其实就存在一定的过滤特性
      • 卷积核权重的设置就是体现出了滤波特性,如果你想集中观察一个位置的特征,那么需要调大该方向上的权重
    • 卷积核的本质就是对周围像素点点试探和选择,可以保留有用的特征
  • 卷积神经网络
    • 通过上述点卷积操作,其实就可以特取图像特征
    • 卷积神经网络将这些特征交给后续神经网络,而后进行后续的图像识别功能
    • 卷积就是提取特征的一种手段
  • MLP是一种稠密的特征提取方式,而这种方式依赖于全连接层,对于复杂问题,模型往往过于冗余
  • 如果直接将图像像素点输入神经网络显然存在以下问题
    • 图像像素点具有同质化,而前期我们讨论的输入其实本身就是异质化的,也就是说,前面的输入,我们可以认为其本身就是被抽象过的特征
    • 如果直接输入,神经网络的会将其与周围像素点点关系进行抽象,这就导致像素点的特征其实是和具体位置绑定的,不具备可复用性
    • 因此,我们不如直接进行预处理,目的是先对每个像素点与周围像素点的关系进行特征值计算而后交给神经网络再进行更高级的抽象
      特征值 = 卷积核(希望提取的特征) ∗ 图像像素 特征值 = 卷积核(希望提取的特征)*图像像素 特征值=卷积核(希望提取的特征)图像像素
    • 对于多个特征值,最后应该选择哪一个特征值代表这片区域的问题,其实就是将特征值赋予权重而后进行机器学习去调整权重达到最后的期望效果,得到最终的结果就可以集中在一个像素点中
  • 卷积和傅立叶变换的关系
    • 傅立叶变换的定义
      F ( w ) = ∫ − ∞ + ∞ f ( t ) ∗ e − i w t d t F(w)=\int_{-\infty}^{+\infty}f(t)*e^{-iwt}dt F(w)=+f(t)eiwtdt
      F ( f ) = ∫ − ∞ + ∞ f ( t ) ∗ e − i 2 Π t d t F(f)=\int_{-\infty}^{+\infty}f(t)*e^{-i2\Pi t}dt F(f)=+f(t)eitdt
    • 图片就是一个静态信号,是一个以(x, y)为底层二维,RGB为z轴的一个空间域信号,可以通过傅立叶变换转换成变换域
    • 变换域的一个点其实就是把空间域的全部信息进行了浓缩,这就有提取的意思
    • 那么还原一个原始特征我们就可以理解为傅立叶变换得到的特征值乘模型相加即可得到原始信息
      模 式 w : e i w t 模式_w:e^{iwt} w:eiwt
      特征 值 w : F ( w ) = ∣ F ( w ) ∣ ∗ e i φ − 提供幅度和相位信息 特征值_w:F(w)=|F(w)|*e^{i\varphi}-提供幅度和相位信息 特征w:F(w)=F(w)eiφ提供幅度和相位信息
      f ( t ) = 1 2 Π ∫ − ∞ + ∞ F ( w ) e i w t d w − 还原信息 f(t)=\frac{1}{2\Pi}\int_{-\infty}^{+\infty}F(w)e^{iwt}dw-还原信息 f(t)=1+F(w)eiwtdw还原信息
    • 那是否可以理解为直接进行傅立叶变换就可以进行模式识别呢
      • 答案显然是否定的,傅立叶变换是针对全局进行的,对于相同波形,如果在时间域上出现的次数不同其傅立叶变换显然不同,那么我们需要对局部特征进行提取即加窗傅立叶变换
    • 加窗—Gabor变换
      F ( n , s ) = ∫ − ∞ + ∞ f ( t ) d n , s ( t ) d t F(n, s) = \int_{-\infty}^{+\infty}f(t)d_{n,s}(t)dt F(n,s)=+f(t)dn,s(t)dt
      d n , s ⃗ = g ( t − s ) ∗ e i n t \vec{d_{n,s}}=g(t-s)*e^{int} dn,s =g(ts)eint
      g a ( t − s ) = 1 2 Π a e − ( t − s ) 2 4 a ∗ 2 g_a(t-s) = \frac{1}{2\sqrt{\Pi a}}e^{-\frac{(t-s)^2}{4a}}*2 ga(ts)=2Πa 1e4a(ts)22
      n 就是模式特征, s 就是窗口位置 n就是模式特征,s就是窗口位置 n就是模式特征,s就是窗口位置
    • 图像卷积变换后的点阵就是F(n,s)其中s就是图像像素点对应的位置信息,n就是不同模式的编号,卷积核的大小就是参数a,卷积核的具体数值要去梯度下降进行机器学习
    • 卷积神经网络的底层是傅立叶变换,而傅立叶变换的底层是希尔伯特空间坐标变换
  • 两个原则
    • 平移不变形—不论什么位置在变换域中都是相同的特征值—傅立叶变换
    • 局部性—不同位置的两个一样的图像特征值相同—Gabor变换(加窗提取)
  • 如何从全连接层基于两个原则变为卷积
    • 其实从本质上讲卷积就是一个特殊的全连接层
  • 二维卷积层相关定义
    • 输入 X : n h \bm{X}:n_h X:nh * n w n_w nw
    • 卷积核 W : k h ∗ k w \bm{W}:k_h * k_w W:khkw
    • 偏差 b ∈ R b\in\mathbb{R} bR
    • 输出 Y : ( n h − k h + 1 ) ∗ ( n w − k w + 1 ) \bm{Y}:(n_h-k_h+1)*(n_w-k_w+1) Y:(nhkh+1)(nwkw+1)
    • 输出少了图像长-卷积核长+步长
      Y = X ∗ W + b \bm{Y = X * W + b} Y=XW+b
    • W W W b b b都是可学习参数
  • 卷积层中的超参数
    • 核矩阵的大小是超参数
  • 代码实现卷积层
import torch 
from torch import nn
!pip install d2l
!pip install matplotlib_inline
from d2l import torch as torch
def corr2d(X, K):
  """计算二维互相关运算"""
  h, w = K.shape
  Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
  for i in range(Y.shape[0]):
    for j in range(Y.shape[1]):
      Y[i,j] = (X[i:i+h, j:j+w] * K).sum()
  return Y
# 定义一个二维卷积层
# kernel_size传递参数为一个元组
class Conv2D(nn.Module):
  def __init__(self, kernel_size):
    super().__init__()
    self.weight = nn.Parameter(torch.rand(kernel_size))
    self.bias = nn.Parameter(torch.zeros(1))
  def forward(self, x):
    return corr2d(x, self.weight) + self.bias
  • 利用卷积层进行一个简单的边缘检测
# 进行一个竖线的边缘检测
X = torch.ones((6, 8))
X[:, 2:6] = 0
X
K = torch.tensor([[1.0,-1.0]])
# 注意两个[[]]的意思是创建了一个矩阵如果是[]则只是一个向量
Y = corr2d(X, K)
Y
  • 上例子是由人工定义的卷积核,那么现在我们进行训练得到
conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias=False)
# 这行代码创建了一个输入通道数为1,输出通道数为1,卷积核尺寸为(1,2)的二维卷积层,不使用偏置。
X = X.reshape((1, 1, 6, 8))
# 张量的形状为(1,1,6,8),其中1表示这是一个批量(batch)中的第1个样本,1表示该样本有1个通道(channel),6表示该样本有6行,8表示该样本有8列。
Y = Y.reshape((1, 1, 6, 7))
for i in range(10):
  Y_hat = conv2d(X)
  l = (Y_hat-Y)**2
  conv2d.zero_grad()
  l.sum().backward()
  conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad
  if (i + 1)%2==0:
    print(f'batch{i+1}, loss {l.sum():.3f}')
conv2d.weight.data.reshape((1,2)) # 所学卷积核的权重张量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值