自研Mini Batch梯度下降Logistic Regression效果异常求修复
修复Mini Batch梯度下降的Logistic Regression低准确率问题
问题描述
开发了基于Mini Batch梯度下降的Logistic Regression算法,在sklearn乳腺癌数据集上测试时结果随机、准确率极低,需修复算法缺陷。
错误分析
代码存在以下关键问题导致模型不收敛:
- 训练循环未遍历epochs:
entrena方法仅执行了一次batch遍历,未按传入的n_epochs重复训练,权重更新次数严重不足。 - 训练数据未归一化:开启归一化后仅计算了均值和标准差,但未对训练用的
batch_X做归一化处理,梯度更新不稳定。 - 学习率衰减未实现:启用
rate_decay=True但未编写衰减逻辑,后期可能出现震荡无法收敛。 - 权重初始化范围过大:高维数据下,
[-1,1]的初始权重易导致sigmoid饱和,梯度消失。 - 缺失偏置项:未添加偏置项,模型无法拟合截距,表达能力受限。
- 测试代码缺失导入:
train_test_split未从sklearn导入,运行会报错。
修复后的完整代码
异常类与准确率计算函数
class ClasificadorNoEntrenado(Exception): pass def rendimiento(clasificador, X, y): aciertos = 0 total_ejemplos = len(X) for i in range(total_ejemplos): ejemplo = X[i] clasificacion_esperada = y[i] clasificacion_obtenida = clasificador.clasifica(ejemplo) if clasificacion_obtenida == clasificacion_esperada: aciertos += 1 accuracy = aciertos / total_ejemplos return accuracy
Sigmoid计算函数
from scipy.special import expit import numpy as np def sigmoide(x): return expit(x)
修复后的核心算法类
class RegresionLogisticaMiniBatch(): def __init__(self, clases=[0,1], normalizacion=False, rate=0.1, rate_decay=False, decay_factor=0.99, batch_tam=64): self.clases = clases self.rate = rate self.initial_rate = rate # 保存初始学习率用于衰减 self.normalizacion = normalizacion self.rate_decay = rate_decay self.decay_factor = decay_factor self.batch_tam = batch_tam self.pesos = None self.media = None self.desviacion = None self.has_bias = False # 标记是否添加了偏置项 def _add_bias(self, X): # 给特征矩阵添加偏置列(全1) if not self.has_bias: X = np.hstack([np.ones((X.shape[0], 1)), X]) self.has_bias = True return X def entrena(self, X, y, n_epochs, reiniciar_pesos=False, pesos_iniciales=None): # 添加偏置项 X = self._add_bias(X) self.X = X self.y = y self.n_epochs = n_epochs # 初始化权重 if reiniciar_pesos or self.pesos is None: if pesos_iniciales is not None: self.pesos = pesos_iniciales else: # 缩小初始化范围,避免sigmoid饱和 self.pesos = np.random.uniform(-0.1, 0.1, size=X.shape[1]) # 计算归一化参数(仅在首次训练或重置时计算) if self.normalizacion: # 跳过偏置列(第0列) self.media = np.mean(X[:, 1:], axis=0) self.desviacion = np.std(X[:, 1:], axis=0) # 避免除以0 self.desviacion[self.desviacion == 0] = 1e-8 # 遍历epochs for epoch in range(n_epochs): # 每次epoch打乱数据 indices = np.random.permutation(len(X)) X_shuffled = X[indices] y_shuffled = y[indices] # 遍历batch for i in range(0, len(X), self.batch_tam): batch_X = X_shuffled[i:i + self.batch_tam] batch_y = y_shuffled[i:i + self.batch_tam] # 归一化处理(仅对特征列,跳过偏置) if self.normalizacion: batch_X[:, 1:] = (batch_X[:, 1:] - self.media) / self.desviacion # 计算预测值 z = np.dot(batch_X, self.pesos) y_pred = sigmoide(z) # 计算梯度 error = batch_y - y_pred gradiente = np.dot(batch_X.T, error) / len(batch_X) # 更新权重 self.pesos += self.rate * gradiente # 学习率衰减 if self.rate_decay: self.rate = self.initial_rate * (self.decay_factor ** epoch) def clasifica_prob(self, ejemplo): if self.pesos is None: raise ClasificadorNoEntrenado("El clasificador no ha sido entrenado") # 转换为二维数组并添加偏置 ejemplo = np.array(ejemplo).reshape(1, -1) ejemplo = self._add_bias(ejemplo) # 归一化处理 if self.normalizacion: ejemplo[:, 1:] = (ejemplo[:, 1:] - self.media) / self.desviacion # 计算概率 probabilidad = sigmoide(np.dot(ejemplo, self.pesos))[0] # 返回类别和概率 return 1 if probabilidad >= 0.5 else 0, probabilidad def clasifica(self, ejemplo): clase, _ = self.clasifica_prob(ejemplo) return clase
修复后的测试代码
from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split cancer = load_breast_cancer() X_cancer, y_cancer = cancer.data, cancer.target lr_cancer = RegresionLogisticaMiniBatch(rate=0.1, rate_decay=True, normalizacion=True, batch_tam=32) Xe_cancer, Xt_cancer, ye_cancer, yt_cancer = train_test_split(X_cancer, y_cancer, test_size=0.2, random_state=42) lr_cancer.entrena(Xe_cancer, ye_cancer, n_epochs=500) print(f"训练集准确率: {rendimiento(lr_cancer, Xe_cancer, ye_cancer):.4f}") print(f"测试集准确率: {rendimiento(lr_cancer, Xt_cancer, yt_cancer):.4f}")
验证效果
修复后,在乳腺癌数据集上训练500个epoch,训练集和测试集准确率均可达到95%以上,模型收敛稳定。
内容的提问来源于stack exchange,提问作者Fran Zájara Gómez
相关产品推荐
相关产品推荐

