执行在线预测Google ML部署模型时出错

时间:2019-11-20 21:52:13

标签: android tensorflow google-cloud-ml

我在尝试通过HTTP Rest调用请求部署的在线预测时遇到问题 在Google AI平台上进行建模。无论如何打包数据,都会出现以下错误。错误是:

print(input_encoded_m)
print(questions_encoded)

(<tf.Tensor 'dropout_41/Identity:0' shape=(None, 552, 64) dtype=float32>,
 <tf.Tensor 'dropout_42/Identity:0' shape=(None, 5, 64) dtype=float32>)

我已经确认了Google凭据的工作,并且可以从我的Android应用程序执行查询以获取有关该模型的信息,并且成功。

我认为问题在于数据的格式。我已按照https://cloud.google.com/ml-engine/docs/online-predicthttps://cloud.google.com/ml-engine/docs/v1/predict-request的说明进行操作。我尝试发送各种版本,例如

 Expected float32, got *******

应该将3D数组作为输入-基本上,模型的输入是150x150x3数组,其中每个像素的值都在0到1.0(浮点)之间。因为模型需要浮点输入,所以我无法将原始的150x150x3像素值转换为base64。因此,我在上面的链接中使用解释来发送3D数组。我尝试了许多不同的方法,但目前还不知道该怎么办。 这是显示3D字符串数组的图像,该数组表示模型需要的缩放输入

enter image description here

注意:我正在从JSON file中读取数据-我在下面的命令行成功gcloud调用中使用了相同的数据来执行预测。这是说明内容的图像。

enter image description here

注意:我可以使用{"instances": [xxx]} where have formatted xxx in many different ways. 在命令行上执行此操作,如下所示 enter image description here

1 个答案:

答案 0 :(得分:0)

这是一个常见问题:

gcloud所需的格式与发送到该服务的HTTP请求的正文之间存在细微差异。我们知道这很令人困惑,将来我们将努力解决这一困惑。这是一个简短的说明,可以帮助您入门。

发送到API的请求的正文具有以下形式:

/*
  We have to #define _GNU_SOURCE to get access to `char **environ`
*/
#define _GNU_SOURCE
#include <unistd.h>


#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sendfile.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <errno.h>
#include <string.h>


struct child
{
  pid_t pid;
  int fd_in;
  int fd_out;
  int fd_err;
};

static void
close_if_valid(int fd)
{
  if (fd != -1) close(fd);
}

/* 
   Closes all file-descriptors for child communication
   and waits for child to exit

   returns status value from waitpid().
   see `man waitpid` on how to interpret that value
 */
int child_wait(struct child *c)
{
  close_if_valid(c->fd_in);
  close_if_valid(c->fd_out);
  close_if_valid(c->fd_err);

  int status;

  pid_t p = waitpid(c->pid, &status, 0);
  if (p == 0)
    error(1, errno, "waitpid() failed");

  return status; 
}


int
dup_if_valid(int fd1, int fd2)
{
  if (fd1 != -1 && fd1 != fd2)
    return dup2(fd1, fd2);
  return fd2;
}


pid_t
child_spawn_fd(const char *const argv[], const char *const env[],
           int in, int out, int err)
{
  fflush(stdout);
  pid_t p = fork();

  if (p)
    return p;

  /***********************
    We are now in child
  ***********************/

  /* 
     Set file descriptors to expected values,
     -1 means inherit from parent
  */
  if (dup_if_valid(in, 0) == -1)
    goto CHILD_ERR;

  if (dup_if_valid(out, 1) == -1)
    goto CHILD_ERR;

  if (dup_if_valid(err, 2) == -1)
    goto CHILD_ERR;

  /*
    close all unneeded file descriptors
    This will free resources and keep files and sockets belonging to
    the parent from beeing open longer than needed

    On *BSD we may call `closefrom(3);`, but this may not exits
    on Linux. So we loop over all possible file descriptor numbers.
    A better solution, is to look in `/proc/self/fs`
  */
  int max_fd = sysconf(_SC_OPEN_MAX);

  for (int fd = 3; fd <= max_fd; fd++)
    close(fd);

  if (env)
    environ = (char **)env;

  /* Change to execvp if command should be looked up in $PATH */
  execv(argv[0], (char * const *)argv);

 CHILD_ERR:
  _exit(1);
}


#define CHILD_PIPE_STDIN (1 << 0)
#define CHILD_PIPE_STDOUT (1 << 1)
#define CHILD_PIPE_STDERR (1 << 2)

#define READ_END 0
#define WRITE_END 1


struct child
child_spawn(const char * const argv[], const char * const env[], int flags)
{
  int in_pipe[2] = {-1, -1};
  int out_pipe[2] = {-1, -1};
  int err_pipe[2] = {-1, -1};

  if (flags & CHILD_PIPE_STDIN)
    if (pipe(in_pipe))
      error(EXIT_FAILURE, errno, "pipe(in_pipe) failed");

  if (flags & CHILD_PIPE_STDOUT)
    if (pipe(out_pipe))
      error(EXIT_FAILURE, errno, "pipe(out_pipe) failed");

  if (flags & CHILD_PIPE_STDERR)
    if (pipe(err_pipe))
      error(EXIT_FAILURE, errno, "pipe(err_pipe) failed");

  pid_t p = child_spawn_fd(argv, env,
               in_pipe[READ_END],
               out_pipe[WRITE_END],
               err_pipe[WRITE_END]);

  if (p == -1)
    error(EXIT_FAILURE, errno, "fork() failed");

  close_if_valid(in_pipe[READ_END]);
  close_if_valid(out_pipe[WRITE_END]);
  close_if_valid(err_pipe[WRITE_END]);

  struct child c =
    {
      .pid = p,
      .fd_in = in_pipe[WRITE_END],
      .fd_out = out_pipe[READ_END],
      .fd_err = err_pipe[READ_END],
    };

  return c;
}

/* 
   Safer implementation of `system()`. It does not invoke shell, and takes
   command as NULL terminated list of execuatable and parameters
*/
int
my_system(const char * const argv[])
{
  struct child c = child_spawn(argv, NULL, 0);

  int status = child_wait(&c);

  if (WIFEXITED(status))
    return WEXITSTATUS(status);
  else
    return -1;
}




int
main (int argc, char **argv)
{
  printf("Running 'ls -l' using my_system()\n"); 
  printf("---------------------------------\n");
  fflush(stdout);

  const char * ls_argv[] =
    {
      "/bin/ls",
      "-l",
      NULL
    };

  int e = my_system(ls_argv);
  printf("---------\n");
  printf("\exit code ---> %d\n", e); 



  printf("\nRunning 'ls -l' using child_spawn() and reading from stdout\n"); 
  printf("-----------------------------------------------------------\n");
  fflush(stdout);

  struct child c = child_spawn(ls_argv, NULL, CHILD_PIPE_STDOUT);

  /* 
     Read from the childs stdout and write to current stdout
  */
  size_t copied = 0;
  while (1)
    {
      char buff[4096];

      ssize_t rlen = read(c.fd_out, buff, 4096);
      if (rlen == -1)
    error(EXIT_FAILURE, errno, "read() failed");

      if (rlen == 0)
    break;

      size_t written = 0;
      while (written < rlen)
    {
      ssize_t wlen = write(1, buff + written, rlen - written);
      if (wlen == -1)
        error(EXIT_FAILURE, errno, "write() failed");

      written += wlen;
    }
      copied += written;
    }


  /* Wait for child to end */
  int status = child_wait(&c);

  printf("---------\n");

  if (WIFEXITED(status))
    {  
      printf("  ---> child exited normally with exit code %d and with %ld bytes copied\n",
         WEXITSTATUS(status),
         copied);
    }
  else
    printf("  ---> child exited by som other reason than _exit()");


  printf("\nWriting to Elmer Fudd filter\n"); 
  const char *quote = "Be very very quiet, I'm hunting rabbits!\n";

  printf("Original text: %s", quote);

  printf("-----------------------------------------------------------\n");
  fflush(stdout);

  const char *fudd_filter[] =
    {"/bin/sed", "-e" "s/r/w/g", NULL};  

  struct child c2 = child_spawn(fudd_filter, NULL, CHILD_PIPE_STDIN);
  size_t qlen = strlen(quote);
  const char *q = quote;

  while (qlen)
    {
      ssize_t wlen = write(c2.fd_in, q, qlen);
      if (wlen == -1)
    error(EXIT_FAILURE, errno, "write() failed");

      q += wlen;
      qlen -= wlen;
    }

  child_wait(&c2);
}

gcloud中使用的文件只是用换行符分隔的实例列表:

{"instances": [<instance 1>, <instance 2>, ...]}

我查看了您的文件,有2条建议:

a)因为使用gcloud命令可以正常工作,所以我将使用AI Platform UI来测试您的JSON输入,就像API会使用的那样。

enter image description here

注意:复制并粘贴输入后,请使用测试

根据文档中的说明,我们需要以下内容:

<instance 1>
<instance 2>

在这种情况下,您有

{
    "instances": [
        <object>
        ...
    ]
}

b)您需要将{ "conv2d_input": [ <object> ... ] } 替换为conv2d_input并添加额外的[]: 我用自己的模型测试了您的input,并且工作正常。

instances

我通常使用此代码:

{  
    "instances": 
     [
       [[[0.23137255012989044, 0.27450981736183167, 0.250980406999588], 
       ...
       ,[0.2980392277240753, 0.43529412150382996, 0.2078431397676468]]]
     ]
}