使用Densenet模型训练自己的数据集

前言:

之前四篇文章分别介绍了如何使用AlexNet 、VGG19 、 ResNet_152  、 Inception_V4训练自己数据集,本节将介绍最后一个经典图像识别模型Densenet,Densenet是CVPR2017年的Best Paper, DenseNet脱离了加深网络层数(ResNet)和加宽网络结构(Inception)来提升网络性能的定式思维。它建立的是前面所有层与后面层的密集连接(dense connection)。关于Densenet架构上的具体讲解可以参考知乎上的一篇文章PS一下因为是CVPR2017的最佳论文,所以关于架构的讲解有很多。有问题上知乎。

模型一: Densenet结构图

我们首先看一下Densenet的架构图,用于判断咱们后面的代码是否正确。PS一下很不喜欢Densenet代码的结构,因为和Inception 系列一样都是封装好了一个个的块,运行的时候需要执行多少遍块中代码就来几次循环。别人参考模型的时候很不方便。

同样首先是程序的主程序:

主程序运行的过程可以参考一下第一节的内容。

# -*- coding: utf-8 -*-
# @Time    : 2019/7/2 23:16
# @Author  : YYLin
# @Email   : [email protected]
# @File    : DenseNet_Train.py
from DenseNet import DenseNet
import tensorflow as tf
import os
import cv2
import numpy as np
from keras.utils import to_categorical

# 定义一些模型中所需要的参数
batch_size = 64
img_high = 100
img_width = 100
Channel = 3
label = 9

# 定义一些仅仅用于 Densenet 的超参
growth_k = 12
nb_block = 3

# 定义输入图像的占位符
inputs = tf.placeholder(tf.float32, [batch_size, img_high, img_width, Channel], name='inputs')
y = tf.placeholder(dtype=tf.float32, shape=[batch_size, label], name='label')
keep_prob = tf.placeholder("float")
is_train = tf.placeholder(tf.bool)

# 调用 DenseNet 架构
score = DenseNet(x=inputs, nb_blocks=nb_block, filters=growth_k, training=is_train).model
softmax_result = tf.nn.softmax(score)

# 定义损失函数 以及相对应的优化器
cross_entropy = -tf.reduce_sum(y*tf.log(softmax_result))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

# 显示最后预测的结果
correct_prediction = tf.equal(tf.argmax(softmax_result, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))


# 只需要最后一步 如何加载数据集 参考之前的AC-GAN 今天晚上完成!!!!!!!!!!!
# 现在的我只需要加载图像和对应的label即可 不需要加载text中的内容
def load_satetile_image(batch_size=128, dataset='train'):
    img_list = []
    label_list = []
    dir_counter = 0

    if dataset == 'train':
        path = '../Dataset/baidu/train_image/train'

        # 对路径下的所有子文件夹中的所有jpg文件进行读取并存入到一个list中
        for child_dir in os.listdir(path):
            child_path = os.path.join(path, child_dir)
            for dir_image in os.listdir(child_path):
                img = cv2.imread(os.path.join(child_path, dir_image))
                img = img/255.0
                img_list.append(img)
                label_list.append(dir_counter)

            dir_counter += 1
    else:
        path = '../Dataset/baidu/valid_image/valid'

        # 对路径下的所有子文件夹中的所有jpg文件进行读取并存入到一个list中
        for child_dir in os.listdir(path):
            child_path = os.path.join(path, child_dir)
            for dir_image in os.listdir(child_path):
                img = cv2.imread(os.path.join(child_path, dir_image))
                img = img / 255.0
                img_list.append(img)
                label_list.append(dir_counter)

            dir_counter += 1

    # 返回的img_list转成了 np.array的格式
    X_train = np.array(img_list)
    Y_train = to_categorical(label_list, 9)
    # print('to_categorical之后Y_train的类型和形状:', type(Y_train), Y_train.shape)

    # 加载数据的时候 重新排序
    # print('X_train.shape, Y_train.shape:', X_train.shape, Y_train.shape)
    data_index = np.arange(X_train.shape[0])
    np.random.shuffle(data_index)
    data_index = data_index[:batch_size]
    x_batch = X_train[data_index, :, :, :]
    y_batch = Y_train[data_index, :]

    return x_batch, y_batch


# 开始feed 数据并且训练数据
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(500000//batch_size):
        # 加载训练集和验证集
        img, img_label = load_satetile_image(batch_size, dataset='train')
        img_valid, img_valid_label = load_satetile_image(batch_size, dataset='vaild')
        # print('使用 mnist.train.next_batch加载的数据集形状', img.shape, type(img))

        # 源码之中是增加了正则化项 但是损失函数中暂时不再增加
        if i % 20 == 0:
            train_accuracy = accuracy.eval(feed_dict={inputs: img, y: img_label, is_train: True})
            print("step %d, training accuracy %g" % (i, train_accuracy))
        train_step.run(feed_dict={inputs: img, y: img_label, is_train: True})

        if i % 50 == 0:
            valid_socre = accuracy.eval(feed_dict={inputs: img_valid, y: img_valid_label, is_train: False})
            print("step %d, valid accuracy %g" % (i, valid_socre))


然后是本程序核心,Densenet模型:

咱们一步步的比较一下,上述架构图和下面代码的之间的关系

第一:  因为代码比较复杂,所以首先从整体上比较一下,也即是上图中的dense_block、 transition_layer、conv_layer、Max_Pooling在代码中是否存在。    直观上看是存在的,

第二: 图中前两层分别是卷积核大小为 7 * 7 和 3 * 3的卷积操作和池化操作,    代码中符合

第三: 图中接下来为三个dense_block和transition_layer  代码中我定义的是也是三个[dense_block和transition_layer]交错。

第四: 图中接下来是一层dense_block,然后经过一个卷积核大小为7*7的池化层,进入全连接层    代码中首先是dense_block 经过卷积核大小为7*7 的池化层进入全连接层。

Densenet是最后一篇要介绍的经典图像识别的模型了。每一篇我都会把论文中图像的架构拿出来和最后写的代码做个比较,我的意思不是经典模型一点都不能修改,而是通过模型和代码结合更好的理解这些模型。大家会发现我的模型中都没有涉及模型的保存操作,因为接下来我会介绍两篇如何使用已经训练好的模型,进行图像识别。而且我们发现最后训练的图像效果好像不是很好。这个首先是数据集的问题,卫星图像本身很难分清,单单使用一个模型肯定不会很好的,融合模型才是比赛取胜的关键。最主要的一个原因是比赛中我们不仅有图像还有对应文本数据。

# -*- coding: utf-8 -*-
# @Time    : 2019/7/2 10:23
# @Author  : YYLin
# @Email   : [email protected]
# @File    : DenseNet.py
import tensorflow as tf
from tensorflow.contrib.layers import batch_norm, flatten
from tensorflow.contrib.framework import arg_scope
import numpy as np
class_num = 9
dropout_rate = 0.5


def conv_layer(input, filter, kernel, stride=1, layer_name="conv"):
    with tf.name_scope(layer_name):
        network = tf.layers.conv2d(inputs=input, filters=filter, kernel_size=kernel, strides=stride, padding='SAME')
        return network


def Global_Average_Pooling(x, stride=1):

    width = np.shape(x)[1]
    height = np.shape(x)[2]
    pool_size = [width, height]
    return tf.layers.average_pooling2d(inputs=x, pool_size=pool_size, strides=stride)


def Batch_Normalization(x, training, scope):
    with arg_scope([batch_norm],
                   scope=scope,
                   updates_collections=None,
                   decay=0.9,
                   center=True,
                   scale=True,
                   zero_debias_moving_mean=True):
        return tf.cond(training,
                       lambda: batch_norm(inputs=x, is_training=training, reuse=None),
                       lambda: batch_norm(inputs=x, is_training=training, reuse=True))


def Drop_out(x, rate, training):
    return tf.layers.dropout(inputs=x, rate=rate, training=training)


def Relu(x):
    return tf.nn.relu(x)


def Average_pooling(x, pool_size=[2, 2], stride=2, padding='VALID'):
    return tf.layers.average_pooling2d(inputs=x, pool_size=pool_size, strides=stride, padding=padding)


def Max_Pooling(x, pool_size=[3, 3], stride=2, padding='VALID'):
    return tf.layers.max_pooling2d(inputs=x, pool_size=pool_size, strides=stride, padding=padding)


def Concatenation(layers):
    return tf.concat(layers, axis=3)


def Linear(x):
    return tf.layers.dense(inputs=x, units=class_num, name='linear')


class DenseNet():
    def __init__(self, x, nb_blocks, filters, training=True):
        self.nb_blocks = nb_blocks
        self.filters = filters
        self.training = training
        self.model = self.Dense_net(x)

    def bottleneck_layer(self, x, scope):
        # print(x)
        with tf.name_scope(scope):
            x = Batch_Normalization(x, training=self.training, scope=scope + '_batch1')
            x = Relu(x)
            x = conv_layer(x, filter=4 * self.filters, kernel=[1, 1], layer_name=scope + '_conv1')
            x = Drop_out(x, rate=dropout_rate, training=self.training)

            x = Batch_Normalization(x, training=self.training, scope=scope + '_batch2')
            x = Relu(x)
            x = conv_layer(x, filter=self.filters, kernel=[3, 3], layer_name=scope + '_conv2')
            x = Drop_out(x, rate=dropout_rate, training=self.training)

            # print(x)

            return x

    def transition_layer(self, x, scope):
        with tf.name_scope(scope):
            x = Batch_Normalization(x, training=self.training, scope=scope + '_batch1')
            x = Relu(x)
            # x = conv_layer(x, filter=self.filters, kernel=[1,1], layer_name=scope+'_conv1')

            # https://github.com/taki0112/Densenet-Tensorflow/issues/10

            in_channel = x.shape[-1]
            x = conv_layer(x, filter=in_channel, kernel=[1, 1], layer_name=scope + '_conv1')
            x = Drop_out(x, rate=dropout_rate, training=self.training)
            x = Average_pooling(x, pool_size=[2, 2], stride=2)

            return x

    def dense_block(self, input_x, nb_layers, layer_name):
        with tf.name_scope(layer_name):
            layers_concat = list()
            layers_concat.append(input_x)

            x = self.bottleneck_layer(input_x, scope=layer_name + '_bottleN_' + str(0))

            layers_concat.append(x)

            for i in range(nb_layers - 1):
                x = Concatenation(layers_concat)
                x = self.bottleneck_layer(x, scope=layer_name + '_bottleN_' + str(i + 1))
                layers_concat.append(x)

            x = Concatenation(layers_concat)

            return x

    def Dense_net(self, input_x):
        x = conv_layer(input_x, filter=2 * self.filters, kernel=[7, 7], stride=2, layer_name='conv0')
        x = Max_Pooling(x, pool_size=[3, 3], stride=2)

        for i in range(self.nb_blocks):
            # 6 -> 12 -> 48
            x = self.dense_block(input_x=x, nb_layers=4, layer_name='dense_' + str(i))
            x = self.transition_layer(x, scope='trans_' + str(i))

        """
        x = self.dense_block(input_x=x, nb_layers=6, layer_name='dense_1')
        x = self.transition_layer(x, scope='trans_1')
        x = self.dense_block(input_x=x, nb_layers=12, layer_name='dense_2')
        x = self.transition_layer(x, scope='trans_2')
        x = self.dense_block(input_x=x, nb_layers=48, layer_name='dense_3')
        x = self.transition_layer(x, scope='trans_3')
        """

        x = self.dense_block(input_x=x, nb_layers=32, layer_name='dense_final')

        # 100 Layer
        x = Batch_Normalization(x, training=self.training, scope='linear_batch')
        x = Relu(x)
        x = Global_Average_Pooling(x)
        x = flatten(x)
        x = Linear(x)

        # x = tf.reshape(x, [-1, 10])
        return x

 

Densenet实验结果: 

昨天晚上训练了一夜。只是训练了800个epoch。但是可以看到验证集上准确率正在增加,训练集上的准确率在动荡。所以整体而言模型还是不错的,我想如果放在mnist数据集上效果一定很不错。

猜你喜欢

转载自blog.csdn.net/qq_41776781/article/details/94495098