Я пытаюсь воспроизвести следующие результаты R в Python. В этом конкретном случае прогнозирующий навык R ниже, чем навык Python, но в моем опыте это обычно не так (отсюда и причина желать воспроизвести результаты в Python), поэтому, пожалуйста, игнорируйте эту деталь здесь.

Цель состоит в том, чтобы предсказать вид цветка («версиколор» 0 или «вирджиника» 1). У нас есть 100 маркированных образцов, каждый из которых состоит из 4 цветочных характеристик: длина чашелистика, ширина чашелистика, длина лепестка, ширина лепестка. Я разделил данные на обучающие (60% данных) и тестовые наборы (40% данных). 10-кратная перекрестная проверка применяется к обучающему набору для поиска оптимальной лямбды (оптимизированный параметр - «C» в scikit-learn).

Я использую glmnet в R с альфа-значением, установленным на 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
5
Oliver Angelil 24 Апр 2017 в 10:09

2 ответа

Проблема, с которой вы столкнулись, заключается в упорядочении наборов данных (обратите внимание, я не проверял код R, но я уверен, что это проблема). Если я запускаю ваш код, а затем запустить этот

print np.bincount(y_train) # [50 10]
print np.bincount(y_test) # [ 0 40]

Вы можете видеть, что тренировочный набор не является репрезентативным для тестового набора. Однако, если я внесу пару изменений в ваш код 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]
1
piman314 24 Апр 2017 в 12:55

Я должен обижаться с парой вещей здесь.

Во-первых, «для python - функция LogisticRegressionCV scikit-learn с решателем« liblinear »(единственный решатель, который может использоваться с штрафом L1)». Это явно ложно, если только вы не хотели это квалифицировать более определенным образом. Просто посмотрите на описания sklearn.linear_model классы, и вы увидите несколько, которые конкретно упоминают L1. Я уверен, что другие позволят вам также реализовать это, но мне не очень хочется их считать.

Во-вторых, ваш метод разделения данных далеко не идеален. Посмотрите на ваши входные и выходные данные после разделения, и вы обнаружите, что в вашем разделении все тестовые образцы имеют целевые значения 1, в то время как цель 1 составляет только 1/6 вашей обучающей выборки. Этот дисбаланс, который не отражает распределение целей, приведет к тому, что ваша модель будет плохо подходить. Например, просто с помощью sklearn.model_selection.train_test_split сразу после установки классификатора LogisticRegressionCV точно так же, как вы это сделали, вы получите .92

Теперь все это говорит о том, что есть пакет glmnet для python, и вы можете повторить свои результаты с помощью этого пакет . Существует блог авторов этого проекта, в котором обсуждаются некоторые ограничения при попытке воссоздать результаты glmnet с помощью sklearn. В частности :

«В Scikit-Learn есть несколько решателей, похожих на glmnet, ElasticNetCV и LogisticRegressionCV, но у них есть некоторые ограничения. Первый работает только для линейной регрессии, а второй не обрабатывает штраф эластичной сети». - Билл Латтнер GLMNET для PYTHON

1
Grr 24 Апр 2017 в 12:57
43581850