我正在尝试自学使用Python进行logistic回归。我试图将这里的教程应用到维基百科条目中这里的小数据集上。
似乎有些地方不太对劲。维基百科和Excel Solver(使用这个视频中的方法验证)给出的截距为-4.0777,系数为1.5046,但从GitHub示例中构建的代码输出分别为-0.924200和0.756024。
我尝试使用的代码如下。有没有明显的错误?
import numpy as npimport pandas as pdfrom patsy import dmatricesfrom sklearn.linear_model import LogisticRegressionX = [0.5,0.75,1.0,1.25,1.5,1.75,1.75,2.0,2.25,2.5,2.75,3.0,3.25,3.5,4.0,4.25,4.5,4.75,5.0,5.5]y = [0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1]zipped = list(zip(X,y))df = pd.DataFrame(zipped,columns = ['study_hrs','p_or_f'])y, X = dmatrices('p_or_f ~ study_hrs', df, return_type="dataframe")y = np.ravel(y)model = LogisticRegression()model = model.fit(X,y)print(pd.DataFrame(np.transpose(model.coef_),X.columns))>>> 0Intercept -0.924200study_hrs 0.756024
回答:
解决方案
只需更改模型创建行如下:
model = LogisticRegression(C=100000, fit_intercept=False)
问题的分析
默认情况下,sklearn解决的是正则化的LogisticRegression,其拟合强度为C=1
(C值小-正则化强,C值大-正则化弱)。
这个类使用liblinear库、newton-cg和lbfgs求解器实现了正则化的logistic回归。它可以处理密集和稀疏输入。为了获得最佳性能,请使用C顺序数组或包含64位浮点数的CSR矩阵;任何其他输入格式都将被转换(并复制)。
因此,要获得他们的模型,你应该拟合
model = LogisticRegression(C=1000000)
这将得到
Intercept -2.038853 # 这实际上是截距的一半study_hrs 1.504643 # 这是正确的
此外,问题还出在你使用patsy处理数据的方式上,请看简化的、正确的示例
import numpy as npfrom sklearn.linear_model import LogisticRegressionX = [0.5,0.75,1.0,1.25,1.5,1.75,1.75,2.0,2.25,2.5,2.75,3.0,3.25,3.5,4.0,4.25,4.5,4.75,5.0,5.5]y = [0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1]X = np.array([[x] for x in X])y = np.ravel(y)model = LogisticRegression(C=1000000.)model = model.fit(X,y)print('coef', model.coef_)print('intercept', model.intercept_)
得到
coef [[ 1.50464059]]intercept [-4.07769916]
问题到底是什么?当你使用dmatrices
时,它默认会在你的输入数据中嵌入一列全为1的值(偏置项)
X = [0.5,0.75,1.0,1.25,1.5,1.75,1.75,2.0,2.25,2.5,2.75,3.0,3.25,3.5,4.0,4.25,4.5,4.75,5.0,5.5]y = [0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1]zipped = list(zip(X,y))df = pd.DataFrame(zipped,columns = ['study_hrs','p_or_f'])y, X = dmatrices('p_or_f ~ study_hrs', df, return_type="dataframe")print(X)
这导致
Intercept study_hrs0 1 0.501 1 0.752 1 1.003 1 1.254 1 1.505 1 1.756 1 1.757 1 2.008 1 2.259 1 2.5010 1 2.7511 1 3.0012 1 3.2513 1 3.5014 1 4.0015 1 4.2516 1 4.5017 1 4.7518 1 5.0019 1 5.50
这就是为什么结果中的偏置项只是真实值的一半——scikit-learn也添加了一列全为1的值…所以你现在有两个偏置项,因此最优解是给每个偏置项分配原本应该给单个偏置项的一半权重。
那么你可以做什么呢?
- 不要以这种方式使用patsy
- 禁止patsy添加偏置项
- 告诉sklearn不要添加偏置项
.
import numpy as npimport pandas as pdfrom patsy import dmatricesfrom sklearn.linear_model import LogisticRegressionX = [0.5,0.75,1.0,1.25,1.5,1.75,1.75,2.0,2.25,2.5,2.75,3.0,3.25,3.5,4.0,4.25,4.5,4.75,5.0,5.5]y = [0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1]zipped = list(zip(X,y))df = pd.DataFrame(zipped,columns = ['study_hrs','p_or_f'])y, X = dmatrices('p_or_f ~ study_hrs', df, return_type="dataframe")y = np.ravel(y)model = LogisticRegression(C=100000, fit_intercept=False)model = model.fit(X,y)print(pd.DataFrame(np.transpose(model.coef_),X.columns))
得到
Intercept -4.077571study_hrs 1.504597
如所期望的