torch函数学习

这篇博客介绍了PyTorch中张量的基本操作,包括按布尔mask或数值索引取值、按元素运算、广播机制以及维度判断。还详细讲解了如何初始化张量、计算梯度以及使用`backward()`和`zero_()`函数进行梯度下降。此外,讨论了在梯度震荡时如何调整学习率以避免发散问题。最后,通过线性回归模型展示了训练过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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更大,一直恶性循环导致发散结果。

解决
  1. 不用sdg了
  2. 调整步长,当梯度下降时,如果出现震荡现象就是步长不合适,调小学习率lr,让loss刚开始很小,就可以防止发散。实验表明这种方法可以解决这一问题:
    调整学习率为0.003
    在这里插入图片描述
    调整为0.0003
    在这里插入图片描述
    动态调整,逐步提升学习率
    在这里插入图片描述
    在这里插入图片描述
    动态调整,逐步降低学习率
    在这里插入图片描述
    在这里插入图片描述
    虽然此时loss为4,4左右但w已经收敛到真值
    在这里插入图片描述
    可以看到当,样本噪声变大,(0-10)的正太分布要比(0-1)的样本的绝对噪声的幅值要大很多,收敛的loss也没有(0-1)大。
启发

1.当问题发散时,可以调整学习率,说明初始l太大,sgd方法虽然收敛快,但需要初始loss不要太大(初值要好)。
2.问题的噪声越大,越复杂,收敛时(最小值)的能量就越大。

神经网络训练(pytorch)

要点:

  1. 前向传播(包括loss和net)
  2. 求梯度(自动)
  3. 优化方法(自动)
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}')
### PyTorch 损失函数的使用方法与实现方式 #### 一、PyTorch 中常见的损失函数及其用途 PyTorch 提供了多种内置的损失函数,用于解决不同的机器学习问题。以下是几种常用的损失函数: 1. **NLLLoss**: `torch.nn.NLLLoss` 是一种负对数似然损失函数,适用于分类任务中的概率分布计算。为了配合该函数,模型输出通常会经过 `LogSoftmax` 转换为对数概率后再传入此函数[^1]。 2. **CrossEntropyLoss**: 这是一种集成化的损失函数,内部实现了 Softmax NLLLoss 的功能,因此可以直接接受未经处理的原始分数作为输入[^4]。其定义如下: ```python import torch import torch.nn as nn # 定义 CrossEntropyLoss 函数 cross_entropy_loss = nn.CrossEntropyLoss() # 假设预测值真实标签分别为 predictions labels predictions = torch.randn(3, 4) # 批量大小为3,类别数为4 labels = torch.tensor([1, 2, 3]) # 对应的真实标签 # 计算交叉熵损失 loss = cross_entropy_loss(predictions, labels) print(loss) ``` 3. **MSELoss**: 平方误差损失函数 (`Mean Squared Error Loss`) 主要应用于回归问题中。可以通过调整参数 `reduction` 来控制返回的是均值还是逐项损失[^3]: ```python import torch import torch.nn as nn # 定义 MSE 损失函数 mse_loss_fn = nn.MSELoss(reduction='mean') # 输入张量目标张量 input_tensor = torch.tensor([2.0, 4.0, 6.0]) target_tensor = torch.tensor([1.0, 3.0, 5.0]) # 计算 MSE 损失 loss = mse_loss_fn(input_tensor, target_tensor) print(loss) ``` 4. **自定义损失函数**: 如果现有的损失函数无法满足需求,可以手动编写新的损失函数。例如,在某些特定场景下可能需要设计特殊的对比损失或排序损失[^5]。 #### 二、具体实现细节分析 - **关于 NLLLoss 的实现**: 当前版本支持通过指定维度来执行 LogSoftmax 变换,并随后利用 NLLLoss 计算最终的结果。需要注意的是,默认情况下会对所有样本求平均;如果希望保留每条记录单独的损失值,则需设置参数 `reduction='none'`[^2]。 - **针对多类别的交叉熵优化**: 在实际项目里推荐优先选用 `nn.CrossEntropyLoss()` 替代组合形式(即分别调用 softmax nllloss),因为前者不仅简化了代码逻辑而且性能更优[^4]。 - **灵活运用 reduction 参数**: 不同的任务背景决定了是否应该采用整体评估指标或者局部反馈机制。比如在训练初期阶段可能会倾向于观察个体表现以便及时发现问题所在;而后期则更加关注全局效果提升效率。 #### 、总结说明 综上所述,合理选择并正确配置适合当前应用场景下的损失函数至关重要。无论是标准库提供的通用解决方案亦或是定制开发专属方案都需要充分理解各自特点以及适用范围才能达到最佳实践目的。 ```python import torch import torch.nn as nn # 自定义一个简单的线性网络结构 class SimpleNet(nn.Module): def __init__(self): super(SimpleNet, self).__init__() self.fc = nn.Linear(10, 3) def forward(self, x): return self.fc(x) model = SimpleNet() criterion = nn.CrossEntropyLoss() # 或者其他类型的损失函数 optimizer = torch.optim.SGD(model.parameters(), lr=0.01) inputs = torch.randn(5, 10) # 构造一批次随机数据 targets = torch.randint(0, 3, (5,)) # 随机生成对应的目标值 outputs = model(inputs) loss = criterion(outputs, targets) print(f"初始损失: {loss.item()}") optimizer.zero_grad() loss.backward() optimizer.step() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值