TensorFlow自定义逻辑回归模型无法学习二分类问题求助
问题描述
我用TensorFlow自定义模型拟合乳腺癌数据集做二分类任务(通过30个特征预测是否患癌),但无论调整学习率(0.1至0.001)或增加epochs,模型都无法有效学习:最高准确率仅60%,最低损失为0.66,且所有预测结果均为True。对比Keras Sequential API构建的同类模型,准确率能达到95%,想排查是数据缩放存在问题,还是其他环节出错?
自定义模型代码
def loss(y, y_pred): return tf.reduce_mean(-y*tf.math.log(y_pred) - (1-y)*tf.math.log(1-y_pred)) class Model: def __init__(self, n_feature, lr= 0.001): self.lr = lr self.W = tf.Variable(tf.random.uniform((n_feature+1, 1), -1, 1)) tf.print(self.W) def __call__(self, X): return tf.sigmoid(tf.matmul(X, self.W)) def predict(self, X): return tf.sigmoid(tf.matmul(X, self.W)) def predict_classes(self, X): a = tf.round(self.predict(X)) return tf.where(a > 0.5, tf.ones_like(a), tf.zeros_like(a)) def fit(self, X, y, epochs=1000): for epoch in tqdm(range(epochs)): with tf.GradientTape() as t: y_pred = self.predict(X) loss_i = loss(y, y_pred) grad_W = t.gradient(loss_i, self.W) self.W.assign_sub(grad_W*self.lr) print(f"Loss is {loss_i}")
数据预处理代码(已添加偏置项)
from sklearn.datasets import load_breast_cancer data = load_breast_cancer() n_feature = len(data.feature_names) print(f"No of features: {n_feature}") x = (data.data-data.data.min(axis=0))/(data.data.max(axis=0)-data.data.min(axis=0)) x = np.concatenate((x, np.ones((len(data.data), 1))), axis=1) X = tf.convert_to_tensor(x, dtype=tf.float32) y = tf.convert_to_tensor(data.target, dtype=tf.float32)
模型调用代码
model = Model(n_feature=n_feature) model.fit(X, y) y_hat = model.predict_classes(X) from sklearn.metrics import accuracy_score print(f"Accuracy: {accuracy_score(y.numpy(), y_hat.numpy())}")
问题排查与解决方案
1. 核心问题:标签维度不匹配
自定义模型中,y的形状是(样本数,),而模型输出y_pred的形状是(样本数,1),两者维度不匹配会导致广播机制异常,损失计算和梯度更新出现错误,这是模型无法学习的主要原因。Keras会自动处理维度对齐,但自定义模型需要手动调整。
修正方法:在数据预处理阶段,将标签转换为二维张量:
y = tf.convert_to_tensor(data.target.reshape(-1, 1), dtype=tf.float32)
2. 损失函数的数值稳定性问题
手动实现的交叉熵未处理y_pred接近0或1的情况,会出现log(0)导致的NaN值,进而使梯度计算失效。
修正方法:给y_pred添加截断,避免极端值:
def loss(y, y_pred): # 截断y_pred到[1e-7, 1-1e-7]范围,避免log(0) y_pred = tf.clip_by_value(y_pred, 1e-7, 1 - 1e-7) return tf.reduce_mean(-y * tf.math.log(y_pred) - (1 - y) * tf.math.log(1 - y_pred))
3. 预测逻辑冗余
predict_classes中先执行tf.round再用tf.where判断是冗余操作,tf.round已经会将≥0.5的值转为1,<0.5的值转为0,可简化代码:
def predict_classes(self, X): return tf.round(self.predict(X))
或者直接用阈值判断,更直观:
def predict_classes(self, X): return tf.cast(self.predict(X) > 0.5, tf.float32)
4. 权重初始化范围优化
当前权重初始化在[-1, 1]区间,对于31个特征(含偏置)的模型来说,初始输出可能偏向极端值,影响模型初期学习。可以缩小初始化范围,比如:
self.W = tf.Variable(tf.random.uniform((n_feature+1, 1), -0.1, 0.1))
5. 学习率与训练策略
尝试将学习率调高至0.1或0.05,同时在训练时加入验证集(拆分数据集为训练/测试集),避免在全量数据上评估导致的结果偏差:
from sklearn.model_selection import train_test_split x_train, x_test, y_train, y_test = train_test_split(x, data.target, test_size=0.2, random_state=42) # 再转换为TensorFlow张量进行训练
修正后效果
调整上述问题后,模型的损失会逐步下降,准确率能提升至与Keras模型相近的水平(约95%左右)。
内容的提问来源于stack exchange,提问作者Sayan Dey

