DataLens

NumPy ↔ tensors; devices (CPU/GPU)

Move data between NumPy, pandas and PyTorch without surprises, understand shared memory, and put tensors and models on the right device.

Level: Foundations · 7 min read

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

The idea

Real projects mix libraries: data arrives as a pandas DataFrame, preprocessing happens in NumPy or scikit-learn, the model runs in PyTorch, and the results go back into a DataFrame or a Plotly chart. Two things make these hand-overs smooth:

  1. knowing when a conversion copies data and when it shares memory;
  2. knowing which device (CPU or GPU) a tensor lives on.

From NumPy to PyTorch

Your data usually starts life in NumPy or pandas, so the first step of most PyTorch projects is converting it. There are two ways, and the difference matters:

import numpy as np
import torch

arr = np.array([1.0, 2.0, 3.0])
shared = torch.from_numpy(arr)       # shares memory with arr
copied = torch.tensor(arr)           # an independent copy

arr[0] = 100.0
print(shared)
print(copied)
print(shared.dtype)
tensor([100.,   2.,   3.], dtype=torch.float64)
tensor([1., 2., 3.], dtype=torch.float64)
torch.float64
X = torch.from_numpy(np.random.default_rng(0).random((3, 2))).float()
print(X.dtype, X.shape)
torch.float32 torch.Size([3, 2])

From PyTorch to NumPy

And back again: after training, you'll often want predictions as a NumPy array, to chart them or put them in a DataFrame:

t = torch.tensor([0.25, 0.5, 0.75])
a = t.numpy()           # shares memory (CPU tensors only)
t[0] = 9.0
print(a)
[9.   0.5  0.75]

Two rules for .numpy():

From pandas

Most real data arrives as a DataFrame, like the churn table from the ML track. The usual route goes through NumPy:

import pandas as pd

df = pd.DataFrame({"tenure_months": [12, 33, 5], "orders_90d": [7, 4, 0], "churned": [0, 0, 1]})
X = torch.tensor(df[["tenure_months", "orders_90d"]].to_numpy(), dtype=torch.float32)
y = torch.tensor(df["churned"].to_numpy(), dtype=torch.float32)
print(X)
print(y)
tensor([[12.,  7.],
        [33.,  4.],
        [ 5.,  0.]])
tensor([0., 0., 1.])

The usual route is DataFrame → NumPy (.to_numpy()) → tensor, choosing dtype=torch.float32 explicitly.

Devices

A tensor lives on a device: the CPU, an NVIDIA GPU ("cuda"), or Apple Silicon's GPU ("mps"). Maths can only combine tensors on the same device.

device = (
    "cuda" if torch.cuda.is_available()
    else "mps" if torch.backends.mps.is_available()
    else "cpu"
)
print("using", device)

t = torch.ones(3)
print(t.device)
t = t.to(device)       # copies to the device (no-op if it's already there)
using mps
cpu

The printed device depends on your machine: cuda on an NVIDIA GPU machine, mps on a Mac with Apple Silicon, cpu otherwise.

The standard pattern: pick device once at the top, then move the model and every batch to it:

model = MyModel().to(device)
for X_batch, y_batch in loader:
    X_batch, y_batch = X_batch.to(device), y_batch.to(device)
    predictions = model(X_batch)

Forget one .to(device) and you get the famous error "Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu". Read it as a checklist: which tensor didn't get moved?

Key idea

Choose the device once; move the model and each batch to it; bring results back with .detach().cpu().numpy(). from_numpy and .numpy() share memory; torch.tensor(...) copies.

When does the GPU help?

GPUs are fast at big, parallel tensor maths: large matrix multiplications over big batches. For small tabular models (a few thousand rows, a small network), the time spent copying data to the GPU can exceed the time saved, and the CPU is perfectly fine. Measure before assuming.

Why this matters for clean code

Keep conversions at the edges of your code: convert once when data enters the model code, and once when results leave. A device variable defined in one place (often from a config file) lets the same script run on a laptop and on a GPU server without edits. Scattered .cuda() calls are the PyTorch version of hard-coded file paths.

Recap

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

Next: Autograd: how gradients work →