Написание этой экзотической архитектуры NN с керасами, тензорным потоком и питоном - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь заставить Кераса обучить мультиклассовую модель классификации, которая может быть записана в сети следующим образом:

enter image description here Единственный набор обучаемых параметров - это те, которые enter image description here, все остальные даны. Функции fi представляют собой комбинации обычных математических функций (например, enter image description here. Сигма обозначает суммирование предыдущих членов, а softmax - обычная функция. (X1, x2, ... xn) - элементы поезд или набор тестов и elements являются специфическим подмножеством уже выбранных исходных данных.

Модель в более глубоком:

В частности, при заданном (x_1, x_2, ..., x_n) входе в поезд или набор тестов сеть оценивает

enter image description here

, где fi - математические функции, elements - строки определенного подмножества исходных данных, а коэффициенты enter image description here - параметры, которые я хочу обучить. Поскольку я использую keras, я ожидаю, что он добавит термин смещения в каждую строку.

После вышеупомянутой оценки я применю слой softmax (каждая из m строк выше - это числа, которые будут входными для функции softmax).

В конце я хочу скомпилировать модель и запустить model.fit как обычно.

Проблема в том, что я не могу перевести выражение в keras sintax.

Моя попытка:

Следуя приведенной выше проблеме с сетью, я сначала попытался рассмотреть каждое из выражений вида row как лямбда-слои в последовательной модели, но лучшее, что я смог получить, - это сочетание плотного слоя с линейной активацией. (который будет играть роль параметров строки: parameters), за которым следует лямбда-слой, выводящий вектор vector без требуемого суммирования, следующим образом:

model = Sequential()
#single row considered:
model.add(Lambda(lambda x:  f_fixedRow(x), input_shape=(nFeatures,))) 
#parameters set after lambda layer to get (a1*f(x1,y1),...,an*f(xn,yn)) and not (f(a1*x1,y1),...,f(an*xn,yn))
model.add(Dense(nFeatures, activation='linear')) 

#missing summation: sum(x)
#missing evaluation of f in all other rows

model.add(Dense(classes,activation='softmax',trainable=False)) #should get all rows
model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

Кроме того, мне пришлось определить функцию в вызове лямбда-функции с фиксированным аргументом elements (поскольку лямбда-функция могла иметь только входные слои в качестве переменной):

def f_fixedRow(x):
   #picking a particular row (as a vector) to evaluate f in (f works element-wise)
   y=tf.constant(value=x[0,:],dtype=tf.float32) 
   return f(x,y)

Мне удалось написать функцию f с тензорным потоком (работающим поэлементно подряд), хотя это - возможный источник проблем в моем коде (и описанный выше обходной путь кажется неестественным).

Я также подумал, что, если бы я мог правильно записать поэлементную сумму вектора в вышеупомянутой попытке, я мог бы повторить ту же процедуру параллельно с функциональным API-интерфейсом keras, а затем вставить выходные данные каждой параллельной модели в Функция softmax, как мне нужно.

Другой подход, который я рассмотрел, состоял в том, чтобы обучить параметры, сохраняя их естественную матричную структуру, видимую в Описание сети , возможно, записывая матричный лямбда-слой, но я не смог найти ничего связанного с этой идеей.

В любом случае, я не уверен, что это хороший способ работы с этой моделью в keras, возможно, я упускаю важный момент из-за нестандартного способа записи параметров или отсутствия опыта с тензорным потоком. Любые предложения приветствуются.

1 Ответ

0 голосов
/ 16 января 2019

Для этого ответа важно, чтобы f была тензорной функцией, которая работает поэлементно. (Без повторов). Это достаточно просто сделать, просто проверьте keras бэкэнд-функции .

Предположения:

  • Набор x_pk является постоянным, в противном случае это решение должно быть пересмотрено.
  • Функция f является поэлементной (если нет, укажите f для лучшего кода)

Ваша модель будет нуждаться в x_pk в качестве тензорного ввода . И вы должны сделать это в функциональной модели API .

import keras.backend as K
from keras.layers import Input, Lambda, Activation
from keras.models import Model

#x_pk data
x_pk_numpy = select_X_pk_samples(x_train)
x_pk_tensor = K.variable(x_pk_numpy)

#number of rows in x_pk
m = len(x_pk_numpy)

#I suggest a fixed batch size for simplicity
batch = some_batch_size

Сначала давайте поработаем над функцией, которая будет x и x_pk вызывать f.

def calculate_f(inputs): #inputs will be a list with x and x_pk
    x, x_pk = inputs

    #since f will work elementwise, let's replicate x and x_pk so they have equal shapes 
    #please explain f for better optimization

    # x from (batch, n) to (batch, m, n)
    x = K.stack([x]*m, axis=1)

    # x_pk from (m, n) to (batch, m, n)
    x_pk = K.stack([x_pk]*batch, axis=0)
        #a batch size of 1 could make this even simpler    
        #a variable batch size would make this more complicated
        #certain f functions could make this process unnecessary    

    return f(x, x_pk)

Теперь, в отличие от слоя Dense, эта формула использует веса a_pk, умноженные на элементы. Итак, нам нужен пользовательский слой:

class ElementwiseWeights(Layer):
    def __init__(self, **kwargs):
        super(ElementwiseWeights, self).__init__(**kwargs)

    def build(self, input_shape):
        weight_shape = (1,) + input_shape[1:] #shape (1, m, n)

        self.kernel = self.add_weight(name='kernel', 
                                  shape=weight_shape,
                                  initializer='uniform',
                                  trainable=True)

        super(ElementwiseWeights, self).build(input_shape)  

    def compute_output_shape(self,input_shape):
        return input_shape

    def call(self, inputs):
        return self.kernel * inputs

Теперь давайте создадим нашу функциональную модель API:

#x_pk model tensor input
x_pk = Input(tensor=x_pk_tensor) #shape (m, n)

#x usual input with fixed batch size
x = Input(batch_shape=(batch,n))  #shape (batch, n)

#calculate F
out = Lambda(calculate_f)([x, xp_k]) #shape (batch, m, n)

#multiply a_pk
out = ElementwiseWeights()(out) #shape (batch, m, n)

#sum n elements, keep m rows:
out = Lambda(lambda x: K.sum(x, axis=-1))(out) #shape (batch, m)

#softmax
out = Activation('softmax')(out) #shape (batch,m)

Продолжите эту модель с тем, что вы хотите и закончите:

model = Model([x, x_pk], out)
model.compile(.....)
model.fit(x_train, y_train, ....) #perhaps you might need .fit([x_train], ytrain,...)

Редактировать для функции f

Вы можете иметь предложенный f, например:

#create the n coefficients:
coefficients = np.array([c0, c1, .... , cn])
coefficients = coefficients.reshape((1,1,n))

def f(x, x_pk):

    c = K.variable(coefficients) #shape (1, 1, n)
    out = (x - x_pk) / c
    return K.exp(out)
  • Этот f будет принимать x с формой (batch, 1, n), без stack, используемого в функции calculate_f.
  • Или может принять x_pk с формой (1, m, n), допускающей переменный размер партии.

Но я не уверен, что возможно иметь обе эти фигуры вместе. Тестирование это может быть интересно.

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