J'ai essayé d'implémenter ADALINE en Python

Suite de Dernière fois

2.4 ADALINE et la convergence d'apprentissage

La différence avec le Perceptron précédent Le point d'utiliser une fonction d'activation linéaire au lieu d'une fonction d'étape unitaire pour mettre à jour les poids

fig1

2.5 Minimisation de la fonction de coût par la méthode de descente de gradient

J(\mathbf{w}) = \frac{1}{2} \sum_{i} (y^{(i)} - \phi (z^{(i)}))^2

L'apprentissage peut être convergé en faisant progresser l'apprentissage de sorte qu'il devienne la valeur minimale de la fonction de coût.

ADALINE peut trouver le poids qui minimise la fonction de coût en utilisant la méthode de descente de gradient en utilisant une fonction d'activation différenciable et en utilisant la somme des carrés d'erreur comme fonction de coût pour en faire une fonction convexe.

fig2

Un schéma du principe de la méthode de descente de gradient

Pour mettre à jour les poids en utilisant la méthode de descente du gradient, faites un pas en arrière le long du gradient $ \ nabla J (\ mathbf {w}) $ de la fonction de coût $ J (\ mathbf {w}) $

\mathbf{w} := \mathbf{w} + \Delta \mathbf{w}
\Delta \mathbf{w} = - \eta \nabla J(\mathbf{w})
\frac{\partial J}{\partial w_j} = - \sum_i (y^{(i)} - \phi (z^{(i)})) x_j^{(i)}

Donc

\Delta w_j = - \eta \frac{\partial J}{\partial w_j} = \eta \sum_i (y^{(i)} - \phi (z^{(i)})) x_j^{(i)}

2.5.1 Implémenter ADALINE en Python

Les règles d'apprentissage d'ADALINE sont très similaires à celles de Perceptron. Mis en œuvre ci-dessous

import numpy as np
class AdalineGD(object):
    """Classificateur ADAptive LInear NEuron
    
Paramètres
    -----------
    eta : float
Taux d'apprentissage(0.Supérieur à 0 1.Valeur inférieure ou égale à 0)
    n_iter : int
Nombre de formations dans les données de formation
    random_state : int
Graine aléatoire pour l'initialisation du poids
attribut
    -----------
    w_ :Tableau à 1 dimension
Poids après conformité
    cost_ :liste<- error_Est le coût_Changer pour
Fonction de coût de la somme des carrés d'erreur à chaque époque
    """
    def __init__(self, eta=0.01, n_iter=50, random_state=1):
        self.eta = eta
        self.n_iter = n_iter
        self.random_state = random_state
        
    def fit(self, X, y):
        """S'adapte aux données d'entraînement
        
Paramètres
        ------------
        X : {Structure de données de type tableau}, shape = [n_samples, n_features]
Données d'entraînement
            n_samples est le nombre d'échantillons, n_fonctionnalités est le nombre de fonctionnalités
        y :Structure de données de type tableau, shape = [n_samples]
Variable objective
            
Valeur de retour
        ------------
        self : object
        """
        rgen = np.random.RandomState(self.random_state)
        self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1])
        self.cost_ = []

        for _ in range(self.n_iter): #Répétez les données d'entraînement pour le nombre de formations
            net_input = self.net_input(X) #Vous n'avez pas besoin de boucler pour chaque échantillon comme vous le feriez avec une implémentation de Perceptron (je ne sais pas pourquoi Perceptron a bouclé pour chaque échantillon).
            #Parce que la méthode d'activation est juste une fonction égale
            #Ce code n'a aucun effet. Mis en œuvre comme un simple concept de fonction d'activation.
            #Dans le cas de la mise en œuvre de la régression logistique, il semble qu'il soit seulement nécessaire de passer à la fonction sigmoïde.
            output = self.activation(net_input)
            #Erreur y^(i) - φ(z^(i))Calculs de
            errors = y - output
            #Poids w_1, ..., w_m mise à jour
            self.w_[1:] += self.eta * X.T.dot(errors)
            #Poids w_Mettre à jour 0
            self.w_[0] += self.eta * errors.sum()
            #Calcul de la fonction de coût
            cost = (errors ** 2).sum() / 2.0
            #Stockage des coûts
            self.cost_.append(cost)
        return self

    def net_input(self, X):
        """Calculer l'entrée totale"""
        return np.dot(X, self.w_[1:]) + self.w_[0] #Vous n'êtes pas obligé de boucler avec xi

    def activation(self, X):
        """Calculer la sortie de la fonction d'activation linéaire"""
        return X
    
    def predict(self, X):
        """Renvoie le libellé de la classe après une étape"""
        return np.where(self.activation(self.net_input(X)) >= 0.0, 1, -1)

Essayez avec des données bien classées de Perceptron.

import numpy as np
from sklearn.datasets import load_iris
import pandas as pd
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['target'] = iris.target
df2 = df.query("target != 1").copy() #Exclure l'étiquette 1
df2["target"] -= 1 #Étiquette 1-Aligner sur 1
X = df2[['petal width (cm)', 'sepal width (cm)']].values
Y = df2['target'].values

import matplotlib.pyplot as plt
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 4))

ada1 = AdalineGD(n_iter=10, eta=0.01).fit(X, Y)
ax[0].plot(range(1, len(ada1.cost_)+1), np.log10(ada1.cost_), marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')

ada2 = AdalineGD(n_iter=10, eta=0.0001).fit(X, Y)
ax[1].plot(range(1, len(ada2.cost_)+1), ada2.cost_, marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Sum-squared-error')
ax[1].set_title('Adaline - Learning rate 0.0001')

output_5_1.png

← est le log de la somme des carrés d'erreur pour le nombre d'époques lorsque le taux d'apprentissage est de 0,01, et → est la somme des carrés des erreurs pour le nombre d'époques lorsque le taux d'apprentissage est de 0,0001.

Il s'avère que si vous ne sélectionnez pas bien η, il ne convergera pas bien.

fig3

Si le taux d'apprentissage est élevé, nous visons la valeur minimale comme indiqué sur la figure, mais nous sautons par-dessus la valeur minimale et montons vers la rive opposée.

2.5.2 Améliorer la descente de gradient grâce à la mise à l'échelle des caractéristiques

Mettez à l'échelle les fonctionnalités pour des performances optimales des algorithmes d'apprentissage automatique.

Cette fois, normalisé $ \ mathbf {x} '\ _j = \ frac {\ mathbf {x} \ _ j- \ mu_j} {\ sigma_j} $

Cela donne aux données les caractéristiques d'une distribution normale standard et permet à l'apprentissage de converger rapidement.

#Standardisation
X_std = np.copy(X)
X_std[:, 0] = (X[:, 0] - X[:, 0].mean()) / X[:, 0].std()
X_std[:, 1] = (X[:, 1] - X[:, 1].mean()) / X[:, 1].std()

from sklearn import preprocessing
ss = preprocessing.StandardScaler()
X_std2 = ss.fit_transform(X)

print(X_std[:3])
print(X_std2[:3]) #le même
[[-1.02461719  0.71907625]
 [-1.02461719 -0.4833924 ]
 [-1.02461719 -0.00240494]]
[[-1.02461719  0.71907625]
 [-1.02461719 -0.4833924 ]
 [-1.02461719 -0.00240494]]
#Fonction de tracé des limites implémentée plus tôt

from matplotlib.colors import ListedColormap

def plot_decision_regions(X, y, classifier, resolution=0.02):
    
    #Préparation des marqueurs et des cartes de couleurs
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])
    
    #Diagramme de la zone de décision
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    #Générer des points de grille
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    #Prédire en convertissant chaque entité en un tableau unidimensionnel
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    #Conversion des résultats de prédiction en taille de données de point de grille d'origine
    Z = Z.reshape(xx1.shape)
    #Tracé de contour de point de grille
    plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap)
    #Réglage de la plage d'axe
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())
    
    #Tracer des échantillons par classe
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0],
                    y=X[y == cl, 1],
                    alpha=0.8,
                    c=colors[idx],
                    marker=markers[idx],
                    label=cl,
                    edgecolor='black')
ada = AdalineGD(n_iter=15, eta=0.01)
ada.fit(X_std, Y)

plot_decision_regions(X_std, Y, classifier=ada)
plt.title('Adaline - Gradient Descent')
plt.xlabel('petal width [standardized]')
plt.ylabel('sepal width [standardized]')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Sum-squared-error')
plt.tight_layout()
plt.show()

output_9_0.png output_9_1.png

On voit qu'elle a convergé et que les frontières de la classification sont fermement fermées.

On peut confirmer que la normalisation est efficace car elle n'a pas bien convergé au même rythme d'apprentissage.

ada1 = AdalineGD(n_iter=15, eta=0.01)
ada1.fit(X, Y)

plot_decision_regions(X, Y, classifier=ada1)
plt.title('Adaline - Gradient Descent')
plt.xlabel('petal width [standardized]')
plt.ylabel('sepal width [standardized]')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

plt.plot(range(1, len(ada1.cost_) + 1), ada1.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Sum-squared-error')
plt.tight_layout()
plt.show()

output_11_0.png

output_11_1.png

↑ Même si j'ai essayé d'utiliser des données non standardisées, cela n'a pas fonctionné.

Après cela, je vais essayer avec les données qui n'ont pas pu être classées la dernière fois.

import numpy as np
import matplotlib.pyplot as plt
df3 = df.query("target != 0").copy() #Exclure l'étiquette 0
y = df3.iloc[:, 4].values
y = np.where(y == 1, -1, 1) #étiquette 1-Réglez 1 sur 1 et autre (étiquette 2) sur 1.

# plt.scatter(df3.iloc[:50, 1], df3.iloc[:50, 0], color='orange', marker='o', label='versicolor')
# plt.scatter(df3.iloc[50:, 1], df3.iloc[50:, 0], color='green', marker='o', label='virginica')
# plt.xlabel('sepal width [cm]')
# plt.ylabel('sepal length [cm]')
# plt.legend(loc='upper left')
# plt.show()

X2 = df3[['sepal width (cm)', 'sepal length (cm)']].values
from sklearn import preprocessing
sc = preprocessing.StandardScaler()
X2_std = sc.fit_transform(X2)

ada2 = AdalineGD(n_iter=15, eta=0.01)
ada2.fit(X2_std, y)

plot_decision_regions(X2_std, y, classifier=ada2)
plt.title('Adaline - Gradient Descent')
plt.xlabel('sepal width [standardized]')
plt.ylabel('sepal length [standardized]')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

plt.plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Sum-squared-error')
plt.tight_layout()
plt.show()

output_13_0.png output_13_1.png

Bien entendu, elle n'est pas linéaire et complètement séparable, mais comme la frontière est tracée à une position raisonnable, elle peut avoir convergé vers le coût minimum.


2.6 Apprentissage automatique à grande échelle et méthode de descente de gradient probabiliste

Si le nombre de données est important, le coût de calcul de la méthode jusqu'à ce point (méthode de descente de gradient par lots) devient élevé.

→ Utiliser la méthode de descente de gradient probabiliste

\Delta \mathbf{w} = \eta \sum_i (y^{(i)} - \phi (z^{(i)})) \mathbf{x}^{(i)}

Au lieu de cela, mettez à jour les pondérations étape par étape pour chaque échantillon

\eta (y^{(i)} - \phi (z^{(i)})) \mathbf{x}^{(i)}

Il est important de trier les échantillons dans un ordre aléatoire. Les avantages de cette méthode sont qu'elle converge à grande vitesse, il est facile de sortir des valeurs minimales peu profondes (il y a beaucoup de bruit sur la surface d'erreur), et elle peut être utilisée pour l'apprentissage en ligne.

Apprentissage en ligne… Lorsque de nouvelles données d'entraînement arrivent, entraînez-vous sur place et mettez à jour le modèle. Vous pouvez vous adapter rapidement aux changements.

Implémentez ADALINE en utilisant la méthode de descente de gradient probabiliste ci-dessous

import numpy as np
from numpy.random import seed

class AdalineSGD(object):
    """Classificateur ADAptive LInear NEuron
    
Paramètres
    -----------
    eta : float
Taux d'apprentissage(0.Supérieur à 0 1.Valeur inférieure ou égale à 0)
    n_iter : int
Nombre de formations dans les données de formation
    shuffle : bool (Vrai par défaut)
Si True, mélangez les données d'entraînement pour chaque époque pour éviter la circulation
    random_state : int
Graine aléatoire pour l'initialisation du poids
attribut
    -----------
    w_ :Tableau à 1 dimension
Poids après conformité
    cost_ :liste
Fonction de coût de la somme des carrés des erreurs pour faire la moyenne de tous les échantillons d'apprentissage à chaque époque
    """
    def __init__(self, eta=0.01, n_iter=10, shuffle=True, random_state=None):
        self.eta = eta
        self.n_iter = n_iter
        self.w_initialized = False #Indicateur d'initialisation du poids
        self.shuffle = shuffle
        self.random_state = random_state
        
    def fit(self, X, y):
        """S'adapte aux données d'entraînement
        
Paramètres
        ------------
        X : {Structure de données de type tableau}, shape = [n_samples, n_features]
Données d'entraînement
            n_samples est le nombre d'échantillons, n_fonctionnalités est le nombre de fonctionnalités
        y :Structure de données de type tableau, shape = [n_samples]
Variable objective
            
Valeur de retour
        ------------
        self : object
        """
        #Génération de vecteur de poids
        self._initialize_weights(X.shape[1])
        self.cost_ = []
        #Répétez pour le nombre de formations
        for i in range(self.n_iter):
            #Mélanger les données d'entraînement si spécifié
            if self.shuffle:
                X, y = self._shuffle(X, y)
            #Générer une liste pour stocker le coût de chaque échantillon
            cost = []
            #Calcul pour chaque échantillon
            for xi, target in zip(X, y):
                #Mise à jour du poids et calcul des coûts à l'aide de la fonction xi et de la variable objectif y
                cost.append(self._update_weights(xi, target))
            #Calcul du coût moyen de l'échantillon
            avg_cost = sum(cost)/len(y)
            #Stockage du coût moyen
            self.cost_.append(avg_cost)
        return self

    def partial_fit(self, X, y):
        """Ajuster les données d'entraînement sans réinitialiser les poids"""
        #Effectuer l'initialisation s'il n'est pas initialisé
        if not self.w_initialized:
            self._initialize_weights(X.shape[1])
        #Lorsque le nombre d'éléments de la variable objectif y est égal ou supérieur à 2
        #Mettre à jour les poids avec les quantités de caractéristiques xi et la cible pour chaque échantillon
        if y.ravel().shape[0] > 1:
            for xi, target in zip(X, y):
                self._update_weights(xi, target)
        #Lorsque le nombre d'éléments de la variable objectif y est 1.
        #Mettre à jour les poids avec la caractéristique X et la variable objective y pour l'ensemble de l'échantillon
        else:
            self._update_weights(X, y)
        return self

    def _shuffle(self, X, y):
        """Mélanger les données d'entraînement"""
        r = self.rgen.permutation(len(y))
        return X[r], y[r] #Le shuffle peut être réalisé en passant un tableau à l'index

    def _initialize_weights(self, m):
        """Initialiser les poids à de petits nombres aléatoires"""
        self.rgen = np.random.RandomState(self.random_state)
        self.w_ = self.rgen.normal(loc=0.0, scale=0.01, size=1 + m)
        self.w_initialized = True
        
    def _update_weights(self, xi, target):
        """Mettre à jour les pondérations à l'aide des règles d'apprentissage ADALINE"""
        #Calcul de la sortie de la fonction d'activation
        output = self.activation(self.net_input(xi))
        #Calcul d'erreur
        error = target - output
        #Mise à jour du poids
        self.w_[1:] += self.eta * xi.dot(error)
        self.w_[0] += self.eta * error
        #Calcul des coûts
        cost = 0.5 * error**2
        return cost

    def net_input(self, X):
        """Calculer l'entrée totale"""
        return np.dot(X, self.w_[1:]) + self.w_[0]

    def activation(self, X):
        """Calculer la sortie de la fonction d'activation linéaire"""
        return X
    
    def predict(self, X):
        """Renvoie le libellé de la classe après une étape"""
        return np.where(self.activation(self.net_input(X)) >= 0.0, 1, -1)

ada = AdalineSGD(n_iter=15, eta=0.01, random_state=1)
ada.fit(X_std, y)
plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline - Stochastic Gradient Descent')
plt.xlabel('petal width [standardized]')
plt.ylabel('sepal width [standardized]')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Average Cost')
plt.show()

output_17_0.png output_17_1.png

Le coût moyen diminue bientôt. La limite est la même que la méthode de descente de gradient par lots.

Pour l'apprentissage en ligne, vous pouvez mettre à jour en appelant partial_fit.

Après avoir appris Perceptron et ADALINE, j'ai considérablement approfondi ma compréhension, donc je pense qu'il sera plus facile de comprendre d'autres algorithmes d'apprentissage automatique à l'avenir avec cette application, donc c'était bien d'étudier.

Jusqu'à présent, j'utilisais d'autres algorithmes comme des boîtes noires, donc c'était bien de pouvoir en connaître le contenu.

J'ai un cahier dans Gist.

(Les diagrammes conceptuels, etc. sont cités du chapitre 2 de "Théorie et pratique par les experts en programmation Python Machine Learning" comme précédemment.)

Recommended Posts

J'ai essayé d'implémenter ADALINE en Python
J'ai essayé d'implémenter PLSA en Python
J'ai essayé d'implémenter la permutation en Python
J'ai essayé d'implémenter PLSA dans Python 2
J'ai essayé d'implémenter PPO en Python
J'ai essayé d'implémenter TOPIC MODEL en Python
J'ai essayé d'implémenter le tri sélectif en python
J'ai essayé d'implémenter un pseudo pachislot en Python
J'ai essayé d'implémenter GA (algorithme génétique) en Python
J'ai essayé d'implémenter un automate cellulaire unidimensionnel en Python
J'ai essayé d'implémenter la fonction d'envoi de courrier en Python
J'ai essayé d'implémenter le blackjack du jeu Trump en Python
J'ai essayé de mettre en œuvre un jeu de dilemme de prisonnier mal compris en Python
J'ai essayé d'implémenter StarGAN (1)
J'ai essayé d'implémenter la régression linéaire bayésienne par échantillonnage de Gibbs en python
J'ai essayé d'implémenter le jeu de cartes de Trump en Python
J'ai essayé de représenter graphiquement les packages installés en Python
Je veux facilement implémenter le délai d'expiration en python
J'ai essayé d'implémenter Mine Sweeper sur un terminal avec python
J'ai essayé d'implémenter le perceptron artificiel avec python
J'ai essayé de résumer comment utiliser les pandas de python
J'ai essayé d'implémenter Deep VQE
J'ai essayé de toucher Python (installation)
J'ai essayé de mettre en place une validation contradictoire
J'ai essayé d'implémenter Realness GAN
J'ai essayé la notification de ligne en Python
J'ai essayé d'implémenter le tri par fusion en Python avec le moins de lignes possible
J'ai essayé d'implémenter ce qui semble être un outil de snipper Windows avec Python
J'ai essayé de créer une API list.csv avec Python à partir de swagger.yaml
J'ai essayé "Comment obtenir une méthode décorée en Python"
J'ai fait un chronomètre en utilisant tkinter avec python
J'ai essayé de résumer la gestion des exceptions Python
J'ai essayé d'implémenter Autoencoder avec TensorFlow
Entrée standard Python3 que j'ai essayé de résumer
J'ai essayé d'utiliser l'optimisation bayésienne de Python
Je voulais résoudre ABC159 avec Python
J'ai essayé d'implémenter CVAE avec PyTorch
[Python] J'ai essayé de calculer TF-IDF régulièrement
J'ai essayé de toucher Python (syntaxe de base)
J'ai essayé Python> autopep8
Implémenter XENO avec python
J'ai essayé de déboguer.
Implémenter sum en Python
J'ai essayé Python> décorateur
Implémenter Traceroute dans Python 3
J'ai essayé d'implémenter la lecture de Dataset avec PyTorch
Je veux faire le test de Dunnett en Python
Essayez d'implémenter Oni Mai Tsuji Miserable avec python
Je veux créer une fenêtre avec Python
J'ai essayé de jouer à un jeu de frappe avec Python
Comment implémenter la mémoire partagée en Python (mmap.mmap)
J'ai essayé d'intégrer Keras dans TFv1.1
J'ai essayé de simuler "Birthday Paradox" avec Python
J'ai essayé la méthode des moindres carrés en Python
J'ai écrit "Introduction à la vérification des effets" en Python
J'ai essayé de sortir LLVM IR avec Python