신경처리 (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
#include
#include
#include
#include
#define PATTERN 8
#define LROWS 5
#define LCOLS 4
#define KROWS 2
#define KCOLS 5
#define ran() ((rand() % 10000) / 10000.0 / 5) - 0.1
float 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();
}
}