数组的提取操作

在数据处理的工程中,经常会涉及到数组提取的操作。很多时候都需要从一个大数组中按维度,按长度等提取出一个小数组。本文将总结一些常用的提取操作。

用在方括号提取数组

对于tensor, pytorch, numpy这些拥有数组处理能力的库而言,其本身具备很强大的数据处理功能。大部分的数据提取操作都可以通过方括号操作符完成数据提取的任务。我们以pytorch为例,pytorch的数组方括号操作符中支持3类对象:

  1. 使用冒号: 描述某个维度中提取的范围。例如a[0:4, 4: -1]
  2. 使用一个整数数字指定某个维度中的特定项目。例如a[2, 8]
  3. 使用一个可迭代的整数队列指定某个维度中的一几个或者一个特定的项目。例如a[[0,1,2], [0,1,5]]

这3类对象在同一个提取操作中可以同时存在。这里,前2个提取操作都非常好理解。而3个提取操作会有一些麻烦。

冒号的应用和python常规中常规操作一样,a[:10, 4:7]表示对a数组的0维度,提取从0开始到第10项结尾的数据。在第1维度,提取从第4项开始到第7项结尾的数据。

单个整数数字表示对某个维度提取指定的某个元素。采用这样的提取方法,得到的数组会比原始数组减少一个维度。例如:

>>> a
tensor([[0., 1., 2.],
        [1., 3., 3.]])
>>> a[:,1]
tensor([1., 3.])
>>> a.shape
torch.Size([2, 3])
>>> a[:,1].shape
torch.Size([2])

上面例子可以看到a[:,1]变成了1维数组。

使用可迭代的整数队列指定项目提取时,情况就要复杂一些。它有以下几条规则:

  1. 在方括号中所有采用迭代对象做提取应用的,所有迭代对象必须拥有同样的长度,或者长度维1。
  2. 如果迭代对象的最大长度不是1, 而所有迭代对象中存在长度为1的。则长度为1的迭代对象在应用中将会被看做迭代对象长度为那个统一的最大长度。且,其数值全部为其原始整数的拷贝。例如a[[0], [1,2,3]]将被视作a[[0,0,0], [1,2,3]]。关于这一点,我们可以通过代码轻松验证:
>>> b = torch.tensor([[[0,1,2], [3,4,5]],[[6,7,8], [9,10,11]]])
>>> b[[0], [0,1], [0,1]]
tensor([0, 4])
>>> b[[0], :, [0,1]]
tensor([[0, 3],
        [1, 4]])

在这里b[[0], :, [0,1]]显然被当作了b[[0,0], : , [0,1]]来运算。然后又会被当作[b[0,:,0], b[0,:,1]]来看待。这里我们又要注意一个非常重要的点了。这个例子中前面的[0,0]的意义在于在第0维度选择两个相同的项目,后面的[0,1]则并不是每次都在第2维度选择第0个和第1两个项目的意思。后面的[0,1]的意义是在前面对应的第0个项目中在第2维度选择第0个项目,然后在前面对应的第1个项目中在第2维度选择第1个项目。它并没有在前面第0个项目中选择第0和第1,而是只选择0。这里就注意了,由于第2个迭代选择器选择的都是唯一项目,因此这就会发生维度的减少。例如前面的例子中b本来是2*2*3,总共3个维度的数据,而b[[0,0], :, [0,1]]变成了2*2两个维度的数据。这一切都因为后面的[0,1]选择了指定的一个数据(0只对应0, 1也只对应另外一个0),这一点就让b数组的最后一个维度发生了坍缩。

如果一个提取操作使用了n个迭代选择器,m个单1整数选择器,而原来数组的维度如果是t,则提取之后的数组的维度为 t - m - n + 1。

维度坍缩总结起来就是,如果遇见在某一个维度上只作整数式选择一个特定项目时,该维度就会发生坍缩。例如a[0:10, 4:5]在第1维度上4:5是选择了一个项目,但这里是作为数组来选择一个项目的,因此第1维度不会坍缩。但是如果a[0:10, 4]那就会发生坍缩了。然后,如果选择器中存在超过1个的迭代选择器,那么从第2个迭代选择器开始,每一个迭代选择器都等价于在前面的基础上只做整数式的选择。例如a[[0,1], [2,3]]。前面的[0,1]仍然是做数组式的选择,如果其后面没有[2,3],那么[0,1]几乎等价与0:2的效果。这里后面的[2,3]就不再是做数组式选择了,而是整数式地对前面的0, 做整数2的选择,对前面的1,做整数3的选择。

按步距提取

上文中提到过的提取方式,其实还有一个进一步的增强。那就是按照步距提取数据。方括号中的冒号可以再增加一个,然后第3个参数表示步距值。例如:

# array[start:end:step]
a = [1,2,3,4,5]
a[::2] = [1,3,5]
a[::-2] = [5,3,1]

#以上操作换成numpy.array也是一样的效果

步距的引入,可以进一步增强使用方括号做数据的提取操作。

不同维度间数组的运算

在很多人工智能或者数据处理的流程中,我们会经常遇见大量的数组处理问题。很多时候我们需要处理不同维度的数组,或者做改变数组维度等操作。一个优秀的工程师,尤其是一个愿意沉浸在数据变化过程中的工程师必须熟练的掌握处理不同维度的数组之间运算的问题。

在实际应用中,很多数据操作都会改变数组的维度。例如对3维数组的最后一维度做求和操作,则会把原来的数组变成2维的。

>>> a = torch.tensor([[[1,2,3]], [[2,3,4]]])
>>> a.sum(-1)
tensor([[6],
        [9]])
>>> a.sum(-1).shape
torch.Size([2, 1])
>>> a.shape
torch.Size([2, 1, 3])

然后,我们又会有很多运算会对原维度的数据和改变维度后的数组之间做四则运算。例如,我们想让a数组中最后一个维度的数据,每一个都减去其当前维度的和。比如说,我们想让torch.tensor([[[1,2,3]], [[2,3,4]]])变成 torch.tensor([ [[1-6, 2- 6, 3 - 6]], [[2-9, 3-9, 4-9]] ])。那我们里所当然想尝试这么做:

>>> a
tensor([[[1, 2, 3]],
        [[2, 3, 4]]])
>>> a - torch.tensor([6,9])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 2

那么,我们看到,这么直观的让a - [6,9]是不行的。要准确的实现我们的运算想法,就必须得先充分理解不同数组之间做运算的机制。

  1. 一般情况下,不同维度数量,或者同维度的长度不一样的数组之间是不能做四则运算的。标准的数组之间的四则运算只能运行在具备同样维度数量,且每个相对应维度的长度都一样长的数组之间进行。
  2. 不同维度,不同维度长度之间的数组之间只能在各自分别执行自动适配后,双双能满足1的条件后,它们才能达成运算条件。
  3. 数组的自动适配分两个步骤,第一,自动统一维度数量,这个步骤由维度数量少的数组,按最后一个维度对齐,然后再在前面补维度来与维度数量大的数组对齐;第二,在维度数量对齐后,分别对每个维度做长度的对齐操作,如果某个维度上它们长度相同,则看作已经对齐。如果长度比较短的那个数组在该维度的长度为1,则对该数字在该维度上做维度长度的扩张操作,且补齐的数据为那1个数据的拷贝。如果长度较短的那个数组在该维度的长度不为1,则视为此对数组无法自动适配。维度长度的对齐操作是从最后一个维度开始到第0维度结尾的顺序执行。

以上这段定义,可能主要是自动适配的描述不方便理解。下面举例说明:

>>> a
tensor([[[1, 2, 3]],
        [[2, 3, 4]]])
>>> b = torch.tensor([6,9])
>>> a - b

执行a - torch.tensor([6,9])的操作时,首先要让数组的维度数量对齐。a 的shape为[2,1,3], b的shape为[2], 于是首先b的shape会补充为[1,1,2], 即b变成[[[6,9]]]。然后,从最后一个维度开始做维度的长度对齐。b最后一个维度的长度为2, a为3。因此,a, b是无法自动适配了。因为有这条规定"如果长度较短的那个数组在该维度的长度不为1,则视为此对数组无法自动适配。"。因此,我们要想a - b能够执行,则需要b在完成第一步维度数量扩展后的shape为[1,1,1]或者[1,1,3]。我们改变下b的形态:

>>> b = torch.tensor([[6], [9]])
>>> a - b
tensor([[[-5, -4, -3],
         [-8, -7, -6]],

        [[-4, -3, -2],
         [-7, -6, -5]]])

我们这样改变以后,a - b能够顺利执行了。因为b 首先会适配成[[[6], [9]]],然后会适配成[ [[6,6,6], [9,9,9]], [[6,6,6], [9,9,9]] ], 这样b的shape变成了[2,2,3], 而a还是[2,1,3];那么因为a的第二维度的长度为1, 因此a的维度也会自动适配,适配之后的a变为[ [[1,2,3], [1,2,3]], [[2,3,4], [2,3,4]]]。所以这个时候做a - b运算,实际上,在完成互相适配后,是在做如下数组之间的运算:

a - b = [ [[1,2,3], [1,2,3]], [[2,3,4], [2,3,4]] ] - 
[ [[6,6,6], [9,9,9]], [[6,6,6], [9,9,9]] ]

因此也就得到了上述答案:

>>> a - b
tensor([[[-5, -4, -3],
         [-8, -7, -6]],

        [[-4, -3, -2],
         [-7, -6, -5]]])

但,这不是我们最初想要的答案。我们想要的是torch.tensor([[[1,2,3]], [[2,3,4]]])变成 torch.tensor([ [[1-6, 2- 6, 3 - 6]], [[2-9, 3-9, 4-9]] ])。为了达成我们的效果,我们可以做逆向思维。我们想要的答案需要对如下两个数组运算才能达到:

a - b = [ [[1,2,3]], [[2,3,4]] ] - 
[[[6,6,6]], [[9,9,9]]]

那么,接下来我们对[[[6,6,6]], [[9,9,9]]]做压缩,首先做维度长度的压缩,把这个数组变成[[[6]], [[9]]],这个数组的shape变成了[2,1,1],然后第0个维度的长度已经无法进行压缩了,因为[[6]]和[[9]]不相等。接下来,我们看维度数量能不能压缩,压缩维度数量只能搜索排序在前面而且维度长度为1的维度。这里,第0维度的长度是2,因此维度数量无法再进行压缩。所以最后要达成我们的效果b的最简形式为[[[6]], [[9]]]。接下来,让我们验证一下:

>> a 
tensor([[[1, 2, 3]],

        [[2, 3, 4]]])
>>> b = torch.tensor([[[6]], [[9]]])
>>> a - b
tensor([[[-5, -4, -3]],

        [[-7, -6, -5]]])

这个结果,是符合我们预期的。我们知道 a.sum(-1) = [[6], [9]]的,那我们如何优雅的让a.sum(-1)变成当前b的形态,然后再做a - b的运算呢?答案是,使用unsqueeze函数。

>>> a
tensor([[[1, 2, 3]],

        [[2, 3, 4]]])
>>> a.sum(-1).unsqueeze(-1)
tensor([[[6]],

        [[9]]])
>>> a - a.sum(-1).unsqueeze(-1)
tensor([[[-5, -4, -3]],

        [[-7, -6, -5]]])

unsqueeze函数能够给指定维度位置添加维度。我们可以利用它来改变数组的维度。

通过以上说明和示例,我们应该理解到,本质上不同维度形状的数组是不能做四则运算的。要进行四则运算,则需要自动适配。要自动适配成功,就必须满足一些条件。要想利用自动适配原理,来设计不同维度形状之间的数组做运算,可以采用反推的方法,看看当前的数组需要进行怎样变形或者不变形来达到自己的需求。

pytorch中的损失函数

损失函数是用来计算预测结果与期望结果之间的误差。pytorch作为一个成熟的人工智能框架,提供了很多损失函数供我们使用。

CrossEntropy

这个损失函数是在分类问题上经常用到的。它可以直接被用来比较[[a0,a1,a2],[b0,b1,b2]]和[pa, pb]之间的误差。这两个数组什么意义呢?a0,a1,a2的数字分别代表第0轴上的第0号元素在第1轴上3个分类的权重数值。而pa的取值可能是0,1和2。举一个实际例子说明,在对图像识别分类的问题上,我们给图像的种类用数字来分类。例如,这种图如果是狗,则其编号为0;是猫,则编号为1;是牛则编号为2。那么我们给出一个原始图像数据,然后通过一系列神经网络层后,这幅图片对应的数据变成了[a0,a1,a2],其中a0表示这种图片是狗的可能数值代表为a0, 是猫的可能数值代表为a1, 是牛的可能数值代表为a2。如果我们知道这个图片的实际上是一只猫,那就是说,这个图片的实际对应编号为1。那么我们的CrossEntropy就可以计算[a0,a1,a2]和1之间的差异。

从抽象的宏观角度来看,损失函数本质上给人们提供了一种对比不同意义的数据之间的差异。我们从来都是知道矢量和标量的对比是没有意义的,但是,损失函数就是对这个概率的挑战。

在数学公式上看,这个函数实际上是对比预测数组1中,在期望序号的那个数据与0之间的差异。即如果假设预测数组为[a0,a1,a2],而期望序号为i, 那么这个损失函数计算的值为 0 - log(e^ai/ (e^a0 + e^a1 + e^a2))的大小。如果有很多组数据的话,例如[[a0, a1, a2], [b0, b1, b2]]。那么要衡量多组数据的整体误差,则可以是计算各组误差的平均值,或者计算这些误差的和。

pytorch中神经网络层的分类

在pytorch的实际应用中会接触到非常多的网络层的搭建,本文专门用来整理pytorch框架中神经网络层的资料

线性网络层 Linear

线性网络接受输入数据,在forward运算中对输入数据的最后一个维度的数据做线型运算。在定义线性网络时需要定义输入数据的尺寸和输出数据的尺寸。这里需要明确的一点,在实际应用中这里定义的尺寸指的是输入数据中最后一个维度的尺寸。Linear网络层并没有限制输入数据的维度。例如,我们定义一个线性网络L = Linear(3, 4)可以接受[1,2,3]为输入数据,也可以接受[[1,2,3], [4,5,6]]或者[[[1,2,3]],[[4,5,6]]]

>>> a = torch.tensor([1,2,3], dtype = torch.float)
>>> l(a)
tensor([ 0.3342,  0.3121, -0.4418,  0.2728], grad_fn=<AddBackward0>)
>>> a = torch.tensor([[1,2,3]], dtype = torch.float)
>>> l(a)
tensor([[ 0.3342,  0.3121, -0.4418,  0.2728]], grad_fn=<AddmmBackward>)

平铺网络层 Flatten

平铺网络层能够压缩数据维度,调整数据的形态为后面的网络层或者优化参数做准备。需要注意的是Flatten可以指定平铺的维度的范围,而不是像torch.flatten或者numpy.flatten那样直接把所有的维度的数据都压缩到一个维度。

>> a = torch.tensor([[[1,2,3], [4,5,6], [7,8,9]]])
>>> m = torch.nn.Flatten(0, -1) //指定平铺0维到最后1维
>>> m(a)
tensor([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> m = torch.nn.Flatten(0, 1) //指定平铺0,到1维
>>> m(a)
tensor([[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]])

2维卷积层 Conv2d

卷积层对处理和分析图像是十分有用的。使用卷积处理图片可以很大的提升图片处理的效率。

pytorch中的卷积层构建时关键的参数是输入Channels和输出Channels。黑白图片的输入Channels是1,彩色图片的输入Channels是3。要记住,Conv2d接受到的输入矩阵维度排序是(N, C, H, W),而通过opencv等图片处理库导入图片之后图片数据的维度排序是(H, W, C) 。 这里N是代表图片数的维度,C代表每个像素点的维度,H代表图片的高度,W代表图片宽度。

对(N, C, H, W)维度的输入经过卷积处理后得到的结果为(N, Cout, Hout, Wout),其中Cout的数据为构建卷积时输入的参数,然后Hout和Wout分别为:

Hout = (H + 2*padding[0] - dilation[0] * (kernel_size[0] - 1) - 1)/stride[0]

Wout = (W + 2*padding[1] - dilation[1] * (kernel_size[1] - 1) - 1)/stride[1]

我作了一个图专门来描述padding, dilation, stride, kernel_size等参数的定义

ReLU层

ReLU层最基本的可以给当前的数据做一个洗牌,把小与0的数据去除掉。

AvgPool2d层

所有的Pool层做的工作都是downsample的工作,把原先的数据在某些指定的轴上减少维度。AvgPool2d层就是减少图片数据的高和宽轴上的维度,并且对被减少的部分用它们的平均值来代替。

AvgPool2d的原型如下:

CLASStorch.nn.AvgPool2d(kernel_sizestride=Nonepadding=0ceil_mode=Falsecount_include_pad=Truedivisor_override=None)

经过AvgPool2d网络层的处理后,输入数据的形态和输出数据形态的关系如下:

假设输入数据为: (N, C, H_{in}, W_{in})(N,C,Hin​,Win​) 或者 (C, H_{in}, W_{in})(C,Hin​,Win​).

输出数据为: (N, C, H_{out}, W_{out})(N,C,Hout​,Wout​) 或 (C, H_{out}, W_{out})(C,Hout​,Wout​)。那么:

Hout = (Hin + 2 * padding[0] - kernel_size[0])/stride[0] + 1
Wout = (Win + 2 * padding[1] - kernel_size[1])/stride[1] + 1

需要注意的是,这里stride的默认值是与kernel_size的至相等,而不是像Conv2d层,stride的默认值为1。

AdaptiveAvgPool2d层

其作用的原理基本与上面的AvgPool2d相同,不同的是这里接受的参数是output数据的参数。AvgPool2d接受的kernel_size,然后根据kernel_size, padding, stride等参数来导出output数据。而AdaptiveAvgPool2d是指定output数据的H,W的大小,然后系统自动判断生成padding, kernel_size等。

数组变形总结

在人工智能或者很多数据处理的应用中,我们都会遇见各种数组变形的问题。本文将会对pytorch和numpy中各种数组变形函数做个列表总结。

torch.unsqueeze

unsqueeze可以给数组增加一个维度,并指定维度增加的位置。例如如果有一个数组[1,2,3],我们希望给这个数组的0维位置增加一个维度变成[[1,2,3]],则可以

>>> torch.unsqueeze(torch.tensor([1,2,3]), 0)
tensor([[1, 2, 3]])

如果想给这个数组的最后一个维度,或者说,第1个维度增加维度变成[[1],[2],[3]]。则可以采取如下方案:

>>> torch.unsqueeze(torch.tensor([1,2,3]), -1)
tensor([[1],
        [2],
        [3]])
>>> torch.unsqueeze(torch.tensor([1,2,3]), 1)
tensor([[1],
        [2],
        [3]])

torch.flatten, numpy.flatten

这两个函数的作用一样,就是把多维度的数组变成一个维度的数组。

例如把[[1,2,3], [4,5,6]]变成[1,2,3,4,5,6]

torch.nn.flatten

这个flatten函数与前面的不同之处不仅仅在与它的定位是网络层。这里的flatten还可以指定flatten的维度的位置。例如把[[[1,2,3], [4,5,6], [7,8,9]]]转化为[1,2,3,4,5,6,7,8,9]或者[[1,2,3], [4,5,6], [7,8,9]]

>> a = torch.tensor([[[1,2,3], [4,5,6], [7,8,9]]])
>>> m = torch.nn.Flatten(0, -1)
>>> m(a)
tensor([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> m = torch.nn.Flatten(0, 1)
>>> m(a)
tensor([[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]])

神经网络的应用中function的意义

以pytorch为例,定义一个传递函数的示例如下:

class LegendrePolynomial3(torch.autograd.Function):
    """
    We can implement our own custom autograd Functions by subclassing
    torch.autograd.Function and implementing the forward and backward passes
    which operate on Tensors.
    """

    @staticmethod
    def forward(ctx, input):
        """
        In the forward pass we receive a Tensor containing the input and return
        a Tensor containing the output. ctx is a context object that can be used
        to stash information for backward computation. You can cache arbitrary
        objects for use in the backward pass using the ctx.save_for_backward method.
        """
        ctx.save_for_backward(input)
        return 0.5 * (5 * input ** 3 - 3 * input)

    @staticmethod
    def backward(ctx, grad_output):
        """
        In the backward pass we receive a Tensor containing the gradient of the loss
        with respect to the output, and we need to compute the gradient of the loss
        with respect to the input.
        """
        input, = ctx.saved_tensors
        return grad_output * 1.5 * (5 * input ** 2 - 1)

函数满足以下2个功能

  1. 函数接受一个输入tensor并得到一个输出
  2. 接受输出的灰度,计算原始输入的灰度

从宏观的抽象层次可以把某个参数的灰度理解为,该参数对最后结果的影响权重。

pytorch中tensor.backward的gradient参数的意义

Let’s say your output is not a tensor with size 1. In addition, assume that there’s some function f that you’re acting on the output, so that you get something like newOutput = f(output).

You can pass a gradient grad to output.backward(grad). The idea of this is that if you’re doing backpropagation manually, and you know the gradient of the input of the next layer (f in this case), then you can pass the gradient of the input of the next layer to the previous layer that had output output.

在手动调用时,有可能这个output是整个网络的一个层,它后面还有其他层的。如果你打算直接这个output在后面层的graident,这时候就需要吧output在后面层的gradient参数传入进来做backward运算。

安装配置docker + cuda + pytorch的注意事项

  1. 首先要确定当前设备显卡驱动支持的是哪个cuda版本。确定方法可以参照上一篇关于安装cuda + pytorch的注意事项
  2. 需要安装nvidia-container-toolkit如下所示(fedora版本):
dnf config-manager --add-repo https://nvidia.github.io/nvidia-docker/centos7/nvidia-docker.repo
dnf install nvidia-container-toolkit

systemctl restart docker

3. 启动docker时需要指定--gpus all, 例如:

docker run -t -i --rm --name=cuda \
-v ${PWD}/src:/src --gpus all --network=host cuda:10.1 /bin/bash

4. 在虚拟机中安装pytorch时也需要安装当前设备支持的cuda版本。这里同样可以参考上一篇关于安装cuda + pytorch的注意事项

安装cuda + pytorch 的注意事项

  1. 需要确定电脑上的显卡是否支持cuda,当前的显卡是支持哪个cuda版本。在linux版本上,需要给显卡安装nvidia驱动,然后运行nvidia-smi指令查看显卡驱动信息。其结果可能如下所示
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.50       Driver Version: 430.50       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GT 730      Off  | 00000000:01:00.0 N/A |                  N/A |
| 52%   43C    P0    N/A /  N/A |    695MiB /   974MiB |     N/A      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0                    Not Supported                                       |
+-----------------------------------------------------------------------------+

这样的信息就表示当前显卡驱动支持的是cuda 10.1版本

2. 在安装pytorch的时候,需要安装与显卡当前同样cuda版本的软件包。在pytorch上提供的可以安装cuda版本比较多,一般首页提示上只有最新的几个版本。如果这个没有电脑上显卡nvidia-smi指令看到的版本,则需要去pytorch的历史版本上去找。https://pytorch.org/get-started/previous-versions/

torchvision的transforms和datasets

torchvision是pytorch下专门对图形处理的库。因此,提供图形的基本批处理和图形集是其基本的两大功能。而这里提到的transforms和datasets既是对应的这两个功能的核心模块。

transforms

transforms的一大特点是可以组合串联起来。也因此,torchvision提供了compose函数来专门做transforms的串联工作。

torchvision.transforms.Compose(transforms)

Compose函数应用示例

>>> transforms.Compose([
>>>     transforms.CenterCrop(10),
>>>     transforms.ToTensor(),
>>> ])

transforms的应用对象可以是input也可以是target。这里的target指的是用于学习的测试数据集的结果。用于训练深度学习的数据通常不仅仅给出输入数据,还会给出相关标签的结果数据。在训练时,为了让模型有更好的适应能力,通常会对原数据argumentize处理。那么与之相对于的测试结果也应该做相应处理。

对target做transform体现在数据集的函数调用参数上


CLASStorchvision.datasets.MNIST(roottrain=Truetransform=Nonetarget_transform=Nonedownload=False)

datasets

pytorch提供了很多数据集共应用直接调用,用于训练模型。这可以减少很多样板性的工作。

All datasets are subclasses of torch.utils.data.Dataset i.e, they have __getitem__ and __len__ methods implemented. Hence, they can all be passed to a torch.utils.data.DataLoader which can load multiple samples parallelly using torch.multiprocessing workers. For example:

imagenet_data = torchvision.datasets.ImageNet('path/to/imagenet_root/')
data_loader = torch.utils.data.DataLoader(imagenet_data,
                                          batch_size=4,
                                          shuffle=True,
                                          num_workers=args.nThreads)