簡體   English   中英

如何在 Java 的 TensorFlow 模型中提供稀疏占位符

[英]How can I feed a sparse placeholder in a TensorFlow model from Java

我正在嘗試使用 TensorFlow 中的 kNN 算法計算給定地址的最佳匹配,該算法效果很好,但是當我嘗試導出模型並在我們的 Java 環境中使用它時,我陷入了如何提供來自 Java 的稀疏占位符。

這是 python 部分的一個非常精簡的版本,它返回測試名稱和最佳參考名稱之間的最小距離。 到目前為止,這項工作符合預期。 當我導出模型並將其導入我的 Java 程序時,它總是返回相同的值(占位符默認的距離)。 我假設,python 函數sparse_from_word_vec(word_vec)不在模型中,這對我來說完全有意義,但是我應該如何制作這個稀疏張量? 我的輸入是一個字符串,我需要創建一個合適的稀疏張量(值)來計算距離。 我也在Java端搜索了一種生成稀疏張量的方法,但沒有成功。

import tensorflow as tf
import pandas as pd

d = {'NAME': ['max mustermann', 
              'erika musterfrau', 
              'joseph haydn', 
              'johann sebastian bach', 
              'wolfgang amadeus mozart']}

df = pd.DataFrame(data=d)  

input_name = tf.placeholder_with_default('max musterman',(), name='input_name')
output_dist = tf.placeholder(tf.float32, (), name='output_dist')

test_name = tf.sparse_placeholder(dtype=tf.string)
ref_names = tf.sparse_placeholder(dtype=tf.string)

output_dist = tf.edit_distance(test_name, ref_names, normalize=True)

def sparse_from_word_vec(word_vec):
    num_words = len(word_vec)
    indices = [[xi, 0, yi] for xi,x in enumerate(word_vec) for yi,y in enumerate(x)]
    chars = list(''.join(word_vec))
    return(tf.SparseTensorValue(indices, chars, [num_words,1,1]))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    t_data_names=tf.constant(df['NAME'])
    reference_names = [el.decode('UTF-8') for el in (t_data_names.eval())]

    sparse_ref_names = sparse_from_word_vec(reference_names)
    sparse_test_name = sparse_from_word_vec([str(input_name.eval().decode('utf-8'))]*5)

    feeddict={test_name: sparse_test_name,
              ref_names: sparse_ref_names, 
              }    

    output_dist = sess.run(output_dist, feed_dict=feeddict)
    output_dist = tf.reduce_min(output_dist, 0)
    print(output_dist.eval())

    tf.saved_model.simple_save(sess,
                               "model-simple",
                               inputs={"input_name": input_name},
                               outputs={"output_dist": output_dist})

這是我的 Java 方法:

public void run(ApplicationArguments args) throws Exception {
  log.info("Loading model...");

  SavedModelBundle savedModelBundle = SavedModelBundle.load("/model", "serve");

  byte[] test_name = "Max Mustermann".toLowerCase().getBytes("UTF-8");


  List<Tensor<?>> output = savedModelBundle.session().runner()
      .feed("input_name", Tensor.<String>create(test_names))
      .fetch("output_dist")
      .run();

  System.out.printl("Nearest distance: " + output.get(0).floatValue());

}

我能夠讓你的例子工作。 在深入研究之前,我對您的 Python 代碼有一些評論。

您在整個代碼中將變量output_dist用於 3 種不同的值類型。 我不是 Python 專家,但我認為這是不好的做法。 您也從未實際使用input_name占位符,除非將其導出為輸入。 最后一個是tf.saved_model.simple_save已棄用,您應該改用tf.saved_model.Builder

現在為解決方案。

使用命令jar tvf libtensorflow-xxxjar (感謝這篇文章)查看libtensorflow jar 文件,您可以看到沒有用於創建稀疏張量的有用綁定(也許提出功能請求?)。 因此,我們必須將輸入更改為稠密張量,然后向圖中添加操作以將其轉換為稀疏張量。 在您的原始代碼中,稀疏轉換在 python 端,這意味着 java 中加載的圖形不會有任何操作。

這是新的python代碼:

import tensorflow as tf
import pandas as pd

def model():
    #use dense tensors then convert to sparse for edit_distance
    test_name = tf.placeholder(shape=(None, None), dtype=tf.string, name="test_name")
    ref_names = tf.placeholder(shape=(None, None), dtype=tf.string, name="ref_names")

    #Java Does not play well with the empty character so use "/" instead
    test_name_sparse = tf.contrib.layers.dense_to_sparse(test_name, "/")
    ref_names_sparse = tf.contrib.layers.dense_to_sparse(ref_names, "/")

    output_dist = tf.edit_distance(test_name_sparse, ref_names_sparse, normalize=True)

    #output the index to the closest ref name
    min_idx = tf.argmin(output_dist)

    return test_name, ref_names, min_idx

#Python code to be replicated in Java
def pad_string(s, max_len):
    return s + ["/"] * (max_len - len(s))

d = {'NAME': ['joseph haydn', 
              'max mustermann', 
              'erika musterfrau', 
              'johann sebastian bach', 
              'wolfgang amadeus mozart']}

df = pd.DataFrame(data=d)  
input_name = 'max musterman'

#pad dense tensor input
max_len = max([len(n) for n in df['NAME']])

test_input = [list(input_name)]*len(df['NAME'])
#no need to pad, all same length
ref_input = list(map(lambda x: pad_string(x, max_len), [list(n) for n in df['NAME']]))


with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    test_name, ref_names, min_idx = model()

    #run a test to make sure the model works
    feeddict = {test_name: test_input,
                ref_names: ref_input,
            }
    out = sess.run(min_idx, feed_dict=feeddict)
    print("test output:", out)

    #save the model with the new Builder API
    signature_def_map= {
    "predict": tf.saved_model.signature_def_utils.predict_signature_def(
        inputs= {"test_name": test_name, "ref_names": ref_names},
        outputs= {"min_idx": min_idx})
    }

    builder = tf.saved_model.Builder("model")
    builder.add_meta_graph_and_variables(sess, ["serve"], signature_def_map=signature_def_map)
    builder.save()

這是加載和運行它的java。 這里可能有很大的改進空間(java 不是我的主要語言),但它給了你這個想法。

import org.tensorflow.Graph;
import org.tensorflow.Session;
import org.tensorflow.Tensor;
import org.tensorflow.TensorFlow;
import org.tensorflow.SavedModelBundle;

import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;

public class Test {
    public static byte[][] makeTensor(String s, int padding) throws Exception
    {
        int len = s.length();
        int extra = padding - len;

        byte[][] ret = new byte[len + extra][];
        for (int i = 0; i < len; i++) {
            String cur = "" + s.charAt(i);
            byte[] cur_b = cur.getBytes("UTF-8");
            ret[i] = cur_b;
        }

        for (int i = 0; i < extra; i++) {
            byte[] cur = "/".getBytes("UTF-8");
            ret[len + i] = cur;
        }

        return ret;
    }
    public static byte[][][] makeTensor(List<String> l, int padding) throws Exception
    {
        byte[][][] ret = new byte[l.size()][][];
        for (int i = 0; i < l.size(); i++) {
            ret[i] = makeTensor(l.get(i), padding);
        }

        return ret;
    }
    public static void main(String[] args) throws Exception {
        System.out.println("Loading model...");

        SavedModelBundle savedModelBundle = SavedModelBundle.load("model", "serve");


        List<String> str_test_name = Arrays.asList("Max Mustermann",
            "Max Mustermann",
            "Max Mustermann",
            "Max Mustermann",
            "Max Mustermann");
        List<String> names = Arrays.asList("joseph haydn",
            "max mustermann",
            "erika musterfrau",
            "johann sebastian bach",
            "wolfgang amadeus mozart");

        //get the max length for each array
        int pad1 = str_test_name.get(0).length();
        int pad2 = 0;
        for (String var : names) {
            if(var.length() > pad2)
                pad2 = var.length();
        }


        byte[][][] test_name = makeTensor(str_test_name, pad1);
        byte[][][] ref_names = makeTensor(names, pad2);

        //use a with block so the close method is called
        try(Tensor t_test_name = Tensor.<String>create(test_name))
        {
            try (Tensor t_ref_names = Tensor.<String>create(ref_names))
            {
                List<Tensor<?>> output = savedModelBundle.session().runner()
                    .feed("test_name", t_test_name)
                    .feed("ref_names", t_ref_names)
                    .fetch("ArgMin")
                    .run();

                System.out.println("Nearest distance: " + output.get(0).longValue());
            }
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM