{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"%matplotlib inline\n#這是juoyter notebook的magic word˙\n\nimport matplotlib\nimport matplotlib.pyplot as plt\nfrom IPython import display","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-19T08:07:36.445749Z","iopub.execute_input":"2021-06-19T08:07:36.446163Z","iopub.status.idle":"2021-06-19T08:07:36.456750Z","shell.execute_reply.started":"2021-06-19T08:07:36.446082Z","shell.execute_reply":"2021-06-19T08:07:36.455750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 病理切片癌症檢測","metadata":{}},{"cell_type":"code","source":"import os\n#判斷是否在jupyter notebook上\ndef is_in_ipython():\n    \"Is the code running in the ipython environment (jupyter including)\"\n    program_name = os.path.basename(os.getenv('_', ''))\n\n    if ('jupyter-notebook' in program_name or # jupyter-notebook\n        'ipython'          in program_name or # ipython\n        'jupyter' in program_name or  # jupyter\n        'JPY_PARENT_PID'   in os.environ):    # ipython-notebook\n        return True\n    else:\n        return False\n\n\n#判斷是否在colab上\ndef is_in_colab():\n    if not is_in_ipython(): return False\n    try:\n        from google import colab\n        return True\n    except: return False\n\n#判斷是否在kaggke_kernal上\ndef is_in_kaggle_kernal():\n    if 'kaggle' in os.environ['PYTHONPATH']:\n        return True\n    else:\n        return False\n\nif is_in_colab():\n    from google.colab import drive\n    drive.mount('/content/gdrive')","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:07:36.464084Z","iopub.execute_input":"2021-06-19T08:07:36.464319Z","iopub.status.idle":"2021-06-19T08:07:36.470987Z","shell.execute_reply.started":"2021-06-19T08:07:36.464296Z","shell.execute_reply":"2021-06-19T08:07:36.470043Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.environ['TRIDENT_BACKEND'] = 'pytorch'\n\nif is_in_kaggle_kernal():\n    os.environ['TRIDENT_HOME'] = './trident'\nelif is_in_colab():\n    os.environ['TRIDENT_HOME'] = '/content/gdrive/My Drive/trident'\n\n#為確保安裝最新版 \n!pip uninstall tridentx -y\n!pip install tridentx --upgrade\n\n#調用trident api\nimport trident as T\nfrom trident import *\nfrom trident.models import resnet,efficientnet","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:07:36.476251Z","iopub.execute_input":"2021-06-19T08:07:36.476486Z","iopub.status.idle":"2021-06-19T08:07:54.722654Z","shell.execute_reply.started":"2021-06-19T08:07:36.476463Z","shell.execute_reply":"2021-06-19T08:07:54.721533Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import glob\n#透過glob所全部train資料夾中所有可用圖片\nimgs=glob.glob('../input/histopathologic-cancer-detection/train/*.tif')\nprint(len(imgs))\n","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:07:54.724583Z","iopub.execute_input":"2021-06-19T08:07:54.724984Z","iopub.status.idle":"2021-06-19T08:07:58.936951Z","shell.execute_reply.started":"2021-06-19T08:07:54.724917Z","shell.execute_reply":"2021-06-19T08:07:58.936093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#檢視一下圖片image2array是將圖片或圖片路徑變成numpy向量，然後我們再把向量轉回image指的是pillow的bitmap圖片\nprint(image2array(imgs[0]).shape)\narray2image(image2array(imgs[0]))","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:07:58.938558Z","iopub.execute_input":"2021-06-19T08:07:58.938819Z","iopub.status.idle":"2021-06-19T08:07:58.996451Z","shell.execute_reply.started":"2021-06-19T08:07:58.938793Z","shell.execute_reply":"2021-06-19T08:07:58.995644Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"接下來要把有帶標籤的train_labels.csv讀取一下。編碼為何是'utf-8-sig'而不是'utf-8'呢?那其實是因為微軟與linux的utf-8定義不一致，一個有帶BOM一個沒有，所以為了避免誤觸地雷，建議用'utf-8-sig'，兩邊都相容比較不會有問題。讀入後逐列切割出圖檔編號與標籤，目前標籤值只有0與1，這是一個二元預測的題目。","metadata":{}},{"cell_type":"code","source":"#ImageDatset(imgs,symbol='image')\nf=open('../input/histopathologic-cancer-detection/train_labels.csv','r',encoding='utf-8-sig')\ndata=f.readlines()\nprint(data[:3])\nimage_path=[]\nlabels=[]\ntest_image_path=[]\ntest_labels=[]\ndata=data[1:] #拿掉第一筆標頭\nrandom.shuffle(data)#隨機洗牌\nfor row in data:\n    cols=row.strip().split(',') #移除\\n然後逗號分割\n    if random.random()<=0.3:\n        test_image_path.append('../input/histopathologic-cancer-detection/train/{0}.tif'.format(cols[0]))\n        test_labels.append(int(cols[1]))\n    else:\n        image_path.append('../input/histopathologic-cancer-detection/train/{0}.tif'.format(cols[0]))\n        labels.append(int(cols[1]))\nprint(len(image_path))\nprint(len(labels))\nprint(len(test_image_path))\nprint(len(test_labels))\n","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:07:58.997900Z","iopub.execute_input":"2021-06-19T08:07:58.998163Z","iopub.status.idle":"2021-06-19T08:07:59.836507Z","shell.execute_reply.started":"2021-06-19T08:07:58.998139Z","shell.execute_reply":"2021-06-19T08:07:59.835669Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"接下來就是組裝出要使用來建模的dataset，基本上DataProvider是提供數據的接口，裡面有兩組Iterator，分別是TrainData與TestData，而Iterator控制內部的各個資料集(Dataset)如何構成批次的數據。","metadata":{}},{"cell_type":"code","source":"#資料集\nds1=ImageDataset(image_path,symbol='image')\nds2=LabelDataset(labels,symbol='label')\n\nds1_t=ImageDataset(test_image_path,symbol='image')\nds2_t=LabelDataset(test_labels,symbol='label')\n\n#與Iterator構成data provider\ndata_provider=DataProvider(traindata=Iterator(data=ds1,label=ds2),testdata=Iterator(data=ds1_t,label=ds2_t))\n\n#設定DataProvider的預處理流程\ndata_provider.image_transform_funcs=[Normalize(127.5,127.5)]\n\n#即可完成設定，可以透過next()來確認數據是否正常拋出，以及是否有正確產生輸出數據的signature\nimg_data,label_data=data_provider.next()\nprint(data_provider.signature)\nprint(img_data.shape)\nprint(label_data.shape)","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:07:59.837707Z","iopub.execute_input":"2021-06-19T08:07:59.838068Z","iopub.status.idle":"2021-06-19T08:07:59.996212Z","shell.execute_reply.started":"2021-06-19T08:07:59.838031Z","shell.execute_reply":"2021-06-19T08:07:59.995274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"接下來示範一下兩個我個人很喜歡的小功能。","metadata":{}},{"cell_type":"code","source":"data_provider.class_names['zh-TW']={0:'[陰性',1:'陽性'}\ndata_provider.label_statistics()\n","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:07:59.997330Z","iopub.execute_input":"2021-06-19T08:07:59.997653Z","iopub.status.idle":"2021-06-19T08:08:00.031794Z","shell.execute_reply.started":"2021-06-19T08:07:59.997617Z","shell.execute_reply":"2021-06-19T08:08:00.030668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"然後我們可以透過preview_images()函數來檢閱一下圖片經過數據增強步驟之後的樣子","metadata":{}},{"cell_type":"code","source":"data_provider.batch_size=16\ndata_provider.preview_images()","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:00.033375Z","iopub.execute_input":"2021-06-19T08:08:00.033982Z","iopub.status.idle":"2021-06-19T08:08:00.236504Z","shell.execute_reply.started":"2021-06-19T08:08:00.033925Z","shell.execute_reply":"2021-06-19T08:08:00.235716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"我們可以在data_provider.image_transform_funcs=[Normalize(127.5,127.5)]直接加入各種視覺處理的transform，來達到數據增強的目的，例如我加入常見的明暗變化、飽和度變化以及色相變化，同時加入隨機擦除以及加入胡椒鹽噪音","metadata":{}},{"cell_type":"code","source":"data_provider.image_transform_funcs=[\n    RandomAdjustGamma(scale=(0.6,1.4)),#調整明暗\n    RandomAdjustHue(scale=(-0.5,0.5)),#調整色相\n    RandomAdjustSaturation(scale=(0.6,1.4)),#調整飽和度\n    SaltPepperNoise(0.1),#加入胡椒鹽噪音\n    RandomErasing(), #加入隨機擦去\n    Resize((112,112),True), #縮放尺寸\n    Normalize(127.5,127.5)] #標準化\nimg_data,label_data=data_provider.next()\nprint(img_data.shape)\nprint(label_data.shape)\ndata_provider.preview_images()","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:00.238538Z","iopub.execute_input":"2021-06-19T08:08:00.238869Z","iopub.status.idle":"2021-06-19T08:08:00.638459Z","shell.execute_reply.started":"2021-06-19T08:08:00.238831Z","shell.execute_reply":"2021-06-19T08:08:00.632053Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from trident.models import efficientnet\nnet1=efficientnet.EfficientNetB0(pretrained=True,include_top=True,freeze_features=True,input_shape=(3,112,112),classes=2)\nnet1.summary()\n#在輸出添加噪音，讓機器不會做出曖昧不明的決策\nnet1.model[-1].add_noise=True\nnet1.model[-1].noise_intensity=0.12","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:00.640094Z","iopub.execute_input":"2021-06-19T08:08:00.640470Z","iopub.status.idle":"2021-06-19T08:08:06.469592Z","shell.execute_reply.started":"2021-06-19T08:08:00.640436Z","shell.execute_reply":"2021-06-19T08:08:06.468806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"net2=efficientnet.EfficientNetB0(pretrained=True,include_top=True,freeze_features=True,input_shape=(3,112,112),classes=2)\nnet2.model[-1].add_noise=True\nnet2.model[-1].noise_intensity=0.12\nnet2.summary()\n\nnet3=efficientnet.EfficientNetB0(pretrained=True,include_top=True,freeze_features=True,input_shape=(3,112,112),classes=2)\nnet3.model[-1].add_noise=True\nnet3.model[-1].noise_intensity=0.12\n\n\n","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:06.470882Z","iopub.execute_input":"2021-06-19T08:08:06.471245Z","iopub.status.idle":"2021-06-19T08:08:08.399920Z","shell.execute_reply.started":"2021-06-19T08:08:06.471197Z","shell.execute_reply":"2021-06-19T08:08:08.399068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nnet4=efficientnet.EfficientNetB0(pretrained=True,include_top=True,freeze_features=True,input_shape=(3,112,112),classes=1)\nnet4.model.remove_at(-1) #拿掉softmax\nnet4.model.add_module('tanh',Tanh())\nnet4.model.block7a.trainable=True\nnet4.model.top_dropout.dropout_rate=0.2\nnet4.summary()","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:08.401163Z","iopub.execute_input":"2021-06-19T08:08:08.401489Z","iopub.status.idle":"2021-06-19T08:08:09.656530Z","shell.execute_reply.started":"2021-06-19T08:08:08.401455Z","shell.execute_reply":"2021-06-19T08:08:09.655711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import roc_curve, auc, roc_auc_score\ndef auc(output,target):\n    \n    if ndim(output)>1 and int_shape(output)[-1]>1 :\n        output_np=to_numpy(exp(output))[:,1]\n    elif ndim(output)>1 and int_shape(output)[-1]==1 :\n        output_np=to_numpy(exp(output))[:,0]\n    else:\n        output_np=to_numpy(output)\n    target_np=to_numpy(target)\n    return roc_auc_score(target_np, output_np)\n\n\n\ndef draw_roc(training_context):\n    if training_context['steps']==10 or (training_context['steps']+1)%100==0:\n        traindata=training_context['train_data']\n        data_feed=training_context['data_feed']\n        target_np=to_numpy(traindata[data_feed['target']])\n        output=traindata[data_feed['output']]\n        if ndim(output)>1 and int_shape(output)[-1]>1 :\n            output_np=to_numpy(exp(output))[:,1]\n        elif ndim(output)>1 and int_shape(output)[-1]==1 :\n            output_np=to_numpy(exp(output))[:,0]\n        else:\n            output_np=to_numpy(output)\n        \n        fpr, tpr,_=roc_curve(target_np, output_np)\n        plt.figure(1)\n        plt.plot([0, 1], [0, 1], 'k--')\n        plt.plot(fpr, tpr, label='area = {:.3f}'.format(roc_auc_score(target_np, output_np)))\n        plt.xlabel('False positive rate')\n        plt.ylabel('True positive rate')\n        plt.title('ROC curve')\n        plt.legend(loc='best')\n        plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:09.659773Z","iopub.execute_input":"2021-06-19T08:08:09.660092Z","iopub.status.idle":"2021-06-19T08:08:09.816566Z","shell.execute_reply.started":"2021-06-19T08:08:09.660064Z","shell.execute_reply":"2021-06-19T08:08:09.815784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#baseline\nnet1.with_optimizer(Adam,lr=1e-3)\\\n.with_loss(CrossEntropyLoss)\\\n.with_metric(accuracy,ignore_index=0)\\\n.with_metric(recall,ignore_index=0)\\\n.with_metric(auc)\\\n.with_regularizer('l2',1e-5)\\\n.with_model_save_path('./Models/eff0_1.pth')\\\n.trigger_when('on_batch_end',frequency=1,action=draw_roc)\\\n.unfreeze_model_scheduling(200,unit='batch',module_name='block7a')\n\n\n#challenger1 使用DiffGrad優化器、累積梯度\nnet2.with_optimizer(DiffGrad,lr=1e-3,gradient_centralization='all')\\\n.with_loss(CrossEntropyLoss)\\\n.with_metric(accuracy,ignore_index=0)\\\n.with_metric(recall,ignore_index=0)\\\n.with_metric(auc)\\\n.with_regularizer('l2',1e-5)\\\n.with_model_save_path('./Models/eff0_2.pth')\\\n.trigger_when('on_batch_end',frequency=1,action=draw_roc)\\\n.with_accumulate_grads(5)\\\n.unfreeze_model_scheduling(200,unit='batch',module_name='block7a')\\\n.with_automatic_mixed_precision_training()\n\n\n\n#challenger2 使用DiffGrad優化器、累積梯度、CrossEntropyLoss進階的選項以及多增加F1Score\nnet3.with_optimizer(DiffGrad,lr=1e-3,gradient_centralization='all')\\\n.with_loss(CrossEntropyLoss(auto_balance=True,label_smooth=True))\\\n.with_loss(F1ScoreLoss(auto_balance=True))\\\n.with_metric(accuracy,ignore_index=0)\\\n.with_metric(recall,ignore_index=0)\\\n.with_metric(auc)\\\n.with_regularizer('l2',1e-5)\\\n.with_model_save_path('./Models/eff0_3.pth')\\\n.trigger_when('on_batch_end',frequency=1,action=draw_roc)\\\n.unfreeze_model_scheduling(200,unit='batch',module_name='block7a')\\\n.with_accumulate_grads(5)\\\n.with_automatic_mixed_precision_training()\n\n\n#challenger3  使用DiffGrad優化器、累積梯度、把它視為迴歸問題\ndef l2_loss(output,target):\n    target_adj=(target.float()*2.0)-1   #[0,1]->[-1,1]\n    #因為最後一層全連接層沒有直接加噪音功能，所以得自己加入\n    noise=random_normal_like(output,mean=0.0, std=0.02,dtype=output.dtype).detach()\n    output=output+noise\n    return ((output-target_adj.detach())**2).mean()\n\ndef l1_loss(output,target):\n    target_adj=(target.float()*2.0)-1   #[0,1]->[-1,1]\n    #因為最後一層全連接層沒有直接加噪音功能，所以得自己加入\n    noise=random_normal_like(output,mean=0.0, std=0.02,dtype=output.dtype).detach()\n    output=output+noise\n    return (abs(output-target_adj.detach())).mean()\n\n\n#因為我們輸出長度不為2，所以需要調整正確率公式\ndef fixed_accuracy(output,target):\n    #避免被計算梯度\n    output=output.detach()\n    target=target.detach()\n    #輸出大於0則為答案1\n    output=greater_equal(output.squeeze(1),0,dtype=dtype.float32)\n    target=target.float()\n    \n    return equal(output,target,dtype=dtype.float32).mean()\n\n#因為我們輸出長度不為2，所以需要調整召回率公式\ndef fixed_recall(output,target):\n    #避免被計算梯度\n    output=output.detach()\n    target=target.detach()\n    #輸出大於0則為答案1\n    output=greater_equal(output.squeeze(1),0,dtype=dtype.float32)\n    target=target.float()\n    \n    return (equal(output,target,dtype=dtype.float32)*equal(target,1,dtype=dtype.float32)).sum()/target.sum()\n\nnet4.with_optimizer(DiffGrad,lr=1e-3,gradient_centralization='all')\\\n.with_loss(l1_loss)\\\n.with_loss(l2_loss)\\\n.with_metric(fixed_accuracy,name='accuracy')\\\n.with_metric(fixed_recall,name='recall')\\\n.with_metric(auc)\\\n.with_regularizer('l2',1e-5)\\\n.with_model_save_path('./Models/eff0_4.pth')\\\n.trigger_when('on_batch_end',frequency=1,action=draw_roc)\\\n.unfreeze_model_scheduling(500,unit='batch',module_name='block6d')\\\n.with_accumulate_grads(5)\\\n.with_automatic_mixed_precision_training()\n\n#if os.path.exist('./Models/eff0_1.pth'):\n#    net1.load_model('./Models/eff0_1.pth')\n#if os.path.exist('./Models/eff0_2.pth'):\n#    net2.load_model('./Models/eff0_2.pth')\n#if os.path.exist('./Models/eff0_3.pth'):\n#    net3.load_model('./Models/eff0_3.pth')\n#if os.path.exist('./Models/eff0_4.pth'):\n#    net4.load_model('./Models/eff0_4.pth')\n","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:09.817833Z","iopub.execute_input":"2021-06-19T08:08:09.818188Z","iopub.status.idle":"2021-06-19T08:08:09.923226Z","shell.execute_reply.started":"2021-06-19T08:08:09.818143Z","shell.execute_reply":"2021-06-19T08:08:09.922466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nplan=TrainingPlan()\\\n    .add_training_item(net1,name='net1')\\\n    .add_training_item(net2,name='net2')\\\n    .add_training_item(net3,name='net3')\\\n    .add_training_item(net4,name='net4')\\\n    .with_data_loader(data_provider)\\\n    .with_batch_size(128)\\\n    .repeat_epochs(5)\\\n    .out_sample_evaluation_scheduling(100)\\\n    .print_gradients_scheduling(100,unit='batch')\\\n    .print_progress_scheduling(10,unit='batch')\\\n    .display_loss_metric_curve_scheduling(200)\\\n    .save_model_scheduling(50,unit='batch')\n","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:09.924449Z","iopub.execute_input":"2021-06-19T08:08:09.924770Z","iopub.status.idle":"2021-06-19T08:08:09.930694Z","shell.execute_reply.started":"2021-06-19T08:08:09.924736Z","shell.execute_reply":"2021-06-19T08:08:09.929654Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plan.start_now()","metadata":{"execution":{"iopub.status.busy":"2021-06-19T08:08:09.931840Z","iopub.execute_input":"2021-06-19T08:08:09.932295Z","iopub.status.idle":"2021-06-19T08:40:02.729979Z","shell.execute_reply.started":"2021-06-19T08:08:09.932260Z","shell.execute_reply":"2021-06-19T08:40:02.729038Z"},"trusted":true},"execution_count":null,"outputs":[]}]}