You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从零实现线性模型与神经网络,遇预测值普遍过低问题求助

线性模型预测值普遍偏低的问题排查与解决

我从零实现了两个逻辑回归模型(用于分类任务),但都出现预测值普遍偏低的问题:

  • Framingham心脏病数据集:预测值均低于0.3
  • 泰坦尼克号数据集:预测值甚至低于0.05

Framingham心脏病数据集实现代码

import numpy as np
import pandas as pd

dataset = pd.read_csv(r'...\framingham.csv')

print(dataset['TenYearCHD'].value_counts())
dataset = dataset.dropna()
print(dataset['TenYearCHD'].value_counts())

for col in dataset.columns:
    if dataset[col].nunique() > 10:
        max_n = dataset[col].max()
        dataset[col] /= max_n


def one_hot_encoding(col):
    for uni in dataset[col].unique():
        dataset[f"{col}_{uni}"] = (dataset[col] == uni).astype(int)
    dataset.drop(col, axis=1, inplace=True)

one_hot_encoding("education")

y = dataset["TenYearCHD"].to_numpy()
dataset.drop("TenYearCHD", axis=1, inplace=True)
print(dataset)

w = np.random.rand(len(dataset.columns))-0.5
b = 0

print(np.max(w))

LEARNING_RATE = 0.001
EPOCHES = 15

def forward_pass(w, xz, b):
    #return w * x + b
    return np.dot(w, xz.T) + b

def sigmoid(z):
    return 1/(1+np.exp(-z))


x = dataset.to_numpy()

train_x, test_x = np.split(x, [int(0.80*len(x))])
train_y, test_y = np.split(y, [int(0.80*len(x))])


for epoch in range(EPOCHES):
    z = forward_pass(w, train_x, b)
    z = sigmoid(z)
    MSE = np.mean((z-train_y)**2)

    dw = np.dot(train_x.T, (z-train_y))
    db = np.mean((z-train_y))

    w -= LEARNING_RATE*dw
    b -= LEARNING_RATE*db

    print(f"Epoch: {epoch} MSE: {MSE}")

z = sigmoid(forward_pass(w, test_x, b))
MSE = np.mean((z-test_y)**2)
print(MSE)
print(w)
for x, y in zip(test_x, test_y):
    prediction = sigmoid(forward_pass(w, x, b))
    print(prediction, y)

泰坦尼克号数据集实现代码

import numpy as np
import pandas as pd

train = pd.read_csv(r"...\train.csv")
test = pd.read_csv(r"...\test.csv")

def dataset_orangize(dataset):
    dataset = dataset.drop(['Ticket', 'Name', 'PassengerId', 'Cabin'],axis=1)
    dataset.dropna(inplace=True)
    dataset = one_hot_encoder(dataset, 'Sex')
    dataset = one_hot_encoder(dataset, 'Pclass')
    dataset = one_hot_encoder(dataset, 'Embarked')
    dataset['Age'] /= np.max(dataset['Age'])
    dataset['Fare'] /= np.log(dataset['Fare']+1)
    dataset.dropna(inplace=True)
    return dataset

def splitter(dataset):
    labels = dataset['Survived']
    dataset.drop('Survived', axis=1, inplace=True)
    return dataset, labels

def one_hot_encoder(dataset, col_name):
    for uni in dataset[col_name].unique():
        dataset[col_name+'_'+str(uni)] = (dataset[col_name]==uni).astype(int)
    dataset.drop(col_name, axis=1, inplace=True)
    return dataset
train_x, train_y = splitter(dataset_orangize(train))
test = dataset_orangize(test)

LEARNING_RATE = 0.03
EPOCHES = 20

w = np.random.randn(len(train_x.columns))-0.5
b = 0

train_x = train_x.to_numpy()
train_y = train_y.to_numpy()

def sigmoid(z):
    return 1/(1+np.exp(-z))


for epoch in range(EPOCHES):
    z=np.dot(train_x, w.T)+b
    z = sigmoid(z)
    MSE = np.mean((z-train_y)**2)

    dw = np.mean(np.dot(train_x.T, z-train_y))
    db = np.mean(z-train_y)

    w -= dw*LEARNING_RATE
    b -= db*LEARNING_RATE

    print(f'MSE: {MSE}    EPOCHES: {epoch}')


z=np.dot(train_x, w)+b
z = sigmoid(z)

for x, y in zip(train_x, train_y):
    z = np.dot(x, w.T) + b
    z = sigmoid(z)
    print(z, y)

问题原因分析

通用问题

  1. 损失函数选择错误:分类任务用MSE(均方误差)损失不合适。MSE对分类任务的梯度更新效率极低,当预测值离真实标签(0或1)较远时,sigmoid函数的梯度趋近于0,导致权重更新缓慢,模型难以收敛到合适参数。
  2. 特征缩放不规范:
    • Framingham数据集仅对唯一值>10的特征做最大值归一化,其他特征(如二元特征)未统一处理,不同特征尺度差异大,导致梯度更新不均衡。
    • 最大值归一化受异常值影响大,不如Z-score标准化((x - mean)/std)稳定,后者能让特征均值为0、方差为1,有助于梯度下降收敛。
  3. 训练参数不合理:两个模型的迭代次数(15、20)太少,学习率设置要么过小(Framingham的0.001)要么适配性差,导致权重未更新到最优值。

泰坦尼克号模型特有问题

  1. 特征处理错误:dataset['Fare'] /= np.log(dataset['Fare']+1) 是错误操作,这会让Fare的值被放大(因为log(Fare+1)远小于Fare本身),导致该特征的权重被反向压制,最终拉低整体预测值。正确做法是对Fare做对数变换:dataset['Fare'] = np.log(dataset['Fare']+1),之后再做归一化/标准化。
  2. 梯度计算严重错误:dw = np.mean(np.dot(train_x.T, z-train_y)) 中,np.mean会把原本和权重同维度的向量压缩成一个标量,导致所有权重都被更新同一个值,完全破坏参数学习逻辑,这是预测值极低的核心原因。正确的dw应该是每个特征维度的平均梯度,即 dw = np.dot(train_x.T, z-train_y) / len(train_x)。

Framingham模型特有问题

  1. 类别不平衡:从TenYearCHD的value_counts可以看出,患病样本占比远低于健康样本,模型会偏向预测多数类(0),导致预测值普遍偏低。

解决建议

  1. 替换损失函数:改用二元交叉熵损失,对应梯度计算调整为:
    # 交叉熵损失
    loss = -np.mean(train_y * np.log(z) + (1-train_y)*np.log(1-z))
    # 梯度计算(和MSE的dw/db形式一致,但损失函数更适合分类)
    dw = np.dot(train_x.T, (z-train_y)) / len(train_x)
    db = np.mean(z-train_y)
    
  2. 修正特征处理:
    • 泰坦尼克号:将Fare的处理改为dataset['Fare'] = np.log(dataset['Fare']+1),然后对所有特征做Z-score标准化。
    • 两个数据集统一使用Z-score标准化,确保特征尺度一致。
  3. 修复梯度计算:泰坦尼克号模型中去掉dw的np.mean,改为除以样本数得到平均梯度。
  4. 调整训练参数:
    • 迭代次数增加到100~1000次,观察损失下降趋势,直到损失稳定。
    • 学习率调整为0.01~0.1(可根据损失变化微调,若损失震荡则降低学习率)。
  5. 处理类别不平衡:Framingham数据集可采用以下方式:
    • 使用加权交叉熵损失,给少数类(患病样本)更高的权重。
    • 对少数类做过采样,或对多数类做欠采样,平衡样本分布。
  6. 优化权重初始化:采用Xavier初始化(w = np.random.randn(len(dataset.columns)) * np.sqrt(1/len(dataset.columns))),配合标准化后的特征,让初始输出更稳定。

内容的提问来源于stack exchange,提问作者6haun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 10:10:01