案例一:鸢尾花数据的分类

一:准备数据

1.1:读入数据集

from sklearn.datasets import load_iris
from sklearn import datasets
import pandas
import matplotlib.pyplot as plt
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target
print(x_data.shape)
print(y_data.shape)

1.2: 数据集乱序

print(y_data)

# seed()+shuffle
import random

seed=10
random.seed(seed)
random.shuffle(x_data)
random.seed(seed)# 一定得重复在写一遍,和上面的seed要相同,不然y_batch和x_batch打乱顺序会不一样
random.shuffle(y_data)

print(y_data)

1.3:生成训练集和测试集

from sklearn.model_selection import train_test_split
(X_train,X_test,y_train,y_test) = train_test_split(x_data, y_data, train_size=0.8, random_state=seed)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)

1.4:(特征 - 标签)配对,且每次只读入一部分(batch)进行训练

import tensorflow as tf
# import tensorflow.compat.v1 as tf
# tf.enable_eager_execution()

train_data = tf.data.Dataset.from_tensor_slices((X_train, y_train)).batch(30)
test_data = tf.data.Dataset.from_tensor_slices((X_test, y_test)).batch(30)

batch(30):以每组30个的形式,喂入模型时以bacth为单位。一般选2的幂次。

# DatasetV1Adapter类型
train_data
test_data

二:搭建网络

定义神经网络中所有可训练参数

w1 = tf.Variable(tf.random.truncated_normal([4,3], stddev=0.1, seed=1, dtype=tf.float64))
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1, dtype=tf.float64))

因为X的特征有4个,所以第一层有4个输入。
且最终分类结果只有3个,所以最后一层输入为3
又因为模型只有两层,所以第一层的输出 = 下一层的输入,为3。
所以:

  • w1维度为:(4, 3)
  • b1维度为:(3, )
lr = 0.1  # 学习率为0.1
train_loss_results = []  # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据
test_acc = []  # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据
epoch = 500  # 循环500轮
loss_all = 0  # 每轮分4个step,loss_all记录四个step生成的4个loss的和

三:参数优化

嵌套循环迭代,with结构更新参数,显示当前loss

for epoch in range(epoch):# 数据集级别迭代
    for step,(x_train,y_train) in enumerate(train_data):# batch级别迭代
        with tf.GradientTape() as tape: # 记录梯度信息
            # 正向传播
            y = tf.matmul(x_train, w1) + b1 # 神经网络乘、加运算
            y = tf.nn.softmax(y) # 使分类输出转换成概率的形式,(注意:此操作后,与独热码同量级,可相减求loss)
            y_one_hot = tf.one_hot(y_train, depth=3, dtype=tf.float64)# 将标签值转换成独热码格式,方便计算loss和acc
            
            # 计算损失loss
            loss = tf.reduce_mean(tf.square(y_one_hot - y))
#            # 这两句话的区别是 loss = tf.reduce_mean(tf.square(y_one_hot, y))
            loss_all += loss.numpy()

        # 计算loss对各个参数的梯度(导数)
        grads = tape.gradient(loss, [w1, b1])
        
        # 梯度自更新 w1 = w1 - lr*grads[0]; b1 = b1- lr*grads[1]
        w1.assign_sub(lr*grads[0])
        b1.assign_sub(lr*grads[1])  
    print("Epoch {}, loss: {}".format(epoch, loss_all/4))
    train_loss_results.append(loss_all / 4)
    loss_all = 0
    
    # 求模型的准确率
    total_correct, total_number = 0, 0
    for x_test, y_test in test_data:
        y = tf.matmul(x_test, w1)+b1 # 模型对三个分类的预测概率
        y = tf.nn.softmax(y) # 对y进行归一化
        pred = tf.argmax(y, axis=1) # 获取概率值最大的下标(也就是获取y的分类)
        pred = tf.cast(pred, dtype=y_test.dtype)
        # 若分类正确,则correct=1,否则为0
        correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
        total_correct += int(tf.reduce_sum(correct)) # 把一个批次的分类结果累加起来,保存到变量中
        total_number += x_test.shape[0]

    acc = total_correct / total_number
    test_acc.append(acc)
    print("Test acc: ", acc)
    print("-----------------------------------------------")

这里的 y 相当于是 y_predict,y_test 相当于y_true。通过softmax将 y 转换成符合概率分布的概率值(不是很理解为什么要加这一步,不加好像也可以)。

因为 y 的维度为[batch, 3],y_test 的维度为 [batch, 1]。采取的方法是将 y_test 转换成(三维)独热编码。

损失函数 loss 即为两者的方差。

acc/loss可视化

plt.title("Loss Function Curve")
plt.xlabel("Epoch")
plt.ylabel("Loss")
# 画出trian_loss_results曲线,且连线图标是Loss
plt.plot(train_loss_results, label="$Loss$")
# 画出曲线的图标(右上角)
plt.legend()
plt.show()

plt.title("Acc Curve")
plt.xlabel("Epoch")
plt.ylabel("Acc")
plt.plot(test_acc, label="$Accuracy$")# 连线图标是Accuracy
plt.legend()
plt.show()

posted @ 2020-11-05 21:14  linlk  阅读(593)  评论(0编辑  收藏  举报