You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自研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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 18:24:55