Обслуживание переобученного модуля Tensorflow Hub с новыми функциями - PullRequest
0 голосов
/ 08 ноября 2018

Я обучил модель Tensorflow, используя оценки и встраивание слов TF Hub Elmo + новые функции, и пытался заставить ее работать с Tensorflow Serving для прогнозов. Когда я пытаюсь выполнить прогноз, я получаю следующую ошибку:

Traceback (most recent call last):
  File "client.py", line 49, in <module>
    run(args.host, args.port, args.text, args.model, args.signature_name)
  File "client.py", line 29, in run
    response = stub.Predict(request, 10.0)
  File "/home/user/anaconda3/envs/twitter/lib/python3.6/site-packages/grpc/_channel.py", line 500, in __call__
    return _end_unary_response_blocking(state, call, False, None)
  File "/home/user/anaconda3/envs/twitter/lib/python3.6/site-packages/grpc/_channel.py", line 434, in _end_unary_response_blocking
    raise _Rendezvous(state, None, None, deadline)
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with (StatusCode.FAILED_PRECONDITION, Serving signature key "serving_default" not found.)>

Вот все соответствующие файлы, необходимые для воспроизведения шагов:

train.py

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import numpy as np
import pandas as pd
import tensorflow as tf
import tensorflow_hub as hub
import json
import urllib

from sklearn.preprocessing import MultiLabelBinarizer

tf.logging.set_verbosity(tf.logging.INFO)

tf.app.flags.DEFINE_integer(
    'steps', 10, 'The number of steps to train a model')
tf.app.flags.DEFINE_string(
    'model_dir', './models/ckpt/', 'Dir to save a model and checkpoints')
tf.app.flags.DEFINE_string(
    'saved_dir', './models/pb/', 'Dir to save a model for TF serving')
FLAGS = tf.app.flags.FLAGS

'''
Data loading and preprocessing method
Dropped rows that do not contain sentiment
Casted text column to all lowercase to normalize tweets
Created new feature column for tweets containing a website or link
Created new feature column to check for profanity from a text file
'''


def load_and_preprocess():
    data = pd.read_csv('https://www.figure-eight.com/wp-content/uploads/2016/03/Apple-Twitter-Sentiment-DFE.csv',
                       encoding="ISO-8859-1")
    data['text'] = data['text'].str.lower()
    data = data[data.sentiment.str.contains("not_relevant") == False]
    data['contains_url'] = data['text'].str.contains('http').astype(int)
    profanity = pd.read_fwf('data/profanity.txt', header=None)
    words = [any(i in words for i in profanity[0].values)
             for words in data['text'].str.split().values]
    words = np.array(words, dtype=np.float32)
    data['contains_profanity'] = words.astype(int)
    sentiment = data['sentiment']
    text = data['text']
    url = data['contains_url']
    profanity = data['contains_profanity']

    return data, sentiment, text, url, profanity


data, sentiment, text, url, profanity = load_and_preprocess()


def serving_input_receiver_fn():
    feature_spec = {
        "text": tf.placeholder(dtype=tf.string, shape=[None]),
        "url": tf.placeholder(dtype=tf.bool, shape=[None]),
        "profanity": tf.placeholder(dtype=tf.bool, shape=[None]),
    }
    return tf.estimator.export.ServingInputReceiver(feature_spec, feature_spec)


def main(unused_argv):
    train_size = int(len(text) * .8)
    train_text = text[:train_size]
    train_sentiment = sentiment[:train_size]
    train_url = url[:train_size]
    train_profanity = profanity[:train_size]

    test_text = text[train_size:]
    test_sentiment = sentiment[train_size:]
    test_url = url[train_size:]
    test_profanity = profanity[train_size:]

    text_embeddings = hub.text_embedding_column(
        "text",
        module_spec="https://tfhub.dev/google/elmo/2", trainable=True
    )

    encoder = MultiLabelBinarizer()
    encoder.fit_transform(train_sentiment)
    train_encoded = encoder.transform(train_sentiment)
    test_encoded = encoder.transform(test_sentiment)
    num_classes = len(encoder.classes_)

    multi_label_head = tf.contrib.estimator.multi_label_head(
        num_classes,
        loss_reduction=tf.losses.Reduction.SUM_OVER_BATCH_SIZE
    )

    estimator = tf.contrib.estimator.DNNEstimator(
        head=multi_label_head,
        hidden_units=[64, 10],
        feature_columns=[text_embeddings],
        model_dir=FLAGS.model_dir
    )

    # Format our data for the numpy_input_fn
    features = {
        "text": np.array(train_text),
        "url": np.array(train_url),
        "profanity": np.array(train_profanity)
    }
    labels = np.array(train_encoded)

    train_input_fn = tf.estimator.inputs.numpy_input_fn(
        features,
        labels,
        shuffle=True,
        batch_size=FLAGS.steps,
        num_epochs=10
    )

    estimator.train(input_fn=train_input_fn)
    estimator.export_savedmodel(
        FLAGS.saved_dir, serving_input_receiver_fn=serving_input_receiver_fn)

    eval_input_fn = tf.estimator.inputs.numpy_input_fn({"text": np.array(
        test_text).astype(np.str)}, test_encoded.astype(np.int32), shuffle=False)
    estimator.evaluate(input_fn=eval_input_fn)


if __name__ == "__main__":
    tf.app.run()
client.py

import numpy as np
import tensorflow as tf

from grpc.beta import implementations
import argparse

from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2
from tensorflow_serving.apis import classification_pb2


def run(host, port, text, model, signature_name):
    text = "My cat only chews @apple cords. Such an #AppleSnob.".encode()
    url = np.array([0]).astype(np.bool)
    profanity = np.array([0]).astype(np.bool)

    # establish a connection
    channel = implementations.insecure_channel(host, port)
    stub = prediction_service_pb2.beta_create_PredictionService_stub(channel)

    request = predict_pb2.PredictRequest()
    request.model_spec.name = 'elmo'
    request.model_spec.signature_name = signature_name

    request.inputs['text'].CopyFrom(tf.make_tensor_proto(text))
    request.inputs['url'].CopyFrom(tf.make_tensor_proto(url))
    request.inputs['profanity'].CopyFrom(tf.make_tensor_proto(profanity))

    response = stub.Predict(request, 10.0)
    print(response)


def parse_args(parser):


    options = parser.parse_args()
    return options


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--host", type=str, default="localhost")
    parser.add_argument("--port", type=int, default=8500)
    parser.add_argument("--text", type=str)
    parser.add_argument("--model", type=str)
    parser.add_argument('--signature_name', default='serving_default', type=str)

    args = parser.parse_args()
    run(args.host, args.port, args.text, args.model, args.signature_name)
Dockerfile

FROM ubuntu:18.04

RUN apt-get update && apt-get install -y \
        curl \
        libcurl3-dev \
        unzip \
        wget \
        && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Installing tensorflow-model-server
RUN TEMP_DEB="$(mktemp)" \
    && wget -O "$TEMP_DEB" 'http://storage.googleapis.com/tensorflow-serving-apt/pool/tensorflow-model-server-1.8.0/t/tensorflow-model-server/tensorflow-model-server_1.8.0_all.deb' \
    && dpkg -i "$TEMP_DEB" \
    && rm -f "$TEMP_DEB"

# gRPC port
EXPOSE 8500

# REST API port
EXPOSE 8501

# Serving the model
CMD tensorflow_model_server \
  --port=8500 \
  --rest_api_port=8501 \
  --model_name="$MODEL_NAME" \
  --model_base_path="$MODEL_PATH"
env.yml

name: elmo
channels:
    - https://conda.anaconda.org/menpo
    - conda-forge
dependencies:
    - python=3
    - scikit-learn
    - protobuf
    - openblas
    - scipy
    - numpy
    - pandas
    - pillow
    - h5py
    - pip:
        - grpcio
        - grpcio-tools
        - tensorflow
        - tensorflow-serving-api
        - tensorflow-hub
        - flake8
Steps to reproduce

1. conda env create -f env.yml
2. python train.py --steps 100 --saved_dir ./models/ --model_dir ./elmo_ckpt
  - This takes a while to train, so I have provided my repo with the model files already in it
3. docker build --rm -f Dockerfile -t tensorflow-elmo:latest .
4. docker run --rm -v ${PWD}/models:/models -e MODEL_NAME='elmo' -e MODEL_PATH='/models' -p 8500:8500 -p 8501:8501 --name tensorflow-server tensorflow-elmo:latest
5. python client.py --model elmo

Я пробовал использовать разные опции в signature_name, и модель была названа в честь контейнера Docker, который, как я выяснил, исправил некоторые проблемы других людей. Вот репозиторий с уже подготовленными файлами моделей и контрольных точек в интересах времени: Репозиторий с файлами моделей

Буду признателен за любую помощь!

1 Ответ

0 голосов
/ 09 ноября 2018

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

Кроме того, ваш код использует устаревший tf.contrib.estimator и смешивает его с tf.estimator. Я настоятельно рекомендую перейти на tf.estimator.

Это, похоже, не связано с использованием TensorFlow Hub. К тому времени, когда ваш оценщик экспортирует модель, все содержимое модуля уже встроено в нее.

...