SeSAC - 머신러닝 데이터 분석
Andrew Ng
https://www.coursera.org/instructor/andrewng
서비스 모델을 만들 때 필요한것
- input data의 형식 : 입력이 무엇인가?
- output data의 형식 : 출력으로 무엇이 나올것인가?
- post function
CRISP-DM의 단계에서 고려해봐야할것
- 비즈니스 이해
- problem scoping : 윤리적으로 해도 되는것인가?
3-2 선형 회귀
k-최근접 이웃의 한계
import numpy as np
perch_length = np.array(
[8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0,
21.0, 21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5,
22.5, 22.7, 23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5,
27.3, 27.5, 27.5, 27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0,
36.5, 36.0, 37.0, 37.0, 39.0, 39.0, 39.0, 40.0, 40.0, 40.0,
40.0, 42.0, 43.0, 43.0, 43.5, 44.0]
)
perch_weight = np.array(
[5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0,
110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0,
130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0,
197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0,
514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0,
820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0,
1000.0, 1000.0]
)
from sklearn.model_selection import train_test_split
# 훈련 세트와 테스트 세트로 나눕니다
train_input, test_input, train_target, test_target = train_test_split(
perch_length, perch_weight, random_state=42)
# 훈련 세트와 테스트 세트를 2차원 배열로 바꿉니다
train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor(n_neighbors=3)
# k-최근접 이웃 회귀 모델을 훈련합니다
knr.fit(train_input, train_target)
print(knr.predict([[50]]))
-> [1033.33333333]
import matplotlib.pyplot as plt
# 50cm 농어의 이웃을 구합니다
distances, indexes = knr.kneighbors([[50]])
# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 훈련 세트 중에서 이웃 샘플만 다시 그립니다
plt.scatter(train_input[indexes], train_target[indexes], marker='D')
# 50cm 농어 데이터
plt.scatter(50, 1033, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
선형회귀는 오차 베이스
- 2차원 평면에서 직선은 몇개나 그릴 수 있을까? -> 무한대를 그릴 수 있다.
- 그렇다면 선형회귀는 원본 데이터와 가장 오차가 작은 직선을 그리는것 위의 분홍색 선은 검정색 선보다 원본 데이터를 더 잘 나타내고 있다고 볼수 있다.
# 50센치 물고기의 가장 가까운 3 물고기의 평균 무게
print(np.mean(train_target[indexes]))
# -> 1033.3333333333333
print(knr.predict([[100]]))
# -> [1033.33333333]
# -> 문제가 있다. 그렇다면?
# 100cm 농어의 이웃을 구합니다
distances, indexes = knr.kneighbors([[100]])
# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 훈련 세트 중에서 이웃 샘플만 다시 그립니다
plt.scatter(train_input[indexes], train_target[indexes], marker='D')
# 100cm 농어 데이터
plt.scatter(100, 1033, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
선형 회귀
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
# 선형 회귀 모델 훈련
lr.fit(train_input, train_target)
# 50cm 농어에 대한 예측
print(lr.predict([[50]]))
# -> [1241.83860323]
# 기울기와 절편
# 만약, 0cm짜리 물고기가 있다면, 그 물고기의 무게는 -709.01그램이다.
print(lr.coef_, lr.intercept_)
# -> [39.01714496] -709.0186449535477
# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 15에서 50까지 1차 방정식 그래프를 그립니다
plt.plot([15, 50], [15*lr.coef_+lr.intercept_, 50*lr.coef_+lr.intercept_])
# 50cm 농어 데이터
plt.scatter(50, 1241.8, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
print(lr.score(train_input, train_target))
print(lr.score(test_input, test_target))
# 과적합 되어있다. 곡선이라면 어떨까?
# 0.939846333997604
# 0.8247503123313558
다항 회귀
# 곡선으로 측정
# 입력값에 임의로 제곱항을 만들어 2차함수를 만든다
train_poly = np.column_stack((train_input ** 2, train_input))
test_poly = np.column_stack((test_input ** 2, test_input))
# 2차원 데이터
print(train_poly.shape, test_poly.shape)
# -> (42, 2) (14, 2)
lr = LinearRegression()
lr.fit(train_poly, train_target)
# 입력 또한 제곱을 넣어준다.
print(lr.predict([[50**2, 50]]))
# -> [1573.98423528]
print(lr.coef_, lr.intercept_)
# -> [1.01433211 -21.55792498] 116.0502107827827
print(lr.score(train_poly, train_target))
print(lr.score(test_poly, test_target))
# 0.9706807451768623
# 0.9775935108325122
다항회귀가 선형회귀보다 훨씬 정확한데도 왜 사용하지 않을까?
- 과적합을 피하기 위해서
- 원본 데이터의 기하학적 특성을 파악할 수 없다. = 차원이 높아지면 우리는 볼수없기 때문에(실제 데이터를 시각화 할 수 없다)
3-3 특성 공학과 규제
데이터 준비
import pandas as pd
df = pd.read_csv('https://bit.ly/perch_csv_data')
# 데이터프레임을 넘파이 행렬로 바꾼다.
perch_full = df.to_numpy()
print(perch_full)
# 물고기의 길이, 높이, 두께
[[ 8.4 2.11 1.41]
[13.7 3.53 2. ]
[15. 3.82 2.43]
[16.2 4.59 2.63]
[17.4 4.59 2.94]
[18. 5.22 3.32]
[18.7 5.2 3.12]
[19. 5.64 3.05]
[19.6 5.14 3.04]
[20. 5.08 2.77]
[21. 5.69 3.56]
[21. 5.92 3.31]
[21. 5.69 3.67]
[21.3 6.38 3.53]
[22. 6.11 3.41]
[22. 5.64 3.52]
[22. 6.11 3.52]
[22. 5.88 3.52]
[22. 5.52 4. ]
[22.5 5.86 3.62]
[22.5 6.79 3.62]
[22.7 5.95 3.63]
[23. 5.22 3.63]
[23.5 6.28 3.72]
[24. 7.29 3.72]
[24. 6.38 3.82]
[24.6 6.73 4.17]
[25. 6.44 3.68]
[25.6 6.56 4.24]
[26.5 7.17 4.14]
[27.3 8.32 5.14]
[27.5 7.17 4.34]
[27.5 7.05 4.34]
[27.5 7.28 4.57]
[28. 7.82 4.2 ]
[28.7 7.59 4.64]
[30. 7.62 4.77]
[32.8 10.03 6.02]
[34.5 10.26 6.39]
[35. 11.49 7.8 ]
[36.5 10.88 6.86]
[36. 10.61 6.74]
[37. 10.84 6.26]
[37. 10.57 6.37]
[39. 11.14 7.49]
[39. 11.14 6. ]
[39. 12.43 7.35]
[40. 11.93 7.11]
[40. 11.73 7.22]
[40. 12.38 7.46]
[40. 11.14 6.63]
[42. 12.8 6.87]
[43. 11.93 7.28]
[43. 12.51 7.42]
[43.5 12.6 8.14]
[44. 12.49 7.6 ]]
import numpy as np
perch_weight = np.array(
[5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0,
110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0,
130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0,
197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0,
514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0,
820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0,
1000.0, 1000.0]
)
from sklearn.model_selection import train_test_split
# train_test_split default 값 75 : 25
train_input, test_input, train_target, test_target = train_test_split(perch_full, perch_weight, random_state=42)
사이킷런의 변환기
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures()
poly.fit([[2, 3]])
print(poly.transform([[2, 3]]))
# PolynomialFeatures = 임의의 값을 주면 자동으로 다항식을 만들어 준다.
# 1, 2^1, 3^1, 2^2, 2*3, 3^2
# -> [[1. 2. 3. 4. 6. 9.]]
poly = PolynomialFeatures(include_bias=False)
poly.fit([[2, 3]])
print(poly.transform([[2, 3]]))
# -> [[2. 3. 4. 6. 9.]]
poly = PolynomialFeatures(include_bias=False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
print(train_poly.shape)
# -> (42, 9)
poly.get_feature_names_out()
test_poly = poly.transform(test_input)
다중 회귀 모델 훈련하기
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))
# -> 0.9903183436982125
print(lr.score(test_poly, test_target))
# -> 0.9714559911594111
poly = PolynomialFeatures(degree=5, include_bias=False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
test_poly = poly.transform(test_input)
print(train_poly.shape)
# -> (42, 55)
lr.fit(train_poly, train_target)
# 트레이닝 데이터는 거의 1에 가까운 값이 됐지만
print(lr.score(train_poly, train_target))
# -> 0.9999999999996433
# 테스트 데이터는 엉망이 됐다
# 과적합(overfitting)
# -> 그냥 직선을 써라!
print(lr.score(test_poly, test_target))
# -> -144.40579436844948
규제(regularization)
- 릿지(Ridge)
- 라쏘(Lasso)
- 두가지 다 과적합을 막는 방법이다
미분이 불가능 함수
- x값이 0일때 미분할 수 없다
- 모든 꺾이는(특이점이 있는) 함수는 미분할 수 없다
https://m.blog.naver.com/alwaysneoi/100135882596
- 실제값(y)과 예측값($\widehat{y}$)의 차이 = 오차
- 오차를 구할 때 부호의 문제를 해결하려면? 절대값을 씌워야한다.
- 하지만 절대값을 사용하지 못하는 이유는 위의 이유와 같다.
선형회귀의 Cost function
평균 제곱근 오차, Root Mean Square Error, RMSE
[\sqrt{\frac{1}{n}\sum_{i=1}^{n}((y_{i}-\widehat{y_{i}})^2)}]
- $n$: 데이터 포인트의 개수를 나타낸다.
- $y_{i}$: 실제 관측된 값(데이터 포인트), 여기서 $i$는 데이터 포인트의 인덱스를 나타낸다.
- $\widehat{y_{i}}$: 모델이 예측한 값, 실제 관측된 값과 모델이 예측한 값 사이의 차이를 나타낸다.
- $(y_{i}-\widehat{y_{i}})^2$: 각 데이터 포인트에 대한 오차의 제곱을 나타낸다. 이것은 실제 값과 예측 값의 차이를 제곱한 값이다.
- $\sum_{i=1}^{n}$: 모든 데이터 포인트에 대해 오차 제곱을 합산하는 부분이다. $i$는 1부터 $n$까지 변화한다.
- $\frac{1}{n}$: 데이터 포인트의 개수로 나눠줌으로써 오차 제곱의 평균을 계산한다.
- $\sqrt{\text{…}}$: 앞서 계산한 평균 오차 제곱을 제곱근을 취해 RMSE를 계산한다. 이것은 오차의 제곱 평균의 제곱근으로, 실제 값과 예측 값 사이의 평균적인 오차 크기를 나타낸다.
평균 제곱 오차, Mean Square Error, MSE
- 선형회귀 모델의 예측 성능 평가 지표
- RMSE에 제곱근을 취하지 않은것
[\frac{1}{n}\sum_{i=1}^{n}((y_{i}-\widehat{y_{i}})^2)]
- 릿지 (Ridge) 회귀:
- 목적 함수: \(\text{MSE} + \alpha \sum_{j=1}^{p} \beta_j^2\)
여기서 $\text{MSE}$는 평균 제곱 오차(Mean Squared Error)를 나타내고, $\beta_j$는 모델의 가중치(weight)입니다. $\alpha$는 정규화 강도를 조절하는 매개변수로, 릿지 회귀에서는 이 값이 커질수록 가중치의 크기를 줄이는 역할을 한다.
- 라쏘 (Lasso) 회귀:
-
목적 함수: $$\text{MSE} + \alpha \sum_{j=1}^{p} \beta_j $$
마찬가지로 $\text{MSE}$는 평균 제곱 오차이고, $\beta_j$는 가중치다. 라쏘 회귀에서는 가중치의 절댓값을 사용하며, $\alpha$ 역시 정규화 강도를 조절하는 매개변수로 정규화 강도를 조절한다. 라쏘는 가중치를 0으로 만들 수 있어 변수 선택의 역할을 하며, 특성 선택에 유용하다.
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_poly)
train_scaled = ss.transform(train_poly)
test_scaled = ss.transform(test_poly)
릿지(Ridge)
from sklearn.linear_model import Ridge
ridge = Ridge()
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
# -> 0.9896101671037343
print(ridge.score(test_scaled, test_target))
# -> 0.9790693977615387
import matplotlib.pyplot as plt
train_score = []
test_score = []
alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
for alpha in alpha_list:
# 릿지 모델을 만듭니다
ridge = Ridge(alpha=alpha)
# 릿지 모델을 훈련합니다
ridge.fit(train_scaled, train_target)
# 훈련 점수와 테스트 점수를 저장합니다
train_score.append(ridge.score(train_scaled, train_target))
test_score.append(ridge.score(test_scaled, test_target))
plt.plot(np.log10(alpha_list), train_score)
plt.plot(np.log10(alpha_list), test_score)
plt.xlabel('alpha')
plt.ylabel('R^2')
plt.show()
ridge = Ridge(alpha=0.1)
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
print(ridge.score(test_scaled, test_target))
# 0.9903815817570367
# 0.9827976465386928
라쏘(Lasso)
from sklearn.linear_model import Lasso
lasso = Lasso()
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target))
# -> 0.989789897208096
print(lasso.score(test_scaled, test_target))
# -> 0.9800593698421883
train_score = []
test_score = []
alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
for alpha in alpha_list:
# 라쏘 모델을 만듭니다
lasso = Lasso(alpha=alpha, max_iter=10000)
# 라쏘 모델을 훈련합니다
lasso.fit(train_scaled, train_target)
# 훈련 점수와 테스트 점수를 저장합니다
train_score.append(lasso.score(train_scaled, train_target))
test_score.append(lasso.score(test_scaled, test_target))
plt.plot(np.log10(alpha_list), train_score)
plt.plot(np.log10(alpha_list), test_score)
plt.xlabel('alpha')
plt.ylabel('R^2')
plt.show()
lasso = Lasso(alpha=10)
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target))
print(lasso.score(test_scaled, test_target))
# 0.9888067471131867
# 0.9824470598706695
print(np.sum(lasso.coef_ == 0))
# 40
로지스틱 회귀(Logistic Regression)
- 로지스틱 회귀는 ‘분류’다.
- 비선형적, 분류의 문제(0이냐 1이냐 등의 명목형 수치)
더미 변수 처리, 원핫 인코딩(GPT 참고)
더미 변수 처리 (Dummy Variable Handling):
더미 변수 처리는 범주형 변수를 수치형 변수로 변환하는 작업을 말합니다. 범주형 변수는 명목형이나 순서형 변수일 수 있습니다. 명목형 변수의 경우, 각 범주(category)를 0 또는 1로 인코딩하여 표현합니다. 예를 들어, “성별”이라는 범주형 변수가 있다면 “남성”을 1, “여성”을 0 또는 그 반대로 인코딩할 수 있습니다.
순서형 변수의 경우에도 마찬가지로 더미 변수 처리를 할 수 있습니다. 예를 들어, “학력”이라는 범주형 변수가 있다면 “고졸”, “대학생”, “석사” 등을 0 또는 1로 인코딩할 수 있습니다.
원핫 인코딩 (One-Hot Encoding):
원핫 인코딩은 범주형 변수의 각 범주를 이진(0 또는 1) 형태로 나타내는 방법 중 하나입니다. 각 범주에 대해 하나의 새로운 이진 변수(dummy variable)를 생성하고, 해당 범주에 해당하는 변수에 1을 할당하고 나머지 변수에는 0을 할당합니다.
예를 들어, “과일”이라는 범주형 변수가 “사과”, “바나나”, “딸기” 세 가지 범주를 가지고 있다면, 각각의 범주를 나타내는 세 개의 이진 변수를 생성하고 해당하는 곳에 1을 할당하고 나머지에는 0을 할당합니다. 이렇게 하면 각 과일이 해당하는 범주를 표현할 수 있습니다.
원핫 인코딩은 머신러닝 모델이 범주형 데이터를 이해하고 처리할 수 있도록 도와주며, 더미 변수와 비슷한 개념이지만 더 다양한 범주를 표현할 수 있습니다.
-> 명목형 수치들을 가중치를 가질 수 있게 바꿔주는것
로지스틱 함수(Logistic Function), 시그모이드 함수(Sigmoid Function)
https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%A7%80%EC%8A%A4%ED%8B%B1_%ED%95%A8%EC%88%98
[f(z) = \frac{1}{1 + e^{-z}}]
- 이 로지스틱 함수의 z 자리에 선형회귀식을 넣는다
- 직선 함수를 z자리에 넣으면 곡선으로 바뀐다
- 로지스틱 함수의 값은 0에서 부터 1 사이의 값이 나온다
로지스틱 함수의 Cost function
[cost F = -(Plog_{e}\widehat{P})]
- $P$: 실제 관측값을 나타낸다. 이 값은 0 또는 1일 수 있으며, 각 데이터 포인트의 실제 클래스를 나타낸다.
- $\widehat{P}$: 모델의 예측 확률을 나타낸다. 이 값은 로지스틱 함수를 통해 계산된 예측 확률이며, 0과 1 사이의 값이다.
- $\log_e$: 자연 로그(natural logarithm)를 나타낸다. 자연 로그는 $e$ (2.71828…)를 밑으로 하는 로그를 의미한다.
럭키백의 확률
import pandas as pd
fish = pd.read_csv('https://bit.ly/fish_csv_data')
fish.head()
# 종, 무게, 길이, 직경, 높이, 두께
Species | Weight | Length | Diagonal | Height | Width | |
---|---|---|---|---|---|---|
0 | Bream | 242.0 | 25.4 | 30.0 | 11.5200 | 4.0200 |
1 | Bream | 290.0 | 26.3 | 31.2 | 12.4800 | 4.3056 |
2 | Bream | 340.0 | 26.5 | 31.1 | 12.3778 | 4.6961 |
3 | Bream | 363.0 | 29.0 | 33.5 | 12.7300 | 4.4555 |
4 | Bream | 430.0 | 29.0 | 34.0 | 12.4440 | 5.1340 |
print(pd.unique(fish['Species'])
# 7가지 종류의 물고기
# ['Bream' 'Roach' 'Whitefish' 'Parkki' 'Perch' 'Pike' 'Smelt']
# 전체 중에 Species 값을 빼고 독립변수로 사용한다.
fish_input = fish[['Weight','Length','Diagonal','Height','Width']].to_numpy()
print(fish_input[:5])
[[242. 25.4 30. 11.52 4.02 ]
[290. 26.3 31.2 12.48 4.3056]
[340. 26.5 31.1 12.3778 4.6961]
[363. 29. 33.5 12.73 4.4555]
[1. 29. 34. 12.444 5.134 ]]
# 실제론 글자로 되어있지만, 숫자로 만들어 처리한다
fish_target = fish['Species'].to_numpy()
from sklearn.model_selection import train_test_split
# 정규화를 한다.
# 모든 변수가 자신의 성질을 유지하되, 변수들 간에 비교를 할수 있게 -1에서 1사이의 값으로 바꾼다.
train_input, test_input, train_target, test_target = train_test_split(fish_input, fish_target, random_state=42)
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
trst_scaled = ss.transform(test_input)
k-최근접 이웃 분류기의 확률 예측
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier(n_neighbors=3)
kn.fit(train_scaled, train_target)
print(kn.score(train_scaled, train_target))
print(kn.score(test_scaled, test_target))
# 0.8907563025210085 0.85
print(kn.classes_)
# ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
print(kn.predict(test_scaled[:5]))
# ['Perch' 'Smelt' 'Pike' 'Perch' 'Perch']
import numpy as np
proba = kn.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=4))
# ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
[[0. 0. 1. 0. 0. 0. 0. ] # Perch 일 확률이 100%다
[0. 0. 0. 0. 0. 1. 0. ] # Smelt 일 확률이 100%다
[0. 0. 0. 1. 0. 0. 0. ] # Pike 일 확률이 100%다
[0. 0. 0.6667 0. 0.3333 0. 0. ] # Perch일 확률이 66%, Roach일 확률이 33%
[0. 0. 0.6667 0. 0.3333 0. 0. ]]
# 4번째 데이터의 값을 넣었더니
distances, indexes = kn.kneighbors(test_scaled[3:4])
print(train_target[indexes])
# Roach 33% Perch 66%
# [['Roach' 'Perch' 'Perch']]
로지스틱 회귀
import numpy as np
import matplotlib.pyplot as plt
z = np.arange(-5, 5, 0.1)
phi = 1 / (1 + np.exp(-z))
# phi = 1 / (1 + np.exp(10*-z))
plt.plot(z, phi)
plt.xlabel('z')
plt.ylabel('phi')
plt.show()
로지스틱 회귀로 이진 분류 수행하기
char_arr = np.array(['A', 'B', 'C', 'D', 'E'])
print(char_arr[[True, False, True, False, False]])
# ['A', 'C']
# True 값만 출력됐다.
# 7종류 중 2종류의 물고기만 따로 뽑았다
bream_smelt_indexes = (train_target == 'Bream') | (train_target == 'Smelt')
train_bream_smelt = train_scaled[bream_smelt_indexes]
target_bream_smelt = train_taget[bream_smelt_indexes]
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)
print(lr.predict(train_bream_smelt[:5]))
# ['Bream' 'Smelt' 'Bream' 'Bream' 'Bream']
# 이진분류는 그것일 확률이 맞냐 아니냐를 뽑는다.
print(lr.predict_proba(train_bream_smelt[:5]))
[[0.99759855 0.00240145] # 99.7% 확률로 bream
[0.02735183 0.97264817]
[0.99486072 0.00513928]
[0.98584202 0.01415798]
[0.99767269 0.00232731]]
print(lr.classes_)
# ['Bream' 'Smelt']
print(lr.coef_, lr.intercept_)
# [[-0.4037798 -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]
decisions = lr.decision_function(train_bream_smelt[:5])
print(decisions)
# [-6.02927744 3.57123907 -5.26568906 -4.24321775 -6.0607117 ]
from scipy.special import expit
print(expit(decisions))
# [0.00240145 0.97264817 0.00513928 0.01415798 0.00232731]
로지스틱 회귀로 다중 분류 수행하기
# `C`: 규제 강도를 나타내는 매개변수. 작은 값일수록 규제가 강화되며, 큰 값일수록 규제가 약화된다. 따라서 `C` 값이 20인 경우, 규제가 상대적으로 약화된 모델이 생성.
# `max_iter`: 최적화 알고리즘이 수렴하기 위한 최대 반복 횟수를 지정 1000으로 설정되어 있으므로, 최대 1000번의 반복을 통해 모델을 학습
lr = LogisticRegression(C=20, max_iter=1000)
lr.fit(train_scaled, train_target)
print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))
# 0.9327731092436975
# 0.925
print(lr.predict(test_scaled[:5]))
# ['Perch' 'Smelt' 'Pike' 'Roach' 'Perch']
# probability 계산
proba = lr.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=3)
# softmax function을 거쳐서 아래의 값이 나온다.
# softmax function : 각 클래스에 대한 확률값을 0과 1 사이의 값으로 만들고, 모든 클래스에 대한 확률의 합이 1이 되도록 조정
[[0. 0.014 0.841 0. 0.136 0.007 0.003] # 84% 확률로 3번째 물고기
[0. 0.003 0.044 0. 0.007 0.946 0. ]
[0. 0. 0.034 0.935 0.015 0.016 0. ]
[0.011 0.034 0.306 0.007 0.567 0. 0.076]
[0. 0. 0.904 0.002 0.089 0.002 0.001]]
print(lr.classes_)
# ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
# 기울기가 각각 7개의 logistic function 가중치가 5개, 절편이 7개
print(lr.coef_.shape, lr.intercept_.shape)
# (7, 5) (7,)
decision = lr.decision_function(test_scaled[:5])
print(np.round(decision, decimals=2))
[[ -6.5 1.03 5.16 -2.73 3.34 0.33 -0.63]
[-10.86 1.93 4.77 -2.4 2.98 7.84 -4.26]
[ -4.34 -6.23 3.17 6.49 2.36 2.42 -3.87]
[ -0.68 0.45 2.65 -1.19 3.26 -5.75 1.26]
[ -6.4 -1.99 5.82 -0.11 3.5 -0.11 -0.71]]
from scipy.special import softmax
proba = softmax(decision, axis=1)
print(np.round(proba, decimals=3))
[[0. 0.014 0.841 0. 0.136 0.007 0.003]
[0. 0.003 0.044 0. 0.007 0.946 0. ]
[0. 0. 0.034 0.935 0.015 0.016 0. ]
[0.011 0.034 0.306 0.007 0.567 0. 0.076]
[0. 0. 0.904 0.002 0.089 0.002 0.001]]