watch: PyTorch - 刘二 05 | Linear Regression

Table of contents

用pytorch 提供的工具构建线性模型

  1. forward 前馈:求一组样本的损失
  2. backward 反向:求损失关于各w的梯度
  3. update 梯度下降更新w
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import torch

## 1. 准备数据
x_data = torch.Tensor([[1.0],[2.0],[3.0]])  #3条数据作为一个batch,把3个样本一次性求出一个损失,定义成矩阵以便利用numpy的广播机制
y_data = torch.Tensor([[2.0],[4.0],[6.0]])  #y与x一样也是3x1的矩阵

## 2. 设计模型: 计算 y_pred = x*w +b
class LinearModel(torch.nn.Module): #把模型定义为一个类,所有的神经网络模型都继承自nn.Module(包含了很多训练方法(i.e.反向传播))
    def __init__(self):     #必须实现构造函数,初始化对象
        super(LinearModel, self).__init__() #super调用父类的构造函数
        self.linear = torch.nn.Linear(1,1)  #构造线性单元对象:输入样本和输出样本的特征(维度)都是1列,样本条数是一个batch(3条)

    def forward(self, x):   #必须实现计算预测值方法
        y_pred = self.linear(x)     #linear也是callable的对象,加上括号()会调用nn.Linear的 call 方法,其中包含forward方法 wx+b
        return y_pred

model = LinearModel()   #实例化模型对象

## 3. 构造损失函数和优化器
criterion = torch.nn.MSELoss(size_average = False)      #损失函数MSE, 得到标量损失值,这个过程会构建计算图,所以也应该继承自nn.Moudle

optimizer = torch.optim.SGD(model.parameter(), lr=0.01) #实例化优化器对象,它不会构建计算图, 传入需要优化的参数,learning rate固定

## 4. 训练周期
for epoch in range(100):
    y_pred = model(x_data)  
    loss = criterion(y_pred, y_data)#前馈:计算预测值和损失
    print(epoch, loss)              #loss是对象,打印时会自动调用__str__();不会产生计算图

    optimizer.zero_grad()           #所有参数的梯度归零
    loss.backward()                 #反向传播
    optimizer.step()                #进行一次更新,根据所有参数的梯度和步长做更新

## 输出w和b
print('w=', model.linear.weight.item())
print('b=', model.linear.bias.item())

## 测试模型
x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred =', y_test.data)

输入数据的格式

  • 用矩阵一次性计算出一个batch的y_pred 或 loss

  • numpy的广播机制:做运算的两个数组维度不同,把小矩阵(重复)扩充到与大矩阵相同的大小

  • 运算:

    $$ \begin{aligned} \begin{bmatrix} y\_{pred}^{(1)} \\\ y\_{pred}^{(2)} \\\ y\_{pred}^{(3)} \end{bmatrix}\_{3\times 1} = w \cdot \begin{bmatrix} x^{(1)} \\\ x^{(2)} \\\ x^{(3)} \end{bmatrix}\_{3\times 1} + b \end{aligned} $$

    其中 w 和 b 会触发广播机制,变成 $[w\ w\ w]^T_{3\times 1},\ [b\ b\ b]^T_{3\times 1}$

    计算损失:

    $$ \begin{bmatrix} loss\_1 \\\ loss\_2 \\\ loss\_3 \end{bmatrix} = \begin{pmatrix} \begin{bmatrix} \hat{y\_1} \\\ \hat{y\_2} \\\ \hat{y\_3} \end{bmatrix} - \begin{bmatrix} y\_1 \\\ y\_2 \\\ y\_3 \end{bmatrix} \end{pmatrix}^2 $$

    loss 需要是一个标量,所以需求和:$loss = (\frac{1}{N}) \sum_{i}^N loss_i$。向量没法backward

nn.Linear类

  • class torch.nn.Linear(in_features, out_features, bias=True) Docs-Linear

    对数据数据应用一个线性变换:$y_{1\times n} = w^T_{1\times 3} X_{3\times n}+b$,n条样本,x有3个特征,y有一个特征

  • 传入参数:

    1. in_feature: 每个输入样本的维度(列数,特征)
    2. out_feature: 输出样本的列数
    3. bias: 是否需要b,默认为true
  • 线性单元包括两个成员Tensors(weight, bias)可以完成w*x+b的运算(同样继承自module,可以反向传播)

magic method __call__()

  • callable 对象是一个可以被调用执行的对象
  • 如果为一个类编写了__call__()方法,那么在该类的对象后面加括号,就会调用执行 __call__() 方法
1
2
3
4
5
class Foobar:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):    #实现此方法,让对象可调用

*args 代表没有固定数量和变量名的输入参数,是一个元组;**kwargs表示带变量名的输入参数,是一个字典:

1
2
def fun(*args, **kwargs):
    pass

func(1,2,3, x=3, y=5)*args为(1,2,3),**kwargs{'x':3, 'y':5},就可以遍历各个参数了

nn.MSELoss类

  • class torch.nn.MSELoss(size_average=True, reduce=True)

  • 继承自nn.Module

  • $\hat{𝐲} - 𝐲$,减完之后求平方,求和

  • 参数:

    1. size_average=True 是否最后的损失要求均值,$\frac{1}{N}$没什么用,求导与w无关
    2. reduce=True 是否降维

SGD类

  • class torch.optim.SGD(params.lr=<object, object>, momentum=0, dampening=0, weight_decay=0, nesterov=False)

    params 是需要优化的参数。使用model.parameters(),这个成员函数会检查model中的所有成员,有哪些参数需要用梯度下降更新,加入需要训练的参数集合中。搜索model中的linear成员时,会调用linear.params(),linear有2个参数,

Built with Hugo
Theme Stack designed by Jimmy