title: How to use the Universal Sentence Encoder Tensorflow model with ML.NET?
body: <p>I have C# code which invokes Python to compute compute Universal Sentence Encoder (USE) embeddings. However, I figured that it would probably be much cleaner to do this using ML.NET (and thus without involving Python). Thing is that I haven't worked with Tensorflow despite doing simple stuff like the above (and I don't have time currently to dive into the details, although I'd like to at some point in time), and the ML.NET abstraction is not as high as I had hoped for. If somebody could jump in and fill in the missing pieces, I would highly appreciate that (and probably package and share a C# USE wrapper on GitHub).</p>
  <p>Here's the prerequisites:</p>
  <ul>
  <li>I'm using <a href="https://tfhub.dev/google/universal-sentence-encoder-large/5" rel="nofollow noreferrer">this model</a></li>
  <li>Loading the model works fine. I looked into the model's schema and input schema as provided by the C# API, but this is beyond me for now. Here's the input schema (as printed from C#):</li>
  </ul>
  <pre><code>serving_default_inputs: Vector&lt;String&gt; {TensorflowOperatorType}
  saver_filename: Vector&lt;String&gt; {TensorflowOperatorType}
  </code></pre>
  <ul>
  <li>And here's an excerpt of the (rather large) complete schema - I think that I have skipped the model internals (I guess that I only need to figure out for now how to feed data into the model and get the result back, so I hope that I have caught the right parts):</li>
  </ul>
  <pre><code>save_counter/Read/ReadVariableOp: Vector&lt;Int64&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  Embeddings/sharded_0/Read/ReadVariableOp: Vector&lt;Single, 13334, 320&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  ... (28 of these)
  Embeddings/sharded_29/Read/ReadVariableOp: Vector&lt;Single, 13334, 320&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  EncoderDNN/DNN/ResidualHidden_0/dense/kernel/part_0/Read/ReadVariableOp: Vector&lt;Single, 11, 320&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  ... (lot's of apparently internal stuff)
  EncoderTransformer/hidden_layers/tanh_layer_0/dense/bias/part_29/Read/ReadVariableOp: Vector&lt;Single, 17&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  StatefulPartitionedCall: Vector&lt;Single&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  Const: Vector&lt;String&gt; {TensorflowOperatorType}
  serving_default_inputs: Vector&lt;String&gt; {TensorflowOperatorType}
  StatefulPartitionedCall_1: Vector&lt;Single, 512&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  saver_filename: Vector&lt;String&gt; {TensorflowOperatorType}
  StatefulPartitionedCall_2: Vector&lt;String&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  StatefulPartitionedCall_3: Vector&lt;String&gt; {TensorflowOperatorType, TensorflowUpstreamOperators}
  </code></pre>
  <p>Finally, here's my rather naive C# code. Note that from the &quot;guessing&quot; part on, I have no clue what I'm doing at all - I went for <code>Const</code> since USE produces a constant-size array of floats, and for <code>serving_default_inputs</code> since it has <code>inputs</code> in its name - the rest is merely for demonstrating what I want to do. Excuse my ignorance :-)</p>
  <pre><code>using Microsoft.ML;
  using Microsoft.ML.Transforms;
  using System.Collections.Generic;
  using System.IO;
  using System.Reflection;
  
  namespace ML.UniversalSentenceEncoder
  {
      public class UniversalSentenceEncoder
      {
          public double[] ComputeEmbedding(string text)
          {
              string modelDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), &quot;ML&quot;, &quot;UniversalSentenceEncoder&quot;, &quot;Model&quot;);
              MLContext mlContext = new MLContext();
              TensorFlowModel model = mlContext.Model.LoadTensorFlowModel(modelDir);
  
              // here's where my guessing started :-)
              IEstimator&lt;ITransformer&gt; estimator = model.ScoreTensorFlowModel(&quot;Const&quot;, &quot;serving_default_inputs&quot;);
              IDataView dataView = mlContext.Data.LoadFromEnumerable(new List&lt;string&gt;());
              ITransformer transformer = estimator.Fit(dataView);
              PredictionEngine&lt;string, List&lt;double&gt;&gt; engine = mlContext.Model.CreatePredictionEngine&lt;string, List&lt;double&gt;&gt;(transformer);
              return engine.Predict(text).ToArray();
          }
  
      }
  }
  </code></pre>
  <p>In case this is useful (which I kind of doubt): The above code throws an exception at the <code>ITransformer transformer...</code> line: <code>System.ArgumentOutOfRangeException: 'Could not find source column 'serving_default_inputs' Arg_ParamName_Name'</code></p>