Документация Engee

Руководство

Основы

Введение

Вероятностная программа представляет собой код Julia, заключенный в макрос @model. Она может использовать произвольный код Julia, но для обеспечения корректности выводов она не должна иметь внешних эффектов или изменять глобальное состояние. Переменные, размещенные в стеке, безопасны, но изменяемые объекты, размещенные в куче, могут привести к возникновению трудно находимых ошибок при использовании копирования задач. По умолчанию Libtask детально копирует объекты Array и Dict при копировании задачи, чтобы избежать ошибок с данными, хранящимися в изменяемой структуре в моделях Turing.

Для задания распределений случайных величин в программах Turing следует использовать нотацию ~:

x ~ distr, где x — это символ, а distr — распределение. Если x не определен в функции модели внутри вероятностной программы, случайная переменная с именем x, распределенную в соответствии с distr, помещается в текущую область. distr может быть значением любого типа, реализующим rand(distr), который выбирает значение из распределения distr. Если x определен, он используется для обуславливания в стиле, подобном Anglican (другой язык PPL). В данном случае x является наблюдаемым значением, которое, как предполагается, было взято из распределения distr. Вероятность вычисляется с помощью logpdf(distr,y). Операторы наблюдения должны быть упорядочены так, чтобы каждый возможный запуск проходил через них в точно таком же порядке. Это эквивалентно требованию не помещать их в стохастический порядок выполнения.

Доступны следующие методы: выборка по значимости (IS), последовательное Монте-Карло (SMC), частицы Гиббса (PG), гамильтониан Монте-Карло (HMC), гамильтониан Монте-Карло с двойным средним (HMCDA) и выборка без разворота (NUTS).

Простой демонстрационный пример гауссиана

Ниже приведен простой демонстрационный пример гауссиана, иллюстрирующий базовое использование Turing.jl.

# Импортируем пакеты.
using Turing
using StatsPlots

# Определим простую нормальную модель с неизвестными средним и дисперсией.
@model function gdemo(x, y)
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))
    x ~ Normal(m, sqrt(s²))
    y ~ Normal(m, sqrt(s²))
end

Примечание. Для проверки правильности: предварительное ожидание равно mean(InverseGamma(2, 3)) = 3/(2 - 1) = 3, а предварительное ожидание m равно 0. Это легко сделать с помощью Prior:

p1 = sample(gdemo(missing, missing), Prior(), 100000)

Мы можем выполнить вывод с помощью функции sample, первым аргументом которой является вероятностная программа, а вторым — сэмплер. Более подробная информация о каждом сэмплере находится в разделе об API.

#  Запустим сэмплер, соберем результаты.
c1 = sample(gdemo(1.5, 2), SMC(), 1000)
c2 = sample(gdemo(1.5, 2), PG(10), 1000)
c3 = sample(gdemo(1.5, 2), HMC(0.1, 5), 1000)
c4 = sample(gdemo(1.5, 2), Gibbs(PG(10, :m), HMC(0.1, 5, :s²)), 1000)
c5 = sample(gdemo(1.5, 2), HMCDA(0.15, 0.65), 1000)
c6 = sample(gdemo(1.5, 2), NUTS(0.65), 1000)

Модуль MCMCChains (который повторно экспортирован в Turing) предоставляет средства построения графиков для объектов Chain, возвращаемых функцией sample. Дополнительную информацию о наборе инструментов для диагностики цепей MCMC см. в репозитории MCMCChains.

# Обобщим результаты.
describe(c3)

# Построим график результатов.
plot(c3)
savefig("gdemo-plot.png")

Для каждого сэмплера используются следующие аргументы.

  • SMC: количество частиц.

  • PG: количество частиц, количество итераций.

  • HMC: размер шага с перешагиванием, количество шагов с перешагиванием.

  • Гиббс: сэмплер компонентов 1, сэмплер компонентов 2, …​

  • HMCDA: общая длина перешагивания, целевой коэффициент принятия.

  • NUTS: количество шагов адаптации (необязательно), целевой коэффициент принятия.

Подробную информацию о сэмплерах можно найти в документации по API Turing.jl.

Объяснение синтаксиса моделирования

Используя этот синтаксис, вероятностная модель определяется на языке Turing. Функция модели, сгенерированная в Turing, может быть использована для того, чтобы обусловить модель данными. Впоследствии функцию выборки можно применять для генерации образцов из апостериорного распределения.

В следующем примере определенная модель обусловлена данными (arg1 = 1, arg2 = 2) путем передачи (1, 2) функции модели.

@model function model_name(arg_1, arg_2)
  ...
end

Затем условная модель может быть передана в функцию выборки для проведения апостериорного вывода.

model_func = model_name(1, 2)
chn = sample(model_func, HMC(..)) # Выполним вывод путем выборки с помощью HMC.

Возвращенная цепочка содержит образцы переменных в модели.

var_1 = mean(chn[:var_1]) # Возьмем среднее значение переменной с именем var_1.

Ключ (:var_1) может быть Symbol или String. Например, чтобы извлечь x[1], можно использовать chn[Symbol("x[1]")] или chn["x[1]"]. Чтобы получить все параметры, связанные с определенным символом, воспользуйтесь group. Например, если у вас есть параметры "x[1]", "x[2]" и "x[3]", вызов group(chn, :x) или group(chn, "x") вернет новую цепочку, содержащую только "x[1]", "x[2]" и "x[3]".

В Turing нет декларативной формы. В общем случае имеет значение порядок расположения строк макроса @model. Например, работает следующий пример:

# Определим простую нормальную модель с неизвестными средним и дисперсией.
@model function model_function(y)
    s ~ Poisson(1)
    y ~ Normal(s, 1)
    return y
end

sample(model_function(10), SMC(), 100)

Но если мы поменяем местами строки s ~ Poisson(1) и y ~ Normal(s, 1), модель перестанет корректно выполнять выборку:

# Определим простую нормальную модель с неизвестными средним и дисперсией.
@model function model_function(y)
    y ~ Normal(s, 1)
    s ~ Poisson(1)
    return y
end

sample(model_function(10), SMC(), 100)

Выборка нескольких цепочек

Turing поддерживает распределенную и потоковую параллельную выборку. Для этого вызовите sample(model, sampler, parallel_type, n, n_chains), где parallel_type может быть либо MCMCThreads(), либо MCMCDistributed() для потоковой и параллельной выборки соответственно.

Наличие нескольких цепочек в одном объекте полезно для оценки сходимости. Для некоторых диагностических функций, например gelmandiag, требуется несколько цепочек.

Если вам не нужен параллелизм или вы работаете с более старой версией Julia, можно сделать выборку нескольких цепочек с помощью функции mapreduce:

# Замените num_chains ниже любым количеством цепочек, выборку которых нужно сделать.
chains = mapreduce(c -> sample(model_fun, sampler, 1000), chainscat, 1:num_chains)

Теперь переменная chains содержит объект Chains, который можно индексировать по цепочке. Чтобы извлечь первую цепочку из объекта chains, используйте chains[:,:,1]. Метод тот же, если вы используете любой из приведенных ниже методов параллельной выборки.

Многопоточная выборка

Если вы хотите выполнить многопоточную выборку и используете Julia 1.3 или более позднюю версию, можно вызвать sample со следующей сигнатурой:

using Turing

@model function gdemo(x)
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))

    for i in eachindex(x)
        x[i] ~ Normal(m, sqrt(s²))
    end
end

model = gdemo([1.5, 2.0])

# Выполним выборку четырех цепочек, каждая из которых содержит 1000 образцов, с помощью нескольких потоков.
sample(model, NUTS(), MCMCThreads(), 1000, 4)

Имейте в виду, что Turing не может добавить потоки — вы сами должны запустить свой экземпляр Julia с несколькими потоками, чтобы поработать с любым видом параллелизма. Сведения о том, как это сделать, приведены в документации по Julia.

Распределенная выборка

Чтобы выполнить распределенную выборку (с использованием нескольких процессов), необходимо сначала импортировать Distributed.

Параллельная выборка процесса может быть выполнена следующим образом.

# Загрузим Distributed, чтобы добавить процессы и макрос @everywhere.
using Distributed

# Загрузим Turing.
using Turing

# Добавим четыре процесса, которые будут использоваться для выборки.
addprocs(4)

# Инициализируем все во всех процессах.
# Примечание. Это нужно сделать после загрузки Turing,
#       чтобы не пришлось предварительно компилировать каждый процесс.
#       Если этого не сделать, параллельная выборка может не сработать.
@everywhere using Turing

# Определим тип модели для всех процессов.
@everywhere @model function gdemo(x)
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))

    for i in eachindex(x)
        x[i] ~ Normal(m, sqrt(s²))
    end
end

# Объявим экземпляр модели в любом месте.
@everywhere model = gdemo([1.5, 2.0])

# Выполним выборку четырех цепочек, каждая из которых содержит 1000 образцов, с помощью нескольких потоков.
sample(model, NUTS(), MCMCDistributed(), 1000, 4)

Выборка из безусловного распределения (априорного)

Turing позволяет делать выборку из априорного распределения объявленной модели. Если вы хотите построить цепочку из априорного распределения, чтобы проверить свои априорные распределения, можно просто выполнить следующее.

chain = sample(model, Prior(), n_samples)

Вы также можете запустить свою модель (как если бы она была функцией) из априорного распределения, вызвав модель без указания входных данных или сэмплера. В приведенном ниже примере мы задаем модель gdemo, которая возвращает две переменные, x и y. Модель включает x и y в качестве аргументов, но вызов функции без передачи x или y означает, что компилятор Turing будет считать, что это отсутствующие значения, которые нужно взять из соответствующего распределения. Для получения выбранных значений x и y требуется оператор return.

@model function gdemo(x, y)
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))
    x ~ Normal(m, sqrt(s²))
    y ~ Normal(m, sqrt(s²))
    return x, y
end

Присвойте переменной функцию с отсутствующими (missing) входными данными, и Turing выполнит выборку из априорного распределения.

# Образцы из p(x,y)
g_prior_sample = gdemo(missing, missing)
g_prior_sample()

Вывод:

(0.685690547873451, -1.1972706455914328)

Выборка из условного распределения (апостериорного)

Обработка наблюдений как случайных переменных

Входные данные модели, имеющие значение missing, рассматриваются как параметры, т. е. случайные переменные, подлежащие оценке или выборке. Это может быть полезно, если нужно смоделировать выборы для этого параметра или вы делаете выборку из условного распределения. Turing поддерживает следующий синтаксис:

@model function gdemo(x, ::Type{T} = Float64) where {T}
    if x === missing
        # Инициализируем `x`, если отсутствует
        x = Vector{T}(undef, 2)
    end
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))
    for i in eachindex(x)
        x[i] ~ Normal(m, sqrt(s²))
    end
end

# Создадим модель при x = missing
model = gdemo(missing)
c = sample(model, HMC(0.01, 5), 500)

Обратите внимание на необходимость инициализации x, когда это значение отсутствует, поскольку в дальнейшем в модели мы будем выполнять итерации по его элементам. Сгенерированные значения для x можно извлечь из объекта Chains с помощью c[:x].

Turing также поддерживает смешанные значения missing и не-missing в x, где отсутствующие значения будут рассматриваться как случайные переменные, подлежащие выборке, в то время как остальные будут рассматриваться как наблюдения. Например:

@model function gdemo(x)
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))
    for i in eachindex(x)
        x[i] ~ Normal(m, sqrt(s²))
    end
end

# x[1] является параметром, но x[2] является наблюдением
model = gdemo([missing, 2.4])
c = sample(model, HMC(0.01, 5), 500)

Значения по умолчанию

Аргументы моделей Turing могут иметь значения по умолчанию, подобно тому, как значения по умолчанию работают в обычных функциях Julia. Например, в следующем примере missing присваивается x и рассматривается как случайная переменная. Если значение по умолчанию не отсутствует (missing), x будет присвоено это значение, и оно будет считаться наблюдением.

using Turing

@model function generative(x = missing, ::Type{T} = Float64) where {T <: Real}
    if x === missing
        # Инициализируем x, если отсутствует
        x = Vector{T}(undef, 10)
    end
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))
    for i in 1:length(x)
        x[i] ~ Normal(m, sqrt(s²))
    end
    return s², m
end

m = generative()
chain = sample(m, HMC(0.01, 5), 1000)

Доступ к значениям внутри цепочки

Получить доступ к значениям внутри цепочки можно несколькими способами.

  1. Преобразовать их в объект DataFrame.

  2. Использовать их необработанную форму AxisArray.

  3. Создать трехмерный объект Array.

Например, пусть c будет Chain.

  1. DataFrame(c) преобразует c в DataFrame,

  2. c.value получает значения внутри c в виде AxisArray,

  3. c.value.data получает значения внутри c в виде трехмерного массива (Array).

Типы переменных и параметры типов

Тип элемента вектора (или матрицы) случайных величин должен соответствовать eltype его априорного распределения, <: Integer для дискретных распределений и <: AbstractFloat для непрерывных распределений. Более того, если непрерывная случайная величина должна быть отобрана с помощью гамильтонова сэмплера, тип элемента вектора должен быть либо: 1. Real, чтобы включать автоматическое дифференцирование через модель, которая использует специальные числовые типы, являющиеся подтипами Real, либо 2. Неким параметром типа T, определенным в заголовке модели с использованием синтаксиса параметра типа, например function gdemo(x, ::Type{T} = Float64) where {T}. Аналогично, при использовании сэмплера частиц переменная Julia должна быть либо: 1. Array, либо 2. Экземпляром некоторого параметра типа T, определенного в заголовке модели с использованием синтаксиса параметра типа, например function gdemo(x, ::Type{T} = Vector{Float64}) where {T}.

Запрос вероятностей из модели или цепочки

Сначала рассмотрим следующую упрощенную модель gdemo.

@model function gdemo0(x)
    s ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s))
    x ~ Normal(m, sqrt(s))
end

# Создадим экземпляры трех моделей с разными значениями x
model1 = gdemo0(1)
model4 = gdemo0(4)
model10 = gdemo0(10)

Теперь выполните запрос к этим моделям: вычислим правдоподобие x = 1.0 при значениях s = 1.0 и m = 1.0 для параметров:

prob"x = 1.0 | model = model1, s = 1.0, m = 1.0"
prob"x = 1.0 | model = model4, s = 1.0, m = 1.0"
prob"x = 1.0 | model = model10, s = 1.0, m = 1.0"

Обратите внимание, что даже если мы используем три модели, инстанцированные тремя различными значениями x, мы должны получить то же правдоподобие. Мы можем легко проверить это значение:

pdf(Normal(1.0, 1.0), 1.0)

Теперь рассмотрим следующую модель gdemo:

@model function gdemo(x, y)
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))
    x ~ Normal(m, sqrt(s²))
    y ~ Normal(m, sqrt(s²))
end

# Создадим экземпляр модели.
model = gdemo(2.0, 4.0)

Ниже приведены примеры допустимых запросов модели или цепочки Turing:

  • prob"x = 1.0, y = 1.0 | model = model, s = 1.0, m = 1.0" вычисляет правдоподобие x = 1 и y = 1 с заданными s = 1 и m = 1.

  • prob"s² = 1.0, m = 1.0 | model = model, x = nothing, y = nothing" вычисляет совместную вероятность s = 1 и m = 1, игнорируя x и y. x и y игнорируются, поэтому их можно по желанию исключить из RHS |, но рекомендуется их определить.

  • prob"s² = 1.0, m = 1.0, x = 1.0 | model = model, y = nothing" вычисляет совместную вероятность s = 1, m = 1 и x = 1, игнорируя y.

  • prob"s² = 1.0, m = 1.0, x = 1.0, y = 1.0 | model = model" вычисляет совместную вероятность всех переменных.

  • После выборки MCMC при заданном chain prob"x = 1.0, y = 1.0 | chain = chain, model = model" вычисляет поэлементное правдоподобие x = 1.0 и y = 1.0 для каждого образца в chain.

  • Если во время выборки использовалось save_state=true (т. е. sample(model, sampler, N; save_state=true)), можно просто выполнить prob"x = 1.0, y = 1.0 | chain = chain".

Во всех вышеперечисленных случаях для вычисления логарифмических вероятностей вместо prob можно использовать logprob.

Оценки максимального правдоподобия и максимального апостериорного значения

Turing поддерживает два метода оценки режимов — оценку максимального правдоподобия (MLE) и оценку максимального апостериорного значения (MAP). Оптимизацию выполняет пакет Optim.jl. Оценка режимов в настоящее время является необязательным инструментом и будет недоступна, если вы не установили Optim вручную и не загрузили пакет с помощью оператора using. Чтобы установить Optim, выполните import Pkg; Pkg.add("Optim").

Оценка режимов работает только в том случае, если все параметры модели непрерывны — дискретные параметры пока невозможно оценить с помощью MLE/MAP.

Чтобы понять, как работает оценка режимов, сначала загрузим Turing и Optim, чтобы включить оценку режимов, а затем объявим модель:

# Обратите внимание, что для работы оценки режима требуется явная загрузка Optim,
# так как Turing не загружает набор оптимизации, если не загружен пакет Optim.
using Turing
using Optim

@model function gdemo(x)
    s² ~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))

    for i in eachindex(x)
        x[i] ~ Normal(m, sqrt(s²))
    end
end

После определения модели можно построить экземпляр модели обычным способом:

# Создадим данные для передачи модели.
data = [1.5, 2.0]

# Создадим экземпляр модели gdemo с нашими данными.
model = gdemo(data)

Как правило, на этом этапе оценка режима выполняется быстро и легко. Turing расширяет функцию Optim.optimize и принимает структуры MLE() или MAP(), которые сообщают Turing, какую оценку предоставить, — MLE или MAP, соответственно. По умолчанию используется оптимизатор LBFGS, но эту настройку можно изменить. Схема базового использования выглядит так:

# Создание оценки MLE.
mle_estimate = optimize(model, MLE())

# Создание оценки MAP.
map_estimate = optimize(model, MAP())

Если вы хотите перейти на другой оптимизатор, например NelderMead, просто поместите свой оптимизатор в слот третьего аргумента:

# Использование NelderMead
mle_estimate = optimize(model, MLE(), NelderMead())

# Использование SimulatedAnnealing
mle_estimate = optimize(model, MLE(), SimulatedAnnealing())

# Использование ParticleSwarm
mle_estimate = optimize(model, MLE(), ParticleSwarm())

# Использование Newton
mle_estimate = optimize(model, MLE(), Newton())

# Использование AcceleratedGradientDescent
mle_estimate = optimize(model, MLE(), AcceleratedGradientDescent())

В некоторых методах могут возникнуть проблемы с вычислением режима из-за недостаточного количества итераций или из-за того, что целевая функция перемещалась вверх между вызовами функции. Turing предупредит вас, если Optim не удалось выполнить схождение с помощью Optim.converge. Типичным решением этой проблемы может быть добавление большего числа итераций или разрешение оптимизатору увеличиваться между итерациями функции:

# Увеличим количество итераций и разрешим функции eval увеличиваться между вызовами.
mle_estimate = optimize(model, MLE(), Newton(), Optim.Options(iterations=10_000, allow_f_increases=true))

Другие параметры для Optim доступны здесь.

Анализ оценки режима

Turing расширяет несколько методов из StatsBase, которые можно использовать для анализа результатов оценки режима. Помимо прочих, в число реализованных методов входят vcov, informationmatrix, coeftable, params и coef.

Давайте рассмотрим нашу оценку ML, полученную выше, используя coeftable:

# Импортируем StatsBase, чтобы использовать его статистические методы.
using StatsBase

# Выведем таблицу коэффициентов.
coeftable(mle_estimate)
─────────────────────────────
   estimate  stderror   tstat
─────────────────────────────
s    0.0625  0.0625    1.0
m    1.75    0.176777  9.8995
─────────────────────────────

Стандартные погрешности рассчитываются по информационной матрице Фишера (обратный гессиан логарифма вероятности или логарифма совместности). t-статистика будет знакома статистикам-фреквентистам. Предупреждение. Стандартные погрешности, рассчитанные таким образом, не всегда подходят для оценок MAP, поэтому будьте осторожны при их интерпретации.

Выборка с MAP/MLE в качестве начальных состояний

Можно начать выборку цепочки из оценки MLE/MAP, извлекая вектор значений параметров и предоставляя его функции sample с помощью ключевого слова init_params. Например, вот как сделать выборку из полного апостериорного значения, используя оценку MAP в качестве отправной точки:

# Создание оценки MAP.
map_estimate = optimize(model, MAP())

# Выборка с использованием оценки MAP в качестве отправной точки.
chain = sample(model, NUTS(), 1_000, init_params = map_estimate.values.array)

Дополнительные сведения

Композиционная выборка с использованием интерфейса Гиббса

Turing.jl предоставляет интерфейс Гиббса для объединения различных сэмплеров. Например, можно объединить сэмплер HMC с сэмплером PG, чтобы выполнить вывод для разных параметров в одной модели, как показано ниже.

@model function simple_choice(xs)
    p ~ Beta(2, 2)
    z ~ Bernoulli(p)
    for i in 1:length(xs)
        if z == 1
            xs[i] ~ Normal(0, 1)
        else
            xs[i] ~ Normal(2, 1)
        end
    end
end

simple_choice_f = simple_choice([1.5, 2.0, 0.3])

chn = sample(simple_choice_f, Gibbs(HMC(0.2, 3, :p), PG(20, :z)), 1000)

Сэмплер Gibbs можно использовать для задания уникальных бэкендов автоматического дифференцирования для различных пространств переменных. Дополнительные сведения см. в статье об автоматическом дифференцировании.

Более подробную информацию о композиционной выборке в Turing.jl можно найти в соответствующей статье.

Работа с filldist и arraydist

Turing предоставляет filldist(dist::Distribution, n::Int) и arraydist(dists::AbstractVector{<:Distribution}) в качестве упрощенного интерфейса для построения произведения распределений, например для моделирования набора переменных, которые имеют одинаковую структуру, но различаются по группам.

Построение произведения распределений с помощью filldist

Функция filldist предоставляет общий интерфейс для построения произведения распределений по распределениям того же типа и параметризации. Обратите внимание, что, в отличие от интерфейса произведения распределений, предоставляемого Distributions.jl (Product), filldist поддерживает произведение распределений по одномерным или многомерным распределениям.

Пример использования:

@model function demo(x, g)
  k = length(unique(g))
  a ~ filldist(Exponential(), k) # = Product(fill(Exponential(), k))
  mu = a[g]
  x .~ Normal.(mu)
end

Построение произведения распределений с помощью arraydist

Функция arraydist предоставляет общий интерфейс для построения произведения распределений по распределениям разного типа и параметризации. Обратите внимание, что, в отличие от интерфейса произведения распределений, предоставляемого Distributions.jl (Product), arraydist поддерживает произведение распределений по одномерным или многомерным распределениям.

Пример использования:

@model function demo(x, g)
  k = length(unique(g))
  a ~ arraydist([Exponential(i) for i in 1:k])
  mu = a[g]
  x .~ Normal.(mu)
end

Работа с MCMCChains.jl

Turing.jl заключает свои примеры в оболочку, используя MCMCChains.Chain, так что все функции, работающие для MCMCChains.Chain, могут быть многократно использованы в Turing.jl. Двумя типичными функциями являются MCMCChains.describe и MCMCChains.plot. Их можно использовать для полученной цепочки chn так, как показано ниже. Дополнительные сведения о MCMCChains см. в репозитории GitHub.

describe(chn) # Приводит статистику по образцам.
plot(chn) # Строит график статистики по образцам.

В дополнение к describe и plot в пакете MCMCChains есть множество функций, например используемых для диагностики сходимости. Дополнительные сведения о пакете см. в репозитории GitHub.

Изменение параметров по умолчанию

Некоторые заданные по умолчанию параметры Turing.jl можно изменить для более эффективного использования.

Размер фрагмента AD

ForwardDiff (бэкенд AD по умолчанию в Turing) использует прямой режим работы AD с фрагментами. Размер фрагмента можно задать вручную с помощью setchunksize(new_chunk_size).

Бэкенд AD

Turing поддерживает четыре пакета автоматического дифференцирования (AD) в бэкенде во время выборки. Бэкендом AD по умолчанию является ForwardDiff для AD с прямым режимом. Также поддерживаются три бэкенда AD с обратным режимом, а именно Tracker, Zygote и ReverseDiff. Zygote и ReverseDiff поддерживаются при необходимости, если они явным образом загружены пользователем с помощью using Zygote или using ReverseDiff рядом с using Turing.

Дополнительные сведения о бэкенде автоматического дифференцирования в Turing см. в статье об автоматическом дифференцировании.

Ведение журнала хода выполнения

Для регистрации хода выполнения выборки Turing.jl использует ProgressLogging.jl. Ведение журнала хода выполнения включено по умолчанию, но может замедлить вывод. Его можно включить или отключить, задав именованному аргументу progress выборки (sample) значение true или false, соответственно. Кроме того, вы можете включить или отключить ведение журнала хода выполнения глобально, вызвав setprogress!(true) или setprogress!(false), соответственно.

Turing использует эвристику для выбора подходящего бэкенда визуализации. Если вы используете Juno, ход выполнения отображается с помощью индикатора хода выполнения в окне Atom. Для записных книжек Jupyter бэкендом по умолчанию является ConsoleProgressMonitor.jl. Во всех остальных случаях журналы хода выполнения отображаются с помощью TerminalLoggers.jl. Или же если вы предоставите собственный бэкенд визуализации, Turing будет использовать его вместо бэкенда по умолчанию.