从零实现线性模型与神经网络,遇预测值普遍过低问题求助
线性模型预测值普遍偏低的问题排查与解决
我从零实现了两个逻辑回归模型(用于分类任务),但都出现预测值普遍偏低的问题:
- 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)
问题原因分析
通用问题
- 损失函数选择错误:分类任务用MSE(均方误差)损失不合适。MSE对分类任务的梯度更新效率极低,当预测值离真实标签(0或1)较远时,sigmoid函数的梯度趋近于0,导致权重更新缓慢,模型难以收敛到合适参数。
- 特征缩放不规范:
- Framingham数据集仅对唯一值>10的特征做最大值归一化,其他特征(如二元特征)未统一处理,不同特征尺度差异大,导致梯度更新不均衡。
- 最大值归一化受异常值影响大,不如Z-score标准化(
(x - mean)/std)稳定,后者能让特征均值为0、方差为1,有助于梯度下降收敛。
- 训练参数不合理:两个模型的迭代次数(15、20)太少,学习率设置要么过小(Framingham的0.001)要么适配性差,导致权重未更新到最优值。
泰坦尼克号模型特有问题
- 特征处理错误:
dataset['Fare'] /= np.log(dataset['Fare']+1)是错误操作,这会让Fare的值被放大(因为log(Fare+1)远小于Fare本身),导致该特征的权重被反向压制,最终拉低整体预测值。正确做法是对Fare做对数变换:dataset['Fare'] = np.log(dataset['Fare']+1),之后再做归一化/标准化。 - 梯度计算严重错误:
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模型特有问题
- 类别不平衡:从
TenYearCHD的value_counts可以看出,患病样本占比远低于健康样本,模型会偏向预测多数类(0),导致预测值普遍偏低。
解决建议
- 替换损失函数:改用二元交叉熵损失,对应梯度计算调整为:
# 交叉熵损失 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) - 修正特征处理:
- 泰坦尼克号:将Fare的处理改为
dataset['Fare'] = np.log(dataset['Fare']+1),然后对所有特征做Z-score标准化。 - 两个数据集统一使用Z-score标准化,确保特征尺度一致。
- 泰坦尼克号:将Fare的处理改为
- 修复梯度计算:泰坦尼克号模型中去掉dw的
np.mean,改为除以样本数得到平均梯度。 - 调整训练参数:
- 迭代次数增加到100~1000次,观察损失下降趋势,直到损失稳定。
- 学习率调整为0.01~0.1(可根据损失变化微调,若损失震荡则降低学习率)。
- 处理类别不平衡:Framingham数据集可采用以下方式:
- 使用加权交叉熵损失,给少数类(患病样本)更高的权重。
- 对少数类做过采样,或对多数类做欠采样,平衡样本分布。
- 优化权重初始化:采用Xavier初始化(
w = np.random.randn(len(dataset.columns)) * np.sqrt(1/len(dataset.columns))),配合标准化后的特征,让初始输出更稳定。
内容的提问来源于stack exchange,提问作者6haun
相关产品推荐
相关产品推荐

