27 lines
4.4 KiB
C++
27 lines
4.4 KiB
C++
// Learned nonlinear hidden layer executes as integer AltAILIF/Delta neurons.
|
|
#include <knp/framework/model_executor.h>
|
|
#include <knp/framework/network.h>
|
|
#include <knp/neuron-traits/altai_lif.h>
|
|
#include <knp/synapse-traits/delta.h>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
using P=knp::core::Population<knp::neuron_traits::AltAILIF>;using R=knp::core::Projection<knp::synapse_traits::DeltaSynapse>;using N=knp::neuron_traits::neuron_parameters<knp::neuron_traits::AltAILIF>;
|
|
template<class T>std::vector<T>read(const std::filesystem::path&p){std::ifstream f(p,std::ios::binary);if(!f)throw std::runtime_error("Missing file");std::vector<char>b((std::istreambuf_iterator<char>(f)),{});if(b.size()%sizeof(T))throw std::runtime_error("Truncated file");std::vector<T>v(b.size()/sizeof(T));std::memcpy(v.data(),b.data(),b.size());return v;}
|
|
int main(int argc,char**argv)try{
|
|
if(argc!=5)throw std::runtime_error("features.bin modeldir cpu|cuda hidden_counts.bin");auto root=std::filesystem::path(argv[2]);auto x=read<int32_t>(argv[1]),w=read<int32_t>(root/"hidden_w.bin"),b=read<int32_t>(root/"hidden_b.bin"),thr=read<int32_t>(root/"hidden_thresholds.bin");constexpr int F=800,H=256,FRAME=72;
|
|
if(x.empty()||x.size()%F||w.size()!=F*H||b.size()!=H||thr.size()!=H)throw std::runtime_error("Invalid dimensions");size_t count=x.size()/F;for(size_t row=0;row<count;row++){double norm=0;for(int f=0;f<F;f++){int a=x[row*F+f];if(a<0||a>640)throw std::runtime_error("Input outside bounds");norm+=double(a)*a;}if(norm>2063.*2063.)throw std::runtime_error("Input normalization bound violated");}
|
|
int nb=0,nr=0;std::vector<int>reset(H);for(int h=0;h<H;h++){if(thr[h]<1||thr[h]>65535)throw std::runtime_error("Invalid threshold");double norm=0;for(int f=0;f<F;f++){int z=w[f*H+h];if(std::abs(int64_t(z))>127)throw std::runtime_error("Invalid weight");norm+=double(z)*z;}int64_t bound=int64_t(std::ceil(2063*std::sqrt(norm)))+std::abs(int64_t(b[h]))+1;if(2*bound>=16777216)throw std::runtime_error("Reset exactness bound exceeded");reset[h]=int(bound);nb=std::max(nb,int((std::abs(int64_t(b[h]))+65534)/65535));nr=std::max(nr,(reset[h]+65534)/65535);}
|
|
P pop([&](size_t h){N n;n.activation_threshold_=thr[h];n.negative_activation_threshold_=0;n.is_diff_=true;n.is_reset_=false;n.saturate_=true;n.potential_leak_=0;return n;},H);std::vector<R::Synapse>syn;
|
|
auto add=[&](int i,int h,int a){if(a)syn.push_back({{float(a),1,knp::synapse_traits::OutputType::EXCITATORY},size_t(i),size_t(h)});};
|
|
for(int h=0;h<H;h++){for(int f=0;f<F;f++)add(f,h,w[f*H+h]);int bias=b[h];for(int j=0;j<nb;j++){int z=std::clamp(bias,-65535,65535);add(F+j,h,z);bias-=z;}int r=reset[h];for(int j=0;j<nr;j++){int z=std::min(r,65535);add(F+nb+j,h,-z);r-=z;}}
|
|
R proj(knp::core::UID{false},pop.get_uid(),[&](size_t i)->std::optional<R::Synapse>{return syn[i];},syn.size());auto pid=proj.get_uid(),nid=pop.get_uid();knp::framework::Network net;net.add_population(std::move(pop));net.add_projection(std::move(proj));knp::framework::Model m(std::move(net));knp::core::UID ic,oc;m.add_input_channel(ic,pid);m.add_output_channel(oc,nid);
|
|
auto gen=[&](knp::core::Step t){knp::core::messaging::SpikeData v;size_t row=t/FRAME;int phase=t%FRAME;if(row>=count)return v;if(phase==0){for(int j=0;j<nr;j++)v.push_back(F+nb+j);}else if(phase==4){for(int f=0;f<F;f++)for(int k=0;k<x[row*F+f];k++)v.push_back(f);for(int j=0;j<nb;j++)v.push_back(F+j);}return v;};
|
|
std::string mode=argv[3];if(mode!="cpu"&&mode!="cuda")throw std::runtime_error("Invalid backend");knp::framework::BackendLoader loader;knp::framework::ModelExecutor e(m,loader.load("/home/user/KNP/build/bin/libknp-"+std::string(mode=="cuda"?"altai-cuda":"cpu-single-threaded")+"-backend.so"),{{ic,gen}});std::vector<int32_t>out(count*H);
|
|
auto collect=[&](size_t t){for(auto&msg:e.get_loader().get_output_channel(oc).read_some_from_buffer(t,t)){size_t i=msg.header_.send_time_/FRAME;int phase=msg.header_.send_time_%FRAME;if(i<count&&phase>=5&&phase<69)for(auto h:msg.neuron_indexes_)++out[i*H+h];}};
|
|
e.start([&](size_t t){if(t)collect(t-1);return t<count*FRAME;});std::ofstream f;f.exceptions(std::ios::badbit|std::ios::failbit);f.open(argv[4],std::ios::binary);f.write(reinterpret_cast<const char*>(out.data()),out.size()*4);f.close();std::cout<<"HIDDEN "<<count<<" reset_lines "<<nr<<'\n';
|
|
}catch(const std::exception&e){std::cerr<<e.what()<<'\n';return 1;}
|