20251011_Pytorch从入门到精通
讲的很不错! 就是这课太老了 复习下机器学习基础还能看 用作入门也很好 其他的略过即可
1 2
py
import torch
from torch import autograd
x = torch.tensor(1.)
a = torch.tensor(1., requires_grad=True)
b = torch.tensor(2., requires_grad=True)
c = torch.tensor(3., requires_grad=True)
y = a**2*x + b*x +c
a.grad, b.grad, c.grad # None
grads = autograd.grad(y, [a,b,c])
grads[0], grads[1], grads[2] # 有值了
常用层:
py
nn.Linear
nn.Conv2d
nn.LSTM
nn.ReLU
nn.Sigmoid
nn.Softmax
nn.CrossEntropyLoss
nn.MSE
3
conda安装
cuda安装(显卡驱动安装) 环境变量配置
pytorch安装
VSCode安装
4~13
梯度下降
- linear regression
- logistic regression
- classification
用激活函数 增强模型的非线性表达能力
讲的真不错!
14~15
py
# 数据类型
a = torch.randn(2, 3)
print("打印张量类型:", a.type())
# type(a)
isinstance(a, torch.FloatTensor)
a = a.cuda()
isinstance(a, torch.cuda.FloatTensor)
b = torch.tensor(1.3)
len(b.shape) # 标量 shape为 0,size=[]
torch.FloatTensor(1) # 这个不是标量 而是一个长度为1的向量
torch.from_numpy(data)
a.shape[0]
a.size(0)
list(a.shape)[0]
a.numel() # 元素数 多维维度数相乘
a.dim() #
16~17
py
# 创建tensor
a = np.ones([2,3])
torch.from_numpy(a)
torch.tensor([1,2,3]) # from list
torch.set_default_tensor_type(torch.DoubleTensor) # 默认是 torch.FloatTensor
a = torch.rand(3,3) # [0.1]之间 随机均匀初始化
torch.rand_like(a)
torch.randint(1, 10 , [3, 3]) # [1,10)之间 均匀分布
torch.randn(3,3) # 正太分布
torch.full([2,3], 1)
torch.arange(0, 10, 2) # [0,10) 等差
torch.linspace(0, 10 , steps=4) # [0,10] 等分
torch.logspace(0, -1 ,steps=10)
torch.ones(2,2)
torch.ones_like(a)
torch.zeros(3,3)
torch.eye(3,4) # 对角线全1
torch.randperm(10) # 0~9 随机打乱
18~19
py
# 索引和切片
a[:,:,::2,::2]
a.index_select(2, torch.arange(8)) # 第一个参数指定 要采样的维度 第二个参数指定该维度要采样的索引
x = torch.randn(3,4)
mask = x.ge(0.5) # 大于等于0.5的位置 设为1 其余为0
torch.masked_select(x, mask) # 会把选中的值 放到一个1为向量中 如torch.Size([3])
20~33
张量操作
py
# 维度变换
# view
a = torch.rand(4, 1, 28 ,28)
a.view(4, 28*28)
# reshape
a.reshape(4, 28*28)
# squeeze 删减维度
a.squeeze() # 挤压所有
a.squeeze(0)
# unsqueeze 增加维度
a.unsqueeze(0)
# transpose 交换维度
a.transpose(1, 3)
# t 转置
a.T
a.t()
# permute 多维度交换
a.permute(0,2,3,1)
# expand 扩展维度
a = torch.rand(1, 32, 1 ,1)
a.expand(4,32,14,-1) # -1表示不变
# repeat 扩展维度
# contiguous
# torch.all
# torch.eq
BroadCast机制
py
[4, 3, 32, 32]
+[32, 32]
+[3, 1, 1]
+[1, 1, 1, 1]
py
# 拼接和拆分
# Cat
a = torch.rand(4, 32, 8)
b = torch.rand(5, 32, 8)
torch.cat([a,b], dim=0) # [9 32 8]
# Stack:会创建一个新的维度
a = torch.rand(4,1, 16, 8)
b = torch.rand(4,1, 16, 8)
torch.stack([a,b], dim=2) # [4 1 2 16 8]
torch.stack([a,b], dim=0) # [2 4 1 16 8]
# Split:根据单元长度拆分
c = torch.rand(3, 4,1, 16, 8)
aa, _ = c.split([2, 1], dim=0) # [2 4 1 16 8]
aa, _, _ = c.split(1, dim=0) # [1 4 1 16 8]
# Chunk
aa, _, _ = c.chunk(3, dim=0) # [1 4 1 16 8]
py
# 数学运算
# add/minus/multiply/divide:对应位置元素操作
a + b
torch.add(a , b)
torch.sub(a , b)
torch.mul(a , b)
torch.div(a , b)
# matmul 矩阵相乘
a @ b
torch.matmul(a, b)
a = torch.rand(4,3,28,64)
b = torch.rand(4,3,64,32)
torch.matmul(a,b) # [4,3,28,32] 高维的只取最后两维进行运算(适用BroadCast机制)
# pow
a = torch.full([2,2], 3)
a.pow(2) # 2次方
aa = a ** 2
# sqrt/rsqrt
aa.sqrt() # 平方根
aa = a ** 0.5
aa.rsqrt() # 平方根倒数
# exp、log
a = torch.exp(torch.ones(2,2)) # e的1次方矩阵
torch.log(a) # 默认以e为底的对数计算
# round
a.floor() # 向下取整
a.ceil() # 向上取整
a.trunc() # 整数部分
a.frac() # 小数部分
a.round() # 四舍五入取整
# 其他
grad = torch.rand(2,3) * 15
grad.max()
grad.median()
grad.clamp(10) # 裁剪掉小于10的数据 相应位置赋值为10
grad.clamp(0, 10) # 常用在梯度剪裁 防止梯度弥散和梯度爆炸
py
# 统计方法
# norm:计算范数(注意不是normalize的意思)
a = torch.full([8], 1)
b = a.view(2,4)
c = a.view(2,2,2)
a.norm(1) # L1范数 所有元素绝对值和 8
a.norm(2) # L2范数 所有元素绝对值的平方和再开方(模计算) 2.828
b.norm(1, dim=1) # 4 矩阵
b.norm(2, dim=1) # 2 矩阵
c.norm(1, dim=0) # 2 矩阵
c.norm(2, dim=0) # 1.414 矩阵
# mean sum
# prod:累乘
# max min
# argmin argmax:默认会打平 可指定dim 返回的是索引 支持keepdim参数
# kthvalue topk
a.topk(3, dim=1)
a.topk(3, dim=1, largest=False) # 最小的k个
# kthvalue # 排序第k个元素
torch.eq(a,b) # 逐位置判断
torch.equal(a,b) # 判断张量是否一样
# where
# gather
torch.where(condition, x, y)
torch.gather(input, dim, index, out=None) # 查表
prob = torch.randn(4,10)
idx = prob.topk(3, dim=1)
idx = idx[1]
label = torch.arange(10) + 100
torch.gather(label.expand(4,10), dim=1, index=idx.long())
34~40
梯度:函数对所有自变量的偏微分向量
- 向量方向 表示 函数在当前点 变化的方向
- 向量的模 表示 函数在当前点 变化的速率
ResNet中的残差链接 可以起到 平滑超平面的作用,使得寻找极值点更容易;
影响寻找极小值的因素:参数初始值、学习率、动量(momentum,如何逃离局部极小值,就像沿着梯度下降方向运动的一个惯性)
激活函数和它的梯度:
sigmoid
(-inf, +inf) 压缩到=> (0, 1)连续光滑 导数∂(1 - ∂)- 缺点:梯度弥散,值0或1附近很大范围 不会更新或更新很慢
ReLU:
- Rectified Linear Unit
- 小于0不响应 大于0时线性响应,梯度为1(不变)
Loss及其梯度
- MSE:Mean Squared Error
- Cross Entropy Loss:一般和Softmax激活函数一起使用
py
x = torch.ones(1)
w = torch.full([1], 2)
w.requires_grad_()
mse = F.mse_loss(torch.ones(1), x*w)
# 方式1
torch.autograd.grad(mse, [w])
# 方式2
mse.backward()
mse.grad # 多维向量的话 可以进一步使用norm查看模的大小
41~52
感知机 MLP多层 的梯度推到(厉害了!)
求导链式法则:∂y/∂x = ∂y/∂u * ∂u/∂x
py
# 优化问题shi'jian
def himmelblau(x):# 一个用于测试梯度下降的经典函数
return (x[0] ** 2 + x[1] - 11) ** 2 + (x[0] + x[1] ** 2 - 7) ** 2
x = np.arange(-6, 6 , 0.1)
y = np.arange(-6, 6 , 0.1)
X, Y = np.meshgrid(x,y)
Z = himmelblau([X, Y])
fig = plt.figure("himmelblau")
ax = fig.gca(projection="3d")
ax.plot_surface(X,Y,Z)
ax.view_init(60, -30)
ax.set_xlabel("x")
ax.set_ylabel("y")
plt.show()
x = torch.tensor([0., 0.], requires_grad=True) # 优化的参数初始点
optimizer = torch.optim.Adam([x], lr=1e-3)
for step in range(20000):
pred = himmelblau(x)
optimizer.zero_grad()
pred.backward()
optimizer.step()
if step % 200 == 0:
print(step, x.tolist(), pred.item())
regression vs classification
MSE vs Cross Entropy
交叉熵损失:
- 熵是surprise的衡量度,熵越大越稳定,熵越小,惊喜度越高;
- 交叉熵则是衡量两个分布之间的不稳定程度(KL散度的衡量)
F.cross_entropy(logits, torch.tensor([3]))的输入是logits,内置了Softmax,这里的3是label;
讲的很好!
py
device = torch.device("cuda:0")
tensor_data.to(device)
# 代替
tensor_data.cpu()
tensor_data.cuda()
53~59
py
# acc计算示例
logits = torch.rand(4, 10)
pred = F.softmax(logits, dim=1)
pred_label = pred.argmax(dim=1)
logits.argmax(dim=1)
label = torch.tensor([9,3,2,4])
correct = torch.eq(pred_label, label)
correct.sum().float().item() / 4 # item() 转numpy
训练过程可视化:
- tensorboardX
- visdom
py
# pip install tensorboardX
from tensorboardX import SummaryWriter
writer = SummaryWriter()
writer.add_scalar(...)
writer.add_scalars(...)
writer.add_image(...)
writer.add_text(...)
writer.add_histogram(...)
writer.close()
过拟合 vs 欠拟合
- underfitting:模型表达能力不够
- overfitting:模型表达能力强,拟合了噪声,导致泛化能力变差
- 更多的数据
- 更可是的模型规模
- 正则化 Regularization/weight decay
- dropout:
torch.nn.Dropout(0.5)删除50%的神经元(model的train和eval模式切换) - 数据增强
- early stopping
py
# L2-Regularization
optimizer = optim.SGD(net.parameters(), lr=learning_rate, weight_decay=0.01)
数据集的划分及作用;
py
torch.utils.data.random_split(...)
K-fold cross-validation:
- 合理利用train-val,k折交叉验证;
- randomly sample 1/k as val set;
动量和学习率衰减
- momentum
- learning rate decay
py
optimizer = optim.SGD(net.parameters(), lr=learning_rate, momentum=args.momentum weight_decay=args.weight_decay)
schedulre = ReduceLRRnPlateau(optimizer, "min")
...
schedulre.step()
Stochastic Gradient Descent:基于Batch的随机梯度下降;
60~71
卷积神经网络
-
input_channels
-
kernel_channels
-
kernel_size
-
stride
-
padding
Input:(N, C, H, W)
Output:(N,C,H,W)Hout = (H + 2 x padding[0] - dilation[0] x (kernel_size[0] - 1) - 1) / stride[0] + 1
Wout = (W + 2 x padding[1] - dilation[1] x (kernel_size[1] - 1) - 1) / stride[1] + 1layer = nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=0)
layer.weight
layer.bias
pooling池化层:最大池化 平均池化
nn.MaxPool2d
upsample上采样
F.interpolate
ReLU:conv2d batchNorm Pooling ReLU
batchNorm:特征缩放,Normalization的一种,基于Batch计算的统计值(均值和方差),完成Batch数据输入的标准差标准化,使用可优化的参数学习实际的Norm用的标准差和方差;
py
x = torch.rand(1, 16 , 7,7)
layer = nn.BatchNorm2d(16)
out = layer(x)
# 统计的均值方差
# layer.running_mean
# layer.running_var
# 可优化的参数
layer.weight
layer.bias
1x1的卷积的作用:维度转换;
经典卷积网模型:ResNet残差网!
py
# 基本单元
class ResBlk(nn.Module):
def __init__(self, ch_in, ch_out):
self.conv1 = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1)
self.bn1 = nn.BatchNorm2d(ch_out)
self.conv2 = nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(ch_out)
self.extra = nn.Sequential()
if ch_out != ch_in:
# c不同 使用1x1卷积 进行对齐
self.extra = nn.Sequential(
nn.Conv2d(ch_in, ch_out, kernel_size=1, stride=1),
nn.BatchNorm2d(ch_out)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out = self.extra(x) + out
return out
72~74
nn.Module模块:所有网络结构的父类
- 大量基础网络结构
- nn.Sequential() 网络结构组织容器
- .parameters迭代器 返回模型所有参数
- .to(device)
- save and load
py
device = torch.device('cuda')
net = Net()
net.to(device)
net.load_state_dict(torch.load('ckpt.mdl'))
torch.save(net.state_dict(), 'ckpt.mdl')
数据增强:data argumentation
- 数据多样性
- 旋转 剪裁 调色 加噪声
- Flip翻转 Rotate RandomMove&Crop GAN
py
# torchvision
transform = transforms.Compose([
transforms.RandomHorizentalFlip(), # 随机 可能做也可能不做
transforms.RandomVerticalFlip(),
transforms.RandomRotation(15),
transforms.RandomRotation([90, 180, 270]),
transforms.Resize([32, 32]) # 参照中心点缩放
transforms。RandomCrop([28, 28])
transforms.ToTensor(), # 附带0~1
# transforms.Normalize((0.1307,),(0.3081,)) # 标准化
])
# 叠加高斯噪声
75~94
Data
- torchvision.datasets
- torchvision.transforms
- torch.utils.data.DataLoader
Model
- torch.nn
- torch.optim
py
mode.eval()
with torch.no_grad():
pass
ResNet:
py
self.extra = nn.Sequential()
if ch_out != ch_in:
self.extra = nn.Sequential(
nn.Conv2d(ch_in, ch_out, kernel_size = 1, stride=2),
nn.BatchNorm2d(ch_out)
)
# ...
out = self.extra(x) + out
py
# [b, 512, h, w] => [b, 512, 1, 1]
x = F.adaptive_avg_pool2d(x, [1, 1])
x = x.view(x.size(0), -1)
x = self.outlayer(x)
Embedding;
RNN:时间维度共享权值;(略)
梯度爆炸(Gradient Exploading)的解决:对梯度进行剪裁(clipping)
py
loss = criterion(output, y)
model.zero_grad()
loss.backward()
for p in model.parameters():
print(p.grad.norm()) # 查看参数梯度的模
torch.nn.utils.clip_grad_norm_(p, 10)
optimizer.step()
梯度弥散(Gradient Vanishing):累乘算子导致的;
LSTM(略);
nn.Embedding(vocab_size, embedding_dim)- nn.RNN
- nn.LSTM
- nn.LSTMCell
Google CoLab
95~106
自定义数据集
- 继承
torch.utils.data.Dataset__len__/__getitem__
py
class Flattern(nn.Module):
def __init__(self):
super(Flattern, self).__init__()
def forward(self, x):
shape = torch.prod(torch.tensor(x.shape[1:])).item()
return x.view(-1, shape)
def plot_image(img, label, name):
fig = plt.figure()
for i in range(6):
plt.subplot(2,3, i+1)
plt.tight_layout()
plt.imshow(img[i][0] * 0.3081 + 0.1307, cmap="gray", interpolation="none")
plt.title("{} : {}".format(name, label[i].item()))
plt.xticks([])
plt.yticks([])
plt.show()
# random.shuffle([1,2,3])
# PIL.Image
# lambda x: Image.open(x).convert('RGB')
glob.glob是Python标准库中的文件路径匹配函数,通过通配符实现灵活的文件搜索。
匹配当前目录文件
glob.glob('*.txt'):返回当前目录下所有.txt文件路径列表。
递归搜索子目录
glob.glob('**/*.py', recursive=True):搜索所有.py文件,支持多层子目录。
通配符示例
*: 匹配任意文件名(如*.png匹配所有图片文件) ?: 匹配单个字符(如file?.txt匹配file1.txt、file2.txt) []: 匹配指定范围(如[0-9].log匹配数字结尾的日志文件)
便捷载入图片数据集:torchvision.datasets.ImageFolder(root, transform)
打印模型参数量:sum(map(lambda p:p.numel(), model.parameters()))
py
for epoch in rage(epochs):
train(train_db)
if epoch%10 == 0:
val_acc = evaluate(val_db)
if val_acc is the best:
save_ckpt()
if out_of_patience():
break
load_ckpt()
test_acc = evaluate(test_db)
# model.load_state_dict(torch.load('best.mdl'))
迁移学习(Transfer learning):初始权重 复用参数 微调训练;
- torchvision.models导入resnet18,可以这样初始化直接带权重;
py
trained_model = resnet18(pretrained=True)
model = nn.Sequential(
*list(trained_model.children())[:-1],
Flatten(),
nn.Linear(512,10)
)
x = torch.randn(2,3,224,224)
print(model(x).shape) # [2,512,1,1]
107~117
无监督学习;unsupervised learning
https://projector.tensorflow.org/
Auto-Encoders
- denoising
- dropout
- adversarial(VAE )
KL散度:度量两个分布的差异,差异越小,KL散度值越小;
py
# VAE
x = x.view(batchsz, 28* 28)
h_ = self.encoder(x)
# [b, 20] => [b, 10] x 2
mu, sigma = h_.chunk(2, dim=1)
h = mu + sigma * torch.randn_like(sigma)
# 用 mu和sigma的均值和方差 来便捷的计算h与符合正太分布的分布间 的kl散度
kld = 0.5 * torch.sum(
torch.pow(mu, 2) +
torch.pow(sigma, 2) -
torch.log(1e-8 + torch.pow(sigma, 2)) - 1
) / np.prod(x.shape)
x = self.decoder(h)
x = x.view(batchsz, 1, 28, 28)
118~129
GAN(generative-adversarial-networks)
- Generator
- Discriminator:判别Generator 与Label间分布的相似性
微积分 必要学习;否则公式推导都看不懂;
Weight Clipping:约束网络的表达能力,有利于模型收敛;
Gradient Penalty
tensor_x.detach() # stop gradient
py
# Gradient Penalty
def gradient_penalty(D, xr, xf):
# param D:
# param xr [b,2]
# param xf [b, 2]
t = torch.rand(batchsz, 1).cuda()# 均匀分布
t = t.expand_as(xr)
mid = t * xr + (1 - t) * xf # 线性插值
mid.requires_grad_()
pred = D(mid)
grads = autograd.grad(outputs=pred, inputs=mid, grad_output=torch.ones_like(pred), create_graph=True, retain_graph=True, only_inputs=True)[0]
gp = torch.pow(grads.norm(2, dim=1) - 1, 2).mean() # 梯度2范数 和 1越接近越好
return gp
gp = gradient_penalty(D, xr, xf.detach()) # 通过增加线性插值的梯度惩罚损失项
loss_D = lossr + lossf + 0.2 * gp # 解决kl散度恒定 loss为0 梯度不更新的问题
130~135
- Ubuntu
lsb_release -a
- anaconda
whereis pythonwitch pythonsource ~/.bashrc
- Cuda Cudnn
- 参考官网 下载cuda并安装 安装会自动安装GOU驱动;
- nvidia-smi:驱动可用
- nvcc -V:Cuda可用
- gpustat
- htop
- Cudnn下载需要注册Nvidia(不一定要安装,如果环境已经有了)
- Pytorch
136~150
略