Як я можу використовувати пакетну нормалізацію в TensorFlow?


77

Я хотів би використовувати пакетну нормалізацію в TensorFlow. Я знайшов відповідний вихідний код C ++ уcore/ops/nn_ops.cc . Однак я не знайшов цього задокументованого на tensorflow.org.

BN має різну семантику в MLP та CNN, тому я не впевнений, що саме робить цей BN.

Я не знайшов методу, який також називається MovingMoments.


1
Я вважаю, що зараз у нього є документація: tensorflow.org/versions/r0.9/api_docs/python/…
Буратіно,

Я не думаю, що вже існує такий tf.Op (BatchNormWithGlobalNormalization)
Буратіно,

Я думаю, що є офіційний рівень BN github.com/tensorflow/tensorflow/blob/…
Буратіно,

Відповіді:


57

Оновлення липня 2016 р . Найпростіший спосіб використовувати пакетну нормалізацію в TensorFlow - за допомогою інтерфейсів вищого рівня, що надаються або в contrib / шарах , і в tflearn , і в тонкому .

Попередня відповідь, якщо ви хочете зробити самостійно : рядок документації для цього покращився з моменту випуску - див. Коментар до документів у головній гілці замість тієї, яку ви знайшли. Це, зокрема, уточнює, що це результат від tf.nn.moments.

Ви можете побачити дуже простий приклад його використання в тестовому коді batch_norm . Для більш реального прикладу використання я включив нижче допоміжний клас та примітки щодо використання, які я написав для власного використання (гарантія не надається!):

"""A helper class for managing batch normalization state.                   

This class is designed to simplify adding batch normalization               
(http://arxiv.org/pdf/1502.03167v3.pdf) to your model by                    
managing the state variables associated with it.                            

Important use note:  The function get_assigner() returns                    
an op that must be executed to save the updated state.                      
A suggested way to do this is to make execution of the                      
model optimizer force it, e.g., by:                                         

  update_assignments = tf.group(bn1.get_assigner(),                         
                                bn2.get_assigner())                         
  with tf.control_dependencies([optimizer]):                                
    optimizer = tf.group(update_assignments)                                

"""

import tensorflow as tf


class ConvolutionalBatchNormalizer(object):
  """Helper class that groups the normalization logic and variables.        

  Use:                                                                      
      ewma = tf.train.ExponentialMovingAverage(decay=0.99)                  
      bn = ConvolutionalBatchNormalizer(depth, 0.001, ewma, True)           
      update_assignments = bn.get_assigner()                                
      x = bn.normalize(y, train=training?)                                  
      (the output x will be batch-normalized).                              
  """

  def __init__(self, depth, epsilon, ewma_trainer, scale_after_norm):
    self.mean = tf.Variable(tf.constant(0.0, shape=[depth]),
                            trainable=False)
    self.variance = tf.Variable(tf.constant(1.0, shape=[depth]),
                                trainable=False)
    self.beta = tf.Variable(tf.constant(0.0, shape=[depth]))
    self.gamma = tf.Variable(tf.constant(1.0, shape=[depth]))
    self.ewma_trainer = ewma_trainer
    self.epsilon = epsilon
    self.scale_after_norm = scale_after_norm

  def get_assigner(self):
    """Returns an EWMA apply op that must be invoked after optimization."""
    return self.ewma_trainer.apply([self.mean, self.variance])

  def normalize(self, x, train=True):
    """Returns a batch-normalized version of x."""
    if train:
      mean, variance = tf.nn.moments(x, [0, 1, 2])
      assign_mean = self.mean.assign(mean)
      assign_variance = self.variance.assign(variance)
      with tf.control_dependencies([assign_mean, assign_variance]):
        return tf.nn.batch_norm_with_global_normalization(
            x, mean, variance, self.beta, self.gamma,
            self.epsilon, self.scale_after_norm)
    else:
      mean = self.ewma_trainer.average(self.mean)
      variance = self.ewma_trainer.average(self.variance)
      local_beta = tf.identity(self.beta)
      local_gamma = tf.identity(self.gamma)
      return tf.nn.batch_norm_with_global_normalization(
          x, mean, variance, local_beta, local_gamma,
          self.epsilon, self.scale_after_norm)

Зверніть увагу, що я назвав це a, ConvolutionalBatchNormalizerоскільки воно закріплює використання tf.nn.momentsсуми по осях 0, 1 і 2, тоді як для неконволюційного використання вам може знадобитися лише вісь 0.

Зворотній зв'язок вдячний, якщо ви використовуєте його.


Мені важко застосувати це до підграфа convnet, який я повторно використовую в своїй мережі LSTM. За замовчуванням створюється інший нормалізатор для кожного кроку часу, до якого застосовується підграф. Будь-які ідеї щодо його нормалізації за всіма програмами підграфа?
Йорен Ван Северен

1
Ви намагалися створити bn поза підграфом і передати його конструктору підграфа? bn = Conv...er(args); ... createSubgraph(bn, args);а потім просто викликати bn.normalizeвсередині підграфа.
dga

1
Я не розумію, чому в цьому прикладі ви обчислюєте ковзне середнє під час фази тесту?
jrabary

Навпаки - під час навчання ( if train:), він обчислює середнє значення та stddev вхідної партії ( tf.nn.moments(x, [0, 1, 2])). Під час оцінки / тестування він витягує збережену ковзну середню ( self.ewma_trainer.average(self.mean)). Заплутаним може бути те, що виклик averageметоду ewma повертає збережене середнє значення , він не оновлює його. Оновлення виконується за допомогою self.mean.assign(mean)рядка, який зберігає поточне середнє значення пакету в "self.mean", а потім ewma_trainer.apply, який оновлює EWMA на основіself.mean
dga

1
@dga: так, я це зробив, і він запустився (раніше спричинив помилку), але я бачив дивну поведінку. Я будую графік удвічі більше, ніж у github.com/tensorflow/tensorflow/blob/master/tensorflow/models/ ... і використовую другий для тестування на більших поїздах та діючих партіях. З нормалізацією партії я отримую збільшення / випадкові втрати та відповідно. для другого графіка, тоді як перший, що використовується для навчальної операції, показує приємно зменшуються втрати.
Йорен Ван Северен

54

Станом на TensorFlow 1.0 (лютий 2017 р.) Є також високий рівень tf.layers.batch_normalization API, включений до самого TensorFlow.

Це дуже просто у використанні:

# Set this to True for training and False for testing
training = tf.placeholder(tf.bool)

x = tf.layers.dense(input_x, units=100)
x = tf.layers.batch_normalization(x, training=training)
x = tf.nn.relu(x)

... за винятком того, що він додає додаткові операції до графіка (для оновлення його середніх та дисперсійних змінних) таким чином, що вони не будуть залежністю вашого навчального завдання. Ви можете просто запустити ops окремо:

extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
sess.run([train_op, extra_update_ops], ...)

або додайте операційне оновлення як залежності навчальної програми вручну, а потім просто запустіть свою навчальну програму як зазвичай:

extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(extra_update_ops):
    train_op = optimizer.minimize(loss)
...
sess.run([train_op], ...)

1
@MiniQuark, будь ласка, детальніше про залежності? Я не зовсім розумію цю частину.
mamafoku

5
@mamafoku Алгоритм пакетної норми повинен обчислити середнє та стандартне відхилення всього вашого навчального набору. Вони обчислюються під час навчання, але вони не використовуються під час навчання, лише під час висновку. Це обчислення виконується з використанням експоненціальних середніх значень. Це не залежить від решти тренувань, тому ви повинні запускати експоненціальний середній крок обчислення (тобто extra_update_ops) "вручну" на кожній ітерації тренування, разом із регулярним тренуванням, або ви можете зробити тренування тренуванням залежним extra_update_ops(використовуючи control_dependencies()блок ). Сподіваюся, це допомагає.
MiniQuark

Отож, враховуючи, що це update_upsслугує для оновлення ковзаючого середнього та дисперсії ковзання, не було б сенсу включати його взагалі, якщо ми просто тестуємо попередньо навчену мережу, чи правильно?
Андрес Феліпе

Яке значення axisслід використовувати в конволюційних мережах?
Jonas Adler

1
@ gantzer89 Правильно. Якщо ви завантажуєте попередньо навчену мережу, контрольний пункт включатиме значення середнього та дисперсії, розрахованих під час навчання. Середнє значення та відхилення не слід оновлювати під час тестування.
Метью Рахц,

32

Наведене нижче для мене чудово працює, для цього не потрібно викликати EMA-заявку зовні.

import numpy as np
import tensorflow as tf
from tensorflow.python import control_flow_ops

def batch_norm(x, n_out, phase_train, scope='bn'):
    """
    Batch normalization on convolutional maps.
    Args:
        x:           Tensor, 4D BHWD input maps
        n_out:       integer, depth of input maps
        phase_train: boolean tf.Varialbe, true indicates training phase
        scope:       string, variable scope
    Return:
        normed:      batch-normalized maps
    """
    with tf.variable_scope(scope):
        beta = tf.Variable(tf.constant(0.0, shape=[n_out]),
                                     name='beta', trainable=True)
        gamma = tf.Variable(tf.constant(1.0, shape=[n_out]),
                                      name='gamma', trainable=True)
        batch_mean, batch_var = tf.nn.moments(x, [0,1,2], name='moments')
        ema = tf.train.ExponentialMovingAverage(decay=0.5)

        def mean_var_with_update():
            ema_apply_op = ema.apply([batch_mean, batch_var])
            with tf.control_dependencies([ema_apply_op]):
                return tf.identity(batch_mean), tf.identity(batch_var)

        mean, var = tf.cond(phase_train,
                            mean_var_with_update,
                            lambda: (ema.average(batch_mean), ema.average(batch_var)))
        normed = tf.nn.batch_normalization(x, mean, var, beta, gamma, 1e-3)
    return normed

Приклад:

import math

n_in, n_out = 3, 16
ksize = 3
stride = 1
phase_train = tf.placeholder(tf.bool, name='phase_train')
input_image = tf.placeholder(tf.float32, name='input_image')
kernel = tf.Variable(tf.truncated_normal([ksize, ksize, n_in, n_out],
                                   stddev=math.sqrt(2.0/(ksize*ksize*n_out))),
                                   name='kernel')
conv = tf.nn.conv2d(input_image, kernel, [1,stride,stride,1], padding='SAME')
conv_bn = batch_norm(conv, n_out, phase_train)
relu = tf.nn.relu(conv_bn)

with tf.Session() as session:
    session.run(tf.initialize_all_variables())
    for i in range(20):
        test_image = np.random.rand(4,32,32,3)
        sess_outputs = session.run([relu],
          {input_image.name: test_image, phase_train.name: True})

Дякую за ще одну відповідь :). Що ти control_flow_ops.cond? Це так tf.control_flow_ops.cond? Я не знайшов його в тенсорфлу. Ви враховували різницю в продуктивності? Оскільки, якщо контрольна залежність застосовується в шарі, то, можливо, обчислення має чекати кожного шару, а не чекати кожної ітерації, і чи може це занадто багато чекати? Я фактично використовую вашу версію, пошарову, оскільки вона простіша, але спробую глобальну пізніше.
Шон Лі,

Я оновив відповідь. Це tensorflow.python.control_flow_ops, що ще не задокументовано. Думаю, застосування EMA не буде коштувати багато часу, оскільки це елементна операція над вектором, довжина якого, як правило, становить кілька сотень. Але я ще не перевірив це.
bgshi

Я підтвердив те, що сказав @jrocks у своїй відповіді, ваш код є якоюсь помилкою. Будь ласка, зверніть увагу.
myme5261314

@ myme5261314 @jrock Ви маєте рацію, схоже, ema_apply_opїї також викликають під час тестування. Я відредагував свою відповідь, перейшовши phase_trainз tf.Variableбулевого значення на python. Однак тепер вам доведеться створити окремі графіки для навчання та тестування. Дякую за ваш відгук та вибачте за пізню відповідь.
bgshi

3
ваш код справді необхідний, враховуючи наявність офіційного рівня BN? код: github.com/tensorflow/tensorflow/blob/…
Буратіно

14

Існує також "офіційний" рівень нормалізації партії, кодований розробниками. Вони не мають дуже хороших документів про те, як ними користуватися, але ось як ними користуватися (за моїми словами):

from tensorflow.contrib.layers.python.layers import batch_norm as batch_norm

def batch_norm_layer(x,train_phase,scope_bn):
    bn_train = batch_norm(x, decay=0.999, center=True, scale=True,
    updates_collections=None,
    is_training=True,
    reuse=None, # is this right?
    trainable=True,
    scope=scope_bn)
    bn_inference = batch_norm(x, decay=0.999, center=True, scale=True,
    updates_collections=None,
    is_training=False,
    reuse=True, # is this right?
    trainable=True,
    scope=scope_bn)
    z = tf.cond(train_phase, lambda: bn_train, lambda: bn_inference)
    return z

щоб насправді використовувати його, вам потрібно створити заповнювач, train_phaseякий вказує, чи навчаєтесь ви на етапі висновку (як у train_phase = tf.placeholder(tf.bool, name='phase_train')). Його значення можна заповнити під час висновку або тренувань таким чином, tf.sessionяк:

test_error = sess.run(fetches=cross_entropy, feed_dict={x: batch_xtest, y_:batch_ytest, train_phase: False})

або під час навчання:

sess.run(fetches=train_step, feed_dict={x: batch_xs, y_:batch_ys, train_phase: True})

Я майже впевнений, що це правильно, згідно з обговоренням у github .


Здається, є ще одне корисне посилання:

http://r2rt.com/implementing-batch-normalization-in-tensorflow.html


зверніть увагу, що updates_collections=Noneце важливо. Я не розумію, чому, але це так. Найкраще пояснення, яке я знаю, But what it is important is that either you pass updates_collections=None so the moving_mean and moving_variance are updated in-place, otherwise you will need gather the update_ops and make sure they are run.але я не зовсім розумію, чому це пояснення, але емпірично я спостерігав, що MNIST працює добре, коли його немає, і жахливий, коли ні.
Буратіно

11

Ви можете просто використовувати вбудований рівень batch_norm:

batch_norm = tf.cond(is_train, 
    lambda: tf.contrib.layers.batch_norm(prev, activation_fn=tf.nn.relu, is_training=True, reuse=None),
    lambda: tf.contrib.layers.batch_norm(prev, activation_fn =tf.nn.relu, is_training=False, reuse=True))

де prev - результат попереднього рівня (може бути як повністю зв’язаним, так і згортковим шаром), а is_train - це логічне заповнювач. Просто використовуйте batch_norm як вхідні дані для наступного шару.


1
У вас є приклад, коли ви не переходите is_trainяк заповнювач? Я не можу цього зробити, передаючи python логічне значення, з яким не працюю, tf.condі визначаючи дві норми пакетів, якщо гілки дають мені "повторне використання = True не може використовуватися без name_or_scope" (навіть коли я вказую для них область змінної) ...
sygi

@sygi, ти можеш використовувати tf.cast(True/False, tf.bool)операцію.
І.

@sygi, Та я знаю, ви можете сказати, наприклад: var1 = True or False, а потім сказати: tf.cast(var1, tf.bool). Це мало б спрацювати
І.

чому ви встановлюєте reuse=Trueі лише на етапі тесту?
JenkinsY

11

Оскільки нещодавно хтось редагував це, я хотів би пояснити, що це вже не проблема.

Ця відповідь не здається правильною, якщо для неї phase_trainвстановлено значення false, вона все одно оновлює значення середнього значення та дисперсію. Це можна перевірити за допомогою наступного фрагмента коду.

x = tf.placeholder(tf.float32, [None, 20, 20, 10], name='input')
phase_train = tf.placeholder(tf.bool, name='phase_train')

# generate random noise to pass into batch norm
x_gen = tf.random_normal([50,20,20,10])
pt_false = tf.Variable(tf.constant(True))

#generate a constant variable to pass into batch norm
y = x_gen.eval()

[bn, bn_vars] = batch_norm(x, 10, phase_train)

tf.initialize_all_variables().run()
train_step = lambda: bn.eval({x:x_gen.eval(), phase_train:True})
test_step = lambda: bn.eval({x:y, phase_train:False})
test_step_c = lambda: bn.eval({x:y, phase_train:True})

# Verify that this is different as expected, two different x's have different norms
print(train_step()[0][0][0])
print(train_step()[0][0][0])

# Verify that this is same as expected, same x's (y) have same norm
print(train_step_c()[0][0][0])
print(train_step_c()[0][0][0])

# THIS IS DIFFERENT but should be they same, should only be reading from the ema.
print(test_step()[0][0][0])
print(test_step()[0][0][0])

Я оновив свою відповідь. У початковій версії виникла помилка, яка викликає ema_apply_opвиклик навіть тоді phase_train=False.
bgshi

2
Дякуємо за оновлення, все ще не можу коментувати вашу тему (ура для реп), але, схоже, це повинно працювати зараз. Завдяки @ myme5261314 також.
jrock

3

За допомогою вбудованого шару batch_norm TensorFlow нижче наведено код для завантаження даних, побудови мережі з одним прихованим шаром ReLU та нормалізацією L2 та введення нормалізації пакетів як для прихованого, так і поза шаром. Це чудово працює і добре тренується. Просто FYI цей приклад в основному побудований на даних та коді з курсу Udacity DeepLearning. PS Так, його частини так чи інакше обговорювались у відповідях раніше, але я вирішив зібрати в одному фрагменті коду все, щоб у вас був приклад цілого мережевого навчального процесу з нормалізацією пакетів та його оцінкою

# These are all the modules we'll be using later. Make sure you can import them
# before proceeding further.
from __future__ import print_function
import numpy as np
import tensorflow as tf
from six.moves import cPickle as pickle

pickle_file = '/home/maxkhk/Documents/Udacity/DeepLearningCourse/SourceCode/tensorflow/examples/udacity/notMNIST.pickle'

with open(pickle_file, 'rb') as f:
  save = pickle.load(f)
  train_dataset = save['train_dataset']
  train_labels = save['train_labels']
  valid_dataset = save['valid_dataset']
  valid_labels = save['valid_labels']
  test_dataset = save['test_dataset']
  test_labels = save['test_labels']
  del save  # hint to help gc free up memory
  print('Training set', train_dataset.shape, train_labels.shape)
  print('Validation set', valid_dataset.shape, valid_labels.shape)
  print('Test set', test_dataset.shape, test_labels.shape)

image_size = 28
num_labels = 10

def reformat(dataset, labels):
  dataset = dataset.reshape((-1, image_size * image_size)).astype(np.float32)
  # Map 2 to [0.0, 1.0, 0.0 ...], 3 to [0.0, 0.0, 1.0 ...]
  labels = (np.arange(num_labels) == labels[:,None]).astype(np.float32)
  return dataset, labels
train_dataset, train_labels = reformat(train_dataset, train_labels)
valid_dataset, valid_labels = reformat(valid_dataset, valid_labels)
test_dataset, test_labels = reformat(test_dataset, test_labels)
print('Training set', train_dataset.shape, train_labels.shape)
print('Validation set', valid_dataset.shape, valid_labels.shape)
print('Test set', test_dataset.shape, test_labels.shape)


def accuracy(predictions, labels):
  return (100.0 * np.sum(np.argmax(predictions, 1) == np.argmax(labels, 1))
          / predictions.shape[0])


#for NeuralNetwork model code is below
#We will use SGD for training to save our time. Code is from Assignment 2
#beta is the new parameter - controls level of regularization.
#Feel free to play with it - the best one I found is 0.001
#notice, we introduce L2 for both biases and weights of all layers

batch_size = 128
beta = 0.001

#building tensorflow graph
graph = tf.Graph()
with graph.as_default():
      # Input data. For the training data, we use a placeholder that will be fed
  # at run time with a training minibatch.
  tf_train_dataset = tf.placeholder(tf.float32,
                                    shape=(batch_size, image_size * image_size))
  tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))
  tf_valid_dataset = tf.constant(valid_dataset)
  tf_test_dataset = tf.constant(test_dataset)

  #introduce batchnorm
  tf_train_dataset_bn = tf.contrib.layers.batch_norm(tf_train_dataset)


  #now let's build our new hidden layer
  #that's how many hidden neurons we want
  num_hidden_neurons = 1024
  #its weights
  hidden_weights = tf.Variable(
    tf.truncated_normal([image_size * image_size, num_hidden_neurons]))
  hidden_biases = tf.Variable(tf.zeros([num_hidden_neurons]))

  #now the layer itself. It multiplies data by weights, adds biases
  #and takes ReLU over result
  hidden_layer = tf.nn.relu(tf.matmul(tf_train_dataset_bn, hidden_weights) + hidden_biases)

  #adding the batch normalization layerhi()
  hidden_layer_bn = tf.contrib.layers.batch_norm(hidden_layer)

  #time to go for output linear layer
  #out weights connect hidden neurons to output labels
  #biases are added to output labels  
  out_weights = tf.Variable(
    tf.truncated_normal([num_hidden_neurons, num_labels]))  

  out_biases = tf.Variable(tf.zeros([num_labels]))  

  #compute output  
  out_layer = tf.matmul(hidden_layer_bn,out_weights) + out_biases
  #our real output is a softmax of prior result
  #and we also compute its cross-entropy to get our loss
  #Notice - we introduce our L2 here
  loss = (tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
    out_layer, tf_train_labels) +
    beta*tf.nn.l2_loss(hidden_weights) +
    beta*tf.nn.l2_loss(hidden_biases) +
    beta*tf.nn.l2_loss(out_weights) +
    beta*tf.nn.l2_loss(out_biases)))

  #now we just minimize this loss to actually train the network
  optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)

  #nice, now let's calculate the predictions on each dataset for evaluating the
  #performance so far
  # Predictions for the training, validation, and test data.
  train_prediction = tf.nn.softmax(out_layer)
  valid_relu = tf.nn.relu(  tf.matmul(tf_valid_dataset, hidden_weights) + hidden_biases)
  valid_prediction = tf.nn.softmax( tf.matmul(valid_relu, out_weights) + out_biases) 

  test_relu = tf.nn.relu( tf.matmul( tf_test_dataset, hidden_weights) + hidden_biases)
  test_prediction = tf.nn.softmax(tf.matmul(test_relu, out_weights) + out_biases)



#now is the actual training on the ANN we built
#we will run it for some number of steps and evaluate the progress after 
#every 500 steps

#number of steps we will train our ANN
num_steps = 3001

#actual training
with tf.Session(graph=graph) as session:
  tf.initialize_all_variables().run()
  print("Initialized")
  for step in range(num_steps):
    # Pick an offset within the training data, which has been randomized.
    # Note: we could use better randomization across epochs.
    offset = (step * batch_size) % (train_labels.shape[0] - batch_size)
    # Generate a minibatch.
    batch_data = train_dataset[offset:(offset + batch_size), :]
    batch_labels = train_labels[offset:(offset + batch_size), :]
    # Prepare a dictionary telling the session where to feed the minibatch.
    # The key of the dictionary is the placeholder node of the graph to be fed,
    # and the value is the numpy array to feed to it.
    feed_dict = {tf_train_dataset : batch_data, tf_train_labels : batch_labels}
    _, l, predictions = session.run(
      [optimizer, loss, train_prediction], feed_dict=feed_dict)
    if (step % 500 == 0):
      print("Minibatch loss at step %d: %f" % (step, l))
      print("Minibatch accuracy: %.1f%%" % accuracy(predictions, batch_labels))
      print("Validation accuracy: %.1f%%" % accuracy(
        valid_prediction.eval(), valid_labels))
      print("Test accuracy: %.1f%%" % accuracy(test_prediction.eval(), test_labels))

як отримати набір даних, щоб спробувати запустити приклад ur? тобто `` /home/maxkhk/Documents/Udacity/DeepLearningCourse/SourceCode/tensorflow/examples/udacity/notMNIST.pickle ''
Піноккіо

@Pinocchio - це курс Udacity для глибокого навчання, і це робиться в першому завданні там, ви можете перевірити мій код для цього тут: github.com/MaxKHK/Udacity_DeepLearningAssignments/blob/master/…
Максим Хайтович

Здається, ви не оновлюєте ковзаючі середні рівня batch_norm під час тренування
Темак

0

Отже, простий приклад використання цього класу batchnorm:

from bn_class import *

with tf.name_scope('Batch_norm_conv1') as scope:
    ewma = tf.train.ExponentialMovingAverage(decay=0.99)                  
    bn_conv1 = ConvolutionalBatchNormalizer(num_filt_1, 0.001, ewma, True)           
    update_assignments = bn_conv1.get_assigner() 
    a_conv1 = bn_conv1.normalize(a_conv1, train=bn_train) 
    h_conv1 = tf.nn.relu(a_conv1)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.