preview
Rede neural na prática: Lendo PNG do MNIST

Rede neural na prática: Lendo PNG do MNIST

MetaTrader 5Aprendizado de máquina |
20 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Rede neural na prática: Retro propagação manual, foi explicado um pouco como poderia ser feita a retro propagação dentro daquilo que seria um conjunto de perceptrons ligados em sequência. Apesar de não ser a melhor maneira de explicar aquele tópico, como será visto futuramente. O que foi visto ali, nos permite entrar em outra questão de maneira mais imediata.

Muito bem, então como não temos certas coisas disponíveis para uso imediato, precisaremos implementar as mesmas para podermos dar alguns passos novos naquilo que pretendemos explicar sobre redes perceptron.

Nosso atual problema é carregar imagens de um banco de dados público. No caso o banco MNIST. Normalmente as imagens neste banco são do tipo específico, mas em outros casos podemos precisar lidar com tipos diferentes, como no caso o PNG. E este será o objetivo deste artigo, criar um mecanismo o mais básico possível para carregar este tipo de padrão de imagem.


De volta a prancheta

Neste momento nos encontramos em uma encruzilhada. Isto por que dependendo do tipo de decisão que tomemos, obteremos um tipo ou outro de sistema perceptron sendo de fato implementado.

Sabemos que pode parecer estranho dizer isto, já que muitos acham que redes perceptron, tem um proposito geral. Contudo, isto não é a realidade da coisa. Muitas das vezes, quando vemos algum tipo de sistema perceptron sendo criado, e usando mecanismos genéricos, estamos na realidade usando algum tipo de framework, voltado para implementar tais sistemas.

Porém, este tipo de objetivo não é o que pretendemos alcançar com estes artigos. A meta é, explicar como tais sistemas podem ser confeccionados em uma linguagem como MQL5, sem fazer uso de nenhum tipo de ferramenta externa. Com isto, apesar da rede que implementaremos ser bastante limitada. Ela irá mostrar a você como a coisa toda de fato funciona por debaixo dos panos. Sendo apenas uma questão de escala. Se a escala for aumentada e tivermos dados suficientes para treinar algum modelo, podemos quem sabe, criar um mini GPT, ou algo do tipo. Mas estes não é nosso proposito.

Então qual o nosso atual problema neste momento? Bem, meu amigo, o problema são dados. Não importa o tipo de rede perceptron que você queira implementar, o problema sempre será os dados. Dados são a alma de qualquer tipo de sistema perceptron. Sem eles tais sistemas não podem ser construídos de maneira alguma. Se bem, que existe uma forma de fazer o sistema perceptron gerar seus próprios dados. Mas não abordaremos este tipo de coisa nesta sequência. Isto para não gerar confusão em suas pobres cabecinhas.

Sabendo que o problema são dados, dados estes que serão utilizados durante o treinamento e ajuste da própria rede perceptron. Precisamos, assim como no passado pesquisadores precisaram, de algum tipo de banco de dados. No caso, houve um esforço muito grande para que a coleta e classificação manual, sim foi tudo feito por humanos, de tais dados fossem feitas com todo o cuidado e zelo. Isto por que, tais informações são de fato algo bastante precioso.

Existem diversos bancos de dados públicos disponíveis para serem utilizados. Contudo, acreditamos que o mais nostálgico e interessante para muita gente é o de reconhecimento de caracteres. Isto por que, é realmente algo que podemos interagir logo de imediato, tendo um conteúdo que de certa forma nos é bastante familiar. O banco de dados ao qual estamos nos referindo é o MNIST baseado em caracteres numéricos hindu-arábicos.

Ok, tendo decidido que faremos uso de um dos bancos de dados MNIST, vamos entender o motivo desta decisão. Existe toda uma história por trás deste banco de dados. Mas isto, fica a cargo de cada um pesquisar para compreender melhor, porque ele existe, e como tudo começou. Se você pesquisar na WEB, encontrará muitas referências a respeito dele. Mas principalmente encontrará muitos, explicando como desenvolver uma rede perceptrons simples, que fazem uso do Python, para conseguir de alguma forma replicar os resultados demonstrados quando usamos frameworks.

Basicamente, muitos entusiastas, começam a estudar os tais frameworks, devido justamente a este banco de dados MNIST e dos modelos de código em Python divulgados. Já que é ali é aonde grande parte dos recursos são explorados.

Mas não somos interessados em Python, e não me julguem mal por tais palavras, mas nosso objetivo é o MQL5 e entender como as coisas funcionam de fato. Assim, vamos abandonar a possibilidade de fazer as coisas em Python e focar no MQL5. Contudo, quando tudo estiver devidamente implementado, você conseguirá entender como de fato a rede perceptron consegue fazer o que faz. Não mascarando nenhum tipo de conhecimento, com coisas como frameworks já prontos e cheios de detalhes obscuros, por assim dizer.

Muito bem, a caminhada será longa e morosa. Mas o primeiro passo precisa ser dado. E basicamente, oque precisaremos fazer, foge um pouco daquilo que seria o proposito principal do MetaTrader 5. A plataforma foi pensada para ser usada como sistema de negociação. Contudo, aqui precisaremos extrapolar e levar a plataforma para um nível de utilização totalmente diferente. E muitas das coisas que precisamos, não estão presentes na biblioteca do MQL5. Entre elas, a leitura de imagens no formato PNG.

Em essência, boa parte do MNIST, se encontra neste formato. Podendo em alguns casos estar no formato binário. Porém, vamos considerar que usemos dados que estejam em PNG, onde temos uma enorme quantidade de imagens de 28 por 28 pixel, disponíveis para serem utilizadas. Normalmente as mesmas estarão em preto e branco, com tons de cinza. Assim, se você baixar este banco de dados, poderá ver algo muito parecido com a imagem abaixo:

Imagem 01

O banco que utilizaremos, basicamente terá este tipo de informação. Cada uma das imagens, de fato é um arquivo diferente. Podendo assim ser utilizado para criar todo um treinamento, ou experimentar o uso de uma rede perceptron específica.

Se você procurar, com calma, encontrará muitos mencionando o uso do TensorFlow neste tipo de rede que será construída. Mas o TensorFlow, é apenas um, entre tantos métodos de implementar o sistema de aprendizagem. É claro que fazendo uso de tal coisa, teríamos logo de início, todas as coisas que precisaríamos em mãos. Não precisando de forma alguma estudar uma maneira de implementar toda a matemática envolvida. Mas apesar de isto, ser de fato uma mão na roda. Em alguns casos fazer uso de tal solução, não é algo desejável. Isto por que, podemos acabar necessitando de muito mais poder computacional, do que realmente seria necessário, se implementássemos uma solução específica para um dado problema. Perceba o seguinte, não estou falando que este ou aquele framework é bom, ou ruim. Ou que devemos abandonar todos e começar de novo. Não é isto. O que quero é mostrar como tais coisas funcionam internamente.

Porém, para se fazer isto, é preciso, mergulhar em um mundo, do qual muitos de vocês, se quer tem noção de como realmente funciona. Por isto que sistemas perceptron é um assunto tão complexo e, ao mesmo tempo, muito interessante. Pois sempre que imaginamos já saber algo, surge algo novo para aprendermos.

Ok, mas vamos voltar o nosso problema original. No atual momento, a classe C_Neuron, ainda não está no ponto ideal para ser usada em uma implementação, como a que precisamos de fato criar. Ainda não temos meios de configurar uma topologia de rede neural, que nos ajudará bastante na confecção do que será implementado. Não temos meios de ler arquivos PNG, isto por que a biblioteca do MQL5, não nos fornece tais meios por padrão. E além de tudo, ainda precisamos entender outras coisas, das quais ainda não foram explicadas. Isto devido ao fato, de não ter tido de fato, necessidade de entrar em tais pontos. Enfim, parece que teremos uma longa jornada pela frente. Mas como diz o ditado. Tudo se inicia, ao se dar o primeiro passo. E este acabamos de dar, ao decidirmos seguir na direção do sistema de reconhecimento de dígitos manuscritos. Que fazem parte do banco de dados MNIST.

Bem, a primeira coisa a se fazer, será conseguir de alguma forma ler a imagem em PNG, usando para isto o MQL5. Então para separar devidamente os assuntos, vamos abrir um novo tópico. Já que é algo que precisa ser bem compreendido como se faz.


Entendendo a imagem PNG

Muitos de vocês, deve achar que as coisas em computação, são algum tipo de mágica. Ou no mínimo, as pessoas que criam as coisas, são algum tipo de entidade divina ou algum tipo de semi deus. Já que tudo parece não fazer muito sentido, quanto se está começando. Mas essencialmente, em computação, tudo e absolutamente tudo, se resume a uma única coisa: BITS. Tenha sempre isto em mente, e você conseguirá fazer qualquer coisa em computação. Como os bits, são organizados é o segredo e nos diz o que eles de fato fazem. Mas não apenas isto, cada modelo de organização, por si só não diz nada a respeito do que está presente em um banco de bits. Tal banco é chamado de arquivo.

Para que este banco de bits, ou arquivo, faça algum sentido, precisamos de que eles estejam organizados de uma forma muito específica. Não existe, de forma alguma um arquivo totalmente aleatório. Pois se tal arquivo fosse construído, ele teria como conteúdo apenas lixo. Mas mesmo um arquivo que parece ser lixo, na verdade, pode conter algum tipo de informação de grande valor. Tudo é uma questão de saber como o arquivo foi de fato organizado, para ser finalmente construído. Sabendo disto, você pode fazer qualquer coisa.

Então ao olharmos um arquivo de imagem. No caso específico de um arquivo PNG. Você poderá ver algo parecido com a imagem abaixo.

Imagem 02

Agora lhe pergunto: Qual o sentido que estas informações, vistas na imagem acima, tem para você? Possivelmente não faz nenhum sentido. Mas se você usar um programa, ou aplicação que consiga entender estes dados. Você poderá ser apresentado a algo como mostrado na imagem logo a seguir.

Imagem 03

Bem, agora aquelas mesmas informações passam a fazer algum sentido, não é verdade? Pode até não ser a informação real. Já que existem modelos de criptografia e compactação, que tem justamente este objetivo. Criar algo que faça sentido para uns. Mas que, na verdade, tem como intuito, transmitir alguma outra mensagem, que estará oculta. Exceto se você use a aplicação correta para descobrir a mensagem correta. Enfim, isto é algo para quem sabe no futuro, possamos falar em mais detalhes, explicando melhor como este tipo de coisa funciona e pode ser implementada. Já que também é algo muito interessante, e torna este trabalho em redes perceptron, brincadeira de criança, por assim dizer.

Mas vamos deixar isto por hora e voltar ao nosso tema principal. Se você observar e estudar arquivos binários. Irá mais hora ou menos hora, notar algo que sempre se repete, dependendo do conteúdo do arquivo. Normalmente isto se encontra bem no início do arquivo, e é conhecido como FILE SIGNATURE, ou literalmente, assinatura do arquivo. Na imagem bem binário, podemos ver isto sendo feito. Observe esta região demarcada vista abaixo.

Imagem 04

Esta parte, é a tal FILE SIGNATURE, que diz o tipo de estrutura na qual o arquivo foi criado. Agora preste atenção ao seguinte: Aqui não temos necessidade de implementar todos os tipos possíveis de construção de imagens PNG, pelo menos não pretendo fazer isto neste momento. Já que precisamos apenas que o código leia, o tipo de imagem. E estas informações vistas nestas imagens acima, representam exatamente o tipo de coisa na qual precisaremos ler do arquivo PNG. Então não tente usar o código, como se ele pudesse ler qualquer imagem PNG, pois isto não ocorrerá na prática. Dito isto podemos continuar.

A primeira coisa de fato que faremos, será criar um código base. Este pretende testar se estamos ou não inserindo, ou tentando ler uma imagem PNG. Este tipo de coisa é bastante simples de ser criada. Mas já vamos fazer as coisas estruturando o código. De modo a não precisarmos refazer o código depois. Com isto, a primeira parte do código é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. class C_PNG
05.   {
06. private:
07. 
08. public:
09.                      C_PNG(const string szFileName)
10.      {
11. #define macroERROR(msg) { Print(msg); FileClose(f_handle); return; }
12. 
13.       const uchar    File_Signature[] =
14.         {
15.          0x89, 0x50, 0x4E, 0x47,
16.          0x0D, 0x0A, 0x1A, 0x0A
17.         };
18. 
19.       int            f_handle;
20.       uint           size;
21.       uchar          buff[];
22. 
23.       if((f_handle = FileOpen(szFileName, FILE_BIN)) < 0)
24.          macroERROR(StringFormat("Error accessing file: %s", szFileName));
25. 
26.       size = FileReadArray(f_handle, buff, 0, File_Signature.Size());
27.       if(size != File_Signature.Size())
28.          macroERROR(StringFormat("Error reading the file: %s", szFileName));
29. 
30.       if(ArrayCompare(buff, File_Signature, 0, 0, size) != 0)
31.          macroERROR("File is not a PNG image.");
32. 
33.       Print("ok..");
34. 
35.       FileClose(f_handle);
36. 
37. #undef macroERROR
38.      }
39.   };
40. //+------------------------------------------------------------------

Código 01

Este código é a nossa classe inicial, e para experimentar ela, precisaremos de outro código. Como a coisa está apenas começando, podemos usar um script, por ser um código mais limpo inicialmente. Este pode ser visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
04. #include <Neural Network\C_PNG.mqh>
05. //+------------------------------------------------------------------+
06. //| Script program start function                                    |
07. //+------------------------------------------------------------------+
08. void OnStart(void)
09.   {
10.    C_PNG            png("mnist/10032.png");
11.   }
12. //+------------------------------------------------------------------

Código 02

Como estes dois códigos são bastante simples de entender, não entraremos em detalhes sobre como eles funcionam. Contudo, ao executar este código 02, e contando que o arquivo indicado na linha oito de fato exista, você poderá ver o seguinte resultado no terminal do MetaTrader 5.

Imagem 05

Este é a mesma informação que está sendo informada graças a linha trinta da classe C_PNG. Se ela apareceu, significa que está tudo ok. E que podemos prosseguir, na parte onde começaremos a leitura da imagem em si. Mas para tornar a coisa mais didática, criaremos um novo tópico. Assim ficará mais fácil entender o que será feito a seguir.


Blocos de BYTE

Uma vez cumprido a etapa anterior. Já temos como saber se estamos lidando com uma imagem, ou com outro tipo de coisa. Caso estejamos lidando com uma imagem, podemos passar para a parte onde capturaremos a estrutura da mesma. Normalmente, este tipo de coisa vária de arquivo para arquivo. Mas de uma forma geral, todos têm algum tipo de estrutura, onde as coisas serão organizadas de uma determinada maneira. Olhando o arquivo em si, pode tudo parecer uma grande bagunça. Mas acreditem, existe de fato uma organização interna sendo feita. Caso contrário, qualquer informação ali, seria perdida para sempre.

No nosso caso específico, estaremos lidando com uma imagem PNG. Ela é organizada em blocos de dados, muito bem definidos. Se você olhar novamente a imagem do conteúdo interno, verá uma informação aparecendo, em uma posição específica do arquivo. Esta pode ser vista na imagem abaixo.

Imagem 06

Este é um ponto que nos interessa. Já que ele contém metadados, que nos diz diversas coisas sobre a imagem que se encontra no arquivo. Basicamente, entender o que, e como estes valores estão organizados, nos ajudará a reconstruir a imagem em si. Mas aqui é onde, entender a linguagem que estamos usando, nos permitirá acessar as coisas com mais precisão e facilidade. Existe um problema é que acomete todo software, sendo este, a largura de bits.

Arquivos são organizados em bits, apesar de muitas das vezes, os lemos como sendo palavras, números ou qualquer outra coisa. Eles bem lá no fundo, são apenas bits. Mas estes bits podem ser organizados de forma diferente. Isto olhando as coisas em termos de largura de bits. E dependendo da linguagem usada, precisamos adaptar o código, a fim de ter a largura correta. Caso contrário, todo o conteúdo lido será inútil, pois ele estará comprometido por certos bits estarem invadindo área, que não lhes pertence. Mas felizmente o MQL5, tem as larguras corretas. Apesar de não ter a mesma flexibilidade que o C / C++, aquilo que o MQL5, nos fornece de estrutura, já será o suficiente. Então precisaremos criar uma estrutura interna, a fim de ler o bloco que nos informa algumas coisas sobre a imagem. Isto é bem simples de ser feito, tanto a criação, quanto a leitura, como você pode ver no código abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. class C_PNG
05.   {
06. private:
07.    struct st_IHDR
08.      {
09.       uint           lenght;
10.       uchar          type[4];
11.       uint           width;
12.       uint           height;
13.       uchar          depth;
14.       uchar          colour;
15.       uchar          compression;
16.       uchar          filter;
17.       uchar          intelace;
18.      };
19. 
20. public:
21.                      C_PNG(const string szFileName)
22.      {
23. #define macroERROR(msg) { Print(msg); FileClose(f_handle); return; }
24. #define PrintX(a)       PrintFormat("%s: %d => 0x%x", #a, a, a);
25. 
26.       const uchar    File_Signature[] =
27.         {
28.          0x89, 0x50, 0x4E, 0x47,
29.          0x0D, 0x0A, 0x1A, 0x0A
30.         };
31. 
32.       int            f_handle;
33.       uint           size;
34.       uchar          buff[];
35.       st_IHDR        ihdr;
36. 
37.       if((f_handle = FileOpen(szFileName, FILE_BIN)) < 0)
38.          macroERROR(StringFormat("Error accessing file: %s", szFileName));
39. 
40.       size = FileReadArray(f_handle, buff, 0, File_Signature.Size());
41.       if(size != File_Signature.Size())
42.          macroERROR(StringFormat("Error reading the file: %s", szFileName));
43. 
44.       if(ArrayCompare(buff, File_Signature, 0, 0, size) != 0)
45.          macroERROR("File is not a PNG image.");
46. 
47.       FileReadStruct(f_handle, ihdr);
48. 
49.       PrintX(ihdr.lenght);
50.       PrintX(ihdr.width);
51.       PrintX(ihdr.height);
52. 
53.       FileClose(f_handle);
54. 
55. #undef PrintX
56. #undef macroERROR
57.      }
58.   };
59. //+------------------------------------------------------------------

Código 03

A estrutura em si, está sendo criada na linha oito, com isto, ao executarmos novamente o código, poderemos ver as seguintes informações.

Imagem 07

Mais que maravilha. Que espetáculo. Olha só que bela porcaria. Acabamos de perceber que existe um bug na linguagem de programação. Isto por que, as informações que deveriam vir do arquivo, são exatamente as que foram mostradas na imagem anterior. Ou seja, os valores estão investidos. Neste momento, muitos desistem e começam a xingar de DEUS ao DIABO, lançando todo tipo de ofensas a linguagem de programação usada. Já que quando ele usa outra, as coisas vêm corretas, como esperado. Mas antes que você, comece a pensar que de fato a linguagem tem um bug. Vamos entender, o que, na verdade, está acontecendo aqui.

O detalhe, é que você deve estar acostumado a ver valores inteiros do tipo LITTLE ENDIAN. Contudo, existe também o tipo BIG ENDIAN, onde os valores são armazenados de maneira um pouco diferente. Mas, por que disto? Bem, o motivo é a CPU. Este tipo de coisa, nada tem a ver com a linguagem de programação. O detalhe, é que algumas CPUs, para usam a notação LITTLE ENDIAN, enquanto outras a notação BIG ENDIAN. Sei que parece coisa de maluco. Durante muito tempo tive problemas em entender, este detalhe sobre os valores que a CPU de fato entende. Já que dependendo da implementação, como os valores são representados pode mudar radicalmente. Então o que você viu, não é um defeito na linguagem de programação. Mas sim uma questão da implementação da CPU.

Existem diversas formas de se resolver isto. Mas basicamente, o que precisamos fazer, é girar os bits para ter uma representação, que nos seja realmente compreensível. Já que os valores esperados eram de 28 de altura e 28 de largura. E não aqueles valores malucos. Mas como estou querendo incentivar ao máximo o uso do MQL5 puro. Vamos usar o que ele tem a nos oferecer. Pois, por melhor que programemos, não teremos acesso a CPU da mesma forma que os desenvolvedores da linguagem terão. Assim, é preferível usar funções da biblioteca padrão, envés de criar alguma função com o mesmo propósito. Corrigindo o código, passaremos a ter aquilo que é mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. class C_PNG
05.   {
06. private:
07.    union uint32
08.      {
09.       uint           value;
10.       uchar          c[sizeof(uint)];
11.      };
12. 
13.    struct st_IHDR
14.      {
15.       uint32         lenght;
16.       uchar          type[4];
17.       uint32         width;
18.       uint32         height;
19.       uchar          depth;
20.       uchar          colour;
21.       uchar          compression;
22.       uchar          filter;
23.       uchar          intelace;
24.      };
25. 
26. public:
27.                      C_PNG(const string szFileName)
28.      {
29. #define macroERROR(msg) { Print(msg); FileClose(f_handle); return; }
30. #define PrintX(a)       PrintFormat("%s: %d => 0x%X", #a, a, a);
31. 
32.       const uchar    File_Signature[] =
33.         {
34.          0x89, 0x50, 0x4E, 0x47,
35.          0x0D, 0x0A, 0x1A, 0x0A
36.         };
37.       const uchar    Header[] = { 'I', 'H', 'D', 'R' };
38. 
39.       int            f_handle;
40.       uint           size;
41.       uchar          buff[];
42.       st_IHDR        ihdr;
43. 
44.       if((f_handle = FileOpen(szFileName, FILE_BIN)) < 0)
45.          macroERROR(StringFormat("Error accessing file: %s", szFileName));
46. 
47.       size = FileReadArray(f_handle, buff, 0, File_Signature.Size());
48.       if(size != File_Signature.Size())
49.          macroERROR(StringFormat("Error reading the file: %s", szFileName));
50. 
51.       if(ArrayCompare(buff, File_Signature, 0, 0, size) != 0)
52.          macroERROR("File is not a PNG image.");
53. 
54.       FileReadStruct(f_handle, ihdr);
55.       if(ArrayCompare(ihdr.type, Header) != 0)
56.          macroERROR("Error reading the header.");
57. 
58.       ArrayReverse(ihdr.lenght.c);
59.       ArrayReverse(ihdr.width.c);
60.       ArrayReverse(ihdr.height.c);
61. 
62.       PrintX(ihdr.lenght.value);
63.       PrintX(ihdr.width.value);
64.       PrintX(ihdr.height.value);
65. 
66.       FileClose(f_handle);
67. 
68. #undef PrintX
69. #undef macroERROR
70.      }
71.   };
72. //+------------------------------------------------------------------

Código 04

É verdade que isto parece ser mais complicado do que realmente é. Mas acredite, é mais simples fazer as coisas assim. Desta maneira, quando executarmos novamente o código, o resultado é o que vemos abaixo.

Imagem 08

Muito bem, já temos a primeira parte do que precisamos. Tudo que temos de fazer agora, é ler a imagem para poder apresentar, ou utilizar ela de alguma forma. Mas para fazer isto da maneira correta, precisamos recorrer à documentação do formato. Isto a fim de realmente conseguir remontar a imagem da maneira correta. No final deste artigo, vou deixar no campo de referências, onde você pode conseguir mais informações. Já que talvez você queira utilizar imagens diferentes da que estarei mostrando aqui.

De qualquer forma, a parte básica da leitura está concluída. Assim para garantir um bom entendimento, vamos a um novo tópico.


Carregando a imagem PNG

Tudo que foi feito até este momento, faz parte de um bloco de construção que sempre se repete. Independentemente do tipo de coisa que estamos fazendo. Seja carregando uma imagem. Tentando descompactar um arquivo, ou qualquer outra coisa. Tudo é basicamente feito desta mesma maneira. Mas como aqui estamos interessando em carregar uma imagem. O que foi feito até este ponto, é idêntico a qualquer imagem PNG que será carregada. Porém, o que faremos agora é algo voltado ao que de fato precisamos. Então se você desejar fazer diferente, terá que estudar a documentação do formato. Nosso bloco de imagem é visto logo abaixo.

Imagem 09

Já a imagem em si, se encontra presente nesta região demarcada abaixo.

Imagem 10

Porém, preste atenção, estes dados que estamos vendo, estão compactados. Muito bem, já sabemos o que precisamos saber. Mas podemos deixar o código um tanto quanto mais simples. Apesar deste mostrado até o momento funcionar. Tivemos de fazer algumas coisas, que usando o MQL5, se tornam um tanto quanto desnecessárias. Assim, todo o código visto anteriormente, pode ser melhorado para outro visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. class C_PNG
05.   {
06. private:
07.    union uint32
08.      {
09.       uint           value;
10.       uchar          c[sizeof(uint)];
11.      };
12. 
13.    union un_IHDR
14.      {
15.       struct stIHDR
16.         {
17.          uint32      width;
18.          uint32      height;
19.          uchar       depth;
20.          uchar       colour;
21.          uchar       compression;
22.          uchar       filter;
23.          uchar       intelace;
24.         } infos;
25.       uchar          c[sizeof(stIHDR)];
26.      };
27. 
28.    bool              Read_Chunk(int &file,const uchar &type[],uchar &buff[],uint &crc)
29.      {
30.       uint32         len;
31.       uchar          loc[];
32. 
33.       FileReadArray(file, len.c, 0, 4);
34.       ArrayReverse(len.c);
35.       FileReadArray(file, loc, 0, 4);
36. 
37.       if(len.value > 0)
38.          FileReadArray(file, buff, 0, len.value);
39. 
40.       FileReadArray(file, len.c, 0, 4);
41.       ArrayReverse(len.c);
42.       crc = len.value;
43. 
44.       return(ArrayCompare(type, loc) == 0);
45.      }
46. 
47. public:
48.                      C_PNG(const string szFileName)
49.      {
50. #define macroERROR(msg) { Print(msg); FileClose(f_handle); return; }
51. 
52.       const uchar    File_Signature[] =
53.         {
54.          0x89, 0x50, 0x4E, 0x47,
55.          0x0D, 0x0A, 0x1A, 0x0A
56.         };
57.       const uchar    Chunk_IHDR[] = { 'I', 'H', 'D', 'R' };
58.       const uchar    Chunk_IDAT[] = { 'I', 'D', 'A', 'T' };
59.       const uchar    Chunk_IEND[] = { 'I', 'E', 'N', 'D' };
60. 
61.       int            f_handle;
62.       uint           tmp;
63.       uchar          buff[];
64.       uchar          img[];
65.       un_IHDR        ihdr;
66. 
67.       if((f_handle = FileOpen(szFileName, FILE_BIN)) < 0)
68.          macroERROR(StringFormat("Error accessing file: %s", szFileName));
69. 
70.       tmp = FileReadArray(f_handle, buff, 0, File_Signature.Size());
71.       if(tmp != File_Signature.Size())
72.          macroERROR(StringFormat("Error reading the file: %s", szFileName));
73. 
74.       if(ArrayCompare(buff, File_Signature, 0, 0, tmp) != 0)
75.          macroERROR("File is not a PNG image.");
76. 
77.       if(!Read_Chunk(f_handle, Chunk_IHDR, ihdr.c, tmp))
78.          macroERROR("Error in image data.");
79. 
80.       ArrayReverse(ihdr.infos.width.c);
81.       ArrayReverse(ihdr.infos.height.c);
82. 
83.       if(!Read_Chunk(f_handle, Chunk_IDAT, img, tmp))
84.          macroERROR("Error in image data.");
85.       if(!Read_Chunk(f_handle, Chunk_IEND, buff, tmp))
86.          macroERROR("Error in image data.");
87. 
88.       FileClose(f_handle);
89. 
90.       PrintFormat("width: %d", ihdr.infos.width.value);
91.       PrintFormat("height: %d", ihdr.infos.height.value);
92.       ArrayPrint(img);
93. 
94. #undef macroERROR
95.      }
96.   };
97. //+------------------------------------------------------------------+

Código 05

Agora ao executarmos este código, temos o resultado mostrado abaixo:

Imagem 11

Ou seja, já temos todas as informações que precisamos, de uma só fez. Agora tudo que precisamos, é descompactar a imagem que se encontra no array IMG, que foi mostrada acima. Note como o código, ficou muito mais simples de ser lido, e como só falta descompactar a imagem., podemos partir para o código final que pode ser visto abaixo, na íntegra.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. class C_PNG
005.   {
006. private:
007.    union uint32
008.      {
009.       uint           value;
010.       uchar          c[sizeof(uint)];
011.      };
012. 
013.    union un_IHDR
014.      {
015.       struct stIHDR
016.         {
017.          uint32      width;
018.          uint32      height;
019.          uchar       depth;
020.          uchar       colour;
021.          uchar       compression;
022.          uchar       filter;
023.          uchar       intelace;
024.         } infos;
025.       uchar          c[sizeof(stIHDR)];
026.      } m_IHDR;
027. 
028.    uchar             m_Pixels[];
029. 
030.    bool              Read_Chunk(int &file,const uchar &type[],uchar &buff[],uint &crc)
031.      {
032.       uint32         len;
033.       uchar          loc[];
034. 
035.       FileReadArray(file, len.c, 0, 4);
036.       ArrayReverse(len.c);
037.       FileReadArray(file, loc, 0, 4);
038. 
039.       if(len.value > 0)
040.          FileReadArray(file, buff, 0, len.value);
041. 
042.       FileReadArray(file, len.c, 0, 4);
043.       ArrayReverse(len.c);
044.       crc = len.value;
045. 
046.       return(ArrayCompare(type, loc) == 0);
047.      }
048. 
049.    bool              Decode(const uchar &img[],uint width,uint height)
050.      {
051.       uchar          buff[];
052.       uchar          key[];
053. 
054.       if(!CryptDecode(CRYPT_ARCH_ZIP, img, key, buff))
055.          return(false);
056. 
057.       ArrayResize(m_Pixels, width * height);
058.       for(uint c = 0, c1 = 0, t = 1, m = buff.Size(); c < m; c++, c1++, t++)
059.         {
060.          m_Pixels[c1] = buff[c];
061.          if(t == width)
062.            {
063.             c++;
064.             t = 0;
065.            }
066.         }
067. 
068.       return(true);
069.      }
070. 
071. public:
072.                      C_PNG(const string szFileName)
073.      {
074. #define macroERROR(msg) { Print(msg); FileClose(f_handle); return; }
075. 
076.       const uchar    File_Signature[] =
077.         {
078.          0x89, 0x50, 0x4E, 0x47,
079.          0x0D, 0x0A, 0x1A, 0x0A
080.         };
081.       const uchar    Chunk_IHDR[] = { 'I', 'H', 'D', 'R' };
082.       const uchar    Chunk_IDAT[] = { 'I', 'D', 'A', 'T' };
083.       const uchar    Chunk_IEND[] = { 'I', 'E', 'N', 'D' };
084. 
085.       int            f_handle;
086.       uint           tmp;
087.       uchar          buff[];
088.       uchar          img[];
089. 
090.       ArrayResize(m_Pixels, 0);
091. 
092.       if((f_handle = FileOpen(szFileName, FILE_BIN)) < 0)
093.          macroERROR(StringFormat("Error accessing file: %s", szFileName));
094. 
095.       tmp = FileReadArray(f_handle, buff, 0, File_Signature.Size());
096.       if(tmp != File_Signature.Size())
097.          macroERROR(StringFormat("Error reading the file: %s", szFileName));
098. 
099.       if(ArrayCompare(buff, File_Signature, 0, 0, tmp) != 0)
100.          macroERROR("File is not a PNG image.");
101. 
102.       if(!Read_Chunk(f_handle, Chunk_IHDR, m_IHDR.c, tmp))
103.          macroERROR("Error in image data.");
104. 
105.       ArrayReverse(m_IHDR.infos.width.c);
106.       ArrayReverse(m_IHDR.infos.height.c);
107. 
108.       if(!Read_Chunk(f_handle, Chunk_IDAT, img, tmp))
109.          macroERROR("Error in image data.");
110.       if(!Read_Chunk(f_handle, Chunk_IEND, buff, tmp))
111.          macroERROR("Error in image data.");
112. 
113.       FileClose(f_handle);
114. 
115.       if(!Decode(img, m_IHDR.infos.width.value, m_IHDR.infos.height.value))
116.          macroERROR("Error in image data.");
117. 
118. #undef macroERROR
119.      }
120. 
121.    uint              GetImage(uchar &arr[])
122.      {
123.       if(!m_Pixels.Size())
124.          return(0);
125. 
126.       ArrayCopy(arr, m_Pixels);
127. 
128.       return(m_IHDR.infos.width.value);
129.      }
130.   };
131. //+------------------------------------------------------------------+

Código 06

Note que na linha vinte e sete, temos um array. Este é o que receberá a imagem já descompactada. Já o processo de descompactação é algo relativamente simples. Isto por que o MQL5, conta com uma chamada que nos ajuda a fazer isto sem dificuldades. A tal chamada é vista na linha cinquenta e um. O valor para o array, Key pode ficar como sendo vazio. Já que não existe nenhum tipo de chave de segurança sendo utilizada aqui. Se o processo ocorrer de maneira correta, no array buff, teremos as informações descompactadas.

Porém, elas ainda contêm informações que apenas nos atrapalharão. Precisamos filtrar o conteúdo, antes de ter tudo terminado. Assim na linha cinquenta e três, alocamos o espaço necessário para a imagem. E na linha cinquenta e quatro entramos em um laço, cujo objetivo e remover alguns valores, que não fazem parte da imagem final. Quando este laço terminar, teremos no array m_Pixels, a imagem que queremos e utilizaremos.

Para finalizar, esta classe. Na linha cento e nove, temos uma função que visa, retornar a imagem, assim como também a largura da mesma. Desta forma, podemos fazer a leitura de maneira muito mais tranquila e sem sobressaltos.

Ok, agora vamos ver o código que faz uso desta classe. O mesmo pode ser visto na íntegra logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include <Neural Network\C_PNG.mqh>
05. #define protected public
06. #include <Canvas\Canvas.mqh>
07. #undef protected
08. //+------------------------------------------------------------------+
09. void SaveInText(string szFileName,const uchar &buff[],const uint width)
10.   {
11.    int               file;
12.    string            s = "";
13. 
14.    if((file = FileOpen(szFileName + ".txt", FILE_WRITE | FILE_TXT)) == INVALID_HANDLE)
15.       return;
16. 
17.    for(uint c = 0, t = 1; c < buff.Size(); c++, t++)
18.      {
19.       s += StringFormat("%3d ", buff[c]);
20.       if(t == width)
21.         {
22.          s += "\n\r";
23.          FileWriteString(file, s);
24.          s = "";
25.          t = 0;
26.         }
27.      }
28. 
29.    s += "\n\r";
30.    FileWriteString(file, s);
31. 
32.    FileClose(file);
33.   }
34. //+------------------------------------------------------------------+
35. void View_IMAGE(uint x,uint y,uchar s,const uchar &buff[],const uint width)
36.   {
37.    CCanvas          canvas;
38.    const uint       real = width * s;
39. 
40.    if(!canvas.CreateBitmapLabel(0, 0, "tmp", 100, 100, real, real,
41.                                 COLOR_FORMAT_ARGB_NORMALIZE))
42.      {
43.       Print("Error creating canvas: ", GetLastError());
44.       return;
45.      }
46. 
47.    color            bmp[];
48.    double           fx = (real * 1.0) / width;
49.    uint             pyi, pyf, pxi, pxf, tmp, uc;
50. 
51.    ArrayResize(bmp, real * real);
52.    for(uint cy = 0, y = 0; cy < width; cy++, y += width)
53.      {
54.       pyf = (uint)(fx * cy) * real;
55.       tmp = pyi = (uint)(fx * (cy - 1)) * real;
56. 
57.       for(uint x = 0; x < width; x++)
58.         {
59.          pxf = (uint)(fx * x);
60.          pxi = (uint)(fx * (x - 1));
61.          uc = ColorToARGB(buff[y + x], 255);
62.          bmp[pxf + pyf] = (color)uc;
63. 
64.          for(pxi++; pxi < pxf; pxi++)
65.             bmp[pxi + pyf] = (color)uc;
66.         }
67. 
68.       for(pyi += real; pyi < pyf; pyi += real)
69.          for(uint x = 0; x < real; x++)
70.             bmp[x + pyi] = bmp[x + tmp];
71.      }
72. 
73.    ArrayCopy(canvas.m_pixels, bmp);
74.    ArrayFree(bmp);
75. 
76.    canvas.Update();
77.   }
78. //+------------------------------------------------------------------+
79. //| Script program start function                                    |
80. //+------------------------------------------------------------------+
81. void OnStart(void)
82.   {
83.    uchar             pixels[];
84.    uint              width;
85. 
86.    C_PNG            png("mnist/10032.png");
87.    if(!(width = png.GetImage(pixels)))
88.       return;
89. 
90.    View_IMAGE(10, 10, 10, pixels, width);
91.    SaveInText("mnist", pixels, width);
92. 
93.    Print("ok...");
94.   }
95. //+------------------------------------------------------------------+

Código 07

Como este código é bem simples, não vejo nenhum ponto onde realmente precisamos dar algum destaque especial. Mas ao executar ele, você terá como resultado o que é mostrado na imagem abaixo. Isto olhando o gráfico criado no MetaTrader 5.

Imagem 12

Um detalhe: Esta imagem acima, está usando cores falsas. Já que o correto seria que fizéssemos a plotagem em tons de cinza. Mas de qualquer forma, o que nos interessa, é que a imagem do arquivo PNG, foi devidamente carregada e já poderá ser utilizada. Porém, como também estamos criando um arquivo de texto, e este terá como sendo o seu conteúdo, o valor de cada um dos pixels. Também teremos algo que pode ser visto na imagem abaixo.

Imagem 13

E é justamente estes valores que nos interessa no final das contas. Pois eles é que serão utilizados depois, quando formos alimentar a entrada da nossa rede perceptron. Mas por hora, o que temos feito até este momento já nos é mais que o suficiente. Isto para podermos trabalhar, nas próximas coisas que serão vistas já no próximo artigo.


Considerações Finais

Neste artigo, começamos a implementar, os pontos dos quais precisamos para demonstrar o funcionamento, do que talvez seja a rede perceptron, mais didática de todas.

Visto que ela é usada em diversos experimentos servindo para explicar muitas coisas. Tal modelo de rede perceptron, faz uso do banco de dados MNIST. Que pode ser baixado, a fim de que você também construa uma rede com fins didáticos. Como se trata de quatro arquivos compactados. E baixar eles, depende do interesse de cada um. Vou deixar a parte referente a isto, a cargo de cada um de vocês, pelo menos por hora. De qualquer forma, deixarei sempre alguns dos PNGs disponíveis no anexo. Isto para que você também tenha algo utilizável.

Uma observação importante: Como o código visto neste artigo, visa exclusivamente cobrir arquivo PNG, que fazem parte do MNIST. O mesmo não conseguirá renderizar corretamente outros modelos de arquivos PNG. Visto que não é este o propósito do código. E como bônus, também deixaremos, onde você pode baixar, um código-fonte de outro programador MQL5, voltado para renderizar outros tipos de PNG.

Se vamos dar crédito, vamos dar a quem merece.
Arquivo MQ5 Descrição
Scripts\Example 01  Demonstração básica

Referencias

PNG (Portable Network Graphics)

PNG - library for MetaTrader 5

Arquivos anexados |
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Do básico ao intermediário: Arquivo template (III) Do básico ao intermediário: Arquivo template (III)
No passado, publiquei um artigo que gerou muita confusão e proporcionou pouco entendimento por parte de muitos que o leram. Pois bem, neste artigo, vamos rever de forma muito mais bem explicada, exatamente aquele conceito que outrora não fazia nenhum sentido. Ou seja, vamos ver como colocar mais de um indicador em uma única sub janela de forma que eles sejam facilmente lidos e compreendidos.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Técnicas do MQL5 Wizard que você deve saber (Parte 59): Aprendizado por Reforço (DDPG) com Padrões da Média Móvel e do Oscilador Estocástico Técnicas do MQL5 Wizard que você deve saber (Parte 59): Aprendizado por Reforço (DDPG) com Padrões da Média Móvel e do Oscilador Estocástico
Continuamos nosso último artigo sobre DDPG com indicadores de Média Móvel e Estocástico, examinando outras classes-chave de Aprendizado por Reforço cruciais para a implementação do DDPG. Embora estejamos codificando principalmente em Python, será exportado para o formato ONNX para o MQL5, onde a integraremos como um recurso em um Expert Advisor montado pelo Wizard.