动物识别专家系统是流行的专家系统实验模型,它用产生式规则来表示知识,共15条规则、可以识别七种动物,这些规则既少又简单,可以改造他们,也可以加进新的规则,还可以用来识别其他东西的新规则来取代这些规则。
识别七种动物共需要30个特征和事实,用0~29给它们编号。
String[] features = {"有毛", "产奶", "有羽毛", "会飞", "会下蛋", "吃肉", "有犬齿", "有爪", "眼睛盯前方", "有蹄",
"反刍", "黄褐色", "有斑点", "有黑色条纹", "长脖", "长腿", "不会飞", "会游泳", "黑白两色", "善飞",
"哺乳类", "鸟类", "肉食类", "蹄类", "企鹅", "海燕", "鸵鸟", "斑马", "长颈鹿", "虎", "金钱豹"};
产生式规则就用三维数组表示:
int[][][] expr = new int[][][]{
{{0}, {20}},
{{1}, {20}},
{{2}, {21}},
{{3, 4}, {21}},
{{20, 5}, {22}},
{{6, 7, 8}, {22}},
{{20, 8}, {23}},
{{20, 9}, {23}},
{{22, 11, 12}, {30}},
{{22, 11, 13}, {29}},
{{23, 14, 15, 12}, {28}},
// 如果动物是蹄类(23),且有黑色条纹(13),则该动物对应事实数组的第27个“斑马”
{{23, 13}, {27}},
{{21, 14, 15, 16}, {26}},
{{21, 19}, {25}},
{{21, 17, 18, 16}, {24}}};
// 规则库,存放已知的所有规则(产生式规则)
List<Rule> rules = new ArrayList<>();
// 事实库,动态变化的
List<Integer> conditions = new ArrayList<>();
使用可变的列表,容易添加和删除已有条件、事实。
在30个事实中:
String[] features = {"有毛", "产奶", "有羽毛", "会飞", "会下蛋", "吃肉", "有犬齿", "有爪", "眼睛盯前方", "有蹄",
"反刍", "黄褐色", "有斑点", "有黑色条纹", "长脖", "长腿", "不会飞", "会游泳", "黑白两色", "善飞",
"哺乳类", "鸟类", "肉食类", "蹄类", "企鹅", "海燕", "鸵鸟", "斑马", "长颈鹿", "虎", "金钱豹"};
前20个是用户输入的特征,而下面的四个(“哺乳类”, “鸟类”, “肉食类”, “蹄类”)属于推理的中间结果,最后的七个(“企鹅”, “海燕”, “鸵鸟”, “斑马”, “长颈鹿”, “虎”, “金钱豹”)是最终的识别结果。
在用户输入条件到事实库后(这些条件就是事实),事实库并不是不变的。
比如用户输入了0(“有毛”)、5(“吃肉”),那么其实真正的事实库是(0、5、20),因为在匹配第一条规则:{{0}, {20}}的时候,有条件0,所以可以得到规则1的结论(20),并且该结论也应当加入事实库,因为这是已经知道的了,然后匹配到规则5:{{20, 5}, {22}}时,就可以得到结论22(“肉食类”)。
所以在从上到下匹配这15条产生式规则的过程中,如果匹配了某一条产生式规则,就该把这条规则的结论加入事实库;而且也发现了匹配规则是要注意顺序的,上面的例子中如果先匹配了规则5,那么此时事实库中只有(0和5),就得不到中间结论22。
这说明规则的摆放是要有顺序的,产生某编号的规则(即结论为某编号规则)应该排在使用该编号的规则(即条件为某编号规则)之前。这15条结论是正好符合这样的顺序的。
有了上面的顺序意识之后,推理机的实现就很容易了:就是从上到下,从第一条产生式规则开始判断当前的事实库能否匹配每条规则,匹配成功,就把该条规则的结论编号加入事实库,匹配完规则库中所有产生式规则后就可以输出结论了(我的处理):
什么结论都推不出(一条结论都没匹配上):
推出中间结果:
推出最终结果,识别到了动物:
Java实现(IDE为IDEA)