하의도 여행기(2) - 故 김대중 전 대통령 생가 방문기[2016.04.09]

 지난 포스팅에서 페리를 타고 하의도에 도착하기 까지의 우여곡절을 다루었습니다. 이번 포스팅에서는 하의도를 직접 걸어다니면서 본 풍경들을 가볍게 다루어보고자 합니다.




 지도상으로 보았을 때 섬 면적을 보고 걸어서 완주할 만할 것 같은 착각을 가지고 섬에 입도하였으나 맨발로 섬을 이리저리 걸어다니다 보니 제 생각이 매우 큰 오산이었음을 절실히 깨닫게 되었답니다.


하의도 선착장에 도착하자마자 하의도라 적혀있는 비석이 방문객을 맞이합니다.



선착장을 좀 더 걸어나가면 손 모형이 삼거리 중앙에 설치되어 있는 것을 보실 수 있습니다.



아니 이런 조그마한 섬 안에도 버수정류장이 있다니?!

물론 사람들이 사는 동네이니 버스가 있을만 하지만

이때까지도 본인은 섬 안을 걸어다닐 만한 거리라 단단히 착각하고 있었습니다.



하의도에 방문한 관광객들을 위한 안내표시판이 눈에 들어옵니다.



버스정류장 벽면에 붙어있던 하의도 내에 운행중인 버스 시간표 입니다.



선착장 인근에는 보시는 바와 같이 바다를 향해 의자가 설치되어 있는 것을 볼 수 있습니다.



실제로 앉아서 바다를 보면 탁 트인 풍경이 생각보다 볼 만합니다.



섬을 걸어가던 도중 처음으로 방문하게 된 하의3도농민운동기념관 입니다.

하의도에서 농사를 짓던 농민들의 애환이 담긴 역사를 볼 수 있는 곳이고 섬 내에 설치된 대표적인 관광지 중 하나힙니다.



육지에서도 쉽게 볼 수 있는 흔한 우리나라의 시골풍경입니다.



눈앞에 펼쳐진 길을 걷고 걷다보면...



드디어 김대중 대통령 생가 눈에 들어오기 시작합니다!



김대중 대통령 생가임을 알리는 기념석이 눈에 들어옵니다.



나름 전 대통령 생가 터이다 보니 한산한 섬에 공원을 만든 듯 한 모습입니다.



눈에 들어오는 이 초가집이 바로 김대중 전 대통령 생가를 복원한 곳입니다.


생가터 멀리에 천일염 전시관이 눈에 들어옵니다.

허나 문이 잠긴 것으로 보아 관리가 되고 있지 않는 듯 합니다.



생가터 인근에는 김대중 대통령의 생애가 담긴 사진자료들이 전시되어 있습니다.



생가 복원장소 옆에는 보시는 바와 같이 故 김대중 전 대통령의 추모관이 마련되어 있습니다.



우리나라의 민주화를 위해 꿋꿋이 독재자와 싸워왔던 김대중 대통령의 대선 포스터들이 눈에 들어옵니다.



건장안 성인 남성의 키만한 김대중 대통령 동상



원래 생가터는 이곳이었는데 땅 주인과의 사정이 있어서였는지

복원된 생가는 원 생가터의 바로 왼편에 만들어졌습니다.



생가 전경 지도입니다.

생가 뒤편에는 유스호스텔이 추가로 건설되어 있습니다.



생가터 벽쪽에는 김대중 전 대통령의 지난 업적들이 소개되고 있습니다.



인동초와 같이 파란만장한 삶을 살았던 김대중 전 대통령.

소년시절 이 작은 섬에서 과연 미래의 지도자가 되고자 하던 꿈을 꾸었을까요?



천일염 전시관에서 바라본 김대중 전 대통령 생가터 입니다.


 이밖에 하의도의 풍경은 3부에서 계속 이어가도록 하겠습니다~!



하의도 여행기(3) - 천일염 레일, 그리고 다시 육지로[2016.04.09]

300x250

Infineon AURIX TC237 사용기(1) - TriCore™ Entry Tool Chain 설치하기

임베디드/MCU 2016. 4. 26. 00:31

 이번에 기회가 되어 Infeneon의 AURIX TC23x 시리즈 MCU를 만져보게 되면서 이와 관련된 자료들을 블로그로 포스팅 해볼까 합니다. 이전에 TI사의 Tiva C 시리즈를 다루던 당시처럼 관련 내용을 다룬 국내의 포스팅이 없어 TI 콘테스트를 준비하던 당시처럼 맨땅에 해딩하는 듯한 기분이 없지않아 느껴집니다.


 TI사의 MCU를 사용하기 위해 Code Composer Studio를 사용하듯이 이번에 다루는 Infineon사의 AURIX TC23x 시리즈의 MCU를 사용하기 위한 통합개발환경인 TriCore™ Entry Tool Chain이 필요합니다. 이를 얻기 위해 HIGHTEC이라는 사이트에서 TriCore™ Entry Tool Chain의 컴파일러를 사용할 수 있는 라이센스를 무료로 발급받으시면 됩니다.


http://free-entry-toolchain.hightec-rt.com/



 위 사이트의 화면에서 보시는 바와 같이 오른쪽 부분에 이름, 이메일주소 등의 신상정보를 입력하신 후 TriCore™ Entry Tool Chain을 사용하고자 하는 PC의 MAC어드레스를 입력해야 합니다.

 컴퓨터의 MAC어드레스를 알아내기 위해 CMD를 통해 명령어를 입력하여야 합니다. '윈도키+R'를 누른 후 cmd를 입력하시어 명령 프롬포트를 실행합니다.


위 화면의 경우 Windows 10 작업표시줄에 있는 검색창을 통해 cmd를 입력한 모습입니다.


 명령 프롬포트를 실행하신 후 다음과 같이 명령어를 입력해줍니다.

> ipconfig/all



 실행하시면 보시는 바와 같이 자신의 컴퓨터에 할당된 MAC어드래스(물리적 주소)를 확인하실 수 있습니다. 만약 자신의 PC가 랜선을 연결하여 인터넷을 하는 경우 '이더넷'을, 와이파이를 통해 인터넷을 하시는 경우 'Wi-Fi'에 있는 MAC어드래스를 입력하시면 되겠습니다. 만약 가상머신을 사용하여 인터넷을 사용하시는 경우 해당 가상머신에 할당된 MAC어드래스를 사용하시면 되겠습니다.



 자신의 신상정보를 모두 입력하셨을 경우 위와 같이 라이센스가 성공적으로 발급되었음을 확인하실 수 있습니다. 발급된 라이센스는 'Download "license.lic"'를 통해 다운받으시거나 자신의 이메일을 통해 다운받으실 수 있으며 설치 패키지 또한 메일 주소로 다운로드 경로를 제공합니다.



 다운로드가 설치를 진행하도록 합니다. 첫 화면에서 License에 동의함을 선택하신 후 'Next'버튼을 클릭합니다.



 설치 모드를 선택합니다. 자신이 선호하는 모드를 선택하신 후 'Next'를 클릭합니다.



 TriCore™ Entry Tool Chain를 설치하고자 하는 경로를 설정합니다.



 Product Selection Key를 입력하는 부분입니다. 이 부분에서 라이센스 부여 여부가 결정되는 것으로 보이며 보통은 초기에 Product Selection Key가 미리 설정되어 있어서 그냥 'next'버튼을 누르고 넘어가시면 되겠습니다.



 설치가 진행중입니다. 설치가 완료되면 바탕화면에 'TriCore™ Entry Tool Chain'의 바로가기가 설치되었음을 확인하실 수 있습니다.


※TriCore™ Entry Tool Chain은 Eclipse를 기반으로 만들어진 IDE(통합 개발 환경)이며, Eclipse는 Java가 설치된 환경에서 동작합니다. 만약 자신의 컴퓨터에 JDK가 설치되어 있지 않을 경우 아래의 화면과 같은 오류가 발생합니다. 이 경우 Oracle에서 제공하는 JDK(32-bit)를 설치해 주시기 바랍니다.


http://www.oracle.com/technetwork/java/javase/downloads/index.html



※64-bit 환경의 운영체제를 사용하시는 경우 JDK가 설치되어 있음에도 아래와 같은 오류가 발생하는 경우가 있습니다. 이는 TriCore™ Entry Tool Chain가 32-bit 기반의 프로그램이기 때문에 64-bit 환경의 JDK에서는 실행할 수 없는 겁니다. 이 경우 Oracle에서 제공하는 JDK(32-bit)를 설치해 주시기 바랍니다.


http://www.oracle.com/technetwork/java/javase/downloads/index.html




 설치가 완료된 후 TriCore™ Entry Tool Chain을 실행하면 위와 같은 화면이 나타며 IDE가 실행됩니다.



 TriCore™ Entry Tool Chain를 처음 실행하였을 경우 사용자의 라이센스를 확인하여야 한다는 경고문이 등장합니다. Yes를 눌러줍니다.



 위와 같이 'Add license'이 등장하면 홈페이지에 입력하였던 정보를 그대로 입력해줍니다. 'Finish'버튼을 누르면 인증이 완료되며 Infineon의 AURIX TC23x 시리즈를 개발할 수 있는 환경을 구축하실 수 있습니다.


 설치과정에서 발생하였던 몇몇 이슈들에 대해 알아봅니다.


Q1.설치 후 인증과정에서 라이센스를 발급받았던 신상정보를 그대로 입력했음에도 컴파일러가 작동하지 않습니다.

->TriCore™ Entry Tool Chain의 라이센스를 최종으로 획득하기 위해서는 해당 MAC어드래스를 통해 인터넷이 연결되어 있어야 합니다. 만약 다운로드 당시 LAN의 MAC어드래스로 라이센스를 받아두고 Wi-Fi를 통해 인증을 시도하는 경우 인증이 되지 않는 경우가 발생합니다.


Q2.위의 방법대로 인터넷에 연결된 상태에서 라이센스 인증을 시도하였으나 여전히 컴파일러가 작동하지 않습니다.

->만약 라이센스 인증을 여러 번 시도하였을 경우 최근에 수행하였던 라이센스만 인증이 됩니다. 이 경우 자신의 이메일에 있는 가장 최근에 도착한 license.lic파일을 다운로드 받으신 후 C:/HIGHTEC/licenses 폴더에 삽입합니다. 이 때 기존의 lic파일을 삭제해야 TriCore™ Entry Tool Chain IDE가 단 하나의 라이센스만 인식할 수 있습니다.

300x250

하의도 여행기(1) - 처음으로 페리를 타보다[2016.04.09]

 제가 지금까지 여행을 하면서 가장 기억에 남는 여행 수단으로 선박을 이용하여 섬으로 떠나는 여행이었습니다. 이전에 울릉도와 독도를 여행갔을 때 처음으로 5시간 이상을 배 안에서 보냈었는데 그 때 만큼 제 기억속에 강한 인상을 남겼던 여행은 없었던 것으로 기억하고 있습니다. 거대한 여객선만 타본 저에게 있어 작은 선박을 처음 탄 저에게 끔찍한 배멀미를 선물했던 악몽이 떠오르는군요. 하지만 기나긴 고통을 인내하고 도착한 울릉도는 정말 제겐 매력적인 관광지였습니다. 이 맛에 많은 여행객들이 배를 타고 떠나는 섬 여행을 즐기는가봅니다.

 이번 여행기는 그토록 학수고대하였던 섬인 하의도를 방문하던 도중의 이야기입니다. 비교적 배편과 행선지가 다양한 울릉도와는 달리 하의도는 배편이 하루에 최대 5편 이상도 없기 때문에 시간이 맞지 않으면 방문하기 참으로 어려운 섬이다보니 제게는 반드시 가고팠던 섬이기도 합니다.


 우선 하의도에 가기 위해서는 목포에 위치한 목포연안여객선터미널에서 배편을 타야 합니다. 바로 인근에 있는 목포항국제여객터미널과는 다른 곳이니 참고하셔야 합니다.



전남 목포시 해안로 182


 위 지도에서 보시는 바와 같이 두 여객터미널이 같이 위치하고 있으며 하의도로 가기 위해서는 지도상으로 오른쪽에 위치한 여객터미널로 가시면 됩니다.

 목포버스터미널에서 연안터미널로 가시는 분은 목포 시내버스 1번을 타시고 '여객선터미널'에서 하차하시면 됩니다. 목포역에서 목포연안여객선터미널까지 걸어갈 경우 약 15~20분 정도 소요됩니다.


 섬 여행시 가~장~! 중요한 정보! 바로 배편 운항 시간표를 참고하여야 합니다. 선박의 경우 계절 및 시기에 따라 운항시간이 변경될 수 있기 때문에 반드시 여행 전 해운사에 연락하여 일정을 확인하시길 바랍니다.



목포 ↔ 하의도 여객선 시간표(2016년 4월 기준)


 목포 → 하의도

06:30(조양페리 1호)

07:10(엔젤호)

10:10(조양페리 2호)

13:30(조양페리 1호)

14:30(엔젤호)


 하의도 → 목포

08:30(엔젤호)

09:00(조양페리 1호)

13:00(조양페리 2호)

15:50(엔젤호)

16:20(조양페리 1호)


배편 가격(2016년 4월 기준)

엔젤호    22,500원(약 70분 소요)

조양페리 14,700원(약 150분 소요)


연락처

조양운수(주) 061)244-0038


 확실히 배편이 다른 여행수단들 보다는 비교적 비싼 편입니다. 쾌속선이 페리보다 다소 빠른편이긴 합니다만 시간대에 맞는 배를 타야 할 경우 제일 먼저 탈 수 있는 수단을 이용하는 수 밖에 없지요.



 하의도로 들어가는 배편은 06:30분 배펀인 조양페리 1호에 승선하였습니다만 1시간이 지난 7시 30분인 지금까지도 사진에서 보시는 바와 같이 안개가 자욱히 끼어있어 배가 목포를 떠날 기미가 보이지 않고 있습니다......



 난생 처음타는 페리인데다가 혹여나 사고가 나지 않길 바라며 노심초사하는 심정입니다.



 1시간 지연되었음에도 여전히 안개때문에 떠나는 시간 조차 한치 앞도 볼 수 없는 상황입니다.



바로 옆 페리에 탑승하신 분들도 배가 떠나기를 학수고대 하는 모습입니다.



출항이 지연되고 있는 동안 잠시 배 주변을 구석구석 구경해보도록 합시다.



역시 우체국은 섬 지역에서 가장 믿음직한 물류수단입니다. 어느 지역에서 온 택배도 모두 배송해주니 말이죠.



 페리 내부의 모습입니다. 페리의 특성상 배가 많이 흔들리기 때문에 이렇게 바닥에 누워 배가 도착하기만을 간절히 기다려야만 합니다.



 항상 배를 탈 때마다 구명조끼의 위치를 숙지해둡니다. 절대 벌어져서는 안될 일입니다만 만일의 사태를 대비해야 살아남을 수 있으니 말이지요.



 어느덧 안개가 걷히고 슬슬 출발할 듯한 분위기가 감돌기 시작합니다.



 아까전까지만 해도 안개때문에 보이지 않던 배들이 모이기 시작합니다.



 바로 옆에 있던 페리는 바로 출발하는 모습입니다.



이제 곧 제가 탄 배도 떠날 순서가 되었군요.



선원들이 페리를 출항시키기 위해 출입문을 닫고 있습니다. 그토록 기다리던 출항의 순간이로군요!



 드디어 배가 여객선터미널을 떠나기 시작합니다!



 안개 앞에 발이 묶였던 페리는 힘찬 물보라를 일으키며 목적지를 향해 항해를 시작합니다.



 어느덧 목포 시내의 모습이 한눈에 들어옵니다. 배가 본격적으로 여행지를 향해 출발했다는 의미기도 하지요.



 페리는 어느덧 목포대교 하단을 지나갑니다.



 바다를 건너 섬을 향해 뻗어나가는 목포대교의 모습이 참으로 경의롭습니다.



 페리 1층에서 바라본 뒷모습입니다. 세찬 물보라가 페리를 밀어내는 듯한 모습을 보이고 있습니다.



 페리에 탑승한 차량들은 배 안에서 최대한 움직이지 않도록 끈으로 확실히 결속되어 있습니다.



 페리의 전면에서 바라본 모습입니다. 마치 차들이 당장이라도 앞으로 나아갈 듯한 모습을 보는것만 같군요.



 페리에 탑승하였던 차량의 운전자 분들이 잠시 나와 바람을 쐬는 듯한 모습입니다.



 섬 사이로 삐죽 나온 듯한 차의 모습이 참 앙증맞게 보이는군요.



 무인도같이 생긴 섬에도 보시는 바와 같이 민가가 보입니다. 눈앞에 보이는 보트는 급할 때 쓰는 용도이려나요?



 페리 안에서 급할 때 이용할 수 있도록 화장실도 마련되어 있습니다.



 페리 안에서 길고 긴 2시간 반 정도의 시간이 흐르고 어느덧 눈앞에 하의도가 모습을 드러내기 시작합니다!



 하의도에 도착한 저를 맞이해 주는 농협 간판이 눈에 들어오는군요.



 몇달전에 새로 오픈한 하의도 여객터미널입니다. 이전 시설보다 디자인에 신경을 쓴 모양입니다.



 하의도에 도착한 페리는 실어두었던 차량들을 힘껏 토해냅니다.



 이렇게 조그마한(?) 섬에 주유소가 떡하니 있는 모습이 저에겐 상당히 신기해 보였는데

 조그마한(?) 하의도에 도착했다넌 저의 생각은 하의도를 구석구석 돌아다니면서 산산히 부서지고 맙니다.


 다음 포스팅에서 2부를 진행하도록 하겠습니다~!


하의도 여행기(2) - 故 김대중 전 대통령 생가 방문기[2016.04.09]


300x250

푸르른 바다위에서 펼쳐지는 길! 진도 신비의 바닷길 축제[2016.04.08]

 평소 여행을 좋아하던 저에게 있어 정말 가고 싶었던 여행지 중 하나는 특정 기간동안만 개최되는 축제들입니다. 특히 몇몇 축제들은 기상 상황도 따라주어야 하기 때문에 더더욱 보기 어려운 축제도 있습니다. 이번에 제가 소개해드릴 진도 신비의 바닷길 축제의 경우 날씨 뿐 아니라 달과 지구의 거리에 따라 축제의 개최기간이 정해지기 때문에 어찌보면 우주적 스케일의 축제라 할 수 있겠습니다.

 진도 신비의 바닷길 축제에 관심을 가지게 된 계기는 어느 바쁘디 바쁜 일상을 보내던 제가 뉴스를 통해 보게 된 광경이었습니다.



 물론 뉴스를 통해 각 지방의 전통 문화나 축제와 같은 관광지에 대한 소식은 종종 접할 때 마다 가고 싶다는 생각은 해왔지만 제게 있어 신비의 바닷길은 다른 관광지들보다 상당히 큰 매력을 느꼈습니다.



 위의 화면과 같이 사람들이 바다 위를 서슴없이 걸어가는 광경을 보았을 때 한 편으로는 놀라우면서도 기회가 된다면 꼭 가보고 싶다는 생각이 들 정도였습니다. 특히나 바닷길이 열리는 날이 한정되어 있기 때문에 축제 기간이 한정되어 있다는 점 또한 저에게는 큰 매력이었습니다.

 마침 올해 2016년에는 진도 신비의 바닷길이 4월 초순으로 비교적 춥지 않는 날씨에 진행되기에 날씨도 딱 좋다고 판단이 되어 무심코 카메라를 들고 진도로 향하게 됩니다.


 - 진도 신비의 바닷길 찾아가는 방법

1. 자가용을 이용해서 가실 경우 아래 주소를 내비로 찍어서 오시면 되겠습니다.

위치 검색시 '가계해수욕장'이라 치시면 찾아가기 더욱 수월합니다!


전라남도 진도군 고군면 신비의바닷길 148






2. 여행사를 통해 패키지관광으로 방문하는 방법입니다.

패키지관광으로 진행되므로 신비의 바닷길 이외에 곳곳에 있는 진도의 관광지를 방문하기가 수월하니 이 방법도 추천드리는 바입니다.


3. 대중교통을 타고 이용하시는 경우

 제 경우 버스를 타고 관광지까지 방문하였습니다. 진도군 내의 농어촌버스는 타지역에 비해 뜸하게 운영되기 때문에 시간표를 숙지하지 않고 오실 경우 낭패를 볼 수도 있기 때문에 필히 확인하시고 가시길 바랍니다. 


 - 목포/광주를 경유하시면 진도로 가는 버스를 쉽게 구할 수 있으니 참고하시길 바랍니다. 광주보다 목포에서 차가 좀 더 많습니다.

 - 서울에서 출발하는 경우 동서울터미널(강변)에서 진도로 바로 갈 수 있는 버스노선이 있습니다. 2016년 기준으로 아침과 오후 2대가 운행중입니다.


 버스를 타고 방문하시는 분이라면 아래와 같이 진도공용터미널에 도착하실 수 있습니다.



 진도공용터미널에 도착하신 다음 '회동'방면으로 가는 군내버스 승차권을 구매합니다. 2016년 4월 현재의 가격으로 각각 2100원과 2800원으로 전자의 경우 약 25분, 후자의 경우 약 35분정도 소요됩니다. 회동에서 터미널로 돌아오는 시간은 아래 시간표에서 회동까지 걸리는 소요시간을 더해주면 되겠습니다. 전자의 경우인 송군·회동 방면에서 온 버스의 경우 막차는 약 오후 6시 25~30분 정도로 예상하시면 되겠습니다.



 송군·회동 방면이 25분, 가계·회동 방면이 35분 가량 소요됩니다. 물론 버스들 자체가 몇 대 운행되지 않으니 먼저 오는 버스를 타는 쪽이 여러모로 편합니다.



 이렇게 '회동'방면으로 써있는 곳으로 가셔서 대기하시면 되겠습니다.


 방문 당시 터미널 안에서 외지에서 온 방문객을 위해 신비의 바닷길 축제에 가는 방법을 안내해주시는 문들도 계십니다. 지자체 차원에서 진도 읍내로 운행하는 셔틀버스도 운행해준다면 더 좋았을텐데 말이지요.




 터미널 내 슈퍼에서 진도 특산물을 팔고 있는 것을 보실 수 있습니다. 특히 홍주는 제 가족들이 즐겨마시는 술 중 하나이고 증류주이기에 맛도 참 좋습니다. 육지의 안동소주 만큼 도수도 40도에 가까운 독한 술이죠.



 가까이에서 찍어보니 하나만 사갈까 하는 생각이 물씬 풍깁니다. 500ml 한 병이 1만5천원 정도 하니 간단히 음주하고 싶으신 분들께 좋을 듯 하네요!



 진도 읍내에는 위와 같이 신비의 바닷길 축제가 진행중임을 알리는 현수막을 걸어두고 있습니다.



 버스를 기다리던 도중 발견한 진도군 관광 안내도입니다. 신비의 바닷길은 진도의 동쪽에 있는 가계 해수욕장 인근에 있음을 확인하실 수 있습니다.



 진도 신비의 바닷길은 일본의 한 가수가 노래가사의 소재로 사용되기도 해서 일본 내에서도 알려진 관광지라고 하더군요. 그 때문인지 관광지에 가던 도중 일본에서 오신 관광객분들을 만나기도 하였습니다. 지자체에서 나름 해외에 홍보하는 관광지인데 진도 내 관광하기 좋게 개선해줄 의향은 없는걸까요......



 마침 회동으로 가는 농어촌버스가 터미널로 들어옵니다!



 버스 앞면에 '회동'이라고 적힌 것을 확인하시고 버스에 승차하시면 되겠습니다.



 버스를 타고 가던 도중 고군면 면내를 지나



 드넓은 시골풍경을 지나치다보면 어느새 신비의 바닷길에 버스가 도착하게 됩니다.



 진도 내에 생각보다 진돗개들이 많이 돌아다니고 있더군요.

 다른건 몰라도 진도군에서는 진돗개를 상당히 깐깐하게 관리를 하고 있어서 이 개들이 섬 밖으로는 쉽게 나갈 수 없다고 하더군요.

 개인적으로 진돗개의 용맹한 모습이 참으로 맘에 듭니다.



 신비의 바닷길이 열리기를 기다리는 동안 각종 축제들이 펼쳐집니다. 기다리던 와중에 울리는 풍악소리가 참으로 흥겹습니다.



진도군의 주요 행사이기도 하다보니 군내에서 행사 홍보가 나름 적극적인 편입니다.



저 멀리 배들이 보입니다. 잠시후 배들이 지나가던 곳으로 사람이 걸어다닐 수 있는 길이 열리는 것이지요!



축제 기간동안 신비의 바닷길 인근에는 다양한 먹을거리들이 판매되고 있습니다.

홍어회가 먹을만하다고 하던데 시간이 된다면 한 번 먹어보고 싶네요.



회동 전체 안내지도입니다.



행사도중 각종 안내를 받을 수 있는 종합상황실이 위치해있습니다.



놀랍게도 신비의 바닷길이 보시는 바와 같이 도로명주소로 쓰이고 있습니다! 관광지 차원에서 상당히 재치있는 지명이네요.



신비의 바닷길을 통해 걸어갈 수 있는 모도로 가는 배를 타는 곳입니다.



저 멀리 모도가 보이는군요. 평소에는 배를 타고 가야 하는 저 섬을 걸어갈 수 있다는 겁니다!



행사기간에 신비의 바닷길에 가기 위해서는 입장권 5000원을 내야 합니다.

이 입장권으로 진도 내에서 5000원 상품권처럼 사용할 수 있어 지역 경제에 나름 도움이 될 수 있을 듯 합니다.



신비의 바닷길이 열리기를 치켜보는 관광객들의 모습입니다.



신비의 바닷길 인근에 이렇게 한 사당이 위치한 것을 보실 수 있습니다.



이곳은 신비의 바닷길의 전설로 전해져 내려오는 뽕할머니의 영당이었습니다.

뽕할머니가 바다 건너에 있는 가족들을 보고싶어 하셨는데 정말 기적같이 바닷길이 열리면서 할머니가 바다를 건너갈 수 있게 해주었다는 전설은 할머니의 간절한 마음이 반영되었다 할 수 있지요.



그런데 할머니 곁에 있는 소주는 다름아닌 요즘 유행하는 과실주들이었습니다!

뽕할머니도 우행하는 술을 드신다니!



관광지 한 컨에는 대학생들이 그린 그림들이 걸려있습니다.



혹시 바다를 건너고자 하시는 분이라면 인근에서 대여할 수 있는 장화를 사서 쓰시는 걸 추천드립니다.

생각보다 바닥이 질어서 걸어다니기가 불안합니다. 자칫하면 뻘에 빠져 신발을 버릴 수 도 있으니까요.



길 한켠에서는 풍물놀이가 한창입니다.



신기하게도 편의점이 간이천막을 설치하고 영엽중이더군요.

생각보다 많은 사람들이 편의점을 이용하고 있던 모습입니다.



어느덧 물이 빠지기 시작했는지 선두에 계신 분이 바다위를 걷기 시작합니다.



바닷길이 열릴 때 즈음이 되니 대기중이던 사람들이 분주해지기 시작합니다.



바다를 건너기 전 사람들이 뽕할머니 동상 앞에서 소원을 비는 듯 보입니다.



어느덧 사람들이 바다 위를 따라서 걸어가기 시작합니다.




바닷길이 열리는 모습이 드론을 통해 실시간으로 중계되고 있는 모습입니다.



바닷길이 열리는 곳 바로 앞에는 바닷길 체험관이 들어서있습니다.

건물 내 카페에는 신비의 바닷길 보기위해 사람들이 몰려있습니다.




신비의 바닷길 입구에는 뽕할머니상이 모도를 바라보는 방향으로 서있습니다.

할머니 옆에 있는 호랑이가 상당히 용맹스러워 보이는군요.



바닷길을 건너던 행렬도 어느덧 중반에 다다르고 있습니다.



바닷길이 열린 광경을 본 사람들이 모여서 강을 건너갈 준비를 하고 있습니다.







바닷물이 빠진 길을 따라 사람들이 조개를 캐고 있습니다.



바닷길을 따라 이어진 행렬은 계속되고 있습니다.



신비의 바닷길 인근에 있는 전망대에 올라와보았습니다. 사진에 보이는 의자에 앉아있으면 사람들이 바닷길을 건너고 있는 모습을 한눈에 볼 수 있어 참 좋군요.






바닷길이 열린지 20분 경과되었으나 여전히 많은 사람들이 바다를 건너고 있는 모습입니다.



사람들이 바다를 건너는 광경을 멀리서 바라보는 광경입니다.



진도답게 진돗개를 정말 많이 볼 수 있었습니다.

위 두 친구는 관광객들 보호차원에서 인지 집 안에 가두어둔 모습입니다.


 진도 신비의 바닷길에 관심있는 분이라면 한 번 방문해보시기를 권장드립니다! 아, 혹시 진도에 방문하신다면 가시는 길에 특산물인 진도 홍주를 지인들에게 추천해보시기를 권장드립니다. 정말 좋습니다!

300x250

안드로이드 프레임워크 프로그래밍(28) [System Service의 proxy와 native의 Interface 상속 구조]

 안드로이드 프레임워크를 공부하는 데 있어 다루게 되는 내용 중 가장 중요한 것을 꼽아본다면 각 프로세스간의 통신 방식인 Binder의 활용이 아닐까 생각합니다. 실제로 안드로이드 운영체제 내에서 동작하는 System Service가 Android 애플리케이션 프로세스 상호간에 통신하기 위해서는 IPC(Interprocess Communication)통신의 일종인 Binder 통신을 사용합니다. 안드로이드 운영체제 또한 리눅스를 기반으로 만들어졌으므로 프로세스의 구조 또한 리눅스의 그것과 비슷하다고 할 수 있습니다. 다만, 기존 리눅스에서 사용하는 IPC 뿐 아니라 RPC(Remote Process Communication)를 사용해 AIDL(Android Interface Definition Language)를 통해 좀 더 간단하게 프로세스간 통신을 할 수 있게 하고 있습니다.



 위의 그램은 Application에서의 프로세스와 System Service에서의 프로세스가 Binder 드라이버를 통해 커널 상으로 통신하고 있는 모습을 그림으로 나타낸 것입니다. 본 포스팅에서는 위와 같은 Binder를 사용하기 위해 proxy와 native에서 Interface를 구성하는 과정에 대해 간단히 설명드리겠습니다. Android에서 proxy에 해당되는 프로세스는 앞부분에 bp를, native에 해당되는 프로세스는 bn을 붙여줍니다. 아마도 약자는 각각 binder proxy, binder native로 추정됩니다.


 먼저 System Service중 하나인 CameraService를 예를 들어 설명해 보겠습니다. 실제로 CameraService는 System Service의 일부로서 이를 사용하기 위해서는 Interface로 구성된 ICameraService를 통해 Binder를 통한 프로세스 통신을 해야 합니다.


/frameworks/av/include/camera/ICameraService.h

1
2
3
4
5
6
7
8
9
10
11
12
class ICameraService : public IInterface
{
public:
....
    virtual status_t connect(const sp<ICameraClient>& cameraClient,
            int cameraId,
            const String16& clientPackageName,
            int clientUid,
            /*out*/
            sp<ICamera>& device) = 0;
....
}
cs


 ICameraService 클래스는 IInterface 클래스를 상속받고 있으며 내부에는 virtual 함수 connect()가 있습니다. 차후 connect()함수는 ICameraService 클래스를 상속하는 클래스에서 정의될 것입니다.


/frameworks/native/include/binder/IInterface.h

1
2
3
4
5
6
7
8
9
10
11
class IInterface : public virtual RefBase
{
public:
            IInterface();
            static sp<IBinder>  asBinder(const IInterface*);
            static sp<IBinder>  asBinder(const sp<IInterface>&);
 
protected:
    virtual                     ~IInterface();
    virtual IBinder*            onAsBinder() = 0;
};
cs


 IInterface 클래스가 RefBase 클래스를 상속받고 


/frameworks/rs/cpp/util/RefBase.h

1
2
3
4
5
6
7
class RefBase
{
public:
 
...
 
}
cs


 RefBase 클래스는 안드로이드 native 단계에서의 framework 내에 있는 거의 대다수의 Class의 최상 부모입니다. 안드로이드에서는 이를 통해 각 클래스의 성질을 설정하고 있다고 생각해주시면 되겠습니다.


 그렇다면 위에서 설명해드렸던 ICameraService를 상속받는 클래스는 어떤 것이 있을까요? Application 프로세스가 System Service 프로세스와 통신하기 위해서는 총 2가지의 클래스를 사용하게 됩니다. Application 프로세스에서는 BpCameraService 클래스를, System Service 프로세스에서는 BnCameraService를 통해 Application 프로세스에서 요구하는 명령을 수행하게 됩니다.

 아래 소스코드는 ICameraService 클래스를 상속받는 BpCameraService의 모습을 나타냅니다.


/frameworks/av/camera/ICameraService.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class BpCameraService: public BpInterface<ICameraService>
{
public:
    BpCameraService(const sp<IBinder>& impl)
        : BpInterface<ICameraService>(impl)
    {
    }
....
    // connect to camera service (android.hardware.Camera)
    virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
                             const String16 &clientPackageName, int clientUid,
                             /*out*/
                             sp<ICamera>& device)
    {
        Parcel data, reply;
        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
        data.writeStrongBinder(cameraClient->asBinder());
        data.writeInt32(cameraId);
        data.writeString16(clientPackageName);
        data.writeInt32(clientUid);
        remote()->transact(BnCameraService::CONNECT, data, &reply);
 
        if (readExceptionCode(reply)) return -EPROTO;
        status_t status = reply.readInt32();
        if (reply.readInt32() != 0) {
            device = interface_cast<ICamera>(reply.readStrongBinder());
        }
        return status;
    }
....
}
cs

 소스코드를 보시면 아시듯이 BpCameraService는 BpInterface 클래스를 수식하는데 BpInterface 클래스는 ICameraService 클래스를 template로 설정하고 있는 것을 볼 수 있습니다. 그렇다면 BpInterface는 어떻게 생겼을까요? 


/frameworks/native/include/binder/IInterface.h
1
2
3
4
5
6
7
8
9
10
template<typename INTERFACE>
class BpInterface : public INTERFACEpublic BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);
 
protected:
    virtual IBinder*            onAsBinder();
};
 
cs

 놀랍게도 BpInterface는 template로 설저하였던 INTERFACE를 그대로 상속받는 것으로 설정하고 있습니다. 즉, 위의 경우 template인 INTERFACE는 ICameraService인 것이지요. 이렇게 BpInterface는 ICameraService와 BpRefBase 클래스를 다중상속 받고 있는 것입니다.

/frameworks/native/include/binder/Binder.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class BpRefBase : public virtual RefBase
{
protected:
                            BpRefBase(const sp<IBinder>& o);
    virtual                 ~BpRefBase();
    virtual void            onFirstRef();
    virtual void            onLastStrongRef(const void* id);
    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
 
    inline  IBinder*        remote()                { return mRemote; }
    inline  IBinder*        remote() const          { return mRemote; }
 
private:
                            BpRefBase(const BpRefBase& o);
    BpRefBase&              operator=(const BpRefBase& o);
 
    IBinder* const          mRemote;
    RefBase::weakref_type*  mRefs;
    volatile int32_t        mState;
};
cs


 BpRefBase 클래스 또한 RefBase를 상속받고 있습니다. 결과적으로 BpCameraService는 RefBase를 다이아몬드 상속으로 두 번 이상 상속받고 있는 것입니다. 그 때문인지 RefBase는 virtual 클래스로 상속받는 것으로 이해할 수 있습니다.


 위에서 설명한 소스코드의 상속 구조도를 이미지로 표현하면 다음과 같습니다. native에서의 상속 구조도 나타내 보았으니 여러분들께서 BnCameraService를 직접 확인해보시면 원리를 파악하는데 큰 도움이 되실 겁니다. :)






300x250

Windows 10에서 SSL이 적용된 사이트 접속이 되지 않을 때 해결방법

공대생의 팁 2016. 2. 29. 23:32

 최신버전인 Windows 10을 사용할 때 마다 Windows 7에서 발전된 기능들이 신기하면서도 바로 익숙해질 수 있어 참 좋습니다. 다만, 국내의 인터넷 환경에서는 아직까지도 Windows 10에서는 접속이 원활하지 않는 사이트들이 종종 등장합니다. 특히 기존 SSL 보안 방식을 적용하는 사이트들의 경우 메인 페이지 조차 뜨지 못하는 모양입니다.


 운전면허를 발급할 일이 있어 도로교통공단 홈페이지 'e-운전면허(http://dls.koroad.or.kr)'에 접속하였더니 아래와 같은 화면이 저를 반겨주고 있는 것입니다.



 최신 버전의 Chorme의 경우 SSL이 적용된 페이지에 접속되지 않고 위와 같이 에러 화면이 나타납니다.

"해당 웹페이지를 사용할 수 없음" "ERR_SSL_VERSION_OR_CIPHER_MISMATCH"


 물론 최신 버전의 파이어폭스에서 또한 이와 유사한 화면을 만나볼 수 있습니다.

"연결이 안전하지 않음" "ssl_error_no_cypher_overlap"


 그렇다면 과연 Internet Explorer로는 메인 페이지라도 확인할 수 있을까요?



 "이 페이지를 표시할 수 없습니다." "[고급] 설정에서 TLS 1.0, TLS 1.1 및 TLS 1.2를 켠 다음 다시 연결해 보십시오. 이 오류가 지속되면 이 사이트에서 지원되지 않는 프로토콜을 사용하는 것일 수 있습니다. 사이트 관리자에게 문의하십시오.


 최신 버전의 Internet Explorer에서 또한 일반 설정으로는 SSL 보안이 적용되어 있는 페이지를 바로 보실 수 없습니다. Windows 10에서 부터는 SSL 보안이 적용된 웹페이지에 접속하기 위해서는 설정을 변경해야 할 필요가 있습니다.


 먼저 설정 버튼(톱니바퀴)를 클릭하신 후, 인터넷옵션(O)를 클릭합니다.



 '고급'탭을 선택하신 후 'SSL 2.0 사용' 또는 'SSL 3.0 사용'을 체크하신 후 새로고침 버튼을 누르거나 F5키를 누릅니다.



 설정이 제대로 되었다면 아래와 같이 메인 페이지가 웹브라우저에 나타나는 것을 확인하실 수 있습니다.



300x250

우분투 커널 업데이트 후 VirtualBox가 제대로 동작되지 않을 때 해결방법(버전 5.0.14 이후)

공대생의 팁 2016. 1. 21. 16:44

 리눅스 환경에서 VirtualBox를 사용할 때 가장 불편한 점 중 하나는 리눅스 커널 이미지를 업데이트할 때 마다 VirtualBox를 다시 컴파일 해야 하는 경우이지요. 그런데 업데이트를 하던 도중에도 종종 VirtualBox를 실행한 후 경고문으로 나오는 명령어를 입력해도 컴파일이 진행되지 않는 경우가 종종 발생합니다.

 이전 포스팅에서 기존의 명령어로 컴파일이 수행되지 않을 때 해결방법으로 제시하였던 명령어가 아래와 같았습니다.

# sudo /sbin/rcvboxdrv setup

그런데 이번에는 아래와 같은 에러가 나타나는 것을 보았습니다.

 위와 같이 나올 경우 아래의 명령어를 입력하시면 해결하실 수 있습니다.


$ sudo /usr/lib/virtualbox/vboxdrv.sh setup


위 명령어를 수행한 후 VirtualBox가 정상적으로 동작하는 것을 확인하실 수 있습니다.

출저 : http://unix.stackexchange.com/questions/255519/virtualbox-kernel-module-installing-issue

300x250

기업 탐방기록 - SAP 코리아 방문기[2016.01.12]

흔치않은일상 2016. 1. 15. 17:11

 정보화 시대에 도래하게 되면서 기업들은 기존에 자신들이 해오던 업무 처리를 전산으로 변경하고 있습니다. 그 과정에서 기업들은 자신들의 주요 업무에 최적화된 전산 시스템을 사내에 구축해야 합니다. 하지만 거대한 기업 내에서 전산 작업을 전담하는 부서를 만들기엔 상당히 부담스러운 과정인 데다가 초기 설비 비용 또한 상당합니다. 게다가 중소기업에서 이를 단독으로 수행하는 업무를 하기엔 비용 이외의 상당한 부담이 따릅니다.

 이러한 기업들을 위해서 솔루션을 제시하여 전산 시스템을 설비하고 이를 관리하는 회사가 필요하게 되었는데요 이번에 소개해드릴 SAP사가 바로 그러한 회사중 하나입니다.

 SAP사는 1967년 독일 만하임에서 IBM 출신 엔지니어들이 설립한 소프트웨어 기업입니다. 일반적인 사람들에게 SAP는 잘 알려지지 않은 B2B 기업입니다만 주요 고객인 기업에게 있어 상당한 인지도를 가지고 있는 회사입니다. SAP의 주요 고객은 자신들이 주로 다루는 전산 업무를 처리하기 용이한 솔루션인 소프트웨어를 필요로 하는 회사들입니다. 최근 빅데이터가 주목을 받게 되면서 SAP에서도 빅데이터를 사용하기를 원하는 기업들을 대상으로 SAP HANA를 제공하고 있는데요. 특히 회사내 데이터들을 시각화하여 이를 그래프와 같은 수치로 제공하는 프로그램인 Lumira또한 SAP사의 주요 상품이기도 하지요.


다양한 형식으로 가공된 데이터들(xml, MySQL 등)의 데이터를 수치로 시각화하여

자료를 재구성하는 프로그램 Lumira


 SAP의 한국지사인 SAP 코리아는 다른 해외지사와는 다르게 현지에 연구개발 투자를 하여 국내에서도 소프트웨어 개발 부서를 두고 있습니다. 인원은 약 200명 규모로 한국에서 개발된 소프트웨어가 해외에서도 사용되기도 할 정도로 SAP는 한국에서의 투자도 상당할 것으로 보입니다.


 이번 SAP 코리아 방문은 고려대학교 공학교육거점센터에서 진행된 '빅데이터를 활용한 SW 융합 교육과정'의 일정차 방문하게 되었습니다. SAP 코리아는 지하철 도곡역 인근에 있는 군인공제회 빌딩 내에 위치해 있습니다.



 도곡역 4번 출구에서 내려 계속 앞으로 가다보면 군인공제회 건물을 보실 수 있습니다.


 저 멀리 입주 기업 간판이 눈에 보입니다. 가까이서 확인해 보면...



 위에서 보시는 바와 같이 SAP사의 로고를 만나보실 수 있습니다. 군인공제회 입구로 들어가 오른쪽 엘리베이터 타는 곳으로 이동합니다.



 안내판을 보이는 것과 같이 SAP 코리아는 총 4개의 층에 상주하고 있습니다. 경비원에게 방문 목적을 말한 후 사내에 들어가보도록 합니다.



엘리베이터에서 내리시면 보이는 바와 같이 SAP사의 모습을 볼 수 있습니다.



제가 이번에 SAP 코리아를 방문한 목적이기도 한 빅데이터 SW 융합교육과정 프로그램 입니다.



 사내에서 헌혈 캠페인이 진행중인 것으로 보입니다. 피와 같은 발음인 P를 활용한 직원분들의 재치있는 아이디어가 인상깊습니다.



 제가 교육을 받았던 SAP 코리아 24층 안내도 입니다.



 올해로 SAP가 한국에 진출한지 20주년을 맞아 사내에서 다양한 이벤트가 진행되고 있는 모습을 볼 수 있었습니다.



 한 켠에는 직원들이 물품들을 쉽게 사용할 수 있도록 구비되어 있습니다.



 회의실 내부입니다. IT 기업들에서 흔히 볼 수 있는 분위기의 모습입니다.

 회의실 내에 있는 책상은 높낮이를 자유롭게 바꿀 수 있는 점이 참 신기하더군요.



 사내에서 바깥을 바라본 모습입니다. 일을 하다 창밖을 보면 어려운 일도 술술 풀릴 듯 한 느낌이 듭니다.



사내 사무실 모습입니다. 개인 사석이라기 보다는 공용 좌석에서 자유롭게 사용하는 듯한 모습입니다.

아마 사석의 경우 다른 층에 구비되어 있는 것으로 보입니다.



직원 한 분께서 열심히 일을 하고 계시는 모습입니다.



사내에 구비된 사물함입니다. 자동식으로 자신의 사원증이 있어야 열리는 구조인 듯 합니다.



사무실에서 회의실 사이의 통로입니다.



벽에 붙여둔 말들이 참 재치있군요. 이런 분위기의 회사 참 좋습니다!



Quiet room이라 적혀있는 것으로 보아 조용하게 업무를 보아야 하는 곳으로 보입니다.

안에 전화기가 있는 것으로 보아 중요한 통화가 필요할 때 쓰이는 곳으로 보입니다.



회사 한 켠에 마련되있는 카페테리아입니다. 냉장고와 커피머신 등 없는게 없어 보입니다.



회사 창밖을 바라본 풍경입니다. 역시 강남은 빌딩의 숲으로 둘러쌓여있는 모습이로군요.



SAP 코리아에서 진행하는 Design Thinking 수업이 한창 진행중인 모습입니다.




 SAP 코리아에서는 대학생과 직장인 및 관공서에서 근무하는 공무원들을 대상으로 Design Thinking 수업을 진행하고 있습니다. 실제 Design Thinking은 미국 캘리포니아의 Stanford University에서 2박 3일 일정으로 구성되어 있는데, 이 수업에서는 단 1일로 압축해서 진행하고 있습니다. 확실히 수업에 참가해보니 너무 짧은 시간에 많은 것을 다루려다 보니 시간 부족 등의 아쉬운 점들이 느껴집니다. 하지만 평소에는 쉽게 접하기 어려운 수업이기에 개인적으로는 실리콘벨리 탐방 이후로 상당히 인상깊었던 시간이었습니다.




 Design Thinking 수업을 통해 SAP 코리아에 방문한 이번 기회에 많은 것을 느꼈습니다. 작년 실리콘벨리 탐방을 하였을 때 느꼈던 회사 내의 자유로운 IT 기업의 분위기를 이곳 대한민국에서도 느낄 수 있었다는 점이 상당히 큰 의미가 아니었나 싶습니다.


 현재 SAP 코리아에서 진행하고 있는 Design Thinking 교육과정은 실제 미국의 IT 기업들이 회사내에서 활용하고 있는 방식 중 하나입니다. 회사내 직원들이 서로 수평관계로서 서로의 생각을 공유하는 집단 지성을 통해 좋은 아이디어를 찾아가는 과정은 상명하복 문화가 주를 이루고 있는 우리나라의 기업문화에 있어서 상당히 신선한 충격일 것입니다. 실제 국내 기업들도 이 Design Thinking 교육과정을 통해서 자신의 회사가 발전할 수 있다면 이를  적용해 보겠다는 의향을 비친 기업인 분들도 계시다고 하니 한 편으로는 우리나라 기업들의 달라질 기업문화의 모습을 살짝 기대해 보기도 합니다.


 언제부턴가 빅데이터라는 말이 유행하고 있습니다. SAP 또한 후발주자로서 빅데이터를 처리하는 분산컴퓨팅 사업에 많은 투자를 하고 있는 것을 보면 앞으로도 빅데이터는 반짝 하고 사라지는 유행으로 끊나지 않고 오늘날의 인터넷처럼 일상의 일부로서 자리잡을 것으로 보입니다. 그런만큼 빅데이터를 설계하는 데 있어 중요한 Design Thinking또한 빅데이터의 시대를 맞이하기 위해 준비해야 할 전략이 될 것으로 보입니다.

300x250

Spectral Clustering 알고리즘 응용(Minimum Cut)

공대생의 팁 2016. 1. 1. 23:56

 지난 포스팅에서 Spectral Clustering Algorithm(스펙트럼 군집화 알고리즘)의 정의와 원리에 대해 설명을 하였습니다. 이번 포스팅에서는 Spectral Cluster로 분류된 그래프 자료를 통해 각각의 집단 그룹으로 나누어주는 알고리즘인 Minimum cut에 대해 알아보도록 하겠습니다.


 본 포스팅에서는 이전 포스팅에서 설명하였던 Spectral Clustering 알고리즘을 숙지하고 이를 통해 구하게 되는 유사도 행렬(Affinity Matrix)를 알고 있다는 전제하에 설명할 것입니다. Spectral Clustering에 대해 자세히 알고자 하시는 분께서는 아래 포스팅을 참고해 주시길 바랍니다.


Spectral Clustering Algorithm(스펙트럼 군집화 알고리즘)

http://elecs.tistory.com/167


 지난 포스팅에서 다루었던 각 Cluster 그래프를 한 번 더 보도록 합시다.

 위 그래프는 같은 Cluster에 속한 데이터끼리는 굵은 선으로, 서로 다른 Cluser에 속한 데이터끼리는 가느다란 선으로 표시되어 있습니다. 위 데이터를 Spectral Clustering을 통해 Affinity Matrix로 각 데이터 사이의 유사값을 구함으로서 각 Cluster가 서로 구분될 수 있도록 수치화 하였습니다.

 각 데이터간의 유사도를 표현한 Affinity Matrix를 가지고 위 데이터를 명확하게 나눌 수 있는 방법은 존재할까요? Minimum cut 알고리즘은 주어진 데이터 사이의 유사도 값 중 가장 작은 값으로 각 Cluster 그룹을 나누어주는 기능을 합니다. 위의 그래프를 보았을 때 가느다란 선으로 연결된 데이터 사이를 나누게 되면 각 그룹이 뚜렷다게 구분되는 것을 보실 수 있습니다.

 예시를 통해 자세히 알아보도록 하겠습니다. 아래는 데이터 사이의 선분 vertex 사이에 유사값을 부여한 그래프입니다.



 직관상으로 보았을 때 Minimum cut 알고리즘을 적용하게 된다면 유사도 1과 2로 되어있는 부분을 나누면 두 개의 Cluster로 분별될 수 있다는 것을 직감하실 수 있습니다. 아래는 위의 그림에 Minimum cut을 적용한 그림입니다.

 위 그림대로 각 Cluster 사이의 vertex를 자름으로서 두 개의 Cluster로 분류된 것을 확인할 수 있습니다. 이를 식으로 구현하기 위해서는 어떻게 진행하게 될까요? Minimum Cut 알고리즘은 다음과 같은 해법을 제시합니다.

 아래 식에서 A와 B는 2개의 Cluster를 말하며, w는 Affinity Matrix(유사도 행렬)의 요소 중 하나입니다.

 위 식의 결과값은 두 cluster를 나눌 때 잘리는 부분의 유사값 w를 모두 합한 값입니다. 즉 결과값은 두 Cluster로 분리하기 위해 자르는 유사값의 총 합이 최소가 될 경우 위 식이 성립된다는 것을 알 수 있습니다.

 위 식을 증명하는 방법을 알아보겠습니다. 이전 포스팅(Spectral Clustering 알고리즘)에서 서로 다른 Cluster를 분류할 때 다음 두 조건을 만족해야 한다고 하였습니다.


1. 같은 Cluster 내의 요소끼리의 유사도는 큰 값을 갖는다.

2. 다른 Cluster에 속한 요소끼리의 유사도는 작은 값을 갖는다.


 위 조건을 식으로 나타내면 다음과 같습니다.


 위의 식을 설명하자면 각 Cluster 그룹 A와 B에 속하는 affinity matrix 요소의 값을 모두 더한 후 A와 B 사이를 연결하는 affinity matrix 요소의 값을 모두 더한 값의 2배를 뺀 결과값이 최대가 되는 경우를 나타낸 것입니다. 위 식을 응용하면 아래와 같은 식을 만들 수 있습니다.


 위 식은 affinity matrix 내의 모든 값들을 더한 후 이를 위에서 구했던 식을 뺀 값이 최소가 되는 경우를 식으로 표현한 것입니다. 위 식이 maximum이 되는 값을 찾는 경우이므로 아래의 식은 minimum이 된다는 것을 유추할 수 있습니다.

 유도한 식을 구하기 위해 이를 실제 Affinity Matrix 행렬을 통해 설명해보겠습니다. 아래는 Affinity Matrix인 W를 나타냅니다.

 위의 모든 행렬요소의 값을 구합니다. 이는 아래의 과정을 나타내는 것입니다.


 다음으로 각 행렬 내에서 같은 Cluster 그룹에 속하는 요소들을 모두 더합니다.

위에서 붉은 색으로 강조한 부분을 모두 더합니다. 이는 아래의 과정을 나타낸 것입니다.

끝으로 각 행렬 내에서 다른 Cluster 그룹끼리 연결된 요소들의 값을 모두 더합니다.


 위에서 파란 색으로 강조한 부분을 모두 더합니다. 이는 아래의 과정을 나타낸 것입니다.


 위 과정의 계산을 모두 수행하게 되면 최종값은 붉은 색으로 강조한 부분이 빠지고 파란 색으로 강조한 부분이 두드러지게 됨을 알 수 있습니다. 즉, 각 Cluster 사이에서 cut 되는 부분에 해당되는 요소값이 남는다는 것을 알 수 있습니다. 즉 위의 식은 아래와 같은 원리의 값이 됨을 알 수 있습니다.



 그렇다면 주어진 Affinity Matrix 값을 사용하여 어떻게 최소값을 구할 수 있을까요? 이전 포스팅에서 설명드렸던 고유벡터를 사용하면 행렬의 minimum값을 구할 수 있습니다. 그럼 위 Affinity Matrix를 사용하여 고유벡터를 구하는 과정을 보겠습니다.


 위 식에서 주어진 각 값을 행렬 D와 W로 나누어봅니다. 이때 행렬 D는 대각행렬(Diagonal Matrix)로서 각 대각행렬의 행은 Affinity Matrix의 같은 행 내의 모든 값의 합을 나타냅니다. 아래는 이를 정의한 내용입니다.


 위에서 구한 행렬 (D-W) 행렬의 최소값은 아래의 식과 같이 고유벡터 x를 구함으로서 알 수 있습니다.

이 때 나오게 되는 고유벡터에서 2번째로 작은 고유벡터를 통해 Minimum Cut를 구할 수 있습니다. 이는 이전 포스팅에서 언급한 바와 같이 1번째로 작은 고유벡터를 사용할 경우 각 Cluster 그룹의 분리가 눈에 띄게 나타나지 않기 때문입니다.


 지금까지 Minimun Cut을 사용하여 Spectral Cluster 알고리즘을 나타내는 과정을 살펴보았습니다. 그런데 우리가 지금까지 언급하였던 Minimun Cut 알고리즘에는 단점이 하나 존재합니다. Minimum Cut의 경우 무조건 최소값을 찾는 과정을 거치기 때문에 우리들이 바라는 바와 같이 각 Cluster 그룹이 나누어지지 않는 경우가 발생하는데 아래는 그러한 사례 중 하나를 나타낸다.

 이상적인 방향으로 보았을 때 위의 경우처럼 다른 색깔로 분류된 Cluster 그룹끼리 나누어 지는 것이 맞으나 실제로는 이보다 더 작은 최소값을 갖고 있는 부분에서 나누어지는 것을 볼 수 있습니다. 이처럼 우리의 의도적으로 알고리즘이 적용되지 않는 이유는 Outliar와 같이 해당 cluster에서 다소 거리가 있는 부분에 있는 데이터가 있어 이 부분이 의도치않게 나누어져서 하나의 Cluster가 분류되지 않고 쪼개져 버리는 것을 보실 수 있습니다.

 이러한 Minimum Cut을 대체하게 된 것이 바로 Normalized Cut입니다. Normalized Cut 알고리즘에 대한 자세한 사항은 다음 포스팅에서 이어가도록 하겠습니다.

300x250

안드로이드 프레임워크 프로그래밍(27) [Fedora에서 AOSP 안드로이드 운영체제 컴파일하기]

안드로이드/프레임워크 2015. 12. 31. 12:27

 일반적으로 안드로이드 운영체제를 Build 할 때 거의 대부분의 경우 Ubuntu 환경에서 수행됩니다. AOSP 공식 홈페이지에서도 Ubuntu를 권장하고 있는 바이기도 합니다.

 그래도 혹여나 하는 마음에 Fedora 운영체제에서 안드로이드를 Build 해보는 과정에 대해 포스팅을 해보고자 합니다. 같은 RPM 패키지를 사용하는 Redhat이나 OpenSUSE에서도 이 포스팅의 내용을 적용할 수 있으리라 생각합니다.


Build Version    : Android 6.0.1(Marshmellow)

OS             : Fedora 23 (Twenty Three) 64-bit

JDK Version     : OpenJDK 1.7.0


1. 빌드하고자 하는 AOSP 소스코드를 다운로드 받습니다. 관련 내용에 대해 자세히 알아보고자 하시는 분은 아래 포스팅을 참조해 주기길 바랍니다.

http://elecs.tistory.com/56


$ mkdir ~/bin

$ export PATH=$PATH:~/bin

$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo

$ chmod a+x ~/bin/reop

$ mkdir ~/aosp 

$ cd ~/aosp

$ repo init -u https://android.googlesource.com/platform/manifest -b android-6.0.1_r1

$ repo sync -j4


 위 과정까지 마치셨다면 아래와 같이 aosp 폴더에 소스코드 다운로드 된 것을 확인하실 수 있습니다.


2. 다운로드한 안드로이드 소스코드를 컴파일할 수 있는 환경을 설정합니다. Marshmellow(6.0) 버전의 경우 OpenJDK 1.7.0을 설치해야 합니다. Oracle JDK로 컴파일을 시도하려 해도 시작하기 전에 컴파일이 중단되어버립니다. Fedora의 경우 dnf를 통해서는 최신 버전의 자바만 지원해주기 때문에 사용자가 직접 OpenJDK를 설치해야 합니다. 설치 방법은 아래의 포스팅을 참조해 주시기 바랍니다.


Fedora에 이전 버전의 OpenJDK 설치하기(Install OpenJDK 7 in Fedora 23)

http://elecs.tistory.com/166


만약 설치한 이후에도 해당 버전이 적용되어 있지 않았을 경우 아래의 명령어를 통해 직접 설치해줍시다.


# alternatives --install /usr/bin/java java /usr/lib/jvm/java-1.7.0-openjdk/bin/java 1

# alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-1.7.0-openjdk/bin/javac 1

# alternatives --install /usr/bin/javadoc javadoc /usr/lib/jvm/java-1.7.0-openjdk/bin/javadoc 1


3. dnf를 통해 안드로이드를 빌드하기 위해 필요한 패키지를 설치합니다. 


# dnf install bison gcc xorg-x11-fonts-Type1 libpng15 


4. 이제 다운로드 받은 소스코드를 build 하기 위한 준비과정을 진행해 보도록 하겠습니다. 먼저 build 환경을 초기화합니다.


$ ~/aosp

$ source build/envsetup.sh

$ lunch


 안드로이드 6.0.1 버전 기준으로 build 환경은 다음과 같이 나타납니다.


 여기서 자신이 build 하고자 하는 환경을 선택합니다. 본 포스팅의 경우 Android Studio를 통한 에뮬레이터에서의 실행을 목표로 함으로 1번 혹은 14번을 선택합니다. 혹시 자신이 build 하고자 하는 환경에 대한 설정을 알고 싶으신 분은 아래의 포스팅을 참조해 주시기 바랍니다.


안드로이드 프레임워크 프로그래밍(3) [NEXUS5에 소스 빌드하기]

http://elecs.tistory.com/59


5. 이제 build를 해보도록 합니다.


$ make update-api && make -j4


아래와 같은 결과가 나왔다면 Fedora 운영체제 환경에서 안드로이드 이미지를 빌드하는 데에 성공한 겁니다!


 


300x250

[Hadoop]분산 행렬곱 연산 하둡 예제로 맵리듀스 이해하기(Matrix Multiplication with Hadoop)

프로그래밍 팁/Hadoop 2015. 12. 29. 15:35

 보통 거의 대부분의 프로그램들의 경우 입문자들을 위한 'Hello, World!' 예제들이 제공되어서 해당 프로그램의 이용 방법들을 이해하는 것이 용이한 편인데 이번에 공부하게 된 하둡의 경우 맵리듀스의 특성 때문에 Hello World 예제를 제공하는 것이 애매하지 않았나 싶습니다. 그 만큼 하둡 입문자들에게 있어서 맵리듀스의 원리를 체감하는게 힘들것이라 생각합니다.


 마침 하둡 예제를 찾아보던 도중 맵리듀스의 원리를 쉽게 이해할 수 있는 예제를 알게 되어 이렇게 소개합니다. 여러분들도 열심히 보시면서 이해할 수 있는 기회가 되었으면 합니다!


- 개발 환경

언어 : Java

IDE : VI

JDK 버전 : 1.7.0_91

운영체제 : Ubuntu 14.04

Hadoop 버전 : 2.6.0

Protoc 버전 : 2.5.0

실행 환경 : Terminal(우분투)


 1. Hadoop의 분산 실행 방식인 Map Reduce



 위 그림은 Hadoop의 Map Reduce 방식을 이미지로 도식화한 모습입니다. 먼저 Hadoop을 통해 빅데이터가 입력되면 데이텨의 내부는 입력 Split으로 잘게 분리가 된 후 Mapper를 통해 (Key, Value) 쌍으로 묶여서 각 노드로 보내집니다. 이 때 노드로 분배되는 과정은 각 Key의 Hash값을 기준으로 하여 각 Node 내에 있는 Task Container에게 전송됩니다. 이 때 각 Container는 같은 값을 가진 Key끼리 보내지므로 각 Key별로 같은 값을 계산하는 값을 전송할 수 있는 것입니다.

 (Key, Value)쌍은 각 같은 Key 끼리 Reduce로 전송되어지며 Reduce를 통해 설정이 완료되면 Result로 보내지면서 Hadoop의 hdfs 파일시스템에 저장됩니다.


 2. 행렬곱(Matrix Multiplication)의 원리

 행렬곱이란 이름 그대로 두 개의 행렬을 곱해 하나의 새로운 행렬을 구하는 것을 의미합니다. 행렬곱은 선행하는 행렬의 Row와 후행하는 행렬의 Column 내의 각 성분들의 곱을 합하는 것으로 값을 구합니다. 쉬운 예를 구하기 위해 아래와 같이 행렬 AB가 있다고 합시다.



  행렬 A는 i×j 행렬이며 행렬 B는 j×k 행렬입니다. 두 행렬의 행렬곱은 A×B로 나타낼 수 있으며 A의 Row와 B의 Column에 해당하는 요소 각각의 곱의 덧셈을 구하는 것이 행렬곱을 구하는 공식입니다. 이 때, A의 j에 해당하는 Row의 개수와 B의 j에 해당하는 Column의 개수가 일치해야 행렬곱이 성립됩니다..

 위의 행렬 A×B의 결과 값은 아래와 같이 표현할 수 있겠습니다.

 행렬곱 A×B의 최종 결과는 A의 Column 개수인 i와 B의 Row 개수인 k의 조합인 i×k 행렬로 나타납니다.


참고자료 : 위키백과

https://ko.wikipedia.org/wiki/%ED%96%89%EB%A0%AC_%EA%B3%B1%EC%85%88


 3. 행렬곱의 Map Reduce 구현

  Hadoop의 분산 방식을 활용하여 행렬곱의 결과로 나오는 행렬의 각 요소에 대한 계산을 Map Reduce로 분산하여 계산해보도록 합니다.입력값은 아래와 같이 주어졌다고 가정합니다.


/hadoop-matrix-multiplication/MapInput.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
A,0,1,1.0
A,0,2,2.0
A,0,3,3.0
A,0,4,4.0
A,1,0,5.0
A,1,1,6.0
A,1,2,7.0
A,1,3,8.0
A,1,4,9.0
B,0,1,1.0
B,0,2,2.0
B,1,0,3.0
B,1,1,4.0
B,1,2,5.0
B,2,0,6.0
B,2,1,7.0
B,2,2,8.0
B,3,0,9.0
B,3,1,10.0
B,3,2,11.0
B,4,0,12.0
B,4,1,13.0
B,4,2,14.0
cs


 주어진 입력값의 각 줄을 살펴보았을 때 첫 번째 값은 해당 행렬값이 A인지 B인지를 알려주는 값이고 두 번째는 column값, 세 번째는 row값, 네 번째는 해당 요소의 값을 나타냅니다. 즉 A는 2×5 행렬이고 B는 5×3 행렬이며 행렬곱 A×B은 2×3 행렬이 됩니다. 즉, 행렬곱 A×B를 구하기 위해 필요한 Key의 개수는 행렬곱 A×B의 요소의 개수인 2×3=6이라 할 수 있겠습니다.

 행렬곱 연산이 적용된 Map Reduce는 아래와 같이 그림으로 나타낼 수 있겠습니다.


Hadoop으로 입력된 행렬 데이터는 Map을 거치기 전에 입력 split으로 분리되어 각각의 Mapping 과정을 거치게 됩니다. 여기서 빨갛게 표시한 부분이 Key이고 그 뒤의 값이 Value입니다. Map을 거친 행렬의 각 요소는 각 노드 내의 Task Container에 들어가게 되어 Reduce 단계에서 행렬곱 연산을 수행하게 됩니다. 이 때 Map 단계에서 밑줄친 4개의 (Key, Value) 쌍이 Reduce에 넘어와서 서로의 값이 계산되고 있는 것을 보실 수 있습니다. 연산을 마치게 되면 hdfs 파일시스템에 지정된 폴더에 결과를 저장합니다.


 4. 프로그램 구현

 이제 행렬곱을 구현한 Hadoop 예제를 보도록 하겠습니다. 이 과정에 들어가기 앞서 Terminal 환경에서 Hadoop 프로그램을 컴파일 하는 환경을 구축하는 방법을 알아야 합니다. 아래 그 간단한 예제를 포스팅한 내용을 참조해 주시기 바랍니다.


[Hadoop] pom.xml로 maven 컴파일하기

http://elecs.tistory.com/163


 위의 예제를 참고하시면서 아래 행렬곱 분산 처리 프로그램 소스코드를 보시면 대략적인 동작 원리를 이해하실 수 있을 것입니다.


/hadoop-matrix-multiplication/src/main/java/elecs/tistory/com/MatrixMultiplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package elecs.tistory.com
 
import java.io.IOException;
import java.util.*;
 
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
 
public class MatrixMultiplication {
 
    public static class Map extends Mapper<LongWritable, Text, Text, Text> {
        public void map(LongWritable key, Text value, Context context)
          throws IOException, InterruptedException {
            Configuration conf = context.getConfiguration();
            //행렬 A와 B의 크기를 정의한다.
            int m = Integer.parseInt(conf.get("m"));
            int n = Integer.parseInt(conf.get("n"));
            int p = Integer.parseInt(conf.get("p"));
 
            String line = value.toString();
            String[] indicesAndValue = line.split(",");
            Text outputKey = new Text();
            //Key와 Value를 저장할 값을 정의한다.
            Text outputValue = new Text();
            //Split의 각 줄을 , 단위로 나눈다.
 
            //Key는 행렬곱의 결과로 출력되는 행렬의 위치이다.
            //Value는 해당 행렬의 이름과 위치, 값을 정의한다.
            if (indicesAndValue[0].equals("A")) {
                for (int k = 0; k < p; k++) {
                    outputKey.set(indicesAndValue[1+ "," + k);
                    outputValue.set("A," + indicesAndValue[2+ "," + indicesAndValue[3]);
                    context.write(outputKey, outputValue);
                }
            } else {
                for (int i = 0; i < m; i++) {
                    outputKey.set(i + "," + indicesAndValue[2]);
                    outputValue.set("B," + indicesAndValue[1+ "," + indicesAndValue[3]);
                    context.write(outputKey, outputValue);
                }
            }
        }
    }
 
    public static class Reduce extends Reducer<Text, Text, Text, Text> {
        public void reduce(Text key, Iterable<Text> values, Context context)
          throws IOException, InterruptedException {
            Configuration conf = context.getConfiguration();
            String[] value;
 
            //각 행렬의 위치와 값을 저장할 수 있는 Map을 생성한다.
            HashMap<Integer, Float> hashA = new HashMap<Integer, Float>();
            HashMap<Integer, Float> hashB = new HashMap<Integer, Float>();
            for (Text val : values) {
                value = val.toString().split(",");
                if (value[0].equals("A")) {
                    hashA.put(Integer.parseInt(value[1]), Float.parseFloat(value[2]));
                } else {
                    hashB.put(Integer.parseInt(value[1]), Float.parseFloat(value[2]));
                }
            }
            //행렬 A와 B의 크기를 정의한다.
            int m = Integer.parseInt(conf.get("m"));
            int n = Integer.parseInt(conf.get("n"));
            int p = Integer.parseInt(conf.get("p"));
 
            float result = 0.0f;
            float a_ij;
            float b_jk;
 
            //각 행렬의 요소들과 비교하여 일치하면 서로 곱한 후 더한다.   
            for (int j = 0; j < n; j++) {
                a_ij = hashA.containsKey(j) ? hashA.get(j) : 0.0f;
                b_jk = hashB.containsKey(j) ? hashB.get(j) : 0.0f;
                result += a_ij * b_jk;
            }
            if (result != 0.0f) {
                context.write( key, new IntWritable(sum) );
            }
        }
    }
 
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        //행렬의 크기를 설정해줍니다.
        conf.set("m""2");
        conf.set("n""5");
        conf.set("p""3");
 
        Job job = new Job(conf, "MatrixMultiplication");
        job.setJarByClass(MatrixMultiplication.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);
 
        job.setMapperClass(Map.class);
        job.setReducerClass(Reduce.class);
 
        job.setInputFormatClass(TextInputFormat.class);
        job.setOutputFormatClass(TextOutputFormat.class);
 
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
 
        //하둡 분산 프로그램을 실행한다.
        job.waitForCompletion(true);
    }
}
cs

 위 행렬곱 분산 연산 예제를 실행하면 아래와 같은 결과값이 나옵니다. 각 행렬값 요소의 위치는 변경될 수 있습니다.





출저 : http://magpiehall.com/one-step-matrix-multiplication-with-hadoop/

300x250

Spectral Clustering Algorithm(스펙트럼 군집화 알고리즘)

공대생의 팁 2015. 12. 28. 17:41

 

 오늘날은 인터넷을 통해 수많은 정보를 얻을 수 있는 빅데이터 시대에 도래하였습니다. 정보의 양이 매우 방대하다 보니 데이터 하나 하나 마다 개별로 의미를 찾아보기 보다는 전체 데이터의 특성을 분류하여 의미를 찾기 위해 사용되는 정교한 Clustering(군집화) 알고리즘이 필요하게 되었습니다.

 Clustering 알고리즘의 핵심은 주어진 데이터들을 유사한 특성을 가진 데이터들끼리 분류하여 이를 하나의 같은 그룹으로 만들어 주는 기능입니다. 이러한 Clustering 알고리즘의 특성 덕분에 빅데이터 내의 수많은 정보들을 특정한 카테고리나 클래스로 분류할 수 있는 것이지요.

 본 포스팅에서는 이러한 Clustering 알고리즘 중 하나인 Spectral Clustering 알고리즘에 대해 알아보도록 하겠습니다.

 

1. Data Clustering(자료 군집화)

  초기에 주어지는 데이터는 단순히 자신이 가지고 있는 정보를 그저 나열하고 있는 모습을 띄고 있습니다. 이를 하나의 그룹으로 분류하기 위해 서로 유사한 데이터끼리 묶어주는 작업을 하는 것이 Clustering 알고리즘의 목적입니다.

 

 위의 그림에서 보시는 바와 같이 단순히 나열되어 있던 각 데이터들이 유사한 3개의 그룹으로 나누어지게 된 것을 보실 수 있습니다. 여기서 유사하다는 것은 점 사이의 거리로 나타냅니다. 즉, 서로 가까운 점들끼리 묶어보니 3개의 그룹이 만들어지게 된 것이지요.

 그렇다면 이러한 클러스터링 알고리즘은 어떠한 방식이 있는 것일까요?

 

 

 각 그룹을 분류하는 Clustering 알고리즘은 2가지 접근 방법이 있습니다. 하나는 매개변수 모델 기반 방식(Parametric Model-based)과 그래프 기반 방식(Graph-based) 방식이 있습니다. 매개변수 모델 기반 방식의 경우 말 그대로 주어진 데이터를 가지고 임의의 그룹 중심점을 찾은 다음 반복적으로 그룹의 중심점을 찾아가는 과정입니다. k-Means 알고리즘이 이를 사용한 알고리즘으로서, 반복적인 연산을 통해 결과를 얻어냅니다.

 위의 그림과 같이 거리상으로 떨어져있는 2개의 그룹이 존재한다고 하였을 때 임의의 중심점 2개를 잡은 후 해당 점에서 가까운 데이터들을 그룹으로 만든 후 새로 만들어진 그룹 내의 중심점을 잡고, 다시 만든 중심점에서 가까운 데이터들을 그룹으로 만드는 방식을 반복하다 보면 위의 그림과 같이 한 점으로 수렴하게 됩니다. 이러한 매개변수 모델 기반 방식의 최대 단점은 결과가 항상 일정하지 않으며 가끔씩 잘못된 그룹으로 분류되는 경우가 발생할 수 있다는 점입니다.

 

 그래프 기반 방식은 각 데이터의 점들과 다른 점 사이에 선을 긋고 두 데이터 사이의 유사도에 따라 비중을 부여하는 방식입니다. 그래프 기반 방식에 따르면 두 데이터의 유사점이 많으면 비중을 키우고, 유사점이 낮으면 비중을 낮추는 식으로 하여 이를 기준으로 별개의 그룹으로 나누는 방식입니다. 이 방식을 사용하면 매개변수 모델 기반 방식보다 비교적으로 결과값이 거의 일정하게 나온다는 장점이 있습니다. 두 그룹으로 나누는 데 사용하는 방식으로는 Minimum cut과 Normalized cut이 있습니다.

 

2. Segmentation by Graph Partitioning(그래프 클러스터링)

 설명하기에 앞서 Spectral Clustering을 간단하게 설명해주는 동영상을 참고해봅시다. 바로 이해하기 어려우시겠지만 영상으로 한 번 보는 것이 이어서 설명하는 데에 도움이 될 것입니다.

 

 

 

 위 동영상을 시청하셧다면 이제 설명을 해드리도록 하겠습니다. 아래와 같이 9개의 점들이 3개의 그룹을 이루고 있는 데이터가 있다고 가정해봅니다.

 

 이제 Spectral Clustering 알고리즘을 사용하여 위 그림의 데이터들을 Graph로 표현해보도록 하겠습니다. 이들을 그래프로 나타나기 위해서는 유사도 행렬(Affinity Matrix)를 통해 이를 표현해야 합니다.

 아래의 그림은 위 데이터를 유사도 행렬(Affinity Matrix)로 표현한 것입니다. 각 색깔에 맞추어 유사도 행렬을 만든 점을 주목해 주시길 바랍니다.

 

 이제 적용된 유사도 행렬에서 처럼 위 데이터를 그래프로 나타내봅니다. 색깔로 칠해진 부분은 1로 굵은 선으로, 색깔이 칠해지지 않은 부분은 0으로 가느다란 선으로 표시합니다.

 

 어떤가요? 위와 같이 각 데이터를 그래프로 표현하니 눈에띄게 구분이 되고 있는 것을 알 수 있습니다.

 

 지금까지 우리의 시각에서 판단해서 Spectral Clustering 알고리즘을 수행하였습니다. 그렇다면 컴퓨터를 통해 이를 직접 구현하려면 어떠한 방식으로 해야할까요? 각 데이터의 유사도를 사람처럼 판단해서 위와 같은 그래프로 표현할 수 있는 방법은 있는걸까요?

 

3.Affinity Matrix(유사도 행렬 만들기)

 두 데이터의 유사도가 높을수록 최대한 높은 값을 가지게 하고 반대로 유사도가 낮을수록 최대한 0에 가깝게 값을 만든다면 위의 유사도 행렬(Affinity Matrix)과 비슷한 모양의 행렬이 만들어 질 수 있으리라 생각됩니다. 이를 위해 가우시안 커널 σ을 사용하는데 그 공식은 아래와 같습니다. 이 때 w는 데이터 i, j 사이의 선, σ는 커널의 폭, x는 데이터 i, j의 거리 정보로서 두 데이터 i, j 사이의 거리를 나타낸다 볼 수 있겠습니다.

 이 식에서 가장 중요한 부분은 바로 σ입니다. σ는 각 데이터의 거리간격 w의 결과에 지대한 영향을 미치는 변수로 적절한 값으로 이를 설정하여주면 각 그룹 사이의 간격을 적당하게 구분시켜줄 수 있게 됩니다.  아래의 표는 σ의 크기에 따른 유사도 값 w의 크기의 변화를 나타낸 것입니다.

 

 

 위의 그래프에서 볼 수 있는 바와 같이 σ의 값이 작을수록 각 그룹을 세분하게 나누어줄 수 있으나 자칫하면 같은 그룹에 속하는 데이터 사이의 간격이 조금이라도 멀어지면 w값(affinity)이 급격하게 줄어드는 것을 보실 수 있습니다. 반면, σ의 값이 클수록 다소 멀리 떨어져 있는 같은 그룹 내의 데이터를 포함시킬 수 있으나 자칫하면 비교적 거리가 가까운 다른 그룹의 데이터까지 포함해버리는 경우가 발생하게 됩니다. 아래 Reference에 따르면 σ의 값을 각 데이터 i와 j의 주변 점에서 (2d+1)번째로 가까운 점 사이의 거리를 σ로 사용할 경우 최적의 값을 구할 수 있다고 소개하고 있습니다. 이 때 d는 데이터의 차원을 의미합니다. 데이터가 제공하는 정보가 1개라면 3번째로, 2개라면 5번째, 3개라면 7번째로 가까운 점 사이의 거리를 사용해 각 σi와 σj를 구하면 적합한 값 w를 얻을 수 있을 것입니다.

 

 

4. Spectral Clustering 구현하기

 다음과 같은 데이터가 입력값으로 주어졌다고 가정해 봅시다.

1
2
3
4
5
6
1    1
1    2
3    1
3    2
5    1
5    2
cs

 

 위 입력값을 직관적인 관점에서 3개의 cluster로 구분한다면 (1,2), (3,4), (5,6)이 될 것으로 기대할 수 있습니다.

 위 데이터를 Affinity Matrix로 나타내면 다음과 같습니다. 여기서 가우시안 커널 σ은 임의로 설정된 값으로 하여 데이터가 충분히 구분될 수 있도록 하였습니다.

 

 

 주어진 데이터는 총 6개이므로 6X6 크기의 Affinity Matrix가 만들어집니다. 각 행렬의 i열은 위 입력 데이터의 i번째 줄의 데이터를 의미합니다. 자신의 데이터 사이의 거리는 1이며 자신에게 가까운 값일수록 1에 가깝고 멀어질수록 0으로 수렴하고 있는 것을 보실 수 있습니다. 위 데이터를 기준으로 본다면 0.5 이상의 값은 1로, 0.5 이해의 값은 0으로 한다면 위에서 설명하였던 이상적인 Affinity Matrix가 그려지는 것을 알 수 있습니다.

 

 위와 같이 주어진 Affinity Matrix를 사용하여 Spectral Clustering을 구현해봅시다. Spectral Clustering을 구현함에 있어서의 기준은 다음과 같습니다.

 

1. 같은 Cluster 내의 요소끼리의 유사도는 큰 값을 갖는다.

2. 다른 Cluster에 속한 요소끼리의 유사도는 작은 값을 갖는다.

 

 이를 구하기 위해서는 위에서 구한 Affinity Matrix 값 W의 값이 최대로 나오는 Vector 행렬을 구해야 합니다. W와 행렬곱을 하였을 때 최대값이 나오게 하는 벡터를 고유벡터(eigenvector)라 칭합니다. 고유벡터에 대해 알기 위해서는 대학 학부 수준의 선형대수에 대한 기초적인 지식이 있어야 이해할 수 있습니다. 고유벡터에 대해 자세히 알고 싶으신 분은 아래의 블로그 포스팅을 참조하시면 이해하는 데에 도움이 될 것입니다.

 

고유값과 고유벡터[다크 프로그래머]

http://darkpgmr.tistory.com/105

 

 Affinity Matrix 값 W의 고유벡터를 구하면 아래와 같은 값이 도출됩니다. 

1
2
3
4
5
6
[[ -3.58906456e-01  -5.00000000e-01  -3.48118020e-01   3.58906456e-01    3.48118020e-01   5.00000000e-01]
 [ -3.58906456e-01  -5.00000000e-01  -3.48118020e-01  -3.58906456e-01   -3.48118020e-01  -5.00000000e-01]
 [ -4.92313226e-01   2.79483017e-16   5.07570377e-01   4.92313226e-01   -5.07570377e-01  -4.52681896e-16]
 [ -4.92313226e-01   9.86999413e-17   5.07570377e-01  -4.92313226e-01    5.07570377e-01   4.79275406e-16]
 [ -3.58906456e-01   5.00000000e-01  -3.48118020e-01   3.58906456e-01    3.48118020e-01  -5.00000000e-01]
 [ -3.58906456e-01   5.00000000e-01  -3.48118020e-01  -3.58906456e-01   -3.48118020e-01   5.00000000e-01]]
cs

 

 고유벡터의 열(Column) 부분을 보시면 값이 서로 구분되고 있는 것을 보실 수 있습니다. 일반적으로 1번째 Column은 구분이 모호하기 때문에 2번째 Column부터 사용하게 됩니다.

  k 개의 그룹 cluster가 존재할 때 이들을 분별하기 위해서는 Affinity Matrix의 고유벡터의 2번째부터 k번째 column 부분만 보고 분별을 하게 됩니다. 즉 위의 예제의 경우 3개의 cluster로 구분되고 있으니 2,3번째 column만 사용하면 되겠습니다. 이 경우 첫 번째 cluster인 (1,2) 그룹의 경우 (-0.5, -0.3)으로, (3,4) 그룹의 경우 ( 0, 0.5), (5,6) 그룹의 경우 (0.5, -0.3)으로 뚜렷이 구분되고 있음을 확인하실 수 있습니다. 이를 이미지로 표현하면 다음과 같다 볼 수 있겠습니다.

 

 

 

 위에서 보시는 바와 같이 각 그룹이 고유벡터 내의 성분대로 분류하면 뚜렷하게 분류가 되는 것을 확인하실 수 있습니다.

 

 고유벡터값을 토대로 Spectral Clustering을 구현하는 알고리즘으로는 Minimum Cut과 Normalized Cut을 사용하게 되는데요 위 알고리즘은 다음 포스팅에서 이어서 설명을 하도록 하겠습니다.

 

Spectral Cluster 알고리즘 응용(Minimum Cut)

http://elecs.tistory.com/169

 

- Reference

허경용, 김광백, 우영운 (2008), "스펙트럼 군집화에서 블록 대각 형태의 유사도 행렬 구성", 한국멀티미디어학회논문지, 11(9), 1302-1309.

http://www.dbpia.co.kr/Article/NODE01605020

 

스펙트럼 군집화에서 블록 대각 형태의 유사도 행렬 구성

논문, 학술저널 검색 플랫폼 서비스

www.dbpia.co.kr

※2020년 8월 5일 수정

기존의 유튜브 영상이 나오지 않아 새로운 영상으로 변경하였습니다.

300x250