[TOC]
For Beginners
预备步骤
下载和使用库文件
在官方网站上下载对应版本的SMILE库,解压文件后将库文件放在debug文件夹下。需要声明的是,SMILE有两个版本,只有Academic版本才是免费的,Commercial版本需要购买license。如果在Windows下,只需要在工程文件中包含SMILE头文件的地址,并引入头文件即可,此时头文件会自动根据系统设置选择库文件。
在Linux下,需要在g++中运行(假设库文件放在*.cpp文件同文件夹的子文件夹smile下),编译库文件并链接动态库(libsmile.a),系统输出二进制文件(可执行文件a.out)。
g++ -DNDEBUG -O3 -I./smile -L./smile -lsmile *.cpp
使用GeNIe可视化建图
使用GeNIe建图后,将文件保存成xdsl文件。具体参考文献[1]。当然,如果没有安装GeNIe就得自己写xdsl文件。
建立模型
建立网络
DSL_network
SMILE中有关网络的类是DSL_network该类的头文件在"network.h"中,除了构造函数外,成员函数包括:读写文件
int ReadFile(const char *filename, int fileType = 0, void *reserved = NULL);
int WriteFile(const char *filename, int fileType = 0, void *reserved = NULL);
int ReadString(const char *xdslString, void *reserved = NULL); //读xdslString的内容
int WriteString(std::string &xdslOutputString, void *reserved = NULL);建网络有关(点、边)
int FindNode(const char *nodeId) const;
DSL_node* GetNode(int handle);
弄清楚这两个函数的前提是能够用xdsl建图。在xdsl建图中结点的nodeID即是标识符(identifier),对应到SMILE库中就是const char *nodeID,这个在xdsl文件中必须指定,而且不能相同。以上两个函数的作用,FindNode是找到我们用identifier设置的node在系统中分配的handle是多少(int类型)。GetNode是真正用handle来获得该结点,GetNode的输入是handle,输出是结点类的指针(DSL_node),需要注意的是,handle并不是常值可能会改变,因此GetNode不能用一个定值handle,必须配合FindNode。FindNode的输出是handle,输入是标识符字符const char *nodeID。另外,还有一些特殊的函数:int MakeUniform();用以统一所有Node的definition。与目标结点有关
在SMILE库中,目标结点是指在推断算法中会进行更新的结点,其他没有被设为目标的结点,不能保证被更新(跟具体的更新算法有关)。如果用户没有设置目标结点,那么系统默认所有的结点都是目标结点。
int SetTarget(int nodeHandle);
int UnSetTarget(int nodeHandle);
int ClearAllTargets();
int IsTarget(int nodeHandle); //yes:non-zero;no:zero;invalid handle:negative与记录有关
在SMILE中,记录是用case来实现的。
DSL_simpleCase * AddCase(const std::string & name);
DSL_simpleCase * GetCase(int index) const;
int DeleteCase(int index);
void DeleteAllCases();
int GetNumberOfCases() const;
void EnableSyncCases(bool sync);贝叶斯推理--更新权值与设定参数有关
这些方法将在后面的小节中(设定参数与进行推论中)分别介绍如何使用。
int UpdateBeliefs();
void SetDefaultBNAlgorithm(int algorithm);
void SetDefaultIDAlgorithm(int algorithm);
int InvalidateAllBeliefs()
bool CalcProbEvidence(double &pe, bool forceChainRule = false);
int ClearAllEvidence();
DSL_node/DSL_nodeDefinition/DSL_nodeValue
SMILE中有关结点的类是DSL_node、DSL_nodeDefinition、DSL_nodeValue,这些类的头文件分别在"node.h","nodedef.h","nodeval.h"中,除了构造函数外,成员函数包括:DSL_nodeDefinition、DSL_nodeValue分别是结点的定义与条件概率的设定的关键,另外DSL_node还有一些与概率推论无关的,而与结点的颜色、name等属性有关的函数。返回推论相关的DSL_nodeDefinition、DSL_nodeValue指针
DSL_nodeDefinition *Definition();
DSL_nodeValue *Value();返回node其他的性质
const char* GetId() const; //nodeid 就是xdsl中identifier,所以返回值是一个字符串char*
int SetId(const char *newId);
DSL_nodeInfo &Info();
DSL_network *Network();
int Handle();
DSL_userPropertiesDSL_nodeDefinition [todo]
DSL_nodeValue [todo]
DSL_Dmatrix
SMILE中有关概率分布的类是DSL_Dmatrix,类的头文件在"dmatrix.h" 条件概率在DSL_Dmatrix用double类型的vector保存。具体的顺序参考手册中下图。
定位可以用coordinate或者是index的相加。 DSL_Dmatrix还可以用在功效表以及边缘概率分布中。
设定参数(enter observation, set evidence)节点(node)的表示:net.FindNode函数,该参数对应GeNIe中节点的标识符,而不是显示的Name!节点标识符对大小写敏感,并且命名规范为:起始字母为字符,只有字符、数字和下划线。节点状态的表示:net.GetNode函数与GetOutcomesNames函数,FindPosition函数。
设定evidence:注意的是,不是直接设置string函数,而是设定已有的状态,并用其index作为输入。
进行推论(perform inference, retrieve results)
UpdateBeliefs
参数学习
参数学习最好的方法也是通过GeNIe直接操作。
#include
#include
#include <.>
using namespace std;
int main(int argc, char *argv[])
{
ErrorH.RedirectToFile(stdout);
DSL_network net;
int res = net.ReadFile("VentureBN.xdsl");
if (DSL_OKAY != res)
{
return res;
}
int handle = net.FindNode("Forecast");
if(handle < 0)
{
return handle;
}
DSL_node *f = net.GetNode(handle);
int idx= f->Definition()->GetOutcomesNames() ->FindPosition("Poor");
if (idx < 0)
{
return idx;
}
f->Value()->SetEvidence(idx);
net.UpdateBeliefs();
handle=net.FindNode("Success");
if (handle < 0)
{
return handle;
}
DSL_node *s = net.GetNode(handle);
const DSL_Dmatrix &beliefs = *s->Value()->GetMatrix();
const DSL_idArray &outcomes = *s->Definition()->GetOutcomesNames();
for (int i=0;i
{
printf("%s=%g\n",outcomes[i],beliefs[i]);
}
cout << "Hello World!" << endl;
return DSL_OKAY;
}
常见问题
1.Windows下QT编译报错.error: LNK1104: cannot open file 'smile_dbg.lib'。 这种时候,不需要再额外去添加动态库,如下图。相反可以将所有的影子文件夹删除,并且重复几次“清理”和“构建”,并设置项目中的run运行配置。多等待几分钟会自动恢复。(这是非常奇怪的现象,个人猜测或许是Qt版本的原因,但今天确实遇到这个问题,尝试各种办法之后仍然无效,最后又自动恢复正常)
参考文献