预训练模型——猫狗分类再战

前言

  • 使用预训练模型:Inception、Xception、ResNet
  • 环境与库:windows10+python+pycharm+tensorflow+keras+CPU
  • GPU不太行,而且不想在laptop上装,ubuntu太麻烦,window不想装。
  • 数据集来源于kaggle猫狗分类数据集,分为train(labeled)、test(unlabeled)数据集

正文

数据预处理

  • 一些数据处理工作:将标签提取为list,注意ndarray没有append方法;将数据转成dataframe类型,以便后序转为generator进行数据增强等操作;
# train and test path
train_path = 'train'
test_path = 'test'

# get the names of train set and test set
train_filenames = os.listdir(train_path)  # list[str] 25000
test_filenames = os.listdir(test_path)

# get the train categories
categories = []
for train_file in train_filenames:
    categories.append(train_file.split('.')[0])

# get the dataframe format of train set and test set
train_df = pd.DataFrame({
    'image': train_filenames,
    'category': categories
})
test_df = pd.DataFrame({
    'image': test_filenames
})
batch_size = 32

inception_path = 'gap_InceptionV3.h5'
resnet_path = 'gap_ResNet50.h5'
xception_path = 'gap_Xception.h5'

搭建模型

预训练模型

  • 获得预训练模型的输出
  • 函数,用于获得预训练模型的输出:
def model_output_features(model_ref, input_df, image_size, batch_size=32, lambda_func=None):
    width, height = image_size
    train_dataframe, test_dataframe = input_df

    base_model = model_ref(weights='imagenet', include_top=False, pooling='avg')
    my_model = tf.keras.models.Model(inputs=base_model.input,
                                     outputs=base_model.output)
    my_model.trainable = False

    # 25000, 0-1
    # preprocess_input:图像从 RGB 转换为 BGR,
    # 然后每个颜色通道都相对于 ImageNet 数据集以零为中心,没有缩放。
    train_gen = tf.keras.preprocessing.image.ImageDataGenerator(
        rotation_range=15,
        rescale=1. / 255,
        shear_range=0.1,
        zoom_range=0.2,
        horizontal_flip=True,
        width_shift_range=0.1,
        height_shift_range=0.1
    )
    train_generator = train_gen.flow_from_dataframe(
        train_dataframe,
        'train',
        x_col='image',
        y_col='category',
        target_size=image_size,  # "categorical" 将是 2D one-hot 编码标签
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False
    )

    # 12500, 0-1
    test_gen = tf.keras.preprocessing.image.ImageDataGenerator(
        rescale=1. / 255
    )
    test_generator = test_gen.flow_from_dataframe(
        test_dataframe,
        'test',
        x_col='image',
        y_col=None,
        class_mode=None,
        target_size=image_size,
        batch_size=batch_size,
        shuffle=False
    )

    train_classes = train_generator.classes     # list[], 0/1, (25000,0), cat->0, dog->1
    test_activation = my_model.predict(test_generator)
    train_activation = my_model.predict(train_generator)  # return ndarray

    with h5py.File("gap_%s.h5" % model_ref.__name__) as h5:
        h5.create_dataset("train", data=train_activation)
        h5.create_dataset("test", data=test_activation)
        h5.create_dataset("label", data=train_classes)
    return train_activation, test_activation, train_classes

预训练模型输出特征值

  • 使用np.concatenate,将各个预训练模型xceptioninceptionresnet的输出组合成新的特征向量(维度增加,数据量不变)
  • 使用sklearn.utils.shuffle可以将特征向量和标签同步shufflerandom.shuffle做不到这一点
  • 在调试过程中,难免需要反复运行程序,所以做了一个if判断,以进行快速调试
# get the train features and test features of pre models
resnet_train_features = np.array([])
resnet_test_features = np.array([])
resnet_train_categories = np.array([])
if not os.path.lexists(resnet_path):
    resnet_train_features, resnet_test_features, resnet_train_categories = model_output_features(
        tf.keras.applications.ResNet50,
        (train_df, test_df),
        (224, 224),
        batch_size=batch_size,
        lambda_func=tf.keras.applications.resnet50.preprocess_input)
else:
    with h5py.File(resnet_path, 'r') as h:
        resnet_train_features = np.array(h['train'])
        resnet_test_features = np.array(h['test'])
        resnet_train_categories = np.array(h['label'])

inception_train_features = np.array([])
inception_test_features = np.array([])
inception_train_categories = np.array([])
if not os.path.lexists(inception_path):
    inception_train_features, inception_test_features, inception_train_categories = model_output_features(
        tf.keras.applications.InceptionV3,
        (train_df, test_df),
        (299, 299),
        batch_size=batch_size,
        lambda_func=tf.keras.applications.inception_v3.preprocess_input)
else:
    with h5py.File(inception_path, 'r') as h:
        inception_train_features = np.array(h['train'])
        inception_test_features = np.array(h['test'])
        inception_train_categories = np.array(h['label'])

xception_train_features = np.array([])
xception_test_features = np.array([])
xception_train_categories = np.array([])
if not os.path.lexists(xception_path):
    xception_train_features, xception_test_features, xception_train_categories = model_output_features(
        tf.keras.applications.Xception,
        (train_df, test_df),
        (299, 299),
        batch_size=batch_size,
        lambda_func=tf.keras.applications.xception.preprocess_input)
else:
    with h5py.File(xception_path, 'r') as h:
        xception_train_features = np.array(h['train'])
        xception_test_features = np.array(h['test'])
        xception_train_categories = np.array(h['label'])

# combine the train_features and test features
# np.array
train_features = np.concatenate((resnet_train_features,
                                 inception_train_features,
                                 xception_train_features,),
                                axis=1)
# concatenate must the same size
# ndarray
test_features = np.concatenate((resnet_test_features,
                                inception_test_features,
                                xception_test_features,),
                               axis=1)
train_categories = resnet_train_categories

# shuffle the train_features and test_features
train_features, train_categories = sklearn.utils.shuffle(train_features, train_categories, random_state=0)

自定义模型

  • 自定义模型,在dropout之后,直接接给sigmoid,训练速度很快。
# DIY the model using the features of pre-trained models
input_tensor = tf.keras.Input(train_features.shape[1:])     # (25000, 6144)
x = tf.keras.layers.Dropout(0.5)(input_tensor)
x = tf.keras.layers.Dense(1, activation='sigmoid')(x)
model = tf.keras.models.Model(input_tensor, x)
# model.summary()


# compile/config the model
model.compile(optimizer='adadelta',
              loss='binary_crossentropy',
              metrics=['accuracy'])

# train the DIY-model
# for i in train_categories:
#     print(i)
model.fit(train_features, train_categories, batch_size=128, epochs=100, validation_split=0.1)

预测

  • test文件夹中的数据进行预测
  • 一个很大的坑是:在test数据转成dataframe时,数据的顺序变了,所以需要根据图像的名字来重新排序输出结果(要求:0-12500)
  • 出现排序无果时,考虑以下原因:数据类型不是数值型,无法排序;inplace表示是否覆盖原始数据,否则返回排序后数据
  • 最终得分:0.06881,大概在100-150名之间(总1314人)
# print(test_df)
test_pre = model.predict(test_features, verbose=1)
test_pre = np.reshape(test_pre, (-1))   # (12500, ) list
test_pre = test_pre.clip(0.005, 0.995)
# print(test_pre)
ids = list(range(1, 12501))     # (12500, )
for i in range(1, 12501):
    ids[i-1] = test_df.loc[i-1, 'image'].split('.')[0]
df = pd.DataFrame({
    'id': ids,
    'label': test_pre
})

df['id'] = df['id'].astype('int')
df.sort_values(by='id', axis=0, ascending=True, inplace=True)
df.to_csv('pred.csv', index=None)

总结

  • 参考的文档比较老旧了,所以本次实践过程遇到了许多坑,前前后后3、4天的样子才完全弄好
  • github教程上使用的是flow_from_directory产生数据生成器/迭代器,我为了减少不必要的IO操作,所以想使用flow_from_dataframe。在产生数据的时候一定要考虑shuffle参数,比如此处就是默认shuffle=True的,如果我没有考虑的话,那么返回的test_features数据就是乱序的
  • dataframe设置值使用自带的loc方法,排序的话有sort_index和sort_values,坑的话上面提到的:数据类型、以及inplace参数
  • 此处使用多个预训练模型不是为了增多数据量,而是提高每一条数据的‘‘鲁棒性’’

参考

  1. github
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值