Repository: pytorch/ignite

Issue:
DCGAN Example pickle exception
First of all i'm sorry for my poor English
I just tried to run dcgan example and faced with following exception:

```
Traceback (most recent call last):
  File "C:/Users/Daiver/Documents/Depth/ignite_examples/dcgan_orig.py", line 438, in <module>
    alpha=0.98, output_dir=output_dir)
  File "C:/Users/Daiver/Documents/Depth/ignite_examples/dcgan_orig.py", line 417, in main
    trainer.run(loader, epochs)
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\ignite\engine\engine.py", line 326, in run
    self._handle_exception(e)
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\ignite\engine\engine.py", line 289, in _handle_exception
    self._fire_event(Events.EXCEPTION_RAISED, e)
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\ignite\engine\engine.py", line 226, in _fire_event
    func(self, *(event_args + args), **kwargs)
  File "C:/Users/Daiver/Documents/Depth/ignite_examples/dcgan_orig.py", line 414, in handle_exception
    raise e
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\ignite\engine\engine.py", line 313, in run
    hours, mins, secs = self._run_once_on_dataset()
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\ignite\engine\engine.py", line 280, in _run_once_on_dataset
    self._handle_exception(e)
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\ignite\engine\engine.py", line 289, in _handle_exception
    self._fire_event(Events.EXCEPTION_RAISED, e)
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\ignite\engine\engine.py", line 226, in _fire_event
    func(self, *(event_args + args), **kwargs)
  File "C:/Users/Daiver/Documents/Depth/ignite_examples/dcgan_orig.py", line 414, in handle_exception
    raise e
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\ignite\engine\engine.py", line 268, in _run_once_on_dataset
    for batch in self.state.dataloader:
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 819, in __iter__
    return _DataLoaderIter(self)
  File "C:\Users\Daiver\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 560, in __init__
    w.start()
  File "C:\Users\Daiver\Anaconda3\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Users\Daiver\Anaconda3\lib\multiprocessing\context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Users\Daiver\Anaconda3\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)
  File "C:\Users\Daiver\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Users\Daiver\Anaconda3\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
AttributeError: Can't pickle local object 'check_dataset.<locals>.<lambda>'

Process finished with exit code 1
```
I'm pretty sure this is due this line ` to_rgb = transforms.Lambda(lambda img: img.convert('RGB'))`. (i commented this and example failed with channels mismatch error)
Unfortunately i'm not very good with Python/Torch/Ignite so i have no idea why this example attempts to pickle lambda.

This is exactly what i run: https://gist.github.com/Daiver/cfaa55410d3d1f9082197e4113bf7328 (just dcgan example with hardcoded options)

My Python/packages versions
```
torch version: 1.0.0
torchvision version: 0.2.1
ignite version:  0.1.2
Python version: 3.6.5 |Anaconda, Inc.| (default, Mar 29 2018, 13:32:41) [MSC v.1900 64 bit (AMD64)] (Windows)
```

Please, tell me if i can provide more data.



Discussion:
@Daiver thanks for the feedback! Maybe the problem you have is related to your OS (Windows).
You can try to replace the lambda by defining a function:
```python

def to_rgb(img):
    return img.convert('RGB')

def check_dataset(dataset, dataroot):
    # to_rgb = transforms.Lambda(lambda img: img.convert('RGB'))
    # Probably transforms.Lambda is not necessary
    ...
    dataset = dset.MNIST(root=dataroot, download=True, transform=transforms.Compose([to_rgb,
                                                                                         resize,
                                                                                         to_tensor, normalize])
```

Feel free to ask more if need more support on this.
@vfdev-5 thank you for answer! 
Replacing lambda by function helps me, at least training starts. Should i close the issue?
@Daiver glad that it helps ! 

Let's leave it open. I think we need to modify DCGAN example such that we can run it on any platform: Win, LInux, OSX.

And if you would like to contribute, PRs are welcome :) 
See [contributing](https://github.com/pytorch/ignite/blob/master/CONTRIBUTING.md) for more info.
@vfdev-5 looking further into this issue, there might be three solutions below.

* num_workers=0 seems to solve the issue, but will result in a slower process
* align dcgan example closer to the pytorch example, not forcing Discriminator input_channels as 3, but dependent on the dataset. In the official pytorch example
* setup to_rgb as you have shown above.

thoughts?
@anmolsjoshi the 2nd choice looks good. However I would like to ensure that this really solves the problem...
@vfdev-5 just wanted to clarify, aligning it closer to the Pytorch example would also remove to_rgb that is used in MNIST. I'll try tonight and report back.
@anmolsjoshi yes, you can introduce a parameter as `nc`:

https://github.com/pytorch/examples/blob/0d3fe14a1c5a00795e3671ea3473caef6f0da72d/dcgan/main.py#L82-L89

> I'll try tonight and report back.

Sounds good

Resolving patch:
diff --git a/examples/gan/dcgan.py b/examples/gan/dcgan.py
--- a/examples/gan/dcgan.py
+++ b/examples/gan/dcgan.py
@@ -62,7 +62,7 @@ class Generator(Net):
         nf (int): Number of filters in the second-to-last deconv layer
     """
 
-    def __init__(self, z_dim, nf):
+    def __init__(self, z_dim, nf, nc):
         super(Generator, self).__init__()
 
         self.net = nn.Sequential(
@@ -88,7 +88,7 @@ def __init__(self, z_dim, nf):
             nn.ReLU(inplace=True),
 
             # state size. (nf) x 32 x 32
-            nn.ConvTranspose2d(in_channels=nf, out_channels=3, kernel_size=4, stride=2, padding=1, bias=False),
+            nn.ConvTranspose2d(in_channels=nf, out_channels=nc, kernel_size=4, stride=2, padding=1, bias=False),
             nn.Tanh()
 
             # state size. (nc) x 64 x 64
@@ -107,13 +107,13 @@ class Discriminator(Net):
         nf (int): Number of filters in the first conv layer.
     """
 
-    def __init__(self, nf):
+    def __init__(self, nc, nf):
         super(Discriminator, self).__init__()
 
         self.net = nn.Sequential(
 
             # input is (nc) x 64 x 64
-            nn.Conv2d(in_channels=3, out_channels=nf, kernel_size=4, stride=2, padding=1, bias=False),
+            nn.Conv2d(in_channels=nc, out_channels=nf, kernel_size=4, stride=2, padding=1, bias=False),
             nn.LeakyReLU(0.2, inplace=True),
 
             # state size. (nf) x 32 x 32
@@ -166,7 +166,6 @@ def check_dataset(dataset, dataroot):
         dataset (data.Dataset): torchvision Dataset object
 
     """
-    to_rgb = transforms.Lambda(lambda img: img.convert('RGB'))
     resize = transforms.Resize(64)
     crop = transforms.CenterCrop(64)
     to_tensor = transforms.ToTensor()
@@ -177,31 +176,35 @@ def check_dataset(dataset, dataroot):
                                                                                 crop,
                                                                                 to_tensor,
                                                                                 normalize]))
+        nc = 3
 
     elif dataset == 'lsun':
         dataset = dset.LSUN(root=dataroot, classes=['bedroom_train'], transform=transforms.Compose([resize,
                                                                                                     crop,
                                                                                                     to_tensor,
                                                                                                     normalize]))
+        nc = 3
 
     elif dataset == 'cifar10':
         dataset = dset.CIFAR10(root=dataroot, download=True, transform=transforms.Compose([resize,
                                                                                            to_tensor,
                                                                                            normalize]))
+        nc = 3
 
     elif dataset == 'mnist':
-        dataset = dset.MNIST(root=dataroot, download=True, transform=transforms.Compose([to_rgb,
-                                                                                         resize,
+        dataset = dset.MNIST(root=dataroot, download=True, transform=transforms.Compose([resize,
                                                                                          to_tensor,
                                                                                          normalize]))
+        nc = 1
 
     elif dataset == 'fake':
         dataset = dset.FakeData(size=256, image_size=(3, 64, 64), transform=to_tensor)
+        nc = 3
 
     else:
         raise RuntimeError("Invalid dataset name: {}".format(dataset))
 
-    return dataset
+    return dataset, nc
 
 
 def main(dataset, dataroot,
@@ -216,9 +219,13 @@ def main(dataset, dataroot,
     # seed
     check_manual_seed(seed)
 
+    # data
+    dataset, num_channels = check_dataset(dataset, dataroot)
+    loader = data.DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=n_workers, drop_last=True)
+
     # netowrks
-    netG = Generator(z_dim, g_filters).to(device)
-    netD = Discriminator(d_filters).to(device)
+    netG = Generator(z_dim, g_filters, num_channels).to(device)
+    netD = Discriminator(num_channels, d_filters).to(device)
 
     # criterion
     bce = nn.BCELoss()
@@ -227,10 +234,6 @@ def main(dataset, dataroot,
     optimizerG = optim.Adam(netG.parameters(), lr=learning_rate, betas=(beta_1, 0.999))
     optimizerD = optim.Adam(netD.parameters(), lr=learning_rate, betas=(beta_1, 0.999))
 
-    # data
-    dataset = check_dataset(dataset, dataroot)
-    loader = data.DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=n_workers, drop_last=True)
-
     # load pre-trained models
     if saved_G:
         netG.load_state_dict(torch.load(saved_G))


Test patch: