【Pytorch深度学习实践】B站up刘二大人之LinearModel -代码理解与实现(1/9)
【Pytorch深度学习实践】B站up刘二大人之 Gradient Descend-代码理解与实现(2/9)
【Pytorch深度学习实践】B站up刘二大人之 BackPropagation-代码理解与实现(3/9)
【Pytorch深度学习实践】B站up刘二大人之LinearRegressionwithPyTorch-代码理解与实现(4/9)
【Pytorch深度学习实践】B站up刘二大人之LogisticsRegression-代码理解与实现(5/9)
【Pytorch深度学习实践】B站up刘二大人之MultipleDimensionLogisticRegressionModel-代码理解与实现(6/9)
【Pytorch深度学习实践】B站up刘二大人之Dataset&DataLoader-代码理解与实现(7/9)
【Pytorch深度学习实践】B站up刘二大人之SoftmaxClassifier-代码理解与实现(8/9)
【Pytorch深度学习实践】B站up刘二大人之BasicCNN & Advanced CNN -代码理解与实现(9/9)
numpy
和matplotlib
库;x_data
和 y_data
;forward
:输出是预测值y_hat
loss
:损失函数定义为MSE:均方根误差w_list
和纵轴的mse_list
w
表示;x_val
和真实值y_val
;forward
函数,计算预测值 w*x
(y_hat
)loss
函数,计算单个数据的损失数值;x_val
、y_val
、loss_val
;plt.plot(w_list, mse_list)
plt.xlabel(‘w’)
plt.ylabel('Loss)
plt.show()
【Pytorch深度学习实践】B站up刘二大人之LinearModel -代码理解与实现(1/9)
numpy
和matplotlib
库;x_data
和 y_data
;forward
:输出是预测值y_hat
cost
:损失函数定义为MSE:均方根误差gradient
很有意思,也不简单:需要根据反向传播,计算出代价函数对于权重的梯度,计算结果为grad += 2 * x * (x * w - y)
;w_list
和纵轴的mse_list
epoch
可以自定义:
cost
计算数据集的代价值,注意,这里要传入所有数据,而不是再去用·zip·函数配合·for·循环每次取一个,因为在`cost·函数当中,已经写过单个数据对儿从数据集调出的过程;grad
,与上个函数相同;x_val
、y_val
、loss_val
;【Pytorch深度学习实践】B站up刘二大人之 Gradient Descend-代码理解与实现(2/9)
cost
和loss
的区别,而在于权重更新那里我没有懂透;forward
函数没有变化;loss
、gradient
函数有变化,把cost
当中,原本需要把数据对儿从数据集中取出的过程,全部转移到训练过程中,这个过程不是难点,不过刚动手写的时候,就会很困惑,尤其是别的地方也有困惑,就觉得难度很大,应付不过来;【Pytorch深度学习实践】B站up刘二大人之 Gradient Descend-代码理解与实现(2/9)
【Pytorch深度学习实践】B站up刘二大人之 BackPropagation-代码理解与实现(3/9)
torch
包的requires_grad
自动求导,构建反向传播模型:
导入numpy
和matplotlib
库;
导入数据 x_data
和 y_data
;
初始化一个w
数值,这里要用torch.Tensor构建;
把w
的 requires_grad
参数打开;(重点)
定义前向传播函数:
forward
:输出是预测值y_hat
定义损失函数:
loss
:损失函数定义为MSE:均方根误差定义梯度计算函数:
gradient
: 需要根据反向传播,计算出代价函数对于权重的梯度,计算结果为grad += 2 * x * (x * w - y)
;创建两个空列表,因为后面绘图的时候要用:
开始训练:
epoch
可以自定义:x_val
和真实值y_val
;loss
函数,计算单个数据的损失数值;backward
函数直接对损失值做反向传播;w
的 grad.data
做更新,这是重要一步,也是内容非常丰富的地方,不仅是更新了权重,里面还有关于 tensor
的其他基础知识, data
和 grad
的关系, data
是不会构建计算图的,而 grad
是在构建计算图模型;x_val
、y_val
、loss_val
;【Pytorch深度学习实践】B站up刘二大人之 BackPropagation-代码理解与实现(3/9)
【Pytorch深度学习实践】B站up刘二大人之LinearRegressionwithPyTorch-代码理解与实现(4/9)
torch
包的requires_grad
自动求导,构建反向传播模型:
导入numpy
、matplotlib
、os
、torch
库;
导入数据 x_data
和 y_data
,注意这里有变化,用到了torch
中的Tensor
格式,与04课程相同,所以才会出现data
和grad
的重要区别;
创建LinearModel类模型:
init
中,只有一个linear
函数;注意要提前继承(这一步直接写,不深究);forward
方法,这是把之前的前向传播函数,拿进来做了类中的一个方法,返回预测值;实例化一个模型model
;
创建损失函数:通过调用torch.nn
库中的MSELoss
实现;
创建优化器:通过调用torch.optim
库中的SGD
实现;
创建两个空列表,因为后面绘图的时候要用:
创建循环,开始训练:循环的次数epoch
可以自定义
y_hat
用模型model
计算;loss
用创建好的损失函数计算;loss.backward
;optimizer.step( )
很奇特,这里非常关键,理解起来不容易,见下面的链接;在循环中要把计算的结果,放进之前的空列表,用于绘图;
在获得了打印所需的数据列表只有,模式化地打印图像:
【Pytorch深度学习实践】B站up刘二大人之LinearRegressionwithPyTorch-代码理解与实现(4/9)
理解optimizer.zero_grad(), loss.backward(), optimizer.step()的作用及原理
sigmoid
的函数,而这个函数,是在 torch.nn.functional
当中的,所以,先设置一个句柄等待使用: torch.nn.functional as F
;y_data
的值,不再是原来的预测:学习多久,得多少分?而是: x_data
个小时,是否会通过考试,把原本的预测回归问题,转为预测分类问题,而且此处是二分类,最终预测结果只有0
和1
,要么通过,要么不通过;sigmoid
函数,这个函数,值域是(0,1),把原本输出的得分数值转为能够通过考试的概率,就是在这里用到了 F
这个函数句柄,F.sigmoid
;MSE
函数,而是使用分类任务常用的交叉熵损失函数,这个里面的原理,需要懂,刘老师讲完一下子就理解了,用到了 torch,nn,BCELoss
函数;x_test = torch.Tensor([[4.0]]) y_test = model(x_test)
【Pytorch深度学习实践】B站up刘二大人之LogisticsRegression-代码理解与实现(5/9)
【Pytorch深度学习实践】B站up刘二大人之MultipleDimensionLogisticRegressionModel-代码理解与实现(6/9)
anaconda
安装工具包中的自带的压缩文本数据,所以直接采用numpy
中的loadtxt
读取,这个函数可以直接读取Linux
下压缩的格式,此处是.gz
;double
类型的数据,而普通的显卡读取float32
;label
,这是在文本文件中已经确定好了的,最后一列为y_data
,其余为x_data
;init
中,不再只有一个linear
函数;
self.linear
,此处是8 → 6 → 4 → 1
;其中两个参数分别为in_channel
数量和out_channel
数量;Sigmoid
函数,将最终的输出值,转为0-1
区间上的概率值,这个内容是上一节中通过函数句柄F.sigmoid
实现的,但此处的函数名称要大写,容易出错;forward
中,直接调用init
中定义好的各层,此处要注意,输入为x
,返回值也定义为x
吧:x = self.sigmoid(self.linear1(x))
,否则出错了不好排除问题,并且sigmoid
是小写开头,因为在init
中,定义的时候,是以小写开头定义的,注意逻辑;【Pytorch深度学习实践】B站up刘二大人之MultipleDimensionLogisticRegressionModel-代码理解与实现(6/9)
【Pytorch深度学习实践】B站up刘二大人之Dataset&DataLoader-代码理解与实现(7/9)
本节内容,主要是把数据集写成了一个类,这个类要继承Dataset
类,有点像DIY一个数据集的感觉,只有自定义了之后,才能实例化,然后把之前直接在文件夹中读取数据的方式进行了修改;
后面加载数据的DataLoader
(注意L大写),直接可以调用对数据集类做了实例化的对象,即把他当做一个参数,传入DataLoader
当中;
Dataset
后我们必须实现三个函数:
__init__()
是初始化函数,之后只要提供数据集路径,就可以进行数据的加载,也就是说,传入init
的参数,只要有一个文件路径就可以了;getitem__()
通过索引找到某个样本;__len__()
返回数据集大小;【Pytorch深度学习实践】B站up刘二大人之Dataset&DataLoader-代码理解与实现(7/9)
【Pytorch深度学习实践】B站up刘二大人之SoftmaxClassifier-代码理解与实现(8/9)
MNIST
的数据集,那dataloader
和dataset
自然也是内置的,那也就不用自己写dataset
再去继承Dataset
类;train
和test
写成了函数形式,直接在main
函数当中调用即可;softmax
这个函数的机理:y_pred = np.exp(z) / np.exp(z).sum()
;loss = (- y * np.log(y_pred)).sum()
;numpy
的计算法则把公式实现了,之后才去调用了pytorch
当中写好了的函数;NLL-Loss
的概念,并且留了思考题,为什么会:CrossEntropyLoss <==> LogSoftmax + NLLLoss
?pytorch
当中,里面有Softmax
和Softmax_log
两个函数版本;【Pytorch深度学习实践】B站up刘二大人之SoftmaxClassifier-代码理解与实现(8/9)
【Pytorch深度学习实践】B站up刘二大人之BasicCNN & Advanced CNN -代码理解与实现(9/9)
对于基础知识,不写代码了,把所有讲到的知识点,和每个知识点该注意的,以及扩展知识整理记录:
从单通道卷积讲起:即input
图像是单通道,卷积核kernel
也是单通道,那么输出必然也是单通道。这里还没有讲到扩充padding
和滑动步长stride
,所以只关注输入和输出矩阵的尺寸大小;
讲到这,刘老师介绍了CCD相机模型
,这是一种通过光敏电阻,利用光强对电阻的阻值影响,对应地影响色彩亮度实现不同亮度等级像素采集的原件。三色图像是采用不同敏感度的光敏电阻实现的。
还介绍了矢量图像(也就是PPT里通过圆心、边、填充信息描述而来的图像,而非采集的图像);
接下来讲到三通道input图像
的卷积操作:实为将三个通道的input
分别与三个通道kernel
对应相乘,再将对应位置相加,最后输出单通道output图像
的过程;
此处的input、kernel、output
的形状,以及通道数channel
,是要非常注意的,我们玩卷积神经网络,其中,向的卷积层中传递的参数,与这里的参数一一对应,要非常熟悉每一个参数位置,应该是谁的channel
值,后面的full connected layer
也是一样,传入的是位置参数,也就是说,每一个位置必须是所需对应的参数,决不能混乱;
这里还有个我以前搞不清的地方,在刘老师的课上听明白了:就是这种图是什么意思,长宽高分别对应的是图像的宽高通道数,分别怎么对应的,我得插一张图了(这应该是篇博客第一章图):
以上是单个卷积核的获取的结果,也就是说,上图中见kernel
的位置,仅仅是一个(组)卷积核;如果我们要提取不同的特征,就需要不同(组)的卷积核,也就是说,会出现这样一种情况:
于是,kernel其实是一个4维的tensor
,每一个(组)filter
是三维,多个这样的filters
,就需要是4个dimension
的概念,你,看懂了吗。
接下来讲了卷积层建立的方式,以及代码的简单实现,详细内容见本节插入的链接;
在后面讲了三个卷积的基本操作,扩充padding
、步长stride
和池化pooling
:
padding
是为了让源图像最外一圈或多圈像素(取决于kernel
的尺寸),能够被卷积核中心取到。这里有个描述很重要:想要使源图像(1,1)
的位置作为第一个与kernel
中心重合,参与计算的像素,想想看padding
需要扩充多少层,这样就很好计算了吧;stride
操作指的是每次kernel
窗口滑动的步长,默认值当然是1了,插句话,假设不使用扩充padding,output
图像的尺寸就会缩小,想要使输出的尺寸与输入尺寸保持不变,看看上一个知识点的padding
描述,就很好计算需要外圈加几圈去保证输出的尺寸了吧;Fully Connected Layer
(分类器)的输入,需要通过前面最后一层的计算来求得;而FCY
的输出,是确定的(分10类就输出10);GPU
的过程,可以参看我其他的博客,关于这个知识点,我是觉得土堆的讲解更好,整个网络,有三个内容需要传入GPU
去计算:[1]网络模型、[2]数据(input
和label
)、[3]损失函数loss
。参见【PyTorch教程】P30 GPU加速;至此,刘老师Basic CNN
这节课的全部知识点都在这里了,代码的简单实现见节尾链接。
CNN
,刘老师在这节课里讲了两个网络,一个是GoogLeNet
,另一个是ResNet
。分别记录重点:
GoogLeNet
:以往无论的LinearModel
还是SoftmaxClassifier
,其网络结构都是一个线性且没有‘岔路’的传递形式,成为单线结构
,但效果一般;GoogLeNet
是一种串行结构
的复杂网络;想要实现复杂网络,并且较少代码冗余和多次重写相同功能的程序,面向过程的语言使用函数
,面向对象的语言python
使用类
,而在CNN
当中,使用Module
和block
这种模块将具有复用价值的代码块封装成一块积木
,供拼接
使用;GoogLeNet
为自己框架里被重复使用的Module
命名为Inception
,这也电影盗梦空间的英文名,意为:梦中梦、嵌套;GoogLeNet
设计了四条通路支线
,并要求他们保证图像的宽和高W
、H
必须相同,只有通道数C
可以不相同,因为各支线
进行过卷积和池化等操作后,要将W
和H
构成的面为粘合面
,按照C
的方向,拼接concatenate
起来;GoogLeNet
的设计思路是:我把各种形式的核
都写进我的Block
当中,至于每一个支路
的权重,让网络训练的时候自己去搭配;1 * 1
的卷积核:以往我只是表面上觉得,单位像素大小的卷积核,他的意义不过是调整输入和输出的通道数
之间的关系;刘老师举了个例子,让我对这个卷积核有了新的认识:就是加速运算,他的作用的确是加速运算,不过其中的原理是:通过1*1
的核处理过的图像,可以减少后续卷积层的输入通道数;GoogLeNet
代码当中需要注意的是卷积层的输入和输出通道数,里面涉及的细节与其他层之间的差异要特别注意;cancatenate
的时候,有一个参数是选择按照什么方向进行组合,这里的dim=1
的原理在这:(B,C,W,H)
按索引来讲,C
指的是通道数,索引从零开始,C
的位置是1,这个问题困扰了我好久,我一直都以为要死记硬背;GoogLeNet
最后留下了一个问题:通过测试,网络的层数会影响模型的精度,但当时没有意识到梯度消失
的问题,所以GoogLeNet
认为We Need To Go Deeper
;直到何凯明大神的ResNet
的出现,提出了层数越多,模型效果不一定越好
的问题,并针对这个问题提出了解决方案ResNet
网络结构。ResNet
之前,有过对梯度消失
的解决方案:逐层训练
。每当网络想要训练新一层的权重,就将其他层的权重锁住,逐渐将所有层的权重都确定下来,然而神经网络层数太多不容易做到,并且这也太蠢了,失去了人工智能
的该有的智能;ResNet
提出了这样一种方式,来避免深度神经网络在训练过程中出现梯度消失导致靠前面的层没有被充分训练:Plain Net
形式:输入数据x
,经过Weight Layer
(可以是卷积层,也可以是池化或者线性层),再通过激活函数加入非线性影响因素,最后输出结果H(x)
;这种方式使得H(x)
对x
的偏导数的值分布在(0,1)
之间,这在反向传播
、复合函数的偏导数
逐步累乘的过程中,必然会导致损失函数L
对x
的偏导数的值,趋近于0
,而且,网络层数越深,这种现象就会越明显,最终导致最开始的(也就是靠近输入的)层没有获得有效的权重更新,甚至模型失效;ResNet
采用了一个非常巧妙的方式解决了H(x)对x的偏导数的值分布在(0,1)之间
这个问题:在以往的框架中,加入一个跳跃
,再原有的网络输出F(x)
的基础上,将输入x
累加到上面,这样一来,在最终输出H(x)
对输入数据x
求偏导数的时候,这个结果就会分布在(1,2)
之间,这样就不怕网络在更新权重梯度累乘的过程中,出现乘积越来越趋于0
而导致的梯度消失
问题;GoogLeNet
类似,ResNet
的Residual Block
在搭建时,留了一个传入参数的机会,这个参数留给了通道数channel
,Residual Block
的要求是输入与输出的C
,W
,H
分别对应相同,B
是一定要相同的,所以就是说,经过残差模块Residual Block
处理过的图像,并不改变原有的尺寸和通道数;(TBD)Identity Mappings in Deep Residual Networks
:其中给出了很多不同种类的Residual Block
变化的构造形式;Densely Connected Convolutional Networks
:大名鼎鼎的DenseNet
,这个网络结构基于ResNet
跳跃传递的思想,实现了多次跳跃
的网络结构,以后很多通过神经网络提取多尺度
、多层级
的特征,都在利用这种方式,通过Encoder
对不同层级
的语义特征
进行逐步提取,在穿插着
传递到Decoder
过程中不同的层级
上去,旨在融合不同层级的特征,尽可能地挖掘图像全部的特征
;【Pytorch深度学习实践】B站up刘二大人之BasicCNN & Advanced CNN -代码理解与实现(9/9)
【PyTorch教程】P30 GPU加速
numpy
开始实现,还是调用pytorch
调用已经写好的函数直接实现;他们的原理是一样,不过在代码里的表示还有很大不同。这只是我的一点感受。最后,再次感谢刘洪普老师的分享,收获非常大,受教了!