65.9K
CodeProject 正在变化。 阅读更多。
Home

男人、婚姻与机器——人工智能咨询的冒险,第3部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (14投票s)

2011年4月9日

CPOL

5分钟阅读

viewsIcon

55883

downloadIcon

5081

“专家系统”是人工智能最商业成功的应用之一。这个三部分系列描述了如何使用逆向推理算法开发基于专家系统的人工咨询器。

第1部分 | 第2部分

本文的第2部分中,我们学习了如何在CLIPS中编程一个专家系统。在最后一部分中,我们将看到如何将专家系统嵌入到 C++、C#和Java应用程序中。

将知识库转换为“CLIPS”文件

CLIPS文件是一个简单的文本文件。 要在CLIPS中创建一个,请按CTRL N,然后将其保存为“SocratesKnowledgeBase.clp”

您可以使用以下编辑器

  1. 记事本
  2. notepad++(将其语言设置为LISP

组织“先验”问题

我们将首先使用“deffacts 构造来分组所有问题。此构造允许将一组 “先验”或初始知识指定为事实集合。当CLIPS环境重置时,此构造中的事实将被添加到事实列表中。

现在让我们将以下部分添加到“SocratesKnowledgeBase.clp”文本文件中。
首先创建一个“问题”模板

(deftemplate question
    (slot factor (default none)) 
    (slot question-to-ask (default none))
    (multislot choices (default yes no))
    (multislot range (type INTEGER)) 
    (slot has-pre-condition (type SYMBOL) (default no)))

然后添加以下“先验”问题事实

(deffacts questions 
    (question (factor your-age) (question-to-ask "What is your age?") (range 18 120) )
    (question (factor your-partner-age) (question-to-ask "What is the age of the person you wish to marry?") (range 18 120) )
    (question (factor your-work-status) (question-to-ask "What is your work status?") (choices student employed retired) )
    (question (factor your-partner-work-status) (question-to-ask "What is the work status of the person you wish marry ?") (choices student employed retired) )
    (question (factor your-annual-income) (question-to-ask "What is your annual income in USD?") (range 20000 1000000) )
    (question (factor your-partner-annual-income) (question-to-ask "What is your annual income in USD of the person you wish marry?") (range 20000 1000000) ))

接下来创建一个“问题规则”模板

(deftemplate question-rule
    (multislot if (default none))
    (slot then-ask-question (default none))) 

然后添加以下“先验”问题规则事实。

(deffacts question-rules 
    (question-rule (if your-work-status is employed) (then-ask-question your-annual-income)) 
    (question-rule (if your-partner-work-status is employed) (then-ask-question your-partner-annual-income)))

组织“先验”领域规则

创建领域规则模板

(deftemplate domain-rule
    (multislot if (default none))
    (multislot then (default none)))

然后添加以下“先验”领域规则事实,供逆向链接算法使用

(deffacts domain-rules 
    (domain-rule (if age-difference is-more-than 30 )
        (then based-on age-factor the-expert-system-favours-getting-married-with-certainty 20.0 %)) 

    (domain-rule (if income-difference is-more-than 100000 )
        (then based-on income-compatibility the-expert-system-favours-getting-married-with-certainty 15.0 %)) 

    (domain-rule (if income-difference is-more-than 1000 but-less-than 10000 )
    (then based-on income-compatibility the-expert-system-favours-getting-married-with-certainty 55.0 % and
                based-on marriage-penalty-tax-liability the-expert-system-favours-getting-married-with-certainty 25.0 %)) 

    (domain-rule (if your-annual-income is-more-than 100000 and 
                    your-partner-annual-income is-more-than 100000)
    (then based-on income-tax the-expert-system-favours-getting-married-with-certainty 60.0 %)) 

    (domain-rule (if your-annual-income is-less-than 100000 and 
                your-partner-annual-income is-less-than 100000)
    (then based-on income-tax the-expert-system-favours-getting-married-with-certainty 80.0 %)))

创建推断附加事实的规则

首先定义一个答案模板

(deftemplate answer
    (slot known-factor (default none))
    (slot value (default none)))

然后添加规则以推断年龄和收入差异

(defrule calculate-age-difference
    (answer (known-factor your-age) ( value ?your-age))
    (answer (known-factor your-partner-age) ( value ?your-part-age))
    =>
    (assert (answer (known-factor age-difference) (value (abs (- ?your-age ?your-part-age)) )))
)

(defrule calculate-income-difference
    (answer (known-factor your-annual-income) ( value ?your-inc))
    (answer (known-factor your-partner-annual-income) ( value ?your-part-inc))
     =>
    (assert (answer (known-factor income-difference) (value (abs (- ?your-inc ?your-part-inc)) ))))

接下来为结论添加一个事实模板

(deftemplate conclusion
    (slot name (default none))
    (slot confidence-factor (type FLOAT) (default 0.0))
    (slot evaluated (default no)))

创建带前置条件的标记问题规则

上一篇文章中,我们手动标记了一个带前置条件的问题。现在让我们看看如何使用此规则让专家系统自动完成

(defrule mark-questions-with-pre-conditions
    ?q <-(question (factor ?f) (has-pre-condition no))
    (question-rule (then-ask-question ?f) (if $?i&:(> (length$ ?i) 0)) )
   =>
    (modify ?q (has-pre-condition yes)) )

添加规则以接受和验证用户输入

我们现在添加规则,以向用户提示问题、验证问题,然后将响应捕获到答案事实中

(deffunction check-range ( ?min ?max ?answer )
    (if (not (numberp ?answer)) then (return 0) ) 
    (if ( and (>= ?answer ?min) (<= ?answer ?max) ) 
    then (return 1) 
    else (return 0)))

(deffunction ask 
    (?question ?choices ?range)
    (if (eq (length$ ?range) 0) then (printout t ?question ?choices ":") else (printout t ?question "range-" $?range ":"))
    (bind ?answer (read) )
    (if (eq (length$ ?range) 0)
    then (while (not (member$ ?answer ?choices)) do
        (printout t "Invalid option! Please specify one of these options" ?choices ":" ) 
        (bind ?answer (read))
        (if (lexemep ?answer) then (bind ?answer (lowcase ?answer))))
    else (while (eq (check-range (nth$ 1 ?range ) (nth$ 2 ?range ) ?answer) 0 ) do
        (printout t "Invalid input! Please specify a value within the range" $?range ":")
        (bind ?answer (read))
        (if (lexemep ?answer) then (bind ?answer (lowcase ?answer)))))
    (printout t crlf) 
    ?answer)

(defrule ask-question
    ?q <- (question (question-to-ask ?question)
    (factor ?factor)
    (range $?range)
    (choices $?choices)
    (has-pre-condition no))
    (not (answer (known-factor ?factor)))
    =>
    (assert (answer (known-factor ?factor)
    (value (ask ?question ?choices ?range)))))

实现领域规则的逆向链接算法

现在添加实现我们逆向链接算法的规则

(defrule remove-ask-if-in-domain-rules-with-more-than 
(    declare (salience -100)) 
    ?r <- (domain-rule (if ?first-ask-if is-more-than ?min $?rest-of-ifs-true)) 
    (answer (known-factor ?f&:(eq ?f ?first-ask-if)) (value ?a&:(> ?a ?min)) )
   => 
    (if (eq (nth$ 1 ?rest-of-ifs-true) and) 
    then (modify ?r (if (rest$ ?rest-of-ifs-true)))
    else (modify ?r (if ?rest-of-ifs-true))))

(defrule remove-ask-if-in-domain-rules-with-more-than-but-less-than
    ?r <- (domain-rule (if ?first-ask-if is-more-than ?min but-less-than ?max $?rest-of-ifs-true)) 
    (answer (known-factor ?f&:(eq ?f ?first-ask-if)) (value ?a&:(and (> ?a ?min) (< ?a ?max)(numberp ?a))) )
  =>
    (if (eq (nth$ 1 ?rest-of-ifs-true) and) 
    then (modify ?r (if (rest$ ?rest-of-ifs-true)))
    else (modify ?r (if ?rest-of-ifs-true))))

(defrule fire-domain-rule
    ?r <- (domain-rule (if $?a&:(=(length$ ?a) 0)) 
    (then based-on ?factor&:(> (str-length ?factor) 0) the-expert-system-favours-getting-married-with-certainty ?cf % $?rest-of-factors))
   =>
    (if (eq (nth$ 1 ?rest-of-factors) and) 
    then (modify ?r (then (rest$ ?rest-of-factors)))) 
    (assert (conclusion (name ?factor) (confidence-factor ?cf))) )

实现规则以解决问题依赖关系

(defrule remove-ask-if-in-question-rules
    ?r <- (question-rule (if ?first-ask-if is ?val $?rest-of-ifs-true))
    (answer (value ?val) (known-factor ?f&:(eq ?f ?first-ask-if)))
 =>
    (if (eq (nth$ 1 ?rest-of-ifs-true) and) 
    then (modify ?r (if (rest$ ?rest-of-ifs-true)))
    else (modify ?r (if ?rest-of-ifs-true))))
(defrule set-pre-condition-when-no-antecedents
    ?r <- (question-rule (if $?a&:(=(length$ ?a) 0)) (then-ask-question ?f))
    ?q <- (question (factor ?f) (has-pre-condition yes) )
    (not (answer (known-factor ?f)))
  =>
    (modify ?q (has-pre-condition no)))

组合置信因子

(defrule combine-confidence-factors
    ?rem1 <- (conclusion (name ?n) (confidence-factor ?f1))
    ?rem2 <- (conclusion (name ?n) (confidence-factor ?f2))
    (test (neq ?rem1 ?rem2))
   =>
    (retract ?rem1)
    (modify ?rem2 (confidence-factor (/ (- (* 100 (+ ?f1 ?f2)) (* ?f1 ?f2)) 100))))

格式化和打印最终结论

(defrule print-conclusions
    (declare (salience -5000))
    ?c<- (conclusion (confidence-factor ?cf) (name ?n))
  =>
    (printout t "Based on [ " (upcase ?n) " ] expert systems confidence favouring getting married is " ?cf " %" crlf))

保存“SocratesKnowledgeBase.clp”。 此文件应类似于SocratesKnowledgeBase.zip

运行CLIPS文件

按照以下步骤在CLIPS中运行此文件

  1. 按CTRL W禁用跟踪,然后单击“无”按钮
  2. 通过键入(clear)清除CLIPS环境。
  3. 通过键入(clear-window)清除CLIPS窗口。
  4. 按CTRL L加载“SocratesKnowledgeBase.clp”
  5. 通过键入(reset)重置CLIPS环境(以断言初始事实)
  6. 通过键入(run)运行此文件。

CLIPS与C++集成

使用此链接安装CLIPS源代码。

使用MS VS 2008创建一个新的C++控制台应用程序 并将其命名为“Socrates_AsCppConsoleApp”。向此项目添加一个cpp文件并将其命名为“Socrates_AsCppConsoleApp.cpp”。将以下代码添加到您的此文件中。确保将“clipscpp.h”和“CLIPSCPP.lib”的文件路径更改为CLIPS源代码文件夹。

#include "C:\Program Files\CLIPS\Projects\Source\Integration\clipscpp.h"
#pragma comment(lib, "C:\\Program Files\\CLIPS\\Projects\\Libraries\\Microsoft\\CLIPSCPP.lib")
int main()
{ 
    CLIPS::CLIPSCPPEnv theEnv; 
    theEnv.Load("..//SocratesKnowledgeBase.clp");
    theEnv.Reset();
    theEnv.Run(-1);
    return 0;
}

示例代码

使用CLIPSNet将CLIPS与C#集成

解压CLIPSNet_libs.zip并提取“CLIPSNet”库。(使用此链接下载最新版本的CLIPSNet。)

创建一个.net控制台应用程序并将其命名为“Socrates_AsCSharpApp”。添加对CLIPSNet.dll的引用,并确保将“CLIPSLib.dll”复制到bin/debug文件夹中。然后,在program.cs文件中添加以下代码

using System;
namespace Socrates_AsCSharpApp
{
   class Program
   {
    static void Main(string[] args)
    {
        CLIPSNet.Environment theEnv = new CLIPSNet.Environment();
        theEnv.Load(@"..\..\..\SocratesKnowledgeBase.clp");
        theEnv.Reset();
        theEnv.Run(-1);
    }
   }
}

示例代码

使用JESS将CLIPS与Java集成

使用此链接安装JESS。

使用Eclipse创建一个新的Java项目。将jess.jar添加到其外部jar列表。在项目中创建一个新类并将其命名为“Socrates_AsJavaApp”。将以下代码添加到此类中

import jess.JessException;
import jess.Rete;
public class Socrates_AsJavaApp{
public static void main(String[] args) {
    try {
    Rete env = new Rete();
    env.batch("..\\SocratesKnowledgeBase.clp");
    env.reset();
    env.run();
    } catch (JessException e) {
    e.printStackTrace();
    }
  }
}

示例代码

恭喜!。您已进入这篇冗长系列文章的最后一部分。为了奖励您的耐心,我将免费赠送整个“苏格拉底”知识库,并向您展示如何将结果路由到文本文件。

完整的苏格拉底专家系统

使用MS VS 2008创建一个新的C++控制台项目,并将其命名为“Socrates”。向此项目添加一个头文件并将其命名为“CustomFileRouter.h”。

CLIPS I/O路由器

CLIPS允许您编写通用I/O函数并为其分配逻辑名称。这些路由器可用于以下任何I/O函数

  1. 打开
  2. Close
  3. Printout
  4. 读取
  5. Readline
  6. 格式
  7. 重命名

在C++中编写路由器的第一步是扩展“CLIPSCPPRouter类并重写其Query、Print和GetContents函数。在CustomFileRouter.h中键入以下代码

using namespace CLIPS;
class CustomFileRouter : public CLIPSCPPRouter
{
    FILE* _resultsFile;
    char* fName;
public:
    CustomFileRouter(char* fileName)
    {
        _resultsFile = NULL;
        fName = fileName;
    }
    int Query(CLIPSCPPEnv *e,char * name)
    {
        if (strcmp(name,fName) == 0) 
        {
            if(_resultsFile == NULL)
            _resultsFile = fopen(fName , "w+");
            return(TRUE);
        }
        return(FALSE); 
    }
    int Print(CLIPSCPPEnv *e,char * name,char *p)
    {
        fputs(p ,_resultsFile);
        return(TRUE);
    } 
    int Exit(CLIPSCPPEnv *e,int)
    {
        return(TRUE);
    }
    char* GetContents()
    {
        fseek (_resultsFile , 0 , SEEK_END);
        long lSize = ftell (_resultsFile);
        rewind (_resultsFile);
        char *buffer = (char*) malloc (sizeof(char)*lSize);
        ZeroMemory(buffer,lSize);
        fread (buffer,1,lSize,_resultsFile);
        fclose(_resultsFile);
        _resultsFile = NULL;
        return buffer;
    } 
}; 

将路由器添加到CLIPS运行时

向项目添加一个新文件并将其命名为Socrates.cpp。 在main函数中添加以下代码

CLIPSCPPEnv theEnv; 
CustomFileRouter* results_fileRouter = new CustomFileRouter("results_file");
theEnv.AddRouter("results_file",10,results_fileRouter);
CustomFileRouter* questions_fileRouter = new CustomFileRouter("questions_file");
theEnv.AddRouter("questions_file",10,questions_fileRouter);

这将向CLIPS运行时添加两个逻辑路由器,即:“results_file”、“questions_file”。
您可以在CLIPS中的任何I/O函数中使用这些路由器。例如,此命令将打印到“results_file”而不是控制台。

(printout results_file "Hello custom router !!" crlf)

将规则文件嵌入为资源

要将“苏格拉底”打包为一个带有嵌入规则文件的独立应用程序,请按照以下步骤操作。

将CLIPS规则文件添加到您的项目。
然后在resources.h文件中添加以下代码

#define    CLIPS_RULE_FILE 201 
#define    IDR_CLP_FILE 101 

并在.rc文件中添加此内容

IDR_CLP_FILE     CLIPS_RULE_FILE      "SocratesExpertSystemRules.clp" 

以下是如何在运行时加载资源

HMODULE handle = ::GetModuleHandle(NULL); 
HRSRC rc = ::FindResource(handle, MAKEINTRESOURCE(IDR_CLP_FILE), MAKEINTRESOURCE(CLIPS_RULE_FILE)); 
HGLOBAL rcData = ::LoadResource(handle, rc); 
size = ::SizeofResource(handle, rc); 
data = static_cast<const char*>(::LockResource(rcData)); 

示例代码

 

苏格拉底Ionic PWA移动应用程序

这是移动应用程序的源代码。

结论

人工智能咨询器是专家系统,属于基于知识的系统。为了让这些系统能够很好地推理,它们必须对它们所操作的领域有深入的了解。因此,为了让“苏格拉底”准确地推理,需要添加比示例代码更多的领域专业知识。

我无法在这些文章中涵盖专家系统的所有方面,因此这里列出了您可以作为参考的网站。

人工智能咨询的冒险开始吧!

.

© . All rights reserved.