引言写在2022年暑假,结果只写了个环境配置就因为杂七杂八的原因没有学完。现在是研一下学期了,趁着不需要开题的空挡,在学一些专业技能吧。
线性回归其实很容易理解,他的目的就是通过直线拟合输入输出数据,得到输入与输出的变化关系,从而可以实现数据的分析、预测。
详细点解释就是,我们首先通过实验得到输入输出数据;
其次我们将模型已经确定为线性模型,因此可以得到Y=wX+b(其中X为输入数据,Y为输出数据),其中X和X不再是我们之前连接到的标量,比如说0,2这种一个数的形式,而是向量和矩阵,类似于[0, 2, 5],其中Y一般为向量,X一般为矩阵,因为影响输出值的变量可能有很多。比如说我们已知三个房子的价值以及其房间大小、地理位置的信息,那Y就是由三个元素组成的向量,例如[120, 150, 139],那X就是2X3的一个矩阵。
接下来我们先假设w和b为某一向量,通过线性模型Y=wX+b,带入输入数据我们便可以得到预测输出。当然我们刚开始设置的一组w、b不可能良好的拟合,因此我们需要对其进行调整,调整的参考就是误差,将预测值与输出值差的平方作为误差,该值越小说明拟合越好。
那怎么让误差变小呢,简单来说也就是在自变量为w、b,因变量为误差时,找到误差最小时的w、b值。我们只需要让自变量沿其导数的负方向前进便可以使误差减小,也就是梯度下降。插几句,由于损失函数的结构可能很复杂,一般深度学习都求的是数值解,而不是解析解。
在我们设计一个线性回归模型时,大致可以分为以下几个部分:
输入数据与输出数据的创建
每次从数据中提取一部分数据进行小批量随机梯度下降(防止数据量过大,计算不过来)
线性模型计算预测值
预测值与真值做差、平方,得到损失
梯度下降,优化参数,梯度在torch是自动计算的,我们不需要实现
##构造人工数据集
#用的不是torch.random,而是torch.normal
#torch.range()需要设定start和end,并且可以取到end,torch.arrange默认start=0,不会取到end
#torch.matmul可求向量内积与矩阵乘积
def creat_data(real_w, real_b, data_num):
X = torch.normal(0, 1, (len(real_w), data_num))
Y = torch.matmul(real_w, X) + real_b
Y += torch.normal(0, 0.1, torch.matmul(real_w, X).shape)
return X, Y.reshape(-1, 1)
real_w = torch.tensor([0.1, 0.2])
real_b = torch.tensor(0.3)
data_num = 10
features, labels = creat_data(real_w, real_b, data_num)
features = features.T
features.shape, labels.shape
##批量数据提取:配合for循环多次读取,每次都用全部数据的一部分进行训练,但每个epoch都能用到全部数据
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))
# 这些样本是随机读取的,没有特定的顺序
random.shuffle(indices)
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]#返回数据流
##初始化模型参数
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)#打开导数计算
b = torch.zeros(1, requires_grad=True)
##定义模型
def linreg(X, w, b):
return torch.matmul(X, w) + b
##计算损失函数
def squared_loss(y_hat, y):
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
##随机梯度下降
def sgd(params, lr, batch_size):
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()#将梯度填充为0,防止影响后面计算
##训练
lr = 0.03
num_epochs = 3
net = linreg#网络模型
loss = squared_loss#损失函数计算
batch_size = 2
for epoch in range(num_epochs):
i = 0
for X, y in data_iter(batch_size, features, labels):
#print(X, y)
#i = i+1
#print(i)
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward()#自动求导只能求解标量
sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')