Статьи о TensorFlow | TensorFlow Serving API

TensorFlow

"Введение"TensorFlow Serving предоставляет интерфейс GRPC для эффективного выполнения запросов прогнозирования к модели, но он предоставляет только API на основе Python. Если мы хотим использовать другие языки для доступа к GRPC, нам нужно вручную сгенерировать соответствующий файл интерфейса GRPC. В этой статье в основном представлены метод и метод использования инструмента protoc для создания файла API обслуживания TensorFlow, а также приведен полный пример проекта для справки.

Компиляция файла ProtoBuf

TensorFlow Servingоснован наProtocol Bufferсогласие на выполнениеGPRCобщение, исходный код которого начинается сprotoРяд структур данных определен для файлов с суффиксами (message) а такжеRPCСлужить (service), которые используются для представления формата обмена данными и интерфейса для выполнения удаленных операций соответственно.protoСами файлы нельзя использовать непосредственно в коде, их необходимо дополнительно преобразовать в файлы кода, зависящие от языка, для нормальной компиляции и запуска.

Protocol Bufferофициально предоставленProtocol Buffer Compiler (protoc) скомпилировать инструменты дляprotoфайл для компиляции и создания файлов кода, зависящих от языка, инструмент в настоящее время поддерживает работу по генерации кода на нескольких языках, включаяgolang,java,c++а такжеc#Ждать. использоватьprotocИнструменты могут значительно сократить наши усилия по кодированию, позволяя нам больше сосредоточиться на конкретных бизнес-реализациях без хлопот, связанных с определением различных структур данных, зависящих от языка.

Поэтому при использовании других языков сTensorFlow ServingпровестиGRPCПри общении мы должны полагаться наprotocинструменты для создания зависимых от языкаAPIфайл для последующего использования. Следует отметить, что из-заTensorFlow Servingчасть исходного кодаprotoфайл должен зависеть отTensorFlowсерединаprotoфайл, поэтому нам нужно использовать исходный код обоих для создания необходимогоAPIдокумент.

Ниже краткое введениеprotocинструменты вLinuxПроцесс установки под систему:

  1. первый вProtocol BufferизGithubЗагрузите последнюю версию страницы выпуска программного обеспеченияprotocДвоичный сжатый файл пакета или используйте следующую команду для прямой загрузки.

    wget https://github.com/protocolbuffers/protobuf/releases/download/v3.12.3/protoc-3.12.3-linux-x86_64.zip
    
  2. Затем распакуйте архив в/usr/local/protocПод содержанием.

    unzip protoc-3.12.3-linux-x86_64.zip -d /usr/local/protoc
    
  3. Тогда будет/usr/local/protoc/binкаталог добавлен вPATHв переменных окружения. Вы можете добавить следующую строку в/etc/profileдокумент для достижения вышеуказанных целей.

    export PATH=$PATH:/usr/local/protoc/bin
    
  4. Окончательная тестовая установка прошла успешно

    protoc --version
    

Генерация и использование файла API

Вообще говоря, покаProtocol BufferиGRPCПоддерживаемые языки могут быть сгенерированыTensorFlow ServingизAPIдокумент. в предыдущей статьеTensorFlow 2.x 模型 Serving 服务中Я уже представил использованиеPythonвыполнятьGPRCПример запроса, эта статья в основном знакомит с использованиемGolangа такжеJavaгенерироватьTensorFlow ServingизAPIфайл и провестиGPRCспособ запроса.

Чтобы сгенерировать исполняемый файл кода, нам сначала нужноTensorFlowиTensorFlow Servingисходный кодcloneк местному.

mkdir tensorflow-serving-api && cd tensorflow-serving-api
git clone https://github.com/tensorflow/tensorflow.git
git clone https://github.com/tensorflow/serving.git

Затем вы можете использовать исходный кодprotoфайл для создания соответствующего языкаTensorFlow Serving APIфайл.

Golang

в созданииgolangСвязанныйAPIфайл кода, нам нужно установитьgolangокружающая среда и некоторыеprotocПлагины, помогающие нам в операциях по созданию файлов. Следующие связанные операцииLinuxСистема завершена.

  1. Установитьgolang, процесс выглядит следующим образом.

    wget https://dl.google.com/go/go1.14.4.linux-amd64.tar.gz
    tar zxvf go1.14.4.linux-amd64.tar.gz -C /usr/local
    export PATH=$PATH:/usr/local/go/bin
    go version
    
  2. Установитьprotoc-gen-goплагин для генерацииgoдокумент.

    go get -u google.golang.org/protobuf/cmd/protoc-gen-go
    # or
    go get -u github.com/golang/protobuf/protoc-gen-go
    

    protoc-gen-goПо умолчанию он будет установлен в$GOPATH/binкаталог, вам нужно убедиться, что каталог находится вPATHниже, чтобыprotocИнструмент может найти плагин.

  3. Установитьgrpcплагин для генерацииgrpc goдокумент.

    go get -u google.golang.org/grpc
    
  4. Переключите исходный код на указанную ветку или тег.

    cd tensorflow-serving-api/tensorflow
    git checkout tags/v2.2.0
    cd tensorflow-serving-api/serving
    git checkout tags/2.2.0
    
  5. использоватьprotocсоздание инструментаgoдокумент.

    cd tensorflow-serving-api
    protoc -I=serving -I=tensorflow --go_out=plugins=grpc:golang serving/tensorflow_serving/*/*.proto
    protoc -I=serving -I=tensorflow --go_out=plugins=grpc:golang serving/tensorflow_serving/sources/storage_path/*.proto
    protoc -I=serving -I=tensorflow --go_out=plugins=grpc:golang tensorflow/tensorflow/core/framework/*.proto
    protoc -I=serving -I=tensorflow --go_out=plugins=grpc:golang tensorflow/tensorflow/core/example/*.proto
    protoc -I=serving -I=tensorflow --go_out=plugins=grpc:golang tensorflow/tensorflow/core/protobuf/*.proto
    protoc -I=serving -I=tensorflow --go_out=plugins=grpc:golang tensorflow/tensorflow/stream_executor/*.proto
    

    в-IуказанныйprotoПуть к файлу для поиска зависимых файлов, который можно указать несколько раз.--go_outсохранение указаноgoкаталог файла (здесьgolang) и используемыйgrpcплагин. Последний элемент команды указан как подстановочный знакprotoВходное местоположение файла.

    Что касается того, почему выберите вышеprotoфайл дляAPIсоздано, основано на фактическом использовании иprotoЗависимости между файлами определяются. можно начать сservingизprotoНачните с исходного кода и обратитесь к егоPython GRPCРеализация кода примера, найти записьprotoфайл, то по себе и своим зависимостямprotoфайл для создания соответствующегоAPIкодовые файлы, а затем выполнять тесты кодирования для проверки недостатков и заполнения упущений до тех пор, пока все кодовые файлы не будут скомпилированы без ошибок.

  6. После выполнения вышеуказанной командыgolangПод каталогом создаются два каталога, а именноgithub.comиtensorflow_serving, первый содержит отtensorflowв исходном кодеprotoфайл созданgoфайл, последний содержит изservingв исходном кодеprotoфайл созданgoдокумент. так какtensorflowв исходном кодеprotoфайлы содержатgo_packageварианты, такие какoption go_package = "github.com/tensorflow/tensorflow/tensorflow/go/core/framework/tensor_go_proto";, которые определяют сгенерированныйgoвыходной каталог файла, поэтому он сгенерированgoфайл будет вgithub.comкаталог иservingв исходном кодеprotoфайл не содержит этой опции, поэтомуgoВыходной каталог файла по умолчанию находится в том же каталоге, что и исходный файл.

  7. tensorflow_servingсоздается в каталогеgoОшибки циклических ссылок могут возникать в файлах следующим образом:

    import cycle not allowed
    package github.com/alex/tensorflow-serving-api-go
       imports tensorflow_serving/apis
       imports tensorflow_serving/core
       imports tensorflow_serving/apis
    

    В этот момент вам нужноtensorflow_serving/coreПод содержаниемlogging.pb.goдокументы иtensorflow_serving/apisв каталогеprediction_log.pb.goудаление файла для решения вышеуказанной проблемы. Удаление вышеуказанных файлов кода не влияет на последующиеGRPCЗапрос предсказания модели.

  8. Предположим, у меня есть файл с именемfirst_modelМодель развернута вTensorFlow ServingИнформация о метаданных службы выглядит следующим образом:

    $ curl http://localhost:8501/v1/models/first_model/versions/0/metadata
    {
        "model_spec": {
            "name": "first_model",
            "signature_name": "",
            "version": "0"
        },
        "metadata": {
            "signature_def": {
                "signature_def": {
                    "serving_default": {
                        "inputs": {
                            "input_1": {
                                "dtype": "DT_INT64",
                                "tensor_shape": {
                                    "dim": [
                                        {
                                            "size": "-1",
                                            "name": ""
                                        },
                                        {
                                            "size": "31",
                                            "name": ""
                                        }
                                    ],
                                    "unknown_rank": false
                                },
                                "name": "serving_default_input_1:0"
                            }
                        },
                        "outputs": {
                            "output_1": {
                                "dtype": "DT_FLOAT",
                                "tensor_shape": {
                                    "dim": [
                                        {
                                            "size": "-1",
                                            "name": ""
                                        },
                                        {
                                            "size": "1",
                                            "name": ""
                                        }
                                    ],
                                    "unknown_rank": false
                                },
                                "name": "StatefulPartitionedCall:0"
                            }
                        },
                        "method_name": "tensorflow/serving/predict"
                    },
                    "__saved_model_init_op": {
                        "inputs": {},
                        "outputs": {
                            "__saved_model_init_op": {
                                "dtype": "DT_INVALID",
                                "tensor_shape": {
                                    "dim": [],
                                    "unknown_rank": true
                                },
                                "name": "NoOp"
                            }
                        },
                        "method_name": ""
                    }
                }
            }
        }
    }
    

    Нам нужно сосредоточиться на приведенной выше информации.inputsпараметры, которые определяют входные данные для этой моделиkeyзначение (здесьinput_1) , размерность входных данных (здесь(-1, 31)) и тип входных данных (здесьDT_INT64). в ходе выполненияGRPCПри прогнозировании запроса входные данные, указанные в коде, должны совпадать с различной входной информацией, определенной в метаданных, иначе не удастся получить правильный вывод модели.

  9. Создайтеgoпроект кfirst_modelОтправитьGRPCПрогноз запросов. Для получения подробной информации о конкретных проектах см.GithubРеализация и описание выше, здесь указан только код основной функции, а именно:

    package main
    
    import (
       "context"
       "log"
       apis "tensorflow_serving/apis"
       "time"
    
       "github.com/golang/protobuf/ptypes/wrappers"
       "github.com/tensorflow/tensorflow/tensorflow/go/core/framework/tensor_go_proto"
       "github.com/tensorflow/tensorflow/tensorflow/go/core/framework/tensor_shape_go_proto"
       "github.com/tensorflow/tensorflow/tensorflow/go/core/framework/types_go_proto"
       "google.golang.org/grpc"
    )
    
    var (
       // TensorFlow serving grpc address.
       address = "127.0.0.1:8500"
    )
    
    func main() {
       // Create a grpc request.
       request := &apis.PredictRequest{
          ModelSpec: &apis.ModelSpec{},
          Inputs:    make(map[string]*tensor_go_proto.TensorProto),
       }
       request.ModelSpec.Name = "first_model"
       request.ModelSpec.SignatureName = "serving_default"
       // request.ModelSpec.VersionChoice = &apis.ModelSpec_VersionLabel{VersionLabel: "stable"}
       request.ModelSpec.VersionChoice = &apis.ModelSpec_Version{Version: &wrappers.Int64Value{Value: 0}}
       request.Inputs["input_1"] = &tensor_go_proto.TensorProto{
          Dtype: types_go_proto.DataType_DT_INT64,
          TensorShape: &tensor_shape_go_proto.TensorShapeProto{
             Dim: []*tensor_shape_go_proto.TensorShapeProto_Dim{
                &tensor_shape_go_proto.TensorShapeProto_Dim{
                   Size: int64(2),
                },
                &tensor_shape_go_proto.TensorShapeProto_Dim{
                   Size: int64(31),
                },
             },
          },
          Int64Val: []int64{
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          },
       }
    
       // Create a grpc connection.
       conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(10*time.Second))
       if err != nil {
          log.Fatalf("couldn't connect: %s", err.Error())
       }
       defer conn.Close()
    
       // Wrap the grpc uri with client.
       client := apis.NewPredictionServiceClient(conn)
       ctx, cancel := context.WithTimeout(context.Background(), time.Second)
       defer cancel()
       // Send the grpc request.
       response, err := client.Predict(ctx, request)
       if err != nil {
          log.Fatalf("couldn't get response: %v", err)
       }
       log.Printf("%+v", response)
    }
    
  10. GithubАдрес:https://github.com/AlexanderJLiu/tensorflow-serving-api/tree/master/golang

Java

в созданииjavaСвязанныйAPIфайл кода, нам нужно установитьjavaокружающая среда и некоторыеprotocПлагины, помогающие нам в операциях по созданию файлов. Следующие связанные операцииLinuxСистема завершена.

  1. УстановитьOpenJDK.

    # centos
    yum-config-manager --enable rhel-7-server-optional-rpms
    yum install java-11-openjdk-devel
    # ubuntu
    apt-get install openjdk-11-jdk
    # test
    java -version
    
  2. Установитьprotoc-gen-grpc-java, генерироватьgrpc javaдокумент.

    wget https://repo1.maven.org/maven2/io/grpc/protoc-gen-grpc-java/1.30.2/protoc-gen-grpc-java-1.30.2-linux-x86_64.exe
    mv protoc-gen-grpc-java-1.30.2-linux-x86_64.exe /usr/local/protoc/bin/protoc-gen-grpc-java
    
  3. Переключите исходный код на указанную ветку или тег.

    cd tensorflow-serving-api/tensorflow
    git checkout tags/v2.2.0
    cd tensorflow-serving-api/serving
    git checkout tags/2.2.0
    
  4. использоватьprotocсоздание инструментаjavaдокумент.

    cd tensorflow-serving-api
    protoc -I=serving -I=tensorflow --plugin=/usr/local/protoc/bin/protoc-gen-grpc-java --grpc-java_out=java --java_out=java serving/tensorflow_serving/*/*.proto
    protoc -I=serving -I=tensorflow --plugin=/usr/local/protoc/bin/protoc-gen-grpc-java --grpc-java_out=java --java_out=java serving/tensorflow_serving/sources/storage_path/*.proto
    

    в-IуказанныйprotoПуть к файлу для поиска зависимых файлов, который можно указать несколько раз.--pluginуказано для использованияgrpcПуть к плагину.--grpc-java_outуказанныйgrpc javaКаталог, в котором сохранен файл.--java_outуказанныйjavaКаталог, в котором сохранен файл. Последний элемент команды указан как подстановочный знакprotoВходное местоположение файла.

    так какTensorFlowОфициально на основеprotoфайл созданTensorFlowизjavaфайл, поэтому нам не нужно генерировать его самостоятельно и использовать непосредственно изmavenСклад может быть импортирован:implementation("org.tensorflow:proto:1.15.0").

  5. Создайтеjavaпроект кfirst_modelОтправитьGRPCПрогноз запросов. Для получения подробной информации о конкретных проектах см.GithubРеализация и описание выше, здесь указан только код основной функции, а именно:

    package com.github.alex;
    
    import java.util.Arrays;
    import java.util.concurrent.TimeUnit;
    import com.google.protobuf.Int64Value;
    import org.tensorflow.framework.DataType;
    import org.tensorflow.framework.TensorProto;
    import org.tensorflow.framework.TensorShapeProto;
    import org.tensorflow.framework.TensorShapeProto.Dim;
    import io.grpc.ManagedChannel;
    import io.grpc.ManagedChannelBuilder;
    import io.grpc.stub.StreamObserver;
    import tensorflow.serving.Model.ModelSpec;
    import tensorflow.serving.Predict.PredictRequest;
    import tensorflow.serving.Predict.PredictResponse;
    import tensorflow.serving.PredictionServiceGrpc;
    import tensorflow.serving.PredictionServiceGrpc.PredictionServiceBlockingStub;
    import tensorflow.serving.PredictionServiceGrpc.PredictionServiceStub;
    
    public class App {
       public String getGreeting() {
          return "Hello world.";
       }
    
       public static void main(String[] args) {
          PredictRequest.Builder requestBuilder = PredictRequest.newBuilder();
    
          ModelSpec.Builder modelSpecBuilder = ModelSpec.newBuilder();
          modelSpecBuilder.setSignatureName("serving_default");
          modelSpecBuilder.setName("first_model");
          modelSpecBuilder.setVersion(Int64Value.newBuilder().setValue(0L));
          requestBuilder.setModelSpec(modelSpecBuilder.build());
    
          TensorProto.Builder tensorProtoBuilder = TensorProto.newBuilder();
          tensorProtoBuilder.setDtype(DataType.DT_INT64);
          Dim[] dim = {Dim.newBuilder().setSize(1).build(), Dim.newBuilder().setSize(31).build()};
          tensorProtoBuilder
                   .setTensorShape(TensorShapeProto.newBuilder().addAllDim(Arrays.asList(dim)));
          // tensorProtoBuilder.setTensorShape(TensorShapeProto.newBuilder()
          // .addDim(Dim.newBuilder().setSize(1)).addDim(Dim.newBuilder().setSize(31)));
          Long[] inputs = {1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
                   1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L};
          tensorProtoBuilder.addAllInt64Val(Arrays.asList(inputs));
          requestBuilder.putInputs("input_1", tensorProtoBuilder.build());
    
          PredictRequest request = requestBuilder.build();
          System.out.println(request);
    
          String target = "127.0.0.1:8500";
          // Create a communication channel to the server, known as a Channel. Channels are
          // thread-safe and reusable. It is common to create channels at the beginning of your
          // application and reuse them until the application shuts down.
          ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
                   // Channels are secure by default (via SSL/TLS). For the example we disable TLS to
                   // avoid needing certificates.
                   .usePlaintext().build();
          PredictionServiceBlockingStub stub = PredictionServiceGrpc.newBlockingStub(channel);
          try {
                PredictResponse response = stub.predict(request);
                System.out.println(response);
                channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
          } catch (InterruptedException e) {
                e.printStackTrace();
          }
       }
    }
    
  6. GithubАдрес:https://github.com/AlexanderJLiu/tensorflow-serving-api/tree/master/java

Другие языки

я здесьGithubсоздалtensorflow-serving-apiпроект, который предназначен для созданияProtocol BufferиGRPCвсех поддерживаемых языковTensorFlow Serving APIфайл, а пример использования приведен в виде готового проекта.

Проект постепенно совершенствуется и был реализованGolang,Javaа такжеPythonлингвистическийTensorFlow Serving APIИ примеры проектов, в будущем будет добавлено больше языковых реализаций, и каждый может принять участие и внести свой вклад.

Адрес ссылки на проект:https://github.com/AlexanderJLiu/tensorflow-serving-api

использованная литература

  1. Go Installation Instructions
  2. Protocol Buffer Basics: Go
  3. ProtoBuf: Go Generated Code
  4. Go GRPC Examples
  5. Install OpenJDK on Windows and Linux
  6. Protocol Buffer Basics: Java
  7. ProtoBuf: Java Generated Code
  8. GRPC: Java Generated-code Reference