Suite de Dernière fois
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
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.
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}) $
Donc
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')
← 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.
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.
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()
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()
↑ 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()
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.
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
Au lieu de cela, mettez à jour les pondérations étape par étape pour chaque échantillon
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()
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