863 lines (862 with data), 98.1 kB
{
"cells": [
{
"cell_type": "markdown",
"id": "43d6de63",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-16T20:11:29.105213Z",
"start_time": "2022-05-16T20:11:27.031042Z"
}
},
"source": [
"## Binary classification example\n",
"\n",
"In this example we show how the ecgxai package can be used to easily build a classification system for atrial fibrillation. We train the model on the PTB-XL dataset and use 'double residual' convolution resnet architecture. "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "fbd9c54b",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:44:20.919791Z",
"start_time": "2022-05-17T11:44:19.992428Z"
}
},
"outputs": [],
"source": [
"%load_ext autoreload\n",
"%autoreload 2\n",
"# We first import the required data utilities from the ecgxai package\n",
"from ecgxai.utils.dataset import PTBXLDataset\n",
"from ecgxai.utils.transforms import ApplyGain, ToTensor\n",
"\n",
"# We also import some additional utilities from other packages for additionally functionality\n",
"from torch.utils.data import DataLoader\n",
"from torchvision.transforms import Compose"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "8d8e45a1",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:44:22.928971Z",
"start_time": "2022-05-17T11:44:20.921179Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 21837/21837 [00:00<00:00, 186045.27it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Using downloaded and verified file: /workspace/misc/PTB_XL/PTB_XL.tar.gz\n",
"Trainset:\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"-- Dataset distribution -- \n",
"Full size: 19653\n",
"[\"AF\"] - Num entries: 1356 (6.9%)\n",
"\n",
" Testset: \n",
"-- Dataset distribution -- \n",
"Full size: 2184\n",
"[\"AF\"] - Num entries: 158 (7.23%)\n"
]
}
],
"source": [
"# The PTBXL dataset class automatically downloads and extract the PTB-XL 12 lead dataset\n",
"dataset = PTBXLDataset(\n",
" # The path parameter defines where the data should be stored and were the data can be found in the future\n",
" path=\"/workspace/misc/PTB_XL\", \n",
" # We use the ApplyGain and ToTensor transformation (chained through the compose class)\n",
" # to format the data into the desired format. In the case of PTB-XL, all the input data\n",
" # is devided by 1000 in the ApplyGain transform to smoothen training (see the next cell for an example).\n",
" transform = Compose([ApplyGain(), ToTensor()]),\n",
" # The use_numpy parameter is set to True to speed up the loading of the data\n",
" use_numpy=True,\n",
" # As we are classifying 'Atrial fibrilation' in this example we tell the dataset to use the \n",
" # 'AF' label. \n",
" labels='AF'\n",
")\n",
"\n",
"# We then randomly split the dataset into a train and test set using a 90%-10% split\n",
"trainset, testset = dataset.train_test_split(ratio=0.1, shuffle=True)\n",
"\n",
"print(\"Trainset:\")\n",
"trainset.print_stats()\n",
"\n",
"print(\"\\n Testset: \")\n",
"testset.print_stats()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "975b3b21",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:44:22.950649Z",
"start_time": "2022-05-17T11:44:22.931519Z"
}
},
"outputs": [],
"source": [
"# The train and testset are then supplied to a pytorch dataloader which we can use to train the model\n",
"train_loader = DataLoader(\n",
" trainset,\n",
" batch_size=64,\n",
" num_workers=8,\n",
" shuffle=True\n",
")\n",
"\n",
"test_loader = DataLoader(\n",
" testset,\n",
" batch_size=64,\n",
" num_workers=8\n",
")"
]
},
{
"cell_type": "markdown",
"id": "12d81e5c",
"metadata": {},
"source": [
"### Checking the data\n",
"\n",
"The cell below plots the first lead of a sample from the dataset which we get using the __\\_\\_getitem\\_\\_()__ function which returns a dictionary. The raw ecg is stored under the 'waveform' key. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "3b14d826",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:44:23.156470Z",
"start_time": "2022-05-17T11:44:22.951914Z"
},
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"dict_keys(['waveform', 'samplebase', 'gain', 'id', 'truebaseline_0', 'truebaseline_1', 'truebaseline_2', 'truebaseline_3', 'truebaseline_4', 'truebaseline_5', 'truebaseline_6', 'truebaseline_7', 'truebaseline_8', 'truebaseline_9', 'truebaseline_10', 'truebaseline_11', 'label'])\n"
]
},
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7f4a76bf1580>]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 1080x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"example = trainset.__getitem__(0)\n",
"\n",
"# We can plot the keys of the dictionary to see what is available in the standard dataset configuration. The 'truebaseline' \n",
"# keys are used for baseline correction in some datasets. \n",
"print(example.keys())\n",
"\n",
"plt.figure(figsize=(15, 4))\n",
"plt.plot(example['waveform'][0])"
]
},
{
"cell_type": "markdown",
"id": "87e2c812",
"metadata": {},
"source": [
"### Initializing the model\n",
"\n",
"We import the CNNDoubleResidual architecture from the ecgxai package and pass it the required hyperparameters to deal with our dataset (e.g. 12 channels, 5000 measurements per lead). We choose to subsample (half the spatial dimension) each layer to limit the required number of parameters. \n",
"\n",
"We also define adittional Sequential, Linear and Reshape modules to create a full pipeline that maps each ecg to a binary output. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "d81d9c02",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:44:23.647229Z",
"start_time": "2022-05-17T11:44:23.157587Z"
}
},
"outputs": [],
"source": [
"import torch.nn as nn\n",
"from ecgxai.network.doubleresidual.modules import CNNDoubleResidual, Reshape\n",
"\n",
"# Initualize the double reisdual convolutional resnet\n",
"cnn = CNNDoubleResidual(\n",
" num_layers=12,\n",
" in_sample_dim=5000,\n",
" in_channels=12,\n",
" kernel_size=7,\n",
" dropout_rate=0.1,\n",
" sub_sample_every=1,\n",
" double_channel_every=4,\n",
" act_func=nn.ReLU(),\n",
" batchnorm=True\n",
")\n",
"\n",
"# use the calculate_output_dim to see what size the output of the CNN is\n",
"cnn_output_dim, cnn_output_channels, cnn_output_samples = cnn.calculate_output_dim()\n",
"\n",
"# We pass this calculated size to a linear layer that then maps the data to a single output per ecg, the final reshape\n",
"# gets rid of additional dimensions of the output tensor (e.g. B x L is reshaped to B)\n",
"class_model = nn.Sequential(\n",
" cnn,\n",
" nn.Linear(cnn_output_dim, 1),\n",
" Reshape(-1)\n",
")"
]
},
{
"cell_type": "markdown",
"id": "de8f815b",
"metadata": {},
"source": [
"### Printing the model archtecture using torchinfo\n",
"\n",
"Using the __torchinfo__ package we can now easily show how the shape of the data changes through the various layers of the chosen architecture and approximately how much compute is needed for each operation. "
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "6ab318d0",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:44:27.797037Z",
"start_time": "2022-05-17T11:44:23.648456Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/opt/conda/lib/python3.8/site-packages/torch/nn/functional.py:652: UserWarning: Named tensors and all their associated APIs are an experimental feature and subject to change. Please do not use them for anything important until they are released as stable. (Triggered internally at /pytorch/c10/core/TensorImpl.h:1156.)\n",
" return torch.max_pool1d(input, kernel_size, stride, padding, dilation, ceil_mode)\n"
]
},
{
"data": {
"text/plain": [
"==============================================================================================================\n",
"Layer (type:depth-idx) Output Shape Param #\n",
"==============================================================================================================\n",
"Sequential [16] --\n",
"├─CNNDoubleResidual: 1-1 [16, 192] --\n",
"│ └─Sequential: 2-1 [16, 192] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-1 [16, 12, 5000] --\n",
"│ │ │ └─MaxPool1d: 4-1 [16, 12, 5000] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-1 [16, 12, 5000] 24\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-2 [16, 12, 5000] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-3 [16, 12, 5006] --\n",
"│ │ │ │ └─Conv1d: 5-4 [16, 12, 5000] 1,020\n",
"│ │ │ │ └─BatchNorm1d: 5-5 [16, 12, 5000] 24\n",
"│ │ │ │ └─ReLU: 5-6 [16, 12, 5000] --\n",
"│ │ │ │ └─Dropout: 5-7 [16, 12, 5000] --\n",
"│ │ │ │ └─ConstantPad1d: 5-8 [16, 12, 5006] --\n",
"│ │ │ │ └─Conv1d: 5-9 [16, 12, 5000] 1,020\n",
"│ │ │ │ └─BatchNorm1d: 5-10 [16, 12, 5000] 24\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-2 [16, 12, 2500] --\n",
"│ │ │ └─MaxPool1d: 4-2 [16, 12, 2500] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-11 [16, 12, 5000] 24\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-12 [16, 12, 5000] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-13 [16, 12, 5006] --\n",
"│ │ │ │ └─Conv1d: 5-14 [16, 12, 2500] 1,020\n",
"│ │ │ │ └─BatchNorm1d: 5-15 [16, 12, 2500] 24\n",
"│ │ │ │ └─ReLU: 5-16 [16, 12, 2500] --\n",
"│ │ │ │ └─Dropout: 5-17 [16, 12, 2500] --\n",
"│ │ │ │ └─ConstantPad1d: 5-18 [16, 12, 2506] --\n",
"│ │ │ │ └─Conv1d: 5-19 [16, 12, 2500] 1,020\n",
"│ │ │ │ └─BatchNorm1d: 5-20 [16, 12, 2500] 24\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-3 [16, 12, 1250] --\n",
"│ │ │ └─MaxPool1d: 4-3 [16, 12, 1250] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-21 [16, 12, 2500] 24\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-22 [16, 12, 2500] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-23 [16, 12, 2506] --\n",
"│ │ │ │ └─Conv1d: 5-24 [16, 12, 1250] 1,020\n",
"│ │ │ │ └─BatchNorm1d: 5-25 [16, 12, 1250] 24\n",
"│ │ │ │ └─ReLU: 5-26 [16, 12, 1250] --\n",
"│ │ │ │ └─Dropout: 5-27 [16, 12, 1250] --\n",
"│ │ │ │ └─ConstantPad1d: 5-28 [16, 12, 1256] --\n",
"│ │ │ │ └─Conv1d: 5-29 [16, 12, 1250] 1,020\n",
"│ │ │ │ └─BatchNorm1d: 5-30 [16, 12, 1250] 24\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-4 [16, 24, 625] --\n",
"│ │ │ └─MaxPool1d: 4-4 [16, 24, 625] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-31 [16, 12, 1250] 24\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-32 [16, 12, 1250] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-33 [16, 12, 1256] --\n",
"│ │ │ │ └─Conv1d: 5-34 [16, 24, 625] 2,040\n",
"│ │ │ │ └─BatchNorm1d: 5-35 [16, 24, 625] 48\n",
"│ │ │ │ └─ReLU: 5-36 [16, 24, 625] --\n",
"│ │ │ │ └─Dropout: 5-37 [16, 24, 625] --\n",
"│ │ │ │ └─ConstantPad1d: 5-38 [16, 24, 631] --\n",
"│ │ │ │ └─Conv1d: 5-39 [16, 24, 625] 4,056\n",
"│ │ │ │ └─BatchNorm1d: 5-40 [16, 24, 625] 48\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-5 [16, 24, 312] --\n",
"│ │ │ └─MaxPool1d: 4-5 [16, 24, 312] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-41 [16, 24, 625] 48\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-42 [16, 24, 625] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-43 [16, 24, 631] --\n",
"│ │ │ │ └─Conv1d: 5-44 [16, 24, 313] 4,056\n",
"│ │ │ │ └─BatchNorm1d: 5-45 [16, 24, 313] 48\n",
"│ │ │ │ └─ReLU: 5-46 [16, 24, 313] --\n",
"│ │ │ │ └─Dropout: 5-47 [16, 24, 313] --\n",
"│ │ │ │ └─ConstantPad1d: 5-48 [16, 24, 318] --\n",
"│ │ │ │ └─Conv1d: 5-49 [16, 24, 312] 4,056\n",
"│ │ │ │ └─BatchNorm1d: 5-50 [16, 24, 312] 48\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-6 [16, 24, 156] --\n",
"│ │ │ └─MaxPool1d: 4-6 [16, 24, 156] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-51 [16, 24, 312] 48\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-52 [16, 24, 312] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-53 [16, 24, 318] --\n",
"│ │ │ │ └─Conv1d: 5-54 [16, 24, 156] 4,056\n",
"│ │ │ │ └─BatchNorm1d: 5-55 [16, 24, 156] 48\n",
"│ │ │ │ └─ReLU: 5-56 [16, 24, 156] --\n",
"│ │ │ │ └─Dropout: 5-57 [16, 24, 156] --\n",
"│ │ │ │ └─ConstantPad1d: 5-58 [16, 24, 162] --\n",
"│ │ │ │ └─Conv1d: 5-59 [16, 24, 156] 4,056\n",
"│ │ │ │ └─BatchNorm1d: 5-60 [16, 24, 156] 48\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-7 [16, 24, 78] --\n",
"│ │ │ └─MaxPool1d: 4-7 [16, 24, 78] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-61 [16, 24, 156] 48\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-62 [16, 24, 156] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-63 [16, 24, 162] --\n",
"│ │ │ │ └─Conv1d: 5-64 [16, 24, 78] 4,056\n",
"│ │ │ │ └─BatchNorm1d: 5-65 [16, 24, 78] 48\n",
"│ │ │ │ └─ReLU: 5-66 [16, 24, 78] --\n",
"│ │ │ │ └─Dropout: 5-67 [16, 24, 78] --\n",
"│ │ │ │ └─ConstantPad1d: 5-68 [16, 24, 84] --\n",
"│ │ │ │ └─Conv1d: 5-69 [16, 24, 78] 4,056\n",
"│ │ │ │ └─BatchNorm1d: 5-70 [16, 24, 78] 48\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-8 [16, 48, 39] --\n",
"│ │ │ └─MaxPool1d: 4-8 [16, 48, 39] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-71 [16, 24, 78] 48\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-72 [16, 24, 78] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-73 [16, 24, 84] --\n",
"│ │ │ │ └─Conv1d: 5-74 [16, 48, 39] 8,112\n",
"│ │ │ │ └─BatchNorm1d: 5-75 [16, 48, 39] 96\n",
"│ │ │ │ └─ReLU: 5-76 [16, 48, 39] --\n",
"│ │ │ │ └─Dropout: 5-77 [16, 48, 39] --\n",
"│ │ │ │ └─ConstantPad1d: 5-78 [16, 48, 45] --\n",
"│ │ │ │ └─Conv1d: 5-79 [16, 48, 39] 16,176\n",
"│ │ │ │ └─BatchNorm1d: 5-80 [16, 48, 39] 96\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-9 [16, 48, 19] --\n",
"│ │ │ └─MaxPool1d: 4-9 [16, 48, 19] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-81 [16, 48, 39] 96\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-82 [16, 48, 39] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-83 [16, 48, 45] --\n",
"│ │ │ │ └─Conv1d: 5-84 [16, 48, 20] 16,176\n",
"│ │ │ │ └─BatchNorm1d: 5-85 [16, 48, 20] 96\n",
"│ │ │ │ └─ReLU: 5-86 [16, 48, 20] --\n",
"│ │ │ │ └─Dropout: 5-87 [16, 48, 20] --\n",
"│ │ │ │ └─ConstantPad1d: 5-88 [16, 48, 25] --\n",
"│ │ │ │ └─Conv1d: 5-89 [16, 48, 19] 16,176\n",
"│ │ │ │ └─BatchNorm1d: 5-90 [16, 48, 19] 96\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-10 [16, 48, 9] --\n",
"│ │ │ └─MaxPool1d: 4-10 [16, 48, 9] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-91 [16, 48, 19] 96\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-92 [16, 48, 19] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-93 [16, 48, 25] --\n",
"│ │ │ │ └─Conv1d: 5-94 [16, 48, 10] 16,176\n",
"│ │ │ │ └─BatchNorm1d: 5-95 [16, 48, 10] 96\n",
"│ │ │ │ └─ReLU: 5-96 [16, 48, 10] --\n",
"│ │ │ │ └─Dropout: 5-97 [16, 48, 10] --\n",
"│ │ │ │ └─ConstantPad1d: 5-98 [16, 48, 15] --\n",
"│ │ │ │ └─Conv1d: 5-99 [16, 48, 9] 16,176\n",
"│ │ │ │ └─BatchNorm1d: 5-100 [16, 48, 9] 96\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-11 [16, 48, 4] --\n",
"│ │ │ └─MaxPool1d: 4-11 [16, 48, 4] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-101 [16, 48, 9] 96\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ReLU: 5-102 [16, 48, 9] --\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3 -- --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─ConstantPad1d: 5-103 [16, 48, 15] --\n",
"│ │ │ │ └─Conv1d: 5-104 [16, 48, 5] 16,176\n",
"│ │ │ │ └─BatchNorm1d: 5-105 [16, 48, 5] 96\n",
"│ │ │ │ └─ReLU: 5-106 [16, 48, 5] --\n",
"│ │ │ │ └─Dropout: 5-107 [16, 48, 5] --\n",
"│ │ │ │ └─ConstantPad1d: 5-108 [16, 48, 10] --\n",
"│ │ │ │ └─Conv1d: 5-109 [16, 48, 4] 16,176\n",
"│ │ │ │ └─BatchNorm1d: 5-110 [16, 48, 4] 96\n",
"│ │ └─ResidualMaxPoolDoubleConvBlockForward: 3-12 [16, 96, 2] --\n",
"│ │ │ └─MaxPool1d: 4-12 [16, 96, 2] --\n",
"│ │ │ └─Sequential: 4 -- --\n",
"│ │ │ │ └─BatchNorm1d: 5-111 [16, 48, 4] 96\n",
"│ │ │ │ └─ReLU: 5-112 [16, 48, 4] --\n",
"│ │ │ │ └─ConstantPad1d: 5-113 [16, 48, 10] --\n",
"│ │ │ │ └─Conv1d: 5-114 [16, 96, 2] 32,352\n",
"│ │ │ │ └─BatchNorm1d: 5-115 [16, 96, 2] 192\n",
"│ │ │ │ └─ReLU: 5-116 [16, 96, 2] --\n",
"│ │ │ │ └─Dropout: 5-117 [16, 96, 2] --\n",
"│ │ │ │ └─ConstantPad1d: 5-118 [16, 96, 8] --\n",
"│ │ │ │ └─Conv1d: 5-119 [16, 96, 2] 64,608\n",
"│ │ │ │ └─BatchNorm1d: 5-120 [16, 96, 2] 192\n",
"│ │ └─Flatten: 3-13 [16, 192] --\n",
"├─Linear: 1-2 [16, 1] 193\n",
"├─Reshape: 1-3 [16] --\n",
"==============================================================================================================\n",
"Total params: 257,401\n",
"Trainable params: 257,401\n",
"Non-trainable params: 0\n",
"Total mult-adds (M): 453.13\n",
"==============================================================================================================\n",
"Input size (MB): 3.84\n",
"Forward/backward pass size (MB): 95.19\n",
"Params size (MB): 1.03\n",
"Estimated Total Size (MB): 100.06\n",
"=============================================================================================================="
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from torchinfo import summary\n",
"\n",
"batch_size = 16\n",
"summary(class_model, input_size=(batch_size, 12, 5000), depth=50)\n"
]
},
{
"cell_type": "markdown",
"id": "e37ec85b",
"metadata": {},
"source": [
"### Defining metrics\n",
"\n",
"As this is a binary classification problem we would like to measure the performance of the model using common metrics such as AUROC, Precision, Recall, Accuracy and F1 score. We use the implemention of these metrics provided by the __TorchMetrics__ package. Each metric is wrapped using a __TorchMetricWrapper (TMW)__ provided by the ecgxai package. The TMW are intialized using a TorchMetric instance, the names of the model outputs that should be passed to the metric and an 'int_arg' parameter. The possible choices for output names can be found in the class definition of the classification system. In this case we use 'y_prob' (the predicted class probability) and 'label' (the true label of each sample). The int_args parameter is used to cast the 'label' output to an integer (originally float) which is required by the TorchMetric package. "
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "64fd1e91",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:50:31.691005Z",
"start_time": "2022-05-17T11:50:31.662974Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/opt/conda/lib/python3.8/site-packages/torchmetrics/utilities/prints.py:36: UserWarning: Metric `AUROC` will save all targets and predictions in buffer. For large datasets this may lead to large memory footprint.\n",
" warnings.warn(*args, **kwargs)\n"
]
}
],
"source": [
"from ecgxai.utils.metrics import TMW\n",
"from torchmetrics import MetricCollection, AUROC, F1Score, Accuracy, Precision, Recall\n",
"\n",
"\n",
"metrics = MetricCollection(\n",
" {\n",
" 'AUROC': TMW(AUROC(), ['y_prob', 'label'], int_args=['label']),\n",
" 'Precision': TMW(Precision(), ['y_prob', 'label'], int_args=['label']),\n",
" 'Recall': TMW(Recall(), ['y_prob', 'label'], int_args=['label']),\n",
" 'Accuracy': TMW(Accuracy(), ['y_prob', 'label'], int_args=['label']),\n",
" 'F1': TMW(F1Score(), ['y_prob', 'label'], int_args=['label'])\n",
" } \n",
")\n"
]
},
{
"cell_type": "markdown",
"id": "7480227e",
"metadata": {},
"source": [
"### Defining a loss function\n",
"\n",
"As this is a binary classification problem we choose te commonly used BCEWithLogitsLoss from the torch package to train our model. This loss is wrapped using the ecgxai TorchWrapper (TW) class, which can be used similarly to the TMW class used to wrap the metrics. In this case we however use the 'y_hat' model output instead of the 'y_prob', as we would like the model output before applying a sigmoid. "
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "f357bf4e",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:50:35.600002Z",
"start_time": "2022-05-17T11:50:35.534565Z"
}
},
"outputs": [],
"source": [
"from ecgxai.utils.loss import TW\n",
"\n",
"loss = TW(nn.BCEWithLogitsLoss(), ['y_hat', 'label'])"
]
},
{
"cell_type": "markdown",
"id": "34c391c6",
"metadata": {},
"source": [
"### Defining a classification system\n",
"\n",
"Now we can define our Binary classification system. The system automatically handles the calculation of the loss and metrics. It also offers a variaty of automatic logging opperations for training, validation and testing. In this case we are only interested in the test_metrics, hence we only pass the defined metrics there. We also pass our model, the defined loss and a learning rate (lr) of 0.001. "
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "3b54ab7b",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:50:37.352599Z",
"start_time": "2022-05-17T11:50:37.325621Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['y_hat', 'label']\n"
]
}
],
"source": [
"from ecgxai.systems.classification_system import ClassificationSystem\n",
"\n",
"system = ClassificationSystem(\n",
" lr=0.001,\n",
" model=class_model,\n",
" test_metrics=metrics,\n",
" loss=loss,\n",
" mode='binary'\n",
")"
]
},
{
"cell_type": "markdown",
"id": "0277de43",
"metadata": {},
"source": [
"### Training and testing the model\n",
"\n",
"To train the model we use __pytorch lighting__. We tell the pytorch lighting trainer object to train the model for 5 epochs and to save the model once done training. Additionally the trainer object will move all the required instances to the GPU if cuda is available to speed up training. "
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "446a400c",
"metadata": {
"ExecuteTime": {
"end_time": "2022-05-17T11:52:04.534202Z",
"start_time": "2022-05-17T11:51:23.898677Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"GPU available: True, used: True\n",
"TPU available: False, using: 0 TPU cores\n",
"IPU available: False, using: 0 IPUs\n",
"/opt/conda/lib/python3.8/site-packages/pytorch_lightning/trainer/configuration_validator.py:122: UserWarning: You defined a `validation_step` but have no `val_dataloader`. Skipping val loop.\n",
" rank_zero_warn(\"You defined a `validation_step` but have no `val_dataloader`. Skipping val loop.\")\n",
"LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n",
"\n",
" | Name | Type | Params\n",
"--------------------------------------------------\n",
"0 | test_metrics | MetricCollection | 0 \n",
"1 | loss | TW | 0 \n",
"2 | model | Sequential | 257 K \n",
"--------------------------------------------------\n",
"257 K Trainable params\n",
"0 Non-trainable params\n",
"257 K Total params\n",
"1.030 Total estimated model params size (MB)\n",
"/opt/conda/lib/python3.8/site-packages/pytorch_lightning/callbacks/model_checkpoint.py:631: UserWarning: Checkpoint directory /workspace/ecgxai/examples/classification/checkpoints exists and is not empty.\n",
" rank_zero_warn(f\"Checkpoint directory {dirpath} exists and is not empty.\")\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "de2639e0e68d444ba6ca99638f3e0cef",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Training: 0it [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import pytorch_lightning as pl\n",
"import torch\n",
"from pytorch_lightning.callbacks import ModelCheckpoint\n",
"\n",
"trainer = pl.Trainer(\n",
" max_epochs=5,\n",
" gpus= 1 if torch.cuda.is_available() else None,\n",
" logger=None,\n",
" callbacks=[\n",
" ModelCheckpoint(\n",
" save_last=True\n",
" ),\n",
" ]\n",
" )\n",
"\n",
"trainer.fit(system, train_loader)\n"
]
},
{
"cell_type": "markdown",
"id": "4988da29",
"metadata": {},
"source": [
"### Testing the model\n",
"\n",
"Now all we need to do is test the model. "
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "1adb4979",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "0bec306c36514f07952a60d22b65c1cf",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Testing: 0it [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/opt/conda/lib/python3.8/site-packages/pytorch_lightning/utilities/data.py:59: UserWarning: Trying to infer the `batch_size` from an ambiguous collection. The batch size we found is 64. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.\n",
" warning_cache.warn(\n",
"/opt/conda/lib/python3.8/site-packages/torchmetrics/utilities/prints.py:36: UserWarning: No positive samples in targets, true positive value should be meaningless. Returning zero tensor in true positive score\n",
" warnings.warn(*args, **kwargs)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"--------------------------------------------------------------------------------\n",
"DATALOADER:0 TEST RESULTS\n",
"{'test_AUROC': 0.9975426197052002,\n",
" 'test_Accuracy': 0.9882909655570984,\n",
" 'test_F1': 0.9247804880142212,\n",
" 'test_Precision': 0.9445728063583374,\n",
" 'test_Recall': 0.9064620137214661,\n",
" 'test_loss': 0.03978169709444046}\n",
"--------------------------------------------------------------------------------\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/opt/conda/lib/python3.8/site-packages/pytorch_lightning/utilities/data.py:59: UserWarning: Trying to infer the `batch_size` from an ambiguous collection. The batch size we found is 8. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.\n",
" warning_cache.warn(\n"
]
},
{
"data": {
"text/plain": [
"[{'test_AUROC': 0.9975426197052002,\n",
" 'test_Accuracy': 0.9882909655570984,\n",
" 'test_F1': 0.9247804880142212,\n",
" 'test_Precision': 0.9445728063583374,\n",
" 'test_Recall': 0.9064620137214661,\n",
" 'test_loss': 0.03978169709444046}]"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"trainer.test(model=system, dataloaders=test_loader)"
]
}
],
"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.8.10"
},
"latex_envs": {
"LaTeX_envs_menu_present": true,
"autoclose": false,
"autocomplete": true,
"bibliofile": "biblio.bib",
"cite_by": "apalike",
"current_citInitial": 1,
"eqLabelWithNumbers": true,
"eqNumInitial": 1,
"hotkeys": {
"equation": "Ctrl-E",
"itemize": "Ctrl-I"
},
"labels_anchors": false,
"latex_user_defs": false,
"report_style_numbering": false,
"user_envs_cfg": false
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": false,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 5
}