Python/[코칭스터디 9기] 인공지능 AI 기초 다지기
[인공지능(AI) 기초 다지기] 3. 기초 수학 첫걸음 (1)
김초송
2023. 2. 1. 20:42
https://www.boostcourse.org/ai100/joinLectures/233687?isDesc=false
인공지능(AI) 기초 다지기
부스트코스 무료 강의
www.boostcourse.org
1. Numpy / 벡터 & 행렬
1) Numerical Python - Numpy
- Numpy
- 파이썬의 고성능 과학 계산용 패키지
- Matrix와 Vector와 같은 Array 연산의 표준
- List보다 빠르고 메모리 효율적
- 반복문 없이 데이터 배열에 대한 처리 지원
- 선형대수와 관련된 다양한 기능 제공
- C, C++, 포트란 등의 언어와 통합 가능
# Array creation
import numpy as np
test_array = np.array(["1", "4", 5, 8], float) # 모두 float으로 자동 형변환
print(test_array)
# array([ 1., 4., 5., 8.]) -> 모두 float 타입
type(test_array[3])
print(test_array.dtype) # tuple
# (4,)
print(test_array.shape)
np.array([["1", "4", 5, 8]], np.float32).shape
# (1, 4)
- numpy는 np.array 함수를 활용하여 배열 생성 -> ndarray
- numpy는 하나의 데이터 type만 배열에 넣을 수 있음
- Dynamic typing (실행 시점에 데이터 타입이 결정됨) not supported -> List와 가장 큰 차이점
- List : 메모리의 주소를 저장, 복사도 메모리 주소가 복사
Numpy Array : 차례대로 값을 쌓기 때문에 빠르게 데이터 처리 - 선언 시점에 데이터 타입을 선언하면 그 데이터 타입만 넣을 수 있음
- .shape() : dimension 반환 -> matrix의 크기를 결정하기 때문에 매우 중요!!!
- .dtype() : array의 single element 데이터 타입 반환 -> 각 element가 차지하는 memory의 크기가 결정됨
- 3rd order tensor : 주로 이미지 처리할 때 사용 (depth, row, column)
- .ndim : number of dimension, dimension의 크기
- .size : 전체 데이터의 갯수
- .nbytes : ndarray의 메모리 크기 반환
- Handling Shape
- reshape
- array의 size는 같지만 dimension이 달라짐 (element 갯수는 같지만 shape 크기를 변경)
- size만 같다면 다차원으로 자유로이 변형 가능
- -1 : size를 기반으로 갯수 선정
ex) 2차원 -> vector , sklearn에서는 y가 vector가 아닌 matrix 형태로 들어가야 할 때
test_matrix = [[1, 2, 3, 4], [1, 2, 45, 8]]
np.array(test_matrix).shape
# (2, 4)
np.array(test_matrix).reshape(2, 2, 2)
'''
array([[[1, 2],
[3, 4]],
[[1, 2],
[5, 8]]])
'''
np.array(test_matrix).reshape(-1, 2).shape # -1 : row의 갯수를 정확히 모를 때
# (4, 2)
- flatten
- 다차원 array를 1차원 array로 변환
- reshape로도 구현 가능
- ex) 문자를 vector로 표현할 때
- Indexing & Slicing
- indexing
- List와 달리 이차원 배열에서 [0,0] ( = [0][0] ) 과 같은 표기법을 제공
- Matrix일 경우 앞은 row, 뒤는 column을 의미
- slicing
- List와 달리 행과 열 부분을 나눠 slicing 가능
- Matrix의 부분 집합을 추출할 때 유용
- [ : , : ] : [row, column] -> for문으로 데이터 안 가져와도 됨
- Arange
- array의 범위를 지정하여 값의 list를 생성하는 명령어
- List의 range와 같은 효과
np.arange(30)
# np.arange(시작, 끝, 간격) -> List는 floating point 불가능
np.arange(0, 5, 0.5)
# Array to List
np.arange(0, 5, 0.5).tolist()
# List to Array
np.arange(30).reshape(5, 6)
- Creation Function
- - Ones, Zeros and Empty
- np.zeros(shape, dtype, order) : 0으로 가득찬 ndarray 생성
- ones 동일
- np.empty : shape만 주어지고 비어있는 ndarray 생성
memory initialization 되지 않음 = 메모리 공간만 잡아줌
(실제로 거의 사용하지 않는 듯) - tensorflow : numpy에 미분 가능하게 해준 것
- something_like : 기존 ndarray의 shape 크기 만큼 1, 0 또는 empty array 반환
np.zeros(shape=(10,), dtype=np.int8) # zero vector 생성
np.zeros((2, 5)) # zero matrix 생성 (float 형식)
np.empty(shape=(10,), dtype=np.int8)
np.empty((3, 5))
test_matrix = np.arange(30).reshape(5, 6)
np.ones_like(test_matrix)
- Identity
- 단위 행렬 (i 행렬) 생성
np.identity(n=3, dtype=np.int8) # n : number of rows
'''
array([1, 0, 0],
[0, 1, 0],
[0, 0, 1]], dtype=int8)
'''
np.identity(5)
- Eye
- 대각선인 1인 행렬, k값의 시작 index 변경 가능
np.eye(N=3, M=5, dtype=np.int8)
'''
array([1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0]], dtype=int8)
'''
np.eye(3, 5, k=2)
'''
array([0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0. ,0., 0., 0., 1.]])
'''
- diag
- 대각 행렬의 값을 추출함
matrix = np.arange(9).reshape(3, 3)
np.diag(matrix)
# array([0. 4. 8])
np.diag(matrix, k=1) # k : start index
# array([1, 5])
- random sampling
- 데이터 분포에 따른 sampling으로 array를 생성
np.random.uniform(0, 1, 10).reshape(2, 5) # 균등분포
np.random.normal(0, 1, 1).reshape(2, 5) # 정규분포
- Operation Function
- sum
- ndarray의 element들 간의 합 = List의 sum
- mean & std
- ndarray의 element들 간의 평균 또는 표준 편차를 반환
test_array = np.arange(1, 11)
test_array.sum(dtype=np.float)
# 55.0
- axis
- 모든 operation function을 실행할 때, 기준이 되는 dimension 축
- axis = 0 : 세로 (열) / axis = 1 : 가로 (행)
- (3, 4) : (axis=0 : 새로 생기는 dimension, axis=1 : 기존 dimension)
test_array = np.arange(1, 13).reshape(3, 4)
test_array.sum(axis=1), test_array.sum(axis=0)
# (array([10, 26, 42]), array([15, 18, 21, 24]))
- concatenate
- array를 합치는 함수
- vstack : vector(행) 방향으로 붙임
hstack : 열 방향으로 붙임 - np.concatenate( (arr1, arr2), axis=0 ) : 행으로 붙임
np.concatenate( (arr1, arr2.T), axis=1 ) : 열로 붙임
a = np.array([[1, 2, 3]])
b = np.array([[2, 3, 4]])
np.concatenate( (a, b), axis=0 )
np.vstack((a, b))
'''
array([[1, 2, 3],
[2, 3, 4]])
'''
- Array Operation
- Operations b/t arrays
- Numpy는 array(vector, matrix 등)간의 기본적인 사칙 연산을 지원함
- * : 행렬곱이 아닌 matrix 내 같은 위치에 있는 element의 값들 끼리 연산
선행대수 계산 -> x.dot(b)
- Element-wise operation
- Array간 shape이 같을 때 일어나는 연산
- Dot product
- Matrix의 기본 연산
- dot 함수 사용
# element_wise
matrix_a = np.arange(1, 13).reshape(3, 4)
matrix_a * matrix_a
'''
array([[ 1, 4, 9, 16],
[ 25, 36, 49, 64],
[ 81, 100, 121, 144]])
'''
# dot product
test_a = np.arange(1, 7).reshape(2, 3)
test_b = np.arange(7, 13).reshape(3, 2)
test_a.dot(test_b)
'''
array([[ 58, 64],
[139, 154]])
'''
- Transpose (역행렬)
- transpose 또는 T arribute 사용
test_a = np.arange(1, 7).reshape(2, 3)
test_a.transpose()
test_a.T
'''
array([[1. 4],
[2, 5],
[3, 6]])
'''
- Broadcasting
- shape이 다른 배열 간 연산을 지원하는 기능
- scalar - vector / vector - matrix 간 연산도 지원
matrix - vector 가 가장 많이 쓰이는 듯,,?
test_matrix = np.array([[1, 2, 3], [4, 5, 6]], float)
scalar = 3
test_matrix + scalar # Matrix - Scalar 덧셈
'''
array([[4., 5., 6.],
[7., 8., 9.]])
'''
- Numpy performance
- 일반적으로 속도는 for loop > list comprehension > numpy 순
100,000,000 번의 loop이 돌 때 약 약 4배 이상의 성능 차이 - Numpy는 C로 구현되어 있어, 성능을 확보하는 대신 파이썬의 가장 큰 특징인 dynamic typing을 포기
- 대용량 계산에서는 가장 흔히 사용됨
- concatenate처럼 할당에서는 연산 속도의 이점이 없음 -> 리스트가 더 빠름
- 일반적으로 속도는 for loop > list comprehension > numpy 순
def sclar_vector_product(scalar, vector):
result = []
for value in vector:
result.append(scalar * value)
return result
iternation_max = 100000000
vector = list(range(iternation_max))
scalar = 2
# timeit : jupyter에서 코드 퍼포먼스를 체크하는 함수
%timeit sclar_vector_product(scalar, vector) # for loop을 이용한 성능
%timeit [scalar * value for value in range(iternation_max)] # list comprehension을 이용한 성능
%timeit np.arange(iternation_max) * scalar # numpy를 이용한 성능
- Comparisons
- All & Any
- Array의 데이터 전부(and) 또는 일부(or)가 조건에 만족 여부 반환
- 일종의 broadcasting (scalar와 matrix 비교)
- All : 모두 조건에 만족하면 True
- Any : 하나라도 조건에 만족하면 True
- all / any 안 쓰면 모든 elemenet의 boolean 값이 리스트로 반환
a = np.arange(10)
a>5
# array([False, False, False, False, False, False, True, True, True, True])
np.any(a>5)
# True
- logical_and / logical_not / logical_or
- and : 두 개의 조건을 모두 만족하면 True
- not : 모든 조건을 만족하지 않을 경우 True
- where
- np.where(condition, true, false) : condition이 True일 경우 true 값 반환, False일 경우 false 값 반환
- np.where(condition) : condition에 만족하는 index 값 반환
- np.where + sort랑 같이 많이 씀
- np.isnan(arr) : Not a Number(null) 값 T/F
- np.isfinite(arr) : is finite number
- argmax & argmin
- argument의 max/min값의 index 반환
- axis 기반으로 반환할 수 있음
a = np.array([1, 2, 34, 5, 8, 78, 23, 3])
np.argmax(a), np.argmin(a)
# (5, 0)
# axis 기준 최대/최솟값의 index
a = np.array([[1, 2, 4, 7], [9, 88, 6, 45], [9, 76, 3, 4]])
np.argmax(a, axis=1), np.argmin(a, axis=0)
# (array([3, 1, 1]), array([0, 0, 2, 2]))
- numpy에 for 문 안 쓰고 원하는 값 찾을 수 있는 방법!! 꼭 기억할 것
- Boolean & Fancy Index
- boolean index
- numpy는 특정 조건에 따른 값을 배열 형태로 추출할 수 있음
- comparison operation 함수들 모두 사용 가능
- 조건이 True인 index의 element만 추출
test_array = np.array([1, 4, 0, 2, 3, 8, 9, 7], float)
test _array > 3
# array([False, True, False, False, False, True, True, True], dtype=bool)
# boolean index
test_array[test_array > 3]
condition = test_array > 3
test_array[condition]
# array([4., 8., 9., 7.])
# 15가 넘는 값들을 binary array로 반환
B = A < 15
B.astype(np.int)
- fancy index ( = bracket index)
- numpy는 array를 index value로 사용해서 값을 추출하는 방법
- index array는 반드시 integer로 선언
- .take : bracket index와 같은 효과 -> 더 권장
- 추천쪽에서 많이 사용..?
- matrix 형태 데이터도 가능
a = np.array([2, 4, 6, 8], float)
b = np.array([0, 0, 1, 3, 2, 1], int)
a[b]
a.take(b)
# array([2., 2., 4., 8., 6., 4.])
# matrix
a = np.array([[1, 4], [9, 16]], float)
b = np.array([0, 0, 1, 1, 0], int)
c = np.array([0, 1, 1, 1, 1], int)
# b를 row index, c를 column index로 변환하여 표시
a[b, c]
# array([ 1., 4., 16., 16., 4.])