TensorFlow 卷积神经网络 - 猫狗识别

最近在尝试学习使用卷积神经网络对猫狗进行识别,准备猫、狗图片各500张图片,图片是大小不一的彩色图片。

大致的实现思路:

  1. 各准备猫、狗500张图片,并对其命名“cat.XXX.jpg”、“dog.XXX.jpg”,计划20%猫、20%狗作为验证,剩下做训练;
  2. 图片大小归一化:使用OpenCV3把图片归一为64*64的彩色图片;
  3. 读取图片内容(numpy数组,batchszie*64*64*3)、标签值(0、1)、图片名(cat.XXX.jpg、dog.XXX.jpg)、类别名(cat、dog)
  4. 设置batchsize、权重、偏置、全连接特征数等参数;
  5. 图像输入(?,64,64,3)---->conv1(?,64,64,3)---->pool1(?,32,32,32)---->conv2(?,32,32,32)---->pool2(?,16,16,32)---->conv3(?,16,16,64)---->pool3(?,8,8,64),输出值转置成一维数组(?,4096)---->FC1(?,1024)---->FC(?,2)
  6. 保存训练好的模型,以便后续预测使用。

好了,如下是实现的代码,训练模型:

import dataset
import tensorflow as tf
import time
from datetime import timedelta
import math
import random
import numpy as np

# 确定好随机种子,以后每次随机结果相同。在每次执行代码时,使每次切分后的训练集、验证集输入结果相同,便于查找问题。
from numpy.random import seed

seed(10)
from tensorflow import set_random_seed

set_random_seed(20)

# 一次迭代32张图像
batch_size = 32

# 指定类别
classes = ['dogs', 'cats']
num_classes = len(classes)

# 判断神经网络是否出现过拟合。80%做训练,20%做测试
validation_size = 0.2
# 输入图像大小都要一样,图像:h=w=64
img_size = 64
# 彩色图,3通道
num_channels = 3
# 指定数据位置文件夹
train_path = 'training_data'

# 使用OpenCV加载所有的训练和验证图片、图片标签在内存中
data = dataset.read_train_sets(train_path, img_size, classes, validation_size=validation_size)

print("Complete reading input data. Will now print a snippet of it")
print("Number of files in Training-set:\t\t{}".format(len(data.train.labels)))
print("Number of files in Validation-set:\t{}".format(len(data.valid.labels)))

session = tf.Session()
x = tf.placeholder(tf.float32, shape=[None, img_size, img_size, num_channels], name='x')

y_true = tf.placeholder(tf.float32, shape=[None, num_classes], name='y_true')
y_true_cls = tf.argmax(y_true, axis=1)

# 网络参数
filter_size_conv1 = 3
num_filters_conv1 = 32

filter_size_conv2 = 3
num_filters_conv2 = 32

filter_size_conv3 = 3
num_filters_conv3 = 64

# 常用的参数:1024、4096、256、512
fc_layer_size = 1024


# 权重参数
def create_weights(shape):
    return tf.Variable(tf.truncated_normal(shape, stddev=0.05))


# 偏置参数
def create_biases(size):
    return tf.Variable(tf.constant(0.05, shape=[size]))


def create_convolutional_layer(input, num_input_channels, conv_filter_size, num_filters):
    # 3 3 3 32
    weights = create_weights(shape=[conv_filter_size, conv_filter_size, num_input_channels, num_filters])
    biases = create_biases(num_filters)

    # 卷积层
    layer = tf.nn.conv2d(input=input, filter=weights, strides=[1, 1, 1, 1], padding='SAME')

    layer += biases

    layer = tf.nn.relu(layer)

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

    return layer


def create_flatten_layer(layer):
    # layer形状:[? 8 8 64],?代表batchsize
    layer_shape = layer.get_shape()
    # layer_shape[1:4](可以取到1,不可以取到4)拿到后三维的元素个数(8*8*64)
    num_features = layer_shape[1:4].num_elements()

    # 把卷积得到的立方体特征值layer拉长,-1代表batchsize
    layer = tf.reshape(layer, [-1, num_features])

    return layer


def create_fc_layer(input, num_inputs,
                    num_outputs, use_relu=True):
    # 定义训练权重、偏置
    weights = create_weights(shape=[num_inputs, num_outputs])
    biases = create_biases(num_outputs)

    layer = tf.matmul(input, weights) + biases

    # 选择保留70%的点,防止过拟合
    layer = tf.nn.dropout(layer, keep_prob=0.7)

    if use_relu:
        layer = tf.nn.relu(layer)

    return layer


layer_conv1 = create_convolutional_layer(input=x, num_input_channels=num_channels,
                                         conv_filter_size=filter_size_conv1,
                                         num_filters=num_filters_conv1)
layer_conv2 = create_convolutional_layer(input=layer_conv1, num_input_channels=num_filters_conv1,
                                         conv_filter_size=filter_size_conv2,
                                         num_filters=num_filters_conv2)
layer_conv3 = create_convolutional_layer(input=layer_conv2, num_input_channels=num_filters_conv2,
                                         conv_filter_size=filter_size_conv3,
                                         num_filters=num_filters_conv3)

layer_flat = create_flatten_layer(layer_conv3)

layer_fc1 = create_fc_layer(input=layer_flat, num_inputs=layer_flat.get_shape()[1:4].num_elements(),
                            num_outputs=fc_layer_size, use_relu=True)

layer_fc2 = create_fc_layer(input=layer_fc1, num_inputs=fc_layer_size,
                            num_outputs=num_classes, use_relu=False)

y_pred = tf.nn.softmax(layer_fc2, name='y_pred')

y_pred_cls = tf.argmax(y_pred, axis=1)
session.run(tf.global_variables_initializer())
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=layer_fc2, labels=y_true)

cost = tf.reduce_mean(cross_entropy)
optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(cost)

correct_prediction = tf.equal(y_pred_cls, y_true_cls)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

session.run(tf.global_variables_initializer())


def show_progress(epoch, feed_dict_train, feed_dict_validate, val_loss, i):
    acc = session.run(accuracy, feed_dict=feed_dict_train)
    val_acc = session.run(accuracy, feed_dict=feed_dict_validate)
    msg = "Training Epoch {0}---- iterations: {1}---- Training Accuracy: {2:>6.1%}, Validation Accuracy: {3:>6.1%},  Validation Loss: {4:.3f}"
    print(msg.format(epoch + 1, i, acc, val_acc, val_loss))


total_iterations = 0

# 保存、读训练好的模型
saver = tf.train.Saver()


def train(num_iteration):
    global total_iterations

    for i in range(total_iterations, total_iterations + num_iteration):
        x_batch, y_true_batch, _, cls_batch = data.train.next_batch(batch_size)
        x_valid_batch, y_valid_batch, _, valid_cls_batch = data.valid.next_batch(batch_size)

        feed_dict_tr = {x: x_batch, y_true: y_true_batch}
        feed_dict_val = {x: x_valid_batch, y_true: y_valid_batch}

        session.run(optimizer, feed_dict=feed_dict_tr)

        # data.train.num_examples=800,每训练完一个Epoch,保存一次模型
        if i % int(data.train.num_examples / batch_size) == 0:
        # if i % 800 == 0:
            val_loss = session.run(cost, feed_dict=feed_dict_val)
            # 800张测试图片,每次喂32张,即喂800/32=25次完成一个Epoch,i=8000,所以模型一共运行了8000/25=320个Epoch
            epoch = int(i / int(data.train.num_examples / batch_size))

            show_progress(epoch, feed_dict_tr, feed_dict_val, val_loss, i)
            # 保存整个session,也可以保存其他值。“global_step=i”代表当前保存的是第几次模型、第几次迭代结果、
            # .meta结尾文件:存储整个网络的结构图;.index结尾文件:;.data结尾文件:存的权重参数
            saver.save(session, './dogs-cats-model/dog-cat.ckpt', global_step=i)

    total_iterations += num_iteration


train(num_iteration=8000)

处理照片所用的dataset类,类似于Java的JavaBean思想:

import cv2
import os
import glob
from sklearn.utils import shuffle
import numpy as np


def load_train(train_path, image_size, classes):
    images = []
    labels = []
    img_names = []
    cls = []

    print('Going to read training images')
    for fields in classes:
        index = classes.index(fields)
        print('Now going to read() files (Index:{})'.format(fields, index))
        path = os.path.join(train_path, fields, '*g')
        # 把所有的文件名称拿到手
        files = glob.glob(path)
        for fl in files:
            image = cv2.imread(fl)
            image = cv2.resize(image, (image_size, image_size), 0, 0, cv2.INTER_LINEAR)
            image = image.astype(np.float32)
            # 预处理:对当前矩阵做乘法,做归一化
            image = np.multiply(image, 1.0 / 255.0)
            images.append(image)
            label = np.zeros(len(classes))
            # 做标签
            label[index] = 1.0
            labels.append(label)
            flbase = os.path.basename(fl)
            img_names.append(flbase)
            cls.append(fields)
    images = np.array(images)
    labels = np.array(labels)
    img_names = np.array(img_names)
    # 图片对应类别
    cls = np.array(cls)

    return images, labels, img_names, cls


class DataSet(object):

    def __init__(self, images, labels, img_names, cls):
        self._num_examples = images.shape[0]

        self._images = images
        self._labels = labels
        self._img_names = img_names
        self._cls = cls
        self._epochs_done = 0
        self._index_in_epoch = 0

    @property
    def images(self):
        return self._images

    @property
    def labels(self):
        return self._labels

    @property
    def img_names(self):
        return self._img_names

    @property
    def cls(self):
        return self._cls

    @property
    def num_examples(self):
        return self._num_examples

    @property
    def epochs_done(self):
        return self._epochs_done

    def next_batch(self, batch_size):
        start = self._index_in_epoch
        self._index_in_epoch += batch_size

        # 防止大于图片总数最大值:500
        if self._index_in_epoch > self._num_examples:
            self._epochs_done += 1
            start = 0
            self._index_in_epoch = batch_size
            assert batch_size <= self._num_examples
        end = self._index_in_epoch

        # 传索引切片回去
        return self._images[start:end], self._labels[start:end], self._img_names[start:end], self._cls[start:end]


def read_train_sets(train_path, image_size, classes, validation_size):
    class DataSets(object):
        pass

    data_sets = DataSets()

    images, labels, img_names, cls = load_train(train_path, image_size, classes)
    # 打乱顺序,洗牌(变量对应关系不会打乱)
    images, labels, img_names, cls = shuffle(images, labels, img_names, cls)

    # 验证集
    if isinstance(validation_size, float):
        validation_size = int(validation_size * images.shape[0])

    # 200个验证集
    validation_images = images[:validation_size]
    validation_labels = labels[:validation_size]
    validation_img_names = img_names[:validation_size]
    validation_cls = cls[:validation_size]

    # 800训练集
    train_images = images[validation_size:]
    train_labels = labels[validation_size:]
    train_img_names = img_names[validation_size:]
    train_cls = cls[validation_size:]

    data_sets.train = DataSet(train_images, train_labels, train_img_names, train_cls)
    data_sets.valid = DataSet(validation_images, validation_labels, validation_img_names, validation_cls)

    return data_sets

加载上述训练好的模型对未知图片进行预测:

import tensorflow as tf
import numpy as np
import os,glob,cv2
import sys,argparse

# 训练、测试时对图像做的预处理应相同
image_size = 64
num_channels = 3
images = []

# 单张图片测试,也可以测试多张图片
path = 'dog.1015.jpg'
image = cv2.imread(path)
image = cv2.resize(image, (image_size, image_size), 0, 0, cv2.INTER_LINEAR)
images.append(image)
images = np.array(images, dtype=np.uint8)
images = images.astype('float32')
images = np.multiply(images, 1.0/255.0)

x_batch = images.reshape(1, image_size, image_size, num_channels)

# 加载已训练好的模型
sess = tf.Session()

# 第一步:导入网络结构图
saver = tf.train.import_meta_graph('./dogs-cats-model/dog-cat.ckpt-6825.meta')
# 第二步:加载权重参数W
saver.restore(sess, './dogs-cats-model/dog-cat.ckpt-6825')

# 加载图
graph = tf.get_default_graph()

#
y_pred = graph.get_tensor_by_name('y_pred:0')

#
x = graph.get_tensor_by_name('x:0')
y_true = graph.get_tensor_by_name('y_true:0')
y_test_images = np.zeros((1, 2))

feed_dict_testing = {x: x_batch, y_true: y_test_images}
result = sess.run(y_pred, feed_dict=feed_dict_testing)

res_label = ['dog', 'cat']
print(res_label[result.argmax()])

模型训练的部分执行结果,8000次训练完,正确率大概可以达到93%左右:

Training Epoch 25---- iterations: 600---- Training Accuracy:  71.9%, Validation Accuracy:  56.2%,  Validation Loss: 0.731
Training Epoch 26---- iterations: 625---- Training Accuracy:  65.6%, Validation Accuracy:  65.6%,  Validation Loss: 0.421
Training Epoch 27---- iterations: 650---- Training Accuracy:  81.2%, Validation Accuracy:  68.8%,  Validation Loss: 0.579
Training Epoch 28---- iterations: 675---- Training Accuracy:  75.0%, Validation Accuracy:  65.6%,  Validation Loss: 0.551
Training Epoch 29---- iterations: 700---- Training Accuracy:  71.9%, Validation Accuracy:  53.1%,  Validation Loss: 0.671
Training Epoch 30---- iterations: 725---- Training Accuracy:  78.1%, Validation Accuracy:  75.0%,  Validation Loss: 0.581
Training Epoch 31---- iterations: 750---- Training Accuracy:  65.6%, Validation Accuracy:  59.4%,  Validation Loss: 0.777
Training Epoch 32---- iterations: 775---- Training Accuracy:  71.9%, Validation Accuracy:  75.0%,  Validation Loss: 0.448
Training Epoch 33---- iterations: 800---- Training Accuracy:  81.2%, Validation Accuracy:  75.0%,  Validation Loss: 0.584
Training Epoch 34---- iterations: 825---- Training Accuracy:  84.4%, Validation Accuracy:  71.9%,  Validation Loss: 0.541
Training Epoch 35---- iterations: 850---- Training Accuracy:  65.6%, Validation Accuracy:  50.0%,  Validation Loss: 0.775
Training Epoch 36---- iterations: 875---- Training Accuracy:  75.0%, Validation Accuracy:  78.1%,  Validation Loss: 0.454
Training Epoch 37---- iterations: 900---- Training Accuracy:  84.4%, Validation Accuracy:  59.4%,  Validation Loss: 0.727

预测程序执行结果:

dog

Process finished with exit code 0

参数设置不同,训练的结果也不同。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值