Итак, вы закончили обучение своей модели, и пришло время получить представление о том, чему она научилась. Вы сами решаете, какой тензор должен быть интересен, и идете искать его в своем коде — узнавать, как он называется. А потом до вас доходит — вы забыли дать ему имя. Вы также забыли обернуть блок логического кода именованной областью. Это означает, что вам будет трудно получить ссылку на тензор. Это справедливо как для скриптов Python, так и для TensorBoard:
Видите этот маленький красный круг, затерянный в море тензоров? Найти его сложно…
Это облом! Было бы намного лучше, если бы это выглядело примерно так:
Это больше походит на это! Каждый набор тензоров, образующих логическую единицу, заключен в именованную область.
Почему график не может быть автоматически построен таким образом, чтобы он напоминал ваш код? Я имею в виду, скорее всего, вы не построили модель, используя одну функцию, не так ли? Ваша кодовая база содержит несколько функций, каждая из которых образует логическую единицу, заслуживающую отдельной именованной области видимости!
Допустим, у вас есть тензор x
которая была определена функцией f
который, в свою очередь, был вызван g
. Это означает, что когда вы писали код, вы имели в виду эту логическую структуру: g
-> f
-> x
. Было бы здорово, если бы модель автоматически строилась таким образом, чтобы имя тензора было g/f/x
?
Если подумать, это довольно просто сделать. Все, что вам нужно сделать, это просмотреть все ваши функции и добавить одну строку кода:
def f():
with tensorflow.name_scope(‘f’):
# define tensors
Так что же не так с этим подходом?
- Название функции
f
появляется дважды — как в объявлении функции, так и в качестве аргументаtensorflow.name_scope
. Может быть, на следующей неделе вы измените название функции на что-то более осмысленное, скажем,foo
. К сожалению, вы можете забыть обновить имя области видимости! - Вы должны применить отступ ко всему телу
f
. Хотя это не так уж и плохо, лично мне не нравятся высокие уровни отступов. скажемf
содержит цикл for, который содержит оператор if, содержащий еще один цикл for. Благодаря звонку вtensorflow.name_scope
мы уже на уровне отступа 4!
Мы можем обойти эти недостатки, используя простое метапрограммирование — на помощь приходят декораторы Python!
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
Как это работает? @
является синтаксическим сахаром. Это эквивалентно следующему:
def foo():
# define tensors
foo = name_scope(foo)
name_scope
получает функцию в качестве аргумента (f
) и возвращает новую функцию (func
). func
создает именованную область, а затем вызывает f
.
Результат? Все тензоры, которые определяются f
будет создан внутри именованной области. Имя области видимости будет именем исходной функции («foo») — благодаря f.__name__
.
Одна небольшая проблема заключается в том, что хотя имена функций могут начинаться с «_», имена областей тензорного потока не могут. Вот почему мы должны использовать re
.
Задача написания чистого кода тензорного потока незначительна по сравнению с исследовательской задачей сделать модель действительно хорошей.
Таким образом, легко поддаться искушению просто сосредоточиться на исследовательских аспектах вашей работы. Однако в долгосрочной перспективе важно не пренебрегать ремонтопригодностью и читабельностью вашего кода, в том числе графа.
Подход декоратора немного облегчает мою работу, и я надеюсь, что вы тоже извлечете из этого пользу. У вас есть другие советы, которыми вы хотели бы поделиться? Напишите строчку в комментариях!
Первоначально опубликовано мной на
Engineering.taboola.com.