TensorFlow二分类单层神经网络输出值不在预期[0,1]范围求助
我用TensorFlow训练一个二分类的简单神经网络,训练数据标签为0和1。训练过程看起来正常,准确率约80%,AUC约75%,但模型评估时输出的概率分数集中在[0.1, 0.35]区间,预期应该覆盖[0,1]范围。
以下是我的模型构建代码:
# initialize model model = keras.models.Sequential() # get input-dimension arr = LoadTTree(self,file=self.TrainTreeDir,print=False) self.InputDimension = len(arr[0][0]) # build the input layer model.add(keras.Input(shape = (self.InputDimension), name = "inputlayer")) # build the hidden layer model.add(keras.layers.Dense(self.Nodes, name = "hiddenlayer", activation = self.ActivationFunction)) # build the output layer model.add(keras.layers.Dense(self.OutputDimension, activation=self.OutputActivation, name="output_layer")) # build KerasModel, compile and print model-summary self.KerasModel = model self.KerasModel.compile(loss = Loss(self), optimizer = Optimizer(self), weighted_metrics = self.Metrics) self.KerasModel.summary()
参数设置:
ActivationFunction = sigmoid, OutputActivation = sigmoid, Loss = binary_crossentropy, Optimizer = adam
训练代码:
hist = self.KerasModel.fit(self.data_train, self.labels_train, epochs=self.Epochs, batch_size=self.BatchSize, verbose=self.Verbosity, sample_weight=self.weights_train, validation_data=(self.data_val,self.labels_val,self.weights_val))
其中Epochs = 50,我已经尝试调整损失函数和激活函数,但问题仍未解决。请问这是什么原因导致的?
可能的原因及解决方法
1. 训练数据标签分布严重不平衡
如果训练集中标签为1的样本占比极低,模型会倾向于输出低概率(偏向预测0),以此获得更高的整体准确率。
排查方法:
计算训练集标签的均值:
import numpy as np print("训练集1类样本占比:", np.mean(self.labels_train))
如果占比远低于50%(比如低于10%),就说明存在数据不平衡问题。
解决方法:
- 调整分类阈值:不要用0.5作为分界点,根据验证集的F1分数或召回率找到最优阈值;
- 使用Focal Loss替代普通的binary crossentropy,降低易分类样本(0类)的权重,迫使模型关注少数类;
- 对少数类进行过采样,或对多数类进行欠采样,平衡样本分布。
2. 输入特征未做归一化/标准化
Sigmoid激活函数对输入数值范围非常敏感:如果输入特征的数值过大或过小,会导致隐藏层的输出落在Sigmoid的饱和区(两端),使得模型无法学习到有效的特征表示,最终输出层的概率也会集中在狭窄区间。
排查方法:
查看输入特征的统计信息:
print("特征均值:", np.mean(self.data_train, axis=0)) print("特征标准差:", np.std(self.data_train, axis=0))
如果特征之间数值差异极大(比如有的特征均值是1000,有的是0.01),就需要做归一化。
解决方法:
用StandardScaler或MinMaxScaler对输入特征做标准化/归一化,注意要只在训练集上拟合,再应用到验证集和测试集:
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() self.data_train = scaler.fit_transform(self.data_train) self.data_val = scaler.transform(self.data_val)
3. 隐藏层激活函数选择不当
隐藏层使用Sigmoid容易导致梯度消失:当输入数值较大时,Sigmoid的梯度趋近于0,模型参数更新缓慢,无法学到有效的特征映射,最终输出概率无法覆盖全范围。
解决方法:
将隐藏层的激活函数替换为ReLU(或LeakyReLU),避免梯度消失问题:
ActivationFunction = 'relu' # 替换原来的sigmoid
4. 样本权重设置不合理
如果给标签为0的样本设置了过高的权重,模型会优先保证0类的预测正确,从而倾向于输出低概率值。
排查方法:
检查样本权重的分布:
# 查看0类和1类样本的平均权重 class_0_weights = self.weights_train[self.labels_train == 0] class_1_weights = self.weights_train[self.labels_train == 1] print("0类样本平均权重:", np.mean(class_0_weights)) print("1类样本平均权重:", np.mean(class_1_weights))
如果0类的平均权重远高于1类,就会导致模型偏向输出低概率。
解决方法:
调整样本权重,使两类的权重相对均衡,或者根据样本频率设置权重(比如1类权重=总样本数/(2*1类样本数),0类同理)。
5. 训练轮次不足
虽然设置了50个epochs,但模型可能还没有完全收敛,参数还在优化过程中,导致输出概率尚未覆盖全范围。
排查方法:
绘制训练和验证的loss曲线,观察是否还在下降:
import matplotlib.pyplot as plt plt.plot(hist.history['loss'], label='训练loss') plt.plot(hist.history['val_loss'], label='验证loss') plt.legend() plt.show()
如果loss曲线还在持续下降,说明模型还可以继续训练。
解决方法:
增加epochs数量(比如调到100或200),或者添加EarlyStopping回调,让模型在验证集loss不再下降时自动停止:
from keras.callbacks import EarlyStopping early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True) hist = self.KerasModel.fit(..., callbacks=[early_stop])
内容的提问来源于stack exchange,提问作者Lukas Kretschmann

