祝你好运的技术博客

Published on

《动手学深度学习》学习笔记(二)

Authors
  • avatar
    Name
    祝你好运
    Twitter

注:说实话如果单纯看书,我觉得难度很大,但现在有了ChatGPT,不懂的直接甩过去,给我讲的明明白白,顺带一连串的彩虹屁,让我觉得我马上就能训练出ChatGPT 6!

第二章:预备知识

数据操作

首先我后面的学习都只用PyTorch,别的几种都不用了。然后PyTorch在导入的时候其实是torch,这个习惯了就好了。

初始化

然后我们数据操作先讲的就是数据初始化,比如生成12个浮点数(注意这里是0到11而不是1到12):

x = torch.arange(12, dtype=torch.float32)
x
tensor([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11.])

其实我认为刚开始学习的时候,应该教下面的,少一些参数,会更容易学习:

x = torch.arange(12)
x
tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

很多时候我们想要一份全0数据,注意这里是一份3维数据,第一维有2个元素,第三维有4个。

torch.zeros((2, 3, 4))
tensor([[[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]],

        [[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]]])

有些时候我们又想要全1的数据:

torch.ones((2, 3, 4))
tensor([[[1., 1., 1., 1.],
         [1., 1., 1., 1.],
         [1., 1., 1., 1.]],

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

当然随机数也非常重要,尤其是我们训练开始的时候,直接用随机数初始化,然后开始训练。注意下面的是randn而不是randrand是随机出0到1的数,而randn是随机出0附近的正态分布的数,这个如果不了解可以去问大模型,我已经问明白了(当然也是之前学过,大概是高中):

torch.randn(3, 4)
tensor([[-0.7823, -2.0361,  0.6767,  1.6542],
        [ 0.4277, -1.8274,  0.0578, -0.3718],
        [-0.0502, -0.9920,  0.7946,  0.4622]])

调整大小

注意我们这里用的x就是上面的哈

X = x.reshape(3, 4)
X
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]])
X.shape
torch.Size([3, 4])

索引与切片

这里的索引与切片就类似Python里面的数组的索引和切片,那边懂了这里就没问题。比如下面的X[-1],这里意思就是X的第一维数据的最后一个元素。X[1:3]这个就是X的第一维数据做切片,从第1到第2个,一共两个。

X[-1], X[1:3]
(tensor([ 8.,  9., 10., 11.]),
 tensor([[ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.]]))

而且修改值的时候,可以用单个值覆盖多维数据: 比如

x = torch.randn(3, 4, 5)
x[1:2] = 6
tensor([[[-1.8204, -1.2844,  0.4844,  0.4788, -0.0531],
         [-0.1200, -0.7851, -0.3964,  0.6313,  0.7284],
         [-1.0022,  0.5124,  1.1530,  0.4703, -0.0768],
         [ 0.4584,  0.6001, -0.6398, -0.3655,  0.4246]],

        [[ 6.0000,  6.0000,  6.0000,  6.0000,  6.0000],
         [ 6.0000,  6.0000,  6.0000,  6.0000,  6.0000],
         [ 6.0000,  6.0000,  6.0000,  6.0000,  6.0000],
         [ 6.0000,  6.0000,  6.0000,  6.0000,  6.0000]],

        [[-0.2148, -0.5980, -0.0466, -0.3548,  1.2645],
         [ 0.6480,  0.2927,  0.3504, -1.9341, -0.9903],
         [ 0.0111, -1.1888, -1.4203,  0.8937,  0.3412],
         [ 0.4003,  0.5873, -0.4195, -0.9600,  0.8594]]])

操作符

这里介绍了一些常见的操作符,比如加减乘除次方,拼接,求自然指数,求和。注意,有些操作符是要求两个运算数的维度要一样,比如下面的求和就会报错:

>>> x = torch.tensor([1, 2, 4])
>>> y = torch.tensor([3, 6, 9, 12])
>>> x + y
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 (4) at non-singleton dimension 0

而有些运算符就不要求,比如求和,求自然指数

torch.exp(x)
X.sum()

对于维度不一样,又想做计算,可能会用到Broadcasting这种技术,他会把两个参与计算的tensor的维度搞成一样,这样就可以计算了。不是说维度不一样的数据就可以用broadcasting,而是数据复制的时候没有歧义(也就是要被复制的那一行或者列只有1个元素),比如下面的x和y

>>> x = torch.tensor([[1, 2, 3]])
>>> y = torch.tensor([[1], [2], [3]])
>>> x + y
tensor([[2, 3, 4],
        [3, 4, 5],
        [4, 5, 6]])

节省内存

当数据量很大的时候,复制数据会变得很慢,所以原地操作就是分必要了,而Python这种语言,他有的是原地修改,有些是复制的,除了基础类型(整数,浮点数,布尔),别的基本都是原地修改(字符串和元组例外)。树上举的例子也很好:

before = id(X)
X += Y
id(X) == before
True