手写神经网络实现手写数字识别

一、网络结构

       一个输入层,两个隐藏层,一个输出层。输入层有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()    

 

 

  • 16
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值