• Nenhum resultado encontrado

Equação 10: Honore

1. INTRODUÇÃO

3.3. ABORDAGEM PPM

3.3.3. Implementação

A implementação do sistema para treinamento dos autores e de classificação de autoria foi feita por meio da linguagem Java, versão 1.6. Um problema encontrado foi com relação à manutenção dos contextos PPM em memória, principalmente quando estes ultrapassavam o tamanho cinco e podiam exceder o uso de memória. Outro problema foi o da eficiência computacional, pois encontrar contextos e símbolos nas tabelas de probabilidade condicionais pode ser custoso, especialmente para contextos grandes.

Com isso, o início do desenvolvimento da ferramenta se deu com a criação do algoritmo PPM, para realizar compressão de dados e para verificar a viabilidade das diversas estruturas possíveis para implementação.

Para este trabalho foram implementadas duas estruturas de dados. A primeira utilizou a estrutura de tabelas hash [CLRS01]. A segunda implementação utilizou um de tipo árvore especial, conhecida como árvore trie [Sal04].

Na implementação por meio de tabela hash, foi criada uma tabela para cada contexto e a chave da tabela era o contexto k, para k de i = 0 ... N, onde N é número máximo de contextos, enquanto o valor era uma lista de símbolos que já tinha aparecido no contexto. O objeto símbolo foi composto pelo caractere associado e por um contador da presença do símbolo em um dado contexto. Quando o contexto é k = 0, o contador do símbolo é igual a quantidade da ocorrência do caractere no texto completo. Para o contexto k = -1, que ocorre no momento em que o símbolo a ser codificado ainda não apareceu no texto, foi utilizada a estrutura de dados Set, cujo conceito é o de um conjunto, em que elementos não se repetem.

No entanto, esta implementação, embora fácil de codificar e de se aproximar mais da lógica do modelo PPM, é ineficiente, e para um contexto pequeno ela rapidamente excede o uso máximo de memória, causando erro. Em testes realizados para compressão de um documento com 16 MBytes, utilizando o contexto de tamanho seis, houve estouro de memória, o que torna inviável sua utilização, pois testes com um contexto maior devem ser realizados em busca de uma compressão eficiente. Além disso, o custo computacional com esse método é muito alto, demorando cerca 180 segundos para obter uma taxa de classificação quando o contexto cinco é utilizado. Para contextos menores, até três, no entanto, o custo computacional é menor, ficando em torno de 30 segundos a classificação dos textos.

Devido a esse problema na estrutura de dados utilizada, outra solução foi buscada para tentar tanto melhorar a eficiência computacional da ferramenta como também aumentar o contexto sem haver o consumo excessivo de memória. A segunda implementação, e a que é utilizada no sistema desenvolvido neste trabalho, é baseada na árvore trie. A árvore trie é proposta em [Sal04] como uma estrutura de dados adequada para implementação do PPM, pois evita um consumo excessivo de memória, além de melhorar a eficiência computacional na busca e inserção dos nós.

A árvore trie tem essa denominação devido a palavra em inglês que representa busca, reTRIEval, também conhecida como árvore de prefixo, e é um tipo de árvore em que todos os descendentes de um nível do nó podem ser

determinados apenas pelo prefixo que levou até aquele nível da árvore. Algumas características que favorecem o uso deste tipo de árvore é a busca rápida de um símbolo e a utilização de menos espaço para armazenamento das cadeias de símbolos.

A profundidade de uma árvore trie para um contexto máximo N é N + 1, sem considerar o nó raiz. Portanto, para um contexto N = 2 tem-se uma profundidade 3, independentemente do tamanho do documento a ser comprimido. O nível 0 é a raiz da árvore, que não contém nenhum símbolo. O nível 1 da árvore, abaixo da raiz, contém a ocorrência não condicional dos símbolos no texto. Do nível 2 em diante da árvore, tem-se o número de ocorrências nos contextos. Um exemplo de criação de uma árvore trie pode ser visto na Figura 6 para a mensagem “zxzyzxxyzx”.

Figura 6: Processo de criação de uma árvore trie (Fonte: [Sal04])

Um nó presente na árvore trie é composto pelos seguintes atributos: • O símbolo, neste trabalho o caractere em ASCII;

• O contador de ocorrência do símbolo em um dado contexto; • Um ponteiro inferior, que referencia o filho mais a esquerda do nó; • Um ponteiro à direita, que referencia o vizinho à direita do nó

corrente;

Um vine pointer, que referencia o contexto imediatamente anterior ao atual. Por exemplo, um símbolo “f” codificado no contexto “abcd”, deverá atualizar também o contexto “bcd” adicionando 1 ao contador deste símbolo no contexto. Para isso, o vine pointer do símbolo “d” deverá ser recuperado, pois ele está referenciando o símbolo “d” do contexto anterior ao que se está, que é o “bcd”. No momento da construção da árvore, há ainda o ponteiro base, ou base

algoritmo para adição ou atualização de um nó dado um contexto, pode ser visto a seguir:

1. Recuperar o ponteiro base. Se o contexto máximo já foi atingido, recuperar o vine pointer do nó que o ponteiro base referencia, inserir o novo nó e ajustar o ponteiro base para apontar para este novo nó. Caso o símbolo deste novo nó já esteja presente como filho do vine pointer, atualizar o contador do símbolo, e fazer com que o ponteiro base referencie esse nó. Há ainda o caso em que o buffer do contexto máximo não foi totalmente preenchido, e para isso, não é necessário recuperar o

vine pointer do nó corrente, sendo necessário apenas adicionar o nó no

contexto em que se está, e fazer o ponteiro base apontar para ele.

2. Repetir o procedimento da etapa 1, mas sem atualizar o ponteiro base. Seguir o vine pointer, e adicionar um novo nó que representa o novo símbolo nos contextos inferiores, ou atualizar o contador caso ele já exista nesses outros contextos. Caso o vine pointer ainda não esteja ajustado, é nesse momento que se deve criar a referência e fazer a referência correta.

3. Repetir este processo da etapa 2 até que se encontre o nó raiz.

O vine pointer e todo este processo podem ser vistos na Figura 10, para a seqüência de caracteres “assanissimassa”. A complexidade computacional deste algoritmo tanto para inclusão como atualização de um símbolo em um contexto é de O(N), para um contexto com no máximo N caracteres.

Durante este processo, o codificador aritmético reúne os contadores necessários para codificação do símbolo. Caso um símbolo não seja encontrado no momento em que o contexto máximo foi atingido, deve haver uma nova inserção. No entanto, deverá haver o chaveamento para um contexto inferior, na tentativa de encontrar este símbolo neste novo contexto. Nesse momento, deverá ser codificado o escape, ou seja, a não ocorrência de um símbolo até aquele momento, no contexto em que se encontra.

Para acoplamento do codificador aritmético com o algoritmo PPM implementado, foi utilizado a Application Program Interface (API) Colloquial Arith Code [Ari07]. Uma API é uma biblioteca de serviços e componentes, onde diversas funcionalidades já criadas podem ser reusadas por outros programas de computador.

Após a criação do compressor PPM-C, iniciaram-se os ajustes necessários nos módulos de treinamento e de classificação. O módulo de construção inicial foi o de treinamento, seguido pelo de teste.

Para o ajuste do compressor PPM no módulo de treinamento, uma modificação realizada foi com relação a não necessidade de comprimir o arquivo utilizado para definição de cada escritor, visto que o importante era apenas obter o modelo de cada autor. Com isso, a codificação aritmética foi excluída.

No momento em que o arquivo de treinamento era finalizado para construção do modelo, este era salvo no disco rígido e nenhuma alteração era realizada. Cada nó é preservado, bem como as suas referências, para que possa ser utilizado na fase de classificação sem a necessidade alteração das referências dos ponteiros.

As adequações ocorridas no compressor PPM para a etapa de classificação foi recuperar o modelo estático referente a cada escritor, criado na fase de treinamento, e utilizá-lo apenas para codificação. O codificador utilizado foi o aritmético. Nesta fase, o texto para teste será comprimido por todos os

modelos, e para cada compressão é armazenado a RC referente a cada autor. A razão de compressão é obtida dividindo-se o tamanho do arquivo em sua forma original pelo tamanho do arquivo comprimido. O autor que apresentar a maior RC é selecionado como o escritor do texto.

Documentos relacionados