プログラムの解説
情報科学研究科 情報通信技術論
工学部電子情報システム・応物系
(通信・情報コース)
パターン認識論
サンプルプログラム
(sample1.c)
の内容
サンプルプログラム (sample1.c) は 2 クラスの 2 次元データに対して、Nearest Neighbor 法を用 いて認識を行うプログラムである。入力データは、クラス 1 の学習データ (2D-class1.dat)、クラ ス 2 の学習データ (2D-class2.dat)、未知データ (2D-test.dat) の 3 つである。 Nearest Neighbor Nearest Neighbor 法とは、未知データの最近傍に存在するデータが所属するクラスを、未知デー タのクラスであると判別する方法である。 すなわち、未知データと全学習データの距離を計算し、距離が最小となる学習データが所属する クラスを認識結果とする (図 1)。
ᧂ⍮䊂䊷䉺
䉪䊤䉴䋱ቇ⠌䊂䊷䉺
䉪䊤䉴
2ቇ⠌䊂䊷䉺
ᧂ⍮䊂䊷䉺䈲䉪䊤䉴䋱䈪䈅䉎䈫್ᢿ
図 1: NN 法サンプルプログラム
サンプルプログラム (sample1.c) を以下に示す。 /* --- */ /* パターン認識論 サンプルプログラム (2 次元データ 2 クラス ver.) */ /* --- */ /* Nearest Neighbor */ #include <stdio.h> #include <stdlib.h> #include <math.h> #define LDATA 20 // 学習データ数 #define TDATA 40 // テストデータ数 #define DIM 2 // データ次元数 double class1[LDATA][DIM]; // クラス 1 の学習データ double class2[LDATA][DIM]; // クラス 2 の学習データ double test[TDATA][DIM]; // テスト (未知) データ int tans[TDATA]; // テストデータの正解 int result[TDATA]; // 認識結果 double met1[TDATA]; // 最小値 (クラス 1) double met2[TDATA]; // 最小値 (クラス 2)int main(int argc, char* argv[]){
/* --- 関数のプロトタイプ宣言 --- */ int input(); // データ入力
int nearest(); // Nearest Neighbor法 int output(); // 認識結果出力 /********** データ入力 **********/ input(); /********** Nearest Neighbor法 **********/ nearest(); /********** 認識結果出力 **********/ output(); return(0); }
/* --- */
/* データ入力 */
/* --- */ int input(){
FILE *fin1, *fin2, *fin3; // ファイルポインタ int i, j;
/* --- ファイル open --- */ fin1 = fopen("2D-class1.dat", "r");
if( fin1 == NULL){
fprintf(stderr, "file open error : 2D-class1.dat \n"); return -1; }
fin2 = fopen("2D-class2.dat", "r"); if( fin2 == NULL){
fprintf(stderr, "file open error : 2D-class2.dat \n"); return -1; }
fin3 = fopen("2D-test.dat" , "r"); if( fin3 == NULL){
fprintf(stderr, "file open error : 2D-test.dat \n"); return -1; } /* --- データの読み込み --- */ for(i=0;i<LDATA;i++){ for(j=0;j<DIM;j++){ fscanf(fin1, "%lf", &class1[i][j]); fscanf(fin2, "%lf", &class2[i][j]); } } for(i=0;i<TDATA;i++){
for(j=0;j<DIM;j++) fscanf(fin3, "%lf", &test[i][j]); fscanf(fin3, "%d", &tans[i]);
}
/* --- ファイル close --- */ fclose(fin1); fclose(fin2); fclose(fin3);
return 0; }
/* --- */ /* Nearest Neighbor */ /* --- */ int nearest(){
double min1, min2, dist, tmp; /* min1・min2 はクラス1・2に属 */ /* するデータの中で一番近い距離 */ int i, j, k; for(k=0;k<TDATA;k++){ min1 = min2 = 9999999; // 初期値を大きな値に設定 for(i=0;i<LDATA;i++){ /* --- クラス1のデータの中で一番近いデータの距離を測定 --- */ tmp = 0; for(j=0;j<DIM;j++)
tmp += (test[k][j] - class1[i][j]) * (test[k][j] - class1[i][j]); dist = sqrt(tmp);
if(min1 > dist) min1 = dist; /* min1に代入 */
/* --- クラス2のデータの中で一番近いデータの距離を測定 --- */ tmp = 0;
for(j=0;j<DIM;j++)
tmp += (test[k][j] - class2[i][j]) * (test[k][j] - class2[i][j]); dist = sqrt(tmp);
if(min2 > dist) min2 = dist; /* min2に代入 */ }
met1[k] = min1; met2[k] = min2;
/* 最近傍点のクラスを認識結果とする */ if(min1 <= min2) result[k] = 1; else result[k] = 2; } return 0; } /* --- */ /* 結果出力 */ /* --- */ int output(){
FILE *fout; int k;
/* --- ファイル open --- */ fout = fopen("2D-result.dat", "w"); if( fout == NULL){
fprintf(stderr, "file open error : result.dat \n"); return -1; }
/* --- データ出力 --- */
fprintf(fout, "NUM MIN1 MIN2 RES ANS o/x\n"); fprintf(fout, "---\n"); for(k=0;k<TDATA;k++){
fprintf(fout, "%3d %4.2lf %4.2lf %3d %3d " , k+1, met1[k], met2[k], result[k], tans[k]); if(result[k] == tans[k]) fprintf(fout, "o \n"); else fprintf(fout, "x \n"); } /* --- ファイル close --- */ fclose(fout); return 0; }
サンプルプログラムの解説
関数のプロトタイプ宣言
サンプルプログラム内で使用している関数は、
input() 学習データ、テストデータの読み込みを行う nearest() Nearest Neighbor 法
output() 認識結果の出力を行う の 3 つである。 これらの関数を用いるためには、リスト 1 のように関数のプロトタイプ宣言を行う必要がある。 リスト 1 /* --- 関数のプロトタイプ宣言 --- */ int input(); // データ入力
int nearest(); // Nearest Neighbor法 int output(); // 認識結果出力 データの読み込み 入力ファイルの形式は、 データ 1 の 1 次元目 (x の値) データ 1 の 2 次元目 (y の値) データ 2 の 1 次元目 (x の値) データ 2 の 2 次元目 (y の値) データ 3 の 1 次元目 (x の値) データ 3 の 2 次元目 (y の値) .. . ... のようになっている。各データを配列に格納するには、i をデータ番号、j を次元としたとき、以 下のように配列の添字を与えれば良い。 class[i][j] 2 次元配列の第 1 添字をデータ番号、第 2 添字を次元数に割り当てる。したがって、配列の宣言 は以下のように行う。 リスト 2 #define LDATA 20 // 学習データ数 #define TDATA 40 // テストデータ数 #define DIM 2 // データ次元数 double class1[LDATA][DIM]; // クラス 1 の学習データ double class2[LDATA][DIM]; // クラス 2 の学習データ double test[TDATA][DIM]; // テスト (未知) データ
また、データの読み込み部は以下のようになる。 リスト 3 /* --- データの読み込み --- */ for(i=0;i<LDATA;i++){ for(j=0;j<DIM;j++){ fscanf(fin1, "%lf", &class1[i][j]); fscanf(fin2, "%lf", &class2[i][j]); } } for(i=0;i<TDATA;i++){
for(j=0;j<DIM;j++) fscanf(fin3, "%lf", &test[i][j]); fscanf(fin3, "%d", &tans[i]);
}
サンプルプログラム実行の手順
1. 以下の URL より、sample1.c、2D-class1.dat、2D-class2.dat、2D-test.dat をダウンロードす る。 http://www.it.ecei.tohoku.ac.jp/pattern/ 2. サンプルプログラムをコンパイルする。 % cc sample1.c -lm 3. サンプルプログラムを実行する % a.out 4. 実行結果を確認する % less result.datテストプログラム
(u-eigen.c
、
g-eigen.c)
の内容
テストプログラム (u-eigen.c、g-eigen.c) は、N 次元の正方行列の固有値問題・一般固有値問題 を解くプログラムである。固有値、固有ベクトルを出力する。このプログラムでは CLAPACK と いう数値計算ライブラリを使用している。 • u-eigen.c - 固有値問題 Ax = λx を解く • g-eigen.c - 一般固有値問題 Ax = λBx を解くテストプログラム
固有値問題・一般固有値問題を解くテストプログラムを以下に示す。 • 固有値問題 (u-eigen.c) /* 固有値問題 Ax = λ x *//* C interface example for dsyev hidehisa_2006/05/01 */
#include <stdio.h> #include <stdlib.h>
#include <PRT.h> /* #define L (64) */
int main( void ){
FILE *fp; int ds; int N; // 配列の次元数 double A[L][L],E[L],V[L][L]; int i,j; /* --- ファイル --- */ fp=fopen( "NA4.dat", "r"); // 入力ファイル if( fp==NULL ){
fprintf(stderr,"file open error\n"); return(-1); } else{ fscanf(fp, "%d", &N ); // 配列の次元数 for(i=0;i<N;i++) for(j=0;j<N;j++) fscanf(fp,"%lf", &A[i][j] ); // 配列の入力 fclose( fp );
/* --- 関数コール --- */ ds=dsyev(N,A,E,V);
if( ds < 0 ){ perror("dsyev"); exit(2); }
/* --- 固有値・固有ベクトル出力 --- */ fprintf(stderr,"*** Output ***\n\n");
for(i=0;i<N;i++) fprintf(stderr,"\t Eigenvalue"); fprintf(stderr,"\n");
for(i=0;i<N;i++) fprintf(stderr,"\t %9.6lf", E[i] ); fprintf(stderr,"\n\n");
for(i=0;i<N;i++) fprintf(stderr,"\t Eigenvector"); fprintf(stderr,"\n");
for(j=0;j<N;j++){
for(i=0;i<N;i++) fprintf(stderr,"\t %9.6lf", V[i][j] ); fprintf(stderr,"\n"); } fprintf(stderr,"\n"); return( 0 ); } • 一般固有値問題 (g-eigen.c) /* 一般固有値問題 Ax = λ Bx */
/* C interface example for dsygv hidehisa_2006/05/01 */
#include <stdio.h> #include <stdlib.h>
#include <PRT.h> /* #define L ( 64 ) */
int main( void ){
FILE *fp; int ds; int N; // 配列の次元 double A[L][L],B[L][L],E[L],V[L][L]; int i,j; /* --- ファイル --- */ fp=fopen( "NAB4.dat", "r"); if( fp==NULL ){
fprintf(stderr,"file open error\n"); return(-1); }
fscanf(fp, "%d", &N ); // 配列の次元数 if( N > L ){ perror("Input N"); exit(1); }
for(i=0;i<N;i++) for(j=0;j<N;j++) fscanf(fp,"%lf", &A[i][j] ); // 入力 A for(i=0;i<N;i++) for(j=0;j<N;j++) fscanf(fp,"%lf", &B[i][j] ); // 入力 B fclose( fp ); } /* --- 関数コール --- */ ds=dsygv(N,A,B,E,V);
if( ds < 0 ){ perror("dsygv"); exit(2); }
/* --- 固有値・固有ベクトル出力 --- */ fprintf(stderr,"*** Output ***\n\n");
for(i=0;i<N;i++) fprintf(stderr,"\t Eigenvalue"); fprintf(stderr,"\n");
for(i=0;i<N;i++) fprintf(stderr,"\t %9.6lf", E[i] ); fprintf(stderr,"\n\n");
for(i=0;i<N;i++) fprintf(stderr,"\t Eigenvector"); fprintf(stderr,"\n");
for(j=0;j<N;j++){
for(i=0;i<N;i++) fprintf(stderr,"\t %9.6lf", V[i][j] ); fprintf(stderr,"\n");
}
fprintf(stderr,"\n");
return( 0 ); }