【不周山之数据科学】TensorFlow 学习笔记

本文是我学习深度学习的笔记,来自网上的各类 jupyter notebook。


更新历史

  • 2017.05.31: 开始更新

快速入门

TensorFlow 是一个基于图计算的通用系统,常常被用于进行机器学习等任务。

TensorFlow 由张量(tensor)而得名,张量是多维的数组。一个向量是一维数组,我们称为一阶张量;一个矩阵是二维数组,我们称为二阶张量。名字中的 flow 表示计算的过程是基于图的,数据在图中流动。神经网络的训练和推断包含计算图中许多节点的矩阵计算的传播(propagation)

在 TensorFlow 中搞事的流程大概是:创建张量 -> 添加计算操作 -> 执行。很重要的一点是定义这些操作时,计算并不会立即执行,TensorFlow 在所有的操作添加完成后,会优化计算图,决定如何计算,最后才生成各种数据。正因如此,TensorFlow 中的 tensor 可以看作是一个占位符,等数据到来,然后执行计算。

我们来看看如何用 TensorFlow 做向量相加,代码如下:

from __future__ import print_function
import tensorflow as tf
# TensorFlow 中的操作都需要在 Session 的上下文中进行,Session 本身保存计算图的信息(张量和操作)
with tf.Session():
input1 = tf.constant([1.0, 1.0, 1.0, 1.0])
input2 = tf.constant(2.0, shape=[4])
input3 = tf.constant(3.0, shape=[4])
output = tf.add(tf.add(input1, input2), input3)
# 具体的计算在执行这一句时进行,因为 result 的值需要计算才可以得到,前面都是在定义计算图
result = output.eval()
print("result: ", result)

处理完向量,我们来看看矩阵,其实也差不多

import tensorflow as tf
import numpy as np
# 矩阵相加
with tf.Session():
input1 = tf.constant(1.0, shape=[2, 3])
input2 = tf.constant(np.reshape(np.arange(1.0, 7.0, dtype=np.float32), (2, 3)))
output = tf.add(input1, input2)
print(output.eval())
with tf.Session():
input_features = tf.constant(np.reshape([1, 0, 0, 1], (1, 4)).astype(np.float32))
weights = tf.constant(np.random.randn(4, 2).astype(np.float32))
output = tf.matmul(input_features, weights)
print("Input:")
print(input_features.eval())
print("Weights:")
print(weights.eval())
print("Output:")
print(output.eval())

前面我们都是用的 constant 常量,接下来我们搞一搞变量

#@test {"output": "ignore"}
import tensorflow as tf
import numpy as np
with tf.Session() as sess:
# 设置俩变量 total 和 weights,会不停变化
total = tf.Variable(tf.zeros([1, 2]))
weights = tf.Variable(tf.random_uniform([1,2]))
# 初始化刚才定义的变量
tf.global_variables_initializer().run()
# 更新数值,但是这里并不会真的计算
update_weights = tf.assign(weigths, tf.random_uniform([1, 2], -1.0, 1.0))
update_total = tf.assign(total, tf.add(total, weights))
for _ in range(5):
# 这里要先更新 weights 再更新 total
sess.run(update_weights)
sess.run(update_total)
print(weights.eval(), total.eval())

这里我们注意是先建模再计算这个流程即可。

一个简单的神经网络

我们来构造一个非常简单的神经网络,来计算 x 和 y 这两个变量的线性回归。这个函数会为我们随机生成的带噪声的线性数据找到最合适的 $w_1$ 和 $w_2$,即满足 $y = w_2x+w_1$ 。代码如下:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# 设置带噪声的线性数据
num_examples = 50
# 这里会生成一个完全线性的数据
X = np.array([np.linspace(-2, 4, num_examples), np.linspace(-6, 6, num_examples)])
# 数据展示
# plt.figure(figsize=(4,4))
# plt.scatter(X[0], X[1])
# plt.show
# 这里给数据增加噪声
X += np.random.randn(2, num_examples)
# 数据展示
# plt.figure(figsize=(4,4))
# plt.scatter(X[0], X[1])
# plt.show
# 我们的目标就是通过学习,找到一条拟合曲线,去还原最初的线性数据
# 把数据分离成 x 和 y
x, y = X
# 添加固定为 1 的 bias
x_with_bias = np.array([(1., a) for a in x]).astype(np.float32)
# 用来记录每次迭代的 loss,之后用于展示结果
losses = []
# 迭代次数
training_steps = 50
# 学习率,也叫做步长,表示我们在梯度下降时每次迭代所前进的长度,过大则学不到准确的值,过小则训练太慢
learning_rate = 0.002
# TensorFlow 中所有的代码都需要在 session 中
with tf.Session() as sess:
# 设置所有的张量,变量和操作
# 输入层是 x 值和 bias 节点
input = tf.constant(x_with_bias)
# target 是 y 的值,需要被调整成正确的尺寸(就是转置一下)
target = tf.constant(np.transpose([y]).astype(np.float32))
# weights 是变量,每次循环都会变,这里直接随机初始化(高斯分布,均值 0,标准差 0.1)
weights = tf.Variable(tf.random_normal([2, 1], 0, 0.1))
# 初始化所有的变量
tf.global_variables_initializer().run()
# 设置循环中所要做的全部操作
# 对于所有的 x,根据现有的 weights 来产生对应的 y 值,也就是计算 y = w2 * x + w1 * bias
yhat = tf.matmul(input, weights)
# 计算误差,也就是预计的 y 和真实的 y 的区别
yerror = tf.subtract(yhat, target)
# 我们想要最小化 L2 损失,是误差的平方,会惩罚大误差,放过小误差
loss = tf.nn.l2_loss(yerror)
# 上面的 loss 函数相当于
# loss = 0.5 * tf.reduce_sum(tf.multiply(yerror, yerror))
# 执行梯度下降
# 更新 weights,比如 weights += grads * learning_rate
# 使用偏微分更新 weights
update_weights = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)
# 上面的梯度下降相当于
# gradient = tf.reduce_sum(tf.transpose(tf.multiply(input, yerror)), 1, keep_dims=True)
# update_weights = tf.assign_sub(weights, learning_rate * gradient)
# 现在我们定义了所有的张量,也初始化了所有操作(每次执行梯度下降优化)
for _ in range(training_steps):
# 重复跑,更新变量
update_weights.run()
# 如果没有用 tf.train.GradientDescentOptimizer,就要用下面的方式
# sess.run(update_weights)
# 记录每次迭代的 loss
losses.append(loss.eval())
# 训练结束
betas = weights.eval()
yhat = yhat.eval()
# 展示训练趋势
fig, (ax1, ax2) = plt.subplots(1, 2)
plt.subplots_adjust(wspace=.3)
fig.set_size_inches(10, 4)
ax1.scatter(x, y, alpha=.7)
ax1.scatter(x, np.transpose(yhat)[0], c="g", alpha=.6)
line_x_range = (-4, 6)
ax1.plot(line_x_range, [betas[0] + a * betas[1] for a in line_x_range], "g", alpha=.6)
ax2.plot(range(0, training_steps), losses)
ax2.set_ylabel("Loss")
ax2.set_xlabel("Training steps")
plt.show()

从零开始上手 MNIST 数据集

这是 Tensorflow 官方 Docker 镜像中的最后一篇教程,主要介绍如何利用 tf 完成手写数字的识别,这里我把 jupyter 的代码稍加改动(并加了些注释),方便大家在本地使用。

注:详细的解释都在注释里了,这里就不再赘述。

# -*- coding: utf-8 -*-
import gzip, binascii, struct, numpy
import matplotlib.pyplot as plt
import numpy as np
import os
from six.moves.urllib.request import urlretrieve
import tensorflow as tf
# 这里需要翻墙,不然下载巨慢
SOURCE_URL = 'http://yann.lecun.com/exdb/mnist/'
WORK_DIRECTORY = "./mnist-data"
# 如果下载好了,那么就不会再次下载
def maybe_download(filename):
"""A helper to download the data files if not present."""
if not os.path.exists(WORK_DIRECTORY):
os.mkdir(WORK_DIRECTORY)
filepath = os.path.join(WORK_DIRECTORY, filename)
if not os.path.exists(filepath):
filepath, _ = urlretrieve(SOURCE_URL + filename, filepath)
statinfo = os.stat(filepath)
print('Successfully downloaded', filename, statinfo.st_size, 'bytes.')
else:
print('Already downloaded', filename)
return filepath
# 这里把所有的训练数据都搞下来
train_data_filename = maybe_download('train-images-idx3-ubyte.gz')
train_labels_filename = maybe_download('train-labels-idx1-ubyte.gz')
test_data_filename = maybe_download('t10k-images-idx3-ubyte.gz')
test_labels_filename = maybe_download('t10k-labels-idx1-ubyte.gz')
# 如果遇到 MacOS 的 Python as a framework 的问题,参考
# https://stackoverflow.com/questions/29433824/unable-to-import-matplotlib-pyplot-as-plt-in-virtualenv
# 这里我们先看看数据集里有什么,只是一个展示,并不会对图片进行预处理
def sanity_check():
with gzip.open(test_data_filename) as f:
# Print the header fields.
for field in ['magic number', 'image count', 'rows', 'columns']:
# struct.unpack reads the binary data provided by f.read.
# The format string '>i' decodes a big-endian integer, which
# is the encoding of the data.
print(field, struct.unpack('>i', f.read(4))[0])
# Read the first 28x28 set of pixel values.
# Each pixel is one byte, [0, 255], a uint8.
buf = f.read(28 * 28)
image = numpy.frombuffer(buf, dtype=numpy.uint8)
# Print the first few values of image.
print('First 10 pixels:', image[:10])
# We'll show the image and its pixel value histogram side-by-side.
# 输出原始图片和直方图,来看看具体的样子
_, (ax1, ax2) = plt.subplots(1, 2)
# To interpret the values as a 28x28 image, we need to reshape
# the numpy array, which is one dimensional.
ax1.imshow(image.reshape(28, 28), cmap=plt.cm.Greys)
ax2.hist(image, bins=20, range=[0,255])
plt.show()
# 这里是把 [0, 255] 映射到 [-0.5, 0.5] 之后的展示
# Let's convert the uint8 image to 32 bit floats and rescale
# the values to be centered around 0, between [-0.5, 0.5].
#
# We again plot the image and histogram to check that we
# haven't mangled the data.
scaled = image.astype(numpy.float32)
scaled = (scaled - (255 / 2.0)) / 255
_, (ax1, ax2) = plt.subplots(1, 2)
ax1.imshow(scaled.reshape(28, 28), cmap=plt.cm.Greys)
ax2.hist(scaled, bins=20, range=[-0.5, 0.5])
plt.show()
# 这里读取 Label,也是一个测试而已
with gzip.open(test_labels_filename) as f:
# Print the header fields.
for field in ['magic number', 'label count']:
print(field, struct.unpack('>i', f.read(4))[0])
print('First label:', struct.unpack('B', f.read(1))[0])
# 简单显示一下,然后进行之后的步骤
sanity_check()
# 处理图片数据
IMAGE_SIZE = 28
PIXEL_DEPTH = 255
# 这个函数会提取并处理数据
def extract_data(filename, num_images):
"""Extract the images into a 4D tensor [image index, y, x, channels].
For MNIST data, the number of channels is always 1.
Values are rescaled from [0, 255] down to [-0.5, 0.5].
"""
print('Extracting', filename)
with gzip.open(filename) as bytestream:
# Skip the magic number and dimensions; we know these values.
bytestream.read(16)
buf = bytestream.read(IMAGE_SIZE * IMAGE_SIZE * num_images)
data = numpy.frombuffer(buf, dtype=numpy.uint8).astype(numpy.float32)
data = (data - (PIXEL_DEPTH / 2.0)) / PIXEL_DEPTH
data = data.reshape(num_images, IMAGE_SIZE, IMAGE_SIZE, 1)
return data
train_data = extract_data(train_data_filename, 60000)
test_data = extract_data(test_data_filename, 10000)
# 这里把处理后的输出展示下
print('Training data shape', train_data.shape)
_, (ax1, ax2) = plt.subplots(1, 2)
ax1.imshow(train_data[0].reshape(28, 28), cmap=plt.cm.Greys)
ax2.imshow(train_data[1].reshape(28, 28), cmap=plt.cm.Greys)
plt.show()
# 接下来处理标签,我们需要把类别处理成向量,如果是第二类,那么对应 [0,1,0,...,0],即第二个位置为 1
NUM_LABELS = 10
def extract_labels(filename, num_images):
"""Extract the labels into a 1-hot matrix [image index, label index]."""
print('Extracting', filename)
with gzip.open(filename) as bytestream:
# Skip the magic number and count; we know these values.
bytestream.read(8)
buf = bytestream.read(1 * num_images)
labels = numpy.frombuffer(buf, dtype=numpy.uint8)
# Convert to dense 1-hot representation.
return (numpy.arange(NUM_LABELS) == labels[:, None]).astype(numpy.float32)
train_labels = extract_labels(train_labels_filename, 60000)
test_labels = extract_labels(test_labels_filename, 10000)
# 同样测试一下数据
print('Training labels shape', train_labels.shape)
print('First label vector', train_labels[0])
print('Second label vector', train_labels[1])
# 这里我们把数据分成训练、测试和验证集
VALIDATION_SIZE = 5000
validation_data = train_data[:VALIDATION_SIZE, :, :, :]
validation_labels = train_labels[:VALIDATION_SIZE]
train_data = train_data[VALIDATION_SIZE:, :, :, :]
train_labels = train_labels[VALIDATION_SIZE:]
train_size = train_labels.shape[0]
print('Validation shape', validation_data.shape)
print('Train size', train_size)
# 这里开始定义模型
# 从原始输入开始,进行卷积(convolution)和池化(max pooling)处理,在全连接层之前会用 ReLU
# 作为激活函数,最后用 softmax 来处理输出,把类别信息转化成概率,训练的时候使用 Dropout
#
# 准备模型可以分三步
# 1. 定义变量,来保存我们要训练的权重 weights
# 2. 定义模型的图结构
# 3. 把模型的图分别用于训练、测试和验证(复制几份)
# 先处理好变量
# 从效率的角度考虑,我们会把样本分组,这里是一个组的样本数量
BATCH_SIZE = 60
# 因为是灰度图,所以只有一个通道 channel
NUM_CHANNELS = 1
# 固定随机种子,保证每次的结果一致(不然没办法验证数据和模型)
SEED = 42
# 我们在这里把训练数据和类别标签『喂』给模型,不过这里只是一个占位符(placeholder)
# 真正训练的时候,这些节点在每一步会获取批量数据
train_data_node = tf.placeholder(
tf.float32,
shape=(BATCH_SIZE, IMAGE_SIZE, IMAGE_SIZE, NUM_CHANNELS))
train_labels_node = tf.placeholder(tf.float32,
shape=(BATCH_SIZE, NUM_LABELS))
# 对于验证和测试数据,直接保存到一个常量节点里即可(不存在训练的过程,不需要是变量)
validation_data_node = tf.constant(validation_data)
test_data_node = tf.constant(test_data)
# 下面的变量保存着所有的需要训练的权重。后面的参数定义了这些变量的初始化条件
# 用高斯分布初始化卷积的 weights
conv1_weights = tf.Variable(
tf.truncated_normal([5, 5, NUM_CHANNELS, 32], # 5x5 filter, depth 32.
stddev=0.1,
seed=SEED))
# 初始的 bias 为 0
conv1_biases = tf.Variable(tf.zeros([32]))
# 第二层的卷积权重,32 个输入(对应上面的 32),然后下面是 64 维
conv2_weights = tf.Variable(
tf.truncated_normal([5, 5, 32, 64],
stddev=0.1,
seed=SEED))
# 同理,bias 也是 64 维,但是这里用 0.1
conv2_biases = tf.Variable(tf.constant(0.1, shape=[64]))
# 然后是一个全连接的网络,共 512 维,为什么呢,因为我们有卷积和池化的存在,所以是 32*64/4
# (?这里我也不是很确定)
fc1_weights = tf.Variable( # fully connected, depth 512.
tf.truncated_normal([IMAGE_SIZE // 4 * IMAGE_SIZE // 4 * 64, 512],
stddev=0.1,
seed=SEED))
fc1_biases = tf.Variable(tf.constant(0.1, shape=[512]))
fc2_weights = tf.Variable(
tf.truncated_normal([512, NUM_LABELS],
stddev=0.1,
seed=SEED))
fc2_biases = tf.Variable(tf.constant(0.1, shape=[NUM_LABELS]))
print('变量设置完毕')
# 定义好了各种需要训练的变量,我们可以在 TensorFlow 图中把这些变量连起来了
# 这里我们用一个函数来返回我们需要的 tf graph,这里有一个参数来控制是训练还是其他
# 如果是训练,我们需要使用 dropout
def model(data, train=False):
"""模型定义"""
# 2D 卷积,使用相同 padding,意思是输入的 feature 大小和输出的一致,
# strides 是一个四维数组 [image index, y, x, depth]
conv = tf.nn.conv2d(data,
conv1_weights,
strides=[1, 1, 1, 1],
padding='SAME')
# 对卷积和偏置做 ReLU 操作
# Bias and rectified linear non-linearity.
relu = tf.nn.relu(tf.nn.bias_add(conv, conv1_biases))
# 池化,这里我们的 pooling window 是 2,每个 stride 是 2
# Max pooling. The kernel size spec ksize also follows the layout of
# the data. Here we have a pooling window of 2, and a stride of 2.
pool = tf.nn.max_pool(relu,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME')
conv = tf.nn.conv2d(pool,
conv2_weights,
strides=[1, 1, 1, 1],
padding='SAME')
relu = tf.nn.relu(tf.nn.bias_add(conv, conv2_biases))
pool = tf.nn.max_pool(relu,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME')
# 把 feature map 转为 2D 矩阵,并传给全连接网络
pool_shape = pool.get_shape().as_list()
reshape = tf.reshape(
pool,
[pool_shape[0], pool_shape[1] * pool_shape[2] * pool_shape[3]])
# Fully connected layer. Note that the '+' operation automatically
# broadcasts the biases.
hidden = tf.nn.relu(tf.matmul(reshape, fc1_weights) + fc1_biases)
# Add a 50% dropout during training only. Dropout also scales
# activations such that no rescaling is needed at evaluation time.
if train:
hidden = tf.nn.dropout(hidden, 0.5, seed=SEED)
return tf.matmul(hidden, fc2_weights) + fc2_biases
# 定义了图的基本结构,我们就可以分别为 训练、测试和验证来提取模型了(也会根据不同的类型做一些自定义)
# train_prediction 保存训练的图,使用 cross-entropy loss 和 weight regularization
# 我们也会在训练的过程中调整学习率(通过 exponential_decay 操作来完成,会使用 MomentumOptimizer)
# 验证和测试的图比较简单,我们只需要使用验证和测试集作为输入,用 softmax 分类器作为输出
# 训练的计算
# Training computation: logits + cross-entropy loss.
logits = model(train_data_node, True)
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
labels=train_labels_node, logits=logits))
# L2 正则化
# L2 regularization for the fully connected parameters.
regularizers = (tf.nn.l2_loss(fc1_weights) + tf.nn.l2_loss(fc1_biases) +
tf.nn.l2_loss(fc2_weights) + tf.nn.l2_loss(fc2_biases))
# Add the regularization term to the loss.
loss += 5e-4 * regularizers
# Optimizer: set up a variable that's incremented once per batch and
# controls the learning rate decay.
batch = tf.Variable(0)
# Decay once per epoch, using an exponential schedule starting at 0.01.
learning_rate = tf.train.exponential_decay(
0.01, # Base learning rate.
batch * BATCH_SIZE, # Current index into the dataset.
train_size, # Decay step.
0.95, # Decay rate.
staircase=True)
# Use simple momentum for the optimization.
optimizer = tf.train.MomentumOptimizer(learning_rate,
0.9).minimize(loss,
global_step=batch)
# Predictions for the minibatch, validation set and test set.
train_prediction = tf.nn.softmax(logits)
# We'll compute them only once in a while by calling their {eval()} method.
validation_prediction = tf.nn.softmax(model(validation_data_node))
test_prediction = tf.nn.softmax(model(test_data_node))
# 准备好了训练、测试和验证的模型之后,我们就可以来真正执行训练了。
# 所有的操作都需要在 session 中,在 python 中像是
# with tf.Session() as s:
# ...training / test / evaluation loop...
# 但是我们这里想要保持 session 方便我们去探索训练的过程,使用 InteractiveSession
# 我们先创建一个 session 并初始化我们刚才定义的变量
s = tf.InteractiveSession()
# Use our newly created session as the default for subsequent operations.
s.as_default()
# 初始化刚才定义的变量
tf.global_variables_initializer().run()
# 我们现在可以开始训练了,这里我们用 minibatch 的方法(而不是一次只训练一个样本)
BATCH_SIZE = 60
# 提取第一个 batch 的数据和标签
# Grab the first BATCH_SIZE examples and labels.
batch_data = train_data[:BATCH_SIZE, :, :, :]
batch_labels = train_labels[:BATCH_SIZE]
# This dictionary maps the batch data (as a numpy array) to the
# node in the graph it should be fed to.
feed_dict = {train_data_node: batch_data,
train_labels_node: batch_labels}
# Run the graph and fetch some of the nodes.
_, l, lr, predictions = s.run(
[optimizer, loss, learning_rate, train_prediction],
feed_dict=feed_dict)
print(predictions[0])
# The highest probability in the first entry.
print('First prediction', numpy.argmax(predictions[0]))
# But, predictions is actually a list of BATCH_SIZE probability vectors.
print(predictions.shape)
# So, we'll take the highest probability for each vector.
print('All predictions', numpy.argmax(predictions, 1))
print('Batch labels', numpy.argmax(batch_labels, 1))
correct = numpy.sum(numpy.argmax(predictions, 1) == numpy.argmax(batch_labels, 1))
total = predictions.shape[0]
print(float(correct) / float(total))
confusions = numpy.zeros([10, 10], numpy.float32)
bundled = zip(numpy.argmax(predictions, 1), numpy.argmax(batch_labels, 1))
for predicted, actual in bundled:
confusions[predicted, actual] += 1
plt.grid(False)
plt.xticks(numpy.arange(NUM_LABELS))
plt.yticks(numpy.arange(NUM_LABELS))
plt.imshow(confusions, cmap=plt.cm.jet, interpolation='nearest')
def error_rate(predictions, labels):
"""Return the error rate and confusions."""
correct = numpy.sum(numpy.argmax(predictions, 1) == numpy.argmax(labels, 1))
total = predictions.shape[0]
error = 100.0 - (100 * float(correct) / float(total))
confusions = numpy.zeros([10, 10], numpy.float32)
bundled = zip(numpy.argmax(predictions, 1), numpy.argmax(labels, 1))
for predicted, actual in bundled:
confusions[predicted, actual] += 1
return error, confusions
# 这里训练 n 轮,每轮都是 minibatch
train_round = 3
for i in range(train_round):
print("Training Round ", i+1 )
# Train over the first 1/4th of our training set.
steps = train_size // BATCH_SIZE
for step in range(steps):
# Compute the offset of the current minibatch in the data.
# Note that we could use better randomization across epochs.
offset = (step * BATCH_SIZE) % (train_size - BATCH_SIZE)
batch_data = train_data[offset:(offset + BATCH_SIZE), :, :, :]
batch_labels = train_labels[offset:(offset + BATCH_SIZE)]
# This dictionary maps the batch data (as a numpy array) to the
# node in the graph it should be fed to.
feed_dict = {train_data_node: batch_data,
train_labels_node: batch_labels}
# Run the graph and fetch some of the nodes.
_, l, lr, predictions = s.run(
[optimizer, loss, learning_rate, train_prediction],
feed_dict=feed_dict)
# Print out the loss periodically.
if step % 100 == 0:
error, _ = error_rate(predictions, batch_labels)
print('Step %d of %d' % (step, steps))
print('Mini-batch loss: %.5f Error: %.5f Learning rate: %.5f' % (l, error, lr))
print('Validation error: %.1f%%' % error_rate(
validation_prediction.eval(), validation_labels)[0])
test_error, confusions = error_rate(test_prediction.eval(), test_labels)
print('Test error: %.1f%%' % test_error)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.grid(False)
plt.xticks(numpy.arange(NUM_LABELS))
plt.yticks(numpy.arange(NUM_LABELS))
plt.imshow(confusions, cmap=plt.cm.jet, interpolation='nearest');
for i, cas in enumerate(confusions):
for j, count in enumerate(cas):
if count > 0:
xoff = .07 * len(str(count))
plt.text(j-xoff, i+.2, int(count), fontsize=9, color='white')
plt.show()
plt.xticks(numpy.arange(NUM_LABELS))
plt.hist(numpy.argmax(test_labels, 1))
plt.show()

官方文档阅读笔记

使用 TensorFlow,你必须明白 TensorFlow

  • 使用(graph)来表示计算任务
  • 在被称之为会话(Session)的上下文(context)中执行图
  • 使用 tensor 表示数据
  • 通过变量(Variable)维护状态
  • 使用 feed 和 fetch 可以为任意的操作(arbitrary operation)赋值或者从其中获取数据

TensorFlow 是一个编程系统,使用图来表示计算任务。图中的节点被称之为 op (operation 的缩写)。一个 op 获得 0 个或多个 Tensor,执行计算,产生 0 个或多个 Tensor。每个 Tensor 是一个类型化的多维数组。例如,你可以将一小组图像集表示为一个四维浮点数数组,这四个维度分别是 [batch, height, width, channels]

一个 TensorFlow 图描述了计算的过程。为了进行计算,图必须在会话里被启动。会话将图的 op 分发到诸如 CPU 或 GPU 之类的设备上,同时提供执行 op 的方法。这些方法执行后,将产生的 tensor 返回。在 Python 语言中, 返回的 tensor 是 numpy ndarray 对象;在 C 和 C++ 语言中,返回的 tensor 是 tensorflow::Tensor 实例。

  • 计算图
    • TensorFlow 程序通常被组织成一个构建阶段和一个执行阶段。在构建阶段,op 的执行步骤被描述成一个图。在执行阶段,使用会话执行执行图中的 op。例如,通常在构建阶段创建一个图来表示和训练神经网络,然后在执行阶段反复执行图中的训练 op。
    • TensorFlow 支持 C, C++, Python 编程语言。目前,TensorFlow 的 Python 库更加易用,它提供了大量的辅助函数来简化构建图的工作,这些函数尚未被 C 和 C++ 库支持.
    • 三种语言的会话库 (session libraries) 是一致的。
  • 构建图
    • 构建图的第一步,是创建源 op (source op)。源 op 不需要任何输入,例如常量(Constant)。源 op 的输出被传递给其它 op 做运算
    • Python 库中,op 构造器的返回值代表被构造出的 op 的输出,这些返回值可以传递给其它 op 构造器作为输入
    • TensorFlow Python 库有一个默认图 (default graph), op 构造器可以为其增加节点。这个默认图对许多程序来说已经足够用了

在实现上,TensorFlow 将图形定义转换成分布式执行的操作,以充分利用可用的计算资源(如 CPU 或 GPU). 一般你不需要显式指定使用 CPU 还是 GPU,TensorFlow 能自动检测。如果检测到 GPU,TensorFlow 会尽可能地利用找到的第一个 GPU 来执行操作。

如果机器上有超过一个可用的 GPU,除第一个外的其它 GPU 默认是不参与计算的。为了让 TensorFlow 使用这些 GPU,你必须将 op 明确指派给它们执行。with...Device 语句用来指派特定的 CPU 或 GPU 执行操作:

with tf.Session() as sess:
with tf.device("/gpu:1"):
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.],[2.]])
product = tf.matmul(matrix1, matrix2)
...

设备用字符串进行标识。目前支持的设备包括:

  • “/cpu:0”: 机器的 CPU
  • “/gpu:0”: 机器的第一个 GPU,如果有的话
  • “/gpu:1”: 机器的第二个 GPU,以此类推

具体可见 使用 GPUs

文档的其他部分涉及到具体的算法细节,这里不再深究。

GPU 相关

  • 最合适测试 CUDA 的代码是使用 log_device_placement 参数
    • sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
    • print sess.run(c)
    • 观察日志输出是否有 gpu0, gpu1
  • 检测 GPU 的使用率 nvidia-smi -q -g 0 -d UTILIZATION -1
  • Nvidia Docker
    • TODO 需要看如何配置 docker 所能分配的 gpu
  • tensorflow 在训练时默认占用所有 GPU 的显存,配置的方式有
    • 构造 tf.Session() 时配置参数(这里是按照百分比来选择),如
      • gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)
      • sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
      • per_process_gpu_memory_fraction 指定每个 GPU 进程中使用显存的上限,但只能作用于所有 GPU,不能根据不同 GPU 单独配置
    • 设置显存根据需求增长
      • config = tf.ConfigProto()
      • config.gpu_options.allow_growth=True
      • sess = tf.Session(config=config)
    • 在执行训练脚本前使用 export CUDA_VISIBLE_DEVICES=1 来限制可见的 GPU 数目,如果是 python 脚本,可以用 CUDA_VISIBLE_DEVICES=1 python my_script.py,如果想用两个卡,则是 CUDA_VISIBLE_DEVICES=0,1,如果禁用 GPU,则是 CUDA_VISIBLE_DEVICES=""
    • 也可以在 Python 代码中进行设置
      • import os
      • os.environ["CUDA_VISIBLE_DEVICES"] = "0"
    • 如果需要同时限制显存大小,也按需增长,那么可以这样
      • os.environ["CUDA_VISIBLE_DEVICES"] = '0' 指定第一块 GPU 可用
      • config = tf.ConfigProto()
      • config.gpu_options.per_process_gpu_memory_fraction = 0.5 最多只能占用指定 gpu 50% 显存
      • config.gpu_options.allow_growth = True 程序按需申请内存
      • sess = tf.Session(config = config)
    • 需要注意的是,虽然代码或配置层面设置了对显存占用百分比阈值,但实际中如果达到了,程序有需要的话还是会突破的,以上的显存限制仅仅为了跑小数据集时避免对显存的浪费

Nvidia Docker

  • Github

  • 在 Nvidia Docker 层限制 GPU 资源

    • NV_GPU=0,1 nvidia-docker run -it nvidia/cuda nvidia-smi
    • 弄清楚 nvidia_uvm 是个啥
    • nvidia-docker 相当于 docker run --device=/dev/nvidiactl --device=/dev/nvidia-utm --device=/dev/nvidia0
  • 可以做到资源隔离,具体要测试下,看看提供了什么工具

参考链接

捧个钱场?