Mini-patch:从零开始的反向传播(附详细代码)

文章将重点介绍mini-patch逐步实现的反向传播算法。有许多教程和博客详细介绍了反向传播算法,以及演算和代数背后的所有逻辑。因此,我将跳过这一部分,并在数学和使用Python的实现中切入方程式。

关于为什么我们应该从头开始实现一个算法,即使几乎所有框架都已经可以使用它,这也是一个长期存在的普遍问题。显然,在使用某些高级框架时,你甚至都不会注意到反向传播确实发挥了魔力。要完全颠倒地理解它,一次尝试肯定是不够的。反向传播在游戏中也是可以进行实验的。

为什么要使用mini-patch?

mini-patch的原理很简单。通过将数据分成小批处理,并在训练循环的每次迭代中为算法提供一部分数据集,可以节省内存和处理时间。一次馈入10000x10000矩阵不仅会消耗内存,还会花费很长时间才能运行。相反,每次迭代将其降低到50个不仅会减少内存使用量,而且可以跟踪进度。

注意:这与随机方法不同,在随机方法中,我们从每个类别的数据中抽取了分层样本,并在假设模型可以推广的基础上进行训练。

开始实验

这是将用于实验的数据头。

此处的目标变量是占用率,它是类别变量(0/1)。这将是我们将要编码的架构。

算法:

对于i:= 1到i:= m:

执行正向传播或正向传递以计算每一层中神经元的激活值。

反向传播步骤:

使用数据中的标签计算误差项(MSE或LogLoss或您的期望):

隐藏层中的误差项使用以下公式计算:

设置梯度:
初始化Δ= 0

3.梯度下降和权重更新步骤:

代码展示:

<code>weight_dim = [5,H,1] #[number of input features, number of hidden units, number of output units] print("Initializing using He initialization") np.random.seed(3) w1 = np.random.randn(weight_dim[1],weight_dim[0]) * np.sqrt(2/weight_dim[0]) # 0.01 b1 = np.zeros((weight_dim[1],1)) w2 = np.random.randn(weight_dim[2],weight_dim[1]) * np.sqrt(2/weight_dim[1]) # 0.01 b2 = np.zeros((weight_dim[2],1))/<code>

如前所述,这将是一个三层网络。为了使梯度和误差方程更好,更容易地识别,我们保持层的数量简洁。之后,我们将定义一个函数,该函数将用作网络中的转发器。

<code>def forward(X,w1,w2,b1,b2): z1 = np.matmul(w1,np.transpose(np.asmatrix(X))) + b1 a1 = sigmoid(z1) z2 = np.matmul(w2, a1) + b2 a2 = sigmoid(z2) return z1, a1, z2, a2/<code>

这里要注意的一件事是,已经将Input层视为我的第0层。可能还有其他博客/教程被认为是第一名。因此,绝对要为所需的索引编制索引。

因此,现在,在初始化权重和偏差并定义前向传播函数之后,我们将在size = data-of-setset / N的mini-patch上定义反向传播函数。通过调整N来调整所需的批次大小。

<code>def backprop(w1,b1,w2,b2,X_train,X_test): for i in range(epoch): no_of_batches = len(X_train) // N for j in range(no_of_batches): # Initilazing gradients delta1 = np.zeros(w1.shape) #(5,5) delta2 = np.zeros(w2.shape) #(1,5) db1 = 0.0 db2 = 0.0 for row in range(j*N,(j+1)*N): # Drop the date column and the index column X = X_train[row, 2:7] Y = X_train[row, 7] #feed forward z1 , a1 , z2 , a2 = forward(X,w1,w2,b1,b2) #(5,1) (5,1) (1,1) (1,1) h = a2 # (1,1) # initializations d3 = a2 - Y #(1,1) delta2 += d3 * np.transpose(a2) #(1,1) db2 += d3 d2 = np.multiply((np.transpose(w2) * d3), sigmoid_gradient(z1),dtype=float) #(5,1) delta1 += d2 * np.transpose(a1) #(5,5) db1 += d2 # Gradient Descent Step #updating weights after every batch by averaging the gradients w1 = w1 - lr * 1.0/N * delta1 #taking the average of gradients b1 = b1 - lr * 1.0/N * db1 w2 = w2 - lr * 1.0/N * delta1 b2 = b2 - lr * 1.0/N * db2 print("************************************************") print("Train error after epoch {} is: ".format(i), np.sum(error(calc_out(X_train[:,2:7],w1,b1,w2,b2),X_train[:,7])) / len(X_train) * 100) print("Test error after epoch {} is: ".format(i), np.sum(error(calc_out(X_test[:,2:7],w1,b1,w2,b2),X_test[:,7])) / len(X_test) * 100) print("************************************************") print() train_error[i] = np.sum(error(calc_out(X_train[:,2:7],w1,b1,w2,b2),X_train[:,7])) / len(X_train) * 100 test_error[i] = np.sum(error(calc_out(X_test[:,2:7],w1,b1,w2,b2),X_test[:,7])) / len(X_test) * 100 return [w1,b1,w2,b2]/<code>

步骤分解

如前所述,Ist循环会遍历您想要使模型遍历数据的次数,只需将其放在神经网络术语“时代”中即可。第二次循环:指定了批次数量后,此循环针对每个时期“ i”遍历每个微型批次第三循环遍历该小批量中的每个训练示例,并计算梯度和误差值最后,对于每个批次,都执行梯度下降步骤,并对权重矩阵进行更改。

这就是mini-patch的反向传播实现。需要注意的是,此实验为网络中的每一层使用了一个矩阵变量,当网络规模扩大时,这是一种不明智的做法,我们这样做是为了了解它的实际工作原理。如果要增加“隐藏层”的数量,可以简单地使用3d矩阵进行误差和梯度计算,其中第3维将保存该层的值。