Primeiras aulas do curso Machine Learning: Validação de modelos

Machine Learning: Validação de modelos

Validação cruzada e aleatoriedade inicial - Introdução

Olá, tudo bem?

Boas-vindas ao nosso curso de Machine Learning! Aqui vamos discutir sobre a validação dos nossos estimadores. Isso foi feito anteriormente, no curso Introdução à Classificação, que é pré-requisito para este. Nele, usamos a técnica de holdout, com a qual separamos dados de treino e dados de teste, que é suficiente para uma primeira tentativa de validação. Mas, como veremos durante o curso, apresenta alguns problemas.

Em contraponto, existem diversas alternativas baseadas na técnica de validação cruzada que vamos utilizar nesse curso com o estimador que trabalhamos anteriormente para entender melhor como ele funcionará no mundo real.

Estudaremos a motivação para a validação cruzada, a importância da aleatoriedade e simularemos situações de azar, quando não se usa validação cruzada ou estratificação ou ambos. Além disso, falaremos sobre dados que possuem características que os permitem ser independentes entre si.

Neste curso, vamos buscar saber não apenas como nosso algorítimo se comporta com carros que ele já conhece, mas também como ele se comportará com modelos de carros nunca vistos. E, a partir deles, como fazer a validação cruzada com grupos. Além disso, como colocar essa validação cruzada em um processo.

Tudo isso será abordado neste curso!

Validação cruzada e aleatoriedade inicial - A influência da aleatoriedade na validação do modelo

Começaremos o curso importando o projeto do python notebook do Google Colab, como fizemos no curso anterior. Caso você não o tenha cursado, o importante é conhecer o conteúdo apresentado. Vocês encontram o arquivo nas próximas atividades.

Clicando em "UPLOAD > Choose File", escolheremos "Machine_Learning_Validação_pynb". Você pode usar um Jupyter local para trabalhar em seu notebook, ou o Cloud, como usamos no vídeo. Optamos pelo Google por sua praticidade e rapidez.

Vamos dar uma revisada no projeto! Primeiro, carregamos um arquivo .csv.

import pandas as pd

uri = "https://gist.githubusercontent.com/guilhermesilveira/e99a526b2e7ccc6c3b70f53db43a87d2/raw/1605fc74aa778066bf2e6695e24d53cf65f2f447/machine-learning-carros-simulacao.csv"
dados = pd.read_csv(uri).drop(columns=["Unnamed: 0"], axis=1)
dados.head()

Com a uri copiada, podemos colá-la e acessá-la em nosso navegador. Notem que ela possui cinco colunas, sendo que a primeira não tem nome, é apenas um índice. Portanto, vamos descartá-la.

As próximas duas colunas indicam o suposto preço de venda de um carro, e se ele foi ou não (0 ou 1) vendido. Então, cada linha representa um veículo, em um site hipotético de vendas de automóveis, começando por um contador — que não utilizaremos —, seguido por:

Em seguida, o código carrega o arquivo com read_csv e eliminamos a coluna sem nome Unnamed: 0.

dados = pd.read_csv(uri).drop(columns=["Unnamed: 0"], axis=1)

Ao executarmos o código, a biblioteca do Pandas será carregada, inicializando nosso ambiente do Python no Cloud do Google, entre outros, e nos trará o resultado da tabela com apenas as colunas que temos interesse.

precovendidoidade_do_modelokm_por_ano
030941.0211835085.22134
140557.9612012622.05362
289628.5001211440.79806
395276.140343167.32682
4117384.681412770.11290

vendido é uma categoria, a classificação que vamos utilizar são das classe 0 e 1. preco, idade_do_modeloe km_por_ano são as variáveis que temos de features para analisar. Primeiro, separaremos tudo entre os dados x e y, da seguinte forma:

x = dados ["preco", "idade_do_modelo", "km_por_ano")]
y = dados ["vendido"]

Sendo x o que queremos analisar, e y a nossa classificação. Também vamos separar "treino" e "teste", usando o SKLearn. Vamos separar 75% para treino e 25% pra teste.

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score

x = dados[["preco", "idade_do_modelo","km_por_ano"]]
y = dados["vendido"]

SEED = 158020
np.random.seed(SEED)
treino_x, teste_x, treino_y, teste_y = train_test_split(x, y, test_size = 0.25,
                                                         stratify = y)
print("Treinaremos com %d elementos e testaremos com %d elementos" % (len(treino_x), len(teste_x)))

Ao rodarmos o código exibido acima, são separados 7500 elementos para treino e 2500 para teste.

Treinaremos com 7500 elementos e testaremos com 2500 elementos

Na sequência, encontraremos o classificador, mas antes de rodá-lo, decidiremos o que é uma taxa de acerto aceitável, diferenciando uma boa de uma ruim. Uma maneira seria olharmos carro a carro, analisando preco, idade_do_modelo e km_por_ano de cada um e consultarmos um expert humano para saber se o veículo em questão seria ou não vendido. E, com isso, conferir a taxa de acerto de um expert humano. Ou, melhor que um expert humano, seriam vários experts humanos e, a partir deles, tiraríamos uma média, de modo que atingiríamos uma taxa de acerto maior. É desejável que nosso algorítimo seja tão bom quanto ou melhor do que isso.

Para os dados fictícios que geramos para o exercício deste curso, não temos um expert humano. Então a base que assumiremos é a seguinte: ao analisarmos nossos dados, percebe-se que 70% dos carros foram vendidos e 30% dos carros não foram vendidos. Poderíamos, então, supor que esses números vão perdurar, porque são eles que temos como base por nossa experiência. Este algorítimo é um base-line chamado Dummy Classifier, que é estratificado (stratified) por padrão:

from sklearn.dummy import DummyClassifier

dummy_stratified = DummyClassifier()
dummy_stratified.fit(treino_x, treino_y)
acuracia = dummy_stratified.score(teste_x, teste_y) * 100

print("A acurácia do dummy stratified foi de %.2f%%" % acuracia)

Ao executá-lo, obteremos a taxa de acerto de 51% (50.96%).

A acurácia do dummy stratified foi de 50.96%

Um expert humano, provavelmente, teria porcentagem de acerto maior. Todavia, não temos recurso financeiro para este tipo de avaliação.

Portanto, abaixo, rodaremos a árvore de decisão (DecisionTreeClassifier) — abordada em cursos anteriores —, com profundidade máxima (max_depth) igual a 2.


from skleran.tree import DecisionTreeClassifier

SEED = 158020
np.random.seed(SEED)
modelo = DecisionTreeClassifier(max_depth=2)
modelo.fit(treino_x, treino_y)
previsoes = modelo.predict(test_x)

acuracia = accuracy_score(teste_y, previsoes) * 100
print ("A acurácia foi %.2f%%" % acuracia)

Reparem que esses códigos são os que trabalhamos no curso anterior. Ao rodarmos o de DecisionTreeClassifier, a taxa de acerto sobe para, aproximadamente, 72%:

A acurácia foi 71.92%

Mas, suponhamos que experts humanos são capazes de acertar 73%. Para nos igualarmos a eles, precisaremos mudar o algorítimo.

Entretanto, reparem que o código tem um SEED de 158020, que é um número qualquer escolhido aleatoriamente, para que pudéssemos rodar várias vezes, obtendo o mesmo resultado. Então, o que mudaria se alterássemos esse valor?

Vamos aplicar alguns testes, copiando e colando o mesmo código e removendo os imports no início do código.

x = dados[["preco", "idade_do_modelo","km_por_ano"]]
y = dados["vendido"]

SEED = 158020
np.random.seed(SEED)
treino_x, teste_x, treino_y, teste_y = train_test_split(x, y, test_size = 0.25,
                                                         stratify = y)
print("Treinaremos com %d elementos e testaremos com %d elementos" % (len(treino_x), len(teste_x)))

modelo = DecisionTreeClassifier(max_depth=2)
modelo.fit(treino_x, treino_y)
previsoes = modelo.predict(teste_x)

acuracia = accuracy_score(teste_y, previsoes) * 100
print("A acurácia do dummy stratified foi %.2f%%" % acuracia)

Vamos rodar o teste inteiro e conferir o que acontece. Com SEED igual a 158020, obteremos:

Treinaremos com 7500 elementos e testaremos com 2500 elementos
A acurácia foi de 71.92%

A acurácia foi de 71.92%, como esperado. Mas e se alterarmos o valor SEED para 5?

x = dados[["preco", "idade_do_modelo","km_por_ano"]]
y = dados["vendido"]

SEED = 5
np.random.seed(SEED)
treino_x, teste_x, treino_y, teste_y = train_test_split(x, y, test_size = 0.25,
                                                         stratify = y)
print("Treinaremos com %d elementos e testaremos com %d elementos" % (len(treino_x), len(teste_x)))

modelo = DecisionTreeClassifier(max_depth=2)
modelo.fit(treino_x, treino_y)
previsoes = modelo.predict(teste_x)

acuracia = accuracy_score(teste_y, previsoes) * 100
print("A acurácia do dummy stratified foi %.2f%%" % acuracia)

Ao executar o código, obteremos o seguinte retorno:

Treinaremos com 7500 elementos e testaremos com 2500 elementos
A acurácia foi de 76.84%

A acurácia sobe para 76.84%, quase 77%! Então, aquela decisão de julgar isso como bom ou ruim em comparação ao que obtínhamos com Dummy Classifier está melhor, entretanto, ainda estamos vulneráveis a tomar uma decisão baseada em uma aleatoriedade. Não faz sentido validar, julgar se é bom ou ruim, de acordo com um número aleatório. Queremos minimizar o efeito dessa aleatoriedade, dessa escolha aleatória do treino, do teste e da DecisionTreeClassifier, na nossa decisão.

No entanto, se rodamos o treino e o teste uma única vez, corremos o risco de os ter separado mal. Precisamos de outra forma de treinar e testar mais de uma vez para que não tenhamos uma estimativa única, uma maneira que permita rodar o treino e o teste diversas vezes e, a partir disso, obter uma estimativa.

Em vez de um ponto, queremos um intervalo. Precisamos encontrar um método, um algorítimo para isto. Adiante, estudaremos como solucionar essa questão!

Validação cruzada e aleatoriedade inicial - A validação cruzada

Anteriormente, percebemos que, se usarmos a técnica de separar uma parte dos dados para treino e outra para testes, seguramos uma parte dos dados. Em inglês, isto é chamado de Holdout.

Por exemplo, se temos 12 elementos para analisar com o algorítimo, podemos separar os 9 primeiros (de 01 a 09) para treino, e os 3 restantes (de 10 a 12) para testes.

| 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |

Nesse teste, obteríamos o péssimo resultado de 35%. Como falado, isto pode ter sido fruto de uma aleatoriedade, sorte ou azar, porque cada elemento tem uma característica. Então, se tivéssemos separado outros elementos para treino e outros elementos para testes, como 04, 07 e 09 para teste e o restante (01, 02, 03, 05, 06, 08, 10, 11 e 12) para treino:

| 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |

Nesse caso, o resultado seria 78%. Ou ainda, se fizéssemos outra seleção, pegando 02, 04 e 11 para teste, deixando o restante para treino.

| 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |

Então, teríamos 70% como resultado. Que horrível! Por quê? Porque, a cada mudança, ficamos suscetíveis a um número aleatório que define o que será escolhido para treino ou teste, ou de repente à ordem original da sequência — se não fizermos um shuffle — e isto é extremamente arriscado, pois ficamos suscetíveis a esse detalhe horrível, para tomar uma decisão super importante.

Outro exemplo do nosso dia a dia, é quando precisamos fazer uma prova. Imagine que no dia precedente à prova, você receba uma notícia pessoal desagradável, ou acabe brigando com alguém do seu convívio, e, por essa razão, você vá mal na prova e tire 3.5 de 10. Mas se um fator externo afetou seu resultado, não necessariamente significa que você não saiba o conteúdo. Porém, pense: durante um semestre, ou um ano, costuma-se fazer apenas uma prova? Não! É costume que façamos, pelo menos, duas provas e, a partir da soma e divisão do valor de ambas, conseguimos a média!

Então, repare que, no momento em que testamos o algorítimo — ou um(a) aluno(a), por exemplo — diversas vezes, começamos a diminuir a influência de alguns fatores externos. No caso da prova, pode ser alguma situação pessoal; ou no caso de um algoritmo que estivermos rodando, pode ser o shuffle, entre outros problemas. Ou seja, ao rodar o teste várias vezes, podemos tirar uma média simples. Por exemplo, em três provas, considerando que as notas obtidas em cada uma foram: 3.50, 7.80 e 7.00, a média simples delas seria 6.10 (resultado da soma das três notas, dividido por 3).

E podemos fazer a mesma coisa em nosso algorítimo de treino e teste. Em vez de rodar uma única vez o treino e o teste, vamos rodar dois testes, ou seja, treinaremos e testaremos duas vezes e tiraremos a média.

Isso é, pegamos os 12 elementos e definimos que eles serão divididos em dois — podemos dividir entre a primeira metade e a segunda —, treinamos com os 6 primeiros e testamos com os outros 6. O resultado será de 55%. Em ordem contrária, o resultado é de 78%, logo, a média é 66.5% (resultado da soma das duas porcentagens dividido por 2).

Repare que, desta maneira, nós começamos a diminuir a influencia de aleatoriedades nas posições, ou talvez de uma pré determinação das posições dos elementos, sobre o resultado. Em vez de apenas separarmos treino e teste e fazermos o holdout, separaremos em N pedaços (aqui, em 2), para treinar, testar e validar o algoritmo de maneira cruzada. O nome deste processo é Cross Validation, e há diversas maneiras de colocá-lo em prática.

No último exemplo, foram separados dois pedaços e feitos dois testes. Poderíamos ter separado em 4 pedaços e, se fosse o caso, teríamos 4 conjuntos de 3. Então treinaríamos com 3/4 e testaríamos com um 1/4. O resultado seria de 35%. Ao treinar com o 1/4 restante, obteríamos o resultado de 88%. Ao separar 1/4 de novo, obteríamos 76%. Continuando com a separação de outra parcela, obteríamos 74%. Considerando estes valores, a média seria de 68.25%. Então, reparem que, em vez de quebrar em dois pedaços e executar duas vezes o treino-teste, agora quebramos em 4 pedaços e executamos o treino-teste 4 vezes também.

Aqui, por praticidade, foram escolhidos 12 elementos para que fossem divisíveis por 2 e por 4, mas não precisava ser exatamente o mesmo número, poderia ser 3, 3, 3 e 2, se tivéssemos só 11 elementos, por exemplo. O principal aqui é a ideia de que quebrarmos os dados em N pedaços e, então, rodarmos K processos de treino e teste, validando nosso algorítimo de maneira cruzada. Esse processo é chamado de K-fold, referente ao número (K) de vezes que quebraremos os dados para rodar a validação cruzada.

Reparem que, se dividirmos em 8 pedaços, e tivermos N elementos e rodarmos todos eles, teremos 8 números diferentes. Por exemplo, se obtivermos 65%, 76%, 74%, 70%, 66%, 62%, 68% e 69%, a média será de 68.75%. Porém, a média é um ponto.

Nos cursos básicos de estatística, como temos na Alura, percebemos como ela é uma medida difícil de utilizarmos secamente, pontualmente. O mais desejável é usar um intervalo no qual confiamos ter um número razoável para o algorítimo funcionar no mundo real.

Então, quando temos um conjunto de números como esse, que contém 8, podemos usar a média e o desvio padrão, se acreditarmos que essa distribuição é normal. Ambos desviam padrões, e nós temos um intervalo entre 60% e 77%, nesse caso específico.

Nós faremos a construção dessa média em Python, logo a seguir.

Por fim, pense em um caso extremo, no qual temos 12 elementos e os quebramos ao máximo, em 12 pedaços. Assim, em um dos 12 treinos e testes, treinaremos com 11 deles e testaremos com 1. Ou seja, vamos testar o número de elementos que tivermos N vezes. Este algorítimo será muito lento, porque esse processo de validar, deixando um isolado, nos obriga a rodá-lo K vezes (o número de elementos que possuímos).

Entretanto, o processo é o mais fiel ao que esperamos com esse algorítimo no mundo real. Sendo assim, é muito importante decidirmos qual será o número K. Esse caso específico, em que o número K é exatamente o numero de elementos que temos, é chamado de Leave One Out ("deixar um de fora", em português), justamente por deixar um elemento de fora, isolado.

Sobre o curso Machine Learning: Validação de modelos

O curso Machine Learning: Validação de modelos possui 80 minutos de vídeos, em um total de 33 atividades. Gostou? Conheça nossos outros cursos de Machine Learning em Data Science, ou leia nossos artigos de Data Science.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Machine Learning acessando integralmente esse e outros cursos, comece hoje!

  • 1266 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

Premium

  • 1266 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$75
à vista R$900
Matricule-se

Premium Plus

  • 1266 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$100
à vista R$1.200
Matricule-se

Max

  • 1266 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$120
à vista R$1.440
Matricule-se
Conheça os Planos para Empresas

Acesso completo
durante 1 ano

Estude 24h/dia
onde e quando quiser

Novos cursos
todas as semanas