多分类图像识别案例

片信息的读取与写入

二进制文件的读取

使用tf.FixedLengthRecordReader去读取,我们将其保存到TFRecords文件当中,以这种文件格式保存当作模型训练数据的来源

在这里我们设计一个CifarRead类去完成。将会初始化每个图片的大小数据

def __init__(self, filelist=None):
    # 文件列表
    self.filelist = filelist
    # 每张图片大小数据初始化
    self.height = 32
    self.width = 32
    self.channel = 3
    self.label_bytes = 1
    self.image_bytes = self.height * self.width * self.channel
    self.bytes = self.image_bytes + self.label_bytes

读取代码:

def read_decode(self):
    """
    读取数据并转换成张量
    :return: 图片数据,标签值
    """

    # 1、构造文件队列
    file_queue = tf.train.string_input_producer(self.filelist)

    # 2、构造二进制文件的阅读器,解码成张量
    reader = tf.FixedLengthRecordReader(self.bytes)

    key, value = reader.read(file_queue)

    # 解码成张量
    image_label = tf.decode_raw(value, tf.uint8)

    # 分割标签与数据
    label_tensor = tf.cast(tf.slice(image_label, [0], [self.label_bytes]), tf.int32)

    image = tf.slice(image_label, [self.label_bytes], [self.image_bytes])

    print(image)

    # 3、图片数据格式转换
    image_tensor = tf.reshape(image, [self.height, self.width, self.channel])

    # 4、图片数据批处理,一次从二进制文件中读取多少数据出来

    image_batch, label_batch = tf.train.batch([image_tensor, label_tensor], batch_size=5000, num_threads=1, capacity=50000)

    return image_batch, label_batch

保存和读取TFRecords文件当中

我们通过两个接口实现

def write_to_tfrecords(self, image_batch, label_batch):
    """
    把读取出来的数据进行存储(tfrecords)
    :param image_batch: 图片RGB值
    :param label_batch: 图片标签
    :return:
    """

    # 1、构造存储器
    writer = tf.python_io.TFRecordWriter(FLAGS.image_dir)

    # 2、每张图片进行example协议化,存储
    for i in range(5000):
        print(i)

        # 图片的张量要转换成字符串才能写进去,否则大小格式不对
        image = image_batch[i].eval().tostring()

        # 标签值
        label = int(label_batch[i].eval())

        # 构造example协议快,存进去的名字是提供给取的时候使用
        example = tf.train.Example(features=tf.train.Features(feature={
            "image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
            "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
        }))

        writer.write(example.SerializeToString())

    return None

def read_tfrecords(self):

    # 1、构建文件的队列
    file_queue = tf.train.string_input_producer([FLAGS.image_dir])

    # 2、构建tfrecords文件阅读器
    reader = tf.TFRecordReader()

    key, value = reader.read(file_queue)

    # 3、解析example协议块,返回字典数据,feature["image"],feature["label"]
    feature = tf.parse_single_example(value, features={
        "image": tf.FixedLenFeature([], tf.string),
        "label": tf.FixedLenFeature([], tf.int64)
    })

    # 4、解码图片数据,标签数据不用
    # 图片数据处理
    image = tf.decode_raw(feature["image"], tf.uint8)

    # 处理一下形状
    image_reshape = tf.reshape(image, [self.height, self.width, self.channel])

    # 改变数据类型
    image_tensor = tf.cast(image_reshape, tf.float32)

    # 标签数据处理
    label_tensor = tf.cast(feature["label"], tf.int32)

    # 批处理图片数据,训练数据每批次读取多少
    image_batch, label_batch = tf.train.batch([image_tensor, label_tensor], batch_size=10, num_threads=1, capacity=10)

    return image_batch, label_batch

我们将数据读取的代码放入cifar_data.py文件当中,当作我们的原始数据读取,完整代码如下

import tensorflow as tf
import os

"""用于获取Cifar TFRecords数据文件的程序"""

FLAGS = tf.app.flags.FLAGS

tf.app.flags.DEFINE_string("image_dir", "./cifar10.tfrecords","数据文件目录")


class CifarRead(object):
    def __init__(self, filelist=None):
        # 文件列表
        self.filelist = filelist

        # 每张图片大小数据初始化
        self.height = 32
        self.width = 32
        self.channel = 3
        self.label_bytes = 1
        self.image_bytes = self.height * self.width * self.channel
        self.bytes = self.image_bytes + self.label_bytes

    def read_decode(self):
        """
        读取数据并转换成张量
        :return: 图片数据,标签值
        """

        # 1、构造文件队列
        file_queue = tf.train.string_input_producer(self.filelist)

        # 2、构造二进制文件的阅读器,解码成张量
        reader = tf.FixedLengthRecordReader(self.bytes)

        key, value = reader.read(file_queue)

        # 解码成张量
        image_label = tf.decode_raw(value, tf.uint8)

        # 分割标签与数据
        label_tensor = tf.cast(tf.slice(image_label, [0], [self.label_bytes]), tf.int32)

        image = tf.slice(image_label, [self.label_bytes], [self.image_bytes])

        print(image)

        # 3、图片数据格式转换
        image_tensor = tf.reshape(image, [self.height, self.width, self.channel])

        # 4、图片数据批处理

        image_batch, label_batch = tf.train.batch([image_tensor, label_tensor], batch_size=5000, num_threads=1, capacity=50000)

        return image_batch, label_batch

    def write_to_tfrecords(self, image_batch, label_batch):
        """
        把读取出来的数据进行存储(tfrecords)
        :param image_batch: 图片RGB值
        :param label_batch: 图片标签
        :return:
        """

        # 1、构造存储器
        writer = tf.python_io.TFRecordWriter(FLAGS.image_dir)

        # 2、每张图片进行example协议化,存储
        for i in range(5000):
            print(i)

            # 图片的张量要转换成字符串才能写进去,否则大小格式不对
            image = image_batch[i].eval().tostring()

            # 标签值
            label = int(label_batch[i].eval())

            # 构造example协议快,存进去的名字是提供给取的时候使用
            example = tf.train.Example(features=tf.train.Features(feature={
                "image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
            }))

            writer.write(example.SerializeToString())

        return None

    def read_tfrecords(self):

        # 1、构建文件的队列
        file_queue = tf.train.string_input_producer([FLAGS.image_dir])

        # 2、构建tfrecords文件阅读器
        reader = tf.TFRecordReader()

        key, value = reader.read(file_queue)

        # 3、解析example协议块,返回字典数据,feature["image"],feature["label"]
        feature = tf.parse_single_example(value, features={
            "image": tf.FixedLenFeature([], tf.string),
            "label": tf.FixedLenFeature([], tf.int64)
        })

        # 4、解码图片数据,标签数据不用
        # 图片数据处理
        image = tf.decode_raw(feature["image"], tf.uint8)

        # 处理一下形状
        image_reshape = tf.reshape(image, [self.height, self.width, self.channel])

        # 改变数据类型
        image_tensor = tf.cast(image_reshape, tf.float32)

        # 标签数据处理
        label_tensor = tf.cast(feature["label"], tf.int32)

        # 批处理图片数据
        image_batch, label_batch = tf.train.batch([image_tensor, label_tensor], batch_size=10, num_threads=1, capacity=10)

        return image_batch, label_batch


if __name__ == "__main__":

    # 生成文件名列表(路径+文件名)
    filename = os.listdir("./cifar10/cifar-10-batches-bin")

    filelist = [os.path.join("./cifar10/cifar-10-batches-bin", file) for file in filename if file[-3:] == "bin"]

    # 实例化
    cfr = CifarRead(filelist)

    # 生成张量
    image_batch, label_batch = cfr.read_decode()

    # image_batch, label_batch = cfr.read_tfrecords()

    # 会话
    with tf.Session() as sess:
        coord = tf.train.Coordinator()

        threads = tf.train.start_queue_runners(sess=sess, coord=coord, start=True)

        print(sess.run([image_batch, label_batch]))

        # 写进tfrecords文件
        cfr.write_to_tfrecords(image_batch, label_batch)

        coord.request_stop()

        coord.join(threads)

模型接口建立

模型接口的建立

我们将模型接口都放在cifar_omdel.py文件当中,设计了四个函数,input()作为从cifar_data文件中数据的获取,inference()作为神经网络模型的建立,total_loss()计算模型的损失,train()来通过梯度下降训练减少损失

input代码

def input():
    """
    获取输入数据
    :return: image,label
    """

    # 实例化
    cfr = cifar_data.CifarRead()

    # 生成张量
    image_batch, lab_batch = cfr.read_tfrecords()

    # 将目标值转换为one-hot编码格式
    label = tf.one_hot(label_batch, depth=10, on_value=1.0)

    return image_batch, label, label_batch

inference代码

在这里使用的卷积神经网络模型与前面一致,需要修改图像的通道数以及经过两次卷积池化变换后的图像大小。

def inference(image_batch):
    """
    得到模型的输出
    :return: 预测概率输出以及占位符
    """
    # 1、数据占位符建立
    with tf.variable_scope("data"):
        # 样本标签值
        # y_label = tf.placeholder(tf.float32, [None, 10])

        # 样本特征值
        # x = tf.placeholder(tf.float32, [None, IMAGE_HEIGHT * IMAGE_WIDTH * IMAGE_DEPTH])

        # 改变形状,以提供给卷积层使用
        x_image = tf.reshape(image_batch, [-1, 32, 32, 3])

    # 2、卷积池化第一层
    with tf.variable_scope("conv1"):
        # 构建权重, 5*5, 3个输入通道,32个输出通道
        w_conv1 = weight_variable([5, 5, 3, 32])

        # 构建偏置, 个数位输出通道数
        b_conv1 = bias_variable([32])

        # 进行卷积,激活,指定滑动窗口,填充类型
        y_relu1 = tf.nn.relu(tf.nn.conv2d(x_image, w_conv1, strides=[1, 1, 1, 1], padding="SAME") + b_conv1)

        y_conv1 = tf.nn.max_pool(y_relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    # 3、卷积池化第二层
    with tf.variable_scope("conv_pool2"):
        # 构建权重, 5*5, 一个输入通道,32个输出通道
        w_conv2 = weight_variable([5, 5, 32, 64])

        # 构建偏置, 个数位输出通道数
        b_conv2 = bias_variable([64])

        # 进行卷积,激活,指定滑动窗口,填充类型
        y_relu2 = tf.nn.relu(tf.nn.conv2d(y_conv1, w_conv2, strides=[1, 1, 1, 1], padding="SAME") + b_conv2)

        y_conv2 = tf.nn.max_pool(y_relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    # 4、全连接第一层
    with tf.variable_scope("FC1"):
        # 构建权重,[7*7*64, 1024],根据前面的卷积池化后一步步计算的大小变换是32->16->8
        w_fc1 = weight_variable([8 * 8 * 64, 1024])

        # 构建偏置,个数位第一次全连接层输出个数
        b_fc1 = bias_variable([1024])

        y_reshape = tf.reshape(y_conv2, [-1, 8 * 8 * 64])

        # 全连接结果激活
        y_fc1 = tf.nn.relu(tf.matmul(y_reshape, w_fc1) + b_fc1)

    # 5、全连接第二层
    with tf.variable_scope("FC2"):

        # droupout层
        droup = tf.nn.dropout(y_fc1, 1.0)

        # 构建权重,[1024, 10]
        w_fc2 = weight_variable([1024, 10])

        # 构建偏置 [10]
        b_fc2 = bias_variable([10])

        # 最后的全连接层
        y_logit = tf.matmul(droup, w_fc2) + b_fc2

    return y_logit

total_loss代码

def total_loss(y_label, y_logit):
    """
    计算训练损失
    :param y_label: 目标值
    :param y_logit: 计算值
    :return: 损失
    """
    with tf.variable_scope("loss"):

        # softmax回归,以及计算交叉损失熵
        cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_label, logits=y_logit)

        # 计算损失平均值
        loss = tf.reduce_mean(cross_entropy)

    return loss

train代码

def train(loss, y_label, y_logit, global_step):
    """
    训练数据得出准确率
    :param loss: 损失大小
    :return:
    """
    with tf.variable_scope("train"):
        # 让学习率根据步伐,自动变换学习率,指定了每10步衰减基数为0.99,0.001为初始的学习率
        lr = tf.train.exponential_decay(0.001,
                                        global_step,
                                        10,
                                        0.99,
                                        staircase=True)

        # 优化器
        train_op = tf.train.GradientDescentOptimizer(lr).minimize(loss, global_step=global_step)

        # 计算准确率
        equal_list = tf.equal(tf.argmax(y_logit, 1), tf.argmax(y_label, 1))

        accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))

    return train_op, accuracy

完整代码


import tensorflow as tf
import os
import cifar_data
#
#
from tensorflow.examples.tutorials.mnist import input_data

IMAGE_HEIGHT = 32
IMAGE_WIDTH = 32
IMAGE_DEPTH = 3


# 按照指定形状构建权重变量
def weight_variable(shape):
    init = tf.truncated_normal(shape=shape, mean=0.0, stddev=1.0, dtype=tf.float32)
    weight = tf.Variable(init)
    return weight


# 按照制定形状构建偏置变量
def bias_variable(shape):
    bias = tf.constant([1.0], shape=shape)
    return tf.Variable(bias)


def inference(image_batch):
    """
    得到模型的输出
    :return: 预测概率输出以及占位符
    """
    # 1、数据占位符建立
    with tf.variable_scope("data"):
        # 样本标签值
        # y_label = tf.placeholder(tf.float32, [None, 10])

        # 样本特征值
        # x = tf.placeholder(tf.float32, [None, IMAGE_HEIGHT * IMAGE_WIDTH * IMAGE_DEPTH])

        # 改变形状,以提供给卷积层使用
        x_image = tf.reshape(image_batch, [-1, 32, 32, 3])

    # 2、卷积池化第一层
    with tf.variable_scope("conv1"):
        # 构建权重, 5*5, 3个输入通道,32个输出通道
        w_conv1 = weight_variable([5, 5, 3, 32])

        # 构建偏置, 个数位输出通道数
        b_conv1 = bias_variable([32])

        # 进行卷积,激活,指定滑动窗口,填充类型
        y_relu1 = tf.nn.relu(tf.nn.conv2d(x_image, w_conv1, strides=[1, 1, 1, 1], padding="SAME") + b_conv1)

        y_conv1 = tf.nn.max_pool(y_relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    # 3、卷积池化第二层
    with tf.variable_scope("conv_pool2"):
        # 构建权重, 5*5, 一个输入通道,32个输出通道
        w_conv2 = weight_variable([5, 5, 32, 64])

        # 构建偏置, 个数位输出通道数
        b_conv2 = bias_variable([64])

        # 进行卷积,激活,指定滑动窗口,填充类型
        y_relu2 = tf.nn.relu(tf.nn.conv2d(y_conv1, w_conv2, strides=[1, 1, 1, 1], padding="SAME") + b_conv2)

        y_conv2 = tf.nn.max_pool(y_relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    # 4、全连接第一层
    with tf.variable_scope("FC1"):
        # 构建权重,[7*7*64, 1024],根据前面的卷积池化后一步步计算的大小变换是32->16->8
        w_fc1 = weight_variable([8 * 8 * 64, 1024])

        # 构建偏置,个数位第一次全连接层输出个数
        b_fc1 = bias_variable([1024])

        y_reshape = tf.reshape(y_conv2, [-1, 8 * 8 * 64])

        # 全连接结果激活
        y_fc1 = tf.nn.relu(tf.matmul(y_reshape, w_fc1) + b_fc1)

    # 5、全连接第二层
    with tf.variable_scope("FC2"):

        # droupout层
        droup = tf.nn.dropout(y_fc1, 1.0)

        # 构建权重,[1024, 10]
        w_fc2 = weight_variable([1024, 10])

        # 构建偏置 [10]
        b_fc2 = bias_variable([10])

        # 最后的全连接层
        y_logit = tf.matmul(droup, w_fc2) + b_fc2

    return y_logit


def total_loss(y_label, y_logit):
    """
    计算训练损失
    :param y_label: 目标值
    :param y_logit: 计算值
    :return: 损失
    """
    with tf.variable_scope("loss"):
        # 将y_label转换为one-hot编码形式
        # y_onehot = tf.one_hot(y_label, depth=10, on_value=1.0)

        # softmax回归,以及计算交叉损失熵
        cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_label, logits=y_logit)

        # 计算损失平均值
        loss = tf.reduce_mean(cross_entropy)

    return loss


def train(loss, y_label, y_logit, global_step):
    """
    训练数据得出准确率
    :param loss: 损失大小
    :return:
    """
    with tf.variable_scope("train"):
        # 让学习率根据步伐,自动变换学习率,指定了每10步衰减基数为0.99,0.001为初始的学习率
        lr = tf.train.exponential_decay(0.001,
                                        global_step,
                                        10,
                                        0.99,
                                        staircase=True)

        # 优化器
        train_op = tf.train.GradientDescentOptimizer(lr).minimize(loss, global_step=global_step)

        # 计算准确率
        equal_list = tf.equal(tf.argmax(y_logit, 1), tf.argmax(y_label, 1))

        accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))

    return train_op, accuracy


def input():
    """
    获取输入数据
    :return: image,label
    """

    # 实例化
    cfr = cifar_data.CifarRead()

    # 生成张量
    image_batch, lab_batch = cfr.read_tfrecords()

    # 将目标值转换为one-hot编码格式
    label = tf.one_hot(label_batch, depth=10, on_value=1.0)

    return image_batch, label, label_batch

训练以及高级会话函数

主训练逻辑

我们将在cifar_train.py文件实现主要训练逻辑。在这里我们将使用一个新的会话函数,叫tf.train.MonitoredTrainingSession

优点: 1、它自动的建立events文件、checkpoint文件,以记录重要的信息。 2、可以定义钩子函数,可以自定义每批次的训练信息,训练的限制等等

注意:在这个里面我们需要添加一个全局步数,这个步数是每批次训练的时候进行+1计数,内部使用。

代码如下:

import tensorflow as tf
import cifar_model
import time
from datetime import datetime



def train():
    # 在图中进行训练
    with tf.Graph().as_default():
        # 定义全局步数,必须得使用这个,否则会出现StopCounterHook错误
        global_step = tf.contrib.framework.get_or_create_global_step()

        # 获取数据
        image, label, label_1 = cifar_model.input()

        # 通过模型进行类别预测
        y_logit = cifar_model.inference(image)

        # 计算损失
        loss = cifar_model.total_loss(label, y_logit)

        # 进行优化器减少损失
        train_op, accuracy = cifar_model.train(loss, label, y_logit, global_step)

        # 通过钩子定义模型输出
        class _LoggerHook(tf.train.SessionRunHook):
            """Logs loss and runtime."""
            def begin(self):
                self._step = -1
                self._start_time = time.time()

            def before_run(self, run_context):
                self._step += 1
                return tf.train.SessionRunArgs(loss, float(accuracy.eval()))  # Asks for loss value.

            def after_run(self, run_context, run_values):
                if self._step % 10 == 0:
                    current_time = time.time()
                    duration = current_time - self._start_time
                    self._start_time = current_time
                    loss_value = run_values.results
                    examples_per_sec = 10 * 10 / duration
                    sec_per_batch = float(duration / 10)

                    format_str = ('%s: step %d, loss = %.2f (%.1f examples/sec; %.3f '
                                  'sec/batch)')
                    print(format_str % (datetime.now(), self._step, loss_value,
                                        examples_per_sec, sec_per_batch))

        with tf.train.MonitoredTrainingSession(
                checkpoint_dir="./cifartrain/train",
                hooks=[tf.train.StopAtStepHook(last_step=500),# 定义执行的训练轮数也就是max_step,超过了就会报错
                       tf.train.NanTensorHook(loss),
                       _LoggerHook()],
                config=tf.ConfigProto(
                    log_device_placement=False)) as mon_sess:
            while not mon_sess.should_stop():
                mon_sess.run(train_op)


def main(argv):
    train()


if __name__ == "__main__":
    tf.app.run()

猜你喜欢

转载自blog.csdn.net/qq_35654080/article/details/88936404