textos extraindo atributos de -...
TRANSCRIPT
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
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 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
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
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
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
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
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
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.Objetivo: encontrar uma representação vetorial de cada palavra tal que:
56
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
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
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
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
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
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