메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

한빛랩스 - 지식에 가능성을 머지하다 / 강의 콘텐츠 무료로 수강하시고 피드백을 남겨주세요. ▶︎

IT/모바일

에릭 하게만 시리즈 1 - 수치처리 파이썬의 기초

한빛미디어

|

2002-05-02

|

by HANBIT

16,950

저자: 에릭 하게만(Eric Hagemann), 역 전순재

파이썬은 그 자체로도 수학에 강력한 힘을 가진 도구이다. 수치처리 파이썬과 데이터 시각화 도구인 DISLIN, 이 두 가지를 추가하여 확장하면 파이썬은 수치처리 계산과 문제 해결에 관한 한 최강자(powerhouse)가 된다. 수치처리 파이썬(NumPy, Numerical Python)은 파이썬 언어를 확장하여 행렬을 다루며 선형 대수학에 연관된 수학을 지원한다. 파이썬과 같이 NumPy도 협동 노력의 결과이다. 최초로 이 코드를 작성한 사람은 MIT에 다니는 짐 허그닌(Jim Hugunin)이다. 이 그 프로젝트는 결국 Lawrence Livermore National Labs사의 폴 두보아(Paul Dubois)를 비롯한 몇몇 주요 지원자(콘라드 힌센(Konrad Hinsen)과 트레비스 올리펀트(Travis Oliphant))가 맡았다.

수치처리 파이썬 설치하기

수치처리 파이썬을 사용하려면 먼저 이 프로그램을 설치해야 한다. 지시사항 및 다운로드 위치를 알려주는 자세한 사항은 여기를 클릭할 것!
NumPy와 파이썬이 잘 결합하기는 하지만 파이썬의 능력이 과학계에 완전하게 기여하려면 데이터 시각화 패키지가 있어야 한다. 데이터 시각화를 위한 최고의 선택은 DISLIN 패키지이다. 이 패키지는 Max-Planck Institute사의 헬무트 미쉘(Helmut Michels)이 무료로 파이썬 공동체에 제공한다. DISLIN는 광범위한 크로스 플랫폼(Win32/Linux/Unix) 라이브러리로서 파이썬을 비롯하여 많은 언어들로 접근할 수 있다. DISLIN은 2D와 3D 플로팅(plotting) 및 기본적인 그리기 명령어를 지원하기 때문에 유용하면서도 매력적인 그래픽을 만들 수 있다.

앞으로 필자는 실제 애플리케이션에서 NumPy와 DISLIN를 사용하는 법을 보여 줄 생각이다. 우선 선형 대수학과 행렬의 유용하고 흥미로운 세계를 검토하는 것부터 시작하자. 마치 고등학교 시절의 수학으로 되돌아간 느낌을 가질 수도 있겠지만 나를 따라오라. 재미있을 것이다.

행렬과 행렬 수학에 대한 개론

행렬(matrix)은 동질적인 숫자 집단으로 같은 형의 숫자들로 구성된 테이블처럼 특별한 모양을 하고 있다. 가장 흔하게 볼 수 있는 행렬은 일차원 또는 이차원 행렬이다. 일-차원 행렬을 벡터(vectors)라고 부르기도 한다. 아래에 보이는 A, b 그리고 C는 행렬을 보여주는 예이다. 여기서 ab를 벡터라고 부를 수도 있다.

ab는 모두 같은 요소들을 담고 있지만 그 꼴이 서로 다르다. 행렬의 꼴은 행과 열의 개수에 의해서 기술된다. a 행렬은 열 벡터(column vector) 또는 3ⅹ1 행렬로 기술될 수 있다. b 행렬은 행 벡터(row-vector) 또는 1ⅹ3 행렬로 기술될 수 있다. C 행렬은 3ⅹ3 행렬의 예이다.

덧셈과 뺄셈

약간만 고생하면(여러분도 예상하듯이) 원소 대 원소로 행렬끼리 뺄 수 있다. 덧셈과 뺄셈을 할 행렬의 꼴은 반드시 같아야 한다. 따라서 다음 연산은 유효하다.

그러나 다음은 유효하지 않다.

행렬 곱셈

행렬 곱셈(Matrix multiplication)은 더욱 복잡한 연산이다. 행렬 덧셈/뺄셈을 할 때에는 그 꼴이 반드시 같아야 하지만 곱셈을 할 때에는 오직 내항만 같으면 된다. 첫번째 행렬의 열이 두 번째 행렬의 행과 같아야 한다. 결과로 나온 행렬은 남아있는 외항의 크기와 같을 것이다. 일례로, 1ⅹ3 행렬과 3ⅹ1 행렬을 곱하면 1ⅹ1 행렬이 결과가 된다(여기서 3은 내항). 만약 5ⅹ2 행렬과 2ⅹ5 행렬을 곱하면 결과는 5ⅹ5 행렬이 된다.

공 식

관리공식은 다음과 같다

이 공식은 행렬 a의 행과 행렬 b의 열을 곱해서 행렬 c를 어떻게 생성할 수 있는지를 보여준다. 새로 만들어지는 행렬 c(i,j)의 안에 있는 지표들의 용도는 c행렬에서 i 번째 행과 j 번째 열에 위치한 요소를 지정하는 것이다. (같은 개념이 행렬 a와 행렬 b에도 적용된다.) 그리스 문자 시스마(Sigma)는 합계를 뜻한다. 시그마를 k=1 에서부터 n까지 진행되는 일종의 루프로 생각해라. 이 때 n은 내항의 크기이다. 그 루프를 한 번씩 돌 때마다 나오는 결과 값을 모두 더해라.

아래는 같은 값을 가지지만 꼴은 다른 행렬들간의 차이점을 보여준다. 첫 번째 예제는 2ⅹ1 행렬과 1ⅹ2 행렬을 곱해서 2ⅹ2 행렬이 된다. 두 번째 예제는 1ⅹ2 행렬과 2ⅹ1 행렬을 곱해 1ⅹ1 행렬이 됨을 보여준다(다른 말로는 스칼라(scalar)라고도 함).

나눗셈은 어떤가?

행렬 나눗셈은 없다. 그 대신 행렬 나눗셈에 해당하는 개념으로 역행렬이 있다. 행렬 A의 역행렬은 다음과 같은 방정식으로 정의된다.

한 행렬과 그 역행렬을 함께 곱할 경우 그 결과는 I 또는 항등 행렬로 나타낼 수 있다. 항등 행렬(identity matrix)이란 (좌상에서부터 우하까지 이어지는) 주 대각선상에 있는 요소들을 제외하고 모든 요소들이 0인 정방 행렬이다. 3ⅹ3 항등 행렬의 예는 다음과 같다.

전통적인 나눗셈과 역행렬이 약간 다르기는 하지만 그 개념은 다음과 같은 수량(scalar) 수학을 직접적으로 따른다:

여러 가지 제한사항이 있다. 역행렬은 (행과 열의 개수가 같은) 정방 행렬에 대해서만 정의되면 이 외의에 대해서는 존재하지 않을 수도 있다. 이런 제한사항은 0으로는 어떤 수도 나눌 수 없다는 개념과 비슷하다고 볼 수 있다. 따라서 모든 행렬이 역행렬을 갖는 것은 아니라고 할 수 있다. 만약 역행렬이 존재한다면 이 튜토리얼에서 나중에 살펴보겠지만 어려운 방정식을 푸는데 대단히 유용하게 사용된다.

NumPy 그리고 파이썬

수학수업은 이 정도에서 끝내고 수치처리 파이썬에서는 수학이 어떻게 사용되는지 살펴보자. NumPy 배포판 덕분에 다중배열(multi-array)이라는 개념을 파이썬에서 사용할 수 있다. 이 개념은 일정 모양의 용기에 동질적인 숫자들의 집합을 저장하는 능력이다. 마치 행렬과 같은 개념이라는 생각이 들지 않는가? 다중배열은 어떤 수치 값들도 담을 수 있다(정수, 부동 소수점 수, 그리고 복소수 값). 수치처리 파이썬 패키지는 수학 함수들에 대한 확장도 제공하여 스칼라양뿐만 아니라 배열 인수(array arguments)도 다룰 수 있다. 게다가 이 외의 다른 모듈(LinearAlgebra, FFT, RanLib 그리고 Matrix 기타 등등)도 제공된다. 우리는 나중에 LinearAlgebra 모듈을 사용하여 역행렬을 만들어 볼 것이다.

NumPy가 제공하는 연산들은 방금 우리가 검토했던 것과 같은 전통적인 행렬 수학에 정의된 연산들을 뛰어 넘어 확장된다. 비록 확장된 연산들이 비표준이기는 하지만 어떤 상황에서는 더 효율적인 계산을 수행하는데 도움이 된다.

반드시 알아두어야 할 중요한 것은 NumPy가 이진 연산자 "*"를 곱셈에 정의하고 그리고 "/" 를 나눗셈에 대한 연산으로 정의하여 덧셈과 뺄셈처럼 요소별로 처리할 수 있다는 것이다(그렇지만 꼴은 여전히 중요한 문제임). 전통적인 곱셈을 수행하거나 역행렬을 만들고자 한다면 matrixmultiply()inverse()와 같은 함수들을 명시적으로 사용할 필요가 있을 것이다.

행렬과 배열 만들기

NumPy의 기능을 사용하려면 먼저 NumPy를 임포트해야 한다.
>>> from Numeric import *.
일단 NumPy 모듈이 임포트되면 array() 함수로 배열을 만들 수 있다. array() 함수는 인수를 두 개 취한다. 값으로 터플이나 혹은 리스트를 취하고 형으로 Float 또는 Int와 같은 형 코드를 선택적으로 취한다. 리스트는 내포되어 다차원 배열을 만들 수 있다. array() 함수는 지정된 형과 값들을 가진 배열을 하나 생성할 것이다. 형이 지정되지 않으면 그 배열의 형은 그 원소들의 형에 따를 것이다.
>>> a=array((1,2))
>>> print a
[1  2]
터플 안에 담긴 값들이 정수이기 때문에 정수 배열을 만든다.
>>> b=array((1,2),Float)
>>> print b
[ 1.  2.]
정수 인수들을 사용하여 부동소수점 배열 하나를 만든다. 부동 소수점 형은 정수 인수들을 덮어 쓴다.
>>> c=array(((1,2),(3,4)))
>>> print c
[[1 2]
 [3 4]]
다차원 터플을 사용하여 다차원 배열을 만든다.

꼴 갖추기와 꼴 바꾸기

한 배열의 꼴은 터플로 표현된다. 꼴을 보고 싶으면 다음과 같이 타이핑해 넣으면 된다.
>>> a=array((1,2))
>>> a.shape
(2,)

>>> c=array(((1,2),(3,4)))
>>> c.shape
(2, 2)
a는 오직 한 개의 차원만 가지며 (꼴지정 터플(shape tuple)은 값을 오직 한 개만 가짐) 그리고 c는 다차원이라는 것을 주목하라 (꼴지정 터플에 값이 두개이다). 기본으로, 1차원 배열은 오직 길이만을 가진다. 행과 열이라는 개념은 1차원 배열의 구조에는 구현되지 않는다. 그렇지만 배열의 꼴은 reshape() 함수를 통해 변경될 수 있다. reshape 함수는 배열과 꼴 지정 터플(shape tuple)과 같이 두 개의 인수를 취한다.
>>> a=array((1,2))
>>> a.shape
(2,)
 >>> print a
[1 2]
>>> ma=reshape(a,(1,2))
>>> ma.shape
(1, 2) 
>>> print ma
[ [1 2]]
>>> mb=reshape(a,(2,1))
>>> mb.shape
(2, 1)
>>> print mb
[[1]
 [2]]
꼴 바꾸기(reshape)를 사용하여 1차원 배열에 보다 전통적인 행렬같은 꼴을 부여하면 인쇄결과(printout)는 변경된다. 1ⅹ2 행렬 ma에서 여분의 공간은 그 행렬의 다차원적인 본성을 보여준다. a의 출력과 ma의 출력을 대조해 보자. 원래 형태와 바뀐 형태사이의 차이점이 보이는가? 원소들은 똑같지만 그 꼴은 다르다.

이러한 2ⅹ2 행렬을 꼴바꾸기 하면 요소들은 소스 배열에서 행별로 취해진다.:
>>> c=array(((1,2),(3,4)))
>>> c.shape
(2, 2)
>>> print c
[[1 2]
 [3 4]]
>>> d=reshape(c,(1,4))
>>> d.shape
(1, 4)
>>> print d
[ [1 2 3 4]]
행렬 곱셈

덧셈과 뺄셈은 여러분이 예상한 그대로 작동한다. 한 번 시험해 보라! 그러나 곱셈이라는 더 꼼수적인 문제를 살펴보자. 내가 곱셈과 나눗셈에 관하여 이전에 경고했음에도 불구하고 주의를 기울이지 않는다면, 다음 예제에서 어떤 일이 일어나게 될 것인지 궁금할 것이다. 꼴이 다른 배열을 두 개를 만들자. 기본 곱셈 연산을 사용하면 그 꼴에 상관없어 보이는 결과들을 만들어 낸다. 기억하라! 꼴은 행렬 곱셈에서 중요하다. matrixmultiply()를 사용해야만 예상한 결과를 얻는다.
>>> ma
array([ [1, 2]])
>>> mb
array([[1],
       [2]])
>>> ma*mb
array([[1, 2],
       [2, 4]])
>>> mb*ma
array([[1, 2],
       [2, 4]])
>>> matrixmultiply(ma,mb)
array([  [5]])
>>> matrixmultiply(mb,ma)
array([[1, 2],
[2, 4]])
곱셈 연산은 "의사 지표(† 역자 주: 의사 지표(Pseudo Indices)는 주로 파이썬 표현식을 평가하는 동안에 배열이 중심축을 추가함으로써 모습을 바꾸는 능력을 말함)"라는 동작에 의해서 통제된다. 의사 지표는 진짜 유용하다. 이 의사 지표의 개념은 다음 기사에서 설명하겠다. 지금 당장은 matrixmultiply() 함수만으로 예상된 결과들을 달성하자.

역행렬

역행렬을 만들려면, 단순히 inverse() 함수를 사용하기만 하면 된다. 이 함수는 기본 NumPy에 함께 제공되는 LinearAlgebra 모듈에 있다. 이 함수에 접근하려면 먼저 임포트부터 해야 한다..
>>> From LinearAlgebra import *
다음에 보는 간단한 예제에서 이 함수의 사용법을 살펴보자.
>>> a=array(((3,2),(2,4)),Float)
>>> print a
[[ 3.  2.]
 [ 2.  4.]]
>>> a_inv = inverse(a)
>>> print a_inv
[[ 0.5   -0.25 ]
 [-0.25   0.375]]
결과를 점검하려면 a_inva를 곱하라(이렇게 하면 항등 행렬이 나올 것임).
>>> matrixmultiply(a_inv,a)
array([[ 1.00000000e+000,  1.11022302e-016],
       [ 0.00000000e+000,  1.00000000e+000]])
게임 끝

이러한 능력들을 손에 쥐고 무엇을 할 수 있는가? 다시 한번 수학 수업 시간을 돌이켜보면, (다원 연립 방정식을 푸는 것으로 알려져 있기도 한) 일차 연립방정식이라는 주제가 떠 오른다. 다음 예제의 목적은 두 방정식을 모두 (물론 동시에) 참으로 만드는 x와 y의 값을 찾는 것이다.

이 고전적인 문제는 평면에서 두 라인이 서로 교차하는 곳을 찾는 문제이다. 두 라인은 여러 가지 다른 현상들을 표현하기도 하는데, 경제학에서 "수익 Vs 비용"과 같은 현상을 표현하거나 또는 생물학에서 "성장 대 쇠퇴"와 같은 현상을 표현하기도 한다. 어느 경우라도 교차점을 찾아 내는 것은 지겨운 일이고 약간의 변수를 더 추가할 경우 계산은 엄청나게 어려워질 것이다.

여러분은 머리나 컴퓨터를 사용해 이 문제 풀 수 있다. 행렬을 사용해 이 문제를 풀 수 있는 힌트는 동등한 두 개의 2ⅹ1 행렬로 방정식을 나타내는 것이다. 어떻게 행렬 꼴과, 곱셈, 그리고 역(inverses)을 사용하는지 주목하기 바란다.

이것은 다음과 같이 2ⅹ2 행렬과 1ⅹ2 행렬(미지수들)의 곱이 2ⅹ1 행렬과 같다고 다시 쓸 수 있다.

행렬 수학에는 전통적인 대수학에 있는 것보다 약간 더 많은 규칙이 있다. 그 중에 하나를 아래에 있는 방정식에서 볼 수 있다. 미지수에 대하여 해를 찾으려면 z만 왼쪽에 놓아둘 필요가 있다. 대수학적으로 생각하면 제일 먼저 떠오르는 직감은 A로 나누는 것이 될 것이다. 그러나 행렬 나눗셈은 정의되어 있지 않다! 다행스럽게도 우리는 역행렬을 사용할 수 있다. (마치 양편 모두에 1/A를 곱하는 기분으로) 양쪽 모두에 역행렬을 곱해라.

왼쪽에 나온 그 결과는 항등행렬과 z가 곱해져 있다(이 값은 그냥 z이다). 역행렬은 행렬 A와 약분된다. 오른쪽에는 A-1b의 곱이 남았다. 오른쪽에 있는 모든 값들은 아는 값이고 왼쪽에는 모르는 값만 남았으므로, 해답은 바로 눈 앞에 있다! 이러한 방정식들은 그저 역(inverse)과 곱(multiply)만으로도 해결된다.

역과 곱을 그냥 머리로만 계산하고 싶은가? 필자는 그러고 싶지 않다. 다음은 NumPy로 계산하는 방법이다:
>>> A=array(((3,4),(5,2)))
>>> b=reshape(array((11,9)),(2,1))
array()reshape() 함수를 조합하여 b를 어떻게 눈깜짝할 사이에 만들어 내는지 주목하라.
>>> Ainv = inverse(A)
>>> z=matrixmultiply(Ainv,b)
>>> z
array([[ 1.],
[ 2.]])
이것이 바로 그 해답이다. 결과를 원래 행렬 A와 점검해보자.
>>> matrixmultiply(A,z)
array([[ 11.],
[  9.]])
이것은 정답을 보여준다! 잘만 한다면 복잡하게 머리로 문제를 풀기보다는 Numpy로 연습해보는 것이 덜 고통스럽다. 이런 간단한 문제로는 Python/NumPy의 힘을 약간만 볼 수 있을 뿐이다. 진정한 힘은 더 큰 문제들을 해결하는 데서 볼 수 있다. 만약 미지수와 방정식의 개수가 10개 정도나 된다면 그 문제를 Nmpy의 도움 없이 그냥 머리로만 푸는 것은 정말 어려울 것이다.

아직까지는 DISLIN에 관하여 손도 대지 않았으며 다음 기사를 위해 남겨 두겠다. 틀림없이 시각화는 대단히 유용하다. 방정식을 표현해 놓은 그래프를 전부 기억하는가? DISLIN이 여러분 대신 그렇게 할 수 있다. 다음 도표에서, 녹색과 적색 라인은 위에 있는 방정식들이다. 두 라인은 x=1이고 y=2인 점에서 교차하며 이 답이 바로 우리가 얻었던 답이다.

앞으로 다룰 것들

앞으로 다루게 될 기사에서는 NumPy를 가지고 더욱 유용하게 행렬 처리하는 법을 살펴볼 것이다. 또 그 결과들을 DISLIN으로 그래프화 하여 우리가 무엇을 하고 있는지도 보여 줄 것이다. 위에서 본 2D 그래프가 별 재미가 없다고 느낄 수도 있다. 그렇다면 3D 정보를 보여주는 다음 그래프를 보아라. 다음 도표는 (자동차에 사용되는 잠금-방지(anti-lock) 브레이크 시스템이라고 생각되는) 제어 시스템이 불안정한 점들에서 어떻게 반응하는지를 보여주는 그래프이다. 정점들은 실제로 무한대로 올라가지만 도표의 목적을 위하여 잘라 내었다.

더 읽어야 할 것

수치처리 파이썬을 좀더 공부하고 싶은가? 그와 관련된 수학을 기억하려고 고민하고 있는가? 아직도 머리가 허전한가? 다음 기사를 기다리는 동안 더 많은 정보를 얻을 수 있는 방법을 하나 가르쳐 주겠다. 수치처리 파이썬의 매뉴얼을 읽어보는 것은 어떤가? 그것은 튜토리얼로써 뿐만 아니라 레퍼런스로도 사용될 수 있도록 작성되었다.

선형 대수학 및 수학과 관련된 책들을 참고하고 싶은가? 벤 노블(Ben Noble)과 제임스 다니엘(James W. Daniel)이 집필한 『Applied Linear Algebra』를 추천한다. 만약 행렬의 측면 중 계산적인 면에 더 관심이 간다면 진 골럽(Gene H. Golub)과 찰스 반 로운(Charles F. Van Loan)이 집필한 인기만점의 『Matrix Computations』도 역시 읽어볼 만하다고 할 수 있다.
에릭 하게만(Eric Hagemann)은 임베드에서 메인프레임까지 모든 종류의 컴퓨터에서 숫자를 빠르게 처리하기 위한 알고리즘 전문가이다.
TAG :
댓글 입력
자료실

최근 본 상품0