前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Pytorch】笔记一:数据载体张量与线性回归

【Pytorch】笔记一:数据载体张量与线性回归

作者头像
阿泽 Crz
发布2020-08-28 10:43:04
2.6K0
发布2020-08-28 10:43:04
举报

❝阿泽推荐:Miracle 是一个优秀的同学,他记录的 pytorch 系列共有十篇,这是第一篇。 不要觉得 Pytorch 很简单,一定有你所忽视的地方,耐心一点,欢迎复习。也欢迎追剧, ❞

1.写在前面

疫情在家的这段时间,系统的学习一遍 Pytorch 基础知识,因为我发现虽然直接 Pytorch 实战上手比较快,但是关于一些内部的原理知识其实并不是太懂,这样学习起来感觉很不踏实, 对 Pytorch 的使用依然是模模糊糊, 跟着人家的代码用 Pytorch 玩神经网络还行,也能读懂,但自己亲手做的时候,直接无从下手,啥也想不起来, 我觉得我这种情况就不是对于某个程序练得不熟了,而是对 Pytorch 本身在自己的脑海根本没有形成一个概念框架,不知道它内部运行原理和逻辑,所以自己写的时候没法形成一个代码逻辑,就无从下手。这种情况即使背过人家这个程序,那也只是某个程序而已,不能说会 Pytorch, 并且这种背程序的思想本身就很可怕, 所以我还是习惯学习知识先有框架(至少先知道有啥东西)然后再通过实战(各个东西具体咋用)来填充这个框架。而「这个系列的目的就是在脑海中先建一个 Pytorch 的基本框架出来, 学习知识,知其然,知其所以然才更有意思」

今天是该系列的第一篇, 我们直接从 Pytorch 最基础的开始,这部分首先会整理 Pytorch 中数据结构张量的概念和创建方法,然后整理张量的各种操作,最后通过前面所学玩一个简单的线性回归。

「文章大纲如下:」

  1. 张量的简介与创建(张量及各种创建方式);
  2. 张量的基本操作(张量的切分,拼接,索引,变换,数学运算);
  3. 玩一个简单的线性回归模型;
  4. 总结梳理。

思维导图如下:

在这里插入图片描述

2.张量的简介与创建

这部分内容介绍 pytorch 中的数据结构——Tensor,Tensor 是 PyTorch 中最基础的概念,其参与了整个运算过程,主要介绍张量的概念和属性,如 data, device, dtype 等,并介绍 tensor 的基本创建方法,如直接创建、依数值创建和依概率分布创建等。

2.1 张量的简介

  1. 张量的基本概念 张量其实是一个「多维数组」,它是标量、向量、矩阵的高维拓展

在这里插入图片描述

  1. Tensor 与 Variable 在 Pytorch 0.4.0 版本之后其实 Variable 已经并入 Tensor, 但是 Variable 这个数据类型的了解,对于理解张量来说很有帮助, 这到底是个什么呢?Variable 是 torch.autograd 中的数据类型。

Variable 有下面的 5 个属性: 这些属性都是为了张量的自动求导而设置的,从 Pytorch 0.4.0 版开始,Variable 并入了 Tensor,看看张量里面的属性:

可以发现,如今版本里面的 Tensor 共有 8 个属性,上面四个与数据本身相关,下面四个与梯度求导相关。其中有五个是 Variable 并入过来的,这些含义就不解释了,而还有三个属性没有说: 知道了什么是张量,那么如何创建张量呢?

  • dtype:张量的数据类型,如 torch.FloatTensor,torch.cuda.FloatTensor,用的最多的一般是 float32 和 int64(torch.long)
  • shape:张量的形状,如 (64, 3, 224, 224)
  • device:张量所在的设备,GPU/CPU,张量放在 GPU 上才能使用加速。
  1. data:被包装的 Tensor;
  2. grad:data 的梯度;
  3. grad_fn:fn 表示 function 的意思,记录我么创建的创建张量时用到的方法,比如说加法、乘法,这个操作在求导过程需要用到,Tensor 的 Function,是自动求导的关键;
  4. requires_grad:指示是否需要梯度,有的不需要梯度;
  5. is_leaf:指示是否是叶子节点(张量);

2.2 张量的创建

  1. 直接创建张量「torch.Tensor():功能:从 data 创建 Tensor」

这里的 data,就是我们的数据,可以是 list,也可以是 numpy。dtype 这个是指明数据类型,默认与 data 的一致。device 是指明所在的设备,requires_grad 是是否需要梯度,在搭建神经网络的时候需要求导的那些参数这里要设置为 true。pin_memory 是否存于锁页内存,这个设置为 False 就可以。 下面就具体代码演示:

arr = np.ones((3, 3))
print('ndarry的数据类型:', arr.dtype)

t = torch.tensor(arr, device='cuda')
print(t)

## 结果如下:
ndarry的数据类型:float64
tensor([[1., 1., 1.],
     [1., 1., 1.],
     [1., 1., 1.]], device='cuda:0', dtype=torch.float64)

通过 numpy 数组来创建「torch.from_numpy(ndarry):从 numpy 创建 tensor」注意:这个创建的 Tensor 与原 ndarray 「共享内存」, 当修改其中一个数据的时候,另一个也会被改动。

下面具体看代码演示(共享内存):

arr = np.array([[1, 2, 3], [4, 5, 6]])
t = torch.from_numpy(arr)

print(arr, '\n',t)
arr[0, 0] = 0
print('*' * 10)
print(arr, '\n',t)
t[1, 1] = 100
print('*' * 10)
print(arr, '\n',t)

## 结果:
[[1 2 3]
[4 5 6]] 
tensor([[1, 2, 3],
       [4, 5, 6]], dtype=torch.int32)
**********
[[0 2 3]
[4 5 6]] 
tensor([[0, 2, 3],
       [4, 5, 6]], dtype=torch.int32)
**********
[[  0   2   3]
[  4 100   6]] 
tensor([[  0,   2,   3],
       [  4, 100,   6]], dtype=torch.int32)
  1. 依据数值创建「torch.zeros():依 size 创建全 0 的张量」

这些参数都比较好理解,layout 这个是内存中的布局形式,一般采用默认就可以。这个 out,表示输出张量,就是再把这个张量赋值给别的一个张量,但是这两个张量时一样的,指的同一个内存地址。 看代码:

out_t = torch.tensor([1])
t = torch.zeros((3, 3), out=out_t)

print(out_t, '\n', t)
print(id(t), id(out_t), id(t) == id(out_t))   # 这个看内存地址

## 结果:
tensor([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]]) 
tensor([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])
2575719258696 2575719258696 True

「torch.zeros_like(input, dtype=None, layout=None, device=None, requires_grad=False):这个是创建与 input 同形状的全 0 张量」

t = torch.zeros_like(out_t)   # 这里的input要是个张量
print(t)

tensor([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])

除了全 0 张量,还可以创建全 1 张量,用法和上面一样,「torch.ones(), torch.ones_like(), 还可以自定义数值张量:torch.full(), torch.full_like()」

这里的 fill_value 就是要填充的值。

t = torch.full((3,3), 10)
tensor([[10., 10., 10.],
       [10., 10., 10.],
       [10., 10., 10.]])

「torch.arange():创建等差的 1 维张量,数值区间 [start, end),注意这是右边开,取不到最后的那个数。」

这个和 numpy 的差不多,这里的 step 表示的步长。

t = torch.arange(2, 10, 2)  # tensor([2, 4, 6, 8])

「torch.linspace():创建均分的 1 维张量, 数值区间 [start, end] 注意这里都是闭区间,和上面的区分。」

这里是右闭,能取到最后的值,并且这里的 steps 是数列的长度而不是步长。

t = torch.linspace(2, 10, 5)   # tensor([2, 4, 6, 8, 10])

# 那么如果不是那么正好呢? 步长应该是多少?
t = torch.linspace(2, 10, 6)   # tensor([2, 3.6, 5.2, 6.8, 8.4, 10])

# 这个步长是怎么算的?  (end-start) / (steps-1)

除了创建均分数列,还可以创建对数均分数列:

这里的 base 表示以什么为底。最后一个方法就是 「torch.eye():创建单位对角矩阵,默认是方阵」

n,m 分别是矩阵的行数和列数。

  1. 依概率分布创建张量「torch.normal():生成正态分布(高斯分布), 这个使用的比较多」

mean 是均值,std 是标准差。但是这个地方要注意,根据 mean 和 std,分别各有两种取值,所以这里会有四种模式: 这个看代码来的直接:

  • mean 为标量,std 为标量;
  • mean 为标量,std 为张量;
  • mean 为张量,std 为标量;
  • mean 为张量,std 为张量。

# 第一种模式 - 均值是标量, 方差是标量 - 此时产生的是一个分布, 从这一个分部种抽样相应的个数,所以这个必须指定size,也就是抽取多少个数
t_normal = torch.normal(0, 1, size=(4,))
print(t_normal)     # 来自同一个分布

# 第二种模式 - 均值是标量, 方差是张量 - 此时会根据方差的形状大小,产生同样多个分布,每一个分布的均值都是那个标量
std = torch.arange(1, 5, dtype=torch.float)
print(std.dtype)
t_normal2 = torch.normal(1, std)
print(t_normal2)        # 也产生来四个数,但是这四个数分别来自四个不同的正态分布,这些分布均值相等

# 第三种模式 - 均值是张量,方差是标量 - 此时也会根据均值的形状大小,产生同样多个方差相同的分布,从这几个分布中分别取一个值作为结果
mean = torch.arange(1, 5, dtype=torch.float)
t_normal3 = torch.normal(mean, 1)
print(t_normal3)     # 来自不同的分布,但分布里面方差相等

# 第四种模式 - 均值是张量, 方差是张量 - 此时需要均值的个数和方差的个数一样多,分别产生这么多个正太分布,从这里面抽取一个值
mean = torch.arange(1, 5, dtype=torch.float)
std = torch.arange(1, 5, dtype=torch.float)
t_normal4 = torch.normal(mean, std)
print(t_normal4)          # 来自不同的分布,各自有自己的均值和方差


下面一个是「标准正态分布:torch.randn(), torch.randn_like()」

「生成均匀分布:torch.rand(), rand_like() 在 [0,1) 生成均匀分布 torch.randint(), torch.randint_like():区间 [low,hight) 生成整数均匀分布」

下面看最后两个:

  • 「torch.randperm(n):生成从 0 - n-1 的随机排列, n 是张量的长度, 经常用来生成一个乱序索引。 」
  • 「torch.bernoulli(input):以 input 为概率,生成伯努利分布 (0-1 分布,两点分布), input:概率值」

3.张量的操作

这次整理张量的基本操作,比如张量的拼接,切分,索引和变换以及数学运算等,并基于所学习的知识,实现线性回归模型。

3.1 张量的基本操作

  1. 张量的拼接 这是啥意思,stack 会新创建一个维度,然后完成拼接。还是看代码:
    • 「torch.cat(tensors, dim=0, out=None):将张量按维度 dim 进行拼接, tensors 表示张量序列, dim 要拼接的维度」
    • 「torch.stack(tensors, dim=0, out=None):在新创建的维度 dim 上进行拼接, tensors 表示张量序列, dim 要拼接的维度」
# 张量的拼接
t = torch.ones((2, 3))
print(t)

t_0 = torch.cat([t, t], dim=0)       # 行拼接
t_1 = torch.cat([t, t], dim=1)    # 列拼接
print(t_0, t_0.shape)
print(t_1, t_1.shape)

# 结果:
tensor([[1., 1., 1.],
     [1., 1., 1.]])
tensor([[1., 1., 1.],
     [1., 1., 1.],
     [1., 1., 1.],
     [1., 1., 1.]]) torch.Size([4, 3])
tensor([[1., 1., 1., 1., 1., 1.],
     [1., 1., 1., 1., 1., 1.]]) torch.Size([2, 6])

.cat 是在原来的基础上根据行和列,进行拼接,我发现一个问题,就是浮点数类型拼接才可以,long 类型拼接会报错。

下面我们看看 .stack 方法:

t_stack = torch.stack([t,t,t], dim=0)
print(t_stack)
print(t_stack.shape)

t_stack1 = torch.stack([t, t, t], dim=1)
print(t_stack1)
print(t_stack1.shape)

## 结果:
tensor([[[1., 1., 1.],
        [1., 1., 1.]],

       [[1., 1., 1.],
        [1., 1., 1.]],

       [[1., 1., 1.],
        [1., 1., 1.]]])
torch.Size([3, 2, 3])
tensor([[[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]],

       [[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]]])
torch.Size([2, 3, 3])

.stack 是根据给定的维度新增了一个新的维度,在这个新维度上进行拼接,这个 .stack 与其说是从新维度上拼接,不太好理解,其实是新加了一个维度 Z 轴,只不过 dim=0 和 dim=1 的视角不同罢了。dim=0 是横向看,dim=1 是纵向看。所以这两个使用的时候要小心,看好了究竟是在原来的维度上拼接到一块,还是从新维度上拼接到一块。

  1. 张量的切分 **torch.chunk(input, chunks, dim=0):将张量按维度 dim 进行平均切分,返回值是张量列表,注意,如果不能整除, 最后一份张量小于其他张量。chunks 代表要切分的维度。**下面看一下代码实现:
a = torch.ones((2, 7))  # 7
list_of_tensors = torch.chunk(a, dim=1, chunks=3)   # 第一个维度切成三块, 那么应该是(2,3), (2,3), (2,1)  因为7不能整除3,所以每一份应该向上取整,最后不够的有多少算多少
print(list_of_tensors)
for idx, t in enumerate(list_of_tensors):
   print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))

## 结果:
(tensor([[1., 1., 1.],
       [1., 1., 1.]]), tensor([[1., 1., 1.],
       [1., 1., 1.]]), tensor([[1.],
       [1.]]))
第1个张量:tensor([[1., 1., 1.],
       [1., 1., 1.]]), shape is torch.Size([2, 3])
第2个张量:tensor([[1., 1., 1.],
       [1., 1., 1.]]), shape is torch.Size([2, 3])
第3个张量:tensor([[1.],
       [1.]]), shape is torch.Size([2, 1])

「torch.split(tensor, split_size_or_sections, dim=0):这个也是将张量按维度 dim 切分,但是这个更加强大,可以指定切分的长度,split_size_or_sections 为 int 时表示每一份的长度, 为 list 时,按 list 元素切分」

# split
t = torch.ones((2, 5))

list_of_tensors = torch.split(t, [2, 1, 2], dim=1)  # [2 , 1, 2], 这个要保证这个list的大小正好是那个维度的总大小,这样才能切
for idx, t in enumerate(list_of_tensors):
   print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))

## 结果
第1个张量:tensor([[1., 1.],
       [1., 1.]]), shape is torch.Size([2, 2])
第2个张量:tensor([[1.],
       [1.]]), shape is torch.Size([2, 1])
第3个张量:tensor([[1., 1.],
       [1., 1.]]), shape is torch.Size([2, 2])

所以切分,也有两个函数,.chunk 和 .split。.chunk 切分的规则就是提供张量,切分的维度和几份, 比如三份,先计算每一份的大小,也就是这个维度的长度除以三,然后上取整,就开始沿着这个维度切,最后不够一份大小的,也就那样了。所以长度为 7 的这个维度,3 块,每块 7/3 上取整是 3,然后第一块 3,第二块是 3,第三块 1。这样切 .split 这个函数的功能更加强大,它可以指定每一份的长度,只要传入一个列表即可,或者也有一个整数,表示每一份的长度,这个就根据每一份的长度先切着,看看能切几块算几块。不过列表的那个好使,可以自己指定每一块的长度,但是注意一下,这个长度的总和必须是维度的那个总长度才用办法切。

  1. 张量的索引「torch.index_select(input, dim, index, out=None):在维度 dim 上,按 index 索引数据,返回值,以 index 索引数据拼接的张量。」
t = torch.randint(0, 9, size=(3, 3))     #  从0-8随机产生数组成3*3的矩阵
print(t)
idx = torch.tensor([0, 2], dtype=torch.long)   # 这里的类型注意一下,要是long类型
t_select = torch.index_select(t, dim=1, index=idx)  #第0列和第2列拼接返回
print(t_select)

## 结果:
tensor([[3, 7, 3],
     [4, 3, 7],
     [5, 8, 0]])
tensor([[3, 3],
     [4, 7],
     [5, 0]])

「torch.masked_select(input, mask, out=None):按 mask 中的 True 进行索引,返回值:一维张量。input 表示要索引的张量,mask 表示与 input 同形状的布尔类型的张量。这种情况在选择符合某些特定条件的元素的时候非常好使」,注意这个是返回一维的张量。下面看代码:

mask = t.ge(5)   # le表示<=5, ge表示>=5 gt >5  lt <5
print("mask:\n", mask)
t_select1 = torch.masked_select(t, mask)   # 选出t中大于5的元素
print(t_select1)

## 结果:
mask:
tensor([[False,  True, False],
     [False, False,  True],
     [ True,  True, False]])
tensor([7, 7, 5, 8])

所以张量的索引,有两种方式:.index_select 和 .masked_select

  • .index_select:按照索引查找 需要先指定一个 Tensor 的索引量,然后指定类型是 long 的
  • .masked_select:就是按照值的条件进行查找,需要先指定条件作为 mask
  1. 张量的变换「torch.reshape(input, shape):变换张量的形状,这个很常用,input 表示要变换的张量,shape表示新张量的形状。但注意,当张量在内存中是连续时,新张量与input共享数据内存」
# torch.reshape
t = torch.randperm(8)       # randperm是随机排列的一个函数
print(t)

t_reshape = torch.reshape(t, (-1, 2, 2))    # -1的话就是根据后面那两个参数,计算出-1这个值,然后再转
print("t:{}\nt_reshape:\n{}".format(t, t_reshape))

t[0] = 1024
print("t:{}\nt_reshape:\n{}".format(t, t_reshape))
print("t.data 内存地址:{}".format(id(t.data)))
print("t_reshape.data 内存地址:{}".format(id(t_reshape.data))) # 这个注意一下,两个是共内存的

## 结果:
tensor([2, 4, 3, 1, 5, 6, 7, 0])
t:tensor([2, 4, 3, 1, 5, 6, 7, 0])
t_reshape:
tensor([[[2, 4],
        [3, 1]],

       [[5, 6],
        [7, 0]]])
t:tensor([1024,    4,    3,    1,    5,    6,    7,    0])
t_reshape:
tensor([[[1024,    4],
        [   3,    1]],

       [[   5,    6],
        [   7,    0]]])
t.data 内存地址:1556953167336
t_reshape.data 内存地址:1556953167336

上面这两个是共内存的, 一个改变另一个也会改变。这个要注意一下。「torch.transpose(input, dim0, dim1):交换张量的两个维度, 矩阵的转置常用, 在图像的预处理中常用, dim0 要交换的维度, dim1 表示要交换的问题」

# torch.transpose
t = torch.rand((2, 3, 4))      # 产生0-1之间的随机数
print(t)
t_transpose = torch.transpose(t, dim0=0, dim1=2)    # c*h*w     h*w*c, 这表示第0维和第2维进行交换
print("t shape:{}\nt_transpose shape:{}".format(t.shape, t_transpose.shape))

## 结果:
tensor([[[0.7480, 0.5601, 0.1674, 0.3333],
        [0.4648, 0.6332, 0.7692, 0.2147],
        [0.7815, 0.8644, 0.6052, 0.3650]],

       [[0.2536, 0.1642, 0.2833, 0.3858],
        [0.8337, 0.6173, 0.3923, 0.1878],
        [0.8375, 0.2109, 0.4282, 0.4974]]])
t shape:torch.Size([2, 3, 4])
t_transpose shape:torch.Size([4, 3, 2])
tensor([[[0.7480, 0.2536],
        [0.4648, 0.8337],
        [0.7815, 0.8375]],

       [[0.5601, 0.1642],
        [0.6332, 0.6173],
        [0.8644, 0.2109]],

       [[0.1674, 0.2833],
        [0.7692, 0.3923],
        [0.6052, 0.4282]],

       [[0.3333, 0.3858],
        [0.2147, 0.1878],
        [0.3650, 0.4974]]])

「torch.t(input):2 维张量的转置, 对矩阵而言,相当于 torch.transpose(inpuot, 0,1)」

「torch.squeeze(input, dim=None, out=None):压缩长度为 1 的维度, dim 若为 None,移除所有长度为 1 的轴,若指定维度,当且仅当该轴长度为 1 时可以被移除」

# torch.squeeze
t = torch.rand((1, 2, 3, 1))
t_sq = torch.squeeze(t)
t_0 = torch.squeeze(t, dim=0)
t_1 = torch.squeeze(t, dim=1)
print(t.shape)        # torch.Size([1, 2, 3, 1])
print(t_sq.shape)     # torch.Size([2, 3])
print(t_0.shape)     # torch.Size([2, 3, 1])
print(t_1.shape)     # torch.Size([1, 2, 3, 1])

「torch.unsqueeze(input, dim, out=None):依据 dim 扩展维度」

3.2 张量的数学运算

Pytorch 中提供了丰富的数学运算,可以分为三大类:加减乘除,对数指数幂函数,三角函数

这里重点演示一下加法这个函数,因为这个函数有一个小细节:**torch.add(input, alpha=1, other, out=None):逐元素计算input+alpha * other。注意人家这里有个 alpha,叫做乘项因子。类似权重的个东西。**这个东西让计算变得更加简洁, 比如线性回归我们知道有个 y = wx + b, 在这里直接一行代码torch.add(b, w, x) 就搞定。类似的还有两个方法:

  1. torch.addcdiv(input, value=1, tensor1, tensor2, out=None)。这个实现了
out_i = input_i + value\times\frac{tensor1_i}{tensor2_i}
  1. torch.addcmul(input, value=1, tensor1, tensor2, out=None):这个实现了
out_i = input_i + value\times tensor1_i\times tensor2_i

这个在优化的时候经常会用到。

t_0 = torch.randn((3, 3))
t_1 = torch.ones_like(t_0)
t_add = torch.add(t_0, 10, t_1)

print("t_0:\n{}\nt_1:\n{}\nt_add_10:\n{}".format(t_0, t_1, t_add))

## 结果:
t_0:
tensor([[-0.4133,  1.4492, -0.1619],
        [-0.4508,  1.2543,  0.2360],
        [ 1.0054,  1.2767,  0.9953]])
t_1:
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])
t_add_10:
tensor([[ 9.5867, 11.4492,  9.8381],
        [ 9.5492, 11.2543, 10.2360],
        [11.0054, 11.2767, 10.9953]])

下面基于上面的这些方法玩一个线性回归模型。

4.线性回归模型

线性回归是分析一个变量与另外一(多)个变量之间关系的方法。因变量是 y,自变量是 x,关系线性:

y = w\times x + b

任务就是求解 w,b。

我们的求解步骤:

  1. 确定模型:Model -> y = wx + b
  2. 选择损失函数:这里用 MSE :
\frac{1}{m} \sum_{i=1}^{m}\left(y_{i}-\hat{y}_{i}\right)^2
  1. 求解梯度并更新 w, b:
\begin{aligned} &w=w-lr\times w. g r a d\\ &b=b-lr\times w. g r a d \end{aligned}

这就是我上面说的叫做代码逻辑的一种思路,写代码往往习惯先有一个这样的一种思路,然后再去写代码的时候,就比较容易了。而如果不系统的学一遍 Pytorch,一上来直接上那种复杂的 CNN, LSTM 这种,往往这些代码逻辑不好形成,因为好多细节我们根本就不知道。所以这次学习先从最简单的线性回归开始,然后慢慢的到复杂的那种网络。下面我们开始写一个线性回归模型:

# 首先我们得有训练样本X,Y, 这里我们随机生成
x = torch.rand(20, 1) * 10
y = 2 * x + (5 + torch.randn(20, 1))

# 构建线性回归函数的参数
w = torch.randn((1), requires_grad=True)
b = torch.zeros((1), requires_grad=True)   # 这俩都需要求梯度

for iteration in range(100):
 # 前向传播
 wx = torch.mul(w, x)
 y_pred = torch.add(wx, b)
 
 # 计算loss
 loss = (0.5 * (y-y_pred)**2).mean()
 
 # 反向传播
 loss.backward()
 
 # 更新参数
 b.data.sub_(lr * b.grad)    # 这种_的加法操作时从自身减,相当于-=
 w.data.sub_(lr * w.grad)

 # 梯度清零
 w.grad.data.zero_()
 b.grad.data.zero_()

print(w.data, b.data)

我们看一下结果:

在这里插入图片描述

5.总结

今天的学习内容结束, 下面简单的梳理一遍,其实小东西还是挺多的。

  • 首先我们从 Pytorch 最基本的数据结构开始,认识了张量到底是个什么东西,说白了就是个多维数组,而张量本身有很多的属性,有关于数据本身的 data,dtype,shape,dtype,也有关于求导的 requires_grad,grad,grad_fn,is_leaf;
  • 然后我们学习了张量的创建方法,比如直接创建,从数组创建,数值创建,按照概率创建等。这里面涉及到了很多的创建函数 tensor(),from_numpy(),ones(),zeros(),eye(),full(),arange(),linspace(),normal(),randn(),rand(),randint(),randperm() 等;
  • 接着就是张量的操作部分,有基本操作和数学运算,基本操作部分有张量的拼接两个函数 (.cat, .stack),张量的切分两个函数 (.chunk, .split),张量的转置 (.reshape, .transpose, .t),张量的索引两个函数 (.index_select, .masked_select)。数学运算部分,也是很多数学函数,有加减乘除的,指数底数幂函数的,三角函数的很多;
  • 最后基于上面的所学完成了一个简单的线性回归。

这次整理了很多的函数,每个函数的用法不同,具体用法先不用刻意记住,先知道哪些函数具体完成什么功能,到时候用的时,边查边用,慢慢的多练才能熟。

Ok,下一次学习 Pytorch 的动态图机制以及自动求导机制,然后基于前面的这两篇再玩一个逻辑回归,我们从最简单的模型开始 :)

6.引用

  1. 《深度之眼——Pytorch》
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 阿泽的学习笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.写在前面
  • 2.张量的简介与创建
    • 2.1 张量的简介
      • 2.2 张量的创建
      • 3.张量的操作
        • 3.1 张量的基本操作
          • 3.2 张量的数学运算
          • 4.线性回归模型
          • 5.总结
          • 6.引用
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档