leftrk / articles / Issue #60tensorflow 1.3 tips - 2 #60 · open · 39 comments
View on GitHub ↗
**正则化**
```python
weights = tf.constant([[1.0, -2.0], [-3.0, 4.0]])
with tf.Session() as sess:
# 输出为(|1| + |-2| + |-3| + |4|) * 0.5 = 5,其中0.5为正则化项的权重
print(sess.run(tf.contrib.layers.l1_regularizer(.5)(weights)))
# 输出为(1^2 + (-2)^2 + (-3)^2 + 4^2/2) * 0.5 = 7.5,TF会将L2的正则化损失值除以2使得求导更简洁
print(sess.run(tf.contrib.layers.l2_regularizer(.5)(weights)))
```
Comments ```python
# b. 损失函数
# 计算交叉熵及其平均值
# 目标类别只有一个正确答案时可使用tf.nn.sparse_softmax_cross_entropy_with_logits来加速交叉熵计算
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,
labels=tf.argmax(y_, axis=1))
# 计算当前batch中所有样例的交叉熵平均值
cross_entropy_mean = tf.reduce_mean(cross_entropy)
# 计算L2正则化损失,一般只计算权重部分,不使用偏置项
regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
regularaztion = regularizer(weights1) + regularizer(weights2)
# 总的损失函数
loss = cross_entropy_mean + regularaztion
````
```python
global_step = tf.Variable(0, trainable=False)
# 给定滑动平均衰减率、训练轮数,初始化滑动平均类。给定训练轮数可以加快训练早期变量的更新速度
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
# 在所有代表神经网络参数的变量上使用滑动平均:即Graphkeys.TRAINABLE_VARIABLES中的元素,也即所有没有指定trainable=False的参数
variables_averages_op = variable_averages.apply(tf.trainable_variables())
# 滑动平均不会改变变量本身取值,只是维护一个影子变量来记录其滑动平均值,因此需要使用这个滑动平均值时需要明确调用average函数
average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)
```
```python
# c. 反向传播
# 设置指数衰减的学习率。
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE,
global_step,
mnist.train.num_examples / BATCH_SIZE,
LEARNING_RATE_DECAY,
staircase=True)
```
```python
# 优化损失函数(更新神经网络参数)
train_step = tf.train.GradientDescentOptimizer(learning_rate)
.minimize(loss, global_step=global_step)
```
```python
# 每一遍训练,反向传播既需要更新参数,也需要更新每一个参数的滑动平均值
# train_op = tf.group(train_step, variables_average_op)等价下面两行
with tf.control_dependencies([train_step, variables_averages_op]):
train_op = tf.no_op(name='train')
```
```python
# 另;检验使用了滑动平均模型的神经网络前向传播是否正确
# tf.equal判断两个张量的每一维是否相等,返回True/False
correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))
# tf.cast转换数据类型,这里将布尔型转换成实数型
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
```
```python
def inference(input_tensor, reuse=False):
# 定义第一层神经网络的变量和前向传播过程
with tf.variable_scope('layer1', reuse=reuse):
# 根据传进来的reuse判断创建新变量还是使用已创建好的。第一次构造网络时需要创建新的,以后每次调用都使用reuse=True就不需要每次传递变量进来
weights = get_weight_variable([INPUT_NODE, LAYER1_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [LAYER1_NODE], initializer=tf.constant_initializer(0.0))
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
# 定义第二层神经网络的变量和前向传播过程
with tf.variable_scope('layer2', reuse=reuse):
weights = get_weight_variable([LAYER1_NODE, OUTPUT_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [OUTPUT_NODE], initializer=tf.constant_initializer(0.0))
layer2 = tf.matmul(layer1, weights) + biases
# 返回最后的前向传播结果
return layer2
```
```python
# 声明tf.train.Saver类用于保存模型
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(init_op)
saver.save(sess, "Saved_model/model.ckpt")
```
```python
# 该类可以读取checkpoint文件中保存的所有变量,注意后面的.data和.index可以省去
reader = tf.train.NewCheckpointReader('Saved_model/model.ckpt')
# 获取所有变量列表,这个是一个从变量名到变量维度的字典
global_varibales = reader.get_variable_to_shape_map()
for variable_name in global_varibales:
# variable_name为变量名称,global_variables[variable_name]为变量的维度
print(variable_name, global_varibales[variable_name])
# 获取名为v1的变量的取值
print("Value for variable v1 is ", reader.get_tensor("v1"))
```
```python
saver = tf.train.Saver()
with tf.Session() as sess:
# 加载已经保存的模型,并通过已保存的模型中变量来计算加法
saver.restore(sess, "Saved_model/model.ckpt")
print(sess.run(result))
```
```python
saver = tf.train.import_meta_graph("Saved_model/model.ckpt.meta")
with tf.Session() as sess:
saver.restore(sess, "Saved_model/model.ckpt")
# 通过张量名称来获取张量
print(sess.run(tf.get_default_graph().get_tensor_by_name("add:0")))
```
**保存或加载部分变量**
```python
saver = tf.train.Saver([v1])
with tf.Session() as sess:
# 加载已经保存的模型,并通过已保存的模型中变量来计算加法
saver.restore(sess, "Saved_model/model.ckpt")
print(sess.run(result))
```
**变量重命名**
```python
# 注意定义时的name不一样!!!
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name="other-v1")
v2 = tf.Variable(tf.constant(1.0, shape=[1]), name="other-v2")
# 将原来名称为v1的变量加载到现在变量v1中(名称为other-v1)
# 将原来名称为v2的变量加载到现在变量v1中(名称为other-v2)
saver = tf.train.Saver({"v1": v1, "v2": v2})
```
**滑动平均类的保存和加载**
```python
# 通过变量重命名将原来变量v的滑动平均值直接赋值给v。
saver = tf.train.Saver({"v/ExponentialMovingAverage": v})
```
```python
v = tf.Variable(0, dtype=tf.float32, name="v")
ema = tf.train.ExponentialMovingAverage(0.99)
# 通过variables_to_restore函数可以直接生成上面代码中提供的字典:{"v/ExponentialMovingAverage": v}
print(ema.variables_to_restore()) # 输出:{'v/ExponentialMovingAverage': <tf.Variable 'v:0' shape=() dtype=float32_ref>}
saver = tf.train.Saver(ema.variables_to_restore())
```
**常量方式保存(pb格式)**
```python
# 导出当前计算图的GraphDef部分,只需要这一部分就可以完成从输入层到输出层的计算过程graph_def = tf.get_default_graph().as_graph_def()
```
```python
# 将圈中的变量及其取值转化为常量,同时将图中不必要的节点去掉。在5.4.2 节中将会看
# 到一些系统运算也会被转化为计算图中的节点(比如变量初始化操作)。如果只关心程序中定
# 义的某些计算时,和这些计算无关的节点就没有必要导出并保存了。在下面一行代码中,最
# 后一个参数['add']给出了需要保存的节点名称。add 节点是上面定义的两个变量相加的
# 操作。注意这里给出的是计算节点的名称,所以没有后面的:0 。
output_graph_def = graph_util.convert_variables_to_constants(sess, graph_def, ['add'])
with tf.gfile.GFile("Saved_model/combined_model.pb", "wb") as f:
f.write(output_graph_def.SerializeToString())
```
```python
# 读取保存的模型文件,并将文件解析成对应的GraphDef Protocol Buffer
with gfile.FastGFile(model_filename, 'rb') as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
# 将graph_def中保存的图加载到当前的图中。return_elements=["add:0"]给出了返回的张量
# 的名称。在保存的时候给出的是计算节点的名称,所以为"add",在加载的时候给出
# 的是张量的名称,所以为"add:0"。
result = tf.import_graph_def(graph_def, return_elements=["add:0"])
print(sess.run(result))
```
**tf.Coordinator主要用于协同多个线程一起停止**,并提供了`should_stop`、`request_ stop`和`join`三个函数。在启动线程之前,需要先声明一个tf.Coordinator类,并将这个类传入每一个创建的线程中。 **启动的线程需要一直查询tf.Coordinator类中提供的should_stop函数,当这个函数的返回值为True时,则当前线程也需要退出。每一个启动的线程都可以通过调用request_stop函数来通知其他线程退出。** 当某一个线程调用request_stop函数之后,should_stop函数的返回值将被设置为True这样其他的线程就可以同时终止了。
**tf.QueueRunner主要用于启动多个线程来操作同一个队列**,启动的这些线程可以通过上面介绍的tf.Coordinator类来统一管理。
```python
# 先声明一个先进先出的队列,队列中最多100个元素,类型为实数型
queue = tf.FIFOQueue(100, "float")
# 定义队列的入队操作
enqueue_op = queue.enqueue([tf.random_normal([1])])
# 使用tf.train.QueueRunner来创建多个线程运行队列的入队操作
# 第一个参数给出了被操作的队列;
# 第二个参数表示需要启动5个线程,每个线程中运行的是enqueue_op操作
qr = tf.train.QueueRunner(queue, [enqueue_op] * 5)
# 将定义过的QueueRunner加入TensorFlow计算图上指定的集合,默认tf.GraphKeys,QUEUE_RUNNERS
tf.train.add_queue_runner(qr)
# 定义出队操作
out_tensor = queue.dequeue()
```
```python
with tf.Session() as sess:
# 使用tf.train.Coordinator来协同启动的线程
coord = tf.train.Coordinator()
# 使用tf.train.QueueRunner时,需要明确调用tf.train.start_queue_runners来启动所有线程,
# 否则因为没有线程运行入队操作,当调用出队操作时,程序会一直等待入队操作被运行。
# tf.train.start_queue_runners函数会默认启动tf.GraphKeys.QUEUE_RUNNERS集合中所有的QueueRunner,
# 因为这个函数只支持启动指定集合中的QueueRnner,所以一般来说
# tf.train.add_queue_runner函数和tf.train.start_queue_runners函数会指定同一个集合。
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
# 获取队列中的值
for _ in range(3):
print(sess.run(out_tensor)[0])
# 使用tf.train.Coordinator来停止所有线程
coord.request_stop()
coord.join(threads)
# 以上程序将启动5个线程来执行队列入队的操作,其中每一个线程都是将随机数写入队列。
# 于是在每次运行出队操作时,可以得到一个随机数
```
利用数据集读取数据有三个基本步骤:
1. **定义数据集的构造方法**,这个例子使用`tf.data.Dataset.from_tensor_slices()`,表明数据集是从一个张量中构建的,如果数据集是文件中构建的,则需要相应调整不同的构造方法。
2. **定义遍历器**,这个例子使用了简单的`one_shot_iterator`来遍历数据集,稍后将介绍更加灵活的`initializable_iterator`。
3. **使用`get_next`方法从遍历器中读取数据张量,作为计算图其他部分的输入。**
**读取文本文件里的数据**
```python
# 从文本文件创建数据集。假定每行文字是一个训练例子。这里可以提供多个文件。
input_files = ["./test1.txt", "./test2.txt"]
dataset = tf.data.TextLineDataset(input_files)
```
```python
# 定义迭代器。
iterator = dataset.make_one_shot_iterator()
# 这里get_next()返回一个字符串类型的张量,代表文件中的一行。
x = iterator.get_next()
with tf.Session() as sess:
for i in range(4):
print(sess.run(x))
```
**解析TFRecord文件里的数据**
在图像相关任务中,输入数据通常以TFRecord形式存储,这时可以用`TFRecordDataset`来读取数据。与文本文件不同,每一个TFRecord都有自己不同的feature格式,因此在读取TFRecord时,需要提供一个`parser`函数来解析所读取的TFRecord的数据格式。
**使用initializable_iterator来动态初始化数据集**
```python
# 从TFRecord文件创建数据集,具体文件路径是一个placeholder,稍后再提供具体路径。
input_files = tf.placeholder(tf.string)
dataset = tf.data.TFRecordDataset(input_files)
dataset = dataset.map(parser)
# 定义遍历dataset的initializable_iterator。
iterator = dataset.make_initializable_iterator()
image, label = iterator.get_next()
with tf.Session() as sess:
# 首先初始化iterator,并给出input_files的值。
sess.run(iterator.initializer,
feed_dict={input_files: ["output.tfrecords"]})
# 遍历所有数据一个epoch。当遍历结束时,程序会抛出OutOfRangeError。
while True:
try:
x, y = sess.run([image, label])
except tf.errors.OutOfRangeError:
break
```
**在上面的例子中,文件路径使用placeholder和feed_dict的方式传给数据集。使用这种方法,在实际项目中就不需要总是将参数写入计算图的定义,而可以使用程序参数的方式动态指定参数。**
另外注意到,上面例子中的循环体不是指定循环运行10次sess.run,而是使用*while(True) - try - except*的形式来将所有数据遍历一遍(即一个epoch)。这是因为在动态指定输入数据时,不同数据来源的数据量大小难以预知,而这个方法使我们不必提前知道数据量的精确大小。
**数据集的高层操作**
**1. dataset.map**
`dataset = dataset.map(parser)`
`dataset = dataset.map(lambda x:preprocess_for_train(x, image_size, image_size, None))`
**2. dataset.batch & dataset.shuffle**
- `dataset = dataset.batch(batch_size)`,**将数据组合成batch,参数batch_size代表要输出的每个batch由多少条数据组成。如果数据集中包含多个张量,那么batch操作将对每一个张量分开进行**。举例而言,如果数据集中的每一个数据(即iterator.get_next()的返回值)是image、label两个张量,其中image的维度是[300, 300],label的维度是[],batch_size是128,那么经过batch 操作后的数据集的每一个输出将包含两个维度分别是[128, 300, 300]和[128]的张量。
- `dataset = dataset.shuffle(buffer_size)`,随机打乱顺序,参数buffer_size等效于`tf.train.shuffle_batch`中的参数min_after_dequeue。shuffle算法在内部使用一个缓冲区中保存buffer_size条数据,每读入一条新数据时,从这个缓冲区中随机选择一条数据进行输出。缓冲区的大小越大,随机的性能越好,但占用的内存也越多。
**3. dataset.repeat**
repeat是另一个常用的操作方法,它将数据集中的数据复制多份,其中每一份被称为一个epoch。
`dataset = dataset.repeat(N)`
上面代码将数据重复N份。需要指出的是,**如果数据集在repeat前己经进行了shuffle操作,输出的每个epoch中随机shuffle的结果并不会相同。**例如,如果输入数据是[1, 2, 3],shuffle后输出的第一个epoch是[2, 1, 3],而第二个epoch则有可能是[3, 2, 1]。repeat和map、shuffle、batch等操作一样,都只是计算图中的一个计算节点。repeat只代表重复相同的处理过程,并不会记录前一个epoch的处理结果。
**4. 其他数据集操作**
除这些方法以外,数据集还提供了其他多种操作。例如:
- concatenate()将两个数据集顺序连接起来;
- take(N)从数据集中读取前N项数据;
- skip(N)在数据集中跳过前N项数据;
- flap_map()从多个数据集中轮流读取数据;
```python
# 1. 列举输入文件。训练和测试使用不同的数据
train_files = tf.train.match_filenames_once("output.tfrecords")
test_files = tf.train.match_filenames_once("output_test.tfrecords")
```
```python
# 2. 解析一个TFRecord的方法。
def parser(record):
features = tf.parse_single_example(
record,
features={
'image_raw':tf.FixedLenFeature([],tf.string),
'pixels':tf.FixedLenFeature([],tf.int64),
'label':tf.FixedLenFeature([],tf.int64)
})
decoded_images = tf.decode_raw(features['image_raw'],tf.uint8)
retyped_images = tf.cast(decoded_images, tf.float32)
images = tf.reshape(retyped_images, [784])
labels = tf.cast(features['label'],tf.int32)
#pixels = tf.cast(features['pixels'],tf.int32)
return images, labels
```
```python
# 3. 定义训练数据集
image_size = 299 # 定义神经网络输入层图片的大小。
batch_size = 100 # 定义组合数据batch的大小。
shuffle_buffer = 10000 # 定义随机打乱数据时buffer的大小。
# 定义读取训练数据的数据集。
dataset = tf.data.TFRecordDataset(train_files)
dataset = dataset.map(parser)
# 对数据集进行预处理,这里略去
# dataset = dataset.map(lambda image, label:(
# preprocess_for_train(image, image_size, image_size, None), label)
# 对数据进行shuffle和batching操作。这里省略了对图像做随机调整的预处理步骤。
dataset = dataset.shuffle(shuffle_buffer).batch(batch_size)
# 重复NUM_EPOCHS个epoch。间接指定训练的轮数
NUM_EPOCHS = 10
dataset = dataset.repeat(NUM_EPOCHS)
# 定义数据集迭代器。虽然定义数据集时没有直接使用placeholder来提供文件地址,但是
# tf.train.match_filenames_once方法得到的结果和与placeholder的机制类似,也需要
# 初始化,所以这里使用的是initializable_iterator。
iterator = dataset.make_initializable_iterator()
image_batch, label_batch = iterator.get_next()
```
```python
# 4. 定义神经网络的结构个优化过程。这里与7.3.4小节相同。
def inference(input_tensor, weights1, biases1, weights2, biases2):
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
return tf.matmul(layer1, weights2) + biases2
INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500
REGULARAZTION_RATE = 0.0001
TRAINING_STEPS = 5000
weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1))
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))
y = inference(image_batch, weights1, biases1, weights2, biases2)
# 计算交叉熵及其平均值
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=label_batch)
cross_entropy_mean = tf.reduce_mean(cross_entropy)
# 损失函数的计算
regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
regularaztion = regularizer(weights1) + regularizer(weights2)
loss = cross_entropy_mean + regularaztion
# 优化损失函数
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
```
```python
# 5. 定义测试用的数据集及相关
# 与训练时不同,测试数据的Dataset不需要经过随机翻转等预处理操作,
# 也不需要打乱顺序和重复多个epoch,这里使用与训练数据相同的parser
# 进行解析,调整分辨率到网络输入层大小,然后直接进行batching操作。
# 定义测试用的Dataset。
test_dataset = tf.data.TFRecordDataset(test_files)
test_dataset = test_dataset.map(parser)
test_dataset = test_dataset.batch(batch_size)
# 定义测试数据上的迭代器。
test_iterator = test_dataset.make_initializable_iterator()
test_image_batch, test_label_batch = test_iterator.get_next()
# 定义测试数据上的预测结果为logits值最大的分类。
test_logit = inference(test_image_batch, weights1, biases1, weights2, biases2)
predictions = tf.argmax(test_logit, axis=-1, output_type=tf.int32)
```
```python
# 6. 声明会话并运行神经网络的优化过程。
with tf.Session() as sess:
# 初始化变量。
sess.run((tf.global_variables_initializer(),
tf.local_variables_initializer()))
# 初始化训练数据的迭代器。
sess.run(iterator.initializer)
# 循环进行训练,直到数据集完成输入、抛出OutOfRangeError错误。
while True:
try:
sess.run(train_step)
except tf.errors.OutOfRangeError:
break
# 初始化测试数据的迭代器。
sess.run(test_iterator.initializer)
# 获取预测结果。
test_results = []
test_labels = []
while True:
try:
pred, label = sess.run([predictions, test_label_batch])
test_results.extend(pred)
test_labels.extend(label)
except tf.errors.OutOfRangeError:
break
# 计算准确率
correct = [float(y == y_) for (y, y_) in zip (test_results, test_labels)]
accuracy = sum(correct) / len(correct)
print("Test accuracy is:", accuracy)
```
```python
if i % 1000 == 0:
# 配置运行时需要记录的信息
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
# 运行时记录运行信息的proto
run_metadata = tf.RunMetadata()
# 将配置信息和记录运行的proto传入运行的过程,从而记录运行时每一个节点的时间、内存信息
_, loss_value, step = sess.run([train_op, loss, global_step],
feed_dict={x: xs, y_:ys},
options=run_options,
run_metadata=run_metadata)
# 将节点在运行时的信息写入日志文件
train_writer.add_run_metadata(run_metadata, 'step%03d'%i)
print("After %d training step(s), loss on training batch is %g" % (step, loss_value))
```
```python
# 1. 生成变量监控信息并定义生成监控信息日志的操作。
# 其中var给出了需要记录的张量,name给出了在可视化结果中显示的图发名称,这个名称一般与变量名一致。
def variable_summaries(var, name):
# 将生成监控信息的操作放到统一命名空间下
with tf.name_scope('summaries'):
# 通过tf.summary.histogram函数记录张量中元素的取值分布。对于给出的图表
# 名称和张量,tf.summary.histogram函数会生成一个Summary protocol buffer。
# 将Summary写入TensorBoard日志文件后,在HISTOGRAMS栏和DISTRIBUTION栏下
# 都会出现对应名称的图表。和TensorFlow中其他操作类似,
# tf.summary.histogram函数不会立刻被执行,只有当sess.run函数明确调用这
# 个操作时,TensorFlow才会具正生成并输出Summary protocol buffer。
# 下文将更加详细地介绍如何理解HISTOGRAMS栏和DISTRIBUTION栏下的信息。
tf.summary.histogram(name, var)
# 计算变量的平均值,并定义生成平均值信息日志的操作。记录变量平均值信息的日志标签名
# 为'mean/'+name,其中mean为命名空间,/是命名空间的分隔符,从图11.14中可以看到,
# 在相同命名空间中的监控指标会被整合到同一栏中;name则给出了当前监控指标属于哪一个变量。
mean = tf.reduce_mean(var)
tf.summary.scalar('mean/' + name, mean)
# 计算变量的标准差,并定义生成其日志的操作
stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
tf.summary.scalar('stddev/' + name, stddev)
```
```python
# 2. 生成一层全链接的神经网络。
def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):
# 将同一层神经网络放在一个统一的命名空间下
with tf.name_scope(layer_name):
# 声明神经网络边上的权重,并调用生成权重监控信息日志的函数。
with tf.name_scope('weights'):
weights = tf.Variable(tf.truncated_normal([input_dim, output_dim], stddev=0.1))
variable_summaries(weights, layer_name + '/weights')
# 声明神经网络的偏置项,并调用生成偏置项监控信息日志的函数。
with tf.name_scope('biases'):
biases = tf.Variable(tf.constant(0.0, shape=[output_dim]))
variable_summaries(biases, layer_name + '/biases')
with tf.name_scope('Wx_plus_b'):
preactivate = tf.matmul(input_tensor, weights) + biases
# 记录神经网络输出节点在经过激活的数之前的分布。
tf.summary.histogram(layer_name + '/pre_activations', preactivate)
activations = act(preactivate, name='activation')
# 记录神经网络输出节点在经过激活函数之后的分布。在图11.17中,对于layer1,因
# 为使用了ReLU函数作为激活函数,所以所有小于0的值部被设为了0。于是在激活后
# 的layer1/activations图上所有的值都是大于0的。而对于layer2,因为没有使用
# 激活函数,所以layer2/activations和layer2/pre_activations一样。
tf.summary.histogram(layer_name + '/activations', activations)
return activations
```
```python
# 3. 主函数
def main():
mnist = input_data.read_data_sets("../../datasets/MNIST_data", one_hot=True)
# 定义输入
with tf.name_scope('input'):
x = tf.placeholder(tf.float32, [None, 784], name='x-input')
y_ = tf.placeholder(tf.float32, [None, 10], name='y-input')
# 将输入向量还原成图片的像素矩阵,并通过tf.summary.image函数将当前的图片信息写入日志的操作
with tf.name_scope('input_reshape'):
image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])
tf.summary.image('input', image_shaped_input, 10)
hidden1 = nn_layer(x, 784, 500, 'layer1')
y = nn_layer(hidden1, 500, 10, 'layer2', act=tf.identity)
# 计算交叉熵并定义生成交叉熵监控日志的操作
with tf.name_scope('cross_entropy'):
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=y, labels=y_))
tf.summary.scalar('cross_entropy', cross_entropy)
with tf.name_scope('train'):
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
# 当前模型在当前给定数据上的正确率,并定义生成正确率监控日志的操作。如果在sess.run
# 时给定的数据训练batch,那么得到的正确率就是在这个训练batch上的正确率;如果给定的
# 数据为验证或者测试数据,那么得到的正确率就是在当前模型在验证或者测试数据上的正确率。
with tf.name_scope('accuracy'):
with tf.name_scope('correct_prediction'):
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
with tf.name_scope('accuracy'):
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.summary.scalar('accuracy', accuracy)
# 和TensorFlow其他操作类似,tf.summary.scalar、tf.summary.histogram和tf.summary.image
# 函数都不会立即执行,需要通过sess.run来明确调用这些函数。因为程序中定义的写日志操作
# 比较多,一一调用非常麻烦,所以TensorFlow提供了tf.summary.merge_all函数来整理所有的
# 日志生成操作。在TensorFlow程序执行的过程中只要运行这个操作就可以将代码中定义的所有
# 日志生成操作执行一次,从而将所有日志文件。
merged = tf.summary.merge_all()
with tf.Session() as sess:
# 初始化写日志的writer,并将当前TensorFlow计算图写入日志。
summary_writer = tf.summary.FileWriter(SUMMARY_DIR, sess.graph)
tf.global_variables_initializer().run()
for i in range(TRAIN_STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
# 运行训练步骤以及所有的日志生成操作,得到这次运行的日志。
summary, _ = sess.run([merged, train_step], feed_dict={x: xs, y_: ys})
# 将得到的所有日志写入日志文件,这样TensorBoard程序就可以拿到这次运行所对应的
# 运行信息。
summary_writer.add_summary(summary, i)
summary_writer.close()
```
```python
# 生成可视化最终输出层向量所需要的日志文件。
def visualisation(final_result):
# 使用一个新的变量来保存最终输出层向量的结果。因为embedding是通过TensorFlow
# 中变量完成的,所以PROJECTOR可视化的都是TensorFlow中的变量。于是这里要
# 新定义一个变量来保存输出层向量的取值。
y = tf.Variable(final_result, name=TENSOR_NAME)
summary_writer = tf.summary.FileWriter(LOG_DIR)
# 通过projector.ProjectorConfig类来帮助生成日志文件。
config = projector.ProjectorConfig()
# 增加一个需要可视化的embedding结果
embedding = config.embeddings.add()
# 指定这个embedding结果对应的TensorFlow变量名称
embedding.tensor_name = y.name
# 指定embedding结果所对应的原始数据信息。比如这里指定的就是每一张MNIST测试
# 图片对应的真实类别。在单词向量中可以是单词ID对应的单词。这个文件是可选的,
# 如果没有指定那么向量就没有标签。
embedding.metadata_path = META_FIEL
# 指定sprite图像。这个也是可选的,如果没有提供sprite图像,那么可视化的结果
# 每一个点就是一个小圆点,而不是具体的图片。
embedding.sprite.image_path = SPRITE_FILE
# 在提供sprite图像时,通过single_image_dim可以指定单张图片的大小。
# 这将用于从sprite图像中截取正确的原始图片。
embedding.sprite.single_image_dim.extend([28,28])
# 将PROJECTOR所需要的内容写入日志文件。
projector.visualize_embeddings(summary_writer, config)
# 生成会话,初始化新声明的变量并将需要的日志信息写入文件。
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
saver.save(sess, os.path.join(LOG_DIR, "model"), TRAINING_STEPS)
summary_writer.close()
```