Post

1.나의 첫 머신러닝 & 2.데이터 다루기

1.나의 첫 머신러닝 & 2.데이터 다루기

Chapter1. 나의 첫 머신러닝

1.1 인공지능과 머신러닝, 딥러닝

image1.png
인공지능이란?
사람처럼 학습하고 추론할 수 있는 지능을 가진 컴퓨터 시스템을 만드는 기술.

영화 <터미네이터> 같이 사람과 구분하기 어려운 지능을 가진 컴퓨터 시스템 → 인공일반지능 현실에서 우리가 마주하고 있는 인공지능 → 약인공지능 약인공지능은 아직까지는 특정 분야에서 사람의 일을 도와주는 보조 역할만 가능함.(ex. 음성비서, 자율주행 자동차, 기계 번역)

머신러닝이란?
규칙을 일일이 프로그래밍하지 않아도 자동으로 데이터에서 규칙을 학습하는 알고리즘을 연구하는 분야. 인공지능의 하위 분야 중 지능을 구현하기 위한 소프트웨어를 담당하는 핵심 분야이다.

→ 최근 머신러닝 발전은 통계나 수학 이론보다 경험을 바탕으로 발전하는 경우가 많음. 컴퓨터 과학 분야가 이러한 발전을 주도하고 있는데 컴퓨터 과학 분야의 대표적인 머신러닝 라이브러리가 ‘사이킷런’

딥러닝이란?
많은 머신러닝 알고리즘 중에 인공 신경망을 기반으로 한 방법들을 통칭하여 부르는 말.

1.3 인공지능과 머신러닝, 딥러닝

※ 생선 분류 문제
한빛 마켓에서 살아있는 생선을 판매하는 과정에서 물류센터 직원이 생선을 고르지 못하는 문제 발생 → 생선 이름을 자동으로 알려주는 머신러닝 개발

생선의 종류: 도미, 곤들매기, 농어, 강꼬치고기, 로치, 빙어, 송어

주어진 정보: 30cm 이상이면 도미다.

1
2
if fish_length >=30:
    print("도미")  

→ 무조건 30cm 이상이라고 도미라고 할 순 없음.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 도미 데이터
# 생선의 길이
bream_length = [
    25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7,
    31.0, 31.0, 31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5,
    34.0, 34.0, 34.5, 35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5,
    38.5, 39.5, 41.0, 41.0
]

# 생선의 무게
bream_weight = [
    242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0,
    450.0, 500.0, 475.0, 500.0, 500.0, 340.0, 600.0, 600.0,
    700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0,
    700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0,
    925.0, 975.0, 950.0
]

import matplotlib.pyplot as plt  # matplotlib의 pyplot 함수를 plt로 줄여서 사용

plt.scatter(bream_length, bream_weight)
plt.xlabel('length')  # x축은 길이
plt.ylabel('weight')  # y축은 무게
plt.show()

image2.png
생선의 길이가 길수록 무게가 많이 나가기 때문에 위 그래프에 나타나는 선형성은 매우 자연스러움.

1
2
3
4
5
6
7
8
9
10
11
12
# 빙어 데이터
smelt_length = [9.8, 10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 
                12.4, 13.0, 14.3, 15.0]

smelt_weight = [6.7, 7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 
                13.4, 12.2, 19.7, 19.9]

plt.scatter(bream_length, bream_weight)
plt.scatter(smelt_length, smelt_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

image3.png
주황색 점이 빙어의 산점도 → 빙어가 도미에 비해 길이도 무게도 매우 작음. 빙어는 도미에 비해 무게가 길이에 영향을 덜 받음.

※ 첫 번째 머신러닝 프로그램
K-최근접 이웃 알고리즘을 사용하여 도미와 빙어 데이터 구분

1
2
3
4
5
6
7
8
9
# 도미와 빙어 데이터 합치기
length = bream_length + smelt_length  
weight = bream_weight + smelt_weight  

# 2차원 리스트 생성
fish_data = [[l,w] for l, w in zip(length, weight)]

# 결과값 출력
print(fish_data)

image4.png

1
2
3
# 도미와 빙어를 숫자 1과0으로 표현.  
fish_target = [1]*35 + [0]*14 
print(fish_target)

사이킷런 패키지에서 k-최근접 이웃 알고리즘을 구현한 클래스 KNeighborsClassifier 임포트하고 클래스의 객체 생성

1
2
3
from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier()

이 객체에 fish_data와 fish_target을 전달하여 도미를 찾기 위한 기준을 학습시킴 → 훈련(training)이라고 부름, 사이킷런에서는 fit() 메서드 활용

1
kn.fit(fish_data, fish_target)

객체 kn이 얼마나 잘 훈련되었는지 평가 필요. 사이킷런에서 모델을 평가하는 메서드는 score() 활용. → 이 메서드는 0에서 1 사이의 값을 반환함. 1은 모든 데이터를 정확히 맞혔다는 것을 의미

1
kn.score(fish_data, fish_target) 

→ 결과가 1이 나오기 때문에 모든 fish_data를 정확히 맞혔다고 할 수 있음. 이 값을 정확도라고 부름

※ K-최근접 이웃 알고리즘
위에서 사용한 알고리즘이 K-최근접 이웃이다. 이 알고리즘은 매우 간단한데 어떤 데이터에 대한 답을 구할 때 주위의 다른 데이터를 보고 다수를 차지하는 것을 정답으로 사용한다

image4.png
예를 들어 위 사진에서의 삼각형이 어느 생선인지 궁금할 때 삼각형 주변에 어떤 데이터가 더 많은지 판단 → 도미 데이터가 훨씬 근처에 많기 때문에 삼각형을 도미라고 판단

1
2
# 코드로 삼각형 생선 종류 확인
kn.predict([[30,600]]) # output: array([1])  

predict()함수로 새로운 데이터의 정답을 예측할 수 있음. 1이 반환되었음으로 삼각형은 도미인 것으로 출력됨. (주의: fit()함수와 마찬가지로 리스트의 리스트를 전달해야 하기 때문에 포인트를 리스트로 2번 감싸줘야함)

  • 결국 K-최근접 이웃 알고리즘을 위해 준비해야 할 것은 모든 데이터를 가지고 있는 것
  • 알고리즘 특징상 데이터가 아주 많은 경우에는 메모리가 많이 필요하고 직선거리를 계산하는 데에 많은 시간이 필요하다는 단점 존재
  • KNeighborsClassifier 클래스 기본값은 5이지만 n_neighbors 매개변수로 바꿀 수 있음.

chapter2. 데이터 다루기

2.1 훈련 세트와 테스트 세트

chapter1에서 진행한 모델은 도미와 빙어를 구분하는 정답이 존재했음. → 지도학습
반면 비지도학습은 정답이 따로 존재하지 않고 입력 데이터만 사용함. 따라서 무언가를 맞힐 수는 없지만 데이터를 잘 파악하거나 변형하는 데 도움을 줌.
image4.png

도미와 빙어의 데이터와 타깃을 주고 훈련한 다음, 같은 데이터로 테스트한다면 모두 맞히는 것이 당연함(chapter.1) → 훈련 데이터와 테스트 데이터가 각각 달라야 함.

1
2
3
4
5
6
7
8
9
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
# chapter1에서 사용한 생선의 길이와 무게 리스트
# 생선의 길이
bream_length = [
    25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7,
    31.0, 31.0, 31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5,
    34.0, 34.0, 34.5, 35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5,
    38.5, 39.5, 41.0, 41.0
]

# 생선의 무게
bream_weight = [
    242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0,
    450.0, 500.0, 475.0, 500.0, 500.0, 340.0, 600.0, 600.0,
    700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0,
    700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0,
    925.0, 975.0, 950.0
]

# 2차원 리스트 생성
fish_data = [[l,w] for l, w in zip(length, weight)]

fish_target = [1]*35 + [0]*14 

# 사이킷런 KNeighborsClassifier 클래스 임포트 후 모델 객체 생성
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()

# 훈련 세트로 입력값 중 0부터 34번째 인덱스까지 사용
train_input = fish_data[:35]

# 훈련 세트로 타깃값 중 0부터 34번째 인덱스까지 사용
train_target = fish_target[:35]

# 테스트 세트로 입력값 중 35번째부터 마지막 인덱스까지 사용
test_input = fish_data[35:]

# 테스트 세트로 타깃값 중 35번째부터 마지막 인덱스까지 사용
test_target = fish_target[35:]
1
2
3
kn = kn.fit(train_input, train_target)
kn.score(test_input, test_target)
# output: 0 

이러한 결과가 나온 이유는 training set가 모두 도미 데이터만 들어가있기 때문
→ 이렇게 훈련 세트와 테스트 세트에 샘플이 골고루 섞여 있지 않았을 때 샘플링이 한쪽으로 치우치는 것을 샘플링 편향 이라고 부름.

따라서 데이터를 섞든지 골고루 샘플을 뽑아서 훈련 세트와 테스트 세트를 만들어야 함.

1
2
3
4
5
# 생선 데이터를 넘파이 배열로 준비
import numpy as np

input_arr = np.array(fish_data)
target_arr = np.array(fish_target)

이 배열에서 랜덤하게 샘플을 선택하여 훈련 세트와 테스트 세트로 만들어야함.
(주의: input_arr와 target_arr에서 같은 위치는 함께 선택되어야함)

1
2
3
4
5
6
# random.seed()로 동일한 결과 고정 
np.random.seed(42)
index = np.arrange(49)
# 주어진 배열을 무작위로 섞음
np.random.shuffle(index)
print(index)

image4.png

1
2
train_input = input_arr[index[:35]]
train_target = target_arr[index[:35]]

만들어진 index의 첫번째 값은 13이었기 때문에 train_input의 첫번째 원소는 input_arr의 열네번째 원소가 들어있음

1
2
3
# 나머지 14개를 테스트 세트로 생성
test_input = input_arr[index[35:]]
test_target = target_arr[index[35:]]
1
2
3
4
5
6
7
8
# 훈련 세트와 테스트 세트에 도미와 빙어가 잘 섞여 있는지 산점도로 표현  
import matplotlib.pyplot as plt

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(test_input[:,0], test_input[:,1])
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

image4.png
파란색이 훈련 세트, 주황색이 테스트 세트

※ 두번째 머신러닝 프로그램
앞서 만든 훈련 세트와 테스트 세트로 k-최근접 이웃 모델을 훈련.

1
2
3
4
5
# 인덱스를 섞어 만든 train_input과 train_target으로 모델 훈련
kn = kn.fit(train_input, train_target)

# test_input, test_target으로 모델 테스트
kn.score(test_input, test_target)

output: 1
→ 100% 정확도로 테스트 세트에 있는 모든 생선을 맞췄음.

1
2
3
# predict()함수로 테스트 세트의 예측 결과와 실제 타깃 확인
kn.predict(test_input)
test_target

→ 두 출력 결과가 동일하게 나옴.

※ 훈련 모델 평가

  • chapter.1에서는 알고리즘이 훈련 데이터와 테스트 데이터를 동일하게 사용했기 때문에 평가 방법에 오류가 있었음.
  • chapter.2에서는 훈련 데이터와 테스트 데이터를 분리하여 훈련 데이터로 모델을 훈련하고 테스트 데이터로 모델을 평가함.
  • 이때 훈련 데이터에서 도미 또는 빙어 데이터가 한쪽으로 몰리지 않도록 주의해야함.

2.2 데이터 전처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fish_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0, 
                31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0, 
                35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0, 9.8, 
                10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0]
fish_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0, 
                500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0, 
                700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0, 6.7, 
                7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]

import numpy as np

np.column_stack(([1,2,3],[4,5,6]))   
# Output: array([[1,4],
#               [2,5],
#               [3,6]])

column_stack()함수는 전달받은 리스트를 일렬로 세운 다음 차례대로 나란히 연결한다. 연결할 리스트는 파이썬 튜플로 전달
(튜플(tuple): 리스트처럼 원소에 순서가 있지만 한 번 만들어지면 수정할 수 없음. 튜플을 사용하면 함수로 전달한 값이 바뀌지 않는다는 것을 믿을 수 있기 때문에 매개변수 값으로 많이 사용한다.)

1
2
3
4
5
6
7
fish_data = np.column_stack((fish_length, fish_weight))
print(fish_data[:5])
# output: [[25.4 242.]
#          [26.3 290.]
#          [26.5 340.]
#          [29. 363.]
#          [29. 430.]]

넘파이 배열을 출력하면 리스트처럼 한줄로 길게 출력되지 않고 행과 열을 맞춰 정리된 모습으로 출력됨.

1
2
print(np.ones(5))
# output: [1. 1. 1. 1. 1.]

np.ones(), np.zeros()함수로 1이 35개인 배열과 0이 14개인 배열을 간단히 만들 수 있음.
→ 두 배열을 연결할 때는 첫번째 차원을 따라 배열을 연결하는np.concatenate()함수 사용
image4.png

1
fish_target = np.concatenate((np.ones(35), np.zeros(14)))

데이터가 작은 경우에는 파이썬 리스트 작업도 괜찮지만 데이터가 클수록 넘파이 배열 사용이 효율적.

1
2
3
from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, random_state=42)  

train_test_split()함수로 전달되는 리스트나 배열을 비율에 맞게 훈련 세트와 테스트 세트를 나누어줌. 이때 random_state 매개변수로 자체적으로 랜덤 시드를 지정
→ 이 함수는 기본적으로 25%를 테스트 세트로 떼어냄. (해당 문제에서는 13개)

1
print(test_target) # output: [1 0 0 0 1 1 1 1 1 1 1 1 1]

13개의 테스트 세트 중 10개가 도미(1), 3개가 빙어(0)

1
2
train_input, test_input, train_target, test_target = train_test_split(
    fish_data, fish_target, stratify=fish_target, random_state=42)

→ stratify 매개변수에 타깃 데이터를 전달하면 클래스 비율에 맞게 데이터를 나누어줌.

※ chapter 2.1 에서 진행한 모델에서 25cm, 250g인 도미를 빙어라고 예측하는 오류 발생

1
print(kn.predict([[25, 150]]))  #output: [0]
1
2
3
4
5
6
7
import matplotlib.pyplot as plt

plt.scatter(train_input[:, 0], train_input[:, 1])
plt.scatter(25, 150, marker='^')  # marker 매개변수는 모양을 지정합니다
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

image4.png

1
2
3
4
5
6
7
8
distances, indexces = kn.kneighbors([[25,150]])

plt.scatter(train_input[:, 0], train_input[:, 1])
plt.scatter(25, 150, marker='^')
plt.scatter(train_input[indexes, 0], train_input[indexes, 1], marker='D')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

image4.png
marker=’D’로 지정하여 산점도를 마름모로 표시.
삼각형 샘플에 가장 가까운 5개의 샘플이 초록 다이아몬드로 표시됨.
→ 빙어의 수가 4개, 도미 수가 1개인 것을 볼 수 있음.

1
print(distances) #output: [[92.00086956, 130.48375378, 130.73859415, 138.32150953, 138.39320793]]

도미 데이터와의 거리는 92인데 빙어 데이터와의 거리가 130 → 직관적으로 그래프 상으로는 빙어와의 거리가 훨씬 더 크게 나와야함. 이는 x축 범위는 10~40, y축은 0~1000으로 되어있어 오류가 발생한 것

1
2
3
4
5
6
7
plt.scatter(train_input[:, 0], train_input[:, 1])
plt.scatter(25, 150, marker='^^')
plt.scatter(train_input[indexes, 0], train_input[indexes, 1], marker='D')
plt.xlim((0, 1000))
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

image4.png
두 특성의 값이 놓인 범위를 스케일이라고도 함.
데이터 전처리: 알고리즘들이 샘플 간의 거리에 영향을 받지 않도록 특성값을 일정한 기준으로 맞춰주는 것.

  • 가장 널리 사용하는 전처리 방법 중 하나는 표준점수(z점수)이다. 이는 각 특성값이 0에서 표준편차의 몇 배만큼 떨어져 있는지를 나타냄. 이를 통해 실제 특성값의 크기와 상관없이 동일한 조건으로 비교할 수 있다.
    Z-score = $ \frac{X-\mu}{\sigma} $
    ```python mean = np.mean(train_input, axis=0) std = np.std(train_input, axis=0)

z-score

train_scaled = (train_input - mean) / std

plt.scatter(train_scaled[:, 0], train_scaled[:, 1]) plt.scatter(25, 150, marker=’^^’) plt.xlabel(‘length’) plt.ylabel(‘weight’) plt.show()

1
2
3
4
5
6
7
8
9
10
![image4.png](/assets/images/HW2/image13.png)   
→ [20,150]을 z-score로 변환하지 않아서 생긴 오류  

```python
new = ([25,150] - mean) / std
plt.scatter(train_scaled[:, 0], train_scaled[:, 1])
plt.scatter(new[0], new[1], marker='^^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

image4.png

1
2
3
4
5
6
7
kn.fit(train_scaled, train_target)
test_scaled = (test_input - mean) / std
kn.score(test_scaled, test_target) # 테스트 세트 샘플 평가
# output: 1

print(kn.predict([new])) # 수상한 생선 샘플 예측
# output: [1]

위 결과에서 수상한 생선 한마리를 도미(1)로 예측한 것을 볼 수 있음.

1
2
3
4
5
6
7
distances, indexes = kn.kneighbors([new])
plt.scatter(train_scaled[:, 0], train_scaled[:, 1])
plt.scatter(new[0], new[1], marker='^^')
plt.scatter(train_scaled[indexes, 0], train_scaled[indexes, 1], marker='D')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

image4.png

  • 대부분의 머신러닝 알고리즘에서 샘플의 특성에 대한 스케일이 다르면 예측 결과에 오류가 생길 수 있음.
  • 특성을 표준점수로 변환하는 등의 방법을 활용하여 데이터 전처리를 진행하면 해당 문제를 해결할 수 있음
  • 데이터 전처리 과정에서 훈련 세트를 변환한 방식 그대로 테스트 세트를 변환해야 한다는 점 주의
This post is licensed under CC BY 4.0 by the author.

Trending Tags