C++でパーセプトロン
練習としてパーセプトロン(線形学習器)をC++で実装した。
動くには動くが多分バグがあると思う。検証しろよ。
アルゴリズムはCristianini, Shawe-Taylor著 大北訳の「サポートベクターマシン入門」(赤本)を参考にした。
データフォーマットはLIBSVMのものが使えるようにしました。
入力は訓練セットとテストセット、出力はテストセットの正解率など。
なお線形分離不可能なデータセットは学習できません(無限ループになる)。
多分まともにプログラムできる人からしたらゴミみたいなコードだと思います
もっとシンプルに美しく組めるんだろうなぁ。
できれば2次元の場合の可視化をしたい。
#include <cstdlib> #include <iostream> #include <numeric> #include <vector> #include <math.h> using namespace std; struct str{ int label; double b; vector<int> index; vector<double> value; }; double p=1; //learning rate vector <str> trainset; vector <str> testset; str classfier; void input(char* fname, vector<str>* instance){ int n=0,c=0; double v; str st; FILE *fin=fopen(fname, "r"); while(1){ int i=0; fscanf(fin,"%d",&st.label); while(1){ do{ c=fgetc(fin); if(c=='\n') goto out; }while(isspace(c)); ungetc(c,fin); fscanf(fin,"%d:%lf",&n,&v); st.index.push_back(n); st.value.push_back(v); ++i; } out: instance->push_back(st); for(int j=0;j<i;++j){ st.index.pop_back(); st.value.pop_back(); } c=fgetc(fin); if(c==EOF) break; ungetc(c,fin); } } double inner_product(str* x, str* y){ int i,j; double sum=0; for(i=0;i<x->index.size();++i){ for(j=i; j<y->index.size(); ++j){ if(x->index[i]==y->index[j]){ sum += x->value[i] * y->value[j]; } } } return sum; } void vector_sum(str* x, str* y){ int i=0,j=0; str tmp_str; while(1){ if(x->index[i]==y->index[j]){ tmp_str.index.push_back(x->index[i]); tmp_str.value.push_back(p*x->label*x->value[i]+y->value[j]); ++i; ++j; } else if(x->index[i] < y->index[j]){ tmp_str.index.push_back(x->index[i]); tmp_str.value.push_back(p*x->label*x->value[i]); ++i; } else{ tmp_str.index.push_back(y->index[j]); tmp_str.value.push_back(y->value[j]); ++j; } if(i>x->index.size()-1 || j>y->index.size()-1){ if(j<y->index.size()){ while(j<y->index.size()){ tmp_str.index.push_back(y->index[j]); tmp_str.value.push_back(y->value[j]); ++j; } } else if(i<x->index.size()){ while(i<x->index.size()){ tmp_str.index.push_back(x->index[i]); tmp_str.value.push_back(p*x->label*x->value[i]); ++i; } } break; } } classfier = tmp_str; } double function(str *w, str *x){ return (x->label * (inner_product(w,x) + w->b)); } void perceptrons(){ int i,update=0,it=0; double R=0, tmp; classfier.b=0; for(i=0;i<trainset.size();++i){ tmp=inner_product(&trainset[i],&trainset[i]); if(tmp>R) R=tmp; } R=sqrt(R); cout << "R=" << R << endl; classfier.index.push_back(trainset[0].index[0]); classfier.value.push_back(0); do{ update=0; cout << "it : " << it <<endl; for(i=0;i<trainset.size();++i){ cout << "f=" << function(&classfier, &trainset[i]) << " "; cout << "b=" << classfier.b << endl; if(function(&classfier, &trainset[i]) <= 0){ vector_sum(&trainset[i], &classfier); classfier.b=classfier.b+p*trainset[i].label*R*R; ++update; } } ++it; }while(update!=0); } void test(){ double acc=0; for(int i=0;i<testset.size();++i){ if(function(&classfier,&testset[i]) > 0){ ++acc; } } cout << "accuraccy : " << acc / testset.size() << endl; } int main(int argc, char** argv) { if(argc!=2){ cout << "input trainfile testfile" <<endl; } input(argv[1],&trainset); input(argv[2],&testset); cout << "learn " << argv[1] << endl; perceptrons(); cout << endl; cout << "b=" << classfier.b << endl; for(int i=0;i<classfier.index.size();++i){ cout << classfier.index[i] << ":" << classfier.value[i] << endl; } cout << endl; cout<< "test " << argv[2] << endl; test(); return 0; }