Какая разница между сверткой в ​​Керас против Кафе? - PullRequest
0 голосов
/ 16 февраля 2019

Я пытаюсь скопировать большую сеть Caffe в Keras (на основе бэкэнда тензорного потока).Но у меня большие проблемы с этим, даже на одном сверточном слое.

Простая свертка в целом :

Скажем, у нас был четырехмерный вход с формой (1, 500, 500, 3), и нам пришлось выполнить одну свертку на этом входе с фильтрами 96 с размером ядра 11 и 4x4 шагов.

Давайте установим наши весовые и входные переменные:

w = np.random.rand(11, 11, 3, 96)  # weights 1
b = np.random.rand(96)  # weights 2 (bias)

x = np.random.rand(500, 500, 3)

Простая свертка в Керасе :

Вот как это можно определить в Керасе:

from keras.layers import Input
from keras.layers import Conv2D
import numpy as np

inp = Input(shape=(500, 500, 3))
conv1 = Conv2D(filters=96, kernel_size=11, strides=(4, 4), activation=keras.activations.relu, padding='valid')(inp)                                                            


model = keras.Model(inputs=[inp], outputs=conv1)
model.layers[1].set_weights([w, b])  # set weights for convolutional layer


predicted = model.predict([x.reshape(1, 500, 500, 3)])
print(predicted.reshape(1, 96, 123, 123))  # reshape keras output in the form of Caffe

Простая свертка в Caffe :

simple.prototxt:

name: "simple"
input: "inp"
input_shape {
  dim: 1
  dim: 3
  dim: 500
  dim: 500
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "inp"
  top: "conv1"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 96
    kernel_size: 11
    pad: 0
    stride: 4
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "conv1"
  top: "conv1"
}

Caffe в Python:

import caffe

net = caffe.Net('simple.prototxt', caffe.TEST)
net.params['conv1'][0].data[...] = w.reshape(96, 3, 11, 11)  # set weights 1
net.params['conv1'][1].data[...] = b  # set weights 2 (bias)
net.blobs['inp'].reshape(1, 3, 500, 500) # reshape input layer to fit our input array x
print(net.forward(inp=x.reshape(1, 3, 500, 500)).get('conv1'))

Задача :

Если мы выполним оба фрагмента кода, мы заметим, что выходные данные отличаются друг от друга.Я понимаю, что есть несколько различий, таких как симметричное заполнение Caffe, но я даже не использовал здесь заполнение.И все же выход Caffe отличается от выхода Keras ...

Почему это так?Я знаю, что бэкэнд Theano не использует корреляцию, как это делает Caffe, и, следовательно, требует вращения ядра на 180 градусов, но то же самое для тензорного потока?из того, что я знаю, и Tensorflow, и Caffe используют взаимную корреляцию вместо Convolution.

Как я могу сделать две идентичные модели в Keras и Caffe, которые используют свертку?

Любая помощь будет принята,спасибо!

1 Ответ

0 голосов
/ 17 февраля 2019

Я нашел проблему, но пока не знаю, как ее исправить ...

Разница между этими двумя сверточными слоями заключается в выравнивании их элементов.Эта проблема alignment возникает только тогда, когда количество фильтров равно N, так что N > 1 && N > S, где S - это размер фильтра.Другими словами, такая проблема возникает только тогда, когда мы получаем многомерный массив из свертки, который имеет и число строк и количество столбцов больше, чем 1 .

Доказательства:

Чтобы увидеть это, я упростила свои входные и выходные данные, чтобы мы могли лучше проанализировать механику обоих слоев.

simple.prototxt:

input: "input"
input_shape {
  dim: 1
  dim: 1
  dim: 2
  dim: 2
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "input"
  top: "conv1"
  convolution_param {
    num_output: 2
    kernel_size: 1
    pad: 0
    stride: 1
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "conv1"
  top: "conv1"
}

simple.py:

import keras
import caffe
import numpy as np
from keras.layers import Input, Conv2D
from keras.activations import relu
from keras import Model

filters = 2  # greater than 1 and ker_size
ker_size = 1 

_input = np.arange(2 * 2).reshape(2, 2)
_weights = [np.reshape([[2 for _ in range(filters)] for _ in range(ker_size*ker_size)], (ker_size, ker_size, 1, filters)), np.reshape([0 for _ in range(filters)], (filters,))]  # weights for Keras, main weight is array of 2`s while bias weight is array of 0's
_weights_caffe = [_weights[0].T, _weights[1].T]  # just transpose them for Caffe

# Keras Setup

keras_input = Input(shape=(2, 2, 1), dtype='float32')
keras_conv = Conv2D(filters=filters, kernel_size=ker_size, strides=(1, 1), activation=relu, padding='valid')(keras_input)
model = Model(inputs=[keras_input], outputs=keras_conv)
model.layers[1].set_weights([_weights[0], _weights[1]])

# Caffe Setup

net = caffe.Net("simpler.prototxt", caffe.TEST)
net.params['conv1'][0].data[...] = _weights_caffe[0]
net.params['conv1'][1].data[...] = _weights_caffe[1]
net.blobs['input'].data[...] = _input.reshape(1, 1, 2, 2)


# Predictions


print("Input:\n---")
print(_input)
print(_input.shape)
print("\n")

print("Caffe:\n---")
print(net.forward()['conv1'])
print(net.forward()['conv1'].shape)
print("\n")

print("Keras:\n---")
print(model.predict([_input.reshape(1, 2, 2, 1)]))
print(model.predict([_input.reshape(1, 2, 2, 1)]).shape)
print("\n")

Выход :

Input:
---
[[0 1]
 [2 3]]
(2, 2)


Caffe:
---
[[[[0. 2.]
   [4. 6.]]

  [[0. 2.]
   [4. 6.]]]]
(1, 2, 2, 2)


Keras:
---
[[[[0. 0.]
   [2. 2.]]

  [[4. 4.]
   [6. 6.]]]]
(1, 2, 2, 2)

Анализ :

Если вы посмотрите на вывод модели Caffe, вы заметите, что наш массив 2x2 сначала удваивается (так что у нас есть массив из 2 2x2 массивов), а затем выполняется умножение матриц для каждого из этих двух массивов снаша весовая матрица.Примерно так:

Оригинал :

[[[[0. 2.]
   [4. 6.]]

  [[0. 2.]
   [4. 6.]]]]

Преобразованный :

[[[[(0 * 2) (2 * 2)]
   [(4 * 2) (6 * 2)]]

  [[(0 * 2) (2 * 2)]
   [(4 * 2) (6 * 2)]]]]

Tensorflow делает что-то другое, этоКажется, сначала выравнивает 2D-векторы вывода в порядке возрастания после того же, что и Caffe.Это кажется странным поведением, и я не могу понять, почему они так поступили.

Решение:

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

Единственное, что я знаю, это создание пользовательского слоя, который не является очень аккуратным решением дляя.

...