如何在 C# 中实现交互式决策树

Posted

技术标签:

【中文标题】如何在 C# 中实现交互式决策树【英文标题】:How to implement an interactive decision tree in C# 【发布时间】:2020-02-03 12:58:31 【问题描述】:

我需要允许用户通过在屏幕上显示的两个简单选项之间进行选择来选择自己的路径,以便进入下一组选项,直到他们到达一个结局,即应该实现这样的目标:

我尝试了以下代码,但每次只评估左侧。我想知道如何才能获得像上图这样的结果(覆盖所有分支)?例如,如果用户选择“否”,则应用程序不应该向用户询问任何进一步的问题,而只是简单地显示“也许你想要披萨”消息。我已经用决策树算法做到了这一点,需要修复它,使其像上图一样覆盖左右两侧。

namespace ConsoleApp3

    class Program
    
        static void Main(string[] args)
        
            var decisionTree = MainDecisionTree();

            var client = new Client();

            Console.WriteLine("Do you want a book? true/false");
            client.Answer[0] = bool.Parse(Console.ReadLine());
            Console.WriteLine("Do you like it? true/false");
            client.Answer[1] = bool.Parse(Console.ReadLine());
            Console.WriteLine("Are you sure? true/false");
            client.Answer[2] = bool.Parse(Console.ReadLine());

            decisionTree.Evaluate(client);

            Console.WriteLine("Press any key...");
            Console.ReadKey();
        

        private static DecisionQuery MainDecisionTree()
        
            //Decision 2
            var wantBranch = new DecisionQuery
            
                Title = "Do you want a book?",
                Test = (client) => client.Answer[0],
                Positive = new DecisionResult  Result = true ,
                Negative = new DecisionResult  Result = false 
            ;

            //Decision 1
            var deserveBranch = new DecisionQuery
            
                Title = "Do you like it?",
                Test = (client) => client.Answer[1],
                Positive = wantBranch,
                Negative = new DecisionResult  Result = false 
            ;


            //Decision 0
            var sureBranch = new DecisionQuery
            
                Title = "Are you sure?",
                Test = (client) => client.Answer[2],
                Positive = deserveBranch,
                Negative = new DecisionResult  Result = false 
            ;

            return sureBranch;
        
    

    public class DecisionResult : Decision
    
        public bool Result  get; set; 

        public override void Evaluate(Client client)
        
            Console.WriteLine("\r\nThe result: 0", Result ? "Buy it" : "You need to wait");
        
    

    public class DecisionQuery : Decision
    
        public string Title  get; set; 
        public Decision Positive  get; set; 
        public Decision Negative  get; set; 
        public Func<Client, bool> Test  get; set; 

        public override void Evaluate(Client client)
        
            bool result = this.Test(client);
            string resultAsString = result ? "yes" : "no";

            Console.WriteLine($"\t- this.Title? resultAsString");

            if (result) this.Positive.Evaluate(client);
            else this.Negative.Evaluate(client);
        
    

    public abstract class Decision
    
        public abstract void Evaluate(Client client);
    

    public class Client
    
        public bool[] Answer  get; set;  = new bool[3];
    

【问题讨论】:

机器学习? @OlivierRogier 不确定是否相同。我只需要以最简单的方式创建一个简单的决策树。 是的:Decision Trees in Machine Learning @OlivierRogier 那么你对我的问题有什么想法吗?它可以很容易地重新生产。 你写的代码并没有实现所有的分支,问题可能会在过程中被问到,而不是在评估之前。我没有参与机器学习和决策树,但乍一看,您的算法在 MainDecisionTree 中存在缺陷。 【参考方案1】:

如果我理解你的问题,这里是更正你的代码。

我重命名了一些东西。

我调用MakeDecisionTree这个方法,用条件树初始化专家系统并返回根条件。

每个condition 都包含一个sentenceevaluate,它可以是queryresult

对于resultevaluate 显示sentence

对于queryevaluate 方法要求用户通过yesno 回答问题。并使用这个答案,它调用下一个孩子condition的相应evaluate

对不起,我的英语不是我的母语,我不参与人工智能。

static private void DecisionTreeTest()

  Console.WriteLine("Please, answer a few questions with yes or no.");
  Console.WriteLine();
  MakeDecisionTree().Evaluate();

static private bool GetUserAnswer(string question)

  Console.WriteLine(question);
  string userInput;
  while ( true )
  
    userInput = Console.ReadLine().ToLower();
    if ( userInput == "yes" )
      return true;
    else
    if ( userInput == "no" )
      return false;
    else
      Console.WriteLine("Your answer is not supported, retry please." +
                        Environment.NewLine + Environment.NewLine +
                        question);
  

static private DecisionTreeQuery MakeDecisionTree()

  var queryAreYouSure
    = new DecisionTreeQuery("Are you sure?",
                            new DecisionTreeResult("Buy it."),
                            new DecisionTreeResult("You need to wait."),
                            GetUserAnswer);
  var queryIsItAGoodBook
    = new DecisionTreeQuery("Is it a good book?",
                            new DecisionTreeResult("What are you waiting for? Just buy it."),
                            new DecisionTreeResult("Find another one."),
                            GetUserAnswer);
  var queryDoYouLikeIt
    = new DecisionTreeQuery("Do you like it?",
                            queryAreYouSure,
                            queryIsItAGoodBook,
                            GetUserAnswer);
  var queryDoYouWantABook
    = new DecisionTreeQuery("Do you want a book?",
                            queryDoYouLikeIt,
                            new DecisionTreeResult("Maybe you want a pizza."),
                            GetUserAnswer);
  return queryDoYouWantABook;

abstract public class DecisionTreeCondition

  protected string Sentence  get; private set; 
  abstract public void Evaluate();
  public DecisionTreeCondition(string sentence)
  
    Sentence = sentence;
  

public class DecisionTreeQuery : DecisionTreeCondition

  private DecisionTreeCondition Positive;
  private DecisionTreeCondition Negative;
  private Func<string, bool> UserAnswerProvider;
  public override void Evaluate()
  
    if ( UserAnswerProvider(Sentence) )
      Positive.Evaluate();
    else
      Negative.Evaluate();
  
  public DecisionTreeQuery(string sentence,
                           DecisionTreeCondition positive,
                           DecisionTreeCondition negative,
                           Func<string, bool> userAnswerProvider)
    : base(sentence)
  
    Positive = positive;
    Negative = negative;
    UserAnswerProvider = userAnswerProvider;
  

public class DecisionTreeResult : DecisionTreeCondition

  public override void Evaluate()
  
    Console.WriteLine(Sentence);
  
  public DecisionTreeResult(string sentence)
    : base(sentence)
  
  

【讨论】:

谢谢。性能怎么样?以及干净的代码和 SOLID?你确定你的代码够干净吗? 我还没学过SOLID。但或多或少是干净的。例如,我们可以分离用户输入。 关于性能,proc 调用比 JZ/JNZ 慢... 当然可以通过更正gets/sets来增强封装,我们可以添加构造函数等等...... 答案已更新。数据成员是受保护的/私有的,但这取决于树是否可变。【参考方案2】:

你的决策树设计很好,只是你问用户的方式不是。 您提前提出所有问题,然后再转到决策树。 当然,这棵树不能让被问到的问题不被问到。 这不是决策树问题。

你必须把这些对行:

    Console.WriteLine("Are you sure? true/false");
    client.Answer[2] = bool.Parse(Console.ReadLine());

进入 Evaluate 方法,或者您执行回调或任何架构设计以将逻辑与用户界面分开。 决策树必须提出问题,或者至少知道问题。 答案无需存储,输入后即可评价。

也许你传递了一些东西来询问用户评估方法

public interface IAsk

    bool Question(string message);

对于您的示例,您可以使用

public class ConsoleAsk : IAsk

    bool Question(string message)
    
         Console.WriteLine(message);
         return  bool.Parse(Console.ReadLine());
    

然后你像这样修改你的 Evaluate:

    public override void Evaluate(IAsk ask)
    
        bool result = ask.Question("do you feel good or bad");
        string resultAsString = result ? "yes" : "no";

        Console.WriteLine($"\t- this.Title? resultAsString");

        if (result) this.Positive.Evaluate(ask);
        else this.Negative.Evaluate(ask);
    

当然,你用你的 title 属性替换问题。

【讨论】:

我又添加了一些,不确定您阅读的是哪个州。请再读一遍。 decisionTree.Evaluate(new ConsoleAsk());并删除所有“客户”行。 如何更改DecisionResult 类的Evaluate 方法,因为现在当我为第一个问题选择no 时,我应该收到Maybe you want a Pizza 消息,但它给了我you need to wait 看来你已经修复了DecisionQuery类的Evaluate方法,但是DecisionResult Evaluate方法仍然需要修复。 当然,您的 MainDecisionTree 将“您确定”作为首要问题返回。 “返回确定分支”。树的根始终是顶部,而不是底部的东西。您漂亮的图形与您的树不对应。你看你自己,你的图形中有 9 个节点,但你在代码中只创建了 7 个

以上是关于如何在 C# 中实现交互式决策树的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript 中实现决策树。寻找比我丑陋的解决方案更好的解决方案[关闭]

无法使用 Accord.Net 框架实现基本决策树

绘制决策树分类器时出现交互错误,获取值数组.. 使树很难可视化

如何在c#中实现决策矩阵

决策树决策树与Jupyter小部件的交互式可视化

如何基于决策树实现 c# 代码/逻辑?