web-dev-qa-db-fra.com

Quel est le meilleur moyen de mettre en œuvre des contraintes de poids dans TensorFlow?

Supposons que nous avons des poids

x = tf.Variable(np.random.random((5,10)))
cost = ...

Et nous utilisons l'optimiseur Gd:

upds = tf.train.GradientDescentOptimizer(lr).minimize(cost)
session.run(upds)

Comment pouvons-nous implémenter par exemple la non-négativité sur les poids?

J'ai essayé de les couper:

upds = tf.train.GradientDescentOptimizer(lr).minimize(cost)
session.run(upds)
session.run(tf.assign(x, tf.clip_by_value(x, 0, np.infty)))

Mais cela ralentit mon entraînement par un facteur de 50.

Est-ce que quelqu'un connaît un bon moyen d'appliquer de telles contraintes sur les poids dans TensorFlow?

P.S .: dans l'algorithme équivalent de Theano, j'avais

T.clip(x, 0, np.infty)

et ça s'est bien passé.

28
LDGN

Vous pouvez adopter l'approche lagrangienne et ajouter simplement une pénalité pour les caractéristiques de la variable que vous ne voulez pas. 

par exemple. Pour encourager theta à être non négatif, vous pouvez ajouter ce qui suit à la fonction objectif de l'optimiseur.

    added_loss = -tf.minimum( tf.reduce_min(theta),0)

Si une variable theta est négative, alors add2loss sera positif, sinon zéro. Le passage à une valeur significative est laissé au lecteur. Une mise à l'échelle insuffisante n'exercera pas une pression suffisante. Trop peut rendre les choses instables.

21
Mark Borgerding

A partir de TensorFlow 1.4, il existe un nouvel argument à tf.get_variable qui permet de transmettre une fonction de contrainte appliquée après la mise à jour de l'optimiseur. Voici un exemple qui applique une contrainte de non-négativité:

with tf.variable_scope("MyScope"):
  v1 = tf.get_variable("v1", …, constraint=lambda x: tf.clip_by_value(x, 0, np.infty))

contrainte: fonction de projection facultative à appliquer au fichier variable après avoir été mis à jour par un Optimizer (utilisé par exemple pour implémenter les contraintes de norme ou les contraintes de valeur pour les pondérations de couche). La fonction doit prend en entrée le tenseur non projeté représentant la valeur du variable et renvoyer le tenseur pour la valeur projetée (qui doit avoir la même forme). Les contraintes ne sont pas sécuritaires pour utiliser lors d’une formation distribuée asynchrone.

17
Robin Dinse

En exécutant

sess.run(tf.assign(x, tf.clip_by_value(x, 0, np.infty)))

vous ajoutez constamment des noeuds au graphique et le ralentissez.

En fait, vous pouvez simplement définir un clip_op lors de la création du graphique et l'exécuter à chaque fois après la mise à jour des poids:

# build the graph
x = tf.Variable(np.random.random((5,10)))
loss = ...
train_op = tf.train.GradientDescentOptimizer(lr).minimize(loss)
clip_op = tf.assign(x, tf.clip(x, 0, np.infty))

# train
sess.run(train_op)
sess.run(clip_op)
15
lihao

J'ai récemment eu ce problème aussi. J'ai découvert que vous pouvez importer des keras qui ont des fonctions de contrainte de poids Nice comme les utiliser directement dans la contrainte kernen dans tensorflow. Voici un exemple de mon code. Vous pouvez faire la même chose avec le régularisateur de noyau 

from keras.constraints import non_neg

conv1 = tf.layers.conv2d(
    inputs=features['x'],
    filters=32,
    kernel_size=[5,5],
    strides = 2,
    padding='valid',
    activation=tf.nn.relu,
    kernel_regularizer=None,
    kernel_constraint=non_neg(),
    use_bias=False)
1
Jaja Noether

Il existe une solution pratique: Votre fonction de coût peut être écrite par vous-même, pour augmenter les coûts sur les poids négatifs. Je l'ai fait dans un modèle de factorisation matricielle dans TensorFlow avec python, et cela a assez bien fonctionné. Droite? Je veux dire que c'est évident. Mais personne d'autre n'en a parlé, alors allez-y. EDIT: Je viens de voir que Mark Borderding a également donné une autre implémentation de solution basée sur les coûts et les pertes avant moi.

Et si "le meilleur moyen" est souhaité, comme demandé par le PO, alors quoi? Bien "mieux" peut en fait être spécifique à une application, auquel cas vous devez essayer différentes manières avec le jeu de données votre et prendre en compte les exigences de l'application {votre}.

Voici un code de travail permettant d’augmenter le coût des variables de solution négatives indésirables:

cost = tf.reduce_sum(keep_loss) + Lambda * reg # Cost = sum of losses for training set, except missing data.        
if prefer_nonneg: # Optionally increase cost for negative values in rhat, if you want that.
    negs_indices = tf.where(rhat < tf.constant(0.0))
    neg_vals = tf.gather_nd(rhat, negs_indices)
    cost += 2. * tf.reduce_sum(tf.abs(neg_vals))  # 2 is a magic number (empirical parameter)         

Vous êtes libre d'utiliser mon code, mais donnez-moi un peu de crédit si vous choisissez de l'utiliser. Donnez un lien vers cette réponse sur stackoverflow.com s'il vous plaît.

Cette conception serait considérée comme une contrainte douce, car vous pouvez toujours obtenir des pondérations négatives, si vous le laissez, en fonction de votre définition de coût.

Il semble que la contrainte = soit également disponible dans TF v1.4 + en tant que paramètre de tf.get_variable (), où vous pouvez transmettre une fonction comme tf.clip_by_value. Cela semble être une autre contrainte douce, pas une contrainte dure, à mon avis, car cela dépend de votre fonction de bien fonctionner ou non. Cela peut également être lent, car l’autre répondeur a utilisé la même fonction et indiqué que sa convergence était lente, bien qu’ils n’aient pas utilisé le paramètre constraint = pour le faire. Je ne vois aucune raison pour que l'un soit plus rapide que l'autre, car ils utilisent tous les deux la même approche de découpage. Donc, si vous utilisez le paramètre constraint =, vous devez vous attendre à une convergence lente dans le contexte de l'application de l'affiche d'origine.

Il serait plus agréable que TF fournisse également de véritables contraintes strictes à l'API et lui permette de déterminer comment l'implémenter et la rendre efficace en bout de ligne. Je veux dire, cela fait déjà longtemps que je vois cela dans les solveurs de programmation linéaire. L'application déclare une contrainte et le back-end la réalise.

0
Geoffrey Anderson