写点什么

与 DeepLearning 的初次接触(上)

  • 2019-09-27
  • 本文字数:7929 字

    阅读完需:约 26 分钟

与 DeepLearning 的初次接触(上)

随着人工智能近十年来的飞速发展,机器学习、深度学习已从学术研究逐步落实到工业界,特别是近几年机器学习框架,如 TensorFlow、Pytorch、CNTK、Keras 等层出不穷,框架设计越来越面向开发者,特别是 TensorFlow.js 的发布、V8 引擎更高性能新版本的发布等等信息,这些好像在告诉我,将深度学习广泛应用到端已然不远了。

为了不被类似 Alpha 狗等 AI 打败,作为一名程序开发者,学习和了解一些机器学习的知识、最新动态信息还是有必要的,而深度学习作为机器学习的一部分,在工业界已经发挥了重要的作用,所以今天就由浅入深,从理论知识到实战编程,逐步探究人工神经网络为何物。

阅读提示

以下内容面向深度学习初学者,需要具备以下基本素质:

①有任何编程语言的编程经验

②对几何代数相关数学知识有基本理解

③对 TensorFlow、Keras 有所了解(不了解也不会有太大影响)

④对 Jupyter Notebook 有所了解(不了解也不会有太大影响)

内容纲要

以下内容分为四个章节,可能需要 30 分钟阅读时间

第一章节——介绍神经网络重要概念和数学推导

第二章节——实战编写逻辑回归模型

第三章节——使用 TensorFlow 实现浅层神经网络

第四章节——使用 Keras 实现深度神经网络

实战编程内容均通过 Jupyter Notebook 承载。

第一章理清神经网络的基础以及实现步骤

感知机

感知器(英语:Perceptron)是 Frank Rosenblatt 在 1957 年就职于康奈尔航空实验室(Cornell Aeronautical Laboratory)时所发明的一种人工神经网络。它可以被视为一种最简单形式的前馈神经网络,是一种二元线性分类器。


Frank Rosenblatt 给出了相应的感知机学习算法,常用的有感知机学习、最小二乘法和梯度下降法。譬如,感知机利用梯度下降法对损失函数进行极小化,求出可将训练数据进行线性划分的分离超平面,从而求得感知机模型。


**生物学-神经细胞**


!



(神经细胞结构示意图)


感知机是生物神经细胞的简单抽象。神经细胞结构大致可分为:树突、突触、细胞体及轴突。单个神经细胞可被视为一种只有两种状态的机器——激动时为‘是’,而未激动时为‘否’。神经细胞的状态取决于从其它的神经细胞收到的输入信号量,及突触的强度(抑制或加强)。当信号量总和超过了某个阈值时,细胞体就会激动,产生电脉冲。电脉冲沿着轴突并通过突触传递到其它神经元。为了模拟神经细胞行为,与之对应的感知机基础概念被提出,如权量(突触)、偏置(阈值)及激活函数(细胞体)。


**人工神经元**


!



(神经元结构示意图)


在人工神经网络领域中,感知机也被指为单层的人工神经网络,以区别于较复杂的多层感知机(Multilayer Perceptron)。作为一种线性分类器,(单层)感知机可说是最简单的前向人工神经网络形式。尽管结构简单,感知机能够学习并解决相当复杂的问题。感知机主要的本质缺陷是它不能处理线性不可分问题。

(注:以上内容摘自维基百科)


单层感知机的适用和缺陷


适合解决线性问题,如下图



无法解决线性不可分,如常见的 XOR 异或问题,如下图


二分类

目前工业界最能产生价值的是机器学习中的监督学习, 场景有推荐系统,反欺诈系统等等。其中二分类算法应用的尤其之多。



例如上面的图像识别场景, 我们希望判断图片中是否有猫,于是我们为模型输入一张图片,得出一个预测值 y。 y 的取值为 1 或者 0. 0 代表是,1 代表否。

逻辑回归模型

逻辑回归是一个用于二分类(binary classification)的算法。



逻辑回归的 Hypothesis Function(假设函数)



sigmoid 函数



逻辑回归的输出函数



激活函数


也叫激励函数,是为了能够给神经网络加入一些非线性因素,使得神经网络可以更好地解决较为复杂的问题。


常见的激活函数


Sigmoid 函数


tanh 函数


ReLU 函数



损失函数(Lost Function)



代价函数(Cost Function)


为了训练逻辑回归模型的参数参数𝑤和参数𝑏,我们需要一个代价函数,通过训练代价函数来得到参数𝑤和参数𝑏。


代价函数也叫做成本函数,也有人称其为全部损失函数。


损失函数是在单个训练样本中定义的,它衡量的是算法在单个训练样本中表现如何,为了衡量算法在全部训练样本上的表现如何,我们需要定义一个算法的代价函数。


算法的代价函数是对个 m 样本的损失函数求和,然后除以 m:



损失函数只适用于像这样的单个训练样本,而代价函数是参数的总代价,所以在训练逻辑回归模型时候,我们需要找到合适的和,来让代价函数 的总代价降到最低。


代价函数的解释



由上述推论可得出这两个条件概率公式:



以下类似手写截图均来自吴恩达老师的深度学习课程


可将上述公式,合并成如下公式:



解释合并过程




推导到 m 个训练样本的整体训练的代价函数


计算图

计算图是什么


一个神经网络的计算,都是按照前向或反向传播过程组织的。



首先我们计算出一个新的网络的输出(前向过程),紧接着进行一个反向传输操作。后者我们用来计算出对应的梯度或导数。


计算图解释了为什么我们用这种方式组织这些计算过程。


举例说明



概括一下:计算图组织计算的形式是用蓝色箭头从左到右的计算


使用计算图求导数


进行反向红色箭头(也就是从右到左)的导数计算



在反向传播算法中的术语,我们看到,如果你想计算最后输出变量的导数,使用你最关心的变量对𝑣的导数,那么我们就做完了一步反向传播。


当计算所有这些导数时,最有效率的办法是从右到左计算,跟着这个红色箭头走。


所以这个计算流程图,就是正向或者说从左到右的计算来计算成本函数 J(你可能需要优化的函数)。然后反向从右到左是计算导数。

梯度下降

梯度下降法 (Gradient Descent)


梯度下降法可以做什么?


在你测试集上,通过最小化代价函数(成本函数)𝐽(𝑤, 𝑏)来训练的参数𝑤和𝑏



梯度下降法的形象化说明



在这个图中,横轴表示你的空间参数𝑤和𝑏,在实践中,𝑤可以是更高的维度,但是为了 更好地绘图,我们定义𝑤和𝑏,都是单一实数,代价函数(成本函数)𝐽(𝑤, 𝑏)是在水平轴𝑤和 𝑏上的曲面,因此曲面的高度就是𝐽(𝑤, 𝑏)在某一点的函数值。我们所做的就是找到使得代价 函数(成本函数)𝐽(𝑤, 𝑏)函数值是最小值,对应的参数𝑤和𝑏。


以如图的小红点的坐标来初始化参数𝑤和𝑏。



选出最陡的下坡方向,并走一步,且不断地迭代下去。


朝最陡的下坡方向走一步,如图,走到了如图中第二个小红点处。



可能停在这里也有可能继续朝最陡的下坡方向再走一步,如图,经过两次迭代走到 第三个小红点处。



直到走到全局最优解或者接近全局最优解的地方。



通过以上的三个步骤我们可以找到全局最优解,也就是代价函数(成本函数)𝐽(𝑤, 𝑏)这个凸函数的最小值点。


逻辑回归的梯度下降



反向传播计算 𝑤 和 𝑏 变化对代价函数 𝐿 的影响



单个样本的梯度下降算法



网络向量化


我们已经了解了单个训练样本的损失函数,当使用编程进行计算多个训练样本的代价函数时,一般都会想到使用 loop 循环来计算。


但是在数学科学领域,训练一个模型可能需要非常大量的训练样本, 即大数据集,使用 loop 循环遍历的方式是十分低效的。


线性代数的知识起到了作用,我们可以通过将循环遍历的样本数据,进行向量化,更直接的、更高效的计算,提高代码速度,减少等待时间,达到我们的期望。


注意:虽然有时写循环(loop)是不可避免的,但是我们可以使用比如 numpy 的内置函数或者其他办法去计算。



神经网络实现步骤


在深度学习中,我们一般都会使用第三方框架,如 TensorFlow, Keras,训练过程对我们来说是不可见的。


上面讲了很多的函数、以及其概念、作用,这些神经网络的”组件“,或者说,”组成部分“,如何串联起来呢?以及为什么一开始就会提到 ”前向传播“ 和 ”后向传播“ 这两个概念?


在一个简单神经网络的实现中,简化分为几步:


1、引入外部包


2、加载数据集


3、定义计算图


4、初始化参数


5、一次迭代的前向传播:执行计算图,一层层计算,直到计算到输出层,得到预估值、准确率、损失函数


6、一次迭代的反向传播:对上次得到损失函数求导,使用优化函数(梯度下降算法)从右向左对神经网络的每一层计算调参,直到计算到第一个隐含层,得到优化更新的参数


7、重复(5)(6)为一个迭代,记录多个迭代的信息,训练完毕的模型的最终损失和准确率。


8、使用(7)中得到的训练过程数据,绘制训练集、测试集关于迭代次数与损失的关系曲线图以及迭代次数与准确率关系的曲线图,进行分析,以调整超参数。

第二章逻辑回归(Logistic Regression)实现猫咪识别器

导入所需的 package


import numpy as npimport matplotlib.pyplot as pltimport h5pyfrom PIL import Imagefrom loadH5File import load_data
%matplotlib inline
复制代码


加载数据集 (cat / non-cat)


train_set_x, train_set_y, test_set_x, test_set_y, classes = load_data()
复制代码


train_x (209, 64, 64, 3) test_x (50, 64, 64, 3)train_y (1, 209) test_y (1, 50)
复制代码

查阅训练集中的一张图片

一张正确分类的图片


index = 7plt.imshow(train_set_x[index])print ("y = " + str(train_set_y[:,index]) + ", it's a '" + classes[np.squeeze(train_set_y[:,index])].decode("utf-8") +  "' picture.")
复制代码


y = [1], it's a 'cat' picture.
复制代码



一张错误分类的图片


index = 10plt.imshow(train_set_x[index])print ("y = " + str(train_set_y[:,index]) + ", it's a '" + classes[np.squeeze(train_set_y[:,index])].decode("utf-8") +  "' picture.")
复制代码


y = [0], it's a 'non-cat' picture.png
复制代码


猫咪图片数据处理过程示意


实现输入数据扁平变形和标准化(也叫归一化)


将训练集和测试集中的 x 维度切分为(num_px * num_px * 3, m)的形式


reshape 目的:实现矩阵乘法计算


  • x 维度切分为(num_px * num_px * 3, m)的形式。

  • y 维度切分为(1, m)的形式。


标准化目的:预处理的数据被限定在一定的范围内(比如[0,1]或者[-1,1]),从而消除奇异样本数据导致的不良影响。


num_px = train_set_x.shape[1]m_train = train_set_x.shape[0]m_test = test_set_x.shape[0]
# 重塑输入数据train_x_flatten = train_set_x.reshape(train_set_x.shape[0], -1).Ttest_x_flatten = test_set_x.reshape(test_set_x.shape[0], -1).T
print('train_x', train_x_flatten.shape, 'test_x', test_x_flatten.shape) print('train_y', train_set_y.shape, 'test_y', test_set_y.shape)
# 标准化输入数据train_set_x = train_x_flatten/255test_set_x = test_x_flatten/255
复制代码


train_x (12288, 209) test_x (12288, 50)train_y (1, 209) test_y (1, 50)
复制代码


实现激活函数



def sigmoid(z):    '''    计算 z 的非线性输出值
Arguments: z -- 一个标量或者一个 numpy array
Return: s -- sigmoid(z) ''' s = 1 / (1 + np.exp(-z))
return s
复制代码

实现初始化参数函数

如图小红点的坐标来初始化参数𝑤和𝑏。



def initialize_with_zeros(dim):    """    将 w 构造为一个 shape 为 (dim, 1) 的零值向量,将 b 设置为 0
Argument: dim -- 行向量 w 的大小
Returns: w -- 初始化后的行向量, shape 为 (dim, 1) b -- 初始化后的标量 (表示偏差) """
w = np.zeros(shape=(dim, 1)) b = 0
assert(w.shape == (dim, 1)) assert(isinstance(b, float) or isinstance(b, int))
return w, b
复制代码


实现代价函数(前向传播)和梯度下降函数(反向传播)


前向传播



反向传播



def propagate(w, b, X, Y):    '''    实现前向传播和反向传播
Arguments: w -- 权重, 一个行向量 (num_px * num_px * 3, 1) b -- 偏差, 一个标量 X -- 标准化后的 X 矩阵 (num_px * num_px * 3, number of examples)
Return: cost -- 逻辑回归的成本函数(负值) dw -- w 的梯度下降,shape 同 w db -- b 的梯度下降,shape 同 b
''' m = X.shape[1]
# 前向传播 A = sigmoid(np.dot(w.T, X) + b) cost = -1 / m * np.sum(Y * np.log(A) + (1-Y) * np.log(1-A))
# 反向传播 dw = 1 / m * np.dot(X, (A-Y).T) db = 1 / m * np.sum(A-Y)
assert(dw.shape == w.shape) assert(db.dtype == float) cost = np.squeeze(cost) assert(cost.shape == ())
grads = {"dw": dw, "db": db}
return grads, cost
复制代码

实现调参函数

以梯度下降算法不断更新参数,使其达到更优。



def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):    '''    运行梯度下降算法,优化调整 w, b 参数
Arguments: w -- 权重, 一个行向量 (num_px * num_px * 3, 1) b -- 偏差, 一个标量 X -- 标准化后的 X 矩阵,shape 为 (num_px * num_px * 3, number of examples) Y -- 一个由 0, 1 组成(其中 0 表示 non-cat, 1 表示 cat)的列向量, shape 为 (1, number of examples) num_iterations -- 迭代次数/优化更新次数 learning_rate -- 梯度下降的学习率/更新步长 print_cost -- 是否打印损失,默认不打印
Returns: params -- 一个包含了权重 w 和 偏差 b 的字典 grads -- 一个包含了权重 w 和 偏差 b 的梯度的字典 costs -- 一个保留了所有更新计算过的损失值的列表,用于绘制学习曲线 '''
costs = []
for i in range(num_iterations):
# 调用 propagate 函数计算 w, b 的梯度以及损失 grads, cost = propagate(w, b, X, Y)
dw = grads["dw"] db = grads["db"]
# 更新 w, b,优化调整参数 w = w - learning_rate * dw b = b - learning_rate * db
# 保存当次更新的损失 if i % 100 == 0: costs.append(cost)
# 每训练100次,打印一次损失 if print_cost and i % 100 == 0: print("Cost after iteration %i: %f" % (i, cost))
params = {"w": w, "b": b}
grads = {"dw": dw, "db": db}
return params, grads, costs
复制代码


2019-09-27 13:041042

评论

发布
暂无评论
发现更多内容

还不会配置Python虚拟环境?看这篇就懂了

芥末拌个饭吧

后端 配置管理 python 3.5+ 10月月更

Linux下编写ENC28J60网卡驱动,完善网络设备框架

DS小龙哥

10月月更

Apache RocketMQ 在阿里云大规模商业化实践之路

阿里巴巴云原生

Apache 阿里云 云原生 消息队列 商业化

用Java实现计算器功能

魏铁锤

10月月更

WeChat Moments Performance Analysis

David

架构实战营

ReentrantLock可重入、可打断、锁超时实现原理

JAVA旭阳

Java 并发 10月月更

朋友圈高性能复杂度分析

许四多

Ubuntu Server 20.04 LTS下载及安装教程

Yeats_Liao

ubuntu 后端 10月月更

PUTTY-0.75 下载安装及SSH远程连接方法

Yeats_Liao

后端 putty 10月月更

【c++图论例题学习】洛谷 P3366最小生成树

贤鱼很忙

10月月更

常见大数据应用场景

穿过生命散发芬芳

大数据 10月月更

搭建Linux下QT开发环境与Android开发环境(嵌入式与PC机)

DS小龙哥

10月月更

JavaSE 抽象类和接口

whispar

接口 Java应用 10月月更

协同开发时,如何科学的使用Git紧急修复线上bug?

王中阳Go

git 敏捷开发 团队协作 10月月更 git使用规范

Go语言开发小技巧&易错点100例(一)

闫同学

10月月更

如何在 Linux 上重新启动网络接口?

wljslmz

Linux 网络 网络接口 10月月更

Vue组件入门(六)派发事件的参数传递

Augus

Vue 3 10月月更

Go做Web开发,有哪些必懂的概念和底层原理呢?

王中阳Go

Java php Go Web 10月月更

图解ReentrantLock的条件变量Condition机制

JAVA旭阳

Java 并发 10月月更

行业实践:RocketMQ 业务集成典型行业应用和实践

阿里巴巴云原生

阿里云 RocketMQ 云原生

用栈操作构建数组

掘金安东尼

算法 10月月更

Spring Boot「06」Loading initial data

Samson

Java spring 学习笔记 spring-boot 10月月更

【愚公系列】2022年10月 Go教学课程 029-面向对象简介

愚公搬代码

10月月更

百度面试题——迷宫问题(超详细解析)

lovevivi

c 数据结构 10月月更

适用于 Linux 的 Windows 子系统(WSL)安装指南

Yeats_Liao

后端 wsl 10月月更

super关键字的作用

魏铁锤

10月月更

JWT详解

Java学术趴

10月月更

如何提高项目的稳定性和开发效率呢?

王中阳Go

数据库 算法 框架 函数 10月月更

网络协议与传输媒体

急需上岸的小谢

10月月更

Kafka的客户端NetworkClient如何发起的请求

石臻臻的杂货铺

Kafk 10月月更

「数仓建设篇」数仓主题域与主题划分实战

大数据阶梯之路

面试 数仓主题 数仓建设

与 DeepLearning 的初次接触(上)_AI&大模型_江伯_InfoQ精选文章