diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 81d060667..4ad90be7d 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -136,7 +136,7 @@ jobs:
shell: bash --noprofile --norc -o pipefail {0}
- name: Run tests with extras
- run: python -m pytest --pyargs thinc --cov=thinc --cov-report=term
+ run: python -m pytest --pyargs thinc --cov=thinc --cov-report=term -p thinc.tests.enable_tensorflow -p thinc.tests.enable_mxnet
- name: Run tests for thinc-apple-ops
run: |
diff --git a/examples/00_intro_to_thinc.ipynb b/examples/00_intro_to_thinc.ipynb
index 80e0c25a6..c7fbf5292 100644
--- a/examples/00_intro_to_thinc.ipynb
+++ b/examples/00_intro_to_thinc.ipynb
@@ -23,7 +23,7 @@
},
"outputs": [],
"source": [
- "!pip install \"thinc>=8.0.0\" \"ml_datasets>=0.2.0\" \"tqdm>=4.41\""
+ "!pip install \"thinc>=8.2.0\" \"ml_datasets>=0.2.0\" \"tqdm>=4.41\""
]
},
{
@@ -1050,7 +1050,8 @@
"source": [
"from tensorflow.keras.layers import Dense, Dropout\n",
"from tensorflow.keras.models import Sequential\n",
- "from thinc.api import TensorFlowWrapper, Adam\n",
+ "from thinc.api import enable_tensorflow, TensorFlowWrapper, Adam\n",
+ "enable_tensorflow()\n",
"\n",
"width = 32\n",
"nO = 10\n",
@@ -1373,8 +1374,9 @@
"outputs": [],
"source": [
"from mxnet.gluon.nn import Dense, Sequential, Dropout\n",
- "from thinc.api import MXNetWrapper, chain, Softmax\n",
+ "from thinc.api import enable_mxnet, MXNetWrapper, chain, Softmax\n",
"import thinc.util\n",
+ "enable_mxnet()\n",
"\n",
"assert thinc.util.has_mxnet\n",
"\n",
diff --git a/thinc/api.py b/thinc/api.py
index 6f795237a..b2bc346a0 100644
--- a/thinc/api.py
+++ b/thinc/api.py
@@ -11,7 +11,7 @@
use_pytorch_for_gpu_memory,
use_tensorflow_for_gpu_memory,
)
-from .compat import has_cupy
+from .compat import enable_mxnet, enable_tensorflow, has_cupy
from .config import Config, ConfigValidationError, registry
from .initializers import (
configure_normal_init,
@@ -190,6 +190,8 @@
"torch2xp", "xp2torch", "tensorflow2xp", "xp2tensorflow", "mxnet2xp", "xp2mxnet",
"get_torch_default_device",
# .compat
+ "enable_mxnet",
+ "enable_tensorflow",
"has_cupy",
# .backends
"get_ops", "set_current_ops", "get_current_ops", "use_ops",
diff --git a/thinc/backends/_cupy_allocators.py b/thinc/backends/_cupy_allocators.py
index 77c958e36..09322ac00 100644
--- a/thinc/backends/_cupy_allocators.py
+++ b/thinc/backends/_cupy_allocators.py
@@ -12,7 +12,7 @@ def cupy_tensorflow_allocator(size_in_bytes: int):
sitting in the other library's pool.
"""
size_in_bytes = max(1024, size_in_bytes)
- tensor = tensorflow.zeros((size_in_bytes // 4,), dtype=tensorflow.dtypes.float32)
+ tensor = tensorflow.zeros((size_in_bytes // 4,), dtype=tensorflow.dtypes.float32) # type: ignore
# We convert to cupy via dlpack, so that we can get a memory pointer.
cupy_array = cast(ArrayXd, tensorflow2xp(tensor))
address = int(cupy_array.data)
diff --git a/thinc/compat.py b/thinc/compat.py
index 52a73669f..5d600796a 100644
--- a/thinc/compat.py
+++ b/thinc/compat.py
@@ -1,3 +1,5 @@
+import warnings
+
from packaging.version import Version
try: # pragma: no cover
@@ -50,25 +52,45 @@
has_torch_amp = False
torch_version = Version("0.0.0")
-try: # pragma: no cover
+
+def enable_tensorflow():
+ warn_msg = (
+ "Built-in TensorFlow support will be removed in Thinc v9. If you need "
+ "TensorFlow support in the future, you can transition to using a "
+ "custom copy of the current TensorFlowWrapper in your package or "
+ "project."
+ )
+ warnings.warn(warn_msg, DeprecationWarning)
+ global tensorflow, has_tensorflow, has_tensorflow_gpu
import tensorflow
import tensorflow.experimental.dlpack
has_tensorflow = True
has_tensorflow_gpu = len(tensorflow.config.get_visible_devices("GPU")) > 0
-except ImportError: # pragma: no cover
- tensorflow = None
- has_tensorflow = False
- has_tensorflow_gpu = False
-try: # pragma: no cover
+tensorflow = None
+has_tensorflow = False
+has_tensorflow_gpu = False
+
+
+def enable_mxnet():
+ warn_msg = (
+ "Built-in MXNet support will be removed in Thinc v9. If you need "
+ "MXNet support in the future, you can transition to using a "
+ "custom copy of the current MXNetWrapper in your package or "
+ "project."
+ )
+ warnings.warn(warn_msg, DeprecationWarning)
+ global mxnet, has_mxnet
import mxnet
has_mxnet = True
-except ImportError: # pragma: no cover
- mxnet = None
- has_mxnet = False
+
+
+mxnet = None
+has_mxnet = False
+
try:
import h5py
diff --git a/thinc/layers/tensorflowwrapper.py b/thinc/layers/tensorflowwrapper.py
index a77e0b3af..302764c3f 100644
--- a/thinc/layers/tensorflowwrapper.py
+++ b/thinc/layers/tensorflowwrapper.py
@@ -1,3 +1,4 @@
+# mypy: ignore-errors
from typing import Any, Callable, Dict, Optional, Tuple, Type, TypeVar
import srsly
diff --git a/thinc/shims/mxnet.py b/thinc/shims/mxnet.py
index 2dd36a62f..544db59e9 100644
--- a/thinc/shims/mxnet.py
+++ b/thinc/shims/mxnet.py
@@ -1,3 +1,4 @@
+# mypy: ignore-errors
import copy
from typing import Any, cast
diff --git a/thinc/shims/tensorflow.py b/thinc/shims/tensorflow.py
index bcaae3aac..0668ba92c 100644
--- a/thinc/shims/tensorflow.py
+++ b/thinc/shims/tensorflow.py
@@ -1,3 +1,4 @@
+# mypy: ignore-errors
import contextlib
import copy
from io import BytesIO
diff --git a/thinc/tests/enable_mxnet.py b/thinc/tests/enable_mxnet.py
new file mode 100644
index 000000000..b7ccb3e6e
--- /dev/null
+++ b/thinc/tests/enable_mxnet.py
@@ -0,0 +1,6 @@
+from thinc.compat import enable_mxnet
+
+try:
+ enable_mxnet()
+except ImportError:
+ pass
diff --git a/thinc/tests/enable_tensorflow.py b/thinc/tests/enable_tensorflow.py
new file mode 100644
index 000000000..bd1ac7667
--- /dev/null
+++ b/thinc/tests/enable_tensorflow.py
@@ -0,0 +1,6 @@
+from thinc.compat import enable_tensorflow
+
+try:
+ enable_tensorflow()
+except ImportError:
+ pass
diff --git a/thinc/util.py b/thinc/util.py
index 9a1aaf65b..ce8fcbb78 100644
--- a/thinc/util.py
+++ b/thinc/util.py
@@ -151,7 +151,7 @@ def is_torch_mps_array(obj: Any) -> bool: # pragma: no cover
def is_tensorflow_array(obj: Any) -> bool: # pragma: no cover
if not has_tensorflow:
return False
- elif isinstance(obj, tf.Tensor):
+ elif isinstance(obj, tf.Tensor): # type: ignore
return True
else:
return False
@@ -164,7 +164,7 @@ def is_tensorflow_gpu_array(obj: Any) -> bool: # pragma: no cover
def is_mxnet_array(obj: Any) -> bool: # pragma: no cover
if not has_mxnet:
return False
- elif isinstance(obj, mx.nd.NDArray):
+ elif isinstance(obj, mx.nd.NDArray): # type: ignore
return True
else:
return False
@@ -316,15 +316,17 @@ def get_width(
def assert_tensorflow_installed() -> None: # pragma: no cover
"""Raise an ImportError if TensorFlow is not installed."""
- template = "TensorFlow support requires {pkg}: pip install thinc[tensorflow]"
+ template = "TensorFlow support requires {pkg}: pip install thinc[tensorflow]\n\nEnable TensorFlow support with thinc.api.enable_tensorflow()"
if not has_tensorflow:
- raise ImportError(template.format(pkg="tensorflow>=2.0.0"))
+ raise ImportError(template.format(pkg="tensorflow>=2.0.0,<2.6.0"))
def assert_mxnet_installed() -> None: # pragma: no cover
"""Raise an ImportError if MXNet is not installed."""
if not has_mxnet:
- raise ImportError("MXNet support requires mxnet: pip install thinc[mxnet]")
+ raise ImportError(
+ "MXNet support requires mxnet: pip install thinc[mxnet]\n\nEnable MXNet support with thinc.api.enable_mxnet()"
+ )
def assert_pytorch_installed() -> None: # pragma: no cover
@@ -429,32 +431,32 @@ def torch2xp(
def xp2tensorflow(
xp_tensor: ArrayXd, requires_grad: bool = False, as_variable: bool = False
-) -> "tf.Tensor": # pragma: no cover
+) -> "tf.Tensor": # type: ignore # pragma: no cover
"""Convert a numpy or cupy tensor to a TensorFlow Tensor or Variable"""
assert_tensorflow_installed()
if hasattr(xp_tensor, "toDlpack"):
dlpack_tensor = xp_tensor.toDlpack() # type: ignore
- tf_tensor = tf.experimental.dlpack.from_dlpack(dlpack_tensor)
+ tf_tensor = tf.experimental.dlpack.from_dlpack(dlpack_tensor) # type: ignore
elif hasattr(xp_tensor, "__dlpack__"):
dlpack_tensor = xp_tensor.__dlpack__() # type: ignore
- tf_tensor = tf.experimental.dlpack.from_dlpack(dlpack_tensor)
+ tf_tensor = tf.experimental.dlpack.from_dlpack(dlpack_tensor) # type: ignore
else:
- tf_tensor = tf.convert_to_tensor(xp_tensor)
+ tf_tensor = tf.convert_to_tensor(xp_tensor) # type: ignore
if as_variable:
# tf.Variable() automatically puts in GPU if available.
# So we need to control it using the context manager
- with tf.device(tf_tensor.device):
- tf_tensor = tf.Variable(tf_tensor, trainable=requires_grad)
+ with tf.device(tf_tensor.device): # type: ignore
+ tf_tensor = tf.Variable(tf_tensor, trainable=requires_grad) # type: ignore
if requires_grad is False and as_variable is False:
# tf.stop_gradient() automatically puts in GPU if available.
# So we need to control it using the context manager
- with tf.device(tf_tensor.device):
- tf_tensor = tf.stop_gradient(tf_tensor)
+ with tf.device(tf_tensor.device): # type: ignore
+ tf_tensor = tf.stop_gradient(tf_tensor) # type: ignore
return tf_tensor
def tensorflow2xp(
- tf_tensor: "tf.Tensor", *, ops: Optional["Ops"] = None
+ tf_tensor: "tf.Tensor", *, ops: Optional["Ops"] = None # type: ignore
) -> ArrayXd: # pragma: no cover
"""Convert a Tensorflow tensor to numpy or cupy tensor depending on the `ops` parameter.
If `ops` is `None`, the type of the resultant tensor will be determined by the source tensor's device.
@@ -466,7 +468,7 @@ def tensorflow2xp(
if isinstance(ops, NumpyOps):
return tf_tensor.numpy()
else:
- dlpack_tensor = tf.experimental.dlpack.to_dlpack(tf_tensor)
+ dlpack_tensor = tf.experimental.dlpack.to_dlpack(tf_tensor) # type: ignore
return cupy_from_dlpack(dlpack_tensor)
else:
if isinstance(ops, NumpyOps) or ops is None:
@@ -477,21 +479,21 @@ def tensorflow2xp(
def xp2mxnet(
xp_tensor: ArrayXd, requires_grad: bool = False
-) -> "mx.nd.NDArray": # pragma: no cover
+) -> "mx.nd.NDArray": # type: ignore # pragma: no cover
"""Convert a numpy or cupy tensor to a MXNet tensor."""
assert_mxnet_installed()
if hasattr(xp_tensor, "toDlpack"):
dlpack_tensor = xp_tensor.toDlpack() # type: ignore
- mx_tensor = mx.nd.from_dlpack(dlpack_tensor)
+ mx_tensor = mx.nd.from_dlpack(dlpack_tensor) # type: ignore
else:
- mx_tensor = mx.nd.from_numpy(xp_tensor)
+ mx_tensor = mx.nd.from_numpy(xp_tensor) # type: ignore
if requires_grad:
mx_tensor.attach_grad()
return mx_tensor
def mxnet2xp(
- mx_tensor: "mx.nd.NDArray", *, ops: Optional["Ops"] = None
+ mx_tensor: "mx.nd.NDArray", *, ops: Optional["Ops"] = None # type: ignore
) -> ArrayXd: # pragma: no cover
"""Convert a MXNet tensor to a numpy or cupy tensor."""
from .api import NumpyOps
diff --git a/website/docs/api-layers.md b/website/docs/api-layers.md
index 45ad8c824..dbdde5b20 100644
--- a/website/docs/api-layers.md
+++ b/website/docs/api-layers.md
@@ -1003,7 +1003,7 @@ model, e.g. `chain(f, g)` computes `g(f(x))`.
| Argument | Type | Description |
| ----------- | -------------- | --------------------------------- |
-| `layer1 ` | Model | The first model to compose. |
+| `layer1` | Model | The first model to compose. |
| `layer2` | Model | The second model to compose. |
| `*layers` | Model | Any additional models to compose. |
| **RETURNS** | Model | The composed feed-forward model. |
@@ -1795,6 +1795,16 @@ https://github.com/explosion/thinc/blob/master/thinc/layers/torchscriptwrapper.p
+
+In Thinc v8.2+, TensorFlow support is not enabled by default. To enable TensorFlow:
+
+```python
+from thinc.api import enable_tensorflow
+enable_tensorflow()
+```
+
+
+
Wrap a [TensorFlow](https://tensorflow.org) model, so that it has the same API
as Thinc models. To optimize the model, you'll need to create a TensorFlow
optimizer and call `optimizer.apply_gradients` after each batch. To allow
@@ -1820,6 +1830,16 @@ https://github.com/explosion/thinc/blob/master/thinc/layers/tensorflowwrapper.py
+
+In Thinc v8.2+, MXNet support is not enabled by default. To enable MXNet:
+
+```python
+from thinc.api import enable_mxnet
+enable_mxnet()
+```
+
+
+
Wrap a [MXNet](https://mxnet.apache.org/) model, so that it has the same API as
Thinc models. To optimize the model, you'll need to create a MXNet optimizer and
call `optimizer.step()` after each batch. To allow maximum flexibility, the
diff --git a/website/docs/api-util.md b/website/docs/api-util.md
index add7c12e1..c413f3503 100644
--- a/website/docs/api-util.md
+++ b/website/docs/api-util.md
@@ -141,6 +141,14 @@ Converts a class vector (integers) to binary class matrix. Based on
| `label_smoothing` | float | Smoothing-coefficient for label-smoothing. |
| **RETURNS** | Floats2d | A binary matrix representation of the input. The axis representing the classes is placed last. |
+### enable_mxnet {#enable_mxnet tag="function" new="8.2.0"}
+
+Import and enable internal support for MXNet.
+
+### enable_tensorflow {#enable_tensorflow tag="function" new="8.2.0"}
+
+Import and enable internal support for TensorFlow.
+
### xp2torch {#xp2torch tag="function"}
Convert a `numpy` or `cupy` tensor to a PyTorch tensor.
diff --git a/website/docs/usage-frameworks.md b/website/docs/usage-frameworks.md
index 50dbc3da2..ea0f215b1 100644
--- a/website/docs/usage-frameworks.md
+++ b/website/docs/usage-frameworks.md
@@ -81,6 +81,16 @@ Y, backprop = model(X, is_train=True)
dX = backprop(Y)
```
+
+In Thinc v8.2+, TensorFlow support is not enabled by default. To enable TensorFlow:
+
+```python
+from thinc.api import enable_tensorflow
+enable_tensorflow()
+```
+
+
+
```python
### TensorFlow Example {highlight="6"}
from thinc.api import TensorFlowWrapper, chain, Linear