자영이는 전거를 타면서 본 화 야기입니다.
말 그대로 실내 자전거로 운동하면서 지루함을 달래기 위해 아이패드로 본 영화에 대해 극히 
주관적으로 아무런 논리적 분석 없이 의식의 흐름에 따라 적어 내려간 초 간단 감상문임을
참고해주세요.



원제 : Resident Evil: Retribution
상영 : 2012년
장르 : 액션, SF, 공포, 스릴러
감독 : 폴 앤더슨
출연 : 밀라 요보비치
시청 : 넷플릭스
개인 평점 : ★


음…내가 웬만하면 결말은 봐주는데…역시나 너무 가리지 않고 영화를 봐서 그런가…드디어 차마 끝까지 보지 못할
영화가 나타났다…


대체로 게임을 기반으로 한 영화가 쫄망하기 일쑤인데 그래도 시리즈가 6편이나 나왔다는 것은 나름 엄청난 성공을
거둔 것이라 할 수 있다. 뭐 제대로 본 것이 없어 평가할 것도 없지만 아마도 밀라 요보비치의 시원한 액션이 중요한
역할을 했을 것이라 추측해본다.


그런데…
이건 시원하다 못해 냉골을 만들어버렸네…
그래도 적들과 총격전을 하면 좀 숨는 시늉도 하고 그래야 하는 것 아냐?
이놈이고 저놈이고 도대체 뻣뻣이 서서 그냥 쏴대는 것은 어느 부대에서 배운 건지…
아무리 영화적 허용이라 하더라도 적들은 볏단처럼 쓰러져가고 주인공은 보호막이라도 둘러쳐진 듯 상처하나 없는 것은
내가 본 영화 중 가장 심했다…


차라리 게임이 더 현실적이고 긴장감 넘치는데…최근 플스4로 출시된 레지던트 이블 2 : RE를 하는 것이 백번 나을 듯…
내가 왜 이런 영화에 대한 글을 쓰고 있는지도 한심하네…
진짜 모든 것이 끝났으면 좋겠다…-.-


끝!

'문화' 카테고리의 다른 글

[자영이] 레지던트 이블5 : 최후의 심판  (0) 2019.03.23
[자영이] 버드박스  (0) 2019.03.23
무식쟁이 독서일기 ~ 15: 편의점 인간  (0) 2017.11.03
[자작 콩트] Cigarette Blues  (0) 2017.06.04
[영화] 미녀와 야수  (0) 2017.04.13
[영화] 레 미제라블  (0) 2017.02.26
블로그 이미지

마즈다

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

[자영이] 버드박스

문화 2019.03.23 12:56

자영이는 전거를 타면서 본 화 야기입니다.
말 그대로 실내 자전거로 운동하면서 지루함을 달래기 위해 아이패드로 본 영화에 대해 극히 
주관적으로 아무런 논리적 분석 없이 의식의 흐름에 따라 적어 내려간 초 간단 감상문임을
참고해주세요.



원제 : Bird Box
상영 : 2018년
장르 : 드라마, SF, 스릴러
감독 : 수전 비에르
출연 : 산드라 블록, 트래반즈 로즈, 존 말코비치
시청 : 넷플릭스
개인 평점 : ★★★★☆


사실 처음에는 그닥 땡기는 영화가 아니었다.
눈을 가리고 다녀야 한다는 상황이 조금 궁금하기는 했지만 ‘와~봐야겠다!’하는 느낌은 아니었다.
일단 나에게는 넷플릭스 오리지널 컨텐츠라는게 아직 신뢰감을 주지 못하는 상태였고 최근 운동으로 실내 자전거를
타면서 아무거나 봐댔더니 일종의 현타가 온 것인지… 한동안 무엇을 볼까 뒤적거리다가 딱히 땡기는 영화가 없어 선택한 
영화였다.


하지만 결과는 만족스러웠다.
전형적인 포스트 아포칼립스 장르로 어찌보면 상투적이라고 할 수 있는 스토리 전개가 이어진다.
갑작스러운 재앙, 혼돈과 공포, 극한 상황에서 벌어지는 이타와 이기의 갈등 그리고 가까운 이들의 죽음…


이 영화는 극 전체에서 차별 없는 세상에 대한 밑밥을 잘 버무려 놓고 있다.
고집불통의 늙은 백인 남자는 동양계 성 소수자를 좋은 이웃으로 생각하고 있으며, 주인공인 백인 여성은 흑인 남성과
연인 관계로 발전을 한다. 그리고 가장 중요한 결말… 은 스포가 되므로 생략^^


사실 재앙의 원인은 관객의 궁금증을 자아내기에는 충분하지만 납득할만한 존재는 아닌 것으로 보인다.
초자연적인 현상인 것인지, 어떤 영적인 존재인 것인지, 지능이 있는 존재인 것인지…
정작 더 중요한 존재들은 그 것을 바라볼 수 있는 사람들이다. 정신병원에 수용되었던 사람들…


어찌보면 그 사람들도 사회에 의해 병들고 사회로부터 버려진 차별받는 존재이기도 하지만 사실 그들의 행동은
‘정신병자’라기 보다는 자기가 보고싶은 것만을 보고 더 나아가 남들에게도 자기가 보는 것만을 강요하는 그런
무리들의 상징으로 보아야 할 것이다. 그들이야 말로 이 영화가 주는 중요한 메시지의 한 축이 아닌가 싶다.


SNS가 발전을 하면서 사람들은 자기와 뜻을 같이하는 사람의 무리에 지나치게 매몰된다는 분석도 있듯이 이 시대는
서로 다른 곳을 보는 사람들 사이의 전쟁터나 다름 없다. 중도는 양 극단의 공통의 적일 뿐이다. 오로지 자기가 믿고
보는 것만이 아름다우며 그 것을 이해하지 못하는 자들은 우매한 자들이고 사라져야 할 존재들인 것이다.



이 영화의 결말을 보고 드는 생각은 말하자면 콜럼부스의 달걀을 이해한 느낌이랄까?
결과를 알고나니 너무 당연한데 그 발상은 꽤나 신선한 듯한…사실 조금 뭉클한 느낌도 들었다.


이 혼란한 세상을 정화할 존재들은 누구일까?
영화를 보시라~

'문화' 카테고리의 다른 글

[자영이] 레지던트 이블5 : 최후의 심판  (0) 2019.03.23
[자영이] 버드박스  (0) 2019.03.23
무식쟁이 독서일기 ~ 15: 편의점 인간  (0) 2017.11.03
[자작 콩트] Cigarette Blues  (0) 2017.06.04
[영화] 미녀와 야수  (0) 2017.04.13
[영화] 레 미제라블  (0) 2017.02.26
블로그 이미지

마즈다

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



2019/01/13 - [Study/아두이노] - [아두이노] 미니 드론 만들기 #1


2019/01/21 - [Study/아두이노] - [아두이노] 미니 드론 만들기 #2


2019/03/03 - [Study/아두이노] - [아두이노] 미니 드론 만들기 #3


2019/03/11 - [Study/아두이노] - [아두이노] 미니 드론 만들기 #4(이번 글)


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


미니 드론 만들기의 첫 번째 포스팅이 1월 13일이었으니…벌써 시작한 지도 2달여가 되어간다.
하지만 아직도 하드웨어 조립에서 맴돌고 있다…ㅠ.ㅠ


지난 주에 포스팅한 SMD 타입의 nRF24L01 모듈 구입과 보드의 소형화로 하드웨어 작업은 금방 끝이 날 것이라고 
생각했으나 여전히 납땜 할 부분들이 많아 선뜻 손이 가지 않았다. 그리고 지난 주 조립한 아두이노 프로 미니를 중심으로
한 모듈의 조립 상태도 높이가 조금 높아 구입한 프레임에 장착하기가 썩 만족스럽지 않은 상황이었다.


어쨌든 이제 모터 드라이버와 외부전원부까지 납땜을 마치고 하드웨어쪽은 마무리를 지으려는 찰나. 새로운 부품이
눈에 들어왔다.


초소형 DC 모터 드라이버


사실 미니 드론을 만드는 틈틈이 드론 이후 만들 싱글콥터에 대해 이것 저것 아이디어를 짜내고 있던 터였다.
프레임을 만들 플라스틱 용기도 몇 개 준비했고 그동안 사다놓은 모터 스펙도 다시 한 번 확인하고 배터리도 무게와 
용량을 가늠해보고…그리고 싱글콥터에서 가장 부담스러운 부분인 방향 제어를 위한 4개의 블레이드를 작동시킬
서보모터와 관련하여 적당한 서보모터 드라이버를 찾고 있었다.


현재 가지고 있는 서보모터 드라이버는 16개의 서버모터를 컨트롤하는 놈이다보니 크기와 무게 여러모로 부담스러웠다.
딱 4개만 컨트롤 가능한 드라이버가 있으면 좋으련만…그러던 중 그나마 가장 작은 크기로 6개의 서보모터를 컨트롤 할
수 있는 서보 모터 드라이버를 찾았다. 그리고 그 드라이버를 찾은 곳에서…초소형 DC 모터 드라이버를 발견하였다.


일단 크기는 아래 사진으로 확인할 수 있다. 기존 드라이버(요 것도 그나마 작다고 해서 구매한 것이었다)와의 비교, 
그리고 100원짜리 동전과의 비교이다.



작은만큼 허용하는 외부 전압은 10V까지이고 1.5A까지 모터에 공급할 수 있다. 어차피 사용하는 모터가 8520 코어리스
모터로 이정도 스펙이면 충분히 구동시킬 수 있을 것 같았다. 또한 아두이도와의 연결도 3.3V와 5V 모두 가능하여 크게
신경쓰지 않고 사용할 수 있었다.


최종 조립


새로운 초소형 DC 모터 드라이버의 사용으로 전체적으로 하드웨어를 재배치 할 수 있게 되었다. 기존 모터 드라이버가
드론 프레임의 아랫면을 모두 차지하여 나머지 중요 부품들을 프레임 상단에 배치할 수밖에 없었는데 이제는 가능한한
정확하게 수평을 맞추기 위해 MPU-9250 9축 자이로 센서만 프레임 상단에 부착하고 나머지 부품들은 모두 프레임의
하단에 배치시켰다. 드론 프레임과 거의 일체화 되었다고 봐도 될 정도로 잘 들어맞았다.


다만 케이블로 이어 납땜을 하는 과정에서 전선들이 너무 어지럽게 배치되어 보기 안좋게 된 것이 못내 아쉬울 뿐…


어쨌든 더 미룰 수도 없고 해서 납땜을 해야 할 부분은 모두 해버렸다. 이렇게나 열심히 해놓았는데 만일 납땜의 문제로
뭔가 작동을 하지 않는다면…비뚤어지고 말테다…-.-!





총 5개의 보드를 붙인 것 치고는 꽤 슬림하게 잘 배치가 되었다.


삽질…


납땜을 하는 과정에서 무지하게 삽질을 했다. 당연하게도 SMD 타입의 nRF24L01 모듈이 문제였다.
지난 포스팅에서 언급한대로 이번에는 칼팁이 아닌 송곳 팁으로 남땜을 해보았다. 확실히 사용했던 팁 중에 가장
수월하게 작업이 되었다. 하지만 그럼에도 불구하고 SMD 타입의 nRF24L01 모듈같이 작은 영역은 땜질하기가 
너무나 어려웠다. 


첫 번째 시도에서는 기존 코드가 동작하지 않았다. 육안으로도 얼핏 납이 엉겨붙은 부분이 보였는데 아마도 이 때문이 
아닐까 싶었다.


그런데 어렵게 땜질한 것을 다 뜯어내고 새 모듈을 다시 땜질하여 붙였는데 이번에도 작동을 하지 않는 것이었다. 
이번에는 육안으로(고배율 루페로 확인) 확인했을 때에도 특별한 문제가 없어보였는데 작동을 하지 않았다.
이번 주말에 번거로운 작업을 모두 마무리하자는 일념으로 귀차니즘을 무릅쓰고 다시 다 뜯어내고 이번에는 기존의
nRF24L01 모듈을 연결시켜보았다. 그런데 이번에도 역시 동작을 하지 않았다. 그래서 혹시나 하고 송신부를 켠 후
다시 확인해보니 그제서야 신호가 잡혔다.


처음 시도에서는 송신부를 켰음에서 안되었던 것으로 봐서 확실히 문제가 있었던 것 같은데 두 번째 시도에서는
깜빠하고 송신부를 켜지 않은 채 그냥 잘 안된다고 판단하고 뜯어버리고 말았다. 제대로 테스트를 해보았더라면
아마도 일찍 마무리 지을 수 있었을지도…ㅠ.ㅠ


앞으로는 소프트웨어적인 테스트에서도 빠짐없이 모든 경우의 수를 다 테스트 해봐야겠다. 


정리


이렇게 해서 하드웨어 조립은 모두 마쳤다. 하지만 이게 정상적으로 작동을 할 지는 아직 미지수이다. 사실 간단하게 모터
구동 테스트를 위한 코드까지 넣어놓긴 했지만 잘 안될 것이 두려워 실제로 구동시켜 보지는 못했다. 배터리만 연결하면
바로 확인이 되겠지만…ㅠ.ㅠ


진행하다보니 이게 드론을 만들자는 것인지 소형화된 부품을 찾자는 것인지 모르게 되어버렸지만 어쨌든 이제 납땜은
모두 마쳤으니 다음 주말에는 모터의 장상 구동 여부 확인 후 다행히 잘 동작을 하면 본격적인 드론 동작을 구현하는 
단계로 나가고 그렇지 않다면….다시 조립해야 하나…ㅠ.ㅠ?


그저 프로펠러가 돌기를 바랄 뿐이다…

블로그 이미지

마즈다

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



2019/01/13 - [Study/아두이노] - [아두이노] 미니 드론 만들기 #1


2019/01/21 - [Study/아두이노] - [아두이노] 미니 드론 만들기 #2


2019/03/03 - [Study/아두이노] - [아두이노] 미니 드론 만들기 #3 (이번 글)


2019/03/11 - [Study/아두이노] - [아두이노] 미니 드론 만들기 #4





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

갈길은 먼데…내 주력 PC인 2012년 산 맥미니가 말썽을 부렸다…
워낙 맥에 아두이노 관련 중국산 칩들의 호환성이 떨어지는지라 아무래도 윈도우 PC에서 작업을 해야 할 것 같은데
집에 있는 윈도우 PC는 아이들이 주로 사용하다보니 내가 사용할 수 있는 시간이 별로 없었다.


결국 Parallels를 이용하여 맥미니에 윈도우를 설치하고 거기서 작업을 하려고 했는데…
왜??? 맥용 소프트웨어들은 메이저 버전이 올라가면 돈을 새로 받냔 말이다…ㅠ.ㅠ 돈 좀 아껴보려고 크랙버전을 찾아
설치를 좀 해볼까 시도하다가 결국 맥미니가 맛이 갔다. 딱 느낌이 쥬토피아의 나무늘보 같았다…-.-


그리고 Mac OSX 모하비를 클린 설치하기를 수차례…겨우 지난 주말에야 정상 복구 시킬 수 있었다.
하지만 2012년 맥미니의 성능으로는 아무리 메모리를 16Gb 장착을 해도 가상 환경에서 윈도우를 구동하는 것이
만만한 일은 아니었다. 아무래도 2018년 맥미니을 구해야 하나…ㅠ.ㅠ


각설하고 이런 이유로 한동안 드론에 손을 못댔고 그나마 작업한 부분도 블로그에 정리할 수가 없어서 이제야 겨우
조금이나마 진행한 내용을 올려본다.


개선


그동안 진행한 작업이라고는 부품들을 조금이나마 부피를 줄여 조립한 것이 다이다.
현재 사용할 보드들은 아두이노 프로 미니, MPU-9250 9축 자이로 센서, 그리고 통신을 위한 nRF24L01+ 이렇게
3개이다. 모두 크기가 그닥 크지 않은 모듈들이긴 하지만 지난 포스팅에서 정리한 작업처럼 핀헤더를 붙이고 케이블로
연결해서는 답이 안나온다는 것을 확인했다. 내가 구입한 드론 프레임에 올리기에는 높이가 너무 높아져버렸다.


결국 다시 처음의 계획대로 핀헤더 없이 각 보드들을 케이블로 직접 연결하기로 하고 추가로 nRF24L01 모듈이 더 작은
사이즈로 만들어진 것이 있기에 이놈을 추가로 구매해서 부피를 더 줄였다. 아래 사진을 보면 기존 모듈과의 크기를
비교할 수 있다. 거의 절반 가까이 크기가 줄었다.



새로 구입한 소형 nRF24L01 모듈은 부피가 작아진 것은 좋은데 연결 핀이 SMD 타입으로 되어있어(소형화의 당연한
결과이겠지만…) 과연 제대로 땜질을 할 수 있을지 매우 불안했다. 일단은 땜질 하기 전에 어떻게 연결해야 하는지 확인을
해보자. 첫 번째 사진은 기존 모듈의 핀맵이고 두 번째 사진은 새로 구입한 모듈의 핀맵이다.




핀 맵을 확인한 후 인두기를 꺼냈다. 검색을 해보니 작은 부분을 땜질할 때는 칼팁으로 하는 것이 좋다는 말에 처음으로
칼팁을 사용해 보았다. 나는 현재 인두기가 3개가 있는데 제일 처음 샀던 저가형 자야 인두기…사실 이놈으로도 땜질을
잘만 했는데 어느날 땜납이 잘 녹지 않는 것이었다. 싸구려라 맛이 갔구나…생각했는데…선무당이 사람 잡는다고…나중에
보니 당시 사용한 납이 무연납이었던 것이다…ㅠ.ㅠ 어쨌든 당시에는 인두기가 맛이 갔다고 생각해서 다시 그 유명한 하코
인두기 중 가장 저렴한 980 이두기를 새로 구입했다. 이 때 추가로 하코 정품은 아니고 호환 제품으로 칼팁을 하나 더
구했다. 그러다가 다시 banggood.com 검색하다가 싼맛에 인두기를 추가로 하나 질렀는데 여기에는 팁이 5개 정도가
기본 포함되어있었다. 아래 사진의 위에서부터 중국산 인두기, 하코 980, 자야 인두기 이다.



아무튼…그동안 하코 인두기로 잘 썼었는데 팁 갈아 끼워 가면서 쓰기가 귀찮아서 중국산 인두기에 칼팁을 끼워 사용해
보았다. 아니나 다를까…노안과 수전증의 합작으로 막 옆에 핀과 같이 붙여주기도 하고, 가끔은 납이 에베레스트만큼 솟아
오르기도 하고, 물론 아주 가끔은 뿌듯할만큼 잘 붙기도 하고…암튼 솔더위크 엄청 써가면서 겨우겨우 땜질을 마쳤다.
아무래도 땜린이에게는 칼팁도 어려운 것 같고 다음에는 중국산 인두기에 딸려 온 송곳 팁을 써봐야겠다.


언제 어디서 단선이 되거나 쇼트가 날지 모를 아슬아슬한 상태로 땜질을 마치고 나니 제법 아담한 사이즈로 완성이 
되었다. 특히나 새로 구한 nRF24L01 SMD 모듈이 아두이노 프로 미니에 포~옥 안기는 형태가 되어 보기가 참 좋았다^^.
추가로 nRF24L01모듈에 컨덴서(커패시터)를 붙여주면 더 안정적으로 통신을 할 수 있다고 하기에 붙여주었다.






작동 테스트


지난 포스팅까지 진행한 작업에서는 MPU-9250 9축 자이로 센서 동작만 확인을 했다. 그 후 nRF24L01 모듈의
통신을 테스트 해보았는데…이게 뭔 조화인지 전혀 통신이 되지 않는 것이다. 분명 코드는 맞게 작성을 했고 
시리얼 모니터를 보면 nRF24L01 모듈도 정상적으로 연결이 되었는데 조이스틱쪽과 드론쪽의 통신이 되지 않았다.


그러다가 예전에 싱글콥터 만들 때 테스트 하던 아두이노 나노에 연결해서 하니 정상적으로 통신이 되는 것이 아닌가…
그리고 다시 아두이노 프로 미니에 연결을 했더니 그 뒤로 통신이 아주 잘된다…-.- 손톱만한 모듈들에 농락당하는 
삶이라니…ㅠ.ㅠ



일단 통신은 되었지만 수정할 부분이 좀 있다. 조이스틱쪽 코드가 예전에 싱글콥터 개발 시 작성된 코드라 지금 만드는
쿼드콥터에 맞게 수정을 해야 하고 드론쪽도 코딩을 시작해야 하고…일단은 간단하게 코드의 중요 부분만 올려본다.


정리


별로 한 것도 없는데 컴퓨터가 맛이 가는 바람에 시간을 많이 잡아먹었다.
그래도 어영부영 한 1/3은 온 것 같고 이제 진짜로 중요한 코딩과 드론의 최종 조립만 남았다. 이제 컴퓨터도 복구가 
되었으니 조금 더 서둘러서 진행을 해야겠다.

그나저나 모듈들을 이렇게 소형화 하고 나니 다시 싱글콥터에 대한 욕심이 스멀스멀 밀려온다. 이번 미니 드론 작업을
마치면 바로 이어서 싱글콥터 작업을 시작해봐야겠다.

블로그 이미지

마즈다

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



2018/11/25 - [Study/인공지능학습] - [머신러닝 reboot] 개념 잡기 : 경사 하강법 1 - 특성의 scale


2018/12/10 - [Study/인공지능학습] - [머신러닝 reboot] 개념 잡기 : 경사 하강법 2 - step 공식 이해하기


머신러닝 Reboot - 개념 잡기 : 경사 하강법 3 - 경사 하강법의 종류


지난 포스팅에서 경사 하강법의 특성 스케일과 스텝에 대해서 알아보았다.
여전히 이해도가 낮은데다가 너무 세세한 부분까지 이해하려고 하다보니 진도가 잘 안나간다.
그렇다고 뭔가를 확실히 알게된 것도 아니고…ㅠ.ㅠ 그저 ‘아~ 그래서 이럴 것 같다…’라는 느낌적인 느낌을
갖게 된 것 정도가 소득이라면 소득일까…


아직은 계란으로 바위치는 기분이지만 그래도 학습의 끈을 놓을 수는 없다.
그래서 오늘도 정리는 계속된다~


오늘은 경사 하강법의 종류에 대해 정리해보도록 하자.


이 포스팅에 사용된 코드는 모두 핸즈온 머신러닝(오렐리앙 제롱 저, 박해선 역, 한빛미디어 출판)에서
가져왔거나 일부 수정한 코드입니다.



경사 하강법 복습


경사 하강법이란 비용 함수를 최소화 할 수 있는 가중치(𝜭)를 찾아가는 과정으로 그 방법은 비용 함수를 가중치(𝜭)에 
대해 미분하고 이 결과가 작아지는 방향으로 반복해서 진행을 하는 것이다. 이 때 매 반복이 각 단계 사이에 얼마만큼의 
거리를 둘 것인지 하는 학습률이 매우 중요한데 학습률이 너무 높으면 중구난방 발산을 하게 되고 반대로 너무
낮으면 시간이 오래 걸려 최솟값에 도달하지 못할 수 있다.


학습률 적절학습률 적절학습률 낮음학습률 낮음학습률 높음학습률 높음


경사 하강법은 매우 일반적이고 단순한 알고리즘이지만 함수의 그래프가 단 한개의 오목한 점을 가질 때(convex)만
잘 작동을 한다. 함수의 그래프에 오복한 부분이 많은 경우(non-convex) 초기화가 어떻게 되느냐에 따라 지역 
최솟값에서 멈출 수 있다. 



또한 지난 포스팅에서 본 것처럼 특성의 스케일에 민감하여 2개 이상의 특성이 있을 때 각 
특성간의 스케일이 크게 차이가 나면 최솟값에 도달하는데 오랜 시간이 걸리게 된다.


참고 : 특성의 스케일 (https://mazdah.tistory.com/833)


선형 회귀의 경우 비용함수의 그래프는 전역 최솟값 하나만 존재하는 그래프이기 때문에 경사 하강법은 좋은 방법이
될 수 있으며 특성의 스케일만 적절하게 조정을 해주면 될 것이다. 


이러한 경사 하강법은 훈련세트의 범위를 어떻게 지정하여 계산하느냐에 따라 배치 경사 하강법, 확률적 경사 하강법,
미니 배치 경사 하강법으로 나누어 볼 수 있다. 


배치 경사 하강법


배치 경사 하강법은 매 스텝에서 전체 훈련 데이터를 사용하여 계산을 한다. 따라서 매우 큰 훈련 세트에서는 성능이 
떨어지게 된다. 


배치 경사 하강법에 대해서는 이전 포스팅에서 상세하게 다루었으므로 아래 링크를 참조하기 바란다.


참고 : 배치 경사 하강법의 STEP (http://mazdah.tistory.com/834)


아래는 핸즈온 머신러닝(이하 책)에 실린 예제 코드이다. 그래프 그리는 부분을 약간 수정했다. 라이브러리 import와
데이터 생성 부분은 책을 참조하시기 바란다.


# 다른 경사 하강법과의 비교를 위해 theta값을 저장할 배열 theta_path_bgd = [] eta = 0.1 # 학습률 n_iterations = 1000 # 반복 횟수 m = 100 # 샘플 수 theta = np.random.randn(2, 1) # theta 초기화 for iteration in range(n_iterations): gradient = 2/m * X_b.T.dot(X_b.dot(theta) - y) theta = theta - eta * gradient theta_path_bgd.append(theta) # 처음 10번의 step에 대한 그래프와 마지막 step의 그래프를 그리기 위한 코드 if iteration <= 10 or iteration == 999: X_new = np.array([[0], [2]]) X_new_b = np.c_[np.ones((2, 1)), X_new] y_predict = X_new_b.dot(theta) y_predict style = "g--" if iteration == 999: style = "r-" plt.plot(X_new, y_predict, style) plt.plot(X, y, "b.") plt.axis([0,2,0,15]) plt.show() theta ########################################################## # API 설명 # ########################################################## # np.ones((2,1))는 파라미터로 전달된 shape의 배열을 1로 채운다. (기본 데이터 타입은 실수로 1.으로 채워진다.) # np.c_는 파라미터로 전달된 2개의 배열에 대해 동일행의 요소를 배열로 합친 (첫 번째 파라미터의 요소는 0열, 두 번째 파라미터의 요소는 1열) 새로운 배열을 만든다. # 예) # >>> np.c_[[np.array([1,2,3]), np.array([4,5,6])]] # array([[1, 4], # [2, 5], # [3, 6]]) # >>> np.c_[np.array([[1,2,3]]), 0, 0, np.array([[4,5,6]])] # array([[1, 2, 3, 0, 0, 4, 5, 6]])


책에는 처음 10번의 예측 그래프만을 그리고 있으나 배치 경사 하강법 마지막에 설명한 반복 횟수에 대한 설명을 확인
해보고자 마지박 반복 그래프를 하나 더 추가하였다. 처음 10번의 그래프는 초록색 대시로, 마지막 그래프는 빨간 실선
으로 표시했다.


배치 경사 하강법배치 경사 하강법


그래프를 보면 알 수 있듯이 10번째 step의 그래프와 마지막 step의 그래프 사이에는 큰 차이가 없다. 결국 990번은
큰 의미가 없는 step이었다는 뜻인데, 이는 책에서 언급한 바와 같이 반복 횟수의 지정 또한 중요하다는 것을 의미한다.


확률적 경사 하강법


확률적 경사 하강법은 배치 경사 하강법이 훈련 세트가 커짐에 따라 느려지는 단점을 보완한다.


확률적 경사 하강법은 매 스텝에서 무작위로 선택한 한 개의 샘플에 대해서만 그래디언트를 계산하므로 속도가 빠를 뿐만
아니라 메모리 사용에 있어서도 효율이 좋아 매우 큰 훈련 세트도 적용 가능하다. 또한 불규칙성이 지역 최솟값을 건너
뛰게 하여 전역 최솟값을 찾을 가능성을 높여주는 것도 장점으로 볼 수 있다.


다만 최적값에 도달하는 과정이 배치 경사 하강법에 비해 불안정한 것이 단점이다. 또한 최적값에 근접은 하겠지만
말 그대로의 최적값은 얻기 힘들다. 이러한 단점을 해결하는 방법으로 학습률을 조정하는 방법이 있다. 처음에는 학습률을
크게 했다가 점차 학습률을 줄여나가는 방법으로 이 때 학습률을 조정하는 함수를 학습 스케쥴(learning schedule)이라
부른다.


이렇게 확률적 경사 하강법에서는 학습률자체의 크고 작음 뿐만 아니라 학습률을 줄여나가는 속도 역시 중요한 하이퍼 
파라미터가 된다. 너무 빨리 줄여나가면 최솟값에 도달하지 못하고 너무 천천히 줄여나가면 시간이 오래 걸리거나 지역
최솟값에 머물 수 있다.


또다른 특징(단점)으로는 훈련세트를 한 번 도는 동안(1 epoch) 여러번 선택되는 샘플과 한 번도 선택되지 않는 샘플이 
존재할 수 있는데 이를 방지하기 위해 매 epoch에서 훈련 세트를 섞는 작업을 추가할 수 있다. 다만 이렇게 하면 속도가
느려지게 된다.


# 다른 경사 하강법과의 비교를 위해 theta값을 저장할 배열 theta_path_sgd = [] # 확률적 경사 하강법 n_epochs = 50 t0, t1 = 5, 50 # 학습 스케쥴 하이퍼파라미터 # 학습률을 감소시키는 학습 스케쥴 함수 def learning_schedule(t): return t0 / (t + t1) theta = np.random.randn(2,1) for epoch in range(n_epochs): # epoch : 훈련세트 전체가 한 차례 반복 되는 단위. 50번 반복될 동안 전체 샘플 m만큼 # 반복되므로 50 epoch를 실행한다. for i in range(m): # 첫 번째 epoch에서 처음 20개의 샘플에 대한 그래프와 최종 그래프를 그림. if (epoch == 0 and i < 20) or (epoch == 49 and i == 99): y_predict = X_new_b.dot(theta) # 처음 그래프는 초록색 대시로, 나머지 그래프는 파란색 실선으로, 마지막 그래프는 # 빨간색 실선으로 표시한다. style = "" if epoch == 0 and i > 0: style = "b-" elif epoch == 0 and i == 0: style = "g--" elif epoch == 49 and i == 99: style = "r-" plt.plot(X_new, y_predict, style) # 0부터 99(m은 100개) 중에 하나를 랜덤하게 가져옴 random_index = np.random.randint(m) # 무작위로 선택된 인덱스를 확인해보자. 특정 인덱스는 중복해서 사용되고 또 다른 인덱스는 # 사용되지 않는다. if epoch == 0: print("random_index = ", random_index) # 훈련세트와 실제값의 배열에서 랜덤하게 가져온 인덱스 위치의 값 1개씩만 가져온 후 # gradient를 계산한다. xi = X_b[random_index:random_index+1] yi = y[random_index:random_index+1] gradients = 2 * xi.T.dot(xi.dot(theta) - yi) # 배치 경사 하강법과 달리 학습률을 계속해서 감소시킨다. eta = learning_schedule(epoch * m + i) # print("eta : ", eta) theta = theta - eta * gradients theta_path_sgd.append(theta) plt.plot(X, y, "b.") plt.xlabel("$x_1$", fontsize=18) plt.ylabel("$y$", rotation=0, fontsize=18) plt.axis([0,2,0,15]) plt.show() ########################################################## # API 설명 # ########################################################## # # Numpy random # # - randn(d0, d1, ..., dn) : 표준 정규분포를 따르는 무작위 실수를 파라미터로 전달받 차수의 배열에 # 채워 리턴한다. 파라미터가 없는 경우 한 개의 실수만 리턴한다. # 예) # >>> theta = np.random.randn(2,1) # >>> theta # array([[ 0.44730268], # [-0.04431121]]) # # - randint(low, high=None, size=None, dtype='l') : 무작위 정수를 리턴한다. low 파라미터는 # 필수로 low 파라미터만 있는 경우에는 0부터 low-1까지의 범위에서 무작위 정수가 리턴되며 # low와 high가 전달되는 경우 low부터 high-1까지의 범위에서 무작위 정수가 리턴된다. # size가 주어질 경우 size에 해당하는 배열 형태로 리턴된다. # 예) # >>> rival = np.random.randint(2) # >>> rival # 1 (0또는 1이 무작위로 출력됨) # >>> rival = np.random.randint(2, 5) # >>> rival # 1 (2 ~ 4 사이의 정수가 무작위로 출력됨) # >>> rival = np.random.randint(2,5, (2,3)) # >>> rival # array([[3, 3, 4], (2 ~ 4 사이의 정수가 2행 3열의 배열에 무작위로 할당되어 출력됨) # [3, 2, 3]])

확률적 경사 하강법


동일한 내용을 Scikt Learn으로는 다음과 같이 구현 가능하다.


from sklearn.linear_model import SGDRegressor # epoch는 50, 초기 학습률은 0.1로 학습한다. sgd_reg = SGDRegressor(max_iter=50, penalty=None, eta0=0.1) sgd_reg.fit(X, y.ravel()) # 결과값 보기 : intercept_ = 편향, coef_ = 가중치 sgd_reg.intercept_, sgd_reg.coef_ ########################################################## # API 설명 # ########################################################## # # Scikit-learn # # - SGDRegressor (loss=’squared_loss’, penalty=’l2’, alpha=0.0001, l1_ratio=0.15, # fit_intercept=True, max_iter=None, tol=None, shuffle=True, # verbose=0, epsilon=0.1, random_state=None, # learning_rate=’invscaling’, eta0=0.01, power_t=0.25, # warm_start=False, average=False, n_iter=None) # . 확률적 경사 하강법을 수행하는 클래스로 linear_model 모듈에 포함되어있다. # . 파라미터 (상당히 많은 파라미터가 있는데 예제에 명시한 파라미터만 간단히 알아보자) # : max_iter - 전체 훈련 데이터의 반복 횟수(epoch 수)를 지정한다. # : penalty - 정규화 식. 여기서는 사용하지 않음. l2, l1, elasticnet 등을 사용할 수 # 있다. # : eta0 - 학습률의 초깃값. API상에 learning_rate라는 파라미터는 학습 스케쥴을 뜻한다. # 기본값인 ‘invscaling’는 eta = eta0 / pow(t, power_t) 공식에 따라 # 학습률을 조정한다. (자세한 내용은 핸즈온 머신러닝 171쪽 하단의 역주 참조)



미니배치 경사 하강법


미니배치 경사 하강법은 한 epoch에서 미니배치라는 임의의 샘플 세트만으로 계산을 진행하는 것으로 확률적 경사 
하강법이 1개의 샘플만 사용하여 계산하는 것과 비교된다. 한편으로는 확률적 경사 하강법은 미니배치의 크기가 1인
미니배치 경사 하강법으로 볼 수도 있다.


이러한 미니배치 경사 하강법은 확률적 경사 하강법에 비해 안정적으로 최솟값에 접근하지만 지역 최솟값을 벗어나기는
더 힘들다.


장점으로는 행렬 연산에 최적화 되어있으며 GPU를 통해 큰 성능 향상을 얻을 수 있다.


# 다른 경사 하강법과의 비교를 위해 theta값을 저장할 배열 theta_path_mgd = [] n_iterations = 50 # 미니배치 크기를 20으로 주었다. # 이 크기를 1로 주면 확률적 경사 하강법과 동일한 형태의 그래프를 볼 수 있다. minibatch_size =20 np.random.seed(42) theta = np.random.randn(2,1) # 무작위 초기화 t0, t1 = 200, 1000 def learning_schedule(t): return t0 / (t + t1) t = 0 for epoch in range(n_iterations): # 인자로 전달된 벡터 또는 행렬 요소의 순서를 무작위로 바꿈. # 행렬의 경우 첫 번째 인덱스의 순서만 바꾼다. # 매 epoch마다 순서를 뒤섞어 훈련 세트를 고르게 사용하도록 한다. shuffled_indices = np.random.permutation(m) X_b_shuffled = X_b[shuffled_indices] y_shuffled = y[shuffled_indices] # minibatch_size만큼 샘플을 뽑아 계산하므로 for문에서도 minibatch_size만큼씩 # 증가시킨다. for i in range(0, m, minibatch_size): #print(epoch , " : ", i) # 그래프 그리기. minibatch_size가 20이므로 한 epoch에서 5번만에 연산이 끝난다. # 따라서 2 epoch가 진행될 동안 총 10개의 그래프가 그려진다. if epoch < 2 and i < 100: y_predict = X_new_b.dot(theta) style = "b-" if i > 0 else "r--" plt.plot(X_new, y_predict, style) t += 1 # 매 epoch마다 뒤섞은 훈련 세트에서 minibatch_size만큼의 샘플만을 가져와서 # gradient를 계산한다. xi = X_b_shuffled[i:i+minibatch_size] yi = y_shuffled[i:i+minibatch_size] gradients = 2/minibatch_size * xi.T.dot(xi.dot(theta) - yi) eta = learning_schedule(t) theta = theta - eta * gradients theta_path_mgd.append(theta) plt.plot(X, y, "b.") plt.xlabel("$x_1$", fontsize=18) plt.ylabel("$y$", rotation=0, fontsize=18) plt.axis([0,2,0,15]) plt.show() ########################################################## # API 설명 # ########################################################## # # Numpy random # # - permutation(x) : 파라미터로 주어진 배열의 순서를 무작위로 바꾸어 출력한다. # 예) # >>> x = np.random.permutation([1,2,3,4,5,6,7,8,9,10]) # >>> x # array([ 7, 2, 1, 4, 3, 8, 9, 5, 10, 6])



미니배치 크기 20미니배치 크기 20미니배치 크기 1미니배치 크기 1


위의 그래프를 보면 확률적 경사 하강법의 그래프보다 미니배치 경사 하강법에서 미니배치를 1로 준 그래프가 좀 더
발산이 심한데 이는 아마도 하이퍼파라미터가 달라서 발생하는 현상일 것이다.


비교


마지막으로 3개의 경사하강법을 실행하면서 저장한 theta값의 배열을 기반으로 그래프를 그려보면 아래와 같다.



배치 경사 하강법이 곧장 최솟값으로 향한 반면 확률적 경사 하강법과 미니배치 경사 하강법은 최솟값 부근에서 매우
불안정하게 움직인다. 하지만 앞서 살펴본대로 배치 경사 하강법은 샘플이 많아질수록 느려지고 확률적 경사 하강법과
미니배치 경사 하강법도 적절한 하이퍼파라미터를 사용하면 결국은 최솟값에 도달한다.


마지막으로 각 경사 하강법의 특징을 정리하면 다음과 같다.


배치 경사 하강법 : 샘플 수가 클 때 느림, 특성 수에 상관없이 빠름, 특성 스케일 조정 필요

확률적 경사 하강법 : 샘플 수에 상관없이 빠름, 특성 수에 상관없이 빠름, 특성 스케일 조정 필요

미니배치 경사 하강법 : 샘플 수에 상관없이 빠름, 특성 수에 상관없이 빠름, 특성 스케일 조정 필요


더 상세한 비교 내용은 핸즈온 머신러닝 172쪽을 참고하라


다항 회귀


다항회귀는 데이터가 비선형인 경우(2차 이상의 함수 형태를 띠는 경우) 사용할 수 있는 회귀 분석이다.
다항회귀의 경우 직관적으로 접근하는 것이 좀 더 이해하기가 쉬운데 데이터의 그래프를 보고 적절한 차수를
제곱한 특성을 추가하여 분석하는 것이다.


비선형의 데이터를 선형 회귀 분석으로 계산할 수 있는 이유는 선형의 의미가 가설함수의 독립변수에 대한 것이 아니라
가중치 𝜭에 대한 것이기 때문이다. 즉 𝜭에 대한 1차 식(선형 식)으로 풀어낼 수 있다는 의미인 것이다.


이렇게 봤을 때 결과론적이긴 하지만 아래 코드를 보면 결국 이 예제의 가설함수는 위에 언급한 것과 같이 𝜭를 기준으로
본다면 특성이 2개인 함수일 뿐이다. 다만 기존에는 서로 독립적인 특성들, 즉 x1, x2, x3…xn에 대해 다뤘다면 다항
회귀에서는 x, x^2, x^3…x^n과 같이 하나의 특성 x에 대해 그 거듭제곱들이 특성이 되는 점이 다르다고 할 수있다.
결국 다항회귀는 다중회귀의 특수한 형태라고 볼 수 있는 것이다.


다만 우리가 훈련세트를 받았을 때 특성은 오직 x 하나만 주어지기 때문에 추가적인 특성(x의 거듭제곱)을 어떻게
처리할 것인지에 대해서는 선택이 쉽지 않은데 처음 이야기한 바와 같이 직관에 의존해서 결정을 할 수도 있겠지만
너무 차수가 높은 특성이 추가되는 경우 과대적합에 빠지기 쉬운 문제가 있다. 때문에 보통은 교차 검증을 사용하거나
학습 곡선을 살펴 결정하게 된다.


import numpy as np import numpy.random as rnd # 맷플롯립 설정 %matplotlib inline import matplotlib import matplotlib.pyplot as plt from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # 각각 차수를 300, 2, 1로 하여 그래프를 그려본다. 튜플의 앞 두 개의 요소는 그래프 스타일이다. for style, width, degree in (("g-", 1, 300), ("b--", 2, 2), ("r-+", 2, 1)): # 특성을 추가한다. 300차, 2차, 1차를 적용해본다. polybig_features = PolynomialFeatures(degree=degree, include_bias=False) # 표준화 인스턴스를 만든다. std_scaler = StandardScaler() # 추정기로 선형 회귀를 사용한다. lin_reg = LinearRegression() # 2개의 변환기와 1개의 추정기로 구성된 Pipeline을 만든다. polynomial_regression = Pipeline([ ("poly_features", polybig_features), ("std_scaler", std_scaler), ("lin_reg", lin_reg), ]) # Pipeline 실행 polynomial_regression.fit(X, y) y_newbig = polynomial_regression.predict(X_new) plt.plot(X_new, y_newbig, style, label=str(degree), linewidth=width) plt.plot(X, y, "b.", linewidth=3) plt.legend(loc="upper left") plt.xlabel("$x_1$", fontsize=18) plt.ylabel("$y$", rotation=0, fontsize=18) plt.axis([-3, 3, 0, 10]) #save_fig("high_degree_polynomials_plot") plt.show() ########################################################## # API 설명 # ########################################################## # # Scikit-learn # # - LinearRegression(fit_intercept=True, normalize=False, copy_X=True, n_jobs=1) # . linear_model 모듈에 있는 클래스로 최소제곱법을 통한 선형회귀를 수행한다. # . 파라미터 (모든 파라미터는 기본 값이 있는 optional이다.) # : fit_intercept - 연산에 편향(bias)를 포함 시킬 것인지를 결정한다. # : normalize - 특성을 정규화 할 지 여부를 설정한다. fit_intercept가 False이면 # 무시된다. # : copy_X - True면 특성을 복사하고 False면 특성을 덮어씌운다. # : n_jobs - 수행할 job의 수로 사용할 CPU의 수라고 생각하면 된다. -1이면 모든 CPU를 # 사용한다. # # - PolynomialFeatures(degree=2, interaction_only=False, include_bias=True) # . 파라미터로 설정된 차수(degree)와 같거나 작은 차수의 모든 다항식 조합을 특성에 추가한다. # 차수가 2이고 전달된 특성이 [2, 3]라면 [2, 3, 2^2, 2 * 3, 3^2]이 생성된다. # 이 때 사용되는 특성은 2D 이상의 배열이어야 한다. 즉 [[2, 3]] 형태여야 한다. # . 파라미터(모든 파라미터는 기본 값이 있는 optional이다.) # : degree - 다항식의 차수 # : interaction_only - True로 설정되면 제곱수가 포함된 수는 모두 빠지고 원래의 특성 # 값들과 특성들의 곱만 포함된다. 즉 [[2,3]]에 대해 [2, 3, 6]이 # 출력된다. # : include_bias - 편향을 포함할 것인지를 설정한다. # # - Pipeline(steps, memory=None) # . list 타입으로 전달된 첫 번째 파라미터의 요소들을 순차적으로 실행한다. 이 때 앞 단계의 결과가 # 다음 다계의 입력으로 들어간다. 이 파라미터에는 반드시 변환기가 있어야 하며 변환기는 fit() # 함수와 transform() 함수가 구현되어 있어야 한다. Pipeline의 마지막에는 추정기가 실행되며 # 추정기는 fit() 함수만 구현되어 있으면 된다. # * 추정기와 변환기에 대해서는 핸즈온 머신러닝 101쪽의 "사이킷런의 설계 철학"을 참고하라. # ** Pipeline에 대해서는 핸즈온 머신러닝 108쪽 "2.5.5 변환 파이프라인"을 참조하라 # . 파라미터 # : steps - list 타입이어야 하며 변환기와 추정기가 포함되어있어야 한다. 각 요소는 # 이름/추정기(변환기) 쌍으로 되어있으며 마지막 단계는 추정기나 변환기를 모두 # 사용할 수 있지만 이전 단계는 모두 변환기여야 한다. # : memory - 변환기의 캐시 사용 여부를 결정한다. # # - StandardScaler(copy=True, with_mean=True, with_std=True) # . 특성의 평균을 뺀 후 표준편차로 나누어 표준화 해주는 클래스. 평균이 0 표준편차가 1인 # 정규분포로 표준화 한다. # * 자세한 내용은 핸즈온 머신러닝 107쪽 "2.5.4 특성 스케일링" 하단의 표준화를 참조하라. # . 파라미터 # : copy - 복사본 사용 여부를 결정한다. # : with_mean - 스케일링 하기 전에 데이터의 중간을 맞춰준다(의미를 잘 모르겠음...ㅠ.ㅠ). # : with_std - 분산 혹은 표준편차에 맞게 스케일링한다(역시 명확한 의미를 모르겠음...ㅠ.ㅠ).


다항회귀다항회귀


위 그래프를 보면 데이터의 형태가 아래로 오목한 2차 함수의 형태와 유사하다(물론 책의 전개상 데이터를 생성한 
함수가 2차 함수에 노이즈를 추가한 것임을 알고 있으나 실제로는 이 함수를 찾아내는 것이 머신러닝의 역할이다).
따라서 제공된 특성을 제곱한 값으로 특성을 추가한 예측 그래프가 가장 적절해 보인다. 차수를 300으로 올리면
보다 많은 데이터와 일치하게 되지만 너무 훈련 데이터에 과대적합된 형태라 볼 수 있다.


일반적으로 과소적합인 경우에는 더 복잡한 모델을 사용해야 하며, 과대적합인 경우에는 더 많은 훈련 샘플을 추가해야
한다.


정리


가장 기초단계라고 할 수 있는 선형 회귀 이지만 꾸역꾸역 깊이 파고들다보니 역시 수학적인 요소들로 가득 차있다.
현 상황에서야 굳이 각 API들의 세부적인 부분들까지 알 필요 없이 기본적인 사용법만으로 충분하겠으나 실무에
적용하기 위해서는 분명 파라미터들의 의미를 이해하고 자유자재로 사용할 수 있어야 할 것이다.


다만 Scikit Learn의 경우 국내에서는 텐서플로우나 케라스에 밀려 인기가 없는 탓인지 한글로 된 자료 찾기가 쉽지
않았다. 우선은 공식 문서를 이용해 공부를 해야겠으나 우리말로도 못알아듣는 수학적 내용들을 영어로 어찌 이해할 수 
있을지…ㅠ.ㅠ 일단 Scikit Learn의 공식 문서를 링크한다.


https://scikit-learn.org/0.19/_downloads/scikit-learn-docs.pdf


선형회귀의 종류에 대해서는 이정도로 정리하고 다음 포스팅에서는 규제가 있는 선형 모델에 대해 간단하게 알아보도록

하겠다.

블로그 이미지

마즈다

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