{"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":"import os\nimport cv2\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers,Sequential\nfrom tensorflow.keras.callbacks import ModelCheckpoint\nfrom tensorflow.keras.utils import Sequence,plot_model\n\ndef VGG16(height=224,width=224,channels=3,num_classes=2):\n    x = layers.Input(shape=(height, width, channels), dtype=\"float32\",name=\"input\")\n    output = layers.Conv2D(64, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv1_1\")(x)\n    output = layers.Conv2D(64, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv1_2\")(output)\n    output = layers.MaxPooling2D(2, 2, name=\"Pool1\")(output)\n    \n    output = layers.Conv2D(128, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv2_1\")(output)\n    output = layers.Conv2D(128, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv2_2\")(output)\n    output = layers.MaxPooling2D(2, 2, name=\"Pool2\")(output)\n    \n    output = layers.Conv2D(256, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv3_1\")(output)\n    output = layers.Conv2D(256, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv3_2\")(output)\n    output = layers.Conv2D(256, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv3_3\")(output)\n    output = layers.MaxPooling2D(2, 2, name=\"Pool3\")(output)\n    \n    output = layers.Conv2D(512, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv4_1\")(output)\n    output = layers.Conv2D(512, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv4_2\")(output)\n    output = layers.Conv2D(512, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv4_3\")(output)\n    output = layers.MaxPooling2D(2, 2, name=\"Pool4\")(output)\n    \n    output = layers.Conv2D(512, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv5_1\")(output)\n    output = layers.Conv2D(512, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv5_2\")(output)\n    output = layers.Conv2D(512, kernel_size=(3, 3), strides=(1, 1), padding=\"same\", activation=\"relu\", name=\"Conv5_3\")(output)\n    output = layers.MaxPooling2D(2, 2, name=\"Pool5\")(output)\n    output = layers.BatchNormalization(name=\"BN\")(output)\n    \n    output = layers.Flatten(name=\"Faltten\")(output)\n    output = layers.Dense(4096, activation=\"relu\", name=\"FC1\")(output)\n    output = layers.Dense(4096, activation=\"relu\", name=\"FC2\")(output)\n    output = layers.Dropout(0.5, name=\"Drop\")(output)\n    output = layers.Dense(num_classes, activation=\"softmax\", name=\"FC3\")(output)\n    \n    model = keras.Model(inputs=x,outputs=output,name=\"VGG16\")\n    return model\n\n\ndef GoogLeNet(im_height=224, im_width=224, class_num=2, aux_logits=False):\n    # 输入224*224的3通道彩色图片\n    input_image = layers.Input(shape=(im_height, im_width, 3), dtype=\"float32\")\n    x = layers.Conv2D(64, kernel_size=7, strides=2, padding=\"SAME\", activation=\"relu\", name=\"conv2d_1\")(input_image)\n    x = layers.MaxPool2D(pool_size=3, strides=2, padding=\"SAME\", name=\"maxpool_1\")(x)\n    x = layers.Conv2D(64, kernel_size=1, activation=\"relu\", name=\"conv2d_2\")(x)\n    x = layers.Conv2D(192, kernel_size=3, padding=\"SAME\", activation=\"relu\", name=\"conv2d_3\")(x)\n    x = layers.MaxPool2D(pool_size=3, strides=2, padding=\"SAME\", name=\"maxpool_2\")(x)\n    # Inception模块\n    x = Inception(64, 96, 128, 16, 32, 32, name=\"inception_3a\")(x)\n    x = Inception(128, 128, 192, 32, 96, 64, name=\"inception_3b\")(x)\n    x = layers.MaxPool2D(pool_size=3, strides=2, padding=\"SAME\", name=\"maxpool_3\")(x)\n    # Inception模块\n    x = Inception(192, 96, 208, 16, 48, 64, name=\"inception_4a\")(x)\n    # 判断是否使用辅助分类器1。训练时使用，测试时去掉。\n    if aux_logits:\n        aux1 = InceptionAux(class_num, name=\"aux_1\")(x)\n    # Inception模块\n    x = Inception(160, 112, 224, 24, 64, 64, name=\"inception_4b\")(x)\n    x = Inception(128, 128, 256, 24, 64, 64, name=\"inception_4c\")(x)\n    x = Inception(112, 144, 288, 32, 64, 64, name=\"inception_4d\")(x)\n    # 判断是否使用辅助分类器2。训练时使用，测试时去掉。\n    if aux_logits:\n        aux2 = InceptionAux(class_num, name=\"aux_2\")(x)\n    # Inception模块\n    x = Inception(256, 160, 320, 32, 128, 128, name=\"inception_4e\")(x)\n    x = layers.MaxPool2D(pool_size=3, strides=2, padding=\"SAME\", name=\"maxpool_4\")(x)\n    # Inception模块\n    x = Inception(256, 160, 320, 32, 128, 128, name=\"inception_5a\")(x)\n    x = Inception(384, 192, 384, 48, 128, 128, name=\"inception_5b\")(x)\n    # 平均池化层\n    x = layers.AvgPool2D(pool_size=7, strides=1, name=\"avgpool_1\")(x)\n    # 拉直\n    x = layers.Flatten(name=\"output_flatten\")(x)\n    x = layers.Dropout(rate=0.4, name=\"output_dropout\")(x)\n    x = layers.Dense(class_num, name=\"output_dense\")(x)\n    aux3 = layers.Softmax(name=\"aux_3\")(x)\n    # 判断是否使用辅助分类器\n    if aux_logits:\n        model = keras.Model(inputs=input_image, outputs=[aux1, aux2, aux3])\n    else:\n        model = keras.Model(inputs=input_image, outputs=aux3)\n    return model\n\n\nclass Inception(layers.Layer):\n    # ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj分别对应Inception中各个卷积核的个数,**kwargs可变长度字典变量，存放层名称 64, 96, 128, 16, 32, 32\n    def __init__(self, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj, **kwargs):\n        super(Inception, self).__init__(**kwargs)\n        # 分支1\n        self.branch1 = layers.Conv2D(ch1x1, kernel_size=1, activation=\"relu\")\n        # 分支2\n        self.branch2 = Sequential([\n            layers.Conv2D(ch3x3red, kernel_size=1, activation=\"relu\"),\n            layers.Conv2D(ch3x3, kernel_size=3, padding=\"SAME\", activation=\"relu\")])\n        # 分支3\n        self.branch3 = Sequential([\n            layers.Conv2D(ch5x5red, kernel_size=1, activation=\"relu\"),\n            layers.Conv2D(ch5x5, kernel_size=5, padding=\"SAME\", activation=\"relu\")])\n        # 分支4\n        self.branch4 = Sequential([\n            layers.MaxPool2D(pool_size=3, strides=1, padding=\"SAME\"),\n            layers.Conv2D(pool_proj, kernel_size=1, activation=\"relu\")])\n\n    def call(self, inputs, **kwargs):\n        branch1 = self.branch1(inputs)\n        branch2 = self.branch2(inputs)\n        branch3 = self.branch3(inputs)\n        branch4 = self.branch4(inputs)\n        outputs = layers.concatenate([branch1, branch2, branch3, branch4])  # 将4个分支输出按通道连接\n        return outputs\n\n\nclass InceptionAux(layers.Layer):\n    # num_classes表示输出分类节点数，**kwargs存放每层名称\n    def __init__(self, num_classes, **kwargs):\n        super(InceptionAux, self).__init__(**kwargs)\n        self.averagePool = layers.AvgPool2D(pool_size=5, strides=3)  # 平均池化\n        self.conv = layers.Conv2D(128, kernel_size=1, activation=\"relu\")\n\n        self.fc1 = layers.Dense(1024, activation=\"relu\")  # 全连接层1\n        self.fc2 = layers.Dense(num_classes)  # 全连接层2\n        self.softmax = layers.Softmax()  # softmax激活函数\n\n    def call(self, inputs, **kwargs):\n        x = self.averagePool(inputs)\n        x = self.conv(x)\n        x = layers.Flatten()(x)  # 拉直\n        x = layers.Dropout(rate=0.5)(x)\n        x = self.fc1(x)\n        x = layers.Dropout(rate=0.5)(x)\n        x = self.fc2(x)\n        x = self.softmax(x)\n        return x","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-08-07T06:31:31.271694Z","iopub.execute_input":"2021-08-07T06:31:31.272059Z","iopub.status.idle":"2021-08-07T06:31:31.322039Z","shell.execute_reply.started":"2021-08-07T06:31:31.272023Z","shell.execute_reply":"2021-08-07T06:31:31.320984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def data_augment(image_tensor, seed=2021):\n    image = tf.image.random_flip_left_right(image=image_tensor,seed=seed)\n    image = tf.image.resize_with_crop_or_pad(image=image,target_height=int(IMAGE_HEIGHT * 1.2),target_width=int(IMAGE_WIDTH * 1.2))\n    image = tf.image.random_crop(value=image, size=[IMAGE_HEIGHT, IMAGE_WIDTH, CHANNELS])\n    image = tf.image.random_brightness(image=image, max_delta=0.5)\n    return image\n\n# 创建数据生成器\nclass DataSequence(Sequence):\n    def __init__(self, allImgs, allLabels, num_classes, data_length, batch_size, shuffle, augment):\n        self.batch_size = batch_size\n        self.allImgs = allImgs\n        self.allLabels = allLabels\n        self.num_classes = num_classes\n        self.data_length = data_length\n        self.indexes = np.arange(self.data_length)\n        self.shuffle = shuffle\n        self.augment = augment\n\n    def __len__(self):\n        return self.data_length // self.batch_size\n\n    def __getitem__(self, index):\n        batch_inds = self.indexes[index * self.batch_size:(index + 1) * self.batch_size]\n        batch_files = [(self.allImgs[k], self.allLabels[k]) for k in batch_inds]\n        batch_images, batch_labels = self._read_data(batch_files)\n\n        return batch_images, batch_labels\n\n    def on_epoch_end(self):\n        \"\"\"shuffle data after one epoch. \"\"\"\n        np.random.shuffle(self.indexes)\n\n    def _read_data(self, batch_files):\n        images = []\n        labels = []\n\n        for file in batch_files:\n            image = cv2.imread(file[0])\n            if self.augment:\n                image = data_augment(image)\n            images.append(image)\n            label = tf.one_hot(int(file[1]), depth=self.num_classes)\n            labels.append(label)\n\n        return np.array(images, dtype=\"float32\"), np.array(labels)\n\n\ndef get_generator(path, num_classes, batch_size, class_id: dict, shuffle=True, augment=False):\n    data_length = 0\n    allImgs = []\n    allLabels = []\n    image_labels = os.listdir(path)\n    for label in image_labels:\n        images = os.listdir(path + label)  # get image name\n        for i in images:\n            allImgs.append(path + label + '/' + i)\n            allLabels.append(class_id[label])\n        n = len(images)\n        data_length += n\n    generator = DataSequence(allImgs, allLabels, num_classes, data_length, batch_size, shuffle, augment)\n    return generator, data_length\n\nTRAIN_PATH = \"../input/dog-vs-cat-224x224/train_224/\"\nVALID_PATH = \"../input/dog-vs-cat-224x224/valid_224/\"\nCLASS_ID = {'cat':0,'dog':1}\nIMAGE_HEIGHT, IMAGE_WIDTH, CHANNELS = 224, 224, 3\nNUM_CLASSES = 2\nBATCH_SIZE = 8\nEPOCHS = 50\n\n# model = VGG16(IMAGE_HEIGHT, IMAGE_WIDTH, CHANNELS, NUM_CLASSES)\nmodel = GoogLeNet(aux_logits=False)\nmodel.summary()\nplot_model(model,to_file='GoogLeNet.png',show_shapes=True, show_dtype=False, show_layer_names=True, rankdir='TB', expand_nested=True)\n# 编译模型\nmodel.compile(optimizer=tf.keras.optimizers.SGD(),\n              loss=tf.keras.losses.CategoricalCrossentropy(),\n              metrics=['accuracy'])\n\nG_train,NUM_TRAINING_IMAGES = get_generator(path = TRAIN_PATH, \n                                            num_classes = NUM_CLASSES,\n                                            batch_size=BATCH_SIZE,\n                                            class_id = CLASS_ID,\n                                            shuffle=True,\n                                            augment=False)\n\nG_valid,NUM_VALIDATION_IMAGES = get_generator(path = VALID_PATH, \n                                              num_classes = NUM_CLASSES,\n                                              batch_size=BATCH_SIZE,\n                                              class_id = CLASS_ID,\n                                              shuffle=True,\n                                              augment=False)\n\nSTEPS_PER_EPOCH = NUM_TRAINING_IMAGES // BATCH_SIZE","metadata":{"execution":{"iopub.status.busy":"2021-08-07T06:31:31.323868Z","iopub.execute_input":"2021-08-07T06:31:31.324287Z","iopub.status.idle":"2021-08-07T06:31:32.388416Z","shell.execute_reply.started":"2021-08-07T06:31:31.324247Z","shell.execute_reply":"2021-08-07T06:31:32.387291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def lrfn(epoch):\n    LR_START = 0.0001  # 初始学习率\n    LR_MAX = 0.0005  # 最大学习率\n    LR_MIN = 0.0001  # 最小学习率\n    LR_RAMPUP_EPOCHS = 5\n    LR_SUSTAIN_EPOCHS = 0\n    LR_EXP_DECAY = .8\n\n    if epoch < LR_RAMPUP_EPOCHS:\n        lr = (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS * epoch + LR_START\n    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n        lr = LR_MAX\n    else:\n        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n    return lr\n\n\nlr_schedule = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=1)\n\nsave_path = \"./weights/\"\ncheckpoint = ModelCheckpoint(filepath=save_path,\n                             monitor=\"val_accuracy\",\n                             verbose=0,\n                             save_best_only=True,\n                             mode=\"max\",\n                             save_weights_only=False)\n\nhistory = model.fit(\n    G_train,  # 获取训练集\n    steps_per_epoch=STEPS_PER_EPOCH,  # 设置每轮的步数\n    epochs=EPOCHS,  # 设置轮次\n    callbacks=[lr_schedule,checkpoint],  # 设置回调函数\n    validation_data=G_valid  # 设置验证集\n)\n\n# tra_loss = history.history['loss']\n# tra_acc = history.history['accuracy']\n# val_loss = history.history['val_loss']\n# val_acc = history.history['val_accuracy']\n\n# np.save(\"loss.npy\", np.array(tra_loss))\n# np.save(\"acc.npy\", np.array(tra_acc))\n# np.save(\"val_loss.npy\", np.array(val_loss))\n# np.save(\"val_acc.npy\", np.array(val_acc))","metadata":{"execution":{"iopub.status.busy":"2021-08-07T06:31:32.390759Z","iopub.execute_input":"2021-08-07T06:31:32.39107Z"},"trusted":true},"execution_count":null,"outputs":[]}]}