【TensorFlow】TensorFlow实战Google深度学习框架第2版参考代码(06-图像识别与卷积神经网络)

《TensorFlow实战Google深度学习框架第2版》样例代码,因版本、修改、失误等原因,可能与教材不一致或运行失败。内容涉及图像识别与卷积神经网络,如卷积层和池化层前向传播、类似LeNet - 5模型结构实现、Inception模块、数据整理及迁移学习等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《TensorFlow实战Google深度学习框架第2版》教材中的样例代码,由于tensorflow版本、运行过程的修改、敲码过程中的失误、教材样例自带的错误等原因,可能会导致代码与教材不一致或者不能运行成功,仅供参考。

第6章 图像识别与卷积神经网络

  • 6..3 卷积层和池化层的前向传播过程
#!/usr/bin/python
# -*- coding: utf-8 -*-

import tensorflow as tf
from chapter06.lenet5 import mnist_inference

BATCH_SIZE = 100
# 通过tf.get_variable的方式创建过滤器的权重变量和偏置项变量
# 上面介绍了卷积层的参敖个数只和过滤器的尺寸、深度以及当前层节点矩阵的深度有关,所以这里声明的参数变量是一个四维矩阵
# 前面两个维度代表了过滤器的尺寸,第三个维度表示当前层的深度,第四个维度表示过滤撼的深度

filter_weight = tf.get_variable('weights', [5, 5, 3, 16],
                                initializer=tf.truncated_normal_initializer(stddev=0.1))

# 和卷积层的权重类似,当前层矩阵上不同位置的偏置项也是共辜的,所以总共有下-层深度个不同的偏置项
# 样例代码中16为过滤器的深度,也是神经网络中下一层节点矩阵的深度
biases = tf.get_variable('biases', [16], initializer=tf.constant_initializer(0.1))

# tf.nn.conv2d提供了一个非常方便的函数来实现卷积层前向传播的算法
# 这个函数的第一个输入为当前层的节点矩阵,注意这个矩阵是一个四维矩阵,后面三个维度对应一个节点矩阵,第一维对应一个输入batch
# 比如在输入层,input[O , :, :, :]表示第一张图片,input[1, :, :, :]表示第二张图片,以此类推
# tf.nn.conv2d第二个参数提供了卷积层的权重
# 第三个参数为不同维度上的步长,虽然第三个参数提供的是一个长度为4的数组,但是第一维和最后一维的数字要求一定是1,这是因为卷积层的步长只对矩阵的长和宽有效
# 最后一个参数是填充(padding)的方法,TensorFlow中提供SAME或是VALID两种选择,其中SAME表示添加全0填充,VALID表示不添加
conv = tf.nn.conv2d(input, filter_weight, strides=[1, 1, 1, 1], padding='SAME')

# tf.nn.bias_add提供了一个方便的函数给每一个节点加上偏置顷
# 注意这里不能直接使用加法,因为矩阵上不同位置上的节点都需要加上同样的偏置项
bias = tf.nn.bias_add(conv, biases)

# 将计算结果通过ReLU激活函数完成去线性化
actived_conv = tf.nn.relu(bias)

# tf.nn.max_pool实现了最大池化层的前向传播过程,它的参数和tf.nn.conv2d函数类似
# ksize提供了过滤器的尺寸,strides提供了步长信息,padding提供了是否使用全0填充
pool = tf.nn.max_pool(actived_conv, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
  • 6.4 实现类似LeNet-5模型结构的前向传播过程的mnist_ inference.py程序
#!/usr/bin/python
# -*- coding: utf-8 -*-

import tensorflow as tf

# 配置神经网络的参数
INPUT_NODE = 784
OUTPUT_NODE = 10

IMAGE_SIZE = 28
NUM_CHANNELS = 1
NUM_LABELS = 10

# 第一层卷积层的尺寸和深度
CONV1_DEEP = 32
CONV1_SIZE = 5
# 第二层卷积层的尺寸和深度
CONV2_DEEP = 64
CONV2_SIZE = 5
# 全连接层的节点个数
FC_SIZE = 512


# 定义卷积神经网络的前向传播过程
# 这里添加了一个新的参数train,用于区分训练过程和测试过程
# 在这个程序中将用到dropout方法,dropout可以进一步提升模型可靠性并防止过拟合,dropout过程只在训练时使用
def inference(input_tensor, train, regularizer):
    # 声明第一层卷积层的变量并实现前向传播过程
    # 通过使用不同的命名空间来隔离不同层的变量,这可以让每一层中的变量命名只需要考虑在当前层的作用,而不需要担心重名的问题
    # 和标准LeNet-5模型不大一样,这里定义的卷积层输入为28x28x1的原始MNIST图片像素
    # 因为卷积层中使用了全0填充,所以输出为28x28x32的矩阵
    with tf.variable_scope('layer1-conv1'):
        conv1_weights = tf.get_variable(
            "weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv1_biases = tf.get_variable("bias", [CONV1_DEEP], initializer=tf.constant_initializer(0.0))
        # 使用边长为5,深度为32的过滤器,过滤器移动的步长为1,且使用全0填充
        conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')
        relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))

    # 实现第二层池化层的前向传播过程
    # 这里选用最大池化层,池化层过滤器的边长为2,使用全0填充且移动的步长为2
    # 这一层的输入是上一层的输出,也就是28x28×32的矩阵,输出为14x14×32的矩阵
    with tf.name_scope("layer2-pool1"):
        pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")

    # 声明第三层卷积层的变量并实现前向传播过程
    # 这一层的输入为14×14x32的矩阵,输出为14x14x64的矩阵
    with tf.variable_scope("layer3-conv2"):
        conv2_weights = tf.get_variable(
            "weight", [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv2_biases = tf.get_variable("bias", [CONV2_DEEP], initializer=tf.constant_initializer(0.0))
        # 使用边长为5,深度为64的过滤器,过滤器移动的步长为1,且使用全0填充
        conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
        relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))

    # 实现第四层池化层的前向传播过程
    # 这一层和第二层的结构是一样的,这一层的输入为14x14x64的矩阵,输出为7x7x64的矩阵
    with tf.name_scope("layer4-pool2"):
        pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
        # 将第四层池化层的输出转化为第五层全连接层的输入格式
        # 第四层的输出为7x7x64的矩阵,然而第五层全连接层需要的输入格式为向量,所以在这里面要将这个7x7x64的矩阵拉直成一个向量
        # pool2.get_shape函数可以得到第四层输出矩阵的维度而不需要手工计算
        # 注意,因为每一层神经网络的输入输出都为一个batch的矩阵,所以这里得到的维度也包含了一个batch中数据的个数
        pool_shape = pool2.get_shape().as_list()
        # 计算将矩阵拉直成向量之后的长度,这个长度就是矩阵长宽及深度的乘积
        # 注意这里pool_shape[O]为一个batch中数据的个数
        nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
        # 通过tf.reshape函数将第四层的输出变成一个batch的向量
        reshaped = tf.reshape(pool2, [pool_shape[0], nodes])

    # 声明第五层全连接层的变量并实现前向传播过程
    # 这一层的输入是拉直之后的一组向盘,向量长度为3136,输出是一组长度为512的向量
    # 这一层引入了dropout的概念,dropout在训练时会随机将部分节点的输出改为0
    # dropout可以避免过拟合问题,从而使得模型在测试数据上的效果更好,dropout 一般只在全连接层而不是卷积层或者池化层使用
    with tf.variable_scope('layer5-fc1'):
        fc1_weights = tf.get_variable("weight", [nodes, FC_SIZE],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        # 只有全连接层的权重需要加入正则化
        if regularizer != None: tf.add_to_collection('losses', regularizer(fc1_weights))
        fc1_biases = tf.get_variable("bias", [FC_SIZE], initializer=tf.constant_initializer(0.1))

        fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
        if train: fc1 = tf.nn.dropout(fc1, 0.5)

    # 声明第六层全连接层的变量并实现前向传播过程
    # 这一层的输入为一组长度为512的向量,输出为一组长度为10的向量
    # 这一层的输出通过Softmax之后就得到了最后的分类结果
    with tf.variable_scope('layer6-fc2'):
        fc2_weights = tf.get_variable("weight", [FC_SIZE, NUM_LABELS],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        if regularizer != None: tf.add_to_collection('losses', regularizer(fc2_weights))
        fc2_biases = tf.get_variable("bias", [NUM_LABELS], initializer=tf.constant_initializer(0.1))
        logit = tf.matmul(fc1, fc2_weights) + fc2_biases

    # 返回第六层的输
    return logit
  • 6.4 mnist_train.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
from chapter06.lenet5 import mnist_inference

# 配置神经网络的参数
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.01
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 6000
MOVING_AVERAGE_DECAY = 0.99

# 模型保存的路径和文件名
MODEL_SAVE_PATH = "/path/to/MNIST_model/"
MODEL_NAME = "mnist_model"


def train(mnist):
    # 定义输入输出placeholder
    x = tf.placeholder(tf.float32, [BATCH_SIZE, mnist_inference.IMAGE_SIZE, mnist_inference.IMAGE_SIZE,
                                    mnist_inference.NUM_CHANNELS], name='x-input')
    y_ = tf.placeholder(tf.float32, [None, mnist_inference.OUTPUT_NODE], name='y-input')

    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    # 直接使用mnist_inference.py中定义的前向传播过程
    y = mnist_inference.inference(x, False, regularizer)
    global_step = tf.Variable(0, trainable=False)

    # 定义损失函数/学习率/滑动平均操作以及训练过程
    variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    variables_averages_op = variable_averages.apply(tf.trainable_variables())
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        mnist.train.num_examples / BATCH_SIZE,
        LEARNING_RATE_DECAY,
        staircase=True)

    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
    with tf.control_dependencies([train_step, variables_averages_op]):
        train_op = tf.no_op(name='train')

    # 初始化TensorFlow持久化类
    saver = tf.train.Saver()
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(TRAINING_STEPS):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)

            reshaped_xs = np.reshape(xs, (
                BATCH_SIZE,
                mnist_inference.IMAGE_SIZE,
                mnist_inference.IMAGE_SIZE,
                mnist_inference.NUM_CHANNELS))
            _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: reshaped_xs, y_: ys})

            if i % 1000 == 0:
                print("After %d training step(s), loss on training batch is %g." % (step, loss_value))


def main(argv=None):
    mnist = input_data.read_data_sets("/path/to/MNIST_data", one_hot=True)
    train(mnist)


if __name__ == '__main__':
    tf.app.run()
  • 6.4 Inception模块
#!/usr/bin/python
# -*- coding: utf-8 -*-

import tensorflow as tf

slim = tf.contrib.slim

# slim.arg_scope函数可以用于设置默认的参数取值
# slim.arg_scope函数的第一个参数是一个函数列表,在这个列表中的函数将使用默认的参数取值
# 如果在函数调用时指定了stride,那么这里设置的默认值就不会再使用
# 通过这种方式可以进一步减少冗余的代码
with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
                    stride=1, padding='VALID'):
    # 假设输入图片经过之前的神经网络前向传播的结果保存在变量net中
    net = '上一层的的输出节点矩阵'
    # 为一个Inception模块声明一个统一的变量命名空间
    with tf.variable_scope('Mixed_7c'):
        # 给Inception模块中每一条路径声明一个命名空间
        with tf.variable_scope('Branch_0'):
            # 实现一个过滤器边长为1,深度为320的卷积层
            branch_0 = slim.conv2d(net, 320, [1, 1], scope='Conv2d_Oa_1x1')
        # Inception模块中第二条路径,这条计算路径上的结构本身也是一个Inception结构
        with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 384, [1, 1], scope='Conv2d_Oa_1x1')
            # tf.concat函数可以将多个矩阵拼接起来
            # tf.concat 函数的第一个参数指定了拼接的维度,这里给出的"3"代表了矩阵是在深度这个维度上进行拼接
            branch_1 = tf.concat(3, [
                slim.conv2d(branch_1, 384, [1, 3], scope='Conv2d_Ob_1x3'),
                slim.conv2d(branch_1, 384, [3, 1], scope='Conv2d_Oc_3x1')])
        # Inception模块中第三条路径,此计算路径也是一个Inception结构
        with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(
                net, 448, [1, 1], scope='Conv2d_Oa_1x1')
            branch_2 = slim.conv2d(
                branch_2, 384, [3, 3], scope=' Conv2d_Ob_3x3')
            branch_2 = tf.concat(3, [
                slim.conv2d(branch_2, 384,
                            [1, 3], scope='Conv2d_Oc_1x3'),
                slim.conv2d(branch_2, 384,
                            [3, 1], scope='Conv2d_Od 3x1')])

        # Inception模块中第四条路径
        with tf.variable_scope('Branch_3'):
            branch_3 = slim.avg_pool2d(
                net, [3, 3], scope='AvgPool_Oa_3x3')
            branch_3 = slim.conv2d(
                branch_3, 192, [1, 1], scope='Conv2d_Ob_1x1')

        # 当前Inception模块的最后输出是由上面4个计算结果拼攘得到的
        net = tf.concat(3, [branch_0, branch_1, branch_2, branch_3])
  • 6.5 将原始的图像数据整理成模型需要的输入数据
#!/usr/bin/python
# -*- coding: utf-8 -*-

import glob
import os.path
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile

INPUT_DATA = '/path/to/flower_photos'
OUTPUT_FILE = '/path/to/flower_processed_data.npy'  # 输出文件地址,这里先通过numpy来保存

# 测试数据和验证数据比例
VALIDATION_PERCENTAGE = 10
TEST_PERCENTAGE = 10


# 读取数据并将数据分割成训练数据、验证数据和测试数据
def create_image_lists(sess, testing_percentage, validation_percentage):
    sub_dirs = [x[0] for x in os.walk(INPUT_DATA)]
    is_root_dir = True
    # 初始化各个数据集
    training_images = []
    training_labels = []
    testing_images = []
    testing_labels = []
    validation_images = []
    validation_labels = []
    current_label = 0

    # 读取所有的子目录
    for sub_dir in sub_dirs:
        if is_root_dir:
            is_root_dir = False
            continue
        # 获取一个子目录中所有的图片文件
        extensions = ['jpg', 'jpeg', 'JPG', 'JPEG']
        file_list = []
        dir_name = os.path.basename(sub_dir)
        for extension in extensions:
            file_glob = os.path.join(INPUT_DATA, dir_name, '*.' + extension)
            file_list.extend(glob.glob(file_glob))
            if not file_list: continue

            # 处理图片数据
            for file_name in file_list:
                # 读取并解析图片,将图片转化为299×299以便inception-v3模型来处理
                image_raw_data = gfile.FastGFile(file_name, 'rb').read()
                image = tf.image.decode_jpeg(image_raw_data)
                if image.dtype != tf.float32:
                    image = tf.image.convert_image_dtype(image, dtype=tf.float32)
                image = tf.image.resize_images(image, (299, 299))
                image_value = sess.run(image)

                # 随机划分数据集
                chance = np.random.randint(100)
                if chance < validation_percentage:
                    validation_images.append(image_value)
                    validation_labels.append(current_label)
                elif chance < (testing_percentage + validation_percentage):
                    testing_images.append(image_value)
                    testing_labels.append(current_label)
                else:
                    training_images.append(image_value)
                    training_labels.append(current_label)
            current_label += 1
    # 将训练数据随机打乱以获得更好的训练效果
    state = np.random.get_state()
    np.random.shuffle(training_images)
    np.random.set_state(state)
    np.random.shuffle(training_labels)

    return np.asarray([training_images, training_labels,
                       validation_images, validation_labels,
                       testing_images, testing_labels])


# 数据整理主函数
def main():
    with tf.Session() as sess:
        processed_data = create_image_lists(
            sess, TEST_PERCENTAGE, VALIDATION_PERCENTAGE)
        # 通过numpy格式保存处理后的数据
        np.save(OUTPUT_FILE, processed_data)


if __name__ == '__main__':
    main()
  • 6.5 迁移学习过程
#!/usr/bin/python
# -*- coding: utf-8 -*-

import glob
import os.path
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile
import tensorflow.contrib.slim as slim

# 加载通过TensorFlow-Slim定义好的inception_v3模型
import tensorflow.contrib.slim.python.slim.nets.inception_v3 as inception_v3

# 处理好之后的数据文件
INPUT_DATA = '/path/to/flower_processed_data.npy'

# 保存训练好的模型的路径,这里可以将使用新数据训练得到的完整模型保存下来
# 如果计算资源充足,还可以在训练完最后的全连接层之后再训练所有网络层
# 这样可以使得新模型更加贴近新数据
TRAIN_FILE = '/path/to/save_model'
# 谷歌提供的训练好的模型文件地址
CKPT_FILE = '/path/to/inception_v3.ckpt'

# 定义训练中使用的参数
LEARNING_RATE = 0.0001
STEPS = 300
BATCH = 32
N_CLASSES = 5

# 不需要从谷歌训练好的模型中加载的参数
# 这里就是最后的全连接层,因为在新的问题中要重新训练这一层中的参数
# 这里给出的是参数的前缀
CHECKPOINT_EXCLUDE_SCOPES = 'InceptionV3/Logits,InceptionV3/AuxLogits'
# 需要训练的网络层参数名称,在fine-tuning的过程中就是最后的全连接层
# 这里给出的是参数的前缀
TRAINABLE_SCOPES = 'InceptionV3/Logits,InceptionV3/AuxLogits'


# 获取所有需要从谷歌训练好的模型中加载的参数
def get_tuned_variables():
    exclusions = [scope.strip() for scope in CHECKPOINT_EXCLUDE_SCOPES.split(',')]
    variables_to_restore = []
    # 枚举inception-v3模型中所有的参数,然后判断是否需要从加载列表中移除
    for var in slim.get_model_variables():
        excluded = False
        for exclusion in exclusions:
            if var.op.ηame.startswith(exclusion):
                excluded = True
                break
        if not excluded:
            variables_to_restore.append(var)
    return variables_to_restore


# 获取所有需要训练的变量列表
def get_trainable_variables():
    scopes = [scope.strip() for scope in TRAINABLE_SCOPES.split(',')]
    variables_to_train = []
    # 枚举所有需要训练的参数前缀,并通过这些前缀找到所有的参数
    for scope in scopes:
        variables = tf.get_collection(
            tf.GraphKeys.TRAINABLE_VARIABLES, scope)
        variables_to_train.extend(variables)
    return variables_to_train


def main():
    # 加载预处理好的数据
    processed_data = np.load(INPUT_DATA)
    training_images = processed_data[0]
    n_training_example = len(training_images)
    training_labels = processed_data[1]
    validation_images = processed_data[2]
    validation_labels = processed_data[3]
    testing_images = processed_data[4]
    testing_labels = processed_data[5]
    print("%d training examples, %d validation examples and %d "
          "testing examples." % (n_training_example, len(validation_labels), len(testing_labels)))

    # 定义inception-v3的输入,images为输入图片,labels为每一张图片对应的标签
    images = tf.placeholder(tf.float32, [None, 299, 299, 3], name='input_images')
    labels = tf.placeholder(tf.int64, [None], name='labels')
    # 定义inception-v3模型
    # 因为谷歌给出的只有模型参数取值,所以这里需要在这个代码中定义inception-v3的模型结构
    # 虽然理论上需要区分训练和测试中使用的模型,也就是说在测试时应该使用is_training = False
    # 但是因为预先训练好的inception-v3模型中使用的batch normalization参数与新的数据会有差异,导致结果很差
    # 所以这里直接使用同一个模型来进行测试
    with slim.arg_scope(inception_v3.inception_v3_arg_scope()):
        logits, _ = inception_v3.inception_v3(images, num_classes=N_CLASSES)

    # 获取需要训练的变量
    trainable_variables = get_trainable_variables()

    # 定义交叉熵损失,注意在模型定义的时候己经将正则化损失加入损失集合了
    tf.losses.softmax_cross_entropy(tf.one_hot(labels, N_CLASSES), logits, weights=1.0)

    # 定义训练过程,这里minimize的过程中指定了需要优化的变量集合
    train_step = tf.train.RMSPropOptimizer(LEARNING_RATE).minimize(tf.losses.get_total_loss())

    # 计算正确率
    with tf.name_scope('evaluation'):
        correct_prediction = tf.equal(tf.argmax(logits, 1), labels)
        evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    # 定义加载模型的函数
    load_fn = slim.assign_from_checkpoint_fn(CKPT_FILE, get_tuned_variables(), ignore_missing_vars=True)

    # 定义保存新的训练好的模型的函数
    saver = tf.train.Saver()
    with tf.Session() as sess:
        # 初始化没有加载进来的变量,注意这个过程一定要在模型加载之前,否则初始化过程会将已经加载好的变量重新赋值
        init = tf.global_variables_initializer()
        sess.run(init)

    # 加载谷歌已经训练好的模型
    print('Loading tuned variables from %s ' % CKPT_FILE)
    load_fn(sess)
    start = 0
    end = BATCH
    for i in range(STEPS):
        # 运行训练过程,这里不会更新全部的参数,只会更新指定的部分参数
        sess.run(train_step, feed_dict={images: training_images[start:end], labels: training_labels[start:end]})
        # 输出日志
        if i % 30 == 0 or i + 1 == STEPS:
            saver.save(sess, TRAIN_FILE, global_step=i)
            validation_accuracy = sess.run(evaluation_step, feed_dict={
                images: validation_images, labels: validation_labels})
            print('Step % d: Validation accuracy = %.1f%%' % (i, validation_accuracy * 100.0))

        # 因为在数据预处理的时候已经做过了打乱数据的操作,所以这里只需要顺序使用训练数据就好
        start = end
        if start == n_training_example:
            start = 0
        end = start + BATCH
        if end > n_training_example:
            end = n_training_example

    # 在最后的测试数据上测试正确率
    test_accuracy = sess.run(evaluation_step, feed_dict={images: testing_images, labels: testing_labels})
    print('Final test accuracy = %.1f%%' % (test_accuracy * 100))


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

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值