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

为何Sklearn逻辑回归迭代训练与单次训练的评估指标有差异?

问题

我正在以迭代(逐epoch)方式训练sklearn的LogisticRegression,目的是借此获取每一轮训练后的均方误差(MSE),而这无法通过常规方式获取。但我发现,这种迭代训练方式得到的评估指标(如F1 score)与单次调用model.fit()训练得到的结果略有不同,想请教原因。

常规训练方式

model = LogisticRegression(
        random_state=42,
        C=.001,
        penalty='l1',
        max_iter=500,
        solver='saga')

model.fit(X_train, y_train)

# Predict Class labels
pred_labels_val = model.predict(X_val)
pred_labels_test = model.predict(X_test)

# F1 Score
f1_val = metrics.f1_score(y_val, pred_labels_val)
f1_test = metrics.f1_score(y_test, pred_labels_test)

迭代训练方式

为验证结果一致性,我将迭代轮次设置为之前常规训练模型收敛时的轮次,其余参数保持一致,以此获取每一轮的MSE。

epochs = model.named_steps['logisticregression'].n_iter_[0]

model = LogisticRegression(
        random_state=42,
        C=.001,
        penalty='l1',
        max_iter=1,  # this forces training to run for only one epoch
        solver='saga')

for epoch in range(epochs): 
    model.fit(X_train, y_train)
    
# Predict Class labels
pred_labels_val = model.predict(X_val)
pred_labels_test = model.predict(X_test)

# F1 Score
f1_val = metrics.f1_score(y_val, pred_labels_val)
f1_test = metrics.f1_score(y_test, pred_labels_test)

但最终得到的评估指标(如F1 score)存在差异。

原因分析与解决办法

核心原因

  • fit()的初始化逻辑差异:即便设置了random_state=42,每次调用model.fit()时,SAGA求解器的内部状态(梯度缓存、学习率调整轨迹等)会被重置,而非延续上一次的状态。而单次fit()是连续完成所有迭代,内部状态连贯更新,两者的参数更新路径完全不同。
  • 收敛判断的实际差异:单次fit()中,sklearn会在每轮后检查损失变化是否低于tol阈值,达到收敛就提前停止,但n_iter_记录的是总轮次。你用这个轮次强制跑满迭代训练,可能单次训练最后几轮已经停止更新参数,而迭代训练还在继续调整,导致参数结果偏离。
  • 样本打乱的随机性差异:SAGA默认开启shuffle=True,单次fit()仅在训练开始时打乱一次样本;而迭代训练每次调用fit()都会重新打乱样本,每轮的样本顺序不同,参数更新方向自然出现偏差。

解决方法

要让迭代训练和单次训练结果一致,核心是保留求解器的训练状态,而非每次fit()都重新初始化:

  1. 开启warm_start=True:这是关键参数,开启后每次fit()会从上一次训练的参数继续更新,而非重置。修改后的迭代训练代码如下:
# 先获取常规训练的收敛轮次
base_model = LogisticRegression(
        random_state=42,
        C=.001,
        penalty='l1',
        max_iter=500,
        solver='saga')
base_model.fit(X_train, y_train)
epochs = base_model.n_iter_[0]

# 初始化带warm_start的模型
model = LogisticRegression(
        random_state=42,
        C=.001,
        penalty='l1',
        max_iter=1,
        solver='saga',
        warm_start=True)  # 保留训练状态

for epoch in range(epochs): 
    model.fit(X_train, y_train)
    # 在这里记录每一轮的MSE
    y_pred_proba = model.predict_proba(X_train)[:, 1]
    mse = metrics.mean_squared_error(y_train, y_pred_proba)
    print(f"Epoch {epoch+1}, MSE: {mse}")

# 后续评估逻辑不变
pred_labels_val = model.predict(X_val)
pred_labels_test = model.predict(X_test)
f1_val = metrics.f1_score(y_val, pred_labels_val)
f1_test = metrics.f1_score(y_test, pred_labels_test)
  1. 固定样本顺序(可选):如果需要完全一致的样本遍历顺序,可设置shuffle=False,但这可能降低模型收敛速度,需根据场景权衡。
  2. 手动加入收敛判断:在迭代训练中复刻sklearn的收敛逻辑(监测损失变化是否小于tol),避免多跑不必要的迭代轮次。

内容的提问来源于stack exchange,提问作者neverreally

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 04:45:40