掘金 人工智能 02月07日
9种神经网络优化算法详解
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了深度学习中优化器的概念和作用,优化器通过最小化损失函数来改进模型表现。文章详细介绍了梯度下降、随机梯度下降以及小批量随机梯度下降等九种优化器的工作原理、优缺点以及实际应用案例。从基础的梯度计算到动量和自适应学习率的引入,逐步揭示了优化算法在深度学习中的重要性。通过了解这些优化器,读者可以更好地选择和调整训练策略,提升模型性能。

💡**梯度下降(Gradient Descent)**:一种通过迭代调整模型参数以最小化代价函数的基础优化算法。其核心思想是沿着梯度的反方向调整参数,通过计算梯度和更新参数的步骤,最终找到局部最小值。学习率的选择至关重要,过大可能导致发散,过小则收敛过慢。

🚀**随机梯度下降(Stochastic Gradient Descent, SGD)**:每次只使用一个训练样本来计算梯度,减少计算量和内存需求。虽然更新过程较为“嘈杂”,但计算效率高,适合大规模数据集。可以通过调整学习率和优化策略,提高其在实际应用中的表现。

📦**小批量随机梯度下降(Mini-Batch Stochastic Gradient Descent, MB-SGD)**:结合了批量梯度下降和随机梯度下降的优点,每次使用一个小批量的数据来计算梯度,在计算效率和稳定性之间取得平衡。相较于标准SGD,收敛的时间复杂度更低,但更新过程仍然较为“嘈杂”。

💨**SGD with Momentum**:通过引入动量机制,考虑历史梯度信息来加速收敛并减少噪声。动量系数γ通常取值为0.9或0.99,用于控制历史梯度对当前更新的影响。动量的引入有助于平滑更新过程,减少震荡,更快地达到最小值。

公众号:尤而小屋编辑:Peter作者:Peter大家好,我是Peter~在深度学习中,有一个“损失loss”的概念,它告诉我们:模型在训练数据中表现的“多差”。现在,我们需要利用这个损失来训练我们的网络,使其表现得更好。本质上,我们需要做的是利用损失并尝试将其最小化,因为较低的损失意味着我们的模型将会表现得更好。最小化(或最大化)任何数学表达式的这个过程被称为优化。理解全局最小化和局部最小化局部最小化:Local Minima全局最小化:Global Minima优化器如何工作优化器是用于改变神经网络属性(例如权重和学习率)的算法或方法,以减少损失。优化器通过最小化函数来解决优化问题。为了更好地理解优化器的作用,可以想象一个蒙着眼睛的登山者试图走下一座山。无法确切知道他该往哪个方向走,但他能判断自己是在下山(取得进展)还是在上山(失去进展)。只要他一直朝着下山的方向前进,最终就能到达山脚。同样,在训练神经网络时,我们无法从一开始就确定模型的权重应该是什么,但可以通过基于损失函数的不断调整(类似于判断登山者是否在下山)来逐步接近目标。优化器的作用就在于此: 它决定了如何调整神经网络的权重和学习率以减少损失。优化算法通过不断优化损失函数,帮助模型尽可能地输出准确的结果。9种优化器列举9种不同类型的优化器以及它们是如何精确地工作以最小化损失函数的。Gradient DescentStochastic Gradient Descent, SGDMini-Batch Stochastic Gradient Descent, MB-SGDSGD with MomentumNesterov Accelerated Gradient, NAGAdaptive Gradient, AdaGradAdaDeltaRMSpropAdam梯度下降Gradient Descent基本思想梯度下降(Gradient Descent)是一种优化算法,用于寻找可微函数的局部最小值。其目标是通过迭代调整模型参数,最小化代价函数(Cost Function)。以下是梯度下降算法的基本思想:目标:找到模型参数的最优值,使得代价函数达到最小值。方法:利用函数的梯度(即导数)来确定参数调整的方向。梯度(Gradient)指的是函数在某一点的梯度是一个向量,指向函数增长最快的方向。到底什么是梯度?"A gradient measures how much the output of a function changes if you change the inputs a little bit." — Lex Fridman (MIT)梯度下降的核心思想是:沿着梯度的反方向(即函数下降最快的方向)调整参数。算法步骤初始化参数:选择初始参数值(通常随机初始化或使用特定策略)。设置学习率(Learning Rate),学习率决定了每次迭代中参数更新的步长。计算梯度:计算代价函数对每个参数的偏导数,得到梯度向量。更新参数:使用梯度下降更新公式调整参数:θ=θ−α⋅∇J(θ) \theta = \theta - \alpha \cdot \nabla J(\theta) θ=θ−α⋅∇J(θ)其中:\theta\ 是模型参数。\alpha\ 是学习率。∇J(θ)\nabla J(\theta)∇J(θ) 是代价函数 J(\theta)\ 对参数 \theta\ 的梯度。重复计算梯度和更新参数的过程,直到满足终止条件(如收敛或达到最大迭代次数)。收敛条件收敛判断:当代价函数的变化量小于某个阈值,或梯度的范数小于某个阈值时,认为算法收敛。停止条件:可以设置最大迭代次数,防止算法陷入无限循环。学习率的作用学习率的选择:如果学习率过大,可能导致参数更新过度,使代价函数无法收敛甚至发散。如果学习率过小,会使收敛速度过慢,增加训练时间。动态调整学习率:在训练过程中,可以采用动态调整学习率的策略,如学习率衰减(Learning Rate Decay),以加速收敛。通过图像的形式描述不同学习率的过程:可以看到学习率不能过大或过小。for i in range(nb_epochs): params_grad = evaluate_gradient(loss_function, data, params) params = params - learning_rate params_grad优缺点Advantages:计算简单实现容易容易理解Disadvantages:可能陷入局部最小值。权重是在计算整个数据集的梯度之后才更新的。因此,如果数据集太大,可能需要花费较长时间才能收敛到最小值。需要大量内存来计算整个数据集的梯度。案例线性回归+梯度下降:import numpy as npimport matplotlib.pyplot as pltnp.random.seed(0)X = 2 np.random.rand(100, 1) y = 4 + 3 X + np.random.randn(100, 1) Xb = np.c[np.ones((100, 1)), X] w = np.random.randn(2, 1) learning_rate = 0.01 n_iterations = 1000 for iteration in range(n_iterations): y_pred = X_b.dot(w) gradients = 2 / len(X_b) X_b.T.dot(y_pred - y) w = w - learning_rate * gradients if iteration % 100 == 0: loss = np.mean((y_pred - y) * 2) print(f"Iteration {iteration}: Loss = {loss}")print(f"Optimal parameters: w = {w[1][0]}, b = {w[0][0]}")plt.scatter(X, y, color='blue', label='Data points')plt.plot(X, X_b.dot(w), color='red', label='Fitted line')plt.xlabel('X')plt.ylabel('y')plt.legend()plt.show()每100次输出一次Loss值:Iteration 0: Loss = 82.6630580098186Iteration 100: Loss = 1.03474536503957Iteration 200: Loss = 1.0053717575274455Iteration 300: Loss = 0.9992232039352024Iteration 400: Loss = 0.9959987975896929Iteration 500: Loss = 0.9943068126783625Iteration 600: Loss = 0.9934189550799246Iteration 700: Loss = 0.9929530578311317Iteration 800: Loss = 0.9927085814119238Iteration 900: Loss = 0.992580294070233Optimal parameters: w = 2.9827303563323175, b = 4.20607718142562随机梯度下降Stochastic Gradient Descent (SGD)定义随机梯度下降(SGD)是一种优化算法,用于在训练机器学习模型时最小化损失函数。它是梯度下降算法的一种扩展,通过每次只使用一个训练样本(或少量样本)来计算梯度,从而减少计算量和内存需求。基本思想目标:通过迭代更新模型参数,最小化损失函数 J(θ)。方法:每次只使用一个训练样本 (x(i),y(i)) 来计算梯度,并更新参数。在SGD算法中,每次只取一个数据点来计算导数。SGD(随机梯度下降)针对每个训练样本 x(i) 和对应的标签 y(i) 进行参数更新。θ = θ − α⋅∂(J(θ;x(i),y(i)))/∂θ对比随机梯度下降SGD和梯度下降GD:在左边,随机梯度下降(SGD,其中每步 m=1)为每个样本进行一次梯度下降步骤;而在右边是完整的梯度下降(每整个训练集进行1次步骤)。观察结果表明,在SGD中,更新需要比梯度下降更多的迭代次数才能到达最小值。在右边,梯度下降到达最小值的步数更少,但SGD算法更“嘈杂”,需要更多的迭代次数。在TensorFlow中的使用:from tensorflow.keras.optimizers import SGD optimizer = SGD(learning_rate=0.01, momentum=0.0, nesterov=False算法步骤初始化参数:迭代更新:对于每个训练样本 (x(i),y(i)):计算损失函数 J(θ) 关于参数 θ 的梯度 ∇J(θ)。更新参数:重复步骤:重复上述过程,直到满足终止条件(如收敛或达到最大迭代次数)。SGD的代码片段只是在训练样本上增加了一个循环,并针对每个样本计算梯度。for i in range(nb_epochs): np.random.shuffle(data) for example in data: params_grad = evaluate_gradient(loss_function, example, params) params = params - learning_rate params_grad优缺点Advantages:计算效率高:每次只处理一个样本,计算量小,适合大规模数据集。内存需求低:不需要一次性加载整个数据集,节省内存。实时更新:参数在每个样本后更新,能够快速响应数据的变化。Disadvantages:噪声大:每次更新基于单个样本,梯度估计可能不准确,导致更新过程“嘈杂”。收敛速度慢:可能需要更多迭代次数才能收敛到最小值。易陷入局部最小值:由于更新的随机性,可能在局部最小值附近徘徊。随机梯度下降(SGD)通过每次只处理一个样本,减少了计算量和内存需求,同时保持了快速的参数更新能力。虽然存在一定的噪声和收敛速度较慢的问题,但通过适当的调整学习率和优化策略,SGD在许多实际应用中表现出色。案例线性回归+随机梯度下降:import numpy as npimport matplotlib.pyplot as pltnp.random.seed(0)X = 2 np.random.rand(100, 1) y = 4 + 3 X + np.random.randn(100, 1) Xb = np.c[np.ones((100, 1)), X] w = np.random.randn(2, 1) learning_rate = 0.01 n_iterations = 1000 m = len(X_b) for iteration in range(n_iterations): random_index = np.random.randint(m) xi = X_b[random_index:random_index + 1] yi = y[random_index:random_index + 1] gradients = 2 xi.T.dot(xi.dot(w) - yi) w = w - learning_rate gradients if iteration % 100 == 0: y_pred = X_b.dot(w) loss = np.mean((y_pred - y) 2) print(f"Iteration {iteration}: Loss = {loss}")print(f"Optimal parameters: w = {w[1][0]}, b = {w[0][0]}")plt.scatter(X, y, color='blue', label='Data points')plt.plot(X, Xb.dot(w), color='red', label='Fitted line')plt.xlabel('X')plt.ylabel('y')plt.legend()plt.show()Iteration 0: Loss = 69.02554947814826Iteration 100: Loss = 1.0436708721159689Iteration 200: Loss = 1.041294923577367Iteration 300: Loss = 1.010387875806174Iteration 400: Loss = 1.0070132665208844Iteration 500: Loss = 1.091486693566191Iteration 600: Loss = 0.9986328028787116Iteration 700: Loss = 1.0340118681106303Iteration 800: Loss = 0.9984103484305885Iteration 900: Loss = 1.0082506511215945Optimal parameters: w = 3.052173302887621, b = 4.352670551847081小批量随机梯度下降(Mini Batch Stochastic Gradient Descent, MB-SGD)小批量随机梯度下降(Mini Batch Stochastic Gradient Descent, MB-SGD)是梯度下降算法的一种改进,结合了批量梯度下降(Batch Gradient Descent)和随机梯度下降(Stochastic Gradient Descent, SGD)的优点。它通过每次使用一个小批量(Mini-Batch)的数据来计算梯度,从而在计算效率和稳定性之间取得平衡。基本思想目标:通过迭代更新模型参数,最小化损失函数 J(θ)。方法:每次从训练集中随机抽取一个小批量的数据(通常包含几十个样本),计算该小批量数据的梯度,并更新参数。算法步骤初始化参数:随机初始化模型参数θ。设置学习率α。定义小批量的大小m(通常为2的幂,如32、64、128等)。划分数据集:将整个训练集划分为若干个小批量,每个小批量包含 m 个样本。∇J(θ)=1m∑i=1m∇J(θ;x(i),y(i))\nabla J(\theta) = \frac{1}{m} \sum{i=1}^{m} \nabla J(\theta; x^{(i)}, y^{(i)})∇J(θ)=m1​i=1∑m​∇J(θ;x(i),y(i))θ=θ−α⋅∇J(θ)\theta = \theta - \alpha \cdot \nabla J(\theta)θ=θ−α⋅∇J(θ)重复步骤:重复上述过程,直到满足终止条件(如收敛或达到最大迭代次数)。使用mini-batches参数:for i in range(nb_epochs): np.random.shuffle(data) for batch in get_batches(data, batch_size=50): params_grad = evaluate_gradient(loss_function, batch, params) params = params - learning_rate params_gradAdvantages:相比于标准的随机梯度下降(SGD)算法,收敛的时间复杂度更低。Disadvantages::与梯度下降(GD)算法相比,小批量随机梯度下降(MB-SGD)的更新过程更加“嘈杂”。比梯度下降(GD)算法需要更长时间才能收敛。可能会陷入局部最小值。案例逻辑回归+小批量随机梯度下降:import numpy as npimport matplotlib.pyplot as pltnp.random.seed(42)X = np.random.randn(100, 2) y = (X[:, 0] + X[:, 1] > 0).astype(int) Xb = np.c[np.ones((100, 1)), X]theta = np.random.randn(3)def sigmoid(z): return 1 / (1 + np.exp(-z))def mini_batch_gradient_descent(X, y, theta, learning_rate=0.01, batch_size=10, epochs=100): m = len(y) for epoch in range(epochs): indices = np.random.permutation(m) X_shuffled = X[indices] y_shuffled = y[indices] for i in range(0, m, batch_size): xi = X_shuffled[i:i+batch_size] yi = y_shuffled[i:i+batch_size] gradients = xi.T.dot(sigmoid(xi.dot(theta)) - yi) / len(yi) theta -= learning_rate gradients if epoch % 10 == 0: loss = -np.mean(yi np.log(sigmoid(xi.dot(theta))) + (1 - yi) np.log(1 - sigmoid(xi.dot(theta)))) print(f'Epoch {epoch}, Loss: {loss}') return thetalearning_rate = 0.1batch_size = 10epochs = 100theta = mini_batch_gradient_descent(X_b, y, theta, learning_rate, batch_size, epochs)print("Trained parameters:", theta)def predict(X, theta): probabilities = sigmoid(X.dot(theta)) return probabilities >= 0.5predictions = predict(X_b, theta)accuracy = np.mean(predictions == y)print(f"Accuracy: {accuracy 100:.2f}%")def plot_decision_boundary(X, y, theta): plt.figure(figsize=(10, 6)) plt.scatter(X[:, 1], X[:, 2], c=y, marker='o', cmap='winter') x1_min, x1_max = X[:, 1].min() - 1, X[:, 1].max() + 1 x2_min, x2_max = X[:, 2].min() - 1, X[:, 2].max() + 1 xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max, 100), np.linspace(x2_min, x2max, 100)) grid = np.c[np.ones((xx1.ravel().shape[0], 1)), xx1.ravel(), xx2.ravel()] Z = sigmoid(grid.dot(theta)).reshape(xx1.shape) plt.contourf(xx1, xx2, Z, alpha=0.3, cmap='winter') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.title('Decision Boundary and Data Points') plt.show()plot_decision_boundary(X_b, y, theta)Epoch 0, Loss: 0.424476004585312Epoch 10, Loss: 0.2950600705690528Epoch 20, Loss: 0.19517955198064607Epoch 30, Loss: 0.07305972705615815Epoch 40, Loss: 0.15634434000393793Epoch 50, Loss: 0.09763985480946141Epoch 60, Loss: 0.22901934823973308Epoch 70, Loss: 0.06952644778550963Epoch 80, Loss: 0.12401363486230814Epoch 90, Loss: 0.05268405861771795Trained parameters: [-0.17306619 3.80492488 3.80846526]Accuracy: 99.00%SGD with Momentum基本原理小批量随机梯度下降(MB-SGD)算法的一个主要缺点是权重更新非常“嘈杂”。带动量的随机梯度下降(SGD with Momentum)通过降噪梯度克服了这一缺点,它在传统的随机梯度下降(SGD)的基础上引入了动量Momentum机制,在每次更新时,通过考虑历史梯度信息来加速收敛并减少噪声。算法步骤初始化参数随机初始化模型参数 θ\thetaθ初始化动量参数 v (通常初始化为零向量)。设置学习率 α\alphaα 和动量系数γ\gamma γ(通常取值为0.9或0.99)。迭代更新对于每个训练样本(x(i),y(i))(x^{(i)}, y^{(i)}) (x(i),y(i)) 或小批量数据:计算当前梯度∇J(θ)\nabla J(\theta)∇J(θ):∇J(θ)=∂J(θ;x(i),y(i))∂θ\nabla J(\theta) = \frac{\partial J(\theta; x^{(i)}, y^{(i)})}{\partial \theta}∇J(θ)=∂θ∂J(θ;x(i),y(i))​更新动量参数 vvv:v=γ⋅v−α⋅∇J(θ)v = \gamma \cdot v - \alpha \cdot \nabla J(\theta)v=γ⋅v−α⋅∇J(θ)其中:\gamma\ 是动量系数,控制历史梯度的衰减速度。\alpha\ 是学习率,控制更新步长。更新参数θ\thetaθ:θ=θ+v\theta = \theta + vθ=θ+v重复步骤重复上述过程,直到满足终止条件(如收敛或达到最大迭代次数)。优缺点Advantages:减少噪声:通过考虑历史梯度信息,减少了更新过程中的噪声。加速收敛:动量机制帮助模型更快地收敛到最小值,特别是在损失函数的“山谷”中。减少振荡:在接近最小值时,动量机制可以减少参数更新的振荡。Disadvantages:超参数选择:需要选择合适的动量系数 \gamma\ 和学习率 α\alphaα,这可能需要一些实验和调整。计算复杂度:虽然每次更新的计算量与SGD相当,但引入动量机制会增加一些额外的计算开销。在TensorFlow中的使用:from tensorflow.keras.optimizers import SGD optimizer = SGD(learning_rate=0.01, momentum=0.9, nesterov=False)案例import numpy as npimport matplotlib.pyplot as pltnp.random.seed(42)X = np.random.randn(200, 2)y = np.logical_xor(X[:, 0] > 0, X[:, 1] > 0)y = y.astype(int)def sigmoid(x): return 1 / (1 + np.exp(-x))def sigmoid_derivative(x): return x (1 - x)input_size = 2hidden_size = 4output_size = 1W1 = np.random.randn(input_size, hidden_size)b1 = np.zeros((1, hidden_size))W2 = np.random.randn(hidden_size, output_size)b2 = np.zeros((1, output_size))learning_rate = 0.01momentum = 0.9n_iterations = 10000velocity_W1 = np.zeros_like(W1)velocity_b1 = np.zeros_like(b1)velocity_W2 = np.zeros_like(W2)velocity_b2 = np.zeros_like(b2)for iteration in range(n_iterations): hidden_input = np.dot(X, W1) + b1 hidden_output = sigmoid(hidden_input) output_input = np.dot(hidden_output, W2) + b2 output = sigmoid(output_input) loss = np.mean((output - y.reshape(-1, 1)) 2) d_output = (output - y.reshape(-1, 1)) sigmoid_derivative(output) d_hidden = np.dot(d_output, W2.T) sigmoid_derivative(hidden_output) dW2 = np.dot(hidden_output.T, d_output) db2 = np.sum(d_output, axis=0, keepdims=True) dW1 = np.dot(X.T, d_hidden) db1 = np.sum(d_hidden, axis=0, keepdims=True) velocity_W2 = momentum velocity_W2 + learning_rate dW2 velocity_b2 = momentum velocity_b2 + learning_rate db2 velocity_W1 = momentum velocity_W1 + learning_rate dW1 velocity_b1 = momentum velocity_b1 + learning_rate db1 W2 = W2 - velocity_W2 b2 = b2 - velocity_b2 W1 = W1 - velocity_W1 b1 = b1 - velocity_b1 if iteration % 1000 == 0: print(f"Iteration {iteration}, Loss: {loss}")def plot_decision_boundary(X, y, model): x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01), np.arange(y_min, ymax, 0.01)) Z = model(np.c[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) plt.contourf(xx, yy, Z, alpha=0.8) plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k', marker='o') plt.xlabel("X1") plt.ylabel("X2") plt.title("Decision Boundary") plt.show()def predict(X): hidden_input = np.dot(X, W1) + b1 hidden_output = sigmoid(hidden_input) output_input = np.dot(hidden_output, W2) + b2 output = sigmoid(output_input) return (output > 0.5).astype(int)plot_decision_boundary(X, y, predict)Iteration 0, Loss: 0.24866396311697422Iteration 1000, Loss: 0.059649335744367704Iteration 2000, Loss: 0.05161391737798015Iteration 3000, Loss: 0.047237416656551276Iteration 4000, Loss: 0.043973210315089856Iteration 5000, Loss: 0.041256973919964544Iteration 6000, Loss: 0.03895073389030161Iteration 7000, Loss: 0.037002859681704706Iteration 8000, Loss: 0.035360650108840486Iteration 9000, Loss: 0.03396667708959878Nesterov Accelerated Gradient, NAG基本原理动量法每下降一步都是由前面下降方向的一个累积和当前点的梯度方向组合而成。于是一位大神(Nesterov)就开始思考,既然每一步都要将两个梯度方向(历史梯度、当前梯度)做一个合并再下降,那为什么不先按照历史梯度往前走那么一小步,按照前面一小步位置的“超前梯度”来做梯度合并呢?如此一来,小球就可以先不管三七二十一先往前走一步,在靠前一点的位置看到梯度,然后按照那个位置再来修正这一步的梯度方向。如此一来,有了超前的眼光,小球就会更加”聪明“, 这种方法被命名为Nesterov accelerated gradient 简称 NAGNesterov Accelerated Gradient(NAG)是一种改进的梯度下降优化算法,它在传统的动量优化算法的基础上引入了“前瞻性”更新机制,从而提高了收敛速度并减少了震荡。NAG的核心思想是在计算梯度时:先根据之前的动量方向进行一个预期的更新然后再根据这个预期位置计算梯度。这种方法使得参数更新更加“前瞻”,避免了传统动量方法中可能出现的过冲问题。数学公式记vtvtvt为第t次迭代梯度的累积:初始化:v0=0v_0 = 0v0​=0第一次迭代: v1=η∇θJ(θ)v1 = \eta \nabla\theta J(\theta) v1​=η∇θ​J(θ)第二次迭代:v2=γv1+η∇θJ(θ−γv1)v_2 = \gamma v1 + \eta \nabla\theta J(\theta - \gamma v_1)v2​=γv1​+η∇θ​J(θ−γv1​)一般迭代规则(第 ( t ) 次迭代):vt=γvt−1+η∇θJ(θ−γvt−1)vt = \gamma v{t-1} + \eta \nabla\theta J(\theta - \gamma v{t-1})vt​=γvt−1​+η∇θ​J(θ−γvt−1​)其中:η\etaη 是学习率γ\gammaγ 是动量系数∇θJ(θ)\nabla_\theta J(\theta)∇θ​J(θ)是损失函数JJJ关于参数θ\thetaθ的梯度在TensorFlow中的使用:from tensorflow.keras.optimizers import SGD optimizer = SGD(learning_rate=0.01, momentum=0.9, nesterov=True)案例import numpy as npimport matplotlib.pyplot as pltclass LogisticRegressionNAG: def init(self, learning_rate=0.01, momentum=0.9, n_iters=1000): self.learning_rate = learning_rate self.momentum = momentum self.n_iters = n_iters self.weights = None self.bias = None self.v_w = None self.v_b = None def sigmoid(self, z): return 1 / (1 + np.exp(-z)) def fit(self, X, y): n_samples, n_features = X.shape self.weights = np.zeros(n_features) self.bias = 0 self.v_w = np.zeros(n_features) self.vb = 0 losses = [] for in range(self.n_iters): temp_weights = self.weights - self.momentum self.v_w temp_bias = self.bias - self.momentum self.v_b linear_model = np.dot(X, temp_weights) + temp_bias y_pred = self.sigmoid(linear_model) dw = (1 / n_samples) np.dot(X.T, (y_pred - y)) db = (1 / n_samples) np.sum(y_pred - y) self.v_w = self.momentum self.v_w + self.learning_rate dw self.v_b = self.momentum self.v_b + self.learning_rate db self.weights = self.weights - self.v_w self.bias -= self.v_b loss = np.mean(-y np.log(y_pred) - (1 - y) np.log(1 - y_pred)) losses.append(loss) plt.figure() plt.plot(range(self.n_iters), losses, label="Loss") plt.title("Loss Curve for Logistic Regression with NAG") plt.xlabel("Iterations") plt.ylabel("Loss") plt.legend() plt.show() def predict(self, X): linear_model = np.dot(X, self.weights) + self.bias y_pred = self.sigmoid(linear_model) return np.where(y_pred > 0.5, 1, 0)np.random.seed(0)X = 2 np.random.rand(100, 1)y = (X > 1).astype(int).ravel() model = LogisticRegressionNAG(learning_rate=0.1, momentum=0.9, n_iters=1000)model.fit(X, y)X_new = np.array([[0], [2]])y_pred = model.predict(X_new)plt.figure()plt.scatter(X, y, color='blue', label="Actual")plt.scatter(X_new, ypred, color='red', label="Predicted")plt.title("Logistic Regression with NAG")plt.xlabel("X")plt.ylabel("y")plt.legend()plt.show()不同迭代次数下的模型损失loss经过NAG优化后的模型的预测效果(红点)Adaptive Gradient, AdaGrad对于之前讨论的所有算法,学习率都是固定的。Adaptive Gradient(AdaGrad)算法是一种自适应学习率的优化算法,于2011年由Duchi等人提出,它能够根据参数的历史梯度自适应地调整学习率。核心思想AdaGrad的核心思想是对每个参数的学习率进行适应性调整,从而实现对参数的不同历史梯度的平方和进行自适应调整。具体来说,AdaGrad通过累积过去所有梯度的平方和来为每个参数动态调整学习率,使得较少更新频繁出现的特征参数具有更大的学习率,而较频繁更新的特征参数则具有更小的学习率。数学原理梯度计算:g=∇θk−1L(θ)g = \nabla{\theta{k-1}} L(\theta)g=∇θk−1​​L(θ)计算损失函数 L(θ) L(\theta)L(θ) 关于参数 θ\theta θ 在第 k−1k-1k−1 次迭代时的梯度 gg g ;其中∇θ\nabla{\theta}∇θ​表示梯度运算符,θk−1\theta_{k-1}θk−1​表示上一步的参数值。累积梯度平方和更新:rk=rk−1+g⊙grk = r{k-1} + g \odot grk​=rk−1​+g⊙g在第 k 次迭代时,将当前梯度 g 的平方(逐元素相乘)累加到之前的累积梯度平方和 rk−1 r_{k-1} rk−1​ 上,得到新的累积梯度平方和 rk r_k rk​ 。其中,⊙\odot⊙表示元素乘法(即Hadamard乘积)。自适应学习率计算:η=η0rk+ϵ\eta = \frac{\eta_0}{\sqrt{r_k + \epsilon}}η=rk​+ϵ​η0​​计算自适应学习率,其中 η0 \eta_0 η0​ 是初始学习率, rk r_k rk​ 是当前的累积梯度平方和, ϵ \epsilon ϵ 是一个小的正数(防止除零错误)。通过累积梯度平方和rkr_krk​来调整学习率ϵ\epsilonϵ;这样的调整使得学习率对于出现频繁的特征会更小,而对于稀疏特征会更大,有助于提高模型在稀疏数据上的性能参数更新:θk=θ−ηg\theta_k = \theta - \eta gθk​=θ−ηg完整形式:θk=θ−η0rk+ϵ∗∇θk−1L(θ)\theta_k = \theta - \frac{\eta_0}{\sqrt{r_k + \epsilon}} \nabla{\theta{k-1}} L(\theta)θk​=θ−rk​+ϵ​η0​​∗∇θk−1​​L(θ)使用计算出的自适应学习率 η\eta η 和梯度 g gg 来更新参数 θ\thetaθ 到新值 θk\theta_kθk​ ,目的是减少损失函数 L(θ)L(\theta)L(θ) 的值。优缺点Advantage: 不需要手动更新学习率Disadvantage:案例以下是一个基于Adaptive Gradient(AdaGrad)优化算法的Python代码示例,并附带了优化过程的可视化效果。我们将使用一个简单的二维函数 f(x)=x12+x22f(x)=x_1^2+x_2^2f(x)=x12​+x22​ 来展示优化过程,并通过Matplotlib绘制优化路径。import numpy as npimport matplotlib.pyplot as pltdef f(x): return x[0]2 + x[1]2 def grad_f(x): return np.array([2x[0], 2x[1]]) def Adagrad(x_init, step_size, n_iters): eta = step_size G_t = 0 eps = 1e-8 theta = np.tile(x_init, (n_iters, 1)) z = np.tile([f(x_init)], n_iters) for k in range(1, n_iters): g_t = grad_f(theta[k-1]) G_t += g_t2 theta[k] = theta[k-1] - eta * g_t / (np.sqrt(G_t) + eps) z[k] = f(theta[k]) return theta, zx_init = np.array([1.0, 1.0]) step_size = 0.1 n_iters = 50 theta, z = Adagrad(x_init, step_size, n_iters)x1 = np.linspace(-2, 2, 400)x2 = np.linspace(-2, 2, 400)X1, X2 = np.meshgrid(x1, x2)Z = X12 + X22plt.figure(figsize=(10, 6))contour = plt.contour(X1, X2, Z, levels=50, cmap='viridis')plt.clabel(contour, inline=True, fontsize=8)plt.title("AdaGrad Optimization Path")plt.xlabel("x1")plt.ylabel("x2")plt.plot(theta[:, 0], theta[:, 1], 'r-', linewidth=2, label="AdaGrad Path")plt.scatter(theta[:, 0], theta[:, 1], c='r', s=50, zorder=5)plt.scatter([0], [0], c='g', s=100, label="Global Minimum")plt.legend()plt.show()AdaDelta基本原理AdaGrad存在的问题是,随着迭代次数的增加,学习率会变得非常小,这导致收敛速度变慢。为了避免这个问题,AdaDelta算法采用了一种想法,即取梯度的指数衰减平均值。AdaDelta是Adagrad的一个更稳健的扩展,它根据梯度更新的移动窗口来调整学习率,而不是累积所有过去的梯度。这样,即使进行了很多次更新,AdaDelta也能够继续学习。AdaDelta算法并没有低效地存储过去的平方梯度,而是将梯度的累积和递归地定义为所有过去平方梯度的衰减平均值。在时间步 ttt 的运行平均E[g2]tE[g^2]_tE[g2]t​仅依赖于先前的累积平均值和当前的梯度:在AdaDelta算法中不需要设置默认的学习率:优缺点Advantage:AdaDelta不需要手动设置学习率,因为它会根据迭代过程中的梯度信息来自适应地调整学习率。对稀疏数据表现良好:在处理稀疏数据时,AdaDelta能够动态调整学习率,防止学习率过快减小,从而避免收敛速度变慢的问题。减少对初始条件的敏感性:AdaDelta对初始化条件的敏感性较低,因为它会根据训练过程中的情况进行自适应调整。Disadvantage:收敛速度:与一些更新的优化方法(如Adam)相比,AdaDelta可能不会那么快地收敛。案例对经典的Rosenbrock函数进行优化:f(x,y)=(1−x)2+100(y−x2)2f(x,y)=(1-x)^2+100(y-x^2)^2f(x,y)=(1−x)2+100(y−x2)2这个函数只有一个全部最小值点f(x,y)=(1,1)f(x,y)=(1,1)f(x,y)=(1,1),并且在 (0,0)(0,0) (0,0)附近有一个较深的山谷import numpy as npimport matplotlib.pyplot as pltdef f(x): return (1 - x[0])*2 + 100 (x[1] - x[0]2)2def grad_f(x): return np.array([ -2 (1 - x[0]) - 400 x[0] * (x[1] - x[0]*2), 200 (x[1] - x[0]2) ])def adadelta_optimizer(x_init, num_iter=100, rho=0.9, epsilon=1e-8): """ x_init: 初始参数 num_iter: 迭代次数 rho: 衰减率 epsilon: 无穷小量 """ x = x_init E_g2 = np.zeros_like(x) E_delta_x2 = np.zeros_like(x) x_history = [x.copy()] f_history = [f(x)] for i in range(num_iter): g = grad_f(x) E_g2 = rho E_g2 + (1 - rho) g2 delta_x = -np.sqrt(E_delta_x2 + epsilon) / np.sqrt(E_g2 + epsilon) g x += delta_x E_delta_x2 = rho E_delta_x2 + (1 - rho) delta_x2 x_history.append(x.copy()) f_history.append(f(x)) return np.array(x_history), np.array(f_history)x_init = np.array([-1.5, 1.5]) num_iter = 100 x_history, f_history = adadelta_optimizer(x_init, num_iter=num_iter)x1 = np.linspace(-2, 2, 400)x2 = np.linspace(-1, 3, 400)X1, X2 = np.meshgrid(x1, x2)Z = (1 - X1)2 + 100 (X2 - X12)2plt.figure(figsize=(10, 6))contour = plt.contour(X1, X2, Z, levels=50, cmap='viridis')plt.clabel(contour, inline=True, fontsize=8)plt.title("AdaDelta Optimization Path for Rosenbrock Function")plt.xlabel("x1")plt.ylabel("x2")plt.plot(x_history[:, 0], x_history[:, 1], 'r-', linewidth=2, label="AdaDelta Path")plt.scatter(x_history[:, 0], x_history[:, 1], c='r', s=50, zorder=5)plt.scatter([1], [1], c='g', s=100, label="Global Minimum")plt.legend()plt.show()RMSprop(Root Mean Square Propagation)基本原理RMSprop(Root Mean Square Propagation)算法是一种自适应学习率的优化算法,由 Geoffrey Hinton 提出,旨在解决梯度下降及其变体在优化过程中学习率固定不变时可能导致的收敛速度慢或不收敛的问题。RMSprop通过为每个参数动态调整学习率来改进这一点,特别适用于处理非平稳目标函数。基本原理:初始化:参数θ\thetaθ梯度平方的指数加权平均值E[g2]E[g^2]E[g2]为0设置学习率ηηη(比如0.001)设置衰减率 ρ(比如0.9)梯度计算在每次迭代中,计算损失函数 L(θ)L(θ)L(θ)关于参数θθθ的梯度ggg更新梯度平方的指数加权平均值E[g2]E[g^2]E[g2]:参数更新使用下面的参数更新参数θ\thetaθ其中,ϵϵϵ 是一个小的正数(例如10−810^{−8}10−8),用于防止除零错误。优缺点Advantage:自适应学习率:RMSprop为每个参数动态调整学习率,使得学习率与梯度的平方的指数加权平均值的平方根成反比。这有助于处理稀疏数据和非平稳目标函数。防止梯度爆炸:通过限制梯度更新的幅度,RMSprop有助于防止梯度爆炸问题。无需手动调整学习率:RMSprop自动调整学习率,减少了手动调整学习率的需求。Disadvantage:可能需要调整超参数:尽管RMSprop自动调整学习率,但仍然需要选择合适的衰减率 ρ 和学习率 η。在某些情况下可能不稳定:在某些情况下,RMSprop可能不如其他优化算法(如Adam)稳定。案例import numpy as npimport matplotlib.pyplot as pltdef f(x): return (x[0]2 + x[1] - 11)2 + (x[0] + x[1]2 - 7)2def grad_f(x): return np.array([ 4 x[0] (x[0]2 + x[1] - 11) + 2 * (x[0] + x[1]2 - 7), 2 * (x[0]2 + x[1] - 11) + 4 x[1] (x[0] + x[1]2 - 7) ])def rmsprop_optimizer(x_init, num_iter=100, learning_rate=0.001, rho=0.9, epsilon=1e-8): """ x_init: 初始参数 num_iter: 迭代次数 learning_rate: 学习率 rho: 衰减率 epsilon: 无穷小量 """ x = x_init v = np.zeros_like(x) x_history = [x.copy()] f_history = [f(x)] for i in range(num_iter): g = grad_f(x) v = rho v + (1 - rho) g*2 x -= learning_rate g / (np.sqrt(v) + epsilon) x_history.append(x.copy()) f_history.append(f(x)) return np.array(x_history), np.array(f_history)x_init = np.array([0.0, 0.0]) num_iter = 100 x_history, f_history = rmsprop_optimizer(x_init, num_iter=num_iter)x1 = np.linspace(-5, 5, 400)x2 = np.linspace(-5, 5, 400)X1, X2 = np.meshgrid(x1, x2)Z = (X12 + X2 - 11)2 + (X1 + X22 - 7)2plt.figure(figsize=(10, 6))contour = plt.contour(X1, X2, Z, levels=50, cmap='viridis')plt.clabel(contour, inline=True, fontsize=8)plt.title("RMSprop Optimization Path for Himmelblau Function")plt.xlabel("x1")plt.ylabel("x2")plt.plot(x_history[:, 0], x_history[:, 1], 'r-', linewidth=2, label="RMSprop Path")plt.scatter(x_history[:, 0], x_history[:, 1], c='r', s=50, zorder=5)plt.scatter([3], [2], c='g', s=100, label="Global Minimum")plt.scatter([-2.8051, -3.7793, 3.5844], [3.1313, -3.2832, -1.8481], c='b', s=100, label="Local Minima")plt.legend()plt.show()优化器9:Adaptive Moment Estimation(Adam)在机器学习中,Adam(Adaptive Moment Estimation,自适应矩估计)作为一种高效的优化算法脱颖而出。它旨在调整每个参数的学习率。基本原理Adam可以看作是结合了RMSprop和带动量的随机梯度下降(SGD with Momentum)的优化算法。Adam为每个参数计算自适应学习率。除了像AdaDelta和RMSprop那样存储过去梯度平方的指数衰减平均值vtv_tvt​之外,Adam还维护了一个过去梯度的指数衰减平均值mtm_tmt​,这与动量方法类似。如果说动量可以被看作是一个在斜坡上滚动的球,那么Adam的行为则像是一个带有摩擦的重球,因此它更倾向于在误差曲面的平坦最小值处停留。数学公式核心:Adam通过考虑梯度的一阶矩和二阶矩的移动平均值,改进了梯度下降的方法,使得它能够智能地适应每个参数的学习率。其中,mk和vkm_k和v_kmk​和vk​分别是梯度的一阶矩和二阶矩的估计,β1和β2\beta_1和\beta_2β1​和β2​是控制两个矩估计得指数衰减率,范围在0到1之间,通常设置为0.9和0.999。ϵ\epsilonϵ是个非常小的数(例如1e-8),防止除数为零。k是当前迭代的次数,用于做偏差校正。Adam代码的核心代码:def adam_update(parameters, gradients, m, v, t, lr=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8): for param, grad in zip(parameters, gradients): m[param] = beta1 m[param] + (1 - beta1) grad v[param] = beta2 v[param] + (1 - beta2) (grad 2) m_corrected = m[param] / (1 - beta1 t) v_corrected = v[param] / (1 - beta2 t) param_update = lr m_corrected / (np.sqrt(v_corrected) + epsilon) param -= param_update案例import numpy as npimport matplotlib.pyplot as pltdef func(X): return 5 X[0, 0]2 + 2 * X[1, 0]2 + 3 X[0, 0] - 10 X[1, 0] + 4def grad(X): grad_x1 = 10 X[0, 0] + 3 grad_x2 = 4 X[1, 0] - 10 return np.array([[grad_x1], [grad_x2]])class Adam: def init(self, func, grad, seed): self.func = func self.grad = grad self.seed = seed self.xPath = [] self.JPath = [] def get_solu(self, alpha=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8, zeta=1e-6, maxIter=1000): self.xPath = [] self.JPath = [] x = self.seed JVal = self.func(x) self.xPath.append(x) self.JPath.append(JVal) grad = self.grad(x) m, v = np.zeros(x.shape), np.zeros(x.shape) for k in range(1, maxIter + 1): m = beta1 m + (1 - beta1) grad v = beta2 v + (1 - beta2) (grad 2) m_hat = m / (1 - beta1 k) v_hat = v / (1 - beta2 k) x = x - alpha m_hat / (np.sqrt(v_hat) + epsilon) JVal = self.func(x) self.xPath.append(x) self.JPath.append(JVal) grad = self.grad(x) if np.linalg.norm(grad) < zeta: break return x, JValclass AdamPlot: @staticmethod def plot_fig(adamObj): x, JVal = adamObj.get_solu(alpha=0.1) xPath = np.array(adamObj.xPath) JPath = adamObj.JPath fig = plt.figure(figsize=(12, 6)) ax1 = plt.subplot(1, 2, 1) ax2 = plt.subplot(1, 2, 2) ax1.plot(JPath, "k.", markersize=5) ax1.plot(0, JPath[0], "go", label="Starting Point") ax1.plot(len(JPath) - 1, JPath[-1], "r", label="Solution") ax1.set_xlabel("Iteration") ax1.set_ylabel("Loss") ax1.legend() x1 = np.linspace(-10, 10, 300) x2 = np.linspace(-10, 10, 300) x1, x2 = np.meshgrid(x1, x2) f = 5 * x12 + 2 * x2*2 + 3 x1 - 10 x2 + 4 ax2.contour(x1, x2, f, levels=50) ax2.plot(xPath[:, 0, 0], xPath[:, 1, 0], "k--", lw=2) ax2.plot(xPath[0, 0, 0], xPath[0, 1, 0], "go", label="Starting Point") ax2.plot(xPath[-1, 0, 0], xPath[-1, 1, 0], "r", label="Solution") ax2.set_xlabel("$x_1$") ax2.set_ylabel("$x_2$") ax2.legend() plt.tight_layout() plt.show()if name == "main": seed = np.array([[1.0], [1.0]]) adamObj = Adam(func, grad, seed) AdamPlot.plot_fig(adamObj)参考1、www.kdnuggets.com/2020/12/opt…2、blog.csdn.net/qq_38156104…3、medium.com/@piyushkash…4、blog.csdn.net/m0_48923489…5、blog.csdn.net/m0_48923489…6、Adam: A Method for Stochastic OptimizationDiederik P. Kingma, Jimmy Ba

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

优化器 梯度下降 随机梯度下降 深度学习
相关文章