实现单层神经网络

在前面,我们分别使用逻辑回归和 softmax 回归实现了对鸢尾花数据集的分类,逻辑回归能够实现线性二分类的任务,他其实就是最简单的神经网络——感知机。
在这里插入图片描述
而softmax回归则实现的是多分类任务,它也可以看做是输出层有多个神经元的单层神经网络。
在这里插入图片描述
下面,使用神经网络的思想来实现对鸢尾花数据集的分类,这个程序的实现过程和 softmax 回归几乎是完全一样的。

在使用神经网络来解决分类问题时,

首先,要设计神经网络的结构(也就是说确定神经网络有几层,每一层中有几个结点,结点之间又是如何连接的,使用什么激活函数,以及什么损失函数)。
在这里插入图片描述
这里,使用没有隐含层的单层前馈型神经网络来实现对鸢尾花的分类。
在这里插入图片描述
其次,编程来实现,

神经网络是一种数学模型,这些结点和结点之间的关系描述的是数学运算,因此实现神经网络实际就是通过多维数组实现这些数学运算。

在鸢尾花数据集的训练集中,一共有120个样本,如果我们一次输入所有样本,那么输入数据 X 就是一个形状为(120,4)的二维数组(为训练样本的属性值),输出层外是一个形状为(120,3)的二维数组(为对训练样本分类后的标签值)。
在这里插入图片描述
在前面的分类实现中,为了简化编程,我们将偏置项B看做是w0,将权值向量构造为m+1维的 W 矩阵,并且令 x0 为全一数组。将 X 向量构造为 m+1 列。
在这里插入图片描述
将这两个矩阵直接运算,
在这里插入图片描述
可以得到同样的结果。
在这里插入图片描述
在这个实验中,我们将 B 从 W 中分离出来,单独表示,这是考虑到后面实现多层神经网络时,编程更加方便直观。

下面,来介绍下实现神经网络的几个函数:

# 1、softmax
tf.nn.softmax(tf.matmul(X_train, W)+b)  # Y = XW+B

# 2、独热编码 one_hot
tf.one_hot(indices, depth)
# 参数 indices 要求是一个整数, 是一个输入项
# 参数 depth 是独热编码的深度

# 将鸢尾花数据集中的标签值转化为用独热编码表示
# 鸢尾花数据集中的标签值是一个浮点数,所以首先要转换为整数
tf.one_hot(tf.constant(y_test, dtype=tf.int32), 3)

# 3、交叉熵损失函数 tf.keras.losses.categorical_crossentropy
tf.keras.losses.categorical_crossentropy(y_true, y_pred)
# 第一个参数表示为独热编码的标签值
# 第二个参数是softmax函数的输出
# 返回值是一个一维张量
# 其中的每一个元素是每个样本的交叉熵损失值
# 因此, 还需要使用平均值函数得到平均交叉熵损失值
tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y_true=Y_train, y_pred=Y_PRED_train))
# 或使用求和函数得到总的交叉熵损失值
tf.reduce_sum(tf.keras.losses.categorical_crossentropy(y_true=Y_train, y_pred=Y_PRED_train))

完整的程序实现

目标:利用单层神经网络实现对鸢尾花数据集的分类

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

# 鸢尾花数据集中的属性值和标签值都是64位的浮点数
print(train_x.dtype)  # float64
print(train_y.dtype)  # float64

# 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.2
itar = 500

display_step = 100

# 第四步:设置模型参数初始值
np.random.seed(612)
# 这里的W是一个(4, 3) 的矩阵
W = tf.Variable(np.random.randn(4, 3), dtype=tf.float32)
# 这里的B是一个(3, ) 的一维张量, 初始化为0
B = 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:

        # softmax 函数
        # X - (120, 4), W - (4, 3) , 所以 Pred_train - (120, 3), 是每个样本的预测概率
        Pred_train = tf.nn.softmax(tf.matmul(X_train, W) + B)
        # 计算训练集的平均交叉熵损失函数
        Loss_train = tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y_true=Y_train, y_pred=Pred_train))

    Pred_test = tf.nn.softmax(tf.matmul(X_test, W) + B)
    # 计算测试集的平均交叉熵损失函数
    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)

    # 对交叉熵损失函数 WB 求偏导
    grads = tape.gradient(Loss_train, [W, B])
    # 函数assign_sub的作用是实现 Variable 变量的减法赋值
    # 更新模型参数 W
    W.assign_sub(learn_rate * grads[0])  # grads[0] 是 dL_dw, 形状为(4,3)
    # 更新模型偏置项参数 B
    B.assign_sub(learn_rate * grads[1])  # grads[1] 是 dL_db, 形状为(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.066978, TrainAccuracy: 0.333333, TestLoss: 1.880855, TestAccuracy: 0.266667
i: 100, TrainLoss: 0.223813, TrainAccuracy: 0.933333, TestLoss: 0.280151, TestAccuracy: 0.933333
i: 200, TrainLoss: 0.171492, TrainAccuracy: 0.950000, TestLoss: 0.200843, TestAccuracy: 0.966667
i: 300, TrainLoss: 0.144387, TrainAccuracy: 0.958333, TestLoss: 0.161774, TestAccuracy: 0.966667
i: 400, TrainLoss: 0.127350, TrainAccuracy: 0.966667, TestLoss: 0.137980, TestAccuracy: 0.966667
i: 500, TrainLoss: 0.115541, TrainAccuracy: 0.966667, TestLoss: 0.121931, TestAccuracy: 0.966667
"""
# 第六步:数据可视化
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()

运行结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuechanba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值