一、网络结构
一个输入层,两个隐藏层,一个输出层。输入层有64个神经元,第一个隐藏层有256个神经元,第二个隐藏层有64个神经元,最终的输出层有10个神经元。
二、处理数据集
2.1 数据集介绍
采用的手写数字数据集是从sk-learn库里直接加载的。
digits = load_digits
2.2 数据集预处理
1、先把数据和标签分别拿出来:
data=digits.data #这里的data是64维的,8*8
labels=digits.target #labels是10类
2、标准化一下输入
scaler = StandardScaler()
data = scaler.fit_transform(data) # 数据预处理:标准化输入特征
3、构造训练集和测试集
# 将数据集划分为训练集和测试集
#这里是按照8/2分数据集,随机生成(0,1)内的随机数,随机数小于0.8,就放到训练集,大于等于0.8就放到测试集里。得到训练集和测试集的数据以及标签。
4、独热编码
因为是多分类问题,所以对标签采用独热编码
# 将标签进行独热编码,标签对应值的位置为1,其余位置为0
三、定义网络结构以及网络初始化
1、网络结构
2、初始化权重和偏置
这里需要初始化的参数各层之间的权重和偏置,输入到第一个隐藏层,第一个隐藏层到第二个隐藏层,第二个隐藏层到输出层。采用的是高斯初始化,均值为0,方差为0.01,大小就是每层之间的参数。
3、定义激活函数、激活函数的导数以及交叉熵损失函数
四、训练网络
1、设置超参数
学习率为0.01,训练轮次为500次,采用小批量梯度下降法,批量大小为16。
2、打乱训练集
3、前向传播
先获取当前批次大小的数据集,然后前向传播,输入层的数据就是批次大小数据,得到第一个隐藏层的输入为X_batch和权重做点积再加上偏置,再通过sigmoid函数,得到第一个隐藏层的输出。以此类推,知道输出层。
4、计算损失函数
用的交叉熵损失函数
5、反向传播
从后往前,先计算误差函数,再求出对权重和偏置的梯度大小,再用梯度去反向更新权重和偏置。
6、打印损失
五、评估模型
用测试集评估模型的性能,把测试集放到网络中前向传播一遍,得到输出结果,对比标签值,从而计算准确率。
每10个epoch绘制一下loss曲线、训练集准确度和测试集准确度,得到下图:
最终测试集准确率达到了99.09%,测试集准确率达到了96.45%,效果还是可以的。但是由于没有使用自适应学习率,loss曲线在200次迭代之后有明显震荡。
源代码:
import numpy as np
from sklearn.datasets import load_digits
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
# 加载手写数字数据集
digits = load_digits()
data=digits.data
#这里的data是64维的,8*8
labels=digits.target
#labels是10类
scaler = StandardScaler()
data = scaler.fit_transform(data)
# 数据预处理:标准化输入特征
train_data=[]
train_labels=[]
test_data=[]
test_labels=[]
for i in range(len(labels)):
if np.random.rand(1)<0.8:
train_data.append(data[i])
train_labels.append(labels[i])
else:
test_data.append(data[i])
test_labels.append(labels[i])
#这里是按照8/2分数据集,随机生成(0,1)内的随机数,随机数小于0.8,就放到训练集,
# 大于等于0.8就放到测试集里。得到训练集和测试集的数据以及标签。
# 将标签进行独热编码
def one_hot_encode(labels, num_classes):
one_hot_labels = np.zeros((len(labels), num_classes))
for i, label in enumerate(labels):
one_hot_labels[i, label] = 1
return one_hot_labels
# 将数据集划分为训练集和测试集
X_train, X_test, y_train, y_test = train_data,test_data,train_labels,test_labels
X_train=np.array(X_train)
X_test=np.array( X_test)
y_train=np.array(y_train)
y_test=np.array(y_test)
# 独热编码标签
num_classes = len(np.unique(labels))
y_train_onehot = one_hot_encode(y_train, num_classes)
y_test_onehot = one_hot_encode(y_test, num_classes)
# 定义神经网络结构
input_size = X_train.shape[1]
layer1_size=256
layer2_size=64
output_size = num_classes
# 初始化权重和偏置
np.random.seed(42)
weights_input_layer1 = np.random.normal(loc=0, scale=0.01, size=(input_size, layer1_size))
bias_input_layer1 =np.random.normal(loc=0, scale=0.01, size=(1, layer1_size))
weights_layer1_layer2 = np.random.normal(loc=0, scale=0.01, size=(layer1_size, layer2_size))
bias_layer1_layer2 = np.random.normal(loc=0, scale=0.01, size=(1, layer2_size))
weights_layer2_output = np.random.normal(loc=0, scale=0.01, size=(layer2_size,output_size))
bias_layer2_output =np.random.normal(loc=0, scale=0.01, size=(1, output_size))
# 定义激活函数(sigmoid)
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 定义激活函数的导数
def sigmoid_derivative(x):
return x * (1 - x)
# 定义损失函数(交叉熵)
def cross_entropy_loss(predictions, targets):
epsilon = 1e-15
predictions = np.clip(predictions, epsilon, 1 - epsilon)
loss = -np.sum(targets * np.log(predictions)) / len(targets)
return loss
# 设置超参数
learning_rate = 0.01
epochs = 500
batch_size = 16 # 每个小批次的大小
# 训练神经网络
loss_all=[]
epoch_all=[]
train_acc=[]
test_acc=[]
for epoch in range(epochs):
# 将训练集打乱顺序
permutation = np.random.permutation(len(X_train))
X_train_shuffled = X_train[permutation]
y_train_shuffled = y_train_onehot[permutation]
for i in range(0, len(X_train_shuffled), batch_size):
# 获取当前批次
X_batch = X_train_shuffled[i:i+batch_size]
y_batch = y_train_shuffled[i:i+batch_size]
# 前向传播
layer1_input = np.dot(X_batch, weights_input_layer1) + bias_input_layer1
layer1_output = sigmoid(layer1_input)
layer2_input = np.dot(layer1_output, weights_layer1_layer2) + bias_layer1_layer2
layer2_output = sigmoid(layer2_input)
output_layer_input = np.dot(layer2_output , weights_layer2_output) + bias_layer2_output
output_layer_output = sigmoid(output_layer_input)
# 计算损失,交叉熵
loss = cross_entropy_loss(output_layer_output, y_batch)
# 反向传播
output_error = output_layer_output - y_batch
#求误差
output_delta = output_error * sigmoid_derivative(output_layer_output)
#求梯度
layer2_error = output_delta.dot(weights_layer2_output.T)
layer2_delta = layer2_error * sigmoid_derivative(layer2_output)
layer1_error = layer2_delta.dot(weights_layer1_layer2.T)
layer1_delta = layer1_error * sigmoid_derivative(layer1_output)
# 更新权重和偏置,用梯度下降更新
weights_layer2_output -= (layer2_output).T.dot(output_delta) * learning_rate
bias_layer2_output -= np.sum(output_delta, axis=0, keepdims=True) * learning_rate
weights_layer1_layer2 -= (layer1_output).T.dot(layer2_delta) * learning_rate
bias_layer1_layer2-= np.sum(layer2_delta, axis=0, keepdims=True) * learning_rate
weights_input_layer1 -= X_batch.T.dot(layer1_delta) * learning_rate
bias_input_layer1 -= np.sum(layer1_delta, axis=0, keepdims=True) * learning_rate
#计算训练集准确率和测试集准确率
# 打印损失
if epoch % 10 == 0:
print(f"Epoch {epoch}, Loss: {loss}")
loss_all.append(loss)
epoch_all.append(epoch)
# 在训练集上评估模型
layer1_input = np.dot(X_train, weights_input_layer1) + bias_input_layer1
layer1_output = sigmoid(layer1_input)
layer2_input = np.dot(layer1_output, weights_layer1_layer2) + bias_layer1_layer2
layer2_output = sigmoid(layer2_input)
output_layer_input = np.dot(layer2_output , weights_layer2_output) + bias_layer2_output
output_layer_output = sigmoid(output_layer_input)
# 预测结果
predictions = np.argmax(output_layer_output, axis=1)
accuracy = np.mean(predictions == y_train)
print("训练集的准确率:", accuracy)
train_acc.append(accuracy)
# 在测试集上评估模型
layer1_input = np.dot(X_test, weights_input_layer1) + bias_input_layer1
layer1_output = sigmoid(layer1_input)
layer2_input = np.dot(layer1_output, weights_layer1_layer2) + bias_layer1_layer2
layer2_output = sigmoid(layer2_input)
output_layer_input = np.dot(layer2_output , weights_layer2_output) + bias_layer2_output
output_layer_output = sigmoid(output_layer_input)
# 预测结果
predictions = np.argmax(output_layer_output, axis=1)
accuracy = np.mean(predictions == y_test)
print("测试集的准确率:", accuracy,"\n")
test_acc.append(accuracy)
plt.plot(epoch_all,loss_all,label="loss")
plt.plot(epoch_all,train_acc,label="train_acc")
plt.plot(epoch_all,test_acc,label="test_acc")
plt.xlabel('epoch')
plt.ylabel('loss_value and accuracy')
plt.legend()
plt.show()