02 Nov 2017
|
머신러닝
강화 학습(Reinforcement Learning)은 일련의 행동 후에 보상이나 평가가 주어질 때 사용할 수 있는 학습 방법입니다.
여기서는 강화 학습 중 Q-Learning 방법에 대해서 C++ 예제를 살펴보도록 하겠습니다.
Q Value는 어떤 상태에서 취해야 할 각각의 행동들에 대한 지표가 되는 수치입니다.
무작위 행동을 하면서 특정 보상에 도달한 행동에 대해서는 적절한 보상을 해주고 보상에 따라 Q Value를 업데이트 해주면서 결국 정답에 가까운 행동을 할 수 있도록 자연스럽게 유도하는 학습 방법입니다.
여기서는 Q-Learning 방법을 이용하여 미로 찾기를 하는 강화 학습 코드를 살펴보도록 하겠습니다.

위 그림과 같은 미로가 있으며, 가장 마지막 노드인 s14에 도착하면 보상을 주도록 했습니다.
C++ 예제 코드
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static const int NODE_COUNT = 15;
static const double EPSILON = 0.3f;
static const int LEARNING_COUNT = 1000; // 학습 횟수
static const double GAMMA = 0.9f; // 할인율
static const double ALPHA = 0.1; // 학습 계수
int getRandom(int max) {
return rand() % (max + 1);
}
double getRandom() {
return (double)rand() / RAND_MAX;
}
void printQTable(int qtable[NODE_COUNT]) {
for (int i = 1; i < NODE_COUNT; i++) {
printf("%d\t", qtable[i]);
}
printf("\n");
}
void initQTable(int qtable[NODE_COUNT]) {
for (int i = 0; i < NODE_COUNT; i++) {
qtable[i] = getRandom(100);
}
printQTable(qtable);
}
int getNextNode(int pos, int qtable[NODE_COUNT]) {
int left = 2 * pos + 1;
int right = 2 * (pos + 1);
int nextNode;
if (getRandom() < EPSILON) {
if (getRandom(1) == 0) {
nextNode = left;
}
else {
nextNode = right;
}
}
else {
if (qtable[left] > qtable[right]) {
nextNode = left;
}
else {
nextNode = right;
}
}
return nextNode;
}
int updateQTable(int pos, int qtable[NODE_COUNT]) {
int left = 2 * pos + 1;
int right = 2 * (pos + 1);
int qvalue = 0;
int qmax;
if (pos > 6) {
if (pos == 14) {
qvalue = qtable[pos] + ALPHA * (1000 - qtable[pos]);
}
else {
qvalue = qtable[pos];
}
}
else {
if (qtable[left] > qtable[right]) {
qmax = qtable[left];
}
else {
qmax = qtable[right];
}
qvalue = qtable[pos] + ALPHA * (GAMMA * qmax - qtable[pos]);
}
return qvalue;
}
int main() {
srand(time(NULL));
int nodeId;
int qtable[NODE_COUNT];
initQTable(qtable);
for (int i = 0; i < LEARNING_COUNT; i++) {
nodeId = 0;
for (int j = 0; j < 3; j++) {
nodeId = getNextNode(nodeId, qtable);
qtable[nodeId] = updateQTable(nodeId, qtable);
}
printQTable(qtable);
}
return 0;
}
02 Nov 2017
|
머신러닝
귀납적 학습(Inductive Learning)은 구체적인 데이터 값들을 기반으로 특정 규칙이나 지식을 학습하는 것을 말합니다.
간단한 예제를 살펴보도록 하겠습니다. 다음과 같은 데이터가 있다고 가정합시다.
| A |
B |
C |
D |
E |
F |
G |
H |
I |
J |
Value |
| 1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
1 |
| 0 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
| 0 |
1 |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
| 1 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
1 |
1 |
| 1 |
0 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
1 |
A부터 J까지의 값의 패턴에 따라 Value 값이 결정이 된다고 할 때, 과거 기록들을 학습하면 향후 A부터 J까지의 값이 주어지면 Value 값을 추측할 수 있습니다.
다음 코드는 Value 값을 예측할 수 있는 A부터 J까지의 값들의 패턴을 추출하는 예제입니다.
C++ 예제 코드
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static const int DATA_COUNT = 30;
static const int COMPANY_COUNT = 10;
// File에서 데이터를 읽어들임
void loadData(int data[DATA_COUNT][COMPANY_COUNT], int value[DATA_COUNT]) {
freopen("data.txt", "r", stdin);
setbuf(stdout, NULL);
for (int i = 0; i < DATA_COUNT; i++) {
for (int j = 0; j < COMPANY_COUNT; j++) {
scanf("%d", &data[i][j]);
}
scanf("%d", &value[i]);
}
}
// 초기 AnswerSheet는 랜덤값으로 설정
void createRandomAnswerSheet(int answer[COMPANY_COUNT]) {
for (int i = 0; i < COMPANY_COUNT; i++) {
answer[i] = (rand() % 3);
}
}
// 점수 계산
int getScore(int data[DATA_COUNT][COMPANY_COUNT], int value[DATA_COUNT], int answer[COMPANY_COUNT]) {
int score = 0;
for (int i = 0; i < DATA_COUNT; i++) {
int point = 0;
for (int j = 0; j < COMPANY_COUNT; j++) {
if (answer[j] == 2) {
point++;
} else if (answer[j] == data[i][j]) {
point++;
}
if ((point == COMPANY_COUNT) && (value[i] == 1)) {
score++;
} else if ((point != COMPANY_COUNT) && (value[i] == 0)) {
score++;
}
}
}
return score;
}
int main() {
srand(time(NULL));
int score;
int data[DATA_COUNT][COMPANY_COUNT];
int value[DATA_COUNT];
int answer[COMPANY_COUNT];
int bestScore = 0;
int bestAnswer[COMPANY_COUNT];
loadData(data, value);
for (int i = 0; i < 100000; i++) {
createRandomAnswerSheet(answer);
score = getScore(data, value, answer);
if (score > bestScore) {
for (int j = 0; j < COMPANY_COUNT; j++) {
bestAnswer[j] = answer[j];
}
bestScore = score;
for (int j = 0; j < COMPANY_COUNT; j++) {
printf("%1d ", bestAnswer[j]);
}
printf(" --> score: %d\n", bestScore);
}
}
printf("\n\n");
for (int j = 0; j < COMPANY_COUNT; j++) {
printf("%1d ", bestAnswer[j]);
}
printf(" --> score: %d\n", bestScore);
return 0;
}
</pre>
코드를 간략하게 설명하면,
1. 무작위로 랜덤 패턴을 생성(createRandomAnswerSheet)
이 때 패턴은 숫자 0, 1, 2를 사용(2는 와일드 카드)
2. 생성된 랜덤 패턴과 과거 데이터 기록과 유사성 비교
유사성이 있으면 score + 1
3. 가장 높은 score를 획득한 AnswerSheet 추출
과 같습니다.
과거 데이터 기록의 패턴은 0 또는 1의 값을 가지지만 항상 일정한 규칙을 갖고 있는 것은 아니기 때문에 와일드카드(Wildcard)인 2라는 값을 사용합니다.
랜덤 패턴과 과거 데이터 기록 유사성은 A부터 J까지 각 값들을 비교해서 유사성이 있으면 `point` 변수를 1 씩 증가시킵니다.
그리고 해당 패턴과 일치하면서 `Value` 값이 `1`이 되거나, 해당 패턴과 일치하지 않으면서 `Value` 값이 `0`이 되는 경우에 `score` 값을 1 증가시켜줍니다.
14 Sep 2017
|
C++
socketpair
주로 프로세스간 통신을 위해 소켓을 생성할 때 사용하는 방법 중 하나입니다. socketpair() 함수는 주소를 갖지 않는 한 쌍의 소켓을 생성해줍니다. 이 소켓들을 이용해서 부모 프로세스와 자식 프로세스간 통신을 수행할 수 있습니다.
예제 코드
예제 코드는 다음과 같습니다.
#include <cstdio>
#include <unistd.h>
#include <sys/socket.h>
#include <cstring>
#include <sys/wait.h>
int main(int argc, char **argv) {
int ret, socket_fd[2];
char buffer[] = "hello. snowdeer.";
char line[BUFSIZ];
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fd);
if (ret == -1) {
perror("socketpair error!!");
return -1;
}
printf("socket 1 : %d\n", socket_fd[0]);
printf("socket 2 : %d\n", socket_fd[1]);
pid_t pid;
int status;
if ((pid = fork()) < 0) {
perror("fork error!!");
} else if (pid == 0) {
write(socket_fd[0], buffer, strlen(buffer) + 1);
printf("Data send : %s\n", buffer);
close(socket_fd[0]);
} else {
wait(&status);
read(socket_fd[1], line, BUFSIZ);
printf("Data received : %s\n", line);
close(socket_fd[1]);
}
return 0;
}