読者です 読者をやめる 読者になる 読者になる

うまくいかないNB

本格的に勉強したいないぁと思い最近入門機械学習を少しづつ読み進めている.

入門 機械学習

入門 機械学習

 

コードはRで書かれていてGithubから一括DLできる。

とはいえR読めないし書けないマンなので,RubyC++でおんなじようなことをしてみたのだが…

 

目的:メール本文からSpamどうか判定する

使用データ:付録のメールデータ

 

Rubyで各メールのBOWデータを作成

作成データ

  >||

298 spool:1 msgchange:5 columns:1 packed:1 addition:1 handle:1 get:2 changed:1 other:3 lengthen:1 exmh:7 unusual:1 …

83 check:1 save:1 never:1 tue:1 intended:1 didnt:1 aug:2… 

||<

先頭の数字が単一の単語数

 

訓練データは付録のcsvデータ(スパム、非スパムそれぞれで500件づつだったかな)

  >||

"term","frequency","density","occurrence"

"abandoned",2,9.41132182014964e-05,0.002

"ability",2,9.41132182014964e-05,0.002

"abiword",4,0.000188226436402993,0.002

||<

 

このoccurrenceを使ってメールごとの条件付き確率を求める。

スパム、非スパムの事前確率は0.5,未知語はラプラススムージングはせずに定数(0.0001)で扱う。アンダーフローを抑えるために対数とって総和にする。

汚いコードはいずれ直るといいですね…

>|c|

#include <iostream>

#include <fstream>

#include <cstdlib>

#include <cmath>

#include <iomanip>

#include <vector>

#include <algorithm>

#include <map>

#include <sstream>

 

using namespace std;

 

std::map<string,double> Spam;

std::map<string,double> Eham;

const double C = 0.0001;

const double PriorS = 0.5;

const double PriorE = 0.5;

 

void readUID(char *fn1, std::map<string,double>& map){

  ifstream fin;

  int i,j,f;

  double v,w;

  char c;

  string str,str2,gomi;

  fin.open(fn1);

  if(!fin){

    cerr << "ERROR: Failed to open file" << endl;

    exit(1);

  }

  getline(fin,gomi);

  while(1){

    getline(fin,gomi,'"');

    getline(fin,str,'"');

    getline(fin,gomi,',');

    fin >> f >> c >> v >> c >> w;

    //cout << str << " " << f << " " << v << " " << w << endl;

    if(fin.eof()==1) break;

    map.insert(pair<string,double>(str,w));

  }

  cout << endl;

  fin.close();

}

 

 

double calNB(map<string,double> D, map<string,int> M, const double P){

  int i;

  double v=log(P);

  //cout << v << endl;

  map<string,int>::iterator it = M.begin();

  while(it != M.end()){

    if(it->second >= 2){

      //cout << it->first << " " << D[it->first] << endl;

      if(D.count(it->first) == 0){

//xv += log(C);

      }else{

      //cout << it->first << " " << D[it->first] << endl;

v += log(D[it->first]);

      }

      //cout << v << endl;

    }

    it++;

  }

  return v;

}

 

 

void readFile(char *fn1)

{

  ifstream fin;

  int i,j,Dm=0,Dc=0,size,freq;

  double s,e,v;

  char c;

  string str,str2,gomi;

  std::stringstream ss;

  map<string,int> MapA;

  fin.open(fn1);

  if(!fin){

    cerr << "ERROR: Failed to open file" << endl;

    exit(1);

 

  }

  while(1){

    getline(fin,gomi,' ');

    if(fin.eof()==1) break;

    ss << gomi;

    ss >> size;

    ss.str("");

    ss.clear(stringstream::goodbit);

    for(j=0;j<size;j++){

      getline(fin,str,':');

      getline(fin,str2,' ');

      ss << str2;

      ss >> freq;

      //cout << j+1 << " " << str << " " << str2 << " " << freq << endl;

      MapA.insert(pair<string,int>(str,freq));

      ss.str(""); //

      ss.clear(stringstream::goodbit);

    }

    s = calNB(Spam,MapA,PriorS);

    e = calNB(Eham,MapA,PriorE);

    if(s >= e){

      printf("%e %e SPAM\n",s,e);

    }else{

      printf("%e %e HAM\n",s,e);

      Dc++;

    }

    MapA.clear();

    Dm++;

  }

  printf("分類精度=%f\n",(double)Dc/(double)Dm);

  fin.close();

}

 

int main(int argc, char **argv){

  readUID(argv[1],Spam);//read UID1

  cout << "read OK" << endl;

  readUID(argv[2],Eham);//read UID2

  cout << "read OK" << endl;

  readFile(argv[3]); //readRT.lbl

  cout << "print OK\n";

  return 0;

}

<||

 

結果

||>

サンプル

    SPAM      HAM    判定

--------------------------------

-1.256445e+02 -1.793793e+02 SPAM

-3.919911e+01 -6.407220e+01 SPAM

-3.890017e+01 -6.789437e+01 SPAM

-1.519796e+02 -2.220868e+02 SPAM

-1.756950e+01 -2.413611e+01 SPAM

-1.554876e+02 -2.526647e+02 SPAM

-2.959326e+01 -7.078474e+01 SPAM

-4.443161e+01 -1.037077e+02 SPAM

-4.753398e+01 -8.619887e+01 SPAM

…これはおかしい

分類精度が20%とかそれくらいになってしまって参考書とも全然違う(むしろ逆になればちょうどいい?)

 

おかしい可能性があるところ

・資料と文字列処理が異なるのでそれが原因になっている

・Cコードが間違えている

 

再先不安ですね…