线性回归从零实现

ryluo 2020-06-14 01:29:22
机器学习

本问使用tensor从零实现线性回归,使用随机梯度下降的优化算法更新模型,其中也遇到了一些坑记录在文章的最后,本代码是学习伯禹AI《动手学深度学习》课程之后重新写的代码。

线性回归从零实现

简单的线性模型公式如下:

import torch
import numpy as np
import matplotlib.pyplot as plt
import random

# 构造数据
## y = w_1 * x_1 + w_2 * x_2 + b
num_inputs = 2 # 特征的维度
num_examples = 1000 # 样本的数量

## 真实的参数值
true_w = [3.2, -4.5]
true_b = 0.8

# features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float32)
features = torch.randn(num_examples, num_inputs, dtype=torch.float32)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
## 给标签添加扰动
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float32)

## 可视化生成的数据
# plt.scatter(features[:, 1].numpy(), labels.numpy())
# plt.show()

# 定义数据生成函数
def data_iter(features, labels, batch_size):
    num = len(labels)
    index = list(range(num))
    random.shuffle(index)
    for i in range(0, num, batch_size):
        j = torch.LongTensor(index[i:min(i + batch_size, num)])
        yield features.index_select(0, j), labels.index_select(0, j)

# for X, y in data_iter(features, labels, batch_size=5):
#    print(X, y)
#    break

# 定义并参数
W = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32, requires_grad=True)
b = torch.zeros(1, dtype=torch.float32, requires_grad=True)

# print(W, '\n', b)

# 定义损失函数,这里线性回归使用的是均方误差
def Squareloss(y_h, y, batch_size):
    return (y_h - y.view(y_h.size())) ** 2 / 2

# 定义线性模型
def Linreg(W, b, x):
    return torch.mm(x, W) + b

# 开始训练
lr = 0.1
batch_size = 5
epochs = 10
train_loss = 0

model = Linreg
loss = Squareloss
for epoch in range(epochs):
    iters = 0
    for x, y in data_iter(features, labels, batch_size):
        output = model(W, b, x)
        l = loss(output, y, batch_size).sum()
        train_loss += l # 将损失累加用于可视化每个epoch的平均损失变化情况
        # 反向传播
        l.backward()
        # 梯度更新
        W.data -= lr * W.grad / batch_size
        b.data -= lr * b.grad / batch_size
        # 梯度清零
        W.grad.data.zero_()
        b.grad.data.zero_()

        iters += 1

    print('epoch: %d, loss: %.5f' % (epoch + 1, train_loss / iters))

print('predict w, b\n', W, b)
print('true w, b\n', true_w, true_b)

掉过的坑

忘记给损失求和:l = loss(output, y, batch_size),正确的代码如上述代码所示

Traceback (most recent call last):
File "LR.py", line 67, in <module>
 l.backward()
File "D:\ProgramData\Anaconda3\envs\pytorch1.0\lib\site-packages\torch\tensor.py", line 102, in backward
 torch.autograd.backward(self, gradient, retain_graph, create_graph)
File "D:\ProgramData\Anaconda3\envs\pytorch1.0\lib\site-packages\torch\autograd\__init__.py", line 84, in backward
 grad_tensors = _make_grads(tensors, grad_tensors)
File "D:\ProgramData\Anaconda3\envs\pytorch1.0\lib\site-packages\torch\autograd\__init__.py", line 28, in _make_grads
 raise RuntimeError("grad can be implicitly created only for scalar outputs")
RuntimeError: grad can be implicitly created only for scalar outputs