아두이노 미니 드론 만들기 #1


2017년 7월 경…아두이노를 이용하여 싱글콥터(프로펠러가 하나인 드론)을 만들겠다고 한참 삽질을 했었다.
물론 성공하지는 못했다. 다만 잠깐 띄우는 정도…(유튜브 영상 : https://youtu.be/sI5e4swuRTM)


다양한 시도를 하였지만 일부 구현되지 않은 기능(자이로 센서를 이용한 균형 잡기 등)과 적절하게 프레임을 구성하지
못하는 등의 한계로 보류를 해두었다.


그런데 겨울방학을 맞아 아이들이 다니는 태권도장에서 드론 만들기 수업을 한다고 하길래 아이가 하고 싶어하기도 하고
또 어떻게 진행되고 어떤 드론을 만들어 오는지도 궁금하여 시켜주려 하였는데 다른 곳에 돈을 쓰게 되어 결국은 시켜주지
못하게 되었다. 대신 아쉬워하는 둘째 아이를 위해 아빠가 직접 만들어주겠다고 호언장담을 해버렸다…ㅠ.ㅠ


그래서 한동안 묵혀두었던 부품들을 주섬주섬 꺼내고 또 일부 부품들은 새로 구입을 하여 드론 만들기에 돌입을 하였다.
물론 이번에는 싱글콥터가 아닌 쿼드콥터로…


구상


처음에는 시중에 판매하는 아두이노 드론 키트를 구매해볼까 하는 생각도 했다. 그런데 검색 사이트를 통해 아두이노 
드론 키트를 검색하게 되면 대부분 10만원이 넘는 금액의 키트들이 검색된다. 아무래도 비용이 애들 엄마의 허락 범위를
넘을 것 같기도 하고 또 뭔가 남이 상당부분 만들어놓은 것을 조립하는 것은 별로 재미도 없을 것 같아 밑바닥부터 직접 
만들어보기로 했다.


직접 만들기로 하면서 가장 문제가 되었던 것은 바로 프레임이었다. 이제는 3D 프린터가 있어 쉽게 만들 수 있을 것이라
생각했지만 내가 가진 3D 프린터의 출력 사이즈가 작다보니 한 번에 프레임을 찍어낼 수가 없는 상황이었다. 조금은
색다른 형태의 드론을 만들어보고 싶었지만 아무래도 다음 기회로 넘겨야겠다.


결국 프레임도 구매하기로 하고 나머지 부품들도 아두이노와 아두이노 관련 모듈들로 구매하여 조립을 해보기로 하였다.


부품의 준비


이렇게 결정한 후 구매한(혹은 이미 구매를 해놓은) 부품들을 하나씩 정리해보도록 하겠다.
일단 전체 부품들은 다음 사진과 같다.



각각의 부품들을 좀 더 상세하게 살펴보자.


드론 프레임 
- Q100 카피 제품으로 사진은 별도로 안찍었다. 위 사진의 프레임이다. 모터는 8520 모터를 사용
가능하고 프로펠러는 65mm까지 사용 가능하다. 가격은 4,260원


Flight Controller 
- 메인 컨트롤러는 아두이노 프로 미니 3.3v 8Mhz이다. 아두이노 프로 미니를 구매할 때 삽질을 조금
하였는데…아무 생각 없이 전에 하던대로 5V로 작동하는 제품을 선택한 것이다. 드론 자체는 3.7V로
작동을 해야 하는데 아두이노가 5V로 작동을 하게 되면 아두이노를 위해 별도의 배터리를 장착해야 하는
상황이 생긴다. 늦게서야 이 사실을 파악하고 부랴부랴 배송 전에 3.3V짜리로 교환 요청을 했다. 덕분에
배송은 좀 늦어졌지만…ㅠ.ㅠ 가격은 3,300원



자이로 센서 
- MPU-9250 9축 자이로 센서로 구입을 하였다. 원래 전에 구입해놓은 것이 있는데 찾지를 못해 새로
구입을 하였다…ㅠ.ㅠ 가격은 6,050원



DC 모터 드라이버 
- 현재 키트로 판매되고 있는 아두이노 드론들을 보면 베이스 보드라 하여 모터 드라이버 기능이 포함된 별도의
보드가 부품으로 포함되어있다. 내가 회로 설계를 할 줄 안다면 하나 만들어보겠으나 그런 재주는 없으니…-.-
이 부품으로 인해 무게에서 많은 손해를 보고 들어간다…ㅠ.ㅠ 일단 L9110 모터 드라이버 중 가장 작아보이는 
놈으로 골랐다. 보통 DC모터 드라이버가 드라이버 1개 당 DC모터 2개를 제어할 수 있으므로 2개를 구매했다. 
개당 가격은 3,740원



배터리 
- 배터리는 전에 구매해놓은 적이 있는 3.7V 500mah Li-Po 배터리를 사용하기로 했다. 
가격은 잘 기억이 안나지만 5,200원 선일 것이다.



8520 코어리스 모터 
- 모터 역시 예전에 구매해놓은 것인데 유튜브 영상에서 레이서스타 제품이 성능이 높게 나온 것을 보고 뱅굿에서
구매한 모터이다. 일단 RPM이 53500으로 보통 45000정도인 다른 제품보다 높다. 가격은 4개 한묶음으로
15,922원
유튜브 : https://youtu.be/AMWXXCHrHto



프로펠러
- 프로펠러 역시 모터와 함께 구매해 놓은 것으로 유튜브 동영상에서 가장 성능이 높게 나온 것으로 선택한 것이다.
킹콩 65mm 프로펠러이고 10쌍 가격은 5,679원 
유튜브 : https://youtu.be/VtKI4Pjx8Sk



수신기 
- DC 모터 드라이버와 마찬가지로 송수신기 역시 별도의 부품으로 만들어야 한다. 실내 공간이므로 블루투스로도
충분하지만 보다 저렴하기도 하고 예전에 싱글콥터 만들 때 이미 nRF24L01+ 모듈 기반으로 만들어 둔 송신기와 
코드를 재활용할 수도 있어 수신기 역시 nRF24L01+ 모듈을 선택했다. 가격은 1,100원



송신기
- 앞에서 말했듯이 2017년에 싱글콥터 만들 때 만들어놓은 송신기를 재활용 할 것이다. 송신기는 아두이노 나노와
듀얼 조이스틱 모듈 그리고 nRF24L01+ 모듈로 만들어져있다.


가조립과 무게


일단 모든 것을 직접 제작하기로 한 후 가장 걱정이 되는 것은 드론의 무게였다. 워낙에 무게가 적게 나가는 미니 드론이다
보니 1~2g도 영향을 크게 미친다고 하는데 많은 부품들이 기판에 소형화되어 올려진 전용 보드에 비해 개별로 부품을
사용하다보니 무게가 너무 많이 나가지 않을까 걱정이 되었던 것이다. 일단 부품들을 쌓아 올려 무게를 한 번 재보았다.
완성이 된다면 케이블 등으로 인해 무게가 조금 더 늘겠지만 일단 배터리를 포함한 부품만의 무게는약 60g이었고 여기서
배터리를 제외하면 약 42g으로 생각보다는 많이 나가지 않았다.




그리고 부품들을 프레임에 배치를 해보았다. 두 번째 걱정은 작은 프레임 안에 덩치가 큰 부품들을 제대로 배치할 수
있을까 하는 문제였다. 특히나 DC모터 드라이버가 덩치가 커서 조금 걱정이 되었는데 드론 바닥쪽으로 붙여보니 나름
적절하게 배치가 되었다.




정리


일단 이번 주말은 여기까지 진행을 해보았다. 아마 다음주부터는 제대로 골머리좀 썪을 것 같다.
우선 아두이노 프로 미니가 USP 포트가 없는 관계로 USB to TTL모듈을 이용하여 프로그램을 업로드 해야 하기도 하고
각 부품의 배선을 어떻게 하느냐도 문제다. 당연히 프로그래밍도…ㅠ.ㅠ


특히 부품간의 배선은 무게를 최소화 하기 위해 핀헤더라든지 아두이노용 점퍼 케이블은 사용하지 않고 굵기가 가는
래핑와이어를 사용하려고 하는데 납땜을 하자니 조금 귀찮고 그렇다고 절연 테이프로 하자니 그건 또 그것 대로 귀찮고…


일주일간 방법을 잘 모색해보고 다음 주말부터 본격적으로 시작해보자.


블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^



안드로이드 스튜디오 : Gradle sync failed: Uninitialized object exists on backward branch 70


백만 년만에 안드로이드 스튜디오를 3.2.1로 업그레이드한 후 프로젝트를 하나 생성했더니 아래와 같은 오류가
발생을 하였다.


Uninitialized object exists on backward branch 70
Exception Details:
  Location:
	com/android/build/gradle/internal/scope/BuildArtifactsHolder.newArtifact(Lcom/android/build/api/artifact/BuildableArtifact;)Lcom/android/build/gradle/internal/scope/BuildArtifactsHolder$BuildableArtifactData; @119: goto
  Reason:
	Error exists in the bytecode
  Bytecode:
	0x0000000: bb02 0559 2bb9 0208 0100 c000 b82b b902
	0x0000010: 0c01 0001 b902 1202 0059 1302 14b8 0051
	0x0000020: c001 734d 3a0d 3a0c 3a0b 2c4e bb01 a659
	0x0000030: 2c10 0ab8 0177 b701 a7c0 00b8 3a04 2db9
	0x0000040: 018e 0100 3a05 1905 b901 9401 0099 002d
	0x0000050: 1905 b901 9801 003a 0619 0419 06c0 00f1
	0x0000060: 3a07 3a0e 1907 b902 1501 003a 0f19 0e19
	0x0000070: 0fb9 01ad 0200 57a7 ffcf 1904 c001 af3a
	0x0000080: 0e19 0b19 0c19 0d19 0eb7 0218 b0       
  Stackmap Table:
	full_frame(@70,{Object[#2],Object[#83],Object[#371],Object[#371],Object[#184],Object[#400],Top,Top,Top,Top,Top,Uninitialized[#0],Uninitialized[#0],Object[#184]},{})
	same_frame(@122)

Open File


아래는 에러 화면이다.




사실상 기본적인 순서에 따라 프로젝트 하나 생성했을 뿐인데 이러한 문제가 생겨버리면 참으로 당황스럽지 아니할 수 
없다…ㅠ.ㅠ 열심히 구글링한 끝에 JDK 설정이 문제라는 것을 확인할 수 있었다.


해결 방법


해결 방법은 간단하다. 안드로이드 스튜디오의 Welcome 화면의 우측 하단에 보면 Configure 메뉴가 있는데
클릭한 후 가장 아래 있는 Project Defaults > Project Structure를 선택하면 Project Structure 창이 열린다.
여기서 가운데 있는 JDK Location 항목의 Use Embedded JDK (recommended)를 체크해주면 된다.
친절하게 recommended라고 되어있지 않은가…-.-


구글링하다보니 답변에 Project Structure에서 Use Embedded JDK를 체크하라고만 되어있어서 Project 
Structure를 찾는데도 한참 걸렸다. 꾸준히 공부 안하면 이렇게 된다…ㅠ.ㅠ


아래에 참고삼아 스크린샷도 함께 올린다.




블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


머신러닝 Reboot - 개념 잡기 : 경사 하강법 2 - step 공식 이해하기


지난 시간에는 어설프게나마 경사 하강법이 왜 특성에 민감한지 그래프를 통해 알아보았다. 여전히 논리적으로 설명하기 
힘든 부분이 있어 아쉬움이 남지만 직관적으로 봤을 때도 경사 하강법을 수행하기 위해서는 일단 특성들의 스케일을 
맞추는 것이 좋다는 것은 알게 되었다.


오늘은 이어서 어떤식으로 다음 기울기를 찾아 움직이는지 그 과정을 공식을 통해 알아보자.
이번 정리는 오로지 “핸즈온 머신러닝”의 166쪽에 있는 ‘식 4-7 경사 하강법의 스텝’을 이해하기 위한 것이다.


선형 회귀 관련 공식 복습 - 가설 함수와 비용 함수


우선 복습 차원에서 선형 회귀의 가설함수와 비용 함수를 다시 한 번 보자. 여러 표현 방법이 있지만 여기서는 “핸즈온
머신러닝”에서 발췌한 내용으로 정리를 해보겠다.


먼저 가설함수를 보자. 간단한 식임에도 불구하고 다양한 표현이 존재하여 혼란을 주기 일쑤이다. 아래 표현들을 보면서
정리해보자.


선형 회귀 가설 함수그림 1


1번 같은 경우 단순 선형 회귀라고 생각하면 되겠다. 𝜭와 𝑥가 모두 스칼라인 경우인 것이다. 다시 말해 특성이 1개인
경우…


2번과 3번은 모두 다중 선형 회귀를 표현한 식이며 𝜭와 𝑿는 모두 벡터이다. 그런데 묘하게 표현이 다르다.
왜 다른지 차근차근 살펴보자.



기본적으로 벡터는 종벡터(𝑛 X 1) 형태를 취한다. 이 때 𝑛은 특성의 수이다. 우리가 이미 잘 알고 있듯이 이 식들은
가설 함수의 원래 형태인 아래의 형식을 벡터의 곱으로 표현한 것이다.


선형 회귀그림 2


이 식의 𝜭와 𝑥를 각각 벡터로 표시해보자. 벡터는 기본적으로 종벡터 형태를 취한다고 했으니 다음과 같이 표현할 수
있다(여기서 𝜭 의 0번째 요소는 편향을 의미하며 따라서 X의 0번째 요소는 항상 1이다).


그림 3


그림 3-1


그런데 𝜭와 𝑥 가 모두 𝑛 X 1벡터라고 한다면 (𝑛 X 1) ∙ (𝑛 X 1)이 되어 벡터(행렬)의 연산 법칙으로 인해 계산을 할 수 
없게 된다. 따라서 앞에 있는 𝜭를 전치행렬로 만들어 (1 X 𝑛) ∙ (𝑛 X 1)이 되게 함으로써 연산이 가능하게 만드는 것이다.
이 것이 바로 두 번째 식이다. 물론 전치행렬의 성질에 따라 다음과 같이 표현할 수도 있다.


선형 회귀 가설 함수그림 4


3번째 식은 2번째 식을 조금 더 확장한 것이라고 볼 수 있다. 2번이 식에서 𝑋는 𝑛개의 요소를 갖는 벡터였다.
이러한 식이 𝑚개, 즉 𝑛개의 특성을 갖는 샘플이 𝑚개가 있다고 보는 것이다. 따라서 이 때는 식의 결과 역시 
벡터가 되는 것이다. 즉, 3번의 식을 구성하는 각 요소는 다음의 의미가 있다(물론 이 때 편향을 생각하여
𝑋 행렬의 1열은 모두 1로 채워져야 한다).


그림 5


그림 6


그림 3


여기에서 식은 2가지로 표현이 가능하다 𝑋를 𝑛 X 𝑚 행렬로 만든다면 식은 2번의 식과 동일한 형태가 만들어질
것이다. 이렇게 본다면 2번의 식이 가장 일반적인 선형 회귀의 가설함수라고 볼 수 있을 것이다. 그리고 이 식을
선형 회귀의 비용 함수에 대입하게 되면 아래와 같은 비용 함수의 식이 만들어진다.


선형 회귀 비용 함수그림 7


하지만 𝑋를 𝑚 X 𝑛 행렬로 만든다면 3번의 식이 된다. 이 3번의 식은 곧이어 설명할 경사 하강법의 step을
계산하는 공식에 등장하게 된다.


배치 경사 하강법


경사 하강법은 가중치 𝜭의 변화에 따라 비용 함수의 결과가 얼마나 바뀌는지를 확인하는 연속되는 과정이고
이를 알기 위해서는 비용 함수를 𝜭에 대해 미분해야 한다. 위에 언급한 그림7의 비용 함수를 𝜭에 대해 미분하면
다음과 같은 식을 얻을 수 있다(이 과정에서도 변형이 있는데 식 맨 앞의 2/m에서 2를 없애기 위해 미리 비용 함수에
1/2를 곱하는 경우도 있다. 이런 경우 2/m이 아닌 1/m이 된다).


선형 회귀 비용 함수의 편도함수그림 8


우리는 수알못이니 이 과정을 잠깐 설명하면 우선 미분의 성질 중 다음 성질을 알아야 한다. 바로 미분의 연쇄법칙이다.


 (f(g(x)))'=f'(g(x))g'(x)


미분의 연쇄법칙을 적용해보자면 선형 회귀 비용 함수는 다음과 같이 구성되어있다.


미분의 연쇄법칙그림 9


따라서 차례차례 미분을 해보면 다음과 같이 풀이될 수 있다.


미분의 연쇄법칙그림 10


이와 같이 선형 회귀의 비용 함수에서 𝜭에 대해 미분한 도함수는 그림8의 식이 되는 것이다. 이 도함수는 곧 비용 함수의
기울기를 의미하므로 경사 하강법은 이 도함수의 변화를 이용여 최솟값을 찾는 과정이고, 이는 초깃값으로 주어진 𝜭0에서 
학습률과 비용 함수의 도함수를 곱한 값을 빼서 다음 𝜭1를 구하고 다시 이 𝜭1에서 학습률과 비용 함수의 도함수를 곱한 
값을 빼서 𝜭2를 구하는 식으로 이 과정을 반복해 나가는 것이다.


이 과정에서 비용 함수의 도함수를 그대로 사용하는 경우도 있지만 식의 단순화를 위해 이 비용 함수의 도함수의 변화량을
행렬식으로 만들어 한방에 처리하는 방법도 있다. 이 것은 얼마전 포스팅한 정규방정식 관련 글에서 언급했듯이 𝚺는 
행렬로 변환 가능하다는 것으로 설명할 수 있다.


비용 함수의 도함수를 풀어보면 다음과 같다.


선형 회귀 비용 함수의 도함수그림 11


여기서 괄호 안에 있는 각 요소의 점곱(∙)을 기준으로 앞뒤로 분리를 하면 각각 다음과 같은 종벡터를 만들 수 있다.


그림 12


그림 13



각각의 종벡터는 m X 1 형태의 종벡터로 그대로는 곱셈이 성립하지 않으므로 𝑋가 요소인 종벡터를 전치시켜서
1 X m 형태의 횡벡터를 만들어 곱하면 동일한 식이 된다.


그림 14


그림 15


이제 마지막으로 𝜭가 포함된 종벡터를 풀이해보자. 이 종벡터는 다시 아래와 같이 나눠볼 수 있다.


그림 16


여기서 다시 뺄셈 식의 앞부분을 생각해보면 𝑋(i)는 특성 수만큼의 요소를 갖는 벡터들이다. 즉 m행 n열의 행렬이
되는 것이다.


그림 17


하지만 이렇게 되면 𝜭T는 1 X n의 벡터이고 𝑋는 m X n의 행렬이 되어 곱셈식이 성립되지 않는다. 따라서 𝜭T를
다시 전치시켜 n X 1의 종벡터를 만든 후 𝑋 뒤에 곱하면 m X n 행렬과 n X 1 벡터의 곱이 성립된다. 이렇게하여
최종적으로 정리된 선형 회귀 비용 함수를 𝜭에 대해 미분한 도함수의 변화량은 다음과 같이 표현할 수 있다.


그림 18


그리고 경사 하강법의 STEP을 구하는 공식은 아래와 같다.


경사 하강법의 step 계산 공식그림 19


정리


여전히 수학은 어렵다. 나름 치환과 간략화에 주의하면서 각종 공식을 이리 저리 변형시켜가면서 이해하려고 하지만
깔끔하게 정리되지 않는 것은 어쩔 수가 없다. 일단 오늘의 소득이라면 행렬을 횡벡터를 요소로 갖는 종벡터로 생각
하면 조금 더 쉽게 이해되는 경우가 있다는 것 정도…


오늘의 주된 내용은 “핸즈온 머신러닝”의 166쪽에 있는 ‘식 4-7 경사 하강법의 스텝’에 대한 풀이였는데 사실 책을
보면 여전히 이해되지 않는 부분이 있다. 165쪽에 있는 식 4-5 비용 함수의 편도함수 식에서 j의 의미를 잘 모르겠다.
얼핏 봤을 때 특성의 수를 의미할 것 같은데…그리고 괄호 안의 x와 괄호 밖의 x가 다르게 표기된 부분도 잘 이해가
안간다. 이렇게 기호 하나가 추가되는 것만으로도 풀이가 안드로메다로 향하는 것을 보면 아직도 한참 멀었다…ㅠ.ㅠ


일단 내가 정리한 식도 얼추 앞뒤가 맞아 들어가는 것 같으니 우선 오늘의 정리는 마무리 하고 다음 포스팅에서는
여기서 정리한 식을 바탕으로 코드를 통해 배치 경사 하강법, 확률적 경사 하강법, 미니 배치 경사 하강법에 대해
알아보도록 하겠다.


피곤하다…ㅠ.ㅠ

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^



머신러닝 Reboot - 개념 잡기 : 경사 하강법 1 - 특성의 scale


새롭게 시작하는 머신러닝 학습은 기존에 진행하던 학습에서 장애가 되었던 용어와 공식에 대한 몰이해를 극복하고자
진행하려고 한다. 다시 말해 직관적으로는 이해가 가지만 논리적으로 설명할 수없는 개념을 논리적으로 설명 가능하도록
정리해보고자 하는 것이다.


따라서 전체적으로 연관성을 가지고 이어지는 내용이라기 보다는 단편적인 용어의 정의나 공식의 풀이를 중심으로 
하면서 관련 내용을 정리하는 방식으로 진행이 될 것이다. 


이렇게 정리할 대상은 주로 ’핸즈온 머신러닝’이라는 책을 읽으면서 이해가 안가는 부분들을 대상으로 풀이할 것이며
전체적인 순서 역시 ‘핸즈온 머신러닝’의 목차를 따를 것이다. 


들어가는 말


지난 시간에는 선형 회귀 분석의 비용함수로부터 가중치(𝜽 또는 W)의 최솟값을 한방에 알아낼 수 있는 정규방정식
대해 알아보았다. 미분 등 복잡한 계산이 필요 없고 학습률같은 하이퍼파라미터를 관리할 필요가 없으며 또 빠른 예측이
가능하다는 장점이 있지만 특성 수가 늘어남에 따라 속도가 많이 느려지는 단점이 있었다.


오늘은 정규방정식의 단점을 해결할 수 있는, 다시 말해 특성 수에 관계 없이 일정 수준의 성능을 보장해주는 
경사하강법애 대한 내용 중 특성의 스케일에 대해 알아보려고 한다. 


경사하강법은 대체로 특성의 스케일에 민감한 것으로 알려져 있으며 일반적으로 아래 그래프로 그 사실을 설명한다.


핸즈온 머신러닝 발췌핸즈온 머신러닝 발췌

오늘은 경사하강법이 특성에 민감하다는 것을 예제 코드를 통해 조금 더 직관적으로 설명을 하고자 한다. 
사실 논리적으로 증명을 하고싶었으나 역시 나의 실력으로는 역부족이었다. 이 내용과 위의 그래프를 이해하지
못하여 이 포스팅을 준비하는데 무려 3주가 걸겼다…ㅠ.ㅠ


게다가 내가 그간 얼마나 공부를 설렁설렁 했는 지 이번 기회에 알게 되었다. 그동안 나는 선형회귀의 비용함수와 
경사하강법을 동일시 하여 생각했던 것이다. 서로 다른 함수를 동일하다고 생각하고 분석하고 있었으니 답이
나올리가 있나…-.- 겨우 최근에야 경사하강법은 특정 함수(특히 convex 함수)의 최적값을 찾아낼 수 있는
일반적인 알고리즘이라는 말을 이해하게 되었다.


다시 말해 경사하강법은 선형회귀의 비용함수 뿐만 아니라 볼록(또는 오목)한 그래프가 그려지는 함수라면 어떤
함수이든 그 최저점을 찾아낼 수 있는 방법이라는 것이다.


지금부터 코드를 통해 이 내용을 간단히 살펴보자. 너무 간단해서 들어가는 말보다 본문이 짧을지도…-.-


Python 코드로 보는 경사 하강법


이 내용의 원본 소스 출처는 다음과 같다.


https://github.com/shuyangsun/Cost-Function-Graph


이 원본 소스 중 non-convex 함수들에 대한 내용은 제거 하고 convex 함수에 대한 내용만을 남겨 확인해보았다.


일반적으로 특성이 2개인 함수까지는 시각화(그래프로 표현)할 수 있다. 이 부분은 내가 처음 머신러닝을 공부한다고
정리를 하면서 다항로지스틱으로 넘어갈 때 꽤나 답답해 했던 부분이기도 하다. 특성이 2개인 경우까지는 시각화가
가능해서 직관적으로 이해를 할 수 있었는데 특성이 3개 이상 되니 복잡한 수식만으로는 도무지 이해가 가지 않는
것이었다…ㅠ.ㅠ


여전히 특성이 3개 이상인 경우는 이해가 힘들기 때문에 오늘은 특성이 2개인 케이스를 대상으로 설명을 해보겠다.


이 코드에서 사용할 함수는 비용함수는 아니고 f(a,b) = a^2 + b^2 이라는 함수이다. 이 함수가 표현하는 범위를
3차원 그래프로 그려보고 그 범위 안에서 경사하강법이 어떤 경로로 최저점을 찾아가는지 보여주는 것이 아래의
코드이다.


import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

def f(a,b):
	return a**2 + b**2

먼저 필요한 라이브러리들을 import하고 경사하강법을 통해 최저점을 찾아낼 대상 함수 f를 정의했다.


def gradient_descent(theta0, iters, alpha):
	history = [theta0] # to store all thetas
	theta = theta0     # initial values for thetas
	# main loop by iterations:
	for i in range(iters):
		# gradient is [2x, 2y]:
		gradient = [2.0*x for x in theta] #함수 f(x,y)의 미분
		# update parameters:
		theta = [a - alpha*b for a,b in zip(theta, gradient)]
		history.append(theta)
	return history

history = gradient_descent(theta0 = [-1.8, 1.6], iters =30, alpha = 0.03)


다음으로 경사하강법을 함수로 정의하고 호출하여 그 결과를 history에 저장을 한다. 경사하강법의 다음 스텝을 결정하는
일반식은 다음과 같으며 이를 python 코드로 구현한 것이다.




경사하강법을 구현한 함수는 파라미터로 특성의 초깃값과 반복 횟수 그리고 학습률을 전달받는다. 이 코드에서 초깃값은 각 특성의 max에 가까운 값으로 정했다.


# f(x,y) = x^2 + y^2 함수의 그래프 그리기 fig = plt.figure(figsize=(20, 15)) ax = fig.gca(projection='3d') #plt.hold(True) a = np.arange(-2, 2, 0.25) b = np.arange(-2, 2, 0.25) a, b = np.meshgrid(a, b) c = f(a,b) surf = ax.plot_surface(a, b, c, rstride=1, cstride=1, alpha=0.3, linewidth=0, antialiased=False, cmap='rainbow')



주석된 내용처럼 f 함수가 표현하는 함수의 범위를 3차원으로 그려주는 코드이다. 특성 a와 b 모두 -2부터 2 사이의 값을
가지며 0.25씩 증가 하도록 값을 주었다. 함수가 a^2 + b^2이기 때문에 최솟값 0부터 최댓값 8까지의 그릇 모양으로
그래프가 표시된다.


a = np.array([x[0] for x in history])
b = np.array([x[1] for x in history])
c = f(a,b)
ax.scatter(a, b, c, color="r"); 

print(c)

plt.xlabel('Feature A')
plt.ylabel('Feature B')

plt.axis('equal')

plt.show()


이제 마지막으로 이전에 그려진 그래프 내에서 경사하강법을 통해 산출한 위치를 표시해준다. 특성이 2개이기 때문에
각각의 특성에 경사하강법을 적용한 결과를 그래프에 그려보면 최종적으로 아래와 같은 그래프를 볼 수 있다.



이 때 a = np.arange(-2, 2, 0.25)의 범위를 a = np.arange(-10, 10, 0.25)로 늘리게 되면 그래프의 형태가
오목한 그릇 형태가 아닌 u자 모양으로 휘어진 판자의 형태가 된다.



이런 상황에서는 가중치의 초깃값이 커질 수 있고 초깃값이 커지면 최솟값을 찾는데 그만큼 더 시간이 오래 걸리게 되며 이는 곧 특성값의 스케일 차이가 크게 되면 경사하강법의 성능이 나빠지게 된다고 볼 수 있는 것이다. 또한 내가 제대로 
이해하고 있는지 모르겠으나 이 그래프 표현만 놓고 보면 단지 두 개의 특성간에 스케일의 차이가 있을 때 뿐만 아니라 
두 특성의 스케일이 동일하더라도 그 규모가 커지면(예를들어 a와 b의 범위가 모두 10인 경우와 모두 100인 경우)
이 때 역시 경사하강법의 성능이 나빠져 더 많은 횟수를 진행해야 최솟값에 가까워지게 된다.


정리


앞서도 말했지만 이 부분을 이해하기 위해 장장 3주 이상이 걸렸다. 그러다가 위의 python 코드를 발견했고 처음 코드를
실행해봤을 때는 ‘유레카’를 외쳤지만 지금 다시 찬찬히 살펴보는 과정에서는 또 수많은 의문들이 일어나면서 내가 
제대로 이해한 것이 맞는지 알 수 없게 되었다…ㅠ.ㅠ 일단 직관적으로 생각했을 때 작은 수를 계산할 때보다 큰 수를 
계산할 때 더 많은 자원이 필요한 것은 당연한 것이니 특성의 스케일이 크면 그만큼 연산이 오래 걸린다고 보면 될 것이나
역시 완전한 이해는 되지 못한다.


더 나은 해법을 찾다가 contour라는 등고선 형태의 그래프를 그리는 방법이 있다는 것을 알아냈고 이 그래프가 위에
언급한 핸즈온 머신러닝의 그래프와 유사해서 더 설명하기가 좋지 않을까 생각했으나 실제 코드에 적용하는 방법을
몰라 이번 포스팅에서는 다루지 못했다. 시간 날 때 다시 정리를 해봐야겠다.


다음 시간에는 경사하강법의 3가지 종류(배치 경사하강법, 확률적 경사하강법, 미니 배치 경사하강법)에 대해 간단하게
정리해보겠다.

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


2003년경 처음 나왔던 강철의 연금술사를 보았더랬다. 그리고 2009년에 나온 강철의 연금술사:브러더후드를 그저 2003년판의 리메이크일 것이라고

지레 짐작하고 보지 않다가 얼마전 넷플릭스 강제 결재 당하고난 이후 보게 되었다. 넷플릭스 덕분에 우선 죽지 않는 자 아인을 재밌게 봤었는데 그 뒤로 

마땅히 땡기는 것이 없던 차에 눈에 띄어 보게 된 것이다.


초반부가 2003년 판과 거의 비슷해 조금 지루해질 무렵부터 새로운 내용이 전개되니 몰입도가 장난이 아니었다. 새로운 인물들도 매력이 있었고 

툭툭 던지는 개그도 위화감 없이 잘 어우러진 것 같다. 2003년판 2009년판이 모두 명작의 반열이라는데 이의를 달 수가 없었다.


2003년에 볼 때에도 '등가교환'이라는 개념이 꽤나 매력적이었다.

사실 예전부터 생각해온 바지만 세상이 좋아지는 데는 공정한 것 만한 것이 없다고 생각한다. 한 일만큼 보상을 받고 저지른 잘못만큼 벌을 받는...

하지만 세상에서는 그 당연한 것들을 쉽게 볼 수 없다.


물론 자연 법칙으로서의 등가교환 - 질량 보존의 법칙 같은 것? -은 있지만 그 것이 인간 사회로 나오면 근본적으로 등가교환이긴 한데 방향이 달라진다. 

즉, 버는 놈 따로 갖는 놈 따로라는 것...-.-


암튼 잘 먹고 잘 살자!

연금술이 뭐 따로 있나. 잘 먹고 잘 싸는 것도 훌륭한 연금술 아닌가? 덤으로 배까지 나오고...-.-

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^