Значительная разница между выходами модели глубокого тензорного потока керас в Python и преобразовании тензорного потока js - PullRequest
1 голос
/ 08 апреля 2020

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

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

Следующий иллюстративный пример должен служить иллюстрацией. Это было проверено с использованием тензорного потока 2.1.0, тензорного потока js 1.7.2 и Chrome 80.0.3987.163.

Сначала мы построим простую глубокую модель в tf.keras, инициализируем веса для случайных значений и преобразуем это в tenorflow js.

import tensorflowjs as tfjs

from tensorflow.keras.models import Model, save_model
from tensorflow.keras import layers as L
from tensorflow.keras import backend as K
from tensorflow.keras import initializers as I

K.clear_session()

def build_model(depth, size):
    x = L.Input((size), name = 'input')
    inputs = [x]
    outputs = []

    uniform_init = I.RandomUniform(minval=-1, maxval=1, seed=123)

    for i in range(depth):
        x = L.Dense(size,
                    dtype = 'float32',
                    kernel_initializer = uniform_init,
                    bias_initializer = uniform_init,
                    name = 'dense_' + str(i))(x)

        outputs += [x]

    model = Model(inputs=inputs, outputs=outputs)
    return (model, inputs, outputs)

model, inputs, outputs = build_model(10, 50)
model.compile(optimizer='adam', loss='mean_squared_error')
model.summary()

save_model(model, './keras_model.h5')
tfjs.converters.save_keras_model(model, './keras_converted')

Вывод:

Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input (InputLayer)           [(None, 50)]              0         
_________________________________________________________________
dense_0 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_1 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_2 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_3 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_4 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_5 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_6 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_7 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_8 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_9 (Dense)              (None, 50)                2550      
=================================================================
Total params: 25,500
Trainable params: 25,500
Non-trainable params: 0

Далее мы запускаем модель для единичного вектора в tenorflow js и экспортируем выходные данные также из каждого слоя в качестве весов к json.

async function run() {
    const model = await tf.loadLayersModel('/keras_converted/model.json')
    model.summary()

    let input_size = model.inputLayers[0].batchInputShape[1]
    let ones = [...Array(input_size)].map(x => 1)
    let x = tf.tensor(ones).reshape([1,input_size])
    let yhat = model.predict(x).map(x => x.arraySync())

    let i = 0
    let vals = {}

    model.outputLayers.forEach(output => {
        vals[output.name + '_output'] = yhat[i]
        i += 1
    })

    model.layers.forEach(layer => {
        vals[layer.name + '_weights'] = layer.getWeights().map(w => w.arraySync())
    })

    let a = document.createElement('a')
    let file = new Blob([JSON.stringify(vals)], { type: 'text/plain' })
    a.href = URL.createObjectURL(file)
    a.download = 'keras_model_tfjs_output.json'
    a.click()
}

run()

Мы также запускаем модель в Python для того же входного вектора и сравниваем выходные данные различных слоев, а также весовые коэффициенты, чтобы исключить любые изменение весов как возможная причина.

import re
import json
import numpy as np

from tensorflow.keras.models import load_model

model = load_model('./keras_model.h5')

# load tensorflow js output and weights
with open('./keras_model_tfjs_output.json', 'r') as json_file:
    tfjs_vals = json.load(json_file)

# create similar dictionary from running inference in Python
keras_vals = dict()

# run the model on a single vector of ones
yhat = model.predict(np.ones((1,inputs[0].shape[1])))

for i, layer in enumerate(model.layers[1:]):
    keras_vals[layer.name + '_weights'] = layer.get_weights()
    keras_vals[layer.name + '_output'] = yhat[i]

# Compare values in keras_vals and tfjs_vals
def compare_vals(key, a, b):
    for i in range(len(a)):
        print('{0}[{1}]: different values = {2}, average difference = {3}'.\
              format(key, i,
                     np.sum(a[i] != b[i]),
                     np.sum(np.abs(a[i] - b[i])) / np.size(a[i])
        ))

for key in dict.keys(keras_vals):
    compare_vals(key, keras_vals[key], tfjs_vals[key])

Вывод:

dense_0_weights[0]: different values = 0, average difference = 0.0
dense_0_weights[1]: different values = 0, average difference = 0.0
dense_0_output[0]: different values = 0, average difference = 0.0
dense_1_weights[0]: different values = 0, average difference = 0.0
dense_1_weights[1]: different values = 0, average difference = 0.0
dense_1_output[0]: different values = 40, average difference = 1.7833709716796876e-06
dense_2_weights[0]: different values = 0, average difference = 0.0
dense_2_weights[1]: different values = 0, average difference = 0.0
dense_2_output[0]: different values = 43, average difference = 1.2047290802001953e-05
dense_3_weights[0]: different values = 0, average difference = 0.0
dense_3_weights[1]: different values = 0, average difference = 0.0
dense_3_output[0]: different values = 38, average difference = 5.3539276123046876e-05
dense_4_weights[0]: different values = 0, average difference = 0.0
dense_4_weights[1]: different values = 0, average difference = 0.0
dense_4_output[0]: different values = 43, average difference = 0.00024005889892578125
dense_5_weights[0]: different values = 0, average difference = 0.0
dense_5_weights[1]: different values = 0, average difference = 0.0
dense_5_output[0]: different values = 45, average difference = 0.0010586166381835937
dense_6_weights[0]: different values = 0, average difference = 0.0
dense_6_weights[1]: different values = 0, average difference = 0.0
dense_6_output[0]: different values = 46, average difference = 0.00515625
dense_7_weights[0]: different values = 0, average difference = 0.0
dense_7_weights[1]: different values = 0, average difference = 0.0
dense_7_output[0]: different values = 47, average difference = 0.0254345703125
dense_8_weights[0]: different values = 0, average difference = 0.0
dense_8_weights[1]: different values = 0, average difference = 0.0
dense_8_output[0]: different values = 46, average difference = 0.11546875
dense_9_weights[0]: different values = 0, average difference = 0.0
dense_9_weights[1]: different values = 0, average difference = 0.0
dense_9_output[0]: different values = 44, average difference = 0.42203125

Весы идентичны после обратного хода от кераса к тензорному потоку js и обратно. Тем не менее, выходы каждого слоя к концу значительно разошлись.

Есть ли способ обеспечить одинаковую производительность между Python и JS версиями модели? Я попытался запустить это как с бэкэндами WebGL, так и с процессором, и та же проблема сохраняется

...