python不使用框架编写神经网络实现手写数字识别

实验目的及要求

目的:不使用框架, 用python实现神经网络, 学习算法最好的方法就是实现它, 掌握反向传播算法的推导及代码实现,掌握Xavier初始化、Adam算法、数据归一化、batch-normalization、dropout等技术。
要求:实现给定结构和指定初始化和学习算法的网络,不能使用现成的机器学习库,可以使用numpy库,对比1. 有无归一化。2. 有无batch-normalization。3. 有无dropout。的损失曲线和混淆矩阵。

实验环境及采用技术

实验环境:windows10,pycharm,python3.7,mnist数据集,numpy,random。
采用技术:Xavier初始化、Adam算法、数据归一化、batch-normalization、dropout。

数据集

mnist.pkl.gz
下载地址

网络结构

  • 输入:28*28
  • 隐藏层: 两层每层512各结点,激活函数:双曲正切函数
  • 输出层: 10个结点,激活函数:softmax
    Xavier初始化公式
    在这里插入图片描述
    权值学习:Adam算法
    在这里插入图片描述
    梯度下降(左:Momentum, 右:RMSprop)
    在这里插入图片描述
    结合起来
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    损失函数:交叉熵损失函数

训练结果对比:

是否采用输入数据归一化的对比(同样的前提即Xavier初始化、Adam算法)

左:使用归一化的损失曲线
右:未使用归一化的损失曲线
在这里插入图片描述
可以看出使用输入数据归一化的收敛快,震荡效果也少一些,曲线更为平滑。
对应混淆矩阵
在这里插入图片描述
可以看出使用归一化对测试数据的预测效果好一些,预测的准确率更高。

是否采用dropout的对比(同样的前提即Xavier初始化、Adam算法、使用了归一化)

在这里插入图片描述
可以看出使用dropout的前面下降的快,但后面下降的慢且震荡的大,优点和不足都非常明显。
使用dropout后对训练和测试数据的混淆矩阵为
在这里插入图片描述
是否采用batch normalization的对比, 没有做出来

源码(NetWork类)

class Network(object):

    def __init__(self, sizes):
        """初始化参数"""
        self.num_layers = len(sizes)
        self.sizes = sizes
        self.biases = None
        self.weights = None
        # self.random_parameter_init()
        self.xavier_parameter_init()

    def xavier_parameter_init(self):
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        self.weights = []
        for i in range(1, self.num_layers):
            low = -np.sqrt(6.0 / (self.sizes[i - 1] + self.sizes[i]))
            high = np.sqrt(6.0 / (self.sizes[i - 1] + self.sizes[i]))
            self.weights.append(np.random.uniform(low, high, (self.sizes[i], self.sizes[i - 1])))

    def random_parameter_init(self):
        # (y, 1)生产列向量,且服从
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        # x为该层节点数,y为前一层的节点数也就是该层的输入数
        self.weights = [np.random.randn(y, x) for x, y in zip(self.sizes[:-1], self.sizes[1:])]

    def test(self):
        print("biases:", self.biases)
        print("weights type:", type(self.weights))
        print("weights:", self.weights)
        print(list(zip(self.biases, self.weights)))
        print("b.shape", self.biases[1].shape)
        print("w.shape", self.weights[2].shape)

    def feedforward(self, a, ):
        """Return the output of the network if ``a`` is input."""
        """遍历的是非输入层,b是该层的偏置数组,w是该层的权重数组"""
        """遍历完一次即完成一次向前传播"""
        for b, w in list(zip(self.biases, self.weights))[:-1]:
            a = tanh(np.dot(w, a) + b)
        # 最后一层采用softmax激活
        a = softmax(np.dot(self.weights[-1], a) + self.biases[-1])
        # for b, w in zip(self.biases, self.weights):
        #     a = sigmoid(np.dot(w, a)+b)
        return a

    def Adam(self, training_data, epochs, mini_batch_size, eta, test_data=None):
        """
        training_data:训练数据
        epochs:迭代次数
        mini_batch_size:块大小
        eta:学习率
        test_data:测试数据
        """
        '''随机梯度下降'''
        if test_data:
            n_test = len(test_data)
        n = len(training_data)
        losses = []
        # 循环一次即迭代一次
        for j in range(epochs):
            # 将所有元素随机排序
            random.shuffle(training_data)
            # 将所有训练数据按块大小划分成块形成一个块列表
            mini_batches = [training_data[k:k+mini_batch_size] for k in range(0, n, mini_batch_size)]
            # 对块列表进行遍历,每个块进行反向传播,并更新参数
            for i, mini_batch in enumerate(mini_batches):
                nabla_b_g, nabla_w_g = self.mini_batch_backprop(mini_batch)

                beta1 = 0.9
                beta2 = 0.999
                eps_station = 1e-10
                # 初始化m,v
                b_m = [np.zeros(b.shape) for b in self.biases]
                w_m = [np.zeros(w.shape) for w in self.weights]

                b_v = [np.zeros(b.shape) for b in self.biases]
                w_v = [np.zeros(w.shape) for w in self.weights]

                b_res = [np.zeros(b.shape) for b in self.biases]
                w_res = [np.zeros(w.shape) for w in self.weights]
                for index in range(self.num_layers-1):
                    b_m[index] = beta1 * b_m[index] + (1 - beta1) * nabla_b_g[index]
                    b_v[index] = beta2 * b_v[index] + (1 - beta2) * nabla_b_g[index] ** 2
                    m_bar_b = b_m[index] / (1 - beta1 ** (i + 1))
                    v_bar_b = b_v[index] / (1 - beta2 ** (i + 1))
                    b_res[index] = eta * m_bar_b / (np.sqrt(v_bar_b) + eps_station)

                    w_m[index] = beta1 * w_m[index] + (1 - beta1) * nabla_w_g[index]
                    w_v[index] = beta2 * w_v[index] + (1 - beta2) * nabla_w_g[index] ** 2
                    m_bar_w = w_m[index] / (1 - beta1 ** (i + 1))
                    v_bar_w = w_v[index] / (1 - beta2 ** (i + 1))
                    w_res[index] = eta * m_bar_w / (np.sqrt(v_bar_w) + eps_station)

                self.weights = [w - nw for w, nw in zip(self.weights, w_res)]
                self.biases = [b - nb for b, nb in zip(self.biases, b_res)]

            loss = self.get_loss(training_data)
            losses.append(loss)

            # 执行一次迭代用测试数据检测一次正确率
            if test_data:
                print("Epoch: {0}  loss: {1}  acc: {2}/ {3}".format(j, loss, self.evaluate(test_data), n_test))
            else:
                print("Epoch {0} complete".format(j))
        return losses

    def get_loss(self, training_data):
        # loss = log(e, s_max)
        loss = 0
        for x, y in training_data:
            y_pred = self.feedforward(x)
            s_max = y_pred[np.argmax(y_pred)]
            loss += -1*np.log(s_max)
        return loss

    def mini_batch_backprop(self, mini_batch):
        X = np.array([x.reshape(784) for (x, y) in mini_batch])
        Y = np.transpose(np.array([y.reshape(10) for (x, y) in mini_batch]))

        # 初始化b,w
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        # 向前传播
        activation = np.transpose(X)
        # 存放每层的输入,上一层的结果
        activations = [activation]
        # 存放每层未激活的结果(向量)
        zs = []
        for b, w in list(zip(self.biases, self.weights))[:-1]:
            # 向前一层的计算存储相应的数据
            z = np.dot(w, activation) + b
            zs.append(z)
            activation = tanh(z)
            activations.append(activation)
        # 最后一层用softmax
        z = np.dot(self.weights[-1], activation) + self.biases[-1]
        zs.append(z)
        activation = softmax(z)
        activations.append(activation)

        delta = activations[-1] - Y
        # 各列相加保持维度不变
        nabla_b[-1] = np.sum(delta, axis=1, keepdims=True)
        nabla_w[-1] = np.dot(delta, activations[-2].transpose())

        for l in range(2, self.num_layers):
            z = zs[-l]
            sp = tanh_prime(z)
            delta = np.dot(self.weights[-l + 1].transpose(), delta) * sp
            nabla_b[-l] = np.sum(delta, axis=1, keepdims=True)
            nabla_w[-l] = np.dot(delta, activations[-l - 1].transpose())
        nabla_b_g = [nb / len(mini_batch) for nb in nabla_b]
        nabla_w_g = [nw / len(mini_batch) for nw in nabla_w]
        return nabla_b_g, nabla_w_g

    def evaluate(self, test_data):
        test_results = [(np.argmax(self.feedforward(x)), y) for (x, y) in test_data]
        return sum(int(x == y) for (x, y) in test_results)


def softmax(z):
    return np.exp(z) / np.sum(np.exp(z))


def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))


def tanh_prime(x):
    return 1-(tanh(x))**2


def sigmoid(z):
    """The sigmoid function."""
    return 1.0/(1.0+np.exp(-z))


# sigmoid的导数
def sigmoid_prime(z):
    """Derivative of the sigmoid function."""
    return sigmoid(z)*(1-sigmoid(z))

项目源码

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于Numpy+Tensorflow实现全连接神经网络应用于手写数字画板、手写数字识别源码+项目说明.zip ### 一:项目简介 ##### 1. 做了什么 **第一部分<算法实现>**:分别用 Numpy 和 TensorFlows 实现了二个版本的全连接神经网络,用 Mnist 数据集训练,将训练后的模型用真实手机拍照获得的手写数字图片进行识别,最后得到了不错的效果。 **第二部分<算法落地>**:将实现好的神经网路模型封装好,用PyQt5做了一个手写画板的界面,可以实现用户手写输入一个数字笔画后,自动识别转化为对应的数字。 ##### 2. 代码结构 **src/numpy_demo**: 仅用 Numpy 库实现的一个全连接神经网路,无用到开源框架。 **src/tensorflow_demo**: 基于开源框架 TensorFlow 实现的全连接神经网络。 **src/data.rar**: 使用过程中需要用到的数据,运行源码前请先将该压缩文件解压至当前目录。 **dist**: 将 Numpy 版的神经网络,应用于手写画板,打包成的exe,无Python环境也可执行。 ##### 3. 应用程序 打包后的 exe 程序过大,已经单独放至releases下。 ......
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值