本文内容为Andrew ng深度学习课程的第二部分的笔记,本篇总结了如何神经网络的训练和学习过程的实践经验与优化方法。
1 深度学习的实践技巧
应用机器学习是一个需要不断迭代的过程,众多经验参数如层数,隐层单元数,学习率,激活函数,权重参数的初始化方法等等众多的参数需要不断尝试改进以最终的得到较高的准确率。在这个过程中,有着众多的实践技巧。
1.1 训练/开发/测试集
如果针对某一监督学习的任务,当前有一批标注数据集。数据的分类方法:
方法\划分 | 数据集 | 训练集 | 开发集 | 测试集 |
---|---|---|---|---|
激进 | 中小数据集 | 70% | 0% | 30% |
均衡 | 中小数据集 | 60% | 20% | 20% |
现代 | 大数据集 | 98% | 1% | 1% |
在深度学习中,通常需要的数据集都很大,如百万级别的,这样只需要拿出较小比例数据用来做测试集即可。
最重要的一点是,务必保证开发集/测试集数据为来自相同的分布。保持我们训练和应用的目标一致性。
有了数据集的划分,我们可以在相同的训练集上训练不同的模型,并通过测试集进行对比以选择合适的模型。
1.2 偏差与方差
我们用训练集与开发集上的误差来说明偏差与方差的不同情况。
误差\划分 | 例1 | 例2 | 例3 | 例4 |
---|---|---|---|---|
训练集误差 | 1% | 15% | 15% | 1% |
开发集误差 | 11% | 16% | 30% | 1.5% |
结论 | 高方差 | 高偏差 | 方差高/偏差高 | 方差低/偏差低 |
优良的分类器,其偏差与方差均偏低。
下面我们来看下机器学习过程如何解决偏差与方差问题。
A{"训练结果"} -->
|"高偏差"|B["上更大网络/训练更久"]
D-->A
B-->D["训练"]
A-->|"高方差"|C["增加数据/正则化"]
C-->D
1.4 正则化
正则化可以用来解决神经过拟合的问题。
1.4.1 逻辑回归的正则化
L2正则化: J(w,b)=1m∑L(yˆ,y)+λ2m||w||22 J ( w , b ) = 1 m ∑ L ( y ^ , y ) + λ 2 m | | w | | 2 2
L1正则化: J(w,b)=1m∑L(yˆ,y)+λ2m||w||1 J ( w , b ) = 1 m ∑ L ( y ^ , y ) + λ 2 m | | w | | 1
1.4.2 神经网络的正则化
对于神经网络,由于神经网络有多层,因此需要计算所有层的正则化的和,这样损失函数将有两部分构成,一部分为预测的误差,一部分为所有层权重参数的范数。
由于损失函数中增加了正则项,在各层的反向传播过程中需要加入考虑,因此在加入正则化后各层的反向传播计算公式如下:
dW[l]=1mdZ[l]A[l]+λmW[l]
d
W
[
l
]
=
1
m
d
Z
[
l
]
A
[
l
]
+
λ
m
W
[
l
]
W[l]=W[l]−αdW[l]
W
[
l
]
=
W
[
l
]
−
α
d
W
[
l
]
1.4.3 为什么正则化会有用
由于在误差中引入了新的项,当 λ λ 升高时, W W 会变小,这样会使得生成的变小。对于激活函数来说,自变量Z就会取在函数的中间近似于线性的部分,而无法利用非线性的部分。因此会使得参数的过拟合现象降低。
1.5 Dropout正则化
Droupout的作用是随机在神经网络中选择一些单元,使其在运算中不参与计算,这样神经网络的规模将变小,从而实现避免过拟合。同时为了避免不影响后续计算的数值,未被dropout的单元需要整体放大,以补充消除单元的数值。
keep-prob = 0.5
Dl = np.random.rand(Al.shape[0], Al.shape[0])<keep-prob
# 使得对应项置0
Al *= Dl
Al /= keep-prob
需要注意的是,dropout只能用于训练神经网络,在做预测时不需要使用。
通常在神经网络的隐层的前面的层中使用较小的keep-prob,在后面的层中使用大的keep-prob,甚至不使用dropout。
1.6 正则化实现示例
下面的代码展示了在一个3层神经网络上增加L2正则与Dropout的功能的实现。大部分代码在第一课中都已经实现过,这里只是增加了正则项的计算修正。
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def relu(x):
return np.maximum(0, x)
def initialize_parameters(layer_dims):
np.random.seed(3)
parameters = {}
layers = len(layer_dims)
for l in range(1, layers):
parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l - 1]) / np.sqrt(layer_dims[l - 1])
parameters['b' + str(l)] = np.zeros((layer_dims[l], 1))
return parameters
def forward_propagation(X, parameters):
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
W3 = parameters["W3"]
b3 = parameters["b3"]
# 前向传播
Z1 = np.dot(W1, X) + b1
A1 = relu(Z1)
Z2 = np.dot(W2, A1) + b2
A2 = relu(Z2)
Z3 = np.dot(W3, A2) + b3
A3 = sigmoid(Z3)
# 缓存计算中间结果
cache = (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3)
return A3, cache
def forward_propagation_with_dropout(X, parameters, keep_prob=0.5):
np.random.seed(1)
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
W3 = parameters["W3"]
b3 = parameters["b3"]
# 第1层前向计算,Dropout部分节点,且增大未失活节点的权重
Z1 = np.dot(W1, X) + b1
A1 = relu(Z1)
D1 = np.random.rand(A1.shape[0], A1.shape[1])
D1 = D1 < keep_prob
A1 = A1 * D1
A1 = A1 / keep_prob
# 第2层前向计算,处理Dropout
Z2 = np.dot(W2, A1) + b2
A2 = relu(Z2)
D2 = np.random.rand(A2.shape[0], A2.shape[1])
D2 = D2 < keep_prob
A2 = A2 * D2
A2 = A2 / keep_prob
# 输出层计算,无Dropout
Z3 = np.dot(W3, A2) + b3
A3 = sigmoid(Z3)
cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)
return A3, cache
def compute_cost(a3, Y):
m = Y.shape[1]
logprobs = np.multiply(-np.log(a3), Y) + np.multiply(-np.log(1 - a3), 1 - Y)
cost = 1. / m * np.nansum(logprobs)
return cost
def compute_cost_with_regularization(A3, Y, parameters, lambd):
m = Y.shape[1]
W1 = parameters["W1"]
W2 = parameters["W2"]
W3 = parameters["W3"]
cross_entropy_cost = compute_cost(A3, Y)
L2_regularization_cost = lambd * (np.sum(np.square(W1)) + np.sum(np.square(W2)) + np.sum(np.square(W3))) / (2 * m)
cost = cross_entropy_cost + L2_regularization_cost
return cost
def backward_propagation(X, Y, cache):
m = X.shape[1]
(Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
# 输出层梯度计算
dZ3 = A3 - Y
dW3 = 1. / m * np.dot(dZ3, A2.T)
db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
# 第二层梯度计算
dA2 = np.dot(W3.T, dZ3)
dZ2 = np.multiply(dA2, np.int64(A2 > 0))
dW2 = 1. / m * np.dot(dZ2, A1.T)
db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)
# 第一层梯度计算
dA1 = np.dot(W2.T, dZ2)
dZ1 = np.multiply(dA1, np.int64(A1 > 0))
dW1 = 1. / m * np.dot(dZ1, X.T)
db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)
gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,
"dA2": dA2, "dZ2": dZ2, "dW2": dW2, "db2": db2,
"dA1": dA1, "dZ1": dZ1, "dW1": dW1, "db1": db1}
return gradients
def backward_propagation_with_regularization(X, Y, cache, lambd):
m = X.shape[1]
(Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
# 输出层梯度计算,增加了正则项的梯度
dZ3 = A3 - Y
dW3 = 1. / m * np.dot(dZ3, A2.T) + (lambd * W3) / m
db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
# 第二层梯度计算
dA2 = np.dot(W3.T, dZ3)
dZ2 = np.multiply(dA2, np.int64(A2 > 0))
dW2 = 1. / m * np.dot(dZ2, A1.T) + (lambd * W2) / m
db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)
# 第一层梯度计算
dA1 = np.dot(W2.T, dZ2)
dZ1 = np.multiply(dA1, np.int64(A1 > 0))
dW1 = 1. / m * np.dot(dZ1, X.T) + (lambd * W1) / m
db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)
gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3, "dA2": dA2,
"dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
"dZ1": dZ1, "dW1": dW1, "db1": db1}
return gradients
def backward_propagation_with_dropout(X, Y, cache, keep_prob):
m = X.shape[1]
(Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache
# 输出层梯度计算
dZ3 = A3 - Y
dW3 = 1. / m * np.dot(dZ3, A2.T)
db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
# 第二层梯度计算
dA2 = np.dot(W3.T, dZ3)
dA2 = dA2 * D2
dA2 = dA2 / keep_prob
dZ2 = np.multiply(dA2, np.int64(A2 > 0))
dW2 = 1. / m * np.dot(dZ2, A1.T)
db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)
# 第一层梯度计算
dA1 = np.dot(W2.T, dZ2)
dA1 = dA1 * D1
dA1 = dA1 / keep_prob
dZ1 = np.multiply(dA1, np.int64(A1 > 0))
dW1 = 1. / m * np.dot(dZ1, X.T)
db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)
gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3, "dA2": dA2,
"dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
"dZ1": dZ1, "dW1": dW1, "db1": db1}
return gradients
def update_parameters(parameters, grads, learning_rate):
n = len(parameters) // 2 # number of layers
for k in range(n):
parameters["W" + str(k + 1)] = parameters["W" + str(k + 1)] - learning_rate * grads["dW" + str(k + 1)]
parameters["b" + str(k + 1)] = parameters["b" + str(k + 1)] - learning_rate * grads["db" + str(k + 1)]
return parameters
def predict(X, y, parameters):
m = X.shape[1]
p = np.zeros((1, m), dtype=np.int)
a3, caches = forward_propagation(X, parameters)
for i in range(0, a3.shape[1]):
if a3[0, i] > 0.5:
p[0, i] = 1
else:
p[0, i] = 0
# print ("predictions: " + str(p[0,:]))
# print ("true labels: " + str(y[0,:]))
print("Accuracy: " + str(np.mean((p[0, :] == y[0, :]))))
return p
def model(X, Y, learning_rate=0.3, num_iterations=30000, print_cost=True, lambd=0, keep_prob=1):
grads = {}
costs = []
m = X.shape[1]
layers_dims = [X.shape[0], 20, 3, 1]
parameters = initialize_parameters(layers_dims)
for i in range(0, num_iterations):
# Forward propagation
if keep_prob == 1:
a3, cache = forward_propagation(X, parameters)
elif keep_prob < 1:
a3, cache = forward_propagation_with_dropout(X, parameters, keep_prob)
# Cost function
if lambd == 0:
cost = compute_cost(a3, Y)
else:
cost = compute_cost_with_regularization(a3, Y, parameters, lambd)
# Backward propagation.
if lambd == 0 and keep_prob == 1:
grads = backward_propagation(X, Y, cache)
elif lambd != 0:
grads = backward_propagation_with_regularization(X, Y, cache, lambd)
elif keep_prob < 1:
grads = backward_propagation_with_dropout(X, Y, cache, keep_prob)
# Update parameters.
parameters = update_parameters(parameters, grads, learning_rate)
if print_cost and i % 10000 == 0:
print("Cost after iteration {}: {}".format(i, cost))
if print_cost and i % 1000 == 0:
costs.append(cost)
return parameters
1.7 其他正则化方法
Data augmentation:对于图像输入尤其有用,通过将图像左右镜像,颜色变换,旋转角度等,可以将一张图片变成多张,从而增大输入数据集。
Early stoping:在训练过程中关注开发集上的误差,在开发集误差开始变差时,停止训练过程。
1.8 正则化输入
如果输入数据有多个维度,如果不同维度的取值类型不同,同时取值范围也大不相同,这将造成梯度下降在各个维度上是非对称的。在窄维度上需要一个很小的学习率才能找到极值,而在宽维度上将需要很多步骤。因而会造成训练时间过长。
解决这一问题的方法是对输入进行正则化,尽量使得各个维度的输入归一化,从而使得成本函数有更好的对称性。
一种正则化方法是如下公式所示:
x=x−μσ2+ϵ√
x
=
x
−
μ
σ
2
+
ϵ
不仅输入可以进行归一化,各个隐含层的输出在激活前也可以进行归一化。
1.9 梯度消失与梯度爆炸
当神经网络层数过多时,各层的权重参数w在运算过程中会出现指数级的累积效应。
以Relu激活函数为例,忽略参数b,将输出层的运算展开
z[l]=w[l]a[l−1]=w[l](w[l−1]a[l−2])=...=w[l](w[l−1](...w[2](w[1]x)))
z
[
l
]
=
w
[
l
]
a
[
l
−
1
]
=
w
[
l
]
(
w
[
l
−
1
]
a
[
l
−
2
]
)
=
.
.
.
=
w
[
l
]
(
w
[
l
−
1
]
(
.
.
.
w
[
2
]
(
w
[
1
]
x
)
)
)
当l较大时,比如大于100。若w在初始化小于1,则经过若干次乘积,最终z将十分接近于0。而w在初始化时大于1,则经过若干次乘积,最终z将十分巨大。而这种现象对我们训练权重参数十分不利。
1.10 参数初始化
可以通过合适的权重初始化方法来减轻此问题带来的影响,其方法是将权重参数设置为接近1。具体作法与使用的激活函数有关。
He初始化:当使用relu激活时适用。
w[l]=np.random.rand(shape)∗2n[l−1]‾‾‾‾√
w
[
l
]
=
n
p
.
r
a
n
d
o
m
.
r
a
n
d
(
s
h
a
p
e
)
∗
2
n
[
l
−
1
]
Xavier初始化:当使用tanh激活时适用。
w[l]=np.random.rand(shape)∗1n[l−1]‾‾‾‾√
w
[
l
]
=
n
p
.
r
a
n
d
o
m
.
r
a
n
d
(
s
h
a
p
e
)
∗
1
n
[
l
−
1
]
好的初始化可以加速训练过程的收敛,并可以使训练误差收敛在一个较小的水平上。
1.11 梯度逼近与检验
我们可以在运算时动态的检查梯度的计算结果,方法是我们手工计算某点梯度的近似值,并于神经网络反馈的梯度进行对比。
梯度的逼近值计算:
f(θ+ϵ)−f(θ−ϵ)2ϵ
f
(
θ
+
ϵ
)
−
f
(
θ
−
ϵ
)
2
ϵ
梯度检验需要检查每个参数,因而过程十分漫长,只能用于调试,不能用在训练过程中。
2 优化算法
2.1 Mini-batch梯度下降法
在之前实现的梯度下降算法中,我们将整个训练集作为X作为神经网络的输入进行计算。然而如果遇到训练集的规模很大,例如上百万的时候,数据无法全部装入内存,若数据不断在内存和外存之间切换,将十分影响计算效率。问题在于输入的矩阵过大。
Mini-batch梯度下降法的思想是将训练集划分为若干个固定大小(如batch_size=512)的小数据集(mini-batch),每次只运算batch_size个数据。
for i in range(epoches):
num_batches = math.floor(m/batch_size)
for t in range(num_batches):
start,end = t*batch_size, (t+1)*batch_size
A, caches = forward_propagation(X[:,start:end])
cost = compute_cost(A, Y[:,start:end])
grade = backward_propagation(A, caches, parameters)
parameters = update_paramters(paramters, grade)
由于每次mini-batch都会计算损失函数,而mini-batch样本数明显较小,因此在训练过程中损失函数可能不会呈现出明显递减的趋势,而是呈现不断上下波动但整体降低的趋势。
当batch-size为整体样本数时,则mini-batch梯度下降就是原始的batch梯度下降算法,当batch-size为1时就是逐样本的梯度下降(或随机梯度下降)。batch-size越小,则损失函数的优化过程抖动越大,收敛过程越缓慢。一般的batch-size取值范围在64-512之间的2的指数值。当然不管batch-size多大,最好都保证每个mini-batch的数据能装入内存中。
mini-batch的一个变体是每个mini-batch为随机从总体中取batch_size个样本进行训练。
2.2 指数加权移动平均
对于时间序列的数据
θi
θ
i
,移动均线值
vt
v
t
的公式为
v0=0
v
0
=
0
vt=βvt−1+(1−β)θt
v
t
=
β
v
t
−
1
+
(
1
−
β
)
θ
t
我们通过下图可以看出指数加权移动平均的平滑作用。
2.3 动量梯度下降
动量(Momentum)梯度下降是基于指数加权移动平均的想法,对后向传播中计算的dw和db进行指数移动平均化,以平滑其抖动,这样可以加快训练的收敛速度。
下面列出了动量梯度下降中反向传播中w,b的更新计算公式
dW′=β1dW′+(1−β1)dW
d
W
′
=
β
1
d
W
′
+
(
1
−
β
1
)
d
W
db′=β1db′+(1−β1)db
d
b
′
=
β
1
d
b
′
+
(
1
−
β
1
)
d
b
W−=αdW′
W
−
=
α
d
W
′
b−=αdb′
b
−
=
α
d
b
′
通常将 β β 设置为0.9。
下面的代码可以插入到之前实现的神经网络中以实现动量梯度下降。
def initialize_velocity(parameters):
layers = len(parameters) // 2 # number of layers in the neural networks
v = {}
for l in range(layers):
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_momentum(parameters, grads, v, beta, learning_rate):
layers = len(parameters) // 2
for l in range(layers):
v["dW" + str(l+1)] = beta * v["dW" + str(l)] + (1-beta) * grads['dW' + str(l+1)]
v["db" + str(l+1)] = beta * v["db" + str(l)] + (1-beta) * grads['db' + str(l+1)]
# update parameters
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
2.4 RMSprop
RMSprop也是一种加速训练收敛的方法。
下面列出了RMSprop梯度下降中反向传播中w,b的更新计算公式
dW′=β2dW′+(1−β2)(dW)2
d
W
′
=
β
2
d
W
′
+
(
1
−
β
2
)
(
d
W
)
2
db′=β2db′+(1−β2)(db)2
d
b
′
=
β
2
d
b
′
+
(
1
−
β
2
)
(
d
b
)
2
W−=αdWdW′√
W
−
=
α
d
W
d
W
′
b−=αdbdb′√
b
−
=
α
d
b
d
b
′
2.5 Adam优化算法
Adam算法结合了动量梯度与RMSprop的思想。具体计算公式如下:
dW1=β1dW1+(1−β1)dW
d
W
1
=
β
1
d
W
1
+
(
1
−
β
1
)
d
W
db1=β1db1+(1−β1)db
d
b
1
=
β
1
d
b
1
+
(
1
−
β
1
)
d
b
dW2=β2dW2+(1−β2)(dW)2
d
W
2
=
β
2
d
W
2
+
(
1
−
β
2
)
(
d
W
)
2
db2=β2db2+(1−β2)(db)2
d
b
2
=
β
2
d
b
2
+
(
1
−
β
2
)
(
d
b
)
2
dWcorrect1=dW11−βt1
d
W
1
c
o
r
r
e
c
t
=
d
W
1
1
−
β
1
t
dbcorrect1=db11−βt1
d
b
1
c
o
r
r
e
c
t
=
d
b
1
1
−
β
1
t
dWcorrect2=dW21−βt2
d
W
2
c
o
r
r
e
c
t
=
d
W
2
1
−
β
2
t
dbcorrect2=db21−βt2
d
b
2
c
o
r
r
e
c
t
=
d
b
2
1
−
β
2
t
W−=αdWcorrect1dWcorrect2√+ϵ
W
−
=
α
d
W
1
c
o
r
r
e
c
t
d
W
2
c
o
r
r
e
c
t
+
ϵ
b−=αdbcorrect1dbcorrect2+ϵ√
b
−
=
α
d
b
1
c
o
r
r
e
c
t
d
b
2
c
o
r
r
e
c
t
+
ϵ
推荐使用超参数如下表所示:
超参数 | α α | β1 β 1 | β2 β 2 | ϵ ϵ |
---|---|---|---|---|
推荐值 | 待调优 | 0.9 | 0.999 | 10−8 10 − 8 |
2.6 学习率衰减
在神经网络训练的初期往往需要较大的学习率,使得梯度下降可以快速到达某个极值区域。随后则需要较小的学习率,使用逐步逼近极值点。
学习率衰减是一种随着训练轮数增加减小学习率的算法。decay_rate为衰减因子,只需要在每次epoch时更新学习率参数即可,更新公式如下:
α=11+decay_rate∗epoch_num
α
=
1
1
+
d
e
c
a
y
_
r
a
t
e
∗
e
p
o
c
h
_
n
u
m
2.7 局部最优化问题
由于神经网络中参数巨大,因此局部很小问题并不像人们原始认为那样严重。在高维空间中,更多的是要解决如何快速到达的鞍点。
对于属于不同维度的超参数,可以使用组合遍历的方法来寻找最优的参数组合。由于效率的原因,只建议从组合中随机的抽取一部分组合来训练模型,然后在效果好的点附近区域寻找更多的参数组合再进行尝试。概括的说,就是先在参数空间中进行粗略的搜索,然后再进入精细的搜索模式。
3 超参数调试
3.1 调试处理
在第2节中,我们引入了众多的超参数,诸如 α α , β β ,至今为止,最为重要的超参数是学习率。
其次隐层单元数,batch_size参数也较为重要。
最后是层数,移动平均的权重参数,学习率衰减因子等。
3.2 超参数的随机选择
以选择学习率为例,如果希望在[0.0001,1]之间随机选择4个值,如果使用均匀分布的方式随机取值,则90%的值都在[0.1,1]的区间上,只有10%的值在[0.0001, 0.1]区间内,这样显然不太合理。
解决这一问题的方法是使用对数坐标区间,区间转换为[-4, 0],在对数区间内随机取值,再通过指数函数,将随机值转换为原始区间内。
3.3 超参数训练的实践
如果没有大量的资源,训练模型只能一个一个进行,每隔一段时间观察一下模型的效果,然后调整一些参数,继续对模型进行训练。
如果有大量的计算资源,则可以同时训练多个模型,每个模型使用不同的超参数,达到并行的效果。
3.4 Batch Norm拟合进神经网络
3.5 Softmax回归
Softmax回归可以认为是Logistic回归的泛化,它可以输出多个分类时的每个类的归一化概率向量。
Softmax层通常用在神经网络的输出层,其作用可以通过下面的公式说明:
Z[l]=W[l]A[l−1]+b[l]
Z
[
l
]
=
W
[
l
]
A
[
l
−
1
]
+
b
[
l
]
E=eZ[l]
E
=
e
Z
[
l
]
A[l]=E∑Ei,Ei为E的各维度分量
A
[
l
]
=
E
∑
E
i
,
E
i
为
E
的
各
维
度
分
量
3.6 神经网络框架
在如今的深度学习中,深度学习的框架占据着重要的地位,它可以让学术界和业界以统一的标准,快速实现各种想法并进行传播。本节简单介绍当下比较流行框架。
名称 | 框架语言 | API支持 | 来源 | ||
---|---|---|---|---|---|
Caffe | C++ | Python/Matlab | Personal | ||
CNTK | C++ | Python | Microsoft | ||
DL4J | Java | ||||
Keras | Python | ||||
mxnet | C++ | Python/Go/R… | |||
Paddle | Python | Baidu | |||
Tensorflow | C++ | Python | |||
Theano | Python | Montreal University | |||
Torch | Lua |
框架的选择要考虑下面的一些因素:
- 易于学习和使用
- 运行速度
- 社区支持情况和活跃度
- 框架提供的特性
3.7 Tensorflow
可参考Tensorflow的官方教程.