4 个版本

0.1.13 2020 年 7 月 20 日
0.1.12 2020 年 2 月 26 日
0.1.1 2020 年 2 月 9 日
0.1.0 2020 年 1 月 31 日

#2258算法

MIT 许可证

165KB
3K SLoC

Radiate Web

通常,训练深度学习算法是昂贵的 CPU/GPU 操作,而使用 Radiate 也不例外。为了解决这个问题,Radiate Web 通过公开一些数据传输对象来允许您远程构建学习算法,然后将其发送到另一台机器进行训练或测试。这是与 Radiate 一起使用的小扩展。

种群数据传输对象 (DTO)

简单地构建您的传输对象,通过定义一个简单种群参数将其发送到您的其他机器。这不允许您定义一些参数,主要是 'run' 函数,它确定何时停止训练,并且必须在训练机器上。

Radiate 数据传输对象

构建一个带有 NEAT (Neuroeolution of Augmented Topologies) 的 Radiate 遗传算法,通过封装其余的训练选项和环境来发送。

示例

此示例代码可在 此处 找到,它描述了如何使用 RocketTokio 设置客户端和服务器,以构建 Web 服务并处理路由。

客户端

#![feature(proc_macro_hygiene, decl_macro)]

extern crate radiate;
extern crate radiate_web;
extern crate serde;
extern crate serde_json;
extern crate serde_derive;
extern crate reqwest;

use radiate::prelude::*;
use radiate_web::prelude::*;
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let data = generate_post_data();                // generate the data to send
    let client = reqwest::Client::new();            // create the client object 
    let mut headers = HeaderMap::new();             // add application/json to the headers because that is how NEAT is seaialized
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
    let res = client.post("http://0.0.0.0:42069/")  // listn on local host
        .headers(headers)                           // add the headers then add data and send it
        .body(data)
        .send().await;
    Ok(())
}

#[allow(dead_code)]
fn generate_post_data() -> String {
    
    // create an environment
    let neat_env = NeatEnvironment::new()
        .set_input_size(2)
        .set_output_size(1)
        .set_weight_mutate_rate(0.8)
        .set_edit_weights(0.1)
        .set_weight_perturb(1.75)
        .set_new_node_rate(0.03)
        .set_new_edge_rate(0.04)
        .set_reactivate(0.2)
        .set_activation_functions(vec![
            Activation::LeakyRelu(0.02)
        ]);

    // build the neat network
    let net = Neat::new()
        .input_size(2)
        .batch_size(1)
        .dense_pool(1, Activation::Sigmoid);

    // build the population
    let population = NeatPopulationBuilder::new()
            .num_evolve(100)
            .size(100)
            .dynamic_distance(true)
            .debug_process(true)
            .config(Config {
                inbreed_rate: 0.001,
                crossover_rate: 0.75,
                distance: 0.5,
                species_target: 5
            })
            .stagnation(10)
            .genocide(vec![Genocide::KillWorst(0.9)]);
    
    // put it all together
    let radiate_dto = RadiateDto::new()
            .env(neat_env)              // give the dto and neat enviornment
            .train(100, 0.3)            // if you want to train the algorithm traditionally this is where to define it 
            .neat(net)                  // add the neat object
            .population(population)     // add the population object 
            .to_json();                 // put it all to json and return it
    
    // save to a file for testing via Postman
    radiate_dto
}

服务器

这是一个简单的示例,用于训练一个 neat 网络来解决传统的 XOR 问题。

#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use] extern crate rocket;
#[macro_use] extern crate rocket_contrib;
extern crate radiate;
extern crate radiate_web;
extern crate serde;
extern crate serde_json;
extern crate serde_derive;

use std::fs::File;
use radiate::prelude::*;
use radiate_web::prelude::*;
use rocket::config::{Config as RConfig, Environment as REnv};
use rocket_contrib::json::{Json, JsonValue};

fn main() {
    let r_config = RConfig::build(REnv::Staging)
        .address("0.0.0.0")
        .port(42069)
        .finalize()
        .unwrap();

    rocket::custom(r_config)
        .mount("/", routes![run])
        .launch();
}

#[post("/", format = "json", data = "<radiate>")]
fn run(radiate: Json<RadiateDto>) -> Option<JsonValue> {
    
    // unpack the variables
    let env = radiate.0.env?;
    let net = radiate.0.neat?;
    let pop = radiate.0.population?;
    let train = radiate.0.train?;

    // take out the training variables
    let num_evolve = pop.num_evolve?;
    let num_train = train.epochs;
    let learning_rate = train.learning_rate;

    // create a new problem variable
    let xor = XOR::new();

    // set up the population now that it has been recieved
    let (mut solution, _) = Population::<Neat, NeatEnvironment, XOR>::new()
        .constrain(env)
        .populate_clone(net)
        .debug(pop.debug_process?)
        .dynamic_distance(pop.dynamic_distance?)
        .configure(pop.config?)
        .stagnation(pop.stagnation?, pop.genocide?)
        .parental_criteria(pop.parental_criteria?)
        .survivor_criteria(pop.survivor_criteria?)
        .run(|_, fit, num| {
            println!("epoch: {} score: {}", num, fit);
            let diff = 4.0 - fit;
            (diff > 0.0 && diff < 0.01) || num == num_evolve
        }).unwrap();

    // manually train the neural net
    solution.train(&xor.inputs, &xor.answers, learning_rate, Loss::Diff, |iter, _| {
        iter == num_train as usize
    }).unwrap();
    
    // show it
    xor.show(&mut solution);
    Some(json!({"it": "works"}))
}

#[derive(Debug)]
pub struct XOR {
    inputs: Vec<Vec<f32>>,
    answers: Vec<Vec<f32>>
}

impl XOR {
    pub fn new() -> Self {
        XOR {
            inputs: vec![
                vec![0.0, 0.0],
                vec![1.0, 1.0],
                vec![1.0, 0.0],
                vec![0.0, 1.0],
            ],
            answers: vec![
                vec![0.0],
                vec![0.0],
                vec![1.0],
                vec![1.0],
            ]
        }
    }


    fn show(&self, model: &mut Neat) {
        println!("\n");
        for (i, o) in self.inputs.iter().zip(self.answers.iter()) {
            let guess = model.forward(&i).unwrap();
            println!("Guess: {:.2?} Answer: {:.2}", guess, o[0]);
        }
    }
}

impl Problem<Neat> for XOR {

    fn empty() -> Self { XOR::new() }

    fn solve(&self, model: &mut Neat) -> f32 {
        let mut total = 0.0;
        for (ins, outs) in self.inputs.iter().zip(self.answers.iter()) {
            match model.forward(&ins) {
                Some(guess) => total += (guess[0] - outs[0]).powf(2.0),
                None => panic!("Error in training NEAT")
            }
        }
        4.0 - total
    }
}

#[allow(dead_code)]
fn generate_post_data() {
    
    // create an environment
    let neat_env = NeatEnvironment::new()
        .set_input_size(2)
        .set_output_size(1)
        .set_weight_mutate_rate(0.8)
        .set_edit_weights(0.1)
        .set_weight_perturb(1.75)
        .set_new_node_rate(0.03)
        .set_new_edge_rate(0.04)
        .set_reactivate(0.2)
        .set_activation_functions(vec![
            Activation::LeakyRelu(0.02)
        ]);

    // build the neat network
    let net = Neat::new()
        .input_size(2)
        .batch_size(1)
        .dense_pool(1, Activation::Sigmoid);

    // build the population
    let population = NeatPopulationBuilder::new()
            .num_evolve(100)
            .size(100)
            .dynamic_distance(true)
            .debug_process(true)
            .config(Config {
                inbreed_rate: 0.001,
                crossover_rate: 0.75,
                distance: 0.5,
                species_target: 5
            })
            .stagnation(10)
            .genocide(vec![Genocide::KillWorst(0.9)]);
    
    // put it all together
    let radiate_dto = RadiateDto::new()
            .env(neat_env)
            .train(100, 0.3)        // this has it's own DTO too (TrainDto), but it's small
            .neat(net)
            .population(population)
            .to_json();
    
    // save to a file for testing via Postman
    serde_json::to_writer_pretty(&File::create("post_test.json").unwrap(), &radiate_dto).unwrap();
}

依赖关系

~4.5MB
~98K SLoC