textos extraindo atributos de -...

95
Extraindo atributos de textos Prof. Fabrício Olivetti de França

Upload: phungkiet

Post on 15-Dec-2018

218 views

Category:

Documents


0 download

TRANSCRIPT

Extraindo atributos de textos

Prof. Fabrício Olivetti de França

ConfiguraçãoFaça o download do Anaconda Python:

https://www.continuum.io/downloads

E do notebook:

http://folivetti.github.io/courses/Lcon2016/

2

Crie o ambienteCrie o ambiente:

conda create --name lcon python=3 scikit-learn numpy scipy gensim pandas jupyter ipython nltk

pip install powerlaw

Ative-o com source activate lcon

E abra o notebook:

jupyter notebook3

Representação computacionalMuitos algoritmos de aprendizado de máquina foram formalizados assumindo como entrada amostras em um espaço vetorial.

Quando trabalhamos com bases de dados que não são naturalmente representadas no espaço vetorial, devemos extrair os atributos.

4

Documentos de textosd1: o gato caçou o rato

d2: o rato comeu o queijo

Precisamos encontrar v1 e v2 de tal forma que cada posição dos vetores represente um aspecto específico dos documentos.

5

Documentos de textosd1: o gato caçou o rato

d2: o rato comeu o queijo

v1 = [1, 4, 5, 1, 4]

v2 = [1, 4, 5, 1, 6]

6

Documentos de textosd1: o gato caçou o rato

d2: o rato comeu o queijo

v1 = [1, 4, 5, 1, 4]

v2 = [1, 4, 5, 1, 6]

||v1-v2|| = 2, distância baixa, similaridade alta.

[Euclidiana]7

Documentos de textosd1: o gato caçou o rato

d2: o rato comeu o queijo provolone

v1 = [1, 4, 5, 1, 4, ?]

v2 = [1, 4, 5, 1, 6, 9]

8

Bag of WordsCada objeto é representado por um conjunto F’ de atributos que é um subconjunto do conjunto F de atributos observados na base.

O1 = {F1, F4, F5, F9}

O2 = {F3, F4, F5}

9

Bag of WordsÉ possível representar computacionalmente esses conjuntos de diversas formas: vetor binário denso, vetor esparso, bitmaps, tries, etc.

A escolha deve ser de acordo com a aplicação.

10

Bag of WordsNos vetores binários cada posição corresponde a um token do dicionário.

Os vetores terão tamanho igual ao total de tokens distintos.

Um token é um elemento textual: letra, palavra, frase, símbolo, etc.

11

Bag of Wordsd1: o gato caçou o ratod2: o rato comeu o queijo provolone

dicionário = { ‘o’ : 0, ‘gato’ : 1, ‘caçou’ : 2, ‘rato’ : 3, ‘comeu’ : 4, ‘queijo’ : 5, ‘provolone’ : 6}

v1 = [1, 1, 1, 1, 0, 0, 0]

v2 = [1, 0, 0, 1, 1, 1, 1]

12

Bag of WordsCada elemento do vetor representa se determinado token existe ou não no documento.

v1 = [1, 1, 1, 1, 0, 0, 0]

v2 = [1, 0, 0, 1, 1, 1, 1]

sum(v1 == v2)/len(dicionario) = 1/7 = 0.14

[Jaccard]

13

Bag of WordsPodemos também atribuir pesos representando a frequência dos termos:

v1 = [2, 1, 1, 1, 0, 0, 0]

v2 = [2, 0, 0, 1, 1, 1, 1]

np.dot(v1, v2)/(np.dot(v1,v1)*np.dot(v2,v2)) = 5/(7.8) = 0.09

[Cosseno]

14

Cosseno

w1

w2

15

Bag of WordsA biblioteca scikit-learn possui classes para extrair atributos do tipo BOW.

import pandas as pdimport sklearn.feature_extraction.text as txtfeats

tweets = pd.read_csv('tweets.csv.gz')bagofwords = txtfeats.CountVectorizer()bow = bagofwords.fit_transform(tweets.text)

16

Bag of Wordsprint(len(bagofwords.get_feature_names()))>> 231602

bagofwords.get_feature_names()[5000:5004]>> ['alemanharoubo', 'alemanhas', 'alemao', 'alemaoe']

Cada tweet tem em média 16 palavras! Cada tweet será representado por um vetor em que, em média, 231586 elementos serão 0.

17

Bag of Words

Geralmente representamos como vetor esparso:

[ (5002,1), (201288, 1), (220665, 2) ]

O tweet contém uma ocorrência do token 5002, uma do token 201288 e duas do token 220665.

18

Bag of WordsQuando vamos gerar um vetor BOW para um novo exemplo, os tokens novos são ignorados:

novo_tweet = 'um novo tweet sobre um alemao kywz'print(bagofwords.transform([novo_tweet]))

>> (0, 5002) 1 (0, 201288) 1 (0, 220665) 1 (0, 226539) 1 (0, 226860) 2

19

PadronizaçãoMacarrão, macarrão, macarrao, MaCarrãO representam a mesma informação.

Para evitar que eles formem tokens diferentes, antes da construção do dicionário é feito uma padronização:

● Remover acentos● Transformar em minúsculas

20

PadronizaçãoExistem também tokens que não contém informação necessária para o processo de aprendizado:

e, ou, está, quando, etc.

Podemos remover essas palavras com o uso de uma lista de stopwords.

21

Bag of WordsParâmetros de pré-processamento:

strip_accents : remove acentos das palavras com codificação ascii ou unicode.

stop_words : permite usar uma lista de stop words.

lowercase : transforma as letras em caixa baixa (padrão).

binary : não contabiliza a frequência dos termos.

22

Corte de LuhnA lei de Zipf diz que a frequência das palavras em um documento é inversamente proporcional ao seu rank.

Ou seja, a palavra mais frequente vai aparecer duas vezes com mais frequência que a segunda mais frequente, três vezes mais frequente que a terceira, …

Luhn argumentou que as palavras muito frequentes e as pouco frequentes não colaboram para discriminação e similaridade entre documentos.

LUHN, H.P., 'The automatic creation of literature abstracts', IBM Journal of Research and Development, 2, 159-165 (1958). 23

Corte de Luhn

24

Corte de LuhnMas como determinar os cortes? Basicamente queremos o meio da distribuição.

df = sorted(bow.sum(axis=0).A1) # frequencia dos termosfit = powerlaw.Fit(df)mediana = fit.xmin*np.power(2, 1/(fit.power_law.alpha-1))df_max = int(np.round(np.power(mediana, 4/3)))df_min = int(np.round(np.power(mediana, 2/3)))

>> df_min = 106, df_max = 11172

25

Corte de Luhnbagofwords = txtfeats.CountVectorizer(min_df=df_min, max_df=df_max, strip_accents='unicode', stop_words=['e', 'ou', 'ele', 'ela'])bow = bagofwords.fit_transform(tweets.text)

>> 5333 tokens

26

tf-idfUma outra forma de reduzir a influência dos termos frequentes é penalizar as frequências dos tokens pelo inverso da frequência no documento.

Quanto mais a frequência do token se aproxima de N, mais idf se aproxima de 0.

27

tf-idf

tfidf = txtfeats.TfidfVectorizer()bow = tfidf.fit_transform(tweets.text)

28

tf-idf

norm : ‘l1’, ‘l2’ or None, optional. Norma usada para normalizar os vetores resultantes.

use_idf : se deseja usar o idf.

smooth_idf : técnica de smooth para evitar divisões por 0.

sublinear_tf : calcula a frequência dos termos como 1 + log(tf).

29

ContextoUm ponto negativo da tokenização por palavras é que os tokens não possuem o contexto.

Para aliviar esse problema surgiu o n-grams.

30

N-gramsd1: o gato caçou o ratod2: o rato comeu o queijo provolone

2-gram:

dicionário = { ‘o gato’ : 0, ‘gato caçou’ : 1, ‘caçou o’ : 2, ‘o rato’ : 3, ‘rato comeu’ : 4, ‘comeu o’ : 5, ‘o queijo’ : 6, ‘queijo provolone’ : 7}

31

N-gramsbagofwords = txtfeats.CountVectorizer(min_df=df_min, max_df=df_max, strip_accents='unicode', ngram_range=(2,2))bow = bagofwords.fit_transform(tweets.text)

>> 10048 tokens

32

N-grams

analyzer : string, {‘word’, ‘char’, ‘char_wb’}: se os n-grams serão determinados por palavras, por caracteres ou por caracteres dentro das palavras.

ngram_range : tuple (min_n, max_n): gera n-gramas com min_n <= n <= max_n.

33

N-gramsProblemas:

● Pode aumentar muito a dimensionalidade● Muitos tokens continuam sem contexto: “o gato”

34

k-Skip-n-GramsTokeniza realizando pulos de 0 a k tokens.

d1: o gato caçou o ratod2: o rato comeu o queijo provolone

3-skip-2-gram:

dicionário = { ‘o gato’ : 0, ‘o caçou’ : 1, ‘o o’ : 2, ‘gato caçou’: 3, ‘gato o’ : 4, ‘gato rato’ : 5, ...}

35

k-skip-n-gramsTem uma chance maior de capturar o contexto, porém pode aumentar a dimensionalidade exageradamente.

Podemos utilizar o parâmetro tokenizer que permite passar um tokenizador customizado.

A biblioteca nltk implementa o k-skip-n-gram.

36

k-skip-n-gramsskip3n2grams = lambda s: nltk.skipgrams(s.split(), 2, 3)

tfidf = txtfeats.TfidfVectorizer(tokenizer=skip3n2grams, min_df=df_min, max_df=df_max)bow = tfidf.fit_transform(tweets.text)

>> 38330 tokens

37

Aprendendo uma representaçãoIdeia:

d1: o gato caçou o ratod2: o rato comeu o queijo provolone

Aprender que entre “o” e “caçou” vem a palavra “gato”.

38

Continuous Bag-of-Word

o

caçou

gato

39

Aprendendo uma representação

Cada neurônio de entrada e o neurônio de saída é um vetor binário em que o elemento representando a palavra de entrada tem valor 1 e o restante, 0.

40

Continuous Bag-of-Word

o

caçou

gato

v(gato)41

Skip-gram

o

caçou

gato

v(gato)42

Continuous Bag-of-Word

Pesos W1dim: V x N

43

Continuous Bag-of-Word

A entrada são vetores binários, cada vetor binário contém apenas um elemento igual a 1.

entrada x W1 = Soma das linhas de W1 correspondentes as palavras da entrada (a ordem então não importa)

h(entrada x W1) = (entrada x W1) / |entradas|

44

Continuous Bag-of-Word

z = h(x . W1)

y = softmax(z. W2)

45

softmaxSaída é um vetor de dimensão V que representa a distribuição categórica de probabilidade para as possíveis saídas.

46

softmaxO objetivo é minimizar a função custo definida como o logaritmo da probabilidade de p(wt | wt+j) ou p(wt+j | wt) dependendo do modelo de word2vec utilizado.

wt = palavra alvo do vetor

wt+j = contexto da palavra

47

Função custo

48

Atualizando os pesos

O processo de atualização demanda que os pesos das V possíveis palavras de saída sejam atualizados!

Lembrem-se que V é geralmente centenas de milhares!

49

Continuous Bag-of-Word

w1 w2 w3 w4

50

Continuous Bag-of-Word

w1 w2 w3 w4

51

Atualizando os pesos

Agora só precisamos fazer o processo de atualização em log(V) elementos!

52

Demohttps://ronxin.github.io/wevi/

53

GloVe: Global Vectors for Word Repr.X = matriz de co-ocorrência que a palavra j ocorreu no contexto de i com janela k.

54

GloVe: Global Vectors for Word Repr.

55

GloVe: Global Vectors for Word Repr.Objetivo: encontrar uma representação vetorial de cada palavra tal que:

56

GloVe: Global Vectors for Word Repr.Minimizar:

57

GloVe: Global Vectors for Word Repr.Com:

58

PolêmicaNo artigo original os autores indicaram que GloVe era mais rápido que o word2vec e apresentava 12% de melhora na tarefa de analogia de palavras.

Discussão sobre validade dos experimentos:

https://docs.google.com/document/d/1ydIujJ7ETSZ688RGfU5IMJJsbxAi-kRl8czSwpti15s/edit#

59

PolêmicaO argumento é que os autores utilizaram apenas os resultados numéricos do artigo original do word2vec, e isso pode ter causado um viés por conta de diferenças na base de treinamento.

Um novo teste realizado indicou que não existe diferença significativa entre os dois sob as mesmas condições.

60

PolêmicaMas ainda está em discussão…

Código: http://nlp.stanford.edu/projects/glove/

61

doc2vec

o

caçou

gato

v(gato)

doc id

62

doc2vecIgual a word2vec, mas agora existe uma nova entrada identificando o parágrafo/documento.

Com isso, cada documento pode ser descrito também como um vetor.

63

gensimfrom gensim.models import doc2vec

class LabeledLineSentence(object): def __init__(self, stream): self.stream = stream def __iter__(self): for uid, line in enumerate(self.stream): yield doc2vec.LabeledSentence(words=line.split(), tags=['SENT_%s' % uid])

64

gensim

model = doc2vec.Doc2Vec(

LabeledLineSentence(tweets.text.values[:1000])

)

65

MinhashLembrando em nossa representação Bag-of-Words:

Token D1 D2 D3 D4A 1 0 0 1B 0 0 1 0C 0 1 0 1D 1 0 1 1E 0 0 1 0

66

MinhashEla gera uma matriz esparsa e de alta dimensão!

Token D1 D2 D3 D4A 1 0 0 1B 0 0 1 0C 0 1 0 1D 1 0 1 1E 0 0 1 0

67

MinhashO cálculo da similaridade (de Jaccard) pode se tornar muito custosa.

Token D1 D2 D3 D4A 1 0 0 1B 0 0 1 0C 0 1 0 1D 1 0 1 1E 0 0 1 0

68

MinhashPré-calcular uma assinatura da matriz que é equivalente a sim. de Jaccard.

Token D1 D2 D3 D4A 1 0 0 1B 0 0 1 0C 0 1 0 1D 1 0 1 1E 0 0 1 0

69

MinhashSe permutarmos aleatoriamente as linhas dessa matriz:

Token D1 D2 D3 D4B 0 0 1 0A 1 0 0 1E 0 0 1 0C 0 1 0 1D 1 0 1 1

70

MinhashE representarmos cada documento pelo índice da primeira linha contendo 1:

Minhash 2 4 1 2Token D1 D2 D3 D4

B 0 0 1 0A 1 0 0 1E 0 0 1 0C 0 1 0 1D 1 0 1 1

71

MinhashDessa forma cada documento pode ser representado por apenas 1 valor!

Minhash 2 4 1 2Token D1 D2 D3 D4

B 0 0 1 0A 1 0 0 1E 0 0 1 0C 0 1 0 1D 1 0 1 1

72

MinhashMas qual a probabilidade de mh(Di) = mh(Dj)?

73

MinhashQuantas linhas o valor 1 coincide dividido pelo total de linhas com pelo menos um valor 1:

74

MinhashComparando D1 e D4

Minhash 2 4 1 2Token D1 D2 D3 D4

B 0 0 1 0A 1 0 0 1E 0 0 1 0C 0 1 0 1D 1 0 1 1

75

MinhashJ = ⅔ = P(mh(D1)=mh(D4))

Minhash 2 4 1 2Token D1 D2 D3 D4

B 0 0 1 0A 1 0 0 1E 0 0 1 0C 0 1 0 1D 1 0 1 1

76

MinhashSe temos P = 2/3, se fizermos 100 permutações diferentes, teremos cerca de 67 valores iguais de minhash entre os dois documentos!

77

MinhashCada documento é representado por N minhashes diferentes.

Di = [mh1(Di), mh2(Di), …, mhN(Di)]

78

MinhashComputar permutações é custoso!

Vamos simplificar utilizando funções hashes!

79

MinhashDada a função hash:

h(x) = (ax + b) mod p

onde x é o índice do atributo.

ela gera uma permutação aleatória com diferentes valores de a,b e mantendo p fixo.

80

MinhashSorteamos N valores de a e b e escolhemos um p primo.

Escolhemos um p grande o suficiente para representar nossas variáveis.

81

MinhashExemplo:

h1(x) = (x + 1) mod 5

h2(x) = (3x + 1) mod 5

82

Minhashh1(x) = (x + 1) mod 5

h2(x) = (3x + 1) mod 5

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

83

MinhashPara cada documento, marcamos as linhas iguais a 1:

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

84

MinhashRepresentamos o documento com os menores valores marcados de cada hash:

SIG(D1) = [1, 0]

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

85

Minhash - AlgoritmoSIG1 e SIG2 inicializam com o maior valor possível +1.

SIG1 5 5 5 5SIG2 5 5 5 5

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

86

Minhash - AlgoritmoPara cada linha, atualizamos os documentos com valor igual a 1:

SIG1 1 5 5 1SIG2 1 5 5 1

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

87

Minhash - Algoritmo

SIG1 1 5 2 1SIG2 1 5 4 1

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

88

Minhash - AlgoritmoRepare que apenas atualizamos os valores se for menor!

SIG1 1 3 2 1SIG2 1 2 4 1

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

89

Minhash - Algoritmo

SIG1 1 3 2 1SIG2 0 2 0 0

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

90

Minhash - Algoritmo

SIG1 1 3 0 1SIG2 0 2 0 0

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

91

Minhash - AlgoritmoD1' = [1, 0] D3' = [0, 0]

D2' = [3, 2] D4' = [1, 0]

SIG1 1 3 0 1SIG2 0 2 0 0

Linha Token D1 D2 D3 D4 h1 h20 A 1 0 0 1 1 11 B 0 0 1 0 2 42 C 0 1 0 1 3 23 D 1 0 1 1 4 04 E 0 0 1 0 0 3

92

Minhash - AlgoritmoD1' = [1, 0] D3' = [0, 0]

D2' = [3, 2] D4' = [1, 0]

J D2 D3 D4D1 0 0,25 0,67D2 0 0,33D3 0,2

J D2' D3' D4'D1' 0 0,5 1D2' 0 0D3' 0,5

93

CódigoP = 109297

nhashes = 3

alphas = np.random.randint(1, P-1, (nhashes,))

betas = np.random.randint(1, P-1, (nhashes,))

94

CódigoH = [list(map(hash,t.split())) for t in tweets.text[:100]]

MH = np.zeros((100,nhashes))

for i, h in enumerate(H):

for j in range(nhashes):

MH[i,j] = np.min(np.remainder(alphas[j]*h + betas[j], P))

95