2017-04-24 170 views
4

我試圖在Python中重現以下R結果。在這種特殊情況下,R預測技能低於Python技能,但通常我的經驗並非如此(因此希望以Python重現結果的原因),因此請忽略此處的細節。使用Iris數據集在Python中重現R的LASSO/Logistic迴歸結果使用Iris數據集

目標是預測花種('雜色'0或'維吉尼卡'1)。我們有100個標籤樣本,每個樣本包含4個花的特徵:萼片長度,萼片寬度,花瓣長度,花瓣寬度。我將數據分爲訓練(60%的數據)和測試集(40%的數據)。將10倍交叉驗證應用於訓練集以搜索最佳lambda(在scikit-learn中優化的參數爲「C」)。

我在R中使用glmnet,alpha設置爲1(用於LASSO懲罰),python,scikit-learn的LogisticRegressionCV函數與「liblinear」求解器(唯一可用於L1懲罰的求解器) 。兩種語言的交叉驗證中使用的評分指標是相同的。然而不知怎麼的,模型結果是不同的(對每個特徵發現的截距和係數變化很大)。

R代碼裏面

library(glmnet) 
library(datasets) 
data(iris) 

y <- as.numeric(iris[,5]) 
X <- iris[y!=1, 1:4] 
y <- y[y!=1]-2 

n_sample = NROW(X) 

w = .6 
X_train = X[0:(w * n_sample),] # (60, 4) 
y_train = y[0:(w * n_sample)] # (60,) 
X_test = X[((w * n_sample)+1):n_sample,] # (40, 4) 
y_test = y[((w * n_sample)+1):n_sample] # (40,) 

# set alpha=1 for LASSO and alpha=0 for ridge regression 
# use class for logistic regression 
set.seed(0) 
model_lambda <- cv.glmnet(as.matrix(X_train), as.factor(y_train), 
         nfolds = 10, alpha=1, family="binomial", type.measure="class") 

best_s <- model_lambda$lambda.1se 
pred <- as.numeric(predict(model_lambda, newx=as.matrix(X_test), type="class" , s=best_s)) 

# best lambda 
print(best_s) 
# 0.04136537 

# fraction correct 
print(sum(y_test==pred)/NROW(pred)) 
# 0.75 

# model coefficients 
print(coef(model_lambda, s=best_s)) 
#(Intercept) -14.680479 
#Sepal.Length 0   
#Sepal.Width 0 
#Petal.Length 1.181747 
#Petal.Width 4.592025 

Python代碼

from sklearn import datasets 
from sklearn.linear_model import LogisticRegressionCV 
from sklearn.preprocessing import StandardScaler 
import numpy as np 

iris = datasets.load_iris() 
X = iris.data 
y = iris.target 
X = X[y != 0] # four features. Disregard one of the 3 species.                             
y = y[y != 0]-1 # two species: 'versicolor' (0), 'virginica' (1). Disregard one of the 3 species.                    

n_sample = len(X) 

w = .6 
X_train = X[:int(w * n_sample)] # (60, 4) 
y_train = y[:int(w * n_sample)] # (60,) 
X_test = X[int(w * n_sample):] # (40, 4) 
y_test = y[int(w * n_sample):] # (40,) 

X_train_fit = StandardScaler().fit(X_train) 
X_train_transformed = X_train_fit.transform(X_train) 

clf = LogisticRegressionCV(n_jobs=2, penalty='l1', solver='liblinear', cv=10, scoring = ‘accuracy’, random_state=0) 
clf.fit(X_train_transformed, y_train) 

print clf.score(X_train_fit.transform(X_test), y_test) # score is 0.775 
print clf.intercept_ #-1.83569557 
print clf.coef_ # [ 0, 0, 0.65930981, 1.17808155] (sepal length, sepal width, petal length, petal width) 
print clf.C_ # optimal lambda: 0.35938137 

回答

3

有幾件事,在上面的例子不同:所述係數中的

  1. 量表

    • glmnet(https://cran.r-project.org/web/packages/glmnet/glmnet.pdf)標準化的數據和「The係數始終以原始比例返回「。因此,在調用glmnet之前,您沒有調整數據。
    • Python代碼將數據標準化,然後適用於標準化數據。這種情況下的係數是標準化的,而不是原始的尺度。這使得例子之間的係數不可比。
  2. LogisticRegressionCV默認使用stratifiedfolds。 glmnet使用k倍。

  3. 他們正在擬合不同的方程。請注意,scikit-learn邏輯在邏輯方面適合(http://scikit-learn.org/stable/modules/linear_model.html#logistic-regression)與正規化。 glmnet將正規化置於懲罰之上。

  4. 選擇正則化優勢嘗試 - glmnet默認爲100 lambda來嘗試。 scikit LogisticRegressionCV默認爲10.由於scikit解決方程式,範圍介於1e-4和1e4之間(http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegressionCV.html#sklearn.linear_model.LogisticRegressionCV)。

  5. 公差是不同的。在我遇到的一些問題中,收緊寬容顯着地改變了係數。

    • glmnet默認脫粒到1E-7
    • LogisticRegressionCV默認TOL到1E-4
    • 甚至使它們相同後,他們可以不測量同樣的事情。我不知道線性措施。 glmnet - 「每個內部座標 - 下降循環都會一直繼續,直到係數更新後的目標的最大變化小於零偏差的閾值時爲止。」

你可能想嘗試打印正規化路徑,看看他們都非常相似,只是停在不同的強度。那麼你可以研究爲什麼。

即使改變了你可以改變的不是上述所有內容,也可能得不到相同的係數或結果。儘管您在不同的軟件中解決相同的問題,但軟件解決問題的方式可能會有所不同。我們看到不同的尺度,不同的方程,不同的默認值,不同的求解器等。

1

,你現在看到的是數據集的排序問題(注意我沒有檢查將R代碼,但我確定這是問題)。如果我運行你的代碼然後運行這個程序,你可以看到訓練集不代表測試集。但是,如果我對Python代碼進行一些更改,那麼我的測試準確性爲0.9

from sklearn import datasets 
from sklearn import preprocessing 
from sklearn import model_selection 
from sklearn.linear_model import LogisticRegressionCV 
from sklearn.preprocessing import StandardScaler 
import numpy as np 

iris = datasets.load_iris() 
X = iris.data 
y = iris.target 
X = X[y != 0] # four features. Disregard one of the 3 species.                             
y = y[y != 0]-1 # two species: 'versicolor' (0), 'virginica' (1). Disregard one of the 3 species.                    

X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, 
                    test_size=0.4, 
                    random_state=42, 
                    stratify=y) 


X_train_fit = StandardScaler().fit(X_train) 
X_train_transformed = X_train_fit.transform(X_train) 

clf = LogisticRegressionCV(n_jobs=2, penalty='l1', solver='liblinear', cv=10, scoring = 'accuracy', random_state=0) 
clf.fit(X_train_transformed, y_train) 

print clf.score(X_train_fit.transform(X_test), y_test) # score is 0.9 
print clf.intercept_ #0. 
print clf.coef_ # [ 0., 0. ,0., 0.30066888] (sepal length, sepal width, petal length, petal width) 
print clf.C_ # [ 0.04641589] 
+0

非常感謝。然而train_test_split函數似乎很方便(請參閱我對Grr的回覆)我不確定這是否是兩種語言之間差異的原因。我將嘗試在兩者之間實現平衡分割(在R和Python中),然後更新我的初始文章。 –

+0

我建議創建兩個文件,一個用於您的訓練集,另一個用於測試集,並將這些文件讀入Python和R.這是確保您的數據正確分割的最安全的方法。 – ncfirth

1

我不得不在這裏帶幾件東西。首先,「對於python,scikit-learn的LogisticRegressionCV函數和」liblinear「求解器(唯一可以與L1懲罰一起使用的求解器)」。這顯然是錯誤的,除非你打算以更明確的方式來證明這一點。只要看看sklearn.linear_model課程的描述,你會看到一些特別提到L1的。我相信其他人也允許你實現它,但我並不是真的想要數它們。其次,你的分割數據的方法並不理想。看看分割後的輸入和輸出,你會發現在你的分割中,所有的測試樣本的目標值都是1,而1的目標只佔你訓練樣本的1/6。這種不平衡,這不代表目標的分佈,將導致你的模型不適合。例如,只用sklearn.model_selection.train_test_split開箱,然後重新安裝LogisticRegressionCV分類完全按照你有,結果在.92

的艾柯雷現在所有這一切是說,有一個glmnet package for python,你可以使用這個包複製您的結果。這個項目的作者有一篇博客,討論試圖用sklearn重新創建glmnet結果的一些限制。具體來說:

「Scikit,瞭解具有類似於glmnet,ElasticNetCV和LogisticRegressionCV幾個求解器,但它們有一定的侷限性的第一個只適用於線性迴歸,而後者不處理彈性淨處罰「。比爾 - Lattner GLMNET FOR PYTHON

+0

感謝您的時間。我應該說「使用LogisticRegressionCV功能時可以使用L1懲罰的唯一解決方案」。文檔列出了可以使用的四種解算器('newton-cg','lbfgs','liblinear','sag');只有liblinear才能與L1一起使用。是的分裂是不理想的。我不會這樣做的;但是因爲我在R和Python之間以相同的方式分裂,所以我不確定這是不同結果的原因(我不確定如何在R中進行平衡分割)。 python的glmnet包可能是解決方案。謝謝。 –

相關問題