Aplicativo Flask com Tensorflow Distribuído

Husein Zolkepli Segue 6 de nov de 2018 · 5 min ler

E se eu dissermos que podemos dividir nosso cálculo de feed-forward em máquinas diferentes e implantá-lo na API?

Sim, nós podemos!

Eu sei, eu sei, qual é o propósito de distribuir nossos nós de feed-forward em diferentes máquinas corretamente? Quero dizer, as máquinas estão ficando mais baratas, 4GB de RAM não é um problema agora, podemos fazer um único feed-forward em uma única máquina, mas, o que acontecerá no futuro nosso modelo é realmente massivo e nós provavelmente não queremos esgotar nossos recursos de máquina.

Trending AI Articles:

1. Notas do Livro de Aprendizagem Profunda, Capítulo 1

2. Notas do Livro de Aprendizagem Profunda, Capítulo 2

3. Máquinas Demonstram Autoconsciência

4. Workshop de Música Visual e Aprendizagem de Máquina para Crianças

Se você tiver um cluster local (você pode fazer vários PIs de framboesa conectados a um roteador e fazer isso!) Ou nuvens, provavelmente é melhor porque você tem menos afunilamento de rede durante a transferência dos tensores entre suas máquinas.

Então vamos começar!

Começa aqui

Primeiro, precisamos do modelo. A arquitetura é muito simples

Eu treinei o modelo neste caderno, você pode verificá-lo aqui, freeze-model.ipynb

Durante o treinamento, ele salvará os pontos de verificação como /model-test/test .

Suponha que eu tenha duas máquinas, primeiro 192.168.0.101 , segundo 192.168.0.104 .

Copie este código em ambas as máquinas e salve-o como backend.py ,

 import sys 

task_number = int (sys.argv [1])

importar tensorflow como tf

cluster = tf.train.ClusterSpec (
{'worker': ['192.168.0.101:2222', '192.168.0.104:2223']}
)
server = tf.train.Server (cluster, job_name = 'trabalhador', task_index = task_number)

print ('Iniciando servidor nº {}'. format (task_number))

server.start ()
server.join ()

Aqui, você precisa fornecer endereços IP e PORT usados para o servidor GRPC, como sobre, eu defini,

 {'worker': ['192.168.0.101:2222', '192.168.0.104:2223']} 

Certifique-se de ter definido job_name igual à chave do dicionário,

 job_name = 'trabalhador' 

Primeiro índice do trabalhador é 192.168.0.101:2222 , eu ssh ele e,

 python3 backend.py 0 2018-11-05 21: 30: 24.948465: I tensorflow / core / platform / cpu_feature_guard.cc: 141] Sua CPU suporta instruções que este binário TensorFlow não foi compilado para usar: AVX2 FMA 
2018-11-05 21: 30: 24.950125: I tensorflow / core / distributed_runtime / rpc / grpc_channel.cc: 222] Inicializar o GrpcChannelCache para job worker -> {0 -> localhost: 2222, 1 -> 192.168.0.104:2223}
2018-11-05 21: 30: 24.950590: Eu tensorflow / core / distributed_runtime / rpc / grpc_server_lib.cc: 381] Servidor iniciado com destino: grpc: // localhost: 2222

Segundo índice, 192.168.0.104:2223 , eu ssh e,

 python3 backend.py 1 2018-11-05 21: 30: 35.549035: I tensorflow / core / platform / cpu_feature_guard.cc: 141] Sua CPU suporta instruções que este binário TensorFlow não foi compilado para usar: AVX2 FMA 
2018-11-05 21: 30: 35.550251: Eu tensorflow / core / distributed_runtime / rpc / grpc_channel.cc: 222] Inicializar o GrpcChannelCache para o trabalho de trabalho -> {0 -> 192.168.0.101:2222, 1 -> localhost: 2223}
2018-11-05 21: 30: 35.550586: Eu tensorflow / core / distributed_runtime / rpc / grpc_server_lib.cc: 381] Servidor iniciado com destino: grpc: // localhost: 2223

Você deve obter as mesmas saídas como esta. Agora vamos testar alguns cálculos, salve-os como reduce-mean.py ,

 importar tensorflow como tf 
import numpy como np

cluster = tf.train.ClusterSpec (
{'worker': ['192.168.0.101:2222', '192.168.0.104:2223']}
)

x = tf.placeholder (tf.float32, 100)

com tf.device ('/ job: worker / task: 1'):
first_batch = tf.slice (x, [0], [50])
mean1 = tf.reduce_mean (first_batch)

com tf.device ('/ job: worker / task: 0'):
second_batch = tf.slice (x, [50], [-1])
mean2 = tf.reduce_mean (second_batch)
média = (média1 + média2) / 2

sess = tf.InteractiveSession ('grpc: //192.168.0.101: 2222')
result = sess.run (média, feed_dict = {x: np.random.random (100)})
imprimir (resultado)

Executá-lo,

 python3 reduce-mean.py 0.5361499 

O que eu fiz isso, eu peguei um array aleatório inicializado com distribuição normal aleatória com tamanho 100. Primeiros 50 elementos passarão para o segundo worker, outros 50 elementos passarão para o primeiro worker.

Mas, primeiro trabalhador precisou de trabalho feito do segundo trabalhador, para fazer (mean1 + mean2) / 2 .

Se você verificar a saída do terminal do primeiro trabalhador,

 Inicie a sessão principal e91a8d5d551a9e94 com config: gpu_options {allow_growth: true} graph_options {place_pruned_graph: true} 

Estamos gerando com sucesso uma sessão de tensorflow para nosso feed-forward distribuído.

Agora, para o nosso aplicativo de frascos, você pode verificar a fonte completa aqui .

 Modelo de classe: 
def __init __ (
auto,
size_layer,
num_layers,
embedded_size,
dict_size,
dimension_output,
taxa de Aprendizagem,
):
def células (reutilização = Falso):
return tf.nn.rnn_cell.BasicRNNCell (size_layer, reutilização = reutilização)

self.X = tf.placeholder (tf.int32, [Nenhum, Nenhum])
self.Y = tf.placeholder (tf.float32, [Nenhum, dimension_output])
encoder_embeddings = tf.Variable (
tf.random_uniform ([dict_size, embedded_size], -1, 1)
)
com tf.device ('/ job: worker / task: 0'):
encoder_embedded = tf.nn.embedding_lookup (
encoder_embeddings, self.X
)
rnn_cells = tf.nn.rnn_cell.MultiRNNCell (
[cells () para _ no intervalo (num_layers)]
)

saídas, _ = tf.nn.dynamic_rnn (
rnn_cells, encoder_embedded, dtype = tf.float32
)
com tf.device ('/ job: worker / task: 1'):
rnn_W = tf.Variavel (
tf.random_normal ((size_layer, dimension_output))
)
rnn_B = tf.Variable (tf.random_normal ([dimension_output]))
self.logits = tf.add (
tf.matmul (saídas [:, -1], rnn_W), rnn_B, nome = 'logits'
)

Podemos ver que, primeiro trabalhador fará pesquisa embutida e cálculo multi-RNN. Enquanto o segundo worker está esperando que o primeiro worker seja feito e a saída do primeiro worker irá se multiplicar com a camada logits para obter a saída.

Mas,

Frasco só não é suficiente.

Precisamos de outra coisa para torná-la melhor e perfeita. Sim, Gunicorn e Eventlet!

Na verdade, podemos gerar dois gráficos diferentes distribuídos nessas duas máquinas. Isso significa que temos 2 redes neurais geradas 2 vezes simultaneamente e distribuídas entre as máquinas!

Eu chamei isso, monkey.sh ,

 NUM_WORKER = US $ 1 
BIND_ADDR = 0.0.0.0: 8008
gunicorn --timeout 120 --log-level = depurar -w $ NUM_WORKER -b $ BIND_ADDR -k eventlet app

Para desovar é fácil

 bash monkey.sh 2 

2 representa dois modelos gerados distribuídos entre máquinas, significa que podemos responder a muitos mais usuários simultâneos!

Agora tente enrolar

 curl http: // localhost: 8008 /? text = pedaço% 20of% 20shit negativo 

Sim, muito fácil assim.

Agora a restrição é que você tem um gargalo de rede, se seus tensores são tão grandes, pode levar algum tempo para copiar os tensores de uma máquina para outra, então eu realmente sugiro que você use máquinas realmente próximas umas das outras.

Você pode obter todo o código fonte aqui .

Isso é tudo por agora e,

Obrigado por ler isto, encontro-te no próximo artigo!

Não se esqueça de nos dar o seu ?!