diff --git a/tutorials/generative/2d_ddpm/2d_ddpm_compare_schedulers.ipynb b/tutorials/generative/2d_ddpm/2d_ddpm_compare_schedulers.ipynb index 1beca029..f0ec09a7 100644 --- a/tutorials/generative/2d_ddpm/2d_ddpm_compare_schedulers.ipynb +++ b/tutorials/generative/2d_ddpm/2d_ddpm_compare_schedulers.ipynb @@ -444,7 +444,9 @@ "metadata": {}, "source": [ "### Model training\n", - "Here, we are training our model for 100 epochs (training time: ~40 minutes). It is necessary to train for a bit longer than other tutorials because the DDIM and PNDM schedules seem to require a model trained longer before they start producing good samples, when compared to DDPM." + "Here, we are training our model for 100 epochs (training time: ~40 minutes). It is necessary to train for a bit longer than other tutorials because the DDIM and PNDM schedules seem to require a model trained longer before they start producing good samples, when compared to DDPM.\n", + "\n", + "If you would like to skip the training and use a pre-trained model instead, set `use_pretrained=True`. This model was trained using the code in `tutorials/generative/distributed_training/ddpm_training_ddp.py`" ] }, { @@ -817,101 +819,106 @@ } ], "source": [ - "n_epochs = 100\n", - "val_interval = 10\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "for epoch in range(n_epochs):\n", - " model.train()\n", - " epoch_loss = 0\n", - " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader))\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, batch in progress_bar:\n", - " images = batch[\"image\"].to(device)\n", - " optimizer.zero_grad(set_to_none=True)\n", - "\n", - " # Randomly select the timesteps to be used for the minibacth\n", - " timesteps = torch.randint(0, ddpm_scheduler.num_train_timesteps, (images.shape[0],), device=device).long()\n", - "\n", - " # Add noise to the minibatch images with intensity defined by the scheduler and timesteps\n", - " noise = torch.randn_like(images).to(device)\n", - " noisy_image = ddpm_scheduler.add_noise(original_samples=images, noise=noise, timesteps=timesteps)\n", - "\n", - " # In this example, we are parametrising our DDPM to learn the added noise (epsilon).\n", - " # For this reason, we are using our network to predict the added noise and then using L1 loss to predict\n", - " # its performance.\n", - " noise_pred = model(x=noisy_image, timesteps=timesteps)\n", - " loss = F.l1_loss(noise_pred.float(), noise.float())\n", - "\n", - " loss.backward()\n", - " optimizer.step()\n", - " epoch_loss += loss.item()\n", - "\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"loss\": epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "use_pretrained = False\n", "\n", - " if (epoch + 1) % val_interval == 0:\n", - " model.eval()\n", - " val_epoch_loss = 0\n", - " progress_bar = tqdm(enumerate(val_loader), total=len(train_loader))\n", - " progress_bar.set_description(f\"Epoch {epoch} - Validation set\")\n", + "if use_pretrained:\n", + " model = torch.hub.load(\"marksgraham/pretrained_generative_models\", model='ddpm_2d', verbose=True).to(device)\n", + "else:\n", + " n_epochs = 100\n", + " val_interval = 10\n", + " epoch_loss_list = []\n", + " val_epoch_loss_list = []\n", + " for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader))\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", " for step, batch in progress_bar:\n", " images = batch[\"image\"].to(device)\n", + " optimizer.zero_grad(set_to_none=True)\n", + "\n", + " # Randomly select the timesteps to be used for the minibacth\n", " timesteps = torch.randint(0, ddpm_scheduler.num_train_timesteps, (images.shape[0],), device=device).long()\n", + "\n", + " # Add noise to the minibatch images with intensity defined by the scheduler and timesteps\n", " noise = torch.randn_like(images).to(device)\n", - " with torch.no_grad():\n", - " noisy_image = ddpm_scheduler.add_noise(original_samples=images, noise=noise, timesteps=timesteps)\n", - " noise_pred = model(x=noisy_image, timesteps=timesteps)\n", - " val_loss = F.l1_loss(noise_pred.float(), noise.float())\n", + " noisy_image = ddpm_scheduler.add_noise(original_samples=images, noise=noise, timesteps=timesteps)\n", + "\n", + " # In this example, we are parametrising our DDPM to learn the added noise (epsilon).\n", + " # For this reason, we are using our network to predict the added noise and then using L1 loss to predict\n", + " # its performance.\n", + " noise_pred = model(x=noisy_image, timesteps=timesteps)\n", + " loss = F.l1_loss(noise_pred.float(), noise.float())\n", + "\n", + " loss.backward()\n", + " optimizer.step()\n", + " epoch_loss += loss.item()\n", "\n", - " val_epoch_loss += val_loss.item()\n", " progress_bar.set_postfix(\n", " {\n", - " \"val_loss\": val_epoch_loss / (step + 1),\n", + " \"loss\": epoch_loss / (step + 1),\n", " }\n", " )\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", "\n", - " # Sampling image during training\n", - " noise = torch.randn((1, 1, 64, 64))\n", - " noise = noise.to(device)\n", - " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddpm_scheduler)\n", - " plt.figure(figsize=(8, 4))\n", - " plt.subplot(3, len(sampling_steps), 1)\n", - " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", - " plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", - " plt.ylabel(\"DDPM\")\n", - " plt.title(\"1000 steps\")\n", - " # DDIM\n", - " for idx, reduced_sampling_steps in enumerate(sampling_steps):\n", - " ddim_scheduler.set_timesteps(reduced_sampling_steps)\n", - " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddim_scheduler)\n", - " plt.subplot(3, len(sampling_steps), len(sampling_steps) + idx + 1)\n", - " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", - " plt.ylabel(\"DDIM\")\n", - " if idx == 0:\n", - " plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", - " else:\n", - " plt.axis(\"off\")\n", - " plt.title(f\"{reduced_sampling_steps} steps\")\n", - " # PNDM\n", - " for idx, reduced_sampling_steps in enumerate(sampling_steps):\n", - " pndm_scheduler.set_timesteps(reduced_sampling_steps)\n", - " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=pndm_scheduler)\n", - " plt.subplot(3, len(sampling_steps), len(sampling_steps) * 2 + idx + 1)\n", + " if (epoch + 1) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + " progress_bar = tqdm(enumerate(val_loader), total=len(train_loader))\n", + " progress_bar.set_description(f\"Epoch {epoch} - Validation set\")\n", + " for step, batch in progress_bar:\n", + " images = batch[\"image\"].to(device)\n", + " timesteps = torch.randint(0, ddpm_scheduler.num_train_timesteps, (images.shape[0],), device=device).long()\n", + " noise = torch.randn_like(images).to(device)\n", + " with torch.no_grad():\n", + " noisy_image = ddpm_scheduler.add_noise(original_samples=images, noise=noise, timesteps=timesteps)\n", + " noise_pred = model(x=noisy_image, timesteps=timesteps)\n", + " val_loss = F.l1_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"val_loss\": val_epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + " # Sampling image during training\n", + " noise = torch.randn((1, 1, 64, 64))\n", + " noise = noise.to(device)\n", + " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddpm_scheduler)\n", + " plt.figure(figsize=(8, 4))\n", + " plt.subplot(3, len(sampling_steps), 1)\n", " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", - " plt.ylabel(\"PNDM\")\n", - " if idx == 0:\n", - " plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", - " else:\n", - " plt.axis(\"off\")\n", - " plt.title(f\"{reduced_sampling_steps} steps\")\n", - " plt.suptitle(f\"Epoch {epoch+1}\")\n", - " plt.show()" + " plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", + " plt.ylabel(\"DDPM\")\n", + " plt.title(\"1000 steps\")\n", + " # DDIM\n", + " for idx, reduced_sampling_steps in enumerate(sampling_steps):\n", + " ddim_scheduler.set_timesteps(reduced_sampling_steps)\n", + " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddim_scheduler)\n", + " plt.subplot(3, len(sampling_steps), len(sampling_steps) + idx + 1)\n", + " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + " plt.ylabel(\"DDIM\")\n", + " if idx == 0:\n", + " plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", + " else:\n", + " plt.axis(\"off\")\n", + " plt.title(f\"{reduced_sampling_steps} steps\")\n", + " # PNDM\n", + " for idx, reduced_sampling_steps in enumerate(sampling_steps):\n", + " pndm_scheduler.set_timesteps(reduced_sampling_steps)\n", + " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=pndm_scheduler)\n", + " plt.subplot(3, len(sampling_steps), len(sampling_steps) * 2 + idx + 1)\n", + " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + " plt.ylabel(\"PNDM\")\n", + " if idx == 0:\n", + " plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", + " else:\n", + " plt.axis(\"off\")\n", + " plt.title(f\"{reduced_sampling_steps} steps\")\n", + " plt.suptitle(f\"Epoch {epoch+1}\")\n", + " plt.show()" ] }, { @@ -950,21 +957,100 @@ } ], "source": [ - "plt.style.use(\"seaborn\")\n", - "plt.title(\"Learning Curves\", fontsize=20)\n", - "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - "plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - ")\n", - "plt.yticks(fontsize=12)\n", - "plt.xticks(fontsize=12)\n", - "plt.xlabel(\"Epochs\", fontsize=16)\n", - "plt.ylabel(\"Loss\", fontsize=16)\n", - "plt.legend(prop={\"size\": 14})\n", + "if not use_pretrained:\n", + " plt.style.use(\"seaborn\")\n", + " plt.title(\"Learning Curves\", fontsize=20)\n", + " plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + " plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + " plt.yticks(fontsize=12)\n", + " plt.xticks(fontsize=12)\n", + " plt.xlabel(\"Epochs\", fontsize=16)\n", + " plt.ylabel(\"Loss\", fontsize=16)\n", + " plt.legend(prop={\"size\": 14})\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e1e10277-c0d8-43a4-8e5b-8ba58af7acfe", + "metadata": {}, + "source": [ + "### Compare samples from trained model" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "518910a7-ec0b-4885-811b-2e47641195ba", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Using cache found in /home/mark/.cache/torch/hub/marksgraham_pretrained_generative_models_main\n", + "100%|█████████████████████████████████████████████████████████████████████████| 1000/1000 [00:16<00:00, 59.92it/s]\n", + "100%|█████████████████████████████████████████████████████████████████████████| 1000/1000 [00:16<00:00, 60.22it/s]\n", + "100%|███████████████████████████████████████████████████████████████████████████| 500/500 [00:08<00:00, 60.17it/s]\n", + "100%|███████████████████████████████████████████████████████████████████████████| 200/200 [00:03<00:00, 60.41it/s]\n", + "100%|█████████████████████████████████████████████████████████████████████████████| 50/50 [00:00<00:00, 59.84it/s]\n", + "100%|█████████████████████████████████████████████████████████████████████████| 1000/1000 [00:17<00:00, 57.54it/s]\n", + "100%|███████████████████████████████████████████████████████████████████████████| 500/500 [00:09<00:00, 55.42it/s]\n", + "100%|███████████████████████████████████████████████████████████████████████████| 200/200 [00:03<00:00, 55.41it/s]\n", + "100%|█████████████████████████████████████████████████████████████████████████████| 50/50 [00:00<00:00, 55.68it/s]\n" + ] + }, + { + "data": { + "image/png": "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8SJVOmmSxzQaDUWG6vW68ubJ3/inb9eJlfRydeuB25+wxMzCwsLCwmJt4oCTM3qdJiYmEAwGcdppp6FcLqvSGsyhWlxcxO9+9zssLi7C5/NhcHAQW7ZsUSFQkqlGo6EWM280GgiFQujr60MsFnOROM4QLRaLapZoMBhEOBwGYJ5tKSG36zluuierl1Ic+wvdTmSwWI2f/exnOOuss4y//eIXv8AznvEM17af//zn+Md//Ef87ne/Q19fH172spfh6quvRjKZdO1XrVbx/ve/H7fccguWlpZw4okn4qMf/Sie+9zn7pd+X3311Tj++OPx4he/eL+0Z3Fg8etf/xo333wztm3bhl27dmFwcBDPeMYz8NGPfhRHH330qv23b9+Ov//7v8d//ud/IhwO4/zzz8dnPvMZDA0NufZrtVr41Kc+heuvvx5TU1M4+uijcdVVV+GVr3zlfun3l770JcTjcWzdunW/tGdx8OHx0pH7gp///Of4yU9+gne+853IZDKPub2DBQecnEmUy2WMjY2hVCqhUqkob1qhUMD999+PQCCA4eFh1Ot1zM3NYXFxEaOjo8pL1Ww2kc/nUS6XEQqFEI1GUa/XsbCwgHA4rMKiJGlydigARe7kNglTEVqvyQftJiVYwrQ28I53vAOnnHKKa9uRRx7p+v773/8e55xzDo477jh85jOfwfj4OD71qU/hkUcewR133OHad+vWrbjtttvwzne+E0cddRRuuukm/M3f/A22bduG008//TH39+qrr8ZLX/pSS87+l+CTn/wk7rvvPlx00UU48cQTMT09jeuuuw4nnXQS/uu//gtPetKT1L7j4+M444wzkE6ncfXVV6NQKOBTn/oUHnjgAfzqV79SRicAvPe978UnPvEJvOlNb8Ipp5yCH/zgB3jVq14Fn8+HV7ziFY+531/60pewbt06S86eANjfOnJf8POf/xwf+tCHsHXr1v9V5AzOAUA2m3UArPpLJBLOySef7Bx77LHOEUcc4WzevNnZvHmzE4/HHQBOOp12UqmUk0gknFAo5ABw/H6/EwgEHL/f7/h8PtWWz+dzfe/1z9SW3+9X5+M59fMeLH/y+rPZ7IF4zGsW27ZtcwA4t956a8d9zzvvPGf9+vVOLpdT27761a86AJw777xTbfvlL3/pAHCuvfZata1cLjtHHHGE88xnPnO/9DuRSDgX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NjZt2oTdu3fjC1/4Ap761KeqKepPfepTEQgE8MlPfhK5XA6RSARnn302hoeHcf311+O1r30tTjrpJLziFa/A0NAQ9uzZgx/96Ef4q7/6KxcZi0aj+PGPf4yLL74Yp512Gu644w786Ec/wnve8x41Xbib/lh0j5e//OWIxWJ41rOeheHhYTz88MP4yle+gng8jk984hOufT/2sY/hWc96Fs4880y8+c1vxvj4OD796U/jec97Hp7//Oer/U477TRcdNFFuOqqqzA7O4sjjzwSN998M3bt2oWvf/3rbftzzz334G1vexsuuugiHH300Wg0GrjlllsU8SJOPvlk3H333fjMZz6DDRs24LDDDsNpp52GT3ziE9i2bRtOO+00vOlNb8Lxxx+vlkO7++67sbi46DrfwMAATj/9dLz+9a/HzMwMPvvZz+LII4/Em970pp76Y/HY8A//8A/493//d1xwwQVYXFzEN7/5Tdfvr3nNa9Tnx0NH6tixYwfOOeccvOxlL8Pxxx+PYDCIf/u3f8PMzIxrZYGTTz4Z119/PT760Y/iyCOPxPDwMM4++2xcccUV+Pd//3e84AUvwNatW3HyySejWCzigQcewG233YZdu3apUgjA3hym008/HW95y1tQrVbx2c9+FoODgyos2m1/LPY/Hg8dacLznvc8jI6O4q/+6q8wMjKC7du347rrrsP555+vJi2ROL33ve/FK17xCoRCIVxwwQU44ogj8NGPfhRXXXUVdu3ahRe/+MVIpVLYuXMn/u3f/g1vfvOb8a53vUuda8OGDfjkJz+JXbt24eijj8a//uu/4ve//z2+8pWvIBQKdd2f/Yb96ofrEaaChfzTXZQPPvig87znPc+Jx+NOJpNxXv3qVzvT09Or2mw2m87VV1/tHHrooU44HHZOOOEE55vf/Kbx/N22qeO2225znve856mCnps3b3YuvfRSZ2pqyrXfV7/6Vefwww93AoHAqhDntm3bnHPPPddJp9NONBp1jjjiCGfr1q3Ob37zG7XPxRdf7CQSCecvf/mL6ufIyIjzgQ98wFW0sdv+WHSHz33uc86pp57qDAwMOMFg0Fm/fr3zmte8xjNEfO+99zrPetaznGg06gwNDTmXXXaZqxQFUS6XnXe9613O6OioE4lEnFNOOcX58Y9/3LE/jz76qHPJJZc4RxxxhBONRp2BgQHn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j37USSQSzt69e12v33jjjU4gEFDRDEbOYrGYMzk5qbZ79NFHHQDOf/7P/7mv4zFaf22EjfTS61//eufUU0/tus0tt9zSFi1zHMfZv3+/EwgEnI9//OOu15944gknGAy6Xj/33HMdAM5nPvMZ9VqlUnFOP/10Z3x83KlWq76PZ720+nkpjpBKpRIAIBKJtL0XjUZd25RKJd/b+d2nlwYHB1EoFPDDH/7Q93lQP/zhD7G8vIy3vOUtmJ+fVz+BQAB79uzBww8/3PaZ97znPepvRj+q1SoeeuihNR+PUbvOPPNM3Hvvvbj66qvx+7//+7jxxhvxL//yL7AsCzfddJParhdHkiG/bHqJkagHH3wQxWKxr3NxHAff+ta3cOmll8JxHBdzF198MTKZDH72s5+5PvO2t70NqVRK/X/ZZZdh69atKgKzluMxWr1+/etf47rrrsNrXvMaXHnller1jbCRXorFYgiHw/jHf/xHLC0t9X3899xzD84++2wMDQ25OLzwwgvRaDTwz//8z67t3/CGN2D79u3q/zPOOAN79uxRHK71eIxWr42wkV4aHBzE5OQk/u3f/q3vY/z2t7+NZrOJyy+/3MXbxMQETjzxxLa2NhgM4tprr1X/h8NhXHvttZidncX/+3//b83H06+OeucsFosBACqVStt75XLZtU0sFvO9nd99euk//af/hJNOOgmve93rsGPHDlx99dV44IEHfJ3PM888AwA4//zzMTY25vr5wQ9+gNnZWdf2tm3j+OOPd7120kknAYBKva7leIz8affu3Xj961+Phx9+WBWF9uJIMuSXTS8dd9xx+C//5b/gy1/+MkZHR3HxxRfjr//6r5HJZHoe99zcHJaXl3HHHXe08fb2t78dANqYO/HEE13/W5aF3bt3K97WcjxGq9P09DQuueQSDAwM4N5773UN+98IG+mlSCSCT37yk7j//vuxZcsWnHPOOfjUpz6F6elpX+fwzDPP4IEHHmjj8MILLwTQm0NgxfaRw7Uej9H6aq020ksf+MAHkEwmccYZZ+DEE0/Eddddh0ceecTX8TzzzDNwHAcnnnhiG3O/+tWv2njbtm0bEomE6zW9rV3L8fSrTZ9Ko5e2bt0KAJiammp7b2pqCsPDw8or37p1Kx5++OG2UWj8LGuv+tmnl8bHx/H444/jwQcfxP3334/7778fd955J972trfha1/7Wtfz4SiPu+++GxMTE23vc7LHfrSW4zHyr507d6JaraJQKCCdTvfkSNb6bd26FYcOHfLcDkDPusDPfOYzuOqqq/Dd734XP/jBD/Cnf/qn+MQnPoF/+Zd/wY4dOzp+jrxdccUVrmiL1Ctf+cqu372ex2PUvzKZDF73utdheXkZP/7xj9tY2Qgb2Unvfe97cemll+I73/kOHnzwQXzoQx/CJz7xCfzoRz/Cb/zGb3T9bLPZxG//9m/jz/7szzzfZ0PYj9ZyPEbrr7XYSC+dfPLJePrpp/G9730PDzzwAL71rW/hc5/7HD784Q/jIx/5SNfPciTm/fff7+rMUMlkso8zW/vx9K0jkjztoW41Z47jOGNjYx1HIsk6nttvv91zJNLf/M3ftOW9/e7TjxqNhnPttdc6AJxnnnnGcRzHuffeez1rzli/8+CDD/bcL2vOnn76adfr999/vwPA+cY3vuH7eIzWrje+8Y1ONBpVI8uWl5e7jkS6+uqr1Wvve9/7PEdrfvzjH/c1WlPXI4884gBwPvjBD6rXkslkW81ZvV53UqmU85a3vKXnPllzdtNNN7lebzabztatW52LL764r+MxWrtKpZJz9tlnO/F43PnJT37ScbuNsJF+tHfvXicejztvfetb1WuveMUrPGvOTjnlFOc1r3lNz32y5syL2T179jgve9nL+joeoyOntdhIP6pUKs4ll1ziBAIBp1QqOY7jOJ/+9Kc9a84+9alPebafXjr33HOdYDDo5PN51+uf//znHQDOT3/6U9/Hs1466tOawMqInO9973t4/vnn1Wv/8A//gL179+JNb3qTeu31r389QqEQPve5z6nXHMfBF77wBWzfvh1nnnlm3/v00sLCgut/27ZV9IHhW4ZHl5eXXdtefPHFSKfTuPnmm9VMy1Jzc3Ntr91+++2u87n99tsRCoVwwQUX+D4eI//yugc///nPcd999+Giiy5Sc9sMDAzgwgsvxNe//nXkcjm17d133418Pu/i6LLLLkOj0cAdd9yhXqtUKrjzzjuxZ8+ejiM1ASCbzaJer7teO+2002Dbtuv+JhKJNt4CgQDe+MY34lvf+haefPJJX+fKUXLUvffei6mpKbzuda/r63iM1qZGo4E//MM/xE9/+lPcc889eM1rXtNx242wkbqKxaJKf1InnHACUqlUTw4B4PLLL8dPf/pTPPjgg23vLS8vtzH1ne98xxVt/td//Vc8+uijikO/x2O0/toIG+klvW0Lh8M45ZRT4DiOaj87tbV/8Ad/gEAggI985COe85Hq+67X6/jiF7+o/q9Wq/jiF7+IsbExvOpVr/J9POulTU1r3n777VheXsbhw4cBAH/3d3+HyclJACvTYrDw+M///M9xzz334LzzzsMNN9yAfD6PW265BaeddpqqmwGAHTt24L3vfS9uueUW1Go1vPrVr8Z3vvMd/PjHP8bf/M3fuEKbfvfppXe84x1YXFzE+eefjx07duDAgQP47Gc/i9NPP10NUT/99NMRCATwyU9+EplMBpFIBOeffz7Gx8fx+c9/Hn/8x3+M3/zN38Sb3/xmjI2N4eDBg/j+97+P1772tS5nLBqN4oEHHsCVV16JPXv24P7778f3v/99/Pmf/7kaLuzneIz86w//8A8Ri8Vw5plnYnx8HE899RTuuOMOxONx/NVf/ZVr249//OM488wzce655+Jd73oXJicn8ZnPfAYXXXQRfud3fkdtt2fPHrzpTW/CTTfdhNnZWezevRtf+9rXsH//fnzlK1/pejw/+tGP8J73vAdvetObcNJJJ6Fer+Puu+9Wjhf1qle9Cg899BD+x//4H9i2bRuOO+447NmzB3/1V3+Fhx9+GHv27ME73/lOnHLKKVhcXMTPfvYzPPTQQ1hcXHR93/DwMM466yy8/e1vx8zMDG699Vbs3r0b73znO/s6HqO16b/+1/+K++67D5deeikWFxfx9a9/3fX+FVdcof7eCBupa+/evbjgggtw+eWX45RTTkEwGMTf/u3fYmZmBm9+85vVdq961avw+c9/Hh/72Mewe/dujI+P4/zzz8f73/9+3Hffffi93/s9XHXVVXjVq16FQqGAJ554Avfeey/279+vpkIAVmqYzjrrLLz73e9GpVLBrbfeipGREZUW9Xs8RuuvjbCRXrroooswMTGB1772tdiyZQt+9atf4fbbb8cll1yiBi3RcfrgBz+IN7/5zQiFQrj00ktxwgkn4GMf+xhuuukm7N+/H294wxuQSqWwb98+/O3f/i3e9a534X3ve5/6rm3btuGTn/wk9u/fj5NOOgn/+3//bzz++OO44447EAqFfB/Pumld43B9ymvCQv7oIconn3zSueiii5x4PO4MDg46b33rW53p6em2fTYa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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "noise = torch.randn((1, 1, 64, 64))\n", + "noise = noise.to(device)\n", + "image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddpm_scheduler)\n", + "plt.figure(figsize=(8, 4))\n", + "plt.subplot(3, len(sampling_steps), 1)\n", + "plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", + "plt.ylabel(\"DDPM\")\n", + "plt.title(\"1000 steps\")\n", + "# DDIM\n", + "for idx, reduced_sampling_steps in enumerate(sampling_steps):\n", + " ddim_scheduler.set_timesteps(reduced_sampling_steps)\n", + " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddim_scheduler)\n", + " plt.subplot(3, len(sampling_steps), len(sampling_steps) + idx + 1)\n", + " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + " plt.ylabel(\"DDIM\")\n", + " if idx == 0:\n", + " plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", + " else:\n", + " plt.axis(\"off\")\n", + " plt.title(f\"{reduced_sampling_steps} steps\")\n", + "# PNDM\n", + "for idx, reduced_sampling_steps in enumerate(sampling_steps):\n", + " pndm_scheduler.set_timesteps(reduced_sampling_steps)\n", + " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=pndm_scheduler)\n", + " plt.subplot(3, len(sampling_steps), len(sampling_steps) * 2 + idx + 1)\n", + " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + " plt.ylabel(\"PNDM\")\n", + " if idx == 0:\n", + " plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)\n", + " else:\n", + " plt.axis(\"off\")\n", + " plt.title(f\"{reduced_sampling_steps} steps\")\n", "plt.show()" ] }, @@ -1002,7 +1088,7 @@ "formats": "ipynb,py:percent" }, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1016,7 +1102,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.8.13" } }, "nbformat": 4, diff --git a/tutorials/generative/2d_ddpm/2d_ddpm_compare_schedulers.py b/tutorials/generative/2d_ddpm/2d_ddpm_compare_schedulers.py index 80886358..cdef02df 100644 --- a/tutorials/generative/2d_ddpm/2d_ddpm_compare_schedulers.py +++ b/tutorials/generative/2d_ddpm/2d_ddpm_compare_schedulers.py @@ -8,7 +8,7 @@ # format_version: '1.3' # jupytext_version: 1.14.1 # kernelspec: -# display_name: Python 3 +# display_name: Python 3 (ipykernel) # language: python # name: python3 # --- @@ -204,124 +204,177 @@ # %% [markdown] # ### Model training # Here, we are training our model for 100 epochs (training time: ~40 minutes). It is necessary to train for a bit longer than other tutorials because the DDIM and PNDM schedules seem to require a model trained longer before they start producing good samples, when compared to DDPM. +# +# If you would like to skip the training and use a pre-trained model instead, set `use_pretrained=True`. This model was trained using the code in `tutorials/generative/distributed_training/ddpm_training_ddp.py` # %% -n_epochs = 100 -val_interval = 10 -epoch_loss_list = [] -val_epoch_loss_list = [] -for epoch in range(n_epochs): - model.train() - epoch_loss = 0 - progress_bar = tqdm(enumerate(train_loader), total=len(train_loader)) - progress_bar.set_description(f"Epoch {epoch}") - for step, batch in progress_bar: - images = batch["image"].to(device) - optimizer.zero_grad(set_to_none=True) - - # Randomly select the timesteps to be used for the minibacth - timesteps = torch.randint(0, ddpm_scheduler.num_train_timesteps, (images.shape[0],), device=device).long() - - # Add noise to the minibatch images with intensity defined by the scheduler and timesteps - noise = torch.randn_like(images).to(device) - noisy_image = ddpm_scheduler.add_noise(original_samples=images, noise=noise, timesteps=timesteps) - - # In this example, we are parametrising our DDPM to learn the added noise (epsilon). - # For this reason, we are using our network to predict the added noise and then using L1 loss to predict - # its performance. - noise_pred = model(x=noisy_image, timesteps=timesteps) - loss = F.l1_loss(noise_pred.float(), noise.float()) - - loss.backward() - optimizer.step() - epoch_loss += loss.item() - - progress_bar.set_postfix( - { - "loss": epoch_loss / (step + 1), - } - ) - epoch_loss_list.append(epoch_loss / (step + 1)) - - if (epoch + 1) % val_interval == 0: - model.eval() - val_epoch_loss = 0 - progress_bar = tqdm(enumerate(val_loader), total=len(train_loader)) - progress_bar.set_description(f"Epoch {epoch} - Validation set") +use_pretrained = False + +if use_pretrained: + model = torch.hub.load("marksgraham/pretrained_generative_models", model="ddpm_2d", verbose=True).to(device) +else: + n_epochs = 100 + val_interval = 10 + epoch_loss_list = [] + val_epoch_loss_list = [] + for epoch in range(n_epochs): + model.train() + epoch_loss = 0 + progress_bar = tqdm(enumerate(train_loader), total=len(train_loader)) + progress_bar.set_description(f"Epoch {epoch}") for step, batch in progress_bar: images = batch["image"].to(device) + optimizer.zero_grad(set_to_none=True) + + # Randomly select the timesteps to be used for the minibacth timesteps = torch.randint(0, ddpm_scheduler.num_train_timesteps, (images.shape[0],), device=device).long() + + # Add noise to the minibatch images with intensity defined by the scheduler and timesteps noise = torch.randn_like(images).to(device) - with torch.no_grad(): - noisy_image = ddpm_scheduler.add_noise(original_samples=images, noise=noise, timesteps=timesteps) - noise_pred = model(x=noisy_image, timesteps=timesteps) - val_loss = F.l1_loss(noise_pred.float(), noise.float()) + noisy_image = ddpm_scheduler.add_noise(original_samples=images, noise=noise, timesteps=timesteps) + + # In this example, we are parametrising our DDPM to learn the added noise (epsilon). + # For this reason, we are using our network to predict the added noise and then using L1 loss to predict + # its performance. + noise_pred = model(x=noisy_image, timesteps=timesteps) + loss = F.l1_loss(noise_pred.float(), noise.float()) + + loss.backward() + optimizer.step() + epoch_loss += loss.item() - val_epoch_loss += val_loss.item() progress_bar.set_postfix( { - "val_loss": val_epoch_loss / (step + 1), + "loss": epoch_loss / (step + 1), } ) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - - # Sampling image during training - noise = torch.randn((1, 1, 64, 64)) - noise = noise.to(device) - image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddpm_scheduler) - plt.figure(figsize=(8, 4)) - plt.subplot(3, len(sampling_steps), 1) - plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") - plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False) - plt.ylabel("DDPM") - plt.title("1000 steps") - # DDIM - for idx, reduced_sampling_steps in enumerate(sampling_steps): - ddim_scheduler.set_timesteps(reduced_sampling_steps) - image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddim_scheduler) - plt.subplot(3, len(sampling_steps), len(sampling_steps) + idx + 1) + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch + 1) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + progress_bar = tqdm(enumerate(val_loader), total=len(train_loader)) + progress_bar.set_description(f"Epoch {epoch} - Validation set") + for step, batch in progress_bar: + images = batch["image"].to(device) + timesteps = torch.randint( + 0, ddpm_scheduler.num_train_timesteps, (images.shape[0],), device=device + ).long() + noise = torch.randn_like(images).to(device) + with torch.no_grad(): + noisy_image = ddpm_scheduler.add_noise(original_samples=images, noise=noise, timesteps=timesteps) + noise_pred = model(x=noisy_image, timesteps=timesteps) + val_loss = F.l1_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix( + { + "val_loss": val_epoch_loss / (step + 1), + } + ) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + + # Sampling image during training + noise = torch.randn((1, 1, 64, 64)) + noise = noise.to(device) + image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddpm_scheduler) + plt.figure(figsize=(8, 4)) + plt.subplot(3, len(sampling_steps), 1) plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") - plt.ylabel("DDIM") - if idx == 0: - plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False) - else: - plt.axis("off") - plt.title(f"{reduced_sampling_steps} steps") - # PNDM - for idx, reduced_sampling_steps in enumerate(sampling_steps): - pndm_scheduler.set_timesteps(reduced_sampling_steps) - image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=pndm_scheduler) - plt.subplot(3, len(sampling_steps), len(sampling_steps) * 2 + idx + 1) - plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") - plt.ylabel("PNDM") - if idx == 0: - plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False) - else: - plt.axis("off") - plt.title(f"{reduced_sampling_steps} steps") - plt.suptitle(f"Epoch {epoch+1}") - plt.show() + plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False) + plt.ylabel("DDPM") + plt.title("1000 steps") + # DDIM + for idx, reduced_sampling_steps in enumerate(sampling_steps): + ddim_scheduler.set_timesteps(reduced_sampling_steps) + image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddim_scheduler) + plt.subplot(3, len(sampling_steps), len(sampling_steps) + idx + 1) + plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") + plt.ylabel("DDIM") + if idx == 0: + plt.tick_params( + top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False + ) + else: + plt.axis("off") + plt.title(f"{reduced_sampling_steps} steps") + # PNDM + for idx, reduced_sampling_steps in enumerate(sampling_steps): + pndm_scheduler.set_timesteps(reduced_sampling_steps) + image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=pndm_scheduler) + plt.subplot(3, len(sampling_steps), len(sampling_steps) * 2 + idx + 1) + plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") + plt.ylabel("PNDM") + if idx == 0: + plt.tick_params( + top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False + ) + else: + plt.axis("off") + plt.title(f"{reduced_sampling_steps} steps") + plt.suptitle(f"Epoch {epoch+1}") + plt.show() # %% [markdown] # ### Learning curves # %% -plt.style.use("seaborn") -plt.title("Learning Curves", fontsize=20) -plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") -plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", -) -plt.yticks(fontsize=12) -plt.xticks(fontsize=12) -plt.xlabel("Epochs", fontsize=16) -plt.ylabel("Loss", fontsize=16) -plt.legend(prop={"size": 14}) -plt.show() +if not use_pretrained: + plt.style.use("seaborn") + plt.title("Learning Curves", fontsize=20) + plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") + plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", + ) + plt.yticks(fontsize=12) + plt.xticks(fontsize=12) + plt.xlabel("Epochs", fontsize=16) + plt.ylabel("Loss", fontsize=16) + plt.legend(prop={"size": 14}) + plt.show() + + +# %% [markdown] +# ### Compare samples from trained model +# %% +noise = torch.randn((1, 1, 64, 64)) +noise = noise.to(device) +image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddpm_scheduler) +plt.figure(figsize=(8, 4)) +plt.subplot(3, len(sampling_steps), 1) +plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False) +plt.ylabel("DDPM") +plt.title("1000 steps") +# DDIM +for idx, reduced_sampling_steps in enumerate(sampling_steps): + ddim_scheduler.set_timesteps(reduced_sampling_steps) + image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=ddim_scheduler) + plt.subplot(3, len(sampling_steps), len(sampling_steps) + idx + 1) + plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") + plt.ylabel("DDIM") + if idx == 0: + plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False) + else: + plt.axis("off") + plt.title(f"{reduced_sampling_steps} steps") +# PNDM +for idx, reduced_sampling_steps in enumerate(sampling_steps): + pndm_scheduler.set_timesteps(reduced_sampling_steps) + image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=pndm_scheduler) + plt.subplot(3, len(sampling_steps), len(sampling_steps) * 2 + idx + 1) + plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") + plt.ylabel("PNDM") + if idx == 0: + plt.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False) + else: + plt.axis("off") + plt.title(f"{reduced_sampling_steps} steps") +plt.show() # %% [markdown] # ### Cleanup data directory diff --git a/tutorials/generative/2d_ddpm/2d_ddpm_inpainting.ipynb b/tutorials/generative/2d_ddpm/2d_ddpm_inpainting.ipynb index def42048..f4c7856c 100644 --- a/tutorials/generative/2d_ddpm/2d_ddpm_inpainting.ipynb +++ b/tutorials/generative/2d_ddpm/2d_ddpm_inpainting.ipynb @@ -392,7 +392,9 @@ "metadata": {}, "source": [ "### Model training\n", - "Here, we are training our model for 50 epochs (training time: ~33 minutes)." + "Here, we are training our model for 50 epochs (training time: ~33 minutes).\n", + "\n", + "If you would like to skip the training and use a pre-trained model instead, set `use_pretrained=True`. This model was trained using the code in `tutorials/generative/distributed_training/ddpm_training_ddp.py`" ] }, { @@ -633,86 +635,91 @@ } ], "source": [ - "n_epochs = 50\n", - "val_interval = 5\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "\n", - "scaler = GradScaler()\n", - "total_start = time.time()\n", - "for epoch in range(n_epochs):\n", - " model.train()\n", - " epoch_loss = 0\n", - " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, batch in progress_bar:\n", - " images = batch[\"image\"].to(device)\n", - " optimizer.zero_grad(set_to_none=True)\n", - "\n", - " with autocast(enabled=True):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Create timesteps\n", - " timesteps = torch.randint(\n", - " 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device\n", - " ).long()\n", - "\n", - " # Get model prediction\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", - "\n", - " loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " scaler.scale(loss).backward()\n", - " scaler.step(optimizer)\n", - " scaler.update()\n", - "\n", - " epoch_loss += loss.item()\n", - "\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"loss\": epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - "\n", - " if (epoch + 1) % val_interval == 0:\n", - " model.eval()\n", - " val_epoch_loss = 0\n", - " for step, batch in enumerate(val_loader):\n", + "use_pretrained = False\n", + "\n", + "if use_pretrained:\n", + " model = torch.hub.load(\"marksgraham/pretrained_generative_models\", model='ddpm_2d', verbose=True).to(device)\n", + "else:\n", + " n_epochs = 50\n", + " val_interval = 5\n", + " epoch_loss_list = []\n", + " val_epoch_loss_list = []\n", + "\n", + " scaler = GradScaler()\n", + " total_start = time.time()\n", + " for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, batch in progress_bar:\n", " images = batch[\"image\"].to(device)\n", - " with torch.no_grad():\n", - " with autocast(enabled=True):\n", - " noise = torch.randn_like(images).to(device)\n", - " timesteps = torch.randint(\n", - " 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device\n", - " ).long()\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", - " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " val_epoch_loss += val_loss.item()\n", + " optimizer.zero_grad(set_to_none=True)\n", + "\n", + " with autocast(enabled=True):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Create timesteps\n", + " timesteps = torch.randint(\n", + " 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device\n", + " ).long()\n", + "\n", + " # Get model prediction\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + "\n", + " loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + "\n", + " epoch_loss += loss.item()\n", + "\n", " progress_bar.set_postfix(\n", " {\n", - " \"val_loss\": val_epoch_loss / (step + 1),\n", + " \"loss\": epoch_loss / (step + 1),\n", " }\n", " )\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - "\n", - " # Sampling image during training\n", - " noise = torch.randn((1, 1, 64, 64))\n", - " noise = noise.to(device)\n", - " scheduler.set_timesteps(num_inference_steps=1000)\n", - " with autocast(enabled=True):\n", - " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=scheduler)\n", - "\n", - " plt.figure(figsize=(2, 2))\n", - " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", - " plt.tight_layout()\n", - " plt.axis(\"off\")\n", - " plt.show()\n", - "\n", - "total_time = time.time() - total_start\n", - "print(f\"train completed, total time: {total_time}.\")" + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + " for step, batch in enumerate(val_loader):\n", + " images = batch[\"image\"].to(device)\n", + " with torch.no_grad():\n", + " with autocast(enabled=True):\n", + " noise = torch.randn_like(images).to(device)\n", + " timesteps = torch.randint(\n", + " 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device\n", + " ).long()\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"val_loss\": val_epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + " # Sampling image during training\n", + " noise = torch.randn((1, 1, 64, 64))\n", + " noise = noise.to(device)\n", + " scheduler.set_timesteps(num_inference_steps=1000)\n", + " with autocast(enabled=True):\n", + " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=scheduler)\n", + "\n", + " plt.figure(figsize=(2, 2))\n", + " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + " plt.tight_layout()\n", + " plt.axis(\"off\")\n", + " plt.show()\n", + "\n", + " total_time = time.time() - total_start\n", + " print(f\"train completed, total time: {total_time}.\")" ] }, { diff --git a/tutorials/generative/2d_ddpm/2d_ddpm_inpainting.py b/tutorials/generative/2d_ddpm/2d_ddpm_inpainting.py index bc41c57a..a1fdfae2 100644 --- a/tutorials/generative/2d_ddpm/2d_ddpm_inpainting.py +++ b/tutorials/generative/2d_ddpm/2d_ddpm_inpainting.py @@ -186,88 +186,95 @@ # %% [markdown] # ### Model training # Here, we are training our model for 50 epochs (training time: ~33 minutes). +# +# If you would like to skip the training and use a pre-trained model instead, set `use_pretrained=True`. This model was trained using the code in `tutorials/generative/distributed_training/ddpm_training_ddp.py` # %% tags=[] -n_epochs = 50 -val_interval = 5 -epoch_loss_list = [] -val_epoch_loss_list = [] - -scaler = GradScaler() -total_start = time.time() -for epoch in range(n_epochs): - model.train() - epoch_loss = 0 - progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70) - progress_bar.set_description(f"Epoch {epoch}") - for step, batch in progress_bar: - images = batch["image"].to(device) - optimizer.zero_grad(set_to_none=True) - - with autocast(enabled=True): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Create timesteps - timesteps = torch.randint( - 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device - ).long() - - # Get model prediction - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - - loss = F.mse_loss(noise_pred.float(), noise.float()) - - scaler.scale(loss).backward() - scaler.step(optimizer) - scaler.update() - - epoch_loss += loss.item() - - progress_bar.set_postfix( - { - "loss": epoch_loss / (step + 1), - } - ) - epoch_loss_list.append(epoch_loss / (step + 1)) - - if (epoch + 1) % val_interval == 0: - model.eval() - val_epoch_loss = 0 - for step, batch in enumerate(val_loader): +use_pretrained = False + +if use_pretrained: + model = torch.hub.load("marksgraham/pretrained_generative_models", model="ddpm_2d", verbose=True).to(device) +else: + n_epochs = 50 + val_interval = 5 + epoch_loss_list = [] + val_epoch_loss_list = [] + + scaler = GradScaler() + total_start = time.time() + for epoch in range(n_epochs): + model.train() + epoch_loss = 0 + progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70) + progress_bar.set_description(f"Epoch {epoch}") + for step, batch in progress_bar: images = batch["image"].to(device) - with torch.no_grad(): - with autocast(enabled=True): - noise = torch.randn_like(images).to(device) - timesteps = torch.randint( - 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device - ).long() - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - val_loss = F.mse_loss(noise_pred.float(), noise.float()) - - val_epoch_loss += val_loss.item() + optimizer.zero_grad(set_to_none=True) + + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Create timesteps + timesteps = torch.randint( + 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device + ).long() + + # Get model prediction + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + + loss = F.mse_loss(noise_pred.float(), noise.float()) + + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + + epoch_loss += loss.item() + progress_bar.set_postfix( { - "val_loss": val_epoch_loss / (step + 1), + "loss": epoch_loss / (step + 1), } ) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - - # Sampling image during training - noise = torch.randn((1, 1, 64, 64)) - noise = noise.to(device) - scheduler.set_timesteps(num_inference_steps=1000) - with autocast(enabled=True): - image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=scheduler) - - plt.figure(figsize=(2, 2)) - plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") - plt.tight_layout() - plt.axis("off") - plt.show() - -total_time = time.time() - total_start -print(f"train completed, total time: {total_time}.") + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch + 1) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + for step, batch in enumerate(val_loader): + images = batch["image"].to(device) + with torch.no_grad(): + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + timesteps = torch.randint( + 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device + ).long() + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix( + { + "val_loss": val_epoch_loss / (step + 1), + } + ) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + + # Sampling image during training + noise = torch.randn((1, 1, 64, 64)) + noise = noise.to(device) + scheduler.set_timesteps(num_inference_steps=1000) + with autocast(enabled=True): + image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=scheduler) + + plt.figure(figsize=(2, 2)) + plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") + plt.tight_layout() + plt.axis("off") + plt.show() + + total_time = time.time() - total_start + print(f"train completed, total time: {total_time}.") # %% [markdown] # ### Get masked image for inpainting diff --git a/tutorials/generative/2d_ddpm/2d_ddpm_tutorial.ipynb b/tutorials/generative/2d_ddpm/2d_ddpm_tutorial.ipynb index 8e0a1013..1830af19 100644 --- a/tutorials/generative/2d_ddpm/2d_ddpm_tutorial.ipynb +++ b/tutorials/generative/2d_ddpm/2d_ddpm_tutorial.ipynb @@ -42,6 +42,7 @@ "execution_count": 2, "id": "dd62a552", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -137,6 +138,7 @@ "execution_count": 3, "id": "8fc58c80", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -169,6 +171,7 @@ "execution_count": 4, "id": "ad5a1948", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -194,6 +197,7 @@ "execution_count": 5, "id": "65e1c200", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -232,6 +236,7 @@ "execution_count": 6, "id": "e2f9bebd", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -271,6 +276,7 @@ "execution_count": 7, "id": "938318c2", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -320,6 +326,7 @@ "execution_count": 8, "id": "b698f4f8", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -372,6 +379,7 @@ "execution_count": 9, "id": "2c52e4f4", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -407,7 +415,9 @@ "metadata": {}, "source": [ "### Model training\n", - "Here, we are training our model for 75 epochs (training time: ~50 minutes)." + "Here, we are training our model for 75 epochs (training time: ~50 minutes).\n", + "\n", + "If you would like to skip the training and use a pre-trained model instead, set `use_pretrained=True`. This model was trained using the code in `tutorials/generative/distributed_training/ddpm_training_ddp.py`" ] }, { @@ -415,6 +425,7 @@ "execution_count": 10, "id": "0f697a13", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -760,86 +771,91 @@ } ], "source": [ - "n_epochs = 75\n", - "val_interval = 5\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "\n", - "scaler = GradScaler()\n", - "total_start = time.time()\n", - "for epoch in range(n_epochs):\n", - " model.train()\n", - " epoch_loss = 0\n", - " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, batch in progress_bar:\n", - " images = batch[\"image\"].to(device)\n", - " optimizer.zero_grad(set_to_none=True)\n", - "\n", - " with autocast(enabled=True):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Create timesteps\n", - " timesteps = torch.randint(\n", - " 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device\n", - " ).long()\n", - "\n", - " # Get model prediction\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", - "\n", - " loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " scaler.scale(loss).backward()\n", - " scaler.step(optimizer)\n", - " scaler.update()\n", - "\n", - " epoch_loss += loss.item()\n", - "\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"loss\": epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - "\n", - " if (epoch + 1) % val_interval == 0:\n", - " model.eval()\n", - " val_epoch_loss = 0\n", - " for step, batch in enumerate(val_loader):\n", + "use_pretrained = False\n", + "\n", + "if use_pretrained:\n", + " model = torch.hub.load(\"marksgraham/pretrained_generative_models\", model='ddpm_2d', verbose=True).to(device)\n", + "else:\n", + " n_epochs = 75\n", + " val_interval = 5\n", + " epoch_loss_list = []\n", + " val_epoch_loss_list = []\n", + "\n", + " scaler = GradScaler()\n", + " total_start = time.time()\n", + " for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, batch in progress_bar:\n", " images = batch[\"image\"].to(device)\n", - " with torch.no_grad():\n", - " with autocast(enabled=True):\n", - " noise = torch.randn_like(images).to(device)\n", - " timesteps = torch.randint(\n", - " 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device\n", - " ).long()\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", - " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " val_epoch_loss += val_loss.item()\n", + " optimizer.zero_grad(set_to_none=True)\n", + "\n", + " with autocast(enabled=True):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Create timesteps\n", + " timesteps = torch.randint(\n", + " 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device\n", + " ).long()\n", + "\n", + " # Get model prediction\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + "\n", + " loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + "\n", + " epoch_loss += loss.item()\n", + "\n", " progress_bar.set_postfix(\n", " {\n", - " \"val_loss\": val_epoch_loss / (step + 1),\n", + " \"loss\": epoch_loss / (step + 1),\n", " }\n", " )\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - "\n", - " # Sampling image during training\n", - " noise = torch.randn((1, 1, 64, 64))\n", - " noise = noise.to(device)\n", - " scheduler.set_timesteps(num_inference_steps=1000)\n", - " with autocast(enabled=True):\n", - " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=scheduler)\n", - "\n", - " plt.figure(figsize=(2, 2))\n", - " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", - " plt.tight_layout()\n", - " plt.axis(\"off\")\n", - " plt.show()\n", - "\n", - "total_time = time.time() - total_start\n", - "print(f\"train completed, total time: {total_time}.\")" + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + " for step, batch in enumerate(val_loader):\n", + " images = batch[\"image\"].to(device)\n", + " with torch.no_grad():\n", + " with autocast(enabled=True):\n", + " noise = torch.randn_like(images).to(device)\n", + " timesteps = torch.randint(\n", + " 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device\n", + " ).long()\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"val_loss\": val_epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + " # Sampling image during training\n", + " noise = torch.randn((1, 1, 64, 64))\n", + " noise = noise.to(device)\n", + " scheduler.set_timesteps(num_inference_steps=1000)\n", + " with autocast(enabled=True):\n", + " image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=scheduler)\n", + "\n", + " plt.figure(figsize=(2, 2))\n", + " plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + " plt.tight_layout()\n", + " plt.axis(\"off\")\n", + " plt.show()\n", + "\n", + " total_time = time.time() - total_start\n", + " print(f\"train completed, total time: {total_time}.\")" ] }, { @@ -855,6 +871,7 @@ "execution_count": 11, "id": "2cdcda81", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -872,22 +889,23 @@ } ], "source": [ - "plt.style.use(\"seaborn-v0_8\")\n", - "plt.title(\"Learning Curves\", fontsize=20)\n", - "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - "plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - ")\n", - "plt.yticks(fontsize=12)\n", - "plt.xticks(fontsize=12)\n", - "plt.xlabel(\"Epochs\", fontsize=16)\n", - "plt.ylabel(\"Loss\", fontsize=16)\n", - "plt.legend(prop={\"size\": 14})\n", - "plt.show()" + "if not use_pretrained:\n", + " plt.style.use(\"seaborn-v0_8\")\n", + " plt.title(\"Learning Curves\", fontsize=20)\n", + " plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + " plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + " plt.yticks(fontsize=12)\n", + " plt.xticks(fontsize=12)\n", + " plt.xlabel(\"Epochs\", fontsize=16)\n", + " plt.ylabel(\"Loss\", fontsize=16)\n", + " plt.legend(prop={\"size\": 14})\n", + " plt.show()" ] }, { @@ -903,6 +921,7 @@ "execution_count": 12, "id": "1427e5d4", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -972,7 +991,7 @@ "formats": "ipynb,py:percent" }, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -986,7 +1005,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.8.13" } }, "nbformat": 4, diff --git a/tutorials/generative/2d_ddpm/2d_ddpm_tutorial.py b/tutorials/generative/2d_ddpm/2d_ddpm_tutorial.py index 80db0e35..788d3718 100644 --- a/tutorials/generative/2d_ddpm/2d_ddpm_tutorial.py +++ b/tutorials/generative/2d_ddpm/2d_ddpm_tutorial.py @@ -8,7 +8,7 @@ # format_version: '1.3' # jupytext_version: 1.14.1 # kernelspec: -# display_name: Python 3 +# display_name: Python 3 (ipykernel) # language: python # name: python3 # --- @@ -185,108 +185,116 @@ # %% [markdown] # ### Model training # Here, we are training our model for 75 epochs (training time: ~50 minutes). +# +# If you would like to skip the training and use a pre-trained model instead, set `use_pretrained=True`. This model was trained using the code in `tutorials/generative/distributed_training/ddpm_training_ddp.py` # %% jupyter={"outputs_hidden": false} -n_epochs = 75 -val_interval = 5 -epoch_loss_list = [] -val_epoch_loss_list = [] - -scaler = GradScaler() -total_start = time.time() -for epoch in range(n_epochs): - model.train() - epoch_loss = 0 - progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70) - progress_bar.set_description(f"Epoch {epoch}") - for step, batch in progress_bar: - images = batch["image"].to(device) - optimizer.zero_grad(set_to_none=True) - - with autocast(enabled=True): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Create timesteps - timesteps = torch.randint( - 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device - ).long() - - # Get model prediction - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - - loss = F.mse_loss(noise_pred.float(), noise.float()) - - scaler.scale(loss).backward() - scaler.step(optimizer) - scaler.update() - - epoch_loss += loss.item() - - progress_bar.set_postfix( - { - "loss": epoch_loss / (step + 1), - } - ) - epoch_loss_list.append(epoch_loss / (step + 1)) - - if (epoch + 1) % val_interval == 0: - model.eval() - val_epoch_loss = 0 - for step, batch in enumerate(val_loader): +use_pretrained = False + +if use_pretrained: + model = torch.hub.load("marksgraham/pretrained_generative_models", model="ddpm_2d", verbose=True).to(device) +else: + n_epochs = 75 + val_interval = 5 + epoch_loss_list = [] + val_epoch_loss_list = [] + + scaler = GradScaler() + total_start = time.time() + for epoch in range(n_epochs): + model.train() + epoch_loss = 0 + progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70) + progress_bar.set_description(f"Epoch {epoch}") + for step, batch in progress_bar: images = batch["image"].to(device) - with torch.no_grad(): - with autocast(enabled=True): - noise = torch.randn_like(images).to(device) - timesteps = torch.randint( - 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device - ).long() - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - val_loss = F.mse_loss(noise_pred.float(), noise.float()) - - val_epoch_loss += val_loss.item() + optimizer.zero_grad(set_to_none=True) + + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Create timesteps + timesteps = torch.randint( + 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device + ).long() + + # Get model prediction + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + + loss = F.mse_loss(noise_pred.float(), noise.float()) + + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + + epoch_loss += loss.item() + progress_bar.set_postfix( { - "val_loss": val_epoch_loss / (step + 1), + "loss": epoch_loss / (step + 1), } ) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - - # Sampling image during training - noise = torch.randn((1, 1, 64, 64)) - noise = noise.to(device) - scheduler.set_timesteps(num_inference_steps=1000) - with autocast(enabled=True): - image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=scheduler) - - plt.figure(figsize=(2, 2)) - plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") - plt.tight_layout() - plt.axis("off") - plt.show() - -total_time = time.time() - total_start -print(f"train completed, total time: {total_time}.") + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch + 1) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + for step, batch in enumerate(val_loader): + images = batch["image"].to(device) + with torch.no_grad(): + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + timesteps = torch.randint( + 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device + ).long() + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix( + { + "val_loss": val_epoch_loss / (step + 1), + } + ) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + + # Sampling image during training + noise = torch.randn((1, 1, 64, 64)) + noise = noise.to(device) + scheduler.set_timesteps(num_inference_steps=1000) + with autocast(enabled=True): + image = inferer.sample(input_noise=noise, diffusion_model=model, scheduler=scheduler) + + plt.figure(figsize=(2, 2)) + plt.imshow(image[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") + plt.tight_layout() + plt.axis("off") + plt.show() + + total_time = time.time() - total_start + print(f"train completed, total time: {total_time}.") # %% [markdown] # ### Learning curves # %% jupyter={"outputs_hidden": false} -plt.style.use("seaborn-v0_8") -plt.title("Learning Curves", fontsize=20) -plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") -plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", -) -plt.yticks(fontsize=12) -plt.xticks(fontsize=12) -plt.xlabel("Epochs", fontsize=16) -plt.ylabel("Loss", fontsize=16) -plt.legend(prop={"size": 14}) -plt.show() +if not use_pretrained: + plt.style.use("seaborn-v0_8") + plt.title("Learning Curves", fontsize=20) + plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") + plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", + ) + plt.yticks(fontsize=12) + plt.xticks(fontsize=12) + plt.xlabel("Epochs", fontsize=16) + plt.ylabel("Loss", fontsize=16) + plt.legend(prop={"size": 14}) + plt.show() # %% [markdown] # ### Plotting sampling process along DDPM's Markov chain diff --git a/tutorials/generative/distributed_training/ddpm_training_ddp.py b/tutorials/generative/distributed_training/ddpm_training_ddp.py new file mode 100644 index 00000000..0e76979b --- /dev/null +++ b/tutorials/generative/distributed_training/ddpm_training_ddp.py @@ -0,0 +1,333 @@ +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This example shows how to execute distributed training based on PyTorch native `DistributedDataParallel` module. +It can run on several nodes with multiple GPU devices on every node. + +This example is based on the MedNIST Hand dataset. + +If you do not have enough GPU memory, you can try to decrease the input parameter `cache_rate`. + +Main steps to set up the distributed training: + +- Execute `torchrun` to create processes on every node for every GPU. + It receives parameters as below: + `--nproc_per_node=NUM_GPUS_PER_NODE` + `--nnodes=NUM_NODES` + `--node_rank=INDEX_CURRENT_NODE` + For more details, refer to https://pytorch.org/docs/stable/elastic/run.html. +- Wrap the model with `DistributedDataParallel` after moving to expected device. +- Partition dataset before training, so every rank process will only handle its own data partition. + +Note: + `torchrun` will launch `nnodes * nproc_per_node = world_size` processes in total. + Suggest setting exactly the same software environment for every node, especially `PyTorch`, `nccl`, etc. + A good practice is to use the same MONAI docker image for all nodes directly. + Example script to execute this program on every node: + torchrun --nproc_per_node=NUM_GPUS_PER_NODE + --nnodes=NUM_NODES --node_rank=INDEX_CURRENT_NODE + ddpm_training_ddp.py -d DIR_OF_TESTDATA + +Referring to: https://pytorch.org/tutorials/intermediate/ddp_tutorial.html + +This code is based on https://github.com/Project-MONAI/tutorials/blob/main/acceleration/distributed_training/brats_training_ddp.py + +""" + +import argparse +import os +import sys +import time +import warnings +from pathlib import Path +from typing import Optional + +import monai.inferers +import numpy as np +import torch +import torch.distributed as dist +import torch.nn.functional as F +from monai import transforms +from monai.apps import MedNISTDataset +from monai.data import DataLoader, ThreadDataLoader, partition_dataset +from monai.utils import set_determinism +from torch.cuda.amp import GradScaler, autocast +from torch.nn.parallel import DistributedDataParallel + +from generative.inferers import DiffusionInferer +from generative.networks.nets import DiffusionModelUNet +from generative.networks.schedulers import DDPMScheduler + + +class MedNISTCacheDataset(MedNISTDataset): + """ + Enhance the MedNISTDataset to support distributed data parallel. + + """ + + def __init__( + self, + root_dir: str, + section: str, + transform: Optional[transforms.Transform] = None, + cache_rate: float = 1.0, + num_workers: int = 0, + shuffle: bool = False, + ) -> None: + + if not os.path.isdir(root_dir): + raise ValueError("root directory root_dir must be a directory.") + self.section = section + self.shuffle = shuffle + self.val_frac = 0.2 + self.test_frac = 0.0 + self.set_random_state(seed=0) + dataset_dir = Path(root_dir) / "MedNIST" + if not os.path.exists(dataset_dir): + raise RuntimeError(f"cannot find dataset directory: {dataset_dir}, please download it.") + data = self._generate_data_list(dataset_dir) + super(MedNISTDataset, self).__init__(data, transform, cache_rate=cache_rate, num_workers=num_workers) + + def _generate_data_list(self, dataset_dir: Path): + data = super()._generate_data_list(dataset_dir) + # only extract hand data + data = [{"image": item["image"]} for item in data if item["class_name"] == "Hand"] + # partition dataset based on current rank number, every rank trains with its own data + # it can avoid duplicated caching content in each rank, but will not do global shuffle before every epoch + return partition_dataset( + data=data, + num_partitions=dist.get_world_size(), + shuffle=self.shuffle, + seed=0, + drop_last=False, + even_divisible=self.shuffle, + )[dist.get_rank()] + + +def main_worker(args): + # disable logging for processes except 0 on every node + local_rank = int(os.environ["LOCAL_RANK"]) + if local_rank != 0: + f = open(os.devnull, "w") + sys.stdout = sys.stderr = f + if not os.path.exists(args.data_dir): + raise FileNotFoundError(f"missing directory {args.data_dir}") + + # initialize the distributed training process, every GPU runs in a process + dist.init_process_group(backend="nccl", init_method="env://") + device = torch.device(f"cuda:{local_rank}") + torch.cuda.set_device(device) + # use amp to accelerate training + scaler = GradScaler() + torch.backends.cudnn.benchmark = True + + total_start = time.time() + train_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image"]), + transforms.EnsureChannelFirstd(keys=["image"]), + transforms.ScaleIntensityRanged(keys=["image"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True), + transforms.RandAffined( + keys=["image"], + rotate_range=[(-np.pi / 36, np.pi / 36), (-np.pi / 36, np.pi / 36)], + translate_range=[(-1, 1), (-1, 1)], + scale_range=[(-0.05, 0.05), (-0.05, 0.05)], + spatial_size=[64, 64], + padding_mode="zeros", + prob=0.5, + ), + ] + ) + + # create a training data loader + + train_ds = MedNISTCacheDataset( + root_dir=args.data_dir, + transform=train_transforms, + section="training", + num_workers=4, + cache_rate=args.cache_rate, + shuffle=True, + ) + # ThreadDataLoader can be faster if no IO operations when caching all the data in memory + train_loader = ThreadDataLoader(train_ds, num_workers=0, batch_size=args.batch_size, shuffle=True) + + # validation transforms and dataset + val_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image"]), + transforms.EnsureChannelFirstd(keys=["image"]), + transforms.ScaleIntensityRanged(keys=["image"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True), + ] + ) + val_ds = MedNISTCacheDataset( + root_dir=args.data_dir, + transform=val_transforms, + section="validation", + num_workers=4, + cache_rate=args.cache_rate, + shuffle=False, + ) + # ThreadDataLoader can be faster if no IO operations when caching all the data in memory + val_loader = ThreadDataLoader(val_ds, num_workers=0, batch_size=args.batch_size, shuffle=False) + + # create network, loss function and optimizer + model = DiffusionModelUNet( + spatial_dims=2, + in_channels=1, + out_channels=1, + num_channels=(128, 256, 256), + attention_levels=(False, True, True), + num_res_blocks=1, + num_head_channels=256, + ) + model = model.to(device) + scheduler = DDPMScheduler( + num_train_timesteps=1000, + ) + + optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) + + inferer = DiffusionInferer(scheduler) + # wrap the model with DistributedDataParallel module + model = DistributedDataParallel(model, device_ids=[device]) + + # start a typical PyTorch training + best_metric = 10000 + best_metric_epoch = 1000 + print(f"Time elapsed before training: {time.time() - total_start}") + train_start = time.time() + for epoch in range(args.epochs): + epoch_start = time.time() + print("-" * 10) + print(f"epoch {epoch + 1}/{args.epochs}") + epoch_loss = train(train_loader, model, optimizer, inferer, scaler, device) + print(f"epoch {epoch + 1} average loss: {epoch_loss:.4f}") + + if (epoch + 1) % args.val_interval == 0: + metric = evaluate(model, val_loader, inferer, device) + + if metric < best_metric: + best_metric = metric + best_metric_epoch = epoch + 1 + if dist.get_rank() == 0: + torch.save(model.module.state_dict(), Path(args.output_dir) / "best_metric_model.pth") + print(f"Saving model at epoch {epoch+1}") + print( + f"current epoch: {epoch + 1} current val loss: {metric:.4f}" + f"\nbest MSE loss: {best_metric:.4f} at epoch: {best_metric_epoch}" + ) + + print(f"Training time for epoch {epoch + 1} was: {(time.time() - epoch_start):.4f}s") + + print( + f"Training completed, best_metric: {best_metric:.4f} at epoch: {best_metric_epoch}," + f"Total train time: {(time.time() - train_start):.4f}" + ) + dist.destroy_process_group() + + +def train( + train_loader: DataLoader, + model: torch.nn, + optimizer: torch.optim.Optimizer, + inferer: monai.inferers.Inferer, + scaler: GradScaler, + device: torch.device, +): + model.train() + step = 0 + epoch_len = len(train_loader) + epoch_loss = 0 + step_start = time.time() + for batch_data in train_loader: + step += 1 + images = batch_data["image"].to(device) + optimizer.zero_grad(set_to_none=True) + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Create timesteps + timesteps = torch.randint( + 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device + ).long() + + # Get model prediction + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + + loss = F.mse_loss(noise_pred.float(), noise.float()) + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + epoch_loss += loss.item() + print(f"{step}/{epoch_len}, train_loss: {loss.item():.4f}, step time: {(time.time() - step_start):.4f}") + step_start = time.time() + epoch_loss /= step + + return epoch_loss + + +def evaluate(model: torch.nn, val_loader: DataLoader, inferer: monai.inferers.Inferer, device: torch.device): + model.eval() + val_epoch_loss = 0 + with torch.no_grad(): + for step, batch_data in enumerate(val_loader): + images = batch_data["image"].to(device) + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + timesteps = torch.randint( + 0, inferer.scheduler.num_train_timesteps, (images.shape[0],), device=images.device + ).long() + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + val_epoch_loss = val_epoch_loss / (step + 1) + return val_epoch_loss + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "-d", "--data_dir", default="./testdata", type=str, help="directory of downloaded MedNIST dataset" + ) + parser.add_argument("--output_dir", default="/project", type=str, help="directory to save outputs") + parser.add_argument("--epochs", default=300, type=int, metavar="N", help="number of total epochs to run") + parser.add_argument("--lr", default=1e-4, type=float, help="learning rate") + parser.add_argument("-b", "--batch_size", default=1, type=int, help="mini-batch size of every GPU") + parser.add_argument("--seed", default=None, type=int, help="seed for initializing training.") + parser.add_argument("--cache_rate", type=float, default=1.0, help="larger cache rate relies on enough GPU memory.") + parser.add_argument("--val_interval", type=int, default=5) + args = parser.parse_args() + + if args.seed is not None: + set_determinism(seed=args.seed) + warnings.warn( + "You have chosen to seed training. " + "This will turn on the CUDNN deterministic setting, " + "which can slow down your training considerably! " + "You may see unexpected behavior when restarting " + "from checkpoints." + ) + + main_worker(args=args) + + +# usage example (refer to https://github.com/pytorch/pytorch/blob/master/torch/distributed/launch.py): + +# torchrun --nproc_per_node=NUM_GPUS_PER_NODE +# --nnodes=NUM_NODES --node_rank=INDEX_CURRENT_NODE +# ddpm_training_ddp.py -d DIR_OF_TESTDATA + +if __name__ == "__main__": + main()