{"cells":[{"metadata":{},"cell_type":"markdown","source":"# 迁移学习案例研究\n# 一、基础卷积神经网络\n## 数据集样本数量受限的迁移学习（使用kaggle Dogs vs. Cats数据集）\n"},{"metadata":{},"cell_type":"markdown","source":"### 一、了解数据集\n\n　　该数据集为猫狗图像数据集，训练集由50000张照片（其中猫25000张，狗25000张），测试集25000张照片（其中其中猫12500张，狗12500张）。"},{"metadata":{"trusted":true},"cell_type":"code","source":"import glob\n\n# 了解数据集的组成\n\ntrain_files = glob.glob('/kaggle/input/dogs-vs-cats/train/train/*')\ntest_files = glob.glob('/kaggle/input/dogs-vs-cats/test1/test1/*')\n\ntrain_cat_files = [file_name for file_name in train_files if 'cat' in file_name]\ntrain_dog_files = [file_name for file_name in train_files if 'dog' in file_name]\n\nprint('train samples of cat:', len(train_cat_files))\nprint('train samples of dog:', len(train_dog_files))\nprint( train_dog_files[0])\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 二、构建小数据集\n\n　　现在我们构造一个小型数据集，即训练图像包含3000张图片，校验图像包含1000张图片，测试图像包含1000张图片（每类中猫狗图片数量相同）。"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nfrom random import shuffle\nfrom keras.preprocessing.image import load_img, img_to_array\nfrom sklearn.preprocessing import LabelEncoder\n\n# 从猫训练数据中随机抽取1500张训练样本\ncat_train = list(np.random.choice(train_cat_files, size=1500, replace=False))\n\n# 从狗训练数据中随机抽取1500张训练样本\ndog_train = list(np.random.choice(train_dog_files, size=1500, replace=False))\n\n# 从猫训练数据中剔除已经抽取的训练样本\ntrain_cat_files = list(set(train_cat_files) - set(cat_train))\n\n# 从狗训练数据中剔除已经抽取的训练样本\ntrain_dog_files = list(set(train_dog_files) - set(dog_train))\n\n# 从猫训练数据中随机抽取500张校验样本\ncat_val = list(np.random.choice(train_cat_files, size=500, replace=False))\n\n# 从狗训练数据中随机抽取500张校验样本\ndog_val = list(np.random.choice(train_dog_files, size=500, replace=False))\n\n# 从猫训练数据中剔除已经抽取的校验样本\ntrain_cat_files = list(set(train_cat_files) - set(cat_val))\n\n# 从狗训练数据中剔除已经抽取的校验样本\ntrain_dog_files = list(set(train_dog_files) - set(dog_val))\n\n# 从猫训练数据中随机抽取500张测试样本\ncat_test = list(np.random.choice(train_cat_files, size=500, replace=False))\n\n# 从狗训练数据中随机抽取500张测试样本\ndog_test = list(np.random.choice(train_dog_files, size=500, replace=False))\n\n# 合并猫狗训练集\ntrain_files = cat_train + dog_train\n# 合并猫狗校验集\nval_files = cat_val + dog_val\n# 合并猫狗测试集\ntest_files = cat_test + dog_test\n\n# 随机化猫狗训练集\nshuffle(train_files)\n\n# 样本尺寸\nIMG_DIM = (150, 150)\n# 从磁盘加载训练集\nx_train = np.array([img_to_array(load_img(image_file, target_size=IMG_DIM)) for image_file in train_files])\n# 从磁盘加载校验集\nx_val = np.array([img_to_array(load_img(image_file, target_size=IMG_DIM)) for image_file in val_files])\n# 从磁盘加载测试集\nx_test = np.array([img_to_array(load_img(image_file, target_size=IMG_DIM)) for image_file in test_files])\n\n# 将训练集列表转换为numpy矩阵\nx_train = np.array(x_train)\n# 将校验集列表转换为numpy矩阵\nx_val = np.array(x_val)\n# 将测试集列表转换为numpy矩阵\nx_test = np.array(x_test)\n\n# 归一化\nx_train.astype('float32')\nx_train /= 255.0\nx_val.astype('float32')\nx_val /= 255.0\nx_test.astype('float32')\nx_test /= 255.0\n\n# 标签编码\ntrain_labels = [fn.split('/')[-1].split('.')[0].strip() for fn in train_files]\nval_labels = [fn.split('/')[-1].split('.')[0].strip() for fn in val_files]\ntest_labels = [fn.split('/')[-1].split('.')[0].strip() for fn in test_files]\nle = LabelEncoder()\nle.fit(train_labels)\ny_train = le.transform(train_labels)\ny_val = le.transform(val_labels)\ny_test = le.transform(test_labels)\n\n\nprint('x_train shape:', x_train.shape)\nprint('y_train shape:', y_train.shape)\nprint('x_validate shape:', x_val.shape)\nprint('y_validate shape:', y_val.shape)\nprint('x_test shape:', x_test.shape)\nprint('y_test shape:', y_test.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 三、构建基础性卷积神经网络模型\n\n　　我们先构建一个包含3层卷积操作（每层紧接一个最大化池操作）的基础性卷积神经网络模型。其中卷积和池化操作用来自动提取训练样本图像的特征并逐级降采样生成卷积特征图。第三卷积层经过池化操作后的输出为128通道17 x 17特征图，扁平化层将该特征图扁平化为1通道36992维特征向量，紧接着的全连接层将36992维特征向量降维为一通道512维特征向量，最后一个连接层输出预测向量（一通道1维特征向量），即一张图片属于dog (1)或cat (0)的概率。"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout\nfrom keras.models import Sequential\nfrom keras import optimizers\n\nbatch_size = 30\nnum_classes = 2\nepochs = 30\ninput_shape = (150, 150, 3)\n\nmodel = Sequential(name='Basic cnn model')\n\n# 第一卷积层\nmodel.add(Conv2D(16, kernel_size=(3, 3), activation='relu', input_shape=input_shape))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\n\n# 第二卷积层\nmodel.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\n\n# 第三卷积层\nmodel.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\n\n# 扁平化层\nmodel.add(Flatten())\n\n# 第一全连接层\nmodel.add(Dense(512, activation='relu'))\n\n# 第二全连接层\nmodel.add(Dense(1, activation='sigmoid'))\n\n# 编译模型，指定损失计算使用binary_crossentropy，优化器使用RMSprop，模型性能度量使用accuracy\nmodel.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(), metrics=['accuracy'])\n\nmodel.summary()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 四、训练卷积神经网络\n　　指定训练数据、校验数据，每批样本数量、训练趟数、回显级别。 "},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit(x=x_train, y=y_train,\n                    validation_data=(x_val, y_val),\n                    batch_size=batch_size,\n                    epochs=epochs,\n                    verbose=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 五、绘制模型训练的准确度和损失图\n　　通过观察训练和校验的准确度，我们注意到该模型可能存在过拟合问题，我们可以绘制模型训练的准确度和损失图来直观发现拟合问题。"},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nf, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))\nt = f.suptitle('Basic CNN Performance', fontsize=12)\nf.subplots_adjust(top=0.85, wspace=0.3)\n\nepoch_list = list(range(1,31))\nax1.plot(epoch_list, history.history['accuracy'], label='Train Accuracy')\nax1.plot(epoch_list, history.history['val_accuracy'], label='Validation Accuracy')\nax1.set_xticks(np.arange(0, 31, 5))\nax1.set_ylabel('Accuracy Value')\nax1.set_xlabel('Epoch #')\nax1.set_title('Accuracy')\nl1 = ax1.legend(loc=\"best\")\n\nax2.plot(epoch_list, history.history['loss'], label='Train Loss')\nax2.plot(epoch_list, history.history['val_loss'], label='Validation Loss')\nax2.set_xticks(np.arange(0, 31, 5))\nax2.set_ylabel('Loss Value')\nax2.set_xlabel('Epoch #')\nax2.set_title('Loss')\nl2 = ax2.legend(loc=\"best\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"　　你可以清楚地观察到2–3趟训练后模型开始在训练数据上出现拟合。该模型在校验集的分类准确率大约为72%，虽然不完美，但万事开头难！我们能够改进该模型吗？"},{"metadata":{},"cell_type":"markdown","source":"### 六、保存模型\n　　保存模型以便后面我们用测试集对该模型的性能进行评估。"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.save('cats_dogs_basic_cnn.h5')","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":1}