pytorch学习笔记五

pytroch学习笔记五————利用pytorch工具实现之前的简单的模型

上一次我们基本上算手敲了一个可以训练的线性模型出来,了解了学习的基本过程,很明显这略有些许麻烦,如果每个学习过程都需要我们去手动的求导,手动的调参则需要非常大的工作量,pytorch的部分功能可以帮我们省去这些工作,比如说自动求导

自动求导

若要自动求导,则在required_grad属性设置为True即可,此时pytorch将会跟踪这个张量,任何对此张量进行操作都会对其的导数进行计算,并存入grad属性里面,其本质操作相当于一个链式操作,即对每个计算操作产生的导数过程都储存在相应的grad属性下
如下:

params=torch.tensor([1.0,0.0],requires_grad=True)
t_p=model(t_u,*params)
loss=loss_fn(t_p,t_c)
loss.backward()
print(params.grad)

backward()操作即是对loss进行反向遍历,计算出所有对应参数的梯度值
需要注意的是,backward()操作会将计算的结果与grad属性的值进行累加,所以在开始运算时需要对其进行显式的清零
利用grad.zero_()方法可以对其进行清零
所以自动训练的从头到尾的代码如下:

import torch
t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]
t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]
t_c = torch.tensor(t_c)
t_u = torch.tensor(t_u)
t_un=t_u*0.1
def model(t_u,w,b):
    return w*t_u+b
def loss_fn(t_p,t_c):
    squared_diffs=(t_p-t_c)**2
    return squared_diffs.mean()
def training_loop(n_epochs,learning_rate,params,t_u,t_c):
    for epoch in range(1,n_epochs+1):
        if params.grad is not None:
            params.grad.zero_()
        t_p=model(t_u,*params)
        loss=loss_fn(t_p,t_c)
        loss.backward()
        with torch.no_grad():
            params-=learning_rate*params.grad
        if(epoch%500==0):
            print('Epoch %d,Loss %f'%(epoch,float(loss)))
    return params
params=training_loop(n_epochs=5000,learning_rate=1e-2,
    params=torch.tensor([1.0,0.0],requires_grad=True),
    t_u=t_un,t_c=t_c)
print(params)

有时候在进行操作的时候我们不像让梯度产生更新,这时候就用with torch.no_grad():更新封装在非梯度的上下文中,这意味着在with模块,自动求导将不会起作用

优化器

上个代码范例中,我们手动敲了梯度下降的代码,就是with里的代码,这种批量梯度下降的优化方法的代码似乎挺简单,但是有的优化方法的代码就比较复杂,pytorch对其进行了封装,并统称为优化器,存在torch.optim模块当中
创建一个优化器的方法如下:

params=torch.tensor([1.0,0.0],requires_grad=True)
learning_rate=1e-2
optimizer=optim.SGD([params],lr=learning_rate)

优化器里的参数params是对张量对象的引用,所以对优化器里的参数梯度清零即是对整个参数梯度清零,对应的方法是optimizer.zero_grad();此外,让参数按照优化器里的方法进行更新的方法则是optimizer.step(),用这两个方法,则就可以大大简化上面的代码,简化完成后的代码如下:

import torch
import torch.optim as optim
t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]
t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]
t_c = torch.tensor(t_c)
t_u = torch.tensor(t_u)
t_un=t_u*0.1
def model(t_u,w,b):
    return w*t_u+b
def loss_fn(t_p,t_c):
    squared_diffs=(t_p-t_c)**2
    return squared_diffs.mean()
params=torch.tensor([1.0,0.0],requires_grad=True)
learning_rate=1e-2
optimizer=optim.SGD([params],lr=learning_rate)
def training_loop(n_epochs,optimizer,params,t_u,t_c):
    for epoch in range(1,n_epochs+1):
        t_p=model(t_u,*params)
        loss=loss_fn(t_p,t_c)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if(epoch%500==0):
            print('Epoch %d,Loss %f'%(epoch,float(loss)))
    return params

params=training_loop(n_epochs=5000,optimizer=optimizer,
    params=params,
    t_u=t_un,t_c=t_c)
print(params)

这里我们用的是SGD,表示随机梯度下降方法,当然我们也可以尝试其他的优化器,比如说Adam,对于Adam优化器,不介绍过多的细节,它的学习率是自适应设置的,此外,它对参数放缩不太敏感,以至于我们可以使用原始的输入t_u,甚至可以将学习率提高到1e-1,代码如下:

import torch
import torch.optim as optim
t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]
t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]
t_c = torch.tensor(t_c)
t_u = torch.tensor(t_u)
t_un=t_u*0.1
def model(t_u,w,b):
    return w*t_u+b
def loss_fn(t_p,t_c):
    squared_diffs=(t_p-t_c)**2
    return squared_diffs.mean()
params=torch.tensor([1.0,0.0],requires_grad=True)
learning_rate=1e-1
optimizer=optim.Adam([params],lr=learning_rate)
def training_loop(n_epochs,optimizer,params,t_u,t_c):
    for epoch in range(1,n_epochs+1):
        t_p=model(t_u,*params)
        loss=loss_fn(t_p,t_c)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if(epoch%500==0):
            print('Epoch %d,Loss %f'%(epoch,float(loss)))
    return params

params=training_loop(n_epochs=5000,optimizer=optimizer,
    params=params,
    t_u=t_u,t_c=t_c)
print(params)

可以看出优化器的设计使得我们改变训练策略将会变得异常容易

训练验证和过拟合

课本上我们可以得到数据其实分为训练集和验证集,这里就不对其进行赘述了,这里就讲述一下分割数据集的方法

n_sample=t_u.shape[0]
n_val=int(0.2*n_sample)
shuffled_indices=torch.randperm(n_samples)
train_indices=shuffled_indices[:-n_val]
val_indices=shuffled_indices[-n_val:]

train_t_u=t_u[train_indices]
train_t_c=t_c[train_indices]
val_t_u=t_u[val_indices]
val_t_c=t_c[val_indices]

train_t_un=0.1*train_t_u
val_t_un=0.1*val_t_u

torch.randperm(n):将0~n-1(包括0和n-1)随机打乱后获得的数字序列,函数名是random permutation缩写
因此我们有了训练集和测试集,即可判断是否过拟合(训练集的loss小于测试集的loss),修改过后的代码如下:

def training_loop(n_epochs, optimizer, params, train_t_u, val_t_u,
                  train_t_c, val_t_c):
    for epoch in range(1, n_epochs + 1):
        train_t_p = model(train_t_u, *params)
        train_loss = loss_fn(train_t_p, train_t_c)
                             
        val_t_p = model(val_t_u, *params)
        val_loss = loss_fn(val_t_p, val_t_c)
        
        optimizer.zero_grad()
        train_loss.backward()
        optimizer.step()

        if epoch <= 3 or epoch % 500 == 0:
            print(f"Epoch {epoch}, Training loss {train_loss.item():.4f},"
                  f" Validation loss {val_loss.item():.4f}")

    return params
params = torch.tensor([1.0, 0.0], requires_grad=True)
learning_rate = 1e-2
optimizer = optim.SGD([params], lr=learning_rate)

training_loop(
    n_epochs = 3000, 
    optimizer = optimizer,
    params = params,
    train_t_u = train_t_un,
    val_t_u = val_t_un,
    train_t_c = train_t_c,
    val_t_c = val_t_c)

正如教科书上那班,我们希望只在训练集上对参数进行训练而不是在测试集上,上述代码虽然不会在测试集上对参数进行训练,但是所对应的grad属性毫无疑问会依旧进行计算,我们想人为的关闭这个功能以释放额外的开销,就要用到之前用过的上下文管理器torch.no_grad()进行处理,代码如下:

def training_loop(n_epochs, optimizer, params, train_t_u, val_t_u,
                  train_t_c, val_t_c):
    for epoch in range(1, n_epochs + 1):
        train_t_p = model(train_t_u, *params)
        train_loss = loss_fn(train_t_p, train_t_c)

        with torch.no_grad():
            val_t_p = model(val_t_u, *params)
            val_loss = loss_fn(val_t_p, val_t_c)
            assert val_loss.requires_grad == False
            
        optimizer.zero_grad()
        train_loss.backward()
        optimizer.step()

还有set_grad_enabled()方法,可以使用一个布尔表达式设定代码运行时启用或禁用求导的条件,典型的条件是我们是在训练模式下运行还是推理条件下运行,其使用例子如下:

def calc_forward(t_u, t_c, is_train):
    with torch.set_grad_enabled(is_train):
        t_p = model(t_u, *params)
        loss = loss_fn(t_p, t_c)
    return loss
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值