├── README.md ├── data.py ├── img ├── dice-20epochs-example.png ├── segmentation-example1.png └── u-net-architecture.png └── train.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # Liver segmentation project 2 |

Purpose : the objective is to automatically delineate liver on patient scans by computer vision. The method used is from the research paper "U-Net: Convolutional Networks for Biomedical 3 | Image Segmentation"

4 |

In this project we apply the method to the segmentation of liver images as described in this research paper https://arxiv.org/pdf/1702.05970.pdf. 5 |

6 | 7 | ## Data 8 | The data is available in NifTi format here. 9 | This dataset consists of 20 medical examinations in 3D, we have the source image as well as a mask of segmentation of the liver for each of these examinations. We use the nibabel library (http://nipy.org/nibabel/) to read associated images and masks. 10 | 11 | ## Model 12 |

Train a U-net architecture, a fully convolutional network. The principle of this architecture is to add to a usual contracting network, layers with upsampling operators instead of pooling. This allow the network to learn context (contracting path), then localization (expansive path). Context information is propagated to higher resolution layers thanks to skip-connexions. So we have images of the same size as input

13 | 14 | 15 |

16 | 17 | 18 |

in the data.py script, we perform axial cuts of our 3D images. So 256x256 images are input to the network

19 | 20 | ## Evaluation 21 | 22 | As metric we use the Dice coefficient (which is quite similar to the Jaccard coefficient) 23 | 24 | ## How it works 25 |
  1. First download the data whose link has been given previously
  2. 26 |
  3. Create a 'raw' folder 27 |
  4. In the 'raw' folder, create a 'test' folder, and a 'train' folder 28 |
  5. Then separate the data in two sets (train and test, typically we use 13 samples for the train set and 7 for the test set) and put them in the corresponding directories that you can find in the 'raw' folder
  6. 29 |
  7. Run data.py , this will save the train and test data in npy format
  8. 30 |
  9. Finally launch the notebook, you can observe a curve of the Dice coef according to the number of epochs and visualize your predictions in the folder 'preds'
  10. 31 |
32 | (Feel free to play with the parameters : learning rate, optimizer etc.) 33 | 34 | ## Some results 35 | 36 | 37 |

Finally we get the predictions for a particular cut (delineated in yellow)

38 |

39 | 40 |

The evolution of the Dice coef for 20 epochs, this plot shows consistent results and a test Dice coef reaching almost 0.87

41 |

42 | 43 | ## Acknowledgments 44 | 45 | * Ultrasound nerve segmentation 46 | * Institut de recherche contre les cancers de l'appareil digestif 47 | -------------------------------------------------------------------------------- /data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import nibabel 4 | 5 | data_path = 'raw/' 6 | #we will undersample our training 2D images later (for memory and speed) 7 | image_rows = int(512/2) 8 | image_cols = int(512/2) 9 | 10 | 11 | def create_train_data(): 12 | print('-'*30) 13 | print('Creating training data...') 14 | print('-'*30) 15 | train_data_path = os.path.join(data_path, 'train') 16 | images = os.listdir(train_data_path) 17 | #training images 18 | imgs_train = [] 19 | #training masks (corresponding to the liver) 20 | masks_train = [] 21 | #file names corresponding to training masks 22 | training_masks = images[::2] 23 | #file names corresponding to training images 24 | training_images = images[1::2] 25 | 26 | for liver, orig in zip(training_masks, training_images): 27 | #we load 3D training mask (shape=(512,512,129)) 28 | training_mask = nibabel.load(os.path.join(train_data_path, liver)) 29 | #we load 3D training image 30 | training_image = nibabel.load(os.path.join(train_data_path, orig)) 31 | 32 | for k in range(training_mask.shape[2]): 33 | #axial cuts are made along the z axis with undersampling 34 | mask_2d = np.array(training_mask.get_data()[::2, ::2, k]) 35 | image_2d = np.array(training_image.get_data()[::2, ::2, k]) 36 | #we only recover the 2D sections containing the liver 37 | #if mask_2d contains only 0, it means that there is no liver 38 | if len(np.unique(mask_2d)) != 1: 39 | masks_train.append(mask_2d) 40 | imgs_train.append(image_2d) 41 | 42 | imgs = np.ndarray( 43 | (len(imgs_train), image_rows, image_cols), dtype=np.uint8 44 | ) 45 | imgs_mask = np.ndarray( 46 | (len(masks_train), image_rows, image_cols), dtype=np.uint8 47 | ) 48 | 49 | for index, img in enumerate(imgs_train): 50 | imgs[index, :, :] = img 51 | 52 | for index, img in enumerate(masks_train): 53 | imgs_mask[index, :, :] = img 54 | 55 | np.save('imgs_train.npy', imgs) 56 | np.save('masks_train.npy', imgs_mask) 57 | print('Saving to .npy files done.') 58 | 59 | 60 | def load_train_data(): 61 | imgs_train = np.load('imgs_train.npy') 62 | masks_train = np.load('masks_train.npy') 63 | return imgs_train, masks_train 64 | 65 | 66 | def create_test_data(): 67 | print('-'*30) 68 | print('Creating test data...') 69 | print('-'*30) 70 | test_data_path = os.path.join(data_path, 'test') 71 | images = os.listdir(test_data_path) 72 | imgs_test = [] 73 | masks_test = [] 74 | 75 | for image_name in images: 76 | print(image_name) 77 | img = nibabel.load(os.path.join(test_data_path, image_name)) 78 | print(img.shape) 79 | 80 | for k in range(img.shape[2]): 81 | img_2d = np.array(img.get_data()[::2, ::2, k]) 82 | 83 | if 'liver' in image_name: 84 | masks_test.append(img_2d) 85 | 86 | elif 'orig' in image_name: 87 | imgs_test.append(img_2d) 88 | 89 | imgst = np.ndarray( 90 | (len(imgs_test), image_rows, image_cols), dtype=np.uint8 91 | ) 92 | imgs_maskt = np.ndarray( 93 | (len(masks_test), image_rows, image_cols), dtype=np.uint8 94 | ) 95 | for index, img in enumerate(imgs_test): 96 | imgst[index, :, :] = img 97 | 98 | for index, img in enumerate(masks_test): 99 | imgs_maskt[index, :, :] = img 100 | 101 | np.save('imgs_test.npy', imgst) 102 | np.save('masks_test.npy', imgs_maskt) 103 | print('Saving to .npy files done.') 104 | 105 | 106 | def load_test_data(): 107 | imgs_test = np.load('imgs_test.npy') 108 | masks_test = np.load('masks_test.npy') 109 | return imgs_test, masks_test 110 | 111 | 112 | if __name__ == '__main__': 113 | create_train_data() 114 | create_test_data() -------------------------------------------------------------------------------- /img/dice-20epochs-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soribadiaby/Deep-Learning-liver-segmentation/fb39884358a664e96b06bae2c2578875e6f2f98b/img/dice-20epochs-example.png -------------------------------------------------------------------------------- /img/segmentation-example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soribadiaby/Deep-Learning-liver-segmentation/fb39884358a664e96b06bae2c2578875e6f2f98b/img/segmentation-example1.png -------------------------------------------------------------------------------- /img/u-net-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soribadiaby/Deep-Learning-liver-segmentation/fb39884358a664e96b06bae2c2578875e6f2f98b/img/u-net-architecture.png -------------------------------------------------------------------------------- /train.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 14, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from __future__ import print_function\n", 10 | "\n", 11 | "import os\n", 12 | "from skimage.transform import resize\n", 13 | "from skimage.io import imsave\n", 14 | "import numpy as np\n", 15 | "from skimage.segmentation import mark_boundaries\n", 16 | "from keras.models import Model\n", 17 | "from keras.layers import Input, concatenate, Conv2D, MaxPooling2D, Conv2DTranspose\n", 18 | "from keras.optimizers import Adam, SGD\n", 19 | "from keras.callbacks import ModelCheckpoint\n", 20 | "from keras import backend as K\n", 21 | "from skimage.exposure import rescale_intensity\n", 22 | "from keras.callbacks import History\n", 23 | "from skimage import io\n", 24 | "from data import load_train_data, load_test_data" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 15, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "K.set_image_data_format('channels_last') # TF dimension ordering in this code\n", 34 | "\n", 35 | "img_rows = int(512/2)\n", 36 | "img_cols = int(512/2)\n", 37 | "smooth = 1.\n", 38 | "#We divide here the number of rows and columns by two because we undersample our data (We take one pixel over two) " 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 16, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "def dice_coef(y_true, y_pred):\n", 48 | " y_true_f = K.flatten(y_true)\n", 49 | " y_pred_f = K.flatten(y_pred)\n", 50 | " intersection = K.sum(y_true_f * y_pred_f)\n", 51 | " return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n", 52 | "\n", 53 | "\n", 54 | "def dice_coef_loss(y_true, y_pred):\n", 55 | " return -dice_coef(y_true, y_pred)\n", 56 | "\n", 57 | "#The functions return our metric and loss\n" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 17, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "def get_unet():\n", 67 | " inputs = Input((img_rows, img_cols, 1))\n", 68 | " conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(inputs)\n", 69 | " conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1)\n", 70 | " pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)\n", 71 | "\n", 72 | " conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1)\n", 73 | " conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2)\n", 74 | " pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)\n", 75 | "\n", 76 | " conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2)\n", 77 | " conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3)\n", 78 | " pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)\n", 79 | "\n", 80 | " conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(pool3)\n", 81 | " conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv4)\n", 82 | " pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)\n", 83 | "\n", 84 | " conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(pool4)\n", 85 | " conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(conv5)\n", 86 | "\n", 87 | " up6 = concatenate([Conv2DTranspose(256, (2, 2), strides=(2, 2), padding='same')(conv5), conv4], axis=3)\n", 88 | " conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(up6)\n", 89 | " conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv6)\n", 90 | "\n", 91 | " up7 = concatenate([Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(conv6), conv3], axis=3)\n", 92 | " conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(up7)\n", 93 | " conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv7)\n", 94 | "\n", 95 | " up8 = concatenate([Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(conv7), conv2], axis=3)\n", 96 | " conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(up8)\n", 97 | " conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv8)\n", 98 | "\n", 99 | " up9 = concatenate([Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(conv8), conv1], axis=3)\n", 100 | " conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(up9)\n", 101 | " conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv9)\n", 102 | "\n", 103 | " conv10 = Conv2D(1, (1, 1), activation='sigmoid')(conv9)\n", 104 | "\n", 105 | " model = Model(inputs=[inputs], outputs=[conv10])\n", 106 | "\n", 107 | " model.compile(optimizer=Adam(lr=1e-3), loss=dice_coef_loss, metrics=[dice_coef])\n", 108 | "\n", 109 | " return model\n", 110 | "\n", 111 | "#The different layers in our neural network model (including convolutions, maxpooling and upsampling)\n" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 18, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "def preprocess(imgs):\n", 121 | " imgs_p = np.ndarray((imgs.shape[0], img_rows, img_cols), dtype=np.uint8)\n", 122 | " for i in range(imgs.shape[0]):\n", 123 | " imgs_p[i] = resize(imgs[i], (img_cols, img_rows), preserve_range=True)\n", 124 | "\n", 125 | " imgs_p = imgs_p[..., np.newaxis]\n", 126 | " return imgs_p\n", 127 | "\n", 128 | "#We adapt here our dataset samples dimension so that we can feed it to our network" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 19, 134 | "metadata": {}, 135 | "outputs": [], 136 | "source": [ 137 | "import matplotlib.pyplot as plt" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 20, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "def train_and_predict():\n", 147 | " print('-'*30)\n", 148 | " print('Loading and preprocessing train data...')\n", 149 | " print('-'*30)\n", 150 | " imgs_train, imgs_mask_train = load_train_data()\n", 151 | "\n", 152 | " imgs_train = preprocess(imgs_train)\n", 153 | " imgs_mask_train = preprocess(imgs_mask_train)\n", 154 | "\n", 155 | " imgs_train = imgs_train.astype('float32')\n", 156 | " mean = np.mean(imgs_train) # mean for data centering\n", 157 | " std = np.std(imgs_train) # std for data normalization\n", 158 | "\n", 159 | " imgs_train -= mean\n", 160 | " imgs_train /= std\n", 161 | " #Normalization of the train set\n", 162 | "\n", 163 | " imgs_mask_train = imgs_mask_train.astype('float32')\n", 164 | "\n", 165 | " print('-'*30)\n", 166 | " print('Creating and compiling model...')\n", 167 | " print('-'*30)\n", 168 | " model = get_unet()\n", 169 | " model_checkpoint = ModelCheckpoint('weights.h5', monitor='val_loss', save_best_only=True)\n", 170 | " #Saving the weights and the loss of the best predictions we obtained\n", 171 | "\n", 172 | " print('-'*30)\n", 173 | " print('Fitting model...')\n", 174 | " print('-'*30)\n", 175 | " history=model.fit(imgs_train, imgs_mask_train, batch_size=10, epochs=20, verbose=1, shuffle=True,\n", 176 | " validation_split=0.2,\n", 177 | " callbacks=[model_checkpoint])\n", 178 | "\n", 179 | " print('-'*30)\n", 180 | " print('Loading and preprocessing test data...')\n", 181 | " print('-'*30)\n", 182 | " imgs_test, imgs_maskt = load_test_data()\n", 183 | " imgs_test = preprocess(imgs_test)\n", 184 | "\n", 185 | " imgs_test = imgs_test.astype('float32')\n", 186 | " imgs_test -= mean\n", 187 | " imgs_test /= std\n", 188 | " #Normalization of the test set\n", 189 | "\n", 190 | " print('-'*30)\n", 191 | " print('Loading saved weights...')\n", 192 | " print('-'*30)\n", 193 | " model.load_weights('weights.h5')\n", 194 | "\n", 195 | " print('-'*30)\n", 196 | " print('Predicting masks on test data...')\n", 197 | " print('-'*30)\n", 198 | " imgs_mask_test = model.predict(imgs_test, verbose=1)\n", 199 | " np.save('imgs_mask_test.npy', imgs_mask_test)\n", 200 | " print('-' * 30)\n", 201 | " print('Saving predicted masks to files...')\n", 202 | " print('-' * 30)\n", 203 | " pred_dir = 'preds'\n", 204 | " if not os.path.exists(pred_dir):\n", 205 | " os.mkdir(pred_dir)\n", 206 | "\n", 207 | " for k in range(len(imgs_mask_test)):\n", 208 | " a=rescale_intensity(imgs_test[k][:,:,0],out_range=(-1,1))\n", 209 | " b=(imgs_mask_test[k][:,:,0]).astype('uint8')\n", 210 | " io.imsave(os.path.join(pred_dir, str(k) + '_pred.png'),mark_boundaries(a,b))\n", 211 | " #Saving our predictions in the directory 'preds'\n", 212 | " plt.plot(history.history['dice_coef'])\n", 213 | " plt.plot(history.history['val_dice_coef'])\n", 214 | " plt.title('Model dice coeff')\n", 215 | " plt.ylabel('Dice coeff')\n", 216 | " plt.xlabel('Epoch')\n", 217 | " plt.legend(['Train', 'Test'], loc='upper left')\n", 218 | " plt.show()\n", 219 | " #plotting our dice coeff results in function of the number of epochs" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 21, 225 | "metadata": { 226 | "scrolled": true 227 | }, 228 | "outputs": [ 229 | { 230 | "name": "stdout", 231 | "output_type": "stream", 232 | "text": [ 233 | "------------------------------\n", 234 | "Loading and preprocessing train data...\n", 235 | "------------------------------\n", 236 | "------------------------------\n", 237 | "Creating and compiling model...\n", 238 | "------------------------------\n", 239 | "------------------------------\n", 240 | "Fitting model...\n", 241 | "------------------------------\n", 242 | "Train on 1083 samples, validate on 271 samples\n", 243 | "Epoch 1/20\n", 244 | "1083/1083 [==============================] - 18s 17ms/step - loss: -0.6274 - dice_coef: 0.6274 - val_loss: -0.3438 - val_dice_coef: 0.3438\n", 245 | "Epoch 2/20\n", 246 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.6386 - dice_coef: 0.6386 - val_loss: -0.5677 - val_dice_coef: 0.5677\n", 247 | "Epoch 3/20\n", 248 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.8072 - dice_coef: 0.8072 - val_loss: -0.6668 - val_dice_coef: 0.6668\n", 249 | "Epoch 4/20\n", 250 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.8585 - dice_coef: 0.8585 - val_loss: -0.6800 - val_dice_coef: 0.6800\n", 251 | "Epoch 5/20\n", 252 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.8862 - dice_coef: 0.8862 - val_loss: -0.6324 - val_dice_coef: 0.6324\n", 253 | "Epoch 6/20\n", 254 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.8991 - dice_coef: 0.8991 - val_loss: -0.7832 - val_dice_coef: 0.7832\n", 255 | "Epoch 7/20\n", 256 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.9232 - dice_coef: 0.9232 - val_loss: -0.7244 - val_dice_coef: 0.7244\n", 257 | "Epoch 8/20\n", 258 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.9318 - dice_coef: 0.9318 - val_loss: -0.7118 - val_dice_coef: 0.7118\n", 259 | "Epoch 9/20\n", 260 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.9367 - dice_coef: 0.9367 - val_loss: -0.7447 - val_dice_coef: 0.7447\n", 261 | "Epoch 10/20\n", 262 | "1083/1083 [==============================] - 18s 16ms/step - loss: -0.9430 - dice_coef: 0.9430 - val_loss: -0.8505 - val_dice_coef: 0.8505\n", 263 | "Epoch 11/20\n", 264 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.9551 - dice_coef: 0.9551 - val_loss: -0.8539 - val_dice_coef: 0.8539\n", 265 | "Epoch 12/20\n", 266 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.9598 - dice_coef: 0.9598 - val_loss: -0.8495 - val_dice_coef: 0.8495\n", 267 | "Epoch 13/20\n", 268 | "1083/1083 [==============================] - 18s 16ms/step - loss: -0.9658 - dice_coef: 0.9658 - val_loss: -0.8600 - val_dice_coef: 0.8600\n", 269 | "Epoch 14/20\n", 270 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.9447 - dice_coef: 0.9447 - val_loss: -0.8143 - val_dice_coef: 0.8143\n", 271 | "Epoch 15/20\n", 272 | "1083/1083 [==============================] - 18s 16ms/step - loss: -0.9482 - dice_coef: 0.9482 - val_loss: -0.8345 - val_dice_coef: 0.8345\n", 273 | "Epoch 16/20\n", 274 | "1083/1083 [==============================] - 18s 16ms/step - loss: -0.9670 - dice_coef: 0.9670 - val_loss: -0.8631 - val_dice_coef: 0.8631\n", 275 | "Epoch 17/20\n", 276 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.9716 - dice_coef: 0.9716 - val_loss: -0.8764 - val_dice_coef: 0.8764\n", 277 | "Epoch 18/20\n", 278 | "1083/1083 [==============================] - 17s 16ms/step - loss: -0.9712 - dice_coef: 0.9712 - val_loss: -0.8603 - val_dice_coef: 0.8603\n", 279 | "Epoch 19/20\n", 280 | "1083/1083 [==============================] - 18s 16ms/step - loss: -0.9756 - dice_coef: 0.9756 - val_loss: -0.8670 - val_dice_coef: 0.8670\n", 281 | "Epoch 20/20\n", 282 | "1083/1083 [==============================] - 18s 16ms/step - loss: -0.9769 - dice_coef: 0.9769 - val_loss: -0.8538 - val_dice_coef: 0.8538\n", 283 | "------------------------------\n", 284 | "Loading and preprocessing test data...\n", 285 | "------------------------------\n", 286 | "------------------------------\n", 287 | "Loading saved weights...\n", 288 | "------------------------------\n", 289 | "------------------------------\n", 290 | "Predicting masks on test data...\n", 291 | "------------------------------\n", 292 | "1057/1057 [==============================] - 5s 5ms/step\n", 293 | "------------------------------\n", 294 | "Saving predicted masks to files...\n", 295 | "------------------------------\n" 296 | ] 297 | }, 298 | { 299 | "data": { 300 | "image/png": "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\n", 301 | "text/plain": [ 302 | "" 303 | ] 304 | }, 305 | "metadata": { 306 | "needs_background": "light" 307 | }, 308 | "output_type": "display_data" 309 | } 310 | ], 311 | "source": [ 312 | "if __name__ == '__main__':\n", 313 | " train_and_predict()\n" 314 | ] 315 | } 316 | ], 317 | "metadata": { 318 | "kernelspec": { 319 | "display_name": "Python 3", 320 | "language": "python", 321 | "name": "python3" 322 | }, 323 | "language_info": { 324 | "codemirror_mode": { 325 | "name": "ipython", 326 | "version": 3 327 | }, 328 | "file_extension": ".py", 329 | "mimetype": "text/x-python", 330 | "name": "python", 331 | "nbconvert_exporter": "python", 332 | "pygments_lexer": "ipython3", 333 | "version": "3.7.2" 334 | } 335 | }, 336 | "nbformat": 4, 337 | "nbformat_minor": 2 338 | } 339 | --------------------------------------------------------------------------------