Récemment, je me suis éloigné des tâches d'apprentissage automatique (j'écris Rails depuis longtemps ... je suis sur le point de retourner dans le monde du machine learning ...) Je n'ai pas encore lu l'article d'Adam, alors je l'ai lu et mis en œuvre de manière appropriée.
motivation Je souhaite créer un modèle facile à mettre en œuvre, qui présente une bonne efficacité de calcul, qui économise de la mémoire, qui n'est pas facilement affecté par l'échelle et qui s'adapte aux données / paramètres à grande échelle.
Adaptive moment estimation
J'ai fait une comparaison avec Adam, SGDNesterov et la régression logistique au lasso. Les données sont https://www.kaggle.com/c/data-science-london-scikit-learn/data A été utilisé. J'utilise ces données parce que je voulais voir si les performances seraient bonnes même avec une petite quantité de données. Étant donné que les données de test ne sont pas étiquetées, les données de formation sont utilisées séparément pour la formation et les tests à 8: 2. Ce n'est pas en italique dans Adam, SGD Nesterov.
Désolé pour le code sale, mais je vais le coller ci-dessous. Adam
python
# coding: utf-8
import numpy as np
import math
from itertools import izip
from sklearn.metrics import accuracy_score, recall_score
class Adam:
    def __init__(self, feat_dim, loss_type='log', alpha=0.001, beta1=0.9, beta2=0.999, epsilon=10**(-8)):
        self.weight = np.zeros(feat_dim)  # features weight
        self.loss_type = loss_type  # type of loss function
        self.feat_dim = feat_dim  # number of dimension
        self.x = np.zeros(feat_dim)  # feature
        self.m = np.zeros(feat_dim)  # 1st moment vector
        self.v = np.zeros(feat_dim)  # 2nd moment vector
        self.alpha = alpha  # step size
        self.beta1 = beta1  # Exponential decay rates for moment estimates
        self.beta2 = beta2  # Exponential decay rates for moment estimates
        self.epsilon = epsilon
        self.t = 1  # timestep
    def fit(self, data_fname, label_fname):
        with open(data_fname, 'r') as f_data, open(label_fname, 'r') as f_label:
            for data, label in izip(f_data, f_label):
                self.features = np.array(data.rstrip().split(','), dtype=np.float64)
                y = int(-1) if int(label.rstrip())<=0 else int(1)  # posi=1, nega=-Unifié à 1
                # update weight
                self.update(self.predict(self.features), y)
                self.t += 1
        return self.weight
    def predict(self, features): #margin
        return np.dot(self.weight, features)
    def calc_loss(self,m): # m=py=wxy
        if self.loss_type == 'hinge':
            return max(0,1-m)
        elif self.loss_type == 'log':
            # if m<=-700: m=-700
            return math.log(1+math.exp(-m))
    # gradient of loss function
    def calc_dloss(self,m): # m=py=wxy
        if self.loss_type == 'hinge':
            res = -1.0 if (1-m)>0 else 0.0 #perte si la perte ne dépasse pas 0=0.Autrement-Par différenciation de m-Devenir 1
            return res
        elif self.loss_type == 'log':
            if m < 0.0:
                return float(-1.0) / (math.exp(m) + 1.0) # yx-e^(-m)/(1+e^(-m))*yx
            else:
                ez = float( math.exp(-m) )
                return -ez / (ez + 1.0) # -yx+1/(1+e^(-m))*yx
    def update(self, pred, y):
        grad = y*self.calc_dloss(y*pred)*self.features  # gradient
        self.m = self.beta1*self.m + (1 - self.beta1)*grad  # update biased first moment estimate
        self.v = self.beta2*self.v + (1 - self.beta2)*grad**2  # update biased second raw moment estimate
        mhat = self.m/(1-self.beta1**self.t)  # compute bias-corrected first moment estimate
        vhat = self.v/(1-self.beta2**self.t)  # compute bias-corrected second raw moment estimate
        self.alpha *= np.sqrt(1-self.beta2**self.t)/(1-self.beta1**self.t)  # update stepsize
        self.weight -= self.alpha * mhat/(np.sqrt(vhat) + self.epsilon)  # update weight
if __name__=='__main__':
    data_fname = 'train800.csv'
    label_fname = 'trainLabels800.csv'
    test_data_fname = 'test200.csv'
    test_label_fname = 'testLabels200.csv'
    adam = Adam(40, loss_type='hinge')
    adam.fit(data_fname, label_fname)
    y_true = []
    y_pred = []
    with open(test_data_fname, 'r') as f_data, open(test_label_fname, 'r') as f_label:
        for data, label in izip(f_data, f_label):
            pred_label = adam.predict(np.array(data.rstrip().split(','), dtype=np.float64))
            y_true.append(int(label))
            y_pred.append( 1 if pred_label>0 else 0)
    print 'accuracy:', accuracy_score(y_true, y_pred)
    print 'recall:', recall_score(y_true, y_pred)
SGDNesterov
python
# coding: utf-8
import numpy as np
import math
from itertools import izip
from sklearn.metrics import accuracy_score, recall_score
class SgdNesterov:
    def __init__(self, feat_dim, loss_type='log', mu=0.9, learning_rate=0.5):
        self.weight = np.zeros(feat_dim)  # features weight
        self.loss_type = loss_type  # type of loss function
        self.feat_dim = feat_dim
        self.x = np.zeros(feat_dim)
        self.mu = mu  # momentum
        self.t = 1  # update times
        self.v = np.zeros(feat_dim)
        self.learning_rate = learning_rate
    def fit(self, data_fname, label_fname):
        with open(data_fname, 'r') as f_data, open(label_fname, 'r') as f_label:
            for data, label in izip(f_data, f_label):
                self.features = np.array(data.rstrip().split(','), dtype=np.float64)
                y = int(-1) if int(label.rstrip())<=0 else int(1)  # posi=1, nega=-Unifier à 1
                # update weight
                self.update(y)
                self.t += 1
        return self.weight
    def predict(self, features): #margin
        return np.dot(self.weight, features)
    def calc_loss(self,m): # m=py=wxy
        if self.loss_type == 'hinge':
            return max(0,1-m)
        elif self.loss_type == 'log':
            if m<=-700: m=-700
            return math.log(1+math.exp(-m))
    # gradient of loss function
    def calc_dloss(self,m): # m=py=wxy
        if self.loss_type == 'hinge':
            res = -1.0 if (1-m)>0 else 0.0 #perte si la perte ne dépasse pas 0=0.Autrement-Par différenciation de m-Devenir 1
            return res
        elif self.loss_type == 'log':
            if m < 0.0:
                return float(-1.0) / (math.exp(m) + 1.0) # yx-e^(-m)/(1+e^(-m))*yx
            else:
                ez = float( math.exp(-m) )
                return -ez / (ez + 1.0) # -yx+1/(1+e^(-m))*yx
    def update(self, y):
        w_ahead = self.weight + self.mu * self.v
        pred = np.dot(w_ahead, self.features)
        grad = y*self.calc_dloss(y*pred)*self.features # gradient
        self.v = self.mu * self.v - self.learning_rate * grad # velocity update stays the same
        # update weight
        self.weight += self.v
if __name__=='__main__':
    data_fname = 'train800.csv'
    label_fname = 'trainLabels800.csv'
    test_data_fname = 'test200.csv'
    test_label_fname = 'testLabels200.csv'
    sgd_n = SgdNesterov(40, loss_type='hinge')
    sgd_n.fit(data_fname, label_fname)
    y_true = []
    y_pred = []
    with open(test_data_fname, 'r') as f_data, open(test_label_fname, 'r') as f_label:
        for data, label in izip(f_data, f_label):
            pred_label = sgd_n.predict(np.array(data.rstrip().split(','), dtype=np.float64))
            y_true.append(int(label))
            y_pred.append( 1 if pred_label>0 else 0)
    print 'accuracy:', accuracy_score(y_true, y_pred)
    print 'recall:', recall_score(y_true, y_pred)
python
import numpy as np
from itertools import izip
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import recall_score
def get_data(data_fname, label_fname):
    result_data = []
    result_labels = []
    with open(data_fname, 'r') as f_data, open(label_fname, 'r') as f_label:
        for data, label in izip(f_data, f_label):
            result_data.append(data.rstrip().split(','))
            result_labels.append(int(label.rstrip()))
    return np.array(result_data, dtype=np.float64), result_labels
if __name__=='__main__':
     data_fname = 'train800.csv'
    label_fname = 'trainLabels800.csv'
    test_data_fname = 'test200.csv'
    test_label_fname = 'testLabels200.csv'
    data, labels = get_data(data_fname, label_fname)
    test_data, test_labels = get_data(test_data_fname, test_label_fname)
    lr = LogisticRegression()
    model = lr.fit(data, labels)
    y_pred = model.predict(test_data)
    print 'accuracy:', model.score(test_data, test_labels)
    print 'recall:', recall_score(test_labels, y_pred)
Je pensais qu'il y avait peu de données et que ce serait un obstacle à l'optimisation de l'alpha, alors j'ai fixé l'alpha et c'est devenu plus précis. (Je pense que l'optimisation de l'alpha sert à bien apprendre avec un modèle profond. Je pense qu'il est correct de supprimer cette fois.) Le vhat ressemble à ceci:
[  1.01440993   1.03180357   0.95435572   0.9297218   21.07682674
   0.94186528   4.65151802   5.00409033   0.99502491   1.04799237
   1.03563918   1.01860187  24.53366684   0.99717628   4.56930882
   0.99764606   0.95268578   1.00007278   4.94184457   0.96486898
   0.9665374    0.89604119   5.77110996  18.18369869   1.06281087
   0.98975868   1.01176115   1.06529464   5.55623853   5.52265492
   1.00727474   1.00094686   5.23052382   1.0256952    4.53388121
   1.0003947    5.4024963    0.98662918   4.86086664   4.4993808 ]
 [  0.70211545   0.70753131   0.68225521   0.65766954  14.23198314
   0.66457665   3.00986265   3.73453379   0.70920046   0.71507415
   0.7611441    0.71763729  12.45908405   0.71818535   2.44396968
   0.72608443   0.62573733   0.697053     3.06402831   0.64277643
   0.68346131   0.59957144   3.99612146  11.69024055   0.75532095
   0.68612789   0.69620363   0.75933189   3.41557243   4.05831119
   0.7255359    0.72140109   3.55049677   0.73630123   2.77828369
   0.69178571   3.82801224   0.68480352   3.70976494   2.96358695]
Il peut être nécessaire de changer la gestion de la mise à jour automatique de l'alpha pour chaque donnée, mais j'ai trouvé que même les petites données semblent être Adam fortes (petite sensation moyenne). Adam lui-même est déjà implémenté dans le chainer, etc., donc je pense que vous devriez essayer la compatibilité avec diverses données qui l'utilisent.
Nous vous prions de nous excuser pour la gêne occasionnée, mais nous vous serions reconnaissants de bien vouloir signaler toute erreur.
Recommended Posts