1 个不稳定版本

使用旧的 Rust 2015

0.1.0 2017 年 8 月 13 日

#666机器学习

MIT 许可证

195KB
4K SLoC

Build Status

AI_Kit

AI_Kit 旨在成为各种经典 AI 算法的一个依赖项。

核心项目目标是

  • 通过构建特性来提供各种算法的便捷和人体工程学接口。
  • 通过使用特性标志只构建你需要的内容
  • 性能
  • 易于理解的实现

以下所有算法(如下文所述)都操作于几个核心特性,BindingsValueUnifyOperation

ai_kit 提供 可选数据结构,这些数据结构实现了这些特性,允许所有算法直接使用 - 请参阅 DatumRule。以下是一些快速示例,随后是更深入的文档。

安装

您可以通过将以下行添加到您的 Cargo.toml 文件中

[dependencies]
ai_kit = "0.1.0"

并将 extern crate ai_kit 添加到您的 crate 根中。

文档

此 README 提供了简介。

API 文档可在 此处 获取

核心概念

当使用此库时,需要了解三个特性和一个结构

Bindings - 类似于键/值查找,但具有确保两个(或更多)键具有相同值的实用程序。

BindingsValue - 一个特性,允许数据结构被 Bindings 数据结构使用。

Unify - 一个特性,允许数据结构可以与另一个相同类型的数据结构统一。

Operation - 一个特性,用于将一定数量的 Unify 实例映射到一定数量的其他 Unify。这用于实现 正向反向 推理。

Unify

如果两个数据结构的所有组件都相同,或者至少在字段上有所不同,则可以统一。 Datum 结构实现了 Unify以下 是统一数据的一个示例。

Bindings

当成功时,统一过程返回一个 Bindings 结构体,该结构体将变量名映射到它们的值(如果已知)。它还允许指定两个变量是等价的;在这种情况下,当找到其中一个变量的值时,它被认为是另一个变量的值。

任何实现了 ai_kit::core::BindingsValue 的东西都可以与 Bindings 一起使用;《a href="#readme-datum" rel="ugc noopener">Datum 实现了 BindingsValue

// Example of using the ai_kit::datum::Datum for variable bindings.

extern crate ai_kit;

use ai_kit::core::Bindings;
use ai_kit::datum::Datum;

fn main() {
    // Create empty bindings
    let bindings : Bindings<Datum> = Bindings::new();

    // Set the variables "?x" and "?y" equal to each other
    let bindings = bindings
        .set_binding(&"?x".to_string(), Datum::Variable("?y".to_string()));

    // Set the value of "?x"
    let bindings = bindings.set_binding(&"?x".to_string(), Datum::Float(1.0));

    // Verify that "?y" now has the same value
    
    assert_eq!(bindings.get_binding(&"?x".to_string()), Some(Datum::Float(1.0)));
}

操作

有时程序具有某些事实,可以从中推断出更多的事实。这是通过 Operation 特性实现的。这用于实现 正向推理规划。正向链推理(也称为Modus Ponens)的例子如下

All men are mortal.
Socrates is a man.
Therefore Socrates is mortal.

规则 结构体实现了 Operation,我们用它来在 Rust 中执行上述推理。

算法

约束

功能 with-constraint

一个简单且有限的库,用于检查和满足约束。

正向推理

功能 with-forward-inference

正向链推理的实现——本质上这是通过Modus Ponens进行推理。

示例.

规划

功能 with-planner

带有回溯的规划。

示例

谱系

用于表示推导给定推理所采取路径的辅助数据结构和代码。

默认特性格式化实现

上述算法在实现适当核心特性格式(BindingsValueUnifyOperation)的任何结构上运行。

ai_kit 提供了实现核心特性格式的默认结构,这应该足以满足许多用例。

数据

功能 with-datum

datum::Datum 结构体实现了 BindingsValueUnify 特性。

	#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd)]
	pub enum Datum {
	    Nil,
	    String(String),
	    Int(i64),
	    Float(f64),
	    Variable(String),
	    Vector(Vec<Datum>),
	}

数据实现统一

由于 Datum 实现了 Unify 特性,因此 Datum 可以进行统一。


extern crate ai_kit;

use ai_kit::core::{Bindings, Unify};
use ai_kit::datum::Datum;

fn main() {
    let d = Datum::Float(0.0);
    let empty_bindings : Bindings<Datum> = Bindings::new();

    // These datums are the same, so they can be unified
    let bindings = d.unify(&Datum::Float(0.0), &empty_bindings);
    assert!(bindings.is_some());

    // These datums are not the same, so they cannot be unified
    let bindings = d.unify(&Datum::Float(1.0), &empty_bindings);
    assert!(bindings.is_none());
    
    // These datums differ, but the second is a variable, so they can be unified
    let bindings = d.unify(&Datum::Variable("?x".to_string()), &empty_bindings);
    assert!(bindings.is_some());

    // The bindings returned by unification so that the variable ?x now has the same value as d!
    assert_eq!(bindings.unwrap().get_binding(&"?x".to_string()), Some(d));
}

规则

功能 with-rule

rule::Rule 结构体实现了 Operation 特性。

	#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
	pub struct Rule<T: ConstraintValue, U: Unify<T>> {
	    pub constraints: Vec<Constraint>,
	    pub lhs: Vec<U>,
	    pub rhs: U,
	    _marker: PhantomData<T>,
	}

规则实现操作


extern crate ai_kit;

use ai_kit::datum::Datum;
use ai_kit::infer::InferenceEngine;
use ai_kit::rule::Rule;
use std::marker::PhantomData;

fn main() {
    // Encode knowledge about mortality
    let rules = vec![
        (
            "rule_of_mortality".to_string(),    // Rules need ids for inferencing
            Rule {
                constraints: Vec::new(),
                lhs: vec![
                    Datum::Vector(
                        vec![
                            Datum::Variable("?x".to_string()),
                            Datum::String("isa".to_string()),
                            Datum::String("human".to_string()),
                        ]
                    )
                ],
                rhs: Datum::Vector(vec![
                    Datum::Variable("?x".to_string()),
                    Datum::String("isa".to_string()),
                    Datum::String("mortal".to_string()),
                ]),
                _marker: PhantomData,
            }
        ),
    ];
    
    // Setup our initial knowledge about socrates
    let facts = vec![
        (
            "socrates_is_hu``man".to_string(),      // Facts need ids for inferencing
            Datum::Vector(
                vec![
                    Datum::String("socrates".to_string()),
                    Datum::String("isa".to_string()),
                    Datum::String("human".to_string()),
                ]
            )
        ),
    ];

    // Infer new knowledge!
    let mut inf_engine = InferenceEngine::new(
        "demo".to_string(),
        rules.iter().map(|&(ref id, ref f)| (id, f)).collect(),
        facts.iter().map(|&(ref id, ref r)| (id, r)).collect());
    let inferences = inf_engine.chain_forward();
    assert_eq!(inferences.len(), 1);
}

规划示例

数独求解器

待定

N-Queens 求解器

待定

NLP 解析器

此示例接收一系列单词,以及将单词聚集为短语和句子的规则,并构建单词的有效解析。然后,它在当前工作目录中保存实际构建的目标树的 GraphViz .dot 语法 图为 "parse.dot"。


extern crate ai_kit;
#[macro_use]
extern crate serde_json;

use ai_kit::core::Bindings;
use ai_kit::datum::Datum;
use ai_kit::planner::*;
use ai_kit::rule::Rule;
use std::fs::File;
use std::io::Write;
use std::path;

macro_rules! from_json {
    ($type: ty, $json: tt) => ({
        use serde_json;
        let x: $type = serde_json::from_value(json!($json)).expect("Expected json decoding");
        x
    })
}

#[allow(unused)]
fn main() {
    /*
     * Inference rules that encode the parts of speech for each word and how to
     * compose parts of speech into sentences.
     */
    let rules: Vec<Rule<Datum, Datum>> = from_json!(Vec<Rule<Datum, Datum>>, [
          // Parts of speech for each word
          {"lhs": [{"str": "a"}], "rhs": {"str": "det"}},
          {"lhs": [{"str": "the"}], "rhs": {"str": "det"}},
          {"lhs": [{"str": "chased"}], "rhs": {"str": "verb"}},
          {"lhs": [{"str": "chased"}], "rhs": {"str": "verb"}},
          {"lhs": [{"str": "dog"}], "rhs": {"str": "noun"}},
          {"lhs": [{"str": "cat"}], "rhs": {"str": "noun"}},
          // Building phrases into sentences
          {"lhs": [{"str": "det"}, {"str": "noun"}], "rhs": {"str": "np"}},
          {"lhs": [{"str": "verb"}, {"str": "np"}], "rhs": {"str": "vp"}},
          {"lhs": [{"str": "np"}, {"str": "vp"}], "rhs": {"str": "sen"}}
        ]);

    // Our input data - a series of words
    let data: Vec<Datum> = from_json!(Vec<Datum>, [
          {"str": "a"},
          {"str": "the"},
          {"str": "dog"},
          {"str": "cat"},
          {"str": "chased"}
        ]);

    // Specify that our goal is to construct a sentence from the provided data using the provided rules
    let mut planner = Planner::new(&Goal::with_pattern(from_json!(Datum, {"str": "sen"})),
                                   &Bindings::new(),
                                   &PlanningConfig {
                                       max_depth: 5,
                                       max_increments: 50,
                                       // Don't reuse a given piece of data (ie, a word)
                                       reuse_data: false,
                                   },
                                   data.iter().collect(),
                                   rules.iter().collect());

    // Construct the first interpretation
    let result = planner.next();
    assert_eq!(result.is_some(), true);
    let (final_goal, bindings) = result.unwrap();

    // What are our expected leaves of the goal (ie, the order of parsed sentences)
    let expected_leaves: Vec<Datum> = vec![
    	"a".to_string(),
    	"dog".to_string(),
    	"chased".to_string(),
    	"the".to_string(),
    	"cat".to_string()
    ]
        .into_iter()
        .map(|s| Datum::String(s))
        .collect();

    // Verify that the leaves of our plan are as expected
    assert_eq!(final_goal.gather_leaves(&bindings), expected_leaves);

    // Render the plan using graphviz notation
    let graphviz_rendering : String = final_goal.render_as_graphviz();

    // Save the plan in the current working directory
    File::create(path::Path::new(&"parse.dot"))
        .and_then(|mut file| file.write_all(graphviz_rendering.as_str().as_bytes()));
}

以下是 "parse.dot" 预期的内容

graph "goal tree 'sen'" {

"'sen' [Actor(8)]" -- "'np' [Actor(6)]";
"'np' [Actor(6)]" -- "'det' [Actor(0)]";
"'det' [Actor(0)]" -- "'a' [Datum(0)]";

"'np' [Actor(6)]" -- "'noun' [Actor(4)]";
"'noun' [Actor(4)]" -- "'dog' [Datum(2)]";

"'sen' [Actor(8)]" -- "'vp' [Actor(7)]";
"'vp' [Actor(7)]" -- "'verb' [Actor(2)]";
"'verb' [Actor(2)]" -- "'chased' [Datum(4)]";

"'vp' [Actor(7)]" -- "'np' [Actor(6)]";
"'np' [Actor(6)]" -- "'det' [Actor(1)]";
"'det' [Actor(1)]" -- "'the' [Datum(1)]";

"'np' [Actor(6)]" -- "'noun' [Actor(5)]";
"'noun' [Actor(5)]" -- "'cat' [Datum(3)]";

}

如果您已本地安装 graphviz,可以将此图转换为 PNG 文件

dot -Tpng parse.dot > parse.png

功能矩阵

一些功能依赖于其他功能。以下表格总结了这些

功能 需要
with-planner with-constraint
with-forward-inference with-planner with-constraint
with-rule with-constraint
with-constraint N/A
with-pedigree N/A
with-datum N/A

怀疑测试

本文件中的示例在构建过程中使用 skeptic 进行测试。

依赖关系

~4.5MB
~96K SLoC