『アソシアトロン』中野馨著の第4章概念形成の計算

表題の著作の第4章、4.1「概念形成」にかかれているサンプルを、プログラムして計算してみた。

まず、ベイズ定理を用いた計算は、次のようなプログラムだ。g++の一番新しいもので、linuxマシンでコンパイルすればいいと思う。私は、ubuntu 18.04 のg++でコンパイルしている。他のものの場合は、変更が必要になるかもしれない。(なお、その後知ったことだが、これは単純ベイズ分類器 Naive Bayes Classifier というものである。後の記事でもっと詳しく説明するだろう)

#include <iostream>
#include <string>
#include <cmath>

using namespace std;

double pxAB(int x[], double pq[]);

int main(){

    int x[16][4] = {
        {  0,  0,  0,  0}, //0  
        {  0,  0,  0,  1}, //1  
        {  0,  0,  1,  0}, //2  
        {  0,  0,  1,  1}, //3  
        {  0,  1,  0,  0}, //4  
        {  0,  1,  0,  1}, //5  
        {  0,  1,  1,  0}, //6  
        {  0,  1,  1,  1}, //7  
        {  1,  0,  0,  0}, //8  
        {  1,  0,  0,  1}, //9  
        {  1,  0,  1,  0}, //10 
        {  1,  0,  1,  1}, //11 
        {  1,  1,  0,  0}, //12 
        {  1,  1,  0,  1}, //13 
        {  1,  1,  1,  0}, //14 
        {  1,  1,  1,  1}  //15 
        };
        
    double PA = 0.5;
    double PB = 0.5;
    double p[4] = {0.6, 0.8, 0.8, 0.6};
    double q[4] = {0.4, 0.2, 0.2, 0.2};    
    
    for(int d=0;d<16;d++){
        double PxA = pxAB(x[d],p); 
        double PxB = pxAB(x[d],q); 
        double probBx = (PxB*PA)/(PxA*PA+PxB*PB); 
        double probAx = 1-probBx; 
        string AB; 
        if(probAx > probBx) 
            AB = " ==> A";
        else 
            AB = " ==> B";
        cout << "No." << d << " PAx = " << probAx << " PBx = " << probBx << AB <<endl;
    }
    
}

double pxAB(int x[], double pq[]){
    double prob = 1;
    for(int i=0;i<4;i++){
        prob = prob*pow(pq[i],x[i])*pow(1-pq[i],1-x[i]);
    }
    return prob;
}

以下のように、上記の本に書かれているとおりの結果が出力された。

No.0 PAx = 0.0204082 PBx = 0.979592 ==> B
No.1 PAx = 0.111111 PBx = 0.888889 ==> B
No.2 PAx = 0.25 PBx = 0.75 ==> B
No.3 PAx = 0.666667 PBx = 0.333333 ==> A
No.4 PAx = 0.25 PBx = 0.75 ==> B
No.5 PAx = 0.666667 PBx = 0.333333 ==> A
No.6 PAx = 0.842105 PBx = 0.157895 ==> A
No.7 PAx = 0.969697 PBx = 0.030303 ==> A
No.8 PAx = 0.0447761 PBx = 0.955224 ==> B
No.9 PAx = 0.219512 PBx = 0.780488 ==> B
No.10 PAx = 0.428571 PBx = 0.571429 ==> B
No.11 PAx = 0.818182 PBx = 0.181818 ==> A
No.12 PAx = 0.428571 PBx = 0.571429 ==> B
No.13 PAx = 0.818182 PBx = 0.181818 ==> A
No.14 PAx = 0.923077 PBx = 0.0769231 ==> A
No.15 PAx = 0.986301 PBx = 0.0136986 ==> A

しかし、アソシアトロンのほうがうまく行かない。原因がわかったかたは、ツイッターの @wassiisg まで教えていただきたい。色々テスト、計算過程の検証をしたが、3個のデータについて、ベイズ推定の場合と一致しない。プログラムは、以下のようなものである。

#include <iostream>
#include <string>
#include <cmath>

using namespace std;

void estimation(int v[], int data[][4]);

int main(){
    int data[10][4] = {
        {  1,  1,  1,  1}, // A
        {  1,  1,  1,  0}, // A
        {  1,  1,  0,  1}, // A
        {  0,  1,  1,  0}, // A
        {  0,  0,  1,  1}, // A
        {  1,  0,  0,  0}, // B
        {  0,  1,  0,  0}, // B
        {  0,  0,  1,  0}, // B
        {  1,  0,  0,  1}, // B
        {  0,  0,  0,  0}  // B
        };

    int test[16][4] = {
        {  0,  0,  0,  0}, //0  B
        {  0,  0,  0,  1}, //1  NON X
        {  0,  0,  1,  0}, //2  B
        {  0,  0,  1,  1}, //3  A
        {  0,  1,  0,  0}, //4  B
        {  0,  1,  0,  1}, //5  NON
        {  0,  1,  1,  0}, //6  A
        {  0,  1,  1,  1}, //7  B
        {  1,  0,  0,  0}, //8  B
        {  1,  0,  0,  1}, //9  NON
        {  1,  0,  1,  0}, //10 NON X
        {  1,  0,  1,  1}, //11 NON
        {  1,  1,  0,  0}, //12 NON X
        {  1,  1,  0,  1}, //13 A
        {  1,  1,  1,  0}, //14 A
        {  1,  1,  1,  1}  //15 A
        };

    int M[4][4];
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            M[i][j] = 0;
        }
    }
    
    for(int d=0;d<10;d++){
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                int di = data[d][i];
                int dj = data[d][j];
                if(di == 0) di = -1;
                if(dj == 0) dj = -1;
                M[i][j] = M[i][j] + (di*dj);
            }
        }
    }
    cout << "記憶行列 M " << endl;
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            cout << M[i][j] << " ";
        }
        cout << endl;
    }
    
    cout << "概念形成 " << endl;
    for(int d=0;d<16;d++){
        cout << "No." << d << ":: "; 
        int v[4];
        for(int j=0;j<4;j++) v[j] = 0;
        for(int i=0;i<4;i++){
            for(int k=0;k<4;k++){ 
                int dk = test[d][k]; 
                if(dk == 0) dk = -1; 
                v[i] += M[i][k]*dk; 
            } 
            if(v[i] > 0){
                v[i] = 1;
                cout << "1 "; 
            }else{
                 v[i] = 0;
                 cout << "0 "; 
            }
        }
        estimation(v, data);
        cout << endl;
    }
    
    return 0;
}

void estimation(int v[], int data[][4]){
    double minidist = 10000;
    int minino = -1;
    for(int d=0;d<10;d++){
        double dis = 0;
        for(int j=0;j<4;j++){
            // ユークリッドの距離
            dis += (double)(data[d][j]-v[j])*(data[d][j]-v[j]);
            /*
            // Hamming の距離
            if(data[d][j]-v[j] < 0){
                dis += v[j]-data[d][j];
            }else{
                dis += data[d][j]-v[j];
            }
            */
        }
        // ユークリッドの距離
        dis = sqrt(dis); 
        // Hamming の距離
        //dis = dis/2;
        if(dis < minidist){
            minino = d;
            minidist = dis;
        }
    }
    if(minino < 5) cout << " ==> A ";
    else cout << " ==> B ";    
    cout << "( " << minino << ", " << minidist << " )";
}

出力結果は以下のようになる。

記憶行列 M 
10 2 -2 4 
2 10 2 0 
-2 2 10 0 
4 0 0 10 
概念形成 
No.0:: 0 0 0 0  ==> B ( 9, 0 )
No.1:: 0 0 0 1  ==> A ( 4, 1 )
No.2:: 0 0 1 0  ==> B ( 7, 0 )
No.3:: 0 0 1 1  ==> A ( 4, 0 )
No.4:: 0 1 0 0  ==> B ( 6, 0 )
No.5:: 0 1 0 1  ==> A ( 2, 1 )
No.6:: 0 1 1 0  ==> A ( 3, 0 )
No.7:: 0 1 1 1  ==> A ( 0, 1 )
No.8:: 1 0 0 0  ==> B ( 5, 0 )
No.9:: 1 0 0 1  ==> B ( 8, 0 )
No.10:: 1 0 1 0  ==> A ( 1, 1 )
No.11:: 1 0 1 1  ==> A ( 0, 1 )
No.12:: 1 1 0 0  ==> A ( 1, 1 )
No.13:: 1 1 0 1  ==> A ( 2, 0 )
No.14:: 1 1 1 0  ==> A ( 1, 0 )
No.15:: 1 1 1 1  ==> A ( 0, 0 )

ベイズ推定の場合と比べると3つのデータだけ違った結果を出している。