使用阿里云ECS进行神经网络学习-使用神经网络来识别手写数字

简介: 在阿里云的ECS上搭建python环境,完成在经典数据集MNIST上进行识别手写数字的实验。

第一部分
个人信息:

本人今年本科计算机专业毕业,将于9月继续攻读计算机类的硕士。借此时间段和阿里云平台来入门深度学习中的图像分类。我是通过同学介绍来了解“飞天加速计划·高校学生在家实践”活到,之前有过几次的服务器租用,主要为了熟悉Linux操作系统的使用和本科课程相关实验的需要。现在借此博客机会来分享自己在阿里云平台的使用感受。

第二部分
使用内容
大多数人可以轻易识别下面这个手写序列。但是这个轻易是假象。

1.png

在我们大脑的每个半球,人类都有一个初级视觉皮层,也被称为$V1$,包含1.4亿个神经元,它们之间有几百亿个连接。然而,人类的视觉不仅涉及$V1$,还涉及整个系列的视觉皮层--$V2、V3、V4$和$V5$--正在进行逐渐复杂的图像处理。我们的大脑经过数亿
年的进化,适应了对视觉世界的理解。但是这个简单的识别过程是在无意识中完成的,why?

对于普通的编程方式而言,模式识别会很难办,且看起来毫无希望。

神经网络以一种截然不同的方式来处理这个问题,即把大量的手写数字,称为训练样本,然后开发一个能够从这些训练实例中学习的系统。换句话说,神经网络使用这些样本来自动推断识别手写数字的规则。(我们人类的直觉是,例如数字‘9’,上面有一个环,下面有一个竖线。)

我们将编写一个计算机程序,实现一个学习识别手写数字的神经网络(代码仅有74行)。这个简短的程序可以识别数字,准确率超过96%,而且无需人工干预。后面我们会做的更好!事实上,最好的商业神经网络现在已经非常好,以至于银行用它来处理支票,邮局用它来识别地址。

真正的目的是学习一些关于神经网络的关键思想,包括两个重要的神经元-感知器和激活神经元(the perceptron and the sigmoid neuron)和标准的神经网络学习算法-随机梯度下降(stochastic gradient descent)。

感知器(perceptron)

是一种人工神经元,是科学家Frank Rosenblatt在1950s-1960s开发,现代工作中更常见的神经元是一个叫做sigmoid神经元的模型。

感知器如何工作? 一个感知器需要一些二进制输入,并且产生一个二进制输出:

2.png

Rosenblatt提出了一个简单的规则来计算输出。他引入了权重来对应神经元从输入到输出的重要性。神经元的输出取决于输入与权重是否到达一定的阈值。这里权重和阈值都是参数,为了简便,我们将阈值放到等号另外一边,同时令阈值的相反数为感知器的偏置-衡量使感知器输出1(激活-fire)的数值。

为了方便,通常将输入的变量作为输入层来看,这是一个没有输入,只有输出的特殊神经元(简单定义为输出所需值x1的特殊单元)

3.png

感知器可以模拟NAND门电路,加法器等等...

感知器的计算普遍性有利有弊,利: 说明感知器想其他计算设备一样强大,弊:这些仅仅说明感知器是新型NAND门的一种?并不是什么大事!

事实并非如此,我们可以设计学习算法来自动调整人工网络的权重和偏执,这种自动来自外部的刺激,而非人为干预。这种方式可以解决对传统电路而言,很难解决的问题。


激活函数(Sigmoid neurons)

为了了解学习是如何进行的,假设我们对网络中的一些权重(或偏置)做了一个小的改变。我们希望这个权重的微小变化只导致网络输出的微小相应变化。这种特性使得学习成为可能。

why?

例如,假设该网络错误地将一张图片分类为 "8",而它应该是 "9"。我们可以想办法对权重和偏置做一个小小的改变,使网络更亲近于将图像分类为 "9"。然后我们会重复这个过程,一次又一次地改变权重和偏置,以产生越来越好的输出。该网络将进行学习。

4.png

这种特性对感知器而言,很难做到。因为对权重和偏置的微小改变,可能会使感知器输出完全相反的结果。


对此,我们引入与感知器差不多的神经元-激活神经元(sigmoid neurons),尤其在权重和偏置取向两极的情况下极为相似

5.png
6.png

该特性使得在输入和输出上有存在与感知器一定的区别。即输入/输出不是二进制数值,而是[0,1]之间任意实数。该函数的光滑性使得权重和偏置的微小改变影响输出产生微小变化。


神经网络的层次结构

7.png

最左边是含有输入神经元的输入层,最右边是含有输出神经元的输出层,中间叫做隐藏层(仅仅是因为它既不是输入层,也不是输出层)。这里对于输入层和输出层的神经元数量都是直截了当的,但是隐藏层的设计需要一些经验(启发式方法)。


一个简单的手写数字分类器

我们可以将识别手写体问题分解成两个子问题:

  1. 将一张含有多个数字序列的图像分解成含有单个数字图像的方法

8.png
9.png

  1. 识别图像中的单个数字

第二个问题的解决可以间接解决第一个问题,因为如果能够解决好第二个问题,那么说明此时的分割图像也是解决好的。所以我们将注意力集中到第二个问题上来,即识别单个数字。

我们使用三层神经网络解决该问题:

10.png

注意:

  1. 输入是像素的灰度值,范围在0-1之间,其中0.0代表纯白色,1.0代表纯黑色。
  2. 中间的隐藏层根据实验的结果来确定其神经元数量。
  3. 输出层有10个神经单元,分别对应0~9,若第一个神经元被激活,则表示网络认为图像是0,等等。更准确的说,我们取输出层最高的值对应的神经元,如果是第6个神经元,则网络认为输入的图像是数字6。

思考:

第四层将原先的输出层转化成二进制编码格式:

11.png


学习梯度下降

MNIST dataset分为从一批250个人收集的图像(60,000)组成训练集和另外一批250个人的图像(10,000)组成测试集。每个样本形如(x, y=y(x)),其中x为28x28=784维,每一维代表图像的像素值(缩放为[0,1]),y为10维,例如$[0,0,0,0,0,0,1,0,0,0]^T$(代表该输入图像的标签为6)

我们需要一个帮助我们找到合适的权重和偏置,以至让所有样本的网络输出与真实标签接近的学习算法,为了量化我们实现这个目标的距离,我们定义了代价函数,这里是平方误差代价函数。从而将问题转化成使用一个方法自动找到合适的权重和偏置使得代价函数尽可能的小,这个方法叫梯度下降(gradient descent)。

为什么使用平方误差作为代价函数,而不是简单的神经网络正确分类的数量?

因为后者不是关于权重和偏置的平滑函数,也就是说,大多数情况下,对权重和偏置的微小改变不会导致正确分类的训练图像数量有任何变化。而平方误差代价函数可以轻易看见在权重和偏置发生微小变化后导致代价函数上的改善。


为了集中精力,我们将问题先简化成一个关于多变量的函数,并且最小化函数,然后再回到神经网络。例如函数$C(v_1, v_2)$是关于两个自变量的函数:

12.png

也许我们可以使用微积分来找到函数极值的解析解,但是对于成千上万个自变量而言,是一个噩梦!

我们可以将此问题变成球从山的斜坡滚到低处的情景,此时我们需要求导这个数学方法来辅助,因为导数可以告诉我们当地山谷的局部地理情况。该如何移动使得球总是往低处走?

为了数学表示简便,我们定义函数的梯度-由所有偏导数组成的向量,自变量的变化量向量-由所有自变量的变化量组成的向量。所以函数的变化量表示为梯度向量与自变量变化量向量的点积。此处也间接表示了为什么偏导数构成的向量叫梯度,因为它描述了自变量变化量与函数变化量的关系

这里重点在于如何选择当前位置自变量的变化量方向($-\eta\bigtriangledown C$),使得函数变化量保持负号且最小!这样我们重复这样做,直到达到全局最小值。

总结起来,就是梯度下降法不断计算相对于当前位置的梯度向量(由偏导数组成),然后朝着反方向,可以尽可能快的到达谷底。形如:
13.png

注意:

  1. 学习率$lr$不能太大,否则不满足可微的条件,从而导致代价函数变大
  2. 学习率$lr$不能太小,否则会很慢。
  3. 梯度下降方法耗时

值得注意的是,平方误差代价函数可以表示为所有样本代价的均值。也就是说,为了得到自变量当前位置的梯度,需要单独计算所有样本的梯度,这对样本量很大的网络而言,也是噩梦!为此,我们使用随机梯度下降(stochastic gradient descent),其思想在于使用随机小批量样本m来计算的偏导数平均值代替所有样本n计算偏导数平均值。速度增加了n/m倍!

若总样本为n,每次的小批量样本为m,则每次选择样本中不同的m个样本来更新参数,当整个训练集参与一遍后,成为一个epoch!


实现我们的神经网络来分类手写数字

$$sophisticated algorithm \le simple learning algorithm + good training data.$$

走向深度学习

尽管我们的神经网络给我们印象深刻的性能,但是这个性能显着神秘,难以琢磨。 我们能否找到一些方法来理解我们的网络在对手写数字进行分类时所依据的原则?而且,鉴于这样的原则,我们能否做得更好?

神经元作为一种衡量权重证据的手段,假设我们想确定一张图像是否是一张人脸,我们可以使用神经网络,将图像像素作为输入,使用一个输出神经元来判断是否是一张人脸。
Einstein_crop.jpg
Kangaroo.JPG

我们可以手动选择合适的权重和偏置,但是如何做呢?

一个合理的方式是将这个问题变成多个子问题,

  1. 图像左上角有眼睛?
  2. 图像右上角有眼睛?
  3. 图像中间有鼻子?
  4. 图像下面有嘴巴?
  5. ....

如何上面大部份问题神经网络给出的回答都是‘Yes',则可以认为该图像是一张人脸,反之则不是人脸。

不过这只是一个大概的方法,还有许多特殊的情况,例如,图像只有部分脸,侧面脸,带了帽子...。不过,请注意这是为了帮助我们建立关于网络功能的直觉。 矩形是解决子问题对应的网络
tikz14.png

同时子问题还可以进一步的分解,例如:

tikz15.png

最终具体到图像的某个像素,这样的问题可以由连接到图像中原始像素的单个神经元来回答。

最终的结果是一个网络,它将一个非常复杂的问题--这幅图像是否显示了一张脸--分解为非常简单的问题,可以在单个像素的层面上进行回答。它通过一系列的多层来实现这一目标,前期的层回答关于输入图像的非常简单和具体的问题,而后来的层则建立了一个更加复杂和抽象的概念的层次结构。具有这种多层结构的网络--两个或多个隐藏层--被称为深度神经网络(deep neural networks)。

深层网络与浅层网络相比较,有点像将有函数调用能力的编程语言与没有这种调用能力的精简语言相比较。在神经网络中,抽象的形式与在传统编程中不同,但它同样重要。
代码:

'''
MNIST
    50,000 训练集
    10,000 测试集
    10,000 验证集
'''
import random
import numpy as np

class Network(object):
    # 列表sizes 包含了各层的神经元数量。
    def __init__(self, sizes):
        self.num_layers = len(sizes)
        self.sizes = sizes
        # np.random.randn函数用于生成均值为0、标准差为1的高斯分布
        self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
        self.weights = [np.random.randn(y, x)
                         for x, y in zip(sizes[:-1], sizes[1:])]
        # print(self.weights[0], self.weights[1], sep='\n')
        # print(self.weights, self.biases, sep='\n')

    def feedforward(self, a):
        # a是输入,返回每层相应的输出(输入层除外)
        for b, w in zip(self.biases, self.weights):
            # print('b:\n', b, '\nw\n', w)
            a = sigmoid(np.dot(w, a)+b)
            # print('a:\n', a)
        return a


    def SGD(self, traing_data, epochs, mini_batch_size, eta, test_data=None):
        '''
        :param traing_data: 元组的列表,(x,y),x为输入,y为对应的标签
        :param epochs: 希望训练的周期数
        :param mini_batch_size: 每次梯度更新所需的样本数
        :param eta: 学习率
        :param test_data: 测试集
        :return:
        '''
        if test_data:
            n_test = len(test_data)
        n = len(traing_data)
        ''' 在python2中
                xrange() 函数用法与 range 完全相同,
                所不同的是生成的不是一个数组,而是一个生成器。
            在python3中
                xrange被range替代,同时range类型为range,
                是一个生成器,而非list。
        '''
        for j in range(epochs):
            random.shuffle(traing_data)
            mini_batches = [
                traing_data[k: k+mini_batch_size]
                for k in range(0, n, mini_batch_size)
                         ]
            # 对每个mini_batch使用梯度下降来更新权重和偏置
            for mini_batch in mini_batches:
                self.update_mini_batch(mini_batch, eta)
            if test_data:
                print('Epoch {0}: {1}/{2}'.format(j, self.evaluate(test_data), n_test))
            else:
                print('Epoch {0} complete'.format(j))

    def update_mini_batch(self, mini_batch, eta):
        '''
        通过对单个小批次应用反向传播法的梯度下降,更新网络的权重和偏置。
        :param mini_batch: 一个小批样本,是一个元组列表
        :param eta: 学习率
        :return:
        '''
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        for x, y in mini_batch:
            delta_nabla_b, delta_nabla_w = self.backprop(x, y)
            nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
            nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
        self.weights = [w-(eta/len(mini_batch))*nw for w, nw in zip(self.weights, nabla_w)]
        self.biases = [b-(eta/len(mini_batch))*nb for b, nb in zip(self.biases,  nabla_b)]

    def backprop(self, x, y):
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        # 前向传播
        activation = x
        activations = [x]
        zs = []
        for b, w in zip(self.biases, self.weights):
            z = np.dot(w, activation) + b
            zs.append(z)
            activation = sigmoid(z)
            activations.append(activation)
        # 后向传播
        delta = self.cost_derivative(activations[-1], y) * \
                sigmoid_prime(zs[-1])
        nabla_b[-1] = delta
        nabla_w[-1] = np.dot(delta, activations[-2].transpose())

        for l in range(2, self.num_layers):
            z = zs[-l]
            sp = sigmoid_prime(z)
            delta = np.dot(self.weights[-l + 1].transpose(), delta) * sp
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l - 1].transpose())
        return (nabla_b, nabla_w)

    def evaluate(self, test_data):
        '''

        :param test_data:
        :return:    测试集中正确分类的数量,
                    其中分类结果为最后一层神经元中最高激活值的位置
        '''
        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 cost_derivative(self, output_activations, y):
        return (output_activations-y)


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

def sigmoid_prime(z):
    # sigmoid 的导数
    return sigmoid(z)*(1-sigmoid(z))
# net = Network([2, 3, 1])
# print(net.feedforward([[1], [2]]))

实验截图:
截屏2022-08-15 上午11.28.07.png

第三部分:
使用感受和未来展望

首先很感谢阿里云平台对高校学生给予如此实惠的活动,我使用的是Ubuntu操作系统,该平台较为稳定。同时对安装配置相对其他系统也是非常的方便,快捷。自己即将开学,老师布置的任务,自己也是时而有头绪,时而摆烂,希望自己摆正心态,做好自己的人生道路!



相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
7天玩转云服务器
云服务器ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,可降低 IT 成本,提升运维效率。本课程手把手带你了解ECS、掌握基本操作、动手实操快照管理、镜像管理等。了解产品详情: https://www.aliyun.com/product/ecs
相关文章
|
27天前
|
机器学习/深度学习 编解码 人工智能
阿里云gpu云服务器租用价格:最新收费标准与活动价格及热门实例解析
随着人工智能、大数据和深度学习等领域的快速发展,GPU服务器的需求日益增长。阿里云的GPU服务器凭借强大的计算能力和灵活的资源配置,成为众多用户的首选。很多用户比较关心gpu云服务器的收费标准与活动价格情况,目前计算型gn6v实例云服务器一周价格为2138.27元/1周起,月付价格为3830.00元/1个月起;计算型gn7i实例云服务器一周价格为1793.30元/1周起,月付价格为3213.99元/1个月起;计算型 gn6i实例云服务器一周价格为942.11元/1周起,月付价格为1694.00元/1个月起。本文为大家整理汇总了gpu云服务器的最新收费标准与活动价格情况,以供参考。
阿里云gpu云服务器租用价格:最新收费标准与活动价格及热门实例解析
|
2天前
|
Cloud Native Java 编译器
将基于x86架构平台的应用迁移到阿里云倚天实例云服务器参考
随着云计算技术的不断发展,云服务商们不断推出高性能、高可用的云服务器实例,以满足企业日益增长的计算需求。阿里云推出的倚天实例,凭借其基于ARM架构的倚天710处理器,提供了卓越的计算能力和能效比,特别适用于云原生、高性能计算等场景。然而,有的用户需要将传统基于x86平台的应用迁移到倚天实例上,本文将介绍如何将基于x86架构平台的应用迁移到阿里云倚天实例的服务器上,帮助开发者和企业用户顺利完成迁移工作,享受更高效、更经济的云服务。
将基于x86架构平台的应用迁移到阿里云倚天实例云服务器参考
|
9天前
|
存储 弹性计算 运维
阿里云日常运维-购买服务器
这篇文章是关于如何在阿里云购买和配置云服务器ECS的教程。
39 6
阿里云日常运维-购买服务器
|
7天前
|
人工智能 数据中心 云计算
AI网络新生态ALS发起成立,信通院、阿里云、AMD等携手制定互连新标准
9月3日,在2024 ODCC开放数据中心大会上,阿里云联合信通院、AMD等国内外十余家业界伙伴发起AI芯片互连开放生态ALS(ALink System)。
AI网络新生态ALS发起成立,信通院、阿里云、AMD等携手制定互连新标准
|
3天前
|
网络安全 开发工具 云计算
服务器看代码阿里云
随着云计算技术的发展,阿里云作为国内领先的云计算服务提供商,其服务器受到广大用户青睐。本文主要介绍如何在阿里云服务器上便捷地查看与管理代码,如使用SSH连接服务器并通过命令行工具打开文件,以及利用Git进行版本控制和协作开发,提高代码管理效率。无论个人开发者还是企业团队,都能借助阿里云服务器高效地部署与管理应用程序,提升工作效率及产品质量。
27 10
|
17天前
|
弹性计算 负载均衡 数据库
阿里云轻量应用服务器全面解析:收费标准、产品优势及适用场景
在云计算领域,阿里云凭借其强大的技术实力和丰富的产品线,为用户提供了一系列高效、便捷的云服务器产品。其中,轻量应用服务器(Simple Application Server)作为面向个人开发者、中小企业等用户的入门级云产品,凭借其易用性、高性价比以及一站式服务体验,受到了广泛的欢迎。本文将全面解析阿里云轻量应用服务器的收费标准、产品优势以及适用场景,帮助用户更好地了解和选择这一产品。
阿里云轻量应用服务器全面解析:收费标准、产品优势及适用场景
|
21天前
|
弹性计算 负载均衡 数据库
阿里云轻量应用服务器收费标准、性能及适用场景全面解析
阿里云轻量应用服务器(Simple Application Server)作为面向个人开发者、中小企业等用户的入门级云产品,凭借其易用性、高性价比以及一站式服务体验,受到了广泛的欢迎。本文将全面解析阿里云轻量应用服务器的收费标准、最新活动价格以及适用场景,帮助用户更好地了解和选择这一产品。
阿里云轻量应用服务器收费标准、性能及适用场景全面解析
|
7天前
|
人工智能 运维 Cloud Native
专访阿里云:AI 时代服务器操作系统洗牌在即,生态合作重构未来
AI智算时代,服务器操作系统面临的挑战与机遇有哪些?
|
9天前
|
弹性计算 安全 测试技术
阿里云的ECS云服务器应用例
在未来的远程办公时代,“未来空间”打造了一个高效、灵活且安全的在线协作平台,采用阿里云ECS云服务器作为核心基础设施。ECS提供按需付费的弹性计算能力,确保平台响应迅速并能应对流量高峰。其集成的安全特性如安全组和云盾,构建了多层次防护体系,保障数据安全。此外,ECS与阿里云其他服务无缝集成,如RDS、CDN和OSS,实现了高效的数据管理和全球低延迟访问。结合阿里云的机器学习服务,“未来空间”开发了智能会议摘要和情绪分析功能,提升了用户体验。凭借ECS的强大支持,该平台不仅实现了全球团队的高效协作,还赢得了市场的广泛认可,成为远程办公领域的标杆。
|
9天前
|
弹性计算 开发框架 数据可视化
阿里云虚拟主机和云服务器有什么区别?多角度全解析对比
阿里云虚拟主机与云服务器ECS的主要区别在于权限与灵活性。虚拟主机简化了网站搭建流程,预装常用环境,适合初级用户快速建站;而云服务器提供全面控制权,支持多样化的应用场景,如APP后端、大数据处理等,更适合具备技术能力的用户。尽管虚拟主机在价格上通常更优惠,但随着云服务器价格的下降,其性价比已超越虚拟主机,成为更具吸引力的选择。