Sandwood — это язык, компилятор и среда выполнения вероятностных моделей на основе JVM. Он предназначен для написания моделей на языке, знакомом разработчикам Java. Получающиеся модели принимают форму объектов Java, что позволяет им быть хорошо абстрагированными компонентами охватывающей системы.
В традиционной байесовской модели пользователь должен спроектировать модель, а затем реализовать код вывода для любой операции, которую он хочет выполнить с моделью. Это создает ряд проблем:
Создание кода вывода технически сложно и требует много времени. Этот шаг дает возможность внести незначительные ошибки.
Если модель будет изменена, то код вывода придется обновить. Это также трудоемко и технически сложно, что приводит к следующим проблемам:
Это действует как сдерживающий фактор для модификации моделей.
Различные операции вывода могут выходить из строя, поэтому некоторые работают над старой моделью, а некоторые — над новой.
Это предоставляет еще одну возможность для ошибок в алгоритме вывода, поскольку пользователи пытаются внести незначительные изменения в существующий код.
Вероятностное программирование решает эти проблемы, позволяя описывать модели с использованием либо API, либо предметно-ориентированного языка (DSL), как в случае с Sandwood. Sandwood DSL компилируется для создания классов Java, которые представляют модель и реализуют все необходимые операции вывода. Это имеет ряд преимуществ:
Sandwood состоит из 3 компонентов каждый в соответствующем каталоге:
Каждая часть зависит от предыдущих частей. Каждый каталог компонента содержит файл Maven POM для создания компонента. Для компилятора и плагина их необходимо будет вызвать с помощью install
, чтобы сделать их доступными для более поздних этапов, т.е. mvn clean install
. Примеры следует собирать только как mvn clean package
.
После установки Sandwood на данный момент есть 3 способа компиляции модели:
Чтобы использовать Sandwood из командной строки после создания компилятора и среды выполнения, сценарии командной строки, имеющие функциональность, аналогичную javac
можно найти в commandline/SandwoodC/bin
. Чтобы использовать это, пользователь обычно добавляет к пути каталог bin, а затем вызывает sandwoodc.sh HMM.sandwood для компиляции модели HMM. sandwoodc.sh -h
или sandwoodc.bat -h
приведет к распечатке описания использования и доступных опций.
Все функциональные возможности SandwoodC можно реализовать, вызвав метод compile
в org.sandwood.compilation.SandwoodC
и передав массив, содержащий аргументы, которые были бы переданы в командную строку.
Плагин Maven можно использовать для автоматического запуска компиляции файлов Sandwood при сборке зависимого проекта. Чтобы использовать плагин, вам необходимо добавить среду выполнения Sandwood в качестве зависимости и добавить плагин в сборку. Это достигается за счет следующих дополнений в файл POM:
<dependencies>
<dependency>
<groupId>org.sandwood</groupId>
<artifactId>sandwood-runtime</artifactId>
<version>0.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.sandwood</groupId>
<artifactId>sandwoodc-maven-plugin</artifactId>
<version>0.3-SNAPSHOT</version>
<executions>
<execution>
<configuration>
<partialInferenceWarning>true</partialInferenceWarning>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
</configuration>
<goals>
<goal>sandwoodc</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>`
Включение элемента <sourceDirectory>${basedir}/src/main/java</sourceDirectory>
указывает плагину, в каком каталоге искать модели. Другие полезные флаги включают в себя:
debug
Эта опция используется для получения отладочной информации от SandwoodC. Установка для этой опции значения true
приводит к тому, что Sandwood генерирует след своих действий. Значение по умолчанию — false
. Обратите внимание, что этот флаг предназначен для отладки ошибок в конфигурации/компиляторе компилятора, а не в компилируемой модели. Ошибки и предупреждения в файлах модели Sandwood всегда будут возвращаться компилятором.
partialInferenceWarning
Эта опция используется для предотвращения сбоя SandwoodC, когда некоторые шаги вывода не могут быть построены. Установка для этой опции значения true
приводит к тому, что Sandwood просто генерирует предупреждения о пропущенных шагах. Значение по умолчанию — false
.
sourceDirectory
Этот параметр определяет, в каком каталоге искать файлы модели. В этом каталоге модели могут находиться в разных пакетах.
outputDirectory
Этот параметр определяет, в какой каталог должен быть помещен исходный код Java для моделей. Значение по умолчанию — ${project.build.directory}/generated-sources/sandwood
.
calculateIndividualProbabilities
Этот параметр указывает, следует ли рассчитывать вероятности для каждой случайной величины, созданной в цикле, а не для одного значения для всех экземпляров. Значение по умолчанию — false
.
javadoc
Этот параметр указывает компилятору создать JavaDoc, дополняющий модель. Значение по умолчанию — false
.
javadocDirectory
Этот параметр указывает местоположение, в котором должен быть размещен сгенерированный файл.
executable
Этот параметр позволяет указать альтернативную JVM для запуска компилятора Sandwood.
Далее следует введение в то, как писать модели Sandwood и как использовать полученные классы, реализующие эти модели.
Схема шагов, которые проходит модель, можно увидеть на этой диаграмме. Модели начинаются с файла .sandwood
, который компилируется в набор файлов классов. Их можно создавать несколько раз для создания нескольких экземпляров модели с разными конфигурациями.
В качестве рабочего примера мы будем использовать скрытую марковскую модель (HMM). Эта модель написана здесь, в Сэндвуде. Эту модель следует сохранить в файле с именем HMM.sandwood
в каталоге пакета org/sandwood/examples/hmm
. Более полное описание языка можно найти здесь.
package org . sandwood . examples . hmm ;
model HMM ( int [] eventsMeasured , int numStates , int numEvents ) {
//Construct a transition matrix m.
double [] v = new double [ numStates ] <~ 0.1 ;
double [][] m = dirichlet ( v ). sample ( numStates );
//Construct weighting for which state to start in.
double [] initialState = new Dirichlet ( v ). sample ();
//Construct weighting for each event in each state.
double [] w = new double [ numEvents ] <~ 0.1 ;
double [][] bias = dirichlet ( w ). sample ( numStates );
//Allocate space to record the sequence of states.
int sequenceLength = eventsMeasured . length ;
int [] st = new int [ sequenceLength ];
//Calculate the movements between states.
st [ 0 ] = categorical ( initialState ). sampleDistribution ();
for ( int i : [ 1. . sequenceLength ) )
st [ i ] = categorical ( m [ st [ i - 1 ]]). sampleDistribution ();
//Emit the events for each state.
int [] events = new int [ sequenceLength ];
for ( int j = 0 ; j < sequenceLength ; j ++)
events [ j ] = new Categorical ( bias [ st [ j ]]). sample ();
//Assert that the events match the eventsMeasured data.
events . observe ( eventsMeasured );
}
В дополнение к документации по языку Sandwood и комментариям JavaDoc, которые можно создать для модели, в каталоге примеров Sandwood имеется ряд примеров, и мы предлагаем новым пользователям начать с их изучения и изменения.
Описание языка, используемого для описания моделей Sandwood, можно найти здесь. Язык создан с намерением быть знакомым разработчикам Java, но не содержит возможности создавать объекты. В будущем мы планируем добавить поддержку типов записей, чтобы упростить импорт и экспорт данных в модели и из них.
Когда модель компилируется, в том же пакете, в котором определена модель, создается несколько файлов классов. Один из этих классов будет иметь то же имя, что и имя, предоставленное модели, поэтому в данном случае HMM.class , а это — это класс, экземпляр которого пользователь должен создать, чтобы иметь экземпляр модели. Каждая общедоступная переменная в модели соответствует полю в сгенерированном классе. Пример HMM можно увидеть ниже.
При запуске компилятора с установленным флагом javadoc
JavaDoc будет создан для каждого общедоступного метода и класса в сгенерированном файле модели.
После того как модель скомпилирована, нам нужно создать ее экземпляры. Эти экземпляры независимы, и пользователь может создавать столько разных копий модели, сколько пожелает.
Экземпляры объекта модели создаются с помощью конструктора класса. Как описано ранее, для модели обычно имеется три конструктора. Единственный случай, когда их будет меньше, — это когда разные варианты конструктора сопоставляются с одной и той же сигнатурой, и в этом случае один конструктор будет применяться к более чем одному из этих сценариев.
Полный конструктор. Этот конструктор принимает все аргументы, указанные в сигнатуре модели, и устанавливает их. Этот конструктор используется для операций вывода значений и вероятностей.
Пустой конструктор. Этот конструктор не принимает аргументов, оставляя параметры для установки пользователем позже.
Конструктор выполнения. Этот конструктор удаляет только наблюдаемые аргументы, а для наблюдаемых аргументов, размеры которых используются в качестве входных данных для кода, принимает эти измерения вместо полных параметров. Таким образом, в примере HMM параметр eventMeasured станет целым числом, описывающим длину последовательности.
Эти примеры кода демонстрируют, как выполнять вызовы скомпилированных моделей.
Взаимодействие с моделью через объект модели принимает две формы:
Вызовы методов объекта модели для глобальных операций, таких как установка политик хранения по умолчанию, проверка готовности модели к выводу, запуск шагов вывода и т. д.
Вызовы объектов параметров модели. Каждая именованная общедоступная переменная в модели представлена соответствующим полем в объекте модели. Переменные являются общедоступными, если они объявлены во внешней области модели и не помечены как private
, или объявлены во внутренней области и не помечены как public
. Если поле объявлено общедоступным во внутренней итеративной области, например в теле цикла for, значение каждой итерации будет сохранено.
Тип объекта будет зависеть от переменной. Их можно разделить на 3 категории:
Каждое из этих полей ссылается на объект с набором методов, которые позволяют пользователю устанавливать и считывать значения и свойства параметра. Свойства, которые можно устанавливать и читать, включают вероятность параметра, политику хранения параметра и необходимость фиксации текущего значения параметра.
Некоторые из наиболее важных методов объекта параметра при выполнении вывода модели:
getSamples для возврата выборочных значений.
getMAP, чтобы вернуть максимальное значение A Posteriori.
setValue, чтобы разрешить установку определенного значения.
setFixed, который принимает boolean
чтобы отметить значение как фиксированное и, следовательно, не обновляемое во время вывода. Важно установить значение параметра перед его фиксацией.
getLogProbability, который получает логарифмическую вероятность переменной после определения вероятностей.
Есть и другие методы, и мы рекомендуем обратиться к JavaDoc, чтобы ознакомиться с ними.
Над моделью можно выполнять 3 основных типа операций:
setRentionPolicy
в классе модели. При необходимости для отдельных переменных можно установить политику хранения путем вызова соответствующего метода setRetentionPolicy
в каждом объекте переменной.Существует 3 политики выборки:
NONE не записывает никаких значений. Это особенно полезно, если одна из переменных велика, поэтому тратить время и пространство на ее хранение было бы расточительно.
SAMPLE записывает значение каждой итерации алгоритма вывода, поэтому, если выполняется 1000 итераций, из каждой переменной, установленной для этой политики хранения, будет выбрано 1000 значений. Это полезно для расчета дисперсии, а также среднего значения. Однако в этом есть недостаток: если позиции значений в модели могут перемещаться во время вывода, тогда значения не могут быть усреднены. Например, в модели темы темы 2 и 3 могут поменяться местами во время вывода, поэтому усреднение всех значений для темы 2 дает смесь темы 2 и темы 3. Для преодоления этого максимума A Posteriori (MAP) также предоставляется как политика хранения.
MAP или Maximum A Posteriori (MAP) записывает значения переменных, когда модель находится в наиболее вероятном состоянии. Это решает проблему с временными позициями значений, что означает, что значения не могут быть усреднены, но за счет возможности вычисления границ. Этот вариант также имеет преимущества в пространстве, если некоторые переменные большие.
Конфигурация: дополнительные вызовы методов объекта модели позволяют пользователю задавать такие свойства, как выгорание и прореживание, при выполнении этого шага вывода. Бернин игнорирует значения первых n итераций, позволяя модели отойти от начальной точки с низкой вероятностью перед началом выборки. Прореживание уменьшает автокорреляцию, вызванную процедурой MCMC, поскольку учитываются только значения каждой n -й итерации.
Вывод вероятностей. Установив значения некоторых или всех параметров модели, рассчитайте вероятность генерации этих значений. Это можно рассчитать для каждой переменной в модели и для модели в целом.
Выполнение модели Запустите модель, как если бы это был обычный код, генерирующий новые значения для любых параметров, которые не зафиксированы пользователем. Примером использования такого поведения является модель линейной регрессии. В этом случае коэффициенты модели сначала будут выведены с использованием обучающих данных. Как только они будут сделаны, они будут исправлены и добавлен новый набор входных данных. Затем модель будет выполнена для генерации соответствующих прогнозов для этих новых входных данных. Эту форму выполнения также можно использовать для генерации репрезентативных синтетических данных из обученной модели.
Построение и обучение модели
//Load inputs
int nStates = 25 ;
int [] actions = loadActions (....);
int nActions = maxActions (....);
//Construct the model
HMM model = new HMM ( actions , nActions , nStates );
//Set the retention policies
model . setDefaultRetentionPolicy ( RetentionPolicy . MAP );
model . st . setRetentionPolicy ( RetentionPolicy . NONE );
//Pick a random number generator. The ones introduced in Java 17 are faster and better quality.
model . setRNGType ( RandomType . L64X1024MixRandom );
//Instruct the model to use the ForkJoin framework for parallel execution.
model . setExecutionTarget ( ExecutionTarget . forkJoin );
//Run 2000 inference steps to infer model values
model . inferValues ( 2000 );
//Gather the results.
double [] initialState = model . initialState . getMAP ();
double [][] bias = model . bias . getMAP ();
double [][] transitions = model . m . getMAP ();
Построить модель и вывести вероятности
//Load inputs
int nStates = 25 ;
int [] actions = loadActions (....);
int nActions = maxActions (....);
//Load model parameters
double [][] bias = model . bias . getMAP ();
double [][] transitions = model . m . getMAP ();
//Construct the model
HMM model = new HMM ( actions , nActions , nStates );
//Set and fix trained values
model . bias . setValue ( bias );
Model . m . setValue ( transitions );
//Run 2000 inference steps to infer probabilities
model . inferProbabilities ( 2000 );
//Recover the probabilities of the model parameter actions.
double actionsProbability = model . actions . getProbability ();
//Recover the probability of the model as a whole
double modelProbability = model . getProbability ()
Чтобы получить помощь по Sandwood, начните обсуждение или присоединитесь к нему на странице обсуждений.
Этот проект приветствует вклад сообщества. Прежде чем отправлять запрос на включение, ознакомьтесь с нашим руководством по вкладу.
Пожалуйста, ознакомьтесь с руководством по безопасности для нашего ответственного процесса раскрытия уязвимостей безопасности.
Авторские права (c) 2019–2024 гг. принадлежат Oracle и/или ее дочерним компаниям.
Выпущено на условиях универсальной разрешающей лицензии версии 1.0, как показано на https://oss.oracle.com/licenses/upl/.