源代码
import torch
import matplotlib.pyplot as plt
import random
from utils import *
%matplotlib inline
def synthtic_data(w,b,num_examples):
X = torch.normal(0,0.5,(num_examples,len(w)))
y = torch.matmul(X,w) + b
return X,y.reshape((-1,1))
true_w = torch.tensor([-3.,-4.2])
true_b = 3.4
features,labels = synthtic_data(true_w,true_b,1000)
print(f"features: {features[0]},\nlabels: {labels[0]}")
plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1)
def data_iter(batch_size,features,labels):
num_examples = len(features)
indices = list(range(num_examples))
random.shuffle(indices)
for i in range(0,num_examples,batch_size):
batch_indices = torch.tensor(indices[i:min(i+batch_size,num_examples)])
yield features[batch_indices], labels[batch_indices]
batch_size = 10
for X,y in data_iter(batch_size,features,labels):
print(X,"\n",y)
break
w = torch.normal(0,0.01,size = (2,1),requires_grad=True)
b = torch.zeros(1,requires_grad=True)
def linreg(X,w,b):
return torch.matmul(X,w) + b
def squared_loss(y_hat,y):
return (y-y_hat)**2 / 2
def sgd(params,lr,batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad/batch_size
param.grad.zero_()
lr = 0.01
num_epochs = 10
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X,y in data_iter(batch_size,features,labels):
l = loss(net(X,w,b),y)
l.sum().backward()
sgd([w,b],lr,batch_size)
with torch.no_grad():
train_l = loss(net(features,w,b),labels)
print(f"epoch: {epoch + 1},train_l: {train_l.mean()}")
.sum
函数的使用
X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdim=True), X.sum(1, keepdim=True)
X.sum(0, keepdim=False)
(tensor([[5., 7., 9.]]),
tensor([[ 6.],
[15.]]))
tensor([5., 7., 9.])
.sum
会按照轴方向求和,将其理解为某一箭头方向坍缩到其法平面上比较好
softmax 函数
$$softmax(\bold{X})_{ij} = \frac{exp(\bold{X_{ij}})}{\sum_{k}exp(\bold{X_{ik}})}$$$$softmax(\vec{x}) = \frac{e^{x_i}}{\sum_je^{x_j}}$$然后将其对矩阵的每个样本进行操作得到映射的拓展
PyTorch的高级检索
例子:
y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y_hat[[0, 1], y]
tensor([0.1000, 0.5000])
对于$m$个样本的$k$类分类问题,
第二行的y_hat
每个位置的$k$个结果,第$j$个位置的值表示其对应结果的概率
而第三行的[0,1]
表示选中y_hat
的0、1行,
然后y
进列的选择:它选择y的第0列的值对应的y_hat
的第0列元素,也就是0.1;然后选择y的第1列的值对应的y_hat
的第2列元素,也就是0.5
应用
def cross_entropy(y_hat, y):
return - torch.log(y_hat[range(len(y_hat)), y])
(返回的是同y尺寸的$\textit{cross entropy}$向量)
这里用了range(len(y_hat))
来遍历所有行(所有样本);注意不能用[0,len(y(hat)-1)]
代替,后者得到的只有首位两行而不是整个区间
对.backward()
函数的初步理解
-
其目标是计算loss对params的梯度
-
.backward()
方法会从计算图的末端(一般而言是loss)开始反向传播计算每个参数的梯度 -
其要求调用的张量是标量,因此是用如
l.mean().backward()
或l.sum().backward()
,而不是直接l.backward()
对updater.step()
(本质上是optimizer的step)的初步理解
其使用需要一些前置要求:
- 先调用
.backward()
求出梯度 - 先正确初始化optimizer(如正确传入待优化的参数)
- 参数启用梯度
其效果是根据梯度更新对应参数
化简:torch内置初始化策略
def init_weights(m):
if type(m) == torch.nn.Linear:
torch.nn.init.normal_(m.weight,std = 0.01)
net.apply(init_weights)
- 这里
m
的类型是module
,也就是torch.nn.Module
的子类(如nn.Linear
、nn.Conv2d
) - 对比:
torch.nn.init.normal
、torch.nn.init.normal
、torch.normal
- 前二者区别为原地修改与返回张量
- 语法:
torch.nn.init.normal_(tensor, mean=0.0, std=1.0)
- 第三者专门用来生成新张量,有更广的应用范围。而前两者专注于网络架构的参数初始化(代码习惯与api复杂度不同)
优化softmax的计算
数值稳定性存在问题:$exp(x_i)$值过大导致储存为inf
(上溢)
初次处理思路:
$$\hat{y_j} = \frac{exp(x_j-max(x_k))\cdot exp(max(x_k))}{\sum_{k}{exp(x_k-max(x_k))\cdot exp(max(x_k))}} = \frac{exp(x_j-max(x_k))}{\sum_{k}{exp(x_k-max(x_k))}}$$但这仍会存在问题:有些$x_i-max(x_k)$太小导致$exp(x_i-max(x_k))$接近零后被直接舍位为$0$(下溢)(并进一步导致$log(\hat{y_i}) = \text{-inf}$)
$$log(\hat{y_i}) = log(exp(\ldots))-log(\sum exp(\ldots))$$$$log(\hat{y_i})= x_i - max(x_k) - log(\sum_{k}exp(x_i-max(x_k)))$$这在一定程度上与torch用的算法相接近。而cross entropy可以由$L = \sum{y_i log(\hat{y_i})}$的基本形得到
说些什么吧!