인공지능 (AI)

인공지능 프로그래밍 기초 - Numpy

sobal 2025. 6. 23. 23:43

1. 파이썬

 

먼저 파이썬은 코드를 한 번에 기계어로 번역해서 실행파일로 만들지 않고 한 줄씩 읽어서 해석하고 실행하는 인터프리터 언어이다. 따라서 코드를 실행하는 도중에 점검하거나 피드백할 수 있다. 물론 순수 C/C++ 언어보다 런타임 오버헤드가 커 실행 속도가 느리다는 단점이 존재한다.

그리고 파이썬은 간결하고 명료한 문법, 동적 타이피과 덕 타이핑, 방대한 라이브러리, 강력한 데이터 처리 능력 덕분에 인공지능에서 많이 사용되고 있다.

 

2. Colab

 

일반적으로 개인 PC의 성능이 굉장히 여유있다거나 다른 특별한 이유가 있는 게 아니라면 간단한 인공지능 개발 실습을 할 때 Colab을 주로 사용하는 것 같다.

Colab의 특징은 다음과 같다.

  •   TensorFlow, PyTorch, Keras, scikit-learn, OpenCV, Hugging Face Transformers 등 AI/딥러닝에 필요한 거의 모든 패키지가 미리 설치되어 있어, !pip install 없이 바로 코드 작성·실행이 가능하다
  • 보기 편한 Jupyter Notebook 형태로 동작해 한 화면에서 코드, 출력, 그래프, 설명등을 통합 관리할 수 있다.
  • 또한 무료로 어느정도 여유있는 GPU,TPU 자원을 제공한다.
  • 구글 드라이브나 github를 통해 손쉽게 공유 및 연동을 할 수 있다. 
  • 필요할 때 !pip install 또는 apt-get으로 추가 패키지 및 시스템 의존성도 손쉽게 설치할 수 있다.
  • 마크다운 문법이 지원된다.

3. Numpy

 

Numpy는 Numerical Python의 약자이고 한국어로는 넘파이라고 읽는다. 다차원 배열을 효율적으로 처리하고 쉽게 사용할 수 있도록 해주는 파이썬 라이브러리다. 이 Numpy는 데이터 구조 외에도 수치 계산을 위해 효율적으로 구현된 기능을 제공해주고 파이썬으로 진행하는 데이터 분석과 인공지능 학습에 있어 가장 필수적인 라이브러리라고 할 수 있다.

 

Numpy의 특징은 다음과 같다.

  • 고성능 다차원 배열 객체인 ndarray를 통해 n차원 배열을 효율적으로 저장하고 처리할 수 있어 list보다 속도가 빠르고 메모리 사용량이 적다.
  • 내부 C 루프를 이용해 반복문 없이 전체 배열 연산을 수행하여 코드를 간결하고 빠르게 실행할 수 있다. 
  • 덧셈, 뺄셈, 곱셈, 지수, 삼각함수 등 수 백가지의 기본 함수가 제공된다.
  • C/C++,Fortran 같은 언어 통합이 가능하다.
  • BSD 라이선스 하에 활발히 개발 및 배포되고 어느 운영체제에서나 동일하게 동작한다.

 

다차원 배열 (N-Dimensional Array) 생성 :

import numpy as np
print(np.__version__) # 버전 확인

np.array([1, 2, 3, 4, 5])
np.array([[1, 2, 3],
 [4, 5, 6]])
np.array([[[1, 2, 3, 4],
 [5, 6, 7, 8]],
 [[9, 10, 11, 12],
 [13, 14, 15, 16]],
 [[17, 18, 19, 20],
 [21, 22, 23, 24]]])

 

Numpy 모듈 : Numpy 모듈은 np로 사용 가능하다.

import numpy as np
print(np.__version__)

 

ndarray 객체 : N-dimensional Array의 약자이며 Numpy에서 다차원 배열을 나타내는 Numpy의 핵심이다. 기본적으로 array() 함수를 사용해서 다차원 배열을 생성하고 모든 원소는 같은 자료형이어야 한다. 또한 C처럼 한 번 생성된 배열은 원소의 개수를 바꿀 수 없다.

 

Numpy 실습 : 

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

arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr)
print(type(arr))

arr = np.array([[[1, 2, 3, 4], [5, 6, 7, 8]],
 [[9, 10, 11, 12], [13, 14, 15, 16]],
 [[17, 18, 19, 20], [21, 22, 23, 24]]])
print(arr)
print(type(arr))

 

배열의 속성 : 

 

넘파이 배열은 다음과 같은 속성을 가지는데 여기서 특히 shape와 dtype 속성이 중요하다.

  • shape : 배열의 형태를 튜플 자료형으로 표현
  • ndim : 몇 차원인지 
  • size : 원소의 개수
  • dtype : 배열 원소의 자료형
  • itemsize : 배열 원소의 크기 (바이트 단위)

아래와 같이 print_att() 함수를 정의해서 NumPy 배열의 주요 속성(attribute)들을 한 번에 출력해보자. 

import numpy as np

def print_att(arr):

    print("shape    :", arr.shape)     # 배열의 차원 크기 (tuple)
    print("ndim     :", arr.ndim)      # 배열의 축(axis) 개수
    print("size     :", arr.size)      # 전체 원소(element) 개수
    print("dtype    :", arr.dtype)     # 원소 하나의 데이터 타입
    print("itemsize :", arr.itemsize)  # 원소 하나가 차지하는 바이트(byte) 크기

 

np.array() 실습 : 

arr = np.array([1, 2, 3, 4])
print_att(arr)
arr = np.array([[[1, 2, 3, 4], [5, 6, 7, 8]],
 [[9, 10, 11, 12], [13, 14, 15, 16]],
 [[17, 18, 19, 20], [21, 22, 23, 24]]])
print(arr)
print(type(arr))

 

배열의 자료형 : 

 

배열의 자료형에는 기본적로 Boolean, Integer, Unsigned Integer, Float 이 있다.

# 배열 지정
arr = np.array([[1, 2, 3],
 [4, 5, 6]], dtype=np.float64) # 자료형 타입 정하기

# 자료형 확인 
arr.dtype 

# 자료형 변환
arr.astype(np.int32)

 

NumPy 배열을 생성할 때, array() 함수의 dtype 인수로 원하는 자료형을 명시적으로 지정할 수 있다.
지정 가능한 자료형 표시는 크게 다음 세 가지 형태가 있다.

  1. NumPy 내장 타입 객체
    • 이들 각각은 np.dtype 객체이고 배열을 생성할 때 곧바로 사용 가능하다.
np.int32, np.int64, np.float32, np.float64, np.bool_, np.complex128
# 예시
a = np.array([1, 2, 3], dtype=np.int32) b = np.array([1.0, 2.5], dtype=np.float64)

 

   2. 문자열(type‐specifier) 표기법

'i4', 'i8',  # signed integer 4바이트, 8바이트
'u2',        # unsigned integer 2바이트
'f4', 'f8',  # float 4바이트(float32), 8바이트(float64)
'c16',       # complex float 16바이트(complex64)
'U10',       # 유니코드 문자열, 최대 10문자
'S20',       # 바이트열(bytes), 최대 20바이트

 

  • 앞 글자는 타입(i: signed integer, u: unsigned integer, f: float, c: complex, U: Unicode, S: bytes), 뒤 숫자는 바이트 단위 크기를 뜻한다.
  • 예: a = np.array([1,2,3], dtype='i4') (np.int32 와 동일), b = np.array([1.5,2.5], dtype='f8') (np.float64 와 동일)

   3.파이썬 내장 타입

  • 이 경우 NumPy가 대응되는 기본 dtype으로 자동 매핑한다.
  • 예: a = np.array([1,2,3], dtype=int) (보통 np.int64 로 생성), b = np.array([True, False], dtype=bool) (np.bool_ 로 생성)

dtype 인수를 생략하면, 입력된 데이터의 타입을 보고 가장 적절한 공통형으로 자동 추론한다.

  • 정수만 있으면 시스템 기본 정수형(대개 int64)
  • 실수가 섞이면 float64
  • 문자열이 섞이면 모두 U 계열의 유니코드 문자열

호환되지 않는 서로 다른 타입(예: 정수 + 문자열)이 함께 들어오면, 더 상위 호환되는 타입(예: 문자열)으로 자동 변환된다.

 

강제 변환(캐스팅) :

 

dtype을 명시하면, 입력 데이터와 관계없이 해당 자료형으로 배열이 생성된다.

값이 해당 타입 표현 범위를 벗어나면

  • 정수 → 실수: 소수점이 유지
  • 실수 → 정수: 소수점 이하가 버림(truncate)
  • overflow/underflow 시에는 wrap-around 또는 Inf, NaN 발생

변환 불가능한 경우(예: 문자열 'abc' → 정수)에는 에러가 발생한다.

좀 더 깊게 들어가면 바이트 오더(endian) & 구조화된(dtype 복합) 타입이 있다.

 

자료형 반환 : 생성된 배열의 자료형의 변경은 astype() 함수를 사용한다.

 

실습 : 

x = np.array([1, 2, 3, 4])
y = np.array([1, 2, 3, 4], dtype=np.int64)
z = np.array([1, 2, 3, 4], dtype=np.float32)
print(x.dtype, y.dtype, z.dtype)

a = np.array([1.0, 2.0, 3.0, 4.0])
b = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.int32)
c = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32)
print(a.dtype, b.dtype, c.dtype)

d = np.array([1.1, .2, 17, 41])
e = np.array([1.1, .2, 17, 41], dtype=np.int32)
f = np.array([1.1, .2, 17, 41], dtype=np.float32)
print(d.dtype, e.dtype, f.dtype)

h = np.array([1, 2, 3, 4])
print('before : ', h.dtype)
i = i.astype(np.float64)
print('after : ', i.dtype)

 

배열의 형태(shape)와 축(Axis) :

 

축은 기본적으로 우리가 알고 있는 x축, y축, z축과 같은 것으로 이해하면 된다.

shape:(4,3,5)와 같이 나타내며, 순서대로 axis 0, axis 1, axis 2의 개수이다.

배열의 형태는 ndarray 객체의 shape 속성에 튜플 자료형으로 가지고 있는데 shape 속성의 튜플 원소의 개수는 축(axis)의 개수와 같고, 튜플 원소의 순서는 axis의 순서와 일치한다.

 

실습 : 

arr = np.array([[[1, 2, 3, 4], [5, 6, 7, 8]],
 [[9, 10, 11, 12], [13, 14, 15, 16]],
 [[17, 18, 19, 20], [21, 22, 23, 24]]])
print('shape :', arr.shape)

x = np.array([1, 2, 3, 4, 5])
y = np.array([[1, 2, 3, 4, 5]])
z = np.array([[1], [2], [3], [4], [5]])
print(x.shape, y.shape, z.shape)

 

참고로 np.array([1, 2, 3, 4, 5])는  shape : (5, )와 같이 표현되고 축의 개수는 1개이지만

np.array([[1, 2, 3, 4, 5]]) 는 shape : (1, 5) 와 같이 표현되고 축의 개수는 2개이다.

 

배열의 생성함수 :

 

arange() 함수

  • 파이썬 내장 range()처럼 지정한 범위의 연속적인 값을 갖는 1차원 배열을 생성
  • 시그니처 : np.arange(start, stop, step, dtype=None)
    • start : 시작값 (기본 0)
    • stop : 끝값 (끝 숫자는 포함되지 않음)
    • step : 간격 (기본 1)
    • dtype : 결과 배열의 자료형 (지정하지 않으면 입력값 유형에 따라 추론)
  • 주의: 실수 step을 사용할 경우 부동소수점 오차로 원하는 개수만큼 정확히 나눠지지 않을 수 있음
  • 예시:
np.arange(0, 5)        # array([0,1,2,3,4])
np.arange(1, 2, 0.2)   # array([1. ,1.2,1.4,1.6,1.8])
np.arange(5, dtype=np.int16)  # dtype=int16

linspace() 함수

  • 지정한 구간을 균일한 간격으로 나눠서 값 생성
  • 시그니처: np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
    • start : 구간 시작값
    • stop : 구간 끝값
    • num : 생성할 숫자의 개수 (기본 50)
    • endpoint : True면 stop 포함, False면 미포함
    • retstep : 간격값 반환 여부, 기본값은 False
    • dtype : 배열의 자료형
    • axis : 다차원 배열에서 값을 배치할 축

zeros() 함수

  • 모두 0으로 채워진 배열 생성
  • 시그니처: np.zeros(shape, dtype=float, order='C')
    • shape : 배열의 모양, 튜플 또는 정수 (예: (3,4), 5)
    • dtype : 자료형 (기본 float64)
    • order : 메모리 배치순서 ('C' row‐major, 'F' column‐major)
  • 2차원 이상일 경우 반드시 shape=(...,...) 형태로 지정

ones() 함수

  • 모두 1로 채워진 배열 생성
  • 시그니처: np.ones(shape, dtype=float, order='C')
    • shape, dtype, order 인수 동일

full() 함수

  • 지정한 값으로 채워진 배열 생성
  • 시그니처: np.full(shape, fill_value, dtype=None, order='C')
    • shape : 배열 형상
    • fill_value : 채울 값 (스칼라)
    • dtype : 자료형 (생략 시 fill_value 타입 추론)
    • order : 메모리 레이아웃

ones_like(), zeros_like() 함수

  • 기존 배열과 동일한 형상(shape)과 기본 자료형을 갖는 배열을 생성하고, 모두 1 또는 0으로 초기화
  • 시그니처: 
  • np.zeros_like(a, dtype=None, order='K', subok=True, shape=None)
    np.ones_like(a, dtype=None, order='K', subok=True, shape=None) 
    • a : 참조 대상 배열
    • dtype : 자료형 강제 지정 (기본 a.dtype)
    • order : 메모리 배치 순서 ('K'는 원본과 동일)
    • subok : 서브클래스 반환 여부 (기본값: True)
    • shape : 새로운 모양 지정 (None이면 원본과 동일)

실습 :

x = np.arange(0, 20)
y = np.arange(0, 20, 0.5)
z = np.arange(10, 0, -2)
print(x)
print(y)
print(z)

x = np.linspace(0, 30, 5)
y = np.linspace(0, 1, 5)
print(x)
print(y)

x = np.zeros(5)
y = np.zeros((1, 4))
print(x, x.shape)
print(y, y.shape)

x = np.ones(5)
y = np.ones((1, 4))
print(x, x.shape)
print(y, y.shape)

x = np.full((1, 3), 0.5)
print(x)

x = np.array([[1, 2], [3, 4]])
print(x.shape)
y = np.zeros_like(x)
print(y, y.shape)
z = np.ones_like(x)
print(z, z.shape)

b = np.array([[1,2],[3,4]], dtype=np.int8)
np.zeros_like(b)       # 동일 형상, dtype=int8
np.ones_like(b, dtype=float)  # float64 타입