Press "Enter" to skip to content

二、线性代数

一、张量

 

张量表示由一个数值组成的数组,这个数组可能有多个维度

 

import torch

 

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

 

1,shape

 

shape属性可以访问 张量的形状

 

x.shape # torch.Size([15])

 

2,numel()

 

numel()函数可以访问 张量中元素的总数

 

x.numel() # 15

 

3,reshape()

 

可以调用reshape()函数, 改变一个张量的形状而不改变元素数量和元素值

 

x = x.reshape(3,5)
x
"""
tensor([[ 0,  1,  2,  3,  4],
        [ 5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14]])
"""

 

4,使用全0、全1、其他常量或者从特定分布中随机采样的数字

 

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.]]])
"""

 

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.]]])
"""

 

torch.ones((3,4,5))*8
"""
tensor([[[8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.]],
        [[8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.]],
        [[8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.],
         [8., 8., 8., 8., 8.]]])
"""

 

torch.randn(3, 4)
"""
tensor([[-2.9078,  0.4283, -0.7296,  0.0575],
        [-1.3947,  0.5494,  0.9782, -0.3510],
        [-0.2191, -3.2434, -1.6111,  2.0091]])
"""

 

5,为所需张量中的每个元素赋予确定值

 

通过提供包含数值的 Python 列表(或嵌套列表)来为所需张量中的每个元素赋予确定值

 

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

 

6,标准算术运算

 

+、-、*、/、**、^

 

a1 = torch.tensor([1,2,4,8],dtype=torch.float32)
a2 = torch.tensor([2,2,2,2])
a1 + a2, a1 - a2, a1 * a2, a1 / a2, a1 ** a2 , torch.exp(a2)
"""
(tensor([ 3.,  4.,  6., 10.]),
 tensor([-1.,  0.,  2.,  6.]),
 tensor([ 2.,  4.,  8., 16.]),
 tensor([0.5000, 1.0000, 2.0000, 4.0000]),
 tensor([ 1.,  4., 16., 64.]),
 tensor([7.3891, 7.3891, 7.3891, 7.3891]))
 """

 

7,cat()

 

可以调用cat()函数, 把多个张量连结(concatenate)在一起

 

dim=0张量 列 连接

 

dim=1张量 行 连接

 

X = torch.arange(12, dtype=torch.float32).reshape((3, 4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
X, Y, torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
"""
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.]]),
 tensor([[2., 1., 4., 3.],
         [1., 2., 3., 4.],
         [4., 3., 2., 1.]]),
 tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [ 2.,  1.,  4.,  3.],
         [ 1.,  2.,  3.,  4.],
         [ 4.,  3.,  2.,  1.]]),
 tensor([[ 0.,  1.,  2.,  3.,  2.,  1.,  4.,  3.],
         [ 4.,  5.,  6.,  7.,  1.,  2.,  3.,  4.],
         [ 8.,  9., 10., 11.,  4.,  3.,  2.,  1.]]))
"""

 

8,sum()

 

对张量中的所有元素进行求和,产生一个只有一个元素的张量

 

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

 

9,广播机制

 

即使形状不同,仍然可以通过调用广播机制(broadcasting mechanism)来执行按元素操作

 

例如:a和b形状不同,相加不报错,为啥捏?系统会通过广播机制将a和b自动扩充,进行相加

 

a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b, a + b
"""
(tensor([[0],
         [1],
         [2]]),
 tensor([[0, 1]]),
 tensor([[0, 1],
         [1, 2],
         [2, 3]]))
"""

 

 

10,内存分配

 

Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
print(id(Y))
Y = Y + X
print(id(Y))
"""
1426209183608
1426209185448
"""

 

Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
Z = torch.zeros_like(Y)
print(id(Z))
Z = X + Y
print(id(Z))
"""
1426207453976
1426198282056
"""

 

如果在后续计算中没有重复使用 X,可以使用 X[:] = X + YX += Y 来减少操作的内存开销

 

Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
print(id(Y))
Y += X
print(id(Y))
"""
1426209185608
1426209185608
"""

 

Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
Z = torch.zeros_like(Y)
print(id(Z))
Z[:] = X + Y
print(id(Z))
"""
1426198282056
1426198282056
"""

 

11,NumPy张量

 

X = torch.arange(12, dtype=torch.float32).reshape((3, 4))
A = X.numpy()
B = torch.tensor(A)
type(X), type(A), type(B)
"""
(torch.Tensor, numpy.ndarray, torch.Tensor)
"""

 

12,Python 标量

 

a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
"""
(tensor([3.5000]), 3.5, 3.5, 3)
"""

 

二、数据预处理

 

1,创建数据集

 

首先创建一个人工数据集,并存储在CSV(逗号分隔值)文件 ../data/beyond.csv

 

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
')  # 列名
    f.write('NA,Pave,127500
')  # 每行表示一个数据样本
    f.write('2,NA,106000
')
    f.write('4,NA,178100
')
    f.write('NA,NA,140000
')

 

房间数量(“NumRooms”)、巷子类型(“Alley”)、房屋价格(“Price”)

 

2,加载原始数据集

 

导入pandas包并调用read_csv函数

 

“NaN”项代表缺失值

 

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
"""

 

3,处理缺失值

 

为了处理缺失的数据,典型的方法包括 插值法 和 删除法

 

插值法用一个替代值弥补缺失值

 

删除法则直接忽略缺失值

 

在这里,考虑 插值法

 

通过位置索引 iloc ,将data分成inputs和outputs, 其中前者为data的前两列,而后者为data的最后一列

 

inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs, outputs
"""
(   NumRooms Alley
 0       NaN  Pave
 1       2.0   NaN
 2       4.0   NaN
 3       NaN   NaN,
 0    127500
 1    106000
 2    178100
 3    140000
 Name: Price, dtype: int64)
"""

 

对于inputs中缺少的数值,用同一列的均值替换“NaN”项

 

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中的类别值或离散值,将“NaN”视为一个类别。

 

由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。

 

巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。

 

缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。

 

inputs = pd.get_dummies(inputs, dummy_na=True)
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
"""

 

4,转换为张量格式

 

import torch
x, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
x, y
"""
(tensor([[3., 1., 0.],
         [2., 0., 1.],
         [4., 0., 1.],
         [3., 0., 1.]], dtype=torch.float64),
 tensor([127500, 106000, 178100, 140000]))
"""

 

三、线性代数

 

1,标量

 

标量由只有一个元素的张量表示

 

from mxnet import np, npx
import torch
npx.set_np()
x = np.array(3.0)
y = np.array(2.0)
x + y, x * y, x / y, x ** y
"""
(array(5.), array(6.), array(1.5), array(9.))
"""

 

2,向量

 

可以将向量视为标量值组成的列表,这些标量值称为向量的元素(element)或分量(component)

 

通过一维张量处理向量

 

x = torch.arange(4)
x
"""
x = torch.arange(4)
x
1
x = torch.arange(4)
2
x
tensor([0, 1, 2, 3])
"""

 

使用下标来引用向量的任一元素

 

x[2]
"""
tensor(2)
"""

 

2.1,len()

 

通过调用Python的内置len()函数来访问张量的长度

 

len(x)
"""
4
"""

 

2.2,shape

 

当用张量表示一个向量(只有一个轴)时,可以通过.shape属性访问向量的长度

 

形状(shape)是一个元素组,列出了张量沿每个轴的长度(维数)

 

对于只有一个轴的张量,形状只有一个元素。

 

x.shape
"""
torch.Size([4])
"""

 

向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。

 

然而,张量的维度用来表示张量具有的轴数。

 

在这个意义上,张量的某个轴的维数就是这个轴的长度。

 

3,矩阵

 

矩阵,在代码中表示为具有两个轴的张量。

 

指定两个分量和来创建一个形状为m×n的矩阵

 

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]])
"""

 

矩阵的转置T

 

A.T
"""
tensor([[ 0,  4,  8, 12, 16],
        [ 1,  5,  9, 13, 17],
        [ 2,  6, 10, 14, 18],
        [ 3,  7, 11, 15, 19]])
"""

 

4,张量

 

向量是标量的推广,矩阵是向量的推广

 

向量是一阶张量,矩阵是二阶张量

 

张量用特殊字体的大写字母表示(例如:X、Y、Z)

 

X = torch.arange(24).reshape(2, 3, 4)
X
"""
tensor([[[ 0,  1,  2,  3],
         [ 4,  5,  6,  7],
         [ 8,  9, 10, 11]],
        [[12, 13, 14, 15],
         [16, 17, 18, 19],
         [20, 21, 22, 23]]])
"""

 

5,张量算法的基本性质

 

将两个相同形状的矩阵相加,会在这两个矩阵上执行元素加法

 

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
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.]])
"""

 

将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘。

 

a = 2
X = torch.arange(24).reshape(2, 3, 4)
X, a + X, (a * X).shape
"""
(tensor([[[ 0,  1,  2,  3],
          [ 4,  5,  6,  7],
          [ 8,  9, 10, 11]],
 
         [[12, 13, 14, 15],
          [16, 17, 18, 19],
          [20, 21, 22, 23]]]),
 tensor([[[ 2,  3,  4,  5],
          [ 6,  7,  8,  9],
          [10, 11, 12, 13]],
 
         [[14, 15, 16, 17],
          [18, 19, 20, 21],
          [22, 23, 24, 25]]]),
 torch.Size([2, 3, 4]))
"""

 

6,降维

 

6.1,sum()

 

计算任意张量元素的和

 

x = torch.arange(5, dtype=torch.float32)
x, x.sum()
"""
(tensor([0., 1., 2., 3., 4.]), tensor(10.))
"""

 

也可以表示任意形状张量的元素和

 

A = torch.arange(6).reshape(2, 3)
A, A.shape, A.sum()
"""
(tensor([[0, 1, 2],
         [3, 4, 5]]),
 torch.Size([2, 3]),
 tensor(15))
"""

 

默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量

 

也可以指定张量沿哪一个轴来通过求和降低维度

 

axis=0,同一列所在的所有行元素相加

 

axis=1,同一行所在的所有列元素相加

 

A_sum_axis0 = A.sum(axis)#同一列所在的所有行元素相加
A_sum_axis0, A_sum_axis0.shape
"""
(tensor([3, 5, 7]), torch.Size([3]))
"""

 

指定axis=1将通过汇总所有列的元素降维(轴1)

 

因此,输入轴1的维数在输出形状中消失

 

A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape
"""
(tensor([ 3, 12]), torch.Size([2]))
"""

 

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

 

A.sum(axis=[0, 1]), A.sum()
"""
(tensor(15), tensor(15))
"""

 

6.2,mean()

 

总和除以元素总数来计算平均值

 

也可以调用函数来计算任意形状张量的平均值

 

A = torch.arange(6,dtype=torch.float32).reshape(2, 3)
A, A.mean(), A.sum() / A.numel()
"""
(tensor([[0., 1., 2.],
         [3., 4., 5.]]),
 tensor(2.5000),
 tensor(2.5000))
"""

 

计算平均值的函数也可以沿指定轴降低张量的维度

 

A.mean(axis=0), A.sum(axis=0) / A.shape[0]
"""
(tensor([1.5000, 2.5000, 3.5000]), tensor([1.5000, 2.5000, 3.5000]))
"""

 

6.3,非降维求和

 

调用函数来计算总和或均值时保持轴数不变会很有用

 

也就是 keepdims=True ,不会因为 axis 而把某个维度给去掉,而是置为一

 

A = torch.arange(6,dtype=torch.float32).reshape(2, 3)
sum_A = A.sum(axis=1, keepdims=True)
A, sum_A
"""
(tensor([[0., 1., 2.],
         [3., 4., 5.]]),
 tensor([[ 3.],
         [12.]]))
"""

 

keepdims默认为False,axis=1

 

a = torch.ones((2,5,4))
a.shape # torch.Size([2, 5, 4])
a.sum(axis=1).shape # torch.Size([2, 4])
a.sum(axis=1)
"""
tensor([[5., 5., 5., 5.],
        [5., 5., 5., 5.]])
"""
a
"""
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.],
         [1., 1., 1., 1.],
         [1., 1., 1., 1.],
         [1., 1., 1., 1.],
         [1., 1., 1., 1.]]])
"""

 

keepdims默认为True,axis=1

 

a.sum(axis=1,keepdims=True).shape # torch.Size([2, 1, 4])
a.sum(axis=1,keepdims=True)
"""
tensor([[[5., 5., 5., 5.]],
        [[5., 5., 5., 5.]]])
"""

 

keepdims默认为False,axis=[0,2]

 

a.sum(axis=[0,2]).shape # torch.Size([5])
a.sum(axis=[0,2]) # tensor([8., 8., 8., 8., 8.])

 

keepdims默认为True,axis=[0,2]

 

a.sum(axis=[0,2],keepdims=True).shape # torch.Size([1, 5, 1])
a.sum(axis=[0,2],keepdims=True)
"""
tensor([[[8.],
         [8.],
         [8.],
         [8.],
         [8.]]])
"""

 

由于sum_A在对每行进行求和后仍保持两个轴,我们可以通过广播将A除以sum_A

 

A / sum_A
"""
tensor([[0.0000, 0.3333, 0.6667],
        [0.2500, 0.3333, 0.4167]])
"""

 

6.4,cumsum()

 

如果想沿某个轴计算A元素的累积总和, 比如axis=0(按行计算)

 

可以调用cumsum函数。 此函数不会沿任何轴降低输入张量的维度

 

A = torch.arange(6,dtype=torch.float32).reshape(2, 3)
A, A.cumsum(axis=0)
"""
(tensor([[0., 1., 2.],
         [3., 4., 5.]]),
 tensor([[0., 1., 2.],
         [3., 5., 7.]]))
"""

 

7,点积

 

相同位置的按元素乘积的和

 

按元素乘法,然后进行求和来表示两个向量的点积

 

x = torch.arange(4, dtype=torch.float32)
y = torch.tensor([2.0, 1, 4, 3])
x, y, torch.dot(x, y), torch.sum(x * y)
"""
(tensor([0., 1., 2., 3.]), tensor([2., 1., 4., 3.]), tensor(18.), tensor(18.))
"""

 

8,向量积

 

矩阵A和向量x调用 torch.mv(A, x) 时,会执行矩阵-向量积

 

A的列维数(沿轴1的长度)必须与x的维数(其长度)相同

 

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
x = torch.arange(4, dtype=torch.float32)
A, x, A.shape, x.shape, torch.mv(A, x)
"""
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [12., 13., 14., 15.],
         [16., 17., 18., 19.]]),
 tensor([0., 1., 2., 3.]),
 torch.Size([5, 4]),
 torch.Size([4]),
 tensor([ 14.,  38.,  62.,  86., 110.]))
"""

 

9,矩阵乘法

 

矩阵A和矩阵B调用 torch.mm(A, B) 时,会执行矩阵乘法操作

 

前行成后列

 

A是一个5行4列的矩阵,B是一个4行3列的矩阵,两者相乘后,得到了一个5行3列的矩阵

 

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = torch.tensor([[2,3,4],[1,5,9],[7,5,3],[4,5,6]],dtype=torch.float32)
A, B, torch.mm(A, B)
"""
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [12., 13., 14., 15.],
         [16., 17., 18., 19.]]),
 tensor([[2., 3., 4.],
         [1., 5., 9.],
         [7., 5., 3.],
         [4., 5., 6.]]),
 tensor([[ 27.,  30.,  33.],
         [ 83., 102., 121.],
         [139., 174., 209.],
         [195., 246., 297.],
         [251., 318., 385.]]))
"""

 

10,范数

 

一个向量的范数告诉我们一个向量有多大,这里考虑的大小(size)概念不涉及维度,而是分量的大小。

 

有Lp范数衍生出了L1、L2等范数,这些都是针对的 向量

 

10.1,L1范数

 

L1范数:向量元素的绝对值之和

 

先通过绝对值函数,然后按元素求和

 

q = torch.tensor([10.0,14.0])
q, torch.abs(q).sum()
"""
(tensor([10., 14.]), tensor(24.))
"""

 

10.2,L2范数

 

L2范数:向量元素平方和的平方根

 

norm() 函数进行求解

 

q = torch.tensor([3.0,4.0])
q, torch.norm(q)
"""
(tensor([3., 4.]), tensor(5.))
"""

 

10.3,Frobenius范数

 

Frobenius范数: 矩阵 元素平方和的平方根

 

norm()函数进行求解

 

Frobenius范数满足向量范数的所有性质,它就像是 矩阵形向量 的L2范数

 

q = torch.tensor([[1.0,2.0],[3.0,4.0]])
q ,torch.norm(q)
"""
(tensor([[1., 2.],
         [3., 4.]]),
 tensor(5.4772))
"""

 

在深度学习中,经常试图解决优化问题: 最大化分配给观测数据的概率; 最小化预测和真实观测之间的距离。

 

用向量表示物品(如单词、产品或新闻文章),以便最小化相似项目之间的距离,最大化不同项目之间的距离。

 

目标,或许是深度学习算法最重要的组成部分(除了数据),通常被表达为范数。

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注