===============================
수업내용
Numpy
이걸 다 순수 파이썬으로? 너무 힘들고 느려..
numpy
Numerical Python
- 일반 List에 비해 빠르고, 메모리 효율적
- 다양한 기능 제공
- 반복문 없이 데이터 배열에 대한 처리를 지원함
(파이썬 라이브러리를 활용한 데이터 분석)
conda install numpy
ndarray
import numpy as np
test_array = np.array([1,4,"5",8], float)
- 하나의 데이터 타입만 가능
- dynamic typing not support
numpy는 순수 python과 달리 숫자 주소값이 아니라 값 자체를 저장해서 빠르고, 공간이 정해져 있어서 효율적
print(test_array.dtype) # dtype('float64') numpy array의 데이터 type을 반환함
print(test_array.shape) # (3,3) 등으로 표기됨.. numpy array의 dimension 구성을 반환함
- array의 rank에 따라 불리는 이름이 있음
Rank |
Name |
Example |
0 |
Scalar |
7 |
1 |
Vector |
[10, 10] |
2 |
Matrix |
[[10, 10], [15,15]] |
3 |
3-tensor |
[[[1, 5, 9], [2, 6, 10]], [[3, 7, 11],
[4, 8, 12] |
N |
n-tensor |
|
(4,)
(3,4) 행 열. 4가 3개쌓임
(4, 3, 4) . 4가 3개 쌓인게 4개 쌓임
- ndim - number of dimensions
- size - data의 개수
- C의 data type과 compatible
float32. 하나 숫자당 32bits
nbytes - ndarray object의 메모리 크기를 반환함
np.array([[1,2,3],[4.5,5,6]], dtype = np.float32).nbytes
32bits = 4bytes => 6 * 4bytes = 24bytes
np.array([[1,2,3],[4.5,5,6]], dtype = np.int8).nbytes8bits = 1bytes => 6 * 1bytes = 6bytes
- reshape: Array의 shape의 크기를 변경함, element의 갯수는 동일
-1: size를 기반으로 row 개수 선정(element는 동일하니까 알아서 계산)
데이터의 순서는 그대로
- flatten : 다차원 array를 1차원 array로 변환
reshape에서 따옴. 말 그대로 1차원으로 평평하게
indexing & slicing
콤마(,)로 indexing 가능
test_exmaple = np.array([[1, 2, 3], [4.5, 5, 6]], int)
test_exmaple[0,0]
test_exmaple[0,0] = 10 # Matrix 0,0 에 12 할당
test_exmaple[0][0] = 5 # Matrix 0,0 에 12 할당
어떤 곳이든 slicing 가능
test_exmaple = np.array([ [1, 2, 5,8], [1, 2, 5,8],[1, 2, 5,8],[1, 2, 5,8]], int)
test_exmaple[:,1:3] test_exmaple[1,:2]
a = np.arange(100).reshape(10,10)
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24 25 26 27 28 29]
[30 31 32 33 34 35 36 37 38 39]
[40 41 42 43 44 45 46 47 48 49]
[50 51 52 53 54 55 56 57 58 59]
[60 61 62 63 64 65 66 67 68 69]
[70 71 72 73 74 75 76 77 78 79]
[80 81 82 83 84 85 86 87 88 89]
[90 91 92 93 94 95 96 97 98 99]]
a[:, -1].reshape(-1,1)
[19]
[29]
[39]
[49]
[59]
[69]
[79]
[89]
[99]]
[start:end:step] 도 가능
creation function
array를 생성하는 함수들
np.range(30)
np.range(0, 5, 0.5) # step에 float point도 표시 가능
np.arange(30).reshape(5,6)
np.zeros(shape=(10,), dtype=np.int8)
np.zeros((2,5))
np.ones(shape=(10,), dtype=np.int8)
np.ones((2,5))
- empty : shape만 주어지고 비어있는 ndarray 생성 (memory initializetion이 되지 않음)
np.empty(shape=(10,), dtype=np.int8)
np.empty((2,5))
something_like
- 기존 ndarray의 shape 크기 만큼 1, 0 또는 empty array를 반환
test_matrix = np.array(30).reshape(5,6)
np.ones_like(test_matrix) # zeros_like, empty_like
[1,1,1,1,1,1],
...
[1,1,1,1,1,1]])
identity
- 단위 행렬(i 행렬)을 생성함
np.identity(n=3, dtype=np.int8) # n: number of rows
np.identity(5)
eye
- 대각선이 1인 행렬, k값의 시작 index의 변경이 가능
np.eye(3)
np.eye(N=3, M=5, dtype=np.int8)
np.eye(3,5,k=2) # k는 대각선 1 시작점
diag
- 대각 행렬의 값을 추출함
matrix = np.range(9).reshape(3,3)
np.diag(matrix)
array([0, 4, 8])
np.diag(matrix, k=1) # k => start index. k 음수도 가능
random sampling
- 데이터 분포에 따른 sampling으로 array를 생성
np.random.uniform(0, 1, 10).reshape(2,5) 균등분포
array([[ 0.67406593, 0.71072857, 0.06963986, 0.09194939, 0.47293574], [ 0.13840676, 0.97410297, 0.60703044, 0.04002073, 0.08057727]])
np.random.normal(0, 1, 10). reshape(2,5) 정규분포
array([[ 1.02694847, 0.39354215, 0.63411928, -1.03639086, -1.76669162],
[ 0.50628853, -1.42496802, 1.23288754, 1.26424168, 0.53718751]])
operation function
sum. list의 sum과 동일
test_array = np.array(1, 11)
test_array.sum(dtype=np.float)
axis
- 모든 operation function을 실행할 때 기준이 되는 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]))
(3, 4) 에서 axis 0은 3, axis1은 4가 됨
[ 5 6 7 8]
[ 9 10 11 12]]
test_array = np.arange(1,13).reshape(3,4)
third_order_tensor = np.array([test_array,test_array,test_array])
[ 5 6 7 8]
[ 9 10 11 12]]
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]]
third_order_tensor.sum(axis=0)
[15 18 21 24]
[27 30 33 36]]
third_order_tensor.sum(axis=1)
[15 18 21 24]
[15 18 21 24]]
third_order_tensor.sum(axis=2)
[10 26 42]
[10 26 42]]
test_array = np.arange(1,13).reshape(3,4)
array([[ 1, 2, 3, 4], [ 5, 6, 7, 8], [ 9, 10, 11, 12]])
test_array.mean(), test_array.mean(axis=0)
(6.5, array([ 5., 6., 7., 8.]))
test_array.std(), test_array.std(axis=0)
(3.4520525295346629,
array([ 3.26598632, 3.26598632, 3.26598632, 3.26598632]))
그 외에도 다양한 함수들.. numpy mathematical function 검색해봐
concatenate
- numpy array를 합치는(붙이는) 함수
a = np.array([1, 2, 3]) b = np.array([2, 3, 4]) np.vstack((a,b))
array([[1, 2, 3], [2, 3, 4]])
a = np.array([ [1], [2], [3]]) b = np.array([ [2], [3], [4]]) np.hstack((a,b))
[2, 3],
[3, 4]])
a = np.array([[1, 2, 3]])
b = np.array([[2, 3, 4]])
np.concatenate( (a,b) ,axis=0)
[2, 3, 4]])
a = np.array([[1, 2], [3, 4]]) b = np.array([[5, 6]]) np.concatenate( (a, b) ,axis=1)
array([[1, 2, 5],
[3, 4, 6]])
a = np.array([[1, 2], [3, 4]])
b = np.array([5, 6])
여기서 b 차원을 늘리는 방법
b = b.reshape(-1,2) 로 하던가
b = b[np.newaixs, :] 로 가능 np.concatenate( (a, b.T) ,axis=1)
array([[1, 2, 5], [3, 4, 6]])
array operation
numpy array 간의 기본적인 사칙 연산을 지원
a_array = np.array([[1,2,3],[4,5,6]], float)
a_array + a_array
a_array - a_array
a_array * a_array # Matrix 내 같은 위치에 있는 element들 끼리 연산. element wise operation 같은 shape일 때
test_a = np.arange(1,7).reshape(2,3) test_b = np.arange(7,13).reshape(3,2)
test_a.dot(test_b)
test_a.transpose()
test_a.T
test_a.T.dot(test_a) # Matrix 간 곱셈
broadcasting
- Shape이 다른 배열 간 연산을 지원하는 기능
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.]])
test_matrix - scalar
test_matrix * 5test_matrix / 5
test_matrix // 0.2
test_matrix ** 2
Scalar - vector 외에도 vector - matrix 간의 연산도 지원
크기가 달라도 알아서 채워서 진행
test_matrix = np.arange(1,13).reshape(4,3)
test_vector = np.arange(10,40,10)
print(test_matrix)
print(test_vector)
result = test_matrix + test_vector
print(result)
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[10 20 30]
[[11 22 33]
[14 25 36]
[17 28 39]
[20 31 42]]
일반적으로 속도는
for loop < list comprehension < numpy
100,000,000 번의 loop이 돌 때, 약 4배 이상의 성능 차이를 보임
comparisons
all & any
a = np.arange(10)
a<0
array([False, False, False, False, False, False, False, False, False, False], dtype=bool)
(True, False)
np.all(a>5) , np.all(a < 10)
(False, True)
- numpy는 배열의 크기가 동일 할 때 element간 비교의 결과를 Boolean type으로 반환
test_a = np.array([1, 3, 0], float) test_b = np.array([5, 2, 1], float) test_a > test_b
array([False, True, False], dtype=bool)
test_a == test_b
array([False, False, False], dtype=bool)
(test_a > test_b).any()
True
a = np.array([1, 3, 0], float) np.logical_and(a > 0, a < 3) # and 조건의 condition
array([ True, False, False], dtype=bool)
b = np.array([True, False, True], bool) np.logical_not(b) # NOT 조건의 condition
array([False, True, False], dtype=bool)
c = np.array([False, True, False], bool) np.logical_or(b, c) # OR 조건의 condition
array([ True, True, True], dtype=bool)
np.where
np.where(a > 0, 3, 2) # where(condition, TRUE, FALSE)
array([3, 3, 2])
true인 곳엔 3을, false인 곳엔 2를 넣어라
a = np.arange(5, 15)
array([ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
np.where(a>10)
(array([6, 7, 8, 9]),) # index 값 반환
a = np.array([1, np.NaN, np.Inf], float)np.isnan(a)
array([False, True, False], dtype=bool)
np.isfinite(a)
array([ True, False, False], dtype=bool)
argmax & argmin
- array 내 최대값 또는 최소값의 index를 반환함
a = np.array([1,2,4,5,8,78,23,3]) np.argmax(a) , np.argmin(a)
(5, 0)
- axis 기반의 반환
a=np.array([[1,2,4,7],[9,88,6,45],[9,76,3,4]])
[[ 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]))
boolean & fancy index
boolean index
- 특정 조건에 다른 값을 배열 형태로 추출
- Comparison operation 함수들도 모두사용 가능
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)
test_array[test_array > 3]
array([ 4., 8., 9., 7.])
condition = test_array < 3 test_array[condition]
array([ 1., 0., 2.])
matrix에서도 사용가능
fancy index
numpy는 array를 index value로 사용해서 값 추출
a = np.array([2,4,6,8], float)
b = np.array([0,0,1,3,2,1], int)
print(a[b])
print(a.take(b))
[2. 2. 4. 8. 6. 4.]
[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)
print(a[b,c]) # b를 row index, c를 column index로 변환하여 표시함
[ 1. 4. 16. 16. 4.]
a = np.array([[1, 4], [9, 16]], float)
print(a[b])
[[ 1. 4.]
[ 1. 4.]
[ 9. 16.]
[ 9. 16.]
[ 1. 4.]]
numpy data i/o
loadtxt & savetxt
a = np.loadtxt("./populations.txt") a[:10]
array([[ 1900., 30000., 4000., 48300.], [ 1901., 47200., 6100., 48200.], [ 1902., 70200., 9800., 41500.], [ 1903., 77400., 35200., 38200.], [ 1904., 36300., 59400., 40600.], [ 1905., 20600., 41700., 39800.], [ 1906., 18100., 19000., 38600.], [ 1907., 21400., 13000., 42300.], [ 1908., 22000., 8300., 44500.], [ 1909., 25400., 9100., 42100.]])
a_int = a.astype(int) a_int[:3]
array([[ 1900, 30000, 4000, 48300], [ 1901, 47200, 6100, 48200], [ 1902, 70200, 9800, 41500]])
np.savetxt('int_data.csv',a_int, delimiter=",")
numpy object. 을 npy 형태로도 저장 가능.
np.save("npy_test", arr=a_int)
npy_array = np.load(file="npy_test.npy") npy_array[:3]
array([[ 1900, 30000, 4000, 48300], [ 1901, 47200, 6100, 48200], [ 1902, 70200, 9800, 41500]])
[AI Math 1강] 벡터가 뭐에요?
내가 아는 그 벡터다.
같은 모양을 가진 벡터 끼리 더하고, 빼고, 성분곱 가능. 같은 위치의 성분끼리 변함.
노름 (norm)
원점에서부터의 거리 . ||x||. 임의의 차원 d에 대해 성립.
L1-노름은 각 성분의 변화량의 절대값을 모두 더한다.
L2-노름은 피타고라스 정리를 이용해 유클리드 거리를 계산한다.
L2-노름은 np.linalg.norm 이용해도 구현 가능
L1은 멘하탄, L2는 유클리드 인듯
노름의 종류에 따라 기하학적 성질이 달라진다.
노름에 따라 거리 개념이 달라짐.
두 벡터 사이의 거리.
벡터의 뺄셈을 이용. 뺄셈을 거꾸로 해도 거리는 같다.
두 벡터 사이의 각도. 제2 코사인 법칙. 각도는 L-2 노름으로만 계산 가능. 기하학적 정의라서?
d차원에서도? 가능. 일반적인 d차원에서도 계산이 가능하도록 하기 위해 L-2노름 쓴거다.
분자를 쉽게 계산하는 방법이 내적이다.
평범한 행렬곱셈은 X @ Y 으로 사용하고 행렬의 내적은 X @ Y.T 와 같은데 np.inner(X,Y)로 T 없이 사용 가능하다.
내적은 np.inner를 이용해서 계산한다.
def angle(x, y):
v = np.inner(x,y) / (l2_norm(x) * l2_norm(y))
theta = np.arccos(v) # cos 역함수 arccos
return theta
- 내적은 정사영(orthogonal projection)된 벡터의 길이과 관련있다.
- Proj(x)의 길이는 코사인법칙에 의해 ||x||cos(t) 가 된다.
- 내적은 정사영의 길이를 벡터 y의 길이 ||y||만큼 조정한 값이다.
유사도 때문에 두 백터의 내적을 이용해서 두 벡터 사이의 유사도 측정 가능.
코사인 제2법칙과 (a-b)^2 = a^2 - 2ab + b^2 으로 유도되었다.
[AI Math 2강] 행렬이 뭐에요?
벡터가 공간에서 한 점을 의미한다면 행렬은 여러 점들을 나타낸다.
행렬은 벡터를 원소로 가지는 2차원 배열
행렬 곱셉(matrix multiplication)은 i번째 행벡터와 j번째 열벡터 사이의 내적을 성분으로 가지는 행렬을 계산합니다.
넘파이의 np.inner는 i번째 행벡터와 j번째 행벡터 사이의 내적을 성분으로 가지는 행렬을 계산한다.
수학에서 말하는 내적과는 다르므로 주의! 넘파이의 np.inner는 그냥 행은 행끼리 열은 열끼리 곱해서 원소끼리 곱해서 계산함.
z = Ax. 행렬곱을 통해 벡터를 다른 차원의 공간으로
모든 선형변환(linear transform)은 행렬곱으로 계산할 수 있다.
여기선 x가 기존 공간이고 A에 의해 z로 이동함.
되돌리는것도 가능함. 이걸 역행렬이라고 함.
AA^-1 = A-1A = I. 원래 행렬을 원래대로 돌리는 행렬을 항등행렬
np.linalg.inv(X) 를 통해 역핼렬 구하기 가능.
그런데 역행렬이 있는 조건. 행과 열이 같아야 한다. 주어진 행렬의 Determinant가 0이 아니어야 함.
X = np.array([[1,-2,3],
[7,5,0],
[-2,-1,2]])
print(np.linalg.inv(X))
print(X @ np.linalg.inv(X))
[[ 0.21276596 0.0212766 -0.31914894]
[-0.29787234 0.17021277 0.44680851]
[ 0.06382979 0.10638298 0.40425532]]
[[ 1.00000000e+00 -1.38777878e-17 0.00000000e+00]
[-2.22044605e-16 1.00000000e+00 -5.55111512e-17]
[-2.77555756e-17 0.00000000e+00 1.00000000e+00]]
행과 열 다르면 못하나? 아니. 역행렬을 계산할 수 없다면 유사역행렬(pseudo-inverse) 또는 무어-펜로즈(Moore-Penrose) 역행렬 A+를 이용한다.
Y = np.array([[0,1],
[1,-1],
[-2,1]])
print(np.linalg.pinv(Y))
print(Y @ np.linalg.pinv(Y)) # 이러면 안된다.
print(np.linalg.pinv(Y) @ Y)
[[ 5.00000000e-01 4.09730229e-17 -5.00000000e-01]
[ 8.33333333e-01 -3.33333333e-01 -1.66666667e-01]]
[[ 0.83333333 -0.33333333 -0.16666667]
[-0.33333333 0.33333333 -0.33333333]
[-0.16666667 -0.33333333 0.83333333]]
[[ 1.00000000e+00 -2.22044605e-16]
[ 0.00000000e+00 1.00000000e+00]]
행과 열 개수가 다르기 때문에 곱사는 순서 조심해야 한다.
응용1: 연립방정식 풀기
Ax = b
np.linalg.pinv를 이용하면 연립방정식의 해를 구할 수 있다.
Ax = b => x = A+b = AT(AAT)^-1b
응용2: 선형회귀분석
np.linalg.pinv를 이용하면 데이터를 선형모델(linear model)로 해석하는 선형회귀식을 찾을 수 있다.
Xp != y 이기 때문에 최대한 비슷하게 만족하는 식. 선. 데이터를 최대한 잘 표현해줄 수 있는 선.
Xp = y^ ~ y => p = X+y = (XYX)^-1XTy.
L2노름을 최소하하는 베타 p를 찾는다. min(p) ||y-y~||2
선형회귀분석은 X와 y가 주어진 상황에서 계수 p를 찾아야 한다.
선형회귀방정식을 찾는 방법
데이터를 행렬 X로 표현할 수 있다. 여러개의 점을 하나의 행렬로 표현하게 되면 행렬 X로 표현할 수 있는데 이 행렬 X에다가 beta 라는 계수백터를 곱해주게 되면 초록색 선으로 표현되는 선형모델 식을 상상해볼 수 있다. 이때 어떤 beta를 써야 빨간 점을 잘 나타내는 선형모델을 찾을 수 있느냐. 이게 선형회귀분석.
선형회귀분석은 연립방정식과 달리 행이 더 크므로 방정식을 푸는건 불가능. 데이터가 다 Xp 선 위에 있는게 아니니까.
Moore-Penrose 역행렬을 이용하면 y에 근접하는 y-hat을 찾을 수 있다. L-2 노름을 최소화 하는 min
(xi, yi) 인데 n>=m 이어야 함.
# Scikit Learn 을 활용한 회귀분석
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X, y)
y_test = model.predict(x_test)
# Moore-Penrose 역행렬
X_ = np.array(np.append(x[1]) for x in X) # intercept 항 추가
beta = np.linalg.pinv(X_) @ y
y_test = np.append(x, [1]) @ beta
y절편(intercept) 항을 직접 추가해야 한다.
=============================
퀴즈
전치행렬은 행과 열을 교환하여 얻는 행렬
def l2_norm(x):
x_norm = x*x
x_norm = np.sum(x_norm)
x_norm = np.sqrt(x_norm)
return x_norm
def angle(x, y):
v = np.inner(x, y) / (l2_norm(x) * l2_norm(y))
theta = np.arccos(v)
return theta
x = np.array([0,1])
y = np.array([0,2])
print(angle(x,y))
x = np.array([1,-1,1,-1])
y = np.array([4,-4,4,-4])
print(np.inner(x,y))
X = np.array([[1,0,1],
[0,1,0]])
print(np.linalg.pinv(X))
============================
피어세션
scipy 수학과학연산
L1은 멘하탄, L2는 유클리드 인듯
거리값을 합한게 노름
numpy는 주소값이 아닌 수 자체를 저장. c기반. 빠르다.
역행렬이 없으면 유사역행렬 사용. 유사역행렬은 무조건 있다.
피어 강의 복습은 전날에 했던걸 하는걸로 바꿈
======================================
후기
증말 다 까먹었다.. 책 다시보니 기억은 안나지만 이해 근육이 남아있는 느낌이다.
댓글 없음:
댓글 쓰기