{"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":"markdown","source":"Contents\n==\n\n[Introduction to The Kernel](#intro)<br/>\n[1. Preprocessing Data](#pre) <br/>\n&emsp;&emsp; [1.1 Organizing & Splitting Data](#reshape) <br/>\n&emsp;&emsp; [1.2 Augmenting Data](#augment)<br/>\n[2. Building The Model](#model) <br/>\n&emsp;&emsp; [2.1 Model's Callbacks](#call)<br/>\n&emsp;&emsp; [2.2 Model Architecture](#arch)<br/>\n&emsp;&emsp; [2.3 Hyperparameters Tuning](#params)<br/>\n&emsp;&emsp; [2.4 Model Training](#train)<br/>\n[3. Model Evaluation](#eval) <br/>\n[4. Submission](#submit)","metadata":{}},{"cell_type":"markdown","source":"<a id=\"intro\"></a>\n# Introduction to The Kernel\n在本kernel中，我们将使用一个来自另一个在ImageNet上训练的体系结构的预训练编码器，并且只训练解码部分","metadata":{}},{"cell_type":"markdown","source":"## 步骤\n1. 对数据进行预处理，包括重构、归一化和图像增强\n2. 构建模型，尝试不同的架构和超参数调优\n3. 模型评估和计算模型的性能指标","metadata":{}},{"cell_type":"code","source":"# For data handling and manipulation\nimport numpy as np\nimport pandas as pd\nimport cv2\n\n# For Visualiztion\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom IPython.display import clear_output\n\n\n# for model building and trining\nfrom keras import backend as K\nfrom keras.layers import Dense, Activation, Dropout, BatchNormalization, Input, Flatten, Conv2D, MaxPooling2D, Lambda, UpSampling2D, Concatenate\nfrom keras.models import Model\nfrom keras.optimizers import Adam\nfrom keras.callbacks import Callback, EarlyStopping, ReduceLROnPlateau\nfrom keras.applications.inception_resnet_v2 import InceptionResNetV2\nfrom keras.initializers import he_normal\nfrom keras.preprocessing.image import ImageDataGenerator\n\n# For organizing data\nimport os\nimport shutil","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","id":"UE-pbpUHm46s","outputId":"35c3eb2a-f09f-4c52-8843-507d06ed8325","executionInfo":{"status":"ok","timestamp":1585739582682,"user_tz":-120,"elapsed":40292,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:31:40.243248Z","iopub.execute_input":"2022-06-05T13:31:40.243626Z","iopub.status.idle":"2022-06-05T13:31:46.745048Z","shell.execute_reply.started":"2022-06-05T13:31:40.243574Z","shell.execute_reply":"2022-06-05T13:31:46.743935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 查看示例图像文件名- bash命令\n!ls ../input/dog-breed-identification/train/ -U | head -5 ","metadata":{"id":"5JP7R1CudAkn","outputId":"822f1de5-0b03-45cd-b2f4-9c731ac58ce6","executionInfo":{"status":"ok","timestamp":1585739586054,"user_tz":-120,"elapsed":43411,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:32:12.930154Z","iopub.execute_input":"2022-06-05T13:32:12.930642Z","iopub.status.idle":"2022-06-05T13:32:13.972591Z","shell.execute_reply.started":"2022-06-05T13:32:12.930560Z","shell.execute_reply":"2022-06-05T13:32:13.971640Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = pd.read_csv('../input/dog-breed-identification/labels.csv')\nlabels.head(5)","metadata":{"id":"tbAm_aZDEMCN","outputId":"73efc5c9-c9c2-4e5d-cd8a-3cb45499e566","executionInfo":{"status":"ok","timestamp":1585739586056,"user_tz":-120,"elapsed":43179,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:34:16.614566Z","iopub.execute_input":"2022-06-05T13:34:16.614972Z","iopub.status.idle":"2022-06-05T13:34:16.642009Z","shell.execute_reply.started":"2022-06-05T13:34:16.614918Z","shell.execute_reply":"2022-06-05T13:34:16.641351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 共120类标签(狗的种类)\nclasses = np.unique(labels.breed)\nclasses_num = classes.size\nclasses_num","metadata":{"id":"SXn8uytjZ7Mz","outputId":"e0a0aaf7-5ac5-4c1c-ae79-fd203ec64e64","executionInfo":{"status":"ok","timestamp":1585739586058,"user_tz":-120,"elapsed":42981,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:35:53.774087Z","iopub.execute_input":"2022-06-05T13:35:53.774463Z","iopub.status.idle":"2022-06-05T13:35:53.791996Z","shell.execute_reply.started":"2022-06-05T13:35:53.774409Z","shell.execute_reply":"2022-06-05T13:35:53.790886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dir = '../input/dog-breed-identification/train'  # the images directory\nimages_names = os.listdir(train_dir)  # names of the files in the directory\nimages_num = len(images_names)\nprint(f'Number of images: {images_num}')  # 10222张图片用于训练","metadata":{"id":"OkeqeB8-mDft","outputId":"0945cd9a-a511-431d-a8da-2d4669acb804","executionInfo":{"status":"ok","timestamp":1585739586060,"user_tz":-120,"elapsed":42728,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:36:23.497164Z","iopub.execute_input":"2022-06-05T13:36:23.497527Z","iopub.status.idle":"2022-06-05T13:36:23.510898Z","shell.execute_reply.started":"2022-06-05T13:36:23.497474Z","shell.execute_reply":"2022-06-05T13:36:23.509716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"pre\"></a>\n# 1. 数据处理\n","metadata":{}},{"cell_type":"markdown","source":"<a id=\"reshape\"></a>\n## 1.1 拆分训练集/验证集/测试集","metadata":{}},{"cell_type":"code","source":"new_train_dir = '/root/new_train/'  # parent directoiry of the training set\nnew_test_dir = '/root/new_test/'  # parent directory of the validation set\nnew_valid_dir = '/root/new_valid/'  # parent directory of the test set\n!mkdir $new_train_dir\n!mkdir $new_test_dir\n!mkdir $new_valid_dir","metadata":{"id":"D5HR8NNgRebh","execution":{"iopub.status.busy":"2022-06-05T13:39:41.510829Z","iopub.execute_input":"2022-06-05T13:39:41.511513Z","iopub.status.idle":"2022-06-05T13:39:43.764740Z","shell.execute_reply.started":"2022-06-05T13:39:41.511452Z","shell.execute_reply":"2022-06-05T13:39:43.763910Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 为每个品种创建子目录\nfor sub_dir in classes:\n    os.mkdir(new_train_dir+sub_dir)\n    os.mkdir(new_test_dir+sub_dir)\n    os.mkdir(new_valid_dir+sub_dir)\n!ls $new_train_dir","metadata":{"id":"GVPiYjTCqF8w","outputId":"c67af022-c883-4357-a18d-15b4dff706aa","executionInfo":{"status":"ok","timestamp":1585739597485,"user_tz":-120,"elapsed":53083,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:40:34.242050Z","iopub.execute_input":"2022-06-05T13:40:34.242431Z","iopub.status.idle":"2022-06-05T13:40:35.053868Z","shell.execute_reply.started":"2022-06-05T13:40:34.242364Z","shell.execute_reply":"2022-06-05T13:40:35.052763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_jpg = labels.copy(deep=True)\nlabels_jpg['id'] += '.jpg'  \ngrouped_ids = labels_jpg.groupby('breed')['id'].apply(list).to_dict()\nprint(classes[0], grouped_ids[classes[0]])","metadata":{"id":"tAvjQzeEVbsH","outputId":"6c2dba82-0300-46c7-b234-684f88fb1876","executionInfo":{"status":"ok","timestamp":1585739597487,"user_tz":-120,"elapsed":52890,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:50:58.606535Z","iopub.execute_input":"2022-06-05T13:50:58.606923Z","iopub.status.idle":"2022-06-05T13:50:58.736393Z","shell.execute_reply.started":"2022-06-05T13:50:58.606867Z","shell.execute_reply":"2022-06-05T13:50:58.735489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 7:2:1划分训练验证测试集\ntest_split = 0.1\nvalid_split = 0.2","metadata":{"id":"fxyzK4WnqF84","execution":{"iopub.status.busy":"2022-06-05T13:51:07.878673Z","iopub.execute_input":"2022-06-05T13:51:07.879027Z","iopub.status.idle":"2022-06-05T13:51:07.883109Z","shell.execute_reply.started":"2022-06-05T13:51:07.878973Z","shell.execute_reply":"2022-06-05T13:51:07.881826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# iterators to track the final sizes of the sets\ntrain_size = 0\nvalid_size = 0\ntest_size = 0\n\n# loop on the images of each breed and using the defined probabilities assign each image to one of the 3 sets\nfor breed_idx, (breed, breed_images) in enumerate(grouped_ids.items()):\n    for img in breed_images:\n        rnd_prob = np.random.rand()  # give the current image a random number in the range [0, 1]\n        if rnd_prob <= test_split: \n            # copy to the corresponding breed subdirectory in the test directory\n            shutil.copy(train_dir+'/'+img, new_test_dir+'/'+breed) \n            test_size += 1\n            \n        elif rnd_prob <= (test_split + valid_split):\n            # copy to the corresponding breed subdirectory in the validation directory\n            shutil.copy(train_dir+'/'+img, new_valid_dir+'/'+breed)\n            valid_size += 1\n            \n        else:\n            # copy to the corresponding breed subdirectory in the training directory\n            shutil.copy(train_dir+'/'+img, new_train_dir+'/'+breed)\n            train_size += 1\n            \n    clear_output(wait=True)\n    print(f'Organized {breed_idx+1} out of {classes_num} breeds: {breed}')","metadata":{"id":"knir-76OeD8S","outputId":"9ea063b3-8619-4258-e127-900e62394fc5","executionInfo":{"status":"ok","timestamp":1585739598862,"user_tz":-120,"elapsed":13,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:51:17.255004Z","iopub.execute_input":"2022-06-05T13:51:17.255352Z","iopub.status.idle":"2022-06-05T13:52:49.396188Z","shell.execute_reply.started":"2022-06-05T13:51:17.255300Z","shell.execute_reply":"2022-06-05T13:52:49.395359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(train_size, valid_size, test_size)","metadata":{"id":"rhiAymaECWAi","outputId":"85ddb587-58dc-4ede-e8c7-b0d19a4a6e66","executionInfo":{"status":"ok","timestamp":1585739598863,"user_tz":-120,"elapsed":53567,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:53:33.263809Z","iopub.execute_input":"2022-06-05T13:53:33.264164Z","iopub.status.idle":"2022-06-05T13:53:33.268874Z","shell.execute_reply.started":"2022-06-05T13:53:33.264113Z","shell.execute_reply":"2022-06-05T13:53:33.267910Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_breed = classes[0]\n!ls $new_train_dir/$test_breed | head -5","metadata":{"id":"YtD3lE_QIONZ","outputId":"eb13340d-6935-4e2b-a634-fb9aa8d568a9","executionInfo":{"status":"ok","timestamp":1585739602884,"user_tz":-120,"elapsed":57403,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T13:54:26.926138Z","iopub.execute_input":"2022-06-05T13:54:26.926533Z","iopub.status.idle":"2022-06-05T13:54:27.580232Z","shell.execute_reply.started":"2022-06-05T13:54:26.926478Z","shell.execute_reply":"2022-06-05T13:54:27.579321Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"features\"></a>\n## 1.2 描述统计","metadata":{}},{"cell_type":"code","source":"# plt.imshow的图像尺寸\nwidth, height, channels = 512, 512, 3\n\nimages_samples = np.zeros((4, height, width, 3), dtype=float)\nsamples_labels = []\n\n# get random 4 images\nrnd_indexes = np.random.randint(0, images_num, 4)\nfor i, rnd_idx in enumerate(rnd_indexes):\n    img_filename = images_names[rnd_idx]\n    img_id = img_filename[:-4]\n    img_bgr = cv2.imread(train_dir + '/' + img_filename)  # loads the images channels in (blue, green, red) order\n    images_samples[i] = cv2.resize(src=img_bgr[:, :, [2, 1, 0]], dsize=(width, height)) / 255  # store the random image\n    img_label = labels.breed[labels.id == img_id].values[0]\n    samples_labels.append(img_label)  # store the random images' label\n    \n# view the 4 samples\nfig, axs = plt.subplots(1, 4, figsize=(20, 5))\nfor ax, img, label in zip(axs.ravel(), images_samples, samples_labels):\n    ax.imshow(img)\n    ax.axis('off')\n    ax.set_title(f'Class: {label}', size=15);","metadata":{"execution":{"iopub.status.busy":"2022-06-05T14:48:52.908270Z","iopub.execute_input":"2022-06-05T14:48:52.908669Z","iopub.status.idle":"2022-06-05T14:48:53.540351Z","shell.execute_reply.started":"2022-06-05T14:48:52.908617Z","shell.execute_reply":"2022-06-05T14:48:53.539533Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def img_process(img):\n    img_float32 = np.float32(img)\n \n    dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)\n    dft_shift = np.fft.fftshift(dft)\n    # 得到灰度图能表示的形式\n    magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))\n\n    rows, cols = img.shape\n    crow, ccol = int(rows/2) , int(cols/2)     # 中心位置\n\n    # 低通滤波\n    mask = np.zeros((rows, cols, 2), np.uint8)\n    mask[crow-20:crow+20, ccol-20:ccol+20] = 1\n \n    # IDFT\n    fshift = dft_shift*mask\n    f_ishift = np.fft.ifftshift(fshift)\n    img_back_low = cv2.idft(f_ishift)\n    img_back_low = cv2.magnitude(img_back_low[:,:,0],img_back_low[:,:,1])          \n\n    # 高通滤波\n    mask = np.ones((rows, cols, 2), np.uint8)\n    mask[crow-20:crow+20, ccol-20:ccol+20] = 0\n \n    # IDFT\n    fshift = dft_shift*mask\n    f_ishift = np.fft.ifftshift(fshift)\n    img_back_high = cv2.idft(f_ishift)\n    img_back_high = cv2.magnitude(img_back_high[:,:,0],img_back_high[:,:,1])\n    return img_back_low,img_back_high\n\n#  4个增强后样本\nfig, axs = plt.subplots(3, 4, figsize=(20,10))  \nfig.suptitle('Fliter Results', size=32)\nfor axs_col, img in enumerate(images_samples):\n    img = img[:, :, 0]\n    img_back_low,img_back_high = img_process(img)\n    \n    axs[0, axs_col].imshow(img, cmap='gray')\n    axs[0, axs_col].axis('off')\n    axs[0, axs_col].set_title('Original Image', size=15)\n    \n    axs[1, axs_col].imshow(img_back_low, cmap = 'gray')\n    axs[1, axs_col].axis('off')\n    axs[1, axs_col].set_title('LPF Image', size=15)\n    \n    axs[2, axs_col].imshow(img_back_high, cmap = 'gray')\n    axs[2, axs_col].axis('off')\n    axs[2, axs_col].set_title('HPF Image', size=15)","metadata":{"execution":{"iopub.status.busy":"2022-06-05T15:03:47.687669Z","iopub.execute_input":"2022-06-05T15:03:47.688038Z","iopub.status.idle":"2022-06-05T15:03:49.037432Z","shell.execute_reply.started":"2022-06-05T15:03:47.687987Z","shell.execute_reply":"2022-06-05T15:03:49.036421Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"augment\"></a>\n## 1.3 数据增强","metadata":{}},{"cell_type":"markdown","source":"注意:数据集中存在少量标签错误的数据","metadata":{"id":"n3ga0IE2aqxl"}},{"cell_type":"code","source":"norm_factor = 1 / 255\n\n# 使用Keras的ImageDataGenerator类完成数据增强\ntransform_params = {\n    'featurewise_center': False,\n    'featurewise_std_normalization': False,\n    'samplewise_center': False,\n    'samplewise_std_normalization': False,\n    'rotation_range': 30,  # 图像随机在(-30,30)间翻转\n    'width_shift_range': 0.15,  # 图像随机在左右（-0.05,0.05）（百分比）内水平移动\n    'height_shift_range': 0.15,  # 图像随机在上下（-0.05,0.05）（百分比）内水平移动\n    'horizontal_flip': True,  # 图像随机水平翻转\n    'rescale': norm_factor  # 图像按norm_factor缩放尺寸\n}\n\n# the generator used for training - gives augmented images\nimg_gen = ImageDataGenerator(**transform_params) ","metadata":{"id":"EUmdySDHm47h","execution":{"iopub.status.busy":"2022-06-05T14:21:18.861316Z","iopub.execute_input":"2022-06-05T14:21:18.861754Z","iopub.status.idle":"2022-06-05T14:21:18.868282Z","shell.execute_reply.started":"2022-06-05T14:21:18.861697Z","shell.execute_reply":"2022-06-05T14:21:18.867214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 用于验证的生成器——保持图像不变，以便验证错误成为测试错误的良好指示\nimg_feed = ImageDataGenerator(rescale=1/255)","metadata":{"id":"fSzXtTmHTtc1","execution":{"iopub.status.busy":"2022-06-05T14:22:30.147488Z","iopub.execute_input":"2022-06-05T14:22:30.147835Z","iopub.status.idle":"2022-06-05T14:22:30.152645Z","shell.execute_reply.started":"2022-06-05T14:22:30.147787Z","shell.execute_reply":"2022-06-05T14:22:30.151669Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#  4个增强后样本\nfig, axs = plt.subplots(2, 4, figsize=(20,10))  \nfig.suptitle('Augmentation Results', size=32)\n\nfor axs_col, img in enumerate(images_samples):\n    viz_transoform_params = {  # defined each iteration to get new augmentation values each time\n        'theta': np.random.randint(-transform_params['rotation_range'], transform_params['rotation_range']),\n        'tx': np.random.uniform(0, transform_params['width_shift_range']),\n        'ty': np.random.uniform(0, transform_params['height_shift_range']),\n        'flip_horizontal': np.random.choice([True, False], p=[0.5, 0.5])\n    }\n\n    aug_img = img_gen.apply_transform(img, viz_transoform_params)  # the same image after augmentation\n    \n    axs[0, axs_col].imshow(img);\n    axs[0, axs_col].axis('off')\n    axs[0, axs_col].set_title('Original Image', size=15)\n    \n    axs[1, axs_col].imshow(aug_img);\n    axs[1, axs_col].axis('off')\n    axs[1, axs_col].set_title('Augmented Image', size=15)","metadata":{"id":"_lITVFYVm47o","outputId":"3900ca9e-bb1c-4dd6-9a21-bea4ca612605","executionInfo":{"status":"ok","timestamp":1585739624098,"user_tz":-120,"elapsed":77043,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T14:22:57.702744Z","iopub.execute_input":"2022-06-05T14:22:57.703089Z","iopub.status.idle":"2022-06-05T14:22:59.071630Z","shell.execute_reply.started":"2022-06-05T14:22:57.703040Z","shell.execute_reply":"2022-06-05T14:22:59.070787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"model\"></a>\n# 2. 构建模型","metadata":{}},{"cell_type":"markdown","source":"<a id=\"call\"></a>\n## 2.1 设置模型回调","metadata":{}},{"cell_type":"code","source":"# 用于模型训练时绘制训练曲线(准确性，损耗)\nclass Plotter(Callback):\n    def plot(self):  # Updates the graph\n        clear_output(wait=True)\n        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8))\n        \n        # plot the losses\n        ax1.plot(self.epochs, self.losses, label='train_loss')\n        ax1.plot(self.epochs, self.val_losses, label='val_loss')\n        \n        # plot the accuracies\n        ax2.plot(self.epochs, self.acc, label='train_acc')\n        ax2.plot(self.epochs, self.val_acc, label='val_acc')\n    \n        ax1.set_title(f'Loss vs Epochs')\n        ax1.set_xlabel(\"Epochs\")\n        ax1.set_ylabel(\"Loss\")\n        \n        ax2.set_title(f'Accuracy vs Epochs')\n        ax2.set_xlabel(\"Epoches\")\n        ax2.set_ylabel(\"Accuracy\")\n        \n        ax1.legend()\n        ax2.legend()\n        plt.show()\n        \n        # print out the accuracies at each epoch\n        print(f'Epoch #{self.epochs[-1]+1} >> train_acc={self.acc[-1]*100:.3f}%, train_loss={self.losses[-1]:.5f}')\n        print(f'Epoch #{self.epochs[-1]+1} >> val_acc={self.val_acc[-1]*100:.3f}%, val_loss={self.val_losses[-1]:.5f}')\n        \n    def on_train_begin(self, logs={}):\n        # initialize lists to store values from training\n        self.losses = []\n        self.val_losses = []\n        self.epochs = []\n        self.batch_no = []\n        self.acc = []\n        self.val_acc = []\n    \n    def on_epoch_end(self, epoch, logs={}):\n        # append values from the last epoch\n        self.losses.append(logs.get('loss'))\n        self.val_losses.append(logs.get('val_loss'))\n        self.acc.append(logs.get('acc'))\n        self.val_acc.append(logs.get('val_acc'))\n        self.epochs.append(epoch)\n        self.plot()  # update the graph\n        \n    def on_train_end(self, logs={}):\n        self.plot()\n        \n    def load_plot_data(self, data):\n        self.losses, self.val_losses, self.epochs, self.batch_no, self.acc, self.val_acc = data\n    \n    def get_plot_data(self):\n        return [self.losses, self.val_losses, self.epochs, self.batch_no, self.acc, self.val_acc]\n               \nplotter = Plotter()","metadata":{"id":"35sWNtvlm48P","execution":{"iopub.status.busy":"2022-06-05T15:07:21.488840Z","iopub.execute_input":"2022-06-05T15:07:21.489204Z","iopub.status.idle":"2022-06-05T15:07:21.505808Z","shell.execute_reply.started":"2022-06-05T15:07:21.489154Z","shell.execute_reply":"2022-06-05T15:07:21.504678Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 如果val_acc没有提升，用来降低学习率\nplateau_reduce = ReduceLROnPlateau(monitor='val_loss', factor=0.01,\n                              patience=1, min_lr=1e-20)","metadata":{"id":"aoPAkm6ym48L","execution":{"iopub.status.busy":"2022-06-05T15:07:42.760132Z","iopub.execute_input":"2022-06-05T15:07:42.760513Z","iopub.status.idle":"2022-06-05T15:07:42.764740Z","shell.execute_reply.started":"2022-06-05T15:07:42.760459Z","shell.execute_reply":"2022-06-05T15:07:42.763875Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 回滚到训练中获得的最佳权重\ne_stop = EarlyStopping(monitor='val_loss', patience=15, mode='min', restore_best_weights=True)","metadata":{"id":"GrS4aYSDqF9s","execution":{"iopub.status.busy":"2022-06-05T15:08:44.288175Z","iopub.execute_input":"2022-06-05T15:08:44.288543Z","iopub.status.idle":"2022-06-05T15:08:44.292536Z","shell.execute_reply.started":"2022-06-05T15:08:44.288491Z","shell.execute_reply":"2022-06-05T15:08:44.291650Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"callbacks = [plotter, plateau_reduce, e_stop]","metadata":{"id":"YidIMiyjm48U","execution":{"iopub.status.busy":"2022-06-05T15:08:49.634224Z","iopub.execute_input":"2022-06-05T15:08:49.634620Z","iopub.status.idle":"2022-06-05T15:08:49.638753Z","shell.execute_reply.started":"2022-06-05T15:08:49.634567Z","shell.execute_reply":"2022-06-05T15:08:49.637668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"arch\"></a>\n## 2.2 模型架构","metadata":{}},{"cell_type":"markdown","source":"因为训练大型模型的计算成本非常高，所以我们将使用迁移学习，并将预训练模型的编码器层加载到类似的数据集上","metadata":{}},{"cell_type":"code","source":"# 具有activation, batchnorm和dropout的全连接层\ndef dense_block(x, neurons, layer_no):\n    x = Dense(neurons, kernel_initializer=he_normal(layer_no), name=f'topDense{layer_no}')(x)\n    x = Activation('relu', name=f'Relu{layer_no}')(x)\n    x = BatchNormalization(name=f'BatchNorm{layer_no}')(x)\n    x = Dropout(0.5, name=f'Dropout{layer_no}')(x)\n    return x","metadata":{"id":"EYegF7sim47z","execution":{"iopub.status.busy":"2022-06-05T15:10:53.004312Z","iopub.execute_input":"2022-06-05T15:10:53.004710Z","iopub.status.idle":"2022-06-05T15:10:53.010559Z","shell.execute_reply.started":"2022-06-05T15:10:53.004658Z","shell.execute_reply":"2022-06-05T15:10:53.009329Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_model(shape):\n    input_layer = Input(shape, name='input_layer')  \n    \n    # 加载初始化权重的InceptionResNetV2，并移除最后的dense layers - frozen layers\n    incep_res = InceptionResNetV2(include_top=False, weights='imagenet', input_tensor=input_layer)\n    for layer in incep_res.layers:\n        layer.trainable = False\n    \n    # 此处需了解InceptionResNetV2原理和输出层维数\n    # 池化层降低每个feature map的维数\n    pool = MaxPooling2D(pool_size=[3, 3], strides=[3, 3], padding='same')(incep_res.output)\n    # 扁平化\n    flat1 = Flatten(name='Flatten1')(pool)\n    # BN层归一化\n    flat1_bn = BatchNormalization(name='BatchNormFlat')(flat1)\n \n    # 后接三个全连接模块(全连接层+relu激活层【学习到非线性信息】+BN层【归一化，加速收敛，防过拟合】+DropOut层【0.5概率随机丢弃信息，防过拟合】)\n    dens1 = dense_block(flat1_bn, neurons=512, layer_no=1)\n    dens2 = dense_block(dens1, neurons=512, layer_no=2)\n    dens3 = dense_block(dens2, neurons=1024, layer_no=3)\n    # 最后1024*120的全连接层，用softmax激活分类\n    dens_final = Dense(classes_num, name='Dense4')(dens3)\n    output_layer = Activation('softmax', name='Softmax')(dens_final)\n    \n    model = Model(inputs=[input_layer], outputs=[output_layer])\n\n    return model","metadata":{"id":"l1QYGqp0m473","execution":{"iopub.status.busy":"2022-06-05T15:24:28.999670Z","iopub.execute_input":"2022-06-05T15:24:29.000035Z","iopub.status.idle":"2022-06-05T15:24:29.009738Z","shell.execute_reply.started":"2022-06-05T15:24:28.999985Z","shell.execute_reply":"2022-06-05T15:24:29.008264Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"params\"></a>\n## 2.3 调参","metadata":{}},{"cell_type":"code","source":"# hyperparameters\nheight, width, channels_num = 512, 512, 3  # 初始输入512*512*3\nlearning_rate = 0.004\nepochs = 15\nbatch_size = 32  # 过大会超载GPU - 可以通过减小图片大小来增大batch_size","metadata":{"id":"ue17z2dYR-6q","execution":{"iopub.status.busy":"2022-06-05T15:26:41.938937Z","iopub.execute_input":"2022-06-05T15:26:41.939306Z","iopub.status.idle":"2022-06-05T15:26:41.944400Z","shell.execute_reply.started":"2022-06-05T15:26:41.939252Z","shell.execute_reply":"2022-06-05T15:26:41.943096Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = create_model((height, width, channels_num))\noptimizer = Adam(learning_rate)  # 选用Adam优化器调度\n# 多分类交叉熵为计算的loss，acc作为评估指标, 0.004的学习率训练15个epoch\nmodel.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['acc'])\nmodel.summary()","metadata":{"id":"ne2rrapUm48C","outputId":"bfedcc3e-23e3-45a2-fb6d-aaf9cdec7d7e","executionInfo":{"status":"ok","timestamp":1585739633567,"user_tz":-120,"elapsed":81430,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T15:28:58.268519Z","iopub.execute_input":"2022-06-05T15:28:58.268883Z","iopub.status.idle":"2022-06-05T15:30:11.780238Z","shell.execute_reply.started":"2022-06-05T15:28:58.268831Z","shell.execute_reply":"2022-06-05T15:30:11.744112Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"train\"></a>\n## 2.4 模型训练\nColab中训练(GPU配额更高)","metadata":{}},{"cell_type":"code","source":"# 从目录加载后向模型提供增强训练数据\ntrain_gen = img_gen.flow_from_directory(directory=new_train_dir, target_size=(height, width), color_mode='rgb', classes=list(classes), \n                                        class_mode='categorical', batch_size=batch_size, shuffle=True, interpolation='nearest')\n\n# 从目录加载后向提供模型增强验证数据\nvalid_gen = img_feed.flow_from_directory(directory=new_valid_dir, target_size=(height, width), color_mode='rgb', classes=list(classes), \n                                        class_mode='categorical', batch_size=batch_size, shuffle=True, interpolation='nearest')\n\n\n# # 加载在colab上训练好的模型\n# # 用定义好的生成器拟合模型\n# model.fit_generator(train_gen, validation_data=valid_gen, epochs=epochs, \n#                         steps_per_epoch=train_size//batch_size + 1, \n#                         validation_steps=valid_size//batch_size + 1, callbacks=callbacks)","metadata":{"id":"cQjS2O0lm48f","outputId":"4adcc0c1-f450-4863-ace8-668ac708e281","executionInfo":{"status":"ok","timestamp":1585745317045,"user_tz":-120,"elapsed":43,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T15:34:36.289158Z","iopub.execute_input":"2022-06-05T15:34:36.289553Z","iopub.status.idle":"2022-06-05T15:34:36.616592Z","shell.execute_reply.started":"2022-06-05T15:34:36.289496Z","shell.execute_reply":"2022-06-05T15:34:36.615710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"模型的训练曲线","metadata":{}},{"cell_type":"code","source":"!wget \"https://drive.google.com/uc?id=1-3Y-DB5uhOaY69pvVl5rmfirw9aKCYYB&export=download\" -O '/root/training_curves.npy'","metadata":{"execution":{"iopub.status.busy":"2022-06-05T15:35:08.897191Z","iopub.execute_input":"2022-06-05T15:35:08.897583Z","iopub.status.idle":"2022-06-05T15:35:12.867740Z","shell.execute_reply.started":"2022-06-05T15:35:08.897523Z","shell.execute_reply":"2022-06-05T15:35:12.866710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_data = np.load('/root/training_curves.npy', allow_pickle=True)\nplotter.load_plot_data(plot_data)  # load the data into the plotter\nplotter.plot()","metadata":{"execution":{"iopub.status.busy":"2022-06-05T15:35:17.936227Z","iopub.execute_input":"2022-06-05T15:35:17.936649Z","iopub.status.idle":"2022-06-05T15:35:18.426794Z","shell.execute_reply.started":"2022-06-05T15:35:17.936576Z","shell.execute_reply":"2022-06-05T15:35:18.425905Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"保存特定层的权重（确保图层名称与加载的相同，或者可以按顺序加载）","metadata":{}},{"cell_type":"code","source":"# decoder_weights = {}\n# for layer in model.layers[-15:]:\n#     decoder_weights[layer.name] = layer.get_weights()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"加载经过训练的解码器层","metadata":{}},{"cell_type":"code","source":"!wget \"https://drive.google.com/uc?id=1Omp4wFOWc2WslToduWAgBkyRv9poQVKb&export=download\" -O '/root/decoder_weights.npy'","metadata":{"execution":{"iopub.status.busy":"2022-06-05T15:37:02.013427Z","iopub.execute_input":"2022-06-05T15:37:02.013799Z","iopub.status.idle":"2022-06-05T15:37:18.556281Z","shell.execute_reply.started":"2022-06-05T15:37:02.013747Z","shell.execute_reply":"2022-06-05T15:37:18.555411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"decoder_weights = np.load('/root/decoder_weights.npy', allow_pickle=True).item()\nfor layer_name, layer_weights in decoder_weights.items():\n    model.get_layer(layer_name).set_weights(layer_weights)  # set each layer of the decoder with its weights","metadata":{"execution":{"iopub.status.busy":"2022-06-05T15:37:50.860539Z","iopub.execute_input":"2022-06-05T15:37:50.861115Z","iopub.status.idle":"2022-06-05T15:37:51.049245Z","shell.execute_reply.started":"2022-06-05T15:37:50.861043Z","shell.execute_reply":"2022-06-05T15:37:51.048308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# check the dictionary\ndecoder_weights.keys()","metadata":{"execution":{"iopub.status.busy":"2022-06-05T15:37:55.261036Z","iopub.execute_input":"2022-06-05T15:37:55.261416Z","iopub.status.idle":"2022-06-05T15:37:55.266759Z","shell.execute_reply.started":"2022-06-05T15:37:55.261347Z","shell.execute_reply":"2022-06-05T15:37:55.265599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"eval\"></a>\n# 3. 模型评估","metadata":{}},{"cell_type":"code","source":"# 定义测试图像生成器用于模型评估\ntest_gen = ImageDataGenerator(rescale=1/255)\ntest_flow = test_gen.flow_from_directory(new_test_dir,\n        target_size=(512, 512),\n        batch_size=1,\n        shuffle=False)","metadata":{"id":"xhG9T-uTqF96","outputId":"cb3c51de-583d-473d-f288-bd3a51dcf7d6","executionInfo":{"status":"ok","timestamp":1585745372189,"user_tz":-120,"elapsed":1696,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T15:42:24.217144Z","iopub.execute_input":"2022-06-05T15:42:24.217528Z","iopub.status.idle":"2022-06-05T15:42:24.329091Z","shell.execute_reply.started":"2022-06-05T15:42:24.217473Z","shell.execute_reply":"2022-06-05T15:42:24.328171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 测试集上评估模型\nmetrics = model.evaluate_generator(test_flow, steps=test_size)","metadata":{"id":"JLKPO52KqF9-","outputId":"45ab78ef-1a92-4df2-b79b-15f8c2c0761b","executionInfo":{"status":"ok","timestamp":1585745438033,"user_tz":-120,"elapsed":66028,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T15:42:41.386101Z","iopub.execute_input":"2022-06-05T15:42:41.386479Z","iopub.status.idle":"2022-06-05T15:44:11.570297Z","shell.execute_reply.started":"2022-06-05T15:42:41.386422Z","shell.execute_reply":"2022-06-05T15:44:11.568707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"m_names = model.metrics_names\nprint(f'{m_names[0]} = {metrics[0]}\\n{m_names[1]} = {metrics[1]}')","metadata":{"id":"8jkiJrXCqF-B","outputId":"b51bc26c-182c-48aa-a79d-62514cfe46a8","executionInfo":{"status":"ok","timestamp":1585745438035,"user_tz":-120,"elapsed":65407,"user":{"displayName":"Mohammed Asfour","photoUrl":"","userId":"14357519656998048291"}},"execution":{"iopub.status.busy":"2022-06-05T15:44:55.629629Z","iopub.execute_input":"2022-06-05T15:44:55.629999Z","iopub.status.idle":"2022-06-05T15:44:55.638519Z","shell.execute_reply.started":"2022-06-05T15:44:55.629947Z","shell.execute_reply":"2022-06-05T15:44:55.636497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 分类问题上有90%的top-1准确率(top1 accuracy: 预测的label取最终概率向量里面最大的作为预测结果，如过你的预测结果中概率最大的那个分类正确，则预测正确；否则预测错误)，而在随机概率上有0.83%的准确率","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}