2017년 12월 31일 일요일

IIoT시스템 개발을 위한 오픈소스

이 글은 IIoT(Industrial Internet of Things, 산업용 사물인터넷) 개발을 위한 PCB 레이아웃 설계를 위한 오픈소스를 간략히 소개한 글입니다. 이 다음 글을 레퍼런스하였습니다.

TinyCAD 
TinyCAD는 다양한 라이브러리를 지원하는 PCB 레이아웃 및 회로도 설계 도구이다. TinyCAD는 심볼, 텍스트, 주석, 버스 및 전원 신호, 기호 속성에서 도면 편집에 이르는 다양한 기능을 제공한다.

AdvancedHMI
이 프로그램을 이용하면 HMI(Human-Machine Interface)를 손쉽게 만들 수 있다. 이는 Visual Studio가 필요하며 .NET Framework를 기반으로 한다.
SCADA가 가능하며 Omron, Allen Bradley DF1 RS232 드라이버를 지원한다. Linux의 Mono에서 테스트되었으며, Raspberry Pi에서도 작동된다.

Shotcut
Shotcut 비디오 편집 소프트웨어는 다양한 종류의 비디오 및 오디오 형식을 지원한다. 동일한 프로젝트에서 기본 편집, 다중 형식 타임 라인, 해상도 및 프레임 속도를 지원한다. 이 프로그램은 화면, 웹캠, 오디오 캡처에도 사용된다. 최대 4k의 해상도를 지원하고 X11 화면과 Windows DirectShow를 캡처할 수 있다.

iDempiere
기업용 오픈 소스 ERP 소프트웨어이다. 고객 관계 관리 및 공급망 관리를 지원한다. iDempiere는 여러 조직, 다중 언어 지원, 다중 통화 및 다중 계정 스키마를 지원한다. 엔터티, 유효성 검사 규칙, 사용자 지정 응용 프로그램을 관리 할 수 있는 Java 기반 서버로 구성된다.

LibrePlan
프로젝트 관리를 위한 공동 협업 도구이다. 사용자는 작업 및 프로젝트를 계획, 모니터링, 제어, 구성을 할 수 있다.


2017년 12월 21일 목요일

사용자 인터페이스 개발을 위한 오픈소스 프론트엔드 프레임웍 Vue.js

javascript 기반 사용자 인터페이스 개발을 위한 오픈소스 프런트엔드 프레임웍이다. 웹 애플리케이션 프레임웍크를 제공해, 손쉽게 앱 사용자 인터페이스를 개발할 수 있다.

하이브리드 앱 개발을 위해 Cordova(코르도바)와 함께 사용하기도 한다.

Vue는 Evan You에 의해 개발되었으며, Angular의 장점을 도입하였다.

Vue.js는 템플릿 문법을 이용해 DOM에 데이터를 렌더링하기 쉽다.

<div id="app">
  <span v-bind:title="message">
    you can see the binding message.
  </span>
</div>

var app = new Vue({el: '#app', data: {message: 'Hello world! ' + new Data() + '...'}})

Vue는 이미 만들어놓은 컴포넌트를 재활용해, 응용 프로그램을 손쉽게 개발할 수 있다.


레퍼런스
1. vuejs.org
2. kr.vuejs.org

2017년 12월 19일 화요일

Node기반 RESTful API 개발

서버 개발 시 API(Application Program Interface)를 지원해야하는 경우가 종종 생긴다. 이 경우, 많은 대안이 있을 수 있으나, 가볍고, 빠른 개발을 고려한다면, Node기반 RESTful API를 활용할 수 있다.
RESTful API 설명

Node기반 RESTful API 개발을 위해 주로 사용하는 패키지는 익스프레스이다. Node 패키지이므로 아래 명령어로 설치할 수 있다.

npm install express --save

Node와 Mongo DB를 이용하면, 데이터 RESTful 서버를 만드는 것은 그리 어렵지 않다.

좀 더 자세한 개발 방법(Node + Express + Mongo DB)은 아래 튜토리얼을 참고한다.

레퍼런스

AngularJS 기반 앱 어플리케이션 개발

AngularJS는 자바스크립트 기반 오픈소스(github) 프론트엔드 웹 애플리케이션 프레임워기다. 구글 및 여러 커뮤니티에서 유지보수 되고 있으며, 크로스 플랫폼 모바일 앱 개발 프레임웍인 아파치 코도바에 사용된다(위키피디아). MVC(Model View Controller) 구조를 제공함으로써 개발 및 테스트를 단순화한다.


다음은 간단한 AngularJS기반 앱 개발 순서이다. Java나 ASP기반 방법보다 간단하다.

1. set up the development environment
npm install -g @angular/cli

2. create new project
ng new my-app

3. serve the application
cd my-app
ng serve --open

4. Edit your first Angular component
edit src/app/app.component.ts

export class AppComponent {
  title = 'My First Angular App';
}

edit src/app/app.component.css

h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}

5. run
Output of QuickStart app

좀 더 상세한 개발 방법은 다음 튜토리얼을 참고한다.




2017년 12월 13일 수요일

FPGA 하드웨어 기반 고수준 성능 논리칩 개발을 위한 Verilog HDL

이 글은 고수준의 정확성과 성능을 요구하는 논리 칩 개발에 주로 사용되는 FPGA(field programmable gate array) 개발을 위한 Verilog HDL(Hardware Description Language)에 대한 내용을 간략히 요약한다. 최근 FPGA는 딥러닝 기반 비전 등 특수한 분야 응용이 확산되고 있다(사례). 참고로, FPGA는 기존 CPU, GPU에 비해 저전력 (5~6배)과 고속처리를 지원한다.

1. FPGA
FPGA(field programmable gate array, 필드 프로그래머블 게이트 어레이)는 설계 가능 논리 소자와 프로그래밍가능 내부선이 포함된 반도체 소자이다. 설계 가능 논리 소자는 ANDORXORNOT, 더 복잡한 디코더나 계산기능의 조합 기능같은 기본적인 논리 게이트의 기능을 복제하여 프로그래밍할 수 있다. 대부분의 FPGA는 프로그래밍가능 논리 요소 (FPGA 식으로는 논리 블록이라고도 함)에 간단한 플립플롭이나 더 완벽한 메모리 블록으로 된 메모리 요소를 포함하고 있다.
프로그램이 가능한 내부선 계층구조는 FPGA의 논리블록을 시스템 설계자가 요구하는 대로 단일 칩 프로그래밍가능 빵판처럼 내부연결을 할 수 있다. 이 논리블록과 내부선은 제조공정 이후에 소비자/설계자가 프로그램할 수 있으므로 요구되는 어떠한 논리기능도 수행할 수 있다.
자일링스는 FPGA 칩을 개발해 판매하는 회사이다. 이 칩을 이용해 개발 보드 등을 제공하는 회사가 여럿있다. 디질런트도 그 중 하나이다.



2. HDL
HDL은 Hardware Description Language 로 말 그대로, 논리회로를 만들어주기 위한 기술 언어이다. 플립플롭, 시퀀서, 카운터 등 논리회로를 코딩하듯이 만들 수 있다.

3. Verilog
HDL은 VHDL과 Verilog HDL 종류로 나누어 진다. VHDL은 HDL표준이며, Verilog HDL은 C언어와 유사한 HDL이다. 국내에서는 90%이상이 Verilog를 사용한다.

4. Vivado
Vivado 는 자일링스사가 최근 릴리즈한 HDL 코딩을 위한 통합 개발 환경이다. 코딩, 핀 배치 설정, 회로 합성, 회로 구현, 비트스트림 생성 등의 작업을 손쉽게 할 수 있다.

디질런트의 Zynq(징크)칩이 내장된 지보(ZYBO) 보드를 이용하면, Vivado에 비트스트림을 업로드하고, 내장된 CPU를 이용해, 구현된 논리회로를 실행할 수 있다. 참고로, Zynq 시리즈 칩은 PS(process system), PL(process logic)이 함께 임베디드되어 있는 칩이다.
`
Zynq는 FSBL (first stage boot loader)를 이용한다. FSBL은 보드의 SD메모리에 BIN파일로 비트스트림과 함께 다음과 같이 저장된다.

  • SD memory = {BIN, LINUX}
  • BIN = {FSBL, BIT}

SD메모리에 PETA(페타) 리눅스 이미지를 넣으면, 리눅스로 부팅하고, 리눅스에서 FPGA 로직을 실행할 수 있다.
5. 마무리
FPGA칩은 일반적으로 가격이 비싸고, 구현하기 어려우나, 성능과 확장성이 뛰어나 특수한 어플리케이션에서 주로 많이 사용된다. FPGA는 대량으로 칩을 양산하기 어렵지만, 고성능의 비전이나 계산, 타이밍에 정확하고 특수한 시그널을 만들어야할 때 사용한다. 참고로, 윈도우즈, 리눅스, 아두이노, 라즈베리파이와 같이 운영체계 환경에서 실행되는 프로그램은 정확한 타이밍에 특정 시그널을 만들기가 어렵다(이를 위해 리얼타임 OS가 개발되기도 하였음). 

최근 GPU와 경쟁을 하고 있어, 머신러닝, 비전 등을 지원하는 SDK를 릴리즈하고 있다. 아울러, 라즈베리파이와 같은 오픈소스 보드의 확장성, 개발 편의성을 고려해, PYNQ, ARTY와 같은 보드도 개발하고 있다. 




2017년 9월 21일 목요일

BIM VR 모델 뷰어 개발 방법 및 HTC VIVE VR 장단점

몇 년사이에 저렴하고 현실적인 VR기기와 SDK(Software Development Kit)가 출시되면서, BIM과 같은 엔지니어링 분야에도 VR기술이 응용되고 있다. 오늘은 이 기술에 대해 좀 더 깊은 이해를 위해 가상현실(VR. Virtual Reality) 기반 BIM 모델 뷰어 개발 방법을 간략히 요약해 설명한다. 아울러, 활용한 VR기술의 장단점과 BIM과 같은 엔지니어링 분야에 VR기술 적용 시 고려사항을 나눔해 본다.
오토데스크 Revit Live 

1. VR 환경 설정
HTC VIVE(바이브)는 오큘러스 리프트와 같은 VR 장비이다. 가격은 100만원대 초중반이다. VIVE 사이트 메뉴얼에서 알려주는 대로 따라하면 설치하기 그리 어렵지 않다. 홈페이지에 방문해 'VIVE 설정 다운로드' 버튼을 클릭해 프로그램 설치하며 따라하면 된다.

VIVE 설치 단계를 요약하면, 장비를 컴퓨터와 연결하고, HTC VIVE 사이트 및 STEAMVR 사이트를 가입한 후, VIVE 관련 설치파일을 다운로드해 설치해, VR 스페이스 크기 등을 설정한다. 따라하기 어렵다면, 아래 영상을 참고하기를 바란다.
바이브 설정 방법 가이드 영상
 VR 장비 설정
VR room 경계 설정

참고로, 설정 과정에서 몇 가지 문제가 있을 수 있는 데, 대부분 다음과 같은 경우이다.
  1. 바이브에서 지원하지 않는 그래픽카드 인 경우: 참고로 본인 컴퓨터에 설치된 NVIDIA GPU의 경우는 큰 문제가 없었다.
  2. GPU 활성화가 되어있지 않은 경우: VIVE의 Steam 실행 전에 반듯이 GPU 활성화되어 있어야 한다.
  3. 디스플레이 창 모드가 확장 모드가 아닌 경우: 확장 모드로 전환해야 한다. 
  4. 기타: 컴포지터(compositor) 400에러 등 그래픽 출력 모드와 관련된 에러가 발생되는 경우가 많다. 각 에러마다 SteamVR 프로그램 창에 에러 번호가 표시된다. 번호를 구글링하면 해결하는 방법이 나와 있어, 그대로 따라하면 처리 된다. 
VIVE 설정이 제대로 되었다면, SteamVR을 실행하였을 때 다음과 같은 화면을 볼 수 있다.

VIVE 프로그램에서 지시하는 데로, VR 설정을 성공한 후에는 다양한 VR앱을 설치하고, 다른 세계를 포털로 여행할 수 있게 된다. 


2. Simple BIM 모델 뷰어 개발
1. BIM 모델 준비
VR에 BIM 모델을 띄우기 위해 Revit에서 RAC BIM 모델과 Sketchup 에서 구한 3차원 도시 모형을 미리 준비하였다. 이 모델을 FBX과 DAE 모델로 각각 변환하였다.

파일 변환 및 모델을 유니티에 추가하는 방법은 아래 글을 참고하길 바란다.
2. VR BIM 앱 개발
VR기반으로 모델을 띄우기 위해 유니티를 사용한다. 개발 방법은 다음 가이드 영상을 참고한다. 
HTC VIVE 기반 개발 튜토리얼(유니티 엔진)
언리얼(Unreal) 엔진 기반 VR 개발 튜토리얼

이 글에서는 유니티 기반으로 VR 앱을 개발한다. 튜토리얼에서 설명하는 VIVE 앱 개발 순서는 다음과 같다. 
1) 우선 유니티에서 SteamVR Plugin을 다운로드 받아 설치한다.

2) BIM 모델 FBX 및 DAE파일을 유니티에 추가한다.

3) 플레이 버튼이나 Build&Run 메뉴를 실행하면, 바이브에서 VR기반으로 BIM 모델을 관찰할 수 있다. 

4) 만약, 건축 모델과 상호 동작을 하고 싶다면, 기존에 Steam에 설치된 스크립트(C#)을 사용하거나, 다음과 같이 유니티 객체에 스크립트를 새로 만든 후, 원하는 명령을 입력해 컨트롤러나 모델에 적용한다. 
SteamVR에서 제공되는 스크립트 예제
Visual Studio 에서 스크립트 개발

3. 결론
간단한 BIM 뷰어를 VR기반으로 개발해 보았다. 바이브는 오큘러스 리프트 DK 버전을 사용할 때 보다 다음과 같은 장점이 있다. 
  1. 넓은 공간(5미터)을 커버하는 VR장치 추적기와 편리한 설치
  2. 편리한 VR 앱 지원 및 개발 플랫폼과 수많은 VR앱 
  3. 좀 더 가벼워진 HMD
  4. 편리한 개발환경 
특히, 2년전 사용해 보았던 오큘러스 리프트보다 넓은 공간 인식, 좀 더 가벼워진 HMD, 좀 더 정리된 배선, 덜 민감해진 GPU 및 디스플레이 설정 등은 크게 마음에 들었다. 물론 아직까지 다음과 같은 단점은 남아 있다. 참고로 이런 문제들는 기존 오큘러스 리프트도 동일하게 남아 있다.
  1. 고성능 GPU 필요함
  2. 아직은 불편한 HMD
  3. 렌즈 초점 맞추기 어려움
  4. 3차원 어지러움
  5. 픽셀이 보이는 낮은 해상도
  6. 가끔식 발생하는 GPU 및 디스플레이 에러
  7. 장치와 컴퓨터에 연결되는 전선들이 너무 많아 불편함. 무선 지원이 필요함
VR장비도 홀로렌즈 MR과 마찬가지로 개선해야 할 점이 아직 많이 남아 있다. 특히, BIM과 같은 엔지니어링에 필요한 모델을 연계할 경우, 다양한 부분을 신경써야 한다. 간단하게, VR BIM 뷰어 앱을 개발해 보았으나, 다음과 같은 기능은 하나 하나씩 구현해야 한다.
  1. IFC 등 BIM 모델 직접 로딩 및 뷰어 지원
  2. BIM 속성 확인, 필터 및 검색 지원
  3. BIM 모델 정보 수정
  4. BIM 모델 정보 분석
  5. BIM 모델 시뮬레이션
  6. BIM 서버 지원 및 연계
  7. 대용량 BIM 모델 경량화
  8. 이기종 데이터(지형 등) 모델 연계, 로딩 및 가시화
  9. 네트워크 기반 협업 지원
  10. 이외에 BIM 디자인, 시공 간섭 체크 등 다양한 유스케이스에 대한 기능 지원 등 
현재는 VR 기술이 인테리어, 공간 배치, 가상 하우스 모델 등에 활용되고 있으나, 기술이 발전되면, 더욱 많은 활용 사례가 나오리라 예상된다. 

2017년 9월 3일 일요일

무료 오픈소스 MatLab - Octave 소개

공학을 전공하였다면 MatLab이란 이름은 들어 보셨을 것입니다. MatLab은 과학, 공학 분야에서 수치해석, 행렬, 기하와 같은 복잡한 계산을 대신 해 주시는 플랫폼으로, 사용하기 간편한 매틀랩 언어를 제공합니다. 언어의 문법은 C언어와 유사하죠.

MatLab의 단점은 비용과 처리 속도입니다. 처리 속도야 인터프린터 언어라 어쩔 수 없는 부분이 있습니다. 계산 속도 문제는 알고리즘을 개선하거나 C/C++ 기반 수치해석 라이브러리를 직접 이용해야 합니다.

비용이 문제라면, 몇몇 좋은 대안들이 있습니다. 이 글에서 소개할 Octave는 그 중에 하나입니다. Octave(옥타브)는 Matlab 문법을 거의 100% 호환하며, 온라인에서도 서비스 가능한 오픈소스 도구입니다. 이 외에도 Scilab, FreeMat등 괜찬은 도구들이 있습니다(아래 링크 참고).


이 중에 Octave 기능만 살펴보도록 하겠습니다. 

1. 온라인 MatLab 서비스
Octave를 설치할 필요 없이, 온라인상에서 MatLab 서비스를 제공받을 수 있습니다. 이미, 옥타브를 이용해, 학습 서비스를 제공하는 다양한 사이트가 서비스되고 있습니다 

2. Matlab 문법 호환

MatLab 문법을 거의 100% 지원합니다. 매틀랩으로 개발된 소스코드를 많은 수정이 필요없이 가져다 쓸 수 있습니다.
function [max, idx] = vmax (v)
  idx = 1;
  max = v (idx);
  for i = 2:length (v)
    if (v (i) > max)
      max = v (i);
      idx = i;
    endif
  endfor
endfunction
3. GUI와 다양한 툴박스
Matlab과 같이 사용자 친화적인 GUI를 지원합니다. 아울러, 이미지 처리, 음성 처리, 객체 지향 모델링, 시스템 유틸리티 등 다양한 툴박스도 지원하고 있습니다.
I = imread ("myimage.jpg");
S = conv2 (I, ones (5, 5) / 25, "same");
[Dx, Dy] = gradient (S);

4. 오픈소스와 커뮤니티
무엇보다도 가격이 무료인 오픈소스로, 소스를 다운로드 받아 수정을 할 수 있습니다. 아울러, Wiki 페이지를 통해 커뮤니티에서 올린 자료를 활용하고, 도움을 받을 수 있습니다.

Octave 컨퍼런스도 있는 데, 이 곳에서 과학, 공학, 교육 분야에 활용사례들이 공유됩니다.

최근 2017 컨퍼런스는 CERN( Conseil Européen pour la Recherche Nucléaire. 유럽 입자 물리 연구소 ) 에서 3일간 개최되었습니다. 이곳에서 N차원 이미지 처리, 파티클 성능 최적화, 25년간 Octave 개발 과정 및 발전 방향, 희소 행렬 체계, KiCAD, 신경망 기술, 병렬화, 시뮬레이션 기술 등이 소개되었습니다. 흥미로운 점은 컨퍼런스에 아이들과 함께 오는 참석자를 위한 Child Care 서비스가 있다는 점입니다.

5. 마무리
오늘은 과학,공학, 기술 교육 분야에 두루 사용되는 오픈소스인 Octave에 대해서 살펴보았습니다. Matlab과 호환되며, 다양한 기능과 온라인 서비스를 지원하는 Octave는 다양한 분야에서 활용되고 있습니다.  




2017년 9월 2일 토요일

메이커 운동 사회 기여 분석 사례 - 포틀랜드 메이드 보고서

안녕하세요. 간만에 시간이 있어, 항상 마음속에 화두인 국내 메이커 운동 생태계에 대한 이야기를 나눠 보겠습니다. 이를 위해, 국내 메이커 운동의 한계점을 생각해 보고, 메이커 운동에 대한 사회 기여 조사 분석 사례인 오레곤 포틀랜드 메이드를 나눔해 보도록 하겠습니다. 포틀랜드는 메이커 시티 운동에 참여한 많은 미국 도시 중 하나입니다.


1. 국내 메이커 운동의 이슈
선진국과는 다르게 국내 메이커 운동은 정권에 따라 부침이 있는 것 같습니다(사실 모든 분야가 그러하듯 말이죠^^).  예를 들어, 국내에서는 창조경제란 관점에서 진행되었던 메이커 운동이, 최근 4차산업혁명의 관점에서 재조명되면서, 다시 떠오르고 있는 듯한 분위기입니다.
Portland Made

메이커 운동이 국내 교육, 산업, 사회 환경과 충돌하는 지점에 가까이 가신 분들이라면, 다들 다음과 같은 부분들을 절실히 느끼셨을 겁니다.

  1. 사실 국내에는 일반시민이 메이커 운동을 할만한 시간, 자원이 많지 않습니다. 메이커 페어에 꾸준히 참가하시는 분들은 정해져 있는 듯 합니다.
  2. 메이커 공간이 매우 부족합니다. 있는 공간도 접근성이 좋지 않습니다. 학원처럼 사업화된 곳은 비싸서 부담스럽습니다.
  3. 학생들의 메이커 활동은 학업을 포기해야 하거나, 적은 수시 전형 TO를 목표로 해야 동기 유발이 가능합니다. 한국에서 많은 학생들이 하고 있다는 선행학습, 1등급 내신점수, 경쟁률 높은 학교 진학은 어느 정도 포기해야 합니다. 그 적은 수시 특별 전형 TO도 전문학원 및 특정기관에서 스파르타식 커리큘럼으로 포트폴리오를 집중관리되는 학생과 단지 메이커를 좋아하는 학생이 서로 경쟁하기는 사실상 힘듭니다.
  4. 3의 경우에는 사설 학원에서 이미 메이커 운동을 사업화하고 있습니다. 이 경우, 자기 주도적인 메이커 운동 보다는 아웃소싱화가 진행됩니다. 사실, 메이커 운동을 통한 자녀 교육은 사실 장난감 사주듯 아웃소싱할만한 것이 아닐 겁니다.
  5. 3, 4와 같이 사교육 시장이 움직이고 있어, 학원가에서 찍어내기 교육이 점점 많아지고 있습니다. 덩달아, 강사 및 교구 수요는 증가하고 있습니다. 현재는 메이커 운동이 4차산업혁명 교육과 함께 취급되어가고 있습니다.
  6. 패밀리 메이커 운동은 지속적이고 이상적인 모델입니다. 다만, 국내는 부모가 아이와 함께메이커 교육을 경험할만한 복지국가는 아닌 듯 합니다. 최소 6시/주말 부모 칼퇴근과 보편적인 메이커 운동 공교육이 이뤄지지 않은 한 쉽지 않은 일입니다.
  7. 국내 메이커 운동에 참가하는 학생, 학부모의 기대치와 실제 현업 메이커 장인들의 대우는 괴리가 매우 큼니다. 정부와 언론에서 코딩, 메이커, STEAM 교육의 중요성을 말하지만, 정작 저의 개발자, 엔지니어 친구들은 을도 아닌 정이나 병에서 낮은 대우를 받고 코딩하고 있는 경우가 많습니다. 공장에서 일하고 있는 테크니션 동료들의 작업환경, 복지와 대우는 매우 취약합니다. 우리나라는 Field 작업에 대한 대우가 관리직보다 좋지 않습니다. 엔지니어, 장인들이 자기 기술을 브랜드화하기 매우 힘든 사회 환경입니다.
  8. 조잡한 취미생활하는 수준에서 메이커 운동을 바라보는 부정적인 시선도 존재합니다. 메이커 운동이 사회에 얼마나 기여하는 지를 모르고 있는 경우도 많습니다. 메이커들에게 열정페이를 요구하는 기관도 많습니다. 열정페이는 지속적이기 힘들고, 말이 달라지면 서로 좋지 않게 끝나는 경우가 많았습니다. 가끔 4차 산업혁명이 메이커 운동과 관련되어 있다기에 메이커운동에 참여한 적이 없음에도 메이커운동을 해야 한다고 소리높이시는 분들도 있습니다. 정책과 관련된 분이시라면, 오바마 행정부의 메이커 운동 이니셔티브를 참고하시면 좋을 듯 합니다. 직접 참여하신다면 메이커 운동에 대해 좀 더 이해하실수 있을 것 입니다. 이런 내용은 여기서는 논외로 하겠습니다.

당연히 앞서 모든 문제들은 메이커 운동에 참여하거나 하고 싶어하는 부모, 시민, 아이들, 메이커, 교육자 탓은 아닙니다. 심지어 학원의 탓도 아니라고 생각합니다. 사교육 시장에서 교육 정책이 못하는 부분을 커버해주는 부분도 분명 존재하고 있기 때문입니다. 물론 전문 학원, 교구 제작사들은 메이커 운동의 본질보다는 부모의 불안함에 편승해 사교육을 조장하려 한다면 문제가 있을 겁니다.
South Korea Education Problem(Jeyup S. Kwwak, 2014, WSJ)

이런 상황을 조장한 배경은 아직 대량생산을 목적으로하는 대기업과 공장에 필요한 인재를 찍어내려는 주입식 교육, 점수로 줄세워서 그 시스템에 적응한 학생들만 인재로 취급하며, 전문적인 지식과 자원을 집중 투입하려는 관주도 엘리트 교육정책에 있다고 생각합니다.

이 정책은 각자 고유한 재능이 있는 학생들에게 교육 자원을 공평하게 제공해 주길보다 몇개의 자리만 두고, 재능이 서로 다른 학생과 학부모들끼리 경쟁해 싸워 이겨서 그 자리를 차지하라고 강요합니다. 이 과정에서 교육이 뒷거래되는 부작용도 발생합니다.

국가, 대기업 중심 대량생산을 위한 교육(Euroean press phto agency)

과거 머리좋은 엘리트가 국가경제의 재목이 되길 바라는 좋은 의도?라 볼 수도 있었겠으나, 그 과정에서 후순위 투자로 밀리는 수많은 학생들은 그 재능을 꽃피우지 못하고, 교육 정책의 희생자가 되길 마련입니다. 과연 누가 재능의 높고 낮음을 특정 잣대로만 평가할 수 있을까요?

아이러니하게도 우리가 배우려는 선진 산업 모델은 국내 교육 정책으로는 불가능한 것입니다. 많은 스타트업들이 어떻게 좋은 아이디어를 생각해내고, 각자의 재능을 조합해, 구글, 페이스북, AirBnB, 우버, 인스타그램과 같은 회사가 되었는 지를 추적해 보면 누구나 느낄 수 있을 것입니다.

AirBnB workplace(AirBnB)

우리 교육 정책은 방향이 선진국과 반대 방향으로 가고 있었습니다. 선진 교육은 학생 각자의 재능을 꽃피우기 위해 보편적인 공교육을 시행합니다. 입시 점수로 사람을 평가하고 서열화해 콘크리트처럼 굳어진 시스템의 하부층을 생산하는 교육 정책은 비효율적이고 비인간적입니다. 게다가, 경쟁에서 이겨 대기업에 취직한들 경력 개발보다 아웃소싱 관리만 하다 40대 퇴직하는 경우가 다반사입니다. 이런 교육 정책의 이익은 시스템의 극소수만 가져가기 마련입니다.

코딩과 메이커 교육이 중요하다고 교과목과 입시에 반영되면, 학생이나 학부모는 없는 시간을 짜내서 빠르게 학습할 수 있는 스파르타식 학습 방법을 찾을 수 밖에 없습니다. 선진국 북유럽과 같이 보편적 교육이 가능한 자원을 정책적으로 마련해 주지 않음에도 무리하게 추진된 정책의 결과는 사교육 시장의 확대밖에 없을 겁니다.

지금까지 경험한 메이커 운동은 우리 사회, 교육, 산업 구조와 밀접한 관련이 있었습니다. 메이커 운동이 시작된 선진국은 직접 가서 보면 우리와 별반 다른것이 없는 듯 보이는 메이커 스페이스, 커뮤니티를 가지고 있습니다. 하지만, 좀 더 깊이 파본 분들은 그 하부 구조가 우리와 많이 다르다는 것을 알 수 있을 겁니다.

메이커 교육이 현재 교육의 대안이 되려면, 기존에 쌓인 문제들부터 함께 해결해나가야 할 것입니다.

2. 해외 메이커 운동 사회 기여도 조사 분석 사례
메이커 운동이 무엇인지 확인해 보려면, 메이커 운동의 현황을 살펴보고, 어떤 이익과 문제가 있는 지를 분석해, 이를 공유하려는 활동이 중요할 것입니다. 특히, 사회에 얼마나 공헌하고 있는 지는 메이커 운동의 의미와 관련있는 중요한 것 중 하나입니다. 이와 관련해, 포틀랜드에서 도시 지역 메이커 운동을 조사 분석한 사례가 있어 공유합니다. 앞의 모든 이슈에 대해 답해주지는 못하지만, 이런 조사가 사회에서 메이커 운동의 의미와 생태계의 중요성을 깨닫게 해 줄 수는 있습니다.

포틀랜드는 장인, 수공업자가 많은 곳으로 자부심을 가지고 있는 도시입니다. 포틀랜드에서 사람과 제품을 대표할 공간을 만들기 위해 Portland Made Collective(www.portlandmade.com)를 조직했습니다. Portland Made Collective에는 장인, 생산자를 포함한 약 400명의 회원이 있습니다.

포틀랜드 주립 대학의 Charles Heyring교수는 2014년 Portland Made Collective (PMC) 회원을 대상으로 설문 조사를 실시하여 산업에 미치는 영향을 분석해 발표하였습니다. 이  분석에서 회원들이 올리는 매출액은 2억 8천 8백만달러였습니다. 이 설문 조사에서 회원들은 기업가보다는 메이커 또는 장인으로 자신을 칭하는 것을 선호했다고 합니다. PMC는 이후 2015년 회원수가 2.7배 증가했고, 지금도 꾸준히 증가하는 중입니다.

이들은 메이커 설문 조사를 통해, 우리가 누구이며, 무엇을 하는 지, 어떻게 산업적으로 공헌하는 지를 알게 되었다고 합니다.

Portland Made Collective 설문 조사의 내용은 다음과 같습니다.

  • 리포트 목적, 응답자 카테고리, 생산자와 제공 서비스, 연도별 분석, 고용, 이익, 정체성, 사업장 위치, 마켓, 문제점과 도전과제

이 지역에서 설문한 메이커 소득 분포와 서비스 분포는 다음과 같습니다.


이 설문에서는 회원들의 연평균 소득도 분석했습니다. 참고로, 이 설문은 2014년도에 수행되었고, 2015년에는 회원수가 3배 가까이 증가했음을 참고하시길 바랍니다.

각자 본인의 아이덴티티를 무엇이라 생각하지는 설문도 해 보았네요. 회원들 다수는 본인이 메이커나 장인이라고 생각하고 있었습니다. 이에 대한 자부심이 사업의 원동력으로 보입니다.

메이커 운동의 커뮤니티 역할을 할 수 있는 회원 사업장 위치도 분석했습니다.

메이커들이 사업을 할 때 장애요인도 분석했습니다. 포틀랜드 메이커들은 마케팅, 제품 개발, 효과적인 사업 시스템, 펀드, 인력 순으로 응답했습니다. 펀드보다는 마케팅 및 제품 개발 시스템이 더 높다 것에 시사점이 있습니다.  
이 조사에서 메이커는 메이커 생태계에 대한 접근성이 제품 개발 시 영감, 문제 해결, 자원 교환, 마케팅 기회, 포틀랜드 메이드 정체성을 얻는 데 중요하다고 생각했다고 합니다. 

포틀렌드 메이드 조사 분석의 상세한 내용은 다음 링크를 참고하시길 바랍니다. 
이 조사 보고서의 맨 마지막 두 장은 이와 같은 조사에 사용할 수 있는 설문지 샘플이 있습니다.

포틀랜드 메이드 조사 분석 내용은 그 방법보다, 조사 이후 포틀랜드 도시에서 시행한 조치가 더 인상적입니다. 이 조사를 바탕으로 메이커들이 마케팅, 제품 제조에 도움을 받을 수 있는 공간을 확충하고, 지역 대학과 연계하여, 스타트업을 지원하고, 도시에서 정책적으로 메이커를 지원하는 조직을 만들어, 이런 조사를 통해 매년 생태계를 개선하려는 노력합니다.

개방성과 팩트에 기반한 문제 개선과 지속적인 노력은 우리가 닮아가려는 선진 조직에 어느 곳에나 볼 수 있는 인상적인 장면입니다. 어느 분야 등 발전하기 위해서는 합리성, 개방성, 지속성이 전재되어야 합니다.

이런 관점에서 포틀랜드 메이드 조사 보고서와 같은 접근은 중요하다고 생각합니다.

3. 마무리
사실 메이커 운동을 사회적 기여 관점에서만 조사 분석하기에는 아쉬운 감이 있습니다. 이외에 얻을 수 있는 더 많은 것들이 있기 때문입니다. 본인의 경우, 메이커 운동을 하면서 뜻밖에 소득이 있었습니다.

사회와 교육 다방면에 관심이 많아졌습니다. 가족과 함께 메이커 페어에 자주 참가하면서, 국내 사회, 교육 구조적 문제에 대해 많은 생각을 하게 되었습니다. 이 과정에서 전혀 의도하지 않았던, 학원 광고에도 함께 인용되기도 했었습니다 ㅎ(주요 언론임에도 본인 협의 없이 재료로 출연되었던지라 기사 광고비가 궁금했었습니다). 패밀리 메이커 운동을 공유하기위해 출판사에 낸 책이 학원용으로 홍보되기도 하였습니다. 이는 언론이나 사교육이 메이커 문화를 어떻게 받아들이는 지 관심을 가지게 된 계기가 되었습니다.

안타깝게도 언론에 메이커 운동에 대해 이야기하는 것은 힘들었습니다(앞에 이슈를 여러번 강조했으나, 아쉽게도 그 내용과 대안을 고민하고 보도한 곳도 없었습니다). 국내 언론은 어떤 교육적 이슈든 산업적인 면에서 바라보는 경향이 있었습니다(타이틀도 4차산업혁명 교육으로 각색되어 나갑니다). 메이커 운동이 우리 밥그릇이 될 수 있는 지, 해외 수출에 도움이 될 수 있는 지 말이죠. 이는 앞서 말한 한국사회의 구조와 관련되어 있습니다. 굳이 언론의 탓만도 아닌 것 같습니다.

제가 하는 일과 관련해서도 개방적인 형태의 커뮤니티가 가능한지를 생각하게 되었습니다. 무엇을 하든, 시도할 수 있는 잉여 시간이 있어야 가능한 것 같습니다. 개인적으로 스스로 생각에만 멈춰있지 않을려고 노력하지만, 사회적 구조도 동시에 바꿔나가야 메이커 운동은 가능한 것 같습니다.

쉽지 않은 상황에서 메이커 운동에 참가하시는 분들이 힘을 낼 수 있는 환경이 되었으면 좋겠습니다.


2017년 7월 11일 화요일

윈도우즈 10에서 우분투 앱 설치 및 실행

이 글은 윈도우즈 10에서 우분투 설치 및 실행하는 방법을 간단히 소개한다. 이와 관련된 글은 다음을 참고하였다.

한 컴퓨터에서 우분투와 윈도우를 사용하기 위한 방법은 다음과 같다.

  • 멀티부트 설치
  • VMware에 우분투 설치
  • 도커에 우분투 설치

멀티부트가 성능면에서는 제일 좋지만, 설치가 번거롭기 이를데 없다. VMware는 무거운 가상머신을 설치하고, 우분투를 설치해 한다. 도커는 설치가 손쉬우나, 가상머신처럼 약간의 성능 손실을 감수해야 한다.

MS가 최근 샌드박스에 우분투 앱 설치하는 방법을 릴리즈했다. 앱설치하는 방식으로 설치가 쉽다. 샌드박스도 가상머신이라 성능손실은 있다. 다만, 앱스토어에 우분투 앱을 제공한다는 것에 놀라는 사람이 많다. 한번 사용해 볼만한 방식이라 관련 내용을 남긴다.

2017년 7월 10일 월요일

딥러닝 GAN 기반 image-to-image 모델

이 글은 딥러닝 GAN(Generative Adversarial Network. 생성 대립 신경망)모델에 기반한 image-to-image를 소개한다. 관련 논문은 다음 링크를 참고한다. 신경망의 종류와 개념 소개에 대해서는 여기를 참고한다.
1. 소개
이 글은 조건적 adversarial 네트워크를 이용한 GAN에 기반해, 이미지-이미지 생성 문제에 대한 범용 솔루션을 제안한다. 이러한 네트워크는 입력 이미지에서 출력 이미지로의 매핑을 학습할 수 있다. 이 접근법은 레이블 맵에서 사진을 합성하고, 가장자리 맵에서 객체를 재구성하고, 다른 작업 중에서 이미지를 페인팅하는 데 효과적이다. 

GAN기반 image-to-image

2. 내용
GAN은 랜덤 노이즈 벡터 z에서 출력 이미지 y : G : z → y 로의 매핑을 학습하는 생성 모델이다. 대조적으로 조건부 GAN은 관찰된 이미지 x와 랜덤 노이즈 벡터 z에서 y : G : {x, z} → y 로의 매핑을 학습한다. 생성기 G는 "가짜"이미지 감지 시, 훈련된 discrimintor D에 의해 "실제"이미지와 유사한 이미지를 출력하도록 훈련된다. 다음 그림은 훈련과정을 보여준다.

Training conditional GAN

G는 목적함수를 최소화하기 위해 반복 훈련하다. 이 목적함수는 D가 최대화하려는 것에 비해 대조된다. 

G∗ = arg minG maxD LcGAN (G, D)
LGAN(G, D)=Ey∼pdata(y) [log D(y)] + Ex∼pdata(x),z∼pz(z)[log(1 − D(G(x, z))]

다음은 이를 통해 훈련된 image-image net을 이용하여, 이미지를 생성한 예이다.
손실 차이에 따른 결과의 품질

3. 마무리
GAN은 사람의 인지적 활동을 모사하는 데 탁월한 성능을 보여준다. 특히, 예술 분야에서 GAN은 많은 관심을 받고 있는 흥미로운 학습 방법이다. 

레퍼런스





2017년 7월 4일 화요일

딥러닝 기반 3차원 객체 인식 VoxNet

이 글은 3차원 점군에서 객체를 인식하는 일반적인 접근법인 복셀을 이용한 VoxNet에 관한 소개이다. 이 글은 Dimatura VoxNet 레퍼런스를 참고하였다.

1. 머리말
VoxNet은 3차원 점군을 복셀로 근사화시켜, CNN을 이용해 형상을 인식한다.

2. 설치
VoxNet은 Theano와 Lasagne 를 기반으로 개발되었다. path.py, scikit-learn 모듈이 필요하다. 다음과 같이 설치한다.
git clone git@github.com:dimatura/voxnet.git
cd voxnet
pip install --editable .

3. 훈련
데이터는 ModelNet 10을 사용하였다. 이는 3D ShapeNet 프로젝트에서 얻은 것이다. 복셀화를 손쉽게 하기 위해, .mat 파일을 이용하였다. 데이터셋은 다음과 같이 얻는다.
# scripts/download_shapenet10.sh
wget http://3dshapenets.cs.princeton.edu/3DShapeNetsCode.zip 
unzip 3DShapeNetsCode
python convert_shapenet10.py 3DShapeNets
훈련은 다음과 같이 실행한다.
cd scripts/
python train.py config/shapenet10.py shapenet10_train.tar
훈련 결과는 metrics.jsonl 파일에 저장된다.

4. 테스트
테스트는 다음과 같이 실행한다.
python test.py config/shapenet10.py shapenet10_test.tar --out-fname out.npz
가시화는 다음과 같이 실행한다.
python output_viz.py out.npz shapenet10_test.tar out.html


레퍼런스



2017년 7월 2일 일요일

딥러닝 기반 3차원 비전 객체 인식 PointNet 분석

이 글은 최근 발표된 딥러닝 기반 3차원 비전 객체 인식 기술인 PointNet을 분석해 본다. 이 글은 아래 레퍼런스를 참고한다.
PointNet 분류 결과

1. 머리말
PointNet은 CVPR 2017 컨퍼런스에서 발표된 arXiv 기술을 기반으로 한다. 스캔 결과로 얻어지는 3차원 점군을 인식하기 위해서, 많은 연구자들은 점군을 복셀(voxel) 형식으로 변환한다. 복셀은 점군을 요약하기에는 좋으나, 빈공간이 많이 발생하여, 비효율적인 부분이 있다. PointNet은 입력 시 포인트 순열의 불변성을 이용한다. PointNet을 통해, 객체 분류, 세그먼테이션, 스캔 장면의 의미 해석 등에 필요한 아키텍처를 제공한다. PointNet은 간단하지만, 매우 효율적으로 3차원 점군에서 객체를 인식한다. PointNet은 ShapeNet Part Dataset을 기반으로 훈련을 하였다.


2. 설치하기
PointNet은 텐서플로우 1.01, h5py, CUDA 8.0, cuDNN 5.1, 우분투 14.04를 사용한다.
만약, h5py 가 없다면, 다음과 같이 패키지를 설치한다. PointNet은 HDF5 를 사용하며, 관련된 상세 내용는 여기를 참고하길 바란다.
sudo apt-get install libhdf5-dev
sudo pip install h5py
참고로, 아나콘다에서는 다음과 같이 설치하면 된다.
conda install h5py

3. 신경망 훈련하기
다음과 같이 실행한다.
python train.py
로그 파일은 log폴더에 저장된다. 점군은 HDF5 파일로 ModelNet40 형식으로 다운로드 된다(416MB). 각 점군은 2048 포인트들을 담고 있다.
다음과 같이 학습 결과를 텐서보드를 통해 확인할 수 있다.
tensorboard --logdir log
훈련후에는 다음과 같이 정확도를 평가할 수 있다.
python evaluate.py --visu
만약, 미리 준비한 점군이 있다면, utils/data_prep_util.py 유틸리티 함수를 통해 HDF5 파일을 읽고 쓸 수 있다.
참고로, 훈련을 위해서는 ShapeNetPart 데이터(약 1.08GB)를 다음과 같이 다운로드해야 한다.
cd part_seg
sh download_data.sh
훈련과정 및 결과는 다음과 같다.

다음은 인식한 객체 정확도이다.
참고로, 훈련과정에서 사용된 포인트 클라우드는 이미지로 저장되어 확인할 수 있다.



4. 데이터 구조 분석
훈련용 데이터 파일은 총 6개이며, (3 x 2048 x 2048) 데이터셋으로 포인트클라우드(점군)와 라벨 데이터셋(2048)이 저장되어 있다. 테스트용 데이터는 총 2개이다. 파일포맷은 HDF5이다.
훈련용 데이터셋(3 x 2048 x 2048) HDF5파일

5. 신경망 구조 분석
PointNet 신경망 구조는 다음과 같다. n 개 포인트 클라우드 (점군)을 입력한다. 입력 및 특징 변환을 수행 하고, max pooling을 통해 특징을 일반화한다. 출력으로 m 개 클래스 스코어가 분류된다. 신경망 구조는 분류(classification), 세그먼테이션(segmentation) 네트워크로 구성되어 있다. 세그먼트 네트워크는 분류 네트워크를 확장하였다. Batchnorm(Batch Normalization)은 ReLU 함수를 적용한다. Dropout은 분류 네트워크의 마지막 mlp(multi layer perception. 다층 레이어 퍼셉트론)에만 적용하였다.
PointNet 구조

분류 네트워크 구조는 다음과 같다.
1. n개의 3차원 포인트 좌표값이 input points로 입력
   1) T-Net 으로 3x3 텐서 변환
   2) matrix multiply 연산 처리
2. 변환된 n x 3 데이터가 mlp 64x64로 전달되어, n x 64 텐서로 출력됨
3. feature transform을 통해 계산된 n x 64 텐서 출력
4. mlp 64x128x1024 로 변환된어 n x 1024 텐서로 출력
5. max pooling 을 통해 일반화된 특징 벡터 1024 출력
6. mlp 512 x 256 x k 로 출력해 score 벡터 k 계산

세그먼트 네트워크 구조는 다음과 같다.
1. 분류 네트워크의 1 ~ 4번까지 그래프 구조는 재활용됨
2. n x 1088 텐서가 mlp 512x256을 통해 point feature 텐서 n x 128 로 출력
3. n x 128 텐서가 mlp 128 x m 을 통해 n x m 텐서로 출력

6. 소스코드 분석
텐서플로우를 사용하고 파이썬으로 코딩된 전체 소스코드는 261라인이다. 좀 더 깊은 이해를 위해, 소스코드의 주요 부분을 확인해 보자. 

MAX_NUM_POINT = 2048   # 입력 포인트 갯수
NUM_CLASSES = 40       # 클래스 갯수
BN_INIT_DECAY = 0.5    # 학습 속도 

# 훈련용, 테스트용 포인트 클라우드 데이터 준비
TRAIN_FILES = provider.getDataFiles( \
    os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/train_files.txt'))
TEST_FILES = provider.getDataFiles(\
    os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/test_files.txt'))

def train():
    with tf.Graph().as_default():
        with tf.device('/gpu:'+str(GPU_INDEX)):   # 신경망 계산 시 GPU 사용
            pointclouds_pl, labels_pl = MODEL.placeholder_inputs(BATCH_SIZE, NUM_POINT) 

            # prediction 모델 구성
            pred, end_points = MODEL.get_model(pointclouds_pl, is_training_pl, bn_decay=bn_decay)
            loss = MODEL.get_loss(pred, labels_pl, end_points)

            # 정확도 모델 구성
            correct = tf.equal(tf.argmax(pred, 1), tf.to_int64(labels_pl))
            accuracy = tf.reduce_sum(tf.cast(correct, tf.float32)) / float(BATCH_SIZE)

            # 최적화 옵션에 따른 모델 정의
            if OPTIMIZER == 'momentum':
                optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=MOMENTUM)
            elif OPTIMIZER == 'adam':
                optimizer = tf.train.AdamOptimizer(learning_rate)      # 아담 최적화 모델
            train_op = optimizer.minimize(loss, global_step=batch)   # 오차 최소화 모델
            
        sess.run(init, {is_training_pl: True})     # 세션 시작

        for epoch in range(MAX_EPOCH):     # 세대별 학습
            train_one_epoch(sess, ops, train_writer)   # 훈련
            eval_one_epoch(sess, ops, test_writer)     # 평가
            
            if epoch % 10 == 0:
                save_path = saver.save(sess, os.path.join(LOG_DIR, "model.ckpt"))   # 모델 저장

def train_one_epoch(sess, ops, train_writer):
    np.random.shuffle(train_file_idxs)   # 임의로 서플링함
    
    for fn in range(len(TRAIN_FILES)):
        current_label = np.squeeze(current_label)  # 라벨값
        
        for batch_idx in range(num_batches):       # 배치 갯수만큼 루프
            rotated_data = provider.rotate_point_cloud(current_data[start_idx:end_idx, :, :])  # 회전 변환
            jittered_data = provider.jitter_point_cloud(rotated_data)  # 지터 처리
            feed_dict = {ops['pointclouds_pl']: jittered_data,
                         ops['labels_pl']: current_label[start_idx:end_idx],
                         ops['is_training_pl']: is_training,}   # 피드값 입력
            summary, step, _, loss_val, pred_val = sess.run([ops['merged'], ops['step'],

                ops['train_op'], ops['loss'], ops['pred']], feed_dict=feed_dict)   # 세션 실행

def eval_one_epoch(sess, ops, test_writer):
    for fn in range(len(TEST_FILES)):
        current_label = np.squeeze(current_label)
        num_batches = file_size // BATCH_SIZE
        for batch_idx in range(num_batches):
            feed_dict = {ops['pointclouds_pl']: current_data[start_idx:end_idx, :, :],
                         ops['labels_pl']: current_label[start_idx:end_idx],
                         ops['is_training_pl']: is_training}
            summary, step, loss_val, pred_val = sess.run([ops['merged'], ops['step'],  # 예측
                ops['loss'], ops['pred']], feed_dict=feed_dict)
            pred_val = np.argmax(pred_val, 1)
            correct = np.sum(pred_val == current_label[start_idx:end_idx])

실제 pointnet 신경망 정의는 다음 폴더 아래에 있다.

이 중에 pointnet_cls, pointnet_seg 가 분류, 세그먼테이션 신경망을 구성하는 모듈이다. pointnet_cls.py는 전체 98라인이고, pointnet_seg.py는 115라인이다. 설명은 주석으로 처리하였으니, 앞의 신경망 구조와 비교해 확인해 보자.

# pointnet_cls.py
def placeholder_inputs(batch_size, num_point):
    pointclouds_pl = tf.placeholder(tf.float32, shape=(batch_size, num_point, 3))
    labels_pl = tf.placeholder(tf.int32, shape=(batch_size))
    return pointclouds_pl, labels_pl


def get_model(point_cloud, is_training, bn_decay=None):
    """ Classification PointNet, input is BxNx3, output Bx40 """
    with tf.variable_scope('transform_net1') as sc:
        transform = input_transform_net(point_cloud, is_training, bn_decay, K=3)
    point_cloud_transformed = tf.matmul(point_cloud, transform)  # 점군 변환
    input_image = tf.expand_dims(point_cloud_transformed, -1)    # 차원 확장

    net = tf_util.conv2d(input_image, 64, [1,3],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv1', bn_decay=bn_decay)   # conv1. 64 1 x 3 컨볼류션 적용
    net = tf_util.conv2d(net, 64, [1,1],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv2', bn_decay=bn_decay)   # conv2. 64 1 x 1 컨볼루션 적용

    with tf.variable_scope('transform_net2') as sc:
        transform = feature_transform_net(net, is_training, bn_decay, K=64)  # 변환
    end_points['transform'] = transform
    net_transformed = tf.matmul(tf.squeeze(net, axis=[2]), transform)  # matmul 계산
    net_transformed = tf.expand_dims(net_transformed, [2])             # 텐서 확장

    net = tf_util.conv2d(net_transformed, 64, [1,1],           
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv3', bn_decay=bn_decay)    # conv3. 64 1 x 1 컨볼루션 적용
    net = tf_util.conv2d(net, 128, [1,1],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv4', bn_decay=bn_decay)    # conv4. 128 1 x 1 컨볼루션 적용
    net = tf_util.conv2d(net, 1024, [1,1],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv5', bn_decay=bn_decay)    # conv5. 1024 1 x 1 컨볼루션 적용

    # Symmetric function: max pooling
    net = tf_util.max_pool2d(net, [num_point,1],                   # max pooling
                             padding='VALID', scope='maxpool')

    net = tf.reshape(net, [batch_size, -1])                            # reshape
    net = tf_util.fully_connected(net, 512, bn=True, is_training=is_training,
                                  scope='fc1', bn_decay=bn_decay)                     # fc1 512
    net = tf_util.dropout(net, keep_prob=0.7, is_training=is_training,            # dp1. dropout 0.7
                          scope='dp1')
    net = tf_util.fully_connected(net, 256, bn=True, is_training=is_training,  
                                  scope='fc2', bn_decay=bn_decay)                     # fc2 256
    net = tf_util.dropout(net, keep_prob=0.7, is_training=is_training,
                          scope='dp2')                                                       # dp2. dropout 0.7
    net = tf_util.fully_connected(net, 40, activation_fn=None, scope='fc3')    # fc3. 40 

def get_loss(pred, label, end_points, reg_weight=0.001):
    """ pred: B*NUM_CLASSES,
        label: B, """
    loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=pred, labels=label)  # softmax 
    classify_loss = tf.reduce_mean(loss)                                                           # reduce mean

    transform = end_points['transform']  # B x K x K
    K = transform.get_shape()[1].value    # transform
    mat_diff = tf.matmul(transform, tf.transpose(transform, perm=[0,2,1]))
    mat_diff -= tf.constant(np.eye(K), dtype=tf.float32)
    mat_diff_loss = tf.nn.l2_loss(mat_diff)                           # loss 계산


# pointnet_seg.py
def get_model(point_cloud, is_training, bn_decay=None):
    """ Classification PointNet, input is BxNx3, output BxNx50 """
    with tf.variable_scope('transform_net1') as sc:
        transform = input_transform_net(point_cloud, is_training, bn_decay, K=3)
    point_cloud_transformed = tf.matmul(point_cloud, transform)
    input_image = tf.expand_dims(point_cloud_transformed, -1)

    net = tf_util.conv2d(input_image, 64, [1,3],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv1', bn_decay=bn_decay)
    # 여기까지는 분류 네트워크와 구조 동일

    net = tf_util.conv2d(concat_feat, 512, [1,1],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv6', bn_decay=bn_decay)
    net = tf_util.conv2d(net, 256, [1,1],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv7', bn_decay=bn_decay)
    net = tf_util.conv2d(net, 128, [1,1],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv8', bn_decay=bn_decay)
    net = tf_util.conv2d(net, 128, [1,1],
                         padding='VALID', stride=[1,1],
                         bn=True, is_training=is_training,
                         scope='conv9', bn_decay=bn_decay)

    net = tf_util.conv2d(net, 50, [1,1],
                         padding='VALID', stride=[1,1], activation_fn=None,
                         scope='conv10')
    net = tf.squeeze(net, [2]) # B x N x C


    return net, end_points


레퍼런스