import torch.optim as optimfrom torch.utils.data import DataLoader, TensorDataset# Function to train the modeldef train_model(model, optimizer, criterion, train_loader, num_epochs=100): losses = [] for epoch in range(num_epochs): # loop for each training epoch for inputs, targets in train_loader: # Clear gradients before each batch to avoid accumulation optimizer.zero_grad() # Reset gradients to zero # Forward pass - get model predictions outputs = model(inputs) # Calculate loss based on predictions and targets loss = criterion(outputs, targets) # Backward pass - calculate gradients for parameter updates based on the loss loss.backward() # Update model parameters based on calculated gradients optimizer.step() # Track loss for monitoring training progress losses.append(loss.item()) return losses# Setup for trainingX, y = generate_sparse_data() # This function generates our sparse datasetinput_size = X.shape[1]train_size = int(0.8 * len(X))X_train, X_test = X[:train_size], X[train_size:]y_train, y_test = y[:train_size], y[train_size:]train_dataset = TensorDataset(X_train, y_train)train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)model = SparseNN(input_size)criterion = nn.BCELoss() # Assuming binary classification with BCE lossoptimizer = optim.Adagrad(model.parameters(), lr=0.01)# Train the modellosses = train_model(model, optimizer, criterion, train_loader)技术支持
在此设置中,我们:
创建 SparseNN 模型。
定义损失函数(交叉熵 Binary Cross Entropy)。
以 0.01 的学习率初始化 Adagrad 优化器。
训练 100 个 epoch,并记录每个 epoch 的损失。
现在,为了评估模型表现,我们编写一个评估函数:
defevaluate_model(model, test_loader): model.eval() correct =0 total =0with torch.no_grad():for inputs, targets in test_loader: outputs = model(inputs) predicted =(outputs >0.5).float() total += targets.size(0) correct +=(predicted == targets).sum().item() accuracy = correct / total return accuracy# Evaluate the modeltest_dataset = TensorDataset(X_test, y_test)test_loader = DataLoader(test_dataset, batch_size=32)accuracy = evaluate_model(model, test_loader)print(f"Adagrad Accuracy: {accuracy:.4f}")技术支持
import matplotlib.pyplot as pltplt.figure(figsize=(12,6))for name, data in results.items(): plt.plot(data['losses'], label=f"{name} (Accuracy: {data['accuracy']:.4f})")plt.xlabel('Epochs')plt.ylabel('Loss')plt.title('Training Loss Comparison (Sparse Data)')plt.legend()plt.show()技术支持
我们将所有代码片段合并,完整代码如下。
import torchimport torch.nn as nnimport torch.optim as optimfrom torch.utils.data import DataLoader, TensorDatasetimport matplotlib.pyplot as plt# Step 1: Generate sparse synthetic datadef generate_sparse_data(n_samples=1000, n_features=100, sparsity=0.95): X = torch.randn(n_samples, n_features) mask = torch.rand(n_samples, n_features) < sparsity X[mask] = 0 weights = torch.randn(n_features) y = (torch.matmul(X, weights) > 0).float().view(-1, 1) return X, y# Step 2: Define the neural networkclass SparseNN(nn.Module): def __init__(self, input_size): super(SparseNN, self).__init__() self.fc1 = nn.Linear(input_size, 50) self.fc2 = nn.Linear(50, 1) self.activation = nn.ReLU() self.output_activation = nn.Sigmoid() def forward(self, x): x = self.activation(self.fc1(x)) x = self.output_activation(self.fc2(x)) return x# Step 3: Train the modeldef train_model(model, optimizer, criterion, train_loader, num_epochs=100): losses = [] for epoch in range(num_epochs): for inputs, targets in train_loader: optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() losses.append(loss.item()) return losses# Step 4: Evaluate the modeldef evaluate_model(model, test_loader): model.eval() correct = 0 total = 0 with torch.no_grad(): for inputs, targets in test_loader: outputs = model(inputs) predicted = (outputs > 0.5).float() total += targets.size(0) correct += (predicted == targets).sum().item() accuracy = correct / total return accuracy# Step 5: Main function to run the experimentdef run_experiment(optimizer_class, lr=0.01): # Generate sparse data X, y = generate_sparse_data() input_size = X.shape[1] train_size = int(0.8 * len(X)) X_train, X_test = X[:train_size], X[train_size:] y_train, y_test = y[:train_size], y[train_size:] # Create data loaders train_dataset = TensorDataset(X_train, y_train) test_dataset = TensorDataset(X_test, y_test) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=32) # Initialize model and training components model = SparseNN(input_size) criterion = nn.BCELoss() # Use default hyperparameters for each optimizer if optimizer_class == optim.SGD: optimizer = optimizer_class(model.parameters(), lr=lr, momentum=0.9) elif optimizer_class == optim.RMSprop: optimizer = optimizer_class(model.parameters(), lr=lr, alpha=0.99) else: optimizer = optimizer_class(model.parameters(), lr=lr) # Train the model losses = train_model(model, optimizer, criterion, train_loader) # Evaluate the model accuracy = evaluate_model(model, test_loader) return losses, accuracy# Step 6: Compare Adagrad with other optimizersoptimizers = { 'Adagrad': optim.Adagrad, 'SGD': optim.SGD, 'Adam': optim.Adam, 'RMSprop': optim.RMSprop}results = {}for name, opt_class in optimizers.items(): losses, accuracy = run_experiment(opt_class) results[name] = {'losses': losses, 'accuracy': accuracy}# Step 7: Plot resultsplt.figure(figsize=(12, 6))for name, data in results.items(): plt.plot(data['losses'], label=f"{name} (Accuracy: {data['accuracy']:.4f})")plt.xlabel('Epochs')plt.ylabel('Loss')plt.title('Training Loss Comparison (Sparse Data)')plt.legend()plt.show()print("Final accuracies:")for name, data in results.items(): print(f"{name}: {data['accuracy']:.4f}")技术支持
想象您正下山行走,但地形不断变化——有的地方陡峭,有的地方平坦。如果您每一步的步幅都相同,可能会很吃力,或者花很长时间才能到达山脚。在机器学习中,梯度下降中也会遇到类似的挑战:如果对所有参数使用相同的学习率,学习过程可能会变慢。
这就是 Adagrad 派上用场的地方。它会根据参数在训练过程中变化的程度来调整每个参数的步长,从而帮助模型更快、更有效地学习,尤其是在不同特征具有不同尺度时。本文将介绍 Adagrad 的工作原理,并剖析其背后的数学。同时,我们还将展示如何使用PyTorch 实现它,并与其他优化器进行比较,帮助您为项目选择最合适的方案。
什么是 Adagrad?
Adagrad(Adaptive Gradient,自适应梯度)是一种用于机器学习的优化算法,特别适用于训练深度神经网络。它旨在根据每个参数的历史梯度为其自适应调整学习率。这种方法能够提高机器学习模型的训练效率,特别适合处理稀疏数据或不同参数具有不同收敛速度的场景。
Adagrad 会对不常见特征给予更高的学习率,对频繁出现的特征给予较低的学习率,因此非常适合处理稀疏数据。此外,它还能减少手动调整学习率的需求!
Adagrad 算法详解
我们来逐步拆解 Adagrad 算法:
步骤 1:初始化参数
首先,准备:
步骤 2:计算梯度
对于每个参数 0t 在时间步 t,计算损失函数关于该参数的梯度。这个梯度告诉我们应当如何以及朝哪个方向更新参数以减小损失。将该梯度记为 gt,其中 t 是当前时间步。
步骤 3:累积平方梯度
Adagrad 并不直接更新参数,而是为每个参数跟踪其平方梯度的累积和。对每个参数 i,在每个时间步计算并累积平方梯度。这个累积量记为 Gt。
Gt =Gt-1 + gt2
其中:
这个累积量帮助 Adagrad 根据每个参数被更新的程度,分别调整其学习率。
步骤 4:更新参数
现在我们有了累积的平方梯度 Gt,即可更新每个参数 0t。Adagrad 的更新规则如下:
具体含义如下:
这个项:
会有效降低累计梯度较大的参数的学习率;相反,累计梯度较小的参数会拥有更大的学习率。
Adagrad 的公式会自动自适应学习率,具体如下:
在 PyTorch 中实现 Adagrad
PyTorch 提供了 Adagrad 的内置实现,可通过
torch.optim模块访问。首先使用 pip 安装 PyTorch:通过我们的免费课程学习如何构建您的第一个神经网络:Introduction to Deep Learning with PyTorch。
以下是在 PyTorch 中实现 Adagrad 优化器的分步指南:
步骤 1:导入必要的库
步骤 2:定义一个简单的神经网络
步骤 3:初始化模型与优化器
步骤 4:训练循环
在这段代码中,我们使用 AdaGrad 优化器训练了一个简单的线性模型。学习率被设为 0.01,但 AdaGrad 会基于累积的平方梯度为每个参数分别调整学习率。运行代码,您将看到类似这样的输出:
输出显示了每个 epoch 的损失,体现了模型的学习效果。初始时损失较高,但随着训练推进,损失通常会下降,表示模型在改进。使用随机输入时出现一些波动是正常的,但总体上,模型会逐步学到更好的预测。
如果您想深入了解深度学习,以及这一机器学习分支如何改变世界,请查看我们的课程Deep Learning in Python。
Adagrad 与其他优化器对比
我们将 Adagrad 与其他优化器进行比较,例如Adam、SGD 和 RMSProp。
Adagard 与 Adam
学习率自适应
Adagrad 和Adam 都会为每个参数自适应调整学习率,但方式不同。Adagrad 根据平方梯度之和缩放学习率,这可能导致学习率随时间衰减。相较之下,Adam 使用梯度的一阶与二阶矩,从而在训练过程中保持更稳定的学习率。
处理稀疏数据:
Adagrad 尤其适合处理稀疏数据,因为它能为不常见特征分配更大的学习率。这使其非常适用于自然语言处理等常见稀疏梯度的应用。虽然 Adam 也能处理稀疏数据,但由于其在更广泛应用中的稳健性与效率,往往更受青睐。
收敛与性能
在实践中,Adam 往往因更快的收敛和更优的性能而受到青睐,尤其在深度学习模型和大数据集上。它利用动量与偏置校正,有助于避免陷入局部极小值与鞍点,而这可能是 Adagrad 的局限所在。
了解用于图像与序列数据建模的基础深度学习架构,如 CNN、RNN、LSTM 和 GRU。请查看我们的课程Intermediate Deep Learning with PyTorch。
Adagrad 与其他梯度下降算法
我们将 Adagrad 与 SGD 和 RMSProp 进行比较
AdaGrad 与随机梯度下降(SGD)
随机梯度下降是一种基础优化算法,通过损失函数相对于参数的梯度来更新模型参数。它以简单易实现著称。然而,SGD 的学习率是常数,在处理稀疏数据或特征尺度不一致时可能存在局限。
AdaGrad 与 RMSProp
RMSProp 是对 AdaGrad 的改进,引入了衰减因子来缓解学习率不断减小的问题。借助这一衰减因子,RMSProp 能在整个训练过程中维持更稳定的学习率。
我们来总结一下 Adagrad、SGD 和 RMSProp 的优缺点:
优化器
优点
缺点
Adagrad
1. 自动为每个参数自适应学习率。
2. 对稀疏数据有效。
3. 无需手动调节学习率。
1. 学习率随时间衰减,可能导致过早收敛。
2. 训练后期可能过慢。
SGD
1. 简单易实现。
2. 由于随机性,有助于跳出局部极小值。
1. 更新方差高,可能导致不稳定。
2. 需要仔细调节学习率与动量。
RMSProp
1. 基于平方梯度的移动平均自适应学习率。
2. 适合非平稳环境。
1. 需要仔细调节超参数。
2. 本身不包含动量机制。
常见用例与局限
我们来探讨 AdaGrad 在不同场景下的用例与局限。
Adagrad 的用例
Adagrad 为每个参数自适应学习率的能力,使其非常适合以下情形:
Adagrad 的局限
虽然 Adagrad 在特定情境下非常强大,但也存在一些局限:
缓解局限性的策略
您可以使用以下策略来应对 Adagrad 的局限:
动手练习:使用 Adagrad 优化模型
我们将构建一个简单的神经网络,使用 Adagrad 优化器进行训练,并与其他常用优化器的性能进行对比。
如果您想进一步学习 PyTorch,请查看我们的指南:PyTorch Tutorial: Building a Simple Neural Network From Scratch.
我们先在 PyTorch 中创建一个基础的神经网络模型。我们将设计一个适用于稀疏数据的二分类网络,Adagrad 在这类任务中表现突出。
该网络包含一个输入层、一个包含 50 个神经元的隐藏层和一个单神经元的输出层。隐藏层使用 ReLU 激活函数,输出层使用 Sigmoid 激活,适用于二分类任务。
接下来,我们用 Adagrad 优化器来设置训练流程。我们会编写一个训练函数:
在此设置中,我们:
现在,为了评估模型表现,我们编写一个评估函数:
该函数用于计算模型在测试集上的准确率。
接下来,我们与其他常见优化器(SGD、Adam、RMSprop)进行对比。我们将创建一个函数来为每个优化器运行实验:
运行上述代码后,我们得到如下结果:
这些结果显示,在我们的稀疏数据任务中,Adagrad 表现最佳,准确率达到 95.00%。这说明 Adagrad 非常适合稀疏数据场景。Adam 紧随其后,而 SGD 与 RMSprop 在本例中的表现相对较弱。
为了可视化训练过程,我们可以绘制损失曲线:
我们将所有代码片段合并,完整代码如下。
结果如下:
结论
由于具备自适应学习率,Adagrad 在处理稀疏数据时非常有用。我们构建了一个基础神经网络并用 Adagrad 进行训练,随后与其他知名优化算法进行了对比。结果强烈支持 Adagrad,显示其在稀疏数据上更具优势。
我们还精选了一些关于 PyTorch、深度学习和其他优化算法的优质资源。欢迎查阅以获得更深入的学习: