第二周学习周报
- 摘要
- Abstract
- 一、深度学习
- 1.Backpropagation(反向传播)
- 1.1 链式法则
- 1.2 Forward pass(前向传播)
- 1.3 Backward pass(向后传播)
- 1.4 总结
- 2. Regression(神奇宝贝案例)
- 2.1 第一步:设置Model(A set of function)
- 2.2 第二步:评估函数的好坏(goodness of function)
- 2.3 第三步:找到最好的function(best function)
- 2.3.1 Gradient Descent
- 2.3.1.1 Gradient Descent的最小值问题(linear regression没有local optimal)
- 2.4 结果以及优化
- 2.4.1 过拟合现象(overfitting)
- 2.6 改善模型
- 2.6.1 重新设置模型(Redesign the model)
- 2.6.2 正则化(Regularization)
- 二、Pytorch学习
- 1. Pytorch加载数据的初认知
- 1.1 Dataset与Dataloader
- 1.2 Dataset代码实战
- 总结
摘要
这周主要对Deep Learning进行了进一步的学习,内容包括反向传播算法的过程原理。还学习了regression,根据宝可梦的案例学习到了Loss中使用正则化的技巧。此还继续学习了Pytorch课程,包括学习Dataset和Dataloader各自的功能,以及Dataset的代码实战。
Abstract
This week, I mainly conducted further learning on Deep Learning, including the process principle of backpropagation algorithm. I also learned about regression and learned the technique of using regularization in Loss based on the case of Pok é mon. I also continued to study Python courses, including learning the functions of Dataset and Dataloader, as well as practical coding for Dataset.
一、深度学习
1.Backpropagation(反向传播)
回顾一下梯度下降的过程:
在学习梯度下降的算法中,我们计算的过程中的θ这个vector(向量)是非常长的,即可能会有上百万个参数,为了有效率地计算面对这么多参数,我们就需要使用反向传播算法去完成θ的计算了。
1.1 链式法则
链式法则是在学习高等数学中求复合函数导数非常常用的一种方法。完成链式求导主要需要掌握两个步骤:
1、列出各个变量之间的关系
2、根据关系写出链式(同一条路径相乘、不同路径相加)
例子如下:
掌握了链式法则后,我们就要用来解决实际的问题了。
假设我们神经网络结构如下图所示,
其中Cn是yn与ŷn的交叉熵(可以理解为它们之间的距离,距离越近则误差越小)
我们要求Cn对w(权重未知量)的偏导。
1.2 Forward pass(前向传播)
因为我们的C是经过整个神经网络得出结果后得出预测值y与真实值ŷ计算出来的,所以是一个整体的性的值,要求偏导,就要一层一层的计算。
所以,我们把下图的下三角先提出来,先处理这个部分的计算从而以小见大,理解整个个过程的计算。
在这个上三角中,我们有::
feature :x1与x2
未知量:w1,w2,b
z:是他们计算结果。
那么我们如何用这些变量来表示C对w的偏导呢?
先拿w1举例:
在上述计算总结中,我们很明显可以发现一个规律,就是w(权重)对应的偏导,就是其对应输入的值。
比如,下图中,w = 1对应的偏导为 -1(输入值)、w = -1 对应的偏导值为0.12(输入值)…以此类推。
1.3 Backward pass(向后传播)
那么处理完z对w的偏导,还有一个令人头痛的C对z的偏导要计算,因为我们如果用z的变量表达C,就要一直推导,非常麻烦,因为z后面还有N多层。那么要如何解决呢?
假设我们再往下走一层,就有了以下参数:
a:z经过sigmoid运算后的结果。
w3、w4…:未知数
z’‘,z’':同z一个意思。
表示如下:
假设1:其下一层就是输出层。
计算方式如下:
假设其下一层不是输出层,就要找其下一层再推导,直到找到输出层为止。
所以,一开始从输出层往前推导快一点,因为都是已知结果
1.4 总结
计算z对w偏导用forward pass,计算C对z的偏导用Backward pass
2. Regression(神奇宝贝案例)
在学习regression中,我们了解到其实一个回归问题,用于解决预测问题。
比如输入对应的函数,就会输出一个结果。
如下图所示,可以用于股市趋势的预测、自动驾驶的场景、网络购物推介等
下面我们用预测宝可梦的战斗力的例子,来更加深入的了解Regression。
我们输入函数的参数如下:
Xcp:是feature,表示宝可梦原先的战斗力。
Xs:是宝可梦的名字。
Xhp:是宝可梦的血量
Xw:宝可梦的重量
Xh:宝可梦的高度
2.1 第一步:设置Model(A set of function)
假设我们设置一组函数(可以有无数个),这些函数都是线性的(linear model),但是不一定是正确的,需要使用training date来验证哪个函数最合理,最后用来预测宝可梦进化后的战斗力。
2.2 第二步:评估函数的好坏(goodness of function)
我们根据进化后的真实值与初始值一一对应起来,并做成一幅直观的图。
这些数据称之为training date
横轴代表初始战斗力(x1…x10)。
纵轴代表精华后的真实战斗力(ŷ1…ŷ10)。
要评估模型的好坏,就要用到Loss function,这里我们采用平方差的方式来表示Loss的大小,其实Loss function就是用来输出这个模型到底有多差(所以L越小越好)
关于括号里面数字的由来:
在下面的图像中,图像的颜色代表L的大小,越红代表数值越大,越偏蓝色代表数值越小
2.3 第三步:找到最好的function(best function)
找到最佳的function,就要找到w与b使L 最小(min)。
2.3.1 Gradient Descent
为了找到最小值,我们还是使用 Gradient Descent,接下来我们复习一下:
假设只有一个未知数w。
其中包含local optimal(是我们随机取点,一直找到的最小值)和global optimal(全局最小值,基本上很难实现)
假设有两个未知数w和b,我们可以以此类推:
我们就可以先带入w0,b0,求偏导值,然后算出w1,b1,再代入w1,b1求出w2,b2.以此类推。
这时候的L的梯度就是一个二维的Vector
再多的参数就再加维度即可。
于是我们把,偏导完整的格式写出来,如下图所示:
用图像来表示上述过程,如下图:
2.3.1.1 Gradient Descent的最小值问题(linear regression没有local optimal)
当我们使用gradient decent时候,会出现一个问题:
比如在下图的左图中,如果我们随机取不同的点,L最小值也会不一样。
完全看我们的人品
但是!
线性回归不会出现这个问题,
因为其Loss function是convex(凸面的),无论我们从哪一点出发,都可以找到同一个最小值。
2.4 结果以及优化
经过计算,我们得到了最佳的w与b。
运用这套线性的模型,我们最终得出一个结果:
在测试集上的L为35.0,在训练集上为31.9.
那么我们是否有办法让其L更低,让模型更加准确呢?
2.4.1 过拟合现象(overfitting)
于是我们可以增加未知量,让模型变得更加复杂,模型也就更加的精准。
我们先添加一个w2的未知数到方程中,看到结果在测试集和训练集中的Loss都减少了,
测试集的平均错误:从35.0->18.4
训练集的平均错误:从31.9->15.4
于是我们继续增加w3,w4,w5,持续增加模型的复杂度,如下图所示:
按照理论来说,我们模型的复杂度越高,在训练集中的错误就越低(前提是使用gradient descent找到最佳的未知数值)。
但是从这个表格中,我们就发现一个问题:
随着模型越来越复杂,我们的Training的average Error使越来越低,但是我们在Testing上的Error却在w4后越来越高,特别是加入w5后达到了惊人的232.1。
这种现象就称之为过拟合现象(overfitting),通常是由于模型过于复杂导致的。
因为我们最重要的还是要预测数据,所以testing上的Error是非常重要的(要做大考型选手,不做模拟哥)
因此,我们最佳的模型实际上就是加入w3后的模型。
2.6 改善模型
我们上面设置的模型,只考虑了个别的情况,但是我们的宝可梦进化实际上是受很多条件影响的,例如:不同物种之间进化后的强度是不一样的。
因此我们要考虑上一些隐藏的因素,再重新设置模型。
2.6.1 重新设置模型(Redesign the model)
我们可以设置这样一个Model:使用一个类似编程语言中的if语句来完成不同物种进化使用不同model的功能
如下图中所示:
当我要预测Pidgey的进化后的攻击力,其他物种的Xs都为0,只有Xs=Pidgey的Xs为1
经过这一改变后,
我们的Training Data的Average error为3.8
Tesing Data的Average error 为14.3.
很明显这种对症下药的model效果非常显著
再或者我们在这个基础上,让模型更加复杂一点我们再增加一些未知数,模型说不定会预测地更加的精准
2.6.2 正则化(Regularization)
正则化是一种防止模型过拟合的手段,通过给损失函数加上一些限制条件,使模型参数更接近于0。
我们在Loss function中可以加入一个λ∑(wi)²,来使得线段更加平滑。
为什么要变得平滑呢?
因为越平滑的线就越能够减少外界的干扰,预测的准确度就越高(比如我们的输入数据有受到了一些干扰,会影响结果,我们加入这个部分后,就能减少这些干扰对结果的影响)
我们通过控制λ的大小来控制线的平滑程度,我们需要平滑,但不能过于平滑。
因为可以看到当我们的λ从0增加到100,其在训练集和测试集上的Error都再一直下降
但是100后,图像就有一个转折点,再增加Error就变大了。
因此我们要根据测试结果,选定一个最佳λ,使得我们的效果最佳。
二、Pytorch学习
1. Pytorch加载数据的初认知
1.1 Dataset与Dataloader
在Pytorch中如何读取数据主要分为两个类,一个是Dataset、一个是Dataloader。
假设数据就是一堆垃圾,我们要在这堆垃圾中寻找我们有用的数据。
就要使用Dataset完成,然后经过Dataloader打包后,再传入网路中,如下图所示:
其中Dataset与Dataloader的功能如下:
名称 | Dataset | Dataloader |
---|---|---|
功能 | 提供一种方式去获取数据以及其label (获取每一种数据以及其label、并告诉我们共有多少数据) | 为后面的网络提供不同的数据形式 |
1.2 Dataset代码实战
接下来,我们下载一个数据集,来学习Dataset
数据集下载链接https://download.pytorch.org/tutorial/hymenoptera_data.zip
解压打开后可以看到这个数据集包括训练集和验证集,里面都是一些蚂蚁和蜜蜂的图片,如下图所示:
这里的label 就是文件夹对应的名称(这是众多的组织形式之一)
接下来,我们启动jupyter notebook,输入以下代码,引入Dataset类
from torch.utils.data import Dataset
然后我们可以使用help函数,查看使用方法
help(Dataset)
或者使用**Dataset??**查看更加直观的使用方法
Dataset??
从上述英文,我们大致了解到Dataset的作用就是用来提取数据的。
图片作为输入,一般是将图片的路径
接下来我们把数据集放到项目里(目的是为了生产相对路径,好说明图片位置):
我们在python console中输入代码,将图片的绝对路径传输进去
from PIL import Image //导入PIL用于导入图片
img_path = "E:\DeepLearing\learn_pytorch\\Dataset\\train\\ants\\0013035.jpg" //图片绝对路径
img = Image.open(img_path) //赋值给img
img.size //查看图片的尺寸
img.show() //展示图片
效果如下:
import os
dir_path = "Dataset/train/ants"
img_path_list = os.listdir(dir_path)
然后,如果我们想要获取图片的所有地址,就需要用list获取图片文件夹,然后再用getitem获取的idx获取每张图片的地址。
在pycharm上输入如下代码:
(//后面为注释)
from torch.utils.data import Dataset //引入Dataset类
from PIL import Image //用于导入图片
import os //用于导入图片路径
class MyData(Dataset):
def __init__(self, root_dir, label_dir): //初始化。self用于该类的全局变量,用于后面两个函数的作为变量使用
self.root_dir = root_dir //根路径 比如:"Dataset/train"
self.label_dir = label_dir //标签名,比如"ants"或"bees"
self.path = os.path.join(self.root_dir, self.label_dir)//拼接在一起
self.img_path = os.listdir(self.path) //转化为字节流
def __getitem__(self, idx): //该函数用于获取各图片
img_name = self.img_path[idx] //idx,表示每张图片序号,例如:idx = 0表示第一张图
img_item_path = os.path.join(self.root_dir, self.label_dir, img_name) //拼接在一起
img = Image.open(img_item_path) //赋值图片路径
label = self.label_dir //赋值标签名字
return img, label //返回图片信息和标签名
def __len__(self): //用于返回有多少张图
return len(self.img_path) //返回图片长度
//类实例化
root_dir = "Dataset/train" //训练集根路径
ants_label_dir = "ants" //蚂蚁标签
bees_label_dir = "bees" //蜜蜂标签
ants_dataset = MyData(root_dir, ants_label_dir) //创建蚂蚁dataset实例
bees_dataset = MyData(root_dir, bees_label_dir)//创建蜜蜂dataset实例
train_dataset = ants_dataset + bees_dataset //创建蚂蚁和蜜蜂dataset实例
输入后便创建了实例
运行如下代码,展示蚂蚁训练集第一张图片:
img,label = ants_dataset[0]
img.show()
运行如下代码,展示蜜蜂训练集第一张图片:
img,label = bees_dataset[0]
img.show()
len(bees_dataset)//蜜蜂训练集图片数量
len(ants_dataset)//蚂蚁训练集图片数量
len(train_dataset)//总训练集数量(蜜蜂 + 蚂蚁)
接下来,我们输入如下代码,创建image对应的label文件
首先我们要在Dataset目录下创建一个ants_label与bees_label用来存放txt的label文件。
代码如下:
import os
root_dir = "Dataset/train"
target_dir = "ants_image"
img_path = os.listdir(os.path.join(root_dir, target_dir))
label = target_dir.split('_')[0]
out_dir = "ants_label"
for i in img_path:
file_name = i.split('.jpg')[0]
with open(os.path.join(root_dir, out_dir, "{}.txt".format(file_name)), 'w') as f:
f.write(label)
这段代码的作用是将指定目录下的所有以".jpg"结尾的图片文件的标签写入同名的".txt"文件中。假设有一个名叫"antsimage"的目录,里面存放了一些以"ants"开头的蚂蚁图片,我们需要将其标签写入同名的".txt"文件中,以便后续使用。
代码核心部分使用了Python的os模块来定位文件位置和创建文件
主要分为以下步骤:
1. 定义根目录rootdir、目标目录targetdir和标签label。在该段代码中,rootdir指的是存放所有图片的目录;targetdir指的是存放待处理图片的目录名称,本例中为"ants_image";而label则是标签,这里为"ants"。
2. 获取目标文件夹下所有图片文件的名称,并去掉文件扩展名".jpg",只保留文件名。
3. 遍历所有文件,使用with open()语句创建同名".txt"文件,并向其中写入标签label。
4. 循环结束后,所有的图片的标签都写入了同名".txt"文件中,存放在指定的目录out_dir下。
总之,这段代码的作用是将一些图片的标签写入同名文件中,方便后续使用。
将上述代码的ants换成bees就可以生成蜜蜂的label文件
总结
这一周在Deep Learning的课程李宏毅中学习到了反向传播算法、regression宝可梦案例,其中在反向传播算法主要是为了提高了在梯度下降算法中的计算效率,在反向传播算法中分为foward pass和 backward pass两种模式,都是解决了在链式求导的裂项中如何求值的问题。在regression中复习了之前的内容,并学习率正则化(Regularization)这个知识点,主要用于防止过拟合,使Loss变得平滑,减少干扰。此外还继续学习了pytorch,学会了如何使用Dataset和定义其里面的getitem(用于获取图片)和len(用于统计图片数量)函数,并用文件操作完成label文件的批量创建。
最后希望继续保持学习的热情,更多的去了解底层原理,下一周计划学习classification的神奇宝贝案例、逻辑回归,以及继续学习pytorch的TensorBoard的使用课程。