TensorFlow 作为深度学习框架,当然是为了帮助我们更便捷地去构建神经网络。所以,本文将会了解如何使用 TensorFlow 来构建神经网络,并学会 TensorFlow 构建神经网络的重要函数和方法。
知识点
- NumPy 构建神经网络
- TensorFlow 构建神经网络
- TensorFlow 完成 DIGITS 分类
- TensorFlow 实现 Mini Batch 训练
NumPy 构建神经网络
前文中,我们已经了解了 TensorFlow 的工作机制,并学会使用 TensorFlow 参与运算。TensorFlow 作为最出色的深度学习开源框架,在我看来主要有 2 点优势。第一是基于计算图的数值计算过程,最终目的是提升计算速度。其次就是对各种常用神经网络层进行封装,提高构建模型的速度。
所以,本次实验我们将了解如何使用 TensorFlow 构建神经网络。
人工神经网络的实验中,我们使用 NumPy 构建简单的全连接神经网络。全连接指的是其每一个节点都与上一层每个节点相连。例如我们当时实现过的网络结构:
对于神经网络的实现,主要是前向传播和反向传播两个部分。前向传播当然是从输入 → 输出的计算,而反向传播则通过计算梯度来更新网络权重。这里,我们直接将前面感知机和人工神经网络实验中写过的代码拿过来使用。
import numpy as np
class NeuralNetwork:
def __init__(self, X, y, lr):
"""初始化参数"""
self.input_layer = X
self.W1 = np.ones((self.input_layer.shape[1], 3)) # 初始化权重全为 1
self.W2 = np.ones((3, 1))
self.y = y
self.lr = lr
def forward(self):
"""前向传播"""
self.hidden_layer = sigmoid(np.dot(self.input_layer, self.W1))
self.output_layer = sigmoid(np.dot(self.hidden_layer, self.W2))
return self.output_layer
def backward(self):
"""反向传播"""
d_W2 = np.dot(self.hidden_layer.T, (2 * (self.output_layer - self.y) *
sigmoid_derivative(np.dot(self.hidden_layer, self.W2))))
d_W1 = np.dot(self.input_layer.T, (
np.dot(2 * (self.output_layer - self.y) * sigmoid_derivative(
np.dot(self.hidden_layer, self.W2)), self.W2.T) * sigmoid_derivative(
np.dot(self.input_layer, self.W1))))
# 参数更新
self.W1 -= self.lr * d_W1
self.W2 -= self.lr * d_W2
下面,使用示例数据完成神经网络训练。
import pandas as pd
# 直接运行加载数据集
df = pd.read_csv(
"https://labfile.oss.aliyuncs.com/courses/1081/course-12-data.csv", header=0)
df.head() # 预览前 5 行数据
from matplotlib import pyplot as plt
%matplotlib inline
X = df[['X0', 'X1']].values # 输入值
y = df[['Y']].values # 真实 y
nn_model = NeuralNetwork(X, y, lr=0.001) # 定义模型
loss_list = [] # 存放损失数值变化
def sigmoid(x):
"""激活函数"""
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x):
"""sigmoid 函数求导"""
return sigmoid(x) * (1 - sigmoid(x))
# 迭代 200 次
for _ in range(200):
y_ = nn_model.forward() # 前向传播
nn_model.backward() # 反向传播
loss = np.square(np.subtract(y, y_)).mean() # 计算 MSE 损失
loss_list.append(loss)
plt.plot(loss_list) # 绘制 loss 曲线变化图
plt.title(f"final loss: {loss}")
可以看到,仅包含 1 个隐含层的网络,使用 NumPy 实现的代码量就已不算少。深度神经网络中,可能会有数百层及数千个神经元,使用 NumPy 实现的复杂度可想而知。
如果你仔细观察上面的代码,你就会发现随着神经网络的复杂程度增加,不仅代码量不断加大,最大的麻烦还在于求解梯度。所以,像 TensorFlow 等深度学习框架带来的最大便利在于,我们仅需要构建一个神经网络前向传播图,训练时就能够实现自动求导并更新参数。下面,我们就尝试使用 TensorFlow 的方式来重新构建上方包含 1 个隐含层的神经网络,并最终完成训练。
import tensorflow as tf
# 将数组转换为常量张量
X = tf.cast(tf.constant(df[['X0', 'X1']].values), tf.float32)
y = tf.constant(df[['Y']].values)
X.shape, y.shape
上面的代码中,tf.cast
主要用于转换张量,类型为 tf.float32
,这是为了和后面权重张量类型统一。通过输出可以看出,样本为 150 个,特征为 2 个,目标值 1 个。
定义模型类
接下来,我们就可以构建前向传播计算图了。这部分与 NumPy 构建前向传播过程非常相似,只是更换为使用 TensorFlow 构建。一般情况下,我们会将前向传播过程使用自定义模型类封装,并使用 TensorFlow 提供的 tf.Variable
随机初始化参数 W。
class Model(object):
def __init__(self):
# 初始化权重全为 1,也可以随机初始化
# 选择变量张量,因为权重后续会不断迭代更新
self.W1 = tf.Variable(tf.ones([2, 3]))
self.W2 = tf.Variable(tf.ones([3, 1]))
def __call__(self, x):
hidden_layer = tf.nn.sigmoid(tf.linalg.matmul(X, self.W1)) # 隐含层前向传播
y_ = tf.nn.sigmoid(tf.linalg.matmul(hidden_layer, self.W2)) # 输出层前向传播
return y_