6
$\begingroup$
cnn = NetChain[
   {
    EmbeddingLayer[16, 100],
    ConvolutionLayer[64, 3, "Interleaving" -> True],
    AggregationLayer[Mean, 1],
    LinearLayer[2],
    SoftmaxLayer[]
    },
   "Input" -> 10
   ] // NetInitialize

enter image description here

Export["Python\\cnn.json", cnn, "MXNet"]
import mxnet as mx
import numpy as np

cnn = mx.gluon.SymbolBlock.imports('cnn.json', ['Input'], 'cnn.params')

cnn(mx.nd.array([[1,2,3,4,5,6,7,8,9,10]]))

[[0.52003264 0.47996736]]

cnn@Range[1, 10]

{0.520033,0.479967}

Mathematica embeds integers between 1 and n.

enter image description here

And MXNet embeds integers between 0 and n-1. Integers equal or greater than n are for unknown tokens.

cnn(mx.nd.array([[0,1,2,3,4,5,6,7,8,9]]))

[[0.49799052 0.50200945]]

cnn(mx.nd.array([[90,91,92,93,94,95,96,97,98,99]]))

[[0.48555845 0.51444155]]

cnn(mx.nd.array([[91,92,93,94,95,96,97,98,99,100]]))

[[0.4880071 0.51199293]]

cnn(mx.nd.array([[91,92,93,94,95,96,97,98,99,101]]))

[[0.4880071 0.51199293]]

No difference in output between 100 and 101 at the end.

And Mathematica has the same output as for unknown token at the end.

cnn@Range[91, 100]

{0.488007, 0.511993}

$\endgroup$
7
$\begingroup$

Hi from a developer of the Machine Learning team at Wolfram Research!

The behaviour you point out is the expected one. Let's take a look at how our EmbeddingLayer is represented in the exported mxnet model.

cnn = NetInitialize @ NetChain[
   {
    EmbeddingLayer[16, 100],
    ConvolutionLayer[64, 3, "Interleaving" -> True],
    AggregationLayer[Mean, 1],
    LinearLayer[2],
    SoftmaxLayer[]
    },
   "Input" -> 10
   ];
Export["cnn.json", cnn, "MXNet"];

It is associated with nodes {0, 1, 2, 3} in the graph below:

enter image description here

You can hover over the graph nodes to inspect their details, or import as a dataset (only displaying the relevant nodes for simplicity):

enter image description here

You can see that the input is fed to a PlusScalar node subtracting one, thus converting our 1-indexing convention to the 0-indexing used by mxnet. Since this conversion is made inside the mxnet symbol it is carried over in python, so you will get integers between 1 and n there as well. In general, our mxnet exporter always produces the exact same mxnet symbol we run in the backend, without making any attempt of translating between conventions.

As for the out-of-bounds inputs, mxnet behaviour is properly documented here: https://mxnet.incubator.apache.org/api/python/symbol/symbol.html#mxnet.symbol.Embedding

By default, if any index mentioned is too large, it is replaced by the index that addresses the last vector in an embedding matrix.

This is not true for our framework instead:

In[27]:= cnn[{91, 92, 93, 94, 95, 96, 97, 98, 99, 101}]

During evaluation of In[27]:= NetChain::invindata3: Data supplied to port "Input" could not be encoded; input is not a length-10 vector of integers between 1 and 100.

Out[27]= $Failed

Again, in this case we don't follow mxnet convention and just fail with an appropriate message. We have our own, completely separated input validation and error reporting system. In above example, the system is rejecting the input and the computation returns with a $Failed before even reaching mxnet.

Example

emb = NetChain[{EmbeddingLayer[2, 3, "Weights" -> Table[{i, i}, {i, 1, 3}]]}, "Input" -> 1]

enter image description here

Export["Python\\emb.json", emb, "MXNet"]
import mxnet as mx

emb = mx.gluon.SymbolBlock.imports('emb.json', ['Input'], 'emb.params')

emb(mx.nd.array([[0,1,2,3,4]]))

[[[1. 1.] [1. 1.] [2. 2.] [3. 3.] [3. 3.]]]

$\endgroup$
  • $\begingroup$ Thanks for you answer! +1 from me. Added an example. $\endgroup$ – Alexey Golyshev Apr 24 at 3:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.