This error means you used a NumPy array where GPU-aware code expects an object with a .device attribute (e.g., a PyTorch, CuPy, or JAX array).
When Python throws AttributeError: ‘NumPy NdArray’ object has no attribute ‘device’, it’s telling you the array in hand is a plain CPU-backed numpy.ndarray, which doesn’t track accelerator placement or expose a .device property. Frameworks that run on GPUs—PyTorch, CuPy, JAX—do expose device info on their array/tensor types. Mixing them without converting leads to this exact message. NumPy focuses on fast CPU arrays and documents attributes such as shape, dtype, and ndim, not device (NumPy ndarray reference, class docs). In contrast, PyTorch tensors expose tensor.device (PyTorch tensor tutorial, tensor attributes), CuPy arrays expose cupy.ndarray.device (CuPy basics, CuPy ndarray), and modern JAX arrays expose Array.device (JAX Array.device).
Why You See AttributeError: ‘NumPy NdArray’ Object Has No Attribute ‘Device’
Quick check: scan your recent edits for a call that reads arr.device or arr.to(device). If arr came from numpy (e.g., np.array(...)), it won’t have .device. That attribute exists on GPU-aware types only:
- PyTorch tensors — expose
tensor.deviceand accepttensor.to("cuda")(docs). - CuPy arrays — expose
cupy_arr.deviceand live on a CUDA device (docs). - JAX arrays — expose
Array.deviceand support device placement APIs (docs).
Common triggers include passing an np.ndarray into code that assumes a tensor, calling .to(device) on a NumPy array, or logging array.device during debugging. The fix is to convert the array to the right type before using GPU-only attributes or methods, or to stop calling GPU attributes when you intend to run purely on the CPU.
Fast Diagnosis: What Type Is My Array?
Single print: run this tiny block to see both type and safe device info:
import numpy as np
def where_is(x):
t = type(x).__name__
if hasattr(x, "device"):
# PyTorch or CuPy or JAX Array
return f"{t} on {getattr(x, 'device')}"
return f"{t} (no .device)"
a = np.arange(6)
print(where_is(a)) # numpy.ndarray (no .device)
Library-aware probe: expand to cover common stacks:
import numpy as np
try:
import torch
except ImportError:
torch = None
try:
import cupy as cp
except ImportError:
cp = None
try:
import jax
import jax.numpy as jnp
except ImportError:
jax = None
jnp = None
def inspect(x):
t = type(x).__name__
if torch is not None and isinstance(x, getattr(torch, "Tensor", ())):
return f"PyTorch Tensor on {x.device}"
if cp is not None and isinstance(x, getattr(cp, "ndarray", ())):
return f"CuPy ndarray on {x.device}"
if jnp is not None and isinstance(x, getattr(jnp, "ndarray", ())):
# jax.Array in newer versions; fallback handles display
return f"JAX Array on {getattr(x, 'device', 'unknown')}"
if isinstance(x, np.ndarray):
return "NumPy ndarray on CPU (no .device)"
return f"{t} (unrecognized)"
print(inspect(np.arange(3)))
This tells you instantly whether you’re holding a CPU NumPy array or a GPU-aware object that supports .device (as documented in the PyTorch, CuPy, and JAX references linked above).
Fixes By Stack: Convert Or Stop Calling .device
PyTorch: Convert NumPy To A Tensor, Then Move To GPU
When working with PyTorch, feed NumPy arrays through a tensor factory and place them on the desired device. PyTorch exposes the device property on tensors and shows it in the tutorials and attribute docs (tutorial, attributes).
- Create a tensor —
torch.from_numpy(x)shares memory on CPU;torch.tensor(x)copies. Pick the one you need (see general notes in PyTorch docs and community write-ups). - Place on device — use
tensor.to("cuda")/tensor.to("cpu")and then accesstensor.device.
import numpy as np, torch
x_np = np.random.rand(4, 4).astype("float32")
# Share CPU memory, then move to GPU if available
t = torch.from_numpy(x_np)
if torch.cuda.is_available():
t = t.to("cuda") # now t.device is cuda:0 or similar
print(t.device)
Round-trip: when a model returns a GPU tensor and you need a NumPy array, bring it back to the CPU first, then detach and convert:
y_t = t * 2
y_np = y_t.to("cpu").detach().numpy()
CuPy: Use GPU Arrays Natively Or Convert Both Ways
CuPy arrays live on a CUDA device and expose arr.device (CuPy basics, CuPy ndarray). Convert NumPy <→ CuPy as needed:
- To CuPy —
cp.asarray(np_arr)moves data to the current GPU. - Back to NumPy —
cp.asnumpy(cp_arr)copies data to host memory.
import numpy as np, cupy as cp
x_np = np.arange(8)
x_cp = cp.asarray(x_np)
print(x_cp.device) # <CUDA Device 0>
x_back = cp.asnumpy(x_cp)
JAX: Keep Arrays On The Right Device
Modern JAX arrays expose Array.device and APIs for placement (Array.device). Use jax.device_put or Array.to_device to place data, then read arr.device as needed (to_device).
import jax, jax.numpy as jnp
x = jnp.arange(5)
print(x.device) # shows CPU or an accelerator device
y = x.to_device(jax.devices()[0])
Table: Which Arrays Have .device?
| Library | Object Type | .device Available? |
|---|---|---|
| NumPy | numpy.ndarray |
No (CPU array; use shape/dtype/ndim) |
| PyTorch | torch.Tensor |
Yes (tensor.device; move with .to(...)) |
| CuPy | cupy.ndarray |
Yes (arr.device; CUDA device) |
| JAX | jax.Array |
Yes (Array.device) |
Safe Patterns That Prevent The Error
Guard conversions: place conversion at module boundaries so downstream code always receives the right type. This removes guesswork and suppresses AttributeError at the call site.
# io.py
import numpy as np
def load_numpy(path: str) -> np.ndarray:
return np.load(path)
# pipeline_torch.py
import torch
def as_tensor_on_device(x, device="cuda"):
t = x if isinstance(x, torch.Tensor) else torch.as_tensor(x)
return t.to(device) if torch.cuda.is_available() else t
Check types at the edge: if your function needs a tensor, assert once and fail early with a clear message.
def requires_tensor(x):
import torch
if not isinstance(x, torch.Tensor):
raise TypeError("Expected torch.Tensor; got %r" % type(x))
Keep logging neutral: when you only want to log device info, try a helper that won’t call .device on NumPy:
def device_or_cpu(x):
return getattr(x, "device", "cpu")
Typical Scenarios And The Fix That Works
Training A PyTorch Model With NumPy Batches
You load data with NumPy, then pass it straight into a model that expects tensors. Access to batch.device or a .to(...) call fails.
- Convert early — wrap your DataLoader or collate step to return tensors.
- Pin once — keep tensors on the right device from the start to avoid extra transfers.
import numpy as np, torch
def collate_fn(batch_np):
batch_np = np.stack(batch_np)
batch_t = torch.as_tensor(batch_np) # no copy if possible
return batch_t.to("cuda") if torch.cuda.is_available() else batch_t
Migrating NumPy Code To CuPy For GPU Speedups
You swapped np with cp in some places, but mixed arrays remain. Calls to arr.device hit plain NumPy arrays.
- Normalize inputs — run
x = cp.asarray(x)at the start of each GPU path. - Return policy — return CuPy arrays in GPU paths; convert back to NumPy at the boundary with
cp.asnumpy()as needed.
Logging Device Info Across Libraries
Mixed stacks (PyTorch + CuPy + JAX) can all expose .device, but NumPy does not. Your logger calls x.device blindly and crashes during CPU-only test runs.
- Safe logger — use
getattr(x, "device", "cpu"). - Per-type branches — check
isinstanceand format strings per library.
When You Should Not Convert
If your pipeline is CPU-only, stop calling GPU-specific methods altogether. NumPy arrays are perfect for this path and the official docs list the attributes you can rely on: shape, dtype, ndim, and similar (NumPy ndarray reference). Replace arr.device with a neutral display, keep math in NumPy, and remove stray .to("cuda") calls.
Troubleshooting Checklist
- Confirm the type — log
type(x)before using.device. - Respect library boundaries — convert once, not in the inner loop.
- Pick the right factory — in PyTorch,
torch.from_numpyshares CPU memory;torch.tensorcopies; move with.to(...)after creation (attributes). - Keep device consistent — a batch mixing CPU and GPU arrays will trigger device mismatches elsewhere.
- Guard logging — never assume
.deviceexists.
Worked Examples: From Error To Clean Run
1) PyTorch Inference With NumPy Input
# Bad: model(input_np) then print(input_np.device)
import numpy as np, torch
input_np = np.random.randn(2, 3).astype("float32")
model = torch.nn.Linear(3, 1).eval()
# Fix: convert and place once
x = torch.from_numpy(input_np) # or torch.tensor(input_np)
x = x.to("cuda") if torch.cuda.is_available() else x
with torch.inference_mode():
out = model(x)
print(out.device) # valid on tensors
2) CuPy Kernels With Mixed Arrays
# Bad: cp function receives NumPy; then code checks arr.device
import numpy as np, cupy as cp
def gpu_norm(x):
x = cp.asarray(x) # normalize input
return cp.linalg.norm(x)
print(gpu_norm(np.arange(10)))
3) JAX Utilities With Plain NumPy
# Bad: np array passed to a helper that logs x.device
import numpy as np, jax, jax.numpy as jnp
def jax_helper(x):
x = jnp.asarray(x)
print(x.device) # now safe in JAX
return jnp.sum(x)
print(jax_helper(np.arange(5)))
Performance Notes And Data Movement
Every cross-library conversion can copy data. PyTorch’s docs and community notes explain that torch.from_numpy can share CPU memory with a NumPy array, while moving to CUDA still performs a device transfer. Pick a path and stick with it to reduce transfers. CuPy’s docs describe that cupy.ndarray lives on a CUDA device; moving back to NumPy with cp.asnumpy copies data to host memory (CuPy basics). JAX arrays carry device placement info and provide placement APIs (Array.device, to_device).
Bottom Line Fix
The message AttributeError: ‘NumPy NdArray’ object has no attribute ‘device’ appears when GPU-aware code touches a plain numpy.ndarray. Convert to the framework’s array type and place it on the right device, or stop calling .device when staying on CPU. NumPy’s array model doesn’t include device state (NumPy docs), while PyTorch, CuPy, and JAX do (PyTorch, CuPy, JAX). Apply one of the conversion snippets above and the error clears.
Keyword Variant: Fixing The “Numpy Ndarray Has No Attribute Device” Error By Library
This short recap ties the main remedies to each stack and ensures the exact topic is easy to scan during a quick fix:
- PyTorch path — make tensors with
torch.from_numpyortorch.tensor, then move with.to("cuda"); readtensor.deviceas documented in the PyTorch tensor attribute pages. - CuPy path — convert with
cp.asarray, usearr.devicefor logs, convert back withcp.asnumpy; CuPy docs describe device behavior. - JAX path — ensure JAX arrays via
jnp.asarrayand queryArray.deviceor place withto_device; the JAX API pages outline both. - CPU-only path — keep plain NumPy and remove calls to
.device. Useshape,dtype, andndimfrom the NumPy reference.
Use these patterns and the exact error string—AttributeError: ‘NumPy NdArray’ Object Has No Attribute ‘Device’—won’t show up again in your logs.
