在前面,我们使用了单层神经网络实现了对鸢尾花数据集的分类,
下面,将使用多层神经网络来实现对鸢尾花数据集的分类。
第一层是输入层到隐含层,相应的权值矩阵为 W1 ,
隐含层中的阈值是 B1 ,隐含层的输出是:
假设增加的隐含层中有 16 个神经元(结点),那么 B1 的形状就是 (16,),因为输入层中有四个结点,因此, W1 的形状是 (4,16),
第二层是隐含层到输出层,
从隐含层到输出层的权值矩阵为 W2 ,输出层中的阈值是 B2 ,因为输出层中有三个节点,所以B2 的形状是(3,),W2 的形状是(16,3)。
这个增加的隐含层,使用上节课中介绍的 Relu 函数作为激活函数,
因为是多分类问题,因此,输出层的激活函数仍然使用 Softmax 函数,损失函数仍然使用交叉熵损失函数。
下面,我们来看下代码实现。
import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False
# 目标:使用花萼长度、花萼宽度、花瓣长度、花瓣宽度四种属性将三种鸢尾花区分开
# 第一步:加载数据集
TRAIN_URL = "http://download.tensorflow.org/data/iris_training.csv"
train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL)
df_iris_train = pd.read_csv(train_path, header=0) # 表示第一行数据作为列标题
TEST_URL = "http://download.tensorflow.org/data/iris_test.csv"
test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL)
df_iris_test = pd.read_csv(test_path, header=0)
# 第二步:数据处理
# 2.1 转化为NumPy数组
iris_train = np.array(df_iris_train) # 将二维数据表转换为 Numpy 数组, (120, 5), iris的训练集中有120条样本,
iris_test = np.array(df_iris_test) # 将二维数据表转换为 Numpy 数组, (30, 5), iris的测试集中有30条样本,
# 2.2 提取属性和标签
train_x = iris_train[:, 0:4] # 取出鸢尾花训练数据集中属性列
train_y = iris_train[:, 4] # 取出最后一列作为标签值, (120,)
test_x = iris_test[:, 0:4] # 取出鸢尾花训练数据集中属性列
test_y = iris_test[:, 4] # 取出最后一列作为标签值, (30, )
# 2.3 数据归一化
# 可以看出这两个属性的尺寸相同,因此不需要进行归一化,可以直接对其进行中心化处理
# 对每个属性进行中心化, 也就是按列中心化, 所以使用下面这种方式
train_x = train_x - np.mean(train_x, axis=0)
test_x = test_x - np.mean(test_x, axis=0)
# 此时样本点的横坐标和纵坐标的均值都是0
# 2.4 生成多元模型的属性矩阵和标签列向量
X_train = tf.cast(train_x, tf.float32)
# 创建张量函数tf.constant()
Y_train = tf.one_hot(tf.constant(train_y, dtype=tf.int32), 3) # 将标签值转换为独热编码的形式
print(X_train.shape) # (120, 4)
print(Y_train.shape) # (120, 3)
X_test = tf.cast(test_x, tf.float32)
# 创建张量函数tf.constant()
Y_test = tf.one_hot(tf.constant(test_y, dtype=tf.int32), 3) # 将标签值转换为独热编码的形式
print(X_test.shape) # (30, 4)
print(Y_test.shape) # (30, 3)
# 第三步:设置超参数和显示间隔
learn_rate = 0.5
itar = 50
display_step = 10
# 第四步:设置模型参数初始值
np.random.seed(612)
# 隐含层
# 这里的 W1 是一个(4, 16) 的矩阵
W1 = tf.Variable(np.random.randn(4, 16), dtype=tf.float32)
# 这里的 B1 是一个(16, ) 的一维张量, 初始化为 0
B1 = tf.Variable(np.zeros([16]), dtype=tf.float32)
# 输出层
# 这里的 W2 是一个(16, 3) 的矩阵
W2 = tf.Variable(np.random.randn(16, 3), dtype=tf.float32)
# 这里的 B2 是一个(3, ) 的一维张量, 初始化为 0
B2 = tf.Variable(np.zeros([3]), dtype=tf.float32)
# 第五步:训练模型
cross_train = [] # 列表cross_train用来保存每一次迭代的交叉熵损失
acc_train = [] # 用来存放训练集的分类准确率
cross_test = [] # 列表cross_test用来保存每一次迭代的交叉熵损失
acc_test = [] # 用来存放测试集的分类准确率
for i in range(0, itar + 1):
with tf.GradientTape() as tape:
# 5.1:定义网络结构
# H = X*W1 + B1
Hidden_train = tf.nn.relu(tf.matmul(X_train, W1) + B1)
# Y = H*W2 + B2
Pred_train = tf.nn.softmax(tf.matmul(Hidden_train, W2) + B2)
# 计算训练集的平均交叉熵损失函数
Loss_train = tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y_true=Y_train, y_pred=Pred_train))
# H = X*W1 + B1
Hidden_test = tf.nn.relu(tf.matmul(X_test, W1) + B1)
# Y = H*W2 + B2
Pred_test = tf.nn.softmax(tf.matmul(Hidden_test, W2) + B2)
# 计算测试集的平均交叉熵损失函数
Loss_test = tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y_true=Y_test, y_pred=Pred_test))
# 计算准确率函数 -- 因为不需要对其进行求导运算, 因此也可以把这条语句写在 with 语句的外面
Accuarcy_train = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(Pred_train.numpy(), axis=1), train_y), tf.float32))
Accuarcy_test = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(Pred_test.numpy(), axis=1), test_y), tf.float32))
# 记录每一次迭代的交叉熵损失和准确率
cross_train.append(Loss_train)
cross_test.append(Loss_test)
acc_train.append(Accuarcy_train)
acc_test.append(Accuarcy_test)
# 对交叉熵损失函数 W 和 B 求偏导
grads = tape.gradient(Loss_train, [W1, B1, W2, B2])
# 函数assign_sub的作用是实现 Variable 变量的减法赋值
# 更新模型参数 W1
W1.assign_sub(learn_rate * grads[0]) # grads[0] 是 dL_dw1, 形状为(4, 16)
# 更新模型偏置项参数 B1
B1.assign_sub(learn_rate * grads[1]) # grads[1] 是 dL_db1, 形状为(16, )
# 更新模型参数 W2
W2.assign_sub(learn_rate * grads[2]) # grads[0] 是 dL_dw2, 形状为(16, 3)
# 更新模型偏置项参数 B2
B2.assign_sub(learn_rate * grads[3]) # grads[1] 是 dL_db2, 形状为(3, )
if i % display_step == 0:
print("i: %i, TrainLoss: %f, TrainAccuracy: %f, TestLoss: %f, TestAccuracy: %f"
% (i, Loss_train, Accuarcy_train, Loss_test, Accuarcy_test))
"""
i: 0, TrainLoss: 2.205641, TrainAccuracy: 0.433333, TestLoss: 1.721138, TestAccuracy: 0.400000
i: 10, TrainLoss: 0.205314, TrainAccuracy: 0.941667, TestLoss: 0.249661, TestAccuracy: 0.966667
i: 20, TrainLoss: 0.149540, TrainAccuracy: 0.950000, TestLoss: 0.167103, TestAccuracy: 1.000000
i: 30, TrainLoss: 0.122346, TrainAccuracy: 0.958333, TestLoss: 0.124693, TestAccuracy: 1.000000
i: 40, TrainLoss: 0.105099, TrainAccuracy: 0.958333, TestLoss: 0.099869, TestAccuracy: 1.000000
i: 50, TrainLoss: 0.092934, TrainAccuracy: 0.958333, TestLoss: 0.084885, TestAccuracy: 1.000000
"""
# 第六步:数据可视化
plt.figure(figsize=(12, 5))
plt.subplot(121)
plt.plot(acc_train, color="blue", label="train")
plt.plot(acc_test, color="red", label="test")
plt.title("迭代次数和损失值曲线图", fontsize=22)
plt.xlabel('迭代次数', color='r', fontsize=16)
plt.ylabel('损失值', color='r', fontsize=16)
plt.legend()
plt.subplot(122)
plt.plot(cross_train, color="blue", label="train")
plt.plot(cross_test, color="red", label="test")
plt.title("迭代次数和准确率曲线图", fontsize=22)
plt.xlabel('迭代次数', color='r', fontsize=16)
plt.ylabel('准确率', color='r', fontsize=16)
plt.legend()
plt.show()
运行效果如下,
对比单层神经网络和多层神经网络实现的对鸢尾花数据集的分类的效果。