我已经使用估计器和 TF Hub Elmo 词嵌入 + 新功能训练了一个 Tensorflow 模型,并且一直在尝试让它与 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 容器命名,我发现它解决了其他人的一些问题。为了节省时间,这里是带有训练模型和检查点文件的存储库:带有模型文件的存储库
任何帮助,将不胜感激!