一:准备数据
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()