diff --git a/thinc/neural/ops.cpp b/thinc/neural/ops.cpp
index eeabfc0..979cd3f 100644
--- a/thinc/neural/ops.cpp
+++ b/thinc/neural/ops.cpp
@@ -2398,6 +2398,7 @@ static char __pyx_k_stop[] = "stop";
 static char __pyx_k_test[] = "__test__";
 static char __pyx_k_ASCII[] = "ASCII";
 static char __pyx_k_Sized[] = "Sized";
+static char __pyx_k_bytes[] = "bytes";
 static char __pyx_k_class[] = "__class__";
 static char __pyx_k_delta[] = "delta_";
 static char __pyx_k_dtype[] = "dtype";
@@ -2687,6 +2688,7 @@ static PyObject *__pyx_n_s_batch_outer;
 static PyObject *__pyx_n_s_best__bo;
 static PyObject *__pyx_n_s_bias;
 static PyObject *__pyx_n_s_buff;
+static PyObject *__pyx_n_s_bytes;
 static PyObject *__pyx_n_s_c;
 static PyObject *__pyx_n_u_c;
 static PyObject *__pyx_n_s_class;
@@ -2942,6 +2944,7 @@ static PyObject *__pyx_slice__25;
 static PyObject *__pyx_slice__27;
 static PyObject *__pyx_slice__28;
 static PyObject *__pyx_slice__30;
+static PyObject *__pyx_slice__58;
 static PyObject *__pyx_slice__59;
 static PyObject *__pyx_slice__60;
 static PyObject *__pyx_slice__61;
@@ -3045,6 +3048,7 @@ static PyObject *__pyx_codeobj__38;
 static PyObject *__pyx_codeobj__39;
 static PyObject *__pyx_codeobj__40;
 static PyObject *__pyx_codeobj__41;
+static PyObject *__pyx_codeobj__42;
 static PyObject *__pyx_codeobj__43;
 
 /* "thinc/neural/ops.pyx":39
@@ -11094,15 +11098,15 @@ static PyObject *__pyx_pf_5thinc_6neural_3ops_8NumpyOps_16increment_slices(CYTHO
  *                 workon[i] += to_add[i]
  * 
  *     def random_bytes(self, n):             # <<<<<<<<<<<<<<
- *         cdef bytes output = b'\0' * n
- *         cdef unsigned char* arr = <unsigned char*>output
+ *         return self.xp.random.bytes(n)
+ * 
  */
 
 /* Python wrapper */
 static PyObject *__pyx_pw_5thinc_6neural_3ops_8NumpyOps_19random_bytes(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
 static PyMethodDef __pyx_mdef_5thinc_6neural_3ops_8NumpyOps_19random_bytes = {"random_bytes", (PyCFunction)__pyx_pw_5thinc_6neural_3ops_8NumpyOps_19random_bytes, METH_VARARGS|METH_KEYWORDS, 0};
 static PyObject *__pyx_pw_5thinc_6neural_3ops_8NumpyOps_19random_bytes(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
-  CYTHON_UNUSED PyObject *__pyx_v_self = 0;
+  PyObject *__pyx_v_self = 0;
   PyObject *__pyx_v_n = 0;
   int __pyx_lineno = 0;
   const char *__pyx_filename = NULL;
@@ -11160,15 +11164,14 @@ static PyObject *__pyx_pw_5thinc_6neural_3ops_8NumpyOps_19random_bytes(PyObject
   return __pyx_r;
 }
 
-static PyObject *__pyx_pf_5thinc_6neural_3ops_8NumpyOps_18random_bytes(CYTHON_UNUSED PyObject *__pyx_self, CYTHON_UNUSED PyObject *__pyx_v_self, PyObject *__pyx_v_n) {
-  PyObject *__pyx_v_output = 0;
-  unsigned char *__pyx_v_arr;
+static PyObject *__pyx_pf_5thinc_6neural_3ops_8NumpyOps_18random_bytes(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_self, PyObject *__pyx_v_n) {
   PyObject *__pyx_r = NULL;
   __Pyx_TraceDeclarations
   __Pyx_RefNannyDeclarations
   PyObject *__pyx_t_1 = NULL;
-  unsigned char *__pyx_t_2;
-  int __pyx_t_3;
+  PyObject *__pyx_t_2 = NULL;
+  PyObject *__pyx_t_3 = NULL;
+  PyObject *__pyx_t_4 = NULL;
   int __pyx_lineno = 0;
   const char *__pyx_filename = NULL;
   int __pyx_clineno = 0;
@@ -11179,63 +11182,65 @@ static PyObject *__pyx_pf_5thinc_6neural_3ops_8NumpyOps_18random_bytes(CYTHON_UN
   /* "thinc/neural/ops.pyx":281
  * 
  *     def random_bytes(self, n):
- *         cdef bytes output = b'\0' * n             # <<<<<<<<<<<<<<
- *         cdef unsigned char* arr = <unsigned char*>output
- *         fill_random_bytes(arr, n)
- */
-  __pyx_t_1 = PyNumber_Multiply(__pyx_kp_b__42, __pyx_v_n); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  if (!(likely(PyBytes_CheckExact(__pyx_t_1))||((__pyx_t_1) == Py_None)||(PyErr_Format(PyExc_TypeError, "Expected %.16s, got %.200s", "bytes", Py_TYPE(__pyx_t_1)->tp_name), 0))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __pyx_v_output = ((PyObject*)__pyx_t_1);
-  __pyx_t_1 = 0;
-
-  /* "thinc/neural/ops.pyx":282
- *     def random_bytes(self, n):
- *         cdef bytes output = b'\0' * n
- *         cdef unsigned char* arr = <unsigned char*>output             # <<<<<<<<<<<<<<
- *         fill_random_bytes(arr, n)
- *         return output
- */
-  __pyx_t_2 = __Pyx_PyObject_AsUString(__pyx_v_output); if (unlikely((!__pyx_t_2) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 282; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __pyx_v_arr = ((unsigned char *)__pyx_t_2);
-
-  /* "thinc/neural/ops.pyx":283
- *         cdef bytes output = b'\0' * n
- *         cdef unsigned char* arr = <unsigned char*>output
- *         fill_random_bytes(arr, n)             # <<<<<<<<<<<<<<
- *         return output
- * 
- */
-  __pyx_t_3 = __Pyx_PyInt_As_int(__pyx_v_n); if (unlikely((__pyx_t_3 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 283; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __pyx_f_5thinc_6neural_3ops_fill_random_bytes(__pyx_v_arr, __pyx_t_3);
-
-  /* "thinc/neural/ops.pyx":284
- *         cdef unsigned char* arr = <unsigned char*>output
- *         fill_random_bytes(arr, n)
- *         return output             # <<<<<<<<<<<<<<
- * 
+ *         return self.xp.random.bytes(n)             # <<<<<<<<<<<<<<
  * 
+ * class CupyOps(Ops):
  */
   __Pyx_XDECREF(__pyx_r);
-  __Pyx_INCREF(__pyx_v_output);
-  __pyx_r = __pyx_v_output;
+  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_xp); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+  __Pyx_GOTREF(__pyx_t_2);
+  __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_random); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+  __Pyx_GOTREF(__pyx_t_3);
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_bytes); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+  __Pyx_GOTREF(__pyx_t_2);
+  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+  __pyx_t_3 = NULL;
+  if (CYTHON_COMPILING_IN_CPYTHON && likely(PyMethod_Check(__pyx_t_2))) {
+    __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
+    if (likely(__pyx_t_3)) {
+      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2);
+      __Pyx_INCREF(__pyx_t_3);
+      __Pyx_INCREF(function);
+      __Pyx_DECREF_SET(__pyx_t_2, function);
+    }
+  }
+  if (!__pyx_t_3) {
+    __pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_v_n); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+    __Pyx_GOTREF(__pyx_t_1);
+  } else {
+    __pyx_t_4 = PyTuple_New(1+1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+    __Pyx_GOTREF(__pyx_t_4);
+    __Pyx_GIVEREF(__pyx_t_3); PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_3); __pyx_t_3 = NULL;
+    __Pyx_INCREF(__pyx_v_n);
+    __Pyx_GIVEREF(__pyx_v_n);
+    PyTuple_SET_ITEM(__pyx_t_4, 0+1, __pyx_v_n);
+    __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_t_4, NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+    __Pyx_GOTREF(__pyx_t_1);
+    __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+  }
+  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+  __pyx_r = __pyx_t_1;
+  __pyx_t_1 = 0;
   goto __pyx_L0;
 
   /* "thinc/neural/ops.pyx":280
  *                 workon[i] += to_add[i]
  * 
  *     def random_bytes(self, n):             # <<<<<<<<<<<<<<
- *         cdef bytes output = b'\0' * n
- *         cdef unsigned char* arr = <unsigned char*>output
+ *         return self.xp.random.bytes(n)
+ * 
  */
 
   /* function exit code */
   __pyx_L1_error:;
   __Pyx_XDECREF(__pyx_t_1);
+  __Pyx_XDECREF(__pyx_t_2);
+  __Pyx_XDECREF(__pyx_t_3);
+  __Pyx_XDECREF(__pyx_t_4);
   __Pyx_AddTraceback("thinc.neural.ops.NumpyOps.random_bytes", __pyx_clineno, __pyx_lineno, __pyx_filename);
   __pyx_r = NULL;
   __pyx_L0:;
-  __Pyx_XDECREF(__pyx_v_output);
   __Pyx_XGIVEREF(__pyx_r);
   __Pyx_TraceReturn(__pyx_r, 0);
   __Pyx_RefNannyFinishContext();
