이 몇 달 동안 혼신을 다 해서 만들고 있는 게 있었습니다.


게임하는 인공지능입니다.


요즘 대세가 기계학습(머신러닝)이잖아요.


제가 만든 것이 아래 영상에 있어요.



저는 이것을 보상중추가 있는 인공신경망이라고 부릅니다.


인공신경망이란, 사람이 공학적인 방법으로 생물의 신경을 모사한 것입니다.


컴퓨터과학자 입장에서 신경망은 데이타가 어떻게 저장될지를 정의하는 추상적인 자료구조입니다.


그리고 그 신경망에 대해 가하는 연산을 정의하는 것은 미리 프로그램된 학습규칙과 보상이라는 생득관념입니다.


여기에서의 학습규칙은 유전 알고리즘이며, 신경망과 그 유전 알고리즘이 학습의 지표로 삼는 보상은 생물이 선천적으로 얻는 상벌의 감각과 같기 때문에 그 보상을 평가하고 그에 따라 신경망을 개선시키는 프로그램을 보상중추라고 했어요.





적용해 보고 싶었던 기계학습 기법은 텍사스 오스틴(대학교)의 Kenneth O. Stanley 박사님이 개발하신 NEAT(NeuroEvolution of Augmenting Topologies)입니다.


이 기법으로 슈퍼마리오를 플레이하는 AI를 유튜브에서 본 이후로 언젠가는 구현해 봐야겠다고 생각했거든요.


처음에는 한두 달 하고 끝내려고 했는데 생각보다 대형 프로젝트더군요.


이걸 만들어야겠다고 처음 생각한 것이 작년 10월에 아래 글을 적을 때부터였던 것 같습니다.


2018/10/09 - [잡담] - 유전자, 염색체, 유전체의 차이점 간단 요약


그리고는 10월 11월에는 언노운치트 같은 사이트 기웃거리면서 게임 해킹하는 법만 실컷 배웠어요.


제일 먼저 인공지능에게 게임을 시킬 수가 있어야 하니까요.


그 이후에는 이 한 편의 논문만 보고 바닥부터 기어올라가면서 A부터 Z까지 모든 것을 한 땀 한 땀 새로 만들었습니다.


개발에 사용한 언어는 C랑 Makefile 아주 조금 빼고는 전부 Go 언어입니다.


Go 언어를 쓴 이유는 두 가지입니다.


- 오픈소스의 정신이 잘 살아 있으며, 돈 없고, 백(Back) 없고, 혼자 필요한 것을 찾아서 공부하는 사람들을 위해서 모든 것이 아주 잘 준비되어 있습니다.


- 몇 번 연습 삼아 클라이언트 수정을 Go 언어로 해 보면서 C/C++ 이상으로 게임 해킹하기 좋은 언어가 Go 언어라는 확신을 얻었기 때문입니다.


2018/10/09 - [Methods] - Go 언어로 메모리 조작과 API 후킹을 구현하기 (윈도우즈 앱 리버스 엔지니어링)

2018/10/09 - [Methods] - 한글을 배운 윈도우 계산기 (Windows 10 UWP 앱 리버싱)


그밖에 Go 언어로 다른 사람들이 만들어 놓은 NEAT 라이브러리가 있는 것은 봤는데 태반은 아예 제대로 작동하지도 않고 영 엉망이더라고요.


실은 처음에는 텍사스 오스틴(NEAT 원작자와 같은 소속 학교)의 염진석씨가 구현해 둔 이것을 그대로 가져다가 쓰려고 했는데...


https://github.com/jinyeom/neat


나중에 코드를 자세히 들여다 보니 개체의 변이인가 교배인가가 제대로 구현이 안 되어 있습니다.


결국 염진석씨 구현에서는 다른 건 안 보고 작명만 좀 퍼왔어요.


yaricom/goNEAT이라는 구현이 오리지널 구현이랑 거의 똑같고 잘 되기는 하는 것 같은데 코딩 스타일이 카멜이 아니라 snake_case라는 점이 마음에 안 들었습니다.


구성이나 인터페이스도 직관적이지 않고요.


그래서 직접 구현했지요.


구현하면서 가장 참고할 만했던 코드는 2002년 당시 원작자의 오리지널 구현이랑 SethBling MarI/O입니다.

나머지 구현은 위 두 구현의 유사품에 불과하거나, 그렇지 않은 경우 참고하기 약간 어려운 점이 있어요.

그렇게 해서 거의 완성해 두고 위 첨부 스샷에 보이는 신경망을 만들어 사진 찍은 것이 올해 3월 초입니다.


나머지 시간에는 하이퍼 파라미터를 깔짝거리고 테스트 수행을 몇 번 하면서 노트북의 GPU/CPU 파워를 혹사시켰을 뿐이죠.


노트북에 물리적인 손상이 있을 때까지 풀가동하였습니다.


내가 가지고 있는 유일한 PC가 그 노트북 한 대뿐임에도 말이에요.


그래서 테스트를 중단한 상태에요.


지금도 팬이 자기 멋대로 돌아가요.


논문에서 대충 읽고 넘어간 부분은 나중에 테스트를 해 보면 무조건 발목을 잡더군요.

결과물은 그렇게 논문을 몇 번 다시 읽고 꼼꼼한 수정을 거쳐서 만든 물건입니다.


아래 깃헙 주소로 올려 두었어요.


https://github.com/NaniteFactory/naneat


아래 주소에서 API 레퍼런스도 제공합니다.


https://godoc.org/github.com/NaniteFactory/naneat


패키지 이름은 NaNEAT이고, OpenGL 백엔드로 신경망의 실시간 시각화를 지원하는 것이 다른 라이브러리와의 차별점입니다.


멀티 스레딩 기반으로, Go의 장점을 십분 발휘하여 자원의 수직적 확장이 아닌 수평적 스케일링이 가능토록 설계된 것이 특징입니다.


그러니까, 이 라이브러리로 만든 서비스에 Dockerfile 작성해서 잔뜩 배포하면 꽤 어려운 문제도 재미있게 쉽게 풀 수 있지 않을까 하고 기대하고 있습니다.



  • 심교훈 2019.04.29 14:36 신고 LINK EDIT/DELETE REPLY

    저도 관심을 갖는 분야인데, 저도 간단한 것부터 도전해봐야겠네요 ㅎㅎ

  • Cashy 2019.05.03 04:08 신고 LINK EDIT/DELETE REPLY

    흥미롭네요. 구현도 굉장히 잘 하신 것 같고, youtube 영상에서 날아다니는 벌들?을 바로 쏘지 못하는 것은 좀 아쉽지만 학습도 나름대로 잘 되는 것 같네요. 저는 이제 공부를 시작하려고 해서... 나중에 내공이 쌓이면 구현하신 코드 github에서 참고해서 보겠습니다. 감사합니다.

    • 코코넛냠냠 2019.05.04 06:54 신고 LINK EDIT/DELETE

      한국에서 이런 주제로 공부하고 있거나 그걸 직접 구현해 본 사람이 많이 있지는 않을 거에요. 이렇게 희박한 관심 주제에 칭찬과 댓글을 주셔서 반갑기도 하고 너무 고맙습니다. >_< (깃헙 별 하나 받아서 기분 좋아졌음) 만약에 비슷한 것을 만드신다면 참고용으로는 솔직히 K. O. Stanley가 C++로 구현한 것보단 제 구현이 훨씬 더 잘 읽힐 거라고 자신합니다. ㅋㅋㅋㅋ

      유전 알고리즘의 묘미는 주어진 문제에 대해서 보통 사람이 예상하지 못하는 해결방안을 찾는다는 점인데요. 영상에서 마지막에 날아다니는 것들을 총 쏴 맞혀서 죽이지 않고 저렇게 한 자리에 앉아서 칼질만으로 전부 다 잡는다는 것은 보통 사람이 찾아낼 수 있는 답안은 아니지요.

      왜 그런 선택을 할까 생각해 보면. 이 신경망에게는 점수가 커다란 보상이거든요. 적에게 칼을 한 번 꽂으면 500점이 오르고 총알을 맞추면 100점이에요. 총을 쏴서 맞춰서 죽이는 경우보다 칼을 꽂아서 죽일 때 점수가 높게 평가되니까 저런 식으로 이상하게 진화한 것 같아요. 그래서 저는 이 놈을 '어쌔신 크리드'라는 이름으로 불렀습니다.

      학습 초창기에는 더 별의 별 놈들이 많아요. 적이 공격하는 타이밍에 맞춰서 점프로 공격을 피하는 애도 있고, 좌우로 스프링클러처럼 총알을 흩뿌리면서 앞으로 전진하는 개체 등등... 아무튼 몇 세대인가 지나면서는 한 자리에 앉아서 총을 천천히 쏘는 쪽으로 변하더라고요. 별난 개체를 전부 보존할 만한 (알고리즘의 여유나) 컴퓨팅 자원이 없어서 제일 안정적인 애들 빼고는 전부 읍참마속을 하거든요.

      그래서 지금 이 메탈슬러그 인공지능의 문제점은... 아쉽다고 보신 것처럼 점수에 너무 집착한다는 점이 첫째고 다음으로 입력이 약간 부실하다는 점인데 다른 알고리즘으로 특징을 미리 추출한 뒤에 그걸 이 유전 알고리즘에 입력으로 넘겨 주면 1탄 정도는 깰 만큼으로 꽤나 괜찮게 할 수 있지 않을까 해요. 테스트를 해 보고 싶지만 지금으로서는 CPU 파워도 부족하고 제가 다른 게임에 빠져서 미뤄 둔 상태네요. ㅋㅋ

Top