L'article précédent était ici
Dans cet article, nous avons introduit dans Liste des fonctions d'activation et Liste des méthodes de descente de gradient. Activators.py et [optimiseurs. py](https://qiita.com/kuroitu/items/36a58b37690d570dc618#%E5%AE%9F%E8%A3%85%E3%82%B3%E3%83%BC%E3%83%89%E4% Implémentez les fonctions get_act et get_opt pour appeler BE% 8B).
Nous allons également introduire et implémenter la fonction de perte actuellement utilisée.
Tout d'abord, je vais mettre le corps du code de la fonction d'activation.
activators.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
Vient ensuite la localisation de la méthode de descente de gradient. La méthode est la même.
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 = {
    "SDG": 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
C'est la fin de la localisation.
Tout d'abord, quelle est la fonction de perte? Commencer à partir de.
En parlant d'apprentissage profond, le but est d'approcher une certaine fonction objective avec un réseau de neurones. C'est en fait la même chose pour des problèmes tels que la reconnaissance d'image où la fonction objective n'est pas claire. Par exemple, dans le cas de la reconnaissance de nombres manuscrits, le nombre doit être sorti (pour être exact, un vecteur appelé expression one-hot) à la suite de l'entrée d'une image et de l'exécution d'un traitement.
En ce qui concerne cette fonction objective, nous sommes bien sûr moins susceptibles de connaître la fonction exacte. Même avec la reconnaissance numérique que les humains effectuent normalement, la question du type de traitement utilisé pour la reconnaissance n'est pas claire.
Par conséquent, en l'état, il n'y a pas d'indice pour faire progresser l'apprentissage. Avec cela, il est rafraîchissant de savoir comment apprendre et si la politique que vous apprenez est correcte. C'est comme étudier sans objectif.
Cependant, à moins de connaître la solution exacte de la fonction objectif, vous ne pouvez pas faire de différence par rapport à la fonction objectif. C'est là que le concept de ** fonction de perte ** entre en jeu.
En un mot, l'indice d'apprentissage n'est pas «à quel point la fonction objectif» mais «à quelle distance de la fonction objectif».
Prenant comme exemple l'apprentissage supervisé, mesurez à quel point la sortie de la fonction objectif (valeur correcte) et la sortie de la fonction approximative (valeur prédite) sont, et rendez la différence aussi proche de zéro que possible. pense.
La valeur prédite est calculée par ** propagation vers l'avant **, l'erreur entre la valeur de réponse correcte et la valeur prédite est transmise à chaque paramètre pour l'apprentissage de la ** propagation en retour **, et le paramètre est basé sur le gradient s'écoulant dans la propagation en retour. C'est une ** règle d'apprentissage ** à mettre à jour.
Et la ** fonction de perte est une fonction ** qui détermine l'erreur qui coule dans la propagation arrière.
C'est à peu près tout pour la fonction de perte. Voyons quel genre de choses sont concrètes.
Tout d'abord, je présenterai l '** erreur carrée ** utilisée dans le problème des groupes ouverts linéaires.
\mathcal{L}(y) = \cfrac{1}{2} (y - t)^2
$ t $ est la bonne réponse et $ y $ est la valeur prévue. L'équation ci-dessus est une représentation matricielle. La raison de la multiplication par 0,5 est qu'il est nécessaire d'effectuer une différenciation partielle lors de la rétro-propagation, et le coefficient est annulé à ce moment-là.
\cfrac{\partial \mathcal{L}}{\partial y} = \cfrac{1}{2} \times 2(y - t) = y - t
Cela facilite le calcul de la rétropropagation. Soit dit en passant, puisqu'il s'agit d'une erreur quadratique due à une rétropropagation, l '** erreur quadratique moyenne ** suivante est utilisée pour juger si l'apprentissage a convergé.
E = \cfrac{1}{N}\sum_{i=1}^{N}{\mathcal{L}(y_i)} = \cfrac{1}{N}\sum_{i=1}^{N}{\cfrac{1}{2}(y_i - t_i)^2}
N est le nombre de données. Lorsque cette valeur change à peine, cela signifie que l'apprentissage a convergé (il n'est pas garanti qu'il soit suffisamment précis). La mise en œuvre est la suivante. Dans un sens, nous avons implémenté les fonctions «avant» et «arrière» comme des classes pour les traiter comme des couches.
errors.py
class SquareError(Error):
    def forward(self, y, t, *args,**kwds):
        self.y = y
        self.t = t
        self.error = 0.5 * (y - t)**2
        return self.error
    
    
    def backward(self, *args,**kwds):
        return self.t - self.t
Vient ensuite ** l'entropie croisée binaire **. Il s'agit de la fonction d'erreur ajustée lorsque la fonction d'activation de la couche de sortie est la fonction sigmoïde. En d'autres termes, c'est ** la fonction de perte utilisée pour le problème de classification binaire **.
\mathcal{L}(y) = - t \log y - (1 - t) \log(1 - y)
La différenciation est
\cfrac{\partial \mathcal{L}}{\partial y} = \cfrac{y - t}{y(1 - y)}
Et la différenciation de la fonction sigmoïde apparaît dans le dénominateur. Par conséquent, le gradient se propageant à travers la couche de sortie
\underbrace{\cfrac{y - t}{y(1 - y)}}_{Différenciation de la fonction de perte} \times \underbrace{y(1 - y)}_{\textrm{Différenciation de la fonction sigmoïde}} = y - t
Ce sera une forme simple comme.
errors.py
class BinaryCrossEntropy(Error):
    def forward(self, y, t, *args,**kwds):
        self.y = y
        self.t = t
        self.error = - t*np.log(y) - (1 - t)*np.log(1 - y)
        return self.error
    
    
    def backward(self, *args,**kwds):
        return (self.y - self.t) / (self.y*(1 - self.y))
Vient ensuite l '** erreur d'entropie croisée ** utilisée dans ** lors de l'utilisation de la fonction softmax comme fonction d'activation de la couche de sortie dans un problème de classification à valeurs multiples **.
\mathcal{L}(y) = - t \log y
On peut dire que c'est une forme générale d'entropie croisée binaire. La différenciation est
\cfrac{\partial \mathcal{L}}{\partial y_i} = -\cfrac{t_i}{y_i}
Cependant, lors de l'utilisation de la fonction softmax comme fonction d'activation de la couche de sortie, compte tenu de la différenciation partielle pour l'entrée $ x_i $,
\begin{align}
  \left( \cfrac{\partial \mathcal{L}}{\partial y} \times \cfrac{\partial y}{\partial x} \right)_i &= \sum_{j=1}^{n}{\left( \cfrac{\partial \mathcal{L}}{\partial y_j} \times \cfrac{\partial y_j}{\partial x_i} \right)} \\
  &= \sum_{j=1}^{n}{
\left\{ \begin{array}{ccc}
    -\cfrac{t_i}{y_i} \times y_i (1 - y_i) & = t_i y_i - t_i & (j=i) \\
    -\cfrac{t_j}{y_j} \times (-y_i y_j) &= t_j y_i & (j \ne i)
\end{array} \right\}
} \\
  &= \underbrace{(\underbrace{t_i y_i}^{Avec ça} - t_i)}_{j=i} + \underbrace{y_i \sum_{j=1, j\ne i}^{n}{t_j}}_{j \ne i}^{Pour résumer ceci} \\
  &= \underbrace{y_i \sum_{j=1}^{n}{t_j}}^{Sera comme ça} - t_i \\
  &= y_i - t_i \quad (\because \textrm{one-hot}Parce que c'est un vecteur\sum_{j=1}^{n}{t_j} =Devenir 1)
\end{align}
Ce sera une belle forme comme. S'il s'agit d'un graphe de calcul
Quelque chose comme ça. C'est un mystère compliqué ...
errors.py
class CrossEntropy(Error):
    def forward(self, y, t, *args,**kwds):
        self.y = y
        self.t = t
        self.error = - t*np.log(y)
        return self.error
    
    
    def backward(self, *args,**kwds):
        return - self.t/self.y
Je vais également localiser la fonction de perte.
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.y = y
        self.t = t
        self.error = 0.5 * (y - t)**2
        return self.error
    
    
    def backward(self, *args,**kwds):
        return self.y - self.t
class BinaryCrossEntropy(Error):
    def forward(self, y, t, *args,**kwds):
        self.y = y
        self.t = t
        self.error = - t*np.log(y) - (1 - t)*np.log(1 - y)
        return self.error
    
    
    def backward(self, *args,**kwds):
        return (self.y - self.t) / (self.y*(1 - self.y))
    
class CrossEntropy(Error):
    def forward(self, y, t, *args,**kwds):
        self.y = y
        self.t = t
        self.error = - t*np.log(y)
        return self.error
    
    
    def backward(self, *args,**kwds):
        return - self.t/self.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
Je me demande si je vais un jour résumer les fonctions de perte utilisées dans d'autres machines liées au machine learning ... Mais avant cela, dois-je étudier d'autres méthodes d'apprentissage automatique?
Recommended Posts