其实这个比赛早在19年的时候就结束,比赛名为《Understanding Clouds from Satellite Images》,原来的任务其实不仅要识别出来类型还要能够分割出来具体的区域,这里我只是基于这个卫星云数据集来实践多标签分类模型,所以分割就留给以后有时间在做了。
官方地址在这里,截图如下所示:
随便一搜都有很多大佬的实现:
Git里面也有很多原始参赛者的开源提交的项目,感兴趣的话可以去看看,我自己最开始的时候也找了几个star较多的项目来用,但是无一例外都没能运行起来,可能有各种各样的问题在里面吧,所以后面还是觉得自己来实践吧。
看下数据集:
其实,云对于我们来讲还是比较抽象的存在的,不像我之前做的动植物、水果蔬菜之类的,你很确信指导他就是什么,另外我没有这方面的专业背景,所以这一堆图像对于我来说基本都是一样的,还好有标签数据,接下来看下官方提供的标签数据,如下所示:
最开始看到的时候感觉真的就是天书的存在,网上其实也没有找到有用的帮你去理解数据集的说明,在不断地探索下,我逐渐明白了数据集里面用于分类任务的标签数据应该怎么用了,其实核心就是在于Image_Label字段,后面的EncodedPixels是用于分割任务的,这个不在本文的实践范畴内,我们看几个Image_Label的实例:
0011165.jpg_Fish,810165 1002 2811565...
0011165.jpg_Flower
0011165.jpg_Gravel,
0011165.jpg_Sugar,233813 878 235213 878 2366...
Image_Label字段值以下划线作为分隔符,表示每幅图片里面有那种云状类型,我也整体统计过了,一共只包含四种云状类型,分别是:
Fish
Flower
Gravel
Sugar
为了模型计算方便,这里对原始数据进行解析转化处理,代码实现如下所示:
def parseData(dataset, picDir='data/train_images/', labelData='data/train.csv'):
'''
加载本地数据集创建数据集
'''
data_dict={}
df=pd.read_csv(labelData)
data_list=df.values.tolist()
print("data_list_length: ", len(data_list))
for one_list in data_list:
one_pic_id=one_list[0].split('.')[0].strip()
one_pic_class=one_list[0].split("_")[-1].strip()
one_pic_mask=str(one_list[-1]).strip()
if " " in one_pic_mask:
if one_pic_id in data_dict:
data_dict[one_pic_id].append(one_pic_class)
else:
data_dict[one_pic_id]=[one_pic_class]
X, y =[], []
for one_pic in os.listdir(picDir):
one_path = picDir + one_pic
print("one_path: ", one_path)
#图片
one_img = cv2.imread(one_path)
one_img = cv2.resize(one_img,(420,280))
one_img = one_img.transpose((2,0,1))
#标签
one_pic_classes = data_dict[one_pic.split('.')[0].strip()]
one_y = getY(one_pic_classes)
#整合
X.append(one_img)
y.append(one_y)
X = np.array(X)
f = h5py.File(dataset)
f['X'] = X
f['y'] = y
f.close()
模型层面我选择了基于预训练的MobileNet来进行训练,如下所示:
def defineModel(freeze_num=45,h=224,w=224,way=3):
'''
模型初始化
'''
base_model=MobileNet(input_shape=(h,w,way), include_top=False, weights='imagenet')
x=base_model.output
x=GlobalAveragePooling2D()(x)
x=Dense(1024, activation='relu',kernel_regularizer=regularizers.l2(l=0.0001))(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x=Dropout(0.3)(x) #随机丢弃一些神经元
prediction=Dense(4,activation='sigmoid')(x)
model=Model(inputs=base_model.input,outputs=prediction) #构造完新的FC层,加入custom层
#model.summary()
print("layer nums:", len(model.layers))
#层
layer_num=0
for layer in model.layers[:freeze_num]:
layer.trainable = False
for layer in model.layers[freeze_num:]:
layer.trainable = True
for layer in model.layers:
layer_num+=1
print('layer_num====>',layer_num,layer.name,'====>',layer.trainable)
print('layer_num: ', layer_num)
#模型编译
# model.compile(optimizer=SGD(lr=0.0001, momentum=0.9),
# loss='categorical_crossentropy', metrics=['accuracy'])
model.compile(optimizer=SGD(lr=0.0001, momentum=0.9),
loss='binary_crossentropy', metrics=['accuracy'])
return model
模型训练如下:
checkpoint = ModelCheckpoint(
filepath=saveDir + "model.h5",
monitor="val_loss",
verbose=1,
mode="min",
save_best_only="True",
period=1,
)
model = defineModel(freeze_num=82, h=420, w=280, way=3)
history = model.fit(
x_train,
y_train,
batch_size=bsize,
nb_epoch=1000,
callbacks=[checkpoint],
validation_data=(x_test, y_test),
)
# 训练数据曲线可视化
print(history.history.keys())
plt.clf()
plt.figure(figsize=(15, 8))
plt.plot(history.history["acc"])
plt.plot(history.history["val_acc"])
plt.title("model accuracy")
plt.ylabel("accuracy")
plt.xlabel("epochs")
plt.legend(["train", "test"], loc="upper left")
plt.savefig(saveDir + "train_validation_acc.png")
plt.clf()
plt.figure(figsize=(15, 8))
plt.plot(history.history["loss"])
plt.plot(history.history["val_loss"])
plt.title("model loss")
plt.ylabel("loss")
plt.xlabel("epochs")
plt.legend(["train", "test"], loc="upper left")
plt.savefig(saveDir + "train_validation_loss.png")
# 保存模型结构+权重数据
model_json = model.to_json()
with open(saveDir + "structure.json", "w") as f:
f.write(model_json)
model.save_weights(saveDir + "weight.h5")
print("Model Save Success.........................................")
默认执行1000次的迭代计算,结果截图如下所示:
训练集-测试集准确度曲线如下所示:
训练集-测试集损失值曲线如下所示:
为了直观地展示,这里做了简单的界面来可视化展示,如下所示: