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처럼 할당에서는 연산 속도의 이점이 없음 -> 리스트가 더 빠름
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.])