Como adicionar regularizações no TensorFlow?

94

Descobri em muitos códigos de rede neural disponíveis implementados usando o TensorFlow que os termos de regularização são frequentemente implementados adicionando manualmente um termo adicional ao valor de perda.

Minhas perguntas são:

  1. Existe uma maneira mais elegante ou recomendada de regularização do que manualmente?

  2. Eu também acho que isso get_variabletem um argumento regularizer. Como deve ser usado? De acordo com minha observação, se passarmos um regularizador para ele (como tf.contrib.layers.l2_regularizerum tensor que representa o termo regularizado será calculado e adicionado a uma coleção de gráficos chamada tf.GraphKeys.REGULARIZATOIN_LOSSES. Essa coleção será usada automaticamente pelo TensorFlow (por exemplo, usada por otimizadores durante o treinamento)? é esperado que eu use essa coleção sozinho?

Lifu Huang
fonte
1
só pra ser super explícito, é o jeito de fazer isso S = tf.get_variable(name='S', regularizer=tf.contrib.layers.l2_regularizer )?
Pinóquio
@Pinocchio, você descobriu?
Euler_Salter
2
@Euler_Salter Não me lembro mais, desculpe! Não está mais usando tensor flow!
Pinóquio

Respostas:

70

Como você disse no segundo ponto, usar o regularizerargumento é a forma recomendada. Você pode usá-lo em get_variableou configurá-lo uma vez em seu variable_scopee regularizar todas as suas variáveis.

As perdas são coletadas no gráfico e você precisa adicioná-las manualmente à sua função de custo desta forma.

  reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
  reg_constant = 0.01  # Choose an appropriate one.
  loss = my_normal_loss + reg_constant * sum(reg_losses)

Espero que ajude!

Lukasz Kaiser
fonte
2
Obrigado cara. Eu estava pensando que o TensorFlow teria algumas maneiras mais inteligentes de lidar com termos de registro do que manualmente, parece que não: P
Lifu Huang
14
BTW, duas sugestões, corrija-me se eu estiver errado. (1), acho que reg_constantpode não ser necessário, já que os regularizadores no TensorFlow têm um argumento scaleem seus construtores para que o impacto dos termos de registro possa ser controlado de maneira mais refinada. E (2) usar tf.add_npode ser um pouco melhor do que sum, acho que usar soma pode criar muitos tensores no gráfico para armazenar o resultado intermediário.
Lifu Huang
1
Então, só para deixar bem claro, depois de colocar o regularizador na variável S = tf.get_variable(name='S', regularizer=tf.contrib.layers.l2_regularizer ), faço o código que você sugeriu? Como em sum(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES))?
Pinóquio
1
Poderia mostrar como fazer as variáveis ​​de pesos fazerem parte da coleção recuperável por tf.get_collection (tf.GraphKeys.REGULARIZATION_LOSSES)?
Yu Shen
3
Parece que tf.reduce_sumdeve ser usado em vez de sum?
ComputerScientist
45

Alguns aspectos da resposta existente não ficaram imediatamente claros para mim, então aqui está um guia passo a passo:

  1. Defina um regularizador. É aqui que a constante de regularização pode ser definida, por exemplo:

    regularizer = tf.contrib.layers.l2_regularizer(scale=0.1)
  2. Crie variáveis ​​por meio de:

        weights = tf.get_variable(
            name="weights",
            regularizer=regularizer,
            ...
        )

    De forma equivalente, as variáveis ​​podem ser criadas por meio do weights = tf.Variable(...)construtor regular , seguido por tf.add_to_collection(tf.GraphKeys.REGULARIZATION_LOSSES, weights).

  3. Defina algum losstermo e adicione o termo de regularização:

    reg_variables = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
    reg_term = tf.contrib.layers.apply_regularization(regularizer, reg_variables)
    loss += reg_term

    Observação: parece que tf.contrib.layers.apply_regularizationestá implementado como um AddN, portanto, mais ou menos equivalente a sum(reg_variables).

bluenote10
fonte
10
Acho que você está aplicando o regularizador duas vezes - na etapa e na etapa 3. apply_regularizationnão deve ser necessário se você já especificou o regularizador ao criar a variável.
intervalo de
2
@interjay por favor, dê um exemplo, todas essas respostas são super obscuras! Isso ocorre porque sempre há pelo menos uma pessoa escrevendo um comentário abaixo, dizendo que a resposta acima tem algo errado.
Euler_Salter
1
@interjay Tenho certeza de que fazer as duas coisas foi necessário da última vez que testei. Não tenho certeza se isso mudou.
bluenote10
1
Não, isso não faz sentido porque você não precisaria passar o mesmo regularizador para duas funções. A documentação (e o nome) deixa claro que REGULARIZATION_LOSSESé a perda total retornada dos regularizadores, então você está essencialmente ligando regularizer(regularizer(weight)).
entre
1
Acho que a confusão aqui vem da parte "equivalentemente". Ele descreve dois métodos diferentes e você escolhe um, não é um método que envolve aplicar a regularização duas vezes.
gcp
28

Vou dar uma resposta correta simples, pois não encontrei nenhuma. Você precisa de duas etapas simples, o resto é feito por magia tensorflow:

  1. Adicione regularizadores ao criar variáveis ​​ou camadas:

    tf.layers.dense(x, kernel_regularizer=tf.contrib.layers.l2_regularizer(0.001))
    # or
    tf.get_variable('a', regularizer=tf.contrib.layers.l2_regularizer(0.001))
  2. Adicione o termo de regularização ao definir a perda:

    loss = ordinary_loss + tf.losses.get_regularization_loss()
Aliaxia
fonte
Se eu estiver criando um regularizador op por regularizer = tf.contrib.layers.l2_regularizer (0,001), posso passá-lo para várias iniciações de camada? ou preciso criar um regularizador separado para cada camada likeregularizer1 = tf.contrib.layers.l2_regularizer (0,001), regularizer2 = ................. regularizer3 = .... .. e assim por diante?
MiloMinderbinder
@Nitin Você pode usar o mesmo regularizador. É apenas uma função python que aplica perda a pesos como seu argumento.
alyaxey,
1
Parece a solução mais elegante, mas realmente funciona? Qual é a diferença em dizer reg_variables = tf.get_collection (tf.GraphKeys.REGULARIZATION_LOSSES) reg_term = tf.contrib.layers.apply_regularization (regularizer, reg_variables) loss + = reg_term
GeorgeOfTheRF
1
Só quero mencionar que tf.contrib.layers.fully_connected pode substituir tf.layers.dense e, além disso, adicionar mais funcionalidades. Consulte estes: isso , isso e isso .
Osama Salah
16

Outra opção para fazer isso com a contrib.learnbiblioteca é a seguinte, com base no tutorial Deep MNIST no site Tensorflow. Primeiro, supondo que você importou as bibliotecas relevantes (como import tensorflow.contrib.layers as layers), você pode definir uma rede em um método separado:

def easier_network(x, reg):
    """ A network based on tf.contrib.learn, with input `x`. """
    with tf.variable_scope('EasyNet'):
        out = layers.flatten(x)
        out = layers.fully_connected(out, 
                num_outputs=200,
                weights_initializer = layers.xavier_initializer(uniform=True),
                weights_regularizer = layers.l2_regularizer(scale=reg),
                activation_fn = tf.nn.tanh)
        out = layers.fully_connected(out, 
                num_outputs=200,
                weights_initializer = layers.xavier_initializer(uniform=True),
                weights_regularizer = layers.l2_regularizer(scale=reg),
                activation_fn = tf.nn.tanh)
        out = layers.fully_connected(out, 
                num_outputs=10, # Because there are ten digits!
                weights_initializer = layers.xavier_initializer(uniform=True),
                weights_regularizer = layers.l2_regularizer(scale=reg),
                activation_fn = None)
        return out 

Então, em um método principal, você pode usar o seguinte snippet de código:

def main(_):
    mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True)
    x = tf.placeholder(tf.float32, [None, 784])
    y_ = tf.placeholder(tf.float32, [None, 10])

    # Make a network with regularization
    y_conv = easier_network(x, FLAGS.regu)
    weights = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 'EasyNet') 
    print("")
    for w in weights:
        shp = w.get_shape().as_list()
        print("- {} shape:{} size:{}".format(w.name, shp, np.prod(shp)))
    print("")
    reg_ws = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES, 'EasyNet')
    for w in reg_ws:
        shp = w.get_shape().as_list()
        print("- {} shape:{} size:{}".format(w.name, shp, np.prod(shp)))
    print("")

    # Make the loss function `loss_fn` with regularization.
    cross_entropy = tf.reduce_mean(
        tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
    loss_fn = cross_entropy + tf.reduce_sum(reg_ws)
    train_step = tf.train.AdamOptimizer(1e-4).minimize(loss_fn)

Para fazer isso funcionar, você precisa seguir o tutorial MNIST ao qual vinculei anteriormente e importar as bibliotecas relevantes, mas é um bom exercício aprender o TensorFlow e é fácil ver como a regularização afeta a saída. Se você aplicar uma regularização como argumento, poderá ver o seguinte:

- EasyNet/fully_connected/weights:0 shape:[784, 200] size:156800
- EasyNet/fully_connected/biases:0 shape:[200] size:200
- EasyNet/fully_connected_1/weights:0 shape:[200, 200] size:40000
- EasyNet/fully_connected_1/biases:0 shape:[200] size:200
- EasyNet/fully_connected_2/weights:0 shape:[200, 10] size:2000
- EasyNet/fully_connected_2/biases:0 shape:[10] size:10

- EasyNet/fully_connected/kernel/Regularizer/l2_regularizer:0 shape:[] size:1.0
- EasyNet/fully_connected_1/kernel/Regularizer/l2_regularizer:0 shape:[] size:1.0
- EasyNet/fully_connected_2/kernel/Regularizer/l2_regularizer:0 shape:[] size:1.0

Observe que a parte de regularização fornece três itens, com base nos itens disponíveis.

Com regularizações de 0, 0,0001, 0,01 e 1,0, obtenho valores de precisão de teste de 0,9468, 0,9476, 0,9183 e 0,1135, respectivamente, mostrando os perigos de termos de alta regularização.

ComputerScientist
fonte
2
Exemplo realmente detalhado.
stackoverflowuser2010
5

Se alguém ainda estiver procurando, eu só gostaria de acrescentar que em tf.keras você pode adicionar regularização de peso passando-os como argumentos em suas camadas. Um exemplo de adição de regularização L2 obtido no atacado do site Tensorflow Keras Tutorials:

model = keras.models.Sequential([
    keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),
                       activation=tf.nn.relu, input_shape=(NUM_WORDS,)),
    keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),
                       activation=tf.nn.relu),
    keras.layers.Dense(1, activation=tf.nn.sigmoid)
])

Não há necessidade de adicionar manualmente as perdas de regularização com este método, pelo que eu sei.

Referência: https://www.tensorflow.org/tutorials/keras/overfit_and_underfit#add_weight_regularization

MoltenMuffins
fonte
4

Testei tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)e tf.losses.get_regularization_loss()com um l2_regularizerno gráfico, e descobri que eles retornam o mesmo valor. Observando a quantidade do valor, acho que reg_constant já faz sentido no valor definindo o parâmetro de tf.contrib.layers.l2_regularizer.

oceano
fonte
3

Se você tem CNN, pode fazer o seguinte:

Em sua função de modelo:

conv = tf.layers.conv2d(inputs=input_layer,
                        filters=32,
                        kernel_size=[3, 3],
                        kernel_initializer='xavier',
                        kernel_regularizer=tf.contrib.layers.l2_regularizer(1e-5),
                        padding="same",
                        activation=None) 
...

Em sua função de perda:

onehot_labels = tf.one_hot(indices=tf.cast(labels, tf.int32), depth=num_classes)
loss = tf.losses.softmax_cross_entropy(onehot_labels=onehot_labels, logits=logits)
regularization_losses = tf.losses.get_regularization_losses()
loss = tf.add_n([loss] + regularization_losses)
tsveti_iko
fonte
1

Algumas respostas me deixam mais confuso. Aqui, dou dois métodos para torná-lo claro.

#1.adding all regs by hand
var1 = tf.get_variable(name='v1',shape=[1],dtype=tf.float32)
var2 = tf.Variable(name='v2',initial_value=1.0,dtype=tf.float32)
regularizer = tf.contrib.layers.l1_regularizer(0.1)
reg_term = tf.contrib.layers.apply_regularization(regularizer,[var1,var2])
#here reg_term is a scalar

#2.auto added and read,but using get_variable
with tf.variable_scope('x',
        regularizer=tf.contrib.layers.l2_regularizer(0.1)):
    var1 = tf.get_variable(name='v1',shape=[1],dtype=tf.float32)
    var2 = tf.get_variable(name='v2',shape=[1],dtype=tf.float32)
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
#here reg_losses is a list,should be summed 

Então, ele pode ser adicionado à perda total

user3201329
fonte
1
cross_entropy = tf.losses.softmax_cross_entropy(
  logits=logits, onehot_labels=labels)

l2_loss = weight_decay * tf.add_n(
     [tf.nn.l2_loss(tf.cast(v, tf.float32)) for v in tf.trainable_variables()])

loss = cross_entropy + l2_loss
Alex-zhai
fonte
1
Obrigado por este trecho de código, que pode fornecer alguma ajuda limitada e imediata. Uma explicação adequada melhoraria muito seu valor a longo prazo, mostrando por que essa é uma boa solução para o problema e a tornaria mais útil para futuros leitores com outras questões semelhantes. Edite sua resposta para adicionar alguma explicação, incluindo as suposições que você fez.
Maximilian Peters
1

tf.GraphKeys.REGULARIZATION_LOSSES não serão adicionados automaticamente, mas há uma maneira simples de adicioná-los:

reg_loss = tf.losses.get_regularization_loss()
total_loss = loss + reg_loss

tf.losses.get_regularization_loss()usa tf.add_npara somar as entradas de tf.GraphKeys.REGULARIZATION_LOSSESelemento sábio. tf.GraphKeys.REGULARIZATION_LOSSESnormalmente será uma lista de escalares, calculada usando funções de regularizador. Ele obtém entradas de chamadas para tf.get_variableque tenham o regularizerparâmetro especificado. Você também pode adicionar itens a essa coleção manualmente. Isso seria útil ao usar tf.Variablee também ao especificar regularizadores de atividade ou outros regularizadores personalizados. Por exemplo:

#This will add an activity regularizer on y to the regloss collection
regularizer = tf.contrib.layers.l2_regularizer(0.1)
y = tf.nn.sigmoid(x)
act_reg = regularizer(y)
tf.add_to_collection(tf.GraphKeys.REGULARIZATION_LOSSES, act_reg)

(Neste exemplo, presumivelmente, seria mais eficaz regularizar x, já que y realmente se nivela para x grande.)

Elias Hasle
fonte