Pytorch使用(二)

前言

在用Pytorch包编写人工智能代码时最常用的操作就是用梯度去更新参数,也就是
w = w − l r ∗ w . g r a d w=w-lr*w.grad w=wlrw.grad

这个赋值操作很有可能会导致变量的id发生变化,进而给Pytorch中的loss函数的计算带来困惑。还好在Pytorch(一)中我们已经了解到了tensor变量的绝缘与不绝缘,所以接下来用代码来演示变量id发生变化时的处理办法以及如何保持参数id不变。

用Pytorch实现线性回归

假设我们有生成的线性回归训练数据,从数据中学习出最佳的参数,这就是线性回归。接下来从tensor变量的id变化与不变化两种角度给出对应的Pytorch代码实现指导。

参数id发生变化

#导入模块
import torch
from matplotlib import pyplot as plt
torch.set_default_tensor_type(torch.cuda.FloatTensor)
#随机数生成种子
torch.manual_seed(1)
#生成训练用数据
w_0=torch.randn(2)
b_0=torch.randn(1)
print(f"w_0,w_0.dtype,w_0.shape,b_0,b_0.dtype,b_0.shape:{w_0,w_0.dtype,w_0.shape,b_0,b_0.dtype,b_0.shape}")
print("-----------------------------------------------------------")
x_0=torch.randn(10,2)
y_0=torch.mv(x_0,w_0)+b_0
print(f"x_0,x_0.dtype,x_0.shape,y_0.dtype,y_0,y_0.shape:{x_0,x_0.dtype,x_0.shape,y_0.dtype,y_0,y_0.shape}")
print("-----------------------------------------------------------")
#设置模型:步长、待训练参数等
lr=torch.tensor(0.01)
w=torch.randn(2)
w.requires_grad_(True)
b=torch.randn(1)
b.requires_grad_(True)
print(f"w,b:{w,b}")
print("-----------------------------------------------------------")
#先训练十轮,看看效果,后面一千轮再逐渐达到精度要求。
for i in range(10):
    loss=((torch.mv(x_0,w)+b-y_0)**2).sum()
    print(f"loss:{loss}")
    loss.backward()
    print(f"w.grad,b.grad:{w.grad,b.grad}")
    print(id(w),id(b),id(w.data),id(b.data),id(w.detach()),id(b.detach()))
    w=w.data-lr*w.grad.data
    b=b.data-lr*b.grad.data
    print(f"w,b:{w},{b}")
    w.requires_grad_(True)
    b.requires_grad_(True)
print("-----------------------------------------------------------")
#这一千轮静默输出,最终结果单独查看
for i in range(1000):
    loss=((torch.mv(x_0,w)+b-y_0)**2).sum()
    loss.backward()
    w=w.data-lr*w.grad.data
    b=b.data-lr*b.grad.data
    w.requires_grad_(True)
    b.requires_grad_(True)
print(f"w,b,w_0,b_0:{w,b,w_0,b_0}")
print(f"loss:{loss}")

在上述代码中tensor变量的id会发生变化,所以我们重新计算loss的表达式的值。同时由于参数变量每次迭代中都是一个新的tensor变量,所以需要手动再加上求导标志requires_grad

参数id不变化

#导入模块
import torch
from matplotlib import pyplot as plt
torch.set_default_tensor_type(torch.cuda.FloatTensor)
#随机数生成种子
torch.manual_seed(1)
#生成训练用数据
w_0=torch.randn(2)
b_0=torch.randn(1)
print(f"w_0,w_0.dtype,w_0.shape,b_0,b_0.dtype,b_0.shape:{w_0,w_0.dtype,w_0.shape,b_0,b_0.dtype,b_0.shape}")
print("-----------------------------------------------------------")
x_0=torch.randn(10,2)
y_0=torch.mv(x_0,w_0)+b_0
print(f"x_0,x_0.dtype,x_0.shape,y_0.dtype,y_0,y_0.shape:{x_0,x_0.dtype,x_0.shape,y_0.dtype,y_0,y_0.shape}")
print("-----------------------------------------------------------")
#设置模型:步长、待训练参数等
lr=torch.tensor(0.01)
w=torch.randn(2)
w.requires_grad_(True)
b=torch.randn(1)
b.requires_grad_(True)
print(f"w,b:{w,b}")
print("-----------------------------------------------------------")
for i in range(10):
    loss=((torch.mv(x_0,w)+b-y_0)**2).sum()
    print(f"loss:{loss}")
    loss.backward()
    print(f"w.grad,b.grad:{w.grad,b.grad}")
    print(id(w),id(b),id(w.data),id(b.data),id(w.detach()),id(b.detach()))
    w.data=w.data-lr*w.grad.detach()
    b.data=b.data-lr*b.grad.detach()
    #也可以使用
    #w.data=w-lr*w.grad
    #b.data=b-lr*b.grad
    #也可以使用
    #w.data=w.data-lr*w.grad
    #b.data=b.data-lr*b.grad
    #也可以使用
    #w.data=w.clone()-lr*w.grad.clone()
    #b.data=b.clone()-lr*b.grad.clone()
    print(f"w,b:{w},{b}")
    w.grad.data.zero_()
    b.grad.data.zero_()
print("-----------------------------------------------------------")
for i in range(1000):
    loss=((torch.mv(x_0,w)+b-y_0)**2).sum()
    loss.backward()
    w.data=w.data-lr*w.grad.data
    b.data=b.data-lr*b.grad.data
    #也可以使用
    #w.data=w-lr*w.grad
    #b.data=b-lr*b.grad
    #也可以使用
    #w.data=w.data-lr*w.grad
    #b.data=b.data-lr*b.grad
    #也可以使用
    #w.data=w.clone()-lr*w.grad.clone()
    #b.data=b.clone()-lr*b.grad.clone()
    w.grad.data.zero_()
    b.grad.data.zero_()
print(f"w,b:{w,b}")
print(f"loss:{loss}")

其实总结一下就是当通过.data属性更新参数时,参数id不变,此时需要手动将参数的梯度归零,避免累积。当通过直接赋值来更新参数时,参数id变化,此时需要对参数再设置一下需要梯度。至于等号右边的赋值表达式是什么倒是无关紧要,不管什么表达式,都是绝缘的。都不会产生映射现象。

用Pytorch实现感知机

#导入模块
import torch
from matplotlib import pyplot as plt
torch.set_default_tensor_type(torch.cuda.FloatTensor)
#生成数据
w_0=torch.randn(3)
b_0=torch.randn(1)
x_0=torch.randn(10,3)
y_0=torch.where(torch.mv(x_0,w_0)+b_0>0,1,-1)
print(f"w_0,b_0,x_0,y_0:{w_0,b_0,x_0,y_0}")
#使用感知机进行分类。
lr=torch.tensor(0.01)
w=torch.randn(3)
b=torch.randn(1)
w.requires_grad_(True)
b.requires_grad_(True)
loss=torch.exp(-(torch.mv(x_0,w)+b)*y_0).sum()
while (loss>0.1):
    loss=torch.exp(-(torch.mv(x_0,w)+b)*y_0).sum()
    loss.backward()
    w.data=w-lr*w.grad
    b.data=b-lr*b.grad
    w.grad.data.zero_()
    b.grad.data.zero_()
    print(f"w,b,loss:{w,b,loss}")
print(f"w_0,b_0,w,b,loss:{w_0,b_0,w,b,loss}")
print(f"y_0,(torch.mv(x_0,w)+b):{y_0,torch.mv(x_0,w)+b}")

其实这个loss函数设置的有问题,并且本程序中没有考虑参数向量的长度问题,因为当w,b确定以后,2w,2b也一定适合原问题。再说回loss函数的问题,假设所有10个样本都分类正确,也就是
− ( w ∗ x 0 + b ) ∗ y 0 ≤ 0 -(w*x_0+b)*y_0\le0 (wx0+b)y00

此时loss函数的值一定小于10,而我设置的停止目标是0.1,呜呜呜,我是大恶人,刚开始我还设置的是0.0001,我说我电脑怎么呜呜叫呢,那不是风扇的声音,是它在哭😭,本表情来自ubuntu系统自带的字符应用中。还有更多。所以loss只要小于10就可以了。而我加大了很多工作量。其实10也不是很够用,因为可能会有边沿情况,能小尽小最好。
那么问题来了,损失函数写多少能够确保无错误分类的情况存在呢?下面的只是粗略估计,并非严格论证。我们知道错误分类是由于有些数据对 ( x , y ) (x,y) (x,y)所对应的损失过于小,所以才给那些错误分类的点留足了错误空间,而我们只要把所有的错误空间考虑在内一定不会误分类。假设其他带来的损失都足够小,接近为0,不妨设其他九个损失都是0,那剩余的一个点容许的误分类损失为1。
通过分析发现当误差趋于稳定时,所得的分类面基本就是正确的了,所以我修改了代码。

#导入模块
import torch
from matplotlib import pyplot as plt
torch.set_default_tensor_type(torch.cuda.FloatTensor)
#生成数据
w_0=torch.randn(3)
b_0=torch.randn(1)
x_0=torch.randn(10,3)
y_0=torch.where(torch.mv(x_0,w_0)+b_0>0,1,-1)
print(f"w_0,b_0,x_0,y_0:{w_0,b_0,x_0,y_0}")
#使用感知机进行分类。
lr=torch.tensor(0.01)
w=torch.randn(3)
b=torch.randn(1)
w.requires_grad_(True)
b.requires_grad_(True)
loss=torch.exp(-(torch.mv(x_0,w)+b)/torch.sqrt((w*w).sum())*y_0).sum()
temp=loss.clone()+1
while True:
    loss=torch.exp(-((torch.mv(x_0,w)+b)/torch.sqrt((w*w).sum()))*y_0).sum()
    loss.backward()
    if(temp-loss.clone()<0.001):
        break
    temp=loss.clone()
    w.data=w-lr*w.grad
    b.data=b-lr*b.grad
    w.grad.data.zero_()
    b.grad.data.zero_()
    print(f"w,b,loss:{w,b,loss}")
print(f"w_0,b_0,w,b,loss:{w_0,b_0,w,b,loss}")
print(f"y_0,(torch.mv(x_0,w)+b):{y_0,torch.mv(x_0,w)+b}")
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值