[tensorflow]圖片新類別再訓練-花分類-代碼整理

目錄

一、新類別模型的再訓練

1、圖片加載,並將數據集劃分爲訓練集、測試集、驗證集,比例分別爲80%,10%,10%(默認)

2、加載hub某個模型,拉取模型信息,創建圖

3、計算所有圖片的bottlenecks(特徵向量),並緩存

4、新類別模型訓練

5、新類別預測模型保存

二、模型預測

1、預測模型加載

2、加載預測圖片(圖片進行解碼和剪裁),預測圖片類別


原網址:https://www.tensorflow.org/hub/tutorials/image_retraining

一、新類別模型的再訓練

預定義-第三方包

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import collections
from datetime import datetime
import hashlib
import os.path
import random
import re
import sys

import numpy as np
import tensorflow as tf
import tensorflow_hub as hub

tf.logging.set_verbosity(tf.logging.INFO)

預定義--文件路徑 

FLAGS.image_dir = 'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower_photos'
'''
Path to folders of labeled images.
'''

FLAGS.output_graph = 'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\\graph\\output_graph.pb'
'''
Where to save the trained graph.
'''

FLAGS.intermediate_output_graphs_dir = 'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\\intermediate_graph\\'
'''
Where to save the intermediate graphs.
'''

FLAGS.intermediate_store_frequency = 0
"""\
     How many steps to store intermediate graph. If "0" then will not
     store.\
  """
FLAGS.output_labels = 'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\\labels\\output_labels.txt'
'''
Where to save the trained graph\'s labels.
'''

FLAGS.summaries_dir = 'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\\retrain_logs'
'''
Where to save summary logs for TensorBoard.
'''
FLAGS.how_many_training_steps = 4000
'''How many training steps to run before ending.'''

FLAGS.learning_rate = t=0.01
'''How large a learning rate to use when training.'''

FLAGS.testing_percentage = 10
'''What percentage of images to use as a test set.'''

FLAGS.validation_percentage = 10
'''What percentage of images to use as a validation set.'''

FLAGS.eval_step_interval = 10
'''How often to evaluate the training results.'''

FLAGS.train_batch_size = 100
'''How many images to train on at a time.'''

FLAGS.test_batch_size = -1
"""\
  How many images to test on. This test set is only used once, to evaluate
  the final accuracy of the model after training completes.
  A value of -1 causes the entire test set to be used, which leads to more
  stable results across runs.\
  """

FLAGS.validation_batch_size = 100
"""\
  How many images to use in an evaluation batch. This validation set is
  used much more often than the test set, and is an early indicator of how
  accurate the model is during training.
  A value of -1 causes the entire validation set to be used, which leads to
  more stable results across training iterations, but may be slower on large
  training sets.\
  """

FLAGS.print_misclassified_test_images = False
"""\
  Whether to print out a list of all misclassified test images.\
  """

FLAGS.bottleneck_dir = 'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\\bottleneck'
'Path to cache bottleneck layer values as files.'

FLAGS.final_tensor_name = 'final_result'
"""\
  The name of the output classification layer in the retrained graph.\
  """

FLAGS.flip_left_right = False
"""\
  Whether to randomly flip half of the training images horizontally.\
  """

FLAGS.random_crop = 0
"""\
  A percentage determining how much of a margin to randomly crop off the
  training images.\
  """

FLAGS.random_scale = 0
"""\
  A percentage determining how much to randomly scale up the size of the
  training images by.\
  """

FLAGS.random_brightness = 0
"""\
  A percentage determining how much to randomly multiply the training image
  input pixels up or down by.\
  """

FLAGS.tfhub_module = 'https://tfhub.dev/google/imagenet/inception_v3/feature_vector/1'
""" Which TensorFlow Hub module to use. For more options, search https://tfhub.dev for image feature vector modules. """

FLAGS.saved_model_dir = 'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\\exportedGraph'
""" Where to save the exported graph."""

預定義--全局變量

# The location where variable checkpoints will be stored.
CHECKPOINT_NAME = 'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\\retrain_checkpoint\\'

# A module is understood as instrumented for quantization with TF-Lite
# if it contains any of these ops.
FAKE_QUANT_OPS = ('FakeQuantWithMinMaxVars','FakeQuantWithMinMaxVarsPerChannel')

1、圖片加載,並將數據集劃分爲訓練集、測試集、驗證集,比例分別爲80%,10%,10%(默認)

def create_image_lists(image_dir, testing_percentage, validation_percentage):

    """Builds a list of training images from the file system.

    Analyzes the sub folders in the image directory, splits them into stable
    training, testing, and validation sets, and returns a data structure
    describing the lists of images for each label and their paths.

    Args:
    image_dir: String path to a folder containing subfolders of images.
    testing_percentage: Integer percentage of the images to reserve for tests.
    validation_percentage: Integer percentage of images reserved for validation.

    Returns:
    An OrderedDict containing an entry for each label subfolder, with images
    split into training, testing, and validation sets within each label.
    The order of items defines the class indices.
    """

    if not tf.gfile.Exists(image_dir):
        tf.logging.error("Image directory '" + image_dir + "' not found.")
        return None

    result = collections.OrderedDict()
    sub_dirs = sorted(x[0] for x in tf.gfile.Walk(image_dir))
    # The root directory comes first, so skip it.
    is_root_dir = True
    for sub_dir in sub_dirs:
        if is_root_dir:
          is_root_dir = False
          continue
            
        extensions = sorted(set(os.path.normcase(ext)  for ext in ['JPEG', 'JPG', 'jpeg', 'jpg', 'png']))
        file_list = []
        dir_name = os.path.basename(sub_dir)
        if dir_name == image_dir:
            continue
        #tf.logging.info("Looking for images in '" + dir_name + "'")
        for extension in extensions:
            file_glob = os.path.join(image_dir, dir_name, '*.' + extension)
            file_list.extend(tf.gfile.Glob(file_glob))
        if not file_list:
            tf.logging.warning('No files found')
            continue
        if len(file_list) < 20:
            tf.logging.warning( 'WARNING: Folder has less than 20 images, which may cause issues.')
        elif len(file_list) > MAX_NUM_IMAGES_PER_CLASS:
            tf.logging.warning('WARNING: Folder {} has more than {} images. Some images will never be selected.'.format(dir_name, MAX_NUM_IMAGES_PER_CLASS))
        label_name = re.sub(r'[^a-z0-9]+', ' ', dir_name.lower())
        
        training_images = []
        testing_images = []
        validation_images = []
        for file_name in file_list:
            base_name = os.path.basename(file_name)
            hash_name = re.sub(r'_nohash_.*$', '', file_name)
            hash_name_hashed = hashlib.sha1(tf.compat.as_bytes(hash_name)).hexdigest()
            percentage_hash = ((int(hash_name_hashed, 16) %(MAX_NUM_IMAGES_PER_CLASS + 1)) *(100.0 / MAX_NUM_IMAGES_PER_CLASS))
            if percentage_hash < validation_percentage:
                validation_images.append(base_name)
            elif percentage_hash < (testing_percentage + validation_percentage):
                testing_images.append(base_name)
            else:
                training_images.append(base_name)
                
        result[label_name] = {
            'dir': dir_name,
            'training': training_images,
            'testing': testing_images,
            'validation': validation_images,
        }
    return result
def main(_):
    #獲取商品圖片
    image_lists = create_image_lists(FLAGS.image_dir, FLAGS.testing_percentage, FLAGS.validation_percentage)
    class_count = len(image_lists.keys())

image_lists數據格式爲:

([('daisy',
       {'dir': 'daisy',
        'testing':['100080576_f52e8ee070_n.jpg','10172379554_b296050f82_n.jpg',...],
        'training':['10140303196_b88d3d6cec.jpg',...],
        'validation':['102841525_bd6628ae3c.jpg',]}),
  ('dandelion',
        {'dir': 'dandelion',
         'testing': ['10294487385_92a0676c7d_m.jpg',...],
         'training':['10140303196_b88d3d6cec.jpg',...],
         'validation':['102841525_bd6628ae3c.jpg',]}),

2、加載hub某個模型,拉取模型信息,創建圖

#加載tensorflow中的某個模型,並拉取模型信息
def create_module_graph(module_spec):
    """Creates a graph and loads Hub Module into it.

    Args:
    module_spec: the hub.ModuleSpec for the image module being used.

    Returns:
    graph: the tf.Graph that was created.
    bottleneck_tensor: the bottleneck values output by the module.
    resized_input_tensor: the input images, resized as expected by the module.
    wants_quantization: a boolean, whether the module has been instrumented
      with fake quantization ops.
    """
    height, width = hub.get_expected_image_size(module_spec)
    with tf.Graph().as_default() as graph:
        resized_input_tensor = tf.placeholder(tf.float32, [None, height, width, 3])
        m = hub.Module(module_spec)
        bottleneck_tensor = m(resized_input_tensor)
        wants_quantization = any(node.op in FAKE_QUANT_OPS for node in graph.as_graph_def().node)
        
    return graph, bottleneck_tensor, resized_input_tensor, wants_quantization



#對圖片進行解碼和調整大小
def add_jpeg_decoding(module_spec):
    """Adds operations that perform JPEG decoding and resizing to the graph..

    Args:
    module_spec: The hub.ModuleSpec for the image module being used.

    Returns:
    Tensors for the node to feed JPEG data into, and the output of the
      preprocessing steps.
    """
    input_height, input_width = hub.get_expected_image_size(module_spec)
    input_depth = hub.get_num_image_channels(module_spec)
    jpeg_data = tf.placeholder(tf.string, name='DecodeJPGInput')
    
    decoded_image = tf.image.decode_jpeg(jpeg_data, channels=input_depth)
    # Convert from full range of uint8 to range [0,1] of float32.
    decoded_image_as_float = tf.image.convert_image_dtype(decoded_image,tf.float32)
    decoded_image_4d = tf.expand_dims(decoded_image_as_float, 0)
    
    resize_shape = tf.stack([input_height, input_width])
    resize_shape_as_int = tf.cast(resize_shape, dtype=tf.int32)
    resized_image = tf.image.resize_bilinear(decoded_image_4d,resize_shape_as_int)
    
    return jpeg_data, resized_image
def mian(_):
    #加載hub某個模型,並拉取模型信息,創建圖
    module_spec = hub.load_module_spec(FLAGS.tfhub_module)
    graph, bottleneck_tensor, resized_image_tensor, wants_quantization = (create_module_graph(module_spec))

    with tf.Session(graph=graph) as sess:
        # Initialize all weights: for the module to their pretrained values, and for the newly added retraining layer to random initial values.
        init = tf.global_variables_initializer()
        sess.run(init)

        # Set up the image decoding sub-graph.
        jpeg_data_tensor, decoded_image_tensor = add_jpeg_decoding(module_spec)

拉取的模型信息爲:

graph, bottleneck_tensor, resized_image_tensor, wants_quantization:
(<tensorflow.python.framework.ops.Graph at 0x190481d0>,
 <tf.Tensor 'module_apply_default/hub_output/feature_vector/SpatialSqueeze:0' shape=(?, 2048) dtype=float32>,
 <tf.Tensor 'Placeholder:0' shape=(?, 299, 299, 3) dtype=float32>,
 False)

jpeg_data_tensor, decoded_image_tensor:
(<tf.Tensor 'DecodeJPGInput:0' shape=<unknown> dtype=string>,
 <tf.Tensor 'ResizeBilinear:0' shape=(1, 299, 299, 3) dtype=float32>)

3、計算所有圖片的bottlenecks(特徵向量),並緩存

獲取路徑:

def ensure_dir_exists(dir_name):
    #創建文件
    if not os.path.exists(dir_name):
        os.makedirs(dir_name)
        
def get_image_path(image_lists, label_name, index, image_dir, category):
    #獲取一張圖片的路徑
    if label_name not in image_lists:
        tf.logging.fatal('Label does not exist %s.', label_name)
    label_lists = image_lists[label_name]
    if category not in label_lists:
        tf.logging.fatal('Category does not exist %s.', category)
    category_list = label_lists[category]
    if not category_list:
        tf.logging.fatal('Label %s has no images in the category %s.',label_name, category)
    mod_index = index % len(category_list)
    base_name = category_list[mod_index]
    sub_dir = label_lists['dir']
    full_path = os.path.join(image_dir, sub_dir, base_name)
    return full_path        

def get_bottleneck_path(image_lists, label_name, index, bottleneck_dir, category, module_name):
    #獲取一張圖片bottlenecks保存路徑
    module_name = (module_name.replace('://', '~')  # URL scheme.
                 .replace('/', '~')  # URL and Unix paths.
                 .replace(':', '~').replace('\\', '~'))  # Windows paths.
    return get_image_path(image_lists, label_name, index, bottleneck_dir, category) + '_' + module_name + '.txt'

計算一張圖片的bottlenecks,並保存到文件:

#計算一張圖片的bottlenecks,2048維向量
def run_bottleneck_on_image(sess, image_data, image_data_tensor,decoded_image_tensor, resized_input_tensor,bottleneck_tensor):
    # First decode the JPEG image, resize it, and rescale the pixel values.
    resized_input_values = sess.run(decoded_image_tensor, {image_data_tensor: image_data})
    # Then run it through the recognition network.
    bottleneck_values = sess.run(bottleneck_tensor, {resized_input_tensor: resized_input_values})
    bottleneck_values = np.squeeze(bottleneck_values)
    return bottleneck_values

#獲取一張圖片的bottlenecks,並寫入文件
def create_bottleneck_file(bottleneck_path, image_lists, label_name, index, image_dir, category, 
                           sess, jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor):
    """Create a single bottleneck file."""
    tf.logging.info('Creating bottleneck at ' + bottleneck_path)
    image_path = get_image_path(image_lists, label_name, index, image_dir, category)
    if not tf.gfile.Exists(image_path):
        tf.logging.fatal('File does not exist %s', image_path)
    image_data = tf.gfile.FastGFile(image_path, 'rb').read()
    
    try:
        bottleneck_values = run_bottleneck_on_image(sess, image_data, jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor)
    except Exception as e:
        raise RuntimeError('Error during processing file %s (%s)' % (image_path,
                                                                 str(e)))
    bottleneck_string = ','.join(str(x) for x in bottleneck_values)
    with open(bottleneck_path, 'w') as bottleneck_file:
        bottleneck_file.write(bottleneck_string)

緩存所有圖片的bottlenecks(特徵向量):

#讀取一張圖片的bottlenecks
def get_or_create_bottleneck(sess, image_lists, label_name, index, image_dir,category, bottleneck_dir, 
                             jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor, module_name):
    """Retrieves or calculates bottleneck values for an image.

    If a cached version of the bottleneck data exists on-disk, return that, otherwise calculate the data and save it to disk for future use.

    Args:
    sess: The current active TensorFlow Session.
    image_lists: OrderedDict of training images for each label.
    label_name: Label string we want to get an image for.
    index: Integer offset of the image we want. This will be modulo-ed by the available number of images for the label, so it can be arbitrarily large.
    image_dir: Root folder string of the subfolders containing the training images.
    category: Name string of which set to pull images from - training, testing, or validation.
    bottleneck_dir: Folder string holding cached files of bottleneck values.
    jpeg_data_tensor: The tensor to feed loaded jpeg data into.
    decoded_image_tensor: The output of decoding and resizing the image.
    resized_input_tensor: The input node of the recognition graph.
    bottleneck_tensor: The output tensor for the bottleneck values.
    module_name: The name of the image module being used.

    Returns:
    Numpy array of values produced by the bottleneck layer for the image.
    """
    label_lists = image_lists[label_name]
    sub_dir = label_lists['dir']
    sub_dir_path = os.path.join(bottleneck_dir, sub_dir)
    ensure_dir_exists(sub_dir_path)
    bottleneck_path = get_bottleneck_path(image_lists, label_name, index, bottleneck_dir, category, module_name) #獲取圖片bottleneck 路徑
    
    if not os.path.exists(bottleneck_path):
        create_bottleneck_file(bottleneck_path, image_lists, label_name, index, image_dir, category, 
                               sess, jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor) #圖片bottleneck寫入文件
        
    with open(bottleneck_path, 'r') as bottleneck_file: #讀取圖片bottleneck
        bottleneck_string = bottleneck_file.read()
    did_hit_error = False
    try:
        bottleneck_values = [float(x) for x in bottleneck_string.split(',')]
    except ValueError:
        tf.logging.warning('Invalid float found, recreating bottleneck')
        did_hit_error = True
        
    if did_hit_error: #文件存在,但讀取失敗,重新計算,在讀取
        create_bottleneck_file(bottleneck_path, image_lists, label_name, index, image_dir, category, 
                               sess, jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor)
        with open(bottleneck_path, 'r') as bottleneck_file:
            bottleneck_string = bottleneck_file.read()
        bottleneck_values = [float(x) for x in bottleneck_string.split(',')]
    return bottleneck_values



#緩存所有圖片的bottlenecks(特徵向量)
def cache_bottlenecks(sess, image_lists, image_dir, bottleneck_dir,
                      jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor, module_name):
    """Ensures all the training, testing, and validation bottlenecks are cached.

    Because we're likely to read the same image multiple times (if there are no
    distortions applied during training) it can speed things up a lot if we
    calculate the bottleneck layer values once for each image during
    preprocessing, and then just read those cached values repeatedly during
    training. Here we go through all the images we've found, calculate those
    values, and save them off.

    Args:
    sess: The current active TensorFlow Session.
    image_lists: OrderedDict of training images for each label.
    image_dir: Root folder string of the subfolders containing the training
    images.
    bottleneck_dir: Folder string holding cached files of bottleneck values.
    jpeg_data_tensor: Input tensor for jpeg data from file.
    decoded_image_tensor: The output of decoding and resizing the image.
    resized_input_tensor: The input node of the recognition graph.
    bottleneck_tensor: The penultimate output layer of the graph.
    module_name: The name of the image module being used.

    Returns:
    Nothing.
    """
    how_many_bottlenecks = 0
    #ensure_dir_exists(bottleneck_dir)
    if not os.path.exists(dir_name):
        os.makedirs(dir_name)
    
    for label_name, label_lists in image_lists.items():
        for category in ['training', 'testing', 'validation']:
            category_list = label_lists[category]
            for index, unused_base_name in enumerate(category_list):
                get_or_create_bottleneck(sess, image_lists, label_name, index, image_dir, category,bottleneck_dir,
                     jpeg_data_tensor, decoded_image_tensor,resized_input_tensor, bottleneck_tensor, module_name)
                
                how_many_bottlenecks += 1
                if how_many_bottlenecks % 100 == 0:
                    tf.logging.info(str(how_many_bottlenecks) + ' bottleneck files created.')

main函數測試:

if __name__=main(_):
    #獲取商品圖片
    image_lists = create_image_lists(FLAGS.image_dir, FLAGS.testing_percentage,                 FLAGS.validation_percentage)
    class_count = len(image_lists.keys())

    #加載hub某個模型,並拉取模型信息,創建圖
    module_spec = hub.load_module_spec(FLAGS.tfhub_module)
    graph, bottleneck_tensor, resized_image_tensor, wants_quantization = (create_module_graph(module_spec))
    with tf.Session(graph=graph) as sess:
        # Initialize all weights: for the module to their pretrained values, and for the newly added retraining layer to random initial values.
        init = tf.global_variables_initializer()
        sess.run(init)
        # Set up the image decoding sub-graph.
        jpeg_data_tensor, decoded_image_tensor = add_jpeg_decoding(module_spec)

        #緩存所有圖片的bottlenecks(特徵向量)
        cache_bottlenecks(sess, image_lists, FLAGS.image_dir,FLAGS.bottleneck_dir, 
                      jpeg_data_tensor, decoded_image_tensor, resized_image_tensor, bottleneck_tensor, FLAGS.tfhub_module)

圖片bottlenecks(特徵向量)數據格式(2048維向量):

0.063915215,0.7282615,0.30556193,0.42560956,0.2697923,0.06698525,0.88659924,0.32001853,0.025883203,0.08164722,0.33431992,....

4、新類別模型訓練

預定義-隨機獲取一批bottleneck文件數據,用於隨機梯度下降:

#隨機獲取一批bottleneck文件數據,用於隨機梯度下降
def get_random_cached_bottlenecks(sess, image_lists, how_many, category,bottleneck_dir, image_dir,
                                  jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor, module_name):
    class_count = len(image_lists.keys())
    bottlenecks = []
    ground_truths = []
    filenames = []
    if how_many >= 0:
        # Retrieve a random sample of bottlenecks.
        for unused_i in range(how_many):
            label_index = random.randrange(class_count)
            label_name = list(image_lists.keys())[label_index]
            image_index = random.randrange(MAX_NUM_IMAGES_PER_CLASS + 1)
            image_name = get_image_path(image_lists, label_name, image_index, image_dir, category)
            bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, image_index, image_dir, category,bottleneck_dir, 
                                                  jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor, module_name)
            bottlenecks.append(bottleneck)
            ground_truths.append(label_index)
            filenames.append(image_name)
    else:
        # Retrieve all bottlenecks.
        for label_index, label_name in enumerate(image_lists.keys()):
            for image_index, image_name in enumerate(image_lists[label_name][category]):
                image_name = get_image_path(image_lists, label_name, image_index,image_dir, category)
                bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, image_index, image_dir, category,bottleneck_dir, 
                                            jpeg_data_tensor, decoded_image_tensor, resized_input_tensor, bottleneck_tensor, module_name)
                bottlenecks.append(bottleneck)
                ground_truths.append(label_index)
                filenames.append(image_name)
    return bottlenecks, ground_truths, filenames


#tensorflow 可視化
def variable_summaries(var):
    """Attach a lot of summaries to a Tensor (for TensorBoard visualization)."""
    with tf.name_scope('summaries'):
        mean = tf.reduce_mean(var)
        tf.summary.scalar('mean', mean)
        with tf.name_scope('stddev'):
            stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
        tf.summary.scalar('stddev', stddev)
        tf.summary.scalar('max', tf.reduce_max(var))
        tf.summary.scalar('min', tf.reduce_min(var))
        tf.summary.histogram('histogram', var)

輸出結果爲:

#bottlenecks-選取圖片的2048維向量
[[0.034603953, 0.35557234,...],[0.9271929, 0.24392605,..],...]
#ground_truth--選取圖片的類別
[3, 2, 3,....]
#filenames-圖片的路徑
 ['E:\\DataMining\\tensorflow\\google-ImageClassification\\flower_photos\\sunflowers\\4933229357_1c5cc03f65_m.jpg',...]

增加新softmax函數,構建全連接層:

#增加新softmax函數,構建全連接層
def add_final_retrain_ops(class_count, final_tensor_name, bottleneck_tensor, quantize_layer, is_training):
    """Adds a new softmax and fully-connected layer for training and eval.

    We need to retrain the top layer to identify our new classes, so this function
    adds the right operations to the graph, along with some variables to hold the
    weights, and then sets up all the gradients for the backward pass.

    The set up for the softmax and fully-connected layers is based on:
    https://www.tensorflow.org/tutorials/mnist/beginners/index.html

    Args:
    class_count: Integer of how many categories of things we're trying to
        recognize.
    final_tensor_name: Name string for the new final node that produces results.
    bottleneck_tensor: The output of the main CNN graph.
    quantize_layer: Boolean, specifying whether the newly added layer should be
        instrumented for quantization with TF-Lite.
    is_training: Boolean, specifying whether the newly add layer is for training
        or eval.

    Returns:
    The tensors for the training and cross entropy results, and tensors for the
    bottleneck input and ground truth input.
    """
    batch_size, bottleneck_tensor_size = bottleneck_tensor.get_shape().as_list()
    assert batch_size is None, 'We want to work with arbitrary batch size.'
    with tf.name_scope('input'):
        bottleneck_input = tf.placeholder_with_default(bottleneck_tensor,shape=[batch_size, bottleneck_tensor_size],name='BottleneckInputPlaceholder')
        ground_truth_input = tf.placeholder(tf.int64, [batch_size], name='GroundTruthInput')

    # Organizing the following ops so they are easier to see in TensorBoard.
    layer_name = 'final_retrain_ops'
    with tf.name_scope(layer_name):
        with tf.name_scope('weights'):
            initial_value = tf.truncated_normal([bottleneck_tensor_size, class_count], stddev=0.001)
            layer_weights = tf.Variable(initial_value, name='final_weights')
            variable_summaries(layer_weights)

        with tf.name_scope('biases'):
            layer_biases = tf.Variable(tf.zeros([class_count]), name='final_biases')
            variable_summaries(layer_biases)

        with tf.name_scope('Wx_plus_b'):
            logits = tf.matmul(bottleneck_input, layer_weights) + layer_biases
            tf.summary.histogram('pre_activations', logits)

    final_tensor = tf.nn.softmax(logits, name=final_tensor_name)

    # The tf.contrib.quantize functions rewrite the graph in place for
    # quantization. The imported model graph has already been rewritten, so upon
    # calling these rewrites, only the newly added final layer will be
    # transformed.
    if quantize_layer:
        if is_training:
            tf.contrib.quantize.create_training_graph()
        else:
            tf.contrib.quantize.create_eval_graph()

    tf.summary.histogram('activations', final_tensor)

  # If this is an eval graph, we don't need to add loss ops or an optimizer.
    if not is_training:
        return None, None, bottleneck_input, ground_truth_input, final_tensor

    with tf.name_scope('cross_entropy'):
        cross_entropy_mean = tf.losses.sparse_softmax_cross_entropy(labels=ground_truth_input, logits=logits)

    tf.summary.scalar('cross_entropy', cross_entropy_mean)

    with tf.name_scope('train'):
        optimizer = tf.train.GradientDescentOptimizer(FLAGS.learning_rate)
        train_step = optimizer.minimize(cross_entropy_mean)

    return (train_step, cross_entropy_mean, bottleneck_input, ground_truth_input, final_tensor)

輸出數據結果爲:

#train_step
name: "train/GradientDescent"
op: "NoOp"
input: "^train/GradientDescent/update_final_retrain_ops/weights/final_weights/ApplyGradientDescent"
input: "^train/GradientDescent/update_final_retrain_ops/biases/final_biases/ApplyGradientDescent"
#cross_entropy:  
Tensor("cross_entropy/sparse_softmax_cross_entropy_loss/value:0", shape=(), dtype=float32)
#bottleneck_input 
Tensor("input/BottleneckInputPlaceholder:0", shape=(?, 2048), dtype=float32)
#ground_truth_input:  
Tensor("input/GroundTruthInput:0", shape=(?,), dtype=int64)
#final_tensor:  
Tensor("final_result:0", shape=(?, 5), dtype=float32)

對訓練結果的評估:

#增加 訓練結果的評估
def add_evaluation_step(result_tensor, ground_truth_tensor):
    """Inserts the operations we need to evaluate the accuracy of our results.

    Args:
    result_tensor: The new final node that produces results.
    ground_truth_tensor: The node we feed ground truth data
    into.

    Returns:
    Tuple of (evaluation step, prediction).
    """
    with tf.name_scope('accuracy'):
        with tf.name_scope('correct_prediction'):
            prediction = tf.argmax(result_tensor, 1)
            correct_prediction = tf.equal(prediction, ground_truth_tensor)
        with tf.name_scope('accuracy'):
            evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
            
    tf.summary.scalar('accuracy', evaluation_step)
    return evaluation_step, prediction

輸出結果爲:

#evaluation_step:  
Tensor("accuracy/accuracy/Mean:0", shape=(), dtype=float32)
#predictions:  
Tensor("accuracy/correct_prediction/ArgMax:0", shape=(?,), dtype=int64)

用測試集對模型評估:

#用測試集對模型評估

#創建用於評估的會話
def build_eval_session(module_spec, class_count):
    # If quantized, we need to create the correct eval graph for exporting.
    eval_graph, bottleneck_tensor, resized_input_tensor, wants_quantization = (create_module_graph(module_spec))

    eval_sess = tf.Session(graph=eval_graph)
    with eval_graph.as_default():
        # Add the new layer for exporting.
        (_, _, bottleneck_input,ground_truth_input, final_tensor) = add_final_retrain_ops(class_count, FLAGS.final_tensor_name, bottleneck_tensor, wants_quantization, is_training=False)

        # Now we need to restore the values from the training graph to the evalgraph.
        tf.train.Saver().restore(eval_sess, CHECKPOINT_NAME)

        evaluation_step, prediction = add_evaluation_step(final_tensor,ground_truth_input)

    return (eval_sess, resized_input_tensor, bottleneck_input, ground_truth_input,evaluation_step, prediction)

#對測試集進行評估
def run_final_eval(train_session, module_spec, class_count, image_lists,
                   jpeg_data_tensor, decoded_image_tensor, resized_image_tensor, bottleneck_tensor):
    """Runs a final evaluation on an eval graph using the test data set.

    Args:
    train_session: Session for the train graph with the tensors below.
    module_spec: The hub.ModuleSpec for the image module being used.
    class_count: Number of classes
    image_lists: OrderedDict of training images for each label.
    jpeg_data_tensor: The layer to feed jpeg image data into.
    decoded_image_tensor: The output of decoding and resizing the image.
    resized_image_tensor: The input node of the recognition graph.
    bottleneck_tensor: The bottleneck output layer of the CNN graph.
    """
    test_bottlenecks, test_ground_truth, test_filenames = (get_random_cached_bottlenecks(train_session, image_lists,FLAGS.test_batch_size, 'testing', FLAGS.bottleneck_dir,FLAGS.image_dir, 
                                                   jpeg_data_tensor,decoded_image_tensor, resized_image_tensor,bottleneck_tensor, FLAGS.tfhub_module))

    (eval_session, _, bottleneck_input, ground_truth_input, evaluation_step,prediction) = build_eval_session(module_spec, class_count)
    test_accuracy, predictions = eval_session.run([evaluation_step, prediction],
                                        feed_dict={bottleneck_input: test_bottlenecks,ground_truth_input: test_ground_truth})
    tf.logging.info('Final test accuracy = %.1f%% (N=%d)' %(test_accuracy * 100, len(test_bottlenecks)))

訓練新類別預測模型:

#獲取商品圖片
image_lists = create_image_lists(FLAGS.image_dir, FLAGS.testing_percentage, FLAGS.validation_percentage)
class_count = len(image_lists.keys())

#加載hub某個模型,並拉取模型信息,創建圖
module_spec = hub.load_module_spec(FLAGS.tfhub_module)
graph, bottleneck_tensor, resized_image_tensor, wants_quantization = (create_module_graph(module_spec))

#增加新softmax函數,構建全連接層
with graph.as_default():
    (train_step, cross_entropy, bottleneck_input,ground_truth_input, final_tensor) = add_final_retrain_ops(class_count, FLAGS.final_tensor_name, bottleneck_tensor, wants_quantization, is_training=True)

with tf.Session(graph=graph) as sess:
    # Initialize all weights: for the module to their pretrained values,
    # and for the newly added retraining layer to random initial values.
    init = tf.global_variables_initializer()
    sess.run(init)
    # Set up the image decoding sub-graph.
    jpeg_data_tensor, decoded_image_tensor = add_jpeg_decoding(module_spec)
    
    #緩存所有圖片的bottlenecks(特徵向量)
    cache_bottlenecks(sess, image_lists, FLAGS.image_dir,FLAGS.bottleneck_dir, 
                      jpeg_data_tensor, decoded_image_tensor, resized_image_tensor, bottleneck_tensor, FLAGS.tfhub_module)
    
    #增加評估
    evaluation_step, predictions = add_evaluation_step(final_tensor, ground_truth_input)

    #記錄概要信息
    merged = tf.summary.merge_all()
    train_writer = tf.summary.FileWriter(FLAGS.summaries_dir + '/train', sess.graph)
    validation_writer = tf.summary.FileWriter(FLAGS.summaries_dir + '/validation')
    train_saver = tf.train.Saver()
    
    #新類別模型訓練
    for i in range(FLAGS.how_many_training_steps):
        (train_bottlenecks,train_ground_truth, train_filenames) = get_random_cached_bottlenecks(sess, image_lists, FLAGS.train_batch_size, 'training',FLAGS.bottleneck_dir, FLAGS.image_dir, 
                                                            jpeg_data_tensor, decoded_image_tensor, resized_image_tensor, bottleneck_tensor, FLAGS.tfhub_module) #獲取一批bottleneck數據

        
        train_summary,_ = sess.run([merged, train_step], feed_dict={bottleneck_input:train_bottlenecks, ground_truth_input:train_ground_truth}) #完成一次更新
        train_writer.add_summary(train_summary, i)
        
        is_last_step = (i + 1 == FLAGS.how_many_training_steps)
        if (i % FLAGS.eval_step_interval) == 0 or is_last_step:
            train_accuracy, cross_entropy_value = sess.run([evaluation_step, cross_entropy], feed_dict={bottleneck_input: train_bottlenecks, ground_truth_input: train_ground_truth})
            tf.logging.info('%s: Step %d: Train accuracy = %.1f%%, Cross entropy = %f ' %(datetime.now(), i, train_accuracy * 100, cross_entropy_value))
            
            validation_bottlenecks, validation_ground_truth, _ = (get_random_cached_bottlenecks(sess, image_lists, FLAGS.validation_batch_size, 'validation',FLAGS.bottleneck_dir, FLAGS.image_dir, 
                                      jpeg_data_tensor,decoded_image_tensor, resized_image_tensor, bottleneck_tensor,FLAGS.tfhub_module))
            validation_summary, validation_accuracy = sess.run([merged, evaluation_step],feed_dict={bottleneck_input: validation_bottlenecks,ground_truth_input: validation_ground_truth})
            validation_writer.add_summary(validation_summary, i)
            tf.logging.info('%s: Step %d: Validation accuracy = %.1f%% (N=%d)' % (datetime.now(), i, validation_accuracy * 100,len(validation_bottlenecks)))
    
    #訓練完成,保存訓練數據
    train_saver.save(sess, CHECKPOINT_NAME)
    
    #最終的模型對測試集評估
    run_final_eval(sess, module_spec, class_count, image_lists,jpeg_data_tensor, decoded_image_tensor, resized_image_tensor,bottleneck_tensor)

輸出結果爲:

INFO:tensorflow:2019-03-12 16:07:53.515801: Step 0: Train accuracy = 24.0%, Cross entropy = 1.530714 
INFO:tensorflow:2019-03-12 16:07:54.157838: Step 0: Validation accuracy = 23.0% (N=100)
INFO:tensorflow:2019-03-12 16:07:56.185954: Step 9: Train accuracy = 82.0%, Cross entropy = 1.183218 
INFO:tensorflow:2019-03-12 16:07:56.307961: Step 9: Validation accuracy = 69.0% (N=100)
INFO:tensorflow:Final test accuracy = 71.8% (N=365)

5、新類別預測模型保存

將預測模型寫入文件:

#新類別預測圖保存到文件

def save_graph_to_file(graph_file_name, module_spec, class_count):
    """Saves an graph to file, creating a valid quantized one if necessary."""
    sess, _, _, _, _, _ = build_eval_session(module_spec, class_count)
    graph = sess.graph
    output_graph_def = tf.graph_util.convert_variables_to_constants(sess, graph.as_graph_def(), [FLAGS.final_tensor_name])
    with tf.gfile.FastGFile(graph_file_name, 'wb') as f:
        f.write(output_graph_def.SerializeToString())

#新類別預測模型保存
def export_model(module_spec, class_count, saved_model_dir):
    """Exports model for serving. """
    # The SavedModel should hold the eval graph.
    sess, in_image, _, _, _, _ = build_eval_session(module_spec, class_count)
    with sess.graph.as_default() as graph:
        tf.saved_model.simple_save(sess,saved_model_dir,
                        inputs={'image': in_image},outputs={'prediction': graph.get_tensor_by_name('final_result:0')},
                        legacy_init_op=tf.group(tf.tables_initializer(), name='legacy_init_op'))

訓練新預測模型並保存:

#獲取商品圖片
image_lists = create_image_lists(FLAGS.image_dir, FLAGS.testing_percentage, FLAGS.validation_percentage)
class_count = len(image_lists.keys())

#加載hub某個模型,並拉取模型信息,創建圖
module_spec = hub.load_module_spec(FLAGS.tfhub_module)
graph, bottleneck_tensor, resized_image_tensor, wants_quantization = (create_module_graph(module_spec))

#增加新softmax函數,構建全連接層
with graph.as_default():
    (train_step, cross_entropy, bottleneck_input,ground_truth_input, final_tensor) = add_final_retrain_ops(class_count, FLAGS.final_tensor_name, bottleneck_tensor, wants_quantization, is_training=True)

    
with tf.Session(graph=graph) as sess:
    # Initialize all weights: for the module to their pretrained values,
    # and for the newly added retraining layer to random initial values.
    init = tf.global_variables_initializer()
    sess.run(init)
    # Set up the image decoding sub-graph.
    jpeg_data_tensor, decoded_image_tensor = add_jpeg_decoding(module_spec)
    
    #緩存所有圖片的bottlenecks(特徵向量)
    cache_bottlenecks(sess, image_lists, FLAGS.image_dir,FLAGS.bottleneck_dir, 
                      jpeg_data_tensor, decoded_image_tensor, resized_image_tensor, bottleneck_tensor, FLAGS.tfhub_module)
    
    #增加評估
    evaluation_step, predictions = add_evaluation_step(final_tensor, ground_truth_input)
    print('add_evaluation_step:')
    print('evaluation_step: ', evaluation_step)
    print('predictions: ', predictions)

    
    #記錄概要信息
    merged = tf.summary.merge_all()
    train_writer = tf.summary.FileWriter(FLAGS.summaries_dir + '/train', sess.graph)
    validation_writer = tf.summary.FileWriter(FLAGS.summaries_dir + '/validation')
    train_saver = tf.train.Saver()
    
    #新類別模型訓練
    FLAGS.how_many_training_steps = 10
    for i in range(FLAGS.how_many_training_steps):
        (train_bottlenecks,train_ground_truth, train_filenames) = get_random_cached_bottlenecks(sess, image_lists, FLAGS.train_batch_size, 'training',FLAGS.bottleneck_dir, FLAGS.image_dir, 
                                                            jpeg_data_tensor, decoded_image_tensor, resized_image_tensor, bottleneck_tensor, FLAGS.tfhub_module) #獲取一批bottleneck數據
        
        if(i == 1):
            print('train_bottlenecks: ', train_bottlenecks, '\n')
            print('train_ground_truth: ', train_ground_truth, '\n')
            print('train_filenames: ', train_filenames, '\n')
        
        train_summary,_ = sess.run([merged, train_step], feed_dict={bottleneck_input:train_bottlenecks, ground_truth_input:train_ground_truth}) #完成一次更新
        train_writer.add_summary(train_summary, i)
        
        is_last_step = (i + 1 == FLAGS.how_many_training_steps)
        if (i % FLAGS.eval_step_interval) == 0 or is_last_step:
            train_accuracy, cross_entropy_value = sess.run([evaluation_step, cross_entropy], feed_dict={bottleneck_input: train_bottlenecks, ground_truth_input: train_ground_truth})
            tf.logging.info('%s: Step %d: Train accuracy = %.1f%%, Cross entropy = %f ' %(datetime.now(), i, train_accuracy * 100, cross_entropy_value))
            
            validation_bottlenecks, validation_ground_truth, _ = (get_random_cached_bottlenecks(sess, image_lists, FLAGS.validation_batch_size, 'validation',FLAGS.bottleneck_dir, FLAGS.image_dir, 
                                      jpeg_data_tensor,decoded_image_tensor, resized_image_tensor, bottleneck_tensor,FLAGS.tfhub_module))
            validation_summary, validation_accuracy = sess.run([merged, evaluation_step],feed_dict={bottleneck_input: validation_bottlenecks,ground_truth_input: validation_ground_truth})
            validation_writer.add_summary(validation_summary, i)
            tf.logging.info('%s: Step %d: Validation accuracy = %.1f%% (N=%d)' % (datetime.now(), i, validation_accuracy * 100,len(validation_bottlenecks)))
    
    #訓練完成,保存訓練數據
    train_saver.save(sess, CHECKPOINT_NAME)
    
    #最終的模型對測試集評估
    run_final_eval(sess, module_spec, class_count, image_lists,jpeg_data_tensor, decoded_image_tensor, resized_image_tensor,bottleneck_tensor)
    
    #模型保存
    tf.logging.info('Save final result to : ' + FLAGS.output_graph)
    save_graph_to_file(FLAGS.output_graph, module_spec, class_count)
    tf.logging.info('Save new categories to :' + FLAGS.output_labels)
    with tf.gfile.FastGFile(FLAGS.output_labels, 'w') as f:
        f.write('\n'.join(image_lists.keys()) + '\n')
    if FLAGS.saved_model_dir:
        export_model(module_spec, class_count, FLAGS.saved_model_dir)
    

結果輸出爲:

預測圖

新類別名稱:

預測模型:

二、模型預測

參數預定義:

file_name = "E:\\DataMining\\tensorflow\\google-ImageClassification\\flower_photos\\daisy\\5547758_eea9edfd54_n.jpg" #image
model_file = "E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\graph\\output_graph.pb" #graph/model to be execute
label_file = "'E:\\DataMining\\tensorflow\\google-ImageClassification\\flower\\labels\\output_labels.txt" #name of file containing labels
input_height = 299
input_width = 299
input_mean = 0
input_std = 255
input_layer = "Placeholder" #"name of input layer"
output_layer = "final_result" #"name of output layer"

#對類別名稱加載
def load_labels(label_file):
    label = []
    proto_as_ascii_lines = tf.gfile.GFile(label_file).readlines()
    for l in proto_as_ascii_lines:
        label.append(l.rstrip())
    return label

1、預測模型加載

#預測模型加載
def load_graph(model_file):
    graph = tf.Graph()
    graph_def = tf.GraphDef()
    with open(model_file, "rb") as f:
        graph_def.ParseFromString(f.read())
    with graph.as_default():
        tf.import_graph_def(graph_def)
    return graph

加載模型,並引入輸入層和輸出層: 

graph = load_graph(model_file)
input_name = "import/" + input_layer
output_name = "import/" + output_layer
input_operation = graph.get_operation_by_name(input_name)
output_operation = graph.get_operation_by_name(output_name)

2、加載預測圖片(圖片進行解碼和剪裁),預測圖片類別

預測圖片加載,並對圖片進行解碼和剪裁

#預測圖片加載,並對圖片進行解碼和剪裁
def read_tensor_from_image_file(file_name,input_height=299,input_width=299, input_mean=0, input_std=255):
    input_name = "file_reader"
    output_name = "normalized"
    file_reader = tf.read_file(file_name, input_name)
    if file_name.endswith(".png"):
        image_reader = tf.image.decode_png(file_reader, channels=3, name="png_reader")
    elif file_name.endswith(".gif"):
        image_reader = tf.squeeze(tf.image.decode_gif(file_reader, name="gif_reader"))
    elif file_name.endswith(".bmp"):
        image_reader = tf.image.decode_bmp(file_reader, name="bmp_reader")
    else:
        image_reader = tf.image.decode_jpeg(file_reader, channels=3, name="jpeg_reader")
        
    float_caster = tf.cast(image_reader, tf.float32)
    dims_expander = tf.expand_dims(float_caster, 0)
    resized = tf.image.resize_bilinear(dims_expander, [input_height, input_width])
    normalized = tf.divide(tf.subtract(resized, [input_mean]), [input_std])
    sess = tf.Session()
    result = sess.run(normalized)

    return result

圖片類別預測:

#加載圖片
t = read_tensor_from_image_file(file_name, input_height=input_height, input_width=input_width, input_mean=input_mean, input_std=input_std)
with tf.Session(graph=graph) as sess:
    results = sess.run(output_operation.outputs[0], {input_operation.outputs[0]: t})
results = np.squeeze(results)

#輸出預測概率最大的5個類別
top_k = results.argsort()[-5:][::-1]
labels = load_labels(label_file)
for i in top_k:
    print(labels[i], results[i])

輸出結果:

sunflowers 0.27877674
daisy 0.21119775
dandelion 0.17498685
tulips 0.17195
roses 0.16308863

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章