Pré-processamento de Dados Fabrício Olivetti de França Universidade Federal do ABC
Pré-Processamento dos Dados
Tópicos 1. Pré-Processamento dos Dados 2. Conceitos Básicos 3. Representação Textual 4. Padrões Recorrentes 5. Padronizando e Normalizando os Atributos 6. Paralelizando o Pré-Processamento 1
Conceitos Básicos
Data Sets Conjunto de objetos de dados. Objeto de dados é uma entidade: funcionário de uma empresa medições da atividade cerebral em um paciente descrição de uma ocorrência policial 2
Objetos de Dados Idade Local Valor do impulso elétrico etc. 3
Tipos de Atributos Categóricos: Nominal Binário Ordinal Numéricos: Intervalar Razão 4
Atributos Nominais Símbolos ou nomes descritivos: Descreve uma característica Não apresentam relação de ordem, apenas igualdade Ex.: ocupação, cor dos olhos, etc. 5
Atributos Binários Atributo nominal com apenas dois valores possíveis: 0 ou 1. Ex.: resultado de um exame, gênero, etc. 6
Atributos Ordinais Valores que apresentam ordem, mas não possuem relação de magnitude. Ex.: {péssimo, ruim, regular, bom, ótimo} 7
Atributos Intervalares Atributo numérico com relação de ordem e magnitude. Ex.: Temperatura de 20 C é 10 unidades mais quente do que 10 C. Não podemos dizer que 20 C é duas vezes mais quente do que 10 C. 8
Atributos Intervalares O 0 absoluto em Celsius está em 273.15 C. 20 C está a 293.15 de nenhum calor, o dobro desse calor seria 586.30 ou 313.15 C. 9
Atributos de Razões Atributos numéricos com a mesmas propriedades do intervalar, mas com um ponto-zero. Altura Contagem de palavras Temperatura em Kelvin 10
Representando Objetos Precisamos de uma representação numérica. Dados numéricos: ok! (ou quase sempre ok) Dados nominais: vetor binário Dados ordinais: rank 11
Representando Objetos Tabela 1: Base de Dados. Profissão Conceito Nota Engenheiro A 9.5 Professor B 8.4 Engenheiro A 7.3 Gerente D 9.1 12
Atributos Categóricos Binários Tabela 2: Base de Dados. Engenheiro Professor Gerente Conceito Nota 1 0 0 A 9.5 0 1 0 B 8.4 1 0 0 A 7.3 0 0 1 D 9.1 13
Atributos Ordinais Numéricos # conceitos = 5 Rank: 5, 4, 3, 2, 1 z = (rank 1) # 1 Tabela 3: Base de Dados. Engenheiro Professor Gerente Conceito Nota 1 0 0 1 9.5 0 1 0 0.75 8.4 1 0 0 1 7.3 0 0 1 0.25 9.1 14
Leitura da Base de Dados data Profissao = Engenheiro Professor Gerente Estudante deriving (Show, Read, Eq, Enum, Bounded) data Conceito = F D C B A deriving (Show, Read, Enum) type Nota = Double type Objeto = (Profissao, Conceito, Nota) type Objeto = [Double] 15
Leitura da Base de Dados parsefile :: String -> [Objeto] parsefile file = map parseline (lines file) where parseline l = toobj (words l) toobj [w1, w2, w3] = (read w1 :: Profissao, read w2 :: Conceito, read w3 :: Nota) A função words separa uma string em uma lista de strings cortando nos caracteres de espaço. 16
Leitura da Base de Dados transformdata :: [Objeto] -> [Objeto ] transformdata data = map parseobj data where parseobj (prof, conc, nota) = (binariza prof) ++ [rank conc, nota] 17
Leitura da Base de Dados binariza :: Profissao -> [Double] binariza p = map bool2double [p == p p <- profissoes] where profissoes = [minbound..] :: [Profissao] bool2double True = 1.0 bool2double _ = 0.0 18
Leitura da Base de Dados rank :: Conceito -> Double rank co = (fromenum co) / (fromenum A) where fromenum = fromintegral. fromenum 19
Fluxo dos Dados A leitura e transformação dos dados segue um fluxo bem definido. É fácil perceber que, enquanto uma linha do arquivo está sendo processada pela função transformdata, outra pode ser processada pela função parsefile. Arquivo parsefile transformdata 20
Representação Textual
Mineração de Textos Documentos de textos: Não possuem representação vetorial Interdependência dos atributos Tamanho variável 22
Textos como conjuntos Se representarmos os documentos de textos como o conjunto de suas palavras: D1 = Estou assistindo a uma aula de Big Data, mas tudo que aprendi foi Haskell durante a aula! D2 = Hoje aprendi Haskell na aula, será que o que aprendi será útil na minha vida? 23
Textos como conjuntos Se representarmos os documentos de textos como o conjunto de suas palavras: D1 = {Estou, assistindo, a, uma, aula, de, Big, Data, mas, tudo, que, aprendi, foi, Haskell, durante, aula!} D2 = {Hoje, aprendi, Haskell, na, aula, será, que, o, útil, minha, vida?} 24
Textos como conjuntos Calculando a similaridade de Jaccard, temos: D1 D2 = {que, aprendi, Haskell} D1 D2 = {Estou, assistindo, a, uma, aula, de, Big, Data, mas, tudo, que, aprendi, foi, Haskell, durante, aula!, Hoje, na, aula, será, o, útil, minha, vida?} J(D1, D2) = 3 24 = 0.125 Essa representação é conhecida como Bag-of-Words, em que geramos os atributos do texto como atributos categóricos. 25
Normalização do Texto Pode ser interessante padronizar a forma do texto para termos serem considerados como um elemento único do conjunto independente de como é escrito. Por exemplo: Estou, estou, estou, aula!, aula, aula?, útil, util. D1 = {estou, assistindo, a, uma, aula, de, big, data, mas, tudo, que, aprendi, foi, haskell, durante, aula} D2 = {hoje, aprendi, haskell, na, aula, sera, que, o, util, minha, vida} J(D1, D2) = 4 23 = 0.17 26
Eliminação de atributos irrelevantes Podemos eliminar palavras que não apresentam significado sozinhas: D1 = {estou, assistindo, aula, big, data, tudo, aprendi, haskell, durante, aula} D2 = {hoje, aprendi, haskell, aula, sera, util, minha, vida} J(D1, D2) = 4 14 = 0.28 27
Bag-of-Words type Doc = String type Token = String bagofwords :: [Doc] -> [[Token]] bagofwords docs = naovazio $ map tokeniza docs where tokeniza doc = nub $ filter maisde2 $ map normaliza (words doc) normaliza palavra = map tolower $ filter isalphanum palavra maisde2 palavra = (length palavra) > 2 naovazio xs = filter (not. null) xs 28
Bag-of-Words A função tolower converte um caractere maiúsculo para minúsculo, e a função isalphanum retorna verdadeiro se o caractere é uma letra do alfabeto ou um número. 29
Fluxo dos Dados O fluxo dos dados pode ser descrito com o seguinte fluxograma: map tokeniza words map normaliza filter isalphanum map tolower naovazio nub filter maisde2 30
Term-Frequency Se um termo aparece repetidas vezes em um documento, isso significa que ele pode ter uma importância maior do que os outros termos. No nosso exemplo, a repetição do termo Haskell indica um dos temas dos nossos documentos. 31
Term-Frequency A informação de frequência pode ser importante para a representação de nossos documentos. Podemos fazer então: fn(t, d) = f (t, d), d com f (t, d) sendo a frequência do termo t no documento d e d a quantidade de termos no documento d. 32
Term-Frequency A ideia para computar os vetores TF é primeiro representar cada documento como uma lista (token, 1.0) e, em seguida: Ordenar essa lista pelo token Agrupar os itens com mesmo token Somar os valores em cada grupo Dividir os valores pelo número de tokens 33
TF type Freq = Double tf :: [Doc] -> [[(Token, Freq)]] tf docs = naovazio $ map tokeniza docs where tokeniza doc = fn $ map normtupla (words doc) normtupla w = (normaliza w, 1.0) 34
TF fn :: [(Token, Double)] -> [(Token, Freq)] A partir desse momento trabalharemos frequentemente com bases de dados representadas como listas de tuplas. Essas tuplas devem ser encaradas como chave e valor, respectivamente. Para tornar o código mais legível, vamos definir as funções mapbykey, foldbykey, groupbykey, sortbykey 35
mapbykey mapbykey aplica a função g apenas no valor da tupla, deixando a chave intacta: mapbykey g = map (\(k,v) -> (k, g v)) 36
foldbykey foldbykey assume uma lista de tuplas chave-valor em que todas as chaves são iguais. Essa função aplica foldl1 apenas nos valores das tuplas, resultando em uma tupla (k, v) em que k é a chave de todas as tuplas e v o resultado da operação fold: foldbykey g = foldl1 (\(k1,v1) (k2,v2) -> (k1, g v1 v2)) 37
sortbykey sortbykey ordena uma lista de tuplas pela chave: sortbykey = sortby ordenatupla ordenatupla :: (Ord a) => (a, t) -> (a, t) -> Ordering ordenatupla (a1,b1) (a2,b2) a1 < a2 = LT a1 > a2 = GT a1 == a2 = EQ 38
groupbykey groupbykey agrupa uma lista ordenada de tuplas gerando uma lista de listas, com cada lista agrupando as tuplas de mesma chave: groupbykey = groupby agrupatupla agrupatupla :: (Eq a) => (a, t0) -> (a, t0) -> Bool agrupatupla (a1, b1) (a2, b2) = a1==a2 groupbykey [(1,0.1), (1,0.2), (2,0.1)] == [ [(1,0.1),(1,0.2)], [2,0.1] ] 39
TF fn :: [(Token, Double)] -> [(Token, Freq)] fn tokens = mapbykey (/n) $ map (foldbykey (+)) $ groupbykey $ sortbykey tokens where n = length tokens 40
Fluxo dos Dados O fluxo dos dados pode ser descrito com o seguinte fluxograma: map tokeniza words map normaliza sortbykey groupbykey mapbykey map foldbykey 41
Inverse Document Frequency Algumas palavras aparecem com uma frequência muito superior as demais, como: e, que, ou, etc. Essas palavras não costumam apresentar um significado discriminatório e, portanto, podem ter um peso menor. Para isso podemos multiplicar o TF por: idf (t) = log D {d D : t D} 42
TF-IDF Como veremos mais adiante, é possível pensar no vetor IDF como uma matriz associativa. Para isso utilizaremos o HashMap do Haskell. idf :: [[(Token, Freq)]] -> M.HashMap Token Freq idf corpus = M.fromList $ mapbykey (\v -> log (n/v)) $ map (foldbykey (+)) $ groupbykey $ sortbykey $ map (\ (k,v) -> (k,1)) $ concat corpus where n = length corpus 43
HashMap -- importa a biblioteca HashMap.Strict apelidade de M import qualified Data.HashMap.Strict as M -- cria um mapa M.HashMap Integer Double da lista de tuplas mapa = M.fromList [(1, 0.1), (2, 0.3), (3, 0.04)] mapa M.! 2 -- retorna 0.3 concat [ [1,2], [3,4] ] = [1,2,3,4] 44
TF-IDF tfidf :: [[(Token, Freq)]] -> M.HashMap Token Freq -> [[(Token, Freq)]] tfidf tf idf = map multidf tf where multidf = mapbykey (\(k,v) -> (k, v * (idf M.! k)) ) 45
Fluxo dos Dados O fluxo dos dados pode ser descrito com o seguinte fluxograma: map multidf mapbykey concat map sortbykey fromlist mapbykey map foldbykey groupbykey 46
Padrões Recorrentes
Padrões de sequência de funções Nos exemplos anteriores, podemos perceber um padrão recorrente de chamada de funções utilizadas em diversas soluções: map (foldbykey (+)) $ groupbykey $ sortbykey Esse padrão precede a chamada de uma função map que transforma um valor em uma tupla chave-valor (k, v). 49
Padrões de sequência de funções Basicamente esse padrão combina os valores das tuplas com a mesma chave utilizando uma função (nos exemplos utilizamos (+)). É interessante, então, criar a função: combine :: Ord k => (v -> v -> v) -> [(k, v)] -> [(k, v)] combine f xs = map (foldbykey f) $ groupbykey $ sortbykey xs Com isso, muitas funções se tornam sequências de combine. map. 50
fn fn :: [(Token, Double)] -> [(Token, Freq)] fn tokens = mapbykey (/n) $ combine (+) tokens where n = length tokens 51
idf Como veremos mais adiante, é possível pensar no vetor IDF como uma matriz associativa. Para isso utilizaremos o HashMap do Haskell. idf :: [[(Token, Freq)]] -> M.HashMap Token Freq idf corpus = M.fromList $ mapbykey (\v -> log (n/v)) $ combine (+) $ mapbykey (\v -> 1) $ concat corpus where n = length corpus 52
Padronizando e Normalizando os Atributos
Padronização Muitos algoritmos de Aprendizado de Máquina supõem que os valores dos atributos seguem N(0, 1). Se um atributo não segue esse padrão, pode dominar a função-objetivo e se tornar importante demais. 53
Padronização Dado uma matriz de dados X, podemos padronizar os valores de cada um de seus elementos como: ˆX i,j = X i,j X i,j σ j 54
Padronização padroniza :: [[Double]] -> [[Double]] padroniza x = mapcolunas padroniza x padroniza :: [Double] -> [Double] padroniza x = devmedia./ sigma where media xs = (sum xs) / n devmedia = x.- (media x) sigma = sqrt $ media $ devmedia.** 2 n = length x 55
Fluxo dos Dados O fluxo dos dados pode ser descrito com o seguinte fluxograma: mapcolunas padroniza media devmedia./.** 2 media 56
Escalonamento Algoritmos baseados em métodos de gradiente tendem a se beneficiar quando os atributos estão entre [0, 1]. ˆX i,j = X i,j min X :,j max X :,j min X :,j 57
Padronização maxminscale :: [[Double]] -> [[Double]] maxminscale x = mapcolunas maxminscale x maxminscale :: [Double] -> [Double] maxminscale x = map scale x where scale xi = (xi - minimum x) / (maximum x - minimum x) 58
Normalização Finalmente podemos normalizar cada amostra da base utilizando a normalização de vetores: X i,j ˆX i,j = X i p 59
Padronização normaliza :: [[Double]] -> [[Double]] normaliza x = map normaliza x where normaliza xi = xi./ (norma xi) norma xi = sqrt. sum $ xi.^ 2 60
Paralelizando o Pré-Processamento
Paralelizando chunks Conforme vimos na aula anterior, ao paralelizar um programa objetivamos minimizar o número de sparks e tentar distribuir a tarefa de forma homogênea entre as threads. 62
Paralelizando chunks Para isso adotamos a estratégia de dividir nossos dados em pedaços (denominados chunks), processar cada chunk em paralelo e, em seguida, juntar os resultados. 63
Paralelizando chunks Vamos continuar utilizando o tipo ChunksOf definido anteriormente para representar nossos arquivos distribuídos entre diversas máquinas. 64
Paralelizando Leitura dos Dados Com isso, nossa função de processamento dos dados em paralelo simplesmente aplica a função transformdata em cada um dos chunks em paralelo. transformdatapar :: ChunksOf [Objeto] -> ChunksOf [Objeto ] transformdatapar chunks = (map transformdata chunks using parlist rdeepseq) Note que nenhuma alteração é necessária para a função transformdata que continuará recebendo o tipo [Objeto] e retornando [Objeto ]. 65
Paralelizando a Padronização Na padronização, não podemos calcular as três partes da equação em paralelo. Além disso, temos um outro desafio, recebemos pedaços de linhas, mas temos que trabalhar com as colunas: padroniza :: [[Double]] -> [[Double]] padroniza x = mapcolunas padroniza x padroniza :: [Double] -> [Double] padroniza x = (x.- media)./ sigma where media = (sum x) / n sigma = sqrt $ sum $ (x.- media).** 2 n = fromintegral $ length x 66
Paralelizando a Padronização Primeiro, vamos alterar a assinatura da função para sabermos onde queremos chegar: padronizapar :: ChunksOf [[Double]] -> ChunksOf [[Double]] padronizapar chunks = parmap padroniza chunks where padroniza = map (\xi -> (xi.-. media)./. desvio) media = mapreduce id (.+.) chunks desvio = map (\x -> sqrt (x/n)) $ mapreduce desvquad (.+.) chunks desvquad xi = (xi.-. media).**2 n = sum $ map length chunks 67
Paralelizando a Normalização A implementação paralela de normaliza pode ser feita diretamente aplicando a versão sequencial em cada chunk. normalizapar :: ChunksOf [[Double]] -> ChunksOf [[Double]] normalizapar chunks = parmap normaliza chunks normaliza :: [[Double]] -> [[Double]] normaliza x = map normaliza x where normaliza xi = xi./ (norma xi) norma xi = sqrt. sum $ xi.^ 2 68
Paralelizando o TF Da forma que organizamos o algoritmo de TF, utilizando map e combine, basta aplicar a função tf em cada chunk. tfpar :: ChunksOf [Doc] -> ChunksOf [[(Token, Freq)]] tfpar chunks = parmap tf chunks tf :: [[Token]] -> [[(Token, Freq)]] tf docs = map normfreq docs where normfreq doc = fn $ map (\w -> (w, 1.0)) doc 69
Paralelizando a o IDF Para paralelizar o IDF, precisamos calcular a contagem de quantos documentos contém cada token em cada chunk. Em seguida, é necessário combinar os resultados de cada chunk para então calcular o idf. idfpar :: ChunksOf [[(Token, Freq)]] -> M.HashMap Token Freq idfpar chunks = M.fromList $ mapbykey (\v -> log (n/v)) $ combine (+) $ concat $ (map idf chunks using parlist rdeepseq) where n = sum $ map length chunks idf :: [[(Token, Freq)]] -> [(Token, Freq)] idf corpus = combine (+) $ map (\(k,v) -> (k,1) ) $ concat corpus 70
Padrão de paralelismo Note que o padrão de paralelismo do cálculo do IDF difere dos anteriores pois é necessário o uso do combine no lugar de foldl1 e a aplicação de concat na saída do map. 71
Padrão de paralelismo Uma função genérica pode ser escrita da seguinte forma e aplicada em muitas situações em que trabalhamos com lista de tuplas: mapreducebykey :: (NFData k, NFData v, Ord k) => (a -> (k, v)) -> (v -> v -> v) -> ChunksOf [a mapreducebykey f g xs = combine g $ concat $ (map f xs using parlist rdeepseq) where f xi = combine g $ map f xi 72
Padrão de paralelismo Nossa função idfpar ficaria: idfpar :: ChunksOf [[(Token, Freq)]] -> M.HashMap Token Freq idfpar chunks = M.fromList $ mapbykey (\v -> log (n/v)) $ mapreducebykey (\(k,v) -> (k,1)) (+) $ parmap concat chunks where n = sum $ map length chunks 73