Skip to content

Instantly share code, notes, and snippets.

@samyumobi
Created July 30, 2021 19:20
Show Gist options
  • Select an option

  • Save samyumobi/de2fc141b4896908853894e1e686bb8b to your computer and use it in GitHub Desktop.

Select an option

Save samyumobi/de2fc141b4896908853894e1e686bb8b to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": "<a href=\"http://cocl.us/pytorch_link_top\">\n <img src=\"https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/DL0110EN/notebook_images%20/Pytochtop.png\" width=\"750\" alt=\"IBM Product \" />\n</a> \n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<img src=\"https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/DL0110EN/notebook_images%20/cc-logo-square.png\" width=\"200\" alt=\"cognitiveclass.ai logo\" />\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h1><h1>Pre-trained-Models with PyTorch </h1>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "In this lab, you will use pre-trained models to classify between the negative and positive samples; you will be provided with the dataset object. The particular pre-trained model will be resnet18; you will have three questions: \n\n<ul>\n<li>change the output layer</li>\n<li> train the model</li> \n<li> identify several misclassified samples</li> \n </ul>\nYou will take several screenshots of your work and share your notebook. \n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h2>Table of Contents</h2>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<div class=\"alert alert-block alert-info\" style=\"margin-top: 20px\">\n\n<ul>\n <li><a href=\"#download_data\"> Download Data</a></li>\n <li><a href=\"#auxiliary\"> Imports and Auxiliary Functions </a></li>\n <li><a href=\"#data_class\"> Dataset Class</a></li>\n <li><a href=\"#Question_1\">Question 1</a></li>\n <li><a href=\"#Question_2\">Question 2</a></li>\n <li><a href=\"#Question_3\">Question 3</a></li>\n</ul>\n<p>Estimated Time Needed: <strong>120 min</strong></p>\n </div>\n<hr>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h2 id=\"download_data\">Download Data</h2>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Download the dataset and unzip the files in your data directory, unlike the other labs, all the data will be deleted after you close the lab, this may take some time:\n"
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": "--2021-07-30 18:32:57-- https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/DL0321EN/data/images/Positive_tensors.zip\nResolving s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)... 67.228.254.196\nConnecting to s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)|67.228.254.196|:443... connected.\nHTTP request sent, awaiting response... 200 OK\nLength: 2598656062 (2.4G) [application/zip]\nSaving to: \u2018Positive_tensors.zip\u2019\n\nPositive_tensors.zi 100%[===================>] 2.42G 36.6MB/s in 75s \n\n2021-07-30 18:34:12 (33.0 MB/s) - \u2018Positive_tensors.zip\u2019 saved [2598656062/2598656062]\n\n"
}
],
"source": "!wget https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/DL0321EN/data/images/Positive_tensors.zip "
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": "!unzip -q Positive_tensors.zip "
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": "--2021-07-30 18:35:54-- https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/DL0321EN/data/images/Negative_tensors.zip\nResolving s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)... 67.228.254.196\nConnecting to s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)|67.228.254.196|:443... connected.\nHTTP request sent, awaiting response... 200 OK\nLength: 2111408108 (2.0G) [application/zip]\nSaving to: \u2018Negative_tensors.zip\u2019\n\nNegative_tensors.zi 100%[===================>] 1.97G 37.5MB/s in 61s \n\n2021-07-30 18:36:55 (33.1 MB/s) - \u2018Negative_tensors.zip\u2019 saved [2111408108/2111408108]\n\n"
}
],
"source": "! wget https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/DL0321EN/data/images/Negative_tensors.zip\n!unzip -q Negative_tensors.zip"
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": "Negative_tensors Negative_tensors.zip\tPositive_tensors Positive_tensors.zip\r\n"
}
],
"source": "!ls"
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": "/home/wsuser/work/Positive_tensors\r\n"
}
],
"source": "! echo \"$PWD/Positive_tensors\""
},
{
"cell_type": "markdown",
"metadata": {},
"source": "We will install torchvision:\n"
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": "/opt/conda/envs/Python-3.7-OpenCE/lib/python3.7/site-packages/secretstorage/dhcrypto.py:16: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead\n from cryptography.utils import int_from_bytes\n/opt/conda/envs/Python-3.7-OpenCE/lib/python3.7/site-packages/secretstorage/util.py:25: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead\n from cryptography.utils import int_from_bytes\nRequirement already satisfied: torchvision in /opt/conda/envs/Python-3.7-OpenCE/lib/python3.7/site-packages (0.8.2)\nRequirement already satisfied: numpy in /opt/conda/envs/Python-3.7-OpenCE/lib/python3.7/site-packages (from torchvision) (1.19.2)\nRequirement already satisfied: torch in /opt/conda/envs/Python-3.7-OpenCE/lib/python3.7/site-packages (from torchvision) (1.7.1)\nRequirement already satisfied: pillow>=4.1.1 in /opt/conda/envs/Python-3.7-OpenCE/lib/python3.7/site-packages (from torchvision) (8.2.0)\nRequirement already satisfied: typing_extensions in /opt/conda/envs/Python-3.7-OpenCE/lib/python3.7/site-packages (from torch->torchvision) (3.7.4.2)\n"
}
],
"source": "!pip install torchvision"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h2 id=\"auxiliary\">Imports and Auxiliary Functions</h2>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "The following are the libraries we are going to use for this lab. The <code>torch.manual_seed()</code> is for forcing the random function to give the same number every time we try to recompile it.\n"
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": "<torch._C.Generator at 0x7f27bc11d1b0>"
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": "# These are the libraries will be used for this lab.\nimport torchvision.models as models\nfrom PIL import Image\nimport pandas\nfrom torchvision import transforms\nimport torch.nn as nn\nimport time\nimport torch \nimport matplotlib.pylab as plt\nimport numpy as np\nfrom torch.utils.data import Dataset, DataLoader\nimport h5py\nimport os\nimport glob\ntorch.manual_seed(0)"
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": "from matplotlib.pyplot import imshow\nimport matplotlib.pylab as plt\nfrom PIL import Image\nimport pandas as pd\nimport os"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<!--Empty Space for separating topics-->\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h2 id=\"data_class\">Dataset Class</h2>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": " This dataset class is essentially the same dataset you build in the previous section, but to speed things up, we are going to use tensors instead of jpeg images. Therefor for each iteration, you will skip the reshape step, conversion step to tensors and normalization step.\n"
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": "done\n"
}
],
"source": "# Create your own dataset object\n\nclass Dataset(Dataset):\n\n # Constructor\n def __init__(self,transform=None,train=True):\n directory=\"/home/wsuser/work/\"\n positive=\"Positive_tensors\"\n negative='Negative_tensors'\n\n positive_file_path=os.path.join(directory,positive)\n negative_file_path=os.path.join(directory,negative)\n positive_files=[os.path.join(positive_file_path,file) for file in os.listdir(positive_file_path) if file.endswith(\".pt\")]\n negative_files=[os.path.join(negative_file_path,file) for file in os.listdir(negative_file_path) if file.endswith(\".pt\")]\n number_of_samples=len(positive_files)+len(negative_files)\n self.all_files=[None]*number_of_samples\n self.all_files[::2]=positive_files\n self.all_files[1::2]=negative_files \n # The transform is goint to be used on image\n self.transform = transform\n #torch.LongTensor\n self.Y=torch.zeros([number_of_samples]).type(torch.LongTensor)\n self.Y[::2]=1\n self.Y[1::2]=0\n \n if train:\n self.all_files=self.all_files[0:30000]\n self.Y=self.Y[0:30000]\n self.len=len(self.all_files)\n else:\n self.all_files=self.all_files[30000:]\n self.Y=self.Y[30000:]\n self.len=len(self.all_files) \n \n # Get the length\n def __len__(self):\n return self.len\n \n # Getter\n def __getitem__(self, idx):\n \n image=torch.load(self.all_files[idx])\n y=self.Y[idx]\n \n # If there is any transform method, apply it onto the image\n if self.transform:\n image = self.transform(image)\n\n return image, y\n \nprint(\"done\")"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "We create two dataset objects, one for the training data and one for the validation data.\n"
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": "done\n"
}
],
"source": "train_dataset = Dataset(train=True)\nvalidation_dataset = Dataset(train=False)\nprint(\"done\")"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h2 id=\"Question_1\">Question 1</h2>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Prepare a pre-trained resnet18 model :</b>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Step 1</b>: Load the pre-trained model <code>resnet18</code> Set the parameter <code>pretrained</code> to true:\n"
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [],
"source": "# Step 1: Load the pre-trained model resnet18\nmodel = models.resnet18(pretrained=True)\n\n# Type your code here"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Step 2</b>: Set the attribute <code>requires_grad</code> to <code>False</code>. As a result, the parameters will not be affected by training.\n"
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [],
"source": "# Step 2: Set the parameter cannot be trained for the pre-trained model\nfor param in model.parameters():\n param.requires_grad=False\n\n# Type your code here"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<code>resnet18</code> is used to classify 1000 different objects; as a result, the last layer has 1000 outputs. The 512 inputs come from the fact that the previously hidden layer has 512 outputs. \n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Step 3</b>: Replace the output layer <code>model.fc</code> of the neural network with a <code>nn.Linear</code> object, to classify 2 different classes. For the parameters <code>in_features </code> remember the last hidden layer has 512 neurons.\n"
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [],
"source": "# for param in model.parameters():\n\nmodel.fc = nn.Linear(512,7)\n "
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Print out the model in order to show whether you get the correct answer.<br> <b>(Your peer reviewer is going to mark based on what you print here.)</b>\n"
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": "ResNet(\n (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)\n (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)\n (layer1): Sequential(\n (0): BasicBlock(\n (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n )\n (1): BasicBlock(\n (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n )\n )\n (layer2): Sequential(\n (0): BasicBlock(\n (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (downsample): Sequential(\n (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)\n (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n )\n )\n (1): BasicBlock(\n (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n )\n )\n (layer3): Sequential(\n (0): BasicBlock(\n (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (downsample): Sequential(\n (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)\n (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n )\n )\n (1): BasicBlock(\n (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n )\n )\n (layer4): Sequential(\n (0): BasicBlock(\n (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (downsample): Sequential(\n (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)\n (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n )\n )\n (1): BasicBlock(\n (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n (relu): ReLU(inplace=True)\n (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n )\n )\n (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))\n (fc): Linear(in_features=512, out_features=7, bias=True)\n)\n"
}
],
"source": "print(model)"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h2 id=\"Question_2\">Question 2: Train the Model</h2>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "In this question you will train your, model:\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Step 1</b>: Create a cross entropy criterion function \n"
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [],
"source": "# Step 1: Create the loss function\ncriterion = nn.CrossEntropyLoss()\n# Type your code here"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Step 2</b>: Create a training loader and validation loader object, the batch size should have 100 samples each.\n"
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [],
"source": "# mean = [0.485,0.456,0.406]\n# std = [0.229,0.224,0.225]\n# composed = transforms.\ntrain_loader = torch.utils.data.DataLoader(dataset=train_dataset,batch_size=15)\nvalidation_loader = torch.utils.data.DataLoader(dataset=validation_dataset,batch_size=15)"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Step 3</b>: Use the following optimizer to minimize the loss \n"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "optimizer = torch.optim.Adam([parameters for parameters in model.parameters() if parameters.requires_grad],lr=0.001)"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<!--Empty Space for separating topics-->\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "**Complete the following code to calculate the accuracy on the validation data for one epoch; this should take about 45 minutes. Make sure you calculate the accuracy on the validation data.**\n"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "n_epochs=1\nloss_list=[]\naccuracy_list=[]\ncorrect=0\nN_test=len(validation_dataset)\nN_train=len(train_dataset)\nstart_time = time.time()\n#n_epochs\n\nLoss=0\nstart_time = time.time()\nfor epoch in range(n_epochs):\n loss_sublist = []\n for x, y in train_loader:\n\n model.train() \n #clear gradient \n optimizer.zero_grad()\n \n #make a prediction \n z = model(x)\n # calculate loss \n loss = criterion(z,y)\n # calculate gradients of parameters \n loss_sublist.append(loss.data.item())\n # update parameters \n loss.backward()\n optimizer.step()\n #loss_list.append(np.mean(loss_sublist))\n loss_list.append(loss.data)\n correct=0\n for x_test, y_test in validation_loader:\n # set model to eval \n model.eval()\n #make a prediction \n z = model(x_test)\n #find max \n _, yhat = torch.max(z.data,1)\n \n #Calculate misclassified samples in mini-batch \n #hint +=(yhat==y_test).sum().item()\n correct += (yhat==y_test).sum().item()\n \n accuracy=correct/N_test\n accuracy_list.append(accuracy)\n\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Print out the Accuracy and plot the loss stored in the list <code>loss_list</code> for every iteration and take a screen shot.</b>\n"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "accuracy"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "plt.plot(loss_list)\nplt.xlabel(\"iteration\")\nplt.ylabel(\"loss\")\nplt.show()\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h2 id=\"Question_3\">Question 3:Find the misclassified samples</h2> \n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<b>Identify the first four misclassified samples using the validation data:</b>\n"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": " sample10 predicted value: tensor([0]) actual value:tensor([1])\n\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<a href=\"https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/share-notebooks.html\"> CLICK HERE </a> Click here to see how to share your notebook.\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "<h2>About the Authors:</h2> \n\n<a href=\"https://www.linkedin.com/in/joseph-s-50398b136/\">Joseph Santarcangelo</a> has a PhD in Electrical Engineering, his research focused on using machine learning, signal processing, and computer vision to determine how videos impact human cognition. Joseph has been working for IBM since he completed his PhD.\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "## Change Log\n\n| Date (YYYY-MM-DD) | Version | Changed By | Change Description |\n| ----------------- | ------- | ---------- | ----------------------------------------------------------- |\n| 2020-09-21 | 2.0 | Shubham | Migrated Lab to Markdown and added to course repo in GitLab |\n\n<hr>\n\n## <h3 align=\"center\"> \u00a9 IBM Corporation 2020. All rights reserved. <h3/>\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Copyright \u00a9 2018 <a href=\"cognitiveclass.ai?utm_source=bducopyrightlink&utm_medium=dswb&utm_campaign=bdu\">cognitiveclass.ai</a>. This notebook and its source code are released under the terms of the <a href=\"https://bigdatauniversity.com/mit-license/\">MIT License</a>.\n"
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment