{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Определение стадии рака простаты по цифровой патологии биопсии.\n### 1. Что такое рак простаты\nРак простаты (карцинома простаты) – один из самых распространенных видов злокачественных опухолей у мужчин, который развивается из клеток предстательной железы. \n\nСреди мужчин старше 60 лет болен уже каждый сотый. А в преклонном возрасте, после 75 лет, рак предстательной железы находят у одного из восьми мужчин.\n\nТкань простаты преимущественно состоит из мышечных и железистых клеток, а также из соединительной ткани.\n\nПри развитии рака простаты в зависимости от стадии, нормальная железистая ткань \n\n(изображена на рисунке слева  над цифрой «1» имеет бежевый оттенок) \n\nдеформируется и постепенно заменяется соединительной.\n\n\n\n### 2. Диагностика и стадии рака простаты\nДля того, чтобы установить наличие рака простаты и определить его стадию, пациенту необходимо провести диагностику, включающую в себя сдачу крови из вены на ПСА (простато-специфический антиген), а если негативные результы в анализах подтверждаются, далее проводится:\n- УЗИ (Ультразвуковое исследование) простаты                               - БИОПСИЯ (забора тканей) простаты\n\nБиопсия проводится по показаниям после ПСА и УЗИ. \nУ пациента хирургическим путем извлекаются два наиболее характерных фрагмента предполагаемой опухоли. \nДля визуализации контуров железистой ткани, материал пациента окрашивают гематоксилином и эозином (H&E)\nСоединительная ткань приобретает оттенки цвета, а железистая остается белыми и полупрозрачными «просветами-дырами», а также разветвленными полостями.\nФрагмент опухоли сканируют и оцифровывают. Изображения называют цифровой патологией.\n\n### 3. Решение медицинских задач с помощью нейросетей.\nСегодня, поскольку цифровые технологии начинают предлагать новые и менее инвазивные стратегии борьбы с раком, есть надежда, что уже в обозримом будущем лечение рака перейдет на новую стадию развития и это коснется всего населения, а не только людей, обладающих значительными ресурсами.\n\nДиагностика имеет очень важное значение в сфере лечения рака - системы и тесты, используемые для выявления заболевания, особенно раннего, спасают жизни людей, помогая заблаговременно начать лечение и давая пациентам гораздо больше шансов на выживание.\nСейчас в здравоохранении начинают все шире использоваться технологии искусственного интеллекта, в первую очередь в сфере диагностики по медицинским изображениям.\n\nСистема диагностирования рака по фотоснимкам на основе обученной нейросети в некоторых случаях показала лучшие результаты по сравнению с традиционными способами диагностики, сообщила пресс-служба МГУ имени Ломоносова 28 июля со ссылкой на статью в научном журнале Nature Cancer.\n«Предложенный нами метод показывает, что компьютерное зрение наряду с молекулярным профилированием можно использовать при диагностике рака. Экспертная оценка врача остается стандартом для того, чтобы поставить окончательный диагноз, однако компьютеры уже сейчас способны помогать в решении этих задач», — отметил представитель международного коллектива ученых, ведущий специалист Европейского института биоинформатики (EMBL-EBI) Мориц Герштунг.\n\n\n### 4. Связь биопсии и шкалы Глисона.\nВнешний вид желез составляет основу системы оценок Глисона.\nСтепень изменения клеток двух исследуемых участков измеряется по шкале от 1 до 5 - чем более характерны изменения, тем более  агрессивна злокачественная опухоль, тем большая стадия заболевания раком. \nЖелезистая структура, характерная для здоровой ткани простаты, постепенно утрачивается с увеличением баллов, и клетки приобретают нечеткие, «размазанные» контуры.\nБаллы по каждому из двух исследуемых участков опухоли складываются. Это и называется системой оценок (шкалой) Глисона.\nТаким образом, «сумма Глисона» варьирует от 2 (1+1) до 10 (5+5) баллов.\n\nВ нашем датасете train.csv содержится информация сразу по двум оцененным по шкале Глисон участкам, которая представляется в виде суммы балллов. То есть каждая image_id и картинка к ней содержит скан ткани пациента с биопсии (возможно отснятая под разным углом/с разным разрешением), но один.\nИ при этом если человек болен, то картинка имеет ва наибболее выраженных участка измененной железистой и соединительной тканей, больший и меньший по площади, соответственно больший по площади считается первичным участком и балл по нему в шкале записан первым, а вторичный - меньший, балл по нему идет после символа \"+\" вторым."},{"metadata":{},"cell_type":"markdown","source":"### 5. Что такое ISUP.\n\nВ соответствии с действующими рекомендациями Международного общества урологической патологии (ISUP) баллы по Глисону суммируются в баллы ISUP (указывающие на стадии рака) по шкале от 1 до 5 в соответствии со следующим правилом:\n\n(3 и 4 балла - пограничные значения, для более подробного ознакомления по расчету баллов можно воспользоваться ссылкой: http://www.raka-prostati.net/summa-glisona-7.html)\n\n* 6 баллов по шкале Глисона = 1 балл ISUP (1 стадия рака)\n\n* 7 баллов по шкале Глисона (3 + 4) = 2 балла по программе ISUP (2 стадия рака)\n\n* 7 баллов по шкале Глисона (4 + 3) = 3 балла по программе ISUP (3 стадия рака) \n\n* 8 баллов по шкале Глисона = 4 балла ISUP (4 стадия рака)\n\n* 9-10 баллов по шкале Глисона = 5 баллов ISUP (5 стадия рака)\n\nЕсли рака не обнаружено, ISUP (стадия рака) имеет значение 0.\nК раковым относятся ткани простаты, имеющие сумму Глисона выше 6.\n\n\n<img src=\"https://horoshiyurolog.ru/wp-content/uploads/2018/09/1280p.jpg\" height=\"100px\">\n\n### 6. Как выглядит шкала Глисон в нашем датасете?\nТак как к раковым относятся ткани простаты, имеющие сумму Глисона выше 6, наш датасет распознает по каждому из двух участков ткани, взятых на биопсии, только три категории: 3, 4 и 5. Соответственно, оценки по ISUP могут варьироваться от 6(3+3) - 1 стадия рака  до 10 (5+5) - 5 стадия рака.\n\nА. Доброкачественные предстательные железы со складчатым эпителием. Цитоплазма бледная, ядра маленькие и правильные. Железы сгруппированы вместе.\nB. Аденокарцинома предстательной железы - 3 балла по шкале Глисона. Не имеет потери железистой дифференцировки. Между доброкачественными железами инфильтрируют маленькие железы. Цитоплазма часто темная, а ядра увеличены с темным хроматином и некоторыми выступающими ядрышками. Каждая эпителиальная единица обособлена и имеет просвет.\nC. Аденокарцинома предстательной железы - 4 балла по шкале Глисона. Имеет частичную потерю железистой дифференцировки. Есть попытка сформировать просвет, но опухоль не может сформировать полноценные, хорошо развитые железы. На этой микрофотографии виден неправильный решетчатый рак, то есть эпителиальные пласты с множественными просветами. Есть также некоторые плохо сформированные маленькие железы и некоторые сросшиеся железы. Все они включены в шаблон Глисона 4х баллов.\nD. Аденокарцинома предстательной железы - 5 баллов по шкале Глисона. Имеет почти полную утрату железистой дифференцировки. В строме видны рассеянные единичные раковые клетки. Шаблон Глисона 5 баллов может также содержать твердые листы или нити раковых клеток. На всех микрофотографиях видны пятна гематоксилина и эозина при 20-кратном увеличении линзы.\n\nБолее подробные сведения по ссылке: https://www.kaggle.com/c/prostate-cancer-grade-assessment/overview/additional-resources\n\n<img src=\"https://storage.googleapis.com/kaggle-media/competitions/PANDA/GleasonPattern_4squares%20copy500.png\" height=\"100px\">"},{"metadata":{},"cell_type":"markdown","source":"# Особенности данных - присвоение баллов по шкале Глисон.\nВ нашем датасете учтены некоторые особенности оценки по шкале Глисон по доминирующему и вторичному типу опухолей (два фрагмента с биопсии): \n\n1. Увеличение оценки участка на балл за меньший объем площади пораженных клеток.\n\nВторичный фрагмент должен составлять от 5 до 50% всех пораженных клеток, и если условие по каким-о причинам не соблюдается и участок составляет менее 5%, оценка по второму фрагменту увеличивается на один балл. \n\nТо есть 3 стадия рака по ISUP(4+3), где с 4 балла (первый фрагмент) + 3 балла (второй фрагмент) меняется на 4+4 и автоматически зачисляется в 4 стадию рака по ISUP .\n\n2. Увеличение оценки участка на балл за меньший объем площади пораженных клеток.\n\nСамая высокая оценка всегда должна быть частью оценки. \n\nНапример, биопсия, содержащая:\n- 60% (доминирующий тип, первый участок), оцененный на 4 балла по Глисону, \n- 37% ( вторичный тип, второй участок), оцененный на 3 балла по Глисону\n- 3% (третичный тип), оцененный на 5 баллов по Глисону, \n\nдолжна получить оценку 4 + 5 = 9.\n\nПодробное описание по ссылке: https://www.kaggle.com/c/prostate-cancer-grade-assessment/overview/additional-resources "},{"metadata":{},"cell_type":"markdown","source":"# EDA  предобработка данных."},{"metadata":{},"cell_type":"markdown","source":"Для выполнения задачи ML нам предоставлены следующие файлы с данными:\n\n1. Два датасета - обучающий и тестовый [train].csv: \n\nОбучающий и тестовый датасеты train.csv изначально расположены в одном файле train.csv. Тестовыми данными являются последние 500 строк, тренировочными - остальные более 10000.  В данном файле доступна информация по наименованию изображения биопсии, оценке по шкале Глисон и стадии рака по ISUP.\n\n2. train_images: \n\nЦифровая патология (изображения, полученные на биопсии с двух участков опухоли) . \nКаждое имеет расширение tiff file. Целевую переменную имеет только файл train_images.\n\n3. train_label_masks: \n\nМаски сегментации, показывающие, какие части изображения привели к оценке ISUP. Эти маски предназначены для помощи в разработке стратегий выбора наиболее полезных подвыборок изображений.\n"},{"metadata":{},"cell_type":"markdown","source":"Загрузим необходимые библиотеки и используем прямую загрузку всех необходимых данных для задачи напрямую с kaggle"},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"#BASIC\nimport numpy as np \nimport pandas as pd \nimport torch.nn.functional as F\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset,DataLoader\nfrom torchvision import transforms,models\nimport torch.utils.model_zoo as model_zoo\nfrom collections import OrderedDict\nimport math\n\n\nimport os\nimport sys\n\nsys.path = [\n    '../input/efficientnet-pytorch/EfficientNet-PyTorch/EfficientNet-PyTorch-master',\n] + sys.path\n\n# DATA visualization\nimport matplotlib.pyplot as plt\nimport matplotlib\nimport seaborn as sns\nimport PIL\nfrom IPython.display import Image, display\nfrom plotly import graph_objs as go\nimport plotly.express as px\nimport plotly.figure_factory as ff\n\n\nfrom tqdm import tqdm_notebook as tqdm\nimport math\nimport cv2\n\nimport openslide\nimport skimage.io\nimport random\nimport albumentations\n\nfrom PIL import Image\nfrom efficientnet_pytorch import EfficientNet \n\n\n# Metrics\nfrom sklearn.metrics import accuracy_score\nfrom sklearn.metrics import f1_score\n\n\n","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"BASE_FOLDER = \"../input/prostate-cancer-grade-assessment/\"\n!ls {BASE_FOLDER}","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Класс конфигуратор\n\nСоздадим отдельный класс config, который задает параметры для:\n\n1 **device** (виртуальное устройство из инфраструктуры kaggle, чтобы использовать это устройство в качестве исполняемого)\n\n2 ширины и высоты изображения\n\n3 количество фрагментов изображения\n\n4 количество фич, получаемых на выходе после свертки каждого слоя (6)\n\n5 режим дебага(для проведения отладки)\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"class config:\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    IMG_WIDTH = 224\n    IMG_HEIGHT = 224\n    TEST_BATCH_SIZE = 16\n    CLASSES = 6\n    DEBUG = False\n#     DEBUG = True","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#поскольку тестовые картинки для данного соревнования формируются в debag режиме, для дипломной работы взяты \n# тренировочные все за исключением 500 последних изображений биопсии, для тестовых - последние 500 из этого же файла\ntrain = pd.read_csv(BASE_FOLDER+\"train.csv\")[:-500]\ntest = train[-500:]\n#test = pd.read_csv(BASE_FOLDER+\"test.csv\")\nsub = pd.read_csv(BASE_FOLDER+\"sample_submission.csv\")\ndata_dir = '/kaggle/input/prostate-cancer-grade-assessment/train_images'\nmask_dir = '/kaggle/input/prostate-cancer-grade-assessment/train_label_masks'\n\n\ntrain_labels = pd.read_csv('/kaggle/input/prostate-cancer-grade-assessment/train.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if config.DEBUG:\n    data_dir = f'{BASE_PATH}/train_images'\n    test = pd.read_csv(f'{BASE_PATH}/train.csv').head(200)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Посмотрим на основной датасет train.csv"},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.describe().transpose()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.isnull().sum()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**всего 10616 пациентов, пустые значения отсутствуют**"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"unique ids : \", len(train.image_id.unique()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"дублированные записи по пациентам отсутствуют, у каждого свой id"},{"metadata":{"trusted":true},"cell_type":"code","source":"train['data_provider'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"как и было описано, всего два университета - поставщика информации по данным"},{"metadata":{"trusted":true},"cell_type":"code","source":"train['isup_grade'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2892 здоровых пациента, остальные с заюолеваниями от 1 до 5 стадии рака в соответствии с баллами по ISUP"},{"metadata":{"trusted":true},"cell_type":"code","source":"train['gleason_score'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Стоит обратить внимание, что 3+4 и 4+3, равно как и 3+5 и 5+3 - это разные значения, потому что первая оценка присваивается фрагменту доминирующего типа раковых клеток, вторая - вторичному (имеют разные площади сосредоточения), 0+0 - отсутствие рака. Однако при этом лишь стадии 3+4 и 4+3 не эквивалентны, поскольку по первому (большему по площади фрагменту опухоли) присвоена оценка в 4 балла, а вторичному 3 - это третья стадия рака (3 балла по ISUP). Если же ситуация обратная и вторичная опухоль имеет оенку 3, а большая по пллощади(первая) 4 - это 2 стадия рака\nПодробное описание по ссылке: https://www.kaggle.com/c/prostate-cancer-grade-assessment/overview/additional-resources **"},{"metadata":{},"cell_type":"markdown","source":"**Необходимо заметить, что в нашем признаке gleason_score фигурирует помимо 0+0 (рак отсутствует) вариант negative, причем только данные учреждения randbound. \n**"},{"metadata":{"trusted":true},"cell_type":"code","source":"train[train['gleason_score']=='negative']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(len(train[train['gleason_score']=='0+0']['isup_grade']))\nprint(len(train[train['gleason_score']=='negative']['isup_grade']))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**При этом isup=0 и данные train проверены патологами, следовательно, 0+0 и 'negative' - ничто иное, как отсутствие рака (https://www.kaggle.com/c/prostate-cancer-grade-assessment/overview/additional-resources).\nСледовательно, мы можем заменить значение 'negative' на '0+0'**"},{"metadata":{"trusted":true},"cell_type":"code","source":"train['gleason_score'] = train['gleason_score'].apply(lambda x: \"0+0\" if x==\"negative\" else x)\nprint(len(train[train['gleason_score']=='0+0']['isup_grade']))\nprint(len(train[train['gleason_score']=='negative']['isup_grade']))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Как уже указывалось ранее в разделе по проблемам данных, шкала Глисон при участках 3+4 и 4+3 имеет разные значения баллам по ISUP, в то время как '3+5' и '5+3', а также '4+5' и '5+4' одинаковые. А именно: 3+4=2 стадия рака, 4+3=3 стадия рака. Это связанно с тем, что первый участок биопсии берется с самой большей опухоли по площади, где больше раковых клеток. Проверим еще раз на данных**"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train[(train['gleason_score']=='3+4') | (train['gleason_score']=='4+3')]['isup_grade'].unique())\nprint(train[(train['gleason_score']=='3+5') | (train['gleason_score']=='5+3')]['isup_grade'].unique())\nprint(train[(train['gleason_score']=='5+4') | (train['gleason_score']=='4+5')]['isup_grade'].unique())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"** Рассмотрим еще раз подробно варианты присвоения баллов по вариантам '3+4' и '4+3'**"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train[train['gleason_score']=='3+4']['isup_grade'].unique())\nprint(train[train['gleason_score']=='4+3']['isup_grade'].unique())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Видимо, по какой-то причине в данных существует ошибка: не может быть 4+3 оценен во вторую стадию рака. Посмотрим, сколько пациентов существует с такими ошибками в нашем датасете"},{"metadata":{"trusted":true},"cell_type":"code","source":"train[(train['isup_grade'] == 2) & (train['gleason_score'] == '4+3')]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Посколько ошибчна всего лишь одна строка (либо вместо 3 стадии рака присудили 2ю, либо не 4+3 на самом деле), удалим данные, чтобы не путать модель"},{"metadata":{"trusted":true},"cell_type":"code","source":"train.drop([7273],inplace=True)\ntrain.value_counts().sum()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Посмотрим теперь, что лежит в датасете test.csv"},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head(10)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# EDA анализ"},{"metadata":{},"cell_type":"markdown","source":"Итак, посмотрим, какие существуют варианты стадии в процентном соотношении и есть ли зависимость порядка по номеру картинки от стадии"},{"metadata":{"trusted":true},"cell_type":"code","source":"train['image_id']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"((train.groupby('isup_grade')['image_id'].count()).sort_values(ascending=False))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Конечно же в соответствии со статистикой заболеваемости пациентов, рак встречается меньшего количества людей, здоровых, к счастью, больше - до 3000, с 1 стадией около 2600 чел, а с разными тяжелыми случаями до 1500 пациенков на каждую стадию рака со 2й по самую тяжелую 5ю "},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure(figsize=(10,6))\nax = sns.countplot(x=\"isup_grade\", hue=\"data_provider\",palette=[\"#bcbddc\", \"#efedf5\"], data=train)\nfor p in ax.patches:\n    height = p.get_height()\n    ax.text(p.get_x()+p.get_width()/2,\n                height +3,\n                '{:1.2f}%'.format(100*height/10616),\n                ha=\"center\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"При этом больше данных по пациентам из университета karolinska, по каждой стадии есть данные в процентном соотношении"},{"metadata":{},"cell_type":"markdown","source":"Посмотрим по тому же самому принципу на распределение баллов по шкале Глисон"},{"metadata":{"trusted":true},"cell_type":"code","source":"((train.groupby('gleason_score')['image_id'].count()).sort_values(ascending=False))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure(figsize=(10,6))\nax = sns.countplot(x=\"gleason_score\", hue=\"data_provider\",palette=[\"#bcbddc\", \"#efedf5\"], data=train)\nfor p in ax.patches:\n    height = p.get_height()\n    ax.text(p.get_x()+p.get_width()/2,\n                height +3,\n                '{:1.2f}%'.format(100*height/10616),\n                ha=\"center\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Поскольку стадия рака (балл по ISUP) напрямую определен оценкой по Глисону, мы получили примерно те же самые графики визуализации"},{"metadata":{},"cell_type":"markdown","source":"# Image EDA\nПерейдем к разбору изображений для последующего анализа картинок\n\n## 1. Какой формат изображений нам предоставлен и как с ним работать ?\n\nTagged Image File Format (TIFF) - это формат растрового изображения с переменным разрешением, разработанный Aldus (ныне являющийся частью Adobe) в 1986 году. TIFF очень распространен для передачи цветных или полутоновых изображений в приложения для макета страницы, но он менее подходит для доставки веб-контент.\n\nПричины использования:\n* Файлы IFF большие и очень высокого качества. Базовые изображения TIFF легко переносимы; их понимают большинство графических приложений, настольных издательских систем и текстовых редакторов.\n* Спецификация TIFF легко расширяется, хотя это происходит за счет некоторой переносимости. Многие приложения включают в себя собственные расширения, но ряд независимых от приложений расширений распознается большинством программ.\n* Доступны четыре типа базовых изображений TIFF: двухуровневый (черный и белый), шкала серого, палитра (т. Е. Индексированная) и RGB (т. Е. Истинный цвет). Изображения RGB могут содержать до 16,7 миллиона цветов. Палитра и полутоновые изображения ограничены 256 цветами или оттенками. Обычное расширение TIFF также позволяет использовать изображения CMYK.\n* Файлы TIFF могут быть сжаты, а могут и не быть. Для сжатия файлов TIFF можно использовать ряд методов, включая алгоритмы Хаффмана и LZW. Даже сжатые файлы TIFF обычно намного больше, чем аналогичные файлы GIF или JPEG.\n* Поскольку файлы очень большие и существует так много возможных вариантов каждого типа файлов TIFF, немногие веб-браузеры могут отображать их без подключаемых модулей.\n\n## 2. Зачем нам уровни (levels) изображения?\nРазмеры изображения биопсии довольно велики (обычно от 5.000 до 40.000 пикселей по осям x и y). Поэтому используют уменьшение разрешения для больших возможностей распознавания и работы с данныеми.\nКаждый слайд нашего изображения биопсии имеет 3 уровня, которые можно загрузить, что соответствует понижению дискретизации на 1, 4 и 16. Промежуточные уровни могут быть созданы путем понижения дискретизации с более высоким уровнем разрешения.\nРазмеры каждого уровня различаются в зависимости от размеров исходного изображения.\nВ некоторых форматах изображений данные изображения имеют фиксированное количество возможных значений интенсивности. Например, изображение может быть определено как uint8 (8-битное целое число без знака), что означает, что каждый пиксель может иметь значение (интенсивность) от 0 до 255, и каждая интенсивность является целым числом (целым числом) в этом диапазоне. Это дает 256 возможных уровней интенсивности. Другой способ интерпретировать это - слои. Изображение типа RGB (красный, зеленый, синий) использует три слоя для определения цвета (один слой определяет крупномасштабное изображение, некоторые типы изображений содержат более трех слоев). Для каждого пикселя определены 3 уровня интенсивности, по 1 для каждого цвета, и вместе (используя своего рода смешивание цветов) они определяют цвет этих пикселей. Точно так же для шкалы серого может быть два уровня: черный и белый.\n\n## 3. Что такое понижающая(Downsampling) и повышающая(Upsampling) дискретизация при обработке изображений?\nПонижение частоты дискретизации и повышение частоты дискретизации - две фундаментальные и широко используемые операции с изображениями. Они нужны нам для работы с определенной частью изображения (неободимо различение клеток железистой и соединительной ткани - насколько их контуры не похжи на нормальные, насколько их стало меньше/больше - это и выявит стадию заболевания пациента).\nDownsampling - это снижение пространственного разрешения при сохранении того же двухмерного (2D) представления. Обычно он используется для уменьшения требований к хранению и / или передаче изображений.\nUpsampling - повышающая дискретизация - это увеличение пространственного разрешения при сохранении 2D-представления.\nизображения. \nОбычно она используется для увеличения небольшой области изображения и для устранения эффекта пикселизации, который возникает, когда изображение с низким разрешением отображается на относительно большом кадре.\n\n\nOpenSlide (документация по ссылке https://openslide.org/api/python/) является очень удобным инструментом для работы с такими изображениями, \nПреимущество OpenSlide в том, что мы можем загружать произвольные области слайда, не загружая в память все изображение."},{"metadata":{},"cell_type":"markdown","source":"Попробуем загрузить и рассмотреть одну из биопсий (image_id) через OpenSlide."},{"metadata":{},"cell_type":"markdown","source":"# Теперь посмотрим, какую информацию мы можем получить из изображения после создания объекта Openslide."},{"metadata":{},"cell_type":"markdown","source":"Для этого создадим функцию, которая позволяет печатать увеличенные и не масштабированные изображения рядом, \nа также всю информацию, которая может быть получена из них."},{"metadata":{"trusted":true},"cell_type":"code","source":"def image_info(image,lev, coordinate, max_size=(600,400)):\n    slide = openslide.OpenSlide(os.path.join(BASE_FOLDER+\"train_images\", f'{image}.tiff'))\n    # Здесь мы вычисляем \"интервал между пикселями\": физический размер пикселя изображения.\n    # OpenSlide дает разрешение в сантиметрах, поэтому мы конвертируем его в микроны\n    f,ax =  plt.subplots(2 ,figsize=(6,16))\n    spacing = 1 / (float(slide.properties['tiff.XResolution']) / 10000)\n    #Выведем на экран увеличенное изображение\n    patch = slide.read_region(coordinate, lev, (256, 256)) \n    ax[0].imshow(patch) \n    ax[0].set_title('Увеличенный отрезок биопсии')\n    \n    \n    ax[1].imshow(slide.get_thumbnail(size=max_size)) #не увеличенное изображение\n    ax[1].set_title('Полная биопсия')\n    \n    \n    print(f\"File id: {slide}\")\n    print(f\"Размеры: {slide.dimensions}\")\n    print(f\"Microns per pixel / pixel spacing: {spacing:.3f}\")\n    print(f\"Количество слоев: {slide.level_count}\")\n    print(f\"Уменьшение разрешения изображения в слое: {slide.level_downsamples}\")\n    print(f\"Размеры слоя: {slide.level_dimensions}\\n\\n\")\n    print(f\"ISUP стадия рака: {train.loc[train['image_id']==image, 'isup_grade']}\")\n    print(f\"Шкала Глисон: {train.loc[train['image_id']==image, 'gleason_score']}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Рассмотрим слайды с нормальной железистой тканью (без рака) по двум университетам"},{"metadata":{"trusted":true},"cell_type":"code","source":"train[train['isup_grade']==0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_info('0005f7aaab2800f6170c399693a96917', 0, (7000,15000))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_info('ffb16f062dfe8fd1161eb29ad1bd80ab',0,  (2800,17500))\n# '0005f7aaab2800f6170c399693a96917', 1, (7000,15000)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Цвета и структура по двум университетам тканей отличаются. Это сязано с лабораторными процедурами.\nПосмотрим теперь на 1 стадию рака"},{"metadata":{"trusted":true},"cell_type":"code","source":"train['gleason_score'].value_counts()\ntrain[train['gleason_score']=='3+3']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_info('003046e27c8ead3e3db155780dc5498e',0, (17000,5600))\n# image_info('ffb16f062dfe8fd1161eb29ad1bd80ab',0,  (2800,17500))\n# '0005f7aaab2800f6170c399693a96917', 0, (7000,15000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_info('ff3667b139539475d4de2aa1b2325c82',0, (15000,2500))\n# image_info('003046e27c8ead3e3db155780dc5498e',0, (17000,5600))\n# image_info('ffb16f062dfe8fd1161eb29ad1bd80ab',0,  (2800,17500))\n# '0005f7aaab2800f6170c399693a96917', 0, (7000,15000)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Опят видим значительные различия в цвете по поставщику информации. Загрузим также все остальные стадии рака"},{"metadata":{"trusted":true},"cell_type":"code","source":"train[train['gleason_score']=='3+4'].head(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_info('00bbc1482301d16de3ff63238cfd0b34', 0, (3000,8000))\n# image_info('ff3667b139539475d4de2aa1b2325c82',0, (15000,2500))\n# image_info('003046e27c8ead3e3db155780dc5498e',0, (17000,5600))\n# image_info('ffb16f062dfe8fd1161eb29ad1bd80ab',0,  (2800,17500))\n# '0005f7aaab2800f6170c399693a96917', 0, (7000,15000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[train['gleason_score']=='4+3'].head(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_info('0068d4c7529e34fd4c9da863ce01a161', 0, (4000,8500))\n# image_info('00bbc1482301d16de3ff63238cfd0b34', 0, (3000,8000))\n# image_info('ff3667b139539475d4de2aa1b2325c82',0, (15000,2500))\n# image_info('003046e27c8ead3e3db155780dc5498e',0, (17000,5600))\n# image_info('ffb16f062dfe8fd1161eb29ad1bd80ab',0,  (2800,17500))\n# '0005f7aaab2800f6170c399693a96917', 0, (7000,15000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[train['gleason_score']=='4+4'].head(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_info('0018ae58b01bdadc8e347995b69f99aa', 0, (1000,8000))\n# image_info('0068d4c7529e34fd4c9da863ce01a161', 0, (4000,8500))\n# image_info('00bbc1482301d16de3ff63238cfd0b34', 0, (3000,8000))\n# image_info('ff3667b139539475d4de2aa1b2325c82',0, (15000,2500))\n# image_info('003046e27c8ead3e3db155780dc5498e',0, (17000,5600))\n# image_info('ffb16f062dfe8fd1161eb29ad1bd80ab',0,  (2800,17500))\n# '0005f7aaab2800f6170c399693a96917', 0, (7000,15000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[train['gleason_score']=='5+5'].head(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_info('0403dcc49b1420545299f692f7d8e270', 0, (20000,4900))\n# image_info('0018ae58b01bdadc8e347995b69f99aa', 0, (1000,8000))\n# image_info('0068d4c7529e34fd4c9da863ce01a161', 0, (4000,8500))\n# image_info('00bbc1482301d16de3ff63238cfd0b34', 0, (3000,8000))\n# image_info('ff3667b139539475d4de2aa1b2325c82',0, (15000,2500))\n# image_info('003046e27c8ead3e3db155780dc5498e',0, (17000,5600))\n# image_info('ffb16f062dfe8fd1161eb29ad1bd80ab',0,  (2800,17500))\n# '0005f7aaab2800f6170c399693a96917', 0, (7000,15000)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* **Каждый слайд имеет 3 слоя, которые вы можете загрузить, что соответствует понижению дискретизации на 1, 4 и 16. \n* Размеры каждого уровня различаются в зависимости от размеров исходного изображения.\n* Биопсии могут быть представлена в разных расположениях. \n* Между биопсиями наблюдаются заметные различия в цвете **"},{"metadata":{},"cell_type":"markdown","source":"### Выведем сразу несколько изображений с разными ISUP и gleason_score\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"def look_canser(images):\n    '''\n    Функция позволяет загружать изображения через Оpenslide попеременно, работая с ними и затем, закрывая каждое\n    '''\n    f, ax = plt.subplots(2,3, figsize=(18,22))\n    for i, image in enumerate(images):\n        slide = openslide.OpenSlide(os.path.join(BASE_FOLDER+\"train_images\", f'{image}.tiff')) \n        # Здесь мы вычисляем \"интервал между пикселями\": физический размер пикселя в изображении,\n        #OpenSlide дает разрешение в сантиметрах, поэтому мы преобразуем его в микроны.\n        spacing = 1/(float(slide.properties['tiff.XResolution']) / 10000)\n        patch = slide.read_region((1780, 1920), 0, (256, 256)) \n        ax[i//3, i%3].imshow(patch) #Выводим изображения на экран\n        slide.close()       \n        ax[i//3, i%3].axis('off')\n        #подписываем свои изображения\n        image_id = image\n        data_provider = train.loc[train['image_id']==image, 'data_provider']\n        isup_grade = train.loc[train['image_id']==image, 'isup_grade']\n        gleason_score = train.loc[train['image_id']==image, 'gleason_score']\n        ax[i//3, i%3].set_title(f\"ID: {image}\\nSource: {data_provider} ISUP: {isup_grade} Gleason: {gleason_score}\")\n\n    plt.show() \nimages = [\n    '0403dcc49b1420545299f692f7d8e270',\n    '035b1edd3d1aeeffc77ce5d248a01a53',\n    '0018ae58b01bdadc8e347995b69f99aa',\n    '0076bcb66e46fb485f5ba432b9a1fe8a',\n    '068b0e3be4c35ea983f77accf8351cc8',\n    '0838c82917cd9af681df249264d2769c',\n]\n\nlook_canser(images)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## train_laibel_masks\n\n### 1. Зачем нужна маска для изображения\n\nПомимо метки уровня слайда (присутствующей в файле csv), почти все слайды в обучающем наборе имеют связанную маску с дополнительной информацией метки. Эти маски прямо указывают, какие части ткани являются здоровыми, а какие злокачественными. Эти маски предназначены для помощи в разработке стратегий для выбора наиболее полезных подвыборок изображений. Значения маски зависят от поставщика данных:\n\n* Radboud: железы предстательной железы имеют индивидуальную маркировку. Допустимые значения:\n            0: фон (не ткань) или неизвестно\n            1: строма (соединительная ткань, неэпителиальная ткань)\n            2: здоровый (доброкачественный) эпителий\n            3: злокачественный эпителий (Глисон 3)\n            4: злокачественный эпителий (Глисон 4)\n            5: злокачественный эпителий (Глисон 5)\n\n* Karolinska: регионы помечены. Допустимые значения:\n               1: фон (не ткань) или неизвестно\n               2: доброкачественная ткань (строма и эпителий вместе)\n               3: злокачественная ткань (строма и эпителий вместе)"},{"metadata":{},"cell_type":"markdown","source":"посмотрим на какую-нибудь маску"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_labels.head(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"label_mask =  openslide.OpenSlide(os.path.join(mask_dir, f'{\"0005f7aaab2800f6170c399693a96917\"}_mask.tiff'))\ndisplay(label_mask.get_thumbnail(size=(1000,200)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Информация метки хранится в красном (R) канале, другие каналы * установлены на ноль и могут игнорироваться *.\n\n* Маски не являются данными изображения, как WSI. Они представляют собой просто матрицы со значениями, основанными на информации поставщика данных, представленной выше, вместо того, чтобы содержать диапазон значений от 0 до 255, они увеличиваются только до максимума 6, представляя различные метки классов (подробности о метках масок см. в описании набора данных). Поэтому, когда вы пытаетесь визуализировать маску, она будет казаться очень темной, поскольку каждое значение близко к 0. Применение цветовой карты устраняет проблему, назначая каждой метке от 0 до 6 отдельный цвет.\n\nИтак, что нам нужно сделать, это взять файл изображения для чтения с помощью объекта openslide, извлечь значения Red Level и затем применить к нему cmap.\n\nИспользуя небольшую вспомогательную функцию, мы можем отобразить некоторую основную информацию о маске. Чтобы упростить проверку масок, мы сопоставляем метки int с цветами RGB с помощью цветовой палитры. "},{"metadata":{},"cell_type":"markdown","source":"Используем изображения, которые мы рассмотрели ранее (с разными баллами и стадиями рака, также нормальную ткань)"},{"metadata":{"trusted":true},"cell_type":"code","source":"def mask_info(slides):    \n    f, ax = plt.subplots(2,3, figsize=(18,22))\n    for i, slide in enumerate(slides):\n        \n        mask = openslide.OpenSlide(os.path.join(mask_dir, f'{slide}_mask.tiff'))\n        mask_find = mask.read_region((0,0), mask.level_count - 1, mask.level_dimensions[-1])\n        cmap = matplotlib.colors.ListedColormap(['black', 'gray', 'green', 'yellow', 'orange', 'red'])\n\n        ax[i//3, i%3].imshow(np.asarray(mask_find)[:,:,0], cmap=cmap, interpolation='nearest', vmin=0, vmax=5) \n        mask.close()       \n        ax[i//3, i%3].axis('off')\n        \n        image_id = slide\n        data_provider = train.loc[train['image_id']==slide, 'data_provider']\n        isup_grade = train.loc[train['image_id']==slide, 'isup_grade']\n        gleason_score = train.loc[train['image_id']==slide, 'gleason_score']\n        ax[i//3, i%3].set_title(f\"ID: {image_id}\\nSource: {data_provider} ISUP: {isup_grade} Gleason: {gleason_score}\")\n        f.tight_layout()\n        \n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mask_info(images) ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Добавим условие - визуализировать маску, если она есть"},{"metadata":{"trusted":true},"cell_type":"code","source":"def mask_vizual(image,max_size=(600,400)):\n    slide = openslide.OpenSlide(os.path.join(BASE_FOLDER+\"train_images\", f'{image}.tiff'))\n    mask =  openslide.OpenSlide(os.path.join(mask_dir, f'{image}_mask.tiff'))\n    # Here we compute the \"pixel spacing\": the physical size of a pixel in the image.\n    # OpenSlide gives the resolution in centimeters so we convert this to microns.\n    f,ax =  plt.subplots(1,2 ,figsize=(18,22))\n    spacing = 1 / (float(slide.properties['tiff.XResolution']) / 10000)\n    img = slide.get_thumbnail(size=(600,400)) #IMAGE \n    \n    mask_find = mask.read_region((0,0), mask.level_count - 1, mask.level_dimensions[-1])\n    cmap = matplotlib.colors.ListedColormap(['black', 'gray', 'green', 'yellow', 'orange', 'red'])\n    \n    ax[0].imshow(img) \n    #ax[0].set_title('Image')\n    \n    \n    ax[1].imshow(np.asarray(mask_find)[:,:,0], cmap=cmap, interpolation='nearest', vmin=0, vmax=5) #IMAGE MASKS\n    #ax[1].set_title('Image_MASK')\n    \n    \n    image_id = image\n    data_provider = train.loc[train['image_id']==image, 'data_provider']\n    isup_grade = train.loc[train['image_id']==image, 'isup_grade']\n    gleason_score = train.loc[train['image_id']==image, 'gleason_score']\n    ax[0].set_title(f\"ID: {image_id}\\nSource: {data_provider}\\n ISUP: {isup_grade}\\n Gleason: {gleason_score} \\nIMAGE\")\n    ax[1].set_title(f\"ID: {image_id}\\nSource: {data_provider} \\nISUP: {isup_grade} \\nGleason: {gleason_score} \\nIMAGE_MASK\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mask_vizual('0403dcc49b1420545299f692f7d8e270')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for image in images:\n    mask_vizual(image)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"##  Различение злокачественных и незлокачественных областей с помощью МАСКИ\n\nПри визуализации маски необходимо учитывать, что маски разные для разных поставщиков данных"},{"metadata":{"trusted":true},"cell_type":"code","source":"def carnser_or_normal(image_mask):\n    mask =  openslide.OpenSlide(os.path.join(mask_dir, f'{image_mask}_mask.tiff'))\n    mask_level = mask.read_region((0,0),mask.level_count - 1,mask.level_dimensions[-1]) #Selecting the level\n    mask_find = np.asarray(mask_level)[:,:,0] #SELECTING R from RGB\n    mask_background = np.where(mask_find == 0, 1, 0).astype(np.uint8) # SELECTING BG\n    mask_benign = np.where(mask_find == 1, 1, 0).astype(np.uint8) #SELECTING BENIGN LABELS\n    \n    if (train[train['image_id'] == image_mask]['data_provider'] == 'karolinska').empty == True:\n    #train.loc[image_mask,'data_provider'] == 'karolinska':\n        mask_cancerous = np.where(mask_find == 2, 1, 0).astype(np.uint8) #SELECTING CANCEROUS LABELS\n    else:\n#     elif train.loc[image_mask,'data_provider'] == 'radboud':\n        mask_cancerous = np.where(mask_find == 5, 1, 0).astype(np.uint8) #SELECTING NON-CANCEROUS LABELS\n    \n    return mask_background,mask_benign,mask_cancerous","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"background,benign,cancerous = carnser_or_normal('0403dcc49b1420545299f692f7d8e270')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Теперь, когда у нас есть функция, давайте визуализируем"},{"metadata":{"trusted":true},"cell_type":"code","source":"image2 =[ '0403dcc49b1420545299f692f7d8e270',\n    '0068d4c7529e34fd4c9da863ce01a161']\n\nfor image in image2:\n    background,benign,cancerous = carnser_or_normal(image)\n\n    #if train.loc[image,'data_provider'] == 'karolinska'\n    fig, ax = plt.subplots(1, 3, figsize=(18, 12))\n\n    ax[0].imshow(background.astype(float), cmap=plt.cm.gray)\n    ax[0].axis('off')\n    ax[0].set_title('background');\n\n\n#     ax[0].set_title('background,'+'  '+'data_provider:'+train.loc[image][\"data_provider\"]);\n    ax[1].imshow(benign.astype(float), cmap=plt.cm.gray)\n    ax[1].axis('off')\n    ax[1].set_title('benign');\n    ax[2].imshow(cancerous.astype(float), cmap=plt.cm.gray)\n    ax[2].axis('off')\n    ax[2].set_title('cancerous')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Наложение масок на слайды\nТеперь, когда мы узнали, как визуализировать маски и отображать их рядом, давайте наложим их друг на друга.\n\nПоскольку маски имеют тот же размер, что и слайды, мы можем накладывать маски на ткань, чтобы сразу увидеть, какие области являются злокачественными. Это наложение поможет нам определить различные модели роста. Для этого мы загружаем и маску, и биопсию и объединяем их с помощью PIL."},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def find_canser(images, center='radboud', alpha=0.8, max_size=(800, 800)):\n    \"\"\"Наложить маску на биопсию для различения региона раковых клеток\"\"\"\n    f, ax = plt.subplots(2,3, figsize=(18,22))\n    \n    \n    for i, image_id in enumerate(images):\n        slide = openslide.OpenSlide(os.path.join(BASE_FOLDER+\"train_images\", f'{image_id}.tiff'))\n        mask = openslide.OpenSlide(os.path.join(mask_dir, f'{image_id}_mask.tiff'))\n        slide_data = slide.read_region((0,0), slide.level_count - 1, slide.level_dimensions[-1])\n        mask_data = mask.read_region((0,0), mask.level_count - 1, mask.level_dimensions[-1])\n        mask_data = mask_data.split()[0]\n        \n        # Create alpha mask\n        alpha_int = int(round(255*alpha))\n        if center == 'radboud':\n            alpha_content = np.less(mask_data.split()[0], 2).astype('uint8') * alpha_int + (255 - alpha_int)\n        elif center == 'karolinska':\n            alpha_content = np.less(mask_data.split()[0], 1).astype('uint8') * alpha_int + (255 - alpha_int)\n\n        alpha_content = PIL.Image.fromarray(alpha_content)\n        preview_palette = np.zeros(shape=768, dtype=int)\n\n        if center == 'radboud':\n            # Отображение: {0: фон (не ткань) или неизвестно, 1: соединительная ткань, 2: здоровый эпителий, \n            # 3: Gleason 3, 4: Gleason 4, 5: Gleason 5}\n            preview_palette[0:18] = (np.array([0, 0, 0, 0.5, 0.5, 0.5, 0, 1, 0, 1, 1, 0.7, 1, 0.5, 0, 1, 0, 0]) * 255).astype(int)\n        elif center == 'karolinska':\n            # Отображение: {0: фон (не ткань) или неизвестно, 1: доброкачественная,\n            # 2: рак}\n            preview_palette[0:9] = (np.array([0, 0, 0, 0, 1, 0, 1, 0, 0]) * 255).astype(int)\n\n        mask_data.putpalette(data=preview_palette.tolist())\n        mask_rgb = mask_data.convert(mode='RGB')\n        overlayed_image = PIL.Image.composite(image1=slide_data, image2=mask_rgb, mask=alpha_content)\n        overlayed_image.thumbnail(size=max_size, resample=0)\n\n        \n        ax[i//3, i%3].imshow(overlayed_image) \n        slide.close()\n        mask.close()       \n        ax[i//3, i%3].axis('off')\n        image_id = image\n        data_provider = train.loc[train['image_id']==image, 'data_provider']\n        isup_grade = train.loc[train['image_id']==image, 'isup_grade']\n        gleason_score = train.loc[train['image_id']==image, 'gleason_score']\n        ax[i//3, i%3].set_title(f\"ID: {image_id}\\nSource: {data_provider} \\nISUP: {isup_grade} \\nGleason: {gleason_score}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"find_canser(images)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Тренировка модели"},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nБаза данных ImageNet — проект по созданию и сопровождению массивной базы данных аннотированных изображений, предназначенная для отработки и тестирования методов распознавания образов и машинного зрения. По состоянию на 2016 год в базу данных было записано около десяти миллионов URL с изображениями, которые прошли ручную аннотацию для ImageNet, в аннотациях перечислялись объекты, попавшие на изображение, и прямоугольники с их координатами. База данных с аннотацией и URL изображений от третьих лиц доступна непосредственно через ImageNet, но при этом сами изображения не принадлежат проекту. С 2010 года ведётся проект ILSVRC (англ. ImageNet Large Scale Visual Recognition Challenge — Кампания по широкомасштабному распознаванию образов в ImageNet), в рамках которого различные программные продукты ежегодно соревнуются в классификации и распознавании объектов и сцен в базе данных ImageNet.\n\nhttps://wiki2.org/ru/ImageNet - подробное описание\n"},{"metadata":{},"cell_type":"markdown","source":"## EfficientNet-B3 Model\n"},{"metadata":{},"cell_type":"markdown","source":"Для предобработки данных мы использовали трансферное обучение (метод машинного обучения, при котором используют предварительно обученную нейронную сеть для решения аналогичной ранее поставленной перед ней проблемы).\n\nСоздавая «слишком глубокую слоистую сеть» мы можем в какой-то момент получить невысокие результаты. \nEfficientNet -  предобученная на ImageNet сеть. \n\nБаза данных ImageNet — проект по созданию и сопровождению массивной базы данных аннотированных изображений, предназначенная для отработки и тестирования методов распознавания образов и машинного зрения. По состоянию на 2016 год в базу данных было записано около десяти миллионов URL с изображениями, которые прошли ручную аннотацию для ImageNet, в аннотациях перечислялись объекты, попавшие на изображение, и прямоугольники с их координатами. База данных с аннотацией и URL изображений от третьих лиц доступна непосредственно через ImageNet, но при этом сами изображения не принадлежат проекту. С 2010 года ведётся проект ILSVRC (англ. ImageNet Large Scale Visual Recognition Challenge — Кампания по широкомасштабному распознаванию образов в ImageNet), в рамках которого различные программные продукты ежегодно соревнуются в классификации и распознавании объектов и сцен в базе данных ImageNet.\nhttps://wiki2.org/ru/ImageNet - подробное описание\n\n\nEfficientNet — класс новых моделей, который получился из изучения масштабирования (скейлинг, scaling) моделей и балансирования между собой глубины и ширины (количества каналов) сети, а также разрешения изображений в сети. Авторы статьи предлагают новый метод составного масштабирования (compound scaling method), который равномерно масштабирует глубину/ширину/разрешение с фиксированными пропорциями между ними. Из существующего метода под названием «Neural Architecture Search»  для автоматического создания новых сетей и своего собственного метода масштабирования авторы получают новый класс моделей под названием EfficientNets.\nСсылки на статьи и репозиторий:\nhttps://habr.com/ru/post/498168/\nhttps://www.kaggle.com/hmendonca/efficientnet-pytorch https://www.kaggle.com/hmendonca/efficientnet-pytorch\nhttps://github.com/qubvel/efficientnet/blob/master/efficientnet/weights.py\n"},{"metadata":{},"cell_type":"markdown","source":"Создадим отдельные классы EfficientNet, в которых будем обращаться по ссылкам к приложенным предобученным экземплярам моделей данной трансферной сети."},{"metadata":{"trusted":true},"cell_type":"code","source":"\nclass EfficientNetB3(nn.Module):\n    def __init__(self, pretrained):\n        super(EfficientNetB3, self).__init__()\n        if pretrained == True:\n            self.model = EfficientNet.from_name('efficientnet-b3')\n            self.model.load_state_dict(torch.load('../input/efficientnet-pytorch/efficientnet-b3-c8376fa2.pth'))\n        else:\n            self.model = EfficientNet.from_pretrained(None)            \n\n        in_features = self.model._fc.in_features\n        self.l0 = nn.Linear(in_features, config.CLASSES)\n\n    def forward(self, x):\n        bs, _, _, _ = x.shape\n        x = self.model.extract_features(x)\n        x = F.adaptive_avg_pool2d(x, 1).reshape(bs, -1)\n        l0 = self.l0(x)\n        return l0\n    \nclass EfficientNetB0(nn.Module):\n    def __init__(self, pretrained):\n        super(EfficientNetB0, self).__init__()\n        if pretrained == True:            \n            self.model = EfficientNet.from_name('efficientnet-b0')\n            self.model.load_state_dict(torch.load('../input/efficientnet-pytorch/efficientnet-b0-08094119.pth'))\n        else:\n            self.model = EfficientNet.from_pretrained(None)            \n\n        in_features = self.model._fc.in_features\n        self.l0 = nn.Linear(in_features, config.CLASSES)\n\n    def forward(self, x):\n        bs, _, _, _ = x.shape\n        x = self.model.extract_features(x)\n        x = F.adaptive_avg_pool2d(x, 1).reshape(bs, -1)\n        l0 = self.l0(x)\n        return l0\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Преобразование изображения с помощью свертки карты фрагментов "},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_tiles(img, mode=0):\n    result = []\n    h, w, c = img.shape\n    pad_h = (tile_size - h % tile_size) % tile_size + ((tile_size * mode) // 2)\n    pad_w = (tile_size - w % tile_size) % tile_size + ((tile_size * mode) // 2)\n\n    img2 = np.pad(img,[[pad_h // 2, pad_h - pad_h // 2], [pad_w // 2,pad_w - pad_w//2], [0,0]], constant_values=255)\n    img3 = img2.reshape(\n        img2.shape[0] // tile_size,\n        tile_size,\n        img2.shape[1] // tile_size,\n        tile_size,\n        3\n    )\n\n    img3 = img3.transpose(0,2,1,3,4).reshape(-1, tile_size, tile_size,3)\n    n_tiles_with_info = (img3.reshape(img3.shape[0],-1).sum(1) < tile_size ** 2 * 3 * 255).sum()\n    if len(img3) < n_tiles:\n        img3 = np.pad(img3,[[0,n_tiles-len(img3)],[0,0],[0,0],[0,0]], constant_values=255)\n    idxs = np.argsort(img3.reshape(img3.shape[0],-1).sum(-1))[:n_tiles]\n    img3 = img3[idxs]\n    for i in range(len(img3)):\n        result.append({'img':img3[i], 'idx':i})\n    return result, n_tiles_with_info >= n_tiles\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Создание изображения после свертки"},{"metadata":{"trusted":true},"cell_type":"code","source":"def write_image(path, img):\n    img = cv2.convertScaleAbs(img, alpha=(255.0))\n    cv2.imwrite(path, cv2.cvtColor(img, cv2.COLOR_RGB2BGR))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"параметры нового изображения"},{"metadata":{"trusted":true},"cell_type":"code","source":"tile_size = 256\nimage_size = 256\nn_tiles = 16","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Создадим отдельный класс датасета под тренировку"},{"metadata":{"trusted":true},"cell_type":"code","source":"class PANDADataset(Dataset):\n    def __init__(self,\n            df,\n            image_size,\n            n_tiles=n_tiles,\n            tile_mode=0,\n            rand=False,\n        ):\n\n        self.df = df.reset_index(drop=True)\n        self.image_size = image_size\n        self.n_tiles = n_tiles\n        self.tile_mode = tile_mode\n        self.rand = rand\n        # we are in validation part\n        self.aug = albumentations.Compose([\n            albumentations.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225], always_apply=True)\n        ])\n\n    def __len__(self):\n        return self.df.shape[0]\n    \n\n    def __getitem__(self, index):\n        row = self.df.iloc[index]\n        img_id = row.image_id\n        \n        tiff_file = os.path.join(data_dir, f'{img_id}.tiff')\n        image = skimage.io.MultiImage(tiff_file)[1]\n        tiles, OK = get_tiles(image, self.tile_mode)\n\n        if self.rand:\n            idxes = np.random.choice(list(range(self.n_tiles)), self.n_tiles, replace=False)\n        else:\n            idxes = list(range(self.n_tiles))\n\n        n_row_tiles = int(np.sqrt(self.n_tiles))\n        images = np.zeros((image_size * n_row_tiles, image_size * n_row_tiles, 3))\n        for h in range(n_row_tiles):\n            for w in range(n_row_tiles):\n                i = h * n_row_tiles + w\n    \n                if len(tiles) > idxes[i]:\n                    this_img = tiles[idxes[i]]['img']\n                else:\n                    this_img = np.ones((self.image_size, self.image_size, 3)).astype(np.uint8) * 255\n                this_img = 255 - this_img\n                h1 = h * image_size\n                w1 = w * image_size\n                images[h1:h1+image_size, w1:w1+image_size] = this_img\n\n                \n        images = images.astype(np.float32)\n        images /= 255\n        images = images.transpose(2, 0, 1)\n        \n        \n        img = images\n        img = np.transpose(img, (1, 2, 0)) # orig image has shape(3,1024, 1024), converting to (1024, 1024, 3)\n        img = 1 - img\n        img = cv2.resize(img, (768, 768))\n        write_image(f'{img_id}.png', img)\n        \n        # Загружка изображения конечного\n        \n        img = skimage.io.MultiImage(f'{img_id}.png')[-1]\n\n        img = Image.fromarray(img).convert(\"RGB\")\n        img = self.aug(image=np.array(img))[\"image\"]\n        img = np.transpose(img, (2, 0, 1)).astype(np.float32)\n        \n\n        return { 'image': torch.tensor(img, dtype=torch.float) }\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Создадим класс"},{"metadata":{"trusted":true},"cell_type":"code","source":"\n\n# class SEModule(nn.Module):\n\n#     def __init__(self, channels, reduction):\n#         super(SEModule, self).__init__()\n#         self.avg_pool = nn.AdaptiveAvgPool2d(1)\n#         self.fc1 = nn.Conv2d(channels, channels // reduction, kernel_size=1,\n#                              padding=0)\n#         self.relu = nn.ReLU(inplace=True)\n#         self.fc2 = nn.Conv2d(channels // reduction, channels, kernel_size=1,\n#                              padding=0)\n#         self.sigmoid = nn.Sigmoid()\n\n#     def forward(self, x):\n#         module_input = x\n#         x = self.avg_pool(x)\n#         x = self.fc1(x)\n#         x = self.relu(x)\n#         x = self.fc2(x)\n#         x = self.sigmoid(x)\n#         return module_input * x\n\n\n# class Bottleneck(nn.Module):\n#     \"\"\"\n#     Base class for bottlenecks that implements `forward()` method.\n#     \"\"\"\n#     def forward(self, x):\n#         residual = x\n\n#         out = self.conv1(x)\n#         out = self.bn1(out)\n#         out = self.relu(out)\n\n#         out = self.conv2(out)\n#         out = self.bn2(out)\n#         out = self.relu(out)\n\n#         out = self.conv3(out)\n#         out = self.bn3(out)\n\n#         if self.downsample is not None:\n#             residual = self.downsample(x)\n\n#         out = self.se_module(out) + residual\n#         out = self.relu(out)\n\n#         return out\n\n\n# class SEBottleneck(Bottleneck):\n#     \"\"\"\n#     Bottleneck for SENet154.\n#     \"\"\"\n#     expansion = 4\n\n#     def __init__(self, inplanes, planes, groups, reduction, stride=1,\n#                  downsample=None):\n#         super(SEBottleneck, self).__init__()\n#         self.conv1 = nn.Conv2d(inplanes, planes * 2, kernel_size=1, bias=False)\n#         self.bn1 = nn.BatchNorm2d(planes * 2)\n#         self.conv2 = nn.Conv2d(planes * 2, planes * 4, kernel_size=3,\n#                                stride=stride, padding=1, groups=groups,\n#                                bias=False)\n#         self.bn2 = nn.BatchNorm2d(planes * 4)\n#         self.conv3 = nn.Conv2d(planes * 4, planes * 4, kernel_size=1,\n#                                bias=False)\n#         self.bn3 = nn.BatchNorm2d(planes * 4)\n#         self.relu = nn.ReLU(inplace=True)\n#         self.se_module = SEModule(planes * 4, reduction=reduction)\n#         self.downsample = downsample\n#         self.stride = stride\n\n\n# class SEResNetBottleneck(Bottleneck):\n#     \"\"\"\n#     ResNet bottleneck with a Squeeze-and-Excitation module. It follows Caffe\n#     implementation and uses `stride=stride` in `conv1` and not in `conv2`\n#     (the latter is used in the torchvision implementation of ResNet).\n#     \"\"\"\n#     expansion = 4\n\n#     def __init__(self, inplanes, planes, groups, reduction, stride=1,\n#                  downsample=None):\n#         super(SEResNetBottleneck, self).__init__()\n#         self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False,\n#                                stride=stride)\n#         self.bn1 = nn.BatchNorm2d(planes)\n#         self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, padding=1,\n#                                groups=groups, bias=False)\n#         self.bn2 = nn.BatchNorm2d(planes)\n#         self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)\n#         self.bn3 = nn.BatchNorm2d(planes * 4)\n#         self.relu = nn.ReLU(inplace=True)\n#         self.se_module = SEModule(planes * 4, reduction=reduction)\n#         self.downsample = downsample\n#         self.stride = stride\n\n\n# class SEResNeXtBottleneck(Bottleneck):\n#     \"\"\"\n#     ResNeXt bottleneck type C with a Squeeze-and-Excitation module.\n#     \"\"\"\n#     expansion = 4\n\n#     def __init__(self, inplanes, planes, groups, reduction, stride=1,\n#                  downsample=None, base_width=4):\n#         super(SEResNeXtBottleneck, self).__init__()\n#         width = math.floor(planes * (base_width / 64)) * groups\n#         self.conv1 = nn.Conv2d(inplanes, width, kernel_size=1, bias=False,\n#                                stride=1)\n#         self.bn1 = nn.BatchNorm2d(width)\n#         self.conv2 = nn.Conv2d(width, width, kernel_size=3, stride=stride,\n#                                padding=1, groups=groups, bias=False)\n#         self.bn2 = nn.BatchNorm2d(width)\n#         self.conv3 = nn.Conv2d(width, planes * 4, kernel_size=1, bias=False)\n#         self.bn3 = nn.BatchNorm2d(planes * 4)\n#         self.relu = nn.ReLU(inplace=True)\n#         self.se_module = SEModule(planes * 4, reduction=reduction)\n#         self.downsample = downsample\n#         self.stride = stride\n\n\n# class SENet(nn.Module):\n\n#     def __init__(self, block, layers, groups, reduction, dropout_p=0.2,\n#                  inplanes=128, input_3x3=True, downsample_kernel_size=3,\n#                  downsample_padding=1, num_classes=1000):\n#         super(SENet, self).__init__()\n#         self.inplanes = inplanes\n#         if input_3x3:\n#             layer0_modules = [\n#                 ('conv1', nn.Conv2d(3, 64, 3, stride=2, padding=1,\n#                                     bias=False)),\n#                 ('bn1', nn.BatchNorm2d(64)),\n#                 ('relu1', nn.ReLU(inplace=True)),\n#                 ('conv2', nn.Conv2d(64, 64, 3, stride=1, padding=1,\n#                                     bias=False)),\n#                 ('bn2', nn.BatchNorm2d(64)),\n#                 ('relu2', nn.ReLU(inplace=True)),\n#                 ('conv3', nn.Conv2d(64, inplanes, 3, stride=1, padding=1,\n#                                     bias=False)),\n#                 ('bn3', nn.BatchNorm2d(inplanes)),\n#                 ('relu3', nn.ReLU(inplace=True)),\n#             ]\n#         else:\n#             layer0_modules = [\n#                 ('conv1', nn.Conv2d(3, inplanes, kernel_size=7, stride=2,\n#                                     padding=3, bias=False)),\n#                 ('bn1', nn.BatchNorm2d(inplanes)),\n#                 ('relu1', nn.ReLU(inplace=True)),\n#             ]\n#         # To preserve compatibility with Caffe weights `ceil_mode=True`\n#         # is used instead of `padding=1`.\n#         layer0_modules.append(('pool', nn.MaxPool2d(3, stride=2,\n#                                                     ceil_mode=True)))\n#         self.layer0 = nn.Sequential(OrderedDict(layer0_modules))\n#         self.layer1 = self._make_layer(\n#             block,\n#             planes=64,\n#             blocks=layers[0],\n#             groups=groups,\n#             reduction=reduction,\n#             downsample_kernel_size=1,\n#             downsample_padding=0\n#         )\n#         self.layer2 = self._make_layer(\n#             block,\n#             planes=128,\n#             blocks=layers[1],\n#             stride=2,\n#             groups=groups,\n#             reduction=reduction,\n#             downsample_kernel_size=downsample_kernel_size,\n#             downsample_padding=downsample_padding\n#         )\n#         self.layer3 = self._make_layer(\n#             block,\n#             planes=256,\n#             blocks=layers[2],\n#             stride=2,\n#             groups=groups,\n#             reduction=reduction,\n#             downsample_kernel_size=downsample_kernel_size,\n#             downsample_padding=downsample_padding\n#         )\n#         self.layer4 = self._make_layer(\n#             block,\n#             planes=512,\n#             blocks=layers[3],\n#             stride=2,\n#             groups=groups,\n#             reduction=reduction,\n#             downsample_kernel_size=downsample_kernel_size,\n#             downsample_padding=downsample_padding\n#         )\n#         self.avg_pool = nn.AvgPool2d(7, stride=1)\n#         self.dropout = nn.Dropout(dropout_p) if dropout_p is not None else None\n#         self.last_linear = nn.Linear(512 * block.expansion, num_classes)\n\n#     def _make_layer(self, block, planes, blocks, groups, reduction, stride=1,\n#                     downsample_kernel_size=1, downsample_padding=0):\n#         downsample = None\n#         if stride != 1 or self.inplanes != planes * block.expansion:\n#             downsample = nn.Sequential(\n#                 nn.Conv2d(self.inplanes, planes * block.expansion,\n#                           kernel_size=downsample_kernel_size, stride=stride,\n#                           padding=downsample_padding, bias=False),\n#                 nn.BatchNorm2d(planes * block.expansion),\n#             )\n\n#         layers = []\n#         layers.append(block(self.inplanes, planes, groups, reduction, stride,\n#                             downsample))\n#         self.inplanes = planes * block.expansion\n#         for i in range(1, blocks):\n#             layers.append(block(self.inplanes, planes, groups, reduction))\n\n#         return nn.Sequential(*layers)\n\n#     def features(self, x):\n#         x = self.layer0(x)\n#         x = self.layer1(x)\n#         x = self.layer2(x)\n#         x = self.layer3(x)\n#         x = self.layer4(x)\n#         return x\n\n#     def logits(self, x):\n#         x = self.avg_pool(x)\n#         if self.dropout is not None:\n#             x = self.dropout(x)\n#         x = x.view(x.size(0), -1)\n#         x = self.last_linear(x)\n#         return x\n\n#     def forward(self, x):\n#         x = self.features(x)\n#         x = self.logits(x)\n#         return x\n\n\n# def initialize_pretrained_model(model, num_classes, settings):\n#     assert num_classes == settings['num_classes'], \\\n#         'num_classes should be {}, but is {}'.format(\n#             settings['num_classes'], num_classes)\n#     model.load_state_dict(model_zoo.load_url(settings['url']))\n#     model.input_space = settings['input_space']\n#     model.input_size = settings['input_size']\n#     model.input_range = settings['input_range']\n#     model.mean = settings['mean']\n#     model.std = settings['std']\n\n\n# def se_resnext50_32x4d(num_classes=1000, pretrained='imagenet'):\n#     model = SENet(SEResNeXtBottleneck, [3, 4, 6, 3], groups=32, reduction=16,\n#                   dropout_p=None, inplanes=64, input_3x3=False,\n#                   downsample_kernel_size=1, downsample_padding=0,\n#                   num_classes=num_classes)\n#     if pretrained is not None:\n#         settings = config.pretrained_settings['se_resnext50_32x4d'][pretrained]\n#         initialize_pretrained_model(model, num_classes, settings)\n#     return model\n\n\n# def se_resnext101_32x4d(num_classes=1000, pretrained='imagenet'):\n#     model = SENet(SEResNeXtBottleneck, [3, 4, 23, 3], groups=32, reduction=16,\n#                   dropout_p=None, inplanes=64, input_3x3=False,\n#                   downsample_kernel_size=1, downsample_padding=0,\n#                   num_classes=num_classes)\n#     if pretrained is not None:\n#         settings = config.pretrained_settings['se_resnext101_32x4d'][pretrained]\n#         initialize_pretrained_model(model, num_classes, settings)\n#     return model\n\n# class CustomSEResNeXt(nn.Module):\n\n#     def __init__(self, model_name='se_resnext50_32x4d'):\n#         assert model_name in ('se_resnext50_32x4d')\n#         super().__init__()\n        \n#         self.model = se_resnext50_32x4d(pretrained=None)\n#         self.model.avg_pool = nn.AdaptiveAvgPool2d(1)\n#         self.model.last_linear = nn.Linear(self.model.last_linear.in_features, config.CLASSES)\n        \n#     def forward(self, x):\n#         x = self.model(x)\n#         return x","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"В обучении будет участвовать модель efficientnetB0_0"},{"metadata":{"trusted":true},"cell_type":"code","source":"ENSEMBLES = [\n    {\n        'model_name': 'efficientnet-b0',\n        'model_weight': '../input/panda-inference-ensemble-trying-various-models/efficientnetB0_0.pth',\n        'ensemble_weight': 1 \n    }\n#         ,\n#     {\n#         'model_name': 'se_resnext50_32x4d',\n#         'model_weight': '../input/panda-open-models/resnext50_0.pth',\n#         'ensemble_weight': 1 \n#     }\n]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Тренируем модель на подключенном виртуальном девайсе"},{"metadata":{"trusted":true},"cell_type":"code","source":"device = config.device\nmodels = []\n\nensemble = {\n    'model_name': 'efficientnet-b0',\n    'model_weight': '../input/panda-inference-ensemble-trying-various-models/efficientnetB0_0.pth',\n    'ensemble_weight': 1 \n}\nfor ensemble in ENSEMBLES:\n    model = EfficientNetB3(pretrained=True)\n    model.load_state_dict(torch.load(ensemble['model_weight'], map_location=device))\n    model.to(device)\n    models.append(model)\n\n# ensemble = {\n#     'model_name': 'se_resnext50_32x4d',\n#     'model_weight': '../input/panda-open-models/resnext50_0.pth',\n#     'ensemble_weight': 1 \n# }\n# model = CustomSEResNeXt(model_name=ensemble['model_name'])\n# model.load_state_dict(torch.load(ensemble['model_weight'], map_location=device))\n# model.to(device)\n# models.append(model)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Проверка существования тренировочных изображений"},{"metadata":{"trusted":true},"cell_type":"code","source":"def check_for_images_dir():\n    if config.DEBUG:\n        return os.path.exists('../input/prostate-cancer-grade-assessment/train_images')\n    else:\n        return os.path.exists('../input/prostate-cancer-grade-assessment/train_images')      ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Применение предобученной сети к сформированному датасету подготовленных данных"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\npredictions = []\n\nif check_for_images_dir():\n    \n    test_dataset = PANDADataset(\n        test,\n        image_size,\n        n_tiles,\n        0\n    )\n\n# разбиваем изображение биопсии на 16 частей\n    test_data_loader = torch.utils.data.DataLoader(\n        test_dataset,\n        batch_size=config.TEST_BATCH_SIZE,\n        shuffle=False,\n    )\n\n    for model in models:\n        preds = []\n        for idx, d in tqdm(enumerate(test_data_loader), total=len(test_data_loader)):\n            inputs = d[\"image\"]\n            inputs = inputs.to(device)\n\n            with torch.no_grad():\n                outputs = model(inputs)\n            preds.append(outputs.to('cpu').numpy())\n                    \n        predictions.append(np.concatenate(preds))\n    predictions = np.mean(predictions, axis=0)\n    predictions = predictions.argmax(1)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n# Метрика качества модели\n\nДля оценки результатов исследования используем Accuracy и F-меру.\nhttps://habr.com/ru/company/ods/blog/328372/\n\nAccuracy Metric.\n\nИспользуем самую распространенную базовую метрику классификации Accuracy Metric, так как она интуитивно понятна и очевидна — доля правильных ответов созданного нами алгоритма.\nОна измеряет количество верно классифицированных объектов относительно общего количества всех объектов.\n\n\n\nF1 score \n\nF-мера — среднее гармоническое precision и recall. Precision можно интерпретировать как долю объектов, названных классификатором положительными и при этом действительно являющимися положительными, а recall показывает, какую долю объектов положительного класса из всех объектов положительного класса нашел \nалгоритм.\n\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"test['y_pred'] = predictions","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nprint(accuracy_score(test['isup_grade'], test['y_pred']))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print (f1_score(test['isup_grade'], test['y_pred'], average='weighted'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}