L'article précédent était ici
Dans cet article, nous allons essayer d'approcher la fonction sin
en utilisant le code introduit dans la [Série Introduction à l'apprentissage profond](#Série d'apprentissage profond).
Au fait, j'ai trouvé beaucoup de bugs et de correctifs dans ce processus, donc je les reflète dans chaque article.
Le code s'exécute sur un notebook jupyter. L'introduction du notebook jupyter est présentée sur ici. Forfaits requis
numpy
matplotlib
tqdm
Il est devenu.
tqdm
ne montre la progression que d'une manière facile à comprendre, donc si cela pose problème, vous pouvez supprimer la partie appropriée du code de test et cela fonctionnera.
Le code expérimental (test.ipynb
) de cet article est téléchargé sur github tel quel.
LayerManager
](fonctionnalité #Port vers layermanager)Veuillez vous référer aux [Articles précédents](#Deep Learning Series) pour le processus de production détaillé.
_layererror.py
class LayerManagerError(Exception):
"""Classe de base pour les erreurs définies par l'utilisateur dans les modules de couche"""
pass
class AssignError(LayerManagerError):
def __init__(self, value=None):
if not value is None:
self.value = value
self.message = (str(value)
+ ": Assigning that value is prohibited.")
else:
self.value = None
self.message = "Assigning that value is prohibited."
def __str__(self):
return self.message
class UnmatchUnitError(LayerManagerError):
def __init__(self, prev, n):
self.prev = prev
self.n = n
self.message = "Unmatch units: {} and {}.".format(prev, n)
def __str__(self):
return self.message
class UndefinedLayerError(LayerManagerError):
def __init__(self, type_name):
self.type = type_name
self.message = str(type_name) + ": Undefined layer type."
def __str__(self):
return self.message
baselayer.py
import numpy as np
class BaseLayer():
"""
Toutes les classes de couches sous-jacentes
Décrivez le traitement commun à la couche intermédiaire et à la couche de sortie.
"""
def __init__(self, *, prev=1, n=1,
name="", wb_width=5e-2,
act="ReLU", opt="Adam",
act_dic={}, opt_dic={}, **kwds):
self.prev = prev #Nombre de sorties de la couche précédente=Nombre d'entrées dans cette couche
self.n = n #Nombre de sorties dans cette couche=Nombre d'entrées vers la couche suivante
self.name = name #Le nom de cette couche
#Définir le poids et le biais
self.w = wb_width*np.random.randn(prev, n)
self.b = wb_width*np.random.randn(n)
#Fonction d'activation(classe)Avoir
self.act = get_act(act, **act_dic)
#Optimiseur(classe)Avoir
self.opt = get_opt(opt, **opt_dic)
def forward(self, x):
"""
Mise en œuvre de la propagation directe
"""
#Souvenez-vous de votre entrée
self.x = x.copy()
#Propagation vers l'avant
self.u = [email protected] + self.b
self.y = self.act.forward(self.u)
return self.y
def backward(self, grad):
"""
Mise en œuvre de la rétropropagation
"""
dact = grad*self.act.backward(self.u, self.y)
self.grad_w = self.x.T@dact
self.grad_b = np.sum(dact, axis=0)
self.grad_x = [email protected]
return self.grad_x
def update(self, **kwds):
"""
Mise en œuvre de l'apprentissage des paramètres
"""
dw, db = self.opt.update(self.grad_w, self.grad_b, **kwds)
self.w += dw
self.b += db
middlelayer.py
import numpy as np
class MiddleLayer(BaseLayer):
"""
Classe moyenne
La couche d'entrée est également traitée comme l'une des couches intermédiaires en termes de montage.
"""
pass
outputlayer.py
import numpy as np
class OutputLayer(BaseLayer):
"""
Classe de couche de sortie
"""
def __init__(self, *, err_func="Square", **kwds):
#Fonction de perte(classe)Avoir
self.errfunc = get_err(err_func)
super().__init__(**kwds)
def backward(self, t):
"""
Mise en œuvre de la rétropropagation
"""
#Lorsque la fonction d'activation de la couche de sortie est la fonction softmax et la fonction de perte est l'erreur d'entropie croisée
#Cas séparés de propagation d'erreur
if isinstance(self.act, type(get_act("softmax"))) \
and isinstance(self.errfunc, type(get_err("Cross"))):
dact = self.y - t
self.grad_w = self.x.T@dact
self.grad_b = np.sum(dact, axis=0)
self.grad_x = [email protected]
return self.grad_x
elif isinstance(self.act, type(get_act("sigmoid"))) \
and isinstance(self.errfunc, type(get_err("Binary"))):
dact = self.y - t
self.grad_w = self.x.T@dact
self.grad_b = np.sum(dact, axis=0)
self.grad_x = [email protected]
return self.grad_x
else:
grad = self.errfunc.backward(self.y, t)
return super().backward(grad)
def get_error(self, t):
self.error = self.errfunc.forward(self.y, t)
return self.errfunc.total_error()
layermanager.py
import numpy as np
class _TypeManager():
"""
Classe de gestionnaire pour les types de couches
"""
N_TYPE = 2 #Nombre de types de couches
MIDDLE = 0 #Numérotation des couches intermédiaires
OUTPUT = 1 #Numérotation des couches de sortie
class LayerManager(_TypeManager):
"""
Classe Manager pour la gestion des couches
"""
def __init__(self):
self.__layer_list = [] #Liste des couches
self.__name_list = [] #Liste de noms pour chaque couche
self.__ntype = np.zeros(self.N_TYPE, dtype=int) #Nombre de couches par type
def __repr__(self):
layerRepr= "layer_list: " + repr(self.__layer_list)
nameRepr = "name_list: " + repr(self.__name_list)
ntypeRepr = "ntype: " + repr(self.__ntype)
return (layerRepr + "\n"
+ nameRepr + "\n"
+ ntypeRepr)
def __str__(self):
layerStr = "layer_list: " + str(self.__layer_list)
nameStr = "name_list: " + str(self.__name_list)
ntypeStr = "ntype: " + str(self.__ntype)
return (layerStr + "\n"
+ nameStr + "\n"
+ ntypeStr)
def __len__(self):
"""
Fonctions intégrées Python`len`Décrit l'opération lorsqu'elle est appelée depuis.
Renvoie la somme du nombre de couches par type.
"""
return int(np.sum(self.__ntype))
def __getitem__(self, key):
"""
Par exemple
lm = LayerManager()
+----------------+
| (Ajouter un élément à lm) |
+----------------+
x = lm[3].~~
Est appelé lors de l'accès à un élément d'une liste ou d'un tableau, comme
Décrivez l'opération à ce moment-là.
tranche et str,Autoriser uniquement l'accès int.
"""
if isinstance(key, slice):
#Si la clé est une tranche, reportez-vous à la liste des calques avec tranche.
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
return self.__layer_list[key]
elif isinstance(key, str):
#Si key est une chaîne, récupérez l'index dans la liste des noms de chaque couche et
#Renvoie les éléments de la liste des couches applicables.
if key in self.__name_list:
index = self.__name_list.index(key)
return self.__layer_list[index]
else:
#Si la clé n'existe pas, une KeyError est émise.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Si key est un entier, renvoie l'élément correspondant dans la liste des couches.
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
return self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
def __setitem__(self, key, value):
"""
Par exemple
lm = LayerManager()
+----------------+
| (Ajouter un élément à lm) |
+----------------+
lm[1] = x
Est appelé lors de l'accès à un élément d'une liste ou d'un tableau, comme
Décrivez l'opération à ce moment-là.
Seul l'écrasement des éléments est autorisé et l'ajout de nouveaux éléments est interdit.
"""
value_type = ""
if isinstance(value, list):
#Spécifié sur le côté droit'value'Mais'list'Si
#Tous les éléments'BaseLayer'Erreur si classe ou n'en hérite pas.
if not np.all(
np.where(isinstance(value, BaseLayer), True, False)):
self.AssignError()
value_type = "list"
elif isinstance(value, BaseLayer):
#Spécifié sur le côté droit'value'Mais'BaseLayer'Est-ce une classe?
#Erreur s'il n'est pas hérité.
self.AssignError(type(value))
if value_type == "":
value_type = "BaseLayer"
if isinstance(key, slice):
#Si la clé est une tranche, écrasez l'élément dans la liste des calques.
#pourtant'value_type'Mais'list'Sinon, une erreur.
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
if value_type != "list":
self.AssignError(value_type)
self.__layer_list[key] = value
elif isinstance(key, str):
#Si key est une chaîne, récupérez l'index dans la liste des noms de chaque couche et
#Remplacez les éléments dans la liste des couches applicables.
#pourtant'value_type'Mais'BaseLayer'Sinon, une erreur.
if value_type != "BaseLayer":
raise AssignError(value_type)
if key in self.__name_list:
index = self.__name_list.index(key)
self.__layer_list[index] = value
else:
#Si la clé n'existe pas, une KeyError est émise.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Si la clé est un entier, écrasez l'élément correspondant dans la liste des couches.
#pourtant'value_type'Mais'BaseLayer'Sinon, une erreur.
#Aussi, une valeur anormale(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
if value_type != "BaseLayer":
raise AssignError(value_type)
self.__layer_list[key] = value
else:
raise KeyError(key, ": Undefined such key type.")
def __delitem__(self, key):
"""
Par exemple
lm = LayerManager()
+----------------+
| (Ajouter un élément à lm) |
+----------------+
del lm[2]
Parce qu'il est appelé lorsque l'élément de la liste ou du tableau est accédé par l'instruction del comme
Décrivez l'opération à ce moment-là.
Si l'élément spécifié existe, il sera supprimé et renommé.
"""
if isinstance(key, slice):
#Si la clé est une tranche, supprimez l'élément spécifié tel quel
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
del self.__layer_list[slice]
del self.__name_list[slice]
elif isinstance(key, str):
#Si key est une chaîne, récupérez l'index dans la liste des noms de chaque couche et
#Supprimez l'élément concerné.
if key in self.__name_list:
del self.__layer_list[index]
del self.__name_list[index]
else:
#Si la clé n'existe pas, une KeyError est émise.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Si la clé est un entier, supprimez l'élément correspondant dans la liste des couches.
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
del self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
#Renommer
self._rename()
def _rename(self):
"""
Lorsque la dénomination de la liste de noms enfreint les règles en raison de l'opération de liste
Renommez la liste de dénomination et chaque couche pour respecter à nouveau les règles.
La règle de dénomination est[Type de calque][Quel nombre]ça ira.
Si le type de calque est Couche intermédiaire, Milieu
Sortie pour la couche de sortie
Il est abrégé en.
Le nombre est compté par type.
Aussi, ici encore__Compte ntypes.
"""
#Initialiser le nombre de couches par type
self.__ntype = np.zeros(self.N_TYPE)
#Recompter et renommer chaque couche
for i in range(len(self)):
if "Middle" in self.__name_list[i]:
self.__ntype[self.MIDDLE] += 1
self.__name_list[i] = "Middle{}".format(
self.__ntype[self.MIDDLE])
self.__layer_list[i].name = "Middle{}".format(
self.__ntype[self.MIDDLE])
elif "Output" in self.__name_list[i]:
self.__ntype[self.OUTPUT] += 1
self.__name_list[i] = "Output{}".format(
self.__ntype[self.OUTPUT])
self.__layer_list[i].name = "Output{}".format(
self.__ntype[self.OUTPUT])
else:
raise UndefinedLayerType(self.__name_list[i])
def append(self, *, name="Middle", **kwds):
"""
Implémentation de la méthode append familière, qui est une méthode pour ajouter des éléments à une liste.
"""
if "prev" in kwds:
# 'prev'Est inclus dans le mot-clé
#Cela signifie que le nombre d'éléments de la couche précédente est spécifié.
#Fondamentalement, il est censé être le moment d'insérer la première couche, donc
#En dehors de cela, il est essentiellement déterminé automatiquement et n'est pas spécifié.
if len(self) != 0:
if kwds["prev"] != self.__layer_list[-1].n:
#Erreur s'il ne correspond pas au nombre d'unités à la fin.
raise UnmatchUnitError(self.__layer_list[-1].n,
kwds["prev"])
else:
if len(self) == 0:
#La première couche doit toujours spécifier le nombre d'unités d'entrée.
raise UnmatchUnitError("Input units", "Unspecified")
else:
#Le nombre d'unités dans la dernière couche'kwds'Ajouter à
kwds["prev"] = self.__layer_list[-1].n
#Lisez le type de couche et modifiez le nom selon la règle de dénomination
if name == "Middle" or name == "mid" or name == "m":
name = "Middle"
elif name == "Output" or name == "out" or name == "o":
name = "Output"
else:
raise UndefinedLayerError(name)
#Ajoutez un calque.
if name == "Middle":
#Incrémenter la couche par type
self.__ntype[self.MIDDLE] += 1
#Ajouter au nom
name += str(self.__ntype[self.MIDDLE])
#Ajouter à la liste de noms
self.__name_list.append(name)
#Enfin, créez un calque et ajoutez-le à la liste.
self.__layer_list.append(
MiddleLayer(name=name, **kwds))
elif name == "Output":
#C'est également la même chose.
self.__ntype[self.OUTPUT] += 1
name += str(self.__ntype[self.OUTPUT])
self.__name_list.append(name)
self.__layer_list.append(
OutputLayer(name=name, **kwds))
#Si vous ne dessinez pas d'instruction else ici, modifiez le nom selon la règle de dénomination
#Déjà anormal au stade'name'Est omis.
def extend(self, lm):
"""
Un autre gestionnaire de couches qui existe déjà dans la méthode d'extension'lm'Des éléments de
Tout ajouter.
"""
if not isinstance(lm, LayerManager):
# 'lm'Erreur si l'instance de n'est pas LayerManager.
raise TypeError(type(lm), ": Unexpected type.")
if len(self) != 0:
if self.__layer_list[-1].n != lm[0].prev:
#Avec le nombre d'unités dans votre dernière couche
# 'lm'Erreur si le nombre d'entrées dans la première couche de n'est pas le même.
raise UnmatchUnitError(self.__layer_list[-1].n,
lm[0].prev)
#Chaque'extend'Ajouter par méthode
self.__layer_list.extend(lm.layer_list)
self.__name_list.extend(lm.name_list)
#Renommer
self._rename()
def insert(self, prev_name, name="Middle", **kwds):
"""
Dans la méthode d'insertion, spécifiez le nom du calque précédent et combinez-le avec ce calque.
Ajoutez un élément.
"""
# 'prev_name'Erreur si n'existe pas.
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 'prev'Est inclus dans le mot-clé
# 'prev_name'Erreur s'il ne correspond pas au nombre d'unités du calque spécifié dans.
if "prev" in kwds:
if kwds["prev"] \
!= self.__layer_list[self.index(prev_name)].n:
raise UnmatchUnitError(
kwds["prev"],
self.__layer_list[self.index(prev_name)].n)
# 'n'Est inclus dans le mot-clé
if "n" in kwds:
# 'prev_name'Si ce n'est pas le dernier
if prev_name != self.__name_list[-1]:
#Erreur s'il ne correspond pas au nombre d'unités dans la couche suivante.
if kwds["n"] != self.__layer_list[
self.index(prev_name)+1].prev:
raise UnmatchUnitError(
kwds["n"],
self.__layer_list[self.index(prev_name)].prev)
#S'il n'y a pas encore d'éléments'append'Donnez une erreur pour utiliser la méthode.
if len(self) == 0:
raise RuntimeError(
"You have to use 'append' method instead.")
#Obtenir l'index de l'emplacement d'insertion
index = self.index(prev_name) + 1
#Lisez le type de couche et modifiez le nom selon la règle de dénomination
if name == "Middle" or name == "mid" or name == "m":
name = "Middle"
elif name == "Output" or name == "out" or name == "o":
name = "Output"
else:
raise UndefinedLayerError(name)
#Insérer un élément
#En ce moment,'name'Ne respecte pas encore les règles de dénomination,
#Je le renommerai plus tard alors ne vous inquiétez pas.
if "Middle" in name:
self.__layer_list.insert(index,
MiddleLayer(name=name, **kwds))
self.__name_list.insert(index, name)
elif "Output" in name:
self.__layer_list.insert(index,
OutputLayer(name=name, **kwds))
self.__name_list.insert(index, name)
#Renommer
self._rename()
def extend_insert(self, prev_name, lm):
"""
C'est la fonction d'origine.
Il se comporte comme une combinaison de la méthode extend et de la méthode insert.
En termes simples, c'est comme insérer un autre gestionnaire de calques.
"""
if not isinstance(lm, LayerManager):
# 'lm'Erreur si l'instance de n'est pas LayerManager.
raise TypeError(type(lm), ": Unexpected type.")
# 'prev_name'Erreur si n'existe pas.
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
#Le nombre d'unités des couches avant et après l'emplacement spécifié et les première et dernière couches de lm
#S'ils ne correspondent pas, une erreur se produit.
if len(self) != 0:
if self.__layer_list[self.index(prev_name)].n \
!= lm.layer_list[0].prev:
#Avec le nombre d'unités dans votre emplacement désigné'lm'Le premier nombre d'unités dans
#S'ils ne correspondent pas, une erreur se produit.
raise UnmatchUnitError(
self.__layer_list[self.index(prev_name)].n,
lm.layer_list[0].prev)
if prev_name != self.__name_list[-1]:
# 'prev_name'N'est-ce pas ma dernière couche
if lm.layer_list[-1].n \
!= self.__layer_list[self.index(prev_name)+1].prev:
# 'lm'Le nombre d'unités à la fin et au niveau suivant de votre emplacement désigné
# 'prev'Erreur s'il ne correspond pas au nombre d'unités.
raise UnmatchUnitError(
lm.layer_list[-1].n,
self.__layer_list[self.index(prev_name)+1].prev)
else:
#Si vous n'avez aucun élément'extend'J'obtiens une erreur lors de l'utilisation de la méthode.
raise RuntimeError(
"You have to use 'extend' method instead.")
#Obtenir l'index de l'emplacement d'insertion
index = self.index(prev_name) + 1
#Éléments après l'emplacement d'insertion'buf'Après avoir évacué vers, retirez-le une fois
#Ajouter un élément à l'aide de la méthode extend
layer_buf = self.__layer_list[index:]
name_buf = self.__name_list[index:]
del self.__layer_list[index:]
del self.__name_list[index:]
self.extend(lm)
#Ajouter l'élément qui a été évacué
self.__layer_list.extend(layer_buf)
self.__name_list.extend(name_buf)
#Renommer
self._rename()
def remove(self, key):
"""
La méthode remove supprime l'élément avec le nom spécifié.
Il est également autorisé à être spécifié par index.
"""
#Déjà implémenté'del'La phrase est OK.
del self[key]
def index(self, target):
return self.__name_list.index(target)
def name(self, indices):
return self.__name_list[indices]
@property
def layer_list(self):
return self.__layer_list
@property
def name_list(self):
return self.__name_list
@property
def ntype(self):
return self.__ntype
errors.py
import numpy as np
class Error():
def __init__(self, *args,**kwds):
self.error = 0
def forward(self, *args,**kwds):
pass
def backward(self, *args,**kwds):
pass
def total_error(self, *args,**kwds):
return np.sum(self.error)/self.error.size
class SquareError(Error):
def forward(self, y, t, *args,**kwds):
self.error = 0.5 * (y - t)**2
return self.error
def backward(self, y, t, *args,**kwds):
return y - t
class BinaryCrossEntropy(Error):
def forward(self, y, t, *args,**kwds):
self.error = - t*np.log(y) - (1 - t)*np.log(1 - y)
return self.error
def backward(self, y, t, *args,**kwds):
return (y - t) / (y*(1 - y))
class CrossEntropy(Error):
def forward(self, y, t, *args,**kwds):
self.error = - t*np.log(y)
return self.error
def backward(self, y, t, *args,**kwds):
return - t/y
get_err.py
_err_dic = {"Square": SquareError,
"Binary": BinaryCrossEntropy,
"Cross": CrossEntropy,
}
def get_err(name, *args,**kwds):
if name in _err_dic.keys():
errfunc = _err_dic[name](*args,**kwds)
else:
raise ValueError(name + ": Unknown error function")
return errfunc
activations.py
import numpy as np
class Activator():
def __init__(self, *args,**kwds):
pass
def forward(self, *args,**kwds):
raise Exception("Not Implemented")
def backward(self, *args,**kwds):
raise Exception("Not Implemented")
def update(self, *args,**kwds):
pass
class step(Activator):
def forward(self, x, *args,**kwds):
return np.where(x > 0, 1, 0)
def backward(self, x, *args,**kwds):
return np.zeros_like(x)
class identity(Activator):
def forward(self, x, *args,**kwds):
return x
def backward(self, x, *args,**kwds):
return np.ones_like(x)
class bentIdentity(Activator):
def forward(self, x, *args,**kwds):
return 0.5*(np.sqrt(x**2 + 1) - 1) + x
def backward(self, x, *args,**kwds):
return 0.5*x/np.sqrt(x**2 + 1) + 1
class hardShrink(Activator):
def __init__(self, lambda_=0.5, *args,**kwds):
self.lambda_ = lambda_
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
return np.where((-self.lambda_ <= x) & (x <= self.lambda_),
0, x)
def backward(self, x, *args,**kwds):
return np.where((-self.lambda_ <= x) & (x <= self.lambda_),
0, 1)
class softShrink(Activator):
def __init__(self, lambda_=0.5, *args,**kwds):
self.lambda_ = lambda_
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
return np.where(x < -self.lambda_, x + self.lambda_,
np.where(x > self.lambda_, x - self.lambda_, 0))
def backward(self, x, *args,**kwds):
return np.where((-self.lambda_ <= x) & (x <= self.lambda_),
0, 1)
class threshold(Activator):
def __init__(self, threshold, value, *args,**kwds):
self.threshold = threshold
self.value = value
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
return np.where(x > self.threshold, x, self.value)
def backward(self, x, *args,**kwds):
return np.where(x > self.threshold, 1, 0)
class sigmoid(Activator):
def forward(self, x, *args,**kwds):
return 1/(1 + np.exp(-x))
def backward(self, x, y, *args,**kwds):
return y*(1 - y)
class hardSigmoid(Activator):
def forward(self, x, *args,**kwds):
return np.clip(0.2*x + 0.5, 0, 1)
def backward(self, x, *args,**kwds):
return np.where((x > 2.5) | (x < -2.5), 0, 0.2)
class logSigmoid(Activator):
def forward(self, x, *args,**kwds):
return -np.log(1 + np.exp(-x))
def backward(self, x, *args,**kwds):
return 1/(1 + np.exp(x))
class act_tanh(Activator):
def forward(self, x, *args,**kwds):
return np.tanh(x)
def backward(self, x, *args,**kwds):
return 1 - np.tanh(x)**2
class hardtanh(Activator):
def forward(self, x, *args,**kwds):
return np.clip(x, -1, 1)
def backward(self, x, *args,**kwds):
return np.where((-1 <= x) & (x <= 1), 1, 0)
class tanhShrink(Activator):
def forward(self, x, *args,**kwds):
return x - np.tanh(x)
def backward(self, x, *args,**kwds):
return np.tanh(x)**2
class ReLU(Activator):
def forward(self, x, *args,**kwds):
return np.maximum(0, x)
def backward(self, x, *args,**kwds):
return np.where(x > 0, 1, 0)
class ReLU6(Activator):
def forward(self, x, *args,**kwds):
return np.clip(x, 0, 6)
def backward(self, x, *args,**kwds):
return np.where((0 < x) & (x < 6), 1, 0)
class leakyReLU(Activator):
def __init__(self, alpha=1e-2, *args,**kwds):
self.alpha = alpha
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
return np.maximum(self.alpha * x, x)
def backward(self, x, *args,**kwds):
return np.where(x < 0, self.alpha, 1)
class ELU(Activator):
def __init__(self, alpha=1., *args,**kwds):
self.alpha = alpha
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
return np.where(x >= 0, x, self.alpha*(np.exp(x) - 1))
def backward(self, x, *args,**kwds):
return np.where(x >= 0, 1, self.alpha*np.exp(x))
class SELU(Activator):
def __init__(self, lambda_=1.0507, alpha=1.67326, *args,**kwds):
self.lambda_ = lambda_
self.alpha = alpha
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
return np.where(x >= 0,
self.lambda_*x,
self.lambda_*self.alpha*(np.exp(x) - 1))
def backward(self, x, *args,**kwds):
return np.where(x >= 0,
self.lambda_,
self.lambda_*self.alpha*np.exp(x))
class CELU(Activator):
def __init__(self, alpha=1., *args,**kwds):
self.alpha = alpha
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
return np.where(x >= 0,
x,
self.alpha*(np.exp(x/self.alpha) - 1))
def backward(self, x, *args,**kwds):
return np.where(x >= 0, 1, np.exp(x/self.alpha))
class softmax(Activator):
def forward(self, x, *args,**kwds):
return np.exp(x)/np.sum(np.exp(x))
def backward(self, x, *args,**kwds):
return np.exp(x)*(np.sum(np.exp(x))
- np.exp(x))/np.sum(np.exp(x))**2
class softmin(Activator):
def forward(self, x, *args,**kwds):
return np.exp(-x)/np.sum(np.exp(-x))
def backward(self, x, *args,**kwds):
return -(np.exp(x)*(np.sum(np.exp(-x)) - np.exp(x))
/np.sum(np.exp(-x))**2)
class logSoftmax(Activator):
def forward(self, x, *args,**kwds):
return np.log(np.exp(x)/np.sum(np.exp(x)))
def backward(self, x, *args,**kwds):
y = np.sum(np.exp(x))
return (y - np.exp(x))/y
class softplus(Activator):
def forward(self, x, *args,**kwds):
return np.logaddexp(x, 0)
def backward(self, x, *args,**kwds):
return 1/(1 + np.exp(-x))
class softsign(Activator):
def forward(self, x, *args,**kwds):
return x/(1 + np.abs(x))
def backward(self, x, *args,**kwds):
return 1/(1 + np.abs(x)) ** 2
class Swish(Activator):
def __init__(self, beta=1, *args,**kwds):
self.beta = beta
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
return x/(1 + np.exp(-self.beta*x))
def backward(self, x, y, *args,**kwds):
return self.beta*y + (1 - self.beta*y)/(1 + np.exp(-self.beta*x))
def d2y(self, x, *args,**kwds):
return (-0.25*self.beta*(self.beta*x*np.tanh(0.5*self.beta*x) - 2)
*(1 - np.tanh(0.5*self.beta*x)**2))
class Mish(Activator):
def forward(self, x, *args,**kwds):
return x*np.tanh(np.logaddexp(x, 0))
def backward(self, x, *args,**kwds):
omega = (4*(x + 1) + 4*np.exp(2*x)
+ np.exp(3*x) + (4*x + 6)*np.exp(x))
delta = 2*np.exp(x) + np.exp(2*x) + 2
return np.exp(x)*omega/delta**2
def d2y(self, x, *args,**kwds):
omega = (2*(x + 2)
+ np.exp(x)*(np.exp(x)*(-2*np.exp(x)*(x - 1) - 3*x + 6)
+ 2*(x + 4)))
delta = np.exp(x)*(np.exp(x) + 2) + 2
return 4*np.exp(x)*omega/delta**3
class tanhExp(Activator):
def forward(self, x, *args,**kwds):
return x*np.tanh(np.exp(x))
def backward(self, x, *args,**kwds):
tanh_exp = np.tanh(np.exp(x))
return tanh_exp - x*np.exp(x)*(tanh_exp**2 - 1)
def d2y(self, x, *args,**kwds):
tanh_exp = np.tanh(np.exp(x))
return (np.exp(x)*(-x + 2*np.exp(x)*x*tanh_exp - 2)
*(tanh_exp**2 - 1))
class maxout(Activator):
def __init__(self, n_prev, n, k, wb_width=5e-2, *args,**kwds):
self.n_prev = n_prev
self.n = n
self.k = k
self.w = wb_width*np.random.rand((n_prev, n*k))
self.b = wb_width*np.random.rand(n*k)
super().__init__(*args,**kwds)
def forward(self, x, *args,**kwds):
self.x = x.copy()
self.z = np.dot(self.w.T, x) + self.b
self.z = self.z.reshape(self.n, self.k)
self.y = np.max(self.z, axis=1)
return self.y
def backward(self, g, *args,**kwds):
self.dw = np.sum(np.dot(self.w, self.x))
get_act.py
_act_dic = {"step": step,
"identity": identity,
"bent-identity": bentIdentity,
"hard-shrink": hardShrink,
"soft-shrink": softShrink,
"threshold": threshold,
"sigmoid": sigmoid,
"hard-sigmoid": hardSigmoid,
"log-sigmoid": logSigmoid,
"tanh": act_tanh,
"tanh-shrink": tanhShrink,
"hard-tanh":hardtanh,
"ReLU": ReLU,
"ReLU6": ReLU6,
"leaky-ReLU": leakyReLU,
"ELU": ELU,
"SELU": SELU,
"CELU": CELU,
"softmax": softmax,
"softmin": softmin,
"log-softmax": logSoftmax,
"softplus": softplus,
"softsign": softsign,
"Swish": Swish,
"Mish": Mish,
"tanhExp": tanhExp,
}
def get_act(name, *args,**kwds):
if name in _act_dic.keys():
activator = _act_dic[name](*args,**kwds)
else:
raise ValueError(name + ": Unknown activator")
return activator
optimizers.py
import numpy as np
class Optimizer():
"""
Une super classe héritée de la méthode d'optimisation.
"""
def __init__(self, *args,**kwds):
pass
def update(self, *args,**kwds):
pass
class SGD(Optimizer):
def __init__(self, eta=1e-2, *args,**kwds):
super().__init__(*args,**kwds)
self.eta = eta
def update(self, grad_w, grad_b, *args,**kwds):
dw = -self.eta*grad_w
db = -self.eta*grad_b
return dw, db
class MSGD(Optimizer):
def __init__(self, eta=1e-2, mu=0.9, *args,**kwds):
super().__init__(*args,**kwds)
self.eta = eta
self.mu = mu
#Maintenez la valeur de l'étape précédente
self.dw = 0
self.db = 0
def update(self, grad_w, grad_b, *args,**kwds):
dw = self.mu*self.dw - (1-self.mu)*self.eta*grad_w
db = self.mu*self.db - (1-self.mu)*self.eta*grad_b
#L'affectation dans la vue au lieu de la copie est due au fait que ces valeurs peuvent être utilisées
#C'est parce qu'il ne sera pas changé.
self.dw = dw
self.db = db
return dw, db
class NAG(Optimizer):
def __init__(self, eta=1e-2, mu=0.9, *args,**kwds):
super().__init__(*args,**kwds)
self.eta = eta
self.mu = mu
#Contient la valeur de l'étape précédente
self.dw = 0
self.db = 0
def update(self, grad_w, grad_b, w=0, b=0, dfw=None, dfb=None,
nargs=2, *args,**kwds):
if nargs == 1:
grad_w = dfw(w + self.mu*self.dw)
grad_b = 0
elif nargs == 2:
grad_w = dfw(w + self.mu*self.dw, b + self.mu*self.db)
grad_b = dfb(w + self.mu*self.dw, b + self.mu*self.db)
dw = self.mu*self.dw - (1-self.mu)*self.eta*grad_w
db = self.mu*self.db - (1-self.mu)*self.eta*grad_b
#L'affectation dans la vue au lieu de la copie est due au fait que ces valeurs peuvent être utilisées
#C'est parce qu'il ne sera pas changé.
self.dw = dw
self.db = db
return dw, db
class AdaGrad(Optimizer):
def __init__(self, eta=1e-3, *args,**kwds):
super().__init__(*args,**kwds)
self.eta = eta
#Maintenez la valeur de l'étape précédente
self.gw = 0
self.gb = 0
def update(self, grad_w, grad_b, *args,**kwds):
self.gw += grad_w*grad_w
self.gb += grad_b*grad_b
dw = -self.eta*grad_w/np.sqrt(self.gw)
db = -self.eta*grad_b/np.sqrt(self.gb)
return dw, db
class RMSprop(Optimizer):
def __init__(self, eta=1e-2, rho=0.99, eps=1e-8, *args,**kwds):
super().__init__(*args,**kwds)
self.eta = eta
self.rho = rho
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.vw = 0
self.vb = 0
def update(self, grad_w, grad_b, *args,**kwds):
self.vw += (1-self.rho)*(grad_w**2 - self.vw)
self.vb += (1-self.rho)*(grad_b**2 - self.vb)
dw = -self.eta*grad_w/np.sqrt(self.vw+self.eps)
db = -self.eta*grad_b/np.sqrt(self.vb+self.eps)
return dw, db
class AdaDelta(Optimizer):
def __init__(self, rho=0.95, eps=1e-6, *args,**kwds):
super().__init__(*args,**kwds)
self.rho = rho
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.vw = 0
self.vb = 0
self.uw = 0
self.ub = 0
def update(self, grad_w, grad_b, *args,**kwds):
self.vw += (1-self.rho)*(grad_w**2 - self.vw)
self.vb += (1-self.rho)*(grad_b**2 - self.vb)
dw = -grad_w*np.sqrt(self.uw+self.eps)/np.sqrt(self.vw+self.eps)
db = -grad_b*np.sqrt(self.ub+self.eps)/np.sqrt(self.vb+self.eps)
self.uw += (1-self.rho)*(dw**2 - self.uw)
self.ub += (1-self.rho)*(db**2 - self.ub)
return dw, db
class Adam(Optimizer):
def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, eps=1e-8,
*args,**kwds):
super().__init__(*args,**kwds)
self.alpha = alpha
self.beta1 = beta1
self.beta2 = beta2
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.mw = 0
self.mb = 0
self.vw = 0
self.vb = 0
def update(self, grad_w, grad_b, t=1, *args,**kwds):
self.mw += (1-self.beta1)*(grad_w - self.mw)
self.mb += (1-self.beta1)*(grad_b - self.mb)
self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
alpha_t = self.alpha*np.sqrt(1-self.beta2**t)/(1-self.beta1**t)
dw = -alpha_t*self.mw/(np.sqrt(self.vw+self.eps))
db = -alpha_t*self.mb/(np.sqrt(self.vb+self.eps))
return dw, db
class RMSpropGraves(Optimizer):
def __init__(self, eta=1e-4, rho=0.95, eps=1e-4, *args,**kwds):
super().__init__(*args,**kwds)
self.eta = eta
self.rho = rho
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.mw = 0
self.mb = 0
self.vw = 0
self.vb = 0
def update(self,grad_w, grad_b, *args,**kwds):
self.mw += (1-self.rho)*(grad_w - self.mw)
self.mb += (1-self.rho)*(grad_b - self.mb)
self.vw += (1-self.rho)*(grad_w**2 - self.vw)
self.vb += (1-self.rho)*(grad_b**2 - self.vb)
dw = -self.eta*grad_w/np.sqrt(self.vw - self.mw**2 + self.eps)
db = -self.eta*grad_b/np.sqrt(self.vb - self.mb**2 + self.eps)
return dw, db
class SMORMS3(Optimizer):
def __init__(self, eta=1e-3, eps=1e-8, *args,**kwds):
super().__init__(*args,**kwds)
self.eta = eta
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.zetaw = 0
self.zetab = 0
self.sw = 1
self.sb = 1
self.mw = 0
self.mb = 0
self.vw = 0
self.vb = 0
def update(self, grad_w, grad_b, *args,**kwds):
rhow = 1/(1+self.sw)
rhob = 1/(1+self.sb)
self.mw += (1-rhow)*(grad_w - self.mw)
self.mb += (1-rhob)*(grad_b - self.mb)
self.vw += (1-rhow)*(grad_w**2 - self.vw)
self.vb += (1-rhob)*(grad_b**2 - self.vb)
self.zetaw = self.mw**2 / (self.vw + self.eps)
self.zetaw = self.mb**2 / (self.vb + self.eps)
dw = -grad_w*(np.minimum(self.eta, self.zetaw)
/np.sqrt(self.vw + self.eps))
db = -grad_b*(np.minimum(self.eta, self.zetab)
/np.sqrt(self.vb + self.eps))
self.sw = 1 + (1 - self.zetaw)*self.sw
self.sb = 1 + (1 - self.zetab)*self.sb
return dw, db
class AdaMax(Optimizer):
def __init__(self, alpha=2e-3, beta1=0.9, beta2=0.999,
*args,**kwds):
super().__init__(*args,**kwds)
self.alpha = alpha
self.beta1 = beta1
self.beta2 = beta2
#Maintenez la valeur de l'étape précédente
self.mw = 0
self.mb = 0
self.uw = 0
self.ub = 0
def update(self, grad_w, grad_b, t=1, *args,**kwds):
self.mw += (1-self.beta1)*(grad_w - self.mw)
self.mb += (1-self.beta1)*(grad_b - self.mb)
self.uw = np.maximum(self.beta2*self.uw, np.abs(grad_w))
self.ub = np.maximum(self.beta2*self.ub, np.abs(grad_b))
alpha_t = self.alpha/(1 - self.beta1**t)
dw = -alpha_t*self.mw/self.uw
db = -alpha_t*self.mb/self.ub
return dw, db
class Nadam(Optimizer):
def __init__(self, alpha=2e-3, mu=0.975, nu=0.999, eps=1e-8,
*args,**kwds):
super().__init__(*args,**kwds)
self.alpha = alpha
self.mu = mu
self.nu = nu
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.mw = 0
self.mb = 0
self.vw = 0
self.vb = 0
def update(self, grad_w, grad_b, t=1, *args,**kwds):
self.mw += (1-self.mu)*(grad_w - self.mw)
self.mb += (1-self.mu)*(grad_b - self.mb)
self.vw += (1-self.nu)*(grad_w**2 - self.vw)
self.vb += (1-self.nu)*(grad_b**2 - self.vb)
mhatw = (self.mu*self.mw/(1-self.mu**(t+1))
+ (1-self.mu)*grad_w/(1-self.mu**t))
mhatb = (self.mu*self.mb/(1-self.mu**(t+1))
+ (1-self.mu)*grad_b/(1-self.mu**t))
vhatw = self.nu*self.vw/(1-self.nu**t)
vhatb = self.nu*self.vb/(1-self.nu**t)
dw = -self.alpha*mhatw/np.sqrt(vhatw + self.eps)
db = -self.alpha*mhatb/np.sqrt(vhatb + self.eps)
return dw, db
class Eve(Optimizer):
def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, beta3=0.999,
c=10, eps=1e-8, fstar=0, *args,**kwds):
super().__init__(*args,**kwds)
self.alpha = alpha
self.beta1 = beta1
self.beta2 = beta2
self.beta3 = beta3
self.c = c
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.mw = 0
self.mb = 0
self.vw = 0
self.vb = 0
self.f = 0
self.fstar = fstar
self.dtilde_w = 0
self.dtilde_b = 0
def update(self, grad_w, grad_b, t=1, f=1, *args,**kwds):
self.mw += (1-self.beta1)*(grad_w - self.mw)
self.mb += (1-self.beta1)*(grad_b - self.mb)
self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
mhatw = self.mw/(1 - self.beta1**t)
mhatb = self.mb/(1 - self.beta1**t)
vhatw = self.vw/(1 - self.beta2**t)
vhatb = self.vb/(1 - self.beta2**t)
if t > 1:
d_w = (np.abs(f-self.fstar)
/(np.minimum(f, self.f) - self.fstar))
d_b = (np.abs(f-self.fstar)
/(np.minimum(f, self.f) - self.fstar))
dhat_w = np.clip(d_w, 1/self.c, self.c)
dhat_b = np.clip(d_b, 1/self.c, self.c)
self.dtilde_w += (1 - self.beta3)*(dhat_w - self.dtilde_w)
self.dtilde_b += (1 - self.beta3)*(dhat_b - self.dtilde_b)
else:
self.dtilde_w = 1
self.dtilde_b = 1
self.f = f
dw = -(self.alpha*mhatw
/(self.dtilde_w*(np.sqrt(vhatw) + self.eps)))
db = -(self.alpha*mhatb
/(self.dtilde_b*(np.sqrt(vhatb) + self.eps)))
return dw, db
class SantaE(Optimizer):
def __init__(self, eta=1e-2, sigma=0.95, lambda_=1e-8,
anne_func=lambda t, n: t**n, anne_rate=0.5,
burnin=100, C=5, N=16,
*args,**kwds):
"""
Args:
eta: Learning rate
sigma: Maybe in other cases;
'rho' in RMSprop, AdaDelta, RMSpropGraves.
'rhow' or 'rhob' in SMORMS3.
'beta2' in Adam, Eve.
'nu' in Nadam.
To use calculation 'v'.
lambda_: Named 'eps'(ε) in other cases.
anne_func: Annealing function.
To use calculation 'beta' at each timestep.
Default is 'timestep'**'annealing rate'.
The calculated value should be towards infinity
as 't' increases.
anne_rate: Annealing rate.
To use calculation 'beta' at each timestep.
The second Argument of 'anne_func'.
burnin: Swith exploration and refinement.
This should be specified by users.
C: To calculate first 'alpha'.
N: Number of minibatch.
"""
super().__init__(*args,**kwds)
self.eta = eta
self.sigma = sigma
self.lambda_ = lambda_
self.anne_func = anne_func
self.anne_rate = anne_rate
self.burnin = burnin
self.N = N
# Keep one step before and Initialize.
self.alpha_w = np.sqrt(eta)*C
self.alpha_b = np.sqrt(eta)*C
self.vw = 0
self.vb = 0
self.gw = 0
self.gb = 0
def update(self, grad_w, grad_b, t=1, *args,**kwds):
try:
shape_w = grad_w.shape
except:
shape_w = (1, )
try:
shape_b = grad_b.shape
except:
shape_b = (1, )
if t == 1:
# Initialize uw, ub.
self.uw = np.sqrt(self.eta)*np.random.randn(*shape_w)
self.ub = np.sqrt(self.eta)*np.random.randn(*shape_b)
self.vw = (self.sigma*self.vw
+ grad_w*grad_w * (1 - self.sigma) / self.N**2)
self.vb = (self.sigma*self.vb
+ grad_b*grad_b * (1 - self.sigma) / self.N**2)
gw = 1/np.sqrt(self.lambda_ + np.sqrt(self.vw))
gb = 1/np.sqrt(self.lambda_ + np.sqrt(self.vb))
beta = self.anne_func(t, self.anne_rate)
if t < self.burnin:
# Exploration.
self.alpha_w += self.uw*self.uw - self.eta/beta
self.alpha_b += self.ub*self.ub - self.eta/beta
uw = (self.eta/beta * (1 - self.gw/gw)/self.uw
+ np.sqrt(2*self.eta/beta * self.gw)
* np.random.randn(*shape_w))
ub = (self.eta/beta * (1 - self.gb/gb)/self.ub
+ np.sqrt(2*self.eta/beta * self.gb)
* np.random.randn(*shape_b))
else:
# Refinement.
uw = 0
ub = 0
uw += (1 - self.alpha_w)*self.uw - self.eta*gw*grad_w
ub += (1 - self.alpha_b)*self.ub - self.eta*gb*grad_b
# Update values.
self.uw = uw
self.ub = ub
self.gw = gw
self.gb = gb
dw = gw*uw
db = gb*ub
return dw, db
class SantaSSS(Optimizer):
def __init__(self, eta=1e-2, sigma=0.95, lambda_=1e-8,
anne_func=lambda t, n: t**n, anne_rate=0.5,
burnin=100, C=5, N=16,
*args,**kwds):
"""
Args:
eta: Learning rate
sigma: Maybe in other cases;
'rho' in RMSprop, AdaDelta, RMSpropGraves.
'rhow' or 'rhob' in SMORMS3.
'beta2' in Adam, Eve.
'nu' in Nadam.
To use calculation 'v'.
lambda_: Named 'eps'(ε) in other cases.
anne_func: Annealing function.
To use calculation 'beta' at each timestep.
Default is 'timestep'**'annealing rate'.
The calculated value should be towards infinity
as 't' increases.
anne_rate: Annealing rate.
To use calculation 'beta' at each timestep.
The second Argument of 'anne_func'.
burnin: Swith exploration and refinement.
This should be specified by users.
C: To calculate first 'alpha'.
N: Number of minibatch.
"""
super().__init__(*args,**kwds)
self.eta = eta
self.sigma = sigma
self.lambda_ = lambda_
self.anne_func = anne_func
self.anne_rate = anne_rate
self.burnin = burnin
self.N = N
# Keep one step before and Initialize.
self.alpha_w = np.sqrt(eta)*C
self.alpha_b = np.sqrt(eta)*C
self.vw = 0
self.vb = 0
self.gw = 0
self.gb = 0
def update(self, grad_w, grad_b, t=1, *args,**kwds):
try:
shape_w = grad_w.shape
except:
shape_w = (1, )
try:
shape_b = grad_b.shape
except:
shape_b = (1, )
if t == 1:
# Initialize uw, ub.
self.uw = np.sqrt(self.eta)*np.random.randn(*shape_w)
self.ub = np.sqrt(self.eta)*np.random.randn(*shape_b)
self.vw = (self.sigma*self.vw
+ grad_w*grad_w * (1 - self.sigma) / self.N**2)
self.vb = (self.sigma*self.vb
+ grad_b*grad_b * (1 - self.sigma) / self.N**2)
gw = 1/np.sqrt(self.lambda_ + np.sqrt(self.vw))
gb = 1/np.sqrt(self.lambda_ + np.sqrt(self.vb))
dw = 0.5*gw*self.uw
db = 0.5*gb*self.ub
beta = self.anne_func(t, self.anne_rate)
if t < self.burnin:
# Exploration.
self.alpha_w += (self.uw*self.uw - self.eta/beta)*0.5
self.alpha_b += (self.ub*self.ub - self.eta/beta)*0.5
uw = np.exp(-0.5*self.alpha_w)*self.uw
ub = np.exp(-0.5*self.alpha_b)*self.ub
uw += (-gw*grad_w*self.eta
+ np.sqrt(2*self.gw*self.eta/beta)
* np.random.randn(*shape_w)
+ self.eta/beta*(1-self.gw/gw)/self.uw)
ub += (-gb*grad_b*self.eta
+ np.sqrt(2*self.gb*self.eta/beta)
* np.random.randn(*shape_b)
+ self.eta/beta*(1-self.gb/gb)/self.ub)
uw *= np.exp(-0.5*self.alpha_w)
ub *= np.exp(-0.5*self.alpha_b)
self.alpha_w += (uw*uw - self.eta/beta)*0.5
self.alpha_b += (ub*ub - self.eta/beta)*0.5
else:
# Refinement.
uw = np.exp(-0.5*self.alpha_w)*self.uw
ub = np.exp(-0.5*self.alpha_b)*self.ub
uw -= gw*grad_w*self.eta
ub -= gb*grad_b*self.eta
uw *= np.exp(-0.5*self.alpha_w)
ub *= np.exp(-0.5*self.alpha_b)
# Update values.
self.uw = uw
self.ub = ub
self.gw = gw
self.gb = gb
dw = gw*uw*0.5
db = gb*ub*0.5
return dw, db
class AMSGrad(Optimizer):
def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, eps=1e-8,
*args,**kwds):
super().__init__(*args,**kwds)
self.alpha = alpha
self.beta1 = beta1
self.beta2 = beta2
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.mw = 0
self.mb = 0
self.vw = 0
self.vb = 0
self.vhatw = 0
self.vhatb = 0
def update(self, grad_w, grad_b, t=1, *args,**kwds):
self.mw += (1-self.beta1)*(grad_w - self.mw)
self.mb += (1-self.beta1)*(grad_b - self.mb)
self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
self.vhatw = np.maximum(self.vhatw, self.vw)
self.vhatb = np.maximum(self.vhatb, self.vb)
alpha_t = self.alpha / np.sqrt(t)
dw = - alpha_t * self.mw/np.sqrt(self.vhatw + self.eps)
db = - alpha_t * self.mb/np.sqrt(self.vhatb + self.eps)
return dw, db
class AdaBound(Optimizer):
def __init__(self, alpha=1e-3, eta=1e-1, beta1=0.9, beta2=0.999,
eps=1e-8, *args,**kwds):
super().__init__(*args,**kwds)
self.alpha = alpha
self.eta = eta
self.beta1 = beta1
self.beta2 = beta2
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.mw = 0
self.mb = 0
self.vw = 0
self.vb = 0
def update(self, grad_w, grad_b, t=1, *args,**kwds):
self.mw += (1-self.beta1)*(grad_w - self.mw)
self.mb += (1-self.beta1)*(grad_b - self.mb)
self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
etal = self.eta*(1 - 1/((1-self.beta2)*t + 1))
etau = self.eta*(1 + 1/((1-self.beta2)*t + self.eps))
etahatw_t = np.clip(self.alpha/np.sqrt(self.vw), etal, etau)
etahatb_t = np.clip(self.alpha/np.sqrt(self.vb), etal, etau)
etaw_t = etahatw_t/np.sqrt(t)
etab_t = etahatb_t/np.sqrt(t)
dw = - etaw_t*self.mw
db = - etab_t*self.mb
return dw, db
class AMSBound(Optimizer):
def __init__(self, alpha=1e-3, eta=1e-1, beta1=0.9, beta2=0.999,
eps=1e-8, *args,**kwds):
super().__init__(*args,**kwds)
self.alpha = alpha
self.eta = eta
self.beta1 = beta1
self.beta2 = beta2
self.eps = eps
#Maintenez la valeur de l'étape précédente
self.mw = 0
self.mb = 0
self.vw = 0
self.vb = 0
self.vhatw = 0
self.vhatb = 0
def update(self, grad_w, grad_b, t=1, *args,**kwds):
self.mw += (1-self.beta1)*(grad_w - self.mw)
self.mb += (1-self.beta1)*(grad_b - self.mb)
self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
self.vhatw = np.maximum(self.vhatw, self.vw)
self.vhatb = np.maximum(self.vhatb, self.vb)
etal = self.eta*(1 - 1/((1-self.beta2)*t + 1))
etau = self.eta*(1 + 1/((1-self.beta2)*t + self.eps))
etahatw_t = np.clip(self.alpha/np.sqrt(self.vhatw), etal, etau)
etahatb_t = np.clip(self.alpha/np.sqrt(self.vhatb), etal, etau)
etaw_t = etahatw_t/np.sqrt(t)
etab_t = etahatb_t/np.sqrt(t)
dw = - etaw_t*self.mw
db = - etab_t*self.mb
return dw, db
get_opt.py
_opt_dic = {
"SGD": SGD,
"MSGD": MSGD,
"NAG": NAG,
"AdaGrad": AdaGrad,
"RMSprop": RMSprop,
"AdaDelta": AdaDelta,
"Adam": Adam,
"RMSpropGraves": RMSpropGraves,
"SMORMS3": SMORMS3,
"AdaMax": AdaMax,
"Nadam": Nadam,
"Eve": Eve,
"SantaE": SantaE,
"SantaSSS": SantaSSS,
"AMSGrad": AMSGrad,
"AdaBound": AdaBound,
"AMSBound": AMSBound,
}
def get_opt(name, *args,**kwds):
if name in _opt_dic.keys():
optimizer = _opt_dic[name](*args,**kwds)
else:
raise ValueError(name + ": Unknown optimizer")
return optimizer
Voici le code expérimental.
test.py
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import tqdm
#Définition d'objectifs d'apprentissage
def split_test(target, train_indices):
return target[train_indices], target[~ train_indices]
x = np.arange(0, 4, 5e-2)
y = np.sin(x)
x_left = 1
x_right = 3
y_top = np.max(y) + 1
y_bottom = np.min(y) - 1
indices = (x_left <= x) & (x <= x_right)
x_train, x_test = split_test(x, indices)
y_train, y_test = split_test(y, indices)
#Réglage initial
epoch = 10000
error_prev = 0
error = 0
error_list = []
threshold = 1e-8
n_batch = 4
n_train = x_train.size//n_batch
n_test = x_test.size
#Construction de réseau
n_in = 1
n_out = 1
lm = LayerManager()
lm.append(prev=n_in, n=30, act="sigmoid", wb_width=1)
lm.append(n=30, act="sigmoid", wb_width=1)
lm.append(n=n_out, name="o", act="identity", wb_width=1)
#Création d'une base pour les tracés d'animation
n_image = 100
interval = 50
images = []
fig, ax = plt.subplots(1)
fig.suptitle("fitting animation")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_xlim(np.min(x), np.max(x))
ax.set_ylim(y_bottom, y_top)
ax.grid()
ax.plot(x, y, color="r")
ax.plot(np.full_like(np.arange(y_bottom, y_top+1), x_left),
np.arange(y_bottom, y_top+1),
color="g")
ax.plot(np.full_like(np.arange(y_bottom, y_top+1), x_right),
np.arange(y_bottom, y_top+1),
color="g")
#Commencer à apprendre
rand_index = np.arange(x_train.size)
for t in tqdm.tqdm(range(1, epoch+1)):
#Création de scène
if t % (epoch/n_image) == 1:
x_in = x.reshape(-1, 1)
for ll in lm.layer_list:
x_in = ll.forward(x_in)
im, = ax.plot(x, ll.y, color="b")
images.append([im])
#Calcul d'erreur
x_in = x_test.reshape(n_test, n_in)
for ll in lm.layer_list:
x_in = ll.forward(x_in)
error = lm[-1].get_error(y_test.reshape(n_test, n_out))
error_list.append(error)
#Jugement de convergence
if abs(error - error_prev) < threshold:
print("end learning...")
break
else:
error_prev = error
#print("t", t)
np.random.shuffle(rand_index)
for i in range(n_train):
rand = rand_index[i*n_in : (i+n_batch)*n_in]
x_in = x_train[rand].reshape(-1, n_in)
#print("x_in", x_in)
for ll in lm.layer_list:
x_in = ll.forward(x_in)
y_in = y_train[rand].reshape(-1, n_out)
#print("y_in", y_in)
for ll in lm.layer_list[::-1]:
y_in = ll.backward(y_in)
for ll in lm.layer_list:
ll.update()
#Création d'une animation d'adaptation
anim = animation.ArtistAnimation(fig, images, interval=interval, repeat_delay=3000)
#Affichage de transition d'erreur
fig2, ax2 = plt.subplots(1)
fig2.suptitle("error transition")
ax2.set_yscale("log")
ax2.set_xlabel("epoch")
ax2.set_ylabel("error")
ax2.grid()
ax2.plot(error_list)
fig2.show()
fig2.savefig("error_transition.png ")
Je vais expliquer chacun d'eux.
<détails> <détails> <détails> <détails> <détails> Voici un exemple de l'animation créée et de la transition d'erreur.
Ceci n'est qu'un exemple car les paramètres initiaux sont générés avec des nombres aléatoires et des résultats différents sont donnés à chaque fois qu'ils sont exécutés.
En ce qui concerne les données d'entraînement, l'ajustement est terminé dès que possible, et les données d'essai sont également bien ajustées afin de pouvoir être suivies. Bien sûr, puisque les données de test ne sont pas entraînées, elles ** gagnent en polyvalence pour des données inconnues **. Maintenant, portons certaines des fonctions écrites directement dans le code de test vers Ensuite, nous porterons le corps d'apprentissage et l'affichage de la transition d'erreur. Enfin, c'est une animation.
J'y ai pensé, mais je ne pouvais pas penser à un moyen d'insérer une animation avec une grande polyvalence, donc c'est approprié ...
Si vous trouvez quelque chose, changez-le. Ceci conclut l'expérience DNN (Deep Neural Network). Essayez de jouer avec d'autres fonctions diverses.
Recommended Posts
test.py
#Définition d'objectifs d'apprentissage
def split_test(target, train_indices):
return target[train_indices], target[~ train_indices]
x = np.arange(0, 4, 5e-2)
y = np.sin(x)
x_left = 1
x_right = 3
y_top = np.max(y) + 1
y_bottom = np.min(y) - 1
indices = (x_left <= x) & (x <= x_right)
x_train, x_test = split_test(x, indices)
y_train, y_test = split_test(y, indices)
Réglage de la valeur initiale
test.py
#Réglage initial
epoch = 10000
error_prev = 0
error = 0
error_list = []
threshold = 1e-8
n_batch = 4
n_train = x_train.size//n_batch
n_test = x_test.size
Construction de réseau
test.py
#Construction de réseau
n_in = 1
n_out = 1
lm = LayerManager()
lm.append(prev=n_in, n=30, act="sigmoid", wb_width=1)
lm.append(n=30, act="sigmoid", wb_width=1)
lm.append(n=n_out, name="o", act="identity", wb_width=1)
Créer une base pour l'animation
Création d'une base pour l'animation summary>
test.py
#Création d'une base pour les tracés d'animation
n_image = 100
interval = 50
images = []
fig, ax = plt.subplots(1)
fig.suptitle("fitting animation")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_xlim(np.min(x), np.max(x))
ax.set_ylim(y_bottom, y_top)
ax.grid()
ax.plot(x, y, color="r")
ax.plot(np.full_like(np.arange(y_bottom, y_top+1), x_left),
np.arange(y_bottom, y_top+1),
color="g")
ax.plot(np.full_like(np.arange(y_bottom, y_top+1), x_right),
np.arange(y_bottom, y_top+1),
color="g")
Apprentissage
test.py
#Commencer à apprendre
rand_index = np.arange(x_train.size)
for t in tqdm.tqdm(range(1, epoch+1)):
#Création de scène
if t % (epoch/n_image) == 1:
x_in = x.reshape(-1, 1)
for ll in lm.layer_list:
x_in = ll.forward(x_in)
im, = ax.plot(x, ll.y, color="b")
images.append([im])
#Calcul d'erreur
x_in = x_test.reshape(n_test, n_in)
for ll in lm.layer_list:
x_in = ll.forward(x_in)
error = lm[-1].get_error(y_test.reshape(n_test, n_out))
error_list.append(error)
#Jugement de convergence
if abs(error - error_prev) < threshold:
print("end learning...")
break
else:
error_prev = error
#print("t", t)
np.random.shuffle(rand_index)
for i in range(n_train):
rand = rand_index[i*n_in : (i+n_batch)*n_in]
x_in = x_train[rand].reshape(-1, n_in)
#print("x_in", x_in)
for ll in lm.layer_list:
x_in = ll.forward(x_in)
y_in = y_train[rand].reshape(-1, n_out)
#print("y_in", y_in)
for ll in lm.layer_list[::-1]:
y_in = ll.backward(y_in)
for ll in lm.layer_list:
ll.update()
Affichage d'animation et de transition d'erreur
test.py
#Création d'une animation d'adaptation
anim = animation.ArtistAnimation(fig, images, interval=interval, repeat_delay=3000)
#Affichage de transition d'erreur
fig2, ax2 = plt.subplots(1)
fig2.suptitle("error transition")
ax2.set_yscale("log")
ax2.set_xlabel("epoch")
ax2.set_ylabel("error")
ax2.grid()
ax2.plot(error_list)
fig2.show()
fig2.savefig("error_transition.png ")
Résultat expérimental
Fonctionnalité portée vers
LayerManager
LayerManager
.
Tout d'abord, laissez le gestionnaire de couches contenir diverses choses. Modifiez les paramètres initiaux de `test.py` summary>
test.py
#Réglage initial
epoch = 10000
#error_prev = 0
#error = 0
#error_list = []
threshold = 1e-8
n_batch = 4
#n_train = x_train.size//n_batch
#n_test = x_test.size
#Construction de réseau
n_in = 1
n_out = 1
lm = LayerManager((x_train, x_test), (y_train, y_test))
lm.append(prev=n_in, n=30, act="sigmoid", wb_width=1)
lm.append(n=30, act="sigmoid", wb_width=1)
lm.append(n=n_out, name="o", act="identity", wb_width=1)
Portez les paramètres par défaut sur `__init__` dans` layermanager.py` summary>
layermanager.py
def __init__(self, x, y):
self.x_train, self.x_test = x
self.y_train, self.y_test = y
self.__layer_list = [] #Liste des couches
self.__name_list = [] #Liste de noms pour chaque couche
self.__ntype = np.zeros(self.N_TYPE, dtype=int) #Nombre de couches par type
Changer le code d'apprentissage de `test.py` summary>
test.py
#Commencer à apprendre
lm.training(epoch, threshold=threshold, n_batch=n_batch)
Portez le code d'apprentissage vers `layermanager.py` summary>
layermanager.py
def training(self, epoch, n_batch=16, threshold=1e-8, show_error=True):
if show_error:
self.error_list = []
n_in = self.__layer_list[0].prev
n_out = self.__layer_list[-1].n
n_train = self.x_train.size//n_batch
n_test = self.x_test.size
#Commencer à apprendre
error = 0
error_prev = 0
rand_index = np.arange(self.x_train.size)
for t in tqdm.tqdm(range(1, epoch+1)):
#Calcul d'erreur
x_in = self.x_test.reshape(n_test, n_in)
for ll in self.__layer_list:
x_in = ll.forward(x_in)
error = lm[-1].get_error(self.y_test.reshape(n_test, n_out))
if show_error:
error_list.append(error)
#Jugement de convergence
if abs(error - error_prev) < threshold:
print("end learning...")
break
else:
error_prev = error
#print("t", t)
np.random.shuffle(rand_index)
for i in range(n_train):
rand = rand_index[i*n_in : (i+n_batch)*n_in]
x_in = self.x_train[rand].reshape(-1, n_in)
#print("x_in", x_in)
for ll in self.__layer_list:
x_in = ll.forward(x_in)
y_in = self.y_train[rand].reshape(-1, n_out)
#print("y_in", y_in)
for ll in self.__layer_list[::-1]:
y_in = ll.backward(y_in)
for ll in self.__layer_list:
ll.update()
if show_error:
#Affichage de transition d'erreur
self.show_error(**kwds)
def show_errors(self, title="error transition",
xlabel="epoch", ylabel="error", fname="error_transition.png "):
fig, ax = plt.subplots(1)
fig.suptitle(title)
ax.set_yscale("log")
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.grid()
ax.plot(error_list)
fig.show()
if len(fname) != 0:
fig.savefig(fname)
Changer le code d'animation de `test.py` summary>
test.py
#Création d'une base pour les tracés d'animation
n_image = 100
interval = 100
fig, ax = lm.ready_anim(n_image, x, y, title="fitting animation")
#images = []
#fig, ax = plt.subplots(1)
#fig.suptitle("fitting animation")
#ax.set_xlabel("x")
#ax.set_ylabel("y")
#ax.set_xlim(np.min(x), np.max(x))
#ax.set_ylim(y_bottom, y_top)
#ax.grid()
#ax.plot(x, y, color="r")
ax.plot(np.full_like(np.arange(y_bottom, y_top+1), x_left),
np.arange(y_bottom, y_top+1),
color="g")
ax.plot(np.full_like(np.arange(y_bottom, y_top+1), x_right),
np.arange(y_bottom, y_top+1),
color="g")
#Commencer à apprendre
lm.training(epoch, threshold=threshold, n_batch=n_batch)
#Création d'une animation d'adaptation
anim = animation.ArtistAnimation(lm.anim_fig, lm.images,
interval=interval, repeat_delay=3000)
Portez le code d'animation vers `layermanager.py` summary>
layermanager.py
def training(self, epoch, n_batch=16, threshold=1e-8,
show_error=True, **kwds):
if show_error:
self.error_list = []
if self.make_anim:
self.images = []
n_in = self.__layer_list[0].prev
n_out = self.__layer_list[-1].n
n_train = self.x_train.size//n_batch
n_test = self.x_test.size
#Commencer à apprendre
error = 0
error_prev = 0
rand_index = np.arange(self.x_train.size)
for t in tqdm.tqdm(range(1, epoch+1)):
#Création de scène
if self.make_anim:
self.make_scene(t, epoch)
#Calcul d'erreur
Ce qui suit est omis
def show_errors(self, title="error transition",
xlabel="epoch", ylabel="error", fname="error_transition.png ",
**kwds):
Ce qui suit est omis
def ready_anim(self, n_image, x, y, title="animation",
xlabel="x", ylabel="y", ex_color="r", color="b",
x_left=0, x_right=0, y_down = 1, y_up = 1):
self.n_image = n_image
self.x = x
self.color = color
self.make_anim = True
self.anim_fig, self.anim_ax = plt.subplots(1)
self.anim_fig.suptitle(title)
self.anim_ax.set_xlabel(xlabel)
self.anim_ax.set_ylabel(ylabel)
self.anim_ax.set_xlim(np.min(x) - x_left, np.max(x) + x_right)
self.anim_ax.set_ylim(np.min(y) - y_down, np.max(y) + y_up)
self.anim_ax.grid()
self.anim_ax.plot(x, y, color=ex_color)
return self.anim_fig, self.anim_ax
def make_scene(self, t, epoch):
#Création de scène
if t % (epoch/self.n_image) == 1:
x_in = self.x.reshape(-1, 1)
for ll in self.__layer_list:
x_in = ll.forward(x_in)
im, = self.anim_ax.plot(self.x, ll.y, color=self.color)
self.images.append([im])
test.py
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import tqdm
#Définition d'objectifs d'apprentissage
def split_test(target, train_indices):
return (target[train_indices], target[~ train_indices])
x = np.arange(0, 4, 5e-2)
y = np.sin(x)
x_left = 1
x_right = 3
y_top = np.max(y) + 1
y_bottom = np.min(y) - 1
indices = (x_left <= x) & (x <= x_right)
x_train, x_test = split_test(x, indices)
y_train, y_test = split_test(y, indices)
#Réglage initial
epoch = 10000
threshold = 1e-5
n_batch = 4
#Construction de réseau
n_in = 1
n_out = 1
lm = LayerManager((x_train, x_test), (y_train, y_test))
lm.append(prev=n_in, n=30, act="sigmoid", wb_width=1)
lm.append(n=30, act="sigmoid", wb_width=1)
lm.append(n=n_out, name="o", act="identity", wb_width=1)
#Création d'une base pour les tracés d'animation
n_image = 100
interval = 100
fig, ax = lm.ready_anim(n_image, x, y, title="fitting animation")
ax.plot(np.full_like(np.arange(y_bottom, y_top+1), x_left),
np.arange(y_bottom, y_top+1),
color="g")
ax.plot(np.full_like(np.arange(y_bottom, y_top+1), x_right),
np.arange(y_bottom, y_top+1),
color="g")
#Commencer à apprendre
lm.training(epoch, threshold=threshold, n_batch=n_batch)
#Création d'une animation d'adaptation
anim = animation.ArtistAnimation(lm.anim_fig, lm.images,
interval=interval, repeat_delay=3000)
`layermanager.py` entier summary>
layermanager.py
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import tqdm
class _TypeManager():
"""
Classe de gestionnaire pour les types de couches
"""
N_TYPE = 2 #Nombre de types de couches
MIDDLE = 0 #Numérotation des couches intermédiaires
OUTPUT = 1 #Numérotation des couches de sortie
class LayerManager(_TypeManager):
"""
Classe Manager pour la gestion des couches
"""
def __init__(self, x, y):
self.x_train, self.x_test = x
self.y_train, self.y_test = y
self.__layer_list = [] #Liste des couches
self.__name_list = [] #Liste de noms pour chaque couche
self.__ntype = np.zeros(self.N_TYPE, dtype=int) #Nombre de couches par type
def __repr__(self):
layerRepr= "layer_list: " + repr(self.__layer_list)
nameRepr = "name_list: " + repr(self.__name_list)
ntypeRepr = "ntype: " + repr(self.__ntype)
return (layerRepr + "\n"
+ nameRepr + "\n"
+ ntypeRepr)
def __str__(self):
layerStr = "layer_list: " + str(self.__layer_list)
nameStr = "name_list: " + str(self.__name_list)
ntypeStr = "ntype: " + str(self.__ntype)
return (layerStr + "\n"
+ nameStr + "\n"
+ ntypeStr)
def __len__(self):
"""
Fonctions intégrées Python`len`Décrit l'opération lorsqu'elle est appelée depuis.
Renvoie la somme du nombre de couches par type.
"""
return int(np.sum(self.__ntype))
def __getitem__(self, key):
"""
Par exemple
lm = LayerManager()
+----------------+
| (Ajouter un élément à lm) |
+----------------+
x = lm[3].~~
Est appelé lors de l'accès à un élément d'une liste ou d'un tableau, comme
Décrivez l'opération à ce moment-là.
tranche et str,Autoriser uniquement l'accès via int.
"""
if isinstance(key, slice):
#Si la clé est une tranche, reportez-vous à la liste des calques avec tranche.
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
return self.__layer_list[key]
elif isinstance(key, str):
#Si key est une chaîne, récupérez l'index dans la liste des noms de chaque couche et
#Renvoie les éléments de la liste des couches applicables.
if key in self.__name_list:
index = self.__name_list.index(key)
return self.__layer_list[index]
else:
#Si la clé n'existe pas, une KeyError est émise.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Si key est un entier, renvoie l'élément correspondant dans la liste des couches.
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
return self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
def __setitem__(self, key, value):
"""
Par exemple
lm = LayerManager()
+----------------+
| (Ajouter un élément à lm) |
+----------------+
lm[1] = x
Est appelé lors de l'accès à un élément d'une liste ou d'un tableau, comme
Décrivez l'opération à ce moment-là.
Seul l'écrasement des éléments est autorisé et l'ajout de nouveaux éléments est interdit.
"""
value_type = ""
if isinstance(value, list):
#Spécifié sur le côté droit'value'Mais'list'Si
#Tous les éléments'BaseLayer'Erreur si classe ou ne l'hérite pas.
if not np.all(
np.where(isinstance(value, BaseLayer), True, False)):
self.AssignError()
value_type = "list"
elif isinstance(value, BaseLayer):
#Spécifié sur le côté droit'value'Mais'BaseLayer'Est-ce une classe?
#Erreur s'il n'est pas hérité.
self.AssignError(type(value))
if value_type == "":
value_type = "BaseLayer"
if isinstance(key, slice):
#Si la clé est une tranche, écrasez l'élément dans la liste des calques.
#pourtant'value_type'Mais'list'Sinon, une erreur.
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
if value_type != "list":
self.AssignError(value_type)
self.__layer_list[key] = value
elif isinstance(key, str):
#Si key est une chaîne, récupérez l'index dans la liste des noms de chaque couche et
#Remplacez les éléments dans la liste des calques applicables.
#pourtant'value_type'Mais'BaseLayer'Sinon, une erreur.
if value_type != "BaseLayer":
raise AssignError(value_type)
if key in self.__name_list:
index = self.__name_list.index(key)
self.__layer_list[index] = value
else:
#Si la clé n'existe pas, une KeyError est émise.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Si la clé est un entier, écrasez l'élément correspondant dans la liste des couches.
#pourtant'value_type'Mais'BaseLayer'Sinon, une erreur.
#Aussi, une valeur anormale(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
if value_type != "BaseLayer":
raise AssignError(value_type)
self.__layer_list[key] = value
else:
raise KeyError(key, ": Undefined such key type.")
def __delitem__(self, key):
"""
Par exemple
lm = LayerManager()
+----------------+
| (Ajouter un élément à lm) |
+----------------+
del lm[2]
Parce qu'il est appelé lorsque l'élément de la liste ou du tableau est accédé par l'instruction del comme
Décrivez l'opération à ce moment-là.
Si l'élément spécifié existe, il sera supprimé et renommé.
"""
if isinstance(key, slice):
#Si la clé est une tranche, supprimez l'élément spécifié tel quel
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
del self.__layer_list[slice]
del self.__name_list[slice]
elif isinstance(key, str):
#Si key est une chaîne, récupérez l'index dans la liste des noms de chaque couche et
#Supprimez l'élément concerné.
if key in self.__name_list:
del self.__layer_list[index]
del self.__name_list[index]
else:
#Si la clé n'existe pas, une KeyError est émise.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Si la clé est un entier, supprimez l'élément correspondant dans la liste des couches.
#Valeur inhabituelle(Index hors de portée, etc.)Quand est entré
#Python me donne une erreur.
del self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
#Renommer
self._rename()
def _rename(self):
"""
Lorsque la dénomination de la liste de noms enfreint les règles en raison de l'opération de liste
Renommez la liste de dénomination et chaque couche pour respecter à nouveau les règles.
La règle de dénomination est[Type de calque][Quel nombre]ça ira.
Si le type de calque est Couche intermédiaire, Milieu
Sortie pour la couche de sortie
Il est abrégé en.
Le nombre est compté par type.
Aussi, ici encore__Compte ntypes.
"""
#Initialiser le nombre de couches par type
self.__ntype = np.zeros(self.N_TYPE)
#Recompter et renommer chaque couche
for i in range(len(self)):
if "Middle" in self.__name_list[i]:
self.__ntype[self.MIDDLE] += 1
self.__name_list[i] = "Middle{}".format(
self.__ntype[self.MIDDLE])
self.__layer_list[i].name = "Middle{}".format(
self.__ntype[self.MIDDLE])
elif "Output" in self.__name_list[i]:
self.__ntype[self.OUTPUT] += 1
self.__name_list[i] = "Output{}".format(
self.__ntype[self.OUTPUT])
self.__layer_list[i].name = "Output{}".format(
self.__ntype[self.OUTPUT])
else:
raise UndefinedLayerType(self.__name_list[i])
def append(self, *, name="Middle", **kwds):
"""
Implémentation de la méthode append familière, qui est une méthode pour ajouter des éléments à une liste.
"""
if "prev" in kwds:
# 'prev'Est inclus dans le mot-clé
#Cela signifie que le nombre d'éléments de la couche précédente est spécifié.
#Fondamentalement, il est censé être le moment d'insérer la première couche, donc
#En dehors de cela, il est essentiellement déterminé automatiquement et n'est pas spécifié.
if len(self) != 0:
if kwds["prev"] != self.__layer_list[-1].n:
#Erreur s'il ne correspond pas au nombre d'unités à la fin.
raise UnmatchUnitError(self.__layer_list[-1].n,
kwds["prev"])
else:
if len(self) == 0:
#La première couche doit toujours spécifier le nombre d'unités d'entrée.
raise UnmatchUnitError("Input units", "Unspecified")
else:
#Le nombre d'unités dans la dernière couche'kwds'Ajouter à
kwds["prev"] = self.__layer_list[-1].n
#Lisez le type de couche et modifiez le nom selon la règle de dénomination
if name == "Middle" or name == "mid" or name == "m":
name = "Middle"
elif name == "Output" or name == "out" or name == "o":
name = "Output"
else:
raise UndefinedLayerError(name)
#Ajoutez un calque.
if name == "Middle":
#Incrémenter la couche par type
self.__ntype[self.MIDDLE] += 1
#Ajouter au nom
name += str(self.__ntype[self.MIDDLE])
#Ajouter à la liste de noms
self.__name_list.append(name)
#Enfin, créez un calque et ajoutez-le à la liste.
self.__layer_list.append(
MiddleLayer(name=name, **kwds))
elif name == "Output":
#C'est également la même chose.
self.__ntype[self.OUTPUT] += 1
name += str(self.__ntype[self.OUTPUT])
self.__name_list.append(name)
self.__layer_list.append(
OutputLayer(name=name, **kwds))
#Si vous ne dessinez pas d'instruction else ici, modifiez le nom selon la règle de dénomination
#Déjà anormal au stade'name'Est omis.
def extend(self, lm):
"""
Un autre gestionnaire de couches qui existe déjà dans la méthode d'extension'lm'Des éléments de
Tout ajouter.
"""
if not isinstance(lm, LayerManager):
# 'lm'Erreur si l'instance de n'est pas LayerManager.
raise TypeError(type(lm), ": Unexpected type.")
if len(self) != 0:
if self.__layer_list[-1].n != lm[0].prev:
#Avec le nombre d'unités dans votre dernière couche
# 'lm'Erreur si le nombre d'entrées dans la première couche de n'est pas le même.
raise UnmatchUnitError(self.__layer_list[-1].n,
lm[0].prev)
#Chaque'extend'Ajouter par méthode
self.__layer_list.extend(lm.layer_list)
self.__name_list.extend(lm.name_list)
#Renommer
self._rename()
def insert(self, prev_name, name="Middle", **kwds):
"""
Dans la méthode d'insertion, spécifiez le nom du calque précédent et combinez-le avec ce calque.
Ajoutez un élément.
"""
# 'prev_name'Erreur si n'existe pas.
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 'prev'Est inclus dans le mot-clé
# 'prev_name'Erreur s'il ne correspond pas au nombre d'unités du calque spécifié dans.
if "prev" in kwds:
if kwds["prev"] \
!= self.__layer_list[self.index(prev_name)].n:
raise UnmatchUnitError(
kwds["prev"],
self.__layer_list[self.index(prev_name)].n)
# 'n'Est inclus dans le mot-clé
if "n" in kwds:
# 'prev_name'Si ce n'est pas le dernier
if prev_name != self.__name_list[-1]:
#Erreur s'il ne correspond pas au nombre d'unités dans la couche suivante.
if kwds["n"] != self.__layer_list[
self.index(prev_name)+1].prev:
raise UnmatchUnitError(
kwds["n"],
self.__layer_list[self.index(prev_name)].prev)
#S'il n'y a pas encore d'éléments'append'Donnez une erreur pour utiliser la méthode.
if len(self) == 0:
raise RuntimeError(
"You have to use 'append' method instead.")
#Obtenir l'index de l'emplacement d'insertion
index = self.index(prev_name) + 1
#Lisez le type de couche et modifiez le nom selon la règle de dénomination
if name == "Middle" or name == "mid" or name == "m":
name = "Middle"
elif name == "Output" or name == "out" or name == "o":
name = "Output"
else:
raise UndefinedLayerError(name)
#Insérer un élément
#En ce moment,'name'Ne respecte pas encore les règles de dénomination,
#Je le renommerai plus tard alors ne vous inquiétez pas.
if "Middle" in name:
self.__layer_list.insert(index,
MiddleLayer(name=name, **kwds))
self.__name_list.insert(index, name)
elif "Output" in name:
self.__layer_list.insert(index,
OutputLayer(name=name, **kwds))
self.__name_list.insert(index, name)
#Renommer
self._rename()
def extend_insert(self, prev_name, lm):
"""
C'est la fonction d'origine.
Il se comporte comme une combinaison de la méthode extend et de la méthode insert.
En termes simples, c'est comme insérer un autre gestionnaire de calques.
"""
if not isinstance(lm, LayerManager):
# 'lm'Erreur si l'instance de n'est pas LayerManager.
raise TypeError(type(lm), ": Unexpected type.")
# 'prev_name'Erreur si n'existe pas.
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
#Le nombre d'unités des couches avant et après l'emplacement spécifié et les première et dernière couches de lm
#S'ils ne correspondent pas, une erreur se produit.
if len(self) != 0:
if self.__layer_list[self.index(prev_name)].n \
!= lm.layer_list[0].prev:
#Avec le nombre d'unités dans votre emplacement désigné'lm'Le premier nombre d'unités dans
#S'ils ne correspondent pas, une erreur se produit.
raise UnmatchUnitError(
self.__layer_list[self.index(prev_name)].n,
lm.layer_list[0].prev)
if prev_name != self.__name_list[-1]:
# 'prev_name'N'est-ce pas ma dernière couche
if lm.layer_list[-1].n \
!= self.__layer_list[self.index(prev_name)+1].prev:
# 'lm'Le nombre d'unités à la fin et au niveau suivant de votre emplacement désigné
# 'prev'Erreur s'il ne correspond pas au nombre d'unités.
raise UnmatchUnitError(
lm.layer_list[-1].n,
self.__layer_list[self.index(prev_name)+1].prev)
else:
#Si vous n'avez aucun élément'extend'J'obtiens une erreur lors de l'utilisation de la méthode.
raise RuntimeError(
"You have to use 'extend' method instead.")
#Obtenir l'index de l'emplacement d'insertion
index = self.index(prev_name) + 1
#Éléments après l'emplacement d'insertion'buf'Après avoir évacué vers, retirez-le une fois
#Ajouter un élément à l'aide de la méthode extend
layer_buf = self.__layer_list[index:]
name_buf = self.__name_list[index:]
del self.__layer_list[index:]
del self.__name_list[index:]
self.extend(lm)
#Ajouter l'élément qui a été évacué
self.__layer_list.extend(layer_buf)
self.__name_list.extend(name_buf)
#Renommer
self._rename()
def remove(self, key):
"""
La méthode remove supprime l'élément avec le nom spécifié.
Il est également autorisé à être spécifié par index.
"""
#Déjà implémenté'del'La phrase est OK.
del self[key]
def index(self, target):
return self.__name_list.index(target)
def name(self, indices):
return self.__name_list[indices]
@property
def layer_list(self):
return self.__layer_list
@property
def name_list(self):
return self.__name_list
@property
def ntype(self):
return self.__ntype
def training(self, epoch, n_batch=16, threshold=1e-8,
show_error=True, **kwds):
if show_error:
self.error_list = []
if self.make_anim:
self.images = []
n_in = self.__layer_list[0].prev
n_out = self.__layer_list[-1].n
n_train = self.x_train.size//n_batch
n_test = self.x_test.size
#Commencer à apprendre
error = 0
error_prev = 0
rand_index = np.arange(self.x_train.size)
for t in tqdm.tqdm(range(1, epoch+1)):
#Création de scène
if self.make_anim:
self.make_scene(t, epoch)
#Calcul d'erreur
x_in = self.x_test.reshape(n_test, n_in)
for ll in self.__layer_list:
x_in = ll.forward(x_in)
error = lm[-1].get_error(self.y_test.reshape(n_test, n_out))
if show_error:
self.error_list.append(error)
#Jugement de convergence
if abs(error - error_prev) < threshold:
print("end learning...")
break
else:
error_prev = error
np.random.shuffle(rand_index)
for i in range(n_train):
rand = rand_index[i*n_in : (i+n_batch)*n_in]
x_in = self.x_train[rand].reshape(-1, n_in)
for ll in self.__layer_list:
x_in = ll.forward(x_in)
y_in = self.y_train[rand].reshape(-1, n_out)
for ll in self.__layer_list[::-1]:
y_in = ll.backward(y_in)
for ll in self.__layer_list:
ll.update()
if show_error:
#Affichage de transition d'erreur
self.show_errors(**kwds)
def show_errors(self, title="error transition",
xlabel="epoch", ylabel="error", fname="error_transition.png ",
**kwds):
fig, ax = plt.subplots(1)
fig.suptitle(title)
ax.set_yscale("log")
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.grid()
ax.plot(self.error_list)
fig.show()
if len(fname) != 0:
fig.savefig(fname)
def ready_anim(self, n_image, x, y, title="animation",
xlabel="x", ylabel="y", ex_color="r", color="b",
x_left=0, x_right=0, y_down = 1, y_up = 1):
self.n_image = n_image
self.x = x
self.color = color
self.make_anim = True
self.anim_fig, self.anim_ax = plt.subplots(1)
self.anim_fig.suptitle(title)
self.anim_ax.set_xlabel(xlabel)
self.anim_ax.set_ylabel(ylabel)
self.anim_ax.set_xlim(np.min(x) - x_left, np.max(x) + x_right)
self.anim_ax.set_ylim(np.min(y) - y_down, np.max(y) + y_up)
self.anim_ax.grid()
self.anim_ax.plot(x, y, color=ex_color)
return self.anim_fig, self.anim_ax
def make_scene(self, t, epoch):
#Création de scène
if t % (epoch/self.n_image) == 1:
x_in = self.x.reshape(-1, 1)
for ll in self.__layer_list:
x_in = ll.forward(x_in)
im, = self.anim_ax.plot(self.x, ll.y, color=self.color)
self.images.append([im])
en conclusion
Série d'apprentissage en profondeur