小言_互联网的博客

一个简单的深度学习模型,带你深度了解背后原理

544人阅读  评论(0)

全文共4431字,预计学习时长9分钟

深度学习是目前在实际数据科学应用中最流行的模型之一。从图像到文本,再到语音/音乐等领域,它都是一个有效的模型。随着使用量的上升,快速且可扩展地实现深度学习变得至关重要。Tensorflow等深度学习平台的兴起可以帮助简化开发人员的实现方式。

在本文中,我们将学习深度学习的工作原理,并熟悉相关术语,例如反向传播和批量大小。我们将实现一个简单的深度学习模型 - 从理论到scratch实现 - 使用python预定义输入和输出,然后使用Keras和Tensorflow等深度学习平台实现相同的模型。

我们使用Keras和Tensorflow 1.x和2.0编写了这个简单的深度学习模型,该模型具有三种不同级别的复杂性和易编码性。

Scratch的深度学习实现

一个简单的多层感知器有4个输入神经元,隐含层有3个神经元,输出层有1个神经元。我们有三个输入数据样本,用表示,三个期望输出数据样本,用表示。因此,每个输入数据样本都有4个特征。

# Inputs and outputs of the neural net:import numpy as
 npX=np.array([[1.0, 0.0, 1.0, 0.0],[1.0, 0.0, 1.0, 1.0],[0.0, 1.0, 0.0, 
1.0]])yt=np.array([[1.0],[1.0],[0.0]])

该神经网络有4个输入神经元,1个隐含层有3个神经元,输出层有1个神经元

图中 x(m)是X的1个样本,h(m)是x(m) 输入的隐含层输出,Wi和Wh为权重。

神经网络(NN)的目标是为给定输入获得权重和偏差,NN提供期望输出。但是,我们事先并不知道什么是适当的权重和偏差,因此我们需要更新权重和偏差,使得NN、和期望的的输出之间的误差最小化。这种用迭代的方式最小化误差过程称为神经网络训练。

假设隐含层和输出层的激活函数都是sigmoid函数。因此,

神经网络的权值大小、偏差以及输入和输出之间的关系

其中,激活函数是sigmoid函数,m是第m个数据样本, yp(m) 是NN输出。

衡量NN输出与期望输出之间差异的误差函数可以用数学方式表示为:

神经网络所定义的误差是平方误差

上述NN的伪代码总结如下:

用于神经网络训练的伪代码

通过伪代码得出,我们需要计算误差(E)相对于参数(权重和偏差)的偏导数。利用微积分中的链式法则,表示如下:

误差函数对权重的导数

在此我们有两种方式可以用来更新反向路径中的权重和偏差(反向路径指更新权重和偏差,从而最小化误差):

1. 使用训练数据的全部个样本

2. 使用一个样本或一簇样本

第一种方式的批量大小是。如果第二种方式使用一个样本来更新参数,则其批量大小是1。因此批量大小代表用于更新权重和偏差的数据样本数量。

从上述神经网络的实现可以得出,关于参数的误差梯度是按符号计算的,其中具有不同的批量大小。

如上述例子所示,基于scratch创建简单的深度学习模型,方法十分复杂。下一节将介绍如何通过深度学习框架实现可扩展且简单的模型。

Keras、Tensorflow 1.x及2.0的深度学习实现

在上一节中,我们运用链式法则计算了误差梯度参数。这不是一种简单或可扩展的方法。同时,我们会在每次迭代时评估偏导数,因此,尽管偏导数的值很重要,但不需要梯度符号。此时Keras和Tensorflow等深度学习框架可以发挥其作用。深度学习框架使用AutoDiff方法对部分梯度进行数值计算。如果你对AutoDiff不熟悉,那么StackExchange就是一个很好的例子。AutoDiff将复杂表达式分解为一组原始表达式,即最多由一个函数调用组成的表达式。由于已知每个单独表达式的区分规则,因此可以以有效的方式计算最终结果。

我们在Keras、Tensorflow 1.x和Tensorflow 2.0中实现了三个不同级别的NN模型:

1. 高级(Keras和Tensorflow 2.0):

高级Tensorflow 2.0,批量大小1

https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v2_HighLevel_batchsize1_train_on_batch.py

2. 中级(Tensorflow 1.x和2.0):

中级Tensorflow 1.x,批量大小1,

https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v1_MediumLevel_batchsize1.py

中级Tensorflow 1.x,批量大小N,

https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v1_MediumLevel_batchsizeN.py

中级Tensorflow 2.0,批量大小1,

https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v2_MediumLevel_batchsize1.py

中级Tensorflow 2.0,批量大小N

https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v2_MediumLevel_batchsizeN.py

3. 初级(Tensorflow 1.x):

初级Tensorflow 1.x,批量大小N

https://github.com/miladtoutounchian/Deep-Learning-/blob/master/TF_v1_LowLevel_batchsizeN.py

代码片段:

在高级实现中,基于model.train_on_batch运用keras和Tensorflow v 2.0实现模型:

# High-Level implementation of the neural net
 in Tensorflow:model.compile(loss=mse,optimizer=optimizer)for _ in
 range(2000):for step, (x, y) in enumerate(zip(X_data,
 y_data)):model.train_on_batch(np.array([x]), np.array([y]))

在Tensorflow 1.x的中级实现中,定义如下:

E = tf.reduce_sum(tf.pow(ypred - Y, 2))optimizer =
 tf.train.GradientDescentOptimizer(0.1)grads =
 optimizer.compute_gradients(E, [W_h, b_h, W_o, b_o])updates =
 optimizer.apply_gradients(grads)

以此确保在for循环中更新需要更新的变量。在中级实现中,梯度及其更新在for循环外部进行定义,内部则为迭代更新。在Tensorflow v 2.x的中级实现中,使用如下:

# Medium-Level implementation of the neural net in Tensorflow#

 In for_loop

with tf.GradientTape() as tape:   x =
 tf.convert_to_tensor(np.array([x]), dtype=tf.float64)   y =
 tf.convert_to_tensor(np.array([y]), dtype=tf.float64)   ypred =
 model(x)   loss = mse(y, ypred)gradients = tape.gradient(loss,
 model.trainable_weights)optimizer.apply_gradients(zip(gradients,model.t
rainable_weights))

在初级实现中,分别更新每个权重和偏差。在Tensorflow v 1.x初级实现中,定义如下:

# Low-Level implementation of the neural net in Tensorflow:E =
 tf.reduce_sum(tf.pow(ypred - Y, 2))dE_dW_h = tf.gradients(E, [W_h])
[0]dE_db_h = tf.gradients(E, [b_h])[0]dE_dW_o = tf.gradients(E, [W_o])
[0]dE_db_o = tf.gradients(E, [b_o])[0]# In for_loop:evaluated_dE_dW_h =
 sess.run(dE_dW_h,                 feed_dict={W_h: W_h_i, b_h: b_h_i,
 W_o: W_o_i, b_o: b_o_i, X: X_data.T, Y: y_data.T})            W_h_i =
 W_h_i - 0.1 * evaluated_dE_dW_h            evaluated_dE_db_h = 
sess.run(dE_db_h,                             feed_dict={W_h: W_h_i,
 b_h: b_h_i, W_o: W_o_i, b_o: b_o_i, X: X_data.T, Y: y_data.T})
            b_h_i = b_h_i - 0.1 * evaluated_dE_db_h            evaluated_dE_dW_o
 = sess.run(dE_dW_o,                             feed_dict={W_h: W_h_i,
 b_h: b_h_i, W_o: W_o_i, b_o: b_o_i, X: X_data.T, Y: y_data.T})
            W_o_i = W_o_i - 0.1 * evaluated_dE_dW_o            evaluated_dE_db_o
 = sess.run(dE_db_o,                            feed_dict={W_h: W_h_i, b_h: b_h_i, 
W_o: W_o_i, b_o: b_o_i, X: X_data.T, Y: y_data.T})    
        b_o_i = b_o_i - 0.1 * evaluated_dE_db_o

如初级实现所述,开发人员可以更好地控制数值运算和计算的每一步。

上述所示,在Scratch的深度学习实现中,即便只是通过使用梯度符号计算建立一个简单的深度学习模型来更新权重和偏差并不是一种简单或可扩展的方法。而使用用于更新权重和偏差的稳定数值梯度计算AutoDiff,深度学习框架可以加速此进程。

留言 点赞 关注

我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”

(添加小编微信:dxsxbb,加入读者圈,一起讨论最新鲜的人工智能科技哦~)


转载:https://blog.csdn.net/duxinshuxiaobian/article/details/101607727
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场