Neste artigo, eu gostaria de descrever:
- Como construir uma biblioteca Tensorflow;
- Como gerar ligações para o Crystal;
- Como utilizar uma biblioteca Tensorflow em Crystal;
- Compartilhar os resultados do meu trabalho: tensorflow.cr.
Links úteis
- Linguagem Crystal: https://crystal-lang.org/
- Gerador de ligações Crystal: https://github.com/crystal-lang/crystal_lib
- Tensorflow: https://www.tensorflow.org/
- Repositório do Tensorflow: https://github.com/tensorflow/tensorflow
- Repositório do tensorflow.cr: https://github.com/fazibear/tensorflow.cr
- Repositório de exemplos do tensorflow.cr: https://github.com/fazibear/tensorflow.cr_examples
Instale e construa Tensorflow
Para instalar e compilar o Tensorflow, precisaremos instalar estes três itens:
- bazel
- Python
- numpy (pacote Python)
Primeiro, precisamos clonar o repositório Tensorflow inteiro
$ git clone https://github.com/tensorflow/tensorflow
Então, precisamos mudar o diretório e executar a configuração:
$ cd tensorflow $ ./configure
Precisamos responder algumas perguntas sobre o Python e as funcionalidades que nossa biblioteca suportará. Podemos somente apertar “Enter”. Após isso, o bazel irá baixar todas as dependências e configurar tudo.
Precisamos construir apenas uma biblioteca. Vamos fazer isso.
$ cd tensorflow # yes! tensorflow/tensorflow $ bazel build :libtensorflow.so
Conseguimos! O último passo, instalar!
cp ../bazel-bin/tensorflow/libtensorflow.so /usr/local/lib/ libtensorflow.so
Biblioteca instalada.
Gerando ligações
Para trabalhar com a biblioteca Tensorflow, precisamos gerar ligações. O Crystal tem uma aplicação para isso. Tudo o que você precisa é clonar a biblioteca crystal_lab e adicionar o arquivo lib_tensorflow.cr ao diretório de exemplos. Lembre-se de substituir {tensorflow_dir} pelo diretório no qual você clonou o Tensorflow.
@[Include( "tensorflow/c/c_api.h", flags: " -I/{tensorflow_dir}/tensorflow/ -I/{tensorflow_dir}/tensorflow/bazel-genfiles -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 ", prefix: %w(TF_ Tf) )] @[Link("tensorflow")] lib LibTensorFlow end
Agora execute o gerador:
$ crystal src/main.cr -- examples/lib_tensorflow.cr
Feito. Temos as ligações. Podemos copiar a saída para nosso projeto Crystal e começar a brincar com o Tensorflow. Vamos nomeá-lo como lib_tensorflow.cr.
Utilizando o Tensorflow a partir do Crystal
Devido ao fato de a biblioteca Tensorflow não fornecer maneiras fáceis de construir os gráficos, precisaremos utilizar Python.
import tensorflow as tf with tf.Session() as sess: a = tf.Variable(5.0, name='a') b = tf.Variable(6.0, name='b') c = tf.multiply(a, b, name='c') sess.run(tf.global_variables_initializer()) tf.train.write_graph(sess.graph_def, '.', 'graph.pb', as_text=False)
Esse script simples gerará um gráfico e o salvará no arquivo graph.pb.
Geramos o gráfico que precisa de duas variáveis para entrar e retornar um número. É a multiplicação das entradas. Também as nomeamos como “a”, “b”, “c”.
Agora, vamos para o Crystal. Precisamos solicitar nossas ligações primeiro.
require 'lib_tensorflow'
Agora estamos prontos! Precisamos inicializar uma session.
opts = LibTensorflow.new_session_options status = LibTensorflow.new_status graph = LibTensorflow.new_graph session = LibTensorflow.new_session(graph, opts, status)
Como você pode ver, precisamos de um gráfico, opções e estados. Tudo isso pode ser feito utilizando as funções das ligações. Também precisamos verificar se a criação da sessão foi bem sucedida.
puts LibTensorflow.get_code(status)
Tudo o que precisamos fazer é pegar um estado e verificar seu código. Estamos utilizando a função das ligações. A linha acima deve exibir “OK” se tudo estiver bem. Se não estiver, podemos exibir uma mensagem de erro mais detalhada como esta:
puts String.new(LibTensorflow.message(status))
Se temos uma sessão, precisamos carregar um gráfico nela.
file = File.read("./graph.pb") buffer = LibTensorflow.new_buffer_from_string(file, file.size) import_opts = LibTensorflow.new_import_graph_def_options LibTensorflow.graph_import_graph_def(graph, buffer, import_opts, status)
Tudo o que precisamos é ler os dados do arquivo, criar um buffer com uma função das ligações, e passar o buffer para uma função que importará nossos dados.
Novamente, precisamos lembrar de verificar se está tudo bem:
puts LibTensorflow.get_code(status)
Gráfico carregado. Agora precisamos criar os tensors. Eles tratam a entrada e saída de dados. Os tensors tratarão os dados.
Mais uma coisa: o tensor de inicialização precisará de uma função de desalocação. Criamos somente uma função vazia. Criar a função apropriada está além do escopo deste artigo.
deloc = ->(a : Pointer(Void), b : UInt64, c: Pointer(Void)) {}
Agora focamos nas entradas. Precisamos de dois valores. Existem tensors para isso.
a_dims = [] of Int64 a_data = [3.0_f32] of Float32 a_tensor = LibTensorflow.new_tensor( LibTensorflow::Datatype::Float, a_dims, a_dims.size, a_data, a_data.size, deloc, nil) b_dims = [] of Int64 b_data = [5.0_f32] of Float32 b_tensor = LibTensorflow.new_tensor( LibTensorflow::Datatype::Float, b_dims, b_dims.size, b_data, b_data.size, deloc, nil)
E um vazio para saída.
c_dims = [] of Int64 c_data = [] of Float32 c_tensor = LibTensorflow.new_tensor( LibTensorflow::Datatype::Float, c_dims, c_dims.size, c_data, c_data.size, deloc, nil)
Outra coisa são as operações. As operações pegam os dados dos tensors e escrevem dados nos tensors. O tensor de variáveis também está em operação para que nós possamos ler os dados dele. Precisaremos de três operações. Elas estão definidas no gráfico. Lembra de “a”, “b”, “c”?
i1 = LibTensorflow::Output.new i1.oper = LibTensorflow.graph_operation_by_name(graph, "a") i1.index = 0 i2 = LibTensorflow::Output.new i2.oper = LibTensorflow.graph_operation_by_name(graph, "b") i2.index = 0 o1 = LibTensorflow::Output.new o1.oper = LibTensorflow.graph_operation_by_name(graph, "c") o1.index = 0
Para tornar o código mais legível, criamos algumas variáveis para entradas e saídas:
inputs = [i1, i2] of LibTensorflow::Output input_values = [a_tensor, b_tensor] of LibTensorflow::X_Tensor outputs = [o1] of LibTensorflow::Output outputs_values = [c_tensor] of LibTensorflow::X_Tensor
Agora que quase temos todos os dados necessários para executar nosso gráfico, vamos fazer isto:
optss = LibTensorflow.new_buffer meta = LibTensorflow.new_buffer target = [] of LibTensorflow::X_Operation LibTensorflow.session_run(session, nil, inputs, input_values, inputs.size, outputs, outputs_values, outputs.size, target, target.size, nil, status)
Isso! Vamos verificar o estado:
puts LibTensorflow.get_code(status)
Onde está nosso retorno? Claro, vamos pegá-lo do tensor e exibi-lo:
o = outputs_values[0] out_data = LibTensorflow.tensor_data(o) out_value = out_data.as(Float32*) puts out_value.value
Como as saídas dos tensors podem ser diferentes, precisamos colocar ele para o valor apropriado e seguir um ponteiro.
Agora podemos executar nosso programa.
$ crystal src/ok.cr
Depois de alguns “ok”, recebemos “15”, que é o correto.
Primeiro sucesso!
O que vem depois?
Claro, esse não é o fim! Eu quero aprender mais!
Criei o projeto tensorflow.cr. Por enquanto, são simples ligações, mas estou trabalhando em um bonita DSL Crystal para a biblioteca e criar a habilidade de geração de gráficos. É fascinante a maneira como o aprendizado do Tensorflow funciona. Se você está interessado em me ajudar com esse projeto, me avise. Eu apreciarei.
Parcialmente, existe também o repositório tensorflow.cr_examples. Existe um que eu descrevi neste artigo, e também outros que utilizarão o DSL Crystal (essa parte pode não funcionar durante o processo de desenvolvimento).
Espero que tenha sido útil para você. Obrigado por ler!
***
Michał Kalbarczyk faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://blog.fazibear.me/learning-tensorflow-the-other-way-1f0e1764e0a7.