Mapas auto-organizáveis (SOM) são técnicas de visualização de dados não supervisionada que pode ser usada para visualizar conjuntos de dados de alta dimensão em representações menores (tipicamente 2) dimensões. Vamos usar dados sintéticos de uma rede de computadores hipotética, onde dois conjunto de dados normais são comparados a um conjunto de dados com anomalias, ou dados divergentes dos ditos normais.
A visualização do SOM é composta por múltiplos “nós”. Cada vetor de nó tem:
Uma posição fixa na grelha SOM Um vetor de peso da mesma dimensão que o espaço de entrada. (Por exemplo, se seus dados de entrada representam pessoas, ele pode ter variáveis “idade”, “sexo”, “altura” e “peso”, cada nó na grade também terá valores para essas variáveis).
Amostras associadas dos dados de entrada. Cada amostra no espaço de entrada é “mapeada” ou “ligada” a um nó na grade do mapa. Um nó pode representar várias amostras de entrada. O recurso chave para os SOMs é que as características topológicas dos dados de entrada originais são preservadas no mapa. O que isto significa é que as amostras de entrada semelhantes (onde a similaridade é definida em termos das variáveis de entrada (idade, sexo, altura, peso)) são colocadas juntas na grelha SOM. Por exemplo, todas as fêmeas de 55 anos de idade que são aproximadamente 1,6 m de altura serão mapeadas para nós na mesma área da grade. Pessoas mais altas e menores serão mapeadas em outros lugares, levando em conta todas as variáveis. Os machos pesados altos estarão mais próximos no mapa para fêmeas altas e pesadas do que os pequenos homens leves, pois são mais “semelhantes”.
As visualizações SOM típicas são de “heatmaps”. Um heatmap mostra a distribuição de uma variável no SOM. Se imaginarmos o nosso SOM como um quarto cheio de pessoas que estamos olhando para baixo, e que foram para obter cada pessoa na sala para sustentar um cartão colorido que representa a sua idade - o resultado seria um SOM heatmap. Pessoas de idades semelhantes, idealmente, seriam agregadas na mesma área. O mesmo pode ser repetido para a idade, peso, etc. A visualização de diferentes heatmaps permite explorar a relação entre as variáveis de entrada.
A figura abaixo demonstra a relação entre o nível médio de educação ea porcentagem de desemprego usando dois heatmaps. O SOM para estes diagramas foi gerado usando áreas ao redor da Irlanda como amostras.
O algoritmo para produzir um SOM a partir de um conjunto de dados de amostra pode ser resumido da seguinte forma:
Treinamento
O pacote kohonen é um pacote bem documentado em R que facilita a criação e visualização de SOMs. Para iniciar, você só precisará do conhecimento de um pequeno número de funções-chave, o processo geral em R é o seguinte:
# Carregar o pacote kohonen
library("kohonen")
library(readr)
Vamos criar nossas bases de dados sintéticas, duas com dados ditos normais e uma com dados espúrios. Obserem que as sementes são as mesmas para os três dataset sintéticos, ou seja, estamos querendo manter as variáveis sob controle.
set.seed(1)
normal1 <- data.frame(trafic = rnorm(1000,25,3),
prot = rep(c(80,443,21,22,23),c(100,100,100,100,100)),
user = rep(c(1,2,3,4,5),c(200,200,200,200,200)))
set.seed(1)
normal2 <- data.frame(trafic = rnorm(1000,30,5),
prot = rep(c(21,22,80,443,23),c(100,100,100,100,100)),
user = rep(c(3,1,7,9,2),c(200,200,200,200,200)))
set.seed(1)
anormal <- data.frame(trafic = rnorm(1000,25,3),
prot = rep(c(21,80,22,443,23),c(100,100,100,100,100)),
user = rep(c(5,2,7,4,9),c(200,200,200,200,200)))
anormal[125:135,]$trafic <- 100 # inserimos aqui 1% de dados espúrios no tráfego
A matriz de treinamento está divida entre os dados normais e anomalo, n1 e n2 para normais e a para o anomalo.
data_train_matrix_n1 <- as.matrix(scale(normal1))
data_train_matrix_n2 <- as.matrix(scale(normal2))
data_train_matrix_a <- as.matrix(scale(anormal))
som_grid_n1 <- somgrid(xdim = 20, ydim=20, topo="hexagonal")
som_grid_n2 <- somgrid(xdim = 20, ydim=20, topo="hexagonal")
som_grid_a <- somgrid(xdim = 20, ydim=20, topo="hexagonal")
som_model_n1 <- som(data_train_matrix_n1,
grid=som_grid_n1, rlen=1000,
alpha=c(0.05,0.01), keep.data = TRUE)
som_model_n2 <- som(data_train_matrix_n2,
grid=som_grid_n1, rlen=1000,
alpha=c(0.05,0.01), keep.data = TRUE)
som_model_a <- som(data_train_matrix_a,
grid=som_grid_a, rlen=1000,
alpha=c(0.05,0.01), keep.data = TRUE)
Vários tipos diferentes de gráficos são suportados:
Mostra a distância média ao vetor de código mais próximo durante o treinamento.
par(mfrow=c(1,3),mar=c(1, 0.5, 1.5, 0.5))
plot(som_model_n1, type="changes")
plot(som_model_n2, type="changes")
plot(som_model_a, type="changes")
Mostra o número de objetos mapeados para as unidades individuais.
As unidades vazias são representadas em cinza.
par(mfrow=c(1,3),mar=c(0.1, 0.5, 0.1, 0.1))
plot(som_model_n1, type="count")
plot(som_model_n2, type="count")
plot(som_model_a, type="count")
Não há grandes diferenças na contagem, os mapas parecem estar todos normais. Apesar das contagens do terceiro conter mais cores cinzas do que os dois anteriores.
Mostra a soma das distâncias para todos os vizinhos imediatos. Esse tipo de visualização também é conhecido como um gráfico de U-matrix. As unidades perto de um limite de classe podem ser esperadas para ter distâncias médias mais elevadas para seus vizinhos. Apenas disponível para os mapas "som" e "supersom", por enquanto.
par(mfrow=c(1,3),mar=c(0, 0.5, 0, 0))
plot(som_model_n1, type="dist.neighbours")
plot(som_model_n2, type="dist.neighbours")
plot(som_model_a, type="dist.neighbours")
Mostra os vetores da tabela de códigos.
par(mfrow=c(1,3),mar=c(0, 0.5, 0, 0))
plot(som_model_n1, type="codes")
plot(som_model_n2, type="codes")
plot(som_model_a, type="codes")
Observem as faixas nos dois primeiros (normais), e apenas um conjunto diferente no terceiro. aqui são mostradas as distâncias entre os neurônios.
par(mfrow=c(1,3),mar=c(0, 0.5, 0, 0))
plot(som_model_n1, type = "property", property = som_model_n1$codes[[1]][,1],
main=names(som_model_n1$data)[4])
plot(som_model_n2, type = "property", property = som_model_n2$codes[[1]][,1],
main=names(som_model_n2$data)[4])
plot(som_model_a, type = "property", property = som_model_a$codes[[1]][,1],
main=names(som_model_a$data)[4])
Neste mapa de propriedades vamos explorar os dados com as propriedades de suas colunas: Tráfego, protocolos e usuáios.
Para os dados normais #1
par(mfrow=c(1,3),mar=c(0, 0.5, 0, 0))
for(var in 1:3) {
var_unscaled <- aggregate(as.numeric(normal1[,var]),
by=list(som_model_n1$unit.classif),
FUN=mean, simplify=TRUE)[,2]
plot(som_model_n1, type = "property", property=var_unscaled,
main=names(normal1)[var])
}
Para os dados normais #2
par(mfrow=c(1,3),mar=c(0, 0.5, 0, 0))
for(var in 1:3) {
var_unscaled <- aggregate(as.numeric(normal2[,var]),
by=list(som_model_n2$unit.classif),
FUN=mean, simplify=TRUE)[,2]
plot(som_model_n2, type = "property", property=var_unscaled,
main=names(normal2)[var])
}
Para os dados anômalos
par(mfrow=c(1,3),mar=c(0, 0.5, 0, 0))
for(var in 1:3) {
var_unscaled <- aggregate(as.numeric(anormal[,var]),
by=list(som_model_a$unit.classif),
FUN=mean, simplify=TRUE)[,2]
plot(som_model_a, type = "property", property=var_unscaled,
main=names(anormal)[var])
}
Observe que apenas o mapa do tráfego é diferente dos demais, ou seja, é possível verificar qual variável é espúria dentre as váriaveis do modelo.