深度学习笔记 - 刘二大人

1. 梯度下降与反向传播

梯度下降、随机梯度下降、批量随机梯度下降

  • 梯度下降:所有数据一起求损失、求平均,进行梯度下降,可并行,但是效果不好
  • 随机梯度下降:每一个样本数据单独进行梯度下降,不可并行,效果好,但是费时间
  • 批量随机梯度下降:将数据进行分组(batch),对上述两组方法进行中和。batch,原来是指全部数据,所以更正式的是称为mini-batch。

矩阵求导书籍:Matrix cookbook

线性变换,无论使用多少层,最终都可以化简为一层。

为了解决这个问题,对每一层最终输出加一个非线形变换函数,即激活函数,如sigmoid、softmax,ReLU等。

Tensor包含data: w w w,和grad: ∂ l o s s ∂ w \frac{\partial{loss}}{\partial{w}} wloss

torch.tensor([0,1])
w.requires_grad = True  # 计算梯度,默认为False
l = loss(x,y)
l.backward()  # 反向传播,将链路上需要梯度的,计算出来,梯度存到变量(Tensor)w的grad中,然后计算图会被释放掉
w.data = w.data - 0.01*w.grad.detach  # 之前使用.data,现在.data被弃用,如果直接使用w.grad,则会建立计算图

在求损失总和时,因为l=loss(x,y)求出的l是一个Tensor,直接相加,会建立计算图,占用内存,正确的是提取l.item(),如

total_loss += l.item()  # .item:把tensor转为python的float类型

Tensor类型书籍参与运算时会建立计算图

w.grad.data.zero_()  # 将权重变量中梯度的数据清零,如果不清零,梯度会累加
  • w.grad.item():用于获取标量张量中的数值,适用于有一个元素的张量
  • w.grad.data:已弃用,用于获取梯度张量的数据部分,这是一个包含梯度值的张量
  • w.grad.detach():用于将梯度张量从计算图中分离出来,这样不会再建立计算图

构建神经网络步骤:

  • 准备数据
  • 设计模型
  • 构造损失函数和优化器
  • 训练(前馈、反馈、更新)

optimizer.zero_grad()虽然当前计算图中的梯度被清零,但是在优化器步骤中使用的梯度信息仍然存在于参数的.grad属性中。

在Pytorch中,有两个空间存储了梯度信息:

  • 计算图中的梯度
  • 模型参数的.grad属性

2. Logistic回归

Sigmoid函数包含:

  • σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1+e^{-x}} σ(x)=1+ex1

  • e r f ( π 2 x ) erf(\frac{\sqrt\pi}{2}x) erf(2π x)

  • x 1 + x 2 \frac{x}{\sqrt{1+x^2}} 1+x2 x

  • t a n h ( x ) tanh(x) tanh(x)

  • 2 π a r c t a n ( π 2 x ) \frac{2}{\pi}arctan(\frac{\pi}{2}x) π2arctan(2πx)

  • 2 π g d ( π 2 x ) \frac{2}{\pi}gd(\frac{\pi}{2}x) π2gd(2πx)

  • x 1 + ∣ x ∣ \frac{x}{1+|x|} 1+xx

tanh(x)为双曲正切函数,在循环神经网络中经常使用。

在这里插入图片描述

这些函数都被称为sigmoid函数,但是 1 1 + e − x \frac{1}{1+e^{-x}} 1+ex1函数(Logistic函数)最出名,所以人们习惯将其直接称为sigmoid函数。

在这里插入图片描述

模型更改后,损失函数也要变化,之前我们使用的MSE,即 l o s s = ( y ^ − y ) 2 = ( x ⋅ w − y ) 2 loss=(\hat y - y)^2=(x·w-y)^2 loss=(y^y)2=(xwy)2要更改为BCE损失,即二元交叉熵损失函数。交叉熵损失函数为CE Loss(Cross-Entropy Loss)。
l o s s = − ( y l o g y ^ + ( 1 − y ) l o g ( 1 − y ^ ) ) loss = -(ylog\hat y+(1-y)log(1-\hat y)) loss=(ylogy^+(1y)log(1y^))
实际上是求两个分布的差异的大小。

在这里插入图片描述

loss_func = torch.nn.BCELoss(size_average=False)  # size_average:是否给每一个批量求均值,影响如何选择学习率
# size_average被弃用,使用reduction属性进行替代
# size_average=True -> reduction='mean'
# size_average=False -> reduction='sum'

3. 处理多维特征的输入

在这里插入图片描述

在这里插入图片描述


4. 加载数据集

实现Dataset

注意:Dataset是一个抽象类

我们定义自己的数据集,就要继承Dataset这个抽象类

import torch
from torch.utils.data import DataLoader,Dataset
class DiabetesDataset(Dataset):
    def __init__(self):
        pass

    # 允许对象进行下标操作
    def __getitem__(self, item):
        pass

    # 允许通过len(对象)来获取数据集的大小
    def __len__(self):
        pass

__getitem__:允许对象通过下标进行操作

__len__:允许通过len(对象),来获取数据集的大小

__init__中我们有两种选择:

  • 把所有的数据都读到内存里面,适用于小数据集
  • 初始化存储数据的文件名/文件地址,通过__getitem__读取数据的时候再将数据加载进内存

在这里插入图片描述

代码实现:

import torch
from torch.utils.data import DataLoader,Dataset
import numpy as np
class DiabetesDataset(Dataset):
    def __init__(self,filepath):
        xy = np.loadtxt(filepath,delimiter=',',dtype=np.float32)
        self.len = xy.shape[0]
        self.x_data = torch.from_numpy(xy[:,:-1])
        self.y_data = torch.from_numpy(xy[:,[-1]])

    # 允许对象进行下标操作
    def __getitem__(self, index):
        return self.x_data[index],self.y_data[index]

    # 允许通过len(对象)来获取数据集的大小
    def __len__(self):
        return self.len
data = DiabetesDataset('./data/diabetes.csv')
train_data = DataLoader(data,batch_size=64,shuffle=True,num_workers=0)

5. 多分类问题

在这里插入图片描述

Softmax激活函数:
P ( y = i ) = e Z i ∑ j = 0 K − 1 e Z j     i ∈ { 0 , . . . , K − 1 } P(y=i)=\frac{e^{Z_i}}{\sum_{j=0}^{K-1}e^{Z_j}}\ \ \ i\in\{0,...,K-1\} P(y=i)=j=0K1eZjeZi   i{0,...,K1}
在这里插入图片描述

在pytorch中,交叉熵损失包含了softmax,所以神经网络的最后一层不再需要我们家softmax激活函数。

注:CrossEntorpyLoss <==> LogSoftmax + NLLLoss


6. 卷积神经网络

下采样:通道数不变,特征图大小改变

在这里插入图片描述

在这里插入图片描述

对于复杂的网络,在定义网络时,

  • 要减少代码的冗余:
    • 使用函数
    • 使用类

在这里插入图片描述

如GoogLeNet,我们将如图中红圈画出来的框,称为Inception。

Inception块

在这里插入图片描述

我们在构造神经网络时,有些超参数是比较难选的,如卷积核的大小Kernel,所以GoogLeNet就在一个Inception块使用多个卷积,然后将结果摞在一起,比如如果3x3的卷积好用,那么3x3的权重就会变的比较大,其他路线上的权重就会变小。一句话说,就是提供了几种候选的配置,然后通过训练,在候选配置中找到最优的卷积组合。

Concatenate:将张量拼接到一起,沿着通道进行拼接。 但是,注意,对于这些候选的路径,最后拼接之前要保证特征图的宽度和高度一致。

**1x1卷积:**改变通道数量。

FLOPS(S大写):指每秒浮点运算次数。用来衡量硬件的性能。

FLOPs(S小写):浮点运算次数,可以用来衡量算法/模型复杂度。

FLOPs运算量计算方法:

在这里插入图片描述

输入通道大小192,图像大小28x28,卷积核大小:5x5,输出通道为32。
F L O P s = w × h × k 2 × c × c ′ F L O P s = 28 × 28 × 5 2 × 192 × 32 FLOPs = w\times h \times k^2 \times c \times c' \\ FLOPs = 28 \times 28 \times 5^2\times 192 \times 32 FLOPs=w×h×k2×c×cFLOPs=28×28×52×192×32
在神经网络中,我们最大的问题是运算量太大,所以我们要想办法来降低运算量。所以就提出用1x1的卷积来改变通道数量,以降低通道数量来减少运算量。

代码实现Inception:

import torch
from torch import nn

class Inception(nn.Module):
    def __init__(self,in_channel, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.branch_pool_1 = nn.AvgPool2d(3,1,1)
        self.branch_pool_2 = nn.Conv2d(in_channel,24,1)

        self.branch1x1 = nn.Conv2d(in_channel,out_channels=16,kernel_size=1,stride=1)

        self.branch5x5_1 = nn.Conv2d(in_channel,out_channels=16,kernel_size=1)
        self.branch5x5_2 = nn.Conv2d(16,out_channels=24,kernel_size=5,padding=2)

        self.branch3x3_1 = nn.Conv2d(in_channel,out_channels=16,kernel_size=1,stride=1)
        self.branch3x3_2 = nn.Conv2d(16,out_channels=24,kernel_size=3,padding=1)
        self.branch3x3_3 = nn.Conv2d(24,out_channels=24,kernel_size=3,padding=1)

    def forward(self,x):
        branch_pool = self.branch_pool_1(x)
        branch_pool = self.branch_pool_2(branch_pool)

        branch1x1 = self.branch1x1(x)

        branch5x5 = self.branch5x5_1(x)
        branch5x5 = self.branch5x5_2(branch5x5)

        branch3x3 = self.branch3x3_1(x)
        branch3x3 = self.branch3x3_2(branch3x3)
        branch3x3 = self.branch3x3_3(branch3x3)

        # 拼接
        outputs = [branch_pool,branch1x1,branch5x5,branch3x3]
        # dim = 1 是因为 张量的维度为(b,c,w,h)  1:c  1为通道
        return torch.cat(outputs,dim=1)

定义完整的网络

import torch
from torch import nn
from torch.nn import functional as F
class Net(nn.Module):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.conv1 = nn.Conv2d(1,10,kernel_size=5)
        self.conv2 = nn.Conv2d(88,20,kernel_size=5)

        self.incep1 = Inception(in_channel=10)
        self.incep2 = Inception(in_channel=20)

        self.mp = nn.MaxPool2d(2)
        self.fc = nn.Linear(1408,10)

    def forward(self,x):
        in_size = x.size(0)
        x = F.relu(self.mp(self.conv1(x)))
        x = self.incep1(x)
        x = F.relu(self.mp(self.conv2(x)))
        x = self.incep2(x)
        x = x.view(in_size,-1)
        x = self.fc(x)
        return x

梯度消失:

根据链式法则:是很多像的导数连乘,若倒数都是<1的,那么连乘项很多,值就会取向余0,则会出现梯度消失。

为了应对梯度消失问题,提出过一些训练方法:对网络进行逐层的训练。如下图,将第一个隐藏层锁住,不进行参数更更新,只对第二个隐藏层进行训练,训练完成后,再将第一、第二隐藏层都锁住,再加上第三个隐藏层,对第三个隐藏层进行训练,重复此过程。

在这里插入图片描述

Residual net(ResNet)

残差网络。

在这里插入图片描述

ResNet可以解决梯度消失问题,因为 ∂ Z ∂ x = ∂ F ∂ x + 1 \frac{\partial Z}{\partial x} = \frac{\partial F}{\partial x} + 1 xZ=xF+1,当第一项趋向于0的时候,整体的值也会趋向于1。

残差块代码实现:

from torch.nn import functional as F
from torch import nn
class ShortCut(nn.Module):
    def __init__(self,in_channel,out_channel,kernel_size=3,padding=1,stride=1, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.conv1 = nn.Conv2d(in_channel,out_channel,kernel_size=kernel_size,padding=padding,stride=stride)
        self.conv2 = nn.Conv2d(out_channel,out_channel,kernel_size=kernel_size,padding=padding,stride=stride)

        if in_channel!=out_channel:
            self.conv1x1 = nn.Conv2d(in_channel,out_channel,kernel_size=1,padding=0)
        else:
            self.conv1x1 = nn.Identity()

        self.bn1 = nn.BatchNorm2d(out_channel)
        self.bn2 = nn.BatchNorm2d(out_channel)

    def forward(self,x):
        p1 = F.relu(self.bn1(self.conv1(x)))
        p1 = self.bn2(self.conv2(p1))

        p2 = self.conv1x1(x)
        output = p1 + p2
        output = F.relu(output)
        return output
      
if __name__ == '__main__':
    net = ShortCut(3,10)
    x = torch.ones((64,3,447,447))
    out = net(x)
    print(out.shape)

nn.Identity() 是 PyTorch 中的一个特殊模块,它代表恒等映射(identity mapping)。

在残差块中,为了确保残差块的输入和输出维度相匹配,可能需要对输入进行一些变换。如果输入和输出的通道数相同,就不需要进行变换,此时可以使用 nn.Identity(),它不进行任何操作,直接返回输入。


7. 循环神经网络

用来处理带有序列模式的数据。使用权重共享,来减少需要训练的权重的数量。

在这里插入图片描述

x t x_t xt:序列当中时刻t的数据。

在这里插入图片描述

RNN Cell:本质是一个线性层,线性层可以把某一个维度映射到另外一个维度空间里面。与我们平常使用的线性层不同,这个线性层是共享的。

在这里插入图片描述

h 0 h_0 h0是先验知识,传入到RNN Cell,如果没有鲜艳,那就将 h 0 h_0 h0设置为与 h 1 , h 2 . . h_1,h_2.. h1,h2..维度相同的全零。

RNN整体过程, h 0 , x 1 h_0,x_1 h0,x1经过某种运算,生成 h 1 h_1 h1,将 h 1 h_1 h1作为输出送到下一个RNN Cell中,同时将 h 1 h_1 h1作为一个输出,然后在下一个RNN Cell中,将 h 1 , x 2 h_1,x_2 h1,x2作为输入进行运算,输入 h 2 h_2 h2
在这里插入图片描述

所有的RNN Cell使用的是同一个线性层,就是拿同一个线性层反复的参与运算。

用伪代码实现该过程:

h = torch.zeros(...)
line = Liner()
for x in X:
  h = line(x,h)

RNN详细计算过程

输入维度:input_size

隐层维度:hidden_size

在这里插入图片描述

输入先进行线性变换,将输入维度变为隐层维度。所以, W i h W_{ih} Wih的维度应该为 h i d d e n _ s i z e × i n p u t _ s i z e hidden\_size\times input\_size hidden_size×input_size W h h W_{hh} Whh的维度为 h i d d e n _ s i z e × h i d d e n _ s i z e hidden\_size\times hidden\_size hidden_size×hidden_size

W h h h t − 1 + b h h W_{hh}h_{t-1}+b_{hh} Whhht1+bhh的输入应该是一个hidden_size的向量。 W i h x t + b i h W_{ih}x_t+b_{ih} Wihxt+bih的输出是一个hidden_size的向量。

然后将这两个向量直接相加,就把信息融合起来了。

信息融合后,就进行一次激活,使用激活函数为 t a n h tanh tanh函数。( − 1 < t a n h ( x ) < 1 -1<tanh(x)<1 1<tanh(x)<1)。输出的结果就为 h t h_t ht,这一层隐层的输出。

其实,对于两个线性层 W h h h t − 1 + b h h W_{hh}h_{t-1}+b_{hh} Whhht1+bhh W i h x t + b i h W_{ih}x_t+b_{ih} Wihxt+bih,这两个运算可以拼在一起,如(简写)
W 1 h + W 2 x = ( W 1 , W 2 ) ( h x ) W_1h+W_2x = (W_1,W_2){h \choose x} W1h+W2x=(W1,W2)(xh)
使用Pytorch构造RNN

两种方式:

  • 自己制作RNN Cell,自己写一个处理序列的循环
  • 直接使用pytorch提供的RNN

RNN Cell使用

cell = torch.nn.RNNCell(input_size=input_size,hidden_size=hidden_size)

我们定义了rnn cell,我们在调用时,要传入当前这一时刻的输入以及当前的hidden,然后输出一个hidden。

hidden = cell(input,hidden)

我们需要重点关注输入的维度隐层的维度

input:shape(batch, input_size)

(输入)hidden:shape(batch, hidden_size)

(输出)hidden:shape(batch, hidden_size)

dataset.shape = (seqLen,batchSize,inputSize)

代码实现,使用RNN Cell

import torch
from torch import nn

batch_size = 64
seq_len = 3
input_size = 4
hidden_size = 2

cell = nn.RNNCell(input_size,hidden_size)

dataset = torch.randn(seq_len,batch_size,input_size)
hidden = torch.zeros(batch_size,hidden_size)

for idx,input in enumerate(dataset):
    print("="*20,idx,"="*20)
    print("Input size: ",input.shape)
    hidden = cell(input,hidden)
    print("outputs size: ",hidden.shape)
    print(hidden)

直接使用RNN

在这里插入图片描述

import torch
from torch import nn

batch_size = 1
seq_len = 3
input_size = 4
hidden_size = 2
num_layers = 1  # RNN有多少层

inputs = torch.randn(seq_len, batch_size, input_size)
hidden = torch.zeros(batch_size, hidden_size)

cell = nn.RNN(input_size, hidden_size, num_layers=num_layers)

out, hidden = cell(inputs, hidden)
# out:h1,h2,...,hn
# hidden:hn

在这里插入图片描述

使用RNN,需要确定:

  • 批量大小
  • 序列长度
  • 输入维度,输出维度
  • 需要几层的RNN

batch_size=1:相当于只有一句话

seqsize=3:相当于每句话有三个单词

input_size=4:相当于每个单词有4个特征

有10个样本数据,每个样本数据包含3天的天气数据,然后每天的天气的数据有10个特征。

就是seq_size=3,batch_size=10,input_size=10

  • seq_size=3:每个样本是一个包含3个时间步(3天)的序列。
  • batch_size=10:在一个训练批次中,模型将同时处理10个不同的样本。
  • input_size=10:每个时间步(每天)的输入特征有10维。

numLayers:多层RNN

在这里插入图片描述

batch_first属性:如果设置为True·,则需要将batch_size设置为第一个参数。

在这里插入图片描述

例子

“hello” –>“oholo”

在这里插入图片描述

首先,我们要将输入的这些字符进行向量化,用向量进行表示。

根据字符构造一个词典,给每个字符分配一个索引。

在这里插入图片描述

得到索引之后,根据词典,把每一个词变成相应的索引。

使用on-hot,将此转为向量:

在这里插入图片描述

比如h的索引为1,那么向量中,只有第二个数字(索引0,1,…)的值设为1,其他都为0。

然后将独热向量作为RNN的序列的输入。输出为长度为4的向量,表示4个字符对应的值,然后接一个交叉熵损失函数。(多分类问题)

在这里插入图片描述

训练部分代码

使用RNN Cell

for epoch in range(15):
  loss = 0
  optimizer.zero_grad()
  hidden = net.init_hidden()
  print('Predicted string: ',end='')
  for input,label in zip(inputs,labels):
    hidden = net(input,hidden)
    loss += criterion(hidden,label)
    _,idx = hidden.max(dim=1)
    print(idx2char[idx.item()],end='')
  loss.backward()
  optimizer.step()
  print(', Epoch [%d/15] loss=%.4f'%(epoch+1,loss.item()))

使用RNN

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(),lr = 0.05)

for epoch in range(15):
  optimizer.zero_grad()
  outputs = net(inputs)
  loss = criterion(outpus,labels)
  loss.backward()
  optimizer.step()
  
  _,idx = outpus.max(dim=1)
  idx = idx.data.numpy()
  print('Predicted: ',''.join([idx2char[x] for x in idx]),end='')
  print(', Epoch [%d/15] loss=%.3f'%(epoch+1,loss.item()))

ont-hot字/词编码:

  • 独热向量维度太高
  • 向量过于稀疏
  • 硬编码

所以,我们想找到一种低维、稠密、通过学习得到的方法,来建立词和向量之间的关系:一种流行的方法为:Embedding(嵌入层)

Embedding:把一个高维的、稀疏的样本映射到一个稠密的、低位的空间里面。(数据降维)

在这里插入图片描述

假设,原来的输入一共有4个维度,也就是独热向量是4维的。

在这里插入图片描述

假设嵌入层是5的,从4维转换为5维,就构建上图这种的矩阵。可以在里面进行查询。比如输入的是2,就是指第2个字符,在表里面找到第二行(索引:2),把向量输出。

在这里插入图片描述

有了Embedding,网络就可以变为:

在这里插入图片描述

在RNN Cell之上我们又连了一层线性层,因为隐层输出必须和分类的数量一致,但是有时候不一致,就再接一个线性层解决该问题。

LSTM 长短期记忆网络

在这里插入图片描述

RNN网络联系

根据名字,对其不同的语言进行分类,如下

在这里插入图片描述

网络结构:

在这里插入图片描述

在这里插入图片描述

输入为名字,输出为相应的国家。

输入x首先经过嵌入层,然后GRU Layer,然后通过线性层变成想要的输出结果的维度。(GRU是简化的LSTM)

要先将名字转为一个序列

在这里插入图片描述

然后对序列做词典,因为这里都是英文字符,我们可以用ASCII码表(0~127)作为字典,那么我们将字典长度设置为128,然后去求我们的每一个字符所对应的ASCII码值。

在这里插入图片描述

如,第一个名字中的‘M’字符,所对应的为77,意思是指M所对应的为一个独热向量,第77位为1,其他位置全部为0。

存在问题:输入序列的长短不同。解决办法:我们进行padding操作。

在这里插入图片描述

对名字信息处理完成后,对国家信息进行处理,将国家名字转为一个分类索引。

在这里插入图片描述

双向循环神经网络

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
刘二大人的PyTorch课程是一门非常有价值的课程。PyTorch是一种开源的机器学习框架,是当前深度学习领域非常流行的工具之一。这门课程由刘二大人亲自授课,拥有丰富的实战经验和深厚的理论功底,对于学习PyTorch和深度学习的同学来说是一个绝佳的选择。 在这门课程中,刘二大人会从基础开始,逐渐介绍PyTorch的各个方面,包括张量操作、自动微分、搭建神经网络等内容。刘二大人讲解的方式深入浅出,通俗易懂,能够让学生快速上手,理解PyTorch的核心概念和使用方法。 另外,这门课程还将涵盖一些实际项目案例,通过实战演练的方式,帮助学生将理论与实践相结合,掌握如何使用PyTorch解决实际问题。刘二大人会分享一些自己在实战项目中遇到的经验和技巧,对于学生们来说是非常宝贵的学习资源。 除了讲授知识,刘二大人还非常注重培养学生的动手能力和解决问题的能力。他会布置一些编程练习和作业,要求学生们按时完成并提交。通过这些练习和作业,学生们可以不断巩固所学知识,提高自己的编程和解决问题的能力。 总之,刘二大人的PyTorch课程是一门内容丰富、实用性强,能够帮助学生快速上手PyTorch并解决实际问题的课程。无论是对于初学者还是有一定经验的人来说,都是一个非常值得推荐的学习资源。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

mango1698

你的鼓励是我最大的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值