2017년 3월 15일 수요일

텐서플로우 02 군집화 예시 (TensorFlow Clustering K-means algorithm)

 군집화(Clustering)는 레이블이 없는 데이터를 분석할 때 사전 작업으로 사용하기 좋아 널리 이용하는 방법이다. 자율 학습(Unsupervised learning) 의 일종으로도 볼 수 있다.

 이번 포스트에서는 K-means 라고 하는 군집화 알고리즘을 텐서플로우에서 테스트해볼 것이다. K-means 알고리즘은 데이터를 유사한 것끼리 자동으로 그룹화할 때 사용하는 유명한 알고리즘이다. 알고리즘의 결과는 중심(centroid)이라고 부르는 K 개의 점으로 각기 다른 군집의 중심점을 나타내고 데이터들은 K 개의 군집 중 하나에만 속할 수 있다. 한 군집 내의 모든 데이터들은 다른 군집의 중심보다 자기 군집의 중심과 가깝다.

 군집을 구성할 때 모든 경우에 대해 오차함수를 최소화하려면 NP-hard 문제가 되어 계산하기 곤란하므로 휴리스틱(heuristics) 방법을 사용하여 로컬 최소값에 빠르게 수렴하도록 한다. 널리 사용되는 방법 중 하나는 반복 개선(iterative refinement)기법이다.
 반복개선 기법은 크게 3단계로 나눠진다.
 1) 0단계(초기) : K개 중심의 초기 집합을 결정
 2) 1단계(할당) : 각 데이터를 가장 가까운 군집에 할당
 3) 2단계(업데이트) : 각 그룹에 대해 새로운 중심을 계산
예제에서는 초기 중심을 데이터 중에서 임의로 선택할 것이다.
업데이트의 경우 일반적으로 군집 내 데이터의 변화가 없으면 알고리즘이 수렴되었다고 간주한다.

 휴리스틱 기법을 이용하므로 결과가 최적이라는 보장은 없다는 것을 유의해야 한다.

----------------------- Clustering source code ---------------------------

# 샘플데이터 생성
import numpy as np

num_points = 2000
vectors_set = []

for i in range(num_points):
    if np.random.random() > 0.5:
        vectors_set.append([np.random.normal(0.0, 0.9),
                            np.random.normal(0.0, 0.9)])

    else:
        vectors_set.append([np.random.normal(3.0, 0.5),
                            np.random.normal(1.0, 0.5)])


# matplotlib 기반으로하는 seaborn 시각화 패키지 , 데이터 조작 패키지 pandas
# seaborn 은 Anaconda 에서 conda install seaborn 명령으로 설치
# pandas 는 pip install pandas 명령으로 설치

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.DataFrame({"x": [v[0] for v in vectors_set],
                   "y": [v[1] for v in vectors_set]})
sns.lmplot("x", "y", data=df, fit_reg=False, size=6)
plt.show()


# K-means 구현
# 4개의 군집으로 그룹화

import tensorflow as tf

    # 모든 데이터를 상수 텐서로 옮김
vectors = tf.constant(vectors_set)
    # 초기 단계 : 중심 k(4)개를 입력데이터에서 무작위로 선택
k = 4
centroides = tf.Variable(tf.slice(tf.random_shuffle(vectors),[0,0],[k,-1]))
    # vector.get_shape(), centroides.get_shape()
    # 위 주석으로 각 텐서의 구조를 확인해볼 수 있음
 
expanded_vectors = tf.expand_dims(vectors, 0)
expanded_centroides = tf.expand_dims(centroides, 1)

    # 할당 단계 : 유클리드 제곱거리 사용
diff = tf.sub(expanded_vectors, expanded_centroides)
sqr = tf.square(diff)
distances = tf.reduce_sum(sqr, 2)
assignments = tf.argmin(distances,0)

    # 업데이트 : 새로운 중심 계산
means = tf.concat(0,
                  [tf.reduce_mean(
                      tf.gather(vectors,
                                tf.reshape(
                                    tf.where(tf.equal(assignments, c))
                                    ,[1, -1])
                                )
                      , reduction_indices=[1]) for c in range(k)])

update_centroides = tf.assign(centroides, means)

init_op = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init_op)

for step in range(100):
    _, centroid_values, assignment_values = sess.run([update_centroides, centroides, assignments])


# assignment_values 텐서의 결과를 확인

data = {"x": [], "y": [], "cluster": []}

for i in range(len(assignment_values)):
    data["x"].append(vectors_set[i][0])
    data["y"].append(vectors_set[i][1])
    data["cluster"].append(assignment_values[i])

df = pd.DataFrame(data)
sns.lmplot("x", "y", data=df, fit_reg=False, size=6, hue="cluster", legend=False)
plt.show()
 

----------------------- Clustering source code ---------------------------


업데이트 단계 (각 군집에 속한 점들의 평균 계산) 코드 해석
means = tf.concat(0,
                  [tf.reduce_mean(
                      tf.gather(vectors,
                                tf.reshape(
                                    tf.where(tf.equal(assignments, c))
                                    ,[1, -1])
                                )
                      , reduction_indices=[1]) for c in range(k)])

괄호 안쪽부터 차레로 보면
1. equal 함수를 사용하여 assignment 텐서의 각 원소 위치를 True 로 표시하는 불리언 텐서( Dimension(2000) ) 를 만듭니다.
2. where 함수를 사용하여 매개변수 텐서의 True 로 표시된 위치를 가지는 텐서 ( Dimension(2000) * Dimension(1) ) 를 만듭니다.
3. reshape 함수를 사용하여 c 군집에 속한 vectors 텐서의 포인트들의 인덱스로 구성된 텐서 ( Dimension(1) * Dimension(2000) )를 만듭니다.
4. gather 함수를 사용하여 c 군집을 이루는 점들의 좌표를 모은 텐서 ( Dimension(1) * Dimension(2000) * Dimension(2) )를 만듭니다.
5. reduce_mean 함수를 사용하여 c 군집에 속한 모든 점의 평균 값을 가진 텐서 ( Dimension(1) * Dimension(2) )를 만듭니다.


샘플 데이터 ( 2000 sample dots )


군집화 결과 clustering result










2017년 3월 14일 화요일

텐서플로우 01 선형회귀 예시 (TensorFlow Linear Regression)

 선형회귀분석은 변수들 사이의 관계를 분석하는데 사용하는 통계학적 방법이다.
독립변수 x, 상수항 b, 종속변수 y 사이의 관계를 모델링하는 방법으로 두 변수 사이의 관계일 경우 단순회귀라고 하며 여러 개의 변수의 관계일 경우는 다중회귀라 한다.
아래 예시는 y = W * x + b 형태의 간단한 선형회귀 분석 모델을 트레이닝하는 과정을 볼 수 있도록 구현한 것이다.

 변수 x, y 가 y = 0.1 * x + 0.3 관계를 가지면서 오차가 조금씩 있도록 샘플데이터를 생성하였고 매개변수 W, b 가 좋은 값인지 측정하는 오차함수(error function)평균제곱오차(mean square error)로 사용했다.
오차는 반복을 통해 점점 최소화하는데, 값을 최소화 하면서 트레이닝하는 최적화 알고리즘을 경사하강법이라 한다.

 matplot 패키지가 없다pip install matplotlib 명령을 통해 설치하면 된다.

 파이썬 3 버전 이상에서 동작하는 소스코드이며 그 이하 버전의 경우는 수정할 것이 있다.
 range -> xrange   ( 전자가 파이썬 3 이상버전 )
   


---------------------- Linear Regression source code ------------------
import numpy as np


# 임의의 좌표값 생성
# y = 0.1 * x + 0.3 관계를 가지는 데이터에 정규분포를 따르는 오차를 넣음

num_points = 1000
vectors_set = []

for i in range(num_points):
    x1 = np.random.normal(0.0, 0.55)
    y1 = x1 * 0.1 + 0.3 + np.random.normal(0.0, 0.03)
    vectors_set.append([x1, y1])

x_data = [v[0] for v in vectors_set]
y_data = [v[1] for v in vectors_set]


# 비용함수 (오차함수)
import tensorflow as tf

W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b

loss = tf.reduce_mean(tf.square(y - y_data))

# 최적화 알고리즘 사용 (경사하강법)
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)


# 알고리즘 실행 1 - 초기화 실행
init = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init)


# 알고리즘 실행 2 - 훈련 반복 횟수
# pip install matplotlib 설치 필요
import matplotlib.pyplot as plt

for step in range(16):
    sess.run(train)

    print(step, sess.run(W), sess.run(b))
    print(step, sess.run(loss))
# 그림으로 결과를 보고 싶을 때
    plt.plot(x_data, y_data, 'ro')
    plt.plot(x_data, sess.run(W) * x_data + sess.run(b))
    plt.xlabel('x')
    plt.xlim(-2, 2)
    plt.ylim(0.1, 0.6)
    plt.ylabel('y')
    plt.legend()
    plt.show()

---------------------- Linear Regression source code ------------------

실행 결과 예시

반복될 수록 W, b 의 값이 각각 0.1 과 0.3 에 수렴하는 것을 알 수 있다.

















2017년 2월 27일 월요일

웹 프로그래밍 개발환경 구축 ( JDK, 아파치 톰캣, 이클립스, MySQL ) for Windows


1. 파일 다운로드

 설치할 컴퓨터의 사양에 따라 다운로드합니다.

 JDK : http://www.oracle.com/technetwork/java/index.html
     JAVA SE (Standard edition) 최신버전 설치
 Eclipse ( 개발 도구 IDE ) : http://www.eclipse.org
      Eclipse IDE for java EE Developers 다운로드

 Apache Tomcat 9.0 : http://tomcat.apache.org/
      
 MySQL : https://dev.mysql.com/downloads/
     MySQL connector/J (zip 파일),
     MySQL Server (msi 파일) 다운로드


2. JDK 설치 및 환경변수 등록

 JDK 설치 후 C:\programfiles\JAVA 폴더에서 jdk 와 jre 가 설치됨을 확인하고 jdk 폴더안의 bin 폴더까지의 경로를 환경변수로 등록
 ( 제어판 - 시스템 - 고급 시스템 설정 - 환경변수 - 시스템변수 - Path 에  ...\jdk\bin 경로 추가, 세미콜론(;) 으로 구분 )


3. Tomcat 설치
   설치 경로를 C:\ 로 하면 추후에 eclipse 와 연동하기 편함


4. eclipse 와 Tomcat 연동
   작업표시줄 오른쪽에서 Tomcat 서비스 실행 중이면 stop 버튼으로 중지
   eclipse 실행 후 server 패널이 있는지 확인
   (없으면 상단의 탭 Window - Show View - server  ( 없으면 other 에서 찾기))
   server 패널에 Tomcat 이 없으면 상단 탭 Window - Preferences
   Preference 에서 Server - Runtime Environment 에서 Add 로 Tomcat 9.0 추가
   Tomcat 설치 경로 및 jdk 설치하면서 생성된 jre 버전 선택 -> 없으면 추가하여 jre 경로 지정
   eclipse server 패널에 tomcat 생성된 것 확인


5. tomcat 연동 결과 확인
    File - New - Dynamic Web Project 생성 ( 없으면 other 선택, web 폴더에 있음 )
    생성된 프로젝트에 우클릭 - new - JSP file
    생성된 JSP 파일의 <body> 태그 사이에 12345 작성 후 저장
    jsp 파일에 우클릭 - run as - run on server 해서 웹페이지에 12345 확인


6. MySQL 설치
     port number(3306) , root password(자유)

     user(자유) , host(localhost),role(DB admin), password(자유)
       - user 와 password 는 기억해두어야 후에 연동 테스트에 db 연결이 가능함

     설치 과정 중에 user 와 password 로 연결 테스트가 있음

     환경변수에 MySQL Server 의 bin폴더 경로 추가
     (C:\Program Files\MySQL\MySQL Server 5.7\bin)


7. MySQL Server 테스트  ( 생략 가능 )
    시작 - MySQL - MySQL5.7 Command Line Client 실행 (설치 때 사용한 비밀번호 필요)
    명령어 입력 ( // 뒤의 내용은 명령어가 아님 ) 
    show databases;   // db 조회

    create database test_db;  //test_db라는 데이터베이스 생성
    grant all on test_db.* to 'user' @'localhost' identified by 'password';
    //localhost로 접속한 'user'라는 사용자에게 test_db에 대한 모든 권한을 줌.  '' 안의 내용은 모두 설치 시 입력한 내용
    connect test_db;     //DB에 연결
    show tables;    // 현재 db의 table 조회
    create table test_table (
          id varchar(8) not null,
          pw varcahr(16) not null,
          primary key (id)
     );
     //test_table이라는 테이블을 생성
     show tables;
     quit;   // 종료


8. MySQL 연동 및 테스트
     MySQL connector/J 압축파일을 풀어서 mysql-connector-java-5.1.40-bin.jar 파일을 Tomcat 폴더의 lib 폴더와 C:\Program Files\Java\jre1.8.0_121\lib\ext 로 각각 복사한다.

     이클립스에서 jsp 문서를 생성 후 *** 사이의 코드를 복사함 ( 코드 수정 필요 )
***********************************************
<%@ page contentType="text/html; charset=euc-kr" language="java" %>
<%@ page import="java.sql.*" %>

<%
 Connection con = null;
 PreparedStatement pstmt = null;
 ResultSet rs = null;

 try {
  String DB_SERVER = "localhost:3306";  // MySQL 설치시 등록한 포트로 수정필요
  String DB_USERNAME = "user";          // MySQL 설치시 등록한 user 로 수정필요
  String DB_PASSWORD = "password";   // MySQL 설치시 등록한 user 로 수정필요
  String DB_DATABASE = "test_db";       // db 테스트 때 생성한 db 로 수정 필요
  String DB_TABLE = "test_table";          // db 테스트 때 생성한 table 로 수정필요

  String jdbcUrl = "jdbc:mysql://" + DB_SERVER + "/" + DB_DATABASE;

  Class.forName("com.mysql.jdbc.Driver");
  con = DriverManager.getConnection(jdbcUrl, DB_USERNAME, DB_PASSWORD);
  %>

 <h1>Database 연결 테스트</h1>
  Database 연결 성공<br>
  <%
  
  String query = "select * from "+DB_TABLE;
  pstmt=con.prepareStatement(query);
  rs=pstmt.executeQuery();

  int count=0;
  while(rs.next()) {
   
   String number=rs.getString("id");
   String name = rs.getString("pw");
   %>
   <p>[<%=count%>] number : <%=number%>, name : <%=name%></p>
   <%
   count++;
  }
  
 } catch(Exception e) {
  e.printStackTrace();
  out.println("Fail");
 }
%>
**************************************************************************

jsp 실행
Database 연결 성공 이라는 문구가 보이면 JDK, Tomcat, Eclipse, MySQL 연동 성공

텐서플로우 설치 (TensorFlow for windows)

1. 파일 다운로드

  - Anaconda 4.3.0 for windows : https://www.continuum.io/downloads

  - CUDA 8.0.44 : https://developer.nvidia.com/cuda-downloads
      local 버전 권장 ( network 버전은 비교적 오래걸림 )

  - NVIDIA cuDNN : https://developer.nvidia.com/cudnn
      deep learning 용 추가 라이브러리 ( CUDA8.0 은 cuDNN 5.1 설치 )
      회원가입 필요

  - Python 3.5 : https://www.python.org/downloads/release/python-350/
      윈도우에서 tensorFlow 를 시작하려면 3.5 버전이상 필요


2. Anaconda 4.3.0 , CUDA 8.0, Python3.5 설치
    python 설치 시 path 등록에 체크하면 환경변수 등록하는 작업을 생략할 수 있다.


3. cuDNN 압축 해제 후 CUDA 8.0 의 경로에 붙여넣기
    CUDA 경로 : C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0


4. TensorFlow-GPU 설치
 
  1) cmd 관리자 실행하여 다음 명령어를 입력한다.
   pip install tensorflow-gpu

  2) 이전의 명령어에서 pip 버전이 낮아서 불가하다는 문구가 나오면 다음명령어로 pip를 업그레이드 한 후에 다시 1) 을 수행한다.
   python -m pip install --upgrade pip

  3) tensorflow 가 성공적으로 설치가 완료되면 다음의 명령어를 수행한다.
   pip install --upgrade https://storage.googleapis.com/tensorflow/windows/gpu/tensorflow_gpu-0.12.0rc0-cp35-cp35m-win_amd64.whl


5. 설치 확인

   Anaconda prompt를 관리자 권한으로 실행하고 Python 에서 다음의 code 를 입력한다.

   import tensorflow as tf
   hello = tf.constant('Hello, TensorFlow')
   sess = tf.Session()
   print(sess.run(hello))

   결과 : Hello, TensorFlow


텐서플로우 설치에 참고할만한 사이트
1. 텐서플로우 문서 한글 번역본
https://tensorflowkorea.gitbooks.io/tensorflow-kr/content/g3doc/get_started/os_setup.html
2. 블로그
http://comajava.blogspot.kr/2017/01/windows-tensorflow-python-35-anaconda.html

2017년 1월 8일 일요일

NavMesh (Navigation Mesh) 네비게이션 메쉬

네비게이션 메쉬 + A* (Navigation Mesh + AStar)

원문 링크 : http://yoysh.egloos.com/25000 
네비게이션 메쉬 + A* (Navigation Mesh + AStar)
: 3차원 지형을 2D처럼 간단하게 표현 하는 방식으로 Object가 이동 가능한 모든 지형을 Cell(삼각형)으로 표시 하여 A*와 같은 길찾기 알고리즘을 쉽게 적용 할 수 있게 해줍니다.

[Navigation Mesh]
1. NaviCell 만들기
Cell 이란? vetex 세 개로 구성되어 이루어진 하나의 삼각형입니다.
- 삼각형의 사이드 라인 세 개를 만듭니다.
- 평면 방정식을 위한 Plane을 생성합니다.
- Cell의 중점을 계산합니다.
- Cell의 세 사이드 라인의 중점을 계산합니다.(mid[0], mid[1], mid[2])
- Cell의 중점에서 사이드 라인의 중점까지의 거리를 계산해둡니다.
(ArrivalCost)

2. NaviMesh 만들기
Navigation Mesh 란? 지형의 모든 이동 가능한 삼각형의 집합
앞에서 만든 NaviCell의 바로 이웃한 셀들을 연결시켜줍니다.
Link[0], Link[1], Link[2]가 이웃한 NaviCell을 가리키며,
만약 이웃한 Cell이 없다면 NULL

[A*]
3. Actor 만들기
NaviMesh로 부터 자신의 좌표와 자신이 위치한 Cell을 미리 계산해둡니다.
여기서 Actor는 x, z만 알면 평면방정식으로 부터 y값을 구합니다.
(앞으로 x,z는 평면의 좌표이며 y는 높이 값입니다.)

4. Heap 만들기
heap은 End좌표로 부터 거꾸로 start까지의 셀 노드들의 리스트입니다.
end cell은 시작점, start cell이 목표점으로 생각합니다.
EndCell로 부터 인접한 셀들을 구합니다. 이 인접 셀들중 비용이 가장 싼 것을 계산 합니다. 계산식은 Heuristic + ArrivalCost 입니다.
여기서 Heristic은
deltax = (목표점.x - 현제셀중점.x)
deltay = (목표점.y - 현제셀중점.y)
deltaz = (목표점.z - 현제셀중점.z)
max(max(deltax, deltay), deltaz) 값입니다.
이웃 셀중 비용이 싼 셀이 현재 셀로 되며 같은 방식으로 다시 이웃 셀을 비교합니다.
이를 반복 하면 start 셀까지 셀이 이동됩니다.

5. Path 만들기
앞에서 만든 heap으로 부터 start로 부터 end까지의 mid좌표와 cell을 연속적으로 저장해둡니다.

6. 추가 작업
삼각형의 중점으로 Cell들을 이동해 다니게 되면 술취한(갈지자) Actor처럼 보이므로, 앞에서 구해진 Path로 부터 line테스트를 하여 직선 경로를 구해 이를 이동 경로로 사용합니다.
추가사항
네비게이션 메쉬와 A*는 같은 내용의 알고리즘이 아닙니다.
A*는, 경로가 표현되는 공간의 구체적인 표현방식과는 관계없는 알고리즘입니다.
어떠한 형태의 공간이 주어지든(2D격자, 3D격자, 불규칙 메쉬, 6각형2D격자 등등 ..)
이웃(neighbor)에 대한 표현만 주어지면 그 공간에서 실시간으로 최적화된 길찾기를 할 수 있는 알고리즘이 A*입니다.
네비게이션 메쉬는 '불규칙한 메쉬'로 이루어진 3D월드에서 보다 부드러운 주행을 위해서
길찾기에 이용될 월드 표현방법을 단순화시켜 보자는 알고리즘입니다. 말하자면 길을 찾는
것이 최종적인 목적이 아니라, 복잡한 3D공간을 단순화시켜서 생각하기에 편하도록 만들어보자는 겁니다.
네비게이션 메쉬에서 실제로 길을 찾는 알고리즘은 뭐든 상관없습니다.

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

Hun's review

 네비게이션 메쉬 오픈소스
c++ : https://github.com/memononen/recastnavigation
c# : https://github.com/Robmaister/SharpNa

Navigation Mesh 는 맵 또는 씬마다 각각 계산되어야 합니다.