You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Multinomial Logit模型在Python与Stata中结果不一致的技术咨询

为什么scikit-learn和Stata的Multinomial Logit结果不一致?

我经常遇到用户问这个问题,核心差异基本集中在几个关键设置和预处理步骤上,下面帮你拆解原因和对应的解决办法:

一、最常见的差异原因

1. 基准类别选择不同

Stata的mlogit命令默认将因变量的最后一个类别作为基准类别(比如prog如果是1/2/3,就会选3作为参照);而scikit-learn的LogisticRegressionmulti_class='multinomial'模式)默认将因变量的第一个类别(按标签编码顺序)作为基准。这直接导致所有非基准类别的系数和截距都是相对于不同参照的,数值自然不一样。

2. 名义变量的编码方式不一致

你的自变量ses是名义变量,Stata会自动将其转换为虚拟变量并删除最后一个类别以避免共线性;但如果在Python中处理不当:

  • 直接用LabelEncoderses转成数值(比如1/2/3),会被模型当成有序变量,完全偏离名义变量的建模逻辑;
  • OneHotEncoder但没设置drop参数,会保留所有类别,模型会通过正则化自动处理共线性,但结果和Stata的虚拟变量编码不一致。

3. 正则化(惩罚项)的默认设置不同

scikit-learn的LogisticRegression默认启用L2正则化penalty='l2'C=1.0),用来防止过拟合;但Stata的mlogit默认是没有正则化的。正则化会压缩系数大小,这是导致系数数值差异的重要原因。

4. 优化算法与收敛精度差异

Stata默认用牛顿-拉夫森法拟合模型,而scikit-learn默认的solver(比如liblinear)对multinomial模型的支持有限,且默认的收敛阈值(tol)和迭代次数(max_iter)可能和Stata不同,导致最终收敛的系数有细微差别。

二、对应的解决办法

1. 统一基准类别

要么调整Stata的基准类别,要么调整Python的:

  • Stata端:用basecategory()参数指定和Python一致的基准,比如如果Python选prog=1作为基准,Stata命令写:
    mlogit prog ses read write math, basecategory(1)
    
  • Python端:调整因变量的编码顺序,让Stata的基准类别成为Python的第一个类别。比如prog的类别是1/2/3,想以3为基准:
    import pandas as pd
    # 重新编码prog,让3对应0(成为基准),1对应1,2对应2
    df['prog'] = df['prog'].map({3:0, 1:1, 2:2})
    

2. 正确处理名义变量的编码

确保Python生成的虚拟变量和Stata完全一致:

# 对ses生成虚拟变量,删除Stata用作基准的类别(比如ses=3)
df_dummies = pd.get_dummies(df, columns=['ses'], drop_first=False)
# 删除基准类别对应的列
df_dummies = df_dummies.drop('ses_3', axis=1)
# 提取自变量和因变量
X = df_dummies[['ses_1', 'ses_2', 'read', 'write', 'math']]
y = df_dummies['prog']

3. 关闭scikit-learn的正则化

设置penalty='none'(新版本scikit-learn支持),或者用极大的C值近似无正则化:

from sklearn.linear_model import LogisticRegression

# 配置模型:multinomial模式、无正则化、用牛顿法(和Stata算法接近)
model = LogisticRegression(
    multi_class='multinomial',
    penalty='none',
    solver='newton-cg',  # 牛顿-共轭梯度法,和Stata的算法逻辑一致
    fit_intercept=True,  # 保留截距,和Stata一致
    max_iter=1000,       # 增加迭代次数确保收敛
    tol=1e-8             # 提高收敛精度
)
model.fit(X, y)

# 查看系数和截距
print("系数:\n", model.coef_)
print("截距:\n", model.intercept_)

4. 验证数据一致性

最后一定要确认两边用的是完全相同的数据集:

  • 检查是否都排除了缺失值(Stata默认会排除含缺失值的样本,Python要用df.dropna()同步);
  • 变量的顺序、标签完全一致,没有遗漏或额外的变量。

按照以上步骤调整后,你应该能得到和Stata完全一致的系数和截距结果。

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

火山引擎 最新活动