Skip to main content

Accelerated gradient methods for nonconvex sparse learning with SCAD and MCP penalties

Project description

nonconvexAG

MIT License Python 3.7+ PyPI version

Kai Yang kai.yang2@mail.mcgill.ca
ORCID: 0000-0001-5505-6886
GPG: B080 1753 189F BAFE 10B5 3E8A 0F6C F129 F618 CEEF

Accelerated gradient methods with strong rules for nonconvex sparse learning (SCAD/MCP penalties). Paper.

Install

pip install nonconvexAG

How to Use This Package

Step 1: Import Required Functions

import numpy as np
from nonconvexAG import UAG, SolutionPath, StrongRuleSolver
from nonconvexAG.utils import add_intercept

# For legacy compatibility
from nonconvexAG import UAG_LM_SCAD_MCP, UAG_logistic_SCAD_MCP

Step 2: Prepare Your Data

CRITICAL: Always add an intercept column to your design matrix X:

# Your original data
X = np.random.randn(100, 20)  # 100 samples, 20 features
y = np.random.randn(100)       # response

# Add intercept column (REQUIRED)
X_with_intercept = add_intercept(X)  # Now shape is (100, 21)

Step 3: Choose Your Model and Penalty

# For regression problems
solver = UAG(model_type="linear", penalty="SCAD")

# For classification problems  
solver = UAG(model_type="logistic", penalty="MCP")

# Parameters:
# - penalty: "SCAD" or "MCP"
# - a: SCAD parameter (default 3.7)
# - gamma: MCP parameter (default 2.0)

Step 4: Fit the Model

# Single lambda value
solver.fit(X_with_intercept, y, lambda_=0.1)

# Access results
print(f"Intercept: {solver.intercept_}")
print(f"Coefficients: {solver.coef_}")
print(f"Iterations: {solver.n_iter_}")

Step 5: Make Predictions

# Linear regression
y_pred = solver.predict(X_with_intercept)

# Logistic regression
y_pred = solver.predict(X_with_intercept)  # class predictions (0 or 1)

Complete Examples

Example 1: Sparse Linear Regression

import numpy as np
from nonconvexAG import UAG
from nonconvexAG.utils import add_intercept

# Generate sparse data
np.random.seed(42)
n, p = 200, 50
X = np.random.randn(n, p)
true_beta = np.zeros(p)
true_beta[:5] = np.array([3, -2, 0, 1.5, -1])  # 5 true features
y = X @ true_beta + 0.5 * np.random.randn(n)

# Add intercept
X_with_intercept = add_intercept(X)

# Fit SCAD model
solver = UAG(model_type="linear", penalty="SCAD", a=3.7)
solver.fit(X_with_intercept, y, lambda_=0.1)

# Check sparsity
n_nonzero = np.sum(np.abs(solver.coef_) > 1e-6)
print(f"Non-zero coefficients: {n_nonzero}/{p}")

# Evaluate
from sklearn.metrics import mean_squared_error
y_pred = solver.predict(X_with_intercept)
mse = mean_squared_error(y, y_pred)
print(f"MSE: {mse:.4f}")

Example 2: Cross-Validation for Lambda Selection

from nonconvexAG import SolutionPath
from sklearn.model_selection import KFold

# Generate solution path
path = SolutionPath(model_type="linear", penalty="SCAD")
path.fit(X_with_intercept, y, n_lambdas=30)

# 5-fold CV
kf = KFold(n_splits=5, shuffle=True, random_state=42)
cv_errors = np.zeros(len(path.lambda_path_))

for train_idx, val_idx in kf.split(X):
    X_train = X_with_intercept[train_idx]
    y_train = y[train_idx]
    X_val = X_with_intercept[val_idx]
    y_val = y[val_idx]
    
    # Fit path on training data
    path_cv = SolutionPath(model_type="linear", penalty="SCAD")
    path_cv.fit(X_train, y_train, lambdas=path.lambda_path_)
    
    # Evaluate on validation data
    for i, lam in enumerate(path.lambda_path_):
        y_pred = X_val @ np.concatenate([[path_cv.intercept_path_[i]], 
                                         path_cv.coef_path_[:, i]])
        cv_errors[i] += np.mean((y_val - y_pred)**2)

cv_errors /= 5
best_lambda_idx = np.argmin(cv_errors)
best_lambda = path.lambda_path_[best_lambda_idx]

print(f"Best lambda by CV: {best_lambda:.4f}")

# Refit with best lambda
solver_best = UAG(model_type="linear", penalty="SCAD")
solver_best.fit(X_with_intercept, y, lambda_=best_lambda)

Example 3: High-Dimensional Data with Strong Rules

from nonconvexAG import StrongRuleSolver

# Very high dimensional
n, p = 200, 5000
X = np.random.randn(n, p)
true_beta = np.zeros(p)
# 20 true features randomly placed
true_indices = np.random.choice(p, 20, replace=False)
true_beta[true_indices] = np.random.randn(20) * 2
y = X @ true_beta + 0.1 * np.random.randn(n)

X_with_intercept = add_intercept(X)

# Standard solver would be slow
# Use strong rule for efficiency
solver = StrongRuleSolver(model_type="linear", penalty="MCP", gamma=2.0)
solver.fit(X_with_intercept, y, lambda_=0.5)

print(f"Active set size: {len(solver.active_set_)}")
print(f"Speedup factor: ~{p/len(solver.active_set_):.1f}x")

# Check feature selection accuracy
selected = np.where(np.abs(solver.coef_) > 1e-6)[0]
true_positives = len(set(selected) & set(true_indices))
print(f"True features found: {true_positives}/{len(true_indices)}")

Usage

Installation from Source

# Clone repository
git clone https://github.com/Kaiyangshi-Ito/nonconvexAG.git
cd nonconvexAG

# Install in development mode
pip install -e .

# Or build and install
python -m build
pip install dist/nonconvexAG-*.whl

Basic Usage

import numpy as np
from nonconvexAG import UAG
from nonconvexAG.utils import add_intercept

# Generate data
n, p = 100, 20
X = np.random.randn(n, p)
true_beta = np.zeros(p)
true_beta[:5] = [2, -1.5, 1, -0.5, 3]
y = X @ true_beta + 0.1 * np.random.randn(n)

# Add intercept column (IMPORTANT: always do this)
X_with_intercept = add_intercept(X)

# Fit
solver = UAG(model_type="linear", penalty="SCAD")
solver.fit(X_with_intercept, y, lambda_=0.1)

print(f"Intercept: {solver.intercept_}")
print(f"Coefficients: {solver.coef_}")

Logistic Regression

# Binary classification data
from nonconvexAG.utils import add_intercept

# Generate binary data
np.random.seed(42)
n, p = 100, 15
X = np.random.randn(n, p)
true_beta = np.zeros(p)
true_beta[:3] = [1.5, -2, 1]
logits = X @ true_beta
y = (np.random.random(n) < 1 / (1 + np.exp(-logits))).astype(int)

# Add intercept
X_with_intercept = add_intercept(X)

# Fit
solver = UAG(model_type="logistic", penalty="MCP")
solver.fit(X_with_intercept, y, lambda_=0.05)

# Predictions
y_pred = solver.predict(X_with_intercept)

accuracy = np.mean(y_pred == y)
print(f"Accuracy: {accuracy:.3f}")

Solution Path

from nonconvexAG import SolutionPath
from nonconvexAG.utils import add_intercept

# Prepare data
X_with_intercept = add_intercept(X)

# Compute path
path = SolutionPath(model_type="linear", penalty="SCAD")
path.fit(X_with_intercept, y, n_lambdas=50)

# Access results
print(f"Lambda values: {path.lambda_path_}")
print(f"Coefficients shape: {path.coef_path_.shape}")  # (n_features, n_lambdas)

# Find best lambda (example with simple validation)
mse_values = []
for i, lam in enumerate(path.lambda_path_):
    coef = path.coef_path_[:, i]
    y_pred = X @ coef  # Note: X without intercept for prediction
    mse = np.mean((y - y_pred)**2)
    mse_values.append(mse)

best_idx = np.argmin(mse_values)
print(f"Best lambda: {path.lambda_path_[best_idx]:.4f}")

Strong Rules (High-Dimensional)

from nonconvexAG import StrongRuleSolver
from nonconvexAG.utils import add_intercept

# High-dimensional case (p >> n)
n, p = 100, 1000
X = np.random.randn(n, p)
true_beta = np.zeros(p)
true_beta[:10] = np.random.randn(10) * 2  # 10 true features
y = X @ true_beta + 0.1 * np.random.randn(n)

# Add intercept
X_with_intercept = add_intercept(X)

# Fit with strong rule
solver = StrongRuleSolver(model_type="linear", penalty="SCAD")
solver.fit(X_with_intercept, y, lambda_=0.1)

print(f"Active features: {len(solver.active_set_)}/{p}")
print(f"True features recovered: {np.sum(np.abs(solver.coef_[:10]) > 1e-6)}/10")

Key Parameters

  • model_type: "linear" or "logistic"
  • penalty: "SCAD" or "MCP"
  • lambda_: regularization parameter (larger = more sparse)
  • a: SCAD parameter (default: 3.7, recommended)
  • gamma: MCP parameter (default: 2.0)

Important Notes

  1. Always add intercept column using add_intercept(X) before fitting
  2. The package assumes the first column is the intercept (not penalized)
  3. Use cross-validation to select optimal lambda_
  4. For high-dimensional data (p >> n), use StrongRule for speed

Legacy Functions

Still supported for backward compatibility:

  • UAG_LM_SCAD_MCP, UAG_logistic_SCAD_MCP
  • solution_path_LM, solution_path_logistic
  • UAG_LM_SCAD_MCP_strongrule, UAG_logistic_SCAD_MCP_strongrule
  • Memory mapping versions: memmap_* functions

Citation

@article{yang2020restarting,
  title={Restarting accelerated gradient methods with a rough strong convexity estimate},
  author={Yang, Kai},
  journal={arXiv preprint arXiv:2009.10629},
  year={2020}
}

Quick Reference

Classes

# Main solvers
UAG(model_type="linear", penalty="SCAD")           # Standard solver
SolutionPath(model_type="linear", penalty="MCP")   # Compute path
StrongRuleSolver(model_type="logistic", penalty="SCAD")  # High-dimensional

# Utilities
add_intercept(X)                    # Add intercept column
standardize_data(X, y)              # Standardize features
lambda_max_LM(X, y)                 # Get lambda_max

Common Workflows

# 1. Basic fitting
X_with_int = add_intercept(X)
solver = UAG(model_type="linear", penalty="SCAD")
solver.fit(X_with_int, y, lambda_=0.1)

# 2. Solution path
path = SolutionPath(model_type="linear", penalty="MCP")
path.fit(X_with_int, y, n_lambdas=50)

# 3. High-dimensional
solver = StrongRuleSolver(model_type="linear", penalty="SCAD")
solver.fit(X_with_int, y, lambda_=0.1)

Tips

  • Always add intercept with add_intercept(X)
  • Use StrongRule when p > n
  • Try both SCAD and MCP penalties
  • Use cross-validation for lambda selection
  • Check solver.converged_ after fitting

License

GNU Affero General Public License v3.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nonconvexag-1.0.16.tar.gz (62.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nonconvexag-1.0.16-py3-none-any.whl (53.4 kB view details)

Uploaded Python 3

File details

Details for the file nonconvexag-1.0.16.tar.gz.

File metadata

  • Download URL: nonconvexag-1.0.16.tar.gz
  • Upload date:
  • Size: 62.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for nonconvexag-1.0.16.tar.gz
Algorithm Hash digest
SHA256 d557177c6643ea5c9220fe9fe8fd10d598c77fb380815df17582d8ac7b03c345
MD5 9ae552891d0178e7d207fca42e604cc9
BLAKE2b-256 97a3c28aa6b4cad7b6e944cce821137cebcb0a235026f1f46f8d66b4ce134c00

See more details on using hashes here.

File details

Details for the file nonconvexag-1.0.16-py3-none-any.whl.

File metadata

  • Download URL: nonconvexag-1.0.16-py3-none-any.whl
  • Upload date:
  • Size: 53.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for nonconvexag-1.0.16-py3-none-any.whl
Algorithm Hash digest
SHA256 aad3d1d5bdd656dfeea79941f7c904b3a644ad09bfcd64144ca832df30e2ad3e
MD5 8f0295635d354bf3e367877cdef018e9
BLAKE2b-256 259a7a9b87728af1a489cd3811d364038f949c980f0050ed3765f37c3665d2b3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page