Relatório do Trabalho Final de MAE 228

Introdução

Este trabalho é uma realização conjunta de Adriano Mitre, Helton Kishi e Igor Rascovsky.

O objetivo do trabalho foi implementar um sistema de compressão, descompressão e simulação de textos baseado em cadeias de Markov.

Decisões de Projeto

Conjunto de Dados

O conjunto de dados escolhido pelo grupo foi o linguístico.

O arquivo Candido1_bin.txt do pacote pode ser lido em sua forma original pelo sistema R, mas teve de ser pré-processado a fim de adequar-se ao formato de entrada esperado pelo sistema desenvolvido pelo grupo. A versão pré-processada faz parte do pacote entregue (maiores detalhes em leia-me.txt).

Tecnologias Utilizadas

O grupo optou por codificar o sistema na linguagem JavaScript. A decisão levou em conta diversos fatores, tais como facilidade e agilidade no desenvolvimento do sistema e de sua interface, a qual decidimos que deveria ser gráfica e amigável. Além disso, a portabilidade do sistema e legibilidade de seu código foram fatores decisivos na escolha da linguagem.

Adicionalmente, foi utilizada a linguagem Java na codificação do parser da árvore probabilística produzida pelo sistema R.

Recursos Extras

Compressão/Descompressão Adaptativa

O enunciado do trabalho não esclarece se a estimação das probabilidades de transição deve ser estática (offline) ou adaptativa (online). Alguns dias após a publicação do enunciado o professor afirmou tratar-se do primeiro caso, por conta da maior dificuldade da estimação adaptativa.

Na ocasião do esclarecimento, contudo, o grupo já havia implementado grande parte do sistema utilizado estimação adaptativa. Assim, os integrantes optaram por concluir a implementação da versão adaptativa e posteriormente incluir, para efeitos de comparação, a versão estática.

Em aula, o Prof. Antonio Galves fez entender que os grupos implementassem a versão adaptativa teriam pontos de bonificação na nota. Não ficou acertado, no entanto, quantos pontos seriam.

Modelo Independente e de Ordem Superior a Dois

Apesar de o enunciado exigir apenas cadeias de Markov de ordem 1 e 2, o sistema implementado suporta cadeias de qualquer ordem maior ou igual zero, lembrando que k = 0 é o modelo independente (ao longo do texto, k denotará o alcance).

A fim de não sobrecarregar a interface com o usuário o grupo optou por limitar a ordem do modelo em 10, que é o alcance da VLMC ótima para o texto escolhido (detalhes na seção Árvore Probabilística).

Convém notar que o grupo também realizou experimentos com cadeias de ordem diferente de 1 e 2. Além disso, para cada k foram realizados o dobro de testes: um para o compressor estático, outro para o dinâmico.

Algoritmos

Estimação

A estimação das probabilidades de transição é feita como descrito nas notas de aula sobre compressão com cadeias de Markov de alcance fixo, redigidas por Adriano Mitre.

Para a estimação estática, utilizou-se o estimador de máxima verossimilhança da Equação 6. A função que trata disso é estProbMLE() do arquivo core.js.

Já para a estimação adaptativa, utilizou-se o estimador de máxima verossimilhança com a Regra da Sucessão (Equação 7). A função que trata disso é estProb() do arquivo core.js.

Compressão

O algoritmo de compressão utiliza as probabilidades de transição estimadas para produzir códigos que seguem a prescrição de Shannon. O arquivo compression.js contém as principais funções relacionadas à compressão.

Convém destacar que a determinação dos códigos é feita sem a construção explícita da árvore de códigos. As funções que tratam disso são getPrevSameLen() e getCode() do arquivo core.js. Cabe explicitar que foi adotada a ordem lexicográfica usual, tanto para o alfabeto de entrada quando para o código binário.

Descompressão

O algoritmo de descompressão implementado foi o adaptativo, que opera por meio da criação de um vetor associativo que possui como valores os caracteres do alfabeto e cujas chaves são os códigos das mesmas. O arquivo decompression.js contém as principais funções relacionadas à descompressão.

O código associado aos k+1 primeiros caracteres têm, por convenção, o mesmo comprimento, que é o teto do logaritmo na base dois do comprimento do alfabeto de entrada. Portanto, o dicionário é inicializado desta maneira. Conforme o texto vai sendo descomprimido, o dicionário é atualizado.

Simulação

Um modelo de ordem k só determina como se faz para simular caracteres a partir da (k+1)-ésimo. Em face disso, o grupo adotou a seguinte convenção: os k primeiros símbolos da simulação serão os k primeiros símbolos do texto de entrada.

As partições são geradas como uma matriz de transição cumulativa, que se baseia na matriz estimada ao final do processo de compressão. A função que trata disso é criaMatrizTransicaoCumulativa() do arquivo simulation.js.

Depois disso, há um laço principal. A cada iteração do laço simula-se um caractere. A função proximoPassoMarkov() determina a próximo caractere dada um prefixo e a matriz de transição.

Convém notar que, para o caso de passados que não ocorreram no texto de entrada, a simulação será realizada com distribuição de probabilidade uniforme. Por conta disso, pode haver no texto simulado caracteres que não ocorreram no texto de entrada.

O arquivo simulation.js contém as principais funções relacionadas à simulação.

Árvore Probabilística

A árvore probabilística correspondente ao modelo de memória variável foi produzida no sitema R pela seguinte seqüência de comandos:

library(VLMC)
texto <- scan(file="Candido1_bin.txt", sep=" ", what="")
arvore <- vlmc(texto)
arvore
draw(arvore)

O comando arvore acima produz a seguinte saída:

`vlmc', a Variable Length Markov Chain;
alphabet '01234', |alphabet| = 5, n = 13615.
Call: vlmc(dts = txt); default cutoff = 4.744
-> extensions (= $size ) :
ord.MC context nr.leaves total
10 38 17 392

O comando draw(arvore), por sua vez, produz a saída abaixo:

[x]-( 6560 2955 2955 962 182| 13614)-F
+--[0]-( 1794 1880 2125 627 134| 6560) <603.23>
| +--[0]-( 559 987 186 48 14| 1794) <430.17>
| | +--[0]-( 163 396 0 0 0| 559) <89.29>
| | | `--[0]-( 28 135 0 0 0| 163) <6.28>-T
| | +--[1]-( 0 0 151 38 9| 198) <392.05>-T
| | +--[2]-( 396 591 0 0 0| 987) <150.15>-T
| | `--[3]-( 0 0 35 10 5| 50) <99.71>-T
| +--[1]-( 198 0 1481 393 106| 2178) <1221.90>
| | +--[0]-( 75 0 955 236 72| 1338) <11.89>
| | | +--[0]-( 19 0 472 115 41| 647) <5.62>
| | | | `--[0]-( 6 0 178 39 17| 240) <0.37>
| | | | `--[0]-( 2 0 46 10 10| 68) <2.40>
| | | | `--[2]-( 2 0 37 8 8| 55) <0.04>
| | | | `--[0]-( 2 0 22 6 5| 35) <0.31>
| | | | `--[0]-( 1 0 0 0 2| 3) <4.84>-T
| | | `--[2]-( 56 0 483 121 31| 691) <4.03>
| | | +--[0]-( 44 0 344 81 22| 491) <0.36>
| | | | `--[1]-( 36 0 239 61 16| 352) <0.49>
| | | | `--[0]-( 23 0 151 43 10| 227) <0.20>
| | | | `--[2]-( 14 0 79 14 7| 114) <2.18>
| | | | `--[0]-( 10 0 55 12 5| 82) <0.20>
| | | | `--[3]-( 5 0 10 0 0| 15) <4.97>-T
| | | `--[3]-( 0 0 26 12 1| 39) <4.97>-T
| | `--[2]-( 123 0 526 157 34| 840) <14.54>
| | +--[0]-( 86 0 372 105 24| 587) <0.13>
| | | `--[3]-( 7 0 100 23 6| 136) <6.71>-T
| | `--[4]-( 5 0 21 15 0| 41) <4.94>-T
| +--[2]-( 987 893 0 0 0| 1880) <1094.92>-T
| `--[3]-( 50 0 458 186 14| 708) <436.69>-T
+--[1]-( 2178 0 570 160 47| 2955) <823.98>
| +--[0]-( 1338 0 402 106 34| 1880) <3.25>
| | +--[0]-( 647 0 259 60 21| 987) <7.75>-T
| | `--[2]-( 691 0 143 46 13| 893) <9.47>-T
| `--[2]-( 840 0 168 54 13| 1075) <6.09>-T
+--[2]-( 1880 1075 0 0 0| 2955) <1077.57>
| `--[4]-( 45 49 0 0 0| 94) <4.83>-T
+--[3]-( 708 0 166 88 1| 963) <280.94>-T
`--[4]-( 0 0 94 87 0| 181) <248.81>-T

A árvore probabilística produzida corresponde à uma VLMC que atende ao critério de otimalidade apresentado no artigo A Universal Data Compression System de Jorma Rissanen.

No caso do texto utilizado (Candido1_bin.txt), a árvore probabilística tem altura 10, indicando que a fonte que produziu o texto pode ser modelada de maneira ótima por uma cadeia de Markov de alcance 10.

Parser

A representação da árvore produzida pelo pacote VLMC do R é visualmente interessante (considerados os limites de uma representação ASCII), porém inadequada à leitura do programa desenvolvido pelo grupo.

Assim, os integrantes optaram por transformar a representação pelo R em outra mais conveniente. A representação transformada consiste de seqüências como abc,d=1/2, onde 1/2 é a probabilidade do caractere d dado o contexto abc. Exemplo de representação transformada:

0,0=1/3
0,1=2/3
01,0=1/4
01,1=3/4
11,0=1/2
11,1=1/2

A fim de automatizar esta transformação, foi desenvolvido um programa em Java, que recebe um arquivo com a representação textual da árvore tal como produzida pelo R e gera as seqüências no formato acima descrito com suas respectivas probabilidades.

Infelizmente, o aplicativo desenvolvido acabou não sendo utilizado, porque, às vésperas da entrega deste trabalho, divulgou-se que o arquivo que deveria ter sido utilizado era o Candido1_bin.txt, e não Candido1_p.txt, como supusemos inicialmente.

Resultados

O alfabeto de entrada utilizado foi A={0,1,2,3,4}, apelidado de numérico restrito no sistema desenvolvido pelo grupo.

Texto Original

Apresentamos a seguir as taxas de compressão do texto original, em bits por símbolo (média).

tipo/alcance 0 1 2 3 4 5 6 7 8 9 10
Adaptativo 2,645 1,899 1,632 1,590 1,628 1,690 1,782 1,911 2,054 2,218 2,384
Estático 2,642 1,891 1,613 1,536 1,544 1,527 1,491 1,459 1,413 1,347 1,270

Texto Simulado

Em acordo com o enunciado do trabalho, o texto simulado foi produzido por uma cadeia de ordem igual à utilizada na compressão.

Apresentamos a seguir as taxas de compressão do texto simulado, em bits por símbolo (média).

tipo/alcance 1 2
Adaptativo 1,893 1,614
Estático 1,883 1,602

Conclusões

Adaptativo vs. Estático

Naturalmente, as taxas de compressão foram menores para o modelo adaptativo do que para o modelo estático, afinal o último utiliza o próprio estimador de máxima verossimilhança e não um outro que converge para este.

Isso, no entanto, não implica que a compressão efetiva seja sempre maior no caso estático, afinal neste último é necessário incluir no arquivo comprimido um preâmbulo com as probabilidades de transição.

Compressão vs. Alcance

Espera-se que um modelo markoviano de alcance maior permita comprimir tanto ou mais que um modelo de alcance menor, afinal uma cadeia de Markov de ordem k também é uma cadeia de Markov de ordem n > k.

É exatamente isso que acontece no caso estático. No caso adaptativo, entretanto, a situação é diferente. A compressão aumenta com o alcance até k = 3, mas quando k > 3 a compressão se torna inversamente proporcional ao alcance.

Esta situação aparentemente contraditória se deve ao fato de o modelo adaptativo utilizar uma distribuição a priori independente e uniforme (constantes somadas ao numerador e denominador na Equação 7 das notas de aula). Por conta disso, há uma "demora" na convergência para o estimador de máxima verossimilhança, a qual é proporcional ao alcance do modelo. Como se trata de um texto com comprimento finito, o efeito dessa demora é apreciável.

Original vs. Simulado

Em todos os casos, a compressão foi maior com os textos simulados. A razão para isto é o nível de ajuste do modelo à fonte.

O texto original não foi produzido por uma fonte markoviana de ordem k. Conseqüentemente, toda cadeia de Markov de ordem k será apenas uma aproximação da fonte.

No caso do texto simulado, por outro lado, a fonte é uma cadeia de Markov. Assim, há um perfeito ajuste entre modelo e fonte, resultando numa maior taxa de compressão.