Post List

2020년 7월 31일 금요일

ML(머신러닝)과 inference(추론) 최대한 빨리 공부해보기(4) (수정중)

드디어 Convolution Neural Network 까지 왔다. 정말 많이들어본 녀석 중 하나지만 대충 이미지 좋다는 정도만 알고있었다. 정확히 어떤 기능을 하는지, 무엇이 현재 이 분야의 이슈인지 빨리 알아봐야 할 것이다. 수많은 논문중에 눈이 가는 제목 2개를 뽑아 조금 훑어봤는데 꽤나 흥미로운 것 같다.

    Zhijian Liu et al., "Point-Voxel CNN for Efficient 3D Deep Learning" CVPR '19
    Yue Wang et al., "Dynamic Graph CNN for Learning on Point Clouds" CVPR '19
두번째껀 읽지 않았지만 CNN을 통해 3D Deep Learning을 한다는 내용인것 같다. 사실 2D에서 3D로 확장될 수 있다는 것은 어찌보면 당연한 것일지도 모르지만 이쪽에 아예 조예가 없는 나로선 꽤나 흥미로운 것 같다. 잡소리는 여기까지.

Convolution Neural Network

이미지를 컴퓨터로 인식할 수 있을까. 물론 있다. 내가 잘모를 뿐. 아무튼 기본적인 아이디어는 이런 내용인 것 같다. 우리가 이미지를 네트워크로 학습시킬 때 각각의 픽셀을 네트워크의 인풋으로 넣어준다고 가정하자.

픽셀 단위로 학습을 진행한다면, 위와 같이 같은 A글자더라도 왼쪽으로 이동한다거나 회전된다거나 할 경우, 인식하는데 문제가 발생할 것이라는 것을 직관적으로 이해할 수 있다. 더군다나 이미지라는 것은 관측자(카메라)의 위치, 각도, 조도 등 많은 요인에 의해 영향을 받기 때문에 좀더 안정적이고 효율적인 방안이 필요하다. 그렇기 때문에 나온것이 CNN이라는 것이다. 그러면 CNN은 뭐가 어떻게 더 좋은 것일까.

Convolution Layer

일단 Convolution 이란 연산을 지칭하는 단어인데, 수학적으로 정확한 정의는 모르지만 적어도 우리가 다루려고 하는 것은 행렬조각과 Kernel이라 불리는 작은 행렬의 원소간 곱셈의 총합을 구하는 것이다.

위와 같이 이미지에 필터를 슬라이딩 시키면서 연산을 진행한다. 연산을 진행하고 결과로 나온 행렬을 feature map이라 부른다. 이때 궁금한 것은 filter 역시 학습을 통해 구해지는 것일까? 레이어마다 나오는 feature의 개수, 즉 네트워크 구조는 그냥 때려 맞추는 것일까? 아니면 합리적 결정, 혹은 학습을 통해 결정될까. 넘나 어렵다.

Pooling Layer

이렇게 결과로 나온 Feature map은 다시 Pooling Layer를 거치는데 Pooling은 또 뭘까. 찾아보니 네트워크를 학습할때 parameter가 많으면 over-fitting이 발생할 확률이 높다고한다. 이를 방지하는 것이 Pooling Layer라고 한다. Pooling 연산? 작업? 은 2개가 존재하는데 Max Pooling과 Avg Pooling이다.

그림과 같이 연산 자체는 굉장히 간단하다. 연산이 중요한 것이 아니고 데이터의 사이즈를 줄여 over-fitting을 방지하는 것 같은데 해당 내용은 over-fitting이 무엇인지 좀더 알아보면 좀더 명확히 이해할 수 있지 않을까.

CNN 구조

결국 CNN이란 위에서 언급된 Convolution layer와 Pooling layer들이 반복되고 마지막에 Fully connected layer가 붙은 형태를 띈다. 네트워크마다 어떤 이유로 차이가 있을텐데 이것들은 나중에 알아보도록 하자.

대충 이런 내용들을 훑어봤는데 역시 디테일을 이해하기 위해선 실습이 필요하지 않을까. 사실 얼마전에 Anaconda 설치했는데 뭔가 익숙하지 않아서... Matlab 예제로 mnist를 해볼지, python으로 해볼지 고민해봐야겠다.

2020년 7월 29일 수요일

ML(머신러닝)과 inference(추론) 최대한 빨리 공부해보기(3) (수정중)

자율주행, 혹은 robotics에서 어떤 기술이 필요할까 생각하면서 바로 CNN, 이미지/영상 처리를 볼까 생각하다가 그래도 Neural Network한번 보고 넘어가야겠다고 생각을 고쳐먹었다. 계속 이전 포스팅을 정리하지 못하고 넘어가는 느낌이지만 어쩔수 없다 생각한다.

Neural Network
Non-linear Hypothsis

지금까지 우리는 hypothesis가 linear하다는 가정하에 진행을 해왔다. 하지만 어떤 복잡한 문제는 linear하지 않을것이다. 예를들어 집값을 결정할 때 집값에 영향을 주는 feature가 100개라 가정하자. 이때 hypothesis가 linear하다는 전제가 없으면 feature space 가 너무 넓어진다. 기존 linear hypothesis를 구하는 방식을 2차, 3차,...n차로 확장하는 것은 연산량의 증가만 야기하고 복잡한 함수에 맞추다가 overfitting 문제가 발생할 수 있다.

그래서 Neural Network는 뭐가 다른다는 것일까? 유튜브 강의를 보다가 뭔가 intuition이 온 것 같다. 사실 예전에 학부강의에서 XOR classification에 대해 설명 듣긴 했지만 한귀로 듣고 흘려보냈는데...

위 그림과 같이 데이터가 분포할 때 함수로서 데이터들을 구분하기는 어렵다. 그에 비해 AND 나 OR의 경우 쉽게 구할 수 있는데 예시를 보도록하자.
만약 우리가 위처럼 AND, OR의 weight들을 찾았다 가정해보자.
결국 아이디어는 간단한 분류기들을 잘 합성한다면 복잡한 형태의 분류기를 만들 수 있다는 것이다. 이는 우리가 어떤 문제에 접근할 때 말도안되는 함수를 찾아내기보단, 네트워크의 weight을 구하는 것이 효율적이다라는 관점을 보여주는 것 같다(맞나?).

Machine Learning With Neural Network

NN을 이용해 학습을 한다고 하자. 결굴 근본은 cost function을 최소화하는 weight을 찾는 것인데 이제 이 weight들은 네트워크 layer 사이의 간선, 즉 행렬값을 찾는 것과 같아진다.

만약 위와 같은 네트워크에 대한 학습이 끝났을 땐, 5x4, 5x6, 4x5의 크기의 행렬이 구해져있을 것이다. Cost function과 back propagation을 온전히 이해해야 '학습'에 대해 안다고 말할텐데 cost function 이 굉장히 복잡하고 back propagation 역시 직관적으로 와닿지 않으니 일단 보류하고 CNN으로 넘어가보자.

2020년 7월 22일 수요일

ML(머신러닝)과 inference(추론) 최대한 빨리 공부해보기(2) (수정중)

Linear Regression

시간이 촉박하기 때문에 바로 Linear Regression으로 넘어가보자. 이전 포스팅에서 언급했듯이 Regression은 데이터셋에 fit한 함수 f를 찾아나가는 것이다.

Linear Regression With One Variable
Housing Prices

최대한 구현이 어떻게 될지 생각할 수 있게 예시를 사용하도록 하자. 집값에 영향을 주는 요소를 생각해보자. 1차적으로 면적이 넓을수록 가격이 높을 것이다. 그렇다면 내게 면적-집값 이라는 데이터셋이 주어질때 어떻게 학습을 시킬까?

내게 주어진 데이터셋으로 학습을 해서 어떤 모델(함수)을 만들고, 그 모델(함수)에 집의 면적을 입력했을 때 추정 집값이 나와야한다. 집값이 면적으로만 결정된다면, 즉 1차원 함수라 가정하면 다음과 같이 표현될 것이다.
그렇다면 함수를 찾는다는 것은 결국 w(weight), b(bias)를 구하는 것과 같다. regression에 대한 개념을 다시 생각해보면 실제 데이터값 y와 내가 구한 함수 h(x)과 비슷해야 한다. 즉 다음 수식이 최소가 될때의 w,b를 구해야한다.

아주 깔끔하게 요약된 영상을 캡처해서 올려보도록 한다.
출처 : 유튜브 강의

위 그림의 cost function이 최소가 되는 theta0,theta1(w,b)를 찾아야한다. 초기엔 값을 모르기 때문에 임의의 값(0,0)으로 초기화를 한다. 그리고 이런 과정을 거치길 바라는 것이다.

최초 (0,0)으로 초기화 했을때 비용이 빨간 점에서 시작했다면, theta값들을 조절하며 min이 되는 지점을 찾아가는것.
저 derivative가 의미하는 바를 생각해보자. 1변수 함수라고 가정하면 특정 theta에서의 기울기를 뜻하고 결과적으로 theta는 함수값이 감소하는 방향으로 갱신된다. 2변수 함수로 확장하면 산에서 가장 가파른 길로 내려오는 상황이 만들어지는 것이다.

2020년 6월 30일 화요일

matlab SLAM(Simultaneous Localization And Mapping) example(2)

SLAM Algorithm

저번 포스팅에 이어서 실제 SLAM이 어떻게 수행되는지 바로 알아보자.

for i=1:length(pClouds)
    % Read point clouds in sequence
    pc = pClouds{i};

반복문의 시작. 매회 point clouds 1개의 cell을 로드한다. 즉 simulation이 아니라 실제 로봇이었다면 1회 scan을 진행하는 것.

% Remove invalid points outside the max range and unnecessary points
    ind = (-maxLidarRange < pc(:,1) & pc(:,1) < maxLidarRange ...
        & -maxLidarRange  < pc(:,2) & pc(:,2) < maxLidarRange ...
        & (abs(pc(:,2))>abs(0.5*pc(:,1)) | pc(:,1)>0));
    
    
    pcl = pointCloud(pc(ind,:));

주석을 보면 Lidar sensor의 range 밖의 point등과, 불필요한 point들을 필터링한다고 되어있다. matlab을 처음 접한 나로선 저 :(콜론)을 포함한 코드가 뭘 뜻하는지 찾아봐야한다. 콜론은 matlab에서 가장 유용한 연산자 중 하나로, 벡터나 첨자 배열을 만드는 데 쓴다고 한다. 이런 문법같은 것은 사실 코드를 여러번 작성하면서 익숙해지는 것이 답인데 일단 matlab 공부하는 것이 아니니 코드 뜻만 알아보도록 하겠다. pc(:,1) 은 행렬 A의 1번째 열을 뜻한다. 그런데 지금보니까 matlab은 C/C++에서와 같이 인덱싱을 0부터 하지 않는듯 하다. 그리고 지금보니 Z축이 땅에서 수직이다. 코드를 해석보면 이렇게 그릴 수 있지 않을까.

이 코드는 시뮬레이션 코드이기 때문에 Lidar sensor 범위를 조절하고 편의를 위해 정사각형 영역을 사용하고 있지만 사실은 오른쪽과같이 원과 같은 영역을 탐지할 것이다. 다만 왜 2번째 행이 1번째 행의 두배보다 커야하는걸까? 나는 단순히 1,2,3 열이 x,y,z 좌표를 의미한다고 생각했는데 좀더 말이 되는쪽으로 생각해보니 Camera Coordinate 상의 좌표인듯 하다. 하지만 그래도 2행이 1행보다 2배이상 커야 하는 이유는 모르겠다. 보류하고 넘어가자. 결국 pcl 에는 1회 filtered scan point cloud가 배열로 저장될 것이다.

% Remove the points on the ground
    [~, ~, outliers] = ...
        pcfitplane(pcl, maxDistance,referenceVector,maxAngularDistance);
    pcl_wogrd = select(pcl,outliers,'OutputSize','full');
    
    % Remove the points on the ceiling
    [~, ~, outliers] = ...
        pcfitplane(pcl_wogrd,0.2,referenceVector,maxAngularDistance);
    pcl_wogrd = select(pcl_wogrd,outliers,'OutputSize','full');

솔직히 다른 언어들을 접하는 것에 대해 크게 생각해본적 없는데 생각보다 다르니 찾아봐야하는 것이 많다. ~ 연산자는 논리적 not 이나 출력 제한을 의미한다는데 일단 처음 pcfitplane은 outlier[변수]에 outlier index를 저장한다. 그리고 논리적으로는 pcl_wogrd(설마 pcl without ground??)에 기존 pcl에서 outlier들을 제거하는게 맞는거같다. select함수는

2020년 6월 29일 월요일

matlab SLAM(Simultaneous Localization and Mapping) example(1)

한 6개월 전부터 SLAM에 관심이 생겨서 여기저기 유튜브강의와 Probabilistic Robotics 책을 통해 독핵을 해보았다.
뭔가 기본지식을 쌓아나가면서도 내가 제대로 이해한 것이 맞나 의문이 한켠에 남는 느낌을 지울 수가 없었다.
왜 why? 첫째로는 이쪽 분야를 처음 접했으니 여러 수식들과 개념들이 바로바로 이해가 안된다. 기본기 부족이랄까...
둘째로는 부족한 이해를 실습으로 보충하면 좋겠지만 일단 로봇이 없다. 그래서 인터넷이 돌아다니는 dataset을 활용해볼까 했지만 역시 기본기 부족으로 제대로 활용하기 어려웠다.
이렇게 처음으로 시도한 C++로 가장 기본적인 Kalman Filtering 구현이 실패해버렸다

MATLAB

SLAM에 대해 배우고 많은 실험을 해보고 싶기 때문에 뒤적거리다 보니 matlab에서 SLAM 을 simulation 할 수 있는 예제코드가 있었다.
나는 matlab을 써본적도 없고 문법도 모르지만 일단 학교 라이센스를 이용해 설치후 예제를 돌려본다.
이 포스팅 자체가 해당 예제에 대한 포스팅이 될 예정..
matlab과 SLAM 둘다 맨땅에 해딩하는 것이나 마찬가지기 때문에 험난한 여정이 예상되지만 어떻게든 되겠지라는 마인드로 시작한다.
하지만 그 만큼 포스팅 자체의 퀄리티는 일기장 수준일 듯 하다.

Perform SLAM Using 3-D Lidar Point Clouds

matlab slam을 검색하면 처음으로 나오는 예제이다.
일단 여기서 다루는 데이터부터 한번 이해해보자.

load pCloud.mat

맨 처음에 하는것이 바로 pCloud.mat이라는 데이터를 로드하는 것인데 이 데이터가 정확히 어떤 데이터를 담고 있는지 궁금하다.
실제 데이터 파일을 열게되면 다음과 같이 1x240 의 cell을 가진 행벡터로 보여진다.

그리고 그 한개의 cell에는 대량 50000x3 의 데이터가 존재하는데 로봇이 움직일때 lidar 센서로 들어온 좌표정보가 아닐까 추측한다(예제 페이지 설명보니 아마 맞는듯).
즉 240x50000개의 x,y,z 정보가 있는셈이다.

referenceVector = [0, 0, 1];    % normal vector
maxDistance = 0.5;              % for removing ceiling/ground
maxAngularDistance = 15;        %

% sampling (sampling ratio : 0.25)
% point cloud downsampled
randomSampleRatio = 0.25;

% voxel grid size in NDT(Normal Distribution Tranformation)
gridStep = 2.5;
distanceMovedThreshold = 0.3;

데이터 로드 후 이것저것 변수들을 초기화 한다. 일단 randomSampleRatio는 말 그대로 point cloud를 sampling 하는 비율이다. 모든 point cloud들을 전부 보기엔 굉장히 오래걸리니까 샘플링을 하는 듯 하다. point cloud registration의 accuracy도 올려준다는데 샘플링을 안하는게 제일 정확한 것이 아닌가??? 일단 넘어가보자. 또 gridStep이라는 변수가 있는데 내가 알기로는 point 하나하나를 이용하기 보다 여러 point들을 묶어 하나의 voxel로 만들어 이용하는 것이 효율적이라 들었다 이 gridStep이 voxelization에 관여하는 변수인듯 하다. Voxelization에 대해 자세하게 알기 위해선 Normal Distribution Transformation에 대해 알아봐야겠지만 이부분도 일단 스킵하자. 마지막으로 distanceMovedThreshold는 0.3 이상 움직인 후 scan을 수행한다는 뜻.

% Loop Closure Estimation Algorithm?
loopClosureSearchRadius = 3;

nScansPerSubmap = 3;
subMapThresh = 50;

annularRegionLimits = [-0.75,0.75];

rmseThreshold = 0.26;

loopClosureThreshold = 150;
optimizationInterval = 2;

Loop Closure가 뭔가 검색해봤다. Loop Closure(루프결합)은 정확한 지도를 작성함에 있어 중요한 역할을 한다. 루프결합이란 로봇이 지도상에 이미 등록한 위치를 다시 방문하였을 때, 현재 측정된 sensor data와 이미 작성된 map data 사이의 일치를 찾고 누적된 에러를 감소시켜 정확도를 향상하는 과정이다. 시간이 지날 수록 error가 축적되는 SLAM 알고리즘과 같은 문제에서 필수적으로 필요한 프로세스로 보인다. 여기까지 알아보고 다시 진행해보자 loopClosureSearchRadius는 동일 위치에 재방문했다는 decision을 내릴 때 현재 나의 추정위치와 지도상 위치의 거리 Threshold 역할을 하는 듯 하다. 나 자신의 이해를 돕기위해 그림을 그려보도록 하자.

로봇이 파란 곡선을 따라 이동했다고 생각해보자. 사실 저 파란 경로 역시 로봇이 자신이 이동했다고 '추정'하는 경로일 뿐이다. 현재 로봇이 파란지점에 도달했다고 해보자. 이때 저 노란 별을 관측한 데이터와 지도상 빨간점에서 관측한 데이터가 유사할 때 로봇은 '아 내가 사실은 아까 그 빨간 점까지 돌아왔구나' 라고 생각하며 경로와 지도를 수정할 수 있다.
그리고 갑자기 Submap 이란 것이 튀어나오는데 일단 정확히 파악한 것은 아니지만 이해한대로 적어본다. 로봇이 움직이면서 지도를 그리고 submap이라는 local information을 유지한다. loop closure decision을 할때 유지한 submap와 비교를 하는데 바로 직전 subMapThresh 개의 submap은 고려하지 않는다 라는 뜻인듯 하다.

어제에 이어 포스팅을 계속해보자. 일단 어제 코드를 보며 생각했던 것은 일단 SLAM 알고리즘 자체가 크게 3개로 이루어져있다.
1. Kalman filter SLAM
2. Particle filter SLAM
3. Graph SLAM
그런데 일단 이 셋중에 어떤 알고리즘이 이용되어있는지 감을 잡기 힘들다는 것. 코드를 계속 보면 감이올지 안올지 모르겠으나 일단 어제 코드에 이어서 계속 봐보도록 하자.

% 3D Posegraph object for storing estimated relative poses
% poseGraph3D object stores information for 3D pose containing nodes
% connected by edges
pGraph = poseGraph3D;

% Default serialized upper-right triangle of 6-by-6 Information Matrix
infoMat = [1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1];

% Number of closure edges added since last pose graph optimization and map
% refinement
numLoopClosuresSinceLastOptimization = 0; 
% True after pose graph optimization until the next scan
mapUpdated = false;
% Equals to 1 if the scan is accepted
scanAccepted = 0;

% 3D Occupancy grid object for creating and visualizing 3D map

mapResolution = 2; % cells per meter
omap = occupancyMap3D(mapResolution);

일단 실질적으로 SLAM에서 사용되는 것으로 보이는 poseGraph3D의 객체를 생성하고 information matrix 의 upper triangular matrix(상삼각행렬)을 초기화한다. 그리고 scanAccepted 라는 flag 변수가 있는 것을 보니 모든 scan이 accept되지 않는다는 것도 알 수 있다.

pcProcessed = cell(1,length(pClouds));
lidarScans2d = cell(1,length(pClouds)); 
submaps = cell(1,length(pClouds)/nScansPerSubmap);

pcsToView = cell(1,length(pClouds)); 

% Set to 1 to visualize created map and posegraph during build process
viewMap = 1; 
% Set to 1 to visualize processed point clouds during build process
viewPC = 0;

rng(0);

그 후 메모리를 미리 할당해놓는데 이때 눈에 띄는 것은 submaps 를 할당할 때 length(pClouds)/nScansPerSubmap 의 사이즈로 할당하는데 그럼 이전에 언급했던 240 x 50000 이라고 생각했던 x,y,z 좌표 정보는 사실 240번의 scan 정보에 지나지 않았던 것 아닐까? 대략 1번의 scan에 50000개의 좌표가 들어오나보다. 실제로 몇번의 scan이 add 되었나 count 하는 변수가 229까지 카운팅 된것을 보면 240개의 scan중 11개가 drop되고 229개가 accept 된거같다. 이따 확인해봐야겠다. 이제 실제로 루프를 돌며 실제로 SLAM 알고리즘이 어떻게 돌아가는지 봐야하니 이후내용은 다음 포스팅에 올린다.