{"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-11-23T12:52:17.172985Z","iopub.execute_input":"2021-11-23T12:52:17.173728Z","iopub.status.idle":"2021-11-23T12:52:17.185116Z","shell.execute_reply.started":"2021-11-23T12:52:17.173610Z","shell.execute_reply":"2021-11-23T12:52:17.184291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# iWildCam2020 revised1","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-11-23T12:52:17.186713Z","iopub.execute_input":"2021-11-23T12:52:17.187278Z","iopub.status.idle":"2021-11-23T12:52:17.196895Z","shell.execute_reply.started":"2021-11-23T12:52:17.187234Z","shell.execute_reply":"2021-11-23T12:52:17.196035Z"},"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'\n    \nelif is_in_colab():\n    os.environ['TRIDENT_HOME'] = '/content/gdrive/My Drive/trident'\n\n#為確保安裝最新版 \n!pip uninstall tridentx -y\n!pip install ../input/trident/tridentx-0.7.4-py3-none-any.whl --upgrade\nimport json\nimport copy\nimport numpy as np\n#調用trident api\nimport trident as T\nfrom trident import *\nfrom trident.models import resnet,efficientnet\nimport cv2\nimport json\nimport glob","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:17.199012Z","iopub.execute_input":"2021-11-23T12:52:17.199405Z","iopub.status.idle":"2021-11-23T12:52:29.615007Z","shell.execute_reply.started":"2021-11-23T12:52:17.199367Z","shell.execute_reply":"2021-11-23T12:52:29.614067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"由於這個競賽原始數據非常巨大，也因此我們無法直接載入，我已經將所需要的部分檔案上傳到我的google drive並開放權限。然後您只需要利用trident api中的download_file_from_google_drive函數即可方便的下載，預設下載目錄會是trident api的主資料夾下方的downloads資料夾。下載後，我們可以透過讀讀取標註json檔來解讀標註內容。","metadata":{}},{"cell_type":"markdown","source":"了解你的數據是在做數據分析前非常重要的階段，我們也稱這個階段為DAE (Data Exploration Analysis)，如果是處理表格型態的內容，最方便的莫過於pandas。身為數據科學家，千萬不要講到熊貓直覺反應是要點外賣。pandas最大的好處在於它整合了多種數據來源的讀取與寫入，即使是像這次屬於非結構數據的json檔，它也能轉換成表格型態數據(DataFrame)","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\nwith open('../input/iwildcam-2020-fgvc7/iwildcam2020_train_annotations.json') as json_file:\n    train_data = json.load(json_file)\n\n\nwith open('../input/iwildcam-2020-fgvc7/iwildcam2020_test_information.json') as test_json_file:\n    test_data = json.load(test_json_file)\n\n    \ndf_train = pd.DataFrame({'id': [item['id'] for item in train_data['annotations']],\n                         'category_id': [item['category_id'] for item in train_data['annotations']],\n                         'image_id': [item['image_id'] for item in train_data['annotations']],\n                         'location': [item['location'] for item in train_data['images']],\n                         'file_name': [item['file_name'] for item in train_data['images']]})\ndf_test = pd.DataFrame({'image_id': [item['id'] for item in train_data['images']],\n                         'location': [item['location'] for item in train_data['images']],\n                         'file_name': [item['file_name'] for item in train_data['images']]})\n\n\n\ndf_train","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:29.616924Z","iopub.execute_input":"2021-11-23T12:52:29.617247Z","iopub.status.idle":"2021-11-23T12:52:31.563297Z","shell.execute_reply.started":"2021-11-23T12:52:29.617215Z","shell.execute_reply":"2021-11-23T12:52:31.562369Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"接下來可以透過類似的方式將train_data['categories']取出建構成新的dtaframe，並列印一下，這才知道，原來出現最多次的是「人類」這種動物。而除了人類這種動物之外，是一種叫做「眼斑吐綬雞(meleagris ocellata)」的動物。而更變態的是，標註中的類別還出現了0次的動物，而且數量還不少。所以我們這時可以做個過濾，排除掉不存在於圖片標註中的類別","metadata":{}},{"cell_type":"markdown","source":"我們可以利用is_in這個函數來進行欄位的篩選，這才知道像是舊世界綠猴(chlorocebus pygerythrus)等動物其實只出現過一次，而原本類別檔中出現最多次的人類竟然不見了，可見category中的數量是完全不可信的，只能參考它的category_id以及名稱之間的對應。更糟的是，雖然訓練及與測試集的類別表看起來是一致的，但是由於它不可能給測試集的標註，我們只能根據category中數量大於零來作為判斷，然後一去重複比對之下，竟然出現測試集出現了意料之外的動物的這種劇情。我檢查半天我沒寫錯，查了一下討論區，看到了以下留言：\n\nhttps://www.kaggle.com/c/iwildcam-2020-fgvc7/discussion/143071\n\n好吧，只能暫時相信出題方了。而也因此我開始擔心圖片annotations中的註記是否跟圖片一致....","metadata":{}},{"cell_type":"code","source":"df_category_train=pd.DataFrame({'id': [item['id'] for item in train_data['categories']],\n                         'name': [item['name'] for item in train_data['categories']],\n                         'count': [item['count'] for item in train_data['categories']]})\n\ndf_category_test=pd.DataFrame({'id': [item['id'] for item in test_data['categories']],\n                         'name': [item['name'] for item in test_data['categories']],\n                         'count': [item['count'] for item in test_data['categories']]})\n\ndf_category_train=df_category_train.sort_values(['count'],ascending=False) \nprint(df_category_train)\ndf_category_test=df_category_test.sort_values(['count'],ascending=False) \nprint(df_category_test)\n\n#基於標註檔，產生有在標註檔內的所有圖片的清單，進行去重複(set)、排序(sorted)以及轉換成清單(list)\nanimal_category_lists=list(sorted(set([item['category_id'] for item in train_data['annotations']])))\n\n\n#進行篩選\ndf_category_train=df_category_train[df_category_train['id'].isin(animal_category_lists)]\nprint(df_category_train)\n\ndf_category_test=df_category_test[df_category_test['count']>0]\nprint(df_category_test)\n\nanimal_category_lists_train=[category_id.item() for category_id in df_category_train[['id']].to_numpy().astype(np.int64)]\nprint(animal_category_lists_train[:5])\n\nanimal_category_lists_test=[category_id.item() for category_id in df_category_test[['id']].to_numpy().astype(np.int64)]\nprint(animal_category_lists_test[:5])\n\n#檢查是不是所有df_category_test數量不為零的動物都有出現在df_category_train的類別代號中\ncategory_missing_list=[category_id for category_id in animal_category_lists_test if category_id not in animal_category_lists_train]\nprint(category_missing_list)\n\n\n\n","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:31.564646Z","iopub.execute_input":"2021-11-23T12:52:31.565185Z","iopub.status.idle":"2021-11-23T12:52:31.618491Z","shell.execute_reply.started":"2021-11-23T12:52:31.565146Z","shell.execute_reply":"2021-11-23T12:52:31.617671Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"除了數量外，我們也要注意到，出現最多的動物以及出現最少的動物，其圖片數量相差了1.6萬倍，我們可以透過value_counts()來計算次數分配表，然後透過pandas中的plot自動畫圖。","metadata":{}},{"cell_type":"markdown","source":"我們可以透過以下語法抓出所有圖片，以及統計一下數量。經過去重複，還好去重前後數量都是217959，這個數字是合的。沒有出現我覺得最討人厭的一張照片出現兩種動物，也因此，這個題目是可以符合圖像識別的要求(但這只是最低要求，我只是要用它做個baseline以及展示一下多類別不均衡如何解)，那我們就開始來做出baseline吧。","metadata":{}},{"cell_type":"code","source":"import glob\n#透過glob所全部train資料夾中所有可用圖片\nimgs=glob.glob('../input/iwildcam-2020-fgvc7/train/*.jpg')\nprint(len(imgs))\nprint(imgs[:5])\n\n#將圖檔路徑去除資料夾部分後進行去重複\nimg_pathes=[img.split('/')[-1] for img in imgs]\nimg_pathes=list(sorted(set(img_pathes)))\nprint(len(img_pathes))\n\nprint(img_pathes[:5])","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:31.619665Z","iopub.execute_input":"2021-11-23T12:52:31.620053Z","iopub.status.idle":"2021-11-23T12:52:32.923376Z","shell.execute_reply.started":"2021-11-23T12:52:31.620014Z","shell.execute_reply":"2021-11-23T12:52:32.922289Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"但是仔細看可以發現，我們的動物類別id並不是連續數字，中間是有跳過的，而且既然出題方都說了測試集不會出現意料之外的動物，那麼我們就按照訓練集出來的類別清單(animal_category_lists_train)來建構標籤囉。","metadata":{}},{"cell_type":"code","source":"label2category=OrderedDict()\ncategory2label=OrderedDict()\n#產生能將category_id轉label的字典\nfor i in range(len(animal_category_lists_train)):\n    category2label[animal_category_lists_train[i]]=i\n    label2category[i]=animal_category_lists_train[i]\n\n#建構出轉成標籤id\nlabel_idxes=[category2label[item['category_id']] for item in train_data['annotations']]\nimage_pathes=['../input/iwildcam-2020-fgvc7/train/'+item['file_name'] for item in train_data['images']]\n\nprint(label_idxes[:5])\nprint(image_pathes[:5])","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:32.924868Z","iopub.execute_input":"2021-11-23T12:52:32.925300Z","iopub.status.idle":"2021-11-23T12:52:33.027207Z","shell.execute_reply.started":"2021-11-23T12:52:32.925258Z","shell.execute_reply":"2021-11-23T12:52:33.026301Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nimg_ds=ImageDataset(image_pathes,symbol='image')\nlabel_ds=LabelDataset(label_idxes,symbol='label')\n\n#與Iterator構成data provider\ndata_provider=DataProvider(traindata=Iterator(data=img_ds,label=label_ds))\n\ndata_provider.image_transform_funcs=[\n    Resize((224,224)),\n    AutoLevel(),\n    RandomAdjustGamma(scale=(0.8,1.2)),#調整明暗\n    RandomAdjustHue(scale=(-0.2,0.2)),#調整色相\n    RandomAdjustSaturation(scale=(0.8,1.2)),#調整飽和度\n    SaltPepperNoise(0.005, keep_prob=0.5),#加入胡椒鹽噪音\n    GrayMixRGB(keep_prob=0.75),\n    RandomErasing(size_range=(0.05, 0.2), transparency_range=(0.4, 0.8), transparancy_ratio=1.0, keep_prob=0.5), #加入隨機擦去\n    RandomTransformAffine(rotation_range=20, zoom_range=0.00, shift_range=0.00, shear_range=0.2, random_flip=0.15 ),#隨機仿射變換\n    Normalize(127.5,127.5)] #標準化\n\n\ndata,labels=data_provider.next()\nprint(data.shape)\nprint(labels)\n\n","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:33.028527Z","iopub.execute_input":"2021-11-23T12:52:33.028900Z","iopub.status.idle":"2021-11-23T12:52:33.953943Z","shell.execute_reply.started":"2021-11-23T12:52:33.028864Z","shell.execute_reply":"2021-11-23T12:52:33.953015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"用preview_images預覽一下圖片，一看嚇到，是我運氣不好嗎?一預覽就看到一堆純黑的圖。","metadata":{}},{"cell_type":"code","source":"%%time\ndata_provider.preview_images()","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:33.956418Z","iopub.execute_input":"2021-11-23T12:52:33.957010Z","iopub.status.idle":"2021-11-23T12:52:34.616652Z","shell.execute_reply.started":"2021-11-23T12:52:33.956950Z","shell.execute_reply":"2021-11-23T12:52:34.615841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from trident.models import efficientnet\n\nnet1=efficientnet.EfficientNetB0(pretrained=True,include_top=True,classes=len(animal_category_lists_train),input_shape=(3,224,224),freeze_features=True)\nnet1.model[-1].add_noise=True\nnet1.model[-1].noise_intensity=0.12\nnet1.summary()","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:34.617844Z","iopub.execute_input":"2021-11-23T12:52:34.618245Z","iopub.status.idle":"2021-11-23T12:52:35.819830Z","shell.execute_reply.started":"2021-11-23T12:52:34.618202Z","shell.execute_reply":"2021-11-23T12:52:35.819039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"net2=efficientnet.EfficientNetB0(pretrained=True,include_top=False,classes=len(animal_category_lists_train),input_shape=(3,224,224),freeze_features=True)\nnet2.model.add_module('last_conv',Conv2d_Block((3,3),num_filters=len(animal_category_lists_train),use_bias=False,activation=None, normalization='l2'))\ncam=ShortCut(\n    Identity(),\n    Sequential(\n    GlobalAvgPool2d(),\n    Reshape((len(animal_category_lists_train),1,1)),\n    Conv2d((1,1),num_filters=len(animal_category_lists_train),use_bias=False,activation=None)\n    )\n,mode='dot'\n)\n\nnet2.model.add_module('cam',cam)\nnet2.model.add_module('aggregate1',Aggregation('sum',axis=2))\nnet2.model.add_module('aggregate2',Aggregation('sum',axis=3))\nnet2.model.add_module('reshape',Reshape((len(animal_category_lists_train))))\nnet2.model.add_module('sigmoid',Sigmoid())\nnet2.model.add_module('fc',Dense((len(animal_category_lists_train))))\nnet2.model.add_module('softmax',SoftMax(axis=-1,add_noise=True,noise_intensity=0.12))\n\n\nis_resume=False\nif is_resume and os.path.exists('Models/revised_net1.pth'):\n    net1.load_model('Models/revised_net1.pth')\n    print('Models/revised_net1.pth loaded')\nelse:\n    net1.load_model('../input/iwildcam2020-revised1/Models/revised_net1.pth')\n    print('../input/iwildcam2020-revised1/Models/revised_net1.pth loaded')\nif is_resume and os.path.exists('Models/revised_net2.pth'):\n    net2.load_model('Models/revised_net2.pth')\n    print('Models/revised_net2.pth loaded')\nelse:\n    net2.load_model('../input/iwildcam2020-revised1/Models/revised_net2.pth')\n    print('../input/iwildcam2020-revised1/Models/revised_net2.pth loaded')\nnet2.summary()","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:35.821043Z","iopub.execute_input":"2021-11-23T12:52:35.821381Z","iopub.status.idle":"2021-11-23T12:52:37.216145Z","shell.execute_reply.started":"2021-11-23T12:52:35.821343Z","shell.execute_reply":"2021-11-23T12:52:37.215194Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"這兩個baseline主要是用來確認，哪一個類別不平衡的校正策略比較好。請注意，由於建模圖片數量又多，圖檔尺寸又超大，即使只跑一個epoch都要很久，為了不浪費gpu時數資源，請注意，建議要把Resize類的轉換放在第一個，時間會差非常非常多...還有不要用原來的start_now()，改用only_steps(num_steps=步數)只需要指定步數即可。","metadata":{}},{"cell_type":"code","source":"\nfrom trident.callbacks.lr_schedulers import AdjustLRCallbackBase\n\nclass PolyLR(AdjustLRCallbackBase):\n    def __init__(self,max_lr=1e-3,  max_iter=10000):\n        super().__init__()\n        self.max_lr = max_lr\n        self.max_iter=max_iter\n    def on_batch_end(self, training_context):\n        current_step =training_context['steps']\n        lr = self.max_lr * (1 - (current_step/ self.max_iter)) * (1 - (current_step / self.max_iter))\n        if (lr < 1.0e-7):\n            lr = 1.0e-7\n        self.adjust_learning_rate(training_context, lr, verbose=False)","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:37.219082Z","iopub.execute_input":"2021-11-23T12:52:37.219347Z","iopub.status.idle":"2021-11-23T12:52:37.226779Z","shell.execute_reply.started":"2021-11-23T12:52:37.219321Z","shell.execute_reply":"2021-11-23T12:52:37.224937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"net1.with_optimizer(optimizer=AdaBelief,lr=1e-3,betas=(0.9, 0.999),gradient_centralization='all')\\\n.with_loss(CrossEntropyLoss(auto_balance=True))\\\n.with_loss(FocalLoss,loss_weight=0.5)\\\n.with_metric(accuracy,name='accuracy')\\\n.with_metric(accuracy,topk=5,name='top5_accuracy')\\\n.with_regularizer('l2')\\\n.with_model_save_path('Models/revised_net1.pth')\\\n.with_learning_rate_scheduler(PolyLR(max_lr=1e-3,max_iter=3000))\\\n.with_automatic_mixed_precision_training()\n#.with_callbacks(MixupCallback(alpha= 1,loss_criterion=CrossEntropyLoss,loss_weight=0.5)) \\\n\n\n\nnet2.with_optimizer(optimizer=AdaBelief,lr=1e-3,betas=(0.9, 0.999),gradient_centralization='all')\\\n.with_loss(CrossEntropyLoss(auto_balance=True))\\\n.with_loss(FocalLoss,loss_weight=0.5)\\\n.with_metric(accuracy,name='accuracy')\\\n.with_metric(accuracy,topk=5,name='top5_accuracy')\\\n.with_regularizer('l2')\\\n.with_model_save_path('Models/revised_net2.pth')\\\n.with_learning_rate_scheduler(PolyLR(max_lr=1e-3,max_iter=3000))\\\n.with_automatic_mixed_precision_training()\n\n\n\nplan=TrainingPlan()\\\n    .add_training_item(net1)\\\n    .add_training_item(net2)\\\n    .with_data_loader(data_provider)\\\n    .repeat_epochs(2)\\\n    .with_batch_size(64)\\\n    .print_gradients_scheduling(20,unit='batch') \\\n    .print_progress_scheduling(5,unit='batch') \\\n    .display_loss_metric_curve_scheduling(100)\\\n    .save_model_scheduling(10,unit='batch')\n\n#因為圖片數量實在太大，所以改用only_steps\n#而且圖片尺寸也很大，變成載入快取也要很久，建議把print_progress_scheduling設小一點\n#第一次列印進度會較久(須完全載入快取)\n#plan.only_steps(num_steps=300, collect_data_inteval=5)\nplan.start_now()\n","metadata":{"execution":{"iopub.status.busy":"2021-11-23T12:52:37.228130Z","iopub.execute_input":"2021-11-23T12:52:37.228564Z"},"trusted":true},"execution_count":null,"outputs":[]}]}