Numpy 기초 사용법 🌟
Numpy는 Python에서 강력하고 유용한 수치 계산 라이브러리로, 대규모 데이터 배열 및 행렬 연산을 효율적으로 처리할 수 있게 도와줍니다. 이번 글에서는 Numpy의 기본적인 내장 메서드와 기능들에 대해 살펴보겠습니다. 🚀 또한 Numpy와 행렬 연산, 딥러닝의 관계도 함께 알아보겠습니다.
파이썬 데이터분석을 위한 라이브러리 설치
# $conda create -n 가상환경이름 python=3.10 openssl numpy scipy matplotlib ipython scikit-learn pandas pillow jupyter seaborn
1. Numpy 배열의 구조와 기본 메서드 🔍
배열의 모양(shape) 이해하기 🧮
Numpy 배열은 다양한 차원을 가질 수 있으며, 각 차원의 크기를 shape 속성을 통해 확인할 수 있습니다.
# shape 예시
# shape: (4,) - 리스트 또는 1차원 벡터
# shape: (2, 3) - 행 2개, 열 3개 (2차원 배열)
# shape: (4, 3, 2) - 3차원 배열 (행 4개, 열 3개, 깊이 2)
shape는 배열의 구조를 이해하는 데 매우 유용합니다. 예를 들어, 딥러닝에서 데이터셋의 크기나 모델의 출력 크기를 확인할 때 자주 사용됩니다.
배열 생성 및 타입 확인 🛠️
import numpy as np
# 배열 생성
x = np.array([1, 2, 3, 4, 5])
print("배열 x:", x)
# 배열의 데이터 타입 확인
print("Data Type:", x.dtype)
배열 저장 및 불러오기 💾
메모리에 있는 Numpy 배열을 저장하고 불러올 수 있습니다.
# 배열 저장
np.save('my_array', x) # 'my_array.npy' 파일 생성
# 배열 불러오기
y = np.load('my_array.npy')
print("불러온 배열 y:", y)
Tip: 저장한 파일이 실제로 생성되었는지 확인하려면 작업 디렉토리를 확인하세요!
2. 다양한 배열 생성 방법 🔄
Numpy는 다양한 방식으로 배열을 생성할 수 있습니다.
# 0으로 채워진 배열 생성
zeros_array = np.zeros((3, 4))
print("Zeros Array:\n", zeros_array)
# 특정 값으로 채워진 배열 생성
full_array = np.full((7,), 5)
print("Full Array:", full_array)
# 연속된 값으로 채워진 배열 생성
range_array = np.arange(5, 21) # 5부터 20까지
print("Range Array:", range_array)
# 랜덤 값으로 채워진 배열 생성
random_array = np.random.random((10,))
print("Random Array:", random_array)
# 선형 간격을 가진 배열 생성
linspace_array = np.linspace(0, 25, 10) # 0부터 25까지 10개의 값 생성
print("Linspace Array:", linspace_array)
3. 배열 재구성 (Reshape) 🔄
reshape 메서드를 사용하면 배열의 형태를 변경할 수 있습니다. 원래 배열의 요소 수는 변경되지 않아야 합니다.
# 배열 재구성
original_array = np.arange(20)
reshaped_array = original_array.reshape(4, 5)
4. 최소값, 최대값, 합계, 평균 등 계산 🔢
Numpy 배열에서 데이터를 빠르게 분석할 수 있는 다양한 메서드가 있습니다.
# 배열 생성
array = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
# 데이터 전체 분석
print("최소값:", array.min())
print("최대값:", array.max())
print("전체 합:", array.sum())
print("전체 평균:", array.mean())
print("표준편차:", array.std())
print("중앙값:", np.median(array))
# 각 행별 최대값 (축 기준 계산)
print("각 행별 최대값:", array.max(axis=1))
# 각 열별 합
print("각 열별 합:", array.sum(axis=0))
출력:
최소값: 10
최대값: 90
전체 합: 450
전체 평균: 50.0
표준편차: 25.81988897471611
중앙값: 50.0
각 행별 최대값: [30 60 90]
각 열별 합: [120 150 180]
5. 수학적 연산 ➕➖✖️➗
Numpy는 다양한 수학 연산을 지원합니다. 배열 간의 요소별 연산, 스칼라 곱셈, 제곱근 계산 등이 가능합니다.
# 배열 생성
array1 = np.array([1, 2, 3])
array2 = np.array([4, 5, 6])
# 요소별 덧셈
print("Addition:", array1 + array2)
# 요소별 곱셈
print("Multiplication:", array1 * array2)
# 스칼라 곱셈
print("Scalar Multiplication:", array1 * 2)
# 제곱근 계산
print("Square Root:", np.sqrt(array1))
출력:
Addition: [5 7 9]
Multiplication: [ 4 10 18]
Scalar Multiplication: [2 4 6]
Square Root: [1. 1.41421356 1.73205081]
6. 인덱싱과 슬라이싱 ✂️
Numpy 배열은 Python 리스트와 유사한 방식으로 인덱싱과 슬라이싱을 지원합니다.
# 2차원 배열 생성
array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 특정 요소 접근- 2행 3열의 원소 가져오기
print("Element [1, 2]:", array[1, 2])
# 특정 행 슬라이싱- 2행과 모든 열 가져오기
print("Row 2:", array[1, :])
# 특정 열 슬라이싱- 모든행의 3열 가져오기
print("Column 3:", array[:, 2])
# 하위 배열 슬라이싱- 행 인덱스 0에서 1까지, 열 인덱스 1부터 2까지
print("Subarray:")
print(array[0:2, 1:3])
출력:
Element [1, 2]: 6
Row 1: [4 5 6]
Column 2: [3 6 9]
Subarray:
[[2 3]
[5 6]]
Numpy, 행렬 연산, 딥러닝의 관계 🤔
CPU는 왜 행렬 처리에 적합하지 않을까?
CPU는 직렬 처리(순차적으로 하나씩 작업을 수행)하도록 설계되어 있어, 대량의 병렬 연산(예: 대규모 행렬 연산)을 수행하기에는 상대적으로 비효율적입니다. 반면, GPU는 수천 개의 코어를 이용해 병렬로 행렬 연산을 매우 빠르게 처리할 수 있도록 설계되었습니다.
딥러닝에서 행렬이 중요한 이유
딥러닝에서는 뉴런 간의 연결과 가중치 업데이트가 행렬로 표현됩니다. 예를 들어:
- 입력 데이터는 행렬로 표현됩니다. (예: 이미지 데이터 → 픽셀 값의 행렬)
- 뉴런과 가중치의 연산도 행렬 곱셈으로 처리됩니다.
- 백워드 패스(역전파)는 행렬 미분 연산을 통해 가중치를 업데이트합니다.
왜 행렬 연산이 빠를까?
행렬 연산은 고도로 최적화된 알고리즘과 하드웨어 지원 덕분에 빠릅니다.
- BLAS 라이브러리: CPU에서도 행렬 연산을 가속화하기 위한 저수준 최적화 라이브러리를 활용합니다.
- 캐시 활용: 행렬 연산은 데이터의 연속적인 접근 패턴이 많아, CPU/GPU 캐시를 효과적으로 활용할 수 있습니다.
Numpy와 딥러닝의 연결고리
딥러닝은 대량의 데이터를 입력, 처리, 학습하는 과정을 반복하며, 이 과정에서 행렬 연산이 핵심 역할을 합니다. 이러한 행렬 연산은 Numpy를 통해 효율적으로 구현될 수 있습니다. 실제로 많은 딥러닝 프레임워크(TensorFlow, PyTorch 등)도 내부적으로 Numpy 스타일의 연산을 사용합니다. 이를 통해 CPU에서도 효율적인 행렬 연산이 가능합니다!