[Python] LASSO-Regression mit Gleichungsbeschränkung unter Verwendung der Multiplikatormethode

LASSO-Regressionsproblem mit linearer Gleichungsbeschränkung bei der Implementierung des Algorithmus eines Papiers [^ 1]

\begin{align}
\mathop{\text{minimize}}_\beta\quad &\frac{1}{2}\|y-X\beta\|_2^2 + \rho \|\beta\|_1 \quad\\
\text{subject to}\quad &A\beta = b
\end{align}

Es gab eine Szene zu lösen. Da die Berechnung in dem zuvor implementierten LASSO mit Gleichheits- / Ungleichheitsbeschränkung schwer ist, wurde eine neue Version mit nur Gleichheitsbeschränkung implementiert. Setzen Sie in der Implementierung $ Q = X ^ TX, \ p = -X ^ Ty $ und haben Sie ein gleichwertiges Problem

\begin{align}
\mathop{\text{minimize}}_\beta\quad &\frac{1}{2}\beta^TQ\beta + p^T\beta + \rho \|\beta\|_1 \quad\\
\text{subject to}\quad &A\beta = b
\end{align}

Ich dachte über das Lösen nach.

Multiplikatormethode

Optimierungsproblem mit eingeschränkter linearer Gleichung

\begin{align}
\mathop{\text{minimize}}_{\beta,z}\quad &f(\beta) + g(z) \quad\\
\text{subject to}\quad &M\beta + Fz = c
\end{align}

Die Multiplikatormethode ist als Methode zum Umgang mit bekannt. Bei der Multiplikatormethode wird die erweiterte Lagrange-Funktion verwendet

\mathcal{L}_\tau (\beta,z,\nu) = f(\beta) + g(z) + \nu^T(M\beta + Fz - c) + \frac{\tau}{2}\|M\beta + Fz - c\|_2^2

Aktualisieren Sie die Variablen, um sie alternativ für $ \ beta, z $ zu minimieren.

\newcommand{\argmin}{\mathop{\rm arg\,min}\limits}
\begin{align}
\beta^{(t+1)} &\leftarrow \argmin_\beta \mathcal{L}_\tau (\beta, z^{(t)}, \nu^{(t)})\\
z^{(t+1)} &\leftarrow \argmin_z \mathcal{L}_\tau (\beta^{(t+1)}, z, \nu^{(t)})\\
\nu^{(t+1)} &\leftarrow \nu^{(t)} + \tau(M\beta^{(t+1)}+Fz^{(t+1)}-c)
\end{align}

Anwendung der Multiplikatormethode auf die gleichungsbeschränkte LASSO-Regression

\begin{align}
\mathop{\text{minimize}}_\beta\quad &\frac{1}{2}\beta^TQ\beta + p^T\beta + \rho \|z\|_1 \quad\\
\text{subject to}\quad &\left(\begin{array}{c}
A\\
I
\end{array}\right)\beta + \left(\begin{array}{c}
O\\
-I
\end{array}\right) z = \left(\begin{array}{c}
b\\
0
\end{array}\right)
\end{align}

Durch Umschreiben des Problems

\begin{align}
f(\beta)=\frac12 \beta^TQ\beta + p^T\beta,\ 
g(z) = \rho\|z\|_1,\\
M=\left(\begin{array}{c}
A\\
I
\end{array}\right),\ 
F=\left(\begin{array}{c}
O\\
-I
\end{array}\right),\ 
c=\left(\begin{array}{c}
b\\
0
\end{array}\right)
\end{align}

Die Multiplikatormethode kann angewendet werden. Setzen Sie zu diesem Zeitpunkt die erweiterte Lagrange-Funktion auf $ \ nu = (u ^ T, v ^ T) ^ T $.

\mathcal{L}_\tau (\beta,z,\nu) = \frac12 \beta^TQ\beta + p^T\beta + \rho\|z\|_1 + u^T(A\beta - b) + v^T(\beta - z) + \frac{\tau}{2}\|A\beta - b\|_2^2 + \frac{\tau}{2}\|\beta - z\|_2^2

Kann geschrieben werden als.

Beta-Update-Formel

Optimaler Zustand

\frac{\partial \mathcal{L}_\tau}{\partial \beta}(\beta, z^{(t)}, \nu^{(t)}) = Q\beta + p + A^Tu^{(t)} + v^{(t)} + \tau A^T(A\beta - b) + \tau(\beta - z^{(t)}) = 0

Lösen

\beta^{(t+1)} = \left(Q+\tau(A^TA+I)\right)^{-1}\left(-p-A^Tu^{(t)}-v^{(t)}+\tau A^Tb+\tau z^{(t)}\right)

Update mit.

z Formel aktualisieren

Optimaler Zustand

\partial_z \mathcal{L}_\tau(\beta^{(t+1)}, z, \nu^{(t)}) = \rho\partial_z \|z\|_1 - v^{(t)} - \tau\left(\beta^{(t+1)} - z\right)\ni0

Aus der Update-Formel

z^{(t+1)} = s\left(\beta^{(t+1)} + \frac1\tau v^{(t)}; \frac\rho\tau\right)

Wird erhalten. Wobei $ s (\ cdot; \ cdot) $ eine weiche Schwellenwertfunktion ist

s(x; \lambda) = \mathrm{sign}\,(x)\max\left\{x - \lambda, 0\right\}

Repräsentiert.

Variable Initialisierung

\begin{align}
\mathop{\text{minimize}}_\beta\quad &\|\beta\|_1 \quad\\
\text{subject to}\quad &A\beta= b
\end{align}

Durch Lösen erhalten wir $ \ beta ^ {(0)} $, das die Randbedingung erfüllt. Dieses Problem kann als lineares Programmierproblem angesehen werden, indem eine neue Variable $ \ gamma $ eingeführt wird.

\begin{align}
\mathop{\text{minimize}}_{\beta,\gamma}\quad &\sum_{i=1}^p \gamma_i \quad\\
\text{subject to}\quad &A\beta= b,\\
&\ \gamma\geq0,\ \gamma\geq\beta,\ \gamma\geq-\beta
\end{align}

Zusammenfassung des Algorithmus

  1. Initialisieren Sie $ \ beta ^ {(0)} $ und $ z ^ {(0)} \ leftarrow \ beta ^ {(0)}, \ u ^ {(0)} \ leftarrow0, \ v ^ { (0)} \ leftarrow0, \ t \ leftarrow 0 $.
  2. Geben Sie $ z ^ {(t)} $ aus, wenn die Stoppbedingung erfüllt ist
  3. \beta^{(t+1)} \leftarrow \left(Q+\tau(A^TA+I)\right)^{-1}\left(-p-A^Tu^{(t)}-v^{(t)}+\tau A^Tb+\tau z^{(t)}\right)
  4. z^{(t+1)} \leftarrow s\left(\beta^{(t+1)} + \frac1\tau v^{(t)}; \frac\rho\tau\right)
  5. u^{(t+1)} \leftarrow u^{(t)} + \tau(A\beta^{(t+1)}-b)
  6. v^{(t+1)} \leftarrow v^{(t)} + \tau(\beta^{(t+1)}-z^{(t+1)})
  7. Setzen Sie $ t \ leftarrow t + 1 $ und fahren Sie mit Schritt 2 fort.

Quellcode

In Python implementiert. Wir haben scipy.optimize.linprog verwendet, um $ \ beta $ zu initialisieren.

import numpy as np
from scipy.optimize import linprog


class EqualityConstrainedL1QuadraticProgramming:
    """
    Problem
    ----------
    minimize 0.5 * βQβ + pβ + ρ||β||_1
    subject to Aβ = b

    Algorithm
    ----------
    Alternating Direction Method of Multipliers (ADMM)

    Parameters
    ----------
    A : np.ndarray, optional (default=None)
        The equality constraint matrix.

    b : np.ndarray, optional (default=None)
        The equality constraint vector.

    rho : float, optional (default=1.0)
        Constant that multiplies the L1 term.

    tau : float, optional (default=None)
        Constant that used in augmented Lagrangian function.

    tol : float, optional (default=1e-4)
        The tolerance for the optimization.

    max_iter : int, optional (default=300)
        The maximum number of iterations.

    extended_output : bool, optional (default=False)
        If set to True, objective function value will be saved in `self.f`.

    Attributes
    ----------
    f : a list of float
        objective function values.

    coef_ : array, shape (n_features,) | (n_targets, n_features)
        parameter vector.

    n_iter_ : int | array-like, shape (n_targets,)
        number of iterations run by admm to reach the specified tolerance.
    """
    def __init__(
            self,
            A: np.ndarray,
            b: np.ndarray,
            rho: float = 1.0,
            tau: float = 1.0,
            tol: float = 1e-4,
            max_iter: int = 300,
            extended_output: bool = False
    ):
        self.A = A
        self.b = b
        self.rho = rho
        self.tau = tau
        self.tol = tol
        self.max_iter = max_iter
        self.extended_output = extended_output

        self.f = list()
        self.coef_ = None
        self.n_iter_ = None

    def _linear_programming(self, n_features: int) -> np.ndarray:
        """
        Solve following problem.

        Problem:
            minimize ||β||_1 subject to Aβ=b

        Solver:
            scipy.optimize.linprog

        Parameters
        ----------
        n_features : int
            The dimension of decision variables

        Returns
        -------
        : np.ndarray, shape = (n_features, )
            The values of the decision variables that minimizes the objective function while satisfying the constraints.
        """
        # equality constraint matrix and vector
        c = np.hstack((np.zeros(n_features), np.ones(n_features)))
        A_eq = np.hstack((self.A, np.zeros_like(self.A)))
        b_eq = self.b

        # inequality constraint matrix and vector
        eye = np.eye(n_features)
        A_ub = np.vstack((
            np.hstack((eye, -eye)),
            np.hstack((-eye, -eye)),
            np.hstack((np.zeros((n_features, n_features)), -eye))
        ))
        b_ub = np.zeros(n_features * 3)

        return linprog(c=c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq)['x'][:n_features]

    def _prox(self, x: np.ndarray, rho: float) -> np.ndarray:
        """
        Proximal operator for L1 constraint

        Parameters
        ----------
        x : np.ndarray, shape = (n_features, )
            1D array

        rho : float
            a threshold
        """
        return np.sign(x) * np.maximum((np.abs(x) - rho), 0)

    def _objective_function(self, x: np.ndarray, Q: np.ndarray, p: np.ndarray, rho: float):
        """
        Return 1 / 2 * x^T Qx + p^T x + rho * ||x||_1
        """
        return 0.5 * x.dot(Q.dot(x)) + p.dot(x) + np.sum(np.abs(x)) * rho

    def fit(self, Q: np.ndarray, p: np.ndarray) -> None:
        """
        Parameters
        ----------
        Q : np.ndarray, shape = (n_features, n_features)
            Quadratic coefficient.

        p : np.ndarray, shape = (n_features, )
            Linear coefficient.
        """
        n_features = Q.shape[0]
        tau_inv = 1 / self.tau

        # initialize constants
        Q_inv = np.linalg.inv(Q * tau_inv + self.A.T.dot(self.A) + np.eye(n_features))
        p_tau = p * tau_inv
        Ab = self.A.T.dot(self.b)
        rho_tau = self.rho * tau_inv

        # initialize variables
        beta = self._linear_programming(n_features)
        z = np.copy(beta)
        u = np.zeros_like(self.b)
        v = np.zeros_like(beta)

        cost = self._objective_function(beta, Q, p, self.rho)
        # save objective function value if necessary
        if self.extended_output:
            self.f.append(cost)

        # main loop
        for k in range(self.max_iter):
            r = - p_tau - self.A.T.dot(u) - v + Ab + z
            beta = np.dot(Q_inv, r)
            z = self._prox(beta + v, rho_tau)
            u = u + self.A.dot(beta) - self.b
            v = v + beta - z

            pre_cost = cost
            cost = self._objective_function(beta, Q, p, self.rho)
            # save objective function value if necessary
            if self.extended_output:
                self.f.append(cost)
            if np.abs(cost - pre_cost) < self.tol:
                break

        # save result
        self.coef_ = z
        self.n_iter_ = k

Prüfung

Es befasst sich mit dem folgenden Optimierungsproblem mit eingeschränkten linearen Gleichungen.

\begin{align}
\mathop{\text{minimize}}_\beta\quad &\frac{1}{2}\|y-X\beta\|_2^2 + \rho \|\beta\|_1 \quad\\
\text{subject to}\quad &\sum_i\beta_i=1
\end{align}

Ungefähr habe ich Daten mit $ y = X \ beta + \ varepsilon $ generiert und mit dem uneingeschränkten Lasso von Scikit-Learn verglichen.

n_samples, n_features = 1000, 100
param = 0.5

#Datengenerierung
np.random.seed(0)
X = np.random.randn(n_samples, n_features)
beta = np.zeros(n_features)
beta[:n_features//10] = np.random.randn(n_features//10)
beta = beta / np.sum(beta)
y = X.dot(beta) + np.random.randn(n_samples)

#Implementiert
clf = EqualityConstrainedL1QuadraticProgramming(A = np.ones((1, n_features)), b = np.array([1.]), rho=param, tau=n_features)
clf.fit(X.T.dot(X) / n_samples, -X.T.dot(y) / n_samples)

# Scikit-Learn
lasso = Lasso(alpha=param)
lasso.fit(X, y)

Versuchsergebnis: \betaZeichnen Sie den Wert von,\sum_i\beta_i,\ \frac12\|y-X\beta\|_2^2Zeigen Sie den Wert von an. Im Vergleich zu Scikit-learn kann bestätigt werden, dass $ \ beta $ erhalten wird, das die Einschränkungsbedingung erfüllt.

label_true = f'True: sum = {round(np.sum(beta), 5)}, func = {round(0.5 * np.linalg.norm(y - X.dot(beta)), 5)}'
label_equality_constrained = f'Equality Constrained: sum = {round(np.sum(clf.coef_), 5)}, func = {round(0.5 * np.linalg.norm(y - X.dot(clf.coef_)), 5)}'
label_sklearn = f'Scikit-learn: sum = {round(np.sum(lasso.coef_), 5)}, func = {round(0.5 * np.linalg.norm(y - X.dot(lasso.coef_)), 5)}'

plt.figure(figsize=(15, 10))
plt.plot(beta, 'go-', label=label_true)
plt.plot(clf.coef_, 'ro-', label=label_equality_constrained)
plt.plot(lasso.coef_, 'bo-', label=label_sklearn)
plt.grid(True)
plt.legend()
plt.show()

image.png

Recommended Posts

[Python] LASSO-Regression mit Gleichungsbeschränkung unter Verwendung der Multiplikatormethode
Arbeiten mit OpenStack mit dem Python SDK
Einfache LASSO-Regressionsanalyse mit Python (keine Theorie)
Flächenextraktionsmethode mit dem Zellautomaten Versuchen Sie die Flächenextraktion aus dem Bild mit Growcut (Python).
Kernel-Methode mit Python
Erläuterung des Konzepts der Regressionsanalyse mit Python Teil 2
Bestimmen Sie den Schwellenwert mithilfe der P-Tile-Methode in Python
Berechnen Sie den Regressionskoeffizienten der einfachen Regressionsanalyse mit Python
Erläuterung des Konzepts der Regressionsanalyse mit Python Teil 1
Erläuterung des Konzepts der Regressionsanalyse mit Python Extra 1
Ich habe versucht, die Python-Bibliothek von Ruby mit PyCall zu verwenden
[S3] CRUD mit S3 unter Verwendung von Python [Python]
[Python] Berechnungsmethode mit numpy
[Python] Verwenden von OpenCV mit Python (Basic)
Erstellen einer Finite-Elemente-Methode (FEM) mit Python ~ vba → Python-Übersetzung ~
Lineare Regressionsmethode mit Numpy
[Python] Lineare Regression mit Scicit-Learn
Rufen Sie die API mit python3 auf.
Berühren Sie NoSQL mit Python mithilfe des Oracle NoSQL Database Cloud Simulators
Verwenden von OpenCV mit Python @Mac
Senden Sie mit Python mit Google Mail
[In-Database Python Analysis-Lernprogramm mit SQL Server 2017] Schritt 6: Verwenden des Modells
Erstellen einer Finite-Elemente-Methode (FEM) mit Python ~ damyarou spielt ~ herum
Lösen Sie das Python-Rucksackproblem mit der Branch-and-Bound-Methode
Vervollständigung von Python mit Emacs mit Company-Jedi
Extrahieren Sie die xz-Datei mit Python
[Python] Verwenden von OpenCV mit Python (Bildfilterung)
Verwenden von Rstan aus Python mit PypeR
[Python] Verwenden von OpenCV mit Python (Bildtransformation)
[Python] Verwenden von OpenCV mit Python (Kantenerkennung)
Holen Sie sich das Wetter mit Python-Anfragen
Holen Sie sich das Wetter mit Python-Anfragen 2
Finden Sie die Bearbeitungsentfernung (Levenshtein-Entfernung) mit Python
Klicken Sie mit Python auf die Etherpad-Lite-API
Installieren Sie das Python-Plug-In mit Netbeans 8.0.2
Ich mochte den Tweet mit Python. ..
Extrahieren Sie die Targz-Datei mit Python
Hinweise zur Verwendung von rstrip mit Python.
Beherrsche den Typ mit Python [Python 3.9 kompatibel]
Versuchen Sie es mit dem Python Cmd-Modul
Logistische Regressionsanalyse Selbst erstellt mit Python
Bei Verwendung von MeCab mit virtualenv python
Vorsichtsmaßnahmen bei Verwendung von sechs mit Python 2.5
Treffen Sie eine Methode einer Klasseninstanz mit der Python Bottle Web API
Erstellen Sie mit dem Python-Anforderungsmodul einen Datensatz mit Anhängen in KINTONE
Erhalten und schätzen Sie die Form des Kopfes mit Dlib und OpenCV mit Python
[Einführung in Python] Wie wird mit der continue-Anweisung wiederholt?
[AWS] Verwenden von INI-Dateien mit Lambda [Python]
Versuchen Sie es mit der Wunderlist-API in Python
[Python] Legen Sie den Diagrammbereich mit matplotlib fest
Versuchen Sie eine Formel mit Σ mit Python
Sattelpunktsuche mit der Gradientenmethode
Versuchen Sie, die Kraken-API mit Python zu verwenden
Verwenden von Python und MeCab mit Azure Databricks
Maschinelles Lernen mit Python (2) Einfache Regressionsanalyse
Überprüfen Sie die Existenz der Datei mit Python
Versuchen Sie, die Kamera mit Pythons OpenCV zu verwenden
Socket-Kommunikation über Socket-Server mit Python jetzt