吴恩达深度学习第二课第二周 优化方法

1.mini-batch梯度下降

假如我们有一个长度为500w的数据集,这时候我们如果要做梯度下降,每下降一次需要对500w长度的向量进行计算。

这时候我们引入mini-batch算法,假设我们将每批设为1000个长度,这时候我们可以将整个数据分成5000份(结果集同理),命名规则如下

然后使用for循环,对每1000个数据进行一次梯度下降,这样我们500w条数据可以进行5000次下降,提高了收敛速度。

2.理解mini-batch梯度下降算法

如果我们在batch梯度下降法中遇到了代价上升的问题,说明我们的学习率过大。

而如果我们使用mini-batch算法,这却是正常的。

在mini-batch中,不是每次迭代代价都会下降,因为每次迭代中处理的都是不同的样本,因此十分可能我们最终得到的代价曲线是一个如下图的样子

但是我们需要注意,虽然有起伏是无所谓的,但是整体的走势应该是向下的。

噪声的产生是因为可能当前的parameters对某一批的拟合程度比较好或者当前批的数据有一些残缺值等,因此代价会起伏变化。

你需要决定的变量之一是mini-batch大小,m就是训练集的大小,在极端的情况下,mini-batch大小等于m,那么就退化成了batch梯度下降法;

另一种极端是mini-batch的大小为1,这时候就成了另一种算法——随机梯度下降法,既每一个样本都是mini-batch。

对于batch梯度下降法和随机梯度下降法,有如下图

我们看到,随机梯度下降法并不会一直向着最好的方向前进,但是总体趋势是下降的,并且最后不会收敛到某一个具体值,而是在某一值附近浮动,而mini-batch虽然后浮动但是波动不大,总体上较快的逼近收敛值。

如果训练集较小则可以直接使用batch梯度下降法(一般指2000以内)。

大小一般为2的n次方倍。一方面,充分利用的GPU的并行性,另一方面,不会让计算时间特别长

3.指数加权平均数

我们假定全年的温度数据如下

我们使用前一天的温度*0.9+今天的温度*0.1,然后就可以画出下面的曲线

我们将上面的式子推广到一般情况

我们发现上面我们取0.9的时候,实际上是今天的天气权重只有0.1,之前的天气权重为0.9,这样相当于计算了10天的平均气温。而我们取0.98的时候,这时候计算的就是50天的平均气温了,曲线如下

由于仅平均了两天的温度,平均数据太少,所以得到的曲线有更多的噪声,有可能出现异常值,但是这个曲线能够更快的适应温度的变化。

4.理解指数加权平均数

上一节我们已经知道了计算指数加权平均数的关键方程:

我们将v100展开:

我们可以看到我们实际上算的并不是精确的平均数,只不过由于权重的变化,对于很久以前的天数权重很低,导致对v100的影响微不足道。

那么为什么要使用这种不怎么精确的求 平均值的办法呢 ?

那是因为这个公式只有一行,只需要不断的迭代而不需要像之前求平均值,求和再除,浪费了过多的计算机内存。而产生的误差在下一节中也会有适当的误差修正。

5.指数加权平均的偏差修正

这一节学习一个技术名词叫偏差修正,可以让平均数运行更加准确。

由于我们初始化v0=0,因此当我们计算v1的时候,v1=0.98v0+0.02t = 0.02t,这样v1的温度就会不正常的低。这样在前期我们得到的平均数曲线很容易是下图紫色的

那为了修正这个误差我们修改一下公式,将等式右边除(1-theta的t次方)

这样我们计算的时候,在t较小的时候除项很小,会放大结果,vt依然会保持一个较高的水平(实际上是v0和v1的加权平均数),在t较大的时候分母又会趋近于1,和绿线基本拟合。

不过 在机器学习中,在计算指数加权平均数的大部分时候,大家不在乎执行偏差修正,因为大部分人宁愿熬过初始时期,拿到具有偏差的估测,然后继续计算下去。

如果你关心初始时期的偏差,在刚开始计算指数加权移动平均数的时候,偏差修正能帮助你在早期获取更好的估测。

6.动量梯度下降算法

还有一种算法叫Momentum,或者叫动量梯度下降法,运行速度几乎总是快于标准的梯度下降算法。

动量梯度下降算法实际上就是将加权平均数的思想用到了梯度下降上。

正常的梯度下降就像下面这幅图,总是慢慢的向最优点靠近,增大学习率会让曲线像紫色一样波动的更大

我们引入一个机制,类似于引入一个摩擦力机制,公式如下

实际上就是将之前的参数更新方法w=w-a*dw变成w=w-a*( v = theta*v+(1-theta)*dw ),经验表明,theta取0.9是一个很好的鲁棒数,并且在很多时候1-theta项是被删除的,也就是v = theta*v+dw。

7.RMDprop

RMSprop算法,全称 root mean square prop算法,它也可以加速梯度下降。

为了减缓梯度下降过程中的摆动,我们引入新的更新参数的方式。

通过这种方式,我们就可以将下降曲线变成接近绿色的线。

8.Adam优化算法

Adam优化算法基本上就是将Momentum和RMSprop结合在一起。

首先计算

这里的theta1就是之前在Momentum提到过了,缺省值0.9,然后计算

这里的theta2就是之前在RMSprop中使用到的theta,缺省值0.99

然后我们要进行修正(一般这在Adam优化算法中要计算)

最后更新权重其中 Epsilon是一个很小的数,一般为10的-8次方。

Adam算法结合了Momentum和RMSprop梯度下降法,并且是一种极其常用的学习算法,被证明能有效适用于不同神经网络,适用于广泛的结构。

我们一般并不修改theta1和theta2以及Epsilon的值,只需要修改学习率即可。

9.学习率衰减

加快学习算法的一个办法就是随时间慢慢减少学习率,我们称其为学习率衰减。

我们将学习率重新定义为

其中 (decay-rate称为衰减率,epoch-num为代数,alpha为初始学习率),注意,decay-rate是另一个超参。

学习率衰减并不是尝试的要点,设定一个固定的,然后好好调整网络会有很好的效果,学习率衰减的确大有裨益,有时候可以加快训练,但它并不是要率先尝试的内容。

测验

1. 当输入从第八个mini-batch的第七个的例子的时候,你会用哪种符号表示第三层的激活?

[i]{j}(k)上标表示 第i层,第j小块,第k个示例

2. 关于mini-batch的说法哪个是正确的?

  1. 在不同的mini-batch下,不需要显式地进行循环,就可以实现mini-batch梯度下降,从而使算法同时处理所有的数据(矢量化)。
  2. 使用mini-batch梯度下降训练的时间(一次训练完整个训练集)比使用梯度下降训练的时间要快。
  3. mini-batch梯度下降(在单个mini-batch上计算)的一次迭代快于梯度下降的迭代。

3

3 . 为什么最好的mini-batch的大小通常不是1也不是m,而是介于两者之间?

  1. 如果mini-batch大小为1,则会失去mini-batch示例中矢量化带来的的好处。
  2. 如果mini-batch的大小是m,那么你会得到批量梯度下降,这需要在进行训练之前对整个训练集进行处理。

1,2

4. 如果你的模型的成本J随着迭代次数的增加,绘制出来的图如下,那么

img1
  1. 如果你使用的是mini-batch梯度下降,这看起来是可以接受的。但是如果你使用的是下降,那么你的模型就有问题

1

5. 假设一月的前三天卡萨布兰卡的气温是一样的:

假设您使用β= 0.5的指数加权平均来跟踪温度:v0 = 0,vt =βvt−1 +(1-βθt。 如果v2是在没有偏差修正的情况下计算第2天后的值,并且

是您使用偏差修正计算的值。 这些下面的值是正确的是?

6. 下面哪一个不是比较好的学习率衰减方法?

这会使得学习率出现爆炸,而没有衰减。

7. 在伦敦温度数据集上使用指数加权平均值, 您可以使用以下公式来追踪温度

下面的红线使用的是β= 0.9来计算的。 当你改变β时,你的红色曲线会怎样变化?

img 2
  1. 增加β会使红线稍微向右移动
  2. 减少β会在红线内产生更多的振荡

8. 看一下这个图 这些图是由梯度下降产生的; 具有动量梯度下降(β= 0.5)和动量梯度下降(β= 0.9)。 哪条曲线对应哪种算法?

img 3

(1)是梯度下降。 (2)是动量梯度下降(β值比较小)。 (3)是动量梯度下降(β比较大)

9. 假设在一个深度学习网络中批处理梯度下降花费了太多的时间来找到一个值的参数值,该值对于成本函数J(W[1],b[1],…,W[L],b[L])来说是很小的值。 以下哪些方法可以帮助找到J值较小的参数值?

  1. 尝试使用 Adam 算法
  2. 尝试对权重进行更好的随机初始化
  3. 尝试调整学习率α
  4. 尝试mini-batch梯度下降
  5. 试把权值初始化为0

1,2,3,4

10 关于Adam算法,下列哪一个陈述是错误的 ?

Adam应该用于批梯度计算,而不是用于mini-batch。

都可以使用

编程作业

我们首先导包

import numpy as np
import matplotlib.pyplot as plt
import math

from course_2_week_2 import opt_utils
from course_2_week_2 import testCases

看一下数据:

# 加载数据
train_X, train_Y = opt_utils.load_dataset()
plt.show()

生成mini-batch列表:

def random_mini_batches(X, Y, mini_batch_size=64, seed=0):
"""
创建随机batch列表
:param X:
:param Y:
:param mini_batch_size:
:param seed:
:return:
"""

np.random.seed(seed) # 指定随机种子
m = X.shape[1]
mini_batches = []

# 第一步:打乱顺序
permutation = list(np.random.permutation(m)) # 它会返回一个长度为m的随机数组,且里面的数是0到m-1
shuffled_X = X[:, permutation] # 将每一列的数据按permutation的顺序来重新排列。
shuffled_Y = Y[:, permutation].reshape((1, m))

# 第二步,分割
# 计算要分割多少份数据集
num_complete_minibatches = math.floor(m / mini_batch_size)
for k in range(0, num_complete_minibatches):
mini_batch_X = shuffled_X[:, k * mini_batch_size:(k + 1) * mini_batch_size]
mini_batch_Y = shuffled_Y[:, k * mini_batch_size:(k + 1) * mini_batch_size]

mini_batch = (mini_batch_X, mini_batch_Y)
mini_batches.append(mini_batch)

# 处理没法被batch大小处理的部分数据
if m % mini_batch_size != 0:
# 获取最后剩余的部分
mini_batch_X = shuffled_X[:, mini_batch_size * num_complete_minibatches:]
mini_batch_Y = shuffled_Y[:, mini_batch_size * num_complete_minibatches:]

mini_batch = (mini_batch_X, mini_batch_Y)
mini_batches.append(mini_batch)

return mini_batches

然后我们计算 梯度下降的参数更新:

def update_parameters_with_gd(parameters, grads, learning_rate):
    """
    更新数值
    :param parameters:
    :param grads:
    :param learning_rate:
    :return:
    """
    L = len(parameters) // 2  # 神经网络的层数
    # 更新每个参数
    for l in range(L):
        parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
        parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
    return parameters

首先我们先把整体模型先写出来(和之前一样,先不写实现):

def model(X, Y, layers_dims, optimizer, learning_rate=0.0007,
mini_batch_size=64, beta=0.9, beta1=0.9, beta2=0.999,
epsilon=1e-8, num_epochs=10000, print_cost=True, is_plot=True):
"""
定义神经网络模型
:param X:
:param Y:
:param layers_dims:
:param optimizer:
:param learning_rate:
:param mini_batch_size:
:param beta:
:param beta1:
:param beta2:
:param epsilon:
:param num_epochs:
:param print_cost:
:param is_plot:
:return:
"""
L = len(layers_dims)
costs = []
t = 0 # 每学习完一个minibatch就增加1
seed = 10 # 随机种子

# 初始化参数
parameters = opt_utils.initialize_parameters(layers_dims)

# 选择优化器
if optimizer == "gd":
pass # 不使用任何优化器,直接使用梯度下降法
elif optimizer == "momentum":
v = initialize_velocity(parameters) # 使用动量
elif optimizer == "adam":
v, s = initialize_adam(parameters) # 使用Adam优化
else:
print("optimizer参数错误,程序退出。")
exit(1)

# 开始学习
for i in range(num_epochs):
# 定义随机 minibatches,我们在每次遍历数据集之后增加种子以重新排列数据集,使每次数据的顺序都不同
seed = seed + 1
minibatches = random_mini_batches(X, Y, mini_batch_size, seed)

for minibatch in minibatches:
# 选择一个minibatch
(minibatch_X, minibatch_Y) = minibatch
# 前向传播
A3, cache = opt_utils.forward_propagation(minibatch_X, parameters)
# 计算误差
cost = opt_utils.compute_cost(A3, minibatch_Y)
# 反向传播
grads = opt_utils.backward_propagation(minibatch_X, minibatch_Y, cache)
# 更新参数
if optimizer == "gd":
parameters = update_parameters_with_gd(parameters, grads, learning_rate)
elif optimizer == "momentum":
parameters, v = update_parameters_with_momentun(parameters, grads, v, beta, learning_rate)
elif optimizer == "adam":
t = t + 1
parameters, v, s = update_parameters_with_adam(parameters, grads, v, s, t, learning_rate, beta1, beta2,
epsilon)
# 记录误差值
if i % 100 == 0:
costs.append(cost)
# 是否打印误差值
if print_cost and i % 1000 == 0:
print("第" + str(i) + "次遍历整个数据集,当前误差值:" + str(cost))
# 是否绘制曲线图
if is_plot:
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('epochs (per 100)')
plt.title("Learning rate = " + str(learning_rate))
plt.show()

return parameters

然后是比较,首先是不使用任何方法的:

# 使用普通的梯度下降
layers_dims = [train_X.shape[0], 5, 2, 1]
parameters = model(train_X, train_Y, layers_dims, optimizer="gd", is_plot=True)
# 预测
preditions = opt_utils.predict(train_X, train_Y, parameters)
# 绘制分类图
plt.title("Model with Momentum optimization")
axes = plt.gca()
axes.set_xlim([-1.5, 2.5])
axes.set_ylim([-1, 1.5])
opt_utils.plot_decision_boundary(lambda x: opt_utils.predict_dec(parameters, x.T), train_X, train_Y)

输出:

第0次遍历整个数据集,当前误差值:0.690735512291113
第1000次遍历整个数据集,当前误差值:0.6852725328458241
第2000次遍历整个数据集,当前误差值:0.6470722240719003
第3000次遍历整个数据集,当前误差值:0.6195245549970403
第4000次遍历整个数据集,当前误差值:0.5765844355950944
第5000次遍历整个数据集,当前误差值:0.6072426395968576
第6000次遍历整个数据集,当前误差值:0.5294033317684576
第7000次遍历整个数据集,当前误差值:0.46076823985930115
第8000次遍历整个数据集,当前误差值:0.465586082399045
第9000次遍历整个数据集,当前误差值:0.46451797221676844
Accuracy: 0.7966666666666666

代价图(由于是mini-batch,所以代价在变化,但是总体下降):

然后是使用动量更新参数(momentum算法),包括计算v和更新参数两部分:

首先初始化v

def initialize_velocity(parameters):
"""
初始化速度
:param parameters:
:return:
"""
L = len(parameters) // 2
v = {}

for l in range(L):
# zeros_like是初始化一个0矩阵,后台调用实际上还是zeros,只不过这里可以传矩阵而不是shape
v["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l + 1)])
v["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l + 1)])
return v

更新参数,先计算v再更新

def update_parameters_with_momentun(parameters, grads, v, beta, learning_rate):
"""
使用动量更新参数(momentum算法)
:param parameters:
:param grads:
:param v:
:param beta:
:param learning_rate:
:return:
"""
L = len(parameters) // 2
for l in range(L):
# 计算速度
v["dW" + str(l + 1)] = beta * v["dW" + str(l + 1)] + (1 - beta) * grads["dW" + str(l + 1)]
v["db" + str(l + 1)] = beta * v["db" + str(l + 1)] + (1 - beta) * grads["db" + str(l + 1)]

# 更新参数
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * v["dW" + str(l + 1)]
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * v["db" + str(l + 1)]

return parameters, v

然后我们训练测试一下并绘制分类模型:

#使用动量的梯度下降
layers_dims = [train_X.shape[0],5,2,1]
parameters = model(train_X, train_Y, layers_dims, beta=0.9,optimizer="momentum",is_plot=True)
#预测
preditions = opt_utils.predict(train_X,train_Y,parameters)

#绘制分类图
plt.title("Model with Momentum optimization")
axes = plt.gca()
axes.set_xlim([-1.5, 2.5])
axes.set_ylim([-1, 1.5])
opt_utils.plot_decision_boundary(lambda x: opt_utils.predict_dec(parameters, x.T), train_X, train_Y)

输出:

第0次遍历整个数据集,当前误差值:0.6907412988351506
第1000次遍历整个数据集,当前误差值:0.6853405261267578
第2000次遍历整个数据集,当前误差值:0.6471448370095255
第3000次遍历整个数据集,当前误差值:0.6195943032076022
第4000次遍历整个数据集,当前误差值:0.5766650344073023
第5000次遍历整个数据集,当前误差值:0.607323821900647
第6000次遍历整个数据集,当前误差值:0.5294761758786996
第7000次遍历整个数据集,当前误差值:0.46093619004872366
第8000次遍历整个数据集,当前误差值:0.465780093701272
第9000次遍历整个数据集,当前误差值:0.4647395967922748
Accuracy: 0.7966666666666666

最后就是测试Adam算法:

layers_dims = [train_X.shape[0], 5, 2, 1]
# 使用Adam优化的梯度下降
parameters = model(train_X, train_Y, layers_dims, optimizer="adam", is_plot=True)
# 预测
preditions = opt_utils.predict(train_X, train_Y, parameters)

# 绘制分类图
plt.title("Model with Adam optimization")
axes = plt.gca()
axes.set_xlim([-1.5, 2.5])
axes.set_ylim([-1, 1.5])
opt_utils.plot_decision_boundary(lambda x: opt_utils.predict_dec(parameters, x.T), train_X, train_Y)
第0次遍历整个数据集,当前误差值:0.6905522446113365
第1000次遍历整个数据集,当前误差值:0.18550136438550574
第2000次遍历整个数据集,当前误差值:0.150830465752532
第3000次遍历整个数据集,当前误差值:0.07445438570997183
第4000次遍历整个数据集,当前误差值:0.12595915651337164
第5000次遍历整个数据集,当前误差值:0.10434443534245487
第6000次遍历整个数据集,当前误差值:0.10067637504120643
第7000次遍历整个数据集,当前误差值:0.0316520301351156
第8000次遍历整个数据集,当前误差值:0.11197273131244204
第9000次遍历整个数据集,当前误差值:0.19794007152465481
Accuracy: 0.94

总结:

具有动量的梯度下降通常可以有很好的效果,但由于小的学习速率和简单的数据集所以它的影响几乎是轻微的。另一方面,Adam明显优于小批量梯度下降和具有动量的梯度下降,如果在这个简单的模型上运行更多时间的数据集,这三种方法都会产生非常好的结果,然而,我们已经看到Adam收敛得更快。

Adam的一些优点包括相对较低的内存要求(虽然比梯度下降和动量下降更高)和通常运作良好,即使对参数进行微调(除了学习率α
α)

完整代码:

# -*- coding:utf-8 -*-

"""
┏┛ ┻━━━━━┛ ┻┓
      
      
 ┳┛  ┗┳ 
      
      
      
┗━┓   ┏━━━┛
   神兽保佑
   代码无BUG
   ┗━━━━━━━━━┓
        ┣┓
     ┏┛
┗━┓ ┓ ┏━━━┳ ┓ ┏━┛
┃ ┫ ┫ ┃ ┫ ┫
┗━┻━┛ ┗━┻━┛
"""

import numpy as np
import matplotlib.pyplot as plt
import math

from course_2_week_2 import opt_utils
from course_2_week_2 import testCases

plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

# 加载数据
train_X, train_Y = opt_utils.load_dataset()
plt.show()


def update_parameters_with_gd(parameters, grads, learning_rate):
"""
更新数值
:param parameters:
:param grads:
:param learning_rate:
:return:
"""
L = len(parameters) // 2 # 神经网络的层数

# 更新每个参数
for l in range(L):
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]

return parameters


def random_mini_batches(X, Y, mini_batch_size=64, seed=0):
"""
创建随机batch列表
:param X:
:param Y:
:param mini_batch_size:
:param seed:
:return:
"""

np.random.seed(seed) # 指定随机种子
m = X.shape[1]
mini_batches = []

# 第一步:打乱顺序
permutation = list(np.random.permutation(m)) # 它会返回一个长度为m的随机数组,且里面的数是0到m-1
shuffled_X = X[:, permutation] # 将每一列的数据按permutation的顺序来重新排列。
shuffled_Y = Y[:, permutation].reshape((1, m))

# 第二步,分割
# 计算要分割多少份数据集
num_complete_minibatches = math.floor(m / mini_batch_size)
for k in range(0, num_complete_minibatches):
mini_batch_X = shuffled_X[:, k * mini_batch_size:(k + 1) * mini_batch_size]
mini_batch_Y = shuffled_Y[:, k * mini_batch_size:(k + 1) * mini_batch_size]

mini_batch = (mini_batch_X, mini_batch_Y)
mini_batches.append(mini_batch)

# 处理没法被batch大小处理的部分数据
if m % mini_batch_size != 0:
# 获取最后剩余的部分
mini_batch_X = shuffled_X[:, mini_batch_size * num_complete_minibatches:]
mini_batch_Y = shuffled_Y[:, mini_batch_size * num_complete_minibatches:]

mini_batch = (mini_batch_X, mini_batch_Y)
mini_batches.append(mini_batch)

return mini_batches


def initialize_velocity(parameters):
"""
初始化速度
:param parameters:
:return:
"""
L = len(parameters) // 2
v = {}

for l in range(L):
# zeros_like是初始化一个0矩阵,后台调用实际上还是zeros,只不过这里可以传矩阵而不是shape
v["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l + 1)])
v["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l + 1)])
return v


def update_parameters_with_momentun(parameters, grads, v, beta, learning_rate):
"""
使用动量更新参数(momentum算法)
:param parameters:
:param grads:
:param v:
:param beta:
:param learning_rate:
:return:
"""
L = len(parameters) // 2
for l in range(L):
# 计算速度
v["dW" + str(l + 1)] = beta * v["dW" + str(l + 1)] + (1 - beta) * grads["dW" + str(l + 1)]
v["db" + str(l + 1)] = beta * v["db" + str(l + 1)] + (1 - beta) * grads["db" + str(l + 1)]

# 更新参数
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * v["dW" + str(l + 1)]
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * v["db" + str(l + 1)]

return parameters, v


def initialize_adam(parameters):
"""
初始化Adam算法使用的vs
:param parameters:
:return:
"""

L = len(parameters) // 2
v = {}
s = {}

for l in range(L):
v["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l + 1)])
v["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l + 1)])

s["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l + 1)])
s["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l + 1)])

return (v, s)


def update_parameters_with_adam(parameters, grads, v, s, t, learning_rate=0.01, beta1=0.9, beta2=0.999, epsilon=1e-8):
"""
更新参数
:param parameters:
:param grads:
:param v:
:param s:
:param t:
:param learning_rate:
:param beta1:
:param beta2:
:param epsilon:
:return:
"""
L = len(parameters) // 2
v_corrected = {} # 偏差修正后的值
s_corrected = {} # 偏差修正后的值

for l in range(L):
# 计算v
v["dW" + str(l + 1)] = beta1 * v["dW" + str(l + 1)] + (1 - beta1) * grads["dW" + str(l + 1)]
v["db" + str(l + 1)] = beta1 * v["db" + str(l + 1)] + (1 - beta1) * grads["db" + str(l + 1 )]

# 修正误差
v_corrected["dW" + str(l + 1)] = v["dW" + str(l + 1)] / (1 - np.power(beta1, t))
v_corrected["db" + str(l + 1)] = v["db" + str(l + 1)] / (1 - np.power(beta1, t))

# 计算s
s["dW" + str(l + 1)] = beta2 * s["dW" + str(l + 1)] + (1 - beta2) * np.square(grads["dW" + str(l + 1)])
s["db" + str(l + 1)] = beta2 * s["db" + str(l + 1)] + (1 - beta2) * np.square(grads["db" + str(l + 1)])

# 修正误差
s_corrected["dW" + str(l + 1)] = s["dW" + str(l + 1)] / (1 - np.power(beta2, t))
s_corrected["db" + str(l + 1)] = s["db" + str(l + 1)] / (1 - np.power(beta2, t))

# 更新参数
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * (
v_corrected["dW" + str(l + 1)] / np.sqrt(s_corrected["dW" + str(l + 1)] + epsilon))
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * (
v_corrected["db" + str(l + 1)] / np.sqrt(s_corrected["db" + str(l + 1)] + epsilon))

return (parameters, v, s)


def model(X, Y, layers_dims, optimizer, learning_rate=0.0007,
mini_batch_size=64, beta=0.9, beta1=0.9, beta2=0.999,
epsilon=1e-8, num_epochs=10000, print_cost=True, is_plot=True):
"""
定义神经网络模型
:param X:
:param Y:
:param layers_dims:
:param optimizer:
:param learning_rate:
:param mini_batch_size:
:param beta:
:param beta1:
:param beta2:
:param epsilon:
:param num_epochs:
:param print_cost:
:param is_plot:
:return:
"""
L = len(layers_dims)
costs = []
t = 0 # 每学习完一个minibatch就增加1
seed = 10 # 随机种子

# 初始化参数
parameters = opt_utils.initialize_parameters(layers_dims)

# 选择优化器
if optimizer == "gd":
pass # 不使用任何优化器,直接使用梯度下降法
elif optimizer == "momentum":
v = initialize_velocity(parameters) # 使用动量
elif optimizer == "adam":
v, s = initialize_adam(parameters) # 使用Adam优化
else:
print("optimizer参数错误,程序退出。")
exit(1)

# 开始学习
for i in range(num_epochs):
# 定义随机 minibatches,我们在每次遍历数据集之后增加种子以重新排列数据集,使每次数据的顺序都不同
seed = seed + 1
minibatches = random_mini_batches(X, Y, mini_batch_size, seed)

for minibatch in minibatches:
# 选择一个minibatch
(minibatch_X, minibatch_Y) = minibatch
# 前向传播
A3, cache = opt_utils.forward_propagation(minibatch_X, parameters)
# 计算误差
cost = opt_utils.compute_cost(A3, minibatch_Y)
# 反向传播
grads = opt_utils.backward_propagation(minibatch_X, minibatch_Y, cache)
# 更新参数
if optimizer == "gd":
parameters = update_parameters_with_gd(parameters, grads, learning_rate)
elif optimizer == "momentum":
parameters, v = update_parameters_with_momentun(parameters, grads, v, beta, learning_rate)
elif optimizer == "adam":
t = t + 1
parameters, v, s = update_parameters_with_adam(parameters, grads, v, s, t, learning_rate, beta1, beta2,
epsilon)
# 记录误差值
if i % 100 == 0:
costs.append(cost)
# 是否打印误差值
if print_cost and i % 1000 == 0:
print("第" + str(i) + "次遍历整个数据集,当前误差值:" + str(cost))
# 是否绘制曲线图
if is_plot:
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('epochs (per 100)')
plt.title("Learning rate = " + str(learning_rate))
plt.show()

return parameters


# 使用普通的梯度下降
# layers_dims = [train_X.shape[0], 5, 2, 1]
# parameters = model(train_X, train_Y, layers_dims, optimizer="gd", is_plot=True)
#
# # 预测
# preditions = opt_utils.predict(train_X, train_Y, parameters)
#
# # 绘制分类图
# plt.title("Model with Momentum optimization")
# axes = plt.gca()
# axes.set_xlim([-1.5, 2.5])
# axes.set_ylim([-1, 1.5])
# opt_utils.plot_decision_boundary(lambda x: opt_utils.predict_dec(parameters, x.T), train_X, train_Y)


#使用动量的梯度下降
layers_dims = [train_X.shape[0],5,2,1]
parameters = model(train_X, train_Y, layers_dims, beta=0.9,optimizer="momentum",is_plot=True)
#预测
preditions = opt_utils.predict(train_X,train_Y,parameters)

#绘制分类图
plt.title("Model with Momentum optimization")
axes = plt.gca()
axes.set_xlim([-1.5, 2.5])
axes.set_ylim([-1, 1.5])
opt_utils.plot_decision_boundary(lambda x: opt_utils.predict_dec(parameters, x.T), train_X, train_Y)


# Adam
layers_dims = [train_X.shape[0], 5, 2, 1]
# 使用Adam优化的梯度下降
parameters = model(train_X, train_Y, layers_dims, optimizer="adam", is_plot=True)
# 预测
preditions = opt_utils.predict(train_X, train_Y, parameters)

# 绘制分类图
plt.title("Model with Adam optimization")
axes = plt.gca()
axes.set_xlim([-1.5, 2.5])
axes.set_ylim([-1, 1.5])
opt_utils.plot_decision_boundary(lambda x: opt_utils.predict_dec(parameters, x.T), train_X, train_Y)

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注