2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业

实验一 TensorFlow 2.0 新增变化特性以及实验

介绍

TensorFlow 2.0 版本现已推出,相对于 1.x 版本在很多地方都有较大的变动。本次实验中,我们将了解并学习 TensorFlow 2.0 的一些新的特征,同时熟悉一些常用的新 API 方法。

知识点

安装 TensorFlow 2.0
Eager Execution
TensorFlow Keras

#### 学习此课程的前提是已了解并使用过 TensorFlow 1.x。

2019 年初,TensorFlow 官方推出了 2.0 预览版本,也意味着 TensorFlow 即将从 1.x 过度到 2.x 时代。根据 TensorFlow 官方介绍内容 显示,2.0 版本将专注于简洁性和易用性的改善,主要升级方向包括:

使用 Keras 和 Eager Execution 轻松构建模型。
在任意平台上实现稳健的生产环境模型部署。
为研究提供强大的实验工具。
通过清理废弃的 API 和减少重复来简化 API。

TensorFlow 2.0 安装

本人之前已经在自己电脑上安装了tensorflow-2.0.0版本,输入以下代码查看版本号。

import tensorflow as tf
tf._version

Eager Execution

TensorFlow 2.0 带来的最大改变之一是将 1.x 的 Graph Execution(图与会话机制)更改为 Eager Execution(动态图机制)。在 1.x 版本中,低级别 TensorFlow API 首先需要定义数据流图,然后再创建 TensorFlow 会话,这一点在 2.0 中被完全舍弃。

TensorFlow 2.0 中的 Eager Execution 是一种命令式编程环境,可立即评估操作,无需构建图:操作会返回具体的值,而不是构建以后再运行的计算图。实际上,Eager Execution 在 1.x 的后期版本中也存在,但需要单独执行tf.enable_eager_execution() 进行手动启用。不过,2.0 版本的 TensorFlow 默认采用了 Eager Execution,无法关闭并回到 1.x 的 Graph Execution 模式中。

在之前1.x 版本中,如果我们新建一个张量 tf.Variable([[1, 2], [3, 4]]) 并执行输出,那么只能看到这个张量的形状和属性,并不能直接输出其数值。
如今,Eager Execution 模式下则可以直接输出张量的数值了,并以 NumPy 数组方式呈现。

Eager Execution 带来的好处是不再需要手动管理图和会话。例如,现在使用示例张量进行数学计算,可以像 Python 一样直接相加

相对于下面1.x的代码,是一个质的飞跃

init_op = tf.global_variables_initializer()  # 初始化全局变量
with tf.Session() as sess:  # 启动会话
sess.run(init_op)
print(sess.run(c + c))  # 执行计算

TensorFlow 的默认执行模式为 Eager Execution,这就意味着之前基于 Graph Execution 构建的代码将完全无法使用,因为 2.0 中已经没有了相应的 API。例如,先前构建神经网络计算图时,都习惯于使用 tf.placeholder 占位符张量,等最终执行时再传入数据。Eager Execution 模式下,tf.placeholder 已无存在必要,所以此 API 已被移除。
所以,随着 TensorFlow 2.0 默认引入 Eager Execution 机制,也就意味着原 1.x 低阶 API 构建图的方法后续已无法使用。怪不得我之前的代码都报错,运行在1.x上没问题,运行到2.0.0上就各种错误,改变确实太大了,图与会话机制都有了改变

实验总结

本次实验主要对 TensorFlow 2.0 新特征做了介绍和说明,并使用实例对比了 1.x 和 2.0 的不同之处。TensorFlow 2.0 的变化实际上是非常之大的,你会发现一些原先熟悉的 API 完全消失了,先前构建的代码也无法正常执行。这就需要花时间熟悉新的 API,并掌握新的模型构建流程。

TensorFlow Keras

TensorFlow 1.x 中,我们可以通过 tf.layers 高阶层封装开快速搭建神经网络。如果,2.0 已完全移除了 tf.layers 模块,转而引入了 tf.keras

实验二 TensorFlow 2.0 实现线性回归

介绍

以线性回归为例,使用 TensorFlow 2.0 提供的 API 来进行实现。与此同时,我们会使用 1.x API 与之对比。

知识点

  • 低阶 API 实现
  • 高阶 API 实现
  • TensorFlow 1.x 实现

低阶 API 实现

低阶 API 实现,实际上就是利用 Eager Execution 机制来完成。实验首先初始化一组随机数据样本,并添加噪声,然后将其可视化出来。

import matplotlib.pyplot as plt
import tensorflow as tf
%matplotlib inline
TRUE_W = 3.0
TRUE_b = 2.0
NUM_SAMPLES = 100

#初始化随机数据
X = tf.random.normal(shape=[NUM_SAMPLES, 1]).numpy()
noise = tf.random.normal(shape=[NUM_SAMPLES, 1]).numpy()
y = X * TRUE_W + TRUE_b + noise  # 添加噪声
plt.scatter(X, y)

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第1张图片

利用线性函数去拟合,首先初始化w和b。

 class Model(object):
 def __init__(self):
    self.W = tf.Variable(tf.random.uniform([1]))  # 随机初始化参数
    self.b = tf.Variable(tf.random.uniform([1]))
def __call__(self, x):
    return self.W * x + self.b  # w*x + b

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第2张图片

平方差损失函数的使用

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第3张图片

def loss_fn(model, x, y):
y_ = model(x)
return tf.reduce_mean(tf.square(y_ - y))

基础的梯度下降法

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第4张图片

代码如下

EPOCHS = 10
LEARNING_RATE = 0.1

for epoch in range(EPOCHS):  # 迭代次数
with tf.GradientTape() as tape:  # 追踪梯度
    loss = loss_fn(model, X, y)  # 计算损失
dW, db = tape.gradient(loss, [model.W, model.b])  # 计算w、b的梯度
model.W.assign_sub(LEARNING_RATE * dW)  # 更新梯度
model.b.assign_sub(LEARNING_RATE * db)
# 输出计算过程
print('Epoch [{}/{}], loss [{:.3f}], W/b [{:.3f}/{:.3f}]'.format(epoch, EPOCHS, loss,
                                                                 float(model.W.numpy()),
                                                                 float(model.b.numpy())))
                                                                 

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第5张图片

重新拟合

plt.scatter(X, y)
plt.plot(X, model(X), c='r')

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第6张图片

高阶 API 实现

tf.keras 模块下提供的 tf.keras.layers.Dense 全连接层(线性层)实际上就是一个线性计算过程。所以,模型的定义部分我们就可以直接实例化一个全连接层即可。

model = tf.keras.layers.Dense(units=1)  # 实例化线性层
model
#units 为输出空间维度。此时,参数已经被初始化了,所以我们可以绘制出拟合直线。
plt.scatter(X, y)
plt.plot(X, model(X), c='r')
model.variables#打印随机参数

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第7张图片

构建模型迭代过程

EPOCHS = 10
LEARNING_RATE = 0.002
for epoch in range(EPOCHS):  # 迭代次数
    with tf.GradientTape() as tape:  # 追踪梯度
        y_ = model(X)
        loss = tf.reduce_sum(tf.keras.losses.mean_squared_error(y, y_))  # 计算损失
grads = tape.gradient(loss, model.variables)  # 计算梯度
optimizer = tf.keras.optimizers.SGD(LEARNING_RATE)  # 随机梯度下降
optimizer.apply_gradients(zip(grads, model.variables))  # 更新梯度
print('Epoch [{}/{}], loss [{:.3f}]'.format(epoch, EPOCHS, loss))

重新拟合直线

plt.scatter(X, y)
plt.plot(X, model(X), c='r')

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第8张图片

Keras 方式实现

我们这里使用 Keras 提供的 Sequential 序贯模型结构。和上面的例子相似,向其中添加一个线性层。不同的地方在于,Keras 序贯模型第一层为线性层时,规定需指定输入维度,这里为 input_dim=1。

model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(units=1, input_dim=1))
model.summary()  # 查看模型结构
model.compile(optimizer='sgd', loss='mse')
model.fit(X, y, steps_per_epoch=100)
plt.scatter(X, y)
plt.plot(X, model(X), c='r')

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第9张图片

在tensorflow2.0版本下运行1.x版本

我们需要实验 TensorFlow 2.0 中 tensorflow.compat.v1 模块下提供的兼容性代码。

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior() # 关闭 Eager Execution 特性

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第10张图片
2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第11张图片

实验总结

本次实验中,我们利用 TensorFlow 2.0 提供的 Eager Execution 实现了线性回归的经典过程。同时,利用 TensorFlow Keras 高阶 API 简化了实现步骤。实验的最后,使用 TensorFlow 1.x API 进行了对比示例,准确把握 TensorFlow 2.0 和 1.0 之间的区别。

实验三 TensorFlow 2.0 构建神经网络

介绍

前面,我们以线性回归作为例子演示了 TensorFlow 2.0 的新变化和新特性。实际上,TensorFlow 等深度学习框架更重要的作用是用于构建人工神经网络。所以,本次实验将利用 TensorFlow 2.0 来构建一个简单的前馈神经网络。

知识点

低阶 API 构建
Keras 高阶 API 实现

低阶 API 构建--以手写数字识别为例

import numpy as np
import tensorflow as tf
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split

digits = load_digits()
digits_y = np.eye(10)[digits.target.reshape(-1)]  # 标签独热编码
X_train, X_test, y_train, y_test = train_test_split(digits.data, digits_y,
                                                test_size=0.2, random_state=1)

X_train.shape, X_test.shape, y_train.shape, y_test.shape
class Model(object):
def __init__(self):
    self.W1 = tf.Variable(tf.random.normal([64, 30]))  # 随机初始化张量参数
    self.b1 = tf.Variable(tf.random.normal([30]))

    self.W2 = tf.Variable(tf.random.normal([30, 10]))
    self.b2 = tf.Variable(tf.random.normal([10]))

def __call__(self, x):
    x = tf.cast(x, tf.float32)  # 转换输入数据类型
    # 线性计算 + RELU 激活
    fc1 = tf.nn.relu(tf.add(tf.matmul(x, self.W1), self.b1))
    fc2 = tf.add(tf.matmul(fc1, self.W2), self.b2)
    return fc2
    def loss_fn(model, x, y):
preds = model(x)
return tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=preds, labels=y))
def accuracy_fn(logits, labels):
preds = tf.argmax(logits, axis=1)  # 取值最大的索引,正好对应字符标签
labels = tf.argmax(labels, axis=1)
return tf.reduce_mean(tf.cast(tf.equal(preds, labels), tf.float32))
EPOCHS = 100  # 迭代此时
LEARNING_RATE = 0.02  # 学习率
model = Model()  # 模型
for epoch in range(EPOCHS):
with tf.GradientTape() as tape:  # 追踪梯度
    loss = loss_fn(model, X_train, y_train)
trainable_variables = [model.W1, model.b1, model.W2, model.b2]  # 需优化参数列表
grads = tape.gradient(loss, trainable_variables)  # 计算梯度

optimizer = tf.optimizers.Adam(learning_rate=LEARNING_RATE)  # 优化器
optimizer.apply_gradients(zip(grads, trainable_variables))  # 更新梯度
accuracy = accuracy_fn(model(X_test), y_test)  # 计算准确度

# 输出各项指标
if (epoch + 1) % 10 == 0:
    print('Epoch [{}/{}], Train loss: {:.3f}, Test accuracy: {:.3f}'
          .format(epoch+1, EPOCHS, loss, accuracy))

Keras 高阶 API 实现

函数式模型

inputs = tf.keras.Input(shape=(64,))
x = tf.keras.layers.Dense(30, activation='relu')(inputs)
outputs = tf.keras.layers.Dense(10, activation='softmax')(x)

#指定输入和输出
model = tf.keras.Model(inputs=inputs, outputs=outputs)
model.summary()  # 查看模型结构
# 编译模型
model.compile(optimizer=tf.optimizers.Adam(),
          loss=tf.losses.categorical_crossentropy, metrics=['accuracy'])
#训练和评估
model.fit(X_train, y_train, batch_size=64, epochs=10,
      validation_data=(X_test, y_test))
      

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第12张图片

序贯模型

model = tf.keras.Sequential()  # 建立序贯模型
model.add(tf.keras.layers.Dense(units=30, input_dim=64, activation='relu'))  # 隐含层 
model.add(tf.keras.layers.Dense(units=10, activation='softmax'))  # 输出层
model.summary()  # 查看模型结构
model.compile(optimizer='adam', loss='categorical_crossentropy',
          metrics=['accuracy'])
model.fit(X_train, y_train, batch_size=64, epochs=10,
      validation_data=(X_test, y_test))
      

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第13张图片

tf.keras.Model 模型

class Model(tf.keras.Model):
 def __init__(self):
      super(Model, self).__init__()
     self.dense_1 = tf.keras.layers.Dense(30, activation='relu')  # 初始化
     self.dense_2 = tf.keras.layers.Dense(10, activation='softmax')

 def call(self, inputs):
     x = self.dense_1(inputs)  # 前向传播过程
     return self.dense_2(x)
     model = Model()  # 实例化模型
model.compile(optimizer='adam', loss='categorical_crossentropy',
          metrics=['accuracy'])
model.fit(X_train, y_train, batch_size=64, epochs=10,
      validation_data=(X_test, y_test))

2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业_第14张图片

实验总结

本次实验中,我们实验中 TensorFlow 2.0 构建了简单的前馈神经网络,并完全手写字符识别分类。特别地,一定要掌握 Eager Execution 的实现过程,而对于 TensorFlow Keras 的使用也应用有充分的理解。

你可能感兴趣的:(2019-2020-1 20199314 《Linux内核原理与分析》第十二周作业)