一、张量
张量表示由一个数值组成的数组,这个数组可能有多个维度
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 + Y
或 X += 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 osos.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')
房间数量(“NumRooms”)、巷子类型(“Alley”)、房屋价格(“Price”)
2,加载原始数据集
导入pandas包并调用read_csv函数
“NaN”项代表缺失值
import pandas as pddata = 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 Alley0 NaN Pave1 2.0 NaN2 4.0 NaN3 NaN NaN,0 1275001 1060002 1781003 140000Name: 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 torchx, 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 torchnpx.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))
"""
在深度学习中,经常试图解决优化问题: 最大化分配给观测数据的概率; 最小化预测和真实观测之间的距离。
用向量表示物品(如单词、产品或新闻文章),以便最小化相似项目之间的距离,最大化不同项目之间的距离。
目标,或许是深度学习算法最重要的组成部分(除了数据),通常被表达为范数。