8000 [MRG] Added penalty='none' to LogisticRegression by NicolasHug · Pull Request #12860 · scikit-learn/scikit-learn · GitHub
[go: up one dir, main page]

Skip to content

[MRG] Added penalty='none' to LogisticRegression #12860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 9, 2019

Conversation

NicolasHug
Copy link
Member
@NicolasHug NicolasHug commented Dec 24, 2018

Reference Issues/PRs

Closes #6738

What does this implement/fix? Explain your changes.

Support 'none' for penalty in LogisticRegerssion which is equivalent to setting penalty='l2' and C=np.inf.

This is supported by all solvers except liblinear which seems to take forever even on small datasets. For the other solvers, I haven't observed any significant change (in fit time or score) by using C=np.inf instead of the default value (see plots)

Any other comments?

Benchmarks on my laptop (8Go RAM, i5 7th gen) showing logloss and fit time in seconds, averaged over 10 experiments:

setting C to np.inf:

cinf

setting C to default value:

cdefault

I don't know if I did something wrong but the docs say that sag should be faster for large datasets while lbfgs should be slower, but I'm observing the reverse.

from collections import defaultdict
from time import time
import warnings

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.exceptions import ConvergenceWarning
import statsmodels.api as sm
from sklearn.metrics import log_loss


# logit = sm.Logit(y, X)
# res = logit.fit()
# print(log_loss(y, res.predict(X)))

def return_default_dict_list():
    return defaultdict(list)

solvers = ('lbfgs', 'newton-cg', 'sag', 'saga')

n_exp = 10
n_samples_list = [int(x) for x in (1e2, 1e3, 1e4, 5e4, 1e5, 5e5, 1e6)]
durations = defaultdict(return_default_dict_list)
scores = defaultdict(return_default_dict_list)
conv_warn = defaultdict(return_default_dict_list)

for n_samples in n_samples_list:
    print(n_samples)
    for exp in range(n_exp):
        X, y = make_classification(n_samples=n_samples)  # no random state
        for solver in solvers:
            with warnings.catch_warnings(record=True) as ws:
                print(solver)
                tic = time()
                lr = LogisticRegression(C=np.inf, solver=solver, random_state=0)
                lr.fit(X, y)
                duration = time() - tic
                print(f'fit duration: {duration:.3f}s')
                score = log_loss(y, lr.predict_proba(X))
                print(f'logloss: {score}')

                if any(issubclass(w.category, ConvergenceWarning) for w in ws):
                    conv_warn[n_samples][solver].append(True)
                else:
                    conv_warn[n_samples][solver].append(False)

                durations[n_samples][solver].append(duration)
                scores [n_samples][solver].append(score)


import matplotlib.pyplot as plt

fig, axs = plt.subplots(2)

for solver in solvers:
    avg_duration = [np.mean(durations[n_samples][solver]) for n_samples in n_samples_list]
    axs[0].plot(n_samples_list, avg_duration, label=solver)
    axs[0].set_ylabel('duration (s)')

    avg_score = [np.mean(scores[n_samples][solver]) for n_samples in n_samples_list]
    axs[1].plot(n_samples_list, avg_score, label=solver)
    axs[1].set_ylabel('log_loss')

for ax in (axs):
    ax.set_xscale('log')
    ax.set_xlabel('n_samples')
    ax.legend()

plt.show()

8000
Used to specify the norm used in the penalization. The 'newton-cg',
'sag' and 'lbfgs' solvers support only l2 penalties. 'elasticnet' is
only supported by the 'saga' solver.
only supported by the 'saga' solver. If 'none' (not supported by the
liblinear solver), no regularization is applied: this is equivalent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps don't bother stating the equivalence here? There is enough to read

@@ -1705,10 +1731,12 @@ class LogisticRegressionCV(LogisticRegression, BaseEstimator,
l2 penalty with liblinear solver. Prefer dual=False when
n_samples > n_features.

penalty : str, 'l1', 'l2', or 'elasticnet', optional (default='l2')
penalty : str, 'l1', 'l2', 'elasticnet' or 'none', optional (default='l2')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of supporting none in CV, when its role is to determine the optimal C under cross-validation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I was in auto pilot ^^

@NicolasHug NicolasHug changed the title [MRG] Added penalty='none' to LogisticRegression and LogisticRegressionCV [MRG] Added penalty='none' to LogisticRegression Jan 8, 2019
Copy link
Member
@jnothman jnothman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please test the error msg in LRCV(penalty='none'). Ideally, mention that 'none' is not useful w/ LRCV

only supported by the 'saga' solver. If 'none' (not supported by the
liblinear solver), no regularization is applied: this is equivalent
to setting C to ``np.inf`` with 'l2'.
only supported by the 'saga' solver.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps note that 'none' is not useful with LogisticRegressionCV?

Copy link
Member
@jnothman jnothman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@@ -88,6 +88,12 @@ Support for Python 3.4 and below has been officially dropped.
:class:`linear_model.LogisticRegressionCV` now support Elastic-Net penalty,
with the 'saga' solver. :issue:`11646` by :user:`Nicolas Hug <NicolasHug>`.

- |Feature| :class:`linear_model.LogisticRegression` now supports an
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that since we're not adding any new functionality, this should strictly be an enhancement.

@jnothman jnothman merged commit 0a07364 into scikit-learn:master Jan 9, 2019
@jnothman
Copy link
Member
jnothman commented Jan 9, 2019

Thanks!

xhluca pushed a commit to xhluca/scikit-learn that referenced this pull request Apr 28, 2019
xhluca pushed a commit to xhluca/scikit-learn that referenced this pull request Apr 28, 2019
xhluca pushed a commit to xhluca/scikit-learn that referenced this pull request Apr 28, 2019
koenvandevelde pushed a commit to koenvandevelde/scikit-learn that referenced this pull request Jul 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Suggestion: Add support for unpenalized logistic regression
3 participants
0