SeSAC - 머신러닝 데이터 분석


Andrew Ng

https://www.coursera.org/instructor/andrewng


서비스 모델을 만들 때 필요한것

  • input data의 형식 : 입력이 무엇인가?
  • output data의 형식 : 출력으로 무엇이 나올것인가?
  • post function

CRISP-DM의 단계에서 고려해봐야할것

  1. 비즈니스 이해
  • 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

다항회귀가 선형회귀보다 훨씬 정확한데도 왜 사용하지 않을까?

  1. 과적합을 피하기 위해서
  2. 원본 데이터의 기하학적 특성을 파악할 수 없다. = 차원이 높아지면 우리는 볼수없기 때문에(실제 데이터를 시각화 할 수 없다)


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)}]

  1. $n$: 데이터 포인트의 개수를 나타낸다.
  2. $y_{i}$: 실제 관측된 값(데이터 포인트), 여기서 $i$는 데이터 포인트의 인덱스를 나타낸다.
  3. $\widehat{y_{i}}$: 모델이 예측한 값, 실제 관측된 값과 모델이 예측한 값 사이의 차이를 나타낸다.
  4. $(y_{i}-\widehat{y_{i}})^2$: 각 데이터 포인트에 대한 오차의 제곱을 나타낸다. 이것은 실제 값과 예측 값의 차이를 제곱한 값이다.
  5. $\sum_{i=1}^{n}$: 모든 데이터 포인트에 대해 오차 제곱을 합산하는 부분이다. $i$는 1부터 $n$까지 변화한다.
  6. $\frac{1}{n}$: 데이터 포인트의 개수로 나눠줌으로써 오차 제곱의 평균을 계산한다.
  7. $\sqrt{\text{…}}$: 앞서 계산한 평균 오차 제곱을 제곱근을 취해 RMSE를 계산한다. 이것은 오차의 제곱 평균의 제곱근으로, 실제 값과 예측 값 사이의 평균적인 오차 크기를 나타낸다.


평균 제곱 오차, Mean Square Error, MSE

  • 선형회귀 모델의 예측 성능 평가 지표
  • RMSE에 제곱근을 취하지 않은것

[\frac{1}{n}\sum_{i=1}^{n}((y_{i}-\widehat{y_{i}})^2)]


  1. 릿지 (Ridge) 회귀:
  • 목적 함수: \(\text{MSE} + \alpha \sum_{j=1}^{p} \beta_j^2\)

여기서 $\text{MSE}$는 평균 제곱 오차(Mean Squared Error)를 나타내고, $\beta_j$는 모델의 가중치(weight)입니다. $\alpha$는 정규화 강도를 조절하는 매개변수로, 릿지 회귀에서는 이 값이 커질수록 가중치의 크기를 줄이는 역할을 한다.


  1. 라쏘 (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]]

관심있을 포스팅