Pytorch 线性回归 1x1d,斜率始终错误

Tim*_*nen 5 python numpy linear-regression pytorch

我在这里掌握了 pytorch,并决定实现非常简单的 1 对 1 线性回归,从身高到体重。

获得数据集: https: //www.kaggle.com/datasets/mustafaali96/weight-height,但任何其他数据集都可以。

让我们导入有关女性的库和信息:

import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_csv('weight-height.csv',sep=',')
#https://www.kaggle.com/datasets/mustafaali96/weight-height
height_f=df[df['Gender']=='Female']['Height'].to_numpy()
weight_f=df[df['Gender']=='Female']['Weight'].to_numpy()
plt.scatter(height_f, weight_f, c ="red",alpha=0.1)
plt.show()
Run Code Online (Sandbox Code Playgroud)

这给出了测量的女性的良好分散性: 分配

到目前为止,一切都很好。

让我们制作数据加载器:

class Data(Dataset):
  def __init__(self, X: np.ndarray, y: np.ndarray) -> None:
    # need to convert float64 to float32 else
    # will get the following error
    # RuntimeError: expected scalar type Double but found Float
    self.X = torch.from_numpy(X.reshape(-1, 1).astype(np.float32))
    self.y = torch.from_numpy(y.reshape(-1, 1).astype(np.float32))    
    self.len = self.X.shape[0]  
  def __getitem__(self, index: int) -> tuple:
    return self.X[index], self.y[index]  
  def __len__(self) -> int:
    return self.len

traindata = Data(height_f, weight_f)
batch_size = 500
num_workers = 2
trainloader = DataLoader(traindata, 
                         batch_size=batch_size, 
                         shuffle=True, 
                         num_workers=num_workers)
Run Code Online (Sandbox Code Playgroud)

...线性回归模型...

class linearRegression(torch.nn.Module):
    def __init__(self, inputSize, outputSize):
        super(linearRegression, self).__init__()
        self.linear = torch.nn.Linear(inputSize, outputSize)
        

    def forward(self, x):
        out = self.linear(x)
        return out
model = linearRegression(1, 1)
criterion = torch.nn.MSELoss() 
optimizer = torch.optim.SGD(model.parameters(), lr=0.00001)

Run Code Online (Sandbox Code Playgroud)

..让我们训练它:

epochs=10
for epoch in range(epochs):
    print(epoch)
    for i, (inputs, labels) in enumerate(trainloader):
        
        outputs=model(inputs)
        loss = criterion(outputs, labels)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
Run Code Online (Sandbox Code Playgroud)

给出 0,1,2,3,4,5,6,7,8,9 现在让我们看看我们的模型给出了什么:

range_height_f=torch.linspace(height_f.min(),height_f.max(),150)

plt.scatter(height_f, weight_f, c ="red",alpha=0.1)
pred=model(range_height_f.reshape(-1, 1))
plt.scatter(range_height_f, pred.detach().numpy(), c ="green",alpha=0.1)
Run Code Online (Sandbox Code Playgroud)

... 型号错误

为什么要这样做?为什么坡度不对?始终错误的斜率,我可能会添加无论我改变什么,优化器,批量大小,时代,女性到男性..它给了我这个非常错误的斜率,我真的不明白 - 为什么?

编辑1:添加损失,这是情节 损失

编辑2:决定进行一些探索,并使用sklearn进行回归:

from sklearn.model_selection import train_test_split

from sklearn.linear_model import LinearRegression
X_train, X_test, y_train, y_test = train_test_split(height_f, weight_f, test_size = 0.25)

regr = LinearRegression()
regr.fit(X_train.reshape(-1,1), y_train)
plt.scatter(height_f, weight_f, c ="red",alpha=0.1)
range_pred=regr.predict(range_height_f.reshape(-1, 1))
range_pred
plt.scatter(range_height_f, range_pred, c ="green",alpha=0.1)
Run Code Online (Sandbox Code Playgroud)

它给出了以下回归,看起来不错: 斯基学习回归

t = torch.from_numpy(height_f.astype(np.float32))
p=regr.predict(t.reshape(-1,1))
p=torch.from_numpy(p).reshape(-1,1)


w= torch.from_numpy(weight_f.astype(np.float32)).reshape(-1,1)

print(criterion(p,w).item())
Run Code Online (Sandbox Code Playgroud)

但是在这种情况下 criteria=100.65161998527695

Pytorch 自己又收敛到 210 左右

编辑 3 将优化从 SGD 更改为 Adam:

#optimizer = torch.optim.SGD(model.parameters(), lr=0.00001)
optimizer = torch.optim.Adam(model.parameters(), lr=0.5)
Run Code Online (Sandbox Code Playgroud)

在这种情况下 lr 更大,这会产生有趣但一致的结果。这是损失: 亚当损失,这里提出回归: Adam 回归损失

并且,这里也是 Adam 优化器的损失准则的对数: 最后纪元

Sha*_*hai 6

我认为你的问题源于数据不以零为中心。

请参阅此线程的另一个示例,其中在训练之前“居中”数据对 SGD 优化的收敛具有巨大影响。


更新(2022 年 12 月 29 日

TL;DR
这都是关于标准化/初始化的。

详细来说:
您的数据不是以 0 为中心,也没有“很好”地缩放。这使得 SGD(及其所有其他变体)很难进行优化。

这个答案中,我展示了如何集中训练数据(减去平均值并由标准差决定)解决这个问题。

在这里,我将向您展示如何保持数据不变,但更改权重的初始化来解决您的问题。

m_x, s_x为 的均值和标准差X,并且m_y, s_y为 的均值和标准差y。当 pytorch 初始化线性层的
权重a和时,它假设和 的均值和单位方差为零。这里的情况并非如此。离得很远。 因此,我们需要对初始进行相应的 重新调整。 这是它的数学计算: by = aX + bXy
ab
在此输入图像描述

和代码:

mu_x, sig_x, mu_y, sig_y = traindata.X.mean().item(), traindata.X.std().item(), traindata.y.mean().item(), traindata.y.std().item()
# just for fun, here are the values:
# (63.7087, 2.6962, 135.8601, 19.0225)

# start a fresh model and adjust its initial values:
model = linearRegression(1, 1)
model.linear.weight.data *= (sig_x / sig_y)
model.linear.bias.data = sig_y * (-(mu_x/sig_x)+(mu_y/sig_y))

# now you are good to go! continue optimizing like you originally did:
# init an optimizer
optimizer = torch.optim.SGD(model.parameters(), lr=0.00001)

# optimize for 10 epochs (now you don't need this much, you can even increase the learning rate...)
epochs=10
for epoch in range(epochs):
    print(epoch)
    for i, (inputs, labels) in enumerate(trainloader):
        
        outputs=model(inputs)
        loss = criterion(outputs, labels)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
Run Code Online (Sandbox Code Playgroud)

损失曲线如下所示:
在此输入图像描述 优化器收敛到

In []: loss.item()
Out[]: 100.9453125
Run Code Online (Sandbox Code Playgroud)

与 的类似sklearn.linear_model.LinearRegression

绘制数据预测:

在此输入图像描述