TensorFlow逻辑回归模型训练精度无法稳定提升问题咨询
我仔细分析了你的代码和问题描述,发现几个核心问题导致模型无法有效学习——哪怕是简单的AND函数任务,都出现了权重趋近于0、输出趋近50%的情况。下面是具体的问题拆解和修复方案:
1. 输入与模型输出的形状不匹配
你的self.y占位符注释标注为1 x num_sents,但实际传入的batch_y是一维数组(比如batch_size=3时形状为(3,)),而模型输出的self.scores形状是(batch_size, 1)(因为num_outputs=1)。这种形状不匹配会导致损失计算时的广播错误,虽然TensorFlow不会直接报错,但损失值的计算逻辑已经偏离预期,模型自然无法学到有效参数。
修复方法:
- 修改
self.y的定义,明确适配批量输入的形状:self.y = tf.placeholder(tf.float64, shape=[None, 1], name="placeholdery") - 在
get_batch函数中,将返回的y_b转换为二维数组:return (x_b, y_b.reshape(-1, 1))
2. 手动计算交叉熵的数值稳定性问题
你手动用tf.clip_by_value避免log(0)的问题,但TensorFlow内置的tf.nn.sigmoid_cross_entropy_with_logits函数已经做了更完善的数值稳定处理,还会自动完成sigmoid转换和交叉熵计算,避免手动实现的潜在误差。
修复方法:
替换你的损失计算代码为:
self.scores = tf.add(self.wx, self.b) # 用内置函数计算交叉熵,自动处理数值稳定 self.loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=self.scores, labels=self.y)) # 若需要正则化,取消注释下方代码(tf.nn.l2_loss已包含0.5因子) # self.loss += self.reg_const * tf.nn.l2_loss(self.w)
这样可以删掉手动计算probs和clip_by_value的代码,简化逻辑同时提升稳定性。
3. 学习率设置不合理
Adam优化器的默认学习率是0.001,你设置的self.eta=0.01偏大,容易导致训练过程中权重震荡甚至发散,最终模型“放弃学习”,权重逐渐趋近于0。
修复方法:
将学习率调整为更合理的数值:
self.eta = 0.001
如果还是不稳定,可以尝试更小的值(比如0.0001)。
4. 训练时未打乱数据顺序
你的训练循环没有在每个epoch前打乱数据,模型会记住样本的固定顺序,导致训练不稳定——在小数据集上这个问题会格外明显。
修复方法:
在每个epoch开始前,对训练数据的索引进行洗牌,再按洗牌后的索引取batch:
def train(self, x, y, loss_graph_file): session = self.session num_samples = y.shape[0] loss_vect = [] for epoch in range(self.epochs): avg_loss = 0 # 洗牌样本索引 indices = np.random.permutation(num_samples) num_batches = (num_samples + self.batch_size - 1) // self.batch_size for i in range(num_batches): # 按洗牌后的索引取batch batch_indices = indices[i*self.batch_size : min((i+1)*self.batch_size, num_samples)] batch_x, batch_y = get_batch_by_indices(batch_indices, [x, y], dsize=x[2]) _, loss, w = session.run([self.optimizer, self.loss, self.w], {self.x: batch_x, self.y: batch_y}) avg_loss += loss/num_batches loss_vect.append(avg_loss) # 原有的打印、绘图代码不变
对应的get_batch_by_indices函数实现:
def get_batch_by_indices(indices, tensors, dsize): xs, ys = tensors y_b = ys[indices].reshape(-1, 1) inds, vals, _ = xs # 筛选属于当前batch的稀疏索引 mask = np.isin(inds[:,0], indices) nInds = inds[mask].copy() # 将原样本索引转换为batch内的相对索引 idx_map = {old_idx: new_idx for new_idx, old_idx in enumerate(indices)} nInds[:,0] = [idx_map[idx] for idx in nInds[:,0]] nVals = vals[mask] nDsize = (len(indices), dsize[1]) x_b = tf.SparseTensorValue(nInds, nVals, nDsize) return (x_b, y_b)
5. 训练时丢弃了部分样本
原来的num_batches = y.shape[0] // self.batch_size会丢弃最后一个不足batch_size的样本,在小数据集上这会导致模型无法学到全部数据。修改为向上取整的计算方式,确保所有样本都被用到:
num_batches = (y.shape[0] + self.batch_size - 1) // self.batch_size
验证AND函数任务
用修改后的代码测试AND函数时,模型应该能学到有效的参数。比如输入为[[0,0],[0,1],[1,0],[1,1]],标签为[0,0,0,1],训练后权重会呈现正值,偏置为负,最终能达到100%的精度。
按照以上步骤修改后,你的模型应该能稳定提升训练精度,在小数据集上达到100%,在大数据集上也能收敛到合理的精度范围。
内容的提问来源于stack exchange,提问作者npCompleteNoob

