LOADING

加载过慢请开启缓存 浏览器默认开启

深度学习笔记0

深度学习

1. 数据操作 + 数据预处理

1.1 数据操作

tensor一维向量如果只有一个元素,等同于标量,其shape都是: torch.Size([])

import torch
x = torch.arange(12)
x.shape
x.numel()
# reshape是浅拷贝!
X = x.reshape(3, 4)
# 维度
X.ndim

torch.zeros((2, 3, 4))
torch.ones((2, 3, 4))
torch.randn(3, 4)

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

x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y  # **运算符是求幂运算
torch.exp(x) # 相当于是每个元素 -> e^(每个元素)

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]])
# 第一个输出张量的轴-0长度(6)是两个输入张量轴-0长度的总和(3 + 3);
# 第二个输出张量的轴-1长度(8)是两个输入张量轴-1长度的总和(4 + 4)。
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
# 对应位置是否相等,输出同样大小bool矩阵
X == Y
X.sum()

广播机制:

1.本来:相加应该形状一样(都是3 x 4)

2.广播机制:

两个张量必须不为空:

a.维度相同:对于同一维度,如果大小不相等,但是有一个为1,就会自动复制到相等

b.维度不同:维度少的张量在shape的左边补1,直到两个维度一样

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

a = torch.arange(40).reshape((2, 4, 5))
b = torch.arange(5).reshape((1, 5))
a + b
X[-1], X[1:3]
X[1, 2] = 9     #类似于X[1][2]
X[0:2, :] = 12

节省内存:

Y = X + Y 会导致 新分配一个Y,再析构掉原来的Y

解决:

Y+=X
# or
Z[:] = X + Y

这里Z[:] 是在等式左侧,所以可以

如果直接比较Z和Z[:] ,会发现二者id不一样

Z[:]相当于浅拷贝,指向的是Z

NumPy:

# 将深度学习框架定义的张量[转换为NumPy张量(ndarray)]很容易,反之也同样容易。 torch张量和numpy数组将共享它们的底层内存,就地操作更改一个张量也会同时更改另一个张量。
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)

# 要(将大小为1的张量转换为Python标量),我们可以调用item函数或Python的内置函数。
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)

1.2 数据预处理

# 写入csv文件
import os
# os.path.join是用操作系统的分隔符连接,exist_ok是存在就不创建
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')
# 读取csv文件
import pandas as pd

data = pd.read_csv(data_file)
data

处理缺失值:

# 位置索引 iloc: index location
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
# mean是求平均数(数值才生效),fillna是填充
inputs = inputs.fillna(inputs.mean())
print(inputs)

inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)

image-20230822215458152

现在都变成了数值类型,就可以转换为张量了

import torch

X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
# 这里dtype=torch.float64,默认64,比32慢,一般用32
X, y

2. 线性代数

2.1 线性代数

https://www.bilibili.com/video/BV1ys411472E/?p=1

2.2 实现

import torch
x = torch.arange(4)
x[3]
len(x)

A = torch.arange(20).reshape(5, 4)
# 转置
A.T

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone()  # 通过分配新内存,将A的一个副本分配给B,深拷贝
A, A + B
# 这是对应位置相乘
A * B

a = 2
X = torch.arange(24).reshape(2, 3, 4)
# 每个值都加/乘上2,因为广播机制
a + X, (a * X).shape

sum:

x = torch.arange(4, dtype=torch.float32)
# sum默认把所有元素加起来,也就是沿所有轴降低维度,最后成一个标量
x, x.sum()

A = torch.arange(20*2, dtype=torch.float32).reshape(2, 5, 4)
A.shape, A.sum()
# axis表示轴,也就是按【指定轴】降低维度,将指定维度在输出形状中消失
A_sum_axis0 = A.sum(axis=1)
A_sum_axis0, A_sum_axis0.shape

# 这是消去第0轴和第1轴
A.sum(axis=[0, 1])

# 二者等价,numel表示个数
A.mean(), A.sum() / A.numel()
# 也可以指定维度,左边的操作就是右边的步骤,二者等价
A.mean(axis=0), A.sum(axis=0) / A.shape[0]

# 非降维度求和,也即只是将其变为1
sum_A = A.sum(axis=1, keepdims=True)
sum_A, A.sum(axis=1), sum_A.shape, A.sum(axis=1).shape
# 然后就能通过广播,做除法
A / sum_A

# 沿着某个轴,进行累加,第0个+=0,第1个+=第0个,第三个+=前两个...s,也即前缀和
A.cumsum(axis=0)

image-20230827221219483

image-20230827224201792

image-20230827224910929

点积:

x = torch.arange(4, dtype=torch.float32)
y = torch.ones(4, dtype = torch.float32)
x, y, torch.dot(x, y)
# 结果:(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))

# 这个结果和点积结果一致
torch.sum(x * y)

**矩阵*向量:**

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
# 注意A是一个5x4矩阵,x是一个4x1列向量,结果为5x1的列向量
A.shape, x.shape, torch.mv(A, x)

矩阵乘法:

B = torch.ones(4, 3)
# mm只能二维,多维要matmul,可以@表示矩阵乘法
torch.mm(A, B)

范数:

,矩阵元素平方和的平方根:

# 𝐿2范数中常常省略下标2,也就是说‖𝐱‖等同于‖𝐱‖2
u = torch.tensor([3.0, -4.0])
torch.norm(u) # 结果:tensor(5.)

# 𝐿1范数
torch.abs(u).sum() # 结果:tensor(7.)

# 计算矩阵的Frobenius范数
torch.norm(torch.ones((4, 9))) # 结果:tensor(6.)

3. 矩阵计算

矩阵求导的本质与分子布局、分母布局的本质(矩阵求导——本质篇):https://zhuanlan.zhihu.com/p/263777564

向量点乘的求导:https://www.bilibili.com/video/BV1Qd4y1Q77d/?vd_source=7e34085a459b4e9ffab42ccd04776d69

点乘和叉乘满足乘法求导规则,但是记得,如果表示的是矩阵乘法,要将列向量转置一下

默认分子布局

image-20230905213047401

image-20230905213112458

image-20230904231843755

4. 自动求导

链式法则,向量同样适应(可从对应的向量、矩阵大小看出)

一文解释 PyTorch求导相关 (backward, autograd.grad)

https://zhuanlan.zhihu.com/p/279758736?utm_id=0

4.1 实现

import torch

x = torch.arange(4.0)
x

# [在我们计算 𝑦关于 𝐱的梯度之前,需要一个地方来存储梯度。] ,我们不会在每次对一个参数求导时都分配新的内存。 因为我们经常会成千上万次地更新相同的参数,每次都分配新的内存可能很快就会将内存耗尽。
# grad表示梯度缩写gradient
x.requires_grad_(True)  # 等价于x=torch.arange(4.0,requires_grad=True)
x.grad  # 默认值是None

y = 2 * torch.dot(x, x)
y

y.backward() # 反向传播函数
x.grad # tensor([ 0.,  4.,  8., 12.])

x.grad == 4 * x

# 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad

# 对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。
# 本例只想求偏导数的和,所以传递一个1的梯度是合适的
x.grad.zero_()
y = x * x
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad

x.grad.zero_()
y = x * x
# 将u作为常数处理
u = y.detach()
z = u * x

z.sum().backward()
x.grad == u

x.grad.zero_()
y.sum().backward()
x.grad == 2 * x

def f(a):
    b = a * 2
    # norm是求L2范数,即平方和开根号
    while b.norm() < 1000:
        b = b * 2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c

# size=()表示标量
a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()

print(a.grad == d / a)
a.grad

函数关于的梯度应为
因为相当于求范数2结果为