Então você terminou de treinar seu modelo e é hora de obter algumas idéias sobre o que aprendeu. Você decide qual tensor deve ser interessante e vai procurá-lo em seu código - para descobrir qual é o nome dele. Então ele bate em você - você esqueceu de dar um nome. Você também esqueceu de agrupar o bloco de código lógico com um escopo nomeado. Isso significa que você terá dificuldade em obter uma referência ao tensor. Ele é válido para scripts python e para o TensorBoard:
Você pode ver aquele pequeno círculo vermelho perdido no mar de tensores? Achar que é difícil ...
Que chatice! Teria sido muito melhor se parecesse mais com isso:
É mais assim! Cada conjunto de tensores que formam uma unidade lógica é envolvido em um escopo nomeado.
Por que o gráfico não pode ser construído automaticamente de forma que se assemelhe ao seu código? Quero dizer, é mais provável que você não construa o modelo usando uma única função, não é? Sua base de código contém várias funções - cada uma forma uma unidade lógica que merece seu próprio escopo nomeado!
Digamos que você tenha um tensor x que foi definido pela função f , que por sua vez foi chamada por g . Isso significa que enquanto você escrevia o código, você tinha em mente esta estrutura lógica: g - f - x . Não seria ótimo se o modelo fosse construído automaticamente de forma que o nome do tensor fosse g / f / x ?
Venha para pensar sobre isso, é muito simples de fazer. Tudo o que você precisa fazer é revisar todas as suas funções e adicionar uma única linha de código:
1 2 3 | def f(): with tensorflow.name_scope('f'): # define tensors |
Então, o que há de errado com essa abordagem?
- O nome da função f aparece duas vezes - na declaração da função e como argumento para o fluxo tensor . name_scope . Talvez na próxima semana você mude o nome da função para algo mais significativo, digamos foo . Infelizmente, você pode esquecer de atualizar o nome do escopo!
- Você deve aplicar o recuo em todo o corpo de f . Embora não seja tão ruim, pessoalmente não gosto de ter altos níveis de indentação. Digamos que f contém um loop for que contém uma instrução if, que contém outro loop for. Graças a ligar para tensorflow . name_scope , já estamos no nível de indentação 4!
Podemos contornar essas desvantagens usando metaprogramação simples - os decoradores do Python para o resgate!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import re def name_scope(f): def func(*args, **kwargs): name = f.__name__[re.search(r'[^_]', f.__name__).start():] with tensorflow.name_scope(name): return f(*args, **kwargs) return func @name_scope def foo(): # define tensors |
Como funciona? O @ é um açúcar sintático. É equivalente ao seguinte:
1 2 3 4 5 | def foo(): # define tensors foo = name_scope(foo) |
name_scope obtém uma função como argumento ( f ) e retorna uma nova função ( func ). func cria um escopo nomeado e chama f .
O resultado? Todos os tensores definidos por f serão criados dentro de um escopo nomeado. O nome do escopo será o nome da função original ("foo") - graças a f . __name__ .
Um pequeno problema é que, embora os nomes das funções possam começar com "_", os nomes do escopo do tensorflow não podem. É por isso que temos que usar re .
Por que isso é importante?
O desafio de escrever um código de fluxo tensor limpo é insignificante em comparação com o desafio da pesquisa de tornar o modelo realmente bom.
Assim, é fácil ficar tentado a se concentrar apenas nos aspectos de pesquisa do seu trabalho. No entanto, a longo prazo, é importante não negligenciar a capacidade de manutenção e legibilidade do seu código, incluindo os do seu gráfico.
A abordagem do decorador facilita um pouco o meu trabalho, e espero que você também se beneficie. Você tem outras dicas que gostaria de compartilhar? Solte uma linha nos comentários!