import torch
x1 = torch.tensor([1,2]).cuda()#x1就在gpu上了
w1 = torch.tensor([2,0](2,0)).cuda()#w1就在gpu上了
x2 = torch.matmul(w1,x1)#等价于w1 @ x1.这个也是生成在gpu上.注意,@才是线性代数意义上的矩阵乘法.
x1 = torch.tensor([2.0])#输入是一维的list就是一维的tensor.当然,实际上输入的类型远远不止list.注意,要改成浮点数才能进行计算.
w1 = torch.tensor([3.0],requires_grad = True)#表示这是需要求梯度的变量.
x2 = x1 ** w1
#x2对w1的梯度
x2.backward()
#dx2 / dw1.注意,dx2就应该是上面callbackward的位置.dw1就是想要求偏导的变量.
dx2_dw1 = w1.grad
#打印出dx2_dw1应该就是x2对w1求偏导的值.
如果把神经网络弄的更深一点.
x1 = torch.tensor([2.0])
w1 = torch.tensor([3.0], requires_grad = True)
w2 = torch.tensor([4.0]),requires_grad = True)
w3 = torch.tensor([5.0]),requires_grad = True)
target = torch.tensor([1.0])
x2 = w1 * x1
x3 = torch.functional.relu(w2 * x2)#添加relu激活函数的方法是这样的,sigmoid激活函数的方法是torch.sigmoid(w2 * x2)
x4 = w3 * x3
loss = (x4 - target) ** 2
loss.backward()
print(w1.grad)#dloss / dw1
print(w2.grad)#dloss / dw2
print(w3.grad)#dloss / dw3
x1 = torch.tensor([2.0])
w1 = torch.tensor([3.0], requires_grad = True)
w2 = torch.tensor([4.0]),requires_grad = True)
w3 = torch.tensor([5.0]),requires_grad = True)
target = torch.tensor([1.0])
x2 = w1 * x1
x3 = torch.functional.relu(w2 * x2)#添加relu激活函数的方法是这样的,sigmoid激活函数的方法是torch.sigmoid(w2 * x2)
x4 = w3 * x3
loss = (x4 - target) ** 2
loss.backward()
dloss_dw1 = w1.grad
dloss_dw2 = w2.grad
dloss_dw3 = w3.grad
learning_rate = 0.01
w1.data -= learning_rate * dloss_dw1
w2.data -= learning_rate * dloss_dw2
w3.data -= learning_rate * dloss_dw3
为什么不能直接用 w1? 当你创建一个张量并设置 requires_grad=True(例如 w1),PyTorch 的 autograd 引擎就会开始“监视”它。任何涉及到 w1 的计算(如 x2 = w1 * x1)都会被记录下来,形成一个计算图。这个图是计算梯度的基础。
更新也是一种操作:w1 -= learning_rate * dloss_dw1 这个更新步骤本身也是一个计算操作。
如果你直接对 w1 执行这个更新操作,autograd 引擎会尝试将这个操作也加入到计算图中。这意味着,你的权重 w1 不再是计算图的“叶子节点”,它本身也依赖于之前的梯度,这会把整个梯度计算体系搞乱。在下一次调用 loss.backward() 时,PyTorch 不知道该如何正确地回溯,因为用于前向传播的权重本身就有了梯度历史。这通常会导致运行时错误或意想不到的梯度结果。
方案一:旧方法 w1.data (不推荐使用) w1.data 是一个历史遗留的用法。它的作用是直接获取 w1 底层的数据张量,但绕过了 autograd 的跟踪系统。
方案二:现代最佳实践 with torch.no_grad(): (强烈推荐) with torch.no_grad(): 创建了一个上下文环境,告诉 PyTorch 在这个代码块内部,临时禁用所有梯度计算和跟踪。 因此,w1 -= learning_rate * dloss_dw1 就变成了一个纯粹的、不会被跟踪的数值更新操作。 或者说:只要不属于”model”的部分(这肯定不属于模型了,因为这里模型肯定是不会根据学习率来定义并且随着时间变化而变化的) 上面的代码还可以变得更加简洁:
x1 = torch.tensor([2.0])
w1 = torch.tensor([3.0], requires_grad = True)
w2 = torch.tensor([4.0]),requires_grad = True)
w3 = torch.tensor([5.0]),requires_grad = True)
target = torch.tensor([1.0])
parameters = [w1,w2,w3]#这里就是直接合成一个列表了
x2 = w1 * x1
x3 = torch.functional.relu(w2 * x2)#添加relu激活函数的方法是这样的,sigmoid激活函数的方法是torch.sigmoid(w2 * x2)
x4 = w3 * x3
loss = (x4 - target) ** 2
loss.backward()
dloss_dw1 = w1.grad
dloss_dw2 = w2.grad
dloss_dw3 = w3.grad
learning_rate = 0.01
for p in parameters:
p.data -= 0.01 * p.grad
可以设置动态的学习率:(当然,这种设置是不对的)
x1 = torch.tensor([2.0])
w1 = torch.tensor([3.0], requires_grad = True)
w2 = torch.tensor([4.0]),requires_grad = True)
w3 = torch.tensor([5.0]),requires_grad = True)
target = torch.tensor([1.0])
parameters = [w1,w2,w3]#这里就是直接合成一个列表了
x2 = w1 * x1
x3 = torch.functional.relu(w2 * x2)#添加relu激活函数的方法是这样的,sigmoid激活函数的方法是torch.sigmoid(w2 * x2)
x4 = w3 * x3
loss = (x4 - target) ** 2
loss.backward()
dloss_dw1 = w1.grad
dloss_dw2 = w2.grad
dloss_dw3 = w3.grad
learning_rate = 0.01
for p in parameters:
learning_rate = p.grad**2
p.data -= learning_rate * p.grad
可以使用”优化器”进行封装:
```python
x1 = torch.tensor([2.0])
w1 = torch.tensor([3.0], requires_grad = True)
w2 = torch.tensor([4.0]),requires_grad = True)
w3 = torch.tensor([5.0]),requires_grad = True)
target = torch.tensor([1.0])
optimizer = torch.optim.SGD([w1,w2,w3],lr=0.01)#优化器相当于是封装了上面的for p inparameters
x2 = w1 * x1
x3 = torch.functional.relu(w2 * x2)#添加relu激活函数的方法是这样的,sigmoid激活函数的方法是torch.sigmoid(w2 * x2)
x4 = w3 * x3
loss = (x4 - target) ** 2
loss.backward()
dloss_dw1 = w1.grad
dloss_dw2 = w2.grad
dloss_dw3 = w3.grad
optimizer.step()#这里就替代了原本的"一次梯度下降"
当然,除了SGD,还有Adam等. 我们的计算还可能有很多次循环,也就是多次的下降.