1.深度学习与神经网络基础

99封情书 提交于 2019-12-03 18:51:41

*总结涵盖吴恩达老师神经网络和深度学习课程第一周和第二周的内容


提纲

1、深度学习概要

2、神经网络基础

3、课程中编程技巧总结


 

1、深度学习概要

在这一部分,我希望结合视频内容对“深度学习”和“神经网络”两个词形成自己的表述。视频课程里的说法是“深度学习”是用来训练“神经网络”的一种算法,首先,什么是“神经网络”,其次,“训练”是个很抽象的词,我们会说训练人或训练动物,但对于一个非生物的“神经网络”,什么叫“训练”,怎么“训练”?

1.1 神经网络

为说明这个概念,给出两个预测房价的例子。

(1)例1 : 假设有现成的6组数据,每组数据对应一个待售的房屋,含面积(size)和售价(price)两个量,现在希望利用这现成的6组数据,给出现有数据外的一个房屋,仅根据房屋的面积预测房屋的售价。

思路:

建立一个二维坐标轴,横轴代表房屋面积,纵轴代表房屋售价,根据线性回归的知识,我们可以拟合出一条直线

 ,其中 k和 b的值可以根据现成的6组数据求出,同时考虑到实际房屋售价不可能为负,将负值部分用一条与横轴重合的水平直线代替,示意图如下。

这样,我们在房屋面积和售价之间建立了一对一的映射,或者说我们得到了一个以面积为自变量,售价为因变量的函数,给出面积,就能映射出售价。如果我们仅仅关心输入与输出,相当于存在一个黑箱,能将输入的房屋面积转化为输出的房屋售价,这一充当转换器功能的黑箱就是一个“神经元”,或者一个最简单的神经网络。示意图如下,由此我们很容易想到,当输入量的种类增多,我们可以通过增加“神经元”的数量建立起更复杂的神经网络,见第二个例子。

(2)例2 : 实际情况中,决定房屋售价的不仅仅是房屋面积,现在我们假设房屋售价由房屋面积(size)、床铺数量(bedrooms)、所在地邮编(zip code)和所在地富裕情况(wealth)四个输入量决定,试建立由四个输入量到房屋售价输出的神经网络结构。

思路:

最简单粗暴的方法是依旧采用一个神经元,让四个输入同时作用在上面,最终输出售价,但我们发现在建立神经网络前,可以对这四个输入量进行分类。size和bedrooms两个量均在衡量房屋可容纳家庭的大小(family size),zip code一定程度上反映了房屋离学校的距离(walkability),而zip code和wealth两个量又一定程度上反映了附近学校的质量(school quality),进一步,family size、walkability和school quality这三个量决定了房屋的售价,据此,我们建立起如下结构。

可以看到,神经网络规模的增大不仅仅是神经元数量的增加,还有结构层次的增加,当输入的四个输入量作用在最外层的三个神经元后,我们会首先得到三个中间量,接着,三个中间量作用在另一个神经元上,输出最终的结果房屋售价(price)。

同理,如果我们仅仅关心输入与输出,可以将中间所有层次的神经元看成一个黑箱,能将四个输入转化为输出的房屋售价。

(3)结论:

输入信息作用在一层一层神经元上,最终映射出输出,我们基于这样一个与人大脑内信息在神经元间传递的方式类似的过程,设计的一种算法就是“神经网络”。

1.2 深度学习

视频课程里的说法是深度学习是用来“训练”神经网络的一种算法,下面解释“训练”。

采用上述“黑箱”的思路,当神经网络建立完毕后,我们不再关注内部的细节,只考虑从最外层的输入到最终输出的整体的映射,相当于输出是以所有最外层输入量为自变量的一个函数,而这个函数的形式与参数的选取直接影响神经网络的质量,比如第一个例子中根据现成的6组数据拟合的RcLU曲线倾斜部分的斜率和截距的不同取值直接影响到房屋售价的预测值与真实值的差距。

在这种情况下,为了尽可能缩小预测值与真实值的差距,我们利用深度学习算法来“训练”神经网络。所谓“训练”,就是利用现有的数据(训练集)找到函数的最佳参数的过程,其结果是,在根据“训练”选取的参数下,神经网络在应对未知数据(测试集)时,预测值能尽可能地接近真实值。

1.3 建立步骤

根据课程内容与个人理解,针对cost函数只有一个局部最小值的情形,总结基于梯度下降法更新参数的神经网络建立步骤如下:

(1)确定形式:根据现有数据(训练集)选定用于拟合的函数形式(如针对单输入单输出的房价预估选取线性回归函数

),之后利用选定的函数加上训练集的数据建立一个反映整体误差情况的cost函数。最后对参数进行初始化,便得到了“训练”前的神经网络。

(2)正向传播:所谓正向传播,是指根据样本数据计算出cost函数值的过程。

(3)反向传播:由于梯度下架法需要cost函数对于各输入量的偏导数,所谓反向传播,就是从cost函数计算出其关于各样本信息偏导数的过程。

(4)迭代更新:根据梯度下降法,如果cost函数仅存在一个局部最小值,合理选定学习率α ,通过不断执行上述步骤,利用 

更新参数,在这一过程中,cost函数的取值会逐渐逼近该局部最小值。由于实际情况下,计算机无法处理无穷次迭代的问题,通过合理指定迭代次数,让cost函数尽可能逼近局部最小值,选取此时的函数参数,从而得到“训练”后的神经网络。

 

2、神经网络基础

2.1 logistic回归的适用对象

logistic回归是用于二分分类的回归算法,所谓二分分类,就是我们需要预测的结果只存在两种结果,0或1、真或假、是或否,比如判断一个物体是不是苹果,我们的答案只可能存在与“是”和“否”中间,此外不存在第三个答案。而像上述预估房价的例子,房屋的售价根据房屋情况的不同各异,不是一个二分分类问题,也就不能用logistic回归算法进行处理。由于二分分类问题的结果情况少,较为简单,以此作为切入点。

2.2 logistic回归的算法思路

以一道例题结合上述总结的神经网络建立步骤对logistic回归算法进行讲解:现给定 m张图片,利用这些图片设计一个神经网络,用于判断任意一张图片上画的是否是猫,其中每张图片尺寸相同(均为16×16像素,每个像素点含红、蓝、绿三色)。

2.2.1确定形式

(1)确定拟合函数形式:

神经网络的输入为图片,输出为判断结果(是猫/不是猫),因此是一个最简单的单神经元神经网络,仿照第一个预测房价的例子,选用最简单的线性回归函数

,其中

是输入, 

是输出, i 表述 m张图片中的第 i 张 ,w 和 b 是后续需要通过“训练”选定的参数。根据图片的像素情况,每张图片可看作 

的矩阵( 

),因此输入

与参数w 均是 

的矩阵, 最终的结果  

  是一个1×1的矩阵,或者看作一个实数。

由于实际情况,图片的情况只有是猫(标签值为1)和不是猫(标签值为0),我们的预估值只能介于0和1之间,而线性回归函数

 的值明显可能超出这一范围,因此在线性回归的基础上,引入函数 

,令 

,这样,

表示输出预测值,即对于输入 

 ,该图片是猫的概率,这样,预测结果被有效地限制在0和1之间,sigmoid()函数的图像见下图。

(2)Loss函数:

Loss函数用于刻画单个样本的预测值与真值的差距,通常可用 

或 

来衡量,但由于预测值可能围绕真实值上下波动,根据这样的Loss函数后续得到的cost函数可能有多个局部最小值,从而无法运用梯度下降法进行更新参数。所以,我们采用如下的函数形式:

这样,函数只有一个局部最小值,可以运用梯度下降法更新参数。

(3)cost函数:
cost函数用于刻画整体样本的预测值与真值的差距,我们将m各样本对应的Loss函数求和再取算数平均得到cost函数,由于 

是现成训练集里的已知值, 

是以w和b为自变量的函数,由此得到的cost函数同样以w和b为自变量,其表达式如下:

2.2.2 正向传播

对于样本

,首先算出 

,然后利用

算出预测值。接着,根据Loss函数计算单个样本的误差情况,即

,由于样本总数为m,该步骤需要重复进行m次。最后,将m次运算结果求和取算数平均 

 ,得到cost函数,自此,正向传播过程结束。

2.2.3 反向传播

反向传播过程需要得到cost函数对各待定参数的偏导数,本例中,参数有 w 和 b ,其中 w 是一个 

的矩阵,b是一个1×1的矩阵或一个实数,因此我们需要求取

个元素的偏导数(本例中 

)。

设 

是矩阵 

中的第

个元素,由链式求导法则可以求得

,由于矩阵 w 中有 

 个元素,该步骤需重复 

次。接着,根据相同的原理求对b的偏导数得

2.2.4 迭代更新

该步骤利用梯度下降更新函数参数w和b,依据公式

,其中 

为学习率,需要人为选取,若取值过小,会增加收敛到指定点的迭代次数,降低逼近速度,若取值过大,会产生超调,甚至发散。

在学习率选定后,需要对参数w和b进行更新。同样,由于矩阵

中有 

个元素,加上参数 b ,每次迭代总共需要对 

个参数进行更新,而迭代次数需要人为指定。理论上讲,迭代次数越多越好,但迭代次数的增加也同时意味着“训练”的耗时增加,此外,由于函数在逼近局部最小值的过程中,斜率最终会趋近于零,逼近速度会随之降低,因此当迭代次数大到一定程度后,对最终“训练”结果的精度提高不会有太大影响,所以,应平衡好迭代的次数和算法的耗时。

2.3 算法实现

对应于上述思路,实现算法如下:

#针对迭代次数iteration,循环相应次数

for k in range(iteration)

#遍历m个样本

for i in range(m)

#正向传播

#反向传播

#遍历矩阵  

中的 

 个元素

for j in range( 

)

#遍历矩阵

 中的 

个元素

for j in range( 

 )

#更新参数

#遍历矩阵

 中的 

个元素

for j in range( 

 )

2.4 算法优化

通过向量化优化算法,具体来说,利用numpy库中内置的矩阵运算工具尽可能多地代替原算法中的for循环语句,从而减少耗时,达到优化的目的。

(1)为达到这一目的,我们建立几个新的矩阵。

(2)利用新建立的矩阵,对算法中的函数进行相应调整。

 

(3)对算法进行调整,发现除了最外层迭代次数的for循环无法去掉,其余的均可通过向量化去掉,如下所示。

#针对迭代次数iteration,循环相应次数

for k in range(iteration)

#正向传播

#反向传播

#更新参数

2.5 代码呈现

#载入库
import numpy as np
import matplotlib.pyplot as plt
import h5py
import scipy
from PIL import Image
from scipy import ndimage
from lr_utils import load_dataset
%matplotlib inline

# 载入数据
train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()

# 数据预处理
train_set_x = (train_set_x_orig.reshape(train_set_x_orig[0],-1).T) / 255
test_set_x = (test_set_x_orig.reshape(test_set_x_orig[0],-1).T) / 255

# 函数主体
def Mainbody(train_set_x, train_set_y, test_set_x, test_y, iteration, learning_rate, print_cost = False):
	# 初始化参数
	w,b = Initial(train_set_x.shape[1])
	# 正、反向传播与参数迭代更新
	costs,paras,grads = Optimalize(train_set_x, train_set_y, iteration, learning rate, print_cost = False, w, b)
	w = paras["w"]
	b = paras["b"]
	# 预测数据
	predict_train_y = Predict(w, b, train_set_x)
	predict_test_y = Predict(w, b, test_set_x) 
	# 显示结果
	print("Training set accuracy = {} %".format(100*(1 - np.mean(np.abs(predict_train_y - train_set_y)))))
	print("Testing set accuracy = {} %".format(100*(1 - np.mean(np.abs(predict_test_y - test_set_y)))))
	result = {"predict_train_y" : predict_train_y,
		  "predict_test_y" : predict_test_y,
		  "costs" : costs,
		  "w" : w,
		  "b" : b,
		  "iteration" : iteration,
		  "learning_rate" : learnign_rate}
	return result

# 调用子函数
def Sigmoid(z):       #Sigmoid函数
	z = 1 / (1 + np.exp(-z))
	return z

def Initial(dim):     #参数初始化
	w = np.zeros(dim, 1)
	b = 0
	assert(w.shape == (dim, 1))
	assert(isinstance(b, float) or isinstance(b, int))
	return w,b

def Optimalize(X, Y, iteration, learning rate, print_cost = False, w, b):   #正、反向传播与参数迭代更新
	costs = []
	m = X.shape[1]	
	for i in range(iteration)
		# 前向传播
		A = Sigmoid(np.dot(w.T,train_set_x))
		cost = np.sum(Y * np.log(A) + (1 - Y) * np.log(1 - A)) / (-m)
		# 反向传播
		dw = X * (A - Y).T / m
		db = np.sum(A - Y) / m
		# 参数更新
		w = w - learning_rate * dw
		b = b - learnign_rate * db
		if i % 100 == 0:
			costs = costs.append(cost)
		if i % 100 == 0 and print_cost:
			print("After %i iteration cost is %f" %(i, cost))
	paras = {"w" : w,
		 "b" : b}
	grads = {"dw" : dw,
		 "db" : db}
	return costs,paras,grads

def Predict(w, b, X):  #预测数据
	prediction_y = np.zeros(1,X.shape[1])
	A = Sigmoid(np.dot(w.T,X) + b)
	for i in range(X.shape[1])
		if A[0][i] < 0.5 or A[0][i] == 0.5:
			prediction_y[0][i] = 0
		else:
			prediction_y[0][i] = 1
	assert(prediction_y.shape == (1, X.shape[1]))
	return prediction_y

# 代码测试部分
Mainbody(train_set_x, train_set_y, test_set_x, test_y, iteration = 2000, learning_rate = 0.005, print_cost = True)

3、课程中编程技巧总结

(1)对于矩阵 

,求 

,可以利用numpy中的现成函数np.dot :

(2)把一个shape为 (a,b,c,d) 的矩阵X 转化为shape为(b×c×d, a) 的矩阵 X_flatten :

X_flatten = X.reshape(X.shape[0], -1).T。

(3)使用assert()函数,让程序在在出现错误条件时就崩溃,而非在运行时崩溃。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!