Основаны ли keras на замыканиях в python? - PullRequest
3 голосов
/ 20 июня 2019

При работе с keras и tenorflow я обнаружил, что следующие строки кода сбивают с толку.

w_init = tf.random_normal_initializer()
self.w = tf.Variable(initial_value=w_init(shape=(input_dim, units),
                                          dtype='float32'),trainable=True)

Кроме того, я видел что-то вроде:

 Dense(64, activation='relu')(x)

Следовательно, если Dense(...) создаст объект для меня, то как я могу следовать этому с помощью (x)?

Аналогично для w_init выше. Как я могу сказать такую ​​вещь:

tf.random_normal_initializer()(shape=(input_dim, units), dtype='float32'),trainable=True)

Есть ли у нас такая вещь в python "ClassName()" followed by "()" при создании объекта, такого как слой?

Пока я изучал Замыкания в python, я обнаружил, что функция может возвращать другую функцию. Следовательно, это то, что действительно происходит в Керасе?

Любая помощь очень ценится!

Ответы [ 2 ]

2 голосов
/ 20 июня 2019

Это два совершенно разных способа определения моделей.

Keras

Керас работает с концепцией слоев. Каждая строка определяет полный слой вашей сети. В частности, вы ссылаетесь на функциональный API-интерфейс keras. Идея состоит в том, чтобы объединить слои следующим образом:


inp = Input(shape=(28, 28, 1))
x = Conv2D((6,6), strides=(1,1), activation='relu')(inp)
# ... etc ...
x = Flatten()(x)
x = Dense(10, activation='softmax')(x)


model = Model(inputs=[inp], outputs=[x])

Таким образом, вы создали полный CNN всего за несколько строк. Обратите внимание, что вам никогда не приходилось вручную вводить форму весовых векторов или выполняемые операции. Они автоматически выводятся керасом.

Теперь, это просто должно быть скомпилировано - model.compile(...), а затем вы можете обучить его через model.fit(...).

Tensorflow

С другой стороны, TensorFlow немного более низкого уровня. Это означает, что вы должны определить переменные и операции вручную. Поэтому, чтобы написать полностью связанный слой, вам нужно сделать следующее:

# Input placeholders
x = tf.placeholder(tf.float32, shape=(None, 28, 28, 1))
y = tf.placeholder(tf.float32, shape=(None, 10))

# Convolution layer
W1 = tf.Variable(tf.truncated_normal([6, 6, 1, 32], stddev=0.1)) 
b1 = tf.Variable(tf.constant(0.1, tf.float32, [32]))
z1 = tf.nn.conv2d(x_2d, W1, strides=[1, 1, 1, 1], padding='SAME') + b1 
c1 = tf.nn.relu(z1)

# ... etc ...

# Flatten
flat = tf.reshape(p2, [-1, ...]) # need to calculate the ... by ourselves

# Dense
W3 = tf.Variable(tf.truncated_normal([..., 10], stddev=0.1))  # same size as before
b3 = tf.Variable(tf.constant(0.1, tf.float32, [10]))
fc1 = tf.nn.relu(tf.matmul(flat, W3) + b3)

Здесь нужно отметить две вещи. Здесь нет явного определения model, и его нужно обучать через tf.Session с feed_dict, подающим данные для заполнителей. Если вам интересно, вы найдете несколько руководств в Интернете.

Закрытие заметки ...

TensorFlow имеет гораздо более удобный и простой способ определять и обучать модели через стремительное выполнение , которое будет по умолчанию в TF 2.0! Таким образом, код, который вы разместили, в некотором смысле является старым способом работы в тензорном потоке. Стоит взглянуть на TF 2.0, который на самом деле рекомендует все делать так, как надо!


Редактировать (после комментария OP):

Нет, слой не является , а не замыканием . Слой keras - это класс, который реализует метод __call__, который также делает его вызываемым. То, как они это сделали, заключалось в том, что это оболочка для метода call, который обычно пишут пользователи.

Вы можете взглянуть на реализацию здесь

В основном, как это работает:

class MyClass:
    def __init__(self, param):
        self.p = param
    def call(self, x):
        print(x)

Если вы попытаетесь написать c = MyClass(1)(3), вы получите ошибку TypeError, сообщающую, что MyClass не вызывается. Но если вы напишите это так:

class MyClass:
    def __init__(self, param):
        self.p = param
    def __call__(self, x):
        print(x)

Теперь работает. По сути, keras делает это так:

class MyClass:
    def __init__(self, param):
        self.p = param
    def call(self, x):
        print(x)
    def __call__(self, x):
        self.call(x)

Так что, когда вы пишете свой собственный слой, вы можете реализовать свой собственный метод call и метод __call__, который обернет ваш, будет унаследован от базового класса Layer keras.

0 голосов
/ 20 июня 2019

Просто из синтаксиса, я бы сказал, что Dense() возвращает функцию (или, точнее, вызываемый ). Точно так же w_init также вызывается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...