TensorFlow 概念的解析(i.e. 缘由)

TensorFlow 基本概念解析

—— TensorFlow Machine Learning Cookbook

TensorFlow 中的基本概念解析

  1. 声明张量

    tensorflow 中的主要数据结构,用来操作计算图。可以把变量或者占位符声明为张量。

    一旦创建好张量,就可以通过 tf.Varaiable() 函数封装张量作为变量,更多细节如下。
    创建张量并不一定得用 tensorflow 的内建函数,可以使用 tf.convert_to_tensor() 函数将任意 numpy 数组转换为 python 列表,或者将常量转换为一个张量。

    注意,tf.convert_to_tensor() 函数也可以接受张量作为输入。

  2. 占位符 和 变量

    使用 tensorflow 计算图的关键工具是占位符和变量。务必注意两者的区别,和使用情况。

    使用数据的关键点在于搞清楚到底是占位符还是变量。

    • 变量,是 tensorflow 机器学习算法的参数;

    • 占位符,是 tensorflow 对象,用于表示输入输出数据的格式,允许传入指定类型和形状的数据。

    在 tensorflow 中,tf.Variable() 函数创建变量,过程是输入一个张量,返回一个变量。变量声明后需要初始化。

    占位符仅仅声明数据位置,用于传入数据到计算图。占位符通过会话(Session)的 feed_dict 参数获取数据。

  3. 操作(计算)矩阵

    理解 tensorflow 如何操作矩阵,对于理解计算图中数据的流动来说 非常重要。
    tensorflow 提供数值计算工具,并把这些计算添加到计算图中。这些部分对于简单的矩阵计算来说看似有点重,tensorflow 增加这些矩阵操作到计算图进行张量计算。

  4. 声明操作

    tensorflow 计算图的其他操作

    • 开始(会话操作):除了标准数值计算外,tensorflow 提供了很多其他操作。在使用之前,按照惯例创建一个计算图会话。

      import tensorflow as tf
      sess = tf.Session()
      
    • tensorflow 张量基本操作有 add(), sub(), mul(), 和 div()

      print(sess.run(tf.div(3, 4)))
      
    • 知道在计算图中应用什么函数合适是最重要的。大部分情况下,我们关心预处理函数,但也通过组合预处理函数生成许多自定义函数。

      例如: 3 x 2 − x + 10 3x^2 - x + 10 3x2x+10

      def custom_polynomial(vaule):
          return (tf.sub(3*tf.square(value), value) + 10)
      print(sess.run(custom_polynomial(11)))
      
      # 363
      
  5. 实现激励函数

    激励函数是使用所有神经网络算法的必备“神器”。目的是,增强神经网络的表达能力。

tensorflow 进阶

  1. 计算图中的操作

    现在可以把这些对象表示成计算图,下面介绍计算图中对象的操作。

    # 首先,导入tensorflow,创建一个 session(会话),开始一个计算图
    import tensorflow as tf
    sess = tf.Session()
    
    # 1. 声明张量和占位符
    import numpy as np
    x_vals = np.array([1., 3., 5., 7., 9.])
    x_data = tf.placeholder(tf.float32)
    m_const = tf.constant(3.)
    my_product = tf.mul(x_data, m_const)
    # 2. 传入数据并计算
    for x_val in x_vals:
         print(sess.run(my_product, feed_dict={x_data: x_val}))
    
  2. tensorflow 的嵌入 Layer

    下面,我们将用两个矩阵以占位符,然后做加法。传入两个矩阵(三维 numpy 数组)。

    import tensorflow as tf
    sess = tf.Session()
    

    知道数据传入后是如何改变形状是非常重要的。我们将传入两个形状为3x5的 numpy 数组,然后每个矩阵乘以常量矩阵 (5x1), 返回一个 3x1 的矩阵。紧接着再乘以 1x1 矩阵,返回结果矩阵仍然为 3x1。最后,加上一个 3x1 的矩阵。

    # 1. 创建数据和占位符
    my_array = np.array([1.,3.,5.,7.,9.],
                        [-2.,0.,2.,4.,6],
                        [-6.,-3.,0.,3.,6.])
    x_vals = np.array([my_array, my_array+1])
    x_data = tf.placeholder(tf.float32, shape=(3,5))
    
    # 2. 接着,创建矩阵乘法和加法中要用到的常量矩阵
    m1 = tf.constant([[1.],[0.],[-1.],[2.],[4.]])
    m2 = tf.constant([2.])
    a1 = tf.constant([10.])
    
    # 3. 现在声明操作,表示成计算图
    prod1 = tf.matmul(x_data, m1)
    prod2 = tf.matmul(prod1, m2)
    add1 = tf.add(prod2, a1)
    
    # 4. 最后,通过计算图赋值:
    for x_val in x_vals:
        print(sess.run(add1, feed_dict={x_data: x_val}))
    
    
  3. tensorflow 的多层 layer

    目前,我们已经学完在同一个计算图中进行多个操作,接下来将讲述如何连接传播数据的多层 Layer。

    在这节,将介绍如何更好地连接多层 Layer,包括自定义 Layer。
    这里给出一个例子(数据是生成随机图片数据),以更好地理解不同类型的操作和如何利用内建层 Layer 进行计算。我们通过对 2D 图象进行滑动窗口平均,然后通过自定义操作层 Layer 返回结果。

    在这节,我们将会看到 tensorflow 的计算图太大,导致无法完整查看。因此,对各层 Layer 和操作进行层级命名管理。

    import tensorflow as tf
    import numpy as np
    sess = tf.Session()
    
    # 1. 通过 numpy 创建 2D 图象, 4x4 像素图片。我们将创建成 4 维。 注意,tensorflow 的图象函数是处理 4 维图片的 [图片数量,高度,宽度,颜色通道]。这里是一张图片,单颜色通道,所以设置为 1。
    x_shape = [1,4,4,1]
    x_val = np.random.uniform(x_shape)
    
    # 2. 下面在计算图中创建占位符。此例中,占位符是用来传入图片的。
    x_data = tf.placeholder(tf.float32, shape=x_shape)
    
    # 3. 为了创建过滤4x4像素图片的滑动窗口,我们用 tensorflow 的内建函数 conv2d() 卷积 2x2 形状的常量窗口。conv2d() 函数传入滑动窗口、过滤器和步长。本例,将在滑动窗口四个方向上计算,所以在四个方向上都要指定步长。创建一个2x2的窗口,每个方向长度为2的步长。为了计算平均值,我们将用常量为0.25的向量与2x2的窗口卷积。
    my_filter = tf.constant(0.25, shape=[2,2,1,1])
    # 这里注意 my_filter 的形状是 4 维数据
    my_strides = [1,2,2,1]
    mov_avg_layer = tf.nn.conv2d(x_data, my_fiter, my_strides, padding='SAME', name='Moving_Avg_Window')
    
    # 4. 现在定义一个自定义 Layer,操作滑动窗口平均2x2的返回值。自定义函数将输入张量乘以一个2x2的矩阵张量,然后每个元素加1。因为矩阵乘法只计算2维矩阵,所以裁剪图象多余的维度(大小为1)。TensorFlow 通过内建函数 squeeze() 裁剪。下面是新定义的 Layer:
    def custom_layer(input_matrix):
        input_matrix_sqeezed = tf.squeeze(input_matrix)
        A = tf.constant([[1., 2.], [-1., 3.]])
        b = tf.constant(1., shape=[2, 2])
        temp1 = tf.matmul(A, input_matrix_sqeezed)
        temp = tf.add(temp1, b) # Ax + b
        return(tf.sigmoid(temp))
    
    with tf.name_scope('Custom_Layer') as scope:
        custom_layer1 = cunstom_layer(mov_avg_layer)
    
    print(sess.run(custom_layer1, feed_dict={x_data: x_val}))
    
    
  4. TensorFlow 实现损失函数

    损失函数(loss function)对机器学习来讲是非常重要的。它们度量模型输出值与目标值之间的差值。

tensorflow 实现反向传播

使用 tensorflow 的一个优势是,它可以维护操作状态和基于反向传播自动地更新模型变量。本节介绍如何利用这种优势来训练机器学习模型。

# 1. 导入模块
import numpy as np
import tensorflow as tf

# 2. 创建计算图会话
sess = tf.Session()

# 3. 生成数据,创建占位符和变量A
x_vals = np.random.normal(1, 0.1, 100)
y_vals = np.repeat(10., 100)
x_data = tf.placeholder(shape=[1], dtype=tf.float32)
y_target = tf.placeholder(shape=[1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1])) #注意 Variable 变量的定义

# 4. 增加乘法操作
my_output = tf.multiply(x_data, A)

# 5. 增加L2 正则损失函数
loss = tf.square(my_output - y_target)

# 6. 在运行前,需要初始化变量
init = tf.initialize_all_variables()
sess.run(init)

# 7. 声明变量的优化器
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.02)
train_step = my_opt.minimize(loss)

# 8. 最后一步是训练方法
for i in range(100):
    rand_index = np.random.choice(100)
    rand_x = [x_vals[rand_index]]
    rand_y = [y_vals[rand_index]]
    sess.run(train_step, feed_dict={x_data: rand_x, 
                                y_target: rand_y})
    if (i+1) % 25 ==0:
        print('Step #' + str(i+1) + ' A= ' + str(sess.run(A)))
        print('Loss= ' + str(sess.run(loss, 
                                    feed_dict={x_data: rand_x, y_target: rand_y})))

# 分类算法例子

# 10. 首先,重置计算图,并且重新初始化变量
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()

# 11. 从正态分布 N~(-1, 1) 和 N(3, 1) 
x_vals = np.concatenate((np.repeat(0.,50), np.random.normal(3, 1, 50)))
y_vals = np.concatenate((np.repeat(0., 50),np.repeat(1.,50)))
x_data = tf.placeholder(shape=[1], dtype=tf.float32)
y_target = tf.placeholder(shape=[1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(mean=10, shape=[1]))

# 12. 增加转换操作,这里不必封装 sigmoid 函数,因为损失函数中会实现此功能
my_output = tf.add(x_data, A)

# 13. 增加批量数(batch_size)维度
my_output_expanded = tf.expand_dims(my_output, 0)
y_target_expanded = tf.expand_dims(y_target, 0)

# 14. 初始化变量A
init = tf.initialize_all_variables()
sess.run(init)

# 15. 声明损失函数
xentropy = tf.nn.sigmoid_cross_entropy_with_logits(my_output_expanded, 
                                y_target_expanded)

# 16. 增加一个优化器,让tensorflow知道如何更新
my_opt = tf.train.GradientDescentOptimizer(0.05)
train_step = my_opt.minimize(xentropy)

# 17. 最后,通过随机选择的数据迭代几百次,相应地更新变量A。
for i in range(1400):
    rand_index = np.random.choice(100)
    rand_x = [x_vals[rand_index]]
    rand_y = y_vals[rand_index]

    sess.run(train_step, feed_dict = {x_data: rand_x, y_target: rand_y})

    if (i+1) % 200 == 0:
        print('Step #' +str(i+1) + 'A = ' +str(sess.run(A)))
        print('Loss = ' + str(sess.run(xentropy, feed_dict = {x_data: rand_x, 
                y_target: rand_y})))

作为概括,总结以下几点。

  1. 生成数据
  2. 初始化占位符和变量
  3. 创建损失函数
  4. 定义一个优化器算法
  5. 最后,通过随机数据样本进行迭代,更新变量

TensorFlow 实现随机训练和批量训练

根据上面描述的反向传播算法,tensorflow 更新模型变量
它能一次操作一个数据点,也可以一次操作大量数据。一个训练例子上的操作可能导致比较“古怪”的学习过程,但使用大批量的训练会造成计算成本昂贵。到底选用那种训练类型对机器学习的收敛非常关键。

为了让 tensorflow 计算变量梯度来让反向传播工作,我们必须度量一个或者多个样本的损失。与前一节所作的类似,随机训练会一次随机抽样训练数据和目标数据对完成训练。另一个选项是,一次大批量训练取平均损失来进行梯度计算,批量训练大小可以一次上扩到整个数据集。

这里使用随机训练和批量训练,扩展前面的例子

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
sess = tf.Session()

# 声明批量大小
batch_size = 20

# 数据、占位符、变量 (与前面维度不同)
x_vals = np.random.normal(1, 0.1, 100)
y_vals = np.repeat(10., 100)
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1, 1]))

# 在计算图中,增加矩阵乘法操作
my_output = tf.matmul(x_data, A)

# 改变损失函数,因为批量训练时,损失函数是每个数据点 L2 损失的平均值。在 TensorFlow 中通过 reduce_mean() 函数即可实现
loss = tf.reduce_mean(tf.square(my_output - y_target))

# 声明优化器
my_opt = tf.train.GradientDescentOptimizer(0.02)
train_step = my_opt.minimize(loss)

# 初始化变量
init = tf.initialize_all_variables()
sess.run(init)

# 在训练中,通过循环迭代优化模型算法。这部分与前面不同,因为我们想绘制损失值图与随机训练的对比,所以这里初始化一个列表,每间隔5次迭代保存损失函数
loss_batch = []
for i in range(100):
    rand_index = np.random.choice(100, size=batch_size)
    rand_x = np.transpose([x_vals[rand_index]])
    rand_y = np.transpose([y_vals[rand_index]])
    
    sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
    
    if (i+1) % 5 ==0:
        print('Step #' + str(i+1) + ' A= '+ str(sess.run(A)))
        temp_loss = sess.run(loss, feed_dict = {x_data: rand_x, y_target: rand_y})
        print('Loss= ' + str(temp_loss))
        loss_batch.append(temp_loss)

# 批量训练和随机训练的不同在于它们的优化器方法和收敛。找到一个合适的批量大小是挺难的。

# 这里可以初始化变量一下,从而下面不是训练好的模型,可以考察stochastic train重新开始
loss_stochastic = []
for i in range(100):
    rand_index = np.random.choice(100) # no batch_size, 每次随机选取一个值
    rand_x = np.reshape([x_vals[rand_index]],(-1, 1)) # 转换为->(?,1) 的形式,feed_dict的时候才不报错
    rand_y = np.reshape([y_vals[rand_index]],(-1, 1))
    # 这里是np.reshape函数,不是tf.placeholder函数,不能用(None, 1), 需要使用 (-1,1)
    sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
    if (i+1) % 5 ==0:
        print('Step #' + str(i+1) + ' A= '+ str(sess.run(A)))
        temp_loss = sess.run(loss, feed_dict = {x_data: rand_x, y_target: rand_y})
        print('Loss= ' + str(temp_loss))
        loss_stochastic.append(temp_loss)
        
plt.plot(range(0, 100, 5), loss_stochastic, 'b-', label='Stochastic Loss')
plt.plot(range(0, 100, 5), loss_batch, 'r--', label="'Batch' Loss, size=20")
plt.legend(loc = 'upper right', prop={'size': 11})
plt.show()

迭代 100 次的随机训练损失 和 批量训练损失(批量大小为20)。批量训练损失更平滑,随机训练损失更不规则。

tensorflow 实现创建分类器

在本节中,结合前面所所有的知识点,创建一个 iris 数据集的分类器。

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf

from sklearn import datasets

sess = tf.Session()

# 导入 iris 数据集,根据目标数据是否为山鸢花将其转换为 1 或者 0。由于 iris 数据集将山鸢标记为0, 我们将其从0置为1,同时把其他物种标记为0.本次训练只采用两种特征,花瓣的长度和花瓣宽度。这两个特征在x-value的第三列和第四列:
iris = datasets.load_iris()
binary_target = np.array([1. if x==0 else 0. for x in iris.target])
iris_2d = np.array([[x[2], x[3]] for x in iris.data])

# 声明批量训练大小、数据占位符、和模型变量。注意,数据占位符的第一维度设为 None
batch_size = 20 
x1_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
x2_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32) 
# 通过指定 dtype=tf.float32 降低 float 的字节数,可以提高算法的性能。
A = tf.Variable(tf.random_normal(shape=[1, 1]))
b = tf.Variable(tf.random_normal(shape=[1, 1]))

# 定义线性模型
my_mult = tf.matmul(x2_data, A)
my_add = tf.add(my_mult, b)
my_output = tf.subtract(x1_data, my_add)

# 增加 tensorflow 的 sigmoid 交叉熵损失函数
xentropy = tf.nn.sigmoid_cross_entropy_with_logits(labels = my_output, logits = y_target)

# 声明优化方法
my_opt = tf.train.GradientDescentOptimizer(0.05)
train_step = my_opt.minimize(xentropy)

# 创建一个变量初始化操作,并让 tensorflow 执行它
init = tf.initialize_all_variables()
sess.run(init)

# 现在迭代1000次训练线性模型。传入三种数据:花瓣长度、花瓣宽度和目标变量。
for i in range(1000):
    rand_index = np.random.choice(len(iris_2d), size=batch_size)
    
    rand_x = iris_2d[rand_index]
    rand_x1 = np.array([[x[0]] for x in rand_x])
    rand_x2 = np.array([[x[1]] for x in rand_x])
    
    rand_y = np.array([[y] for y in binary_target[rand_index]])
    sess.run(train_step, feed_dict={x1_data: rand_x1, x2_data: rand_x2, y_target: rand_y})
    
    if (i+1) % 200 == 0:
        print('Step #' + str(i) + ' A = ' + str(sess.run(A)) +', b = ' + str(sess.run(b)))

slope = sess.run(A)
intercept = sess.run(b)

我们的目标是利用花瓣长度和花瓣宽度的特征,在山鸢尾和其他物种间拟合一条直线。

TensorFlow 实现模型评估

在使用 tensorflow 训练回归算法 和 分类算法 以后,我们需要评估模型预测值来评估训练的好坏。

模型评估是非常重要的,随后的每个模型都有模型评估方式。使用 tensorflow 时,需要把模型评估加入到计算图中,然后在模型训练完后调用评估模型。

在训练模型过程中,模型评估能洞察模型算法,给出提示信息来调试、提高或者改变整个模型。但是在模型训练中,并不是总需要模型评估,我们将展示如何在回归算法和分类算法中使用它。

模型训练之后,需要定量评估模型的性能如何。在理想情况下,评估模型需要一个训练数据集 和 测试数据集,有时甚至需要一个验证数据集。

想评估一个模型时,就得使用大批量数据点。如果完成批量训练,我们可以重用模型来预测批量数据点。但是如果完成随即训练,就不得不创建单独的评估器来处理批量数据点。

(如果在损失函数中使用的模型输出结果经过转换操作,如 sigmoid_cross_entropy_with_logits() 函数,为了精确计算预测结果,别忘了在模型评估中也要进行转换操作。)

回归算法模型用来预测连续型数值,其目标不是分类值而是数字。为了评估这些回归预测值是否和实际目标相符,我们需要度量两者间的距离。

分类算法模型基于数值型输入预测分类值,实际目标是 1 和 0 的序列。我们需要度量预测值与真实值的距离。分类算法模型的损失函数一般不容易解释模型好坏,所以通常情况下是看准确预测分类的结果的百分比。

# 1. 加载所需要的编程库,创建计算图,数据集,变量,和占位符。
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf

sess = tf.Session()

x_vals = np.random.normal(1, 0.1, 100)
y_vals = np.repeat(10., 100)
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
batch_size = 25
train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]
A = tf.Variable(tf.random_normal(shape=[1, 1]))

# 2. 声明算法模型、损失函数和优化器算法。
my_output = tf.matmul(x_data, A)
loss = tf.reduce_mean(tf.square(my_output - y_target))

init = tf.initialize_all_variables()
sess.run(init)

my_opt = tf.train.GradientDescentOptimizer(0.02)
train_step = my_opt.minimize(loss)

# 3. 训练模型
for i in range(100):
    rand_index = np.random.choice(len(x_vals_train), size=batch_size)
    rand_x = np.transpose([x_vals_train[rand_index]])
    rand_y = np.transpose([y_vals_train[rand_index]])
    sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
    if (i+1) % 25 == 0:
        print('Step #' + str(i+1) + ' A= ' + str(sess.run(A)))
        print('Loss=' + str(sess.run(loss, feed_dict = 
                                     {x_data: rand_x, y_target: rand_y})))

现在,为了评估训练模型,将打印训练数据集和测试数据集训练的 MSE 损失函数值,代码如下:

mes_test = sess.run(loss, feed_dict={x_data: np.transpose([x_vals_test]), y_target: np.transpose([y_vals_test])})
mes_train = sess.run(loss, feed_dict={x_data: np.transpose([x_vals_train]), y_target: np.transpose([y_vals_train])})
print("'MSE' on test:" + str(mes_test))
print("'MSE' on train:" + str(mes_train))

对于分类模型的例子,与前面回归模型类似。

# 重新加载计算图,创建数据集、变量和占位符。记住,分割数据集和目标成为训练集和测试集。
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()

batch_size = 25

x_vals = np.concatenate((np.random.normal(-1, 1, 50), np.random.normal(2, 1, 50)))
y_vals = np.concatenate((np.repeat(0., 50), np.repeat(1., 50)))
y_target = tf.placeholder(shape=[1, None], dtype=tf.float32)

# 在计算图中,增加模型和损失函数,初始化变量,并创建优化器。
my_output = tf.add(x_data, A)

init = tf.initialize_all_variables()
sess.run(init)

xentropy = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y_target, logits=my_output))
my_opt = tf.train.GradientDescentOptimizer(0.05)
train_step = my_opt.minimize(xentropy)

# 现在进行迭代训练
for i in range(1800):
    rand_index = np.random.choice(len(x_vals_train), size=batch_size)
    rand_x = np.reshape(x_vals_train[rand_index],(-1,1))
    rand_y = np.reshape(y_vals_train[rand_index],(-1,1))
    sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
    
    if (i+1) % 200 == 0:
        print('Step #' + str(i+1) + ' A=' + str(sess.run(A)))
        print('Loss =' + str(sess.run(xentropy, feed_dict={x_data: rand_x, y_target: rand_y})))

# 为了评估预测模型,我们创建预测操作。用 squeeze() 函数封装预测操作,使得预测值和目标值有相同的维度。 然后用 equal() 函数检测是否相等,把得到的 ture 或 false 的 boolean 型张量转化为 float32 型, 再对其取平均值,得到一个准确度值。我们将用这个函数评估训练模型和测试模型。
y_prediction = tf.squeeze(tf.round(tf.nn.sigmoid(tf.add(x_data, A))))
correct_prediction = tf.equal(y_prediction, y_target)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
acc_value_test = sess.run(accuracy, feed_dict={x_data: x_vals_test, y_target: y_vals_test})
acc_value_train = sess.run(accuracy, feed_dict={x_data: x_vals_train, y_target: y_vals_train})
print("'Accuracy' on train set: " + str(acc_value_train))
print("'Accuracy' on test set: " + str(acc_value_test))

# 模型训练结果,比如准确度,MSE 等,将帮助我们评估机器学习模型。因为这是一维模型,很容易绘制模型和数据点。用 matplotlib 绘制两个分开的直方图来可视化机器学习模型和数据点。
A_result = sess.run(A)
bins = np.linspace(-5, 5, 50)
plt.hist(x_vals[0:50], bins, alpha=0.5, label='N(-1,1)', color='blue')
plt.hist(x_vals[50:100], bins, alpha=0.5, label='N(2,1)', color='red')
plt.scatter(A_result, A_result, label='A= '+ str(np.round(A_result, 2)))
plt.legend(loc='upper right')
plt.title('Binary Classifier Accuracy = ' + str(np.round(acc_value_test, 2)))
plt.show()

基于 TensorFlow 的线性回归

每个特征的数值直接代表该特征对目标值或者因变量的影响。

  1. 本节使用 TensorFlow 求逆矩阵的方法解决二维线性回归问题。

    线性回归算法能表示为矩阵计算, A x = b Ax = b Ax=b。这里要解决的是用矩阵 x x x 来求解系数。注意,如果观测矩阵不是方阵,那求解出的矩阵 x x x x = ( A T A ) − 1 A T b x = (A^TA)^{-1}A^Tb x=(ATA)1ATb

    # 1. 导入必要的编程库,初始化计算图,并生成数据
    import matplotlib.pyplot as plt
    import numpy as np
    import tensorflow as tf
    sess = tf.Session()
    x_vals = np.linspace(0, 10, 100)
    y_vals = x_vals + np.random.normal(0, 1, 100)
    
    # 2. 创建后续求逆方法所需的矩阵。创建A矩阵,其为矩阵x_vals_column和ones_column的合并。
    # 然后以矩阵y_vals创建b矩阵。
    x_vals_column = np.transpose(np.matrix(x_vals))
    ones_column = np.transpose(np.matrix(np.repeat(1, 100)))
    A = np.column_stack((x_vals_column, ones_column))
    b = np.transpose(np.matrix(y_vals))
    
    # 3. 将A和矩阵转换为张量
    A_tensor = tf.constant(A)
    b_tensor = tf.constant(b)
    
    # 4. 使用 TensorFlow 的 tf.matirx_inverse() 方法-> $x = (A^TA)^{-1}A^Tb$
    tA_A = tf.matmul(tf.transpose(A_tensor), A_tensor)
    tA_A_inv = tf.matrix_inverse(tA_A)
    product = tf.matmul(tA_A_inv, tf.transpose(A_tensor))
    solution = tf.matmul(product, b_tensor)
    solution_eval = sess.run(solution)
    
    # 5. 从解中抽取系数、斜率、和y 解决 y-intercept
    slope = solution_eval[0][0]
    y_intercept = solution_eval[1][0]
    print('slope: ' + str(slope))
    print('y_intercept: ' + str(y_intercept))
    
    best_fit = []
    for i in x_vals:
        best_fit.append(slope*i + y_intercept)
    plt.plot(x_vals, y_vals, 'o', label='Data')
    plt.plot(x_vals, best_fit, '-r', label='Best fit line', linewidth=3)
    plt.legend(loc='upper left')
    plt.show()
    

    与本书的大部分章节不一样的是,这里的解决方法是通过矩阵操作直接求解结果。大部分tensorflow算法是通过迭代训练实现的,利用反向传播自动更新模型变量。这里通过实现数据直接求解方法拟合模型,仅仅是为了说明tensorflow的灵活用法。

  2. 本节用矩阵分解方法,实现矩阵求逆。用户对于分解一个矩阵为多个矩阵的方法感兴趣的原因是,结果矩阵的特性使得其在应用中更为高效。同时,矩阵分解方法求逆优势更高效并且数值稳定。

    tensorflow.cholesky() 
    # 函数进行矩阵分解,并返回矩阵分解的下三角矩阵,因为上三角矩阵是下三角矩阵的转置。
    
  3. 本节将遍历批量数据点,并让 tensorflow 更新斜率和 y 截距。这次将使用 Scikit Learn 的内建数据集 iris。特别地,我们将用数据点(x 值代表花瓣宽度,y 值代表花瓣长度)找到最优直线。选择这两种特征是因为它们具有线性关系,在后续的结果中将会看到。下一小节将讲解不同的损失函数的影响,这一小节使用 L2 正则函数。

    # 1. 导入必要的编程库,创建计算图,加载数据集
    import matplotlib.pyplot as plt
    import numpy as np
    import tensorflow as tf
    from sklearn import datasets
    from tensorflow.python.framework import ops
    ops.reset_default_graph()
    sess = tf.Session()
    iris = datasets.load_iris()
    
    x_vals = np.array([x[3] for x in iris.data])
    y_vals = np.array([y[0] for y in iris.data])
    
    # 2. 声明学习率,批量大小,占位符和模型变量
    learning_rate = 0.05
    batch_size = 25
    x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
    y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
    
    A = tf.Variable(tf.random_normal(shape=[1, 1]))
    b = tf.Variable(tf.random_normal(shape=[1, 1]))
    
    # 3. 增加线性模型, y = Ax + b
    model_output = tf.add(tf.matmul(x_data, A), b)
    
    # 声明 L2 损失函数,其为批量损失的平均值。初始化变量,声明优化器,
    loss = tf.reduce_mean(tf.square(y_target - model_output))
    init = tf.global_variables_initializer()
    sess.run(init)
    
    my_opt = tf.train.GradientDescentOptimizer(learning_rate)
    train_step = my_opt.minimize(loss)
    
    # 4. 现在遍历迭代,并在随机选择的批量数据上进行模型训练。迭代100次,每25次
    # 迭代输出变量值和损失值。注意,这里保存每次迭代的损失值,将其用于后续的可视化。
    loss_vec = []
    for i in range(100):
        rand_index = np.random.choice(len(x_vals), size=batch_size)
        rand_x = np.reshape(np.transpose(x_vals[rand_index]), (-1,1))
        rand_y = np.reshape(np.transpose(y_vals[rand_index]), (-1,1))
        sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
        temp_loss = sess.run(loss, feed_dict={x_data:rand_x, y_target: rand_y})
        loss_vec.append(temp_loss)
        if (i+1) % 25 == 0:
            print('Step # ' + str(i+1) + ' A = ' + str(sess.run(A)) + 'b = '
                + str(sess.run(b)))
            print('Loss = ' + str(temp_loss))
            
    # 5. 抽取系数,创建最佳拟合直线
    slope = sess.run(A)[0][0]
    y_intercept = sess.run(b)[0][0]
    
    print('slope: ' + str(slope))
    print('y_intercept: ' + str(y_intercept))
    
    best_fit = []
    for i in x_vals:
        best_fit.append(slope*i + y_intercept)
        
    # 6. 绘制两幅图,第一幅是拟合的直线,第二幅是迭代100次的L2正则损失函数。
    plt.plot(x_vals, y_vals, 'o', label='Data Points')
    plt.plot(x_vals, best_fit, 'r-', label='Best Fit Line', linewidth=3)
    plt.legend(loc='upper left')
    plt.title('Sepal Length vs Pedal Width')
    plt.xlabel('Pedal Width')
    plt.ylabel('Sepal Lenght')
    plt.show()
    
    plt.plot(loss_vec, 'k-')
    plt.title('L2 Loss per Generation')
    plt.xlabel('Generation')
    plt.ylabel('L2 Loss')
    plt.show()
    
  4. 用 tensorflow 实现弹性网络回归算法(多线性回归)

    本节使用多线性回归的方法实现弹性网络回归算法,以 iris 数据集为训练数据,用花瓣长度、花瓣宽度、和花萼宽度三个特征预测花萼长度。

    # 1. 导入必要的编程库,并初始化一个计算图
    import matplotlib.pyplot as plt
    import numpy as np
    import tensorflow as tf
    from sklearn import datasets
    sess = tf.Session()
    
    # 2. 加载数据集。这次,x_vals 数据将是三列值得数组
    iris = datasets.load_iris()
    x_vals = np.array([[x[1], x[2], x[3]] for x in iris.data])
    y_vals = np.array([y[0] for y in iris.data])
    
    # 3. 声明批量大小、占位符、变量和模型输出。这里唯一不同的是 x_data 占位符的大小为3
    batch_size = 50
    learning_rate = 0.001
    x_data = tf.placeholder(shape=[None, 3], dtype=tf.float32)
    y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
    A = tf.Variable(tf.random_normal(shape=[3, 1]))
    b = tf.Variable(tf.random_normal(shape=[1, 1]))
    model_output = tf.add(tf.matmul(x_data, A), b)
    
    # 4. 对于弹性网络回归算法,损失函数包含斜率的L1正则和L2正则,创建L1和L2正则项,
    # 然后加入到损失函数中
    elastic_param1 = tf.constant(1.)
    elastic_param2 = tf.constant(1.)
    l1_a_loss = tf.reduce_mean(tf.abs(A))
    l2_a_loss = tf.reduce_mean(tf.square(A))
    e1_term = tf.multiply(elastic_param1, l1_a_loss)
    e2_term = tf.multiply(elastic_param2, l2_a_loss)
    loss = tf.expand_dims(tf.add(tf.add(tf.reduce_mean(
        tf.square(y_target - model_output)), e1_term), e2_term), 0)
    
    # 5. 现在初始化变量,声明优化器,然后遍历迭代运行,训练拟合得到系数
    init = tf.global_variables_initializer()
    sess.run(init)
    my_opt = tf.train.GradientDescentOptimizer(learning_rate)
    train_step = my_opt.minimize(loss)
    loss_vec = []
    for i in range(1000):
        rand_index = np.random.choice(len(x_vals), size=batch_size)
        rand_x = np.reshape(x_vals[rand_index],(-1,3))
        rand_y = np.reshape(np.transpose(y_vals[rand_index]),(-1,1))
        sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
        temp_loss = sess.run(loss,feed_dict={x_data: rand_x, y_target: rand_y})
        loss_vec.append(temp_loss[0])
        if (i+1) % 250 == 0:
            print('Step #' + str(i+1) + ' A= ' + str(sess.run(A)) + 
                ' b= '+ str(sess.run(b)))
            print('Loss = ' + str(temp_loss))
            
    # 6. 现在能观察到,随着训练迭代后损失函数已收敛
    plt.plot(loss_vec, 'k-')
    plt.title('Loss per Generation')
    plt.plot('Generation')
    plt.xlabel('Generation')
    plt.ylabel('Loss')
    plt.show()
    
  5. 用 tensorflow 实现逻辑回归算法

    本节将实现逻辑回归算法,预测低出生体重的概率。

    逻辑回归可以将线性回归转换成一个二值分类器。通过 sigmoid 函数将线性回归的输出缩放到 0 和 1 之间。 目标值是 0 或者 1 代表着一个数据点是否属于某一类。如果预测值在截止以上,则预测值被标记为 “1” 类;否则,预测值标为“0” 类。在本例中,为方便起见,将指定截止值设为 0.5。

    在本例中,我们使用多个因素来预测低出生体重。

    # 1. 导入必要的编程库,包括requests 模块,因为我们将通过超链接访问低出生体重
     # 数据集。初始化一个计算图
     import matplotlib.pyplot as plt
     import numpy as np
     import tensorflow as tf
     import requests
     from sklearn import datasets
     from sklearn.preprocessing import normalize
     from tensorflow.python.framework import ops
     ops.reset_default_graph()
     sess = tf.Session()
    
     # 2. 通过 requests 模块加载数据集,指定要使用的特征。实际出生体特征和ID两列不需要
     birthdata_url = 'https://www.umass.edu/statdata/statdata/data/lowbwt.dat'
     birth_file = requests.get(birthdata_url)
     birth_data = birth_file.text.split('\r\n')[5:]
     birth_header = [x for x in birth_data[0].split(' ') if len(x)>=1]
     birth_data = [[float(x) for x in y.split(' ') if len(x)>=1] for y 
                 in birth_data[1:] if len(y)>=1]
     y_vals = np.array([x[1] for x in birth_data])
     x_vals = np.array([x[2:9] for x in birth_data])
    
     # 3. 分割数据集为测试集和训练集
     train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8),
                                     replace=False)
     test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
     x_vals_train = x_vals[train_indices]
     x_vals_test = x_vals[test_indices]
     y_vals_train = y_vals[train_indices]
     y_vals_test = y_vals[test_indices]
    
     # 4. 将所有特征缩放的0和1区间(min-max 缩放),逻辑回归收敛的效果更好。下面将归一化
     # 特征
     def normalize_cols(m):
         col_max = m.max(axis=0)
         col_min = m.min(axis=0)
         return (m - col_min)/(col_max - col_min)
    
     x_vals_train = np.nan_to_num(normalize_cols(x_vals_train))
     x_vals_test = np.nan_to_num(normalize_cols(x_vals_test))
     # 注意,在缩放数据前,需要先分割数据集为测试集和训练集。需要保证训练集和测试机之间没有影响。
    
     # 5. 声明批量大小、占位符、变量和逻辑模型。这步不需要用 sigmoid 函数封装输出结果,因为
     # sigmoid 操作是包含在内建损失函数中的。
     batch_size = 25
     x_data = tf.placeholder(shape=[None, 7], dtype=tf.float32)
     y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
     A = tf.Variable(tf.random_normal(shape=[7,1]))
     b = tf.Variable(tf.random_normal(shape=[1,1]))
     model_output = tf.add(tf.matmul(x_data, A), b)
    
     # 6. 声明损失函数,其包含 sigmoid 函数。初始化变量,声明优化器。
     loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
         logits=model_output, labels=y_target))
     my_opt = tf.train.GradientDescentOptimizer(0.01)
     train_step = my_opt.minimize(loss)
    
     init = tf.global_variables_initializer()
     sess.run(init)
    
     # 7. 除记录损失函数以外,也需要记录分类器在训练集和测试集上的准确度。所以创建一个返回准确度
     # 的预测函数
     prediction = tf.round(tf.sigmoid(model_output))
     predictions_correct = tf.cast(tf.equal(prediction, y_target))
     accuracy = tf.reduce_mean(predictions_correct)
    
     # 8. 开始遍历迭代训练,记录损失值
     loss_vec = []
     train_acc = []
     test_acc = []
     for i in range(1500):
         rand_index = np.random.choice(len(x_vals_train), size=batch_size)
         rand_x = x_vals_train[rand_index]
         rand_y = np.transpose([y_vals_train[rand_index]])
         sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
         temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
         loss_vec.append(temp_loss)
         
     # 9. 绘制图象
     plt.plot(loss_vec, 'k-')
    

基于 TensorFlow 的支持向量机

将介绍 TensorFlow 中,支持向量机的算法使用、实现、和评估。

  • 线性支持向量机的使用
  • 弱化为线性回归
  • TensorFlow 上核函数的使用
  • TensorFlow 实现非线性支持向量机
  • TensorFlow 实现多分类支持向量机

支持向量机算法简介

支持向量机是一种二分类算法。基本观点是找到两类之间的一个线性可分的直线(或者超平面)。

本节中,将建立一个 soft margin 支持向量机,展示如何将其扩展到非线性的场景和多分类目标。

loss function of soft margin:

1 n ∑ i = 1 n m a x ( 0 , 1 − y i ( A x i − b ) ) + a ∣ ∣ A ∣ ∣ 2 \frac{1}{n} \sum_{i=1}^n max(0, 1-y_i(Ax_i-b))+a||A||^2 n1i=1nmax(0,1yi(Axib))+aA2

如果数据点正确分割,乘机 y i ( A x i − b ) y_i(Ax_i-b) yi(Axib) 总是大于1。这意味着损失函数左边项等于0,这时对损失函数有影响的仅仅只有间隔大小。当 a 很大时,模型会倾向于尽量将数据样本分开;当 a 值变小时,会有更多的跨越边界点的存在。

TensorFlow 上核函数的使用

如果想分割非线性数据集,该如何改变线性分类器映射到数据集?答案是,改变 SVM 损失函数中的核函数。本节将详细阐述如何调整核函数,并且分割非线性可分的数据集。

TensorFlow 实现非线性支持向量机

用 TensorFlow 实现多类支持向量机

我们也能用SVM 分类多类目标。在本节中,将详细展示一个多类支持向量机分类器训练 iris 数据集来分类三种花。

SVM 最初是为二分类问题设计的。但是也可以通过一些策略使得其能进行多类分类。主要的两种的策略是:1对多,1对1

  • 1对1 方法是在任意两类样本之间创建一个二值分类器,然后得票最多得类别即为该未知样本的预测类别。但是当类别(k类)很多的时候,就必须创建 k ! / ( k − 2 ) ! 2 ! k!/(k-2)!2! k!/(k2)!2! 个分类器。
  • 1对多 方法是为每类创建一个分类器。最后预测类别是具有最大 SVM 间隔的类别。

本小节将实现 1对多 方法。

最近邻域法

本章知识点

  • 最近邻域法的使用
  • 如何度量文本距离
  • tensorflow 实现混合距离
  • tensorflow 实现地址匹配
  • tensorflow 实现图像识别

最近邻域算法的思想很简单,其先将训练集看作训练模型,然后基于新数据点与训练集的距离来预测新数据点。最直观的最近邻域算法是让预测值与最接近的训练数据集作为同一类。

但是,大部分样本数据集包含一定程度的噪声,更通用的方法是k个邻域的加权平均。

本小节开始介绍最近邻域法的实现,并应用到房价的预测。也许这是学习最近邻域法最好的方式,因为我们将处理数值化的特征和连续型目标。

为了展示在 tensorflow 中如何运用最近邻域法预测,我们将进行波士顿房价数据集训练。这里将利用几个特征的函数来预测平均邻域房价。
我们将从训练好的模型的训练数据集中找到预测数据点的最近邻域,并对实际值进行加权平均。

如何度量文本距离:除了处理数值外,最近邻域法还广泛应用于其他领域。只要有方法来度量距离,即可应用最近邻域算法。如使用 tensorflow 度量文本距离。

Tensorflow 实现混合距离计算:当处理的数据观测点有多种特征时,我们应该意识到不同的特征应该用不同的归一化方式来缩放。
对最近邻域法进行多维度的缩放。扩展多变量的距离函数。特别的,我们将扩展距离函数为特征变量的函数。(用 TensorFlow 实现地址匹配)

用 TensorFlow 实现图像识别:最近邻域算法也常用于图象识别。

神经网络算法

本章中,我们将介绍神经网络算法及其在 TensorFlow 中的实现。后续大部分都会基于神经网络算法,所以,学习如何在 TensorFlow 中,实现神经网络算法非常重要。

  • 实现门函数
  • 使用门函数和激励函数
  • TensorFlow 实现单层神经网络
  • TensorFlow 实现神经网络常见层
  • 使用多层神经网络
  • 线性预测模型的优化
  • TensorFlow 基于神经网络实现井字棋

神经网络算法基础

神经网络算法的概念已出现几十年,但是它仅仅在最近由于计算能力和数据量的提升,能训练大规模网络才获得新的发展。

神经网络算法是对输入数据矩阵进行一系列的基本操作。这些操作通常包括非线性函数的加法和乘法。逻辑回归算法是斜率与特征点积求和后进行非线性 sigmoid 函数计算。神经网络算法表达式更通用,允许任意形式的基本操作和非线性函数的结合,包括绝对值、最大值、最小值等。

黑科技:反向传播,用来更新模型变量。 非线性激励函数,用来解决大部分非线性问题。

用 tensorflow 实现门操作

  • f ( x ) = a ∗ x f(x) = a* x f(x)=ax 门 和 f ( x ) = a ∗ x + b f(x) = a*x + b f(x)=ax+b
      # 加载 tensorflow 模块,创建一个计算图会话
      import tensorflow as tf
      sess = tf.Session()
    
      # 声明模型变量,输入数据集和占位符
      a = tf.Variable(tf.constant(4.))
      x_val = 5.
      x_data = tf.placeholder(dtype=tf.float32)
    
      # 增加操作到计算图中
      multiplication = tf.multiply(a, x_data)
    
      # 声明损失函数:输出结果与预期目标值(50)之间的L2距离函数
      loss = tf.square(tf.subtract(multiplication, 50.))
    
      # 初始化模型变量,声明标准梯度下降优化算法
      init = tf.initialize_all_variables()
      sess.run(init)
      my_opt = tf.train.GradientDescentOptimizer(0.01)
      train_step = my_opt.minimize(loss)
    
      # 优化模型输出结果。连续输入值5,反向传播损失函数来更新模型变量以达到值10
      print('optimizing a Multiplication Gate Output to 50')
    
      for i in range(10):
          sess.run(train_step, feed_dict={x_data: x_val})
          a_val = sess.run(a)
          mult_output = sess.run(multiplication, feed_dict={x_data: x_val})
          print(str(a_val) + '*' + str(x_val) + ' = ' + str(mult_output))
    
      # 对两个嵌套操作的例子, $f(x) = a*x + b$,也执行上述相同的操作
      # 不同的是,本例中包含两个模型变量 a 和 b
    
      from tensorflow.python.framework import ops
      ops.reset_default_graph()
      sess = tf.Session()
    
      a = tf.Variable(tf.constant(1.))
      b = tf.Variable(tf.constant(1.))
      x_val = 5.
      x_data = tf.placeholder(dtype=tf.float32)
    
      two_gate = tf.add(tf.multiply(a, x_data), b)
    
      loss = tf.square(tf.subtract(two_gate, 50.))
    
      my_opt = tf.train.GradientDescentOptimizer(0.01)
      train_step = my_opt.minimize(loss)
    
      init = tf.initialize_all_variables()
      sess.run(init)
    
      # 优化模型变量,训练输出结果,以达到预期目标值 50
      print('\nOptimize Two Gate Output to 50.')
      for i in range(10):
          # run the train step
          sess.run(train_step, feed_dict={x_data: x_val})
          # get the a and b values
          a_val, b_val = (sess.run(a), sess.run(b))
          # run the two-gate graph output
          two_gate_output = sess.run(two_gate, feed_dict={x_data: x_val})
          print(str(a_val) + '*' + str(x_val) + ' + ' + str(b_val) + ' = ' + 
              str(two_gate_output))
    

tensorflow 通过隐式后向传播达到计算门操作的优化。tensorflow 维护模型操作和变量,调整优化算法和损失函数。

我们能扩展操作门,选定哪一个输入是变量,哪一个输入时数据。因为 tensorflow 将调整所有的模型变量来最小化损失函数,而不是调整数据,数据输入声明为占位符。

维护计算图中的状态,以及每次训练迭代自动更新模型变量的隐式能力是 TensorFlow 具有的优势特征之一,该能力让 TensorFlow 威力无穷。

使用门函数和激励函数:现在我们已经学会连接这些操作门函数,接下来使用激励函数来运行计算图输出结果。

# 1. 导入必要的编程库,初始一个计算会话。这里使用 TensorFlow 和 Numpy 模块的随机数生
# 成器。 对于相同的随机种子集,我们应该能够复现。
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
sess = tf.Session()
tf.set_random_seed(5)
np.random.seed(42)

# 2. 声明批量大小,模型变量,数据集和占位符。
batch_size = 50
a1 = tf.Variable(tf.random_normal(shape=[1,1]))
b1 = tf.Variable(tf.random_uniform(shape=[1,1]))
a2 = tf.Variable(tf.random_normal(shape=[1,1]))
b2 = tf.Variable(tf.random_uniform(shape=[1,1]))

x = np.random.normal(2, 0.1, 500)
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)

# 3. 声明两个训练模型,即 sigmoid 激励模型和 ReLU 激励模型。
sigmoid_activation = tf.sigmoid(tf.add(tf.matmul(x_data, a1), b1))
relu_activation = tf.nn.relu(tf.add(tf.matmul(x_data, a2), b2))

# 4. 损失函数都采用模型输出和期望值 0.75 之间的差值的 L2 范数水平
loss1 = tf.reduce_mean(tf.square(tf.subtract(sigmoid_activation, 0.75)))
loss2 = tf.reduce_mean(tf.square(tf.subtract(relu_activation, 0.75)))

# 5. 声明优化算法,初始变量
my_opt = tf.train.GradientDescentOptimizer(0.01)
train_step_sigmoid = my_opt.minimize(loss1)
train_step_relu = my_opt.minimize(loss2)

init = tf.initialize_all_variables()
sess.run(init)

# 6. 遍历迭代训练模型,每个模型迭代750次。保存损失函数输出和激励函数的返回值,以便后续绘图。
loss_vec_sigmoid = []
loss_vec_relu = []
activation_sigmoid = []
activation_relu = []

for i in range(750):
    rand_indices = np.random.choice(len(x), size=batch_size)
    x_vals = np.transpose([x[rand_indices]])
    sess.run(train_step_sigmoid, feed_dict={x_data: x_vals})
    sess.run(train_step_relu, feed_dict={x_data: x_vals})
    
    loss_vec_sigmoid.append(sess.run(loss1, feed_dict={x_data: x_vals}))
    loss_vec_relu.append(sess.run(loss2, feed_dict={x_data: x_vals}))
    
    activation_sigmoid.append(np.mean(sess.run(sigmoid_activation, 
                                               feed_dict={x_data: x_vals})))
    activation_relu.append(np.mean(sess.run(relu_activation, 
                                               feed_dict={x_data: x_vals})))
    
# 7. 绘制损失函数和激励函数的代码
plt.plot(activation_sigmoid, 'k-', label='Sigmoid Activation')
plt.plot(activation_relu, 'r--', label='Relu Activation')
plt.ylim([0, 1.0])
plt.title('Activation Outputs')
plt.xlabel('Generation')
plt.ylabel('outputs')
plt.legend(loc='upper right')
plt.show()

plt.plot(loss_vec_sigmoid, 'k-', label='Sigmoid Loss')
plt.plot(loss_vec_relu, 'r--', label='Relu Loss')
plt.ylim([0, 1.0])
plt.title('Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('Loss')
plt.legend(loc='upper right')
plt.show()

注意,激励函数的选择对于神经网络算法的收敛是非常关键的。

用 TensorFlow 实现单层神经网络

我们已经实现了神经网络算法应用到真实数据集上的大部分操作,本节将实现一个单层神经网络,并在 iris 数据集上进行模型训练。

在本节中,我们将实现一个单隐藏层的神经网络算法。理解全连接神经网络算法主要是基于矩阵乘法的,这一点相当重要。并且,数据集和矩阵的维度对于算法模型正确有序地运行是非常关键的。

由于本例是一个回归算法问题,所以将使用均方误差作为损失函数。

# 导入必要的编程库,创建会画图
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets

# 加载 Iris 数据集,存储花萼长度作为目标值,然后开始一个计算图会话
iris = datasets.load_iris()
x_vals = np.array([x[0:3] for x in iris.data])
y_vals = np.array([x[3] for x in iris.data])
sess = tf.Session()

# 3. 因为数据集比较小,我们设置一个种子,使得返回的结果可以复现
seed = 2
tf.set_random_seed(seed)
np.random.seed(seed)

# 4. 为了准备数据集,我们创建一个80-20分的训练集和测试集。通过min-max 缩放法正则化x特征
# 值为 0 到 1 之间
train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]

def normalize_cols(m):
    col_max = m.max(axis=0)
    col_min = m.min(axis=0)
    return (m - col_min) / (col_max - col_min)

x_vals_train = np.nan_to_num(normalize_cols(x_vals_train))
x_vals_test = np.nan_to_num(normalize_cols(x_vals_test))

# 5. 现在为数据集和目标值声明批量大小和占位符
batch_size = 50
x_data = tf.placeholder(shape=[None, 3], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)

# 6. 这一步相当重要,声明有合适形状的模型变量。我们能声明隐藏层为任意大小,本例中设置为有
# 5个隐藏节点
hidden_layer_nodes = 5
A1 = tf.Variable(tf.random_normal(shape=[3, hidden_layer_nodes]))
b1 = tf.Variable(tf.random_normal(shape=[hidden_layer_nodes]))
A2 = tf.Variable(tf.random_normal(shape=[hidden_layer_nodes, 1]))
b2 = tf.Variable(tf.random_normal(shape=[1]))

# 7. 分两步声明训练模型:第一步,创建一个隐藏层输出;第二步:创建寻你连模型的最后输出
hidden_output = tf.nn.relu(tf.add(tf.matmul(x_data, A1), b1))
final_output = tf.nn.relu(tf.add(tf.matmul(hidden_output, A2), b2))

# 8. 这里定义均方误差作为损失函数
loss = tf.reduce_mean(tf.square(y_target - final_output))

# 9. 声明优化算法,初始化模型变量
my_opt = tf.train.GradientDescentOptimizer(0.005)
train_step = my_opt.minimize(loss)
init = tf.initialize_all_variables()
sess.run(init)

# 10. 遍历迭代训练模型。我们也初始化两个列表(list)存储训练损失和测试损失。在每次迭代
# 训练时,随机选择批量训练数据来拟合模型。
loss_vec = []
test_loss = []
for i in range(500):
    rand_index = np.random.choice(len(x_vals_train), size=batch_size)
    rand_x = x_vals_train[rand_index]
    rand_y = np.transpose([y_vals_train[rand_index]])
    
    sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
    
    temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
    
    loss_vec.append(temp_loss)
    
    test_temp_loss = sess.run(loss, feed_dict={x_data: x_vals_test, y_target:
                                               np.transpose([y_vals_test])})
        
    test_loss.append(np.sqrt(test_temp_loss))
    
    if (i+1) % 50 == 0:
        print('Generation: ' + str(i+1) + '.Loss = ' + str(temp_loss))
        
# 11. 使用 matplotlib 绘制损失函数
plt.plot(loss_vec, 'k-', label='Train Loss')
plt.plot(test_loss, 'r--', label='Test Loss')
plt.title('Loss (MSE) per Generation')
plt.xlabel('Generation')
plt.ylabel('Loss')
plt.legend(loc='upper right')
plt.show()

注意,我们通过可视化测试集和训练集的损失函数可以判定训练数据集上的模型训练是否过拟合,也可以发现测试集的损失函数比训练集更平滑,主要有两个原因:1,训练集的数据批量大小比测试集小;2,模型训练是在训练集上进行的,测试集对模型变量没有影响。

同时,上述代码训练时常不稳定。(是否与 A1,b1; A2, b2 的初始值有关)

用 TensorFlow 实现神经网络常见层:
本节将介绍如何实现常见层,包括卷积层和池化层。在上面我们实现了全连接层,本小节将其扩展到其他层。

我们已经研究了如何连接数据输入和全连接的隐藏层。TensorFlow 中有许多内建函数的多种类型的层,其中最流行的是卷积层和池化层。我们将展示如何在输入数据和全连接数据上创建和使用这些层。

首先,我们来介绍如何在一维数据上使用这些层,然后是在二维数据上使用它们。

神经网络算法的层能以任意形式组合,最常用的使用方法是用卷积层和全连接层来创建特征。如果我们有许多特征,常见的处理方法是采用池化层。在这些层之后常常引入激励函数。卷积神经网络算法,经常会包括卷积层,池化层,激励函数,等等。

# 1. 导入需要的编程库,创建计算图会话
import tensorflow as tf
import numpy as np
sess = tf.Session()

# 2. 初始化数据,该数据为 Numpy 数组,长度为25。创建传入数据占位符。
data_size = 25
data_1d = np.random.normal(size=data_size)
x_input_1d = tf.placeholder(shape=[data_size], dtype=tf.float32)

# 3. 定义一个卷积层。接着声明一个随机过滤层,创建一个卷积层。
# 注意,许多 TensorFlow 的层函数是为 4 维数据设计的(4D = [batch size, width, height,
# channels])。我们需要调输入数据和输出数据,包括扩展维度和降维。
# 在本例中,批量大小为 1,宽度为 1,高度为 25,颜色通道为1。
# 为了扩展维度,使用 expand_dims() 函数; 为了降低维度,使用 squeeze() 函数。
#  卷积层的输出结果的维度公式为 output_size = (W-F+2P)/S + 1,其中 W 为输入数据维度,
# F 为过滤层大小,P 是 padding 大小,S 是步长大小。

def conv_layer_1d(input_1d, my_filter):
    # make 1d input into 4d
    input_2d = tf.expand_dims(input_1d, 0)
    input_3d = tf.expand_dims(input_2d, 0)
    input_4d = tf.expand_dims(input_3d, 3)
    
    # perform convolution 
    convolution_output = tf.nn.conv2d(input_4d, filter=my_filter, 
                        strides=[1,1,1,1], padding="VALID")
    # Now drop extra dimensions
    conv_output_1d = tf.squeeze(convolution_output)
    return(conv_output_1d)

my_filter = tf.Variable(tf.random_normal(shape=[1,5,1,1]))
my_convolution_output = conv_layer_1d(x_input_1d, my_filter)

# 4. TensorFlow 的激励函数默认是逐个元素进行操作。这意味着,在部分层中使用激励函数。下面
# 创建一个激励函数并初始化
def activation(input_1d):
    return(tf.nn.relu(input_1d))

my_activation_output = activation(my_convolution_output)

# 5. 声明一个池化函数,该函数在一维向量的移动窗口上创建池化层函数。对于本例,宽度为5
def max_pool(input_1d, width):
    # first we make the 1d input into 4d
    input_2d = tf.expand_dims(input_1d, 0)
    input_3d = tf.expand_dims(input_2d, 0)
    input_4d = tf.expand_dims(input_3d, 3)
    # perform the max pool operation
    pool_output = tf.nn.max_pool(input_4d, ksize=[1, 1, width, 1],
                                 strides=[1, 1, 1, 1], padding='VALID')
    pool_output_1d = tf.squeeze(pool_output)
    return(pool_output_1d)

my_maxpool_output = max_pool(my_activation_output, width=5)

# 最后一层连接的是全连接层。创建一个函数,该函数输入一维数据,输出值的索引。记住,
# 一维数组做矩阵乘法需要提前扩展为二维。
def fully_connected(input_layer, num_outputs):
    # create weights
    weight_shape = tf.squeeze(tf.stack([tf.shape(input_layer), [num_outputs]]))
    weight = tf.random_normal(weight_shape, stddev=0.1)
    bias = tf.random_normal(shape=[num_outputs])
    # make input into 2d
    input_layer_2d = tf.expand_dims(input_layer, 0)
    # perform fully connected operations
    full_output = tf.add(tf.matmul(input_layer_2d, weight), bias)
    # drop extra dimensions
    full_output_1d = tf.squeeze(full_output)
    return(full_output_1d)

my_full_output = fully_connected(my_maxpool_output, 5)

# 初始化所有变量,运行计算图打印出每层的输出结果
init = tf.initialize_all_variables()
sess.run(init)
feed_dict = {x_input_1d: data_1d}

# convolution output
print('Input = array of length 25')
print('Convolution w/filter, length=5, stride=1, results in an array of length 21:')
print(sess.run(my_convolution_output, feed_dict=feed_dict))
# activation output
print('\nInput = the above array of length 21')
print('ReLU element wise returns the array of length 21')
print(sess.run(my_activation_output, feed_dict=feed_dict))
# maxpool output 
print('\nInput = the above array of length 21')
print('MaxPool, window length = 5, stride=1, results in the array of length 17')
print(sess.run(my_maxpool_output, feed_dict=feed_dict))
# fully connected output
print('\nInput = the above array of length 17')
print('Fully connected layer on all four rows with five output:')
print(sess.run(my_full_output, feed_dict=feed_dict))

神经网络对于 1 维数据非常重要。时序数据集、信号处理数据集和一些文本嵌入数据集都是一维数据,会频繁使用到神经网络算法。

下面开始在二维数据集上进行层函数操作:

# 1. 重置计算图会话
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()

# 2. 初始化输入数组为 10x10 的矩阵,然后初始化计算图的占位符
data_size = [10, 10]
data_2d = np.random.normal(size=data_size)
x_input_2d = tf.placeholder(dtype=tf.float32, shape=data_size)

# 3. 声明一个卷积层函数。因为数据集已经具有高度和宽度了,这里需要再扩展两维(批量大小为 1,
# 颜色通道为 1)即可使用卷积 conv2d() 函数。本例将使用一个随机的 2x2 过滤层,两个方向上
# 的步长和 valid padding (非零 padding)。由于输入数据是 10x10,因此卷积输出为 5x5。
def conv_layer_2d(input_2d, my_filter):
    # First, chage 2d input to 4d
    input_3d = tf.expand_dims(input_2d, 0)
    input_4d = tf.expand_dims(input_3d, 3)
    # Perform convolution
    convolution_output = tf.nn.conv2d(input_4d, filter=my_filter, 
                        strides=[1, 2, 2, 1], padding="VALID")
    # Drop extra dimensions
    conv_output_2d = tf.squeeze(convolution_output)
    return(conv_output_2d)

my_filter = tf.Variable(tf.random_normal(shape=[2,2,1,1]))
my_convolution_output = conv_layer_2d(x_input_2d, my_filter)

# 4. 激励函数是针对逐个元素的,现创建激励函数并初始化
def activation(input_2d):
    return(tf.nn.relu(input_2d))

my_activation_output = activation(my_convolution_output)

# 5. 本例的池化层与一维数据例子中的相似,有一点不同的是,我们需要声明池化层移动窗口的宽度
# 和高度。这里将与二维卷积层一样,将扩展池化层为二维。
def max_pool(input_2d, width, height):
    # make 2d input into 4d
    input_3d = tf.expand_dims(input_2d, 0)
    input_4d = tf.expand_dims(input_3d, 3)
    # perform max pool
    pool_output = tf.nn.max_pool(input_4d, ksize=[1, height, width, 1],
                                 strides=[1,1,1,1], padding="VALID")
    # drop extra dimensions
    pool_out_2d = tf.squeeze(pool_output)
    return(pool_out_2d)

my_maxpool_output = max_pool(my_activation_output, width=2, height=2)

# 6. 本例中的全连接层也与一维数据的输出相似。注意,全连接层的二维输入看作一个对象,为了实现
# 每项连接到每个输出,我们打平二维矩阵,然后再做矩阵乘法时再扩展维度。
def fully_connected(input_layer, num_outputs):
    # Flatten into 1d
    flat_input = tf.reshape(input_layer, [-1])
    # Create weights
    weight_shape = tf.squeeze(tf.stack([tf.shape(flat_input), [num_outputs]]))
    weight = tf.random_normal(weight_shape, stddev=0.1)
    bias = tf.random_normal(shape=[num_outputs])
    # Change into 2d
    input_2d = tf.expand_dims(flat_input, 0)
    # Perform fully connected operations
    full_output = tf.add(tf.matmul(input_2d, weight), bias)
    # Drop extra dimensions
    full_output_2d = tf.squeeze(full_output)
    return(full_output_2d)

my_full_output = fully_connected(my_maxpool_output, 5)

# 7. 初始化变量,创建一个赋值字典
init = tf.global_variables_initializer()
sess.run(init)

feed_dict = {x_input_2d: data_2d}

# 8. 打印每层的输出结果
# Convolution Output
print('\nInput = [10x10] array, 2x2 Convolution, stride=[2x2], results in the [5x5] array')
print(sess.run(my_convolution_output, feed_dict=feed_dict))
# Acivation Output
print('\nInput = the above [5x5] array:')
print('ReLU element wise returns the [5x5] array')
print(sess.run(my_activation_output, feed_dict=feed_dict))
# Max Pool Output
print('\nInput = the above [5x5] array:')
print('MaxPool, 2x2 pooling, strides=[1x1], results in the [4x4] array')
print(sess.run(my_maxpool_output, feed_dict=feed_dict))
# Fully Connected Output
print('\nInput = the above [4x4] array:')
print('Fully connected layer on all four rows with five outputs:')
print(sess.run(my_full_output, feed_dict=feed_dict))

我们学习了如何在一维数据集和二维数据集上使用 tensorflow 的卷积层和池化层。不管输入数据集的形状,最后的输出结果都是相同维度的。这实现了神经网络算法层的灵活性。同时,也了解了神经网络操作中形状和大小的重要性。

用 Tensor Flow 实现多层神经网络

本节将多层神经网络应用到实际场景中,预测低出生体重。

截止目前,我们学习了如何创建神经网络层,我们将运用该方法在低出生体重数据集上预测婴儿出生体重。我们将创建一个包含三个隐藏层的神经网络。低出生体重数据集包括实际的出生体重和是否超过 2500 克的标记。在本例中,我们将预测出生体重(回归预测),然后看最后分类结果的准确度(模型能够鉴别出生体重是否超过 2500 克)。

用 TensorFlow 基于神经网络实现井字棋

为了展示如何应用神经网络算法模型,我们将使用神经网络来学习优化井字棋。明确井字棋是一种决策性游戏,并且走棋步骤优化是确定的。

为了训练神经网络模型,我们有一系列优化的不同走棋棋谱,棋谱基于棋盘位置列表和对应的最佳落子点。考虑到棋盘的对称性,通过只关心不对称的棋盘位置来简化棋盘。井字棋的非单位变换(考虑几何变换)可以通过 90 度、180 度、270 度、Y 轴对称和 X 轴对称旋转获得。如果这个假设成立,我们使用一系列的棋盘位置列表和对应的最佳落子点,应用两个随机变换,然后赋值给神经网络算法模型。

除了计算模型损失之外,我们将用两种方法来检测算法模型的性能。第一种,从训练集中移除一个位置,然后优化走棋。这能看出神经网络算法模型能否生成以前从未有过的走棋(即该走棋不在训练集中);第二种,直接实站井字棋游戏看是否能赢。

# 1. 导入必要的编程库
import tensorflow as tf
import matplotlib.pyplot as plt
import csv
import random
import numpy as np
import random

# 2. 声明训练模型的批量大小
batch_size = 50

# 3. 为了让棋盘看起来更加清楚,我们创建一个井字棋的打印函数
def print_board(board):
    symbols = ['O', ' ', 'X']
    board_plus1 = [int(x) + 1 for x in board]
    board_line1 = ' {} | {} | {}'.format(symbols[board_plus1[0]],
                                         symbols[board_plus1[1]],
                                         symbols[board_plus1[2]])
    board_line2 = ' {} | {} | {}'.format(symbols[board_plus1[3]],
                                         symbols[board_plus1[4]],
                                         symbols[board_plus1[5]])
    board_line3 = ' {} | {} | {}'.format(symbols[board_plus1[6]],
                                         symbols[board_plus1[7]],
                                         symbols[board_plus1[8]])
    print(board_line1)
    print('___________')
    print(board_line2)
    print('___________')
    print(board_line3)
    
# 4. 创建 get_symmetry() 函数,返回变换之后的新棋盘和最佳落子点
def get_symmetry(board, play_response, transformation):
    """
    :param board: list of integers 9 long:
     opposing mark = -1
     friendly mark = 1
     empty space = 0
    :param play_response: integer of where response is (0-8)
    :param transformation: one of five transformations on a board:
     'rotate180', 'rotate90', 'rotate270', 'flip_v', 'flip_h'
    :return: tuple: (new_board, new_response)
    """
    if transformation == 'rotate180':
        new_response = 8 - play_response
        return board[::-1], new_response
    elif transformation == 'rotate90':
        new_response = [6, 3, 0, 7, 4, 1, 8, 5, 2].index(play_response)
        tuple_board = list(zip(*[board[6:9], board[3:6], board[0:3]]))
        return [value for item in tuple_board for value in item], new_response
    elif transformation == 'rotate270':
        new_response = [2, 5, 8, 1, 4, 7, 0, 3, 6].index(play_response)
        tuple_board = list(zip(*[board[0:3], board[3:6], board[6:9]]))[::-1]
        return [value for item in tuple_board for value in item], new_response
    elif transformation == 'flip_v':
        new_response = [6, 7, 8, 3, 4, 5, 0, 1, 2].index(play_response)
        return board[6:9] + board[3:6] + board[0:3], new_response
    elif transformation == 'flip_h':  # flip_h = rotate180, then flip_v
        new_response = [2, 1, 0, 5, 4, 3, 8, 7, 6].index(play_response)
        new_board = board[::-1]
        return new_board[6:9] + new_board[3:6] + new_board[0:3], new_response
    else:
        raise ValueError('Method not implemented.')
        
# 5. 棋盘位置列表和对应的最佳落子点数据位于 .csv 文件中。我们将创建 get_moves_from_csv()
# 函数来加载文件中的棋盘和最佳落子点数据,并保存成元组
def get_moves_from_csv(csv_file):
    """
    :param csv_file: csv file location containing the boards w/ responses
    :return: moves: list of moves with index of best response
    """
    play_moves = []
    with open(csv_file, 'rt') as csvfile:
        reader = csv.reader(csvfile, delimiter=',')
        for row in reader:
            play_moves.append(([int(x) for x in row[0:9]], int(row[9])))
    return play_moves

# 6. 创建一个 get_rand_move() 函数,返回一个随机变换棋盘和落子点。
def get_rand_move(play_moves, rand_transforms=2):
    """
    :param play_moves: list of the boards w/responses
    :param rand_transforms: how many random transforms performed on each
    :return: (board, response), board is a list of 9 integers, response is 1 int
    """
    (board, play_response) = random.choice(play_moves)
    possible_transforms = ['rotate90', 'rotate180', 'rotate270', 'flip_v', 'flip_h']
    for _ in range(rand_transforms):
        random_transform = random.choice(possible_transforms)
        (board, play_response) = get_symmetry(board, play_response, random_transform)
    return board, play_response

# 7. 初始化计算图会话,加载数据文件,创建训练集
moves = get_moves_from_csv('base_tic_tac_toe_moves.csv')
# Create a train set:
train_length = 500
train_set = []
for t in range(train_length):
    train_set.append(get_rand_move(moves))
    
# 8. 前面提到,我们将从训练集中移除一个棋盘位置和对应的最佳落子点,看训练的模型能否可以生成
# 正确走棋。下面棋盘的最佳落子点是棋盘位置索引为6的位置。
test_board = [-1, 0, 0, 1, -1, -1, 0, 0, 1]
train_set = [x for x in train_set if x[0] != test_board]

# 9. 创建 init_weights() 函数和 model() 函数,分别实现初始化模型变量和模型操作。
def init_weights(shape):
    return (tf.Variable(tf.random_normal(shape)))

def model(X, A1, A2, bias1, bias2):
    layer1 = tf.nn.sigmoid(tf.add(tf.matmul(X, A1), bias1))
    layer2 = tf.add(tf.matmul(layer1, A2), bias2)
    # Note: we don't take the softmax at the end 
    # because our cost function does that for us
    return (layer2)

# 10. 声明占位符,变量和模型
X = tf.placeholder(dtype=tf.float32, shape=[None, 9])
Y = tf.placeholder(dtype=tf.int32, shape=[None])

A1 = init_weights([9, 81])
bias1 = init_weights([81])
A2 = init_weights([81, 9])
bias2 = init_weights([9])

model_output = model(X, A1, A2, bias1, bias2)

# 11. 声明算法模型的损失函数,该函数时最后输出的逻辑变换的平均 softmax 值。然后声明训练
# 步长和优化器。为了将来可以和训练好的模型对局,我们也需要创建预测操作。
loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=model_output, labels=Y))
train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)
prediction = tf.argmax(model_output, 1)

# 12. 初始化变量,遍历迭代训练神经网络模型
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

loss_vec = []
for i in range(10000):
    rand_indices = np.random.choice(range(len(train_set)), batch_size, replace=False)
    batch_data = [train_set[i] for i in rand_indices]
    x_input = [x[0] for x in batch_data]
    y_target = np.array([y[1] for y in batch_data])
    sess.run(train_step, feed_dict={X: x_input, Y: y_target})
    
    temp_loss = sess.run(loss, feed_dict={X: x_input, Y: y_target})
    loss_vec.append(temp_loss)
    if i % 500 == 0:
        print('Iteration: {}, Loss: {}'.format(i, temp_loss))
        
# Print loss
plt.plot(loss_vec, 'k-', label='Loss')
plt.title('Loss (MSE) per Generation')
plt.xlabel('Generation')
plt.ylabel('Loss')
plt.show()

# Make Prediction: 为了测试模型,将展示如何在测试棋盘(从训练集中移除的数据)使用。
# 我们希望看到模型能生成预测落子点的索引,并且索引值为6。在大部分情况下,模型都会预测成功
test_boards = [test_board]
feed_dict = {X: test_boards}
logits = sess.run(model_output, feed_dict=feed_dict)
predictions = sess.run(prediction, feed_dict=feed_dict)
print(predictions)

# Declare function to check for win
def check(board):
    wins = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]
    for ix in range(len(wins)):
        if board[wins[ix][0]] == board[wins[ix][1]] == board[wins[ix][2]] == 1.:
            return 1
        elif board[wins[ix][0]] == board[wins[ix][1]] == board[wins[ix][2]] == -1.:
            return 1
    return 0

# Let's play against our model
game_tracker = [0., 0., 0., 0., 0., 0., 0., 0., 0.]
win_logical = False
num_moves = 0
while not win_logical:
    player_index = input('Input index of your move (0-8): ')
    num_moves += 1
    # Add player move to game
    game_tracker[int(player_index)] = 1.
    
    # Get model's move by first getting all the logits for each index
    [potential_moves] = sess.run(model_output, feed_dict={X: [game_tracker]})
    # Now find allowed moves (where game tracker values = 0.0)
    allowed_moves = [ix for ix, x in enumerate(game_tracker) if x == 0.0]
    # Find best move by taking argmax of logits if they are in allowed moves
    model_move = np.argmax([x if ix in allowed_moves else -999.0 for ix, x in enumerate(potential_moves)])
    
    # Add model move to game
    game_tracker[int(model_move)] = -1.
    print('Model has moved')
    print_board(game_tracker)
    # Now check for win or too many moves
    if check(game_tracker) == 1 or num_moves >= 5:
        print('Game Over!')
        win_logical = True

我们训练了一个神经网络用来玩井字棋游戏,该模型需要传入棋盘位置,其中棋盘的位置是用一个九维向量来表示的。然后预测最佳落子点。我们需要赋值可能的井字棋棋盘,应用随机转换来增加训练集的大小。

为了测试算法模型,我们移除一个棋盘位置列表和对应的最佳落子点,然后看训练模型能否生成预测的最佳落子点。最后,我们也和训练模型进行对局,但是结果并不理想。仍需要尝试不同的架构和训练方法来提高效果。

自然语言处理

本章将介绍 TensorFlow 在文本处理中的使用。先介绍“词袋”的工作原理和“词袋”的使用方法,然后讲解更高级的词嵌入方法,比如,Word2Vec 和 Doc2Vec。

  • 词袋的使用
  • TensorFlow 实现 TF-IDF 算法
  • TensorFlow 实现 skip-gram 算法
  • TensorFlow 实现 CBOW 词嵌入模型
  • 使用 TensorFlow 的 Word2Vec 预测
  • TensorFlow 实现基于 Doc2Vec 的情感分析

到目前为止,我们介绍的机器学习算法都是应用到数值输入。如果我们想应用到文本处理,就必须找到一种方法将文本转化成数字。有许多方法可以实现该功能,这里我们将介绍一些常用的方法。

我们将在本章阐述不同的词嵌入方法,试图解决上面的问题。下面先来卡下“词袋”的实现。

TF-IDF算法 词频问题

上一节中,我们已经描述了训练模型前的词嵌套。使用神经网络算法可以得到训练过程中的词嵌套向量。本节将阐述的方法称为 skip-gram 嵌套。

以上所用的模型全部都是线性逻辑回归模型。

卷积神经网络

在过去几年中,卷积神经网络在图象识别方面有了重大突破。

  • Tensorflow 实现简单的 CNN
  • Tensorflow 实现进阶的 CNN
  • 再训练已有的 CNN 模型
  • Tensorflow 实现模仿大师绘画(Neuralstyle)
  • Tensorflow 实现 DeepDreas

卷积神经网络介绍

从数学意义上讲,卷积是一个函数在另外一个函数中的叠加。

在图象中,应用卷积过滤器操作创建新特征层;卷积神经网络也有些必要的操作,比如引入非线性(ReLU函数),或者聚合参数(maxpool函数),以及其他相似的操作。

虽然我们准备创建自定义的 CNN 来进行图像识别,但是,强烈推荐读者使用现有的架构方案,我们在后续章节中也会使用。

  1. 用 TensorFlow 实现简单的 CNN

    本节将开发一个四层卷积神经网络,提升预测MNIST 数字的准确度。前两个卷积层由 Convolution-ReLU-maxpool 操作组成,后两层是全连接层。

     import matplotlib.pyplot as plt
     import numpy as np
     import tensorflow as tf
     from tensorflow.examples.tutorials.mnist import input_data
     from tensorflow.python.framework import ops
     ops.reset_default_graph()
    
     # Start a graph session
     sess = tf.Session()
    
     # Load data
     data_dir = 'temp'
     mnist = input_data.read_data_sets(data_dir, one_hot=False)
    
     # Convert images into 28x28 (they are downloaded as 1x784)
     train_xdata = np.array([np.reshape(x, (28, 28)) for x in mnist.train.images])
     test_xdata = np.array([np.reshape(x, (28, 28)) for x in mnist.test.images])
    
     # Convert labels into one-hot encoded vectors
     train_labels = mnist.train.labels
     test_labels = mnist.test.labels
    
     # Set model parameters
     batch_size = 100
     learning_rate = 0.005
     evaluation_size = 500
     image_width = train_xdata[0].shape[0]
     image_height = train_xdata[0].shape[1]
     target_size = np.max(train_labels) + 1
     num_channels = 1  # greyscale = 1 channel
     generations = 500
     eval_every = 50
     conv1_features = 25
     conv2_features = 50
     max_pool_size1 = 2  # NxN window for 1st max pool layer
     max_pool_size2 = 2  # NxN window for 2nd max pool layer
     fully_connected_size1 = 100
    
     # Declare model placeholders
     x_input_shape = (batch_size, image_width, image_height, num_channels)
     x_input = tf.placeholder(tf.float32, shape=x_input_shape)
     y_target = tf.placeholder(tf.int32, shape=(batch_size))
     eval_input_shape = (evaluation_size, image_width, image_height, num_channels)
     eval_input = tf.placeholder(tf.float32, shape=eval_input_shape)
     eval_target = tf.placeholder(tf.int32, shape=(evaluation_size))
    
     # Declare model parameters, (note that conv1_features means out_channels)
     conv1_weight = tf.Variable(tf.truncated_normal([4, 4, num_channels, conv1_features],
                                                 stddev=0.1, dtype=tf.float32))
     conv1_bias = tf.Variable(tf.zeros([conv1_features], dtype=tf.float32))
    
     conv2_weight = tf.Variable(tf.truncated_normal([4, 4, conv1_features, conv2_features],
                                                 stddev=0.1, dtype=tf.float32))
     conv2_bias = tf.Variable(tf.zeros([conv2_features], dtype=tf.float32))
    
     # fully connected variables
     resulting_width = image_width // (max_pool_size1 * max_pool_size2)
     resulting_height = image_height // (max_pool_size1 * max_pool_size2)
     full1_input_size = resulting_width * resulting_height * conv2_features
     full1_weight = tf.Variable(tf.truncated_normal([full1_input_size, fully_connected_size1],
                             stddev=0.1, dtype=tf.float32))
     full1_bias = tf.Variable(tf.truncated_normal([fully_connected_size1], stddev=0.1, dtype=tf.float32))
     full2_weight = tf.Variable(tf.truncated_normal([fully_connected_size1, target_size],
                                                 stddev=0.1, dtype=tf.float32))
     full2_bias = tf.Variable(tf.truncated_normal([target_size], stddev=0.1, dtype=tf.float32))
    
     # Initialize Model Operations
     def my_conv_net(conv_input_data):
         # First Conv-ReLU-MaxPool Layer
         conv1 = tf.nn.conv2d(conv_input_data, conv1_weight, strides=[1, 1, 1, 1], padding='SAME')
         relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_bias))
         max_pool1 = tf.nn.max_pool(relu1, ksize=[1, max_pool_size1, max_pool_size1, 1],
                                 strides=[1, max_pool_size1, max_pool_size1, 1], padding='SAME')
    
         # Second Conv-ReLU-MaxPool Layer
         conv2 = tf.nn.conv2d(max_pool1, conv2_weight, strides=[1, 1, 1, 1], padding='SAME')
         relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_bias))
         max_pool2 = tf.nn.max_pool(relu2, ksize=[1, max_pool_size2, max_pool_size2, 1],
                                 strides=[1, max_pool_size2, max_pool_size2, 1], padding='SAME')
    
         # Transform Output into a 1xN layer for next fully connected layer
         final_conv_shape = max_pool2.get_shape().as_list()
         final_shape = final_conv_shape[1] * final_conv_shape[2] * final_conv_shape[3]
         flat_output = tf.reshape(max_pool2, [final_conv_shape[0], final_shape])
    
         # First Fully Connected Layer
         fully_connected1 = tf.nn.relu(tf.add(tf.matmul(flat_output, full1_weight), full1_bias))
    
         # Second Fully Connected Layer
         final_model_output = tf.add(tf.matmul(fully_connected1, full2_weight), full2_bias)
         
         return final_model_output
    
     model_output = my_conv_net(x_input)
     test_model_output = my_conv_net(eval_input)
    
     # Declare Loss Function (softmax cross entropy)
     loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=model_output, labels=y_target))
    
     # Create a prediction function
     prediction = tf.nn.softmax(model_output)
     test_prediction = tf.nn.softmax(test_model_output)
    
     # Create accuracy function
     def get_accuracy(logits, targets):
         batch_predictions = np.argmax(logits, axis=1)
         num_correct = np.sum(np.equal(batch_predictions, targets))
         return 100. * num_correct/batch_predictions.shape[0]
    
     # Create an optimizer
     my_optimizer = tf.train.MomentumOptimizer(learning_rate, 0.9)
     train_step = my_optimizer.minimize(loss)
    
     # Initialize Variables
     init = tf.global_variables_initializer()
     sess.run(init)
    
     # Start training loop
     train_loss = []
     train_acc = []
     test_acc = []
    
     for i in range(generations):
         rand_index = np.random.choice(len(train_xdata), size=batch_size)
         rand_x = train_xdata[rand_index]
         rand_x = np.expand_dims(rand_x, 3)
         rand_y = train_labels[rand_index]
         train_dict = {x_input: rand_x, y_target: rand_y}
         
         sess.run(train_step, feed_dict=train_dict)
         temp_train_loss, temp_train_preds = sess.run([loss, prediction], feed_dict=train_dict)
         temp_train_acc = get_accuracy(temp_train_preds, rand_y)
         
         if (i+1) % eval_every == 0:
             eval_index = np.random.choice(len(test_xdata), size=evaluation_size)
             eval_x = test_xdata[eval_index]
             eval_x = np.expand_dims(eval_x, 3)
             eval_y = test_labels[eval_index]
             test_dict = {eval_input: eval_x, eval_target: eval_y}
             test_preds = sess.run(test_prediction, feed_dict=test_dict)
             temp_test_acc = get_accuracy(test_preds, eval_y)
             
             # Record and print results
             train_loss.append(temp_train_loss)
             train_acc.append(temp_train_acc)
             test_acc.append(temp_test_acc)
             acc_and_loss = [(i+1), temp_train_loss, temp_train_acc, temp_test_acc]
             acc_and_loss = [np.round(x, 2) for x in acc_and_loss]
             print('Generation # {}. Train Loss: {:.2f}. Train Acc (Test Acc): {:.2f} ({:.2f})'.format(*acc_and_loss))
             
     # Matlotlib code to plot the loss and accuracies
     eval_indices = range(0, generations, eval_every)
     # Plot loss over time
     plt.plot(eval_indices, train_loss, 'k-')
     plt.title('Softmax Loss per Generation')
     plt.xlabel('Generation')
     plt.ylabel('Softmax Loss')
     plt.show()
    
     # Plot train and test accuracy
     plt.plot(eval_indices, train_acc, 'k-', label='Train Set Accuracy')
     plt.plot(eval_indices, test_acc, 'r--', label='Test Set Accuracy')
     plt.title('Train and Test Accuracy')
     plt.xlabel('Generation')
     plt.ylabel('Accuracy')
     plt.legend(loc='lower right')
     plt.show()
    
     # Plot some samples
     # Plot the 6 of the last batch results:
     actuals = rand_y[0:6]
     predictions = np.argmax(temp_train_preds, axis=1)[0:6]
     images = np.squeeze(rand_x[0:6])
    
     Nrows = 2
     Ncols = 3
     for i in range(6):
         plt.subplot(Nrows, Ncols, i+1)
         plt.imshow(np.reshape(images[i], [28, 28]), cmap='Greys_r')
         plt.title('Actual: ' + str(actuals[i]) + ' Pred: ' + str(predictions[i]),
                 fontsize=10)
         frame = plt.gca()
         frame.axes.get_xaxis().set_visible(False)
         frame.axes.get_yaxis().set_visible(False)
    

CNN 模型在图像识别上的扩展是如何增加网络深度的。如果我们有足够大的数据集,就会提高预测的准确度。重复卷积操作,maxpool 操作和 ReLU 操作是增加神经网络深度的标准方法。许多准确度高的图象识别网络都使用该方法。

  1. 用 TensorFlow 实现进阶的 CNN

    本节将实现一个更复杂的读取图象数据的方法,并使用更大的 CNN 模型进行 CIFAR10 数据集上的图象进行识别。该图片数据集有 60,000 张 32x32 像素的图片,分 10 个类别。

    大部分图片数据集都太大,不能全部放入内存。TensorFlow 的做法是建立一个图象管道,从文件中一次批量读取;而我们会建立一个图象数据读取器,然后创建一个批量数据的队列。

    一般地,对于图象识别的数据,都会在模型训练前将图片随机打乱。本例中,我们将进行随机裁剪,翻转和调节亮度。

    本节是 TensorFlow 官方 CIFAR-10 例子的改写版。我们将官方例子写成脚本,并逐行解读重要的代码。我们也会将一些常见值和参数调整为论文中引用的值,具体会在详细步骤中指明。

    # 加载必要的编程库
    import os
    import sys
    import tarfile
    import matplotlib.pyplot as plt
    import numpy as np
    import tensorflow as tf
    from six.moves import urllib
    from tensorflow.python.framework import ops
    ops.reset_default_graph()
    
    # Change Directory
    try:
        abspath = os.path.abspath(__file__)
    except NameError:
        abspath = os.getcwd()
    dname = os.path.dirname(abspath)
    os.chdir(dname)
    
    # Start a graph session
    sess = tf.Session()
    
    # Set model parameters
    batch_size = 128
    data_dir = 'temp'
    output_every = 50
    generations = 20000
    eval_every = 500
    image_height = 32
    image_width = 32
    crop_height = 24
    crop_width = 24
    num_channels = 3
    num_targets = 10
    extract_folder = 'cifar-10-batches-bin'
    
    # Exponential Learning Rate Decay Params
    learning_rate = 0.1
    lr_decay = 0.1
    num_gens_to_wait = 250.
    
    # Extract model parameters
    image_vec_length = image_height * image_width * num_channels
    record_length = 1 + image_vec_length # ( + 1 for the 0-9 label)
    
    # Load data
    data_dir = 'temp'
    if not os.path.exists(data_dir):
        os.makedirs(data_dir)
    cifar10_url = 'http://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz'
    
    # Check if file exists, otherwise download it
    data_file = os.path.join(data_dir, 'cifar-10-binary.tar.gz')
    if os.path.isfile(data_file):
        pass
    else:
        # Download file
        def progress(block_num, block_size, total_size):
            progress_info = [cifar10_url, float(block_num * block_size) / float(total_size) * 100.0]
            print('\r Downloading {} - {:.2f}%'.format(*progress_info), end="")
        filepath, _ = urllib.request.urlretrieve(cifar10_url, data_file, progress)
        # Extract file
        tarfile.open(filepath, 'r:gz').extractall(data_dir)
        
    # Define CIFAR reader
    def read_cifar_files(filename_queue, distort_images = True):
        reader = tf.FixedLengthRecordReader(record_bytes=record_length)
        key, record_string = reader.read(filename_queue)
        record_bytes = tf.decode_raw(record_string, tf.uint8)
        image_label = tf.cast(tf.slice(record_bytes, [0], [1]), tf.int32)
    
        # Extract image
        image_extracted = tf.reshape(tf.slice(record_bytes, [1], [image_vec_length]),
                                    [num_channels, image_height, image_width])
        
        # Reshape image
        image_uint8image = tf.transpose(image_extracted, [1, 2, 0])
        reshaped_image = tf.cast(image_uint8image, tf.float32)
        # Randomly Crop image
        final_image = tf.image.resize_image_with_crop_or_pad(reshaped_image, crop_width, crop_height)
        
        if distort_images:
            # Randomly flip the image horizontally, change the brightness and contrast
            final_image = tf.image.random_flip_left_right(final_image)
            final_image = tf.image.random_brightness(final_image,max_delta=63)
            final_image = tf.image.random_contrast(final_image,lower=0.2, upper=1.8)
    
        # Normalize whitening
        final_image = tf.image.per_image_standardization(final_image)
        return final_image, image_label
    
    # Create a CIFAR image pipeline from reader
    def input_pipeline(batch_size, train_logical=True):
        if train_logical:
            files = [os.path.join(data_dir, extract_folder, 'data_batch_{}.bin'.format(i)) for i in range(1,6)]
        else:
            files = [os.path.join(data_dir, extract_folder, 'test_batch.bin')]
        filename_queue = tf.train.string_input_producer(files)
        image, label = read_cifar_files(filename_queue)
        
        # min_after_dequeue defines how big a buffer we will randomly sample
        #   from -- bigger means better shuffling but slower start up and more
        #   memory used.
        # capacity must be larger than min_after_dequeue and the amount larger
        #   determines the maximum we will prefetch.  Recommendation:
        #   min_after_dequeue + (num_threads + a small safety margin) * batch_size
        min_after_dequeue = 1000
        capacity = min_after_dequeue + 3 * batch_size
        example_batch, label_batch = tf.train.shuffle_batch([image, label],
                                                            batch_size=batch_size,
                                                            capacity=capacity,
                                                            min_after_dequeue=min_after_dequeue)
    
        return example_batch, label_batch
    
    # Define the model architecture, this will return logits from images
    def cifar_cnn_model(input_images, batch_size, train_logical=True):
        def truncated_normal_var(name, shape, dtype):
            return(tf.get_variable(name=name, shape=shape, dtype=dtype, initializer=tf.truncated_normal_initializer(stddev=0.05)))
        def zero_var(name, shape, dtype):
            return(tf.get_variable(name=name, shape=shape, dtype=dtype, initializer=tf.constant_initializer(0.0)))
        
        # First Convolutional Layer
        with tf.variable_scope('conv1') as scope:
            # Conv_kernel is 5x5 for all 3 colors and we will create 64 features
            conv1_kernel = truncated_normal_var(name='conv_kernel1', shape=[5, 5, 3, 64], dtype=tf.float32)
            # We convolve across the image with a stride size of 1
            conv1 = tf.nn.conv2d(input_images, conv1_kernel, [1, 1, 1, 1], padding='SAME')
            # Initialize and add the bias term
            conv1_bias = zero_var(name='conv_bias1', shape=[64], dtype=tf.float32)
            conv1_add_bias = tf.nn.bias_add(conv1, conv1_bias)
            # ReLU element wise
            relu_conv1 = tf.nn.relu(conv1_add_bias)
        
        # Max Pooling
        pool1 = tf.nn.max_pool(relu_conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],padding='SAME', name='pool_layer1')
        
        # Local Response Normalization (parameters from paper)
        # paper: http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks
        norm1 = tf.nn.lrn(pool1, depth_radius=5, bias=2.0, alpha=1e-3, beta=0.75, name='norm1')
    
        # Second Convolutional Layer
        with tf.variable_scope('conv2') as scope:
            # Conv kernel is 5x5, across all prior 64 features and we create 64 more features
            conv2_kernel = truncated_normal_var(name='conv_kernel2', shape=[5, 5, 64, 64], dtype=tf.float32)
            # Convolve filter across prior output with stride size of 1
            conv2 = tf.nn.conv2d(norm1, conv2_kernel, [1, 1, 1, 1], padding='SAME')
            # Initialize and add the bias
            conv2_bias = zero_var(name='conv_bias2', shape=[64], dtype=tf.float32)
            conv2_add_bias = tf.nn.bias_add(conv2, conv2_bias)
            # ReLU element wise
            relu_conv2 = tf.nn.relu(conv2_add_bias)
        
        # Max Pooling
        pool2 = tf.nn.max_pool(relu_conv2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME', name='pool_layer2')    
        
        # Local Response Normalization (parameters from paper)
        norm2 = tf.nn.lrn(pool2, depth_radius=5, bias=2.0, alpha=1e-3, beta=0.75, name='norm2')
        
        # Reshape output into a single matrix for multiplication for the fully connected layers
        reshaped_output = tf.reshape(norm2, [batch_size, -1])
        reshaped_dim = reshaped_output.get_shape()[1].value
        
        # First Fully Connected Layer
        with tf.variable_scope('full1') as scope:
            # Fully connected layer will have 384 outputs.
            full_weight1 = truncated_normal_var(name='full_mult1', shape=[reshaped_dim, 384], dtype=tf.float32)
            full_bias1 = zero_var(name='full_bias1', shape=[384], dtype=tf.float32)
            full_layer1 = tf.nn.relu(tf.add(tf.matmul(reshaped_output, full_weight1), full_bias1))
    
        # Second Fully Connected Layer
        with tf.variable_scope('full2') as scope:
            # Second fully connected layer has 192 outputs.
            full_weight2 = truncated_normal_var(name='full_mult2', shape=[384, 192], dtype=tf.float32)
            full_bias2 = zero_var(name='full_bias2', shape=[192], dtype=tf.float32)
            full_layer2 = tf.nn.relu(tf.add(tf.matmul(full_layer1, full_weight2), full_bias2))
    
        # Final Fully Connected Layer -> 10 categories for output (num_targets)
        with tf.variable_scope('full3') as scope:
            # Final fully connected layer has 10 (num_targets) outputs.
            full_weight3 = truncated_normal_var(name='full_mult3', shape=[192, num_targets], dtype=tf.float32)
            full_bias3 =  zero_var(name='full_bias3', shape=[num_targets], dtype=tf.float32)
            final_output = tf.add(tf.matmul(full_layer2, full_weight3), full_bias3)
            
        return final_output
    
    # Loss function
    def cifar_loss(logits, targets):
        # Get rid of extra dimensions and cast targets into integers
        targets = tf.squeeze(tf.cast(targets, tf.int32))
        # Calculate cross entropy from logits and targets
        cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
                                                                    logits=logits, labels=targets)
        # Take the average loss across batch size
        cross_entropy_mean = tf.reduce_mean(cross_entropy, name='cross_entropy')
        return cross_entropy_mean
    
    # Train step
    def train_step(loss_value, generation_num):
        # Our learning rate is an exponential decay after we wait a fair number of generations
        model_learning_rate = tf.train.exponential_decay(learning_rate, generation_num,
                                                        num_gens_to_wait, lr_decay, staircase=True)
        # Create optimizer
        my_optimizer = tf.train.GradientDescentOptimizer(model_learning_rate)
        # Initialize train step
        train_step = my_optimizer.minimize(loss_value)
        return train_step
    
    # Accuracy function
    def accuracy_of_batch(logits, targets):
        # Make sure targets are integers and drop extra dimensions
        targets = tf.squeeze(tf.cast(targets, tf.int32))
        # Get predicted values by finding which logit is the greatest
        batch_predictions = tf.cast(tf.argmax(logits, 1), tf.int32)
        # Check if they are equal across the batch
        predicted_correctly = tf.equal(batch_predictions, targets)
        # Average the 1's and 0's (True's and False's) across the batch size
        accuracy = tf.reduce_mean(tf.cast(predicted_correctly, tf.float32))
        return accuracy
    
    # Get data
    print('Getting/Transforming Data.')
    # Initialize the data pipeline
    images, targets = input_pipeline(batch_size, train_logical=True)
    # Get batch test images and targets from pipline
    test_images, test_targets = input_pipeline(batch_size, train_logical=False)
    
    # Declare Model
    print('Creating the CIFAR10 Model.')
    with tf.variable_scope('model_definition') as scope:
        # Declare the training network model
        model_output = cifar_cnn_model(images, batch_size)
        # This is very important!!!  We must set the scope to REUSE the variables,
        #  otherwise, when we set the test network model, it will create new random
        #  variables.  Otherwise we get random evaluations on the test batches.
        scope.reuse_variables()
        test_output = cifar_cnn_model(test_images, batch_size)
        
    # Declare loss function
    print('Declare Loss Function.')
    loss = cifar_loss(model_output, targets)
    
    # Create accuracy function
    accuracy = accuracy_of_batch(test_output, test_targets)
    
    # Create training operations
    print('Creating the Training Operation.')
    generation_num = tf.Variable(0, trainable=False)
    train_op = train_step(loss, generation_num)
    
    # Initialize Variables
    print('Initializing the Variables.')
    init = tf.global_variables_initializer()
    sess.run(init)
    
    # Initialize queue (This queue will feed into the model, so no placeholders necessary)
    tf.train.start_queue_runners(sess=sess)
    
    # Train CIFAR Model
    print('Starting Training')
    train_loss = []
    test_accuracy = []
    for i in range(generations):
        _, loss_value = sess.run([train_op, loss])
        
        if (i+1) % output_every == 0:
            train_loss.append(loss_value)
            output = 'Generation {}: Loss = {:.5f}'.format((i+1), loss_value)
            print(output)
        
        if (i+1) % eval_every == 0:
            [temp_accuracy] = sess.run([accuracy])
            test_accuracy.append(temp_accuracy)
            acc_output = ' --- Test Accuracy = {:.2f}%.'.format(100.*temp_accuracy)
            print(acc_output)
            
    # Print loss and accuracy
    # Matlotlib code to plot the loss and accuracies
    eval_indices = range(0, generations, eval_every)
    output_indices = range(0, generations, output_every)
    
    # Plot loss over time
    plt.plot(output_indices, train_loss, 'k-')
    plt.title('Softmax Loss per Generation')
    plt.xlabel('Generation')
    plt.ylabel('Softmax Loss')
    plt.show()
    
    # Plot accuracy over time
    plt.plot(eval_indices, test_accuracy, 'k-')
    plt.title('Test Accuracy')
    plt.xlabel('Generation')
    plt.ylabel('Accuracy')
    plt.show()
    

下载 CIFAR-10 图片数据集后,我们建立图像管道。关于图象管道的详细信息请见官方 TensorFlow CIFAR-10 示例。我们使用训练图片管道和测试图片管道预测图片的准确分类。在最后,模型训练在测试数据集上达到了 75% 的准确度。

  1. 再训练已有的 CNN 模型

    从原始数据集开始训练一个全新的图象识别模型需要耗费大量时间和计算力。我们可以重用预训练好的网络训练图片,将会缩短计算时间。本节将展示如何使用预训练好的 TensorFlow 图象模型,微调后训练其他图片数据集。

    基本思路:重用与训练模型的卷积层的权重和结构,然后重新训练全连接层。TensorFlow 官方提供了一个利用已有的 CNN 模型进行再训练的例子。本节将展示如何在 CIFAR-10 图片数据集上使用相同的方法。我们采用的 CNN 网络使用了非常流行的 Inception 架构。 Inception CNN 模型由 google 公司创建,并在许多图象识别基准测试中表现不俗。

    下面的 python 脚本显示如何下载 CIFAR-10 图片数据集,自动分割图片数据,标注,并保存到训练集和测试集文件中的十个分类;然后展示如何训练图片数据集。

     import os
     import tarfile
     import _pickle as cPickle
     import numpy as np
     import urllib.request
     import scipy.misc
     from tensorflow.python.framework import ops
     ops.reset_default_graph()
    
     cifar_link = 'https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz'
     data_dir = 'temp'
     if not os.path.isdir(data_dir):
         os.makedirs(data_dir)
         
     # Download tar file
     target_file = os.path.join(data_dir, 'cifar-10-python.tar.gz')
     if not os.path.isfile(target_file):
         print('CIFAR-10 file not found. Downloading CIFAR data (Size = 163MB)')
         print('This may take a few minutes, please wait.')
         filename, headers = urllib.request.urlretrieve(cifar_link, target_file)
         
     # Extract into memory
     tar = tarfile.open(target_file)
     tar.extractall(path=data_dir)
     tar.close()
     objects = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
    
     # Create train image folders
     train_folder = 'train_dir'
     if not os.path.isdir(os.path.join(data_dir, train_folder)):
         for i in range(10):
             folder = os.path.join(data_dir, train_folder, objects[i])
             os.makedirs(folder)
     # Create test image folders
     test_folder = 'validation_dir'
     if not os.path.isdir(os.path.join(data_dir, test_folder)):
         for i in range(10):
             folder = os.path.join(data_dir, test_folder, objects[i])
             os.makedirs(folder)
    
     # Extract images accordingly
     data_location = os.path.join(data_dir, 'cifar-10-batches-py')
     train_names = ['data_batch_' + str(x) for x in range(1,6)]
     test_names = ['test_batch']
    
     # Create train image folders
     train_folder = 'train_dir'
     if not os.path.isdir(os.path.join(data_dir, train_folder)):
         for i in range(10):
             folder = os.path.join(data_dir, train_folder, objects[i])
             os.makedirs(folder)
     # Create test image folders
     test_folder = 'validation_dir'
     if not os.path.isdir(os.path.join(data_dir, test_folder)):
         for i in range(10):
             folder = os.path.join(data_dir, test_folder, objects[i])
             os.makedirs(folder)
             
     # Extract images accordingly
     data_location = os.path.join(data_dir, 'cifar-10-batches-py')
     train_names = ['data_batch_' + str(x) for x in range(1,6)]
     test_names = ['test_batch']
    
     def load_batch_from_file(file):
         file_conn = open(file, 'rb')
         image_dictionary = cPickle.load(file_conn, encoding='latin1')
         file_conn.close()
         return image_dictionary
    
    
     def save_images_from_dict(image_dict, folder='data_dir'):
         # image_dict.keys() = 'labels', 'filenames', 'data', 'batch_label'
         for ix, label in enumerate(image_dict['labels']):
             folder_path = os.path.join(data_dir, folder, objects[label])
             filename = image_dict['filenames'][ix]
             #Transform image data
             image_array = image_dict['data'][ix]
             image_array.resize([3, 32, 32])
             # Save image
             output_location = os.path.join(folder_path, filename)
             scipy.misc.imsave(output_location,image_array.transpose())
             
     # Sort train images
     for file in train_names:
         print('Saving images from file: {}'.format(file))
         file_location = os.path.join(data_dir, 'cifar-10-batches-py', file)
         image_dict = load_batch_from_file(file_location)
         save_images_from_dict(image_dict, folder=train_folder)
    
     # Sort test images
     for file in test_names:
         print('Saving images from file: {}'.format(file))
         file_location = os.path.join(data_dir, 'cifar-10-batches-py', file)
         image_dict = load_batch_from_file(file_location)
         save_images_from_dict(image_dict, folder=test_folder)
         
     # Create labels file
     cifar_labels_file = os.path.join(data_dir,'cifar10_labels.txt')
     print('Writing labels file, {}'.format(cifar_labels_file))
     with open(cifar_labels_file, 'w') as labels_file:
         for item in objects:
             labels_file.write("{}\n".format(item))
    
     # After this is done, we proceed with the TensorFlow fine-tuning tutorial.
    
     # https://www.tensorflow.org/tutorials/image_retraining
    
  2. 用 TensorFlow 实现模仿大师绘画

    图像识别 CNN 模型训练好之后,我们能用网络结构训练其他感兴趣的数据或者图像处理。Stylenet 程序试图学习一副图的风格,并将该图像风格应用于另外一副图(保持后者的图片结构或者内容)。如果我们能找到 CNN 模型中间层节点分离出图象风格,就可以应用于另外的图片内容上。

    A Neural Algorithm of Artistic Style,该文章作者发现一些 CNN 模型的中间层存在某些属性可以编码图片风格和图片内容。最后,我们从风格图片中训练图片风格层,从原始图片中训练图片内容层,并且反向传播这些计算损失函数,从而让原始图片更像风格图片。

    我们将下载文章中推荐的网络——imagenet-vgg-19。 imagenet-vgg-16 网络也表现不错,但是前述文章中推荐的是 imagenet-vgg-19。

    下载预训练的网络,存为 .mat 文件格式。mat 文件格式是一种 matlab 对象,利用 Python 的 scipy 模块读取该文件。下面是下载 mat 对象的链接,该模型保存在 Python 脚本同一文件夹下。

    Using TensorFlow for Stylenet/NeuralStyle

    We use two images, an original image and a style image
    and try to make the original image in the style of the style image.

    Reference paper:
    https://arxiv.org/abs/1508.06576

    Need to download the model ‘imagenet-vgg-verydee-19.mat’ from:
    http://www.vlfeat.org/matconvnet/models/beta16/imagenet-vgg-verydeep-19.mat

     import os
     import scipy.io
     import scipy.misc
     import imageio
     from skimage.transform import resize
     from operator import mul
     from functools import reduce
     import numpy as np
     import tensorflow as tf
     from tensorflow.python.framework import ops
     ops.reset_default_graph()
    
     # Image Files
     original_image_file = 'images/book_cover.jpg'
     style_image_file = 'images/starry_night.jpg'
    
     # Saved VGG Network path under the current project dir.
     vgg_path = 'imagenet-vgg-verydeep-19.mat'
    
     # Default Arguments
     original_image_weight = 5.0
     style_image_weight = 500.0
     regularization_weight = 100
     learning_rate = 10
     generations = 200
     output_generations = 25
     beta1 = 0.9
     beta2 = 0.999
    
     # Read in images
     original_image = imageio.imread(original_image_file)
     style_image = imageio.imread(style_image_file)
    
     # Get shape of target and make the style image the same
     target_shape = original_image.shape
     style_image = resize(style_image, target_shape)
    
     # VGG-19 Layer Setup
     # From paper
     vgg_layers = ['conv1_1', 'relu1_1',
                 'conv1_2', 'relu1_2', 'pool1',
                 'conv2_1', 'relu2_1',
                 'conv2_2', 'relu2_2', 'pool2',
                 'conv3_1', 'relu3_1',
                 'conv3_2', 'relu3_2',
                 'conv3_3', 'relu3_3',
                 'conv3_4', 'relu3_4', 'pool3',
                 'conv4_1', 'relu4_1',
                 'conv4_2', 'relu4_2',
                 'conv4_3', 'relu4_3',
                 'conv4_4', 'relu4_4', 'pool4',
                 'conv5_1', 'relu5_1',
                 'conv5_2', 'relu5_2',
                 'conv5_3', 'relu5_3',
                 'conv5_4', 'relu5_4']
    
     # Extract weights and matrix means
     def extract_net_info(path_to_params):
         vgg_data = scipy.io.loadmat(path_to_params)
         normalization_matrix = vgg_data['normalization'][0][0][0]
         mat_mean = np.mean(normalization_matrix, axis=(0,1))
         network_weights = vgg_data['layers'][0]
         return mat_mean, network_weights
    
     # Create the VGG-19 Network
     def vgg_network(network_weights, init_image):
         network = {}
         image = init_image
    
         for i, layer in enumerate(vgg_layers):
             if layer[0] == 'c':
                 weights, bias = network_weights[i][0][0][0][0]
                 weights = np.transpose(weights, (1, 0, 2, 3))
                 bias = bias.reshape(-1)
                 conv_layer = tf.nn.conv2d(image, tf.constant(weights), (1, 1, 1, 1), 'SAME')
                 image = tf.nn.bias_add(conv_layer, bias)
             elif layer[0] == 'r':
                 image = tf.nn.relu(image)
             else:  # pooling
                 image = tf.nn.max_pool(image, (1, 2, 2, 1), (1, 2, 2, 1), 'SAME')
             network[layer] = image
         return network
    
     # Here we define which layers apply to the original or style image
     original_layers = ['relu4_2', 'relu5_2']
     style_layers = ['relu1_1', 'relu2_1', 'relu3_1', 'relu4_1', 'relu5_1']
    
     # Get network parameters
     normalization_mean, network_weights = extract_net_info(vgg_path)
    
     shape = (1,) + original_image.shape
     style_shape = (1,) + style_image.shape
     original_features = {}
     style_features = {}
    
     # Set style weights
     style_weights = {l: 1./(len(style_layers)) for l in style_layers}
    
     # Computer feature layers with original image
     g_original = tf.Graph()
     with g_original.as_default(), tf.Session() as sess1:
         image = tf.placeholder('float', shape=shape)
         vgg_net = vgg_network(network_weights, image)
         original_minus_mean = original_image - normalization_mean
         original_norm = np.array([original_minus_mean])
         for layer in original_layers:
             original_features[layer] = vgg_net[layer].eval(feed_dict={image: original_norm})
             
     # Get style image network
     g_style = tf.Graph()
     with g_style.as_default(), tf.Session() as sess2:
         image = tf.placeholder('float', shape=style_shape)
         vgg_net = vgg_network(network_weights, image)
         style_minus_mean = style_image - normalization_mean
         style_norm = np.array([style_minus_mean])
         for layer in style_layers:
             features = vgg_net[layer].eval(feed_dict={image: style_norm})
             features = np.reshape(features, (-1, features.shape[3]))
             gram = np.matmul(features.T, features) / features.size
             style_features[layer] = gram
             
     # Make Combined Image via loss function
     with tf.Graph().as_default():
         # Get network parameters
         initial = tf.random_normal(shape) * 0.256
         init_image = tf.Variable(initial)
         vgg_net = vgg_network(network_weights, init_image)
    
         # Loss from Original Image
         original_layers_w = {'relu4_2': 0.5, 'relu5_2': 0.5}
         original_loss = 0
         for o_layer in original_layers:
             temp_original_loss = original_layers_w[o_layer] * original_image_weight *\
                                 (2 * tf.nn.l2_loss(vgg_net[o_layer] - original_features[o_layer]))
             original_loss += (temp_original_loss / original_features[o_layer].size)
    
         # Loss from Style Image
         style_loss = 0
         style_losses = []
         for style_layer in style_layers:
             layer = vgg_net[style_layer]
             feats, height, width, channels = [x.value for x in layer.get_shape()]
             size = height * width * channels
             features = tf.reshape(layer, (-1, channels))
             style_gram_matrix = tf.matmul(tf.transpose(features), features) / size
             style_expected = style_features[style_layer]
             style_losses.append(style_weights[style_layer] * 2 *
                                 tf.nn.l2_loss(style_gram_matrix - style_expected) /
                                 style_expected.size)
         style_loss += style_image_weight * tf.reduce_sum(style_losses)
    
         # To Smooth the results, we add in total variation loss
         total_var_x = reduce(mul, init_image[:, 1:, :, :].get_shape().as_list(), 1)
         total_var_y = reduce(mul, init_image[:, :, 1:, :].get_shape().as_list(), 1)
         first_term = regularization_weight * 2
         second_term_numerator = tf.nn.l2_loss(init_image[:, 1:, :, :] - init_image[:, :shape[1]-1, :, :])
         second_term = second_term_numerator / total_var_y
         third_term = (tf.nn.l2_loss(init_image[:, :, 1:, :] - init_image[:, :, :shape[2]-1, :]) / total_var_x)
         total_variation_loss = first_term * (second_term + third_term)
    
         # Combined Loss
         loss = original_loss + style_loss + total_variation_loss
    
         # Declare Optimization Algorithm
         optimizer = tf.train.AdamOptimizer(learning_rate, beta1, beta2)
         train_step = optimizer.minimize(loss)
    
         # Initialize variables and start training
         with tf.Session() as sess:
             tf.global_variables_initializer().run()
             for i in range(generations):
    
                 train_step.run()
    
                 # Print update and save temporary output
                 if (i+1) % output_generations == 0:
                     print('Generation {} out of {}, loss: {}'.format(i + 1, generations,sess.run(loss)))
                     image_eval = init_image.eval()
                     best_image_add_mean = image_eval.reshape(shape[1:]) + normalization_mean
                     output_file = 'temp_output_{}.jpg'.format(i)
                     imageio.imwrite(output_file, best_image_add_mean)
             
             
             # Save final image
             image_eval = init_image.eval()
             best_image_add_mean = image_eval.reshape(shape[1:]) + normalization_mean
             output_file = 'final_output.jpg'
             #scipy.misc.imsave(output_file, best_image_add_mean)
             imageio.imwrite(output_file, best_image_add_mean)
    

    首先,加载两幅图片,然后加载预训练好的网络权重,为原始图片和风格图片分配网络层。我们计算三种损失函数:原始图片损失、风格图片损失、和总变分损失。然后训练随机噪声图片,其包含风格图片的风格和原始图片的内容。

用 TensorFlow 实现 DeepDream

重用已训练好的 CNN 模型的另外一个应用是利用已标注特征(比如,猫耳朵或者鸟的羽毛)的中间层来迁移任意图片。本节将介绍 TensorFlow 的 Deep Dream 的示例,同样会详细讲解更多细节。

递归神经网络

考虑序列数据情况。

用 TensorFlow 实现 RNN 模型进行垃圾短信预测

应用标准的 RNN 单元预测奇异数值型输出。

对于序列数据,强烈推荐对训练集(全部数据)进行多次训练(对于非序列数据也推荐这样处理)。全部数据进行一次训练称为 epoch。同时强烈建议,在每个 epoch 之前进行随机 shuffle 数据。

用 TensorFlow 实现 LSTM 模型

本节通过引入 LSTM 单元,将 RNN 模型扩展为可以处理长序列的模型。

LSTM 递归神经网络是传统递归神经网络的变种。该时间递归神经网络可以解决变长 RNN 模型的梯度消失或者爆炸问题。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值