Kaggle系列—Leaf Classification(keras LB = 0.00191)

Leaf Classification 是比较老的一个题目了,目前在Kaggle上已经有了很多的优秀kernel,作为一名课余时间自学深度学习的学生,拿这道题目来熟悉CNN和Keras,同时写一下自己在做这道题的过程中遇到的一些问题和自己感悟(PS:private leaderboard score 0.00191)。如若有错误或不足的地方,欢迎各位大佬指正。

一、解题思路介绍

    题目中提供了两种信息,1、原始图片信息,2、预处理特征(the provide pre-extracted features:margin、sharp、texture)。第一种方案,我们可以直接利用已有特征信息,构建常规分类器例如Adaboosting,Svm,Logistic,决策树等等,或者搭建神经网络来处理pre-extracted features。第二种方案,利用图片信息搭建卷积神经网络,当然也可以利用现有的网络模型。第三种方案就是同时利用图片信息与预处理特征,当时看到这个想法的时候(大佬AbhijeetMulgund的思路),就很感兴趣,所以就按照这个思路做了(也算是一个及其简易的modle ensemble吧)。

二、题目代码

   1、读取train.csv,test.csv中的信息,读取image信息,class编码,这些都可以参考AbhijeetMulgund的代码(请不要吝啬你对大佬的vote)。

    2、因为要搭建CNN网络,所以利用已有的image肯定是不够的,需要Data Augmentation,又因为我们需要将两种特征混合到一个网络之中去,因此不能简单的调用ImageDataGenerator,这里我提供两种方案a、重载class ImageDataGenerator,b、自定义generator。这里我说明一下,其实自定义generator更简单一点,但是因为是初学者,所以我就都写了一遍。

    a、重载ImageDataGenerator(利用源代码去改写,我第一次就是打算自己单撸,各种bug大哭

[python]  view plain  copy
 
  1. from keras.preprocessing.image import ImageDataGenerator,NumpyArrayIterator,Iterator  
[python]  view plain  copy
 
  1. class ImageDataGenerator_leaf(ImageDataGenerator):  
  2.     def flow(self, x,pre_feature,y,batch_size=32,   
  3.              shuffle=False, seed=None):  
  4.         return NumpyArrayIterator_leaf(  
  5.             x,pre_feature,y,self,  
  6.             batch_size=batch_size,  
  7.             shuffle=shuffle,  
  8.             seed=seed)  
  9.   
  10. class NumpyArrayIterator_leaf(Iterator):  
  11.     def __init__(self, x,pre_feature, y, image_data_generator,  
  12.                  batch_size=32, shuffle=False, seed=None,  
  13.                  data_format=None,  
  14.                  subset=None)  
  15.         if data_format is None:  
  16.             data_format = K.image_data_format()  
  17.         self.pre_feature = np.asarray(pre_feature)                    #预处理特征  
  18.         if self.x.ndim != 4:  
  19.             raise ValueError('Input data in `NumpyArrayIterator` '  
  20.                              'should have rank 4. You passed an array '  
  21.                              'with shape', self.x.shape)  
  22.         self.x = np.asarray(x)                                        #传入的image信息  
  23.         if y is not None:  
  24.             self.y = np.asarray(y)  
  25.         else:  
  26.             self.y = None  
  27.         self.image_data_generator = image_data_generator  
  28.         self.data_format = data_format  
  29.         self.n = len(self.x)  
  30.         self.batch_size = batch_size  
  31.         self.shuffle  =shuffle  
  32.         self.seed = seed  
  33.         self.index_generator = self._flow_index()  
  34.         super(NumpyArrayIterator_leaf, self).__init__(self.n, batch_size, shuffle, seed)  
  35.   
  36.     def _get_batches_of_transformed_samples(self, index_array):  
  37.         batch_x = np.zeros(tuple([len(index_array)] + list(self.x.shape)[1:]),  
  38.                            dtype=K.floatx())  
  39.         batch_pre_feature = np.zeros(tuple([len(index_array)] +[len(self.pre_feature[0])] ),  
  40.                            dtype=K.floatx())  
  41.         for i, j in enumerate(index_array):  
  42.             x_temp = self.x[j]  
  43.             if self.image_data_generator.preprocessing_function:  
  44.                 x_temp = self.image_data_generator.preprocessing_function(x_temp)  
  45.             x_temp = self.image_data_generator.random_transform(x_temp.astype(K.floatx()))  
  46.             x_temp = self.image_data_generator.standardize(x_temp)  
  47.             batch_x[i] = x_temp  
  48.         if self.y is None:  
  49.             return batch_x  
  50.         batch_pre_feature = self.pre_feature[index_array]  
  51.         batch_y = self.y[index_array]  
  52.         return [batch_x,batch_pre_feature], batch_y  
  53.     def next(self):  
  54.         # Keeps under lock only the mechanism which advances  
  55.         # the indexing of each batch.  
  56.         with self.lock:  
  57.             index_array = next(self.index_generator)  
  58.         # The transformation of images is not under thread lock  
  59.         # so it can be done in parallel  
  60.         return self._get_batches_of_transformed_samples(index_array)  
[python]  view plain  copy
 
  1. imgen = ImageDataGenerator_leaf(  
  2.     rotation_range=20,  
  3.     zoom_range=0.2,  
  4.     horizontal_flip=True,  
  5.     vertical_flip=True,  
  6.     fill_mode='nearest')  
  7. imgen_train = imgen.flow(X_img_tr,X_num_tr, y_tr_cat)  

        b、自定义generator

    ①、先定义生成器

[python]  view plain  copy
 
  1. imgen = ImageDataGenerator(  
  2.     rotation_range=20,  
  3.     zoom_range=0.2,  
  4.     horizontal_flip=True,  
  5.     vertical_flip=True,  
  6.     fill_mode='nearest')  

    ②、定义自己的generator

[python]  view plain  copy
 
  1. def leaf_generator(generator, X_img_tr, X_num_tr, y_tr_cat):  
[python]  view plain  copy
 
  1.   imgen_train = generator.flow(X_img_tr, y_tr_cat)  
[python]  view plain  copy
 
  1.     n = (len(X_img_tr)+31)//32     #这里我使用了默认的batch_size:32,然后根据generator源代码里迭代次数的定义  
  2.     while True:                    # n = (self.n+batch_size-1)//batch_size 计算n值  
[python]  view plain  copy
 
  1.         for i in range(n):                
[python]  view plain  copy
 
  1.             x_img,y = imgen_train.next()  
[python]  view plain  copy
 
  1.             x_num = X_num_tr[imgen.index_array[:32]]  
[python]  view plain  copy
 
  1.             yield [x_img,x_num],y  

    ③、在使用fit_generator的时候传入自定义的leaf_generator

    3、搭建CNN网络

    可以参考上面链接里的代码,但是个人感觉这个网络其实是有一定的问题的,以下是我对这个网络结构的一些见解,如有错误,希望各位看官指正。

    首先我们来看一下网络的summary:

 

可以看到,进入全连接层之后,图像提取的特征值个数是18432,而预处理的特征值个数是192,这将会导致最终预处理数据所占的权重过低,甚至是几乎不起作用,也就失去了这个思路的意义,因此,可以对图像特征添加一个dense层来降维,使两种特征的权重均衡,或者对预处理特征用dense层进行维度提升。这里因为不想增加网络复杂度(主要是电脑配置太弱),采用的是降维的方案。

[python]  view plain  copy
 
  1. def combined_model():  
  2.     image = Input(shape=(96, 96, 1), name='image')  
  3.     x = Conv2D(8,kernel_size =(5,5),strides =(1,1),border_mode='same')(image)  
  4.     x = (Activation('relu'))(x)  
  5.     x = (MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))(x)  
  6.     x = Conv2D(32,kernel_size =(5,5),strides =(1,1),border_mode='same')(x)  
  7.     x = (Activation('relu'))(x)  
  8.     x = (MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))(x)  
  9.   
  10.     x = Flatten()(x)  
  11.     x = Dense(192,activation='relu')(x)  
  12.     numerical = Input(shape=(192,), name='numerical')  
  13.     concatenated = merge([x, numerical], mode='concat')  
  14.   
  15.     x = Dense(100, activation='relu')(concatenated)  
  16.     x = Dropout(.5)(x)  
  17.     out = Dense(99, activation='softmax')(x)  
  18.     model = Model(input=[image, numerical], output=out)  
  19.     model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])  
  20.     return model  

    4、训练网络

三、总结

    对于这个网络结构其实可以有很多的改进,例如把它设计成真正的联合模型(针对这个题目其实没必要),增加网络深度等等。作为初探Kaggle的题目,重心放在整体设计流程上也就可以了。麻雀虽小,五脏俱全,整个流程下来涉及到了DataAugmentation,简易Model Ensemble,也熟悉了简易CNN的搭建,也算是有所收获。

转载于:https://www.cnblogs.com/whlyc/p/8910559.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值