tensorflow2.0实现鸢尾花数据集的分类(2)——神经网络搭建

接我的上一篇博客:
tensorflow2.0实现鸢尾花数据集的分类(1)——鸢尾花数据的读入(详解)


1.我们先说说,机器学习问题的一般流程:

1.数据准备阶段

  • 数据集读入
  • 数据集清洗(本次没有涉及)
  • 数据标准化、归一化等(本次没有涉及,之后的博客会写)
  • 数据集分离,生成训练集和测试集(再此之前,最好将数据集顺序打乱)
  • (在tf中还要配成[输入特征,标签]对,每次读入一个batch)

2.模型搭建

  • 导入模型,定义模型参数(如KNN算法中的K值)
  • 在本次问题则是网络搭建(可以理解为前向传播),需要定义神经网络中所有可训练参数

3.参数优化

  • 嵌套迭代循环,更新参数,不断使损失函数(loss)最小,(可以理解为反向传播)

4.结果测试

  • 计算当前参数前向传播后的准确率

5.结果展示,如打印准确率,loss可视化等

细节还有好多好多,在我遇到的大部分问题中,最耗时的不是模型,也不是调参,而是数据处理


2.几句废言

终于要用tensorflow 2.0了,自从tf API 大更新,笔者就出门右拐,上手pytorch了,pytorch香是挺香,就是感觉缺了点儿什么,好像没那么灵活了,这不,又回来了…


3.代码:数据准备阶段

我们在上一篇中已经详细介绍了数据集:
tensorflow2.0实现鸢尾花数据集的分类(1)——鸢尾花数据的读入(详解)

# 数据集读入
from sklearn.datasets import load_iris 
x_data = load_iris().data    # 返回 iris 数据集所有输入特征 
y_data = load_iris().target   # 返回 iris 数据集所有标签 

# 数据集分离
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x_data, 
													y_data, 
													test_size=0.2, 
													random_state=2718)
# 注: 一定要注意函数train_test_split的返回顺序

# 将标签转化为独热码
y_train = tf.one_hot(y_train, 3, dtype=tf.float64) 
y_test = tf.one_hot(y_test, 3, dtype=tf.float64) 
# 原来的标签值为 0,1,2,有大小之分,而逻辑上鸢尾花的类型没有大小之分,所以应转化
# 转化为独热码之后,将类别分到三个维度上,没有大小之分
# 如果还不太理解独热码,可以继续往下看例子
# 配成[输入特征,标签]对,每次输入一个batch,一个batch为16
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(16)
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(16)

注: tf.data.Dataset.from_tensor_slices参数,必须为元组——(x_train, y_train),否则报错:
ValueError: Can't convert non-rectangular Python sequence to Tensor.

你可能还不太熟悉tf.data.Dataset.from_tensor_slices函数,我们来感性理解一下:

>>> list(test_db.as_numpy_iterator())
[(array([[6.9, 3.2, 5.7, 2.3],
         [5.8, 4. , 1.2, 0.2],
         [6.8, 3.2, 5.9, 2.3],
         [5.9, 3. , 4.2, 1.5],
         [4.4, 2.9, 1.4, 0.2],
         [5.8, 2.8, 5.1, 2.4],
         [5.5, 4.2, 1.4, 0.2],
         [5.7, 3. , 4.2, 1.2],
         [5.1, 3.5, 1.4, 0.3],
         [7.7, 2.8, 6.7, 2. ],
         [5.1, 3.3, 1.7, 0.5],
         [4.4, 3. , 1.3, 0.2],
         [6.1, 2.8, 4. , 1.3],
         [5. , 3.2, 1.2, 0.2],
         [5.5, 2.3, 4. , 1.3],
         [6.8, 2.8, 4.8, 1.4]]),
  array([[0., 0., 1.],
         [1., 0., 0.],
         [0., 0., 1.],
         [0., 1., 0.],
         [1., 0., 0.],
         [0., 0., 1.],
         [1., 0., 0.],
         [0., 1., 0.],
         [1., 0., 0.],
         [0., 0., 1.],
         [1., 0., 0.],
         [1., 0., 0.],
         [0., 1., 0.],
         [1., 0., 0.],
         [0., 1., 0.],
         [0., 1., 0.]])),
 (array([[4.9, 2.5, 4.5, 1.7],
         [7.6, 3. , 6.6, 2.1],
         [5.5, 2.5, 4. , 1.3],
         [6.5, 3.2, 5.1, 2. ],
         [7.9, 3.8, 6.4, 2. ],
         [6. , 2.9, 4.5, 1.5],
         [6.1, 2.8, 4.7, 1.2],
         [7. , 3.2, 4.7, 1.4],
         [5.7, 3.8, 1.7, 0.3],
         [5.5, 2.4, 3.7, 1. ],
         [6. , 2.7, 5.1, 1.6],
         [4.7, 3.2, 1.3, 0.2],
         [5.7, 2.9, 4.2, 1.3],
         [5. , 3.3, 1.4, 0.2]]),
  array([[0., 0., 1.],
         [0., 0., 1.],
         [0., 1., 0.],
         [0., 0., 1.],
         [0., 0., 1.],
         [0., 1., 0.],
         [0., 1., 0.],
         [0., 1., 0.],
         [1., 0., 0.],
         [0., 1., 0.],
         [0., 1., 0.],
         [1., 0., 0.],
         [0., 1., 0.],
         [1., 0., 0.]]))]

看到了吗,就是将数据分了各组而已,小声bb:其实并没有那么简单,tf对数据的存取做了优化,读取速度会更快,这样组织数据,当数据量很大的时候,十分有效!

单说说,.as_numpy_iterator方法
该方法将所有元素转换为numpy数据,并将该数据以迭代器形式返回
(Returns an iterator which converts all elements of the dataset to numpy.)


4.代码:神经网络模型参数

没错,咱们的神经网络结构就这么简单,你可别小瞧了它哈哈
# 定义神经网络中所有可训练的参数
w = tf.Variable(tf.random.truncated_normal([4, 3], 
                                            stddev=0.1))
b = tf.Variable(tf.random.truncated_normal([3], 
                                            stddev=0.1))
  • tf.random.truncated_normal会生成截断正态分布数据,只生成 ( μ 2 σ , μ + 2 σ ) (\mu -2\sigma, \mu +2\sigma ) 这个区间的正态分布数据,若不在这个区间,则重新生成。
  • tf.Variable函数,会返回一个变量,与之对应的是tf.constant,会返回常量

5.代码:前向传播

我们初始化参数之后,就要定义前向传播过程了,说的好像挺高大上,实际上,就是用参数去估计y

# 计算预测值
y = tf.matmul(x_train, w) + b
# 计算损失函数
loss = (y - y_train)**2
loss = tf.reduce_mean(loss, axis=0)

我们这里使用的是均方误差损失函数
这里简单说一下tf.reduce_mean函数,该函数与np.mean函数用法相同,参数axis=0指定在第一维度也就是纵向求均值

6.代码:反向传播更新参数

模型的重中之重便是反向传播更新参数,我们要对loss函数求导:

with tf.GradientTape() as tape:
	#前向传播计算 y, y为预测结果
	y = tf.matmul(x_train, w) + b
	# 计算损失函数
	loss = (y - y_train)**2
	loss = tf.reduce_mean(loss, axis=0)
            
# 求参数导数
grads= tape.gradient(loss, [w, b])
# 更行模型参数,lr是学习率
w.assign_sub(lr*grads[0])
b.assign_sub(lr*grads[1])

看到这里,有些老铁可能默默的想到了tf1.Session(),差不多吧,都是上下文管理器,什么是上下文管理器这里先不深究,我们可以这样理解:

  • 函数tf.GradientTape()提供的上下文,会记录yloss的计算过程,而之后可以通过这个计算过程来求导
  • tape.gradient(loss, [w, b])便是用来求导的函数

求完导数,我们通过通过公式:
w t + 1 = w t l r l o s s w t b t + 1 = b t l r l o s s b \begin{aligned} w_{t+1} &= w_{t} - lr * \frac{\partial loss}{\partial w_t}\\ b_{t+1} &= b_{t} - lr * \frac{\partial loss}{\partial b} \end{aligned}
来更新参数:

w.assign_sub(lr*grads[0])  # 完成自减操作
b.assign_sub(lr*grads[1])

7.代码:计算准确率

def get_accuary(x_test,y_label, w, b):
    """
    计算准确率的函数
    x_test    : 测试数据集
    y_label   : 标签(以独热码的形式传入)
    w         : 参数w
    b         : 参数b
    """
    # 预测标签
    y_pred = tf.matmul(x_test, w) + b;
    # 将预测值转化为概率分布(在此处此步可以省略)
    y_pred = tf.nn.softmax(y_pred, axis=1)
    # # 求预测值中的最大值
    y_pred = tf.argmax(y_pred, axis=1)
    # 求标签中的最大值
    y_label = tf.argmax(y_test, axis=1)
    # 计算准确率
    acc = tf.reduce_mean(tf.cast(y_pred == y_label, tf.float64))
    # 返回准准确率
    return acc.numpy()

8.代码:以上代码集合,即训练模型代码

from sklearn.datasets import load_iris 
x_data = load_iris().data     # 返回 iris 数据集所有输入特征 
y_data = load_iris().target   # 返回 iris 数据集所有标签 
from sklearn.model_selection import train_test_split
# 数据集分离
x_train, x_test, y_train, y_test = train_test_split(x_data, 
													y_data, 
													test_size=0.2, 
													random_state=2718)

import tensorflow as tf
y_train = tf.one_hot(y_train, 3, dtype=tf.float64) # 转化为独热码
y_test = tf.one_hot(y_test, 3, dtype=tf.float64) 

# 配成[输入特征,标签]对
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(16)
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(16)

# 初始化模型参数
w = tf.Variable(tf.random.truncated_normal([4, 3], 
                                            stddev=0.1,
                                            dtype=tf.float64))
b = tf.Variable(tf.random.truncated_normal([3], 
                                            stddev=0.1,
                                            dtype=tf.float64))
# 设置学习率
lr = 0.01
# 设置训练轮数
epoch = 1000
for i in range(epoch): # 训练轮数层面迭代开始
    for step, (x_batch, y_batch) in enumerate(train_db): # batch 层面迭代开始
        with tf.GradientTape() as tape: # 进入计算梯度的上下文
            # 前向传播计算 y, y为预测结果
            y = tf.matmul(x_batch, w) + b
            # 计算损失函数
            loss = (y - y_batch)**2
            loss = tf.reduce_mean(loss, axis=0)
        
        # 求导
        grads = tape.gradient(loss, [w, b])
        # 更新参数
        w.assign_sub(lr*grads[0])
        b.assign_sub(lr*grads[1])
    # 打印训练结果
    print("Epoch {}, loss: {}".format(i+1, loss)) 
    # 计算训练集上的准确率
    acc = get_accuary(x_train, y_train, w, b)
    # acc = get_accuary(x_test, y_test, w, b)
    print("Accuracy {}".format(acc))

喏,咱们的模型训练完了,你可能会说:
在这里插入图片描述
你可能要问,模型呢??咱们的模型是啥??
为了再让你发这个表情包,我会告诉你:
简单的说,咱们的模型就是wb(误

8.代码:测试模型性能

# 很简单,只需将测试集传入,打印准确率即可
acc = get_accuary(x_test, y_test, w, b)
print("Accuracy {}".format(acc))

老铁们,我留了一个坑,你会发现,这神经网络运行完的准确率,咋和线性回归差不多???,无论你怎么改参数,他的最优结果一定和线性回归的结果差不多,甚至不如线性回归的结果!那你又会说,莫非是神经网络的层数太少,表达力不够强?,原因不在那儿

在之前讲解线性回归中,三种梯度下降MGD、BGD与MBGD中已经用到过鸢尾花数据集,没上车的同学可以瞅瞅:
线性回归中,三种梯度下降MGD、BGD与MBGD对比研究(一)——公式推导
线性回归中,三种梯度下降MGD、BGD与MBGD对比研究(二)——Python轮子实现
线性回归中,三种梯度下降MGD、BGD与MBGD对比研究(三)——以鸢尾花数据集为例

嗯?????为啥????这明明就是神经网络啊。咋回事?

tensorflow2.0实现鸢尾花数据集的分类(3)——激活函数
原创文章 66 获赞 14 访问量 9099

猜你喜欢

转载自blog.csdn.net/HaoZiHuang/article/details/105053181