Como Criar um SVM para Big Data em 10 Minutos Usando Python

SVMs são bastante populares em aplicações de machine learning. Apesar de seguirem princípios simples, eles já demonstraram ser muito poderosos em diversas tarefas e datasets. Neste artigo quero demonstrar como implementar um SVM capaz de lidar com dados que chegam em tempo real, sem ter que armazená-los na memória.

Clique aqui para acessar o código e o dataset deste artigo. Eu recomendo rodá-lo usando Pypy.

Principais modos de treinar um modelo de machine learning

Existem três maneiras populares de treinar um modelo: batch learning, mini-batch learning, e stochastic learning.

Batch Learning: no primeiro modo, nós armazenamos todos os dados de treino numa matriz e alimentamos o algoritmo, reduzindo a loss function baseado em todos os exemplos de uma vez. Isso nem sempre é possível devido ao tamanho do dataset. Nestes casos temos que recorrer às duas outras maneiras.

Mini-Batch Learning: neste caso, selecionamos um número N de exemplos e dividimos o training set em blocos deste tamanho. Então treinamos o modelo em um bloco de cada vez.

Stochastic learning: é uma variação do mini-batch, mas com N = 1. Nele utilizamos apenas um exemplo de cada vez para treinar o modelo. Este é o modo preferido em soluções de big data.

Dentro deste modo temos duas divisões principais:

Offline: neste caso temos todos os dados armazenados num banco de dados, mas eles não cabem na memória, então precisamos treinar em um exemplo de cada vez.

Online: neste caso recebemos os exemplos em tempo real, treinamos e descartamos, sem a necessidade de armazená-los.

Além de não ter que carregar todos os dados na memória para treinar o modelo, em alguns casos, usar algoritmos que treinam em um exemplo de cada vez pode ser mais rápido do que o formato que utiliza todos os dados ao mesmo tempo.

Dataset

Neste artigo será usado o Skin Segmentation Data Set que pode ser baixado do banco de dados da UCI. Estes dados se referem a uma tarefa de classificação na qual são extraídos valores RGB de pontos aleatórios da foto do rosto de uma pessoa, e a tarefa é determinar, de acordo com estes valores, se aquele ponto corresponde à pele, ou outra parte da imagem.

Segundo os pesquisadores, foram coletados valores de imagens de diversas idades, raças e gêneros. Uma aplicação prática desta tarefa seria identificar imagens com conteúdo impróprio para menores na internet.

São cerca de 245 mil exemplos, e é um problema de classificação binária.

Algoritmo de treino

O algoritmo utilizado para treino será o PEGASOS. O trabalho acadêmico (em inglês) com os detalhes pode ser encontrado aqui.

Na sua versão mais simples, vamos minimizar a função primal do SVM utilizando um exemplo de cada vez. Como este método se aplica ao subgradiente, ele não garante a diminuição do erro em todas as iterações. Mesmo assim, existem garantias de convergência.

O único parâmetro a escolher é a constante que mede o grau de regularização. Normalmente ela é escolhida usando algum método de validação. Como o objetivo do artigo é demonstrar uma implementação de SVM que pode ser facilmente adaptada para dados que não cabem na memória, ou são recebidos em tempo real, vou fixa-la em 0,1.

Numa aplicação real, pode ser interessante ajustar esse parâmetro usando uma amostra menor dos dados, off-line, com validação cruzada.

Avaliação do resultado

Num ambiente real online, uma maneira de avaliar a performance do modelo seria calcular a loss antes de prever um novo exemplo e fazer a média numa janela de tempo. Este método é chamado de validação progressiva.

No nosso caso, estamos simulando um ambiente online, e para tornar mais clara a eficácia do modelo, vou usar um test set separado, com 20% dos dados.

No repositório deste artigo você encontra os arquivos train.csv e test.csv, já preparados. Cada um contém aproximadamente 25% de exemplos positivos, então além da loss, quero avaliar o número de acertos e erros em cada classe (positivos verdadeiros e negativos verdadeiros).

O training set possui 196.045 exemplos, e o test set, 49.012. Vamos utilizar cada exemplo do training set apenas uma vez, e depois avaliar a performance do modelo no test set.

Como nós não vamos utilizar este test set para ajustar hiperparâmetros, ele é uma estimativa legítima da performance do modelo em novos exemplos. Se você for usá-lo para validar parâmetros, precisará de outro test set.

Implementation

We start by creating a function to read each line of the CSV and transform it into a list with the values of the variables, and the bias (always 1). The function is divided between the training set and test set.

Depois vamos à implementação do algoritmo para treinar o SVM. Criamos algumas funções de apoio, uma para calcular a hinge loss, que é a loss que estamos minimizando, e outra para determinar o sinal da output do SVM. Os pesos iniciais do SVM podem ser 0, já que não temos problemas com simetria, como em redes neurais.

Depois criamos uma função para gerar as previsões. Ela simplesmente fará o produto escalar entre o vetor de pesos, e o vetor de atributos.

Agora vamos à função para que o modelo aprenda os dados. Nela vamos atualizar os pesos com a learning rate (alpha) e o gradiente. Esta função treina em um exemplo de cada vez. A learning rate se ajusta, sendo menor a cada novo exemplo visto.

O gradiente é diferente para o caso em que a classificação atual está errada (o exemplo está do lado errado do hiperplano), e para os exemplos que estão classificados corretamente.

Seria possível implementar esta função usando menos linhas, mas neste caso optei pela clareza. Para verificar esta possibilidade, veja a fórmula na publicação original deste algoritmo.

Agora criamos a função para treinar e avaliar o modelo nos dados. No caso de online learning, receberíamos os dados através de uma stream, então eles seriam vistos apenas uma vez.

Para cada exemplo do training set invocamos a função ‘train’, que ajusta os pesos, de acordo com a learning rate, na direção do gradiente. Lembrando que, por ser um método que utiliza o subgradiente, ele não garante a diminuição da loss em todas as iterações.

Consideramos um exemplo positivo se a distância dele para o hiperplano for positiva, e o contrário para exemplos negativos. É extremamente raro que um exemplo seja absolutamente 0, mas vamos considerar que, se isso acontecer, ele será classificado como negativo.

Além disso, devido à natureza estocástica do algoritmo, vamos treiná-lo várias vezes, e fazer a média da performance modificando a ordem de chegada dos exemplos. Para que os resultados possam ser reproduzidos, determinamos uma seed para o RNG.

Influência do Training Set

Por ser um algoritmo que considera um exemplo de cada vez, a ordem na qual eles são apresentados muda o comportamento, fazendo com que ele busque caminhos diferentes para atingir a mínima. Todos os números reportados nesta parte são avaliados nos exemplos do arquivo “test.csv”.

Treinando em nosso training set original, sem embaralharmos os dados, a performance dele é a seguinte:

Agora, para termos uma estimativa real da performance na prática, vamos testá-lo 100 vezes, modificando a ordem de chegada dos exemplos, a performance média neste caso é:

Um detalhe deste dataset é o fato que a proporção das classes é desequilibrada (75/25). Um remédio simples para isto, no caso de termos muitos dados, é diminuir a proporção da classe maior até atingir o equilíbrio (50/50). Além disso, alguns especialistas sugerem que garantir que o próximo exemplo visto pelo algoritmo seja diferente do anterior possa melhorar o desempenho. Vamos testar essas duas opções adicionando o seguinte código:

Ele fará com que o algoritmo sempre receba um exemplo diferente do anterior, e que a proporção das classes seja aproximadamente igual. Não vamos mexer no test set, ele continua com a proporção original (75/25).

Neste caso, usando apenas a ordem do training set original, a performance se torna:

E fazendo a média de 100 datasets embaralhados:

Vemos uma melhora significativa na quantidade de acertos dos positivos verdadeiros, apesar de um grande aumento na loss. Esse aumento se deve ao fato da loss ser calculada baseando-se em todos os exemplos. Como estamos “puxando” o hiperplano que separa as classes de maneira a aceitar mais erros na classe negativa, em troca de melhor performance na positiva, acabamos aumentando a distância de alguns pontos classificados de maneira incorreta.

Uma alternativa seria manter todos os dados e atribuir uma loss maior, proporcional, a erros em exemplos da classe menor.

Em casos que apresentam classes desproporcionais, como este, é interessante usar métricas que não sejam sensíveis à quantidade de exemplos. Por isso a escolha em reportar os positivos/negativos verdadeiros.

Resultado e Características

O primal do SVM é uma função convexa, então temos a possibilidade de atingir o ponto mínimo global. Entretanto, o fato de recebermos exemplos de maneira aleatória, treinando apenas uma vez em cada um, com uma learning rate diminuindo com o tempo, pode nos impedir de chegar a este ponto numa aplicação prática. Apesar disso, há garantias teóricas de convergência para a mínima global.

Em Machine Learning buscamos minimizar o erro de generalização, ou seja, fora dos exemplos utilizados para treino. Na maioria dos casos, a melhor performance de generalização ocorre antes de atingirmos a mínima no training set.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *