简体   繁体   中英

Saving custom estimators in TensorFlow

I am trying to save a custom estimator after training, but always receive an error. I am using TensorFlow v.1.4, and have tried various solutions I could search on the web and in tutorials and examples.

(Credit: I started following the tutorial at here , but have modified the code to suit).

Here is my code:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 16 10:17:59 2017

@author: ali
"""

import tensorflow as tf
import numpy as np
import shutil

# Define variables
SEQ_LEN = 10
DEFAULTS = [[0.0] for x in range(0, SEQ_LEN)]
BATCH_SIZE = 20
TIMESERIES_COL = 'rawdata'
N_OUTPUTS = 2  # in each sequence, 1-8 are features, and 9-10 is label
N_INPUTS = SEQ_LEN - N_OUTPUTS
N_EPOCHS = 100
LSTM_SIZE = 3  # number of hidden layers in each of the LSTM cells
LEARNING_RATE = 0.01

def create_time_series():
  freq = (np.random.random()*0.5) + 0.1  # 0.1 to 0.6
  ampl = np.random.random() + 0.5  # 0.5 to 1.5
  x = np.sin(np.arange(0,SEQ_LEN) * freq) * ampl
  return x

def to_csv(filename, N):
  with open(filename, 'w') as ofp:
    for lineno in range(0, N):
      seq = create_time_series()
      line = ",".join(map(str, seq))
      ofp.write(line + '\n')

# read data and convert to needed format
def read_dataset(filename, mode=tf.contrib.learn.ModeKeys.TRAIN):  

    def _input_fn():
        num_epochs = N_EPOCHS if mode == tf.contrib.learn.ModeKeys.TRAIN else 1

        # could be a path to one file or a file pattern.
        input_file_names = tf.train.match_filenames_once(filename)

        filename_queue = tf.train.string_input_producer(input_file_names, num_epochs=num_epochs)
        reader = tf.TextLineReader()
        _, value = reader.read_up_to(filename_queue, num_records=BATCH_SIZE)

        value_column = tf.expand_dims(value, -1)
        print('readcsv={}'.format(value_column))

        # all_data is a list of tensors
        all_data = tf.decode_csv(value_column, record_defaults=DEFAULTS)  
        inputs = all_data[:len(all_data)-N_OUTPUTS]  # first few values
        label = all_data[len(all_data)-N_OUTPUTS : ] # last few values

        # from list of tensors to tensor with one more dimension
        inputs = tf.concat(inputs, axis=1)
        label = tf.concat(label, axis=1)
        print('inputs={}'.format(inputs))

        return {TIMESERIES_COL: inputs}, label   # dict of features, label

    return _input_fn


# create the inference model
def simple_rnn(features, labels, mode, params):

    # 0. Reformat input shape to become a sequence
    x = tf.split(features[TIMESERIES_COL], N_INPUTS, 1)
    #print 'x={}'.format(x)

    # 1. configure the RNN
    lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(LSTM_SIZE, forget_bias=1.0)
    outputs, _ = tf.nn.static_rnn(lstm_cell, x, dtype=tf.float32)

    # slice to keep only the last cell of the RNN
    outputs = outputs[-1]
    #print 'last outputs={}'.format(outputs)

    # output is result of linear activation of last layer of RNN
    weight = tf.Variable(tf.random_normal([LSTM_SIZE, N_OUTPUTS]))
    bias = tf.Variable(tf.random_normal([N_OUTPUTS]))
    predictions = tf.matmul(outputs, weight) + bias

    # 2. loss function, training/eval ops
    if mode == tf.contrib.learn.ModeKeys.TRAIN or mode == tf.contrib.learn.ModeKeys.EVAL:
        loss = tf.losses.mean_squared_error(labels, predictions)
        optimizer = tf.train.GradientDescentOptimizer(learning_rate=params["l_rate"])
        train_op = optimizer.minimize(loss=loss, global_step=tf.train.get_global_step())
        eval_metric_ops = {"rmse": tf.metrics.root_mean_squared_error(labels, predictions)}
    else:
        loss = None
        train_op = None
        eval_metric_ops = None

    # 3. Create predictions
    predictions_dict = {"predicted": predictions}

    # 4. return ModelFnOps
    return tf.estimator.EstimatorSpec(
        mode=mode,
        predictions=predictions_dict,
        loss=loss,
        train_op=train_op,
        eval_metric_ops=eval_metric_ops)


def get_train():
    return read_dataset('train.csv', mode=tf.contrib.learn.ModeKeys.TRAIN)

def get_valid():
    return read_dataset('valid.csv', mode=tf.contrib.learn.ModeKeys.EVAL)

def my_serving_input_fn():
    ''' serving input function for saving the estimator'''

    feature_spec = {TIMESERIES_COL: tf.FixedLenFeature(dtype=tf.float32, shape=[N_INPUTS])}

    serialized_tf_example = tf.placeholder(dtype=tf.string, shape=[None], name='input_example_tensor')
    receiver_tensors = {TIMESERIES_COL: serialized_tf_example}
    features = tf.parse_example(serialized_tf_example, feature_spec)

    return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)
    #return tf.estimator.export.build_parsing_serving_input_receiver_fn(feature_spec)()


def generate_nn():
    model_params = {"l_rate": LEARNING_RATE}
    nn = tf.estimator.Estimator(model_fn=simple_rnn, params=model_params, model_dir='./output_dir')
    return nn


def save_nn(nn_estimator, output_dir):
    nn_estimator.export_savedmodel(output_dir, my_serving_input_fn)
    print('Successfully saved the estimator...')


def main():

    # remove previous files
    shutil.rmtree('output_dir', ignore_errors=True) 
    shutil.rmtree('test_dir', ignore_errors=True)

    # generate data
    to_csv('train.csv', 5000)
    to_csv('test.csv', 1000)

    # instantiate the nn estimator
    nn = generate_nn()

    # train nn
    nn.train(get_train(), steps=2000)

    # evaluate nn
    ev = nn.evaluate(input_fn=get_valid())
    print(ev)

    # save nn for future use
    save_nn(nn, './test_dir')

if __name__ == '__main__':
    main()

Here is the error I receive:

   File "/.../RNN-estimators-v3.py", line 172, in <module>
main()

  File "/.../RNN-estimators-v3.py", line 167, in main
save_nn(nn, './test_dir')

  File "/.../RNN-estimators-v3.py", line 142, in save_nn
nn_estimator.export_savedmodel(output_dir, my_serving_input_fn)

  File "/.../anaconda/envs/TF-1-4-CPU/lib/python3.6/site-packages/tensorflow/python/estimator/estimator.py", line 534, in export_savedmodel
serving_input_receiver.receiver_tensors_alternatives)

  File "/.../anaconda/envs/TF-1-4-CPU/lib/python3.6/site-packages/tensorflow/python/estimator/export/export.py", line 195, in build_all_signature_defs
'{}'.format(type(export_outputs)))

ValueError: export_outputs must be a dict and not<class 'NoneType'>

Your help is much appreciated.

Make sure to include export_outputs in your model_fn function when the mode is Predict.

def simple_rnn(features, labels, mode, params):

    # 0. Reformat input shape to become a sequence
    x = tf.split(features[TIMESERIES_COL], N_INPUTS, 1)
    #print 'x={}'.format(x)

    # 1. configure the RNN
    lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(LSTM_SIZE, forget_bias=1.0)
    outputs, _ = tf.nn.static_rnn(lstm_cell, x, dtype=tf.float32)

    # slice to keep only the last cell of the RNN
    outputs = outputs[-1]
    #print 'last outputs={}'.format(outputs)

    # output is result of linear activation of last layer of RNN
    weight = tf.Variable(tf.random_normal([LSTM_SIZE, N_OUTPUTS]))
    bias = tf.Variable(tf.random_normal([N_OUTPUTS]))
    predictions = tf.matmul(outputs, weight) + bias

    # 2. loss function, training/eval ops
    if mode == tf.contrib.learn.ModeKeys.TRAIN or mode == tf.contrib.learn.ModeKeys.EVAL:
        loss = tf.losses.mean_squared_error(labels, predictions)
        optimizer = tf.train.GradientDescentOptimizer(learning_rate=params["l_rate"])
        train_op = optimizer.minimize(loss=loss, global_step=tf.train.get_global_step())
        eval_metric_ops = {"rmse": tf.metrics.root_mean_squared_error(labels, predictions)}
        return  tf.estimator.EstimatorSpec(
            mode=mode,
            loss=loss,
            train_op=train_op,
            eval_metric_ops=eval_metric_ops)

    else:
        loss = None
        train_op = None
        eval_metric_ops = None

        # 3. Create predictions

        export_outputs = {'predict_output': tf.estimator.export.PredictOutput({"pred_output_classes": predictions, 'probabilities': #your probabilities})}
        predictions_dict = {"predicted": predictions}
        # 4. return ModelFnOps

        return tf.estimator.EstimatorSpec(
            mode=mode,
            predictions=predictions_dict,
            loss=loss,
            train_op=train_op,
            eval_metric_ops=eval_metric_ops,export_outputs=export_outputs )

When exporting a graph, there is a required export_outputs field in the EstimatorSpec . See the model_fn documentation for details.

I'd also note that tf.contrib.timeseries has some of this boilerplate written for you (including an RNN example ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM