4586 lines (4585 with data), 322.8 kB
{
"cells": [
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The autoreload extension is already loaded. To reload it, use:\n",
" %reload_ext autoreload\n"
]
}
],
"source": [
"from __future__ import absolute_import\n",
"from __future__ import division\n",
"from __future__ import print_function\n",
"\n",
"\n",
"import numpy as np # linear algebra\n",
"import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n",
"import os\n",
"import datetime\n",
"import seaborn as sns\n",
"import pydicom\n",
"import time\n",
"import gc\n",
"import operator \n",
"from apex import amp \n",
"import matplotlib.pyplot as plt\n",
"import torch\n",
"import torch.nn as nn\n",
"import torch.utils.data as D\n",
"import torch.nn.functional as F\n",
"from sklearn.model_selection import KFold\n",
"from tqdm import tqdm, tqdm_notebook\n",
"from IPython.core.interactiveshell import InteractiveShell\n",
"InteractiveShell.ast_node_interactivity = \"all\"\n",
"import warnings\n",
"warnings.filterwarnings(action='once')\n",
"import pickle\n",
"%load_ext autoreload\n",
"%autoreload 2\n",
"%matplotlib inline\n",
"from skimage.io import imread,imshow\n",
"from helper import *\n",
"from apex import amp\n",
"import helper\n",
"import torchvision.models as models\n",
"import pretrainedmodels\n",
"from torch.optim import Adam\n",
"from defenitions import *"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"SEED = 8153\n",
"device=device_by_name(\"Tesla\")\n",
"#device=device_by_name(\"RTX\")\n",
"#device = \"cpu\"\n",
"torch.cuda.set_device(device)\n",
"sendmeemail=Email_Progress(my_gmail,my_pass,to_email,'se_resnet101-folds results')"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"def get_submission(test_df,pred,do_sigmoid=True):\n",
" if do_sigmoid:\n",
" func = lambda x:torch.sigmoid(x)\n",
" else:\n",
" func = lambda x:x\n",
" epidural_df=pd.DataFrame(data={'ID':'ID_'+test_df.PatientID.values+'_epidural','Label':func(pred[:,0])})\n",
" intraparenchymal_df=pd.DataFrame(data={'ID':'ID_'+test_df.PatientID.values+'_intraparenchymal','Label':func(pred[:,1])})\n",
" intraventricular_df=pd.DataFrame(data={'ID':'ID_'+test_df.PatientID.values+'_intraventricular','Label':func(pred[:,2])})\n",
" subarachnoid_df=pd.DataFrame(data={'ID':'ID_'+test_df.PatientID.values+'_subarachnoid','Label':func(pred[:,3])})\n",
" subdural_df=pd.DataFrame(data={'ID':'ID_'+test_df.PatientID.values+'_subdural','Label':func(pred[:,4])})\n",
" any_df=pd.DataFrame(data={'ID':'ID_'+test_df.PatientID.values+'_any','Label':func(pred[:,5])}) \n",
" return pd.concat([epidural_df,\n",
" intraparenchymal_df,\n",
" intraventricular_df,\n",
" subarachnoid_df,\n",
" subdural_df,\n",
" any_df]).sort_values('ID').reset_index(drop=True)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(674510, 15)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(674252, 15)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>PatientID</th>\n",
" <th>epidural</th>\n",
" <th>intraparenchymal</th>\n",
" <th>intraventricular</th>\n",
" <th>subarachnoid</th>\n",
" <th>subdural</th>\n",
" <th>any</th>\n",
" <th>PID</th>\n",
" <th>StudyI</th>\n",
" <th>SeriesI</th>\n",
" <th>WindowCenter</th>\n",
" <th>WindowWidth</th>\n",
" <th>ImagePositionZ</th>\n",
" <th>ImagePositionX</th>\n",
" <th>ImagePositionY</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>63eb1e259</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>a449357f</td>\n",
" <td>62d125e5b2</td>\n",
" <td>0be5c0d1b3</td>\n",
" <td>['00036', '00036']</td>\n",
" <td>['00080', '00080']</td>\n",
" <td>180.199951</td>\n",
" <td>-125.0</td>\n",
" <td>-8.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2669954a7</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>363d5865</td>\n",
" <td>a20b80c7bf</td>\n",
" <td>3564d584db</td>\n",
" <td>['00047', '00047']</td>\n",
" <td>['00080', '00080']</td>\n",
" <td>922.530821</td>\n",
" <td>-156.0</td>\n",
" <td>45.572849</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>52c9913b1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>9c2b4bd7</td>\n",
" <td>3e3634f8cf</td>\n",
" <td>973274ffc9</td>\n",
" <td>40</td>\n",
" <td>150</td>\n",
" <td>4.455000</td>\n",
" <td>-125.0</td>\n",
" <td>-115.063000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4e6ff6126</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>3ae81c2d</td>\n",
" <td>a1390c15c2</td>\n",
" <td>e5ccad8244</td>\n",
" <td>['00036', '00036']</td>\n",
" <td>['00080', '00080']</td>\n",
" <td>100.000000</td>\n",
" <td>-99.5</td>\n",
" <td>28.500000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>7858edd88</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>c1867feb</td>\n",
" <td>c73e81ed3a</td>\n",
" <td>28e0531b3a</td>\n",
" <td>40</td>\n",
" <td>100</td>\n",
" <td>145.793000</td>\n",
" <td>-125.0</td>\n",
" <td>-132.190000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" PatientID epidural intraparenchymal intraventricular subarachnoid \\\n",
"0 63eb1e259 0 0 0 0 \n",
"1 2669954a7 0 0 0 0 \n",
"2 52c9913b1 0 0 0 0 \n",
"3 4e6ff6126 0 0 0 0 \n",
"4 7858edd88 0 0 0 0 \n",
"\n",
" subdural any PID StudyI SeriesI WindowCenter \\\n",
"0 0 0 a449357f 62d125e5b2 0be5c0d1b3 ['00036', '00036'] \n",
"1 0 0 363d5865 a20b80c7bf 3564d584db ['00047', '00047'] \n",
"2 0 0 9c2b4bd7 3e3634f8cf 973274ffc9 40 \n",
"3 0 0 3ae81c2d a1390c15c2 e5ccad8244 ['00036', '00036'] \n",
"4 0 0 c1867feb c73e81ed3a 28e0531b3a 40 \n",
"\n",
" WindowWidth ImagePositionZ ImagePositionX ImagePositionY \n",
"0 ['00080', '00080'] 180.199951 -125.0 -8.000000 \n",
"1 ['00080', '00080'] 922.530821 -156.0 45.572849 \n",
"2 150 4.455000 -125.0 -115.063000 \n",
"3 ['00080', '00080'] 100.000000 -99.5 28.500000 \n",
"4 100 145.793000 -125.0 -132.190000 "
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_df = pd.read_csv(data_dir+'train.csv')\n",
"train_df.shape\n",
"train_df=train_df[~train_df.PatientID.isin(bad_images)].reset_index(drop=True)\n",
"train_df=train_df.drop_duplicates().reset_index(drop=True)\n",
"train_df.shape\n",
"train_df.head()"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>PatientID</th>\n",
" <th>epidural</th>\n",
" <th>intraparenchymal</th>\n",
" <th>intraventricular</th>\n",
" <th>subarachnoid</th>\n",
" <th>subdural</th>\n",
" <th>any</th>\n",
" <th>SeriesI</th>\n",
" <th>PID</th>\n",
" <th>StudyI</th>\n",
" <th>WindowCenter</th>\n",
" <th>WindowWidth</th>\n",
" <th>ImagePositionZ</th>\n",
" <th>ImagePositionX</th>\n",
" <th>ImagePositionY</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>28fbab7eb</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>ebfd7e4506</td>\n",
" <td>cf1b6b11</td>\n",
" <td>93407cadbb</td>\n",
" <td>30</td>\n",
" <td>80</td>\n",
" <td>158.458000</td>\n",
" <td>-125.0</td>\n",
" <td>-135.598000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>877923b8b</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>6d95084e15</td>\n",
" <td>ad8ea58f</td>\n",
" <td>a337baa067</td>\n",
" <td>30</td>\n",
" <td>80</td>\n",
" <td>138.729050</td>\n",
" <td>-125.0</td>\n",
" <td>-101.797981</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>a591477cb</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>8e06b2c9e0</td>\n",
" <td>ecfb278b</td>\n",
" <td>0cfe838d54</td>\n",
" <td>30</td>\n",
" <td>80</td>\n",
" <td>60.830002</td>\n",
" <td>-125.0</td>\n",
" <td>-133.300003</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>42217c898</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>e800f419cf</td>\n",
" <td>e96e31f4</td>\n",
" <td>c497ac5bad</td>\n",
" <td>30</td>\n",
" <td>80</td>\n",
" <td>55.388000</td>\n",
" <td>-125.0</td>\n",
" <td>-146.081000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>a130c4d2f</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>0.5</td>\n",
" <td>faeb7454f3</td>\n",
" <td>69affa42</td>\n",
" <td>854e4fbc01</td>\n",
" <td>30</td>\n",
" <td>80</td>\n",
" <td>33.516888</td>\n",
" <td>-125.0</td>\n",
" <td>-118.689819</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" PatientID epidural intraparenchymal intraventricular subarachnoid \\\n",
"0 28fbab7eb 0.5 0.5 0.5 0.5 \n",
"1 877923b8b 0.5 0.5 0.5 0.5 \n",
"2 a591477cb 0.5 0.5 0.5 0.5 \n",
"3 42217c898 0.5 0.5 0.5 0.5 \n",
"4 a130c4d2f 0.5 0.5 0.5 0.5 \n",
"\n",
" subdural any SeriesI PID StudyI WindowCenter WindowWidth \\\n",
"0 0.5 0.5 ebfd7e4506 cf1b6b11 93407cadbb 30 80 \n",
"1 0.5 0.5 6d95084e15 ad8ea58f a337baa067 30 80 \n",
"2 0.5 0.5 8e06b2c9e0 ecfb278b 0cfe838d54 30 80 \n",
"3 0.5 0.5 e800f419cf e96e31f4 c497ac5bad 30 80 \n",
"4 0.5 0.5 faeb7454f3 69affa42 854e4fbc01 30 80 \n",
"\n",
" ImagePositionZ ImagePositionX ImagePositionY \n",
"0 158.458000 -125.0 -135.598000 \n",
"1 138.729050 -125.0 -101.797981 \n",
"2 60.830002 -125.0 -133.300003 \n",
"3 55.388000 -125.0 -146.081000 \n",
"4 33.516888 -125.0 -118.689819 "
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"test_df = pd.read_csv(data_dir+'test.csv')\n",
"test_df.head()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"split_sid = train_df.PID.unique()\n",
"splits=list(KFold(n_splits=3,shuffle=True, random_state=SEED).split(split_sid))\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"pickle_file=open(outputs_dir+\"PID_splits.pkl\",'wb')\n",
"pickle.dump((split_sid,splits),pickle_file,protocol=4)\n",
"pickle_file.close()\n"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"def my_loss(y_pred,y_true,weights):\n",
" if len(y_pred.shape)==len(y_true.shape):\n",
" loss = F.binary_cross_entropy_with_logits(y_pred,y_true,weights.expand_as(y_pred))\n",
" else:\n",
" loss0 = F.binary_cross_entropy_with_logits(y_pred,y_true[...,0],weights.repeat(y_pred.shape[0],1),reduction='none')\n",
" loss1 = F.binary_cross_entropy_with_logits(y_pred,y_true[...,1],weights.repeat(y_pred.shape[0],1),reduction='none')\n",
" loss = (y_true[...,2]*loss0+(1.0-y_true[...,2])*loss1).mean() \n",
" return loss"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"class parameter_scheduler():\n",
" def __init__(self,model,do_first=['classifier'],num_epoch=1):\n",
" self.model=model\n",
" self.do_first = do_first\n",
" self.num_epoch=num_epoch\n",
" def __call__(self,epoch):\n",
" if epoch>=self.num_epoch:\n",
" for n,p in self.model.named_parameters():\n",
" p.requires_grad=True\n",
" else:\n",
" for n,p in self.model.named_parameters():\n",
" p.requires_grad= any(nd in n for nd in self.do_first)\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"def get_optimizer_parameters(model,klr):\n",
" param_optimizer = list(model.named_parameters())\n",
" num_blocks=5\n",
" no_decay=['bias']\n",
" optimizer_grouped_parameters=[\n",
" {'params': [p for n, p in param_optimizer if (not any(nd in n for nd in no_decay) and ('classifier' in n))], 'lr':klr*2e-4,'weight_decay': 0.01},\n",
" {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay) and ('classifier' in n)], 'lr':klr*2e-4, 'weight_decay': 0.0}\n",
" ]\n",
" optimizer_grouped_parameters.extend([\n",
" {'params': [p for n, p in param_optimizer if (not any(nd in n for nd in no_decay) and ('wso' in n))], 'lr':klr*5e-6,'weight_decay': 0.01},\n",
" {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay) and ('wso' in n)], 'lr':klr*5e-6, 'weight_decay': 0.0}\n",
" ])\n",
" for i in range(num_blocks):\n",
" optimizer_grouped_parameters.extend([\n",
" {'params': [p for n, p in param_optimizer if (not any(nd in n for nd in no_decay) and ('layer{}'.format(i) in n))], 'lr':klr*(2.0**i)*1e-5,'weight_decay': 0.01},\n",
" {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay) and ('layer{}'.format(i) in n)], 'lr':klr*(2.0**i)*1e-5, 'weight_decay': 0.0}\n",
" ])\n",
" return(optimizer_grouped_parameters)\n",
"\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/plain": [
"<torch._C.Generator at 0x7fa3217195d0>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(449982,)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(224270,)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Downloading: \"http://data.lip6.fr/cadene/pretrainedmodels/se_resnet101-7e38fcc6.pth\" to /home/reina/.cache/torch/checkpoints/se_resnet101-7e38fcc6.pth\n",
"100%|██████████| 189M/189M [09:16<00:00, 355kB/s] \n"
]
},
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+/PTigAQAAQBj0/qnN4QYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCJGtFTj+19zxlwAAAAAElFTkSuQmCC\" width=\"NaN\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=5), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=14062), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=7009), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.08624058152934186, 'val_loss': 0.09350918875872995}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=14062), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=7009), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.09151584892644021, 'val_loss': 0.08955957706256816}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=14062), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=7009), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.08576221452015315, 'val_loss': 0.08700796931223057}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=14062), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=7009), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.0817824281602344, 'val_loss': 0.08581706431311914}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=14062), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%matplotlib nbagg\n",
"\n",
"num_split=0\n",
"np.random.seed(SEED+num_split)\n",
"torch.manual_seed(SEED+num_split)\n",
"torch.cuda.manual_seed(SEED+num_split)\n",
"#torch.backends.cudnn.deterministic = True\n",
"idx_train = train_df[train_df.PID.isin(set(split_sid[splits[num_split][0]]))].index.values\n",
"idx_validate = train_df[train_df.PID.isin(set(split_sid[splits[num_split][1]]))].index.values\n",
"idx_train.shape\n",
"idx_validate.shape\n",
"\n",
"klr=1\n",
"batch_size=32\n",
"num_workers=12\n",
"num_epochs=5\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" )\n",
"\n",
"_=model.to(device)\n",
"weights = torch.tensor([1.,1.,1.,1.,1.,2.],device=device)\n",
"loss_func=my_loss\n",
"targets_dataset=D.TensorDataset(torch.tensor(train_df[hemorrhage_types].values,dtype=torch.float))\n",
"transform=MyTransform(mean_change=15,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.2,0.2),\n",
" rotate=30,\n",
" out_size=512,\n",
" shift=10,\n",
" normal=False)\n",
"imagedataset = ImageDataset(train_df,transform=transform.random,base_path=train_images_dir,\n",
" window_eq=False,equalize=False,rescale=True)\n",
"transform_val=MyTransform(out_size=512)\n",
"imagedataset_val = ImageDataset(train_df,transform=transform_val.random,base_path=train_images_dir,\n",
" window_eq=False,equalize=False,rescale=True)\n",
"combined_dataset=DatasetCat([imagedataset,targets_dataset])\n",
"combined_dataset_val=DatasetCat([imagedataset_val,targets_dataset])\n",
"optimizer_grouped_parameters=get_optimizer_parameters(model,klr)\n",
"sampling=simple_sampler(train_df[hemorrhage_types].values[idx_train],0.25)\n",
"sample_ratio=1.0\n",
"train_dataset=D.Subset(combined_dataset,idx_train)\n",
"validate_dataset=D.Subset(combined_dataset_val,idx_validate)\n",
"num_train_optimization_steps = num_epochs*(sample_ratio*len(train_dataset)//batch_size+int(len(train_dataset)%batch_size>0))\n",
"fig,ax = plt.subplots(figsize=(10,7))\n",
"gr=loss_graph(fig,ax,num_epochs,int(num_train_optimization_steps/num_epochs)+1,limits=(0.05,0.2))\n",
"sched=WarmupExpCosineWithWarmupRestartsSchedule( t_total=num_train_optimization_steps, cycles=num_epochs,tau=1)\n",
"#param_optimizer = model.parameters()\n",
"#optimizer = torch.optim.Adam(param_optimizer, lr=klr*6e-5)\n",
"optimizer = BertAdam(optimizer_grouped_parameters,lr=klr*1e-3,schedule=sched)\n",
"model, optimizer = amp.initialize(model, optimizer, opt_level=\"O1\",verbosity=0)\n",
"history,best_model= model_train(model,\n",
" optimizer,\n",
" train_dataset,\n",
" batch_size,\n",
" num_epochs,\n",
" loss_func,\n",
" weights=weights,\n",
" do_apex=False,\n",
" model_apexed=True,\n",
" validate_dataset=validate_dataset,\n",
" param_schedualer=None,\n",
" weights_data=None,\n",
" metric=None,\n",
" return_model=True,\n",
" num_workers=num_workers,\n",
" sampler=None,\n",
" pre_process = None,\n",
" graph=gr,\n",
" call_progress=sendmeemail)\n",
"\n",
"torch.save(model.state_dict(), models_dir+models_format.format(model_name,version,num_split))\n",
"torch.save(best_model.state_dict(), models_dir+models_format.format(model_name,version+'_best',num_split))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/plain": [
"<torch._C.Generator at 0x7f6d9b3f8650>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(449019,)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(225233,)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVURT/P5QMUhb1KA5JAAAAAElFTkSuQmCC\" width=\"720\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "9b0d0326667a4d59835308cca97fb20e",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=6), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "2874de2909784e37b4de9eb74e88f75d",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=14032), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "9c5025d499ec42dd9100726a368a1f74",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=7039), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.09014417967473456, 'val_loss': 0.09117387986844765}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "938f9926af2a4a229517610761376301",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=14032), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%matplotlib nbagg\n",
"\n",
"num_split=1\n",
"np.random.seed(SEED+num_split)\n",
"torch.manual_seed(SEED+num_split)\n",
"torch.cuda.manual_seed(SEED+num_split)\n",
"#torch.backends.cudnn.deterministic = True\n",
"idx_train = train_df[train_df.PID.isin(set(split_sid[splits[num_split][0]]))].index.values\n",
"idx_validate = train_df[train_df.PID.isin(set(split_sid[splits[num_split][1]]))].index.values\n",
"idx_train.shape\n",
"idx_validate.shape\n",
"\n",
"klr=1\n",
"batch_size=32\n",
"num_workers=12\n",
"num_epochs=6\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" )\n",
"\n",
"_=model.to(device)\n",
"weights = torch.tensor([1.,1.,1.,1.,1.,2.],device=device)\n",
"loss_func=my_loss\n",
"targets_dataset=D.TensorDataset(torch.tensor(train_df[hemorrhage_types].values,dtype=torch.float))\n",
"transform=MyTransform(mean_change=15,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.2,0.2),\n",
" rotate=30,\n",
" out_size=512,\n",
" shift=10,\n",
" normal=False)\n",
"imagedataset = ImageDataset(train_df,transform=transform.random,base_path=train_images_dir,\n",
" window_eq=False,equalize=False,rescale=True)\n",
"transform_val=MyTransform(out_size=512)\n",
"imagedataset_val = ImageDataset(train_df,transform=transform_val.random,base_path=train_images_dir,\n",
" window_eq=False,equalize=False,rescale=True)\n",
"combined_dataset=DatasetCat([imagedataset,targets_dataset])\n",
"combined_dataset_val=DatasetCat([imagedataset_val,targets_dataset])\n",
"optimizer_grouped_parameters=get_optimizer_parameters(model,klr)\n",
"sampling=simple_sampler(train_df[hemorrhage_types].values[idx_train],0.25)\n",
"sample_ratio=1.0\n",
"train_dataset=D.Subset(combined_dataset,idx_train)\n",
"validate_dataset=D.Subset(combined_dataset_val,idx_validate)\n",
"num_train_optimization_steps = num_epochs*(sample_ratio*len(train_dataset)//batch_size+int(len(train_dataset)%batch_size>0))\n",
"fig,ax = plt.subplots(figsize=(10,7))\n",
"gr=loss_graph(fig,ax,num_epochs,int(num_train_optimization_steps/num_epochs)+1,limits=(0.05,0.2))\n",
"sched=WarmupExpCosineWithWarmupRestartsSchedule( t_total=num_train_optimization_steps, cycles=num_epochs,tau=1)\n",
"#param_optimizer = model.parameters()\n",
"#optimizer = torch.optim.Adam(param_optimizer, lr=klr*6e-5)\n",
"optimizer = BertAdam(optimizer_grouped_parameters,lr=klr*1e-3,schedule=sched)\n",
"model, optimizer = amp.initialize(model, optimizer, opt_level=\"O1\",verbosity=0)\n",
"history,best_model= model_train(model,\n",
" optimizer,\n",
" train_dataset,\n",
" batch_size,\n",
" num_epochs,\n",
" loss_func,\n",
" weights=weights,\n",
" do_apex=False,\n",
" model_apexed=True,\n",
" validate_dataset=validate_dataset,\n",
" param_schedualer=None,\n",
" weights_data=None,\n",
" metric=None,\n",
" return_model=True,\n",
" num_workers=num_workers,\n",
" sampler=None,\n",
" pre_process = None,\n",
" graph=gr,\n",
" call_progress=sendmeemail)\n",
"\n",
"torch.save(model.state_dict(), models_dir+models_format.format(model_name,version,num_split))\n",
"torch.save(best_model.state_dict(), models_dir+models_format.format(model_name,version+'_best',num_split))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/plain": [
"<torch._C.Generator at 0x7f61b97615f0>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(449503,)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(224749,)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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Lw2zF63MV+llaV94vx/Hr3sAAED3EEADtPGw5JfOvDs87fPnhRtnLy5MjaYAN71M/fc3zBly4rQaq9a1NhBetHJd7hpj350n5a56O287IVz2iReH6z790nyl+1S7gmLnyRNy1+djv7s1d1tU+zhbs776g/8+8cfbCrfpl047qWGgtTU8ljpFE84+BQCAjiGABugAR3//6vCra+8PP73ivsLL1P/7D7c1ZKXFOXc+lLvO9KKDdstdbcaOGR0mljhwr1s04LmJR1U7AV28zqVccF28C3eoyel62AHd2dZsGPlXSgAAAI0jgAboEJ85447w9XOn5XshvPcXW+90bneNmNpupOccsHPu6vdfLz8wd+2jmt3e6WNRTVBb7Z7wTVUm1d+56J7cNU+bPcyo0cIVjX2lBAAAMLIE0AC0TKODweEG2vvsPDl39Xv38/bPXePst8vwfl/VvFv+6QdXVzXdXCqALhU23/rA0txV9sNLi/b7Du9DWFIrDtekedLBpQAAQPcQQAM9b1QTlkB/9KXNn4rdcdK43PWu4caM//78/XJXnw+86ICwzfjGr/I46DFTclefagLYG2Y/Es66fV6+V16pX+qOuVuHzfOWbjlkMBnJDLjatSHQKs34ewYAADqFABqghE/840Fhrx23CV89+qnhM696Yr5avRccuEvummf7bTovgG63wdT9d902HLH/Tvlebd73wgPCfx71+HwvhJPe+szcDd9wJ3ir/dnFoXEppX4vGxuY8DZjX3O7rXrpFWs3bAy3zllS9TqWZku/j3sXrGiLx8O6jd13sCIAAFRLAA30vFElBtM+8KLHhys+eVR4y2F717WmYXSpX7TBZi1alTuG49fvOSLsvt3EfK96x77i4DBp/Nh8L4RXPW2PcN/xr8z3hme4cdnmKrOuaqYyS2WJ9y/e+rE3ZnR9j/lmZJUbBdAtl0LetNbldSddGT71p9vz1ZH1zlOvDy854dLwP38e+d/PRVPn5w4AAHqPABroKf/87L1zV72xdQRrrQigK7lyxsLctZfhTLv+7Mr7wr+den247YEl+UpjjI4f30YcRpiMih/31z39sYX+qXtuX7itx/I163NXnzS1vHrdxnxveEpNs37yj7flbovtJg6eyB/JCHjukqEnu2msNGl8W94D/tsb5hQ+Bv/vR1eHd8fP2RVrNxSut9L8ZWvCZdMX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\" width=\"720\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "ce86aa1a1d91471ea9df03e5ef36ba46",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=5), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "bfb9c168a858413dae719d8f8ebe0c52",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=18730), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "da40a96ca5c04ae092c1215eee7468a4",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=9365), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.09540571817950666, 'val_loss': 0.09041333776882274}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d6135a25e58745828520fcc13b5107b2",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=18730), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "862cfa133b514ee3a44c6ee1a423d216",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=9365), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.08907948793503523, 'val_loss': 0.08441449151845778}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "057233b9cd564fbcbc4001d5d05bdd73",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=18730), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "2c81c248b19349c6909b0c8b7dd95ff4",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=9365), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.08323106470473873, 'val_loss': 0.083845243132171}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d6f2af85b1294ef28a664be02fa11095",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=18730), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d2ae47d25279493dbecc944aa744b801",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=9365), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.08504001906753503, 'val_loss': 0.08262787327739458}\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "801314597e194ccc9b77ece0fe6d4f8f",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=18730), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "39971b514bc54eddb640c3ca2a482550",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=9365), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'loss': 0.0806176294933715, 'val_loss': 0.08195277172063954}\n",
"\n",
"[0.08195277 0.08262787]\n"
]
}
],
"source": [
"%matplotlib nbagg\n",
"\n",
"num_split=2\n",
"np.random.seed(SEED+num_split)\n",
"torch.manual_seed(SEED+num_split)\n",
"torch.cuda.manual_seed(SEED+num_split)\n",
"#torch.backends.cudnn.deterministic = True\n",
"idx_train = train_df[train_df.PID.isin(set(split_sid[splits[num_split][0]]))].index.values\n",
"idx_validate = train_df[train_df.PID.isin(set(split_sid[splits[num_split][1]]))].index.values\n",
"idx_train.shape\n",
"idx_validate.shape\n",
"\n",
"klr=0.75\n",
"batch_size=24\n",
"num_workers=12\n",
"num_epochs=5\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" )\n",
"\n",
"_=model.to(device)\n",
"weights = torch.tensor([1.,1.,1.,1.,1.,2.],device=device)\n",
"loss_func=my_loss\n",
"targets_dataset=D.TensorDataset(torch.tensor(train_df[hemorrhage_types].values,dtype=torch.float))\n",
"transform=MyTransform(mean_change=15,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.2,0.2),\n",
" rotate=30,\n",
" out_size=512,\n",
" shift=10,\n",
" normal=False)\n",
"imagedataset = ImageDataset(train_df,transform=transform.random,base_path=train_images_dir,\n",
" window_eq=False,equalize=False,rescale=True)\n",
"transform_val=MyTransform(out_size=512)\n",
"imagedataset_val = ImageDataset(train_df,transform=transform_val.random,base_path=train_images_dir,\n",
" window_eq=False,equalize=False,rescale=True)\n",
"combined_dataset=DatasetCat([imagedataset,targets_dataset])\n",
"combined_dataset_val=DatasetCat([imagedataset_val,targets_dataset])\n",
"optimizer_grouped_parameters=get_optimizer_parameters(model,klr)\n",
"sampling=simple_sampler(train_df[hemorrhage_types].values[idx_train],0.25)\n",
"sample_ratio=1.0\n",
"train_dataset=D.Subset(combined_dataset,idx_train)\n",
"validate_dataset=D.Subset(combined_dataset_val,idx_validate)\n",
"num_train_optimization_steps = num_epochs*(sample_ratio*len(train_dataset)//batch_size+int(len(train_dataset)%batch_size>0))\n",
"fig,ax = plt.subplots(figsize=(10,7))\n",
"gr=loss_graph(fig,ax,num_epochs,int(num_train_optimization_steps/num_epochs)+1,limits=(0.05,0.2))\n",
"sched=WarmupExpCosineWithWarmupRestartsSchedule( t_total=num_train_optimization_steps, cycles=num_epochs,tau=1)\n",
"#param_optimizer = model.parameters()\n",
"#optimizer = torch.optim.Adam(param_optimizer, lr=klr*6e-5)\n",
"optimizer = BertAdam(optimizer_grouped_parameters,lr=klr*1e-3,schedule=sched)\n",
"model, optimizer = amp.initialize(model, optimizer, opt_level=\"O1\",verbosity=0)\n",
"history,best_model= model_train(model,\n",
" optimizer,\n",
" train_dataset,\n",
" batch_size,\n",
" num_epochs,\n",
" loss_func,\n",
" weights=weights,\n",
" do_apex=False,\n",
" model_apexed=True,\n",
" validate_dataset=validate_dataset,\n",
" param_schedualer=None,\n",
" weights_data=None,\n",
" metric=None,\n",
" return_model=True,\n",
" num_workers=num_workers,\n",
" sampler=None,\n",
" pre_process = None,\n",
" graph=gr,\n",
" call_progress=sendmeemail)\n",
"\n",
"torch.save(model.state_dict(), models_dir+models_format.format(model_name,version,num_split))\n",
"torch.save(best_model.state_dict(), models_dir+models_format.format(model_name,version+'_best',num_split))"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "34f93b589cc944fbba90d451978944fb",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=6546), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"num_split = 0\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" return_features=True\n",
" )\n",
"model.load_state_dict(torch.load(models_dir+models_format.format(model_name,version,num_split),map_location=torch.device(device)))\n",
"_=model.to(device)\n",
"transform=MyTransform(mean_change=15,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.2,0.2),\n",
" rotate=30,\n",
" out_size=512,\n",
" shift=0,\n",
" normal=False)\n",
"transform_val=MyTransform(out_size=512)\n",
"indexes=np.arange(test_df.shape[0]).repeat(8)\n",
"imagedataset_test=D.Subset(ImageDataset(test_df,transform=transform.random,base_path=test_images_dir,\n",
" window_eq=False,equalize=False,rescale=True),indexes)\n",
"pred,features = model_run(model,imagedataset_test,do_apex=True,batch_size=96,num_workers=18)\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'features_test',num_split),'wb')\n",
"pickle.dump(features,pickle_file,protocol=4)\n",
"pickle_file.close()\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'predictions_test',num_split),'wb')\n",
"pickle.dump(pred,pickle_file,protocol=4)\n",
"pickle_file.close()"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "05daaabcb23e45b3a957ce1e962f4c65",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=6546), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"num_split = 1\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" return_features=True\n",
" )\n",
"model.load_state_dict(torch.load(models_dir+models_format.format(model_name,version,num_split),map_location=torch.device(device)))\n",
"_=model.to(device)\n",
"transform=MyTransform(mean_change=15,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.2,0.2),\n",
" rotate=20,\n",
" out_size=512,\n",
" shift=0,\n",
" normal=False)\n",
"transform_val=MyTransform(out_size=512)\n",
"indexes=np.arange(test_df.shape[0]).repeat(8)\n",
"imagedataset_test=D.Subset(ImageDataset(test_df,transform=transform.random,base_path=test_images_dir,\n",
" window_eq=False,equalize=False,rescale=True),indexes)\n",
"pred,features = model_run(model,imagedataset_test,do_apex=True,batch_size=96,num_workers=18)\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'features_test',num_split),'wb')\n",
"pickle.dump(features,pickle_file,protocol=4)\n",
"pickle_file.close()\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'predictions_test',num_split),'wb')\n",
"pickle.dump(pred,pickle_file,protocol=4)\n",
"pickle_file.close()"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "52fdae8dd0844bff9752c50e22b65882",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=6546), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"num_split = 2\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" return_features=True\n",
" )\n",
"model.load_state_dict(torch.load(models_dir+models_format.format(model_name,version,num_split),map_location=torch.device(device)))\n",
"_=model.to(device)\n",
"transform=MyTransform(mean_change=10,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.15,0.15),\n",
" rotate=20,\n",
" out_size=512,\n",
" shift=0,\n",
" normal=False)\n",
"transform_val=MyTransform(out_size=512)\n",
"indexes=np.arange(test_df.shape[0]).repeat(8)\n",
"imagedataset_test=D.Subset(ImageDataset(test_df,transform=transform.random,base_path=test_images_dir,\n",
" window_eq=False,equalize=False,rescale=True),indexes)\n",
"pred,features = model_run(model,imagedataset_test,do_apex=True,batch_size=96,num_workers=18)\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'features_test',num_split),'wb')\n",
"pickle.dump(features,pickle_file,protocol=4)\n",
"pickle_file.close()\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'predictions_test',num_split),'wb')\n",
"pickle.dump(pred,pickle_file,protocol=4)\n",
"pickle_file.close()"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=6546), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"num_split = 0\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" return_features=True\n",
" )\n",
"model.load_state_dict(torch.load(models_dir+models_format.format(model_name,version,num_split),map_location=torch.device(device)))\n",
"_=model.to(device)\n",
"transform=MyTransform(mean_change=10,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.15,0.15),\n",
" rotate=20,\n",
" out_size=512,\n",
" shift=0,\n",
" normal=False)\n",
"transform_val=MyTransform(out_size=512)\n",
"indexes=np.arange(test_df.shape[0]).repeat(8)\n",
"imagedataset_test=D.Subset(ImageDataset(test_df,transform=transform.random,base_path=test_images_dir,\n",
" window_eq=False,equalize=False,rescale=True),indexes)\n",
"pred,features = model_run(model,imagedataset_test,do_apex=True,batch_size=96,num_workers=18)\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'features_test',num_split),'wb')\n",
"pickle.dump(features,pickle_file,protocol=4)\n",
"pickle_file.close()\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'predictions_test',num_split),'wb')\n",
"pickle.dump(pred,pickle_file,protocol=4)\n",
"pickle_file.close()"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d93176fab6204690b67fe9fcbc49739f",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=6546), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"num_split = 1\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" return_features=True\n",
" )\n",
"model.load_state_dict(torch.load(models_dir+models_format.format(model_name,version,num_split),map_location=torch.device(device)))\n",
"_=model.to(device)\n",
"transform=MyTransform(mean_change=10,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.15,0.15),\n",
" rotate=20,\n",
" out_size=512,\n",
" shift=0,\n",
" normal=False)\n",
"transform_val=MyTransform(out_size=512)\n",
"indexes=np.arange(test_df.shape[0]).repeat(8)\n",
"imagedataset_test=D.Subset(ImageDataset(test_df,transform=transform.random,base_path=test_images_dir,\n",
" window_eq=False,equalize=False,rescale=True),indexes)\n",
"pred,features = model_run(model,imagedataset_test,do_apex=True,batch_size=96,num_workers=18)\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'features_test',num_split),'wb')\n",
"pickle.dump(features,pickle_file,protocol=4)\n",
"pickle_file.close()\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'predictions_test',num_split),'wb')\n",
"pickle.dump(pred,pickle_file,protocol=4)\n",
"pickle_file.close()"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "187e744e36614fab86a4a294251f5f09",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=6546), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"num_split = 2\n",
"model_name,version = 'se_resnet101' , 'classifier_splits'\n",
"model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" return_features=True\n",
" )\n",
"model.load_state_dict(torch.load(models_dir+models_format.format(model_name,version,num_split),map_location=torch.device(device)))\n",
"_=model.to(device)\n",
"transform=MyTransform(mean_change=10,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.15,0.15),\n",
" rotate=20,\n",
" out_size=512,\n",
" shift=0,\n",
" normal=False)\n",
"transform_val=MyTransform(out_size=512)\n",
"indexes=np.arange(test_df.shape[0]).repeat(8)\n",
"imagedataset_test=D.Subset(ImageDataset(test_df,transform=transform.random,base_path=test_images_dir,\n",
" window_eq=False,equalize=False,rescale=True),indexes)\n",
"pred,features = model_run(model,imagedataset_test,do_apex=True,batch_size=96,num_workers=18)\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'features_test',num_split),'wb')\n",
"pickle.dump(features,pickle_file,protocol=4)\n",
"pickle_file.close()\n",
"pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'predictions_test',num_split),'wb')\n",
"pickle.dump(pred,pickle_file,protocol=4)\n",
"pickle_file.close()"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "c14e41f4d70a42748e01e22f6bcdab17",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=28094), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
},
{
"data": {
"text/plain": [
"tensor(0.0821)"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "88b4c86cd84c46e5bf455092d6d83050",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=28094), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
},
{
"data": {
"text/plain": [
"tensor(0.0804)"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "8e0b0f4d32c342bb96330dbe7d3b0228",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=28094), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
},
{
"data": {
"text/plain": [
"tensor(0.0783)"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"for num_split in range(3):\n",
" idx_validate = train_df[train_df.PID.isin(set(split_sid[splits[num_split][1]]))].index.values\n",
" model_name,version = 'se_resnet101' , 'classifier_splits'\n",
" model = MySENet(pretrainedmodels.__dict__['se_resnet101'](num_classes=1000, pretrained='imagenet'),\n",
" len(hemorrhage_types),\n",
" num_channels=3,\n",
" dropout=0.2,\n",
" wso=((40,80),(80,200),(40,400)),\n",
" dont_do_grad=[],\n",
" extra_pool=8,\n",
" return_features=True\n",
" )\n",
" model.load_state_dict(torch.load(models_dir+models_format.format(model_name,version,num_split),map_location=torch.device(device)))\n",
" _=model.to(device)\n",
" transform=MyTransform(mean_change=10,\n",
" std_change=0,\n",
" flip=True,\n",
" zoom=(0.15,0.15),\n",
" rotate=20,\n",
" out_size=512,\n",
" shift=0,\n",
" normal=False)\n",
" transform_val=MyTransform(out_size=512)\n",
" indexes=np.arange(train_df.shape[0]).repeat(4)\n",
" train_dataset=D.Subset(ImageDataset(train_df,transform=transform.random,base_path=train_images_dir,\n",
" window_eq=False,equalize=False,rescale=True),indexes)\n",
" pred,features = model_run(model,train_dataset,do_apex=True,batch_size=96,num_workers=14)\n",
"\n",
" pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'features_train_tta',num_split),'wb')\n",
" pickle.dump(features,pickle_file,protocol=4)\n",
" pickle_file.close()\n",
"\n",
" pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'predictions_train_tta',num_split),'wb')\n",
" pickle.dump(pred,pickle_file,protocol=4)\n",
" pickle_file.close()\n",
"\n",
"\n",
" my_loss(pred[(idx_validate*4+np.arange(4)[:,None]).transpose(1,0)].mean(1),\n",
" torch.tensor(train_df[hemorrhage_types].values[idx_validate],dtype=torch.float),\n",
" torch.tensor([1.,1.,1.,1.,1.,2.]))"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "f8a7ace928d04d278fcc24167c24934b",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(IntProgress(value=0, max=3), HTML(value='')))"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
},
{
"data": {
"text/plain": [
"torch.Size([78545, 24, 6])"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"preds=[]\n",
"for i in tqdm_notebook(range(3)):\n",
" model_name,version, num_split = 'se_resnext101_32x4d' , 'classifier_splits',i\n",
" pickle_file=open(outputs_dir+outputs_format.format(model_name,version,'predictions_test',num_split),'rb')\n",
" pred=pickle.load(pickle_file)\n",
" pickle_file.close()\n",
" preds.append(pred[(np.arange(pred.shape[0]).reshape(pred.shape[0]//8,8))])\n",
"predss = torch.cat(preds,1)\n",
"predss.shape"
]
},
{
"cell_type": "code",
"execution_count": 76,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"78545.0"
]
},
"execution_count": 76,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"torch.Size([78545, 6])"
]
},
"execution_count": 76,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"torch.sigmoid(pred).mean(1)\n",
".mean(1).shape"
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"torch.Size([628360])"
]
},
"execution_count": 70,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pred[(np.arange(pred.shape[0]).reshape()).transpose(1,0)].mean(1)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>ID</th>\n",
" <th>Label</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>ID_000012eaf_any</td>\n",
" <td>0.007760</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>ID_000012eaf_epidural</td>\n",
" <td>0.000215</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>ID_000012eaf_intraparenchymal</td>\n",
" <td>0.000772</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>ID_000012eaf_intraventricular</td>\n",
" <td>0.000473</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>ID_000012eaf_subarachnoid</td>\n",
" <td>0.001052</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>ID_000012eaf_subdural</td>\n",
" <td>0.005370</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>ID_0000ca2f6_any</td>\n",
" <td>0.004738</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>ID_0000ca2f6_epidural</td>\n",
" <td>0.000074</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>ID_0000ca2f6_intraparenchymal</td>\n",
" <td>0.000576</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>ID_0000ca2f6_intraventricular</td>\n",
" <td>0.000235</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>ID_0000ca2f6_subarachnoid</td>\n",
" <td>0.001335</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>ID_0000ca2f6_subdural</td>\n",
" <td>0.001597</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" ID Label\n",
"0 ID_000012eaf_any 0.007760\n",
"1 ID_000012eaf_epidural 0.000215\n",
"2 ID_000012eaf_intraparenchymal 0.000772\n",
"3 ID_000012eaf_intraventricular 0.000473\n",
"4 ID_000012eaf_subarachnoid 0.001052\n",
"5 ID_000012eaf_subdural 0.005370\n",
"6 ID_0000ca2f6_any 0.004738\n",
"7 ID_0000ca2f6_epidural 0.000074\n",
"8 ID_0000ca2f6_intraparenchymal 0.000576\n",
"9 ID_0000ca2f6_intraventricular 0.000235\n",
"10 ID_0000ca2f6_subarachnoid 0.001335\n",
"11 ID_0000ca2f6_subdural 0.001597"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(471270, 2)"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"submission_df=get_submission(test_df,torch.sigmoid(predss).mean(1),False)\n",
"submission_df.head(12)\n",
"submission_df.shape\n",
"sub_num=41\n",
"submission_df.to_csv('/media/hd/notebooks/data/RSNA/submissions/submission{}.csv'.format(sub_num),\n",
" index=False, columns=['ID','Label'])\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.6.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}