Parada Antecipada do Gradiente Descendente Estocástico

Beginner

This tutorial is from open-source community. Access the source code

Introdução

O Gradiente Descendente Estocástico é uma técnica de otimização popular usada para minimizar uma função de perda. A técnica executa passos de descida de gradiente de forma estocástica, ou seja, selecionando aleatoriamente amostras para cada iteração. O método é eficiente, especialmente para ajustar modelos lineares. No entanto, a convergência não é garantida em cada iteração, e a função de perda pode não diminuir necessariamente em cada iteração. Neste caso, monitorar a convergência na função de perda pode ser difícil. Neste laboratório, exploraremos a estratégia de parada antecipada, que é uma abordagem para monitorar a convergência em uma pontuação de validação. Usaremos o modelo SGDClassifier da biblioteca scikit-learn e o conjunto de dados MNIST para ilustrar como a parada antecipada pode ser usada para atingir quase a mesma precisão em comparação com um modelo construído sem parada antecipada, e reduzir significativamente o tempo de treinamento.

Dicas da Máquina Virtual

Após o início da VM, clique no canto superior esquerdo para mudar para a aba Notebook para acessar o Jupyter Notebook para praticar.

Às vezes, pode ser necessário esperar alguns segundos para que o Jupyter Notebook termine de carregar. A validação de operações não pode ser automatizada devido a limitações no Jupyter Notebook.

Se você enfrentar problemas durante o aprendizado, sinta-se à vontade para perguntar ao Labby. Forneça feedback após a sessão, e resolveremos prontamente o problema para você.

Carregar as bibliotecas e o conjunto de dados MNIST necessárias

O primeiro passo é carregar as bibliotecas e o conjunto de dados necessários. Usaremos as bibliotecas pandas, numpy, matplotlib e scikit-learn. Também usaremos a função fetch_openml do scikit-learn para carregar o conjunto de dados MNIST.

import time
import sys
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.utils._testing import ignore_warnings
from sklearn.exceptions import ConvergenceWarning
from sklearn.utils import shuffle

## Carregar o conjunto de dados MNIST
def load_mnist(n_samples=None, class_0="0", class_1="8"):
    """Carregar MNIST, selecionar duas classes, embaralhar e retornar apenas n_samples."""
    ## Carregar dados de http://openml.org/d/554
    mnist = fetch_openml("mnist_784", version=1, as_frame=False, parser="pandas")

    ## pegar apenas duas classes para classificação binária
    mask = np.logical_or(mnist.target == class_0, mnist.target == class_1)

    X, y = shuffle(mnist.data[mask], mnist.target[mask], random_state=42)
    if n_samples is not None:
        X, y = X[:n_samples], y[:n_samples]
    return X, y

X, y = load_mnist(n_samples=10000)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)

Definir o estimador e a estratégia de parada antecipada

O próximo passo é definir o estimador e a estratégia de parada antecipada. Usaremos o modelo SGDClassifier do scikit-learn. Definiremos três critérios de parada diferentes: sem critério de parada, perda de treinamento e pontuação de validação. Usaremos a função fit_and_score para ajustar o estimador no conjunto de treinamento e avaliar no conjunto de treinamento e no conjunto de teste.

@ignore_warnings(category=ConvergenceWarning)
def fit_and_score(estimator, max_iter, X_train, X_test, y_train, y_test):
    """Ajustar o estimador no conjunto de treinamento e avaliar no conjunto de treinamento e no conjunto de teste"""
    estimator.set_params(max_iter=max_iter)
    estimator.set_params(random_state=0)

    start = time.time()
    estimator.fit(X_train, y_train)

    fit_time = time.time() - start
    n_iter = estimator.n_iter_
    train_score = estimator.score(X_train, y_train)
    test_score = estimator.score(X_test, y_test)

    return fit_time, n_iter, train_score, test_score

## Definir o estimador para comparação
estimator_dict = {
    "Sem critério de parada": linear_model.SGDClassifier(n_iter_no_change=3),
    "Perda de treinamento": linear_model.SGDClassifier(
        early_stopping=False, n_iter_no_change=3, tol=0.1
    ),
    "Pontuação de validação": linear_model.SGDClassifier(
        early_stopping=True, n_iter_no_change=3, tol=0.0001, validation_fraction=0.2
    ),
}

Treinar e avaliar o estimador

O próximo passo é treinar e avaliar o estimador usando cada critério de parada. Usaremos um loop para iterar sobre cada estimador e critério de parada, e usaremos outro loop para iterar sobre diferentes iterações máximas. Em seguida, armazenaremos os resultados em um DataFrame pandas para facilitar o plotagem.

results = []
for estimator_name, estimator in estimator_dict.items():
    print(estimator_name + ": ", end="")
    for max_iter in range(1, 50):
        print(".", end="")
        sys.stdout.flush()

        fit_time, n_iter, train_score, test_score = fit_and_score(
            estimator, max_iter, X_train, X_test, y_train, y_test
        )

        results.append(
            (estimator_name, max_iter, fit_time, n_iter, train_score, test_score)
        )
    print("")

## Transformar os resultados em um DataFrame pandas para facilitar a plotagem
columns = [
    "Critério de parada",
    "max_iter",
    "Tempo de ajuste (seg)",
    "n_iter_",
    "Pontuação de treinamento",
    "Pontuação de teste",
]
results_df = pd.DataFrame(results, columns=columns)

Plotar os resultados

O passo final é plotar os resultados. Usaremos dois subplots para plotar as pontuações de treinamento e teste, e o número de iterações e o tempo de ajuste. Usaremos estilos de linha diferentes para cada estimador e critério de parada.

## Definir o que plotar
lines = "Critério de parada"
x_axis = "max_iter"
styles = ["-.", "--", "-"]

## Primeiro gráfico: pontuações de treinamento e teste
fig, axes = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(12, 4))
for ax, y_axis in zip(axes, ["Pontuação de treinamento", "Pontuação de teste"]):
    for style, (criterion, group_df) in zip(styles, results_df.groupby(lines)):
        group_df.plot(x=x_axis, y=y_axis, label=criterion, ax=ax, style=style)
    ax.set_title(y_axis)
    ax.legend(title=lines)
fig.tight_layout()

## Segundo gráfico: n_iter e tempo de ajuste
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12, 4))
for ax, y_axis in zip(axes, ["n_iter_", "Tempo de ajuste (seg)"]):
    for style, (criterion, group_df) in zip(styles, results_df.groupby(lines)):
        group_df.plot(x=x_axis, y=y_axis, label=criterion, ax=ax, style=style)
    ax.set_title(y_axis)
    ax.legend(title=lines)
fig.tight_layout()

plt.show()

Resumo

Neste laboratório, exploramos a estratégia de parada antecipada para monitorar a convergência em uma pontuação de validação ao usar o Gradiente Descendente Estocástico para minimizar uma função de perda. Usamos o modelo SGDClassifier do scikit-learn e o conjunto de dados MNIST para ilustrar como a parada antecipada pode ser usada para atingir quase a mesma precisão em comparação com um modelo construído sem parada antecipada, e reduzir significativamente o tempo de treinamento. Definimos três critérios de parada diferentes: nenhum critério de parada, perda de treinamento e pontuação de validação, e usamos um loop para treinar e avaliar o estimador usando cada critério de parada. Em seguida, plotamos os resultados usando estilos de linha diferentes para cada estimador e critério de parada.