-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from XinshaoAmosWang/DCL-11-add-jupyter-noteboo…
…k-for-training-shufflenet-resnet-18-on-cifar-100 DCL-11: add a jupyter notebook demo for training shufflenetv2 on cifar-100 with a symmetric noise rate of 40%
- Loading branch information
Showing
2 changed files
with
314 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
307 changes: 307 additions & 0 deletions
307
demos_jupyter_notebooks/convnets_cifar100/trainer_cifar100_covnets_proselflc.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,307 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"\n", | ||
"## Import dependencies" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 6, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import os\n", | ||
"import time\n", | ||
"\n", | ||
"import pandas\n", | ||
"import torch\n", | ||
"\n", | ||
"from proselflc.trainer.trainer_cnn_vision_derivedgrad import Trainer\n", | ||
"\n", | ||
"import pprint\n", | ||
"print = pprint.PrettyPrinter(indent=4).pprint" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Add params configurations" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 7, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"{ 'batch_accumu_steps': 10,\n", | ||
" 'batch_size': 128,\n", | ||
" 'classes_per_batch': 64,\n", | ||
" 'counter': 'iteration',\n", | ||
" 'data_name': 'cifar100',\n", | ||
" 'device': 'gpu',\n", | ||
" 'eval_interval': 500,\n", | ||
" 'exp_base': 16,\n", | ||
" 'gamma': 0.1,\n", | ||
" 'logit_soften_T': 0.5,\n", | ||
" 'loss_mode': 'cross entropy',\n", | ||
" 'loss_name': 'proselflc',\n", | ||
" 'lr': 0.2,\n", | ||
" 'lr_scheduler': 'WarmupMultiStepSchedule',\n", | ||
" 'milestones': [20000, 30000],\n", | ||
" 'momentum': 0.9,\n", | ||
" 'network_name': 'shufflenetv2',\n", | ||
" 'num_classes': 100,\n", | ||
" 'num_workers': 8,\n", | ||
" 'sampler': 'BalancedBatchSampler',\n", | ||
" 'symmetric_noise_rate': 0.4,\n", | ||
" 'total_epochs': 100,\n", | ||
" 'transit_time_ratio': 0.5,\n", | ||
" 'trust_mode': 'global*(1-H(p)/H(u))',\n", | ||
" 'warmup_epochs': 0,\n", | ||
" 'weight_decay': 0.001}\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"params = {}\n", | ||
"params.update(\n", | ||
" {\n", | ||
" \"data_name\": \"cifar100\",\n", | ||
" \"num_classes\": 100, # 1000\n", | ||
" \"device\": \"gpu\",\n", | ||
" #\n", | ||
" \"num_workers\": 8,\n", | ||
" #\n", | ||
" \"counter\": \"iteration\",\n", | ||
" \"classes_per_batch\": 8,\n", | ||
" #\n", | ||
" # To get deterministic results, please uncomment the line below\n", | ||
" # to set seed parameter.\n", | ||
" # \"seed\": 123,\n", | ||
" }\n", | ||
")\n", | ||
"\n", | ||
"# data\n", | ||
"params[\"symmetric_noise_rate\"] = 0.4\n", | ||
"\n", | ||
"# network\n", | ||
"params[\"network_name\"] = \"shufflenetv2\"\n", | ||
"\n", | ||
"# for the demo purpose, I set the total epochs to be small.\n", | ||
"params[\"total_epochs\"] = 100\n", | ||
"params[\"eval_interval\"] = 500 # iterations\n", | ||
"\n", | ||
"# batch\n", | ||
"params[\"batch_size\"] = 128\n", | ||
"params[\"sampler\"] = \"BalancedBatchSampler\"\n", | ||
"params[\"classes_per_batch\"] = 64\n", | ||
"\n", | ||
"# learning rate\n", | ||
"params[\"lr\"] = 0.2\n", | ||
"params[\"weight_decay\"] = 1e-3\n", | ||
"params[\"lr_scheduler\"] = \"WarmupMultiStepSchedule\"\n", | ||
"params[\"warmup_epochs\"] = 0\n", | ||
"params[\"milestones\"] = [20000, 30000]\n", | ||
"\n", | ||
"# optimisation\n", | ||
"params[\"momentum\"] = 0.9\n", | ||
"params[\"batch_accumu_steps\"] = 10\n", | ||
"params[\"gamma\"] = 0.1\n", | ||
"\n", | ||
"# loss settings\n", | ||
"params[\"loss_mode\"] = \"cross entropy\"\n", | ||
"params[\"trust_mode\"] = \"global*(1-H(p)/H(u))\"\n", | ||
"params[\"loss_name\"] = \"proselflc\"\n", | ||
"params[\"transit_time_ratio\"] = 0.50\n", | ||
"params[\"exp_base\"] = 16\n", | ||
"params[\"logit_soften_T\"] = 0.5\n", | ||
"\n", | ||
"print(params)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"## Create the folder to store intermediate and final results\n", | ||
"* First, run `sudo mkdir /home/proselflc_experiments/ && sudo chmod -R 777 /home/proselflc_experiments/` so that so you have the write permission.\n", | ||
"* Or set `WORK_DIR = \"/home/your_username/proselflc_experiments/\"`" | ||
], | ||
"metadata": { | ||
"collapsed": false | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 8, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"WORK_DIR = \"/home/proselflc_experiments/\"\n", | ||
"#\n", | ||
"# use the time as a unique experiment identifier\n", | ||
"dt_string = time.strftime(\"%Y%m%d-%H%M%S\")\n", | ||
"summary_writer_dir = (\n", | ||
" params[\"loss_name\"]\n", | ||
" + \"_\"\n", | ||
" + dt_string\n", | ||
")\n", | ||
"params[\"summary_writer_dir\"] = (\n", | ||
" WORK_DIR\n", | ||
" + \"/\"\n", | ||
" + params[\"data_name\"]\n", | ||
" + \"_symmetric_noise_rate_\"\n", | ||
" + str(params[\"symmetric_noise_rate\"])\n", | ||
" + \"/\"\n", | ||
" + params[\"network_name\"]\n", | ||
" + \"/\"\n", | ||
" + summary_writer_dir\n", | ||
")\n", | ||
"if not os.path.exists(params[\"summary_writer_dir\"]):\n", | ||
" os.makedirs(params[\"summary_writer_dir\"])" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"## Init the trainer, and store the params configurations\n", | ||
"For each experiment, we have one unique result folder to store the params configurations and learning curves.\n", | ||
"Therefore, you can revisit any specific experiment whenever you need without losing any details." | ||
], | ||
"metadata": { | ||
"collapsed": false | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 9, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Files already downloaded and verified\n", | ||
"Files already downloaded and verified\n", | ||
"Files already downloaded and verified\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"trainer = Trainer(params=params)\n", | ||
"# params[\"milestones\"] was a list of integers, we convert it to a string before sinking.\n", | ||
"params[\"milestones\"] = str(params[\"milestones\"])\n", | ||
"dataframe = pandas.DataFrame(params, index=[0])\n", | ||
"dataframe.to_csv(\n", | ||
" params[\"summary_writer_dir\"] + \"/params.csv\",\n", | ||
" encoding=\"utf-8\",\n", | ||
" index=False,\n", | ||
" sep=\"\\t\",\n", | ||
" mode=\"w\", #\n", | ||
")" | ||
], | ||
"metadata": { | ||
"collapsed": false | ||
} | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"## Run the trainer and save the final model" | ||
], | ||
"metadata": { | ||
"collapsed": false | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 10, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Evaluating Network.....\n", | ||
"Iteration= (500,2)/(39100, 100), lr=0.2000, batch_mean_epsilon=0.0001, valid batch size=128.0000, batch_mean_gtrust=0.0004, batch_mean_etrust=0.1395\n", | ||
"clean_test: Loss= 4.2554, Accuracy= 0.0538, Entropy= 0.9492, Max_p= 0.0478,\n", | ||
"noisy_subset: Loss= 4.7921, Accuracy= 0.0101, Entropy= 0.9555, Max_p= 0.0434,\n", | ||
"clean_subset: Loss= 4.2743, Accuracy= 0.0535, Entropy= 0.9552, Max_p= 0.0437,\n", | ||
"cleaned_noisy_subset: Loss= 4.2758, Accuracy= 0.0546, Entropy= 0.9554, Max_p= 0.0435,\n", | ||
"Evaluating Network.....\n", | ||
"Iteration= (1000,3)/(39100, 100), lr=0.2000, batch_mean_epsilon=0.0001, valid batch size=128.0000, batch_mean_gtrust=0.0005, batch_mean_etrust=0.2463\n", | ||
"clean_test: Loss= 3.9042, Accuracy= 0.1096, Entropy= 0.9200, Max_p= 0.0681,\n", | ||
"noisy_subset: Loss= 4.8793, Accuracy= 0.0092, Entropy= 0.9332, Max_p= 0.0584,\n" | ||
] | ||
}, | ||
{ | ||
"ename": "KeyboardInterrupt", | ||
"evalue": "", | ||
"output_type": "error", | ||
"traceback": [ | ||
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", | ||
"\u001B[0;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", | ||
"Input \u001B[0;32mIn [10]\u001B[0m, in \u001B[0;36m<cell line: 1>\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[43mtrainer\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mtrain\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 2\u001B[0m torch\u001B[38;5;241m.\u001B[39msave(\n\u001B[1;32m 3\u001B[0m trainer\u001B[38;5;241m.\u001B[39mnetwork,\n\u001B[1;32m 4\u001B[0m params[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124msummary_writer_dir\u001B[39m\u001B[38;5;124m\"\u001B[39m] \u001B[38;5;241m+\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124m/model.pt\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m 5\u001B[0m )\n\u001B[1;32m 6\u001B[0m \u001B[38;5;28mprint\u001B[39m(\n\u001B[1;32m 7\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mThe experiment is finished with details sinked in \u001B[39m\u001B[38;5;132;01m{}\u001B[39;00m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;241m.\u001B[39mformat(\n\u001B[1;32m 8\u001B[0m params[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124msummary_writer_dir\u001B[39m\u001B[38;5;124m\"\u001B[39m]\n\u001B[1;32m 9\u001B[0m )\n\u001B[1;32m 10\u001B[0m )\n", | ||
"File \u001B[0;32m~/Dropbox/GeneralCareerDevelop/PersonalBrand/GithubRepositories/ProSelfLC_tpami2release/src/proselflc/trainer/trainer_cnn_vision_derivedgrad.py:258\u001B[0m, in \u001B[0;36mTrainer.train\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 254\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mtrain\u001B[39m(\u001B[38;5;28mself\u001B[39m) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[1;32m 255\u001B[0m \u001B[38;5;66;03m# #############################\u001B[39;00m\n\u001B[1;32m 256\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m epoch \u001B[38;5;129;01min\u001B[39;00m \u001B[38;5;28mrange\u001B[39m(\u001B[38;5;241m1\u001B[39m, \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mtotal_epochs \u001B[38;5;241m+\u001B[39m \u001B[38;5;241m1\u001B[39m):\n\u001B[1;32m 257\u001B[0m \u001B[38;5;66;03m# train one epoch\u001B[39;00m\n\u001B[0;32m--> 258\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mtrain_one_epoch\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 259\u001B[0m \u001B[43m \u001B[49m\u001B[43mepoch\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mepoch\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 260\u001B[0m \u001B[43m \u001B[49m\u001B[43mdataloader\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mtraindataloader\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 261\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 262\u001B[0m \u001B[38;5;66;03m# #############################\u001B[39;00m\n\u001B[1;32m 263\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39msink_csv_figures()\n", | ||
"File \u001B[0;32m~/Dropbox/GeneralCareerDevelop/PersonalBrand/GithubRepositories/ProSelfLC_tpami2release/src/proselflc/trainer/trainer_cnn_vision_derivedgrad.py:421\u001B[0m, in \u001B[0;36mTrainer.train_one_epoch\u001B[0;34m(self, epoch, dataloader)\u001B[0m\n\u001B[1;32m 408\u001B[0m \u001B[38;5;66;03m# print(str(logits.requires_grad))\u001B[39;00m\n\u001B[1;32m 409\u001B[0m \u001B[38;5;66;03m# print(logits.shape)\u001B[39;00m\n\u001B[1;32m 410\u001B[0m \u001B[38;5;66;03m# print(pred_probs.shape)\u001B[39;00m\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 418\u001B[0m \n\u001B[1;32m 419\u001B[0m \u001B[38;5;66;03m# for logging, I am still using epoch, only to reduce changes here.\u001B[39;00m\n\u001B[1;32m 420\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mcur_time \u001B[38;5;241m%\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mparams[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124meval_interval\u001B[39m\u001B[38;5;124m\"\u001B[39m] \u001B[38;5;241m==\u001B[39m \u001B[38;5;241m0\u001B[39m:\n\u001B[0;32m--> 421\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43meval_helper\u001B[49m\u001B[43m(\u001B[49m\u001B[43mepoch\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 422\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mnetwork\u001B[38;5;241m.\u001B[39mtrain() \u001B[38;5;66;03m# self.network.train(mode=True)\u001B[39;00m\n\u001B[1;32m 424\u001B[0m \u001B[38;5;66;03m# update params\u001B[39;00m\n", | ||
"File \u001B[0;32m~/Dropbox/GeneralCareerDevelop/PersonalBrand/GithubRepositories/ProSelfLC_tpami2release/src/proselflc/trainer/trainer_cnn_vision_derivedgrad.py:479\u001B[0m, in \u001B[0;36mTrainer.eval_helper\u001B[0;34m(self, epoch)\u001B[0m\n\u001B[1;32m 476\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mmax_p_dynamics[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mepoch\u001B[39m\u001B[38;5;124m\"\u001B[39m]\u001B[38;5;241m.\u001B[39mappend(\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mcur_time)\n\u001B[1;32m 478\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m eval_dataname, eval_dataloader \u001B[38;5;129;01min\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mdataloaders\u001B[38;5;241m.\u001B[39mitems():\n\u001B[0;32m--> 479\u001B[0m (eval_loss, eval_accuracy, eval_entropy, eval_max_p) \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mevaluation\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 480\u001B[0m \u001B[43m \u001B[49m\u001B[43mdataloader\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43meval_dataloader\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 481\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 482\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mloss_dynamics[eval_dataname]\u001B[38;5;241m.\u001B[39mappend(eval_loss)\n\u001B[1;32m 483\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39maccuracy_dynamics[eval_dataname]\u001B[38;5;241m.\u001B[39mappend(eval_accuracy)\n", | ||
"File \u001B[0;32m~/.local/share/virtualenvs/ProSelfLC_tpami2release-X5r1NEDs/lib/python3.8/site-packages/torch/autograd/grad_mode.py:28\u001B[0m, in \u001B[0;36m_DecoratorContextManager.__call__.<locals>.decorate_context\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 25\u001B[0m \u001B[38;5;129m@functools\u001B[39m\u001B[38;5;241m.\u001B[39mwraps(func)\n\u001B[1;32m 26\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mdecorate_context\u001B[39m(\u001B[38;5;241m*\u001B[39margs, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs):\n\u001B[1;32m 27\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m\u001B[38;5;18m__class__\u001B[39m():\n\u001B[0;32m---> 28\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mfunc\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n", | ||
"File \u001B[0;32m~/Dropbox/GeneralCareerDevelop/PersonalBrand/GithubRepositories/ProSelfLC_tpami2release/src/proselflc/trainer/trainer_cnn_vision_derivedgrad.py:529\u001B[0m, in \u001B[0;36mTrainer.evaluation\u001B[0;34m(self, dataloader)\u001B[0m\n\u001B[1;32m 523\u001B[0m loss \u001B[38;5;241m=\u001B[39m cross_entropy(\n\u001B[1;32m 524\u001B[0m pred_probs\u001B[38;5;241m=\u001B[39mpred_probs,\n\u001B[1;32m 525\u001B[0m target_probs\u001B[38;5;241m=\u001B[39mlabels,\n\u001B[1;32m 526\u001B[0m )\n\u001B[1;32m 527\u001B[0m \u001B[38;5;66;03m# #############################\u001B[39;00m\n\u001B[0;32m--> 529\u001B[0m test_loss \u001B[38;5;241m+\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[43mloss\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mitem\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 530\u001B[0m _, preds \u001B[38;5;241m=\u001B[39m pred_probs\u001B[38;5;241m.\u001B[39mmax(\u001B[38;5;241m1\u001B[39m)\n\u001B[1;32m 531\u001B[0m _, annotations \u001B[38;5;241m=\u001B[39m labels\u001B[38;5;241m.\u001B[39mmax(\u001B[38;5;241m1\u001B[39m)\n", | ||
"\u001B[0;31mKeyboardInterrupt\u001B[0m: " | ||
] | ||
} | ||
], | ||
"source": [ | ||
"trainer.train()\n", | ||
"torch.save(\n", | ||
" trainer.network,\n", | ||
" params[\"summary_writer_dir\"] + \"/model.pt\",\n", | ||
")\n", | ||
"print(\n", | ||
" \"The experiment is finished with details sinked in {}\".format(\n", | ||
" params[\"summary_writer_dir\"]\n", | ||
" )\n", | ||
")" | ||
], | ||
"metadata": { | ||
"collapsed": false | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"outputs": [], | ||
"source": [], | ||
"metadata": { | ||
"collapsed": false | ||
} | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3 (ipykernel)", | ||
"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.8.10" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 1 | ||
} |