torch函数学习
mask取值
如果取值的数组类型是bool则按照mask取值,如果是long,则按照数组中的标号取值
注意,这里的mask取值会减少数组的纬度,只取mask数组中为1的数。
如果mask是bool,则它与masked_select函数相同
如果不行降低原始组维数,并修改对应为true的元素时,可用x.masked_scatter_(mask,re)
运算
最简单且最有用的操作是按元素(elementwise)运算。 它们将标准标量运算符应用于数组的每个元素。 对于将两个数组作为输入的函数,按元素运算将二元运算符应用于两个数组中的每对位置对应的元素
c =a + b 是数组中每个元素位置运算,如果纬度不同,可以结合广播运算
怎么判断维数
有几个’['就由几维
初始化tensor
x = torch.arange(12)
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]])
len 和 size的区别
len()是最近一维的大小
x.size是整个张量的维数
torch.normal
生成随机数
生成张量的数组
维数比生成维数多1
但是在用[]访问元素时
[0]返回的是第0个张量
而不是整个张量数组。
索引和切片
切片维数的判断
features[:] = feature
feature[1:2]
feature[:1],feature[1]是有区别的 feature[:1]是第一行的张量数组[[x]],而feature[1]是第一行的张量[x]
feature[:1]隐含的是[0,1)是一段,feature的维数等于feature[:1]的维数。feature[1]是feature中的第1个tensor,被拎出来的,因此feature[1]的维数是feature的维数减1.
总结来讲,只要见到":"维数不减,见不到“:”维数减1
如:
X[0:1]是取0这一段,X[0]是取0这个张量
再如:取最后一列
再总结
如何有效判断数据的维数变化:
带":“号是在原始纬度上取维数,不带”:"是减掉该维数
如上面的例子
X是[3,2,2],
X[0:1,:,1:2] 后 变成 [1,2,1]
X[0,:,1] 后 变成 [2]
"::"是什么
双::表示的是跳n取一个数:
>>> range(10)[::2]
[0, 2, 4, 6, 8]
跳2取数
seq[start : end : step]
>>> range(100)[5:18:2]
[5, 7, 9, 11, 13, 15, 17]
在5到8之间跳2取数。
特殊例子 负数
[-3:-1]是最后一个到倒数第三个前闭后开
[::-1] 正1是从头到尾跳1取值,-1是从尾到头跳1取值。
"…"符号
有时候tensor张量的维数较大,我们只想动最后几维,一直使用::占空间,因此用省略号代替之前所有维数都保留。
自动求导
在获得梯度前要调用backward()
y.backward()
x.grad
grad.zero_()清理之前累积计算的梯度
# 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
梯度是累加的,如果不清0则出现错误
连续backward让梯度累加
计算新梯度前应grad.zero_()
grad can be implicitly created only for scalar outputs
向量的对向量的导数是矩阵,而pytorch仅支持计算标量的导数,通常用sum()将向量转化为标量再求导
分离计算
我们不行求某部分的导数时要对它进行标记
我们不想计算y部分的导数,因此使用detach()
梯度震荡时的处理
代码
import torch
import random
from d2l import torch as d2l
# 生成数据
def synthetic_data(w,b,num_examples):
''' 生成Y=Xw+b + 噪声'''
X = torch.normal(0,10,(num_examples,len(w)))
Y = torch.matmul(X,w)+b
Y += torch.normal(0,0.01,Y.shape)
return X,Y.reshape(-1,1)
true_w = torch.tensor([2,3.4])
true_b = 4.2
features,labels = synthetic_data(true_w,true_b,num_examples=1000)
print('features',features[0],"\nlable:",labels[0])
a = ([1,2])
b = ([1,2])
d2l.set_figsize()
d2l.plt.scatter(a,b,1)
d2l.set_figsize()
d2l.plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1)
# 数据输入
#print(features[:,1].detach().numpy().size)
print(len(features))
#a = torch.normal(0,10,(1,2))
a = features[:]
print(a)
print(a.size())
print(len(a))
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
# 定义lossFuction
def squared_loss(y_hat,y):
return (y_hat-y.reshape(y_hat.shape))**2/2
# 梯度下降求解
def sgd(params, lr, batch_size): #@save
"""小批量随机梯度下降"""
with torch.no_grad():#有什么用
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()##
def sgd1(params,lr,batch_size):
with torch.no_grad():
for param in params:
param = param - lr*param.grad/batch_size
param.grad.zero_()
# 训练过程
lr = 0.003
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(0,num_epochs):
# batch size
for X,y in data_iter(batch_size,features,labels):
# 建立映射
#print(X.shape)
l = loss(net(X,w,b),y)
# 反向传播
l.sum().backward()#backward() 之后 grad才会计算
#print(w)
#print(l.sum())
# 参数更新
sgd([w,b],lr,batch_size)
with torch.no_grad():
#lr /= 2
#print(w)
train_l = loss(net(features,w,b),labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
# 调参
问题
w的初值是0,当我生成数据的样本在0-1( X = torch.normal(0,1,(num_examples,len(w))))时 ,训练正常:
但当生成数据样本在0-10时,此时出现不收敛情况:
打印出批量的loss时发现:
误差很大且在震荡
打印出批量的梯度时发现:
很大且在震荡
出现了发散的情况。
分析
sgd梯度下降的梯度来自:
d||L|| = L* x
可以看到梯度是loss L的倍数,如果初始迭代的L就比较大,导致梯度也比较大,偏离真值,使得下一次L更大,一直恶性循环导致发散结果。
解决
- 不用sdg了
- 调整步长,当梯度下降时,如果出现震荡现象就是步长不合适,调小学习率lr,让loss刚开始很小,就可以防止发散。实验表明这种方法可以解决这一问题:
调整学习率为0.003
调整为0.0003
动态调整,逐步提升学习率
动态调整,逐步降低学习率
虽然此时loss为4,4左右但w已经收敛到真值
可以看到当,样本噪声变大,(0-10)的正太分布要比(0-1)的样本的绝对噪声的幅值要大很多,收敛的loss也没有(0-1)大。
启发
1.当问题发散时,可以调整学习率,说明初始l太大,sgd方法虽然收敛快,但需要初始loss不要太大(初值要好)。
2.问题的噪声越大,越复杂,收敛时(最小值)的能量就越大。
神经网络训练(pytorch)
要点:
- 前向传播(包括loss和net)
- 求梯度(自动)
- 优化方法(自动)
num_epochs = 3
#过程:
# 计算Loss
# 更新参数
for epoch in range(num_epochs):
for X,y in data_iter:
l = loss(net(X),y)# 前向传播 (网络结构)-net,lossFunction
trainer.zero_grad()#
l.backward()# sum可以省掉 #后向传播
trainer.step()#更新梯度(优化方法)-优化方法
l = loss(net(features),labels)
print(f'epoch {epoch + 1}, loss: {l}')