신경처리 (Neural Processing)
여러분은 군중 속에서 어떻게 한 얼굴을 인식하는가? 어떻게 경제학자가 이자율의 방향을 예견할 것인가? 이러한 문제에 부딪혔을 때, 인간의 뇌는 정보를 처리하기 위해 상호 연결된 처리 요소인 신경세포(neuron)의 거미줄 (web)을 이용한다. 각 뉴런은 자치적이며 독립적이다. 이는 비동기적으로 작 동한다는 말이며, 다른 말로 해서 다른 사건에 구애받지 않고 발생한다는 것 이다. 이때 제기되는 두 가지 문제(즉 얼굴을 인식하는 것과 이자율을 예측 하는 것)는 다른 문제와 구별되는 두 가지 특징을 갖는다. 첫째는 문제가 복 잡하다는 것인데, 이는 그 답을 얻기 위해 간단한 step by step 알고리즘이 나 정확한 공식을 만들 수가 없다는 것이다. 둘째는 문제를 풀기 위해 주어 지는 데이터가 역시 복잡하고 잡음이 섞이거나 불완전할 수 있다는 것이다. 여러분이 얼굴을 인식하려 할 때에 안경을 잃어버릴 수도 있다. 경제학자는 자신의 마음대로 처리할 수 있는(이기적으로 조작하는)수천 개의 데이터 조각을 가지고 있을 수 도 있으며 이는 경제와 이자율에 대한 예측을 할 때에 적절할 수도 있고 그 렇지 않을 수도 있다.
생물학적 신경구조에 내재된 엄청난 처리 능력은 그 자체의 구조에 대한 연구를 고무하여 인간이 만든 컴퓨터 구조에 응용할 수 있는 힌트를 제공하 였다. 인공 신경망(Artificial Neural Network)이 이러한 주제이며 인간의 뇌 가 하는 것과 비슷한 방식으로 같은 종류의 까다롭고 복잡한 문제를 풀기 위한 합성 뉴런을 구성하는 방법에 대해 다룬다.
신경망이 초기에 인공지능의 해결책이 될 수 있는 가능성을 제시한 이래 많은 사람들이 신경망 모델의 연구에 참여하였다. 그러나 "퍼셉트론"의 발표 후 그 한계를 증명하는 이론의 영항으로 신경망연구에 열의는 줄었으나 다 시 델타 학습 법칙의 모델이 제시되고 다층 뉴런으로 퍼셉트론의 한계 (ex-or gate의 학습)을 벗어남으로써 다시금 활기를 되찾고 있다.
인간의 두뇌 구조를 흉내낸 신경망이 한동안의 침체기를 벗어나 그 중흥 기를 맞이하는데 큰 기여를 한 것들을 들자면 Error Backpropagation(오류 역전파:BP) 학습 방법을 빼놓을 수 없다. 실제로 가장 널리 사용되는 학습법 중의 하나인 BP는 델타 학습 법칙의 일종이다.
델타 학습 법칙의 기본은 현재 주어진 연결 강도로 생성되는 오차값을 구 하여 이를 감소시키는 방향으로 연결 강도의 값을 조정하는 것으로 이 때 오차값의 계산을 위해 각 노드의 올바른 출력값을 제공해 주어야 한다. 델타 학습 법칙을 이용한 단층의 신경망이 퍼셉트론이지만 이는 간단한 XOR 문 제도 해결하지 못하는 단점을 가진다.
XOR 문제를 해결한 BPN 프로그램은 여기를 눌러 down 받는다.
BP는 이러한 문제을 해결하기 위한 방법의 일종으로 다층의 신경망을 학 습시키는데 적합하다.
다음의 프로그래밍 예제는 전가산기의 학습을 시키는 Turbo C 프로그램이 다.
/* Backpropagation for Lerning Full Adder *//* 입력값은 다음과 같다. input E_min : 0.0001 input n(learning ratio) : 0.75 input N(lambda) : 7 input Maximun learing number : 20000 ---- input x[0] = 캐리 input x[1] = 입력1 input x[2] = 입력2*//* Backpropagation for Lerning Full Adder */#include <stdio.h>#include <conio.h>#include <stdlib.h>#include <time.h>#include <math.h>#define PATTERN 8#define LROWS 5#define LCOLS 4#define KROWS 2#define KCOLS 5#define ran() ((rand() % 10000) / 10000.0 / 5) - 0.1float x[PATTERN][LCOLS] = { {0., 0., 0., -1.}, {0., 0., 1., -1.}, {0., 1., 0., -1.}, {0., 1., 1., -1.}, {1., 0., 0., -1.}, {1., 0., 1., -1.}, {1., 1., 0., -1.}, {1., 1., 1., -1} };float d[PATTERN][KROWS] = { {0., 0.}, {0., 1.}, {0., 1.}, {1., 0.}, {0., 1.}, {1., 0.}, {1., 0.}, {1., 1.} };float w_l[LROWS][LCOLS], w_k[KROWS][KCOLS];float NET_l[LCOLS], z[LROWS];float NET_k[KROWS], OUT[KROWS];float delta_OUT[KROWS], delta_z[LROWS];char c;float n = 0.; /* n initialize */float N = 0.; /* lambda initialize */float EMIN = 0.; /* Emin initialize */float wx = 0., wz = 0., charge = 0., delta_w = 0., E = 0.;int i = 0, j = 0, k = 0, l = 0, m = 0;int number = 0; /* TRAIN number initialize */void Forward_Pass(void);void Backward_Pass(void);void Amend_Weight(void);void Input_x(void);void Print_Weight(void);void Initialize_Weight(void);void Delta_Rule(void);void main(void){ do { clrscr(); printf(" Welcome to Backpropagation !\n"); printf(" --- Perform Full Adder ---\n"); printf(" Input E_min : "); scanf("%f", &EMIN); printf(" Input n(learning ratio) : "); scanf("%f", &n); printf(" Input N(lambda) : "); scanf("%f", &N); printf(" Input Maxium learning number : "); scanf("%d", &number); printf(" Perform Backpropagation [Y/N] : "); } while ((c = getche()) != 'y'); Initialize_Weight(); /* weight initialize */ for (l = 0; l < number; l++) { for (k = 0; k < PATTERN; k++) { Forward_Pass(); Backward_Pass(); Delta_Rule(); Amend_Weight(); } if(E < EMIN) break; E = 0.; } Input_x();}void Forward_Pass(){ /* l */ /* Z = f(NE T ) = f( sigma W1 */ /* i i i */ for (i = 0; i < LROWS; i++) NET_l[i] = 0.; for (i = 0; i < LROWS; i++) { for (j = 0; j < LCOLS; j++) { wx = w_l[i][j] * x[k][j]; NET_l[i] = wx + NET_l[i]; } z[i] = 1. / (1. + exp(-N * NET_l[i])); } z[LROWS - 1] = -1.; /* z[4] = -1 */ for (i = 0; i < KROWS; i++) NET_k[i] = 0.; for (i = 0; i < KROWS; i++) { for (j = 0; j < KCOLS; j++) { wz = w_k[i][j] * z[j]; NET_k[i] = wz + NET_k[i]; } OUT[i] = 1. / (1. + exp(-N * NET_k[i])); }}void Backward_Pass(){ charge = 0.; for(i = 0; i < KROWS; i++) { charge = ((d[k][i] - OUT[i]) * (d[k][i] - OUT[i])) + charge; } E = ((1. / 2.) * charge) + E;}void Delta_Rule(){ /* OUTPUT layer */ for(i = 0; i < KROWS; i++) { delta_OUT[i] = (d[k][i] - OUT[i]) * (1. - OUT[i]) * OUT[i]; } /* hidden layer */ for(i = 0; i < KCOLS; i++) { delta_w = 0.; for(m = 0; m < KROWS; m++) { delta_w = (delta_OUT[m] * w_k[m][i]) + delta_w; } delta_z[i] = z[i] * (1. - z[i]) * delta_w; }}void Amend_Weight(){ for(i = 0; i < KROWS; i++) { for(j = 0; j < KCOLS; j++) { w_k[i][j] = w_k[i][j] + (n * delta_OUT[i] * z[j]); } } for(i = 0; i < KROWS; i++) { for(j = 0; j < KCOLS; j++) { w_l[i][j] = w_l[i][j] + (n * delta_z[i] * x[k][j]); } }}void Input_x(){ float temp; do { clrscr(); printf("E_min = %f \tn = %f \t N = %f\n", EMIN, n, N); printf("training number = %d, E = %f\n", l, E); Print_Weight(); printf("\n\n"); printf("--- x[0] = Carry in, x[1] = input 1, x[2] = input 2 --- \n"); for(i = 0; i < 3; i++) { printf("Input x[%d] of the pattern X :", i); scanf("%f", &temp); x[8][i] = temp; } x[8][3] = -1.; Forward_Pass(); printf("\n"); for(i = 0; i < KROWS; i++) { printf("OUT[%d] = %f \t", i, OUT[i]); } printf("\n--- Where, OUT[0] = Carry out, OUT[1] = Sum ---\n"); printf("\n\nAnother Input ? [y/n] : "); } while ((c = getche()) != 'n');}void Print_Weight(){ printf("\n\n"); printf("[Amended Weight at l layer]"); for(i = 0; i < LROWS; i++) { printf("\n"); for(j = 0; j < LCOLS; j++) { printf(" %+.3f", w_l[i][j]); } } printf("\n\n"); printf("[Amended Weight at k layer]"); for(i = 0; i < KROWS; i++) { printf("\n"); for(i = 0; i < KROWS; i++) { for(j = 0; j < KCOLS; j++) { printf(" %+.3f", w_k[i][j]); } printf("\n"); } }}void Initialize_Weight(){ int i, j; time_t t; srand((unsigned) time(&t)); for(i = 0; i < LROWS; i++) for(j = 0; j < LCOLS; j++) { w_l[i][j] = ran(); } for(i = 0; i < KROWS; i++) for(j = 0; j < KCOLS; j++) { w_k[i][j] = ran(); }}