一、简介
1.背景介绍
我们给出六千张图像作为训练集。每个图像中只有一个图形,要么是圆形,要么是正方形。任务是根据这六千张图片训练出一个二元分类模型,并用它在测试集上判断每个图像中的形状是圆还是方;测试集中有些图像既不是圆、也不是方,也请将它们甄别出来。(比赛链接)
2.数据
- 数据文件:
train.csv 训练集,文件大小 34.7mb
test.csv 预测集, 文件大小 30.0mb
sample_submit.csv 提交示例 文件大小 40kb - 数据说明:
训练集中共有6000个灰度图像,预测集中有5191个灰度图像。每个图像中都会含有大量的噪点。
图像的分辨率为40x40,也就是40x40的矩阵,每个矩阵以行向量的形式被存放在train.csv和test.csv中。train.csv和test.csv中每行数据代表一个图像,也就是说每行都有1600个特征。
3.变量说明
变量名 | 释义 |
---|---|
id | 编号 |
p_i_j | 表示图像中第i行第j列上的像素点的灰度值,取值范围在0到255之间,i和j的取值都是0到39。 |
y | 表示该图像中的形状。0表示圆形,1表示方形。这是需要被预测的标签。 |
4.评价方法
提交的结果为每行的预测标签,也就是0、1、2。
评价方法为准确率。0表示圆形,1表示方形,2表示异形。
二、标杆模型
构造卷积网络进行形状识别,卷积神经网络是解决图像相关的机器学习问题的常用模型。
1.加载数据
(1)异性参数设置
# --- 参数设置 ---
# 手动查看测试集中的图像,并且增加了五个异形,放入训练集
num_anno=5
epochs=100
SEED=0
# 考虑到异性并不多,所以设置如下权重,来解决非平衡下的分类
class_weight={0:1.,1:1.,2:10.}
(2)加载训练集测试集
# 加载训练集
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
train_data_raw = np.genfromtxt('train.csv', delimiter=',')
train_data=train_data_raw[1:,1:]
[n_train,d]=train_data.shape
y_train=train_data[:,d-1]
x_train=train_data[:,:-1]
[n_train,d]=x_train.shape
sz=40
x_train=x_train.reshape([n_train,sz,sz])
# 加载测试集
test_data_raw = np.genfromtxt('test.csv', delimiter=',')
test_data=test_data_raw[1:,:]
x_test=test_data[:,1:]
n_test=x_test.shape[0]
x_test=x_test.reshape([n_test,sz,sz])
2.数据处理——利用中值滤波(median filter)进行降噪
(1)linspace:在指定的间隔内返回均匀间隔的数字。
(2)medfilt:中值滤波技术能有效抑制噪声,通过把数字图像中一点的值用该点周围的各点值的中位数来代替,让这些值接近,以消除原图像中的噪声。
代码:
import numpy as np
import pylab as p
import scipy.signal as signal
# get some linear data
x = np.linspace(0,3,101)
print('Without noisy:',x)
# add some noisy signal
x[5::10] = 1.5
print('With noisy:',x)
plt.plot(x)
plt.plot(signal.medfilt(x,3))
plt.plot(signal.medfilt(x,5))
plt.legend(['original signal', 'length 3', 'length 5'])
plt.show()
结果:
3.数据处理
- 利用中值滤波(median filter)进行降噪
中值滤波的主要原理是将数字图像中的某点用该点的邻域中各个像素值的中值所来代替,例如黑点的像素值比较大,在周围都是白点的情况下,用中值进行填充,那么黑点的像素值就会变小,从而能在过滤出噪声点。标杆模型中用某点周围的5个像素值的中值进行代替,如图1:
————————————
均值滤波、中值滤波和高斯滤波 - 利用阈值分割法(threshold segmentation)生成掩膜(binary mask)
在本文中的阈值分割法原理就是以一张图片所有像素值的众数作为阈值,当某点的像素值小于阈值时,则通过布尔值进行分类,分成白点或黑点,例如众数对应是黑点的像素值,那么小于众数的像素值点就被分类为白点,那么一张图片就形成黑白分明的图。如图2:
- 利用形态闭合(morphology closing)来填充图中的小洞
形态变换分为形态闭运算和形态开运算,形态闭运算是形态先膨胀后腐蚀,可用来填充小孔,而开运算则是先腐蚀后膨胀,可用来消除小斑块。本文采用闭运算来填充图2中,阈值分割后的小孔。
膨胀:原理是在二值图像上,找到像素值为1的点,将它的邻近像素点都设置成这个值。1值表示白,0值表示黑,因此膨胀操作可以扩大白色值范围,压缩黑色值范围。
腐蚀:和膨胀相反的操作,将0值扩充到邻近像素。扩大黑色部分,减小白色部分。如图3:
代码:
from skimage.restoration import (denoise_tv_chambolle, denoise_bilateral,
denoise_wavelet, estimate_sigma)
from scipy import ndimage, misc
import statistics
import scipy
def my_preprocessing(I,show_fig=False):
I_median=ndimage.median_filter(I, size=5)
mask=(I_median<statistics.mode(I_median.flatten()))
I_out=scipy.ndimage.morphology.binary_closing(mask,iterations=2)
if(np.mean(I_out[15:25,15:25].flatten())<0.5):
I_out=1-I_out
if show_fig:
fig= plt.figure(figsize=(8, 4))
plt.gray()
plt.subplot(2,4,1)
plt.imshow(I)
plt.axis('off')
plt.title('Image')
plt.subplot(2,4,2)
plt.imshow(I_median)
plt.axis('off')
plt.title('Median filter')
plt.subplot(2,4,3)
plt.imshow(mask)
plt.axis('off')
plt.title('Mask')
plt.subplot(2,4,4)
plt.imshow(I_out)
plt.axis('off')
plt.title('Closed mask')
fig.tight_layout()
plt.show()
return I_out
I_out=my_preprocessing(x_test[6],True);
x_train_prc=np.zeros_like(x_train)
x_test_prc=np.zeros_like(x_test)
for i in range(n_train):
x_train_prc[i]=my_preprocessing(x_train[i])
for i in range(n_test):
x_test_prc[i]=my_preprocessing(x_test[i])
- 添加异性数据
由于训练集中并没有异型的样本,所以当测试集中出现异型样本时,模型不能识别。所以往训练集中添加异型样本,其中异性样本来自测试集,通过对数据的可视化,人工标识异型样本。如下图:
代码:
import pandas as pd
import matplotlib.pyplot as plt
def seek_abnormal(testFile, show_fig=True):
data = pd.read_csv(testFile)
data1 = np.array(data.iloc[0:, 1:])
# print(data1.shape) # (5191, 1600)
# data2 = np.array(data)
# data2 = data2[0:, 1:]
photo1 = np.reshape(data1[119], (40, 40))
photo2 = np.reshape(data1[956], (40, 40))
photo3 = np.reshape(data1[973], (40, 40))
photo4 = np.reshape(data1[974], (40, 40))
photo5 = np.reshape(data1[988], (40, 40))
if show_fig:
plt.subplot(1, 5, 1)
plt.gray()
plt.imshow(photo1)
plt.title('photo1')
plt.subplot(1, 5, 2)
plt.gray()
plt.imshow(photo2)
plt.title('photo2')
plt.subplot(1, 5, 3)
plt.gray()
plt.imshow(photo3)
plt.title('photo3')
plt.subplot(1, 5, 4)
plt.gray()
plt.imshow(photo4)
plt.title('photo4')
plt.subplot(1, 5, 5)
plt.gray()
plt.imshow(photo5)
plt.title('photo5')
if __name__ == '__main__':
testFile = 'test.csv'
# 查找异形图片
seek_abnormal(testFile)
- 数据增广
keras中的图片生成器(ImageDataGenerator),其实就是对数据进行一些处理,比如去中心化、图片宽度、高度的处理、图片旋转等。
4.模型搭建
- 卷积层
原理就是用一个超参数filter对图像矩阵进行局部提取特征。如下图:
如上图所示,该图片的输入为一个三通道的图片,即RGB图像。而超参数为Filter_w0和Filter_w1。其中蓝色部分为图像的像素数据,为了增加对边缘数据的利用,在外层增加了一层灰色全为0的数据,目的就是在用超参数提取局部数据时,能增加外层数据的利用,其中b0为偏置项,一个filter就输出一组数据,即绿色部分。 - 池化层
池化层就是对卷积层的结果进行进一步的提取,设置一个filter,对卷积结果进行局部提取,取局部矩阵中的最大值或者平均值,作为这个局部矩阵的代表值,这样就缩小的图像的大小,例如卷积层输出是4x4大小的矩阵,而池化层filter是2x2矩阵,那么池化后的输出结果为2x2的矩阵。如图6:
- 全连接层
其实就是传统神经网络,如图7:
5.训练卷积神经网络
- 选择卷积网络的理由:卷积神经网络对几何变换、形变、光照具有一定程度的不变性。训练过的卷积神经网络可以用较小的计算代价扫描整幅待检测图像,因此,被广泛应用于目标检测。
- 卷积神经网络(CNN)一般包括卷积层、池化层、全连接层。有时为了防止过拟合,会加入dropout层。
在数据样本量较小时,会进行数据增广。
from keras.callbacks import TensorBoard
from keras.layers import Dense, Dropout, MaxPooling2D, Flatten, Convolution2D
from keras.models import Sequential
from keras.optimizers import Adam
from keras import backend as K
from keras.preprocessing.image import ImageDataGenerator
import tensorflow.compat.v1 as tf
tf.random.set_random_seed(SEED)
session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
#sess = tf.compat.v1.Session(graph=tf.get_default_graph(), config=session_conf)
sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
#K.set_session(sess)
tf.keras.backend.set_session(sess)
def built_model():
n_filter=32;
model = Sequential()
model.add(Convolution2D(filters=n_filter,
kernel_size=(5, 5),
input_shape=(40, 40, 1),
activation='relu'))
model.add(Convolution2D(filters=n_filter,
kernel_size=(5,5),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(filters=n_filter,
kernel_size=(5,5),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(filters=n_filter,
kernel_size=(3, 3),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(units=128,
activation='relu'))
model.add(Dense(3, activation='softmax')) # Final Layer using Softmax
model.compile(loss='categorical_crossentropy',
optimizer=Adam(lr=0.0003),
metrics=['accuracy'])
model.summary()
return model
def train_model(x_train,y_train, x_test, batch_size=64, epochs=20, model=None,
class_weight={0:1.,1:1.,2:10.}):
if np.ndim(x_train)<4:
x_train=np.expand_dims(x_train,3)
x_test=np.expand_dims(x_test,3)
if model is None:
model = built_model()
datagen = ImageDataGenerator(
rotation_range=180,
width_shift_range=0.1,
height_shift_range=0.1,
horizontal_flip=True
)#数据旋转
# 训练模型的同时进行数据增广
history=model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),
steps_per_epoch=len(x_train) / batch_size, epochs=epochs,
class_weight=class_weight,
validation_data=datagen.flow(x_train, y_train, batch_size=batch_size),
validation_steps=1)
print("Loss on training and testing.")
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='valid')
plt.legend()
plt.show()
pred_prob_train = model.predict(x_train, batch_size=batch_size, verbose=1)
pred_train = np.array(pred_prob_train > 0.5).astype(int)
pred_prob_test = model.predict(x_test, batch_size=batch_size, verbose=1)
pred_test = np.array(pred_prob_test > 0.5).astype(int)
y_test_hat=pred_test[:,1]+pred_test[:,2]*2;
y_train_hat=pred_train[:,1]+pred_train[:,2]*2;
return y_train_hat,y_test_hat,history
在不同的python环境里,tensorflow和keras的版本兼容问题会导致报错,所以大家要根据自己python的版本去选择不同版本的tensorflow和keras,刚刚的代码里的set_session函数,tensorflow2.0的版本及以上就没了,所以最推荐的还是tensorflow1.13的版本
三、模型集成
集成原理就是少数服从多数,我们采用三个卷积神经网络模型的预测结果进行一个集成,例如样本1在三个模型中的预测结果是一样时,集成结果即三个模型一致的结果,若三个模型中,两个模型的预测结果是一致,而只有一个模型的预测结果不同时,那就少数服从多数,取两个模型一致的预测结果作为集成结果,若三个模型预测结果均不一样时,取最优模型的预测结果作为集成结果。
————————————
通俗易懂–模型集成(多模型)讲解
代码:
df1=pd.read_csv('my_CNN_prediction.csv')
df2=pd.read_csv('3.csv')
df3=pd.read_csv('4.csv')
df=pd.merge(df1, df2, on = 'id')
df=pd.merge(df,df3,on='id')
df.rename(columns={'y_x':'a','y_y':'b','y':'c'}, inplace=True)
for i in df.index:
#三个结果相同
if df.loc[i,'a']+df.loc[i,'b']+df.loc[i,'c']==0 or df.loc[i,'a']+df.loc[i,'b']+df.loc[i,'c']==3 or df.loc[i,'a']+df.loc[i,'b']+df.loc[i,'c']==6:
df.loc[i,'d']=(df.loc[i,'a']+df.loc[i,'b']+df.loc[i,'c'])/3
df.loc[i,'f']='三个结果相同'
df.loc[i,'e']='abc'
#两个结果相同
elif df.loc[i,'a']==df.loc[i,'b'] and df.loc[i,'b']!=df.loc[i,'c']:
df.loc[i,'d']=df.loc[i,'a']
df.loc[i,'f']='两个结果相同'
df.loc[i,'e']='ab'
elif df.loc[i,'a']==df.loc[i,'c'] and df.loc[i,'b']!=df.loc[i,'c']:
df.loc[i,'d']=df.loc[i,'a']
df.loc[i,'f']='两个结果相同'
df.loc[i,'e']='ac'
elif df.loc[i,'b']==df.loc[i,'c'] and df.loc[i,'a']!=df.loc[i,'c']:
df.loc[i,'d']=df.loc[i,'b']
df.loc[i,'f']='两个结果相同'
df.loc[i,'e']='bc'
#三个结果不相同
else:
df.loc[i,'f']='三个结果不相同'
df.loc[i,'d']=df.loc[i,'b']
print('-'*125)
print('列中各元素数量')
print('-'*125)
print(df['f'].value_counts() )
print('-'*125)
print(df['d'].value_counts() )
print('-'*125)
print(df['e'].value_counts() )
完整代码:https://gitee.com/liu_ji_duan/DuanGe/tree/master/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/%E5%8D%B7%E7%A7%AF%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C
————————
https://www.jianshu.com/p/ffbca46b3a39原文跳转
最后的排名: