Can't convert cuda:0 device type tensor to numpy When trying to enumerate through torch.utils.data.DataLoader object

weirenorweiren: <p>I tried to run a <a href="https://github.com/christianversloot/machine-learning-articles/blob/main/how-to-create-a-neural-network-for-regression-with-pytorch.md#creating-a-mlp-regression-model-with-pytorch" rel="noopener nofollow ugc">multilayer perceptron (MLP) regression model written in PyTorch</a> through GPU in Google Colab. However, I encountered a bunch of errors with different approaches. Below is the code that works fine for CPU.</p>
<pre><code class="lang-auto">import torch
from torch import nn
from torch.utils.data import DataLoader
# from sklearn.datasets import load_boston
from sklearn.preprocessing import StandardScaler

import pandas as pd
import numpy as np


class BostonDataset(torch.utils.data.Dataset):
  '''
  Prepare the Boston dataset for regression
  '''
  def __init__(self, X, y, scale_data=True):
    if not torch.is_tensor(X) and not torch.is_tensor(y):
      # Apply scaling if necessary
      if scale_data:
          X = StandardScaler().fit_transform(X)
      self.X = torch.from_numpy(X)
      self.y = torch.from_numpy(y)

  def __len__(self):
      return len(self.X)

  def __getitem__(self, i):
      return self.X[i], self.y[i]


class MLP(nn.Module):
  '''
    Multilayer Perceptron for regression.
  '''
  def __init__(self):
    super().__init__()
    self.layers = nn.Sequential(
      nn.Linear(13, 64),
      nn.ReLU(),
      nn.Linear(64, 32),
      nn.ReLU(),
      nn.Linear(32, 1)
    )

  def forward(self, x):
    '''
      Forward pass
    '''
    return self.layers(x)


if __name__ == '__main__':

  # Set fixed random number seed
  torch.manual_seed(42)

  # Load Boston dataset
  # X, y = load_boston(return_X_y=True)
  data_url = "http://lib.stat.cmu.edu/datasets/boston"
  raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
  X = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
  y = raw_df.values[1::2, 2]

  # Prepare Boston dataset
  dataset = BostonDataset(X, y)
  trainloader = torch.utils.data.DataLoader(dataset, batch_size=10, shuffle=True, num_workers=1)

  # Initialize the MLP
  mlp = MLP()

  # Define the loss function and optimizer
  loss_function = nn.L1Loss()
  optimizer = torch.optim.Adam(mlp.parameters(), lr=1e-4)

  # Run the training loop
  for epoch in range(0, 5): # 5 epochs at maximum

    # Print epoch
    print(f'Starting epoch {epoch+1}')

    # Set current loss value
    current_loss = 0.0

    # Iterate over the DataLoader for training data
    for i, data in enumerate(trainloader):

      # Get and prepare inputs
      inputs, targets = data
      inputs, targets = inputs.float(), targets.float()
      targets = targets.reshape((targets.shape[0], 1))

      # Zero the gradients
      optimizer.zero_grad()

      # Perform forward pass
      outputs = mlp(inputs)

      # Compute loss
      loss = loss_function(outputs, targets)

      # Perform backward pass
      loss.backward()

      # Perform optimization
      optimizer.step()

      # Print statistics
      current_loss += loss.item()
      if i % 10 == 0:
          print('Loss after mini-batch %5d: %.3f' %
                (i + 1, current_loss / 500))
          current_loss = 0.0

  # Process is complete.
  print('Training process has finished.')
</code></pre>
<p><strong>My questions are as follows:</strong></p>
<ol>
<li><strong>What’s wrong with the current approach shown below and how to fix it?</strong></li>
<li><strong>Are there any general rules for running a model on GPU?</strong></li>
</ol>
<ul>
<li><strong>Should I specify the device globally at the very beginning? If so, why did the initial approach not work?</strong></li>
<li><strong>Should I specfiy the device for each new-defined tensor &amp; model and loaded data?</strong></li>
</ul>
<p>I started with setting the default device to GPU at the very beginning:</p>
<pre><code class="lang-auto">device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
torch.set_default_device(device)
</code></pre>
<p>but it has the following error:</p>
<pre><code class="lang-auto">---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
&lt;ipython-input-18-8054e92f6fef&gt; in &lt;cell line: 59&gt;()
     94 
     95     # Iterate over the DataLoader for training data
---&gt; 96     for i, data in enumerate(trainloader):
     97 
     98       # Get and prepare inputs

8 frames
/usr/local/lib/python3.10/dist-packages/torch/utils/_device.py in __torch_function__(self, func, types, args, kwargs)
     75         if func in _device_constructors() and kwargs.get('device') is None:
     76             kwargs['device'] = self.device
---&gt; 77         return func(*args, **kwargs)
     78 
     79 # NB: This is directly called from C++ in torch/csrc/Device.cpp

RuntimeError: Expected a 'cuda' device type for generator but found 'cpu'
</code></pre>
<p>Then I tried to specify the device for model and data one by one because I realized that variable <code>dataset</code> is not on GPU even if I set the default device to GPU. Below are my modifications:</p>
<pre><code class="lang-auto">device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
...
self.X = torch.from_numpy(X).to(device)
self.y = torch.from_numpy(y).to(device)
...
self.layers = nn.Sequential(nn.Linear(13, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1)).to(device)
</code></pre>
<p>but it has the following error:</p>
<pre><code class="lang-auto">---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
&lt;ipython-input-4-be16076aba77&gt; in &lt;cell line: 55&gt;()
     86 
     87     # Iterate over the DataLoader for training data
---&gt; 88     for i, data in enumerate(trainloader):
     89 
     90       # Get and prepare inputs

8 frames
/usr/local/lib/python3.10/dist-packages/torch/utils/_device.py in __torch_function__(self, func, types, args, kwargs)
     75         if func in _device_constructors() and kwargs.get('device') is None:
     76             kwargs['device'] = self.device
---&gt; 77         return func(*args, **kwargs)
     78 
     79 # NB: This is directly called from C++ in torch/csrc/Device.cpp

RuntimeError: Expected a 'cuda' device type for generator but found 'cpu'
</code></pre>
<p>I did some search and made the following modification:</p>
<pre><code class="lang-auto">trainloader = torch.utils.data.DataLoader(dataset, batch_size=10, shuffle=True, num_workers=1, generator=torch.Generator(device=device))
</code></pre>
<p>but it has the following error:</p>
<pre><code class="lang-auto">---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
&lt;ipython-input-9-24d5f71c82a6&gt; in &lt;cell line: 55&gt;()
     86 
     87     # Iterate over the DataLoader for training data
---&gt; 88     for i, data in enumerate(trainloader):
     89 
     90       # Get and prepare inputs

8 frames
/usr/local/lib/python3.10/dist-packages/torch/utils/_device.py in __torch_function__(self, func, types, args, kwargs)
     75         if func in _device_constructors() and kwargs.get('device') is None:
     76             kwargs['device'] = self.device
---&gt; 77         return func(*args, **kwargs)
     78 
     79 # NB: This is directly called from C++ in torch/csrc/Device.cpp

TypeError: can't convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.
</code></pre>
<p>I searched a lot but people have this problem when they try to plot something. I don’t know why I have the same problem when trying to enumerate through <code>torch.utils.data.DataLoader</code> object. Nevertheless I tried one final thing (on top of the original CPU version) that only applies <code>.to(device)</code> to data in the training loop and hoped at least there is no error during the enumeration:</p>
<pre><code class="lang-auto">device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
...
self.layers = nn.Sequential(nn.Linear(13, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1)).to(device)
...
inputs, targets = inputs.float().to(device), targets.float().to(device)
</code></pre>
<p>But it gives a similar error:</p>
<pre><code class="lang-auto">---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
&lt;ipython-input-22-cf2797cc4e83&gt; in &lt;cell line: 55&gt;()
     86 
     87     # Iterate over the DataLoader for training data
---&gt; 88     for i, data in enumerate(trainloader):
     89 
     90       # Get and prepare inputs

8 frames
/usr/local/lib/python3.10/dist-packages/torch/utils/_device.py in __torch_function__(self, func, types, args, kwargs)
     75         if func in _device_constructors() and kwargs.get('device') is None:
     76             kwargs['device'] = self.device
---&gt; 77         return func(*args, **kwargs)
     78 
     79 # NB: This is directly called from C++ in torch/csrc/Device.cpp

RuntimeError: Expected a 'cuda' device type for generator but found 'cpu'
</code></pre>
<p>I have been trying for hours and really appreciate any help on this problem!</p>

ptrblck: <p>Don’t set the device globally and move each tensor to the corresponding device if needed.</p>

weirenorweiren: <p>Thanks for the reply! I did try to move each tensor or layer to the corresponding device as below:</p>
<pre><code class="lang-auto">device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
...
self.X = torch.from_numpy(X).to(device)
self.y = torch.from_numpy(y).to(device)
...
self.layers = nn.Sequential(nn.Linear(13, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1)).to(device)
</code></pre>
<p>but there is an error when looping through the DataLoader:</p>
<pre><code class="lang-auto">---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
&lt;ipython-input-4-be16076aba77&gt; in &lt;cell line: 55&gt;()
     86 
     87     # Iterate over the DataLoader for training data
---&gt; 88     for i, data in enumerate(trainloader):
     89 
     90       # Get and prepare inputs

8 frames
/usr/local/lib/python3.10/dist-packages/torch/utils/_device.py in __torch_function__(self, func, types, args, kwargs)
     75         if func in _device_constructors() and kwargs.get('device') is None:
     76             kwargs['device'] = self.device
---&gt; 77         return func(*args, **kwargs)
     78 
     79 # NB: This is directly called from C++ in torch/csrc/Device.cpp

RuntimeError: Expected a 'cuda' device type for generator but found 'cpu'
</code></pre>
<p>Do you have any suggestions?</p>

ptrblck: <p>Could you post a minimal and executable code snippet showing this error without setting the device globally, please?</p>

weirenorweiren: <p>I am surprising to find my last approach works after a few days without any changes. <img src="https://discuss.pytorch.org/images/emoji/apple/hushed.png?v=12" title=":hushed:" class="emoji" alt=":hushed:" loading="lazy" width="20" height="20"></p>
<p>Here are some final questions for a wrap-up:</p>
<ol>
<li>
<p>What’s bad about <code>torch.set_default_device(device)</code>? Or under what circumstance you would recommend <code>torch.set_default_device(device)</code>?</p>
</li>
<li>
<p>Are there any differences to set the device inside and outside the model class?</p>
</li>
</ol>
<ul>
<li>For example, both <code>self.layers = nn.Sequential(nn.Linear(13, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1)).to(device) </code> and <code>mlp = MLP().to(device)</code> work. Which would be preferred?</li>
<li>I also tried <code>self.layers(x).to(device)</code> without <code>self.layers = nn.Sequential(nn.Linear(13, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1)).to(device)</code> and it has this error <code>RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cpu and cuda:0! (when checking argument for argument mat1 in method wrapper_CUDA_addmm)</code>. I am curious why is it?</li>
</ul>
<ol start="3">
<li>Regarding the data, should we always put <code>trainloader = torch.utils.data.DataLoader(dataset, batch_size=10, shuffle=True, num_workers=1)</code> on CPU and transfer them onto GPU as <code>inputs, targets = inputs.float().to(device), targets.float().to(device)</code> in the training loop? Or is there a way to put <code>trainloader</code> on GPU at the defining stage so I could avoid repeated transfer process in each epoch?</li>
</ol>
<p>Please see below for the full code:</p>
<pre><code class="lang-auto">import torch
from torch import nn
from torch.utils.data import DataLoader
# from sklearn.datasets import load_boston
from sklearn.preprocessing import StandardScaler

import pandas as pd
import numpy as np


device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# torch.set_default_device(device) # https://pytorch.org/docs/stable/generated/torch.set_default_device.html


class BostonDataset(torch.utils.data.Dataset):
  '''
  Prepare the Boston dataset for regression
  '''
  def __init__(self, X, y, scale_data=True):
    if not torch.is_tensor(X) and not torch.is_tensor(y):
      # Apply scaling if necessary
      if scale_data:
          X = StandardScaler().fit_transform(X)
      self.X = torch.from_numpy(X)
      self.y = torch.from_numpy(y)

  def __len__(self):
      return len(self.X)

  def __getitem__(self, i):
      return self.X[i], self.y[i]


class MLP(nn.Module):
  '''
    Multilayer Perceptron for regression.
  '''
  def __init__(self):
    super().__init__()
    self.layers = nn.Sequential(
      nn.Linear(13, 64),
      nn.ReLU(),
      nn.Linear(64, 32),
      nn.ReLU(),
      nn.Linear(32, 1)
    )

  def forward(self, x):
    '''
      Forward pass
    '''
    return self.layers(x)


if __name__ == '__main__':

  # Set fixed random number seed
  torch.manual_seed(42)

  # Load Boston dataset
  # X, y = load_boston(return_X_y=True)
  data_url = "http://lib.stat.cmu.edu/datasets/boston"
  raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
  X = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
  y = raw_df.values[1::2, 2]

  # Prepare Boston dataset
  dataset = BostonDataset(X, y)
  trainloader = torch.utils.data.DataLoader(dataset, batch_size=10, shuffle=True, num_workers=1) # https://discuss.pytorch.org/t/when-to-set-pin-memory-to-true/19723

  # Initialize the MLP
  mlp = MLP().to(device)

  # Define the loss function and optimizer
  loss_function = nn.L1Loss()
  optimizer = torch.optim.Adam(mlp.parameters(), lr=1e-4)

  # Run the training loop
  for epoch in range(0, 5): # 5 epochs at maximum

    # Print epoch
    print(f'Starting epoch {epoch+1}')

    # Set current loss value
    current_loss = 0.0

    # Iterate over the DataLoader for training data
    for i, data in enumerate(trainloader):

      # Get and prepare inputs
      inputs, targets = data
      inputs, targets = inputs.float().to(device), targets.float().to(device)
      targets = targets.reshape((targets.shape[0], 1))

      # Zero the gradients
      optimizer.zero_grad()

      # Perform forward pass
      outputs = mlp(inputs)

      # Compute loss
      loss = loss_function(outputs, targets)

      # Perform backward pass
      loss.backward()

      # Perform optimization
      optimizer.step()

      # Print statistics
      current_loss += loss.item()
      if i % 10 == 0:
          print('Loss after mini-batch %5d: %.3f' %
                (i + 1, current_loss / 500))
          current_loss = 0.0

  # Process is complete.
  print('Training process has finished.')
</code></pre>

weirenorweiren: <p>Hi <a class="mention" href="/u/ptrblck">@ptrblck</a>, this is a kind reminder that I have uploaded my code with some final questions. Please take a look when you have time and thanks in advance!</p>

ptrblck: <p>Ah sorry, I missed the questions and just saw the update it’s working now.</p>
<ol>
<li>
<p>I personally would not use <code>torch.set_default_device</code> as I was seeing too many issues in the past. With that being said, I don’t know what its current status is and if things improved, but I just stick to explicitly transfer data if needed.</p>
</li>
<li>
<p>Call <code>.to()</code> on the model as it will then recursively call <code>to()</code> with the same arguments on all properly registered submodules, parameters, and buffers.<br>
In your <code>forward</code> pass example you are moving the output of <code>self.layers(x)</code> to <code>device</code>, which might be too late as I assume you want to move the actual <code>self.layers</code> to the <code>device</code>. Moving tensors onto the device in the <code>forward</code> pass is possible and sometimes used in e.g. model sharing approaches, but I would not use it for a single GPU use case, but move the model and input to the <code>device</code> before executing the forward pass.</p>
</li>
<li>
<p>Yes, this would be the common use case. You could also use the main thread to move the data to the GPU inside the <code>Dataset</code>, but should also profile your workload to see if this could even slow down your code.</p>
</li>
</ol>

weirenorweiren: <p>Hi <a class="mention" href="/u/ptrblck">@ptrblck</a>, thanks so much for your detailed explanations and they are super helpful!</p>
<p>Regarding the 3rd point, I tried to move the data to GPU for <code>dataset</code> but there is a bug that <code>RuntimeError: Caught RuntimeError in DataLoader worker process 0.</code>. Then I found that you answered a same problem one year ago! I quoted it below to supplement your answer and hope it could benefit others with similar type of questions.</p>
<aside class="quote no-group" data-username="ptrblck" data-post="2" data-topic="156842" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/ptrblck/48/1823_2.png" class="avatar"><a href="https://discuss.pytorch.org/t/dataset-location-runtimeerror-caught-runtimeerror-in-dataloader-worker-process-0/156842/2">Dataset location (RuntimeError: Caught RuntimeError in DataLoader worker process 0.)</a></div>
<blockquote>
<p>Yes, usually you would load the data on the CPU and push it to the GPU inside the training loop in order to overlap the data loading with the actual model training.<br>
However, if you want to load CUDATensors directly, check the <a href="https://pytorch.org/docs/stable/notes/multiprocessing.html#cuda-in-multiprocessing" rel="noopener nofollow ugc">CUDA multiprocessing docs </a> to avoid re-creating a CUDA context, which raises your error.</p>
</blockquote>
</aside>
<p>Thanks again for your prompt and patient response!</p>