Published 2021. 8. 23. 21:26

머신러닝의 개념

머신러닝은 세가지로 나뉜다.

 

  1. 지도학습(Supervised Learning) : 명확한 결정값이 주어진 데이터를 학습
    1. 분류
    2. 회귀
  2. 비지도학습(Un-supervised Learning) : 결정값이 주어지지 않은 데이터를 학습
    1. 군집화(클러스터링)
    2. 차원 축소
  3. 강화학습(Reinforcement Learning)

머신러닝 알고리즘의 유형

  1. 기호주의 : 결정트리
  2. 연결주의 : 신경망/딥러닝 (심층신경망을 기초로한)
  3. 유전 알고리즘
  4. 베이지안 통계 (기존의 가설을 새로운 데이터를 받으며 갱신)
  5. 유추주의 : KNN, SVM (유사한 것들의 추정)

머신 러닝의 단점

  1. 데이터에 의존적이다. 편향된 데이터만 넣으면 편향된 결과만 나온다.
  2. 최적의 결과를 도출하기 위한 머신러닝 모델은 실제 환경 데이터에 맞지 않을 수 있다. (과적합)
  3. 알고리즘 기반의 결과에 대한 논리적인 이해가 어려울 수 있다.
  4. 데이터만 집어넣으면 자동으로 최적화된 결과를 도출할 것이라는 것은 환상이다. 데이터의 가공, 알고리즘의 최적화가 잘 결합되어야 한다.

 

 

머신러닝 패키지 : ScikitLearn

배열, 선형대수, 통계 패키지 : Numpy, Scipy

데이터 핸들링 : Pandas

시각화 : matplotlib, seaborn

대화형 파이썬 툴 : jupyter notebook

 


머신러닝 속도를 높이는 방법

먼저 파이썬(R도 마찬가지 입니다) 기반의 머신러닝에서 대용량 데이터 처리 시 유의하실 부분에 대해서 말씀 드리고 속도에 대해서 말씀 드리겠습니다. 

 

1. 파이썬 기반의 머신러닝은 대부분 모든 학습 데이터를 일단 메모리에 올린 뒤에 학습을 수행합니다. 만약 서버의 메모리가 8GB 인데 로딩하는 데이터가 8GB를 넘어가면 학습 시 Out of memory가 발생합니다.  사실 전체 메모리가 8GB면 가용 가능한 메모리는 이 보다 훨씬 적습니다(절반 이하로 생각하시면 되며, 필요없는 객체는 바로 메모리에서 삭제하는 코드를 사용하셔야 합니다). 

따라서 100만개 정도의 데이터가 서버 메모리에 올라갈 수 있는지 부터 확인해야 합니다. 100만개 레코드이지만 Feature가 많지 않다면 충분히 8GB정도에 올라갑니다. 먼저 Pandas로 data를 로드 한 뒤에 DataFrame.memory_usage() 로 메모리 사용량을 확인해 보시면 알 수 있습니다. 

 

2. 머신러닝의 속도를 높이는 방법은 A. 속도가 빠른 알고리즘을 적용, B. Multi processing 으로 알고리즘을 적용하는 것입니다.

 

A. 일반적으로 속도가 빠른 알고리즘이 있습니다. Tree기반 앙상블보다는 선형 계열이 빠릅니다. 즉 Logistic Regression이 Random Forest 보다 빠릅니다. 같은 Tree기반 앙상블이더라도 Random Forest가 Gradient Boosting 보다 더 빠릅니다. 또한 XGboost보다는 LightGBM이 더 빠르고 메모리도 더 적게 사용합니다. 그런데 머신러닝의 예측 정확도를 중요시 한다면 학습 속도는 느리지만 보다 성능이 높은 알고리즘을 선택해야 할 수도 있습니다.

 

B. 서버를 여러개 Core를 가진 시스템으로 구성합니다. 사이킷런은 멀티 core로 병렬 처리를 지원합니다. n_jobs=-1을 Estimator 객체에 초기 파라미터로 설정하면 시스템이 가진 모든 CPU 코어를 병렬로 사용하여 학습하게 됩니다. 즉 8Core CPU가 1Core CPU보다 더 빠르게 학습합니다(그렇다고 8배 빠르지는 않습니다. 선형 성능 확장에 제약은 있습니다).

 

요약하자면 100만개 Record의 데이터 세트의 피처 갯수가 몇개인지는 확인할 수는 없지만 메모리에만 들어온다면 1~2 시간내에 학습이 가능하며 만약 학습 시간을 더 줄이고자 한다면 8 Core이상의 시스템에서 구동하시면 훨씬 학습 시간을 개선할 수 있을 것입니다.


넘파이 (ndarray)

ndarray : N차원(Dimension) 배열(Array) 객체


같은 데이터 타입

[1,2,3]

[1.1, 2.2, 3.3]

['홍길동', '임꺽정']

ndarray의 데이터 타입은 그 연산의 특성상 같은 데이터 타입만 가능하다.

list2 = [1, 2, 'test']
array2 = np.array(list2) # 문자형이 있으면 정수형은 문자형으로 변환돼서 저장된다.
print(array2, array2.dtype)

list3 = [1, 2, 3.0]
array3 = np.array(list3) # 실수형이 있으면 정수형은 실수형으로 변환돼서 저장된다.
print(array3, array3.dtype)


>> ['1' '2' 'test'] <U21
>> [1. 2. 3.] float64

astype()

대용량 데이터를 다룰 시 메모리 절약을 위해서 형변환을 특히 고려해야 한다.

 

array_int = np.array([1, 2, 3])
array_float = array_int.astype('float64')
print(array_float, array_float.dtype)

array_int1= array_float.astype('int32')
print(array_int1, array_int1.dtype)

array_float1 = np.array([1.1, 2.1, 3.1])
array_int2= array_float1.astype('int32')
print(array_int2, array_int2.dtype)

>> [1. 2. 3.] float64
>> [1 2 3] int32
>> [1 2 3] int32

axis=0   axis=1

axis 0 : 아래로 내려가는 행 방향

axis 1 : 오른쪽으로 가는 열 방향

array2 = np.array([[1,2,3],
                  [2,3,4]])

print(array2.sum())
print(array2.sum(axis=0))
print(array2.sum(axis=1))

>> 15
>> [3 5 7]
>> [6 9]

 


arange, zeros, ones

sequence_array = np.arange(10)
print(sequence_array)
print(sequence_array.dtype, sequence_array.shape)

zero_array = np.zeros((3,2),dtype='int32')
print(zero_array)
print(zero_array.dtype, zero_array.shape)

one_array = np.ones((3,2))
print(one_array)
print(one_array.dtype, one_array.shape)

>>
[0 1 2 3 4 5 6 7 8 9]
int64 (10,)

[[0 0]
 [0 0]
 [0 0]]
int32 (3, 2)

[[1. 1.]
 [1. 1.]
 [1. 1.]]
float64 (3, 2)

reshape

array1 = np.arange(10)
print('array1:\n', array1)

array2 = array1.reshape(2,5)
print('array2:\n',array2)

array3 = array1.reshape(5,2)
print('array3:\n',array3)

>>
array1:
 [0 1 2 3 4 5 6 7 8 9]
 
array2:
 [[0 1 2 3 4]
 [5 6 7 8 9]]

array3:
 [[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]

 

reshape(-1) : -1의 옆의 숫자에 맞춰 axis 크기 고정

-1 은 불확정 값이다.

array1 = np.arange(10)
print(array1)

#컬럼 axis 크기는 5에 고정하고 로우 axis크기를 이에 맞춰 자동으로 변환. 즉 2x5 형태로 변환 
array2 = array1.reshape(-1,5)
print('array2 shape:',array2.shape)
print('array2:\n', array2)

#로우 axis 크기는 5로 고정하고 컬럼 axis크기는 이에 맞춰 자동으로 변환. 즉 5x2 형태로 변환 
array3 = array1.reshape(5,-1)
print('array3 shape:',array3.shape)
print('array3:\n', array3)

>>
[0 1 2 3 4 5 6 7 8 9]
array2 shape: (2, 5)
array2:
 [[0 1 2 3 4]
 [5 6 7 8 9]]
array3 shape: (5, 2)
array3:
 [[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]

 

reshape()는 (-1, 1), (-1,)와 같은 형태로 주로 사용됨.

또한 -1 은 반드시 한곳에만 입력해야 한다.

# reshape()는 (-1, 1), (-1,)와 같은 형태로 주로 사용됨.
# 1차원 ndarray를 2차원으로 또는 2차원 ndarray를 1차원으로 변환 시 사용. 
array1 = np.arange(5)

# 1차원 ndarray를 2차원으로 변환하되, 컬럼axis크기는 반드시 1이여야 함. 
array2d_1 = array1.reshape(-1, 1)
print("array2d_1 shape:", array2d_1.shape)
print("array2d_1:\n",array2d_1)

# 2차원 ndarray를 1차원으로 변환 
array1d = array2d_1.reshape(-1,)
print("array1d shape:", array1d.shape)
print("array1d:\n",array1d)

>>
array2d_1 shape: (5, 1)
array2d_1:
 [[0]
 [1]
 [2]
 [3]
 [4]]

array1d shape: (5,)
array1d:
 [0 1 2 3 4]

펜시 인덱싱(fancy indexing)

array1d = np.arange(start=1, stop=10)
array2d = array1d.reshape(3,3)
print(array2d)

array3 = array2d[[0,1], 2]
print('array2d[[0,1], 2] => ',array3.tolist())

array4 = array2d[[0,2], 0:2]
print('array2d[[0,2], 0:2] => ',array4.tolist())

array5 = array2d[[0,1]]
print('array2d[[0,1]] => ',array5.tolist())

>>
[[1 2 3]
 [4 5 6]
 [7 8 9]]
array2d[[0,1], 2] =>  [3, 6]
array2d[[0,2], 0:2] =>  [[1, 2], [7, 8]]
array2d[[0,1]] =>  [[1, 2, 3], [4, 5, 6]]

불린 인덱싱(boolean indexing)

False 인 인덱스는 놔두고, True인 인덱스를 받아서 출력한다.

array1d = np.arange(start=1, stop=10)

print(array1d > 5)

var1 = array1d > 5
print("var1:",var1)
print(type(var1))

>>
[False False False False False  True  True  True  True]
var1: [False False False False False  True  True  True  True]
<class 'numpy.ndarray'>

 

# [ ] 안에 array1d > 5 Boolean indexing을 적용 
print(array1d)
array3 = array1d[array1d > 5]
print('array1d > 5 불린 인덱싱 결과 값 :', array3)

>>
[1 2 3 4 5 6 7 8 9]
array1d > 5 불린 인덱싱 결과 값 : [6 7 8 9]

 

boolean_indexes = np.array([False, False, False, False, False,  True,  True,  True,  True])
array3 = array1d[boolean_indexes]
print('불린 인덱스로 필터링 결과 :', array3)

>>
불린 인덱스로 필터링 결과 : [6 7 8 9]

sort()와 argsort()

np.sort(A) : 원 행렬은 그대로 유지한 채, 정렬된 행렬을 반환한다.

A.sort() : 원 행렬 자체를 정렬된 행렬로 변환한다. 

 

org_array = np.array([ 3, 1, 9, 5]) 
print('원본 행렬:', org_array)

# np.sort( )로 정렬 
sort_array1 = np.sort(org_array)         
print ('np.sort( ) 호출 후 반환된 정렬 행렬:', sort_array1) 
print('np.sort( ) 호출 후 원본 행렬:', org_array)

# ndarray.sort( )로 정렬
sort_array2 = org_array.sort()
org_array.sort()
print('org_array.sort( ) 호출 후 반환된 행렬:', sort_array2)
print('org_array.sort( ) 호출 후 원본 행렬:', org_array)

>>
원본 행렬: [3 1 9 5]
np.sort( ) 호출 후 반환된 정렬 행렬: [1 3 5 9]
np.sort( ) 호출 후 원본 행렬: [3 1 9 5]
org_array.sort( ) 호출 후 반환된 행렬: None
org_array.sort( ) 호출 후 원본 행렬: [1 3 5 9]

 

내림차순 정렬 : [::-1]

sort_array1_desc = np.sort(org_array)[::-1]
print ('내림차순으로 정렬:', sort_array1_desc) 

>> 내림차순으로 정렬: [9 5 3 1]

 

axis 방향으로 sort()

array2d = np.array([[8, 12], 
                   [7, 1 ]])

sort_array2d_axis0 = np.sort(array2d, axis=0)
print('로우 방향으로 정렬:\n', sort_array2d_axis0)

sort_array2d_axis1 = np.sort(array2d, axis=1)
print('컬럼 방향으로 정렬:\n', sort_array2d_axis1)

>>
로우 방향으로 정렬:
 [[ 7  1]
 [ 8 12]]
컬럼 방향으로 정렬:
 [[ 8 12]
 [ 1  7]]

 

argsort() : 원본 행렬 정렬 후 행렬의 원래 인덱스가 필요할 때 쓰인다. 반환하는 것은 정렬된 행렬의 원본 행렬 인덱스를 반환한다.

org_array = np.array([ 3, 1, 9, 5]) 
print(np.sort(org_array))

sort_indices = np.argsort(org_array)
print(type(sort_indices))
print('행렬 정렬 시 원본 행렬의 인덱스:', sort_indices)

>>
[1 3 5 9]
<class 'numpy.ndarray'>
행렬 정렬 시 원본 행렬의 인덱스: [1 0 3 2]

 

key-value 형태의 데이터를 John=78, Mike=95, Sarah=84, Kate=98, Samuel=88을 ndarray로 만들고 argsort()를 이용하여 key값을 정렬

name_array=np.array(['John', 'Mike', 'Sarah', 'Kate', 'Samuel'])
score_array=np.array([78, 95, 84, 98, 88])

# score_array의 정렬된 값에 해당하는 원본 행렬 위치 인덱스 반환하고 이를 이용하여 name_array에서 name값 추출.  
sort_indices = np.argsort(score_array)
print("sort indices:", sort_indices)

name_array_sort = name_array[sort_indices]

score_array_sort = score_array[sort_indices]
print(name_array_sort)
print(score_array_sort)

>>
sort indices: [0 2 4 1 3]
['John' 'Sarah' 'Samuel' 'Mike' 'Kate']
[78 84 88 95 98]

 

np.dot(A, B) : 행렬의 내적

np.transpose(A) : 전치 행렬

 

 

 

'💡 AI > ML' 카테고리의 다른 글

ML - 예측 프로세스  (0) 2021.08.25
ML - pandas 기본  (0) 2021.08.24
기계학습기초2 정리  (0) 2021.06.16
기계학습기초1 정리  (0) 2021.06.16
ML W14 - Representing and Mining Text  (0) 2021.06.12
복사했습니다!