ITS's Dev Story

어떤 자연수 n이있을때 d(n) n 자릿수 숫자들과 n자신을 더한 숫자라고 정의한다.


예를 들어 d(91)=9+1+91=101


이때, n d(n) 제네레이터(generator)라고 한다위의 예에서 91 101의 제네러이터이다


어떤 숫자들은 하나 이상의 제네레이터를 가지고 있는데, 101의 제네레이터는 91 아니라 100 있다.  그런데 반대로, 제네레이터가 없는 숫자들도 있으며, 이런 숫자를 인도의 수학자 Kaprekar 셀프 넘버(self-number) 이름 붙였다예를 들어 1,3,5,7,9,20,31 셀프 넘버들이다


이제 문제를 내겠다. 1 이상이고 5000보다 작은 모든 셀프 넘버들의 합을 구하는 프로그램을 작성하라

문제는 게임 회사인 넥슨의 입사문제라고 알려진 문제들 제일 쉬운 것이다.



6. 2진수 변환, 진법 변환

14 2진법으로 고칠 어떤 과정을 거칠까?

14 / 2 = 7 … 0

7 / 2 = 3 … 1

3 / 2 = 1 …. 1


따라서 1110 된다.


십진법 n 2진법으로 나타내는 함수는 f(n, 2) 정의할 있다.

그럼, 십진법 n 2보다 작은 경우 n 출력, 2보다 크거나 같은 경우 점화식을 f(n/k, k), printf(“%d”, n % k); 정리할 있다.




2진법 다른 진법으로 변환하고자 하는 경우 f(n, 2) 부분을 f(n, k) 바꾸기만 하면 된다.

11진법-16진법으로 변환하고자 하는 경우 알파벳을 출력하는 부분을 만들어야 하므로 아래와 같이 서식 문자를 바꾼다

(서식문자만 바꾸면 -> %X[대문자], %x[소문자]) 


아래 예시 코드는 16진법까지 변환할 수 있다.



20진법까지 바꾸고자 하는 경우, char letter[21] =“0123456789ABCDEFGHIJ";  변수를 만들어 변수 내 값을 꺼낸다. 이 부분은 한번 생각해보길 바란다.


7. 우박수(3n+1) Basic


1. 어떤 자연수 입력되면,  

2. n 홀수이면 3n + 1 하고,  

3. n 짝수이면 n/2  한다.  

4. n 1 될때까지 2, 3 과정을 반복한다.  


예를 들어 5 5 → 16 → 8 → 4 → 2 → 1  된다.  

처럼 어떤 자연수 n 입력되면 알고리즘에 의해 1 되는 과정을 모두 출력한다.


솔직히 이 문제는 문제에서 해결 전략을 다 줬다... 종료 조건만 생각하면 된다.



8. 우박수(3n+1) reverse


1. 어떤 자연수  입력되면,  

2. n 홀수이면 3n + 1 하고,  

3. n 짝수이면 n/2  한다.  

4.  n 1 될때까지 2, 3 과정을 반복한다.  


예를 들어 5 5 → 16 → 8 → 4 → 2 → 1  된다.  

 처럼 어떤 자연수 n 입력되면  알고리즘에 의해 1 되는 과정을 모두 출력한다.


근데, 이 순서의 역순을 출력하고자 한다.

즉, 1, 2, 4, 8, 16, 5가 출력되어야 한다.



반대로 한다 (...) 


조건은 같으나 n == 1인 경우 printf("1\n"); 해주고 더 이상 함수를 호출하지 않도록 한다.


9. 삼각형 출력하기 (반대로)


5가 입력되면

*****

****

***

**

*


을 출력한다.


이 문제는 * 출력과, \n 출력할 부분을 구별해 주면 된다.



10. 피보나치 수열 (Large)


자연수 N 입력받아 N번째 피보나치 수를 출력하는 프로그램을 작성하시오.  

, N 커질 있으므로 출력값에 10,009 나눈 나머지를 출력한다.


이 문제는 Large 문제이므로 이전에 계산한 값을 메모리에 저장함으로써 

동일한 계산의 반복 수행을 제거하는 메모이제이션 기법으로 프로그램을 작성한다.





재귀 함수는 자기 자신을 호출하는 함수이다


프로그램 실행 재귀 함수를 만나면, 현재 위치를 저장하여 호출할 함수의 주소로 넘어가 함수 내용을 수행한다

함수 실행이 끝나면 원래 위치로 복귀해 다음 코드를 수행한다.


무한 루프에 빠지지 않기 위해 종료 조건이 있어야 하고, 코드를 단순화할 있다. 무리하게 호출하면 스택 공간을 이용한다는 재귀함수의 특성 때문에스택 오버플로우 일어날 있다.


1. 1부터 n까지 출력하기

scanf로 n을 입력받은 후, solve(1) 호출, 이후, solve(n+1)을 수행해 n과 a가 같으면 종료 (종료조건)


따라서, 소스코드는 아래와 같이 쓸 수 있다.


2. n부터 1까지 출력하기 (역순)

solve 함수에서 printf와 solve(n+1) 위치만 바꾸어 주면 된다...



3. 두 수 사이의 홀수 출력하기

1부터 n까지 출력하는 소스 코드에서 홀수를 찾아 모두 출력한다.

if(n % 2 != 0) 조건을 사용하면 된다.



4. 팩토리얼 출력하기

n을 입력받아 n!을 출력한다.

예를 들어, n이 5인 경우, 5! = 5 * 4 * 3 * 2 * 1 = 120을 출력한다.


이 경우에는 점화식을 세워야 한다. 1!은 1이 되므로 종료조건을 아래와 같이 설정하며,

아닌 경우에는 아래와 같이 점화식이 도출되도록 Code를 작성한다.


f(5)

= 5 * f(4)

= 5 * 4 * f(3)

= 5 * 4 * 3 * f(2)

= 5 * 4 * 3 * 2 * f(1)

= 5 * 4 * 3 * 2 * 1

= 120


따라서, 여기서의 점화식은 n * f(n-1) 이다.



5. 피보나치 수열

N번째 피보나치 수를 출력한다. 이 경우에도 점화식을 세운다.


피보나치 수열의 기본 규칙은 처음 항은 1이고 번째 항부터 이전 항의 합이 된다

, 1, 1, 2 다음부터 1+2  다음 항이 된다 다음은 2+3=5 된다.


따라서, 종료조건 if(n==0) return 0, if(n==1) return 1;

점화식은 f(n-1) + f(n-2) 세우면 된다.









우리가 흔히 '1부터 n까지 더한 값을 출력하는 프로그램을 작성하라' 는 문제를 보면,


대부분이 이렇게 작성할 것이다.

#include <stdio.h>

int main() { int a, sum=0; scanf("%d", &a); for(int i = 1; i <= a; i++) { sum = sum + i; //물론 sum += i로 쓸 수도 있다. } printf("%d", sum); }


그리고, 출력값은 위와 같이 표시될 것이다.


만약, 이 출력값을 반복문 없이 구현하라고 하면 어떻게 구현할 것인가? 

재귀부터 시작해서 등차수열의 합 공식 등... 다양한 방법이 있을 것이다.


그 중 우리는 재귀함수를 사용해서 점화식으로 구현해 보도록 하겠다.


<풀이방법 1> 


먼저, 함수 f(n)은 1부터 n까지의 합으로 정의할 수 있다.


f(1) = 1

f(2) = 1 + 2


....


f(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9

f(10) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10


뭔가 공통점이 보이지 않는가? [f(n), n=10] 을 구하고자 할 때 f(9)는 1 ~ n-1의 합을 구하는 것이고, 

f(10)은 1부터 n까지의 합을 구하는 것이다. 따라서 f(10)은 아래와 같이 작성할 수 있다.


f(10) = f(9) + 10


이를 일반화하면



으로 쓸 수 있다.


이를 코드로 작성하면


으로 작성할 수 있는 것이다. n에 1이 들어가면 1~1의 합이니 return 1, 

1이 아니라면 함수 내 재귀를 수행하는 것이다.


<풀이방법 2>


f(10) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10일 때,

f(5) = 1 + 2 + 3 + 4 + 5 이다.


이를 f(10) = f(5) + 6 + 7 + 8 + 9 + 10 로 표현할 수 있으며,

f(10) = f(5) + (5 + 1) + (5 + 2) + (5 + 3) + (5 + 4) + (5 + 5) 로 표현 가능하다.


정리하면, 

f(10) = f(5) + 1 + 2 + 3 + 4 + 5 + 5 + 5 + 5 + 5 + 5


이 식에서 굵게 표시한 부분은 또 f(5)로 치환할 수 있다. 

빨간색으로 표현한 부분은 5*5로 5의 제곱으로 표현 가능하다.


따라서 f(10) = 2f(5) + 5^2 로 표현할 수 있으며, 입력한 n은 10이므로


로 나타낼 수 있다.


그러나, 이 식은 홀수에서는 표현할 수 없는 식이다. 홀수를 나누면 소수점이 남기 때문.

따라서, 일반항에 가우스를 씌우고 (0.5 잃음), 가우스에서 잃었던 0.5 + n/2할때 생기는 0.5를 더해준다.


그럼 아래와 같이 정리할 수 있다. 


이를 홀, 짝 구분없이 쓸 수 있도록 정리하면, 아래와 같이 표현 가능하다.


프로그래밍 언어에서는 정수를 나누면 소수점 이하를 버리므로 아래와 같이 프로그램을 작성할 수 있다.



직접 여러 숫자를 대입해 보면서 풀어보면 잘 이해가 될 것이다.

OpenBCI로 마인크래프트의 스티브를 움직이는 방법을 알아보겠다. 



OpenBCI 소프트웨어의 최신 버전은 OSC 통신을 지원, Focus 감지 기능을 지원하므로, 이를 이용하면 스티브를 앞으로 움직이기 위해 키보드로 'W' 키를 누르는 과정을 파이썬 소프트웨어로 대체할 수 있다.


1. Python 3.6 버전을 다운받는다. (2.7 버전 안됨! +_+)

 * 이미 파이썬 2.7 버전을 설치한 경우에는 2.7 버전을 지우고, 3.6 설치, 환경변수 재설정 등 각종 설정을 다시 해야 한다. 

 * 다운로드 링크 : https://www.python.org/downloads/release/python-362/


2. Muselab 다운로드

 - Muselab은 OSC 데이터 분석 등에 유용하게 사용되므로, 다운받을 필요가 있다. 다운받지 않아도 프로그램을 동작하는데 문제는 없지만, 신호를 분석할 때에는 필요가 있다.

 * 다운로드 링크 : https://storage.googleapis.com/ix_downloads/musesdk-3.4.1/musesdk-3.4.1-windows-installer.exe


3. OpenBCI S/W 실행, OSC 프로토콜 설정 및 Focus 측정



OpenBCI에서 위와 같이 Control Panel 설정을 변경한다. (설치 및 실행 관련은 전 포스팅에서 설명했으므로 생략한다.)


[Networking] - 프로토콜 OSC로 변경, Data Type은 Focus, IP (127.0.0.1) Port(12345) 변경



위는 Focus Widget인데, focus를 측정하는 소스코드는 아래와 같다.


<W_focus.pde> 소스코드 중 일부


float alpha_avg = 0, beta_avg = 0; boolean isFocused; float alpha_thresh = 0.7, beta_thresh = 0.7, alpha_upper = 2;

<중략>


void update()

{


<중략>

alpha_avg = alpha_avg / alpha_count; beta_avg = beta_avg / beta_count; if (alpha_avg > alpha_thresh && alpha_avg < alpha_upper && beta_avg < alpha_thresh)

{ isFocused = true; }

else

{ isFocused = false; }

}

if (alpha_avg > 0.7 && alpha_avg < 2 && beta_avg < 0.7)


조건에 걸리는 경우, isFocused = true 를 OSC로 보낸다.


4. 제공하는 Python 소스코드를 이용해서, 마무리한다.


아래 소스코드를 SerialFocusAdvanced.py 로 저장하고 실행한다. 없는 라이브러리는 pip을 이용해 받아 준다.

import argparse
import ctypes
from ctypes import wintypes
import time

user32 = ctypes.WinDLL('user32', use_last_error=True)

INPUT_MOUSE    = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2

KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP       = 0x0002
KEYEVENTF_UNICODE     = 0x0004
KEYEVENTF_SCANCODE    = 0x0008

MAPVK_VK_TO_VSC = 0

# msdn.microsoft.com/en-us/library/dd375731
VK_TAB  = 0x09
VK_MENU = 0x12
VK_W = 87

# C struct definitions

wintypes.ULONG_PTR = wintypes.WPARAM

class MOUSEINPUT(ctypes.Structure):
    _fields_ = (("dx",          wintypes.LONG),
                ("dy",          wintypes.LONG),
                ("mouseData",   wintypes.DWORD),
                ("dwFlags",     wintypes.DWORD),
                ("time",        wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

class KEYBDINPUT(ctypes.Structure):
    _fields_ = (("wVk",         wintypes.WORD),
                ("wScan",       wintypes.WORD),
                ("dwFlags",     wintypes.DWORD),
                ("time",        wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

    def __init__(self, *args, **kwds):
        super(KEYBDINPUT, self).__init__(*args, **kwds)
        # some programs use the scan code even if KEYEVENTF_SCANCODE
        # isn't set in dwFflags, so attempt to map the correct code.
        if not self.dwFlags & KEYEVENTF_UNICODE:
            self.wScan = user32.MapVirtualKeyExW(self.wVk,
                                                 MAPVK_VK_TO_VSC, 0)

class HARDWAREINPUT(ctypes.Structure):
    _fields_ = (("uMsg",    wintypes.DWORD),
                ("wParamL", wintypes.WORD),
                ("wParamH", wintypes.WORD))

class INPUT(ctypes.Structure):
    class _INPUT(ctypes.Union):
        _fields_ = (("ki", KEYBDINPUT),
                    ("mi", MOUSEINPUT),
                    ("hi", HARDWAREINPUT))
    _anonymous_ = ("_input",)
    _fields_ = (("type",   wintypes.DWORD),
                ("_input", _INPUT))

LPINPUT = ctypes.POINTER(INPUT)

def _check_count(result, func, args):
    if result == 0:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

user32.SendInput.errcheck = _check_count
user32.SendInput.argtypes = (wintypes.UINT, # nInputs
                             LPINPUT,       # pInputs
                             ctypes.c_int)  # cbSize

# Functions

def PressKey(hexKeyCode):
    x = INPUT(type=INPUT_KEYBOARD,
              ki=KEYBDINPUT(wVk=hexKeyCode))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

def ReleaseKey(hexKeyCode):
    x = INPUT(type=INPUT_KEYBOARD,
              ki=KEYBDINPUT(wVk=hexKeyCode,
                            dwFlags=KEYEVENTF_KEYUP))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

import argparse
import math
import time

from pythonosc import dispatcher
from pythonosc import osc_server

def eeg_handler1(unused_addr, args, ch1):
    print("Blink: ", ch1)

def eeg_handler2(unused_addr, args, ch1):
    print("Jaw Clench: ", ch1)

def eeg_handler3(unused_addr, args, ch1):
    print("Open Bci Focus: ", ch1)
    if(ch1):
        PressKey(VK_W)
        time.sleep(0.25)
        ReleaseKey(VK_W)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--ip", default="0.0.0.0", help="The ip to listen on")
    parser.add_argument("--port", type=int, default=12345, help="The port to listen on")
    args = parser.parse_args()

    dispatcher = dispatcher.Dispatcher()
    dispatcher.map("/debug", print)
    dispatcher.map("/muse/elements/blink", eeg_handler1, "Blink")
    dispatcher.map("/muse/elements/jaw_clench", eeg_handler2, "Jaw_clench")
    dispatcher.map("/openbci", eeg_handler3, "Jaw_clench")

    server = osc_server.ThreadingOSCUDPServer((args.ip, args.port), dispatcher)
    print("Serving on {}".format(server.server_address))
    server.serve_forever()


4. 마인크래프트를 실행, 스티브를 움직여 본다.



최근 서울대학교 과학영재교육원에서 OpenBCI를 이용한 탐구를 진행하고 있다. 


절차상으로도 굉장히 쉽고, 이 내용이 OpenBCI 사이트에 설명이 잘 되어있긴 하지만, 이 글이 내 개인적으로도 (OpenBCI를 Windows PC에 설치할 때마다 헷갈린다), OpenBCI를 공부하거나 연구하는 사람들에게도 도움이 될 것 같아 방법을 정리하여 올린다.




 

<준비물>

OpenBCI Cython (구. OpenBCI 32Bit Board Kit), 골든 일렉트로드 케이블, USB (BLE) 동글, 배터리 박스(?), 배터리, 

Ten20 (전극 테스트를 할 때 필요), Windows 10(아무 Windows든 사실 큰 상관 없음) 이 설치된 PC, 그리고 차 한잔???

 

* 차 한잔은 OpenBCI를 개발하는데 전혀 아무런 영향을 미치지 않습니다... 자신의 정신건강에 도움이 될 뿐 ㅋㅋㅋㅋ

 

1. OpenBCI USB 동글은 FTDI 드라이버로 이루어져 있다. 아래 사이트에서 FTDI 드라이버를 설치한다.

 * 만약 설치하지 않았다면 USB Serial Port가 장치 관리자에서 찾을 수 없다고 뜰 것이고, OpenBCI 프로세싱 기반 소프트웨어에서 포트를 감지하지 않을 것이다.

 

http://www.ftdichip.com/Drivers/VCP.htm

 

사이트 하단에 있는 드라이버를 다운받는다. 운영체제와 자신의 컴퓨터 비트 수를 확인해서 알맞는 드라이버를 다운받는다.

 * 비트수를 확인하려면 Windows 10에서 [내 PC] - 우클릭/[속성] - 시스템 종류를 확인.

 

2. 배터리 박스에 배터리를 장착하고, OpenBCI 본체의 스위치는 PC로 향하게 한다 [BLE/OFF 안됨]. USB 동글의 스위치는 GPIO_6으로 향하게 한다.

 

 * 잠깐! 일반 배터리를 OpenBCI에 연결하면 보드의 파워 서플라이가 나갈 수 있기 때문에, 배터리 박스를 OpenBCI를 구입 시 사용하도록 같이 넣어준 것이다. 절대로 배터리 박스 없이 배터리를 끼우지 말 것!

 

3. http://openbci.com/index.php/downloads 에서 알맞는 버전을 다운받는다.

난 GUI 1.0.0 [64비트 다운로드 링크 : http://openbci.com//apps/v100/application.windows64.zip] 을 다운받았다.

나처럼 하고 싶다면 OpenBCI GUI 최신버전 밑 [ALL OPENBCI GUI RELEASES...] 를 클릭하면 된다.

 

4. 소프트웨어를 실행시켜, 아래와 같이 따라한다.

 

1. LIVE (최신 버전에서는 Cython으로 보일 것이다.) 를 클릭한다.

2. 동글의 포트를 클릭한다. 그리고 SD 카드 옵션이 있는데 자신의 OpenBCI에 SD카드가 끼워져 있고, 한번 해보고 싶다는 사람은 해봐도 무방하다. 안해도 OpenBCI를 탐구하는데 지장은 크지 않은 것 같다.

3. 그리고 Start System을 누른다.

 

 

 

여기까지 따라해서 이 화면이 떴다? 축하한다. 당신의 OpenBCI랑 PC는 연결된 상태라는 것을 증명해 주는 화면이다.

이제 Start Data Stream을 누르고 OpenBCI의 전극을 만져보자. (금색 부분) 아마 뇌파가 요동치는 걸 떠나 최고점에 닿을 것이다.

 

 

이제 골드컵 일렉트로드 케이블에 Ten20 크림을 바르고, 자신의 머리 뇌파를 측정해 보자.

지난 2016년 1월부터, 9개월간의 과정을 거쳐 개발된 프로젝트인 '시각장애인을 위한 이미지 설명 프로그램' 을 공개합니다!


< 소프트웨어의 모든 저작권은 ITSC(Taewon Kang) 에게 있으며, 무단 도용 및 복제, 전송 등의 행위는 저작권법에 의해 처벌받을 수 있음 >


[개발 프로그램 요약]



시각장애인을 위한 이미지 설명 프로그램 (Image Explanation Software for the Blind) 은 눈이 안 보이는 시각장애인에게 보이지 않는 상황에 대해 문장으로 설명하고, 설명된 데이터를 TTS (Text-To-Speech)를 이용, 실시간으로 설명을 들을 수 있도록 개발한 소프트웨어이다. (2016.10.22 수정) 현재도 업그레이드 중이다.


머신러닝 오픈소스 Dataset, Tensorflow를 이용 (오픈소스를 활용하였습니다. 혹여나 오해 없길 바랍니다.) 하므로 가장 정확한 문장 결과를 도출할 수 있으며, 지속적인 학습으로 어떠한 이미지든지 올바른 결과를 도출할 수 있다.

이미지 설명 프로그램의 알고리즘은 촬영된 이미지를 카메라의 Face Recognition처럼 CNN 알고리즘 (특정 부위를 잡아 단어로 설명하는 알고리즘) 을 이용해 단어로 변환이 가능한 특정 부위 각각을 Machine Learning (group of people, table 등) 후 단어를 도출해 낸다. 이후 이 단어들을 바탕으로 RNN 알고리즘이 부가 단어 (a, standing, around 등)를 생성한다. 마지막으로 이를 조합하는 알고리즘을 사용하여 조합하면 단어가 완성된다. 위의 이미지를 참고하면 이해가 쉬울 것이다.

 

현재까지 Image Caption에 대한 연구는 활발히 진행되고 있지만, 시각장애인을 위한 이미지 설명 프로그램은 존재하지 않았다.


시각장애인을 위한 이미지 설명 프로그램은 Raspberry Pi 등의 Embedeed Device를 활용하여 시각장애인이 혼자서도 이미지를 촬영하여 TTS로 설명을 들을 수 있고, 이를 적정기술로 활용해 경제, 사회적으로 약자인 시각장애인 뿐만이 아닌 오지, 즉 제3세계에도 보급이 가능하다. 또한 청소년들이 장비 조립방법, 머신러닝 이론 등을 배울 수 있도록 소프트웨어 교육과도 연계시킬 수 있다.

 

(2016 제 33회 한국정보올림피아드 공모부문 은상 수상작)


[ 경력 사항 ]


수락중학교 2학년, 강 태 원 (Taewon Kang) – itsckr.tistory.com

8살 때 우연히 한 형이 학교홈페이지에 올려놓은 자바스크립트 소스코드를 보고 흥미를 느껴 독학으로 C언어를 깨쳤고, 


10살 때부터는 쉽고 재미있게 IT를 가르쳐 주는 블로그 ‘IT스쿨’을 열어 내가 터득한 IT지식들을 다른 사람들과 공유하였다. 


나는 호기심이 많고 재미있는 아이디어가 많다. 그래서 나의 아이디어를 바로 구현할 수 있는 코딩의 매력에 점점 빠졌고, 사람과 자연을 생각하는 소프트웨어 아키텍트가 되고 싶다는 꿈을 갖게 되었다.


영재교육원의 교육을 꾸준히 받음과 동시에 여러 SW문제를 해결하기 위해 MOOC를 통해서 공부하였고, 여러 AI 관련된 프로그램을 개발하였다. 그리고 현재 Maker로써도 활동하고 있다. 나는 모르는 것을 두려워하지 않고 항상 새로운 것에 도전하며, 사회적 가치를 고민하고 행동하고 있다. 또한, 사람을 위한 기술이 아름답다는 가치관을 가지고 있으며 사회의 불평등, 불편함 해소에 나의 재능을 발휘하여 사회의 문제들을 해결하도록 시도 중이다.


개발프로그램


시각장애인을 위한 이미지 설명 프로그램 (2016, Python)

SendCloud (2013, C++ Builder)

E/V 군관리 시스템 (2014, C++ Builder) / 제31회 한국정보올림피아드 공모부문 금상 수상

New SendCloud (2014, C#)

SimpleMemo (2015, C/C++)

Survlient (2014, PHP/HTML)

움직이는 광고판(2014/2015, C/C++)

LED 블록 가방 (2015, C/C++)

The Diagram (2015, C#)

SendCloud 3.0 (2015, C#/C/C++/PHP/HTML)

The Doodleing (2015, HTML/PHP/MySQL)

아두이노를 통한 철도 ATO/CBTC 시스템 구현 (2015, C#)


[ 제작 배경 ]


사회의 불평등, 불편함 해소에 내가 가지고 있는 재능을 발휘하여 문제를 해결하고 싶다는 생각을 가지고 주변을 유심히 살펴보았다. 어느 날 지하철역에서 시각장애인의 도움요청에 응했고 시각장애인들의 열약하고 불편한 상황을 알게 되었다. 그래서 그러한 문제를 해결해 보려고 프로그램을 만들겠다는 생각을 하였다.

 

또, 우리 학교에는 시각장애인 선생님이 계신다. 옆에 항상 보조 선생님이 계시는데 자주 보조 선생님 이 바뀌고 구인광고를 내는 것을 알게 되었다. 시각장애인은 능력이 있어도 취업이 그리 쉽지 않겠다는 생각을 하였다. 보조선생님 도움 없이 혼자서 교사직을 수행해 낼 수 있는 방법을 찾아내기 위해 직접 인터뷰를 해 보았다.

 

처음 목표는 시각장애인이 길을 걸을 때 음성으로 내비게이션 역할을 해 주는 아이디어였다. 그래서 그런 애플리케이션이 필요한지 여쭈어 보았는데, 이미 그런 프로그램은 사용하고 있는 중이라고 답했다. 시각장애인 선생님은 영어 수업 때 이미지를 볼 수 없기 때문에 그 이미지가 어떤 것인지 문장으로 설명해 주는 프로그램이 필요하다고 하셨다. 그렇다면 수업을 할 때 보조선생님이 없어도 가능하다고 하였다. 


[ 프로그램 소개 ]


이 소프트웨어는 시각장애인 및 시각장애인 교사(보조 교사 없이 혼자서 교사직이 가능하도록)에게 유용한, ‘ImageCNN/RNN 머신러닝 알고리즘을 이용해 문장으로 설명해 주는 소프트웨어이다.

 

머신러닝 결과의 정확성을 높이기 위해 오픈소스 Dataset, Tensorflow를 활용한다.

 

300번 가량의 머신러닝 Test (실사 환경)를 토대로, 내 머신러닝 알고리즘의 정확도를 검증할 수 있었고, 내가 만든 Software를 라즈베리 파이에 32Bit Processor에 64Bit 기반으로 설치하여 이를 일상생활, 수업에 활용할 수 있도록 안경, 지팡이에 장착이 가능한 Embedeed 환경으로 개발하였다. 하드웨어에서는 버튼을 누르면 이미지를 촬영해 해당 이미지를 TTS로 변환해 설명해 준다.



위 이미지는 내가 촬영한 이미지 300개 (실제 라즈베리 파이에서 분석한 결과와 매우 흡사함) 를 분석한 결과로 완벽하게 판독한 이미지, 약간의 에러가 있는 이미지, 다소 잘못 판단한 이미지, 완전히 잘못 판단한 이미지 4개 영역으로 분류하였다. 주로 시각장애인이 보는 환경에서는 (길거리 등에 사람이 있는 환경) 온전한 역할을 수행하기에는 알맞으며, 이런 에러가 있는 이미지들도 알고리즘이 실행하면 실행할수록 계속 학습하도록 체계가 구축되어 있기 때문에, 잘못 판단한 이미지들도 지속적인 학습을 통해 수정되어 나갈 것이다. 향후 이러한 학습 데이터로 미래 사회를 Machine Learning으로 변화시킬 수 있고, 더 정확한 설명 데이터를 제공할 수 있다.


[ 작품 시연 ]



TTS가 들리지 않음 (라즈베리파이에 스피커 미장착)


[ 개발일지 ]


소프트웨어를 개발하면서 항상 내 프로젝트 개발 내용에 대해 개발일지에 기록한다. 이를 통하여 내 프로그램의 에러도 손쉽게 수정할 수 있고, 다른 사람이 내 개발일지를 보았을때 쉽게 프로그램 이해가 가능하다. 현재 [ 243 ]장이다.


최근 Ubuntu로 OpenCV 영상 처리를 하면서 느낀 점이, 설치해야 할 패키지가 너무 많다는 점이였다.



▲ 현재 작업하고 있는 환경


이에, OpenCV를 쉽게 설치할 수 있는 스크립트를 공유하려고 한다.


version="$(wget -q -O - http://sourceforge.net/projects/opencvlibrary/files/opencv-unix | egrep -m1 -o '\"[0-9](\.[0-9]+)+' | cut -c2-)"
echo "OpenCV" $version "설치"
mkdir OpenCV
cd OpenCV
echo "ffmpeg, x264 지우고 다시 설치"
sudo apt-get -qq remove ffmpeg x264 libx264-dev
echo "Dependenices 설치"
sudo apt-get -qq install libopencv-dev build-essential checkinstall cmake pkg-config yasm libjpeg-dev libjasper-dev libavcodec-dev libavformat-dev libswscale-dev libdc1394-22-dev libxine-dev libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev libv4l-dev python-dev python-numpy libtbb-dev libqt4-dev libgtk2.0-dev libfaac-dev libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev x264 v4l-utils cmake qt5-default checkinstall
echo "OpenCV" $version "다운로드"
wget -O OpenCV-$version.zip http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/$version/opencv-"$version".zip/download
echo "OpenCV" $version "설치"
unzip OpenCV-$version.zip
cd opencv-$version
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D INSTALL_C_EXAMPLES=ON -D INSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON -D WITH_QT=ON -D WITH_OPENGL=ON ..
make -j2
sudo checkinstall
sudo sh -c 'echo "/usr/local/lib" > /etc/ld.so.conf.d/opencv.conf'
sudo ldconfig
echo "OpenCV가 설치되었습니다. 버전 : " $version

(소스코드 출처 : http://ledgku.tistory.com/59 소스코드 일부 변형)


쉘 스크립트를 실행하기 위해서는 쉘코드에 실행 권한을 부여해야 한다. 우선 위 소스코드를 파일로 만들어 저장한 후 (파일명.sh)


chmod +x 파일명.sh 를 친다. 


이후, ./파일명.sh 명령어로 쉘코드를 실행하면 된다.


나같은 경우 VM에 1Core, 1GB RAM, Ubuntu 사양에 설치 시 20~30분 정도 걸렸다. 


그냥 스크립트 실행하고 자전거 타고 나갔다 오면 설치가 저절로 다 되어 있을 것이다. 


오늘은 지지 벡터 머신에 대해 알아보기로 하였다. 

이제부터 이 Numpy 기계학습 게시글에는 많은 자료를 올리지 않으려고 한다. 나는 이미 내 머신러닝 프로젝트에 쓸 알고리즘을 이미 찾았기 때문에 이제부터는 간략하게 요약만 할 생각이다. 아래는 위키피디아에서 가지고 온 '서포트 벡터 머신'이라는 자료이다.

-------------------------------------------------------------------------------------------

* Numpy 기계학습의 모든 문서는 제가 머신러닝을 공부하고, 제 프로젝트에 필요한 알고리즘을 찾기 위해 정리하는 일종의 '요점정리 노트' 입니다. 혹여나 오해 없으시기 바랍니다.

-------------------------------------------------------------------------------------------

서포트 벡터 머신(support vector machineSVM[1])은 기계 학습의 분야 중 하나로 패턴 인식, 자료 분석을 위한 지도 학습 모델이며, 주로 분류와 회귀 분석을 위해 사용한다. 두 카테고리 중 어느 하나에 속한 데이터의 집합이 주어졌을 때, SVM 알고리즘은 주어진 데이터 집합을 바탕으로 하여 새로운 데이터가 어느 카테고리에 속할지 판단하는 비확률적 이진 선형 분류 모델을 만든다. 만들어진 분류 모델은 데이터가 사상된 공간에서 경계로 표현되는데 SVM 알고리즘은 그 중 가장 큰 폭을 가진 경계를 찾는 알고리즘이다. SVM은 선형 분류와 더불어 비선형 분류에서도 사용될 수 있다. 비선형 분류를 하기 위해서 주어진 데이터를 고차원 특징 공간으로 사상하는 작업이 필요한데, 이를 효율적으로 하기 위해 커널 트릭을 사용하기도 한다.

이 알고리즘은 데이터에 집합이 주어졌을 때, 주어진 데이터 집합을 바탕으로 하여 데이터가 어느 카테고리에 속하는지 판단하는 분류 모델을 만드는데, 이 분류 모델은 아래와 같이 데이터가 사상된 공간에서 경계로 표현된 선형 분류 모델을 만들게 된다. 



* 사진 출처 : 위키피디아

아래는 SMO 알고리즘의 도움 함수이다.

def loadDataSet(fileName):
    dataMat = []; labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = line.strip().split('\t')
        dataMat.append([float(lineArr[0]), float(lineArr[1])])
        labelMat.append(float(lineArr[2]))
    return dataMat,labelMat

def selectJrand(i,m):
    j=i 
    while (j==i):
        j = int(random.uniform(0,m))
    return j

def clipAlpha(aj,H,L):
    if aj > H: 
        aj = H
    if L > aj:
        aj = L
    return aj

오늘은 여기까지 하기로 했다.

오늘은 로지스틱 회귀를 이용한 말의 배앓이 치사율 평가를 위한 로지스틱 회귀를 사용하는 예제를 풀어 보기로 하였다. 우선, 데이터에서 누락된 값이 있기 때문에 이를 다루는 효과적인 방법들이 필요하게 된다. 이에는 몇 가지 선택사항이 있다. 선택사항은 아래와 같다.

-------------------------------------------------------------------------------------------

* Numpy 기계학습의 모든 문서는 제가 머신러닝을 공부하고, 제 프로젝트에 필요한 알고리즘을 찾기 위해 정리하는 일종의 '요점정리 노트' 입니다. 혹여나 오해 없으시기 바랍니다. (2016.03.14 추가)

-------------------------------------------------------------------------------------------

* 사용 가능한 모든 데이터에서 속성의 평균값 사용

* 알려지지 않은 값은 특별한 값으로 채우기

* 혹은 무시

* 유사한 아이템들의 평균값 사용

* 값을 예측하기 위한 기계학습 알고리즘 사용

이 경우, 사용하게 될 데이터 집합은 전처리가 된것이다. 따라서 기존의 알고리즘으로 쉽게 사용할 수 있다는 장점이 있다. 내가 사용하는 로지스틱 회귀 분류 함수 소스코드는 아래와 같다.


def classifyVector(inX, weights):
    prob = sigmoid(sum(inX*weights))
    if prob > 0.5: return 1.0
    else: return 0.0

def colicTest():
    frTrain = open('horseColicTraining.txt'); frTest = open('horseColicTest.txt')
    trainingSet = []; trainingLabels = []
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr =[]
        for i in range(21):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[21]))
    trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 1000)
    errorCount = 0; numTestVec = 0.0
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr =[]
        for i in range(21):
            lineArr.append(float(currLine[i]))
        if int(classifyVector(array(lineArr), trainWeights))!= int(currLine[21]):
            errorCount += 1
    errorRate = (float(errorCount)/numTestVec)
    print "the error rate of this test is: %f" % errorRate
    return errorRate

def multiTest():
    numTests = 10; errorSum=0.0
    for k in range(numTests):
        errorSum += colicTest()
    print "after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests))
 

그 다음, 파이썬 쉘에 아래와 같이 입력한다.

내가 사용하는 데이터 값은 아래와 같다.

누락된 값은 아래와 같이 0.35가 나왔다.

이는 누락된 값이 30% 이상이므로 나쁘지 않은 결과이다. colicTest() 함수에서 반복 횟수를 변경할 수 있고, 오류율 20%에 근접하기 위해 stochGradAscent1()에서 alpha의 크기를 변경할 수 있다.

오늘은 여기까지 하기로 했다.