概率軟邏輯(PSL,Probabilistic soft logic)示例演練和模塊解釋
這裏將引導您完成Simple Acquaintances示例的Groovy版本。
一、建立示例項目
??首先,確保您的系統滿足先決條件。然後克隆psl-examples存儲庫:
git clone https://github.com/linqs/psl-examples.git
二、運行
??進入簡單groovy示例的根目錄:
cd psl-examples/simple-acquaintances/groovy
??在根目錄下運行(這裏特別容易失敗):
mvn compile
mvn eclipse:eclipse //把項目編譯成eclipse版本
??通過eclipse裏的maven導入項目後,無報錯即可運行,結果如下:
0 [main] INFO org.linqs.psl.examples.simpleacquaintances.Run - Defining model rules
139 [main] INFO org.linqs.psl.examples.simpleacquaintances.Run - Loading data into database
171 [main] INFO org.linqs.psl.examples.simpleacquaintances.Run - Starting inference
389 [main] INFO org.linqs.psl.application.inference.LazyMPEInference - Beginning inference round 1.
488 [main] INFO org.linqs.psl.reasoner.admm.ADMMReasoner - Optimization completed in 411 iterations. Primal res.: 0.028260373, Dual res.: 9.052008E-4
488 [main] INFO org.linqs.psl.application.inference.LazyMPEInference - Inference round 1 complete.
566 [main] INFO org.linqs.psl.application.inference.LazyMPEInference - Beginning inference round 2.
566 [main] INFO org.linqs.psl.reasoner.admm.ADMMReasoner - Optimization completed in 1 iterations. Primal res.: NaN, Dual res.: NaN
566 [main] INFO org.linqs.psl.application.inference.LazyMPEInference - Inference round 2 complete.
584 [main] INFO org.linqs.psl.examples.simpleacquaintances.Run - Inference complete
631 [main] INFO org.linqs.psl.examples.simpleacquaintances.Run - Accuracy: 0.400000, F1: 0.280000, Positive Class Precision: 0.466667, Positive Class Recall: 0.200000, Negative Class Precision: 0.377778, Negative Class Recall: 0.680000
??要查看示例的輸出,請檢查groovy下的inferred-predicates/KNOWS.txt文件:
inferred-predicates/KNOWS.txt
??應該看到一些輸出,如:
‘Arti‘ ‘Ben‘ 0.48425865173339844
‘Arti‘ ‘Steve‘ 0.5642937421798706
< ... 48 rows omitted for brevity ...>
‘Jay‘ ‘Dhanya‘ 0.4534565508365631
‘Alex‘ ‘Dhanya‘ 0.48786869645118713
??輸出的確切順序可能會改變,為簡潔起見,省略了一些行。
??現在我們已經運行了示例,讓我們看看示例中唯一的源文件:
src/main/java/org/linqs/psl/examples/simpleacquaintances/Run.groovy。
三、Groovy源碼文件解析
1. 配置(Configuration)
??在構造函數中,您可以看到創建的ConfigBundle:
config = ConfigManager.getManager().getBundle("simpleacquaintances");
??請註意,傳入的字符串getBundle()是此配置集的前綴。這意味著特定於此程序的所有配置鍵都應該以前綴simpleacquaintances為前綴。
2. 謂詞定義(Defining Predicates)
??該definePredicates()方法為我們的示例定義了三個謂詞:
model.add predicate: "Lived", types: [ConstantType.UniqueStringID, ConstantType.UniqueStringID];
model.add predicate: "Likes", types: [ConstantType.UniqueStringID, ConstantType.UniqueStringID];
model.add predicate: "Knows", types: [ConstantType.UniqueStringID, ConstantType.UniqueStringID];
??這裏的每個謂詞都有兩個唯一的屬性為字符串標識符的作為參數,請註意,對於唯一標識符(Unique),ConstantType.UniqueStringID和ConstantType.UniqueIntID都是可用的。值得一提的是,擁有整數標識符通常需要在用戶方面進行更多預處理,但能獲得更好的性能。
謂詞釋義:
??? Lived 表示一個人住在特定的位置。例如:Lived(Sammy,SantaCruz)表示Sammy住在Santa Cruz。
??? Likes 表示一個人喜歡某事的程度。例如:Likes(Sammy,Hiking)會表明Sammy喜歡徒步旅行的程度。
??? Knows 表明一個人認識其他人。例如:Knows(Sammy,Jay)會表示Sammy和Jay相互認識。
3. 規則定義(Defining Rules)
??該defineRules()方法為該示例定義了六個規則。有些頁面涵蓋了PSL 規則規範和Groovy中的規則規範。我們將討論以下兩條規則:
model.add( rule: "20: Lived(P1, L) & Lived(P2, L) & (P1 != P2) -> Knows(P1, P2) ^2" );
model.add( rule: "5: !Knows(P1, P2) ^2" );
??第一條規則可以理解為 “ 如果P1和P2是不同的兩個人,並且兩者都住在同一個地方L,那麽他們彼此認識 ” 。此規則需要註意的一些要點是:
??? 變量L在兩個Lived原子中重復使用,因此必須引用相同的位置L。
??? (P1 != P2)指的是P1和P2是不同的人(不同的獨特ID)。
??第二條規則是作為先驗的特殊規則。請註意這條規則與其他所有規則的含義並不相同。相反,此規則可以理解為“默認情況下,人們彼此不了解”。因此,該項目將首先相信沒有人相互了解,並且這個先驗的觀念將被其他定義的規則作為證據來克服。
4. 加載數據(Loading Data)
??該loadData()方法將數據從data目錄中的文件加載到PSL正在使用的虛擬數據存儲庫中。為了便於理解,我們來查看兩個文件:
Inserter inserter = dataStore.getInserter(Lived, obsPartition);
inserter.loadDelimitedData(Paths.get(DATA_PATH, "lived_obs.txt").toString());
inserter = dataStore.getInserter(Likes, obsPartition);
inserter.loadDelimitedDataTruth(Paths.get(DATA_PATH, "likes_obs.txt").toString());
??兩個部分都使用一個加載數據Inserter。兩個調用之間的主要區別在於第二個調用正在加載真值,而第一個調用默認真值是1。
??如果我們查看文件內容,我們會看到以下行:
../data/lives_obs.txt
Jay Maryland
Jay California
../data/likes_obs.txt
Jay Machine Learning 1
Jay Skeeball 0.8
??在lives_obs.txt,沒有必要使用真值,因為生活在某個地方是一個離散的行為。你要麽住在那裏,要麽沒有。然而,喜歡某些東西為連續的軟真值會更符合常理。如:Jay有 100%的可能會喜歡機器學習,但他只有80%的可能喜歡Skeeball。
5. 分區(Partitions)
??在PSL中,使用分區來組織數據。分區只不過是數據的容器,但我們使用它們將特定的數據塊保存在一起或分開。例如,如果我們正在運行結果評估模塊,我們必須確保在訓練中不使用我們的測試分區。
??PSL用戶通常在至少三個不同的分區中組織他們的數據(在本例中您可以看到所有這些分區):
???obsPartition 在這個例子中稱為觀察分區:在這個分區中我們放置了實際的觀察數據。在這個例子中,我們把關於誰住在哪裏,誰喜歡什麽,誰知道誰在觀察分區中的所有觀察。
???targetsPartition 在本例中稱為目標分區:在這個分區中,我們放置了我們想要推斷的值的原子。例如,如果我們想要推斷Jay和Sammy相互認識的概率,那麽我們會將原子Knows(Jay, Sammy)放入目標分區。
???truthPartition 在這個例子中稱為事實分區:在這個分區中,我們放置了實際值的數據,但不包括在我們的已知觀察數據,目的用於評估模型推理效果。例如,如果我們知道Jay和Sammy確實相互了解,我們會將Knows(Jay, Sammy)事實分區放入真值1。
6. 運行推理(Running Inference)
??該runInference()方法處理我們加載的所有數據的運行推理。
??在我們進行推理之前,我們必須建立一個用於推理的數據庫:
Database inferDB = dataStore.getDatabase(targetsPartition, [Lived, Likes] as Set, obsPartition);
??getDatabase()方法對於DataStore是獲得一個數據庫的正確方法。此方法至少需要兩個參數:
???允許此數據庫寫入的分區targetsPartition。在推理中,我們將原子的推斷真值寫入目標分區,因此我們需要將其打開以進行寫入。
???要在寫入分區中關閉的一組分區obsPartition。即使我們將值寫入寫分區,我們可能只有一些謂詞,我們實際上想要推斷它們的值。此參數允許您關閉那些沒有更改的謂詞(封閉謂詞)。最後,getDatabase()獲取要包含在此數據庫中的任意數量的只讀分區。在我們的示例中,我們希望在進行推理時包含我們的觀察結果。
??接下來我們準備推理部分:
InferenceApplication inference = new MPEInference(model, inferDB);
inference.inference();
inference.close();
inferDB.close();
??對於MPEInference構造函數,我們提供我們的模型和數據庫來推斷。要查看結果,我們需要查看目標分區。
7. 輸出(Output)
??該方法writeOutput()處理打印(輸出)出推斷的結果。此方法有兩個關鍵行:
Database resultsDB = ds.getDatabase(targetsPartition);
...
for (GroundAtom atom : resultsDB.getAllGrondAtoms(Knows)) {
??第一行得到一個新的數據庫,我們可以從中獲取原子。請註意,我們把targetsPartition作為寫分區傳入,但實際上我們只是從它讀取已經推理得到的結果數據。
??第二行使用Queries來叠代此數據庫中的所有為 Knows 的原子。
8. 評估(Evaluation)
??最後,用evalResults()方法評判我們的模型的效果。本DiscreteEvaluator類提供基本的工具來比較兩個分區。在這個例子中,我們將目標分區與真值分區進行比較。
private void evalResults(Partition targetsPartition, Partition truthPartition) {
Database resultsDB = dataStore.getDatabase(targetsPartition);
Database truthDB = dataStore.getDatabase(truthPartition, [Knows] as Set);
Evaluator eval = new DiscreteEvaluator();
eval.compute(resultsDB, truthDB, Knows);
log.info(eval.getAllStats());
resultsDB.close();
truthDB.close();
}
概率軟邏輯(PSL,Probabilistic soft logic)示例演練和模塊解釋