第六周:机器学习周报

摘要

这一周对机器学习进行了学习,其中学会了如何自动调整学习率中调整参数的知识进行了,对RMS与RMSProp的算法进行了学习和理解。此外,还继续学习了Pytorch,学会了torchvision与DataLoader的使用,了解了其中参数的作用。

Abstract

This week, I learned machine learning, learned how to automatically adjust parameters in the learning rate, and learned and understood the algorithms of RMS and RMSProp. In addition, I continued to learn Pytorch, learned how to use torchvision and DataLoader, and understood the role of parameters in them.

机器学习——类神经网络训练不起来怎么办?

1. 自动调整学习率(learning rate)

如下图所示,当我们在训练的过程中loss不再下降时,我们按照之前的经验,常常会以为我们遇到了gradient = 0的情况,即遇到了 local minima 或者 saddle point
在这里插入图片描述
但是事实真的如此吗?
这个loss不再下降的原因,很明显不是gradient 趋于0造成的,因为在iteration(迭代)到300-500次以及600-700次的时候,gradient任然在起伏,而loss也是几乎不怎么变化
就像遇到了local minima,但是实际的情况就是其在一个狭小的山谷中左右跳跃
所以 training stuck(训练卡顿)≠ small gradient
在这里插入图片描述
那么造成这样的原因是什么呢?
首先我们来回顾上一周的知识
如下图所示Minimum ratio表示真正遇到local minima的比率,很明显我们可以知道,我们在在遇到critical point(临界点)时,大多数都是saddle point的问题,因此我们要重视optimization
在这里插入图片描述
但是!!!
我们上面又说道,遇到critical point 未必是saddle point和local minima的问题,那这张图是什么画出来的?
这个图其实用一般的gradient descent其实是做不到的,需要特别方法train,才可以得到这张图
即使没有关键点,训练也可能很困难
其实走到一个critical point,是一件困难的事
多数时候training在还没有走到critical point的时候就已经停止了
这就说明了当用gradient descent来做optimization的时候你真正的魔王往往不是critical point,而是其他的原因。
那么如果不是critical point的问题,为什么我们的training会卡住呢?
我们可以举一个例子来说明。
如下图中,这边有一个非常简单的error surface,下面来大致介绍一下:
1、只有两个参数,这个两个参数值不一样的时候loss值不一样,然后得到了一个error surface。
2、这个error surface的最低点呢在叉叉的地方
3、事实上,这个error surface是convex(凸面)的形状(即它的等高线是椭圆形的)
只是它在横轴的地方gradient非常的小,所以它的坡度的变化非常的小,导致它非常的平滑。
4、所以这个椭圆的长轴非常的长,短轴呢相对之下比较短。
5、而在纵轴的地方gradient的变化很大,所以error surface的这个纵轴坡度非常的陡峭。
在这里插入图片描述
假设我们的黑点为初始点
然后开始做gradient descent
在这里插入图片描述
当我们的Learning Rate设置为

η = 1 0 − 2 {\color{Red} \eta = 10^{-2}} η=102

可以看到,其在上下两端反复横跳
就比如我把上下两端当作是一个峡谷的两臂,叉叉是最低点,然后其在了两臂反复横跳
在这里插入图片描述
所以我们调整Learning Rate设置为

η = 1 0 − 7 {\color{Red} \eta = 10^{-7}} η=107

可以看到这下的gradient descent有点效果了,终于是往最低点的朝向移动
但是到了横向移动时还是停滞了下来(横向很粗的黑线大概进行了100000次update)
在这里插入图片描述
所以显然就算是一个convex的error surface用gradient descent也很难算好

1.1 特制化的Learning Rate——parameter dependent

面对简单的问题的train都做不好,那如果难的问题又怎么有可能做好呢?所以我们需要更好的gradient descent的版本。
在之前的gradient descent里面所有的参数都是设同样的learning rate。这显然是不够的,我们应该要为每一个参数特制化的learning rate

所以特制化的learning rate怎么做呢?
从刚才的例子里面,其实我们可以学习到一个原则
1、如果在某一个方向上我们的gradient的值很小,即在某一个方向上非常的平坦,那我们会希望learning rate调大一点
2、如果在某一个方向上我们的gradient的值很大,即在某一个方向上非常的非常的陡峭,那我们那我们会希望learning rate可以调小一点
在这里插入图片描述
之前我们在gradient descent时候,算一个新的θ参数是用如下公式表示的:
θ i t + 1 ← θ i t − η g i t {\color{Red} \boldsymbol{\theta}_{i}^{\boldsymbol{t}+\boldsymbol{1}} \leftarrow \boldsymbol{\theta}_{i}^{\boldsymbol{t}}-\eta \boldsymbol{g}_{i}^{\boldsymbol{t}}} θit+1θitηgit

因为我们需要使用特制化的learning rate 所以我们需要加一个σ,然后σ与η组合叫做Parameter dependent(参数依赖),如下所示:

θ i t + 1 ← θ i t − η σ i t g i t {\color{Red} \boldsymbol{\theta}_{i}^{\boldsymbol{t}+\boldsymbol{1}} \leftarrow \boldsymbol{\theta}_{i}^{\boldsymbol{t}}-\frac{\eta}{\sigma_{i}^{t}} \boldsymbol{g}_{i}^{\boldsymbol{t}}} θit+1θitσitηgit

其中根据σ的上、下标我们可以看出:
1、σ是parameter dependent,不同的参数我们要给它不同的σ
2、同时σ是iteration(迭代) dependent,不同的iteration,我们也会有不同的σ

在这里插入图片描述
那接下来我们要看这个parameter dependent的有什么常见的计算方式

1.1.1 Root Mean Square(RMS,均方根)

均方根值RMS 的计算公式,是先将一组数值的平方求和,然后除以数值的数量,最后取平方根。
具体来说,如果有一组数值:
x 1 , x 2 , … , x n x_1,x_2,\dots,x_n x1,x2,,xn
那么这组数值可以通过RMS可以通过以下公式计算:
R M S = 1 N ∑ i = 1 N x i 2 {\color{Red} R M S=\sqrt{\frac{1}{N} \sum_{i=1}^{N} x_{i}^{2}}} RMS=N1i=1Nxi2

其中 N 是是数值的数量, ∑ i = 1 N x i 2 是所有数值的平方和。 其中N是是数值的数量,\sum_{i=1}^{N} x_{i}^{2}是所有数值的平方和。 其中N是是数值的数量,i=1Nxi2是所有数值的平方和。

所以将RMS带入到我们gradient计算中就得到了如下结果:
在这里插入图片描述
这种方式被用在AdaGrad中

AdaGrad,全称Adaptive Gradient,又叫自适应梯度算法,是一种具有自适应学习率的梯度下降优化方法。

如下图:
现在我们有两个参数,一个叫θ₁,一个叫θ₂。
θ₁坡度小,θ₂坡度大。
1、θ₁,因为它坡度小的关系,所以在θ₁这个参数上面算出来的gradient值都比较小,因为gradient算出来的值比较小,然后这个σ是gradient的RSM,算出来的σ也就小,所以会导致learning rate比较大。
2、反过来说θ₂是一个比较陡峭的,这个参数在θ₂这个方向上loss变化比较大。所以算出来的gradient都比较大,所以的σ就比较大,所以会导致learning rate比较小。在update的时候参数update的时候的量就比较小。
3、所以有了σ这一项以后呢,你就可以随着每一个参数gradient的不同来自动的调整learning rate的大小。
在这里插入图片描述

1.1.2 RMSProp

那这个版本还有什么样的问题呢?
我们刚才的假设是同一个参数,它的σ的大小会固定是差不多的值。(因为就算gradient突然变化很大,σ基于RMS的运算,会使其看起来反应很“迟钝”,即坡度突然的变化会使得σ反应不过来)

但事实上并不一定是这个样子的
就算是同一个参数,他需要的learning rate也会随着时间而改变

举例来说:
我们来看这个新月形的error surface,如果我们只考虑横轴的水平线的方向
红色箭头的地方坡度比较陡峭,所以我们需要比较小的learning rate
但是走到了中间绿色箭头这一段的时候,坡度又变得平滑了起来,所以我们在这个地方又需要比较大的learning rate

所以就算是相同参数相同的方向,我们期待的learning rate是可以动态的调整的。
在这里插入图片描述
那我们需要怎么实现呢?
我们使用对RMSProp的改良版
即添加一个α(认为设置大小),控制之前的gradient之和与新的gradient的权重。
下面来详细探讨一下过程:
1、它的第一步跟刚才讲的算RSM的方法是一模一样的,所以就不赘诉了。
2、第二步有什么不同呢?
一样要算出σ₁,只是我们现在算出σ₁的方法,跟刚才算RSM的时候不一样。
刚才在算RSM的时候,每一个gradient都有同等的重要性,但在RSMprop里面,你可以自己调整现代的这个gradient有多重要。
之前的σ₀,算出来σ₀里面就是有g₀,这个σ₀就代表了这个g₀的大小,所以它是σ₀的平方乘上α加上(1-α)现在我们刚算出来的gradient就是g₁的平方,用公式表示为:
σ i 1 = α ( σ i 0 ) 2 + ( 1 − α ) ( g i 1 ) 2 {\color{Red} \sigma_{i}^{1}=\sqrt{\alpha\left(\sigma_{i}^{0}\right)^{2}+(1-\alpha)\left(g_{i}^{1}\right)^{2}} } σi1=α(σi0)2+(1α)(gi1)2

3、以此类推,我们可以知道,到第t+1步update时候我们就可以求得σ等于:
σ i t + 1 = α ( σ i t − 1 ) 2 + ( 1 − α ) ( g i t ) 2 {\color{Red} \sigma_{i}^{t+1}=\sqrt{\alpha\left(\sigma_{i}^{t-1}\right)^{2}+(1-\alpha)\left(g_{i}^{t}\right)^{2}} } σit+1=α(σit1)2+(1α)(git)2
其实就是跟前面的RMS差不多,只不过多了α控制以往的grading之和和如今的gradient权重罢了。
在这里插入图片描述
那用上RMSProp会怎么样呢?
如下图所示:
1、设从这个地方开始,这个黑线是我们的error surface,从这个地方开始update参数
这个球就从这边走走,走到这边那会一路上都很平坦就代表gradient很小,也就代表说这个σ算很小,σ很小,就代表现在update参数的时候,我们会走比较大的步伐。

2、接下来继续滚,滚到gradient突然变大了
如果是原来的RMS计算的话,它反应比较慢。
但如果你用RMSProp,把α设小一点,也就是让新的gradient影响比较大的话,那就可以很快的让σ的值变大。那可以很快的让你的步伐呢变小,也就是踩一个刹车,把learning rate变小。
那如果没有踩刹车的话,你走到这个地方learning rate太大了,那gradient又很大,两个很大的东西乘起来,你可能就啪的一下,就飞到很远的地方

在这里插入图片描述
3、那如果继续走走走,又走到平滑的地方了,因为现在你可以调整α,让他比较看重于最近算出来的gradient。
所以你gradient一变小,σ可能就反应很快它的值就变小了,然后走的步伐呢就变大了
在这里插入图片描述

1.1.3 Adam

其实如今最常用的optimization的策略呢就是Adam
也就是RMSProp + Momentum(第五周周的笔记-1.1处,点击进入
其实简而言之就是一个在learning rate上进行优化;一个在移动距离上增加动量进行优化
在这里插入图片描述
复习Momentum
在这里插入图片描述

1.2 Learning Rate Scheduling(学习率调整策略)

那么如果用上AdaGrad去优化Learning rate会是怎么样呢?
相比与之前的静态的学习率,我们可以看到我们在原来卡住的地方有所前进了。
因为这个左右的方向的这个gradient很小,所以会自动调整左右这个方向learning rate让其变大,所以step变大,就可以不断的前进。
在这里插入图片描述
接下来的问题就是为什么走到这边突然爆炸了呢?
在这里插入图片描述
因为我们是把过去所有看到的gradient都拿来做平均,所以这个纵轴的方向虽然在初始的这个地方感觉gradient很大,但是这边走了很长一段路以后这个纵轴的方向gradient算出来都很小,因为我们在这个y轴的方向后续的gradient很小,所以我们就累积了很小的σ,累积到一个地步以后,这个step就变很大。
然后就暴走就喷出去了。
但喷出去以后有办法修正回来
因为喷出去以后就走到了这个gradient比较大的地方,走到gradient比较大的地方以后,σ又慢慢的变大,update的这个步伐大小又慢慢的变小。
如此往复
在这里插入图片描述

1.2.1 learning rate decay(学习率衰减)

那么我们怎么解决这个问题呢?
就要用到我们的Learning Rate Scheduling(学习率调整策略)
那这个是什么呢?
我们刚才这边还有一项η,这个η是一个固定的值。
我们有一个策略如果η是跟时间有关的会不会更好呢?
那learning rate怎么让它跟时间有关呢?
最常见的策略啊,叫做learning rate decay(学习率衰减)
随着时间不断的进行,随着参数不断的update,让η越来越小。
这个也是合理的
因为一开始我们距离终点很远。随着参数不断update,我们距离终点越来越近,所以我们把learning rate减小,让参数的更新踩一个刹车,让我们参数的更新能够慢下来。
在这里插入图片描述
刚刚的情况,如果加上learning rate decay,我们就可以很平顺的走到终点
在这里插入图片描述

1.2.2 Warm up(学习率预热)

除了learning rate decay以外,还有另外一个也非常常用的learning rate scheduling的方式叫做Warm up(学习率预热)
Warm up这个方法听起来有点匪夷所思

因为Warm up的方法是说我们这个learning rate要先变大再后变小。
在这里插入图片描述
这个方法出现在一个很老的论文中
写道我们用learning rate 0.01去warm up,再把learning rate改成0.1
它还特别加个注解说一开始用0.1就测不好,也没解释为什么
在这里插入图片描述
在transformer里面也用一个式子提了warm up这个黑科技
它遵守这一个神奇的function来设定它的learning rate,而这个函数的图像画出来实际就是warm up的图像(先增后减)
在这里插入图片描述
那为什么要用 warm up呢?
有一个可能的解释是:
1、当我们在用AdaGrad、RMSProp或Adam的时候。我们需要计算σ,这个σ其实就是告诉我们某一个方向它到底有多陡,或者是多平滑,那这个统计的结果要看的够多以后才精准

2、所以一开始我们的σ是不精准的,因此开始的时候不要让我们的参数离初始的地方太远。
所以初始的learning rate比较小的目的是让它探索,收集一些有关error surface的情报和有关σ的统计数据等。

3、等到σ统计的比较精准,以后再把让learning rate慢慢的爬升。

1.3 Optimization的总结

我们这一小节所将讲的是当我们的error surface崎跷的时候,我们需要一些比较好的方法来做optimization

我们从最原始的规定design进化到这个版本。
那这个版本我们改进了什么东西呢?
1、第一个是momentum,就是不完全顺着gradient decent的方向更新参数,而是把过去所有算出来的规律的方向做一个求和当做update方向

2、那接下来到底应该要update多大的步伐呢?我们算要除掉gradient的RMS

3、那总结到这里我就很困惑:
这个momentum是考虑过去所有的gradient。σ也是考虑过去所有的gradient。
一个在分子,一个在分母,都考虑过去所有的gradient 不就是正好抵消了吗?
但是其实momentum跟这σ他们在使用过去所有gradient的方式是不一样的。
①、moment是直接把所有的gradient通通都加起来,所以他有考虑方向,他有考虑gradient的正负号,要考虑gradient是往左走还是往右走。
②、但是这个RMS,它就不考虑gradient的方向了,它只考虑gradient的大小,取得是平方然后开根号。我们是把平方的结果加起来,所以我们只考虑gradient的大小不考虑gradient的方向,
所以momentum跟这个σ算出来的结果并不会互相抵消。

4、最后我们还会加上一个learning rate scheduling
在这里插入图片描述
soft-max函数复习:
在这里插入图片描述

Pytorch学习

1. torchvision中数据集的使用

在这里插入图片描述
我们点入处理视觉部分即Torchvision在这里插入图片描述
介绍为:
The torchvision package consists of popular datasets, model architectures, and common image transformations for computer vision.
(该软件包由流行的数据集、模型组成 体系结构,以及计算机视觉的常见图像转换。)

点击dataset模块我们可以看到:
在这里插入图片描述
其中数据集的类型如下:
在这里插入图片描述
点开MNIST可以查看其说明介绍,下图为翻译后内容
在这里插入图片描述
下面我们会使用CIFAR-10数据集用来完成代码实操:CIFAR主要用于图像识别

除了Dataset模块,我们可以看一下models(模型)模块,也是经常使用的模块
主要是提供一些比较常见的神经网络(有些已经预训练好的神经网络)
其中包括

  1. 分类模型
  2. 语义分割模型
  3. 物体检测模型等
    在这里插入图片描述
    下面进行dataset与transform的联合代码实战
    我们使用CIFAR的dataset完成。使用之前,首先来看看CIFAR的用法,以及参数
    1、root(string型):表示我们的数据集的位置
    2、train(布尔型):如果为true则为训练集。否则为测试集
    3、transform(Callable是一种可调用类型,‌它允许外部程序通过动态链接库被调用执行):上一周所学的内容(我的第五周博客),可以在这里进行使用
    4、target_transform:对target进行一个transform。
    5、download(布尔型):如果为true,则自动下载数据集到root中(如果已经下载则不会再重复下载)
    在这里插入图片描述
    知道大概怎么使用后,就进行代码实战
    创建一个新的py文件,然后输入如下代码,并运行:
import torchvision

# 使用CIFAR10数据集
# 训练集设置
train_set = torchvision.datasets.CIFAR10(root='./dataset', train=True, download=True)
# 测试集设置
test_set = torchvision.datasets.CIFAR10(root='./dataset', train=False, download=True)

运行过后可以看到我们的数据集正在下载:
在这里插入图片描述
等待其下载完毕,我们就可以看到我们的项目目录多了一个datasets的文件夹,里面就是我们下载下来的数据集压缩包和一些文件
在这里插入图片描述
在这里插入图片描述
注意:如果下载十分慢。数据集可以复制链接或者用其他方式下载,下载好压缩包再创建一个文件夹(同root一致),然后再拖进去,也可以完成

下载好后,我们就可以查看数据集,继续输入如下代码,然后运行:

# 查看数据集
print(test_set[0])

该数据集由两部分组成分别是(image,target)
可以看到这是一张32*32大小的图片格式是PIL其中3表示target
在这里插入图片描述
那么target为3是什么呢?
target就是标签。
我们打一个断点(鼠标左键到print指令的行数那里),然后debug(再点击虫子标志),就可以看到变量的详情。
这个数据集是分好类别的,比如:0代表airplane、1代表automobile、2代表bird等等
我们这里的3表示的就是cat
在这里插入图片描述
因为其结构是img,target所以我们可以使用如下方式来查看:

import torchvision

# 使用CIFAR10数据集
# 训练集设置
train_set = torchvision.datasets.CIFAR10(root='./datasets', train=True, download=True)
# 测试集设置
test_set = torchvision.datasets.CIFAR10(root='./datasets', train=False, download=True)

# 查看数据集
print(test_set[0])
# 查看数据集的类别
print(train_set.classes)

# 查看格式
img, target = test_set[0]
print(img)
print(target)
print(test_set.classes[target])
#展示图片
img.show()

在这里插入图片描述
图片隐约能看出是一只猫
在这里插入图片描述
接下来我们再详细的了解一下CIFAR-10的数据集的内容
如下图:
可以看到共有10类,每个类有6000张图
其中有50000张为训练图片、10000张为测试图片
在这里插入图片描述

大概了解了dataset模块的使用后,我们就需用dataset与transform进行联动代码实战了
因为之前我们使用transform时候,都需要转换为tensor类型,所以我们需要用到transform的totensor工具箱完成类别的转换。

import torchvision

# 使用transform转换为tensor类型,compose可以配合裁剪和resize等操作
dataset_transforms = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
]
# 使用CIFAR10数据集
# 加上transform参数
# 训练集设置
train_set = torchvision.datasets.CIFAR10(root='./datasets', train=True, transform=dataset_transforms, download=True)
# 测试集设置
test_set = torchvision.datasets.CIFAR10(root='./datasets', train=False, transform=dataset_transforms, download=True)
# 查看数据集
print(test_set[0])

可以看到数据集已经变为tensor类型了。
在这里插入图片描述
那变为tensor类型,我们就可以使用tensorboard进行显示(正好可以复习一下之前的内容)

import torchvision
from torch.utils.tensorboard import SummaryWriter

# 使用transform转换为tensor类型,compose可以配合裁剪和resize等操作
dataset_transforms = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
])

# 使用CIFAR10数据集
# 训练集设置
train_set = torchvision.datasets.CIFAR10(root='./datasets', train=True, transform=dataset_transforms, download=True)
# 测试集设置
test_set = torchvision.datasets.CIFAR10(root='./datasets', train=False, transform=dataset_transforms, download=True)

# 使用tensorboard进行显示
# 创建writer实例
writer = SummaryWriter("logs_dataset_transform")
# 显示测试集中前10张图片,每个setp代表一张图
for i in range(10):
    img, target = test_set[i]
    writer.add_image("test_set", img, i)
# 关闭writer
writer.close()

在terminal中启动tensorboard

 tensorboard --logdir=logs_dataset_transform --port=6007

效果如下:
在这里插入图片描述
在这里插入图片描述
大致的演示就完成了。
但是dataset模块除了CIFAR10之外,还有CoCo数据集(比较庞大30多G)
可以看到其使用方式,其实跟CIFAR10差不多,具体怎么使用要多看官方文档
在这里插入图片描述

2. DataLoader的使用

上一部分我们学习了dataset如何使用,这一小节我们学习DataLoader的使用
那么它们的区别是什么呢?
我们可以用一张图形象的表示出来
我们的datastet就是一副扑克牌,里面一张一张的牌就是我们的data。
比如上一小节中的test_set就是一副扑克牌,test_set[0]就是其中的一副牌,每一张牌都有对应的数字与含义。
dataloader,loader顾名思义就是加载器,就是对数据进行加载
类似与右图里面的手,从一副扑克牌中取出一张张的牌
dataloader就是需要数据的时候就要去dataset里面取。

那么怎么取和取多少就是由dataloader里面的参数进行控制的
(比如我们想一下取4张牌,是一只手抓还是两只手抓)
大概有了了解之后,我们可以进入Pytorch官网查看DataLoader的使用
打开官网,根据下图顺序操作就可以找到dataloader的说明文档了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从下图的说明中可以看到,dataloader的参数比较多
但是除了dataset外(此dataset就是上一小节所学习的),其余参数都有默认值。
在这里插入图片描述
接下来我们可以认识一下一些常见的参数:
在这里插入图片描述
接下来进行我们dataloader的代码实战
创建一个新的py文件,输入如下代码:

在这之前我们可以用一个图示的过程了解一下运作原理

dataset里有一个getitem()方法,其就是返回img,target
在这里插入图片描述
比如我们的batch size = 4
就会以上述格式提取出4份,然后封装成一个imgs,targets中,再return回去
在这里插入图片描述
接下来我们用代码去体验:
我们依旧使用上以小结的数据集CIFAR10

import torchvision
from torch.utils.data import DataLoader

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, transform=torchvision.transforms.ToTensor())

# dataloader
test_loader = DataLoader(dataset=test_data, batch_size=4, shuffle=True, num_workers=0, drop_last=False)

# 查看测试集第一张图片的数据
img, target = test_data[0]
print(img.shape)
print(target)

for data in test_loader:
    imgs, targets = data
    print(imgs.shape)
    print(targets)

结果如下:
可以看到基本上符合我们上面的流程图
在这里插入图片描述
我们可以配合tensorboard,让其在上面展示更加的直观
把batch size设置为64

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, transform=torchvision.transforms.ToTensor())

# dataloader
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=True, num_workers=0, drop_last=False)

# 查看测试集第一张图片的数据
img, target = test_data[0]
print(img.shape)
print(target)

writer = SummaryWriter('./logs_dataloader')
step = 0
for data in test_loader:
    imgs, targets = data
    # print(imgs.shape)
    # print(targets)
    writer.add_images('test_data', imgs, step)
    step += 1

# 关闭SummaryWriter
writer.close()

在这里插入图片描述

2.1 sampler参数

在上面的输出中细心的我们可以看到
我们test_data[0]与这里的第一个data不是同一个?
这是为什么呢?难道分类是不是从一个开始分吗?
在这里插入图片描述
这是因为我们的sampler的方式时随机采样的
如下图所示:
我们添加了断点,可以看到其采样方式为RandomSampler(即batch size 为4时,在数据集中随机抓取4张图片)
在这里插入图片描述

2.2 drop_last参数

在最后一个step中(就是batch),很明显与其他step不同,这是因为10000/64 等于156余16
余数为16,所以只有16张图,而其他step都有8*8=64张图。
这是由于我们的drop_last设置为False导致的
在这里插入图片描述

在这里插入图片描述
我们将drop_last设置为True看看结果:

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, transform=torchvision.transforms.ToTensor())

# dataloader
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=True, num_workers=0, drop_last=True)

# 查看测试集第一张图片的数据
img, target = test_data[0]
print(img.shape)
print(target)

writer = SummaryWriter('./logs_dataloader')
step = 0
for data in test_loader:
    imgs, targets = data
    # print(imgs.shape)
    # print(targets)
    writer.add_images('test_data_drop_last', imgs, step)
    step += 1

# 关闭SummaryWriter
writer.close()

刷新tensorboard后:
在这里插入图片描述

2.3 shuffle参数

shuffle用于再下一次epoch的时候打乱数据
每轮完所有的个batch就为一次epoch,每轮完一次epoch就进行shuffle
我们先把shuffle设置为False
我们输入如下代码:

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, transform=torchvision.transforms.ToTensor())

# dataloader
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=False, num_workers=0, drop_last=True)

# 查看测试集第一张图片的数据
img, target = test_data[0]
print(img.shape)
print(target)

writer = SummaryWriter('./logs_dataloader')
step = 0
# 设置epoch,每轮完156个batch(因为此时drop_last=True,所以共有156个batch)就为一次epoch
for epoch in range(2):
    for data in test_loader:
        imgs, targets = data
        # print(imgs.shape)
        # print(targets)
        # "Epoch:{}".format(epoch),即当epoch = 1时,就为Epoch_1;当epoch = 2时,就为Epoch_2
        writer.add_images("Epoch:{}".format(epoch), imgs, step)
        step += 1
    epoch = epoch + 1

# 关闭SummaryWriter
writer.close()

在这里插入图片描述

再把shuffle设置为True

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, transform=torchvision.transforms.ToTensor())

# dataloader
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=True, num_workers=0, drop_last=True)

# 查看测试集第一张图片的数据
img, target = test_data[0]
print(img.shape)
print(target)

writer = SummaryWriter('./logs_dataloader')
# 设置epoch,每轮完156个batch(因为此时drop_last=True,所以共有156个batch)就为一次epoch
for epoch in range(2):
    step = 0
    for data in test_loader:
        imgs, targets = data
        # print(imgs.shape)
        # print(targets)
        # "Epoch:{}".format(epoch),即当epoch = 1时,就为Epoch_1;当epoch = 2时,就为Epoch_2
        writer.add_images("Epoch:{}".format(epoch), imgs, step)
        step += 1
    epoch = epoch + 1

# 关闭SummaryWriter
writer.close()

在这里插入图片描述

总结

这一周对机器学习继续进行了探究,学习了自动调整学习率与Loss的知识,当我们的error surface崎跷的时候,我们需要一些比较好的方法来做optimization。其中学会了RMS与RMSProp的算法,以及学会对η进行策略优化,还复习了momentum算法。此外,还对Pytorch进行了更进一步的学习,其中学会了使用torchvision的dataset模块,以及结合之前的transform与tensorboard一同进行联动的代码实践。然后还学会了如何使用DataLoader,其中学会了dataloader中batch size与shuffle以及drop_last的应用,明白了dataloader与dataset的关系。
下一周计划继续对李宏毅的机器学习视频进行学习,开始学习神经卷积网络,然后Pytorch要进入CNN的学习阶段。争取实现理论与代码的协同性。

  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值