
공부를 하기 위해 혼자 공부하는 머신러닝+딥러닝 혼공학습단 14기에 신청했다.
왜냐고 물으신다면.. 혼자서는 하지 않고 같이해도 하지 않으니까!
혼공학습단 14기 일정은 아래와 같다.

Ch.01 나의 첫 머신러닝
머신러닝이란 규칙을 일일이 프로그래밍하지 않아도, 자동으로 데이터에서 규칙을 학습하는 알고리즘을 연구하는 분야
사이킷런이 대표적인 머신러닝 라이브러리이다.
딥러닝은 인공 신경망이라고도 하며, 텐서플로와 파이토치가 대표적인 라이브러리
인공지능은 사람처럼 학습하고 추론할 수 있는 지능을 가진 시스템을 만드는 기술로 강인공지능과 약인공지능으로 나눌 수 있다.
생선 분류 문제
도미와 빙어의 데이터를 가지고 문제를 다룬다.
(도미 35마리와 빙어 14마리)
도미의 데이터
import matplotlib.pyplot as plt
# 도미의 길이
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, 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]
plt.scatter(bream_length, bream_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

빙어의 데이터
# 빙어의 길이
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(smelt_length, smelt_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

도미와 빙어 데이터
아래의 표에서 파란색은 도미, 주황색은 빙어의 데이터이다.

여기까지는 단순 데이터를 그래화 시킨 것이다.
이제 이 데이터들을 이용하여 우리가 분류하지 말고, 머신러닝을 이용하여 분류해보자.
분류할 때 사용할 모델은 사이킷런 패키지에서 k-최근접 이웃 알고리즘을 구현한 클래스인 KNeighborsClassifier를 이용하여 진행한다.
KNeighborsClassifier이란?
“우리 반 친구들 중 나랑 제일 비슷한 애들한테 물어봐서, 걔네가 뭘 좋아하는지 보고 나도 그걸 선택하는 거야!”
사전 작업
# 도미와 빙어의 데이터를 합침.
length = bream_length + smelt_length
weight = bream_weight + smelt_weight
fish_data = [[l, w] for l, w in zip(length, weight)]
# 도미의 데이터는 1로, 빙어의 데이터는 0으로
fish_target = [1] * 35 + [0] * 14
print(fish_target)
[[25.4, 242.0], [26.3, 290.0], [26.5, 340.0], [29.0, 363.0], [29.0, 430.0], [29.7, 450.0], [29.7, 500.0], [30.0, 390.0], [30.0, 450.0], [30.7, 500.0], [31.0, 475.0], [31.0, 500.0], [31.5, 500.0], [32.0, 340.0], [32.0, 600.0], [32.0, 600.0], [33.0, 700.0], [33.0, 700.0], [33.5, 610.0], [33.5, 650.0], [34.0, 575.0], [34.0, 685.0], [34.5, 620.0], [35.0, 680.0], [35.0, 700.0], [35.0, 725.0], [35.0, 720.0], [36.0, 714.0], [36.0, 850.0], [37.0, 1000.0], [38.5, 920.0], [38.5, 955.0], [39.5, 925.0], [41.0, 975.0], [41.0, 950.0], [9.8, 6.7], [10.5, 7.5], [10.6, 7.0], [11.0, 9.7], [11.2, 9.8], [11.3, 8.7], [11.8, 10.0], [11.8, 9.9], [12.0, 9.8], [12.2, 12.2], [12.4, 13.4], [13.0, 12.2], [14.3, 19.7], [15.0, 19.9]]
위와 같이 작업을 한 이유는 사이킷런을 사용하려면 각 특성의 리스트를 세로 방향으로 늘어뜨린 2차원 리스트로 만들어야 한다.
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
# 주어진 데이터로 알고리즘을 훈련
kn.fit(fish_data, fish_target)
# 평가하는 메서드로 0에서 1사이의 값을 반환 / 1은 모든 데이터를 정확히 맞춤
kn.score(fish_data, fish_target)
1.0
여기서 fit은 데이터로 알고리즘을 훈련시키는 것이며,
score는 평가하는 메서드로 0에서 1 사이 값을 반환하며, 1은 모든 데이터를 정확히 맞춘 경우이다.
Ch.02 데이터 다루기
머신러닝 알고리즘
- 지도 학습 -> 정답이 있어 알고리즘이 정답을 맞히는 것을 학습
- 비지도 학습 -> 타깃 없이 입력 데이터만 사용 (정답을 사용하지 않아 정답을 맞힐 수는 없으나, 데이터를 잘 파악 및 번형 하는데 도움을 줌)
훈련 세트와 테스트 세트
# 훈련 세트
train_input = fish_data[:35]
train_target = fish_target[:35]
# 테스트 세트
test_input = fish_data[35:]
test_target = fish_target[35:]
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
0.0
훈련 세트와 테스트 세트를 나누어서 테스트를 하고 score로 평판을 보았더니 0으로 출력이 된다.
분명 아까는 1일 때 모든 데이터를 정확히 맞춘 거라고 보았는데.. 왜? 0으로 출력이 된 걸까?
그 이유는 현재 생선에 대한 데이터는 도미 35마리와 빙어 14마리로 구성되어 있다.
훈련 세트를 구성한 데이터는 :35로 슬라이스 했기 때문에 도미에 대한 데이터만 들어가고, 테스트 세트에는 그 이후이기 때문에 빙어에 대한 데이터만 들어갔다.
그렇기 때문에 제대로 된 도출이 어렵다.
이렇게 샘플링이 한쪽으로 치우쳐진 경우를 "샘플링 편향"이라고 한다.
샘플링 편향일 해결 할 방법은 여러 가지의 방법들이 있지만 여기에서는 넘파이를 이용하여 도미와 빙어 데이터들을 섞어줄 거다.
Numpy란?
파이썬의 대표적인 배열 라이브러리로 파이썬 리스트의 한계를 극복하고, 벡터·행렬 연산부터 통계·수학 계산까지 반복문 없이 빠르고 간편하게 처리할 수 있게 해주는 고성능 수치 계산 라이브러리다.
import numpy as np
# 넘파이 배열로 변경
input_arr = np.array(fish_data)
target_arr = np.array(fish_target)
np.random.seed(42)
# 인덱스를 생성
index = np.arange(49)
# 인덱스를 랜덤하게 섞는다.
np.random.shuffle(index)
print(index)
[13 45 47 44 17 27 26 25 31 19 12 4 34 8 3 6 40 41 46 15 9 16 24 33 30 0 43 32 5 29 11 36 1 21 2 37 35 23 39 10 22 18 48 20 7 42 14 28 38]
섞은 데이터 확인
파란색이 훈련 세트, 주황색이 테스트 세트로 골고루 잘 섞여서 들어간 것을 확인할 수 있다.
test_input = input_arr[index[35:]]
test_target = target_arr[index[35:]]
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()

사이킷런으로 훈련 세트와 테스트 세트 나누기
수상한 도미 한 마리
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
fish_data = np.column_stack((fish_length, fish_weight))
fish_target = np.concatenate((np.ones(35), np.zeros(14)))
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, stratify=fish_target, random_state=42)
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
1.0
위와 같이 세팅 후 스코어 평가를 해보니 1로 나왔다.
하지만 도미 데이터 인 아래의 데이터(25,150)를 넣고 출력하자 결과는 0으로 빙어가 나왔다. (도미는 1, 빙어는 0)
print(kn.predict([[25, 150]]))
[0.]
왜 이런 일이 발생했을까? 해당 데이터를 그래프로 확인해 보자.
import matplotlib.pyplot as plt
plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

그래프 상에서는 분명 아래에 모여있는 빙어 쪽보다는 도미에 가까워 보인다.
그런데 왜? 빙어로 표시가 된 걸까?
우리는 도미인지, 빙어인지 판단을 k-최근접 이웃 모델을 사용하였는데 해당 모델에서 참조한 데이터는 어떤 건지 확인이 필요할 것 같다.
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()

그래프에서 표기된 초록색의 값들이 참조한 값들이다.
그런데 그래프를 가만 보니 뭔가 좀 이상하다.
가로와 세로의 비율이 맞지 않아 더 가까운 도미의 데이터를 나 두고 빙어의 데이터를 참조하여 이와 같은 문제가 발생했던 거다.
그럼 가로축도 세로축과 같이 xlim 함수를 이용하여 기준점을 맞춰보자
데이터 전처리란?
이렇게 기준이 다르다면 알고리즘이 올바르게 예측할 수 없다. 특히 알고리즘이 거리 기반일 경우에는 더 그렇다.
우리가 사용한 k-최근접 이웃도 거리 기반 알고리즘 이기 때문에 특성 값을 일정한 기준으로 맞춰주는 작업이 필요한데 이를 "데이터 전처리"라고 부르며, 가장 널리 사용하는 전처리 방법 중 하나는 표준점수가 있다.
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()

x, y 축 값을 비슷하게 맞추니 산점도가 거의 일직선으로 나타난 것을 확인할 수 있다.
아까 테스트했던 도미 데이터를 다시 한번 확인해 보자!
print(kn.predict([[25, 150]]))
[1.]
결과 값이 1로 바뀐 것을 확인할 수 있다.
그럼 이번엔 그래프에서 참조된 데이터들을 확인해 보자.
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()

빙어의 값을 참조하던 아까와는 달리 거리 수치 상 빙어보다 가까운 도미 데이터들을 참조하고 있는 것으로 변경된 것을 확인할 수 있다. 😉
'Newb > 머신러닝' 카테고리의 다른 글
| [혼공머신] 6주차 [6/6] (8) | 2025.08.15 |
|---|---|
| [혼공머신] 5주차 [5/6] (13) | 2025.08.09 |
| [혼공머신] 4주차 [4/6] (3) | 2025.07.27 |
| [혼공머신] 3주차[3/6] (1) | 2025.07.20 |
| [혼공머신] 2주차 (11) | 2025.07.13 |