Pytorch实现线性回归

【学习视频链接:https://www.bilibili.com/video/BV1Y7411d7Ys?p=5&t=3299】

1 构建数据集(Prepare dataset)

Pytorch中,计算图都是mini-batch形式,所有 X X X Y Y Y都应该为Tensor。

[ y p r e d ( 1 ) y p r e d ( 2 ) ⋮ y p r e d ( n ) ] = ω [ x ( 1 ) x ( 2 ) ⋮ x ( n ) ] + b ; X , Y , ω , b ∈ R \begin{bmatrix} y_{pred}^{(1)}\\ y_{pred}^{(2)}\\ \vdots \\ y_{pred}^{(n)} \end{bmatrix} = \omega \begin{bmatrix} x^{(1)}\\ x^{(2)}\\ \vdots \\ x^{(n)} \end{bmatrix}+ b ;X,Y,\omega,b \in \mathbb{R} ypred(1)ypred(2)ypred(n)=ωx(1)x(2)x(n)+bX,Y,ω,bR

假设 X X X Y Y Y n × 1 n \times 1 n×1维的矩阵,式中的 ω \omega ω b b b会被通过
如同numpy数组的广播将其扩展为 n × 1 n \times 1 n×1维的矩阵。 在这里 ω \omega ω b b b随着 X X X的长度变化而变化,广播的为原来的 ω \omega ω b b b的值,如 b = [ b , b , … , b ] n T b=[b,b,\dots,b]_n^T b=[b,b,,b]nT

[ L o s s ( 1 ) L o s s ( 2 ) ⋮ L o s s ( n ) ] = ( [ y ^ 1 y ^ 2 ⋮ y ^ n ] − [ y 1 y 2 ⋮ y n ] ) 2 \begin{bmatrix} Loss^{(1)}\\ Loss^{(2)}\\ \vdots \\ Loss^{(n)} \end{bmatrix} = \left( \begin{bmatrix} \hat y_{1}\\ \hat y_{2}\\ \vdots \\ \hat y_{n} \end{bmatrix} - \begin{bmatrix} y_{1}\\ y_{2}\\ \vdots \\ y_{n} \end{bmatrix}\right)^2 Loss(1)Loss(2)Loss(n)=y^1y^2y^ny1y2yn2

import torch
# Pytorch中的数据等均为Tensor变量,即矩阵
x_data = torch.Tensor([[1],[2],[3]]) # 相当于一个numpy.array
y_data = torch.Tensor([[2],[4],[6]]) 

2 设计模型(Design model using Class)

2.1 设计模型的流程及注意事项

之前训练是,我们是通过人工来求导数,根据函数的解析式一步步来求导数的值,然后通过链式法则来进行的计算。在Pytorch中,我们就不用分析怎么求导数,然后用链式法则等进行计算了,我们的重点的目标变为构建计算图,通过张量之间的计算,自动计算出梯度。

① 通过x与y的输入,从而确定出w和b的维度大小,从而确定出 y ^ \hat y y^,当然y与 y ^ \hat y y^的维度肯定是一样的。

② 通过设计的模型构建出计算图,其计算流程如下图所示。

③ x到loss为前馈;通过loss调用backward,计算出w和b,然后通过梯度下降法来更新w和b从而进行训练,此为反馈过程。

2.2 Pytorch设计模型

① 将模型定义为一个类(class)

② 定义的模型都要继承torch.nn.Module

③ 构建的函数至少需要两个函数,第一个为def init(self):【称为构造函数,用于初始化对象所要调用的函数】 ,第二个为def forward(self,x)【用于在前馈计算所要执行的计算】。之所以没有反馈的函数,是因为我们调用的Module会自动根据里面的计算图来帮助你自动实现反馈。

④ 若我们要用的模块在Pytorch中没有定义,如果你需要的模块可以由Pytorch支持的基本运算,可以自己将其封装为一个Module,将来通过实例化调用即可自动进行反向传播。

⑤ 如果你觉得Pytorch的计算图算起来效率不高,在计算导数有更高效的方法,这时可以从Functions中继承。Functions是Pytorch的一个类,这个类是需要自己实现反向传播的,这样就可以构建自己的计算块。但是你的基本计算块都可以由现成Pytorch的模块构成,从而进行计算,用Module是最简单的,不用去设计反向传播的倒数来怎么求。

# 通过魔法命令?,查看帮助信息如下
torch.nn.Linear?
  • 初始:torch.nn.Linear(in_features,out_features,bias = True)
  • 对输入数据应用线性变换:$y = xA^T + b $
  • in_features:每个输入样本的大小
  • out_features:每个输出样本的大小
  • 偏置:如果设置为"False",则图层将不会添加偏置。默认值:“True”
# 我们可以通过dir获取Module里面有哪些函数
# 通过判断可以发现在里面
"forward" in dir(torch.nn.Module) and "__call__" in dir(torch.nn.Module)  
True
# 自定义一个Pytorch线性模型的类
class LinerModel(torch.nn.Module):
    def __init__(self):
        # super 父类,调用父类的构造,这一步必须有
        # 第一个参数为定义类的名称,第二个为self
        super(LinerModel,self).__init__()
        '''构造一个对象,包含了权重与偏置Tensor
        Linear是属于Module的,因此可以自动实现前馈和反馈的计算
        nn:neural network的简写'''
        self.linear = torch.nn.Linear(1,1) 
        
    def forward(self,x):
        # 实际上是类的重写,该函数实际上已经在里面被写入了,这里需要重写一下
        # 当类中有"__call__"时才可调用
        y_pred = self.linear(x) # 实现一个可调用的对象
        return y_pred
    
# 实例化类 ,该类为callable   
model = LinerModel()# 是一个含有"__call__"的类,因此可以直接调用
# model(x) # x 就会送入至forward的函数里,然后对x进行计算

定义一个可调用的类:

class CC:
    def __init__(self):
        pass
    
    # 定义一个可调用
    def __call__(self,*args,**kwargs):
        print("hello" + str(args[0]))
        
# 实例化类
d = CC()
d(1,2,3)
hello1
# 一一对应函数传递的写法
def f(a,b,c,x,y):
    print(a)
f(1,2,3,x=3,y=3)
1
# *args:表示不指定变量的多个值传入至函数中,结果以元组的方式返回
# **kwargs:表示指定变量的多个变量传入至函数中,其结果为一个字典
def ff(*args,**kwargs):
    print(args)
    print(kwargs)
ff(1,2,3,x=3,y=3)
(1, 2, 3)
{'x': 3, 'y': 3}

3 构造损失函数和优化器(Construct loss optimizer using Pytorch API)

3.1 构造损失函数

  • torch.nn.MSELoss(size_average=None, reduce=None, reduction=‘mean’)
  • size_average(布尔型,可选):默认损失是该批次中每个损失要素的平均值。如果设置为False,是将每个小批量的损失相加,不求均值。默认值:True(当数据个数不相同时用,含有缺失值时,其余用不用无所谓,还能少有一步计算。)
  • reduce(布尔型,可选):不推荐使用。默认情况下,根据每个小批量的观测值,是否对损失进行平均或求和,具体取决于在size_average上。默认值:True
  • reduction (字符串,可选): none:无操作;mean:输出的总和除以输出中的元素;sum:将对输出求和。默认值:mean。

创建一个标准来测量之间的均方误差(L2范数的平方)

ℓ ( x , y ) = L = { l 1 , … , l N } ⊤ , l n = ( x n − y n ) 2 \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad l_n = \left( x_n - y_n \right)^2 (x,y)=L={l1,,lN},ln=(xnyn)2

ℓ ( x , y ) = { mean ⁡ ( L ) , if reduction = ’mean’; sum ⁡ ( L ) , if reduction = ’sum’. \ell(x, y) = \begin{cases} \operatorname{mean}(L), & \text{if reduction} = \text{'mean';}\\ \operatorname{sum}(L), & \text{if reduction} = \text{'sum'.} \end{cases} (x,y)={mean(L),sum(L),if reduction=’mean’;if reduction=’sum’.

x 和 y x和y xy是任意形状的张量, n n n总计每个元素的个数。

# 需要的数据是"\hat y" 与"y"
criterion = torch.nn.MSELoss(size_average=False)

3.2 构造优化器

torch.optim.SGD(
    params,
    lr=<required parameter>,
    momentum=0,
    dampening=0,
    weight_decay=0,
    nesterov=False,
)
  • params(可迭代):参数的可迭代以优化或命令定义参数组
  • lr(浮动):学习率,可以是根据需要动态变化的
  • momentum(float,可选):动量因子(默认值:0)
  • weight_decay(float,可选):正则化(默认值:0)
  • 衰减动量(float,可选):衰减动量(默认值:0)
  • nesterov(布尔型,可选):启用Nesterov动量(默认值:False)
'''
model.parameters()检查model里面的所有成员
如果有相应的权重,则就会将其都加入到最后要进行训练的参数集合中
model.parameters()实际上调用的是torch.nn.Linear.parameters()
'''
optimizer = torch.optim.SGD(model.parameters(),lr=0.01)

4 训练周期(Training cycle)

前馈:计算损失 , y ^ \hat y y^ l o s s loss loss
反馈:计算梯度
更新:梯度下降算法更新权重

for epoch in range(1000):
    # 前馈计算出 \hat y,即y_pred
    y_pred = model(x_data)
    # 前馈计算出 loss
    loss = criterion(y_pred,y_data)
    # 查看损失,loss输出的时候回自动变为标量
    # print(epoch,loss.data.item())
    # 所有的梯度归零
    optimizer.zero_grad()
    # 反馈,方向传播
    loss.backward()
    # 更新,根据所有参数和学习率来更新
    optimizer.step()
# 输出权重(weight)和偏置(bias)
print(f"w={model.linear.weight.item()}")
print(f"b={model.linear.bias.item()}")
w=1.9997389316558838
b=0.0005934041691944003
# 测试模型(Test Model)
x_test = torch.Tensor([[4]])
y_test = model(x_test)
print(f"y_pred={y_test.data}")
y_pred=tensor([[7.9995]])

如果训练没有达到预想的情况,可适当的增加训练次数。但是这样会有一定的风险,对于训练集上的损失会越来越少,对于测试集上的损失可能就会少着少着就会上升了。因此,在训练时需要对训练集和测试集的损失综合来观察,避免出现过拟合。

5 代码合并与

# 自定义一个Pytorch线性模型的类
class LinerModel(torch.nn.Module):
    def __init__(self):
        # super 父类,调用父类的构造,这一步必须有
        # 第一个参数为定义类的名称,第二个为self
        super(LinerModel,self).__init__()
        '''构造一个对象,包含了权重与偏置Tensor
        Linear是属于Module的,因此可以自动实现前馈和反馈的计算
        nn:neural network的简写'''
        self.linear = torch.nn.Linear(1,1) 
        
    def forward(self,x):
        # 实际上是类的重写,该函数实际上已经在里面被写入了,这里需要重写一下
        # 当类中有"__call__"时才可调用
        y_pred = self.linear(x) # 实现一个可调用的对象
        return y_pred
def train(times,name,lr):
    # 实例化类 ,该类为callable   
    model = LinerModel()# 是一个含有"__call__"的类,因此可以直接调用
    # model(x) # x 就会送入至forward的函数里,然后对x进行计算
    # 需要的数据是"\hat y" 与"y"
    criterion = torch.nn.MSELoss(size_average=False)
    # 构建优化器
    if name == "SGD":
        optimizer = torch.optim.SGD(model.parameters(),lr=lr)
    elif name == "Adagrad":
        optimizer = torch.optim.Adagrad(model.parameters(),lr=lr)
    elif name == "Adam":
        optimizer = torch.optim.Adam(model.parameters(),lr=lr)
    elif name == "Adamax":
        optimizer = torch.optim.Adamax(model.parameters(),lr=lr)
    elif name == "ASGD":
        optimizer = torch.optim.ASGD(model.parameters(),lr=lr)
    elif name == "RMSprop":
        optimizer = torch.optim.RMSprop(model.parameters(),lr=lr)
    elif name == "Rprop":
        optimizer = torch.optim.Rprop(model.parameters(),lr=lr)
     
    aa = []
    for epoch in range(times):
        # 前馈计算出 \hat y,即y_pred
        y_pred = model(x_data)
        # 前馈计算出 loss
        loss = criterion(y_pred,y_data)
        aa.append(loss.data.item())
        # 查看损失,loss输出的时候回自动变为标量
        # print(epoch,loss.data.item())
        # 所有的梯度归零
        optimizer.zero_grad()
        # 反馈,方向传播
        loss.backward()
        # 更新,根据所有参数和学习率来更新
        optimizer.step()
    
    # 输出权重(weight)和偏置(bias)
    print(f"w={model.linear.weight.item()}")
    print(f"b={model.linear.bias.item()}")

    # 测试模型(Test Model)
    x_test = torch.Tensor([[4]])
    y_test = model(x_test)
    print(f"y_pred={y_test.data}")
    
    return aa
import torch

if __name__ == '__main__':
    name = ["Adagrad","Adam","Adamax","ASGD","RMSprop","Rprop","SGD"]
    data = dict()
    for i in name:
        print("*"*20)
        # Pytorch中的数据等均为Tensor变量,即矩阵
        x_data = torch.Tensor([[1],[2],[3]]) # 相当于一个numpy.array
        y_data = torch.Tensor([[2],[4],[6]]) 
        print(i)
        data[i] = train(200,i,0.01)
print("执行结束")
********************
Adagrad
w=-0.2695870101451874
b=0.6175152063369751
y_pred=tensor([[-0.4608]])
********************
Adam
w=1.536633849143982
b=0.958001971244812
y_pred=tensor([[7.1045]])
********************
Adamax
w=1.5282803773880005
b=1.012595295906067
y_pred=tensor([[7.1257]])
********************
ASGD
w=1.8811336755752563
b=0.2701973021030426
y_pred=tensor([[7.7947]])
********************
RMSprop
w=1.0703529119491577
b=1.7555208206176758
y_pred=tensor([[6.0369]])
********************
Rprop
w=1.9999990463256836
b=5.34028117726848e-07
y_pred=tensor([[8.0000]])
********************
SGD
w=1.9451408386230469
b=0.12470770627260208
y_pred=tensor([[7.9053]])
执行结束
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签  
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号
plt.figure(figsize=(10,6),dpi=300)

for i in data.keys():
    y = data[i]
    x = list(range(200))
    plt.plot(x,y,label=i)
plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=0,
       ncol=len(data.keys()), mode="expand", borderaxespad=0.)

plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.grid()
plt.show()

在这里插入图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

而又何羡乎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值