基础张量操作

Concatenate

x = torch.arange(12, dtype=torch.float32).reshape(3,4)
x = torch.tensor([1, 3, 4, 5], [1, 3, 4, 5], [4, 3, 2, 1])
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
# 按第一维拼接,就是行数增加,按第二维拼接,列数增加

Broadcasting

a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a + b

会逐个元素彼此运算,拼成结合二者维度的新张量

tensor([[0, 1],
        [1, 2],
        [2, 3]])

要注意的是只有相同维度,缺省维度,单个维度,可以使用broadcasting,其他均不可

Memory

Python的内存分配机制会让Y=X+Y的Y属于两个不同的内存地址

Z = torch.zero_like(Y) #init Z
before = id(Z)
Z[:] = X + Y #Y[:] = X + Y
id(Z) == before

通过 Z[:] 解决问题,指明使用同一变量

Ture

Conversion

# 转化numpy
A = X.numpy()
B = torch.from_numpy(A)
type(A), type(B) # numpy  tensor
 
a = torch.tensor([3])
a.item() # 将维度为1的tensor转换成python的标量

要注意的是torch和numpy共享一个地址,如果两个变量指示一个数据这两种不同格式,修改其中一个的数据,另一个也会更改

pandas操作

Comm-separated values 即CSV文件,含义是逗号分割的文件

读取

data = pd.read_csv(data_file)
print(data)

选择

name_column = data['Name', 'City']
name_column = data.loc[:, 'Name']

bed bugs 处理

bed bugs是指NaN的数值

文本分类值

inputs, targets = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)

其中get_dummies是将分类数据拆分为独热编码的函数,pandas会默认将文字和空白补充为NaN,使用函数后可以将这些单独列出一列(包括空白的NaN)

   NumRooms  RoofType_Slate  RoofType_nan
0       NaN           False          True
1       2.0           False          True
2       4.0            True         False
3       NaN           False          True

数字空缺值

inputs = inputs.fillna(inputs.mean())

tensor转换

原始值是数值,先转成numpy形式再转成对应的tensor

import torch
 
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))

Linear Algebra

使用order(阶)来代表维度,使用dimension(维)来代表维度的长度

Vector

x = torch.arange(3)
x = torch.rand(10)
len(x)
x.shape

Matrices

A.T # 转置

Hadamard 积,也就是 A * B ,逐元素相乘

Tensor

a = 2
X = torch.arange(24).reshape(3, 4, 2)
a + X, (a * X).shape # 含义是每个元素的运算
X.sum() # 结果是直接全部元素相加

降维

A.sum(axis=0).shape

sum其实是对tensor降维,可以选定第几维进行降维,降维之后变成一个和的标量,但不会影响tensor本身

A.mean() == A.sum() / A.numel() # True
A.mean(axis=0)

非降维Sum

有时候需要保持维度来支持后续运算,例如每行标准化

sumA = A.sum(axis=1, keepdims=True)
reduceA = A.sum(axis=1)
sum_A.shape   reduceA.shape
(torch.size([2, 1])  torch.size([2]))
A.cumsum(axis=0), A

这是按行(列)加,每行每列的元素等于其前面元素的累加和

tensor([[0., 1., 2.],
        [3., 5., 7.]])
tensor([[0., 1., 2.],
        [3., 4., 5.]])

Dot Products

y = torch.ones(3, dtype = torch.float32)
torch.dot(x, y)

逐元素相乘再相加 与 torch.sum(x * y) 等价

矩阵与向量的乘法。 m * n 的矩阵与 n * 1的向量点乘可以看作是n到m的投影变换,也描述了前一层输出确定时,计算网络每层输出的关键(?)

toch.mv(A, x), A@x

mv是matrix与vector,@支持矩阵与矩阵,和矩阵与向量的运算

torch.mm(A, B), A@B

Norm

性质

  • 满足

  • 满足三角不等式

  • 非0,除非向量为0

  • 欧式距离 (平方和开根号)

u = torch.tensor([3.0, -4.0])
torch.norm(u)
  • lasso (绝对值之和)
torch.abs(u).sum()
  • 更高的范式
    上述范数都满足这个范数

  • Frobenius Norm (矩阵各元素平方和的平方根)

torch.norm(torch.ones((4, 9)))
  • 谱范数,描述了矩阵运算Xv 与 原本的向量v相距多长

Automatic Differentitaion

基础计算

x = torcb.arange(4.0)
x # (0., 1., 2., 3.)

现代深度学习框架会自动计算梯度,对每个连续函数传递数据时,框架会构建一个计算图,跟踪每个值并依赖于其他值,为了计算导数,自动微分程序通过链式法则在图中反向工作称为反向传播(backpropagation)

使用梯度计算,可以

x = torch.arange(4.0, require_grad=True)
x.require_grad(True)
x.grad  # 查看梯度

实现y是x的函数

y = 2 * torch.dot(x, x)
y.backward()
x.grad
tensor([ 0.,  4.,  8., 12.])

一般的增加关于x的函数时,程序会自动将其梯度结果加在上次计算的梯度上(即存储在一个地方,两数相加)

x.grad_zero_() # 梯度重置
y = x.sum()
y.backward()
x.grad
tensor([1., 1., 1., 1.])

注意向量函数的对向量的导数是向量

计算相关

向量计算

其实向量对向量求导的结果应该是雅可比矩阵(Jacobian),但是深度学习一般更在乎y的每个分量对完整向量x的梯度之和,得到一个于x形状相同的向量

深度学习在解释非标量张量的梯度是存在差异,Pytorch在非标量上调用backward会出错,除非设置好求梯度对象如何缩减(降维)成标量。具体来说,需要有一个 v 来实现对 而不是

y = x * x
y.backward(gradient=torch.ones(len(x)))
x.grad

结合例子来看,实际上gradient是起到一个权重作用,对于向量y,其提供一个加权和的降维方法,将y改成标量,再对x逐一求导,得到向量结果

Detaching

当计算时,可能会有这样一种情况:

  • 但是只想要关注 z 对 x 的梯度结果,而 y 不过是一个辅助的过程函数

那么需要将y排除在反向传播之外,这需要建立一个新变量u ,保证其来源被抹去,只有数值,使用u来代替y,就可以保证其梯度不会流向x

x.grad.zero_()
y = x * x
u = y.detach()
z = u * x
 
z.sum().backward()
x.grad == u
tensor([True, True, True, True])

其他

另外,自动求导对分段函数也适用,对自定的python计算程序,利用Python控制流(条件,循环,调用等)就可以计算结果的梯度