Esta apresentação é uma introdução aos modelos de núcleo e ao algoritmo de Máquinas de Vetor de Suporte para a Classificação de Documentos. São discutidos métodos de modelagem de dados e o demonstrado o uso da biblioteca LIBSVM.
CLASSIFICAÇÃO
aVANÇADA
Métodos de Núcleo e
Máquinas de Vetor de Suporte
Eduardo Nicola F. Zagari
Sumário
Um exemplo de conjunto de dados
Classificação Linear Básica
Características de Categoria
Normalização dos Dados
Métodos de Núcleo
Conjunto de Dados de
páginas de Encontros
Considere uma página Web imaginária de encontros de casais.
Vamos considerar também que ela reúna as seguintes informações sobre
seus membros:
Idade
É fumante?
Quer ter filhos?
Lista de interesses
Localização
Além destes, são coletadas info para checar se 2 pessoas formam um bom
par, se tiveram um contato inicial e se decidiram se encontrar pessoalmente.
Conjunto de Dados de
páginas de Encontros
Dois aspectos interessantes sobre este conjunto
de dados:
não linearidade
interação entre as variáveis
Por exemplo, considere isoladamente as idades
dos homens e mulheres que combinam e que
não combinam...
Classificação Linear básica
Classificador simples
Encontra uma média de todos os dados em
cada categoria e determina um ponto que
representa o centro de cada uma delas
Pode-se então classificar novos pontos,
determinando a quais dos centros eles estão
mais próximos
Classificação Linear básica
Sempre que tivermos um novo par e queiramos um palpite sobre se 2
pessoas serão compatíveis, pode-se apenas verificar de qual lado da
reta (média) elas estarão mais próximas
Forma de se determinar proximidade numérica: Distância Euclideana
menor distância entre o ponto e as 2 médias, ou
sinal do produto escalar dos 2 vetores:
Vetor A: vetor entre pontos médios (M0 e M1) das categorias =>
M0 - M1
Vetor B: vetor entre o ponto procurado (X) e a média dos pontos
médios (M) => X - (M0 + M1)/2
Classificação Linear básica
Produto Escalar do Vetor A pelo Vetor B
ProdEscalar(Vetor A, Vetor B) =
= ProdEscalar(X - (M0+M1)/2, M0 - M1)
= ProdEscalar(X,M0) - ProdEscalar(X, M1) +
(ProdEscalar(M1,M1) - ProdEscalar(M0,M0))/2
onde ProdEscalar(V1,V2) = V1[0]*V2[0] + V1[1]*V2[1]
Se ProdEscalar(Vetor A, Vetor B) for maior que zero, o ponto X
é da categoria 0 (casal não combina), senão, da categoria 1
(casal combina)
Classificação Linear básica
Assim, teríamos, como exemplo, os resultados:
>> dp_classify([30,30],avgs)
=> 1
>> dp_classify([30,25],avgs)
=> 1
>> dp_classify([25,40],avgs)
=> 0
>> dp_classify([48,20],avgs)
=> 1
Lembre-se de que esse é um classificador linear,
portanto, ele encontra apenas uma linha divisória
Isto explica o resultado do ponto [48, 20]
Características de categoria
Grupos de dados contêm valores numéricos e
de categoria. Alguns classificadores (como a
Árvore de Decisão) suportam os dois tipos,
mas outros não.
Solução: transformar dados em números
Características de categoria
Perguntas de sim/não
Perguntas de Sim/Não
São as mais simples
Sim = 1
Não = -1
Eu não sei = 0
Características de categoria
listas de interesses
Listas de Interesses
Existem várias formas de manipulá-las
A mais simples é tratar cada interesse possível como uma
variável numérica separada:
Tem interesse = 1
Não tem interesse = 0
No caso de lidar com pares de pessoas, a opção mais
intuitiva é usar o número de interesses em comum como
uma variável (Contagem de Equivalências/Semelhanças)
Características de categoria
listas de interesses
O número de interesses elimina algumas informações
potencialmente úteis, como interesses que funcionem bem
juntos (p. ex., beber e dançar)
Classificador que não foi treinado para isto, nunca seria capaz
de aprender tais combinações
Um alternativa a criar uma variável para cada interesse é
organizá-los numa hierarquia
Se o interesse é o mesmo, contagem de semelhanças += 1
Se o interesse é diferente, mas da mesma subcategoria, p. ex.,
contagem de semelhanças += 0,8
Características de categoria
Lidando com localização
Uma das características mais difíceis de lidar:
No nosso caso, parece um bom argumento dizer que
a probabilidade de pessoas que moram perto serem
mais compatíveis
Mas definir “perto” como “viver no mesmo CEP”
pode ser limitante demais
Para isto, geralmente o uso de APIs de Geocoding
são perfeitas: obtém-se latitude e longitude dos
endereços e se calcula a distância entre eles
Características de categoria
Google geocoding API
Para usar o Google Geocoding Web Service,
podemos usar a gem Geokit:
$ sudo gem install geokit
$ irb
>> require 'rubygems'
=> false
>> require 'geokit'
=> true
Agora basta chamar:
>> res = Geokit::Geocoders::GoogleGeocoder.geocode(‘<endereço>’)
>> res.ll
Características de categoria
Google geocoding API
>> lat_long = Geokit::Geocoders::GoogleGeocoder.geocode('Avenida Mns-dr. Emilio Jose
Salim, Parque das Universidades, Campinas - Sao Paulo, Brazil')
#<Geokit::GeoLoc:0x180bbb8
@accuracy=6,
@all=[#<Geokit::GeoLoc:0x180bbb8 ...>],
@city="Campinas",
@country="Brasil",
@country_code="BR",
@district="Parque das Universidades",
@full_address=
"Av. Mns-dr. Em303255lio Jos303251 Salim - Parque das Universidades, Campinas -
S303243o Paulo, Brazil",
@lat=-22.8342006,
@lng=-47.0530948,
@precision="zip+4",
@provider="google",
@province=nil,
@state="S303243o Paulo",
@street_address="Av. Mns Dr. Em303255lio Jos303251 Salim",
@success=true,
@suggested_bounds=
#<Geokit::Bounds:0x17bd828
@ne=#<Geokit::LatLng:0x17bd760 @lat=-22.8310554, @lng=-47.0499702>,
@sw=#<Geokit::LatLng:0x17bd7c4 @lat=-22.8373507, @lng=-47.0562654>>,
@zip=nil>
=> nil
>> pp lat_long.ll
"-22.8342006,-47.0530948"
=> nil
Características de categoria
calculando as distâncias
Se é necessária alta precisão, é complicado
converter latitudes e longitudes em distância em
kms
Mas, como aproximação, pode-se calcular tal
distância, pela fórmula:
sqrt(x**2 + y**2)
onde, x = 69.1*(lat2 - lat1) e y 53*(long2 - long1)
Características de categoria
calculando as distâncias
Ou, pela gem geokit:
>> a = Geokit::Geocoders::GoogleGeocoder.geocode('Campinas,
SP')
>> a.ll
=> "-22.9063648,-47.0615741"
>> b = Geokit::Geocoders::GoogleGeocoder.geocode('Avenida
Mns-dr. Emilio Jose Salim, Parque das Universidades,
Campinas - Sao Paulo, Brazil')
>> b.ll
=> "-22.8342006,-47.0530948"
>> a.distance_to(b, {:units => :kms})
=> 8.07849508688975
Características de categoria
Normalizando os dados
Como a natureza dos dados é diferente
(diferença de idade, opinião sobre filhos etc), é
preciso colocar todos numa escala comum, para
que as diferenças sejam comparáveis entre todas
as variáveis:
Por exemplo, normalizando cada variável
entre 0 e 1
Métodos de Núcleo
Onde estariam os pontos médios de cada classe?
o classificador linear não consegue
diferenciar estas categorias...
Mas, considere o que acontece se elevarmos ao
quadrado todo valor (x,y):
(-1,2) -> (1,4)
(0.5, 1) -> (0.25, 1)
O Truque do núcleo
Nem sempre é factível se transformar os dados
para um novo espaço, a fim de encontrar uma
linha divisória, pois pode ser necessário uma
transformação para um espaço de muitas
dimensões
Entretanto, para algoritmos que usem
produtos escalares, pode-se usar a técnica do
Truque de Núcleo
O Truque do núcleo
Truque de Núcleo:
consiste em substituir a função de produto
escalar por uma nova função que retorna o
que o produto escalar seria, se os dados
tivessem sido transformados primeiro num
espaço dimensional maior, usando alguma
função de mapeamento
p. exemplo, a função de base radial
O Truque do núcleo
Função de base radial:
semelhante ao produto escalar, pois toma 2 vetores e
retorna um valor
no entanto, difere dele por não ser linear e, assim, poder
mapear espaços mais complexos
def rbf(v1, v2, gamma=20)
retorna nulo se v1.length != v2.length
dv = diff(v1, v2) # a ser definida
l = veclength(dv) # a ser definida
Math::exp(-gamma*l)
end
O Truque do núcleo
Cálculo das distâncias dos pontos médios no espaço
transformado:
não temos as médias no novo espaço
sequer temos os locais dos pontos no novo espaço
felizmente, o produto escalar da média de um
grupo de vetores com um vetor A é o mesmo que
a média dos produtos escalares do vetor A com
todos os demais
O Truque do núcleo
Assim, computamos o produto escalar (ou a
função de base radial) entre o ponto que estamos
tentando classificar e todos os outros na
categoria e então determinamos a média de
todos eles
O Truque do núcleo
Deslocamento dos intervalos:
def get_offset(rows, gamma=10)
l0 = pontos_da_categoria_0(rows)
l1 = pontos_da_categoria_1(rows)
sum0 = somatorio_dos_rbf_entre_todos_os_pontos(l0)
sum1 = somatorio_dos_rbf_entre_todos_os_pontos(l1)
return sum1/(l1.length**2) - sum0/(l0.length**2)
end
O Truque do núcleo
Classificador Não Linear:
def nl_classify(point, rows, offset, gamma=10)
Para cada ponto em rows
Se for da categoria 0
sum0 += rbf(point, row.data, gamma)
count0 += 1
senao
sum1 += rbf(point, row.data, gamma)
count1 += 1
fim
fim
retorna 0 se (sum0/count0 - sum1/count1 + offset) > 0
retorna 1
end
ClassificaDOR Não Linear
Assim, teríamos, como exemplo, os resultados:
>> nl_classify([30,30],avgs)
=> 1
>> nl_classify([30,25],avgs)
=> 1
>> nl_classify([25,40],avgs)
=> 0
>> nl_classify([48,20],avgs)
=> 0
Excelente! Ele agora já reconhece que há uma faixa em que
as equivalências são mais prováveis
Veja o resultado do ponto [48, 20] que fica num extremo
Máquinas de vetor de suporte
Os pontos da margem são chamados de vetores de suporte.
O algoritmo que encontra esses vetores e os usa para
encontrar a linha divisória é a máquina de vetor de suporte.
Já vimos que um classificador linear pode se transformar
em um classificador não-linear utilizando o Truque de
Núcleo, contanto que ele utilize produtos escalares para
comparações.
Portanto, SVM podem ser usadas para fazer
classificação não-linear...
Aplicações das
Máquinas de vetor de suporte
SVM funcionam bem com conjuntos de dados de muitas
dimensões. Alguns exemplos incluem:
Classificar expressões faciais
Detectar intrusos utilizando conjuntos de dados militares
Prever a estrutura de proteínas a partir de suas
sequências
Reconhecimento de letra escrita manualmente
Determinar o estrago potencial durante terremotos
Máquinas de vetor de suporte
O princípio das SVMs é o de criar uma função de
predição a partir de um conjunto de treinamento.
Para tanto, SVMs encontram um hiperplano que
tenta dividir as categorias positiva e negativa
com a maior margem possível de todos os seus
lados.
Máquinas de vetor de suporte
Dado um conjunto de treinamento com os pares
onde:
e
as máquinas de vetor de suporte (SVMs) buscam a solução
do seguinte problema de otimização:
Máquinas de vetor de suporte
Os vetores ‘x’ são mapeados para um espaço N-dimensional
pela função O e a SVM encontra um hiperplano de separação
com a máxima margem no maior espaço dimensional.
Máquinas de vetor de suporte
C > 0 é o parâmetro de penalidade do termo de erro e
é chamada de função de núcleo.
Embora existam várias funções de núcleo propostas por pesquisadores, as 4 funções
mais comuns são:
Máquinas de vetor de suporte
Os conceitos matemáticos são profundos e vão
além do escopo desta apresentação...
Mas, “por sorte”, temos uma biblioteca de código
aberto chamada LIBSVM, que pode treinar um
modelo de SVM, fazer e testar previsões dentro
de um conjunto de dados.
Ela possui suporte implementado para a Função
de Base Radial e outros métodos de núcleo.
LIBSVM
Pode-se conseguir a LIBSVM em
http://www.csie.ntu.edu.tw/~cjlin/libsvm/
Para Ruby, existe uma interface para ela usando
SWIG em
http://github.com/tomz/libsvm-ruby-swig
LIBSVM e o
Conjunto de dados de encontros
O primeiro passo é converter o conjunto de dados
normalizados (para equalizar a importância das
variáveis) para as listas exigidas por “Model”.
Criar o conjunto de dados do problema com as listas
de categorias e a dos dados de entrada (“Problem”)
Escolher a função de base radial como núcleo
(“Parameter”)
Criar o modelo (“Model”)
LIBSVM e o
Conjunto de dados de encontros
Agora se pode fazer as previsões via SVM:
>> newrow = [28.0, -1, -1, 26.0, -1, 1, 2, 0.8]
=> [28.0, -1, -1, 26.0, -1, 1, 2, 0.8] Homem não quer filhos,
>> m.predict(scale_func.call(newrow)) mulher quer
=> 0.0
>> newrow = [28.0, -1, 1, 26.0, -1, 1, 2, 0.8]
=> [28.0, -1, 1, 26.0, -1, 1, 2, 0.8] Ambos querem
>> m.predict(scale_func.call(newrow))
=> 1.0
LIBSVM e o
Conjunto de dados de encontros
A LIBSVM também inclui funcionalidade para validação
cruzada dos modelos (“cross_validation”)
Conj. de dados = conj. de treinamento + conj. de teste
Ela precisa de um parâmetro n e divide o conjunto
de dados em n subconjuntos, fazendo a combinação
de conjuntos de treinamento e de teste entre eles
Retorna uma lista de respostas para ser comparada
com a original
LIBSVM e o
Conjunto de dados de encontros
Agora se pode fazer as previsões via SVM:
>> guesses = cross_validation(prob, param, 4)
=> [0.0, 0.0, ...
>> sum = 0
=> 0 120 diferenças entre as
respostas e as
>> answers.length.times do |i| previsões, significa que
?> sum += (answers[i] - guesses[i]).abs o algoritmo conseguiu
?> end 380 equivalêncas
=> 500 corretas.
>> sum
=> 120.0
SVM e Classificação de documentos
Uma outra aplicação em que SVM apresenta
ótimos resultados é a Classificação de Textos
Neste caso, o primeiro passo a se realizar é o
de se preparar os dados, construindo vetores
de documentos
SVM e Classificação de documentos
Converter todos os documentos usando um
“vector space model”. Neste modelo, ao invés
de se trabalhar com palavras ou sentenças,
o texto é quebrado em palavras,
um ID único é associado a cada uma delas, e
o texto é reconstruído como uma sequência
de IDs de palavras únicos
SVM e Classificação de documentos
Neste exemplo, se usarmos o Dicionário
Global para cada documento e marcarmos as
palavras presentes como ‘1’ e as ausentes
como ‘0’, teremos:
Document A: [1, 1, 0, 1, 1], isto é, índices 1,
2, 4 e 5 presentes e o índice 3 ausente
Document B: [0, 1, 1, 1, 1]
SVM e Classificação de documentos
•Kernel Linear made 3 errors on the training set
•Kernel Linear made 2 errors on the test set
•Kernel Polynomial made 0 errors on the training set
• Prediction: 1.0, True label: 1
• Prediction: 0.0, True label: 0
• Prediction: 0.0, True label: 0
•Kernel Polynomial made 0 errors on the test set
•Kernel Radial basis function made 0 errors on the training set
•Kernel Radial basis function made 0 errors on the test set
•Kernel Sigmoid made 3 errors on the training set
•Kernel Sigmoid made 2 errors on the test set
Referências
Segaran, Toby. “Programming Collective Intelligence”
Chih-Wei Hsu, Chih-Chung Chang and Chih-Jen Lin. A Practical Guide
to Support Vector Classification. http://www.csie.ntu.edu.tw/~cjlin/
Igvta.com. “Support Vector Machine in Ruby”. http://
www.igvita.com/2008/01/07/support-vector-machines-svm-in-ruby/
LIBSVM
http://www.csie.ntu.edu.tw/~cjlin/libsvm/
LIBSVM-Ruby
http://github.com/tomz/libsvm-ruby-swig