fff-rs / juice

The Hacker's Machine Learning Engine
1.1k stars 77 forks source link

make layer API rustic #3

Open drahnr opened 7 years ago

drahnr commented 7 years ago

The API includes too much boilerplate, consider builder pattern.

Anton-4 commented 6 years ago

@drahnr Could you add some more detail on what you would like the API to look like or which approach to take? I'm considering helping out.

drahnr commented 6 years ago

@Anton-4 I brainstormed a builder like pattern with one of my former colleagues ( I am looking at you @nicoretti ), I can post it later tonight :) I'd be glad to see some more faces around the codebase 👍

let me know if I can help you getting kickstarted.

drahnr commented 6 years ago

Ideally the both APIs could co-exist for a while before retireing the existing one.

This is only for input:

//     cfg.add_framework(OPENCL).gpuset([0..2])
//     cfg.add_framework()
//     cfg.add_layer(fc1).framework(CUDA).config(LinearConfig().output_size(4096))
//     cfg.add_layer(fc1).framework(AUTOSELECT).config(PoolingConfig().stride(2).output_size(4096))

// impl Config {
//     fn add_layer(self, name : String) -> LayerConfig<> {
//         self.lc = LayerConfig<>::new(name);
//         &self.lc
//     }
// }

// impl Layer {
//     fn new() -> LayerConfig {
//     }
//     fn config(self, lc : LayerConfig) -> Self {
//         self.layerconfig = lc;
//         self
//     }
andreytkachenko commented 5 years ago

Here is my opinion on that issue:

I do want to have sort of device manager, like:


use co::prelude::*;

let mut discoverer = DeviceDiscoverer::new();
/// my laptop has 2 gpus 1 nvidia (CUDA and OpenCL) and integrated intel (OpenCL only) 
discoverer.add_discoverer(co::cuda::CudaDiscoverer::new()); // firstly added - higher priority 
discoverer.add_discoverer(co::opencl::OpenCLDiscoverer::only_gpu());
discoverer.add_discoverer(co::native::NativeDiscoverer::new());
let device_set = discoverer.discover(); // here we have 3 devices - [Nvidia<CUDA>, Intel<OpenCL>, CPU<Native>]
println!("Found: {}", device_set);

let backend = SharedBackend::new(device_set);

let model = build_model(backend);
let mut trainer = Trainer::new();
trainer.set_solver(Adam::new(0.01, ...));
trainer.set_model(model);
let epochs = trainer.fit_iter(1000); // run 1000 epochs
for epoc in epochs {
    println!("{}", epoch);
} 
fn build_model(b: Rc<Backend>) -> impl ILayer {
    model! {
         <Sequential input_shape=(28, 28, 1)>
              <Reshape shape=(768) />
              <Linear units={128} />
              <Activation type={Sigmoid} />

              <Linear units={256} />
              <Activation type={Sigmoid} />

              <Linear units={10} />
              <Activation type={Sigmoid} />
              <Softmax classes={10} />
         </Sequential>
    }
}
drahnr commented 4 years ago

That makes a lot of sense, but this adds a optimization pass (at least a bucket filling pass, but that might be way too simplistic).

I would also prefer something less xml-ish