U-net+代码理解+数据流训练法1

U-net+代码理解+数据流训练法1_第1张图片
网络结构图

Unet前世传奇

什么是图像分割问题呢? 简单的来讲就是给一张图像,检测是用框出框出物体,而图像分割分出一个物体的准确轮廓。也这样考虑,给出一张图像 I,这个问题就是求一个函数,从I映射到Mask。至于怎么求这个函数有多种方法。我们可以看到这个图,左边是给出图像,可以看到人和摩托车,右边是分割结果。
求这个函数有很多方法,但是第一次将深度学习结合起来的是这篇文章全卷积网络(FCN),利用深度学习求这个函数。在此之前深度学习一般用在分类和检测问题上。由于用到CNN,所以最后提取的特征的尺度是变小的。和我们要求的函数不一样,我们要求的函数是输入多大,输出有多大。为了让CNN提取出来的尺度能到原图大小,FCN网络利用上采样和反卷积到原图像大小。然后做像素级的分类。可以看图二,输入原图,经过VGG16网络,得到特征map,然后将特征map上采样回去。再将预测结果和ground truth每个像素一一对应分类,做像素级别分类。也就是说将分割问题变成分类问题,而分类问题正好是深度学习的强项。如果只将特征map直接上采样或者反卷积,明显会丢失很多信息。
FCN采取解决方法是将pool4、pool3、和特征map融合起来,由于pool3、pool4、特征map大小尺寸是不一样的,所以融合应该前上采样到同一尺寸。这里的融合是拼接在一起,不是对应元素相加。由于网络中只有卷积没有全连接,所以这个网络又叫全卷积网络。

Unet今生故事

U-net+代码理解+数据流训练法1_第2张图片

特征提取部分,每经过一个池化层就一个尺度,包括原图尺度一共有5个尺度。
上采样部分,每上采样一次,就和特征提取部分对应的通道数相同尺度融合,但是融合之前要将其crop。这里的融合也是拼接。

一个大神用keras进行图像分割的github实例
最好的是这个数据集很小啊哈哈,30张512*512的训练图像
一个试验者的经验

一个和我配置一样的人的分割实例(数据同)

现在在尝试上面的东西之前,我先瞧瞧学长的代码

model.py 里面定义了u-net模型,第一个参数是pretrained_weights(预设权重值)

main.py 模型训练的时候用到了一个陌生的fit_generator函数训练,

history = model.fit_generator(myGene, steps_per_epoch = 5000,epochs=40, validation_data=valGene,validation_steps=5000,callbac

在这个之前还有一些奇奇怪怪的文件操作
但是有些文章也没有用这个,比如这个

#对模型进行训练和保存
model.fit(
    x = im_array,
    y = la_array,
    batch_size = 10, #batch_size代表每次从im_array中的n个图片中选10个来进行训练
    epochs = 8, #操作8次
    validation_split = 0.2, #取训练集中的0.2作为验证集
    shuffle = True
)
model.save("model_v1.h5") #保存模型 

但它前面做了预处理

def load_png_files(image_path,start,end): # 加载图片成numpy的list
    im_array = []
    for i in range(start,end+1):
        im = Image.open(os.path.join(image_path,str(i)+'.png'))
        tmp_array = np.array(im) #图片转numpy数组
        tmp_array = tmp_array[np.newaxis,:,:] #numpy数组添加一维,为了把二维图片转成三维图片集
        if len(im_array) == 0:
            im_array = tmp_array
        else:
            im_array = np.concatenate((im_array,tmp_array),axis=0) #将新的图片加入到之前的图片集
    return im_array
im_array = im_array[:,:,:,np.newaxis]#给代码增加了一维的通道
model = Unet('resnet34', input_shape = (512, 512, 1), encoder_weights = None)#1代表通道数  model.compile('Adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
#预编译模型

效果:数据集较少, 数据未进行处理等问题, 生成的图像效果不佳

比较两种方法区别:

model.fit(trainX, trainY, batch_size=32, epochs=50)

U-net+代码理解+数据流训练法1_第3张图片

下一个函数:fit_generator
U-net+代码理解+数据流训练法1_第4张图片简易代码如下
U-net+代码理解+数据流训练法1_第5张图片
认识生成器:
由于训练的时候数据比较大, 直接加载进入内存可能会超内存, 所以需要采用生成器, 其原理是每次返回一定数量的数据; 比如我model.fit的时候, batch_size是10, 一般直接10张图就塞进去了。而用生成器的话,生成器每次返回2个数据, 然后model就用这2个去进行训练。重复调用5次生成器, 这样就实现了10个数据的训练.
再回看一下学长代码:

def trainGenerator(batch_size,train_path,original_dir,mask_dir,aug_dict,target_size,image_color_mode = "grayscale",aug_image_save_dir=None,aug_mask_save_dir=None,original_aug_prefix="image",mask_aug_prefix="mask",seed=1):
    original_datagen = ImageDataGenerator(**aug_dict)
    mask_datagen = ImageDataGenerator(**aug_dict)
    aug_dict = dict(rotation_range=0.02,
                width_shift_range=0.05,
                height_shift_range=0.05,
                shear_range=0.05,
                zoom_range=0.05,
                horizontal_flip=True,
                fill_mode='nearest')
    original_generator = original_datagen.flow_from_directory(
            train_path,
            classes = [original_dir],
            class_mode = None,
            color_mode = image_color_mode,
            target_size = target_size,
            batch_size = batch_size,
            save_to_dir = aug_image_save_dir,
            save_prefix = original_aug_prefix,
            seed = seed)
            mask_generator = mask_datagen.flow_from_directory(
        train_path,
        classes = [mask_dir],
        class_mode = None,
        color_mode = image_color_mode,
        target_size = target_size,
        batch_size = batch_size,
        save_to_dir = aug_mask_save_dir,
        save_prefix = mask_aug_prefix,
        seed = seed)

train_generator = zip(original_generator,mask_generator)
----
myGene = trainGenerator(BATCH_SIZE, train_path, type, "GTM", aug_dict, target_size=data_size,
                        aug_image_save_dir=Aug_originall, aug_mask_save_dir=Aug_GTM1)#获得一个训练数据生成器
history = model.fit_generator(myGene,#训练集生成器
 steps_per_epoch = 5000,epochs=40, validation_data=valGene,#验证集生成器
 validation_steps=5000,callbacks=[model_checkpoint])
                        

执行fit-generater时,original_datagen数据流返回BATCH_SIZE个经过各种变形的样本(每次取这么多),形成一个batch。重复这一过程5000(fit_generator的steps_per_epoch参数)次,一个epoch(一轮训练)结束。一个epoch所用样本batch_size乘以steps_per_epoch。当epoch=40(fit_generator的epochs参数)时,模型训练结束。
附上一篇我看不懂的数据流讲解

上面那个代码,先是初始化了一个叫original_datagen的数据生成器,对数据进行各种变形,flow_from_directory(以文件夹路径为参数,生成经过数据提升/归一化后的数据,在一个无限循环中无限产生batch数据)设计了从训练图片文件夹中取数据的方式,参数讲解如下
U-net+代码理解+数据流训练法1_第6张图片
注意 : 我们在进行数据增强的时候, 一定是图像和标签同时进行增强的!我终于知道mask是干什么的了!!实现方法: 为图像设置一个生成器, 标签设置一个生成器, 然后赋予他们同样的seed, 这样他们就会进行相同变化

贴上大神讲解代码

aug = ImageDataGenerator( #定义一个数据增强生成器
    rotation_range = 0.05, # 定义旋转范围
    zoom_range = 0.05, # 按比例随机缩放图像尺寸
 width_shift_range = 0.05, # 图片水平偏移幅度
    height_shift_range = 0.05, # 图片竖直偏移幅度
    shear_range = 0.05, # 水平或垂直投影变换
 horizontal_flip = True, # 水平翻转图像
    fill_mode = "reflect" # 填充像素,出现在旋转或平移之后
)

我想像model.fit中一样从训练集中随机抽出一些作为验证集怎么办?
​这个问题我在网上找了很久, 并没有发现相关的方法, 可能想要实现的话需要自己手写.

你可能感兴趣的:(U-net+代码理解+数据流训练法1)