9月份报名做了 2018之江杯全球人工智能大赛 之零样本图像目标识别 的这道题 - 题目链接-https://tianchi.aliyun.com/competition/entrance/231677/introduction
初赛中获得了135名(共3224支队伍参赛),虽没有进入前100名进入决赛,也勉强算是top %5吧。
在此总结一下比赛的收获:
自四月底完成了12306图片验证码的破解以后的几个月,陆续研究了RNN/Yolo之类的算法,
才发现深度学习实在内容太多,多年不学习,要赶上潮流很困难。。
一个偶然的机会发现了阿里云天池 - 2018之江杯全球人工智能大赛 之零样本图像目标识别 的比赛,
刚开始啥叫零样本目标识别都不知道,以为就是普通的图片分类,于是报名。
因为没太理解题目,直接做了个图像分类,提交测试,发现完全不行。于是仔细研究了一下 零样本图像目标识别,重新开始调整算法。
那么图像分类和零样本目标识别有啥不同??
图像分类 - 拿12306火车票图片来说吧,总共80种,比如其中一类为红枣,给你100个红枣的图片,尽管这100个红枣的图片千差万别,但都是红枣,机器就拿这100个样本学习并记住其
特征,比如红色的、椭圆的,表皮有褶皱的等等。 同样,对于另外79种,也一样操作,学习完成了,你给一张图片,给机器识别,机器提取该张图片的特征后,在学习过的80种中间挑出机器认为概率最大的一个。------- 注意,图片分类,机器识别的结果只能是
机器学习过的80种图片中间的一种,如果你给一个80种图片以外的其他图片,机器是无法识别出来的。
零样本图像目标识别 - 与图像分类不同,比如,给你一类猪的图片,100张各种各样的猪,机器学习完成以后,需提取猪的各种特征,如四条腿、大耳朵、2只眼睛,鼻孔很大等等。学习完成以后呢,
给机器一张牛的图片识别,需要机器认出来这是一头牛 --- 注意:牛这个类别机器以前的学习样本中是没有牛这个类别的。这才是本质的区别!!!!!
图像分类算法很简单,就不用提了,那这个零样本目标识别算法咋整???
零样本图像目标识别的基本思路:
1. 与图片分类已有,对已有样本类别用深度学习模型提取 图片特征
2. 对已有图片文字类别名称矢量化 - word2Vec --- 比赛题目中,出题方已经帮大家完成了矢量化的操作,不用自己单独提取。-- 注意,这个矢量化包括学习样本以外的其他类别,如牛这个类别
3. 对以后图片的各个类别做特征标注 - 比赛题目中,出题方已经帮大家完成了各个类别的标注,不用自己手工标注。 --- 注意,这个标注包括学习样本以外的其他类别,如牛这个类别
4. 将每类图片提取出来的特种映射到 类别名称矢量 或 类别特征标注,使得该图片与对应的矢量或特征标注某种距离最小。
5. 完成学习以后,当输入一张新的图片时,深度学习提取图片特征做矢量映射,
然后计算该矢量与 步骤3或4 的所有类别(包括学习样本的类别以及学习样本以外的类别)的距离,取距离最小的那个类别为识别目标。
在实际比赛中,发现用图片特征标注计算距离效果很差,实际我只用到了word2Vec矢量。
因比赛方提供的数据已经做好了很多准备工作,所以数据预处理工作量不大,参赛者只需实现核心代码,算法代码部分非常简短。
1. 图片特征提取用了残差网络ResNet。
2. 代码用最简单的keras实现
3. 只用了类别矢量化,没用手工标注数据。
4. 预赛二阶段的数据准确率还不到10%,与前排的大神差距非常大。-- 前排大神 都是20%多。
5. 训练时用了数据扩增 data augument,划分了10%的验证集,避免过拟合,使用了checkpoint/early_stop,和动态调整学习率。
算法的非核心代码不值一提,算法的training部分的核心代码非常少,如下所示。供各位有兴趣的同学学习参考。
def res_embed_train_cnn(input_train_pkl,val_data_pkl,class_embed_pkl,output_pkl,bst_model_path):
pkl_file = open(class_embed_pkl, 'rb')
class_embed_mat = pickle.load(pkl_file)
pkl_file.close()
num_feature=class_embed_mat.shape[1]
batch_size = 512
num_class=285
epochs = 300
img_rows, img_cols = 64,64
x, train_y_int = read_training_data_dir(input_train_pkl)
val_x, val_y_int = read_training_data(val_data_pkl)
if K.image_data_format() == 'channels_first':
input_shape = (3, img_rows, img_cols)
input_shape_pics=(len(train_y_int),3, img_rows, img_cols)
val_shape_pics=(len(val_y_int),3, img_rows, img_cols)
else:
input_shape = (img_rows, img_cols, 3)
input_shape_pics=(len(train_y_int),img_rows, img_cols,3)
val_shape_pics=(len(val_y_int),img_rows, img_cols,3)
train_x=x.reshape(input_shape_pics)
val_x=val_x.reshape(val_shape_pics)
train_y=to_categorical(train_y_int, num_classes=num_class)
val_train_y=to_categorical(val_y_int, num_classes=num_class)
#for debug only
#train_x=train_x[0:256]
#val_x=val_x[0:256]
#train_y=train_y[0:256]
#val_train_y=val_train_y[0:256]
#debug end
print("begin cnn training ...")
X_input = Input(shape=input_shape)
# Stage 1
X = Conv2D(64,kernel_size=(3,3),strides=(1,1),name="conv1",padding='same',kernel_initializer=glorot_uniform(seed=0))(X_input)
X = BatchNormalization(axis=3,name="bn_conv1")(X)
X = Activation("relu")(X)
#X = MaxPooling2D(pool_size=(2,2),padding='same',strides=(1,1))(X)
X = MaxPooling2D(pool_size=(2,2),strides=(2,2))(X)
# Stage 2
X = convolutional_block(X,f=3,filters=[64,64,128],stage=2,block="a",s=2,d=0.25)
X = identity_block(X,f=3,filters=[64,64,128],stage=2,block="b")
X = identity_block(X,f=3,filters=[64,64,128],stage=2,block="c")
#Stage 3
X = convolutional_block(X,f=3,filters=[128,128,256],stage=3,block="a",s=2,d=0.25)
X = identity_block(X,f=3,filters=[128,128,256],stage=3,block="b")
X = identity_block(X,f=3,filters=[128,128,256],stage=3,block="c")
X = identity_block(X,f=3,filters=[128,128,256],stage=3,block="d")
# Stage 4
X = convolutional_block(X,f=3,filters=[256,256,512],stage=4,block="a",s=2,d=0.5)
#X = Dropout(0.25)(X)
X = identity_block(X,f=3,filters=[256,256,512],stage=4,block="b")
X = identity_block(X,f=3,filters=[256,256,512],stage=4,block="c")
X = identity_block(X,f=3,filters=[256,256,512],stage=4,block="d")
X = identity_block(X,f=3,filters=[256,256,512],stage=4,block="e")
X = identity_block(X,f=3,filters=[256,256,512],stage=4,block="f")
#Stage 5
X = convolutional_block(X,f=3,filters=[512,512,1024],stage=5,block="a",s=2,d=0.5)
X = identity_block(X,f=3,filters=[512,512,1024],stage=5,block="b")
X = identity_block(X,f=3,filters=[512,512,1024],stage=5,block="c")
#最后阶段
#平均池化
X = AveragePooling2D(pool_size=(2,2))(X)
#输出层
X = Flatten()(X) #展平
X = Dropout(0.5)(X)
X = Dense(num_feature)(X)
X = LeakyReLU()(X)
X=Dense(num_class, activation='softmax', trainable=False, kernel_initializer=custom_kernel_init_embed)(X)
model = Model(X_input, X)
model.compile(loss='categorical_crossentropy', optimizer='adam',metrics=['categorical_accuracy'])
model.summary()
#print("reading train_model data...")
#model.load_weights(output_pkl+'_weights') #for continue training
reduce_lr=ReduceLROnPlateau(monitor='loss', factor=0.1, patience=5, verbose=1, mode='min', min_delta=0.0001, cooldown=10, min_lr=0)
model_checkpoint = ModelCheckpoint(bst_model_path, verbose=1,save_best_only=True,monitor='val_loss',mode='min')
early_stopping=EarlyStopping(monitor='loss',patience=80,verbose=1,mode='min')
#augermentation training
#train_datagen = ImageDataGenerator(rotation_range=30,zoom_range=0.2,channel_shift_range=10,width_shift_range=0.15,shear_range=0.1,
height_shift_range=0.15,horizontal_flip=True,vertical_flip=True)
train_datagen = ImageDataGenerator( rotation_range=15,width_shift_range=0.15,height_shift_range=0.15,horizontal_flip=True,vertical_flip=True)
#train_datagen = ImageDataGenerator( rotation_range=15,width_shift_range=5,height_shift_range=5,horizontal_flip=True,vertical_flip=True)
train_datagen.fit(train_x)
model.fit_generator(train_datagen.flow(train_x,train_y, batch_size=batch_size),steps_per_epoch=len(train_x)/batch_size,
epochs=epochs,verbose=1,validation_data=(val_x,val_train_y),callbacks=[model_checkpoint,early_stopping,reduce_lr])
model.save(output_pkl) #save model,for predict
model.save_weights(output_pkl+'_weights') #save weights,for continue training
print('Training over!')
return