VSCode 환경에서 OpenCV 를 설치할 때 꼭 문제되는 사항이 있다.

 왠지 심리적인 느낌으로 반드시 에러가 날 거라고 생각하긴 했지만 진짜 날 줄은 몰랏다.

 

다음과 같은 문제가 발생할 수 있다.

python -m pip install --upgrade pip
python -m pip install opencv-python

 

위에서 python -m 이 없는 경우에, 즉 pip 만을 이용해서 Library Module 을 import 때리려고 하는데

인식을 못하는 것이다.

 

더 큰 문제는 위와같이 때려도 문제가 나는 경우가 있는데, 그 경우에는 머리털이 곤두서게 된다.

 

(venv) $ pip2 install opencv-python
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting opencv-python
  Using cached opencv-python-4.3.0.38.tar.gz (88.0 MB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  ERROR: Command errored out with exit status 1:
   command: /home/pc_user/.venvs/venv/bin/python /home/pc_user/.venvs/venv/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py get_requires_for_build_wheel /tmp/tmp7q7z4L
       cwd: /tmp/pip-install-C21PKF/opencv-python
  Complete output (22 lines):
  Traceback (most recent call last):
    File "/home/pc_user/.venvs/venv/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py", line 280, in <module>
      main()
    File "/home/pc_user/.venvs/venv/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py", line 263, in main
      json_out['return_val'] = hook(**hook_input['kwargs'])
    File "/home/pc_user/.venvs/venv/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py", line 114, in get_requires_for_build_wheel
      return hook(config_settings)
    File "/tmp/pip-build-env-2FzygL/overlay/lib/python2.7/site-packages/setuptools/build_meta.py", line 146, in get_requires_for_build_wheel
      return self._get_build_requires(config_settings, requirements=['wheel'])
    File "/tmp/pip-build-env-2FzygL/overlay/lib/python2.7/site-packages/setuptools/build_meta.py", line 127, in _get_build_requires
      self.run_setup()
    File "/tmp/pip-build-env-2FzygL/overlay/lib/python2.7/site-packages/setuptools/build_meta.py", line 243, in run_setup
      self).run_setup(setup_script=setup_script)
    File "/tmp/pip-build-env-2FzygL/overlay/lib/python2.7/site-packages/setuptools/build_meta.py", line 142, in run_setup
      exec(compile(code, __file__, 'exec'), locals())
    File "setup.py", line 448, in <module>
      main()
    File "setup.py", line 99, in main
      % {"ext": re.escape(sysconfig.get_config_var("EXT_SUFFIX"))}
    File "/usr/lib/python2.7/re.py", line 210, in escape
      s = list(pattern)
  TypeError: 'NoneType' object is not iterable
  ----------------------------------------
ERROR: Command errored out with exit status 1: /home/pc_user/.venvs/venv/bin/python /home/pc_user/.venvs/venv/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py get_requires_for_build_wheel /tmp/tmp7q7z4L Check the logs for full command output.

 

나랑 완전 100% 같은 경우는 아닌데, 이분은 path 가 root 부터 시작하는 걸 보니 리눅스의 냄새다

 

거기다 가장 큰 단서는 _in_process.py 에서 롤린하다가 에러가 낫다는건데, ㅇ ㅣ부분에서 답변을 살펴보면

 

Python 2.7 is not supported anymore in opencv-python-4.3.0.38. Support was dropped in 4.3.0.36; see this issue.

The workaround I found was to install opencv-python version 4.2.0.32 (which is the latest supported for Python 2.7, see this for all releases) like this:

 

버전이 맘에 안든다는 이야기다

 

python -m pip install opencv-python==4.2.0.32

이렇게 해줫더니 깔끔하게 됫다.

 

stackoverflow.com/questions/63346648/python-2-7-installing-opencv-via-pip-virtual-environment

 

 

 

아! 추가적으로 혹시 모르니까

setting.json 에서 한줄 추가해준다.

"python.linting.pylintArgs": ["--extension-pkg-whitelist=cv2"] 

 

블로그 이미지

remoted

Remoted's IT LAB & POST DATABASE

댓글을 달아 주세요

4.2 수

파이썬에서 수를 나타내는 데이터 유형에는 정수, 실수, 복소수 등 여러 가지가 있다. 이들은 모두 수에 속하지만, 표현할 수 있는 수의 종류와 범위가 다르다. 하나씩 살펴보자.

4.2.1 정수

정수(integer, 줄여서 int)는 자연수에 음양 부호(+, -)가 붙은 것이다. 비트 하나로 부호를 표현하고, 여러 개의 비트 묶음으로 자연수를 표현한다.

여러분은 이미 파이썬에서 정수를 여러 번 사용해 보았다. 파이썬에서 소수점 없이 입력된 수, 즉 0, -15 같은 표현은 모두 정수다. 다음은 대화식 셸에 정수를 입력해 본 것이다.

코드 4-1 정수의 예

>>> 1000 # 양의 정수 1000

>>> -1000 # 음의 정수 -1000

파이썬은 자릿수가 큰 수도 취급할 수 있다. 작은 수를 입력할 때와 마찬가지로 그냥 코드에 입력하면 된다.

코드 4-2 큰 정수

>>> 1000000000

1000000000

일상 생활에서는 큰 수를 알아보기 쉽게 세 자리마다 쉼표를 붙여 1,000,000,000 처럼 표기하곤 한다. 파이썬 코드로는 쉼표 대신 밑줄(_)을 하나씩 붙여 표기할 수 있다.

코드 4-3 큰 수를 알아보기 쉽게 표기하는 방법

>>> 1_000_000_000 # 1000000000과 동일하다

1000000000

팔진법과 십육진법

정수는 팔진법이나 십육진법으로도 표현할 수 있다. 팔진수는 0o로 시작하고, 십육진수는 0x로 시작한다. 0o77는 팔진수 77, 0xff는 십육진수 ff다.

코드 4-4 팔진법과 십육진법으로 정수 표현하기

>>> 0o77 # 팔진수 77 63 >>> 0xff # 십육진수 ff 255 >>> 255 == 0xff # 진법이 달라도 같은 정수다 True

컴퓨터 내부에서는 모든 수를 이진법으로 다룬다. 코드에서 어떤 진법으로 수를 표기하든 이진수가 되므로 컴퓨터에게는 차이가 없다. 그러니 가장 사용하기 편리한 십진법을 사용하면 된다. 십육진법과 팔진법은 비트를 직접 다루거나 색상 정보를 표현하는 등 특수한 경우에 가끔씩 사용된다. 다른 사람의 코드를 읽을 때 “아, 십육진수구나” 하고 알아볼 수 있으면 충분하다.

4.2.2 실수

실수는 -1.0, 3.1415처럼 소수점 아래까지 표현할 수 있는 수다. 1.0은 실수 유형 데이터로, 정수 유형 데이터인 1과는 구별된다.

코드 4-5 실수의 표현

>>> 3.1415 3.1415

파이썬에서는 부동소수점 수(floating point number, 줄여서 float)라는 데이터 유형으로 실수를 다룬다. 부동소수점 수는 ‘소수점이 움직이는 수’라는 뜻이다. 이게 무슨 뜻인지는 곧이어 설명한다. 편의상 부동소수점 수를 그냥 실수라고 부를 때가 많다.

과학 표기법

부동소수점 수를 이해하기 위해 과학 표기법에 관해 잠깐 알아보자. 과학 표기법이란 매우 작은 수와 매우 큰 수를 다루는 일이 많은 과학 분야에서 수를 표기하는 방법이다. 전자계산기에 매우 큰 수를 입력하면 화면에 ‘1.234e5’ 같은 수가 출력되는 것을 볼 수 있다. 과학 표기법으로 표기된 수다.

과학 표기법은 수를 가수와 지수로 나누어 적는 방법이다. 1.234e5에서 e를 중심으로 앞의 수(1.234)는 가수, 뒤의 수(5)는 지수다. 이 수가 실제로 나타내는 값은 가수의 소수점 위치를 지수만큼 오른쪽으로 옮겨 구할 수 있다. 1.234의 소수점을 다섯 칸 오른쪽으로 옮기면 123400.0이 된다. 지수가 음수라면 소수점을 왼쪽으로 옮긴다.

파이썬에서도 과학 표기법으로 실수를 나타낼 수 있다. e를 구분자로 하여 가수e지수로 표기하면 된다.

코드 4-6 과학 표기법으로 실수 표기

>>> 1.23e0 # 1.23 * 10 ^ 0 1.23 >>> 1.23e6 # 1.23 * 10 ^ 6 1230000.0 >>> 1.23e-4 # 1.23 * 10 ^ -4 0.000123

프로그램에 과학 표기법으로 수를 표기할 일이 많지는 않을 것이다. 하지만 연산 결과가 크거나 작을 때 과학 표기법으로 출력되는 경우가 종종 있으니 눈여겨 봐 두는 것도 좋겠다.

과학 표기법은 컴퓨터의 실수 표현 방식과도 관련이 있다. 컴퓨터는 실수를 기억할 때 수를 부호, 지수, 가수로 나누어 각각 비트 묶음에 담아 기억한다. 그렇게 기억해 둔 수에서, 가수를 지수만큼 소수점 이동시키면 실제 값을 구할 수 있다. 소수점의 위치가 움직이는 수라는 뜻의 ‘부동소수점 수’라는 이름도 이 방법에서 유래한 것이다.

실수의 정밀도 문제

수학의 세계에서는 정수가 실수에 포함되는 개념이다. 하지만 프로그래밍에서는 정수와 실수를 서로 다른 유형으로 구별한다. 정수도 실수로 통일해서 취급하면 편리할텐데, 왜 구별하는 것일까? 비트로 정수와 실수를 표현하는 방법이 다르기 때문이다.

정수는 비트를 이용해 정확하게 표현할 수 있다. 하지만 실수는 비트로 정확하게 표현하지 못한다. 이진법에서는 딱 나누어 떨어지는 소수가 별로 없기 때문이다. 0.1이라는 간단한 수조차도 나누어 떨어지지 않는다. (1을 2로 여러 번 나누어서 0.1을 만들려면 몇 번 나누어야 하는지 계산해 보라.) 이런 수는 이진수로는 순환소수가 되며, 비트로 정확하게 표현할 수 없으므로 아무리 정밀하게 표현하려 해도 오차가 있을 수밖에 없다. 다음 코드는 이 문제를 대화식 셸에서 확인해 본 것이다. 파이썬에서 실수를 연산하여 실수 연산 오차를 확인해 보자.

코드 4-7 파이썬의 실수 오차

>>> 1.1 - 1.0 # 계산 결과는 0.1이어야 하지만... 0.10000000000000009

1.1 - 1.0의 계산 결과는 0.1이어야 하지만 실제로는 0.10000000000000009가 나왔다. 매우 미세하지만 오차가 발생했다.

실수를 표현하는 데 사용되는 비트의 양을 늘릴 수록 오차의 크기는 작아진다. 하지만 용량을 아무리 늘려도 오차가 완전히 없어지지는 않는다. 무한히 반복되는 순환소수를 종이에 정확하게 적으려면 무한한 크기의 종이에 무한히 적어야 한다. 컴퓨터의 자원은 유한하기 때문에 실수를 아무리 정밀하게 표현하려 해도 근사치를 사용할 수밖에 없다.

그러면 프로그래밍 할 때 신경써야 할 것은 무엇일까? 1.1 - 1.0 == 0.1처럼 실수를 동등 연산자로 비교하지 말아야 한다. 0.00009 < 1.1 - 1.0 < 0.100001처럼 일정한 오차범위를 정해서 비교하는 것이 더 적절하다.

정수와 실수는 각자의 특징과 한계가 있기 때문에 대부분의 프로그래밍 언어에서 서로 다른 유형으로 구별되어 있다. 처리하려는 데이터에 올바른 유형이 정수인지 실수인지 잘 파악해야 한다.

무한대

실수 데이터에는 양의 무한대와 음의 무한대가 있다. 양의 무한대와 음의 무한대를 나타내는 데이터는 각각 float('inf')와 float('-inf')라는 표현을 이용해 만든다. 무한대는 다른 모든 수보다 크거나, 다른 모든 수보다 작은 수를 표현해야 할 때 사용할 수 있다.

코드 4-8 무한대

>>> float('inf') # 양의 무한대 inf >>> float('-inf') # 음의 무한대 -inf >>> 1e100 < float('inf') # 무한대는 다른 모든 실수보다 크다 True >>> 1e309 # 너무 큰 실수는 무한대로 평가된다 inf >>> 1e-324 # 너무 0에 가까운 실수는 0으로 평가된다 0.0

실수를 나타내는 데 사용한 용량이 제한되어 있으므로, 너무 큰 실수는 무한대로 평가되어 버리고 너무 0에 가까운 수(무한소)는 0으로 평가되어 버린다. 곤란한 일이라고 생각할 수도 있겠지만 일반적인 프로그래밍 상황에서 이런 수를 취급할 일은 거의 없다고 봐도 무방하다. 관측할 수 있는 우주에 존재하는 모든 수소 원자의 수가 1e81개 이하라고 한다.

4.2.3 복소수

파이썬의 수치 데이터 유형에는 앞에서 설명한 정수와 부동소수점 수 외에 복소수(complex number, 줄여서 complex)가 있다. 복소수는 고등학교 수학에서 등장한다. 아직 복소수를 배우지 않았다면 이 절의 내용은 넘어가도 된다.

수학의 표기법에서 복소수는 ‘1+2i’와 같이 실수부와 허수부를 나누어 표기한다. 이 때 ‘i’는 제곱해서 -1이 되는 가상의 상수이며 복소수의 허수부를 나타내기 위한 기호로 사용된다.

파이썬에서 복소수를 표현할 때는 1+2j처럼 i만 j로 바꾸어 표기하면 된다.

코드 4-9 복소수의 표현과 연산

>>> 1-2j # 실수부가 1, 허수부가 -2인 복소수 (1-2j) >>> (1-2j).real # 복소수의 실수부 1.0 >>> (1-2j).imag # 복소수의 허수부 -2.0

복소수에 .real을 계산하면 실수부를, .imag를 계산하면 허수부를 구할 수 있다. 단, 소수점과 구별하기 위해 수를 괄호로 감싸주어야 한다.

4.2.4 수의 연산

데이터는 유형에 따라 가능한 연산이 정해져 있다. 정수, 실수, 복소수는 데이터 유형이 서로 다르지만, 넓은 의미에서 모두 수이기 때문에 연산을 서로 공유한다.

수로 할 수 있는 연산은 대부분 2장에서 살펴보았다. 수는 사칙연산, 거듭제곱, 몫과 나머지 계산, 반올림 계산, 절대값 계산 등이 가능하다. 그리고 방금 배운 복소수의 실수부와 허수부를 구하는 연산도 있다. 이 연산들은 정수, 실수, 복소수 모두 공통적으로 사용 가능하다. 심지어 정수를 반올림하거나 정수의 실수부를 구하는 것도 할 수 있다. 다음 예제를 보자.

코드 4-10 수 유형을 위한 연산은 통용된다

>>> round(10) # 실수와 마찬가지로 정수도 반올림이 된다 10 >>> (10).real # 정수에서 복소수의 실수부를 구하는 연산도 가능하다 10

정수를 반올림하거나 정수의 실수부를 구하는 연산은 쓸모없어 보일 수 있다. 하지만 정수는 실수에 포함되고 실수는 복소수에 포함되므로 수를 위한 연산이 공통적으로 가능한 것은 논리적으로 타당하다. 어떤 변수에 저장된 수가 정수인지 실수인지 복소수인지 모른 채 연산을 해야 한다면 이런 특성이 유용할 것이다.

정수와 실수를 더하거나 실수와 복소수를 곱하는 등 서로 다른 유형의 수를 함께 계산하는 것도 가능하다. 이 때 연산 결과는 더 넓은 범위의 수 유형으로 계산된다. 정수와 실수를 계산하면 실수가 되고, 실수와 복소수를 계산하면 복소수가 된다.

코드 4-11 서로 다른 유형의 수를 함께 연산하기

>>> 10 + 0.5 # 정수와 실수의 연산 -> 실수 10.5 >>> 0.5 * 1+2j # 실수와 복소수의 연산 -> 복소수 (0.5+2j)

그렇다면 수 데이터에 적용할 수 없는 연산에는 무엇이 있을까? 아직 소개한 적은 없지만 곧 배우게 될 텍스트 데이터의 대문자 변경 연산(upper())을 수에 적용해 보자.

코드 4-12 수에 적용할 수 없는 연산의 예

>>> (10).upper() # 텍스트를 위한 연산은 오류를 발생시킨다 AttributeError: 'int' object has no attribute 'upper'

(10).upper()는 10이라는 수를 대문자로 바꾸는 연산이다. 수를 대문자로 바꾼다는 것은 논리적으로도 말이 안 되고, 수 데이터 유형에서 이 연산을 지원하지도 않는다. 그래서 오류가 발생한 것이다.

연습문제

연습문제 4-1 프로그래머의 나이 표기

프로그래머 세 사람이 자신의 나이를 말하고 있다.

프로그래머 A: “저는 0x7d0년에 태어났습니다. 올해로 0x12살이 되었네요.” 프로그래머 B: “그러시군요. 저는 올해 0o22세입니다.” 프로그래머 C: “저는 18살입니다.”

세 사람의 출생년은 각각 언제인가?

힌트: 대화식 셸에 팔진수 또는 십육진수를 입력하면 십진수로 출력된다.

연습문제 4-2 과학 표기법

-252.87을 과학 표기법으로 나타내 보아라.

연습문제 4-3 융통성 있는 실수 비교 함수

두 실수가 거의 같은지 검사하는 함수 almost_equal()을 정의하라. 이 함수는 실수 두 개를 입력받아 두 실수의 차이(오차허용범위)가 0.0001 미만이면 True를 그렇지 않으면 False를 반환한다. 또, 할 수 있다면 함수를 호출할 때 오차허용범위를 지정하도록 정의해 보라.

블로그 이미지

remoted

Remoted's IT LAB & POST DATABASE

댓글을 달아 주세요

1. Minimax Algorithm

Minimax is a decision rule used in decision theory, game theory, statistics and philosophy for minimizing the possible loss for a worst case (maximum loss) scenario.

 

틱-택-토, 체스, 오목과 같은 게임의 인공지능은 어떻게 만들어질까?

 

저런 게임들은 멀리 보는것이 중요하다. 상대방을 파악하고 예상해서 장기적으로 유리하게 이끌어 가야 이기게 된다.

 

컴퓨터는 게임의 판도를 읽을 줄 모르기 때문에, 게임판의 상태를 점수로 변환시키는 과정이 필요하다. 예를 들면 체스에서 폰은 1점, ..., 퀸은 9점 하는 것과 같이 이 과정을 '평가한다'고 하자. 평가하는 규칙을 정한다면 게임판의 모든 상태를 점수로 바꿀 수 있다.

 

여기에 "AI가 유리하면 큰 점수, 유저가 유리하면 작은 점수"가 되도록 규칙을  정해주면 된다. (사실 어떻게 보면 이 과정이 가장 어렵다.)

 

컴퓨터가 이길 수 있는 방법은, 지금 게임판의 상태에서 변경 가능한 모든 경우를 평가해서, 가장 높은 점수의 길로 가는 것이다. 그런데, 이런 상황이라면?

 

 

이때, 컴퓨터인 흰색 퀸이, 다음 차례에 가장 유리해지도록 C5로 가서 폰을 먹는다면, 곧 D6의 폰에게 퀸을 잃게 될 것이다. 이처럼, 평가해야할 게임판은 바로 당장의 게임판이 아닌, 몇수 후의 게임판을 평가해서, 가장 높은 점수의 평가판을 향해 다음 수를 두어야 한다.

 

하지만 그럴때 상대편은 항상 우리에게 최악인 수를 골라서 간다. 왜냐면 그래야 자기들이 이기기 때문이니까. 그래서 우리는 상대편이 던저주는 최악의 수 중에서, 가장 최선의 수를 골라야 한다.

 

그래서 이용되는 알고리즘이 Minimax 알고리즘이다.

 

 

위의 그림을 분석해보자. 현재 상태은 가장 위에 있는 0번째 상황이고, 이 인공지능은 4수를 내다볼 수 있다. 4수째의 상태 중에서 최고의 상태은 가장 왼쪽의 점수가 10인 상태로 가는 것이다. 하지만 그렇다고 현재 선택에서 왼쪽길을 선택한다면, 상대방은 자신에게 가장 유리한 점수가 -10인 상태로 갈 것이다. 결국 본전도 못찾은것. 결국 가장 나은 길은 오른쪽 길이다.

 

이렇게 분석하는 Minimax 알고리즘을 간단한 애니메이션으로 보자.

 

위키피디아의 의사 코드도 살펴보자.

function minimax(node, depth, maximizingPlayer)
    if depth = 0 or node is a terminal node
        return the heuristic value of node
    if maximizingPlayer
        bestValue := -∞
        for each child of node
            val := minimax(child, depth - 1, FALSE))
            bestValue := max(bestValue, val);
        return bestValue
    else
        bestValue := +∞
        for each child of node
            val := minimax(child, depth - 1, TRUE))
            bestValue := min(bestValue, val);
        return bestValue

(* Initial call for maximizing player *)
minimax(origin, depth, TRUE)

 

그런데, 실제 게임은 가짓수가 어마어마하게 많다. 한가지 상태에서 10가지 경우가 나온다면, 3수 앞을 내다보는 인공지능은 1000가지 상태를, 6수 앞을 내다보는 인공지능은 1000000가지 상태를 평가해야 한다. 그래서 저 연산 중 불필요한 연산을 가지치듯이 버리는 알고리즘인 알파-베타 가지치기(Alpha-beta pruning)이 등장한다.

 

2. 알파-베타 가지치기

 

기본 Minimax 알고리즘은 모든 노드를 방문해서 평가해야 했지만, 실제로는 그렇지 않아도 되는 경우가 존재한다.

  • 자신이 그 경우를 택하면 자신이 불리해지는 것이 확정된 경우
  • 어떠한 경우가 자신에게 유리한 것이 확정되어 상대가 그것을 택하지 않을 확률이 높은 경우

두 경우는 애써 모든 자식노드를 검사해봐야 선택하지 않을 확률이 매우 높다. 그래서 저러한 경우가 확정되면 그 노드의 자식노드에 대한 평가를 당장 중지하여야만 불필요한 연산을 줄일 수 있다.

 

약간 길지만 자세한 동영상 설명을 보자.

 

그리고 위키피디아의 의사코드를 음미해보자.

function alphabeta(node, depth, α, β, maximizingPlayer)
    if depth = 0 or node is a terminal node
        return the heuristic value of node
    if maximizingPlayer
        for each child of node
            α := max(α, alphabeta(child, depth - 1, α, β, FALSE))
            if β ≤ α
                break (* β cut-off *)
        return α
    else
        for each child of node
            β := min(β, alphabeta(child, depth - 1, α, β, TRUE))
            if β ≤ α
                break (* α cut-off *)
        return β
(* Initial call *)
alphabeta(origin, depth, -, +, TRUE)

 

위의 동영상을 보면, Alpha 는 이전(상위) 상태들 중에서 AI에게 가장 유리한 상태의 점수, Beta 는 이전(상위) 상태들 중에서 상대방에게 가장 유리한 상태의 점수이다.

 

Alpha cut-off  자신이 상대방보다 불리하여, 자신이 그 경우를 선택하지 않을 때 불필요한 연산을 잘라내는 것이고, Beta cut-off  자신이 상대방보다 유리하여, 상대방이 그 경우를 선택하지 않을 확률이 높을 때 불필요한 연산을 잘라내는 것이다. 둘의 차이점을 잘 알아두자.

 

3. 마무리

 

Alpha-beta pruning 외에도 Negascout 이나 MTD-f 와 같은 최적화 알고리즘이 있다고 한다. 이건 나중에 알아보자.

 

AS3으로 직접 Reversi의 인공지능을 구현해보았다. Depth가 커질수록 강한 인공지능이 되지만 랙은 기하급수적으로 커진다. 5 정도 되면 느려지기 시작하고 6 이상은 정상적인 플레이가 힘들정도..

 

popungpopung.tistory.com/10

블로그 이미지

remoted

Remoted's IT LAB & POST DATABASE

댓글을 달아 주세요

NumPy (Numerical Python)

 

 - Array 구조의 객체 지원

 - 수학적 반복 연산을 빠르게 처리 

     * array 구조는 단 하나의 데이터 타입만 허용

     * 수학적 연산에 반복적인 벡터 연산 가능 (단, 문자 치환은 벡터 연산 불가)

 - 수학적 연산이 많은 딥러닝 수행 시 유용

     * 딥러닝 구조의 핵심 데이터 타입

 - 빠르고 효율적인 메모리 사용

 

 

# 모듈 적용

# numpy 모듈을 np로 사용                             

import numpy as np

 

 

# 생성 (.array, .arange, .random.randn)

np.array([10,20,30])

array([10, 20, 30])

 

np.arange(10)    # 0 ~ n-1 까지의 값을 갖는 1차원 배열

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

 

np.arange(5,15,2)   # n ~ m-1 까지 s 만큼의 간격의 값을 갖는 1차원 배열 

 array([ 5,  7,  9, 11, 13])

 

np.random.randn(2,3)   표준정규분포를 따르는 2차원 난수 배열 

array([[ 1.24508099,  0.29652356, -0.97754508],

       [-1.13415315,  0.73094591, -1.04153084]])

 

np.arange(10).reshape(2,5)    # 2차원 배열 

array([[0, 1, 2, 3, 4],

       [5, 6, 7, 8, 9]])

 

np.arange(24).reshape(2,3,4)   # 3차원 배열(,,), R에서는 (,,)

array([[[ 0,  1,  2,  3],

        [ 4,  5,  6,  7],

        [ 8,  9, 10, 11]],

 

       [[12, 13, 14, 15],

        [16, 17, 18, 19],

        [20, 21, 22, 23]]])

 

 

 

# 모양 변경 (.reshape)

np.arange(10).reshape(2,5)

array([[0, 1, 2, 3, 4],

         [5, 6, 7, 8, 9]])

 

 

# 2차원 배열 색인  

arr1 = np.arange(10).reshape(2,5)

array([[0, 1, 2, 3, 4],

        [5, 6, 7, 8, 9]])

 

arr1[0,2]  ==  arr1[0][2]    # 포인트 색인

2

arr1[[0,1],[1,3]]

array([1, 8])    # (0,1) (1,3) 색인

 

arr1[0]  ==  arr1[0,]  ==  arr1[0,:]     # 행 색인

 

array([0, 1, 2, 3, 4])

 

 

arr1[:,0]     # 열 색인

 

array([0, 5])

 

 

arr1[:,0:3]   # 슬라이스 색인

array([[0, 1, 2],

       [5, 6, 7]])

arr1[:2,3:]

array([[3, 4],

       [8, 9]])

 

arr1[:,[1,3]]   # 부분 색인

array([[1, 3],

       [6, 8]])

 

arr1[0,[2,3]]  ==  arr1[0][[2,3]]   

array([2, 3]))

arr1[[0,1]][:,[0,1]] 

array([[0, 1],

       [5, 6]])

 

 

 

# 3차원 배열 색인 

arr3 = np.arange(24).reshape(2,3,4)  

array([[[ 0,  1,  2,  3],

        [ 4,  5,  6,  7],

        [ 8,  9, 10, 11]],

 

       [[12, 13, 14, 15],

        [16, 17, 18, 19],

        [20, 21, 22, 23]]])

 

arr3[0]    # 층 색인 (가장 높은 차원이 우선 적용)

array([[ 0,  1,  2,  3],

       [ 4,  5,  6,  7],

       [ 8,  9, 10, 11]])

 

arr3[:,0]     # 행 색인

array([[ 0,  1,  2,  3],

       [12, 13, 14, 15]])

 

arr3[1,2,0]    # 열 색인

20

arr3[1][[0,1]][:,[0,2]]

array([[12, 14],

       [16, 18]])

 

arr3[:,0:1, 0:1]    # 슬라이스 색인 (슬라이스 사용 시 차원의 축소 방지)

array([[[ 0]],

 

       [[12]]])

 

arr3[0][[0,1],:][:,[0,1]]  ==  arr3[0:1,0:2,0:2]

array([[0, 1],      # 1층, 1,2행, 1,2열

       [4, 5]])

 

 

 

# 색인 함수 (np.ix_)

arr1 = np.arange(10).reshape(2,5)

arr1[np.ix_([0,1],[0,1])]  ==  arr1[[0,1]][:,[0,1]]  # 순차적이 아닌, 연속적 색인 가능

array([[0, 1],

       [5, 6]])

 

arr2 = np.arange(24).reshape(2,3,4) 

arr2[np.ix_([0,1],[0,2],[0,2])]  ==  arr2[[0,1]][:,[0,2]][:,:,[0,2]]  # 1,2층 / 1,3행 / 1,3열

array([[[ 0,  2],

        [ 8, 10]],

 

       [[12, 14],

        [20, 22]]])

 

 

 

# boolean 색인

arr1 = np.arange(20).reshape(4,5)

array([[ 0,  1,  2,  3,  4],

       [ 5,  6,  7,  8,  9],

       [10, 11, 12, 13, 14],

       [15, 16, 17, 18, 19]])

arr1[[True,False,True,False]]    # True 인 행 색인 (1,3행)

array([[ 0,  1,  2,  3,  4],

       [10, 11, 12, 13, 14]])

arr1[(arr1[:,3] >= 10)]     # 3번째 컬럼이 10 이상인 행 색인 

array([[10, 11, 12, 13, 14],

       [15, 16, 17, 18, 19]])

arr1[~(arr1[:,3] >= 10)]   # 위 결과의 나머지 색인 ('~' 조건의 부정)

array([[0, 1, 2, 3, 4],

       [5, 6, 7, 8, 9]])

 

 

 

# 슬라이스 색인의 뷰

a1 = np.arange(10)

a2 = a1[3:6]     # 슬라이스 색인은 원본과 동일한 메모리 영역을 사용(얇은 복사) = 뷰 객체

a2[1] = 40

a1

array([ 0,  1,  2,  3, 40,  5,  6,  7,  8,  9])

 

a1 = np.arange(10)

a2 = a1[3:6].copy()   # 원본과 메모리를 분리하기 위해 copy() 메서드 사용

a2[1] =40

a1

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

 

 

 

# 연산

# 배열 연산의 전재 조건

1) 배열 전체 크기가 동일

ar1 = np.array([10,20,30])

ar2 = np.array([100,100,100])

ar1 + ar2        (1x3, 1x3) 크기가 동일하므로 연산 가능 

array([110, 120, 130])

 

2) 같은 방향(row or column)의 크기가 동일

arr1 = np.arange(10).reshape(2,5)

arr2 = np.arange(5).reshape(1,5)

arr1 + arr2      # (2x5, 1x5) 전체 크기는 다르지만 같은 방향(column)의 크기가 동일하므로 연산 가능

                        row 수가 부족한 1x5의 배열을 반복(broadcasting) 적용

array([[ 0,  2,  4,  6,  8],

       [ 5,  7,  9, 11, 13]])

 

 

# 크기가 다른 배열을 연산하는 방법

arr1 = np.arange(10).reshape(2,5)

arr1 + arr1[:,0]     # (2x5, 1x2) 로 연산이 불가한 상황

 

1) reshape

arr1 + arr1[:,0].reshape(2,1)    # (2x5, 2x1) 로 크기를 맞추기 위해, 1x2의 배열의 모양을 변경

array([[ 0,  1,  2,  3,  4],

       [10, 11, 12, 13, 14]])

 

2) 차원의 축소 방지

arr1 + arr1[:,0:1]                # 색인 시 차원의 축소가 발생, 차원 축소 방지를 위해 슬라이스 색인을 사용

array([[ 0,  1,  2,  3,  4],

       [10, 11, 12, 13, 14]])

 

 

 

# 복사 (.array, .asarray)

l1 = [1,2,3]

arr1 = np.array(l1)     # 깊은 복사 (다른 메모리 영역 사용)

arr2 = np.asarray(l1)  # 깊은 복사 (다른 메모리 영역 사용)

arr1[0] = 10             # 리스트는 자료구조 자체가 달라지므로 모두 깊은 복사 수행 

arr2[1] = 20

l1                          # 원본 데이터는 보존

[1, 2, 3]

 

arr = np.array([1,2,3])

arr1 = np.array(arr)     # 깊은 복사 (다른 메모리 영역 사용)

arr2 = np.asarray(arr)  # 얇은 복사 (같은 메모리 영역 사용)

arr1[0] = 10

arr2[1] = 20

arr                           # asarray로 복사 후 변경한 데이터가 원본 데이터에 영향

array([ 1, 20,  3])

 

 

 

 

# Q

### 3x5 의 임시 배열을 만든 후

 

arr1 = np.zeros((3,5))

 

# 1) 첫 번째 행의 모든 데이터를 10으로 변경

 

arr1[0] = 10

 

# 2) 두 번째 행, 3,4번째 컬럼 값을 10으로 변경

 

arr1[1,[2,3]] = 10

 

# 3) (10,20,30)의 값을 갖는 배열 생성 후 위 배열과 연산(+)

 

arr2 = np.array([10,20,30])

 

arr1 + arr2.reshape(3,1)

 

###  5x4 형태의 임시 배열을 생성한 후 

arr5 = np.arange(20).reshape(5,4)

# p(1,0), p(3,1) 의 값을 출력

arr5[[1,3],[0,1]]

# 2) 위의 배열에서 arr[1:3,2:4]의 형태와 동일하게 팬시색인을 통해 출력

arr5[[1,2]][:,[2,3]]

arr5[np.ix_([1,2],[2,3])]



출처: https://data-make.tistory.com/111 [Data Makes Our Future]

블로그 이미지

remoted

Remoted's IT LAB & POST DATABASE

댓글을 달아 주세요

numpy array의 특징과 사용법을 정리해보겠습니다.

1. np.array의 특징

  • numpy는 np.array 함수를 활용하여 배열을 생성합니다.
  • numpy는 하나의 데이터 타입만 정의가 가능하며 배열에 넣을 수 있습니다.
  • List와 가장 큰 차이점은 다이나믹 타이핑을 지원하지 않습니다.
  • C의 Array를 사용하여 배열을 생성하여 속도가 빠릅니다.

2. np.array의 구조

 

np.array와 Python list의 차이점

파이썬의 리스트는 데이터 주소값을 저장합니다. 그리고 데이터를 가져 올때는 해당 주소에 가서 데이터를 가져오게 되죠. 하지만 Numpy Array는 C 배열과 유사하여 연속된 주소를 가지고 있습니다. 배열에 담긴 데이터를 가져온다면 순서대로 가져오면 되기 때문에 메모리를 효율적으로 사용합니다.

 

3. np.array 사용법

 

import numpy as np
test_np_array = np.array([1.0,4.0,5.0,'8'], dtype=np.float)
test_np_array

#출력 : array([1., 4., 5., 8.])

np.array로 선언하고 두번째 인자로 데이터 타입을 선언합니다. 선언된 데이터로 배열이 만들어지며 입력시 다른 데이터를 입력하더라도 선언한 데이터 타입으로 변환됩니다.

 

 

dtype은 배열의 요소가 가지는 데이터 타입을 정의하며 정의된 타입에 따라 메모리 크기가 설정됩니다.

 

test_array = [1.0,4.0,5.0,'8.0'];
test_array

# 출력 : [1.0, 4.0, 5.0, '8.0']

python 리스트가 np.array와 다른 점은 입력시 다른데이터 타입을 입력하면 리스트에 입력되는 것을 확인 할 수 있습니다.

test_np_array.dtype # 배열의 타입을 반환한다.

# 출력 : dtype('float64')

test_np_array.shape # 배열의 shape을 반환함

# 출력 : (4,)

dtype : 배열의 타입을 반환

shape : 배열의 차원을 반환

tensor = [
            [[1,2,5,8],[1,2,5,8],[1,2,5,8]],
            [[1,2,5,8],[1,2,5,8],[1,2,5,8]],
            [[1,2,5,8],[1,2,5,8],[1,2,5,8]],
          ]
np.array(tensor, int).ndim # 몇차원인지 반환

# 3

np.array(tensor, int).size # 데이터의 사이즈 반환

# 36

np.array(tensor, int).nbytes # 메모리 크기 반환

# 144

ndim : 배열이 몇 차원인지 반환

size   : 배열의 데이터 사이즈 반환 

nbytes : 배열의 메모리 크기 반환

블로그 이미지

remoted

Remoted's IT LAB & POST DATABASE

댓글을 달아 주세요

arange

 

numpy에서 원하는 숫자 범위를 모두 포함하는 배열을 만드는 함수를 제공합니다.

arange를 사용하면 원하는 숫자 범위, 숫자 간격에 따른 array를 생성할 수 있습니다.

import numpy as np;
np.arange(30) # range : List의 range와 같은 효과, integer로 0부터 29까지 배열 추출

-- 출력 -- 
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29])
np.arange(0, 5, 0.5) # floating point 도 표시가능함

-- 출력 -- 
array([0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5])
create_array = np.arange(30);
create_array.reshape(5,6)

-- 출력 --
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29]])

zeros

zeros는 0으로 가득 찬 array를 생성합니다.

 

np.zeros(shape, dtype, order)

 

np.zeros(shape=(10,), dtype=np.int8) # 10 -zero vector 생성

-- 출력 -- 
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=int8)
np.zeros((2,5)) # 2 by 5 - zero matrix 생성

-- 출력 -- 
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])

ones

ones는 zeros와 마찬가지로 1로 가득찬 array를 생성합니다.

 

np.ones(shape, dtype, order)

 

np.ones(shape=(10,), dtype=np.int8)

-- 출력 -- 
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int8)
np.ones((2,5))

-- 출력 -- 
array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])

empty 

empty는 초기화되지 않은 값으로 zeros나 ones와 마찬가지로 배열을 생성해줍니다.

주의해야 할 것은 메모리도 초기화되지 않기 때문에 예상하지 못한 쓰레기 값이 들어가 있습니다.

np.empty(shape=(10,), dtype=np.int8)

-- 출력 --
array([101,   0, 100,   0, 117,   0, 108,   0, 101,   0], dtype=int8)
np.empty((3,5))

-- 출력 -- 
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])

something_like

 

_like는 지정된 array의 shape 크기만큼 지정된 값으로 채워 array를 반환합니다.

앞에서 정리한 ones, zeros, empty를 사용할 수 있습니다.

 

test_matrix = np.arange(30).reshape(5,6);
np.ones_like(test_matrix)

-- 출력 --
array([[1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1]])
np.zeros_like(test_matrix)

-- 출력 --
array([[0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0]])
np.empty_like(test_matrix)

-- 출력 --
array([[1611640584,        345,         71,          0,          0,
                 0],
       [         0,          0,          0,          0, 1953702523,
        1937077345],
       [1864514082,  573317739, 1667594341, 1869182069, 1868783470,
         578055797],
       [ 741488698, 1702065442, 2019909490, 1936028272, 1852795251,
        2067407475],
       [1881287805, 1869379937,  975332449,    8215899,          0,
                 0]])

empty는 메모리가 초기화되지 않고 할당되기 때문에 쓰레기 값이 있을 수 있습니다.

이 점 꼭 주의 고려하여 사용해야 합니다.

블로그 이미지

remoted

Remoted's IT LAB & POST DATABASE

댓글을 달아 주세요

02-4 튜플 자료형

PYTHON 2020. 12. 9. 23:02

튜플은 어떻게 만들까?

튜플(tuple)은 몇 가지 점을 제외하곤 리스트와 거의 비슷하며 리스트와 다른 점은 다음과 같다.

  • 리스트는 [ ]으로 둘러싸지만 튜플은 ( )으로 둘러싼다.
  • 리스트는 그 값의 생성, 삭제, 수정이 가능하지만 튜플은 그 값을 바꿀 수 없다.

튜플의 모습은 다음과 같다.

>>> t1 = ()

>>> t2 = (1,)

>>> t3 = (1, 2, 3)

>>> t4 = 1, 2, 3

>>> t5 = ('a', 'b', ('ab', 'cd'))

 

리스트와 모습은 거의 비슷하지만 튜플에서는 리스트와 다른 2가지 차이점을 찾아볼 수 있다. t2 = (1,)처럼 단지 1개의 요소만을 가질 때는 요소 뒤에 콤마(,)를 반드시 붙여야 한다는 것과 t4 = 1, 2, 3처럼 괄호( )를 생략해도 무방하다는 점이다.

얼핏 보면 튜플과 리스트는 비슷한 역할을 하지만 프로그래밍을 할 때 튜플과 리스트는 구별해서 사용하는 것이 유리하다. 튜플과 리스트의 가장 큰 차이는 값을 변화시킬 수 있는가 여부이다. 즉 리스트의 항목 값은 변화가 가능하고 튜플의 항목 값은 변화가 불가능하다. 따라서 프로그램이 실행되는 동안 그 값이 항상 변하지 않기를 바란다거나 값이 바뀔까 걱정하고 싶지 않다면 주저하지 말고 튜플을 사용해야 한다. 이와는 반대로 수시로 그 값을 변화시켜야할 경우라면 리스트를 사용해야 한다. 실제 프로그램에서는 값이 변경되는 형태의 변수가 훨씬 많기 때문에 평균적으로 튜플보다는 리스트를 더 많이 사용한다.

튜플의 요소값을 지우거나 변경하려고 하면 어떻게 될까?

앞에서 설명했듯이 튜플의 요솟값은 한 번 정하면 지우거나 변경할 수 없다. 다음에 소개하는 두 예를 살펴보면 무슨 말인지 알 수 있을 것이다.

1. 튜플 요솟값을 삭제하려 할 때

>>> t1 = (1, 2, 'a', 'b')
>>> del t1[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion

튜플의 요소를 리스트처럼 del 함수로 지우려고 한 예이다. 튜플은 요소를 지우는 행위가 지원되지 않는다는 메시지를 확인할 수 있다.

2. 튜플 요솟값을 변경하려 할 때

>>> t1 = (1, 2, 'a', 'b')
>>> t1[0] = 'c'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

튜플의 요솟값을 변경하려고 해도 마찬가지로 오류가 발생하는 것을 확인할 수 있다.

튜플 다루기

튜플은 값을 변화시킬 수 없다는 점만 제외하면 리스트와 완전히 동일하므로 간단하게만 살펴보겠다. 다음 예제는 서로 연관되어 있으므로 차례대로 수행해 보기 바란다.

인덱싱하기

>>> t1 = (1, 2, 'a', 'b')
>>> t1[0]
1
>>> t1[3]
'b'

문자열, 리스트와 마찬가지로 t1[0], t1[3]처럼 인덱싱이 가능하다.

슬라이싱하기

>>> t1 = (1, 2, 'a', 'b')
>>> t1[1:]
(2, 'a', 'b')

t1[1]부터 튜플의 마지막 요소까지 슬라이싱하는 예이다.

튜플 더하기

>>> t1 = (1, 2, 'a', 'b')
>>> t2 = (3, 4)
>>> t1 + t2
(1, 2, 'a', 'b', 3, 4)

튜플을 더하는 방법을 보여 주는 예이다.

튜플 곱하기

>>> t2 = (3, 4)
>>> t2 * 3
(3, 4, 3, 4, 3, 4)

튜플의 곱하기(반복) 예를 보여 준다.

튜플 길이 구하기

>>> t1 = (1, 2, 'a', 'b')
>>> len(t1)
4

튜플의 길이를 구하는 예이다.

블로그 이미지

remoted

Remoted's IT LAB & POST DATABASE

댓글을 달아 주세요

Python Basic I - List

PYTHON 2019. 3. 24. 11:18


그러니까 List 라는 Object는 print 안에서 ,(콤마) 로 나열될 경우에는 뒤에 string 형태가 올 수 없다. 

(string 형태가 개입될 시에, 구분지을 수 없기 때문에 에러를 출력한다.)


따라서 new line 등을 넣고싶을 경우에는 다음과 같이 라인을 나눠야한다.

블로그 이미지

remoted

Remoted's IT LAB & POST DATABASE

댓글을 달아 주세요