Tensorflow собрать и неинформативное обновление веса (tenorflow- Python) - PullRequest
0 голосов
/ 14 апреля 2020

Я строю простую модель в TensorFlow (v 2.1) и сталкиваюсь со странным поведением с tf.gather - Возможно, я не понимаю, что он делает.

Я рассматривая модель, которая может иметь несколько перехватов (т.е. y = a[i] + X@b). Я определяю новый слой, как показано ниже,

class GroupedInterceptLinearCoeffs_gather(tf.keras.layers.Layer):
    """
    """
    def __init__(self, ngroup=1, **kwargs):
        super(GroupedInterceptLinearCoeffs_gather, self).__init__()
        self.ngroup = ngroup

    def build(self, input_shape):
        self.a = self.add_weight(
            shape=(self.ngroup,), dtype="float32",
            initializer="random_normal", trainable=True
        )
        self.b = self.add_weight(
            shape=(input_shape[1][-1],), dtype="float32",
            initializer="random_normal", trainable=True
        )

    @tf.function()
    def call(self, inputs):
        out = tf.gather(self.a, inputs[0], axis=0, batch_dims=0) + tf.linalg.matvec(inputs[1], self.b)
        return out

, а затем проверяю, что он выполняет то, что я ожидаю с

import numpy as np
import tensorflow as tf

nobs = 100

alpha = 1.0  # To keep things simple, we'll only have one intercept here
beta = np.array([0.0, 0.5, 0.25])
L = np.array([[1.0, 0.0, 0.0], [0.25, 1.1, 0.0], [0.2, 0.2, 1.25]])
X = np.random.randn(nobs, 3) @ L

y = alpha + X@beta

. Проверка того, может ли модель воспроизводить мои данные, показывает, что существует (эффективно) 0 ошибка

gilc_g = GroupedInterceptLinearCoeffs_gather(ngroup=1)

gilc_g([np.zeros((X.shape[0],), dtype=np.int32), X.astype(np.float32)])

gilc_g.set_weights([np.array([alpha], dtype=np.float32), beta.astype(np.float32)])

np.max(
    np.abs(
        gilc_g(
            [np.zeros((X.shape[0],), dtype=np.int32), X.astype(np.float32)]
        ).numpy() - (alpha + X@beta)
    )
)

, но когда я пытаюсь подогнать модель к ней, она быстро перестает делать успехи.

class OLS_gather(tf.keras.Model):
    """
    """
    def __init__(self, ngroups=1, name="ols", **kwargs):
        super(OLS_gather, self).__init__(name=name, **kwargs)
        self.lm = GroupedInterceptLinearCoeffs_gather(ngroups)

    def call(self, inputs):
        print(inputs[0].shape)
        print(inputs[1].shape)
        out = self.lm(inputs)

        return out


olsmodel_g = OLS_gather(ngroups=1)

olsmodel_g.compile(optimizer, loss=tf.keras.losses.MeanSquaredError())
olsmodel_g.fit([np.zeros((X.shape[0],), dtype=np.int32), X.astype(np.float32)], y.astype(np.float32), epochs=50)

Проверка весов b показывает, что это не перемещая веса в правильном направлении, но похожая модель (без сбора) быстро сходится (см. этот gist для всего кода). Я неправильно использую tf.gather? Если да, есть ли другой способ «переиндексировать» массив, подобный этому, для создания дубликатов в определенном порядке?

(Кроме того, я знаю, что мне не обязательно создавать свои собственные слои / модели, но Мой пример немного сложнее, и мне нужна пользовательская функция потерь et c ...)

Ответы [ 2 ]

1 голос
/ 14 апреля 2020

Мне удалось воспроизвести описанное вами поведение, я предполагаю, что использование tf.gather для динамического использования весов каким-то образом мешает обучению модели.

Вы можете вообще не использовать tf.gather и вместо этого использовать один - горячее кодирование с tf.one_hot и простое умножение для выбора используемого перехвата.

Приведенный ниже код был успешным для модели с двумя перехватами, где входные данные всегда выбирают первый:

import numpy as np
import tensorflow as tf

nobs = 100

alpha = 1.0
beta = np.array([0.0, 0.5, 0.25])
L = np.array([[1.0, 0.0, 0.0], [0.25, 1.1, 0.0], [0.2, 0.2, 1.25]])
X = np.random.randn(nobs, 3) @ L

y = alpha + X@beta

class GroupedInterceptLinearCoeffs_gather(tf.keras.layers.Layer):
    """"""
    def __init__(self, ngroup=2, **kwargs):
        super(GroupedInterceptLinearCoeffs_gather, self).__init__()
        self.ngroup = ngroup

    def build(self, input_shape):
        self.a = self.add_weight(
            shape=(self.ngroup,), dtype="float32",
            initializer="random_normal", trainable=True
        )
        self.b = self.add_weight(
            shape=(input_shape[1][-1],), dtype="float32",
            initializer="random_normal", trainable=True
        )

    @tf.function()
    def call(self, inputs):
        out = tf.linalg.matvec(inputs[0], self.a) \
            + tf.linalg.matvec(inputs[1], self.b)
        return out

class OLS_gather(tf.keras.Model):
    """"""
    def __init__(self, ngroups=1, name="ols", **kwargs):
        super(OLS_gather, self).__init__(name=name, **kwargs)
        self.lm = GroupedInterceptLinearCoeffs_gather(ngroups)

    def call(self, inputs):
        out = self.lm(inputs)

        return out

olsmodel_g = OLS_gather(ngroups=2)
optimizer_g = tf.keras.optimizers.Adam(learning_rate=1e-2)
olsmodel_g.compile(optimizer_g, loss=tf.keras.losses.MeanSquaredError())

x = [tf.cast(tf.one_hot(np.zeros((X.shape[0],), dtype=np.int32), depth=2), tf.float32),
     tf.constant(X)]
y = tf.constant(y)

olsmodel_g.fit(x=x, y=y,
    epochs=25, batch_size=25, verbose=False
)

print(olsmodel_g.trainable_weights)

olsmodel_g.fit(x=x, y=y,
    epochs=1, batch_size=25
)
0 голосов
/ 14 апреля 2020

С помощью @ spencerlyon2 я думаю, что у нас есть лучшее представление о том, что происходит.

Проблема, как представляется, заключается в том, что, когда одномерный массив передается в качестве входных данных в tenorflow, они автоматически преобразуют это для двумерного массива при стандартизации входных данных (см. эти строки кода ). Это создает несоответствия формы, и вместо того, чтобы выдавать ошибку (?), Он кажется, что работает тихо при подгонке и не обновляет параметры правильно.

Один из способов грубой силы исправить это - просто сжать первый ввод в методе call GroupedInterceptLinearCoeffs_gather.

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