<jupyter_start><jupyter_text>Lineare Regression**Lineare Regression in der Vektorschreibweise:**  $\hat y = h_{\theta}(\textbf{x})=\theta^{T}*\textbf{x}$* $\theta$: Parametervektor mit Bias-Term (Achsenabschnitt) und der $\theta_{0}$ Gewichte aller Merkmale* $\theta^{T}$: Zeilenvektor (transponiert) mit allen Merkmalsgewichten* $\textbf{x}$: Zeilenvektor mit allen Merkmalen* **Wichtig:** $x_{0}$ ist immer 1. Somit ist $\theta_{0}$ immer der Achsenabschnitt.* $\theta^{T} * \textbf{x}$ ist das SkalarproduktDamit man das Modell trainieren (respektive die besten Werte für $\Theta$) ermitteln kann, braucht man ein Qualitätsmass für die Anpassung.Häufig wird der RMSE (Root Mean Square Error) oder auch nur der MSE (Mean Square Error) verwendet.$MSE(X, h_{\theta})=\frac{1}{m}\sum_{i=1}^m(\theta^{T}*x^{(i)}-y^{(i)})^2$* $h_{\theta}$ sagt aus, dass das Modell durch den Vektor $\theta$ paramet  NormalengleichungMit der Normalengleichung kann man einen Wert für $\theta$ finden, welcher die Kostenfunktion minimiert.$\hat \theta = (X^{T}* X)^{-1} * X^{T} * y$* $y$ ist der Vektor mit den Zielwerten (Labels)* Das Hoch minus 1 macht die Matrix inversiv. Siehe: [Inverse Matrix](https://de.wikipedia.org/wiki/Inverse_Matrix)<jupyter_code>import numpy as np

X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100,1)
import matplotlib.pyplot as plt
%matplotlib inline
plt.xkcd()

plt.scatter(X, y)
plt.xlabel('$X$')
plt.ylabel('$y$')
X.shape
# Füge x0 = 1 zu jedem Datenpunkt hinzu
X_b = np.c_[np.ones((100,1)), X]
X_b.shape
theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
theta_best
X_new = np.array([[0], [2]])
# Füge x0 = 1 zu jedem Datenpunkt hinzu
X_new_b = np.c_[np.ones((2,1)), X_new]
X_new_b
y_predict = X_new_b.dot(theta_best)
y_predict
plt.plot(X_new, y_predict, "r")
plt.plot(X, y, "b.")
plt.axis([0,2,0,15])
plt.legend(["Vorhersagen", "Trainingsdaten"])
plt.show()<jupyter_output><empty_output><jupyter_text>Mit Sklearn<jupyter_code>from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X, y)
print(F'Achsenabweichung: {lin_reg.intercept_}\nGewichte: {lin_reg.coef_}')
lin_reg.predict(X_new)<jupyter_output><empty_output><jupyter_text>Nachteil der NormalengleichungDie Normalengleichung wird bei sehr vielen Merkmalen sehr langsam. Vorteil der NormalengleichungSie kann grosse Datensätze sehr effizient behandeln.  Vorhersagen bei doppelt so vielen Datensätzen dauern nur etwa doppel so lange.  GradientenverfahrenDas Gradientenverfahren verändert die Parameter iterativ um so die Kostenfunktion zu minimieren.1. $\theta$ wird mit Zufallszahlen befüllt (zufällige Initialisierung)2. Die Parameter werden schrittweise verbessert um die Kostenfunktion zu senken3. Der Algorithmus konvergiert bei einem MinimumDie Lernrate (ein Hyperparameter) bestimmt die Grösse der Schritte:* Bei einer zu kleinen Lernrate kann es sehr lange dauern, bis ein Minimum gefunden wird.* Bei einer zu grossen Lernrate kann es passieren, dass nie das Minimum gefunden wird, da dieses ständig übersprungen wird.MSE ist als Kostenfunktin für lineare Regressionsmodelle eine **konvexe Funktion**. Dies bedeutet, dass wenn man zwei beliebige Punkte auf der Kurve auswählt, ihre lineare Verbindung niemals die Kurve schneidet.Somit hat die Kostenfunktion die Form einer Schüssel mit einem Minimum. Damit man dieses Minimum möglichst schnell findet, ist es wichtig, dass **alle Merkmale ähnlich skaliert sind**.Dies macht die Schüssel schön rund, also zu einer Halbkugel. Bei unskalierten Merkmalen kann man sich auch eine Ellipse-artige Schüssel vorstellen, bei einer solchen kann es länger dauern, bis man das Minimum erreicht.  Batch-GradientenverfahrenDas Batch-Gradientenverfahren funktioniert mit partieller Ableitung. Es rechnet aus, wie stark sich die Kostenfunktion ändert, wenn man den Parameter $\theta_{j}$ ändert.$$\frac{\delta}{\delta \theta_{j}} * MSE(X, h_{\theta})=\frac{2}{m}\sum_{i=1}^m(\theta^{T}*x^{(i)}-y^{(i)})x_{j}^{(i)}$$Diese Ableitung wird aber nicht einzeln sondern gleich als Vektor für alle Modellparameter berechnet.$$\nabla_{\theta}MSE(\theta)=\frac{2}{m}X^T*(X*\theta-y)$$Mit dieser Formel kann man den Gradientenvektor ermitteln, der vom Minimum weg weist. Um sich dem Minimum zu nähern muss man also in die entgegengesetzte Richtung gehen. Dazu subtrahiert man $\nabla_{\theta}MSE(\theta)$ von $\theta$.Die Lernrate $\eta$ bestimmt, wie gross die Schritte sein sollen.$$\theta^{nächster Schritt}=\theta - \eta\nabla_{\theta}MSE(\theta)$$> Das Batch-Gradientenverfahren ist bei grossen Datensätzen sehr langsam, dafür ist es geeignet bei vielen Merkmalen.Mit Hilfe der Gittersuche kann eine geeignete Lernrate gefunden werden.  Um die Anzahl Iterationen zu bestimmen kann man die Anzahl Iterationen auf einen sehr hohen Wert setzen und den Algorithmus anhalten, sobald der Gradientenvektor ganz klein wird. Der Gradientenvektor ist dann so klein, das er sich dem Minimum kaum noch nähern kann. Dies wird als Toleranz bezeichnet.<jupyter_code># Lernrate eta
eta = 0.1
n_iter = 1000
m = 100
# Zufällige Initialisierung
theta = np.random.randn(2,1)

for iteration in range(n_iter):
    gradients = 2/m * X_b.T.dot(X_b.dot(theta)-y)
    theta = theta -eta * gradients
theta<jupyter_output><empty_output><jupyter_text>Stochastisches GradientenverfahrenDas SGD (Stochastic Gradient Descent) wählt für jeden Schritt nur einen Datenpunkt zufällig aus. Dies macht ihn viel schneller als das Batch-Gradientenverfahren, aber durch seine Zufälligkeit hüpftf die Kostenfunktion ständig auf und ab und findet nie das perfekte Minimum.> SGD lässt sich auch als Out-of-Core-Algorithmus implementierenSGD kann durch die Zufälligkeit gut lokalen Minima entfliehen, kommt aber beim Minimum nie zur Ruhe.  **Simulated Annealing:**  Man wählt die Schritte zu Beginn gross um den lokalen Minima zu entfliehen und dann immer kleiner, damit der Algorithmus beim globalen Minima stehen bleibt.Durch dieses "langsame Abkühlen" wird ein bestmögliches Resultat erzielt.**Learning Schedule:**  Dies ist die Funktion zum festlegen der Lernrate.<jupyter_code>n_epochs = 50
# Hyperparameter für den Learning Schedule
t0, t1 = 5, 50
def learning_schedule(t):
    return t0 / (t + t1)
# Zufällige Initialisierung
theta = np.random.randn(2,1)
for epoch in range(n_epochs):
    for i in range(m):
        random_index = np.random.randint(m)
        xi = X_b[random_index:random_index + 1]
        yi = y[random_index:random_index + 1]
        gradients = 2 * xi.T.dot(xi.dot(theta) - yi)
        eta = learning_schedule(epoch * m + i)
        theta = theta - eta * gradients
theta<jupyter_output><empty_output><jupyter_text>Mit Sklearn<jupyter_code>from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(max_iter=50, penalty=None, eta0=0.1)
sgd_reg.fit(X, y.ravel())
print(F'Achsenabweichung: {sgd_reg.intercept_}\nGewichte: {sgd_reg.coef_}')<jupyter_output>Achsenabweichung: [4.05680318]
Gewichte: [2.97743512]<jupyter_text>Mini-Batch-GradientenverfahrenDies ist eine Kombination des Batch- und des stochastische Gradientenverfahrens. Es werden anstatt nur ein zufälliger Datenpunkt, eine selbst festgelegte Menge an zufällig gewählten Datenpunkten gewählt. Somit muss nicht bei jedem Durchlauf der gesamte Datensatzt verwendet werden aber es ist auch nicht so stark flukturierend wie wenn nur ein einziger Datenpunkt gewählt wird.  Polynomielle RegressionAuch wenn die Daten nicht linear sind, kann man sie trotzdem mit einem Linearen Modell verwenden. Dazu fügt man den Daten einfach ein weiteres vom ersten Merkaml abhängiges Merkmal hinzu. Mit SklearnMan kann PolynomialFeatures von sklearn verwenden, um ein weitere Merkmale aus den bestehnden zu erzeugen. `degree=2` steht für das Quadrat (Polynom 2. Grades).**Achtung:**  Wenn man bereits mehrere Merkmale hat und einen hohes Polynom wählt, so wächst die Anzahl Merkmale explosionsartig!<jupyter_code>m = 100
# Nicht lineare Daten
X = 6 * np.random.rand(m, 1) - 3
# Quadratische Gleichung y = ax^2 + bx + c
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)<jupyter_output><empty_output><jupyter_text>Erstes Merkmal ist immer 1:  (Beachte das oben künstlich eingefügte Rauschen: `np.random.randn(m, 1)`)  $y = 0.5^2 + x + 2.0$<jupyter_code>from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)
X[0]
X_poly[0]
lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)
print(F'Achsenabweichung: {lin_reg.intercept_}\nGewichte: {lin_reg.coef_}')<jupyter_output>Achsenabweichung: [2.06965238]
Gewichte: [[0.96684438 0.48716201]]<jupyter_text>$y = 0.451^2 + 1.146* x + 2.056$  $y = 0.5^2 + x + 2.0$  LernkurvenUm Overfitting oder Underfitting zu erkennen, kann man wie in Kapitel 2 eine Kreuzvalidierung durchführen und die Verallgemeinerungsleistung abschätzen:* Das Modell verallgemeinert sehr gut bei den Trainingsdaten, aber schlecht bei den Validierungsdaten    * Overfitting* Das Modell verallgemeinert bei Trainings- und Validierungsdaten schlecht    * UnderfittingEine andere Möglichkeit Under- und Overfitting zu erkennen sind **Lernkurven**.Eine Lernkurve zeigt die Leistung bei den Trainings- und Validierungsdaten abhängig von der Grösse der Trainingsdaten.<jupyter_code>from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split

def plot_learning_curves(model, X, y):
    X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
    train_errors, val_errors = [], []
    for m in range(1, len(X_train)):
        model.fit(X_train[:m], y_train[:m])
        y_train_predict = model.predict(X_train[:m])
        y_val_predict = model.predict(X_val)
        train_errors.append(mean_squared_error(y_train_predict, y_train[:m]))
        val_errors.append(mean_squared_error(y_val_predict, y_val))
    plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train")
    plt.plot(np.sqrt(val_errors), "b-", linewidth=2, label="val")
    # Damit nur das wichtige Angezeigt wird
    plt.ylim(0, 5)
    plt.legend()
    pass
lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)<jupyter_output><empty_output><jupyter_text>* Am Anfang ist die Lernkurve bei den Validierungsdaten sehr schlecht (hoch), da es mit wenigen Daten schlecht verallgemeinern kann.* Bei den Trainingsdaten ist sie bei wenigen Daten hingegen sehr gut (tief), da es auf die wenigen Daten die es erst kennt, sehr gut vorhersagen kann. UnderfittingWenn die Lernkurve beider Datensätze ein Plateau erreichen und weit oben liegen, dann liegt Underfitting vor.**Beheben:**  Underfitting kann man nicht mit mehr Daten beheben. Da die Daten zu komplex sind für das Modell muss ein komplexeres Modell gewählt werden.<jupyter_code>from sklearn.pipeline import Pipeline

polynomial_regression = Pipeline([
    ('poly_features', PolynomialFeatures(degree=10, include_bias=False)),
    ('lin_reg', LinearRegression())
])

plot_learning_curves(polynomial_regression, X, y)<jupyter_output><empty_output><jupyter_text>* Der Fehler auf den Trainingsdaten ist kleiner als beim Linearen Modell* Es gibt eine grosse Lücke zwischen Trainings- und Validierungsfehler OverfittingDie grosse Lücke zwischen Trainings- und Validierungsfehler besteht wegen dem Overfitting.**Beheben:**  Overfitting kann man unter anderem beheben, indem man solange mehr Trainingsdaten bereitstellt, bis der Validierungsfehler dem Trainingsfehler entspricht.  Das Gleichgewicht zwischen Bias und Varianz VerallgemeinerungsfehlerMan setzt den Verallgemeinerungsfehler eines Modells aus der Summe folgender Komponenten zusammen:**Bias:**  Bias wird durch falsche Annahmen auf die Daten verursacht. Wenn man etwa Annimmt, dass die Daten linear sind, obwohl sie quadratisch wäre, steigt das Bias.Ein Modell mit hohem Bias underfitted die Trainingsdaten sehr wahrscheinlich.**Varianz:**  Wenn das Modell zu empfindlich für die Daten ist, so steigt die Varianz. ein Modell mit hoher Varianz overfitted die Trainingsdaten eher.**Nicht reduzierbare Fehler:**  Diese Fehler werden durch das Rauschen in den Daten oder fehlerhafte Daten verursacht. Um diesen Fehler zu verhindern, muss man die Trainingsdaten bereinigen und gut aufbereiten.  Regularisierte lineare ModelleEine weitere Methode um Overfitting entgegenzuwirken ist es, das Modell zu regulieren/einschränken. Ridge-RegressionMan addiert dazu einen Regulierungsterm der Kostenfunktion hinzu. Dieser soll allerdings nur beim Trainieren zur Kostenfunktion addiert werden, nicht beim evaluieren der Leistung eines trainierten Modells!Es ist oft so, dass die Kostenfunktion beim Trainieren sich vom Qualitätsmass unterscheidet.**Regularisierungsterm:**  $\alpha\sum_{i=1}^n\theta_i^2$Je höher $\alpha$ gewählt ist, desto stärker ist das Modell regularisiert. (Desto näher sind die Gewichte bei null)**Kostenfunktion:**  $J(\theta) = MSE(\theta) + \alpha\sum_{i=1}^n\theta_i^2$**Wichtig:**  * $\theta_0$ wird nicht regularisiert. ($i=1$)* Die Daten sollten vor einer Regularisierung skaliert werden**Geschlossene Form der Ridge-Regression:**  $\hat\theta = (X^T * X + \alpha A)^{-1} * X^T * y$* $A$ ist eine Identitätsmatrix<jupyter_code>from sklearn.linear_model import Ridge
ridge_reg =  Ridge(alpha=1, solver='cholesky')
ridge_reg.fit(X, y)
ridge_reg.predict([[1.5]])
# Penalty l2 entspricht der Ridge-Regression
sgd_reg = SGDRegressor(penalty='l2')
sgd_reg.fit(X, y.ravel())
sgd_reg.predict([[1.5]])<jupyter_output>/opt/conda/lib/python3.6/site-packages/sklearn/linear_model/stochastic_gradient.py:128: FutureWarning: max_iter and tol parameters have been added in <class 'sklearn.linear_model.stochastic_gradient.SGDRegressor'> in 0.19. If both are left unset, they default to max_iter=5 and tol=None. If tol is not None, max_iter defaults to max_iter=1000. From 0.21, default max_iter will be 1000, and default tol will be 1e-3.
  "and default tol will be 1e-3." % type(self), FutureWarning)<jupyter_text>Lasso-Regression> Least Absolute Shrinkage and Selection Operator Regression**Kostenfunktion:**  $J(\theta) = MSE(\theta) + \alpha\sum_{i=1}^n|\theta_i|$**Wichtig:**  Die Lasso-Regression eliminiert die am wenigsten relevanten Merkmale komplett. (Sie werden auf 0 gesetzt)<jupyter_code>from sklearn.linear_model import Lasso

lasso_reg = Lasso(alpha=0.1)
lasso_reg.fit(X, y)
lasso_reg.predict([[1.5]])
# Penalty l1 entspricht der Lasso-Regression
sgd_reg = SGDRegressor(penalty='l1')
sgd_reg.fit(X, y.ravel())
sgd_reg.predict([[1.5]])<jupyter_output>/opt/conda/lib/python3.6/site-packages/sklearn/linear_model/stochastic_gradient.py:128: FutureWarning: max_iter and tol parameters have been added in <class 'sklearn.linear_model.stochastic_gradient.SGDRegressor'> in 0.19. If both are left unset, they default to max_iter=5 and tol=None. If tol is not None, max_iter defaults to max_iter=1000. From 0.21, default max_iter will be 1000, and default tol will be 1e-3.
  "and default tol will be 1e-3." % type(self), FutureWarning)<jupyter_text>Elastic NetElasitc Net ist lediglich eine Mischung aus den Ridge- und der Lasso-Regularisierungstermen. Mit dem Parameter $r$ kann das Mischverhältniss festgelegt werden:* $r=0$ -> Ridge* $r=1$ -> Lasso<jupyter_code>from sklearn.linear_model import ElasticNet

elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic_net.fit(X, y)
elastic_net.predict([[1.5]])<jupyter_output><empty_output><jupyter_text>Wann soll welche Regularisierung verwendet werden?Man sollte nie eine Lineare Regression ohne Regularisierung verwenden.Grundsätzlich kann man Ridge verwenden. Wenn man jedoch vermutet, dass einige Merkmale unwichtig sind, so sollte man Elastic Net oder Lasso verwenden. Hier gilt es grundsätzlich Elastic Net Lasso vorzuziehen, da sich Lasso sprunghaft verhalten kann, wenn verschiedene Merkmale miteinander korrelieren oder es mehr Merkmale als Datenpunkte gibt.  Early StoppingDie wohl einfachste Art der Regularisierung ist es, das Training zu stoppen, sobald der Validierungsfehler ein Minimum erreicht hat. Beim stochastischen- und Mini-Batch-GradientenverfahrenBei diesen Verfahren, sind die Kurvenverläufe nicht immer so glatt und es kann daher schwierig sein zu erkennen, ob nun das Minimum erreicht wurde.Um dies zu umgehen, trainiert man einfach noch ein wenig weiter und beobachtet, ob der Fehler eine Weile oberhalb des Minimums bleibt. Ist dies der Fall, setzt man die Modellparameter wieder auf den Stand, an dem der Validierungsfehler minimal war.<jupyter_code># Daten immer skalieren
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_poly_scaled = scaler.fit_transform(X_poly)
X_poly_scaled[:2]
X_poly[:2]
from copy import deepcopy

# warm_start=True -> die Methode fit() trainiert das bestehende Modell mit den neuen Daten weiter, anstatt von vorne zu beginnen.
sgd_reg = SGDRegressor(max_iter=1, warm_start=True, penalty=None, learning_rate='constant', eta0=0.0005)

X_train_poly_scaled, X_val_poly_scaled, y_train, y_val = train_test_split(X, y.ravel(), test_size=0.2)

minimum_val_error = float('inf')
best_epoch = None
best_model = None
for epoch in range(1000):
    sgd_reg.fit(X_train_poly_scaled, y_train)
    y_val_predict = sgd_reg.predict(X_val_poly_scaled)
    val_error = mean_squared_error(y_val_predict, y_val)
    if val_error < minimum_val_error:
        minimum_val_error = val_error
        best_epoch = epoch
        # deepcopy kopiert auch die Daten (Trainiertes Modell) während sklearn.base.clone ohne Daten kopiert.
        best_model = deepcopy(sgd_reg)

print(best_epoch)
best_model.predict([[1.5]])
best_model<jupyter_output><empty_output><jupyter_text>Logistische RegressionDie Logistische Regression kann auch als Klassifizierer verwendet werden. Dabei wird für jede Kategorie eine Wahrscheinlichkeit der Zugehörigkeit berechnet.Dabei wird eine sigmoide Funktion verwendet, welche nur werte zwischen 1 und 0 ausgeben kann.$\hat p = h_\theta(x) = \sigma(\theta^T * X)$**Logistische Funktion:**  $\sigma(t) = \frac{1}{1 + \exp(-t)}$**Vorhersage eines logistischen Regressionsmodells:**  $\hat y = 0  | \hat p \lt 0.5$  $\hat y = 1  | \hat p \geq 0.5$Das Modell sagt 1 vorher, wenn $\theta^T * x$ positiv ist und 0 wenn es negativ ist.**Kostenfunktion eines einzelnen Trainingspunk:**  $c(\theta) = -log(\hat p) | y = 1$  $c(\theta) = -log(1 - \hat p) | y = 0$Die Kosten sind hoch, wenn ein negativer Datenpunkt nahe 1 ist oder wenn ein positiver Datenpunkt nahe 0 ist. Dies ist gut, denn in diesen Fällen ist der Fehler ja tatsächlich vorhanden. Ist ein positiver Datenpunkt nahe 1 oder ein negativer nahe 0, so sind die Kosten klein.**Log Loss (Kostenfunktion bei der logistischen Regression):**  $J(\theta) = -\frac{1}{m}\sum_{i=1}^m[y^{(i)}log(\hat p^{(i)}) + (1 -y^{(i)})log(1 - \hat p^{(i)})]$Da die Funktion konvex ist, kann man das Gradientenverfahren oder ein anderer Optimierungsalgorithmus verwenden und dieser wird garantiert das globale Optimum finden.**Parielle Ableitung der logistischen Kostenfunktion:**  $\frac{\delta}{\delta\theta_j}J(\theta) = \frac{1}{m}\sum_{i=1}^m(\sigma(\theta^T * x^{(i)}) - y^{(i)})x_j^{(i)}$Für jeden Datenpunkt wird der Vorhersagefehler berechnet und mit dem $j^{ten}$ Merkmalswert multipliziert. Anschliessend wird daraus der Mittelwert aller Trainingsdatenpunkte berechnet.<jupyter_code>from sklearn import datasets

iris = datasets.load_iris()
list(iris.keys())
print(iris.DESCR)
# petal width
X = iris['data'][:,3:]
# 1 wenn iris-virginica, sonst 0
y = (iris['target'] == 2).astype(np.int)
from sklearn.linear_model import LogisticRegression

log_reg = LogisticRegression()
log_reg.fit(X, y)
X_new = np.linspace(0,3,1000).reshape(-1, 1)
y_proba = log_reg.predict_proba(X_new)
plt.plot(X_new, y_proba[:, 1], 'g-', label='Virginica')
plt.plot(X_new, y_proba[:, 0], 'b--', label='Nicht-Virginica')
X_new = np.linspace(0,3,1000).reshape(-1, 1)
y_proba = log_reg.predict_proba(X_new)
decision_boundary = X_new[y_proba[:, 1] >= 0.5][0]
fig, ax = plt.subplots(figsize=(8,3))

ax.plot(X[y==0], y[y==0], 'bs')
ax.plot(X[y==1], y[y==1], 'g^')

ax.plot([decision_boundary, decision_boundary], [-1, 2], 'k:', linewidth=2)
ax.plot(X_new, y_proba[:, 1], 'g-', label='Virginica')
ax.plot(X_new, y_proba[:, 0], 'b--', label='Nicht-Virginica')

plt.text(decision_boundary+0.02, 0.15, 'Decision boundary')
plt.arrow(decision_boundary, 0.08, -0.3, 0, head_width=0.05, head_length=0.1, fc='b', ec='b' )
plt.arrow(decision_boundary, 0.92, 0.3, 0, head_width=0.05, head_length=0.1, fc='g', ec='g' )

ax.set_xlabel('Petal width (cm)', fontsize=14)
ax.set_ylabel('Probability', fontsize=14)
ax.legend(loc='center left', fontsize=14)
ax.set_xlim(0,3)
ax.set_ylim(-0.02, 1.02)<jupyter_output><empty_output><jupyter_text>* Die Entscheidungsgrenze liegt etwa bei 1.6cm, dort ist nicht sicher, ob es sich um eine Virginica oder eine Nicht-Virginica handelt* Wird anstatt `predict_proba()` nur `predict()` verwendet, wird automatisch die Kategorie mit der höchsten Wahrscheinlichkeit gewählt.<jupyter_code>log_reg.predict([[1.5],[1.7]])
X = iris['data'][:, (2,3)]
y = (iris['target'] == 2).astype(np.int)
log_reg = LogisticRegression(solver="liblinear", C=10**10, random_state=42)
log_reg.fit(X, y)
x0, x1 = np.meshgrid(
        np.linspace(2.9, 7, 500).reshape(-1, 1),
        np.linspace(0.8, 2.7, 200).reshape(-1, 1),
    )
X_new = np.c_[x0.ravel(), x1.ravel()]

y_proba = log_reg.predict_proba(X_new)
plt.figure(figsize=(10, 4))
plt.plot(X[y==0, 0], X[y==0, 1], "bs")
plt.plot(X[y==1, 0], X[y==1, 1], "g^")

zz = y_proba[:, 1].reshape(x0.shape)
contour = plt.contour(x0, x1, zz, cmap=plt.cm.brg)

left_right = np.array([2.9, 7])
boundary = -(log_reg.coef_[0][0] * left_right + log_reg.intercept_[0]) / log_reg.coef_[0][1]

plt.clabel(contour, inline=1, fontsize=12)
plt.plot(left_right, boundary, "k--", linewidth=3)
plt.text(3.5, 1.5, "Not Iris-Virginica", fontsize=14, color="b", ha="center")
plt.text(6.5, 2.3, "Iris-Virginica", fontsize=14, color="g", ha="center")
plt.xlabel("Petal length", fontsize=14)
plt.ylabel("Petal width", fontsize=14)
plt.axis([2.9, 7, 0.8, 2.7])
plt.show()<jupyter_output><empty_output><jupyter_text>* Alle Punkte rechts oberhalb der grünen Linie sind mit einer 90% Wahrscheinlichkeit Iris-Virginica RegularisierungSklearn fügt der Logistischen Regression standardmässig einen l2-Strafterm hinzu.> Bei der logistischen Regression heisst der Hyperparameter zum Steuern der Regularisierung nicht alpha sondern C (Der Kehrwert von alpha). Desto grösser C, desto **schwächer** ist das Modell regularisiert.  Softmax-RegressionDie Logistische-Regression lässt sich direkt auf mehrere Kategorien verallgemeinern. Wenn man mehrere Kategorien hat, nennt sich die Softmax-Regression oder multinomiale logistische Regression.Für einen Datenpunkt **x** wird für jede Kategorie ein Score berechnet. Der Score wird in die Softmax-Funktion eingesetzt, welche für jede Kategorie eine Wahrscheinlichkeit berrechnet.Anders als bei den bisherigen Regressionsalgorithmen gibt es hier nicht einen Parametervektor $\theta$ sondern eine Parametermatrix $\Theta$ mit einem Parametervektor für jede Kategorie als Zeilen.> Die Softmax-Regression verwendet zwar mehrere Kategorien, kann aber nur eine Vohrhersagen. Man sollte sie daher nur verwenden, wenn sich die Kategorien gegenseitig ausschliessen.  **Mutlinomial/Multiclass != Multilabel** See: [What is the difference between multilabel and multiclass classification?](https://www.quora.com/What-is-the-difference-between-multilabel-and-multiclass-classification)Bei der Softmax-Regression wird die Kreuzentropie als Kostenfunktion verwendet. Mit dieser wird gemessen, wie gut die Wahrscheinlichkeiten der Kategorien mit den Zielkategorien übereinstimmen. Mit SklearnDie Logistische-Regression verwendet bei mehr als zwei Kategorien standardmässig eine One-versus-All-Strategie. Um Softmax zu verwenden muss man den Hyperparameter `multiclass='multinomial'` setzten und einen Solver angeben, der die Softmax-Regression unterstützt. **`'lbfgs'`** unterstützt die Softmax-Regression.<jupyter_code>X = iris['data'][:, (2,3)]
y = iris['target']
softmax_reg = LogisticRegression(multi_class='multinomial', solver='lbfgs', C=10)
softmax_reg.fit(X, y)
softmax_reg.predict([[5,2]])
softmax_reg.predict_proba([[5,2]])
softmax_reg.coef_
softmax_reg.intercept_<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10640483135450475, 'fraction_numerical': 0.019308984840392753, 'mean_word_length': 5.8242220353238014, 'pattern_counts': {'":': 0, '<': 64, '<?xml version=': 0, '>': 72, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '275894', 'license': 'Apache-2.0', 'n_tokens_mistral': 9039, 'n_tokens_neox': 8793, 'n_words': 2500, 'path': '04_training_linear_models_summary.ipynb', 'repo_name': 'dominikstraessle/handson-ml', 'size': '24341'}