李沫深度学习数理基础和实操—第一二章



[书籍原文]太大了传不上 ¶

分类问题(如图片识别猫狗),回归预测问题(如预测房价),语音识别、语音生成问题,聚类问题(没有指定标签,让机器自动总结不同数据之间得模式来聚类)等。 深度学习存储和操作数据的主要接口是张量(n维数组)。它提供了各种功能,包括基本数学运算、广播、索引、切片、内存节省和转换其他Python对象,一些笔记如下

import torch
x = torch.arange(12)
x = x.reshape(3,4)
y = torch.ones(2,3,4)
y
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.]]])
print(id(y))
2162619729384
y = x+y
print(id(y))#这时候发现y的id变化了,因此不能直接这么操作,可以直接更改y里的值,这样就不会占用新的内存
Z = torch.zeros_like(y)
print('id(Z):', id(Z))
Z[:] = x + y
print('id(Z):', id(Z))#这时候发现两次Z的内存地址没变,更节约内存
2162628254040
id(Z): 2162628254920
id(Z): 2162628254920
x = torch.zeros(2,3)
y = torch.ones(2,3)
x > y
tensor([[False, False, False],
        [False, False, False]])
import os#对于数据预处理,我们先人工定义一个数据集
os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:
    f.write('NumRooms,Alley,Price\n') # 列名
    f.write('NA,Pave,127500\n') # 每行表示一个数据样本
    f.write('2,NA,106000\n')
    f.write('4,NA,178100\n')
    f.write('NA,NA,140000\n')
import pandas as pd
data = pd.read_csv(data_file)
print(data)#发现有缺失值,因此可以选择插值或者删除缺失值来处理
   NumRooms Alley   Price
0       NaN  Pave  127500
1       2.0   NaN  106000
2       4.0   NaN  178100
3       NaN   NaN  140000
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())#这里就是用整列平均值来插值
print(inputs)
   NumRooms Alley
0       3.0  Pave
1       2.0   NaN
2       4.0   NaN
3       3.0   NaN
inputs = pd.get_dummies(inputs, dummy_na=True)#dummy_na就是来检测NAN的
print(inputs)#而对于这种称名变量可以用虚拟变量转换
   NumRooms  Alley_Pave  Alley_nan
0       3.0           1          0
1       2.0           0          1
2       4.0           0          1
3       3.0           0          1
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
X, y #这时候就转换为tensor数据类型了
(tensor([[3., 1., 0.],
         [2., 0., 1.],
         [4., 0., 1.],
         [3., 0., 1.]], dtype=torch.float64),
 tensor([127500., 106000., 178100., 140000.], dtype=torch.float64))

向量:严格来说,仅包含一个数值被称为标量。向量可以被视为标量值组成的列表。这些标量值被称为向量的元素(element)或分量(component)。当向 量表示数据集中的样本时,它们的值具有一定的现实意义。在数学表示法中,向量通常记为粗体、小写的符号(例如,x、y和z)) 维度(dimension)这个词在不同上下文时往往会有不同的含义,这经常会使人感到困惑。为了清楚起见,我们在此明确一下:向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。然而,张量的维度用来表示张量具有的轴数。在这个意义上,张量的某个轴的维数就是这个轴的长度。

矩阵:正如向量将标量从零阶推广到一阶,矩阵将向量从一阶推广到二阶。矩阵,我们通常用粗体、大写字母来表示(例如,X、Y和Z),在代码中表示为具有两个轴的张量。数学表示法使用A ∈ Rm×n 来表示矩阵A,其由m行和n列的实值标量组成。我们可以将任意矩阵A ∈ Rm×n视为一个表格,其中每个元素aij属于第i行第j列

A = torch.arange(20).reshape(5, 4)
A
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]])
A.T #当我们交换矩阵的行和列时,结果称为矩阵的转置(transpose)。通常用a⊤来表示矩阵的转置,如果B = A⊤,则对于任意i和j,都有bij = aji。
tensor([[ 0,  4,  8, 12, 16],
        [ 1,  5,  9, 13, 17],
        [ 2,  6, 10, 14, 18],
        [ 3,  7, 11, 15, 19]])

张量:就像向量是标量的推广,矩阵是向量的推广一样,我们可以构建具有更多轴的数据结构。张量(本小节中的“张量”指代数对象)是描述具有任意数量轴的n维数组的通用方法。例如,向量是一阶张量,矩阵是二阶张量。张量用特殊字体的大写字母表示(例如,X、Y和Z),它们的索引机制(例如xijk和[X]1,2i−1,3)与矩阵类似。

A = torch.arange(20, dtype=torch.float32).reshape(2,5, 2)
B = A.clone() # 通过分配新内存,将A的一个副本分配给B
A, A + B#可以看出张量的运算是这样的
(tensor([[[ 0.,  1.],
          [ 2.,  3.],
          [ 4.,  5.],
          [ 6.,  7.],
          [ 8.,  9.]],
 
         [[10., 11.],
          [12., 13.],
          [14., 15.],
          [16., 17.],
          [18., 19.]]]),
 tensor([[[ 0.,  2.],
          [ 4.,  6.],
          [ 8., 10.],
          [12., 14.],
          [16., 18.]],
 
         [[20., 22.],
          [24., 26.],
          [28., 30.],
          [32., 34.],
          [36., 38.]]]))

具体而言,两个矩阵的按元素乘法称为Hadamard积(Hadamard product)(数学符号⊙)。

A*B
tensor([[[  0.,   1.],
         [  4.,   9.],
         [ 16.,  25.],
         [ 36.,  49.],
         [ 64.,  81.]],

        [[100., 121.],
         [144., 169.],
         [196., 225.],
         [256., 289.],
         [324., 361.]]])

默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。我们还可以指定张量沿哪一个轴来通过求和降低维度。以矩阵为例,为了通过求和所有行的元素来降维(轴0),可以在调用函数时指定axis=0。由于输入矩阵沿0轴降维以生成输出向量,因此输入轴0的维数在输出形状中消失。

A_sum_axis0 = A.sum(axis=0)#在这里,0轴是channel,因此就是0+10、1+11这样
A_sum_axis0, A_sum_axis0.shape
(tensor([[10., 12.],
         [14., 16.],
         [18., 20.],
         [22., 24.],
         [26., 28.]]),
 torch.Size([5, 2]))
A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape
(tensor([[20., 25.],
         [70., 75.]]),
 torch.Size([2, 2]))

沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。

A.sum(axis=[0, 1])
tensor([ 90., 100.])
A.mean(axis=0), A.sum(axis=0) / A.shape[0]
(tensor([[ 5.,  6.],
         [ 7.,  8.],
         [ 9., 10.],
         [11., 12.],
         [13., 14.]]),
 tensor([[ 5.,  6.],
         [ 7.,  8.],
         [ 9., 10.],
         [11., 12.],
         [13., 14.]]))