知识总结:MarsCode AI 的底层原理(五)上 | 豆包MarsCode AI刷题

58 阅读8分钟

第三章 线性分类,SVM,随机梯度下降,多分类

1.线性分类

在实际生活中,分类是很常见的。课室的桌子是线性可分割的,而相对应的,部分美国大选的选区存在“杰利蝾螈”的现象,这种划分就不是线性可分的例子。

最简单的分类是二分分类,我们针对 n 个训练组 { x[i], y[i] }。当 x 属于实数域而 y 的取值只有 -1 和 +1 的时候,我们就可以训练一个分类器 f(x),从而使得 f(x) 符合如下所示的规则:

如果我们将这一个分类器进化为线性分类器,那么线性分类器有一个模式:

其中,在 2D 空间,这一种划分方式是一条线,w 是 normal (法线) ,而 b 是偏差 bias。w 也可以被称为是权重向量。

如果在 3D 空间的话,这一种划分方式是一个平面,m-D 是一个(hyperplane)超平面。

2.支持向量机(理想情况)

在前面的讨论中,我们知道,划分的策略是多种多样的。究竟哪一种划分策略才是最佳的呢?从通用的视角看:划分的两类数据“不偏不倚”是最好的。从数学的角度看,自然是要让各个类别的 Margin (间隔)越大越好。

最大间隔解(Maximum margin solution):most stable under perturbations of the inputs.

咱们就以笔者所在的广州珠江为例子:

直接求解两个直线的距离即可。根据中学数学学习过的直线之间距离的计算公式,我们可以做一个简单的推导:

也就是说,SVM(支持向量机)的目的是为了优化线性分类问题,使得分类面最大最好。我们又可以根据数学换算将最大化问题转化为最小化问题。

以上的推导的基础是理想情况,但是真实的情况可能会出现疏漏等情况。所以开发者需要在寻找最优 margin 和平衡训练数据的潜在错误中去追求一种平衡状态。况且,在这种特殊情况,训练数据甚至可能无法实现线性可分割!

因此,虽然我们用数学推导得到了一个简洁的求解最大值的表达式,并且利用倒数的性质转化为求解最小值,但是单纯的求解最值是无法解决问题的,这就引出了接下来 要解析的概念:松弛变量

3.支持向量机(松弛变量)

4.随机梯度下降

随机梯度下降的动力

① 样例之间信息存在冗余

② 足够的用例意味着我们可以负担得起噪声更新

③ 永不停止的流意味着我们不应该等待所有数据

④ 跟踪非状态的数据意味着目标是有挑战性的

随机梯度下降的好处

  1. 梯度易于计算
  2. 不易出现局部最小值
  3. 可以使用更少的内存
  4. 可以快速地得到一个可行解
  5. 可以用于处理更复杂的模型

随机梯度下降的不足

  1. Variance is large 偏差大
  2. Iterative algortihm 不稳定
  3. 难以达到高精准度

5.mini-batch 随机梯度下降

与其使用一个点,我们会随机地选择一个子集 Sk,从而将优化问题转化为以下形式:

其中,Sk 的大小比原始的数据大小 n 要小,一般来说是 2^3 ~ 2^6

变种算法:Adam 算法 和 AdaGrad 算法

Adam 算法(Adaptive Moment Estimation)

Adam 是一种结合了动量(Momentum)和RMSprop(Root Mean Square Propagation)思想的优化算法。它通过计算梯度的一阶矩(均值)和二阶矩(方差)的估计值来调整每个参数的学习率。具体来说:

  1. 动量项(M) :类似于动量算法,Adam 计算梯度的指数加权平均值,这有助于平滑梯度的波动。
  2. 方差项(V) :类似于RMSprop,Adam 计算梯度平方的指数加权平均值,这有助于调整每个参数的学习率,使其适应参数更新的规模。
  3. 自适应学习率:结合了M和V的估计值,Adam 为每个参数计算一个自适应的学习率,这使得算法在训练过程中更加稳定。

Adam 算法通常表现良好,尤其是在深度学习中,它能够快速收敛,并且对学习率的选择不太敏感。

AdaGrad 算法(Adaptive Gradient Algorithm)

AdaGrad 是一种自适应学习率的优化算法,它通过累积所有梯度的平方来调整每个参数的学习率。AdaGrad 的主要特点包括:

  1. 累积梯度平方:AdaGrad 会累积所有梯度的平方和,这意味着随着时间的增加,分母会不断增大,导致学习率逐渐减小。
  2. 自适应学习率:AdaGrad 为每个参数计算一个基于其历史梯度平方和的倒数的学习率,这意味着频繁更新的参数将获得较小的学习率,而不常更新的参数将获得较大的学习率。
  3. 适用于稀疏数据:由于其自适应学习率的特性,AdaGrad 特别适合处理稀疏数据,因为稀疏数据中的非零特征将获得更大的权重。

然而,AdaGrad 的一个缺点是其累积梯度平方和可能会导致学习率过早地变得非常小,从而使得训练过程在后期几乎无法继续。

学习率

学习率在收敛的工作中发挥了重要的作用:

如果学习率太小 —— 收敛太慢

如果学习率太大 —— 有可能会出现发散的情况

学习率应当保持固定还是有适应性的呢?

收敛很有必要吗?

Non-stationary: 也许无法满足收敛条件

Stationary: 学习率应当随着时间而减小

SGD 随机梯度下降的推荐

① 随机洗牌训练样本:

即使理论上我们应该选择某些案例,但是顺序地经历你的训练集是更加简单的。在每次迭代时,洗牌可以降低顺序的影响。

② 监督训练损失和验证错误:

为验证集留出样本

在训练集和验证集上计算目标函数(虽然成本较高,但比过拟合或浪费计算资源要好)

③ 使用有限的不同/差异校验梯度

错误的计算可能会导致算法不稳定且缓慢

通过轻微扰动参数并检查两个梯度之间的差异来验证你的代码

④ 利用训练样本的稀疏性

⑤ 使用训练集的小样本对学习率进行实验

6.多类别分类问题的定义

在机器学习领域中,有三种常见的分类问题,分别是:

Binary Classification 二分问题

Multi-class Classification 多类别分类问题

Multi-label Classification 多标签分类问题

多类别分类问题是一种常见的分类问题,它希望把一系列的实例分解到 one of the more than two classes 里面。

经典的数据集包括 MNIST, Cifar-10 和 Cifar-100

多类别分类问题有三种通用的策略:

① 转换:将多类别分类问题转化为二分问题,以下是样例

  1. One - vs - Rest 是 或 不是

  好处:可以训练 K 个二分分类器对于 K-way 多分类问题

  坏处:

  ① 二分类器的分布是不平衡的

  ② 决策也许会存在失误

  1. One - vs - One 原子化

  好处:分类是平衡的

  坏处:

  ① 计算复杂性很高

  ② 如果两类别票数相同,就会导致模棱两可

  1. DDAG 决策有向无环图

② 拓展:从二分问题中个拓展出解决方案

在吴恩达老师的机器学习中,老师重点强调了 K-nearest neighbors 分类的策略:

好处:无参分类算法、可以自然地处理二分分类和多分类问题

劣势:计算资源和内存需求高,用非欧氏距离的概念去定义对象的距离是很难的

③ 层级:采用层级化的分类结构

Label Tree(标签树)发挥了很重要的作用:

好处:树的数据结构可以降低预测的成本

坏处:过于依赖好的聚类方法

利用 PyTorch 解决简单的分类问题

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform)

train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(28*28, 512)
        self.fc2 = nn.Linear(512, 256)
        self.fc3 = nn.Linear(256, 10)
    
    def forward(self, x):
        x = x.view(-1, 28*28)
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = Net()

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

def train(model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % 100 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))
                
def test(model, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += criterion(output, target).item()
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
        
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

for epoch in range(1, 11):
    train(model, device, train_loader, optimizer, epoch)
    test(model, device, test_loader)        

常考面试题(面试鸭)

请描述支持向量机 SVM 的基本思想和应用场景

www.mianshiya.com/bank/182183…

SVM 可以处理多分类吗?如何处理多分类?

www.mianshiya.com/bank/182183…

简单说说核函数的原理?

www.mianshiya.com/bank/182183…

SVM 有哪些核函数?分别应用于哪些场景中?

www.mianshiya.com/bank/182183…