Sandwood 是一種基於 JVM 的機率模型的語言、編譯器和執行時間。它旨在允許使用 Java 開發人員熟悉的語言編寫模型。產生的模型採用 Java 物件的形式,使它們成為周圍系統的良好抽象組件。
使用傳統的貝葉斯模型,使用者必須設計模型,然後為他們希望在模型上執行的任何操作實現推理程式碼。這會產生許多問題:
建立推理程式碼在技術上具有挑戰性,而且非常耗時。此步驟提供了引入細微錯誤的機會。
如果模型被修改,那麼推理程式碼也必須更新。這也很耗時且技術上具有挑戰性,導致以下問題:
它可以阻止修改模型。
不同的推理操作可能會不一致,因此一些工作針對舊模型,一些工作針對新模型。
當用戶嘗試對現有程式碼進行細微調整時,它為錯誤進入推理演算法提供了另一個機會。
機率編程透過允許使用 API 或領域特定語言 (DSL) 來描述模型(如 Sandwood 的情況)來克服這些問題。 Sandwood DSL 被編譯為產生代表模型並實作所有所需推理操作的 Java 類別。這有很多優點:
Sandwood 由 3 個組件組成,每個組件都在其對應的目錄中:
每個部分都依賴前面的部分。每個元件目錄都包含一個用於建置元件的 Maven POM 檔案。對於編譯器和插件,需要使用install
來呼叫它們,以使它們可用於後續階段,即mvn clean install
。這些範例只能建置為mvn clean package
。
安裝 Sandwood 後,目前有 3 種編譯模型的方法:
建置編譯器和執行時間後,要從命令列使用 Sandwood,可以在commandline/SandwoodC/bin
中找到與javac
具有類似功能的命令列腳本。要使用此功能,使用者通常會將 bin 目錄新增至路徑中,然後呼叫 sandwoodc.sh HMM.sandwood 來編譯 HMM 模型。 sandwoodc.sh -h
或sandwoodc.bat -h
將列印出用法和可用選項的描述。
SandwoodC 的所有功能都可以透過呼叫org.sandwood.compilation.SandwoodC
中的compile
方法並傳遞一個包含已傳遞到命令列的參數的陣列來實現。
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)。模型是用 Sandwood 編寫的。此模型應保存在套件目錄org/sandwood/examples/hmm
中名為HMM.sandwood
的檔案中。可以在此處找到該語言的更完整描述。
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 Examples 目錄中還有許多範例,我們建議新使用者先檢查和修改這些範例。
用於描述 Sandwood 模型的語言的描述可以在此處找到。該語言的建構目的是讓 Java 開發人員熟悉,但不包含建構物件的能力。我們計劃將來添加對記錄類型的支持,以使模型中的資料匯入和匯出更加簡單。
編譯模型時,會在定義模型的相同套件中產生許多類別檔案。模型實例的類別。模型中的每個公開可見的變數對應於生成的類別中的一個欄位。範例 HMM 如下圖所示。
透過執行帶有javadoc
標誌集的編譯器,將為生成的模型檔案中的每個公共方法和類別建立 JavaDoc。
模型編譯完成後,我們需要實例化它的實例。這些實例是獨立的,使用者可以根據需要建立任意多個不同的模型副本。
模型物件的實例是透過類別建構函式建構的。如前所述,模型通常有 3 個建構函數。唯一會減少的情況是建構函數的不同變體映射到相同的簽名,在這種情況下,一個建構函數將適用於多個場景。
完整建構函式 - 此建構函式取得模型簽章中出現的所有參數並設定它們。此構造函數用於推斷值和推斷機率操作。
空建構函式 - 此建構函式不帶任何參數,將參數留給使用者稍後設定。
執行建構函式 - 此建構函式刪除僅觀察到的參數,並且對於其維度用作程式碼輸入的觀察到的參數,採用這些維度而不是完整參數。因此,在 HMM 範例中,eventsMeasured 參數將變成描述序列長度的整數。
這些程式碼範例示範如何呼叫已編譯的模型。
透過模型物件與模型的交互作用有兩種形式:
呼叫模型物件方法進行全域操作,例如設定預設保留策略、檢查模型是否準備好進行推理以及啟動推理步驟等。
呼叫模型參數物件。模型中的每個命名公共變數都由模型物件中的對應欄位表示。如果變數在模型的最外層作用域中宣告且未標記為private
,或在內部作用域中宣告且未標記為public
,則變數是公共的。如果某個欄位在內部迭代作用域中宣告為公共,例如 for 迴圈體,則會儲存每次迭代的值。
物件的類型取決於變數。這些可以分為 3 類:
這些欄位中的每一個都引用一個具有一組方法的對象,這些方法允許使用者從參數中設定和讀取值和屬性。可以設定和讀取的屬性包括參數的機率、參數的保留策略以及參數是否應固定為其目前值。
參數物件在進行模型推理時比較重要的一些方法有:
getSamples 傳回採樣值。
getMAP 傳回最大後驗值。
setValue 允許將值設為特定值。
setFixed 接受一個boolean
來將值標記為固定,因此在推理過程中不會更新。在修復參數之前設定參數值非常重要。
getLogProbability 在推斷機率後取得變數的對數機率。
還有更多方法,我們建議您查閱 JavaDoc 來熟悉它們。
可以在模型上執行 3 種基本類型的操作:
setRentionPolicy
方法來為整個模型設定保留策略。然後,可以選擇透過呼叫每個變數物件中對應的setRetentionPolicy
方法來設定各個變數的保留策略。抽樣政策有3種:
NONE不記錄值。如果其中一個變數很大,那麼花費時間和空間來儲存它會很浪費,這一點特別有用。
SAMPLE記錄推理演算法每次迭代的值,因此,如果執行 1000 次迭代,將從為此保留策略設定的每個變數中採樣 1000 個值。這對於計算方差和平均值很有用。但這有一個弱點,如果模型內值的位置在推理過程中可以移動,則無法對這些值進行平均。例如,對於主題模型,主題2 和3 可能在推理過程中交換位置,因此對主題2 的所有值進行平均,生成主題2 和主題3 的混合。 MAP)保留政策。
MAP或最大後驗機率 (MAP) 記錄模型處於最可能狀態時的變數值。這克服了瞬態值位置的問題,這意味著值無法被平均,但代價是無法計算邊界。如果某些變數很大,此選項還具有空間優勢。
配置:模型物件上的其他方法呼叫允許使用者在執行此推理步驟時設定屬性,例如老化和細化。 Burnin 忽略前n次迭代的值,讓模型在開始取樣之前遠離低機率起點。細化透過僅考慮每n次迭代的值來減少 MCMC 過程引起的自相關。
推斷機率設定模型中部分或所有參數的值後,即可計算產生這些值的機率。這可以針對模型中的每個變數以及整個模型進行計算。
執行模型運行模型,就好像它是為使用者未修復的任何參數產生新值的常規程式碼。何時使用此行為的一個範例是線性迴歸模型。在這種情況下,首先使用訓練資料推斷模型係數。一旦它們被推斷出來,它們將被固定並成為新的輸入資料集。然後執行該模型以產生該新輸入資料的相應預測。這種執行形式也可用於從經過訓練的模型產生代表性合成資料。
建構和訓練模型
//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 和/或其附屬公司。
根據通用授權許可證 v1.0 發布,如 https://oss.oracle.com/licenses/upl/ 所示。