Hint
Перед прочтением этой статьи советую ознакомиться с предыдущей статьей о TensorFlowKit и поставить star репозиторию.

Я не люблю читать статьи, сразу иду на GitHub
GitHub: TensorFlowKit
GitHub: Example
GitHub: Другое
TensorFlowKit API
Посeтив репозиторий, добавьте его в «Stars» это поможет мне написать больше статей на эту тему.

image

Начиная работать в сфере машинного обучения, мне было тяжело переходить от объектов и их поведений к векторам и пространствам. Сперва все это достаточно тяжело укладывалось в голове и далеко не все процессы казались прозрачными и понятными с первого взгляда. По этой причине все, что происходило внутри моих наработок, я пробовал визуализировать: строил 3D модели, графики, диаграммы, изображения и тд.

Говоря об эффективной разработке систем машинного обучения, всегда поднимается вопрос контроля скорости обучения, анализа процесса обучения, сбора различных метрик обучения и тд. Особая сложность заключается в том, что мы (люди) привыкли оперировать 2х и 3х мерными пространствами, описывая различные процессы вокруг нас. Процессы внутри нейронных сетей происходят в многомерных пространствах, что серьезно усложняет их понимание. Осознавая это, инженеры по всему миру стараются разработать различные подходы к визуализации или трансформации многомерных данных в более простые и понятные формы.

Существуют целые сообщества, решающие такого рода задачи, например Distill, Welch Labs, 3Blue1Brown.


TensorBoard


Еще до начала работы с TensorFlow я начал работать с TensorBoard пакетом. Оказалось, что это очень удобное, кросплатформенное решение для визуализации разного рода данных. Потребовалось пару дней, чтоб “научить” swift приложение создавать отчеты в формате TensorBoard и интегрировать в мою нейронную сеть.

Разработка TensorBoard началась еще в середине 2015 года в рамках одной из лабораторий Google. В конце 2015го Google открыл исходный код и работа над проектом стала публичной.
Текущая версия TensorBoard — это python пакет, созданный в помощь TensorFlow, который позволяет визуализировать несколько типов данных:

  • Скалярные данные в разрезе времени, с возможностью сглаживания;
  • Изображения, в том случае, если ваши данные можно представить в 2D, например: веса сверточной сети (они же фильтры);
  • Непосредственно граф вычислений (в виде интерактивного представления);
  • 2D изменение значения тензора во времени;
  • 3D Гистограмма — изменение распределения данных в тензоре во времени;
  • Текст;
  • Audio;

Кроме того, существует еще проектор (projector) и возможность расширять TensorBoard при помощи плагинов, но об этом я рассказать не успею в этой статье.

Для работы нам понадобится TensorBoard на нашем компьютере (Ubuntu или Mac). У нас должен быть установлен python3. Я советую установить TensorBoard как часть TensorFlow пакета для python.

Linux:
$ sudo apt-get install python3-pip python3-dev
$ pip3 install tensorflow

MacOS:
$ brew install python3
$ pip3 install tensorflow

Запускаем TensorBoard, указав папку в которой мы будем хранить отчеты:

$ tensorboard --logdir=/tmp/example/

Открываем http://localhost:6006/


TensorFlowKit


Пример на GitHub
Не забудьте поставить «start» репозиторию.

Рассмотрим несколько случаев уже непосредственно на примере. Создание отчетов (summary) в формате TensorBoard происходит в момент конструирования графа вычислений. В TensorFlowKit я постарался максимально повторить python подходы и интерфейс, чтобы в дальнейшем можно было пользоваться общей документацией.

Как я уже сказал выше, каждый наш отчет мы собираем в summary. Это контейнер, в котором хранится массив value, каждый из которых представляет какое — либо событие, которое мы хотим визуализировать.

Summary в дальнейшем будет сохранен в файл на файловой системе, где его и прочтет TensorBoard.

image

Таким образом, нам необходимо создать FileWriter, указав граф, который мы хотим визуализировать и создать Summary, в который мы будем складывать наши значения.

let summary = Summary(scope: scope)
let fileWriter = try FileWriter(folder: writerURL, identifier: "iMac", graph: graph)

Запустив приложение и обновив страницу, мы уже можем видеть граф, который мы создали в коде. Он будет интерактивен, так что по нему можно перемещаться.

image

Далее, мы хотим видеть изменение некой скалярной величины во времени, например значение функции потерь (loss function or cost function) и accuracy нашей нейронной сети. Для этого добавляем выходы наших операций в summary:

try summary.scalar(output: accuracy, key: "scalar-accuracy")
try summary.scalar(output: cross_entropy, key: "scalar-loss")

Таким образом, после каждого шага вычислений нашей сессии, TensorFlow автоматически вычитает значения наших операций и передаст их на вход результирующего Summary, который мы сохраним в FileWriter (как это сделать я опишу ниже).

image

Также у нас в нейросети есть большее количество весов и предубеждений (bias). Как правило это различные матрицы достаточно большой размерности и анализировать их значения распечатывая крайне сложно. Будет лучше, если мы построим график распределений (distribution). Добавим в наш Summary еще и информацию о величине изменений весов, которую проделывает наша сеть после каждого шага обучения.

try summary.histogram(output: bias.output, key: "bias")
try summary.histogram(output: weights.output, key: "weights")
try summary.histogram(output: gradientsOutputs[0], key: "GradientDescentW")
try summary.histogram(output: gradientsOutputs[1], key: "GradientDescentB")

Теперь в нашем распоряжении визуализация того, как менялись веса и какими были изменения во время обучения.

image

image

Но это еще не все. Давайте действительно заглянем в устройство нашей нейронной сети.
Каждая картинка рукописного текста, полученная на вход, находит некое отражение в соответствующих ей весах. То есть, поданная на вход картинка умеет активировать определенные нейроны, тем самым имеет некий отпечаток внутри нашей сети. Напомню, что мы имеем 784 веса на каждый нейрон из 10. Таким образом, у нас 7840 весов. Все они представлены в виде матрицы 784x10. Попробуем развернуть всю матрицу в вектор и после этого “вытащить” веса, которые относятся к каждому отдельному классу:

let flattenConst = try scope.addConst(values: [Int64(7840)], dimensions: [1], as: "flattenShapeConst")

let imagesFlattenTensor = try scope.reshape(operationName: "FlattenReshape",
     tensor: weights.variable,
     shape: flattenConst.defaultOutput,
     tshape: Int64.self)

try extractImage(from: imagesFlattenTensor, scope: scope, summary: summary, atIndex: 0)
try extractImage(from: imagesFlattenTensor, scope: scope, summary: summary, atIndex: 1)
…
try extractImage(from: imagesFlattenTensor, scope: scope, summary: summary, atIndex: 8)
try extractImage(from: imagesFlattenTensor, scope: scope, summary: summary, atIndex: 9)

Для этого добавим в граф еще несколько операций stridedSlice и reshape. Теперь, каждый полученный вектор добавим в Summary как картинку:

try summary.images(name: "Image-\(String(index))", output: imagesTensor, maxImages: 255, badColor: Summary.BadColor.default)

В разделе Images в TensorBoard мы теперь видим “отпечатки” весов, такими какими они были во время процесса обучения.

image

Осталось только обработать наш Summary. Для этого нам надо соединить все созданные Summary в один и обработать его во время обучения сети.

let _ = try summary.merged(identifier: "simple")

В момент работы нашей сети:

let resultOutput = try session.run(inputs: [x, y],
     values: [xTensorInput, yTensorInput],
     outputs: [loss, applyGradW, applyGradB, mergedSummary, accuracy],
     targetOperations: [])

let summary = resultOutput[3]
try fileWriter?.addSummary(tensor: summary, step: Int64(index))

Прошу обратить внимание, что в этом примере я не рассматриваю вопрос вычисления accuracy, он вычисляется на данных обучения. Вычислять его на данных для обучения неверно.

image

image

В следующей статье я постараюсь рассказать, как собрать одну нейросеть и запустить ее на Ubuntu, MacOS, iOS из одного репозитория.

Комментарии (14)


  1. AFH
    23.11.2017 13:31

    А вы пробовали пользоваться tf.summary.tensor_summary? Или я не понимаю для чего оно предназначено или оно не работет. Можете подсказать как с этой штукой работать?


    1. Roaming Автор
      23.11.2017 13:35

      Вы говорите, о вызове метода на языке Python. Вся статья (как и предыдущая) рассказывает о том, как использовать TensorFlow в языке Swift.


      1. AFH
        23.11.2017 14:21

        Да, я понимаю, но ведь в Swift-библиотеке есть аналог этого метода? Приходилось вам им пользоваться?


        1. Roaming Автор
          23.11.2017 14:32

          О какой swift библиотеке вы говорите? Не существует официальной реализации (как и полноценной не официальной) TensorFlow для Swift.
          Именно по этой причине я написал свою реализацию — TensorFlowKit.


          1. AFH
            23.11.2017 16:19
            -1

            Да, я её и имею в виду, но по сути это не так и важно, вопрос в равной степени относится и к самому tensorflow.
            Вы пишите что

            В TensorFlowKit я постарался максимально повторить python подходы и интерфейс, чтобы в дальнейшем можно было пользоваться общей документацией.

            Соответственно меня и интересует есть ли в вашей библиотеке аналог tf.summary.tensor_summary, пробовали ли вы им пользоваться и есть ли там какие-то нюансы?


            1. Roaming Автор
              23.11.2017 16:31

              Да, в бибилиотеке TensorFlowKit (автором которой являюсь я), реализован аналог Python API (tf.summary.tensor_summary) класс Summary. Об этом статья и написана.
              У меня складывается впечатление, что Вы не владеете темой TensorFlow in other languages. Советюую ознакомится с этой статьей.


              1. AFH
                23.11.2017 16:40
                +1

                А у вас получалось сохранять для tensorboard какие-нибудь данные через этот метод? Дело в том что теже .scalar .histogram работают отлично и отображаются через tensorboard а вот с tensor_summary у меня не получилось вывести данные. Если вас не затруднит можете показать пример?


                1. Roaming Автор
                  23.11.2017 16:51

                  Да, все что вы видите в этой статье реализовано полностью средствами Swift.
                  Скажите, на каком языке Вы пробовали и если можно, покажите пример того, что не работает.


                  1. AFH
                    23.11.2017 17:12

                    Я пробовал на python:

                    train_writer = tf.summary.FileWriter(a.log_dir, sess.graph)
                    tf.summary.scalar('cross_entropy', cross_entropy)
                    tf.summary.tensor_summary('outputs', y_s)
                    tf.summary.tensor_summary('expected', y_)
                    merged = tf.summary.merge_all()
                            ....
                    summary, _cross_entropy, _y_s = sess.run([merged, cross_entropy, y_s], feed_dict={x: x_validation_matrix, y_: y_validation_matrix})
                    train_writer.add_summary(summary, chunk_num)
                    


                    При этом cross_entropy отображается также как и у вас — в виде графика, а вот тензоры я нигде не вижу.


                    1. Roaming Автор
                      23.11.2017 17:23

                      Скорее всего, Вы имеете в виду Histogram:

                      tf.summary.histogram('histogram', var)
                      


                      Но это «свернутое» отображение в виде распределения:
                      The TensorBoard Histogram Dashboard displays how the distribution of some Tensor in your TensorFlow graph has changed over time. It does this by showing many histograms visualizations of your tensor at different points in time.


                      Так, что будьте внимательны.


                      1. AFH
                        23.11.2017 17:27

                        Нет histogram это другой метод, я имею ввиду tensor_summary


                        1. Roaming Автор
                          23.11.2017 17:36

                          Что непосредственно вы хотите видеть, как это должно выглядеть?
                          У вас есть тенсор n*m (скорее всего вектор, n*1). Он изменяется во времени.
                          В TensodBoard нет представления для такой конструкции. Можно конечно поиграться с image. Но скорее всего ваши данные нельзя представить в виде image.

                          В идеале что — то такое должно быть.
                          image
                          Но такого нет.


                          1. AFH
                            23.11.2017 17:47

                            Нет, я просто хочу сохранить тензор с данными (также как мы, например, сохраняем промежуточные изображения) чтобы потом через tensorboard посмотреть этот тензор просто в виде вектора/матрицы и т.п.
                            Я допускаю что я мог просто не так понять предназначение этого метода. Думал, может вы сталкивались и знаете зачем он нужен и как правильно пользоваться.


                1. Roaming Автор
                  23.11.2017 17:07

                  Сам summary это только контейнер для SummaryValue.

                  # То есть Вам надо создать value:
                  tf.summary.scalar("Reward", episode_reward)
                  tf.summary.scalar("Reward2", episode_reward2)
                  
                  # Смержить все Summary в один Output
                  summary_op = tf.summary.merge_all()
                  
                  # Добавить summary на "просчет" в сессию.
                  summary_str = session.run(summary_op)
                  
                  # Передать результат в контроллер которые сохранит в файл.
                  ...
                  writer = writer_summary(summary_dir + "/tmp/", session.graph)
                  writer.add_summary(summary_str, float(T))