2025. 3. 10. 01:30ㆍ머신러닝, 딥러닝 개념/지도 학습
내가 포스팅을 5일 정도 쉬었는데... 사실 그동안 논건 아니고 포스팅을 뭘 써야할 지 고민을 많이 했다. 뭘 써야할지 고민해야 될 정도로 머신러닝은 양이 방대한데 또 나는 4학년이니까 이거 개념 정리 하는거 말고도 졸업 프로젝트도 해야 되고, 공인 영어 성적이나 자격증도 따야되고, 공모전도 나가보고... 학부 연구생이나 인턴도 해야될 것 같은데 신경써야 할 일들이 너무 많은 것 같아서 힘들다.
이번 포스팅은 다음 교재의 내용을 참고해서 작성했다.
https://web.stanford.edu/~jurafsky/slp3/5.pdf
① 로지스틱 회귀(Logistic Regression)
로지스틱 회귀는 이름은 회귀지만 사실 분류 목적으로 쓰이는 알고리즘이다. 어떤 결과가 발생할 확률을 예측함으로써 이진 분류 작업을 수행하기 위한 지도 학습 알고리즘이다. 즉, 로지스틱 회귀 모델은 확률 계산을 통해 '예/아니오, 0/1, 참/거짓' 같이 둘 중 하나를 예측한다.
$y \in \{0, 1\}$인 타깃값 $y$가 있다고 하자. 즉, $y$는 0 또는 1의 값만 가질 수 있다. 그렇다면 어떤 입력값 $x$에 대해서 $y=f(x)$를 만족시키는 함수 $f$를 정의할 수 있을까?
(1) 시그모이드 함수(Sigmoid Function)
시그모이드 함수(또는 로지스틱 함수(Logistic Function)) $\sigma (x)$는 다음과 같이 정의된다.
$$\sigma (x) = \frac{1}{1+e^{-x}} = \frac{1}{1+\exp(-x)}$$ ($e^x=\exp(x)$이다.)
그리고 이 함수의 그래프는 다음과 같이 생겼다.
그래프를 보다시피, 이 함수는 $0<\sigma (x)<1$ 범위의 값만을 가질 수 있다. 따라서 확률값을 표현하기에 적절한 함수라고 할 수 있다. 그리고 $x=0$ 부근에서는 거의 선형적인 형태를 보이지만 양 끝에서는 평탄해지므로 이상치(outlier) 값을 0이나 1로 수축시킬 수 있다. 또한 미분 결과가 깔끔하다는 장점도 있어서 로지스틱 회귀를 할 때 이 시그모이드 함수가 이용된다.
시그모이드 함수의 주요한 성질들로는 다음과 같은 것들이 있다. ($\sigma(x) = \frac{1}{1+e^{-x}}$라는 정의만 이용하면 충분히 유도할 수 있는 성질들이다.)
① $1 - \sigma(x) = \sigma(-x)$
② $\frac{d \sigma(x)}{dx} = \sigma(x)(1-\sigma(x))$
(2) 로지스틱 회귀(Logistic Regression)
가중치 벡터 ${\rm w} = \begin{bmatrix} w_1 \\ w_2 \\ \vdots \\ w_n \end{bmatrix}$와 입력 벡터 ${\rm x} = \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix}$, 그리고 편향 $b$에 대한 선형 회귀식은 ${\rm w \cdot x} + b$이다(이전 포스팅에서는 행렬 방식으로 ${\rm w^Tx} + b$라고 썼지만 내적 기호 $\cdot$ 을 써서 표현해도 의미는 같음).
로지스틱 회귀에서 입력 벡터 $\rm x$에 대해 타깃값 $y$가 1이 될 확률 $P(y=1|{\rm x})$는 다음과 같이 시그모이드 함수에 선형 회귀식을 입력값으로 넣은 형태로 정의된다.
$$P(y=1|{\rm x}) = \sigma( {\rm w \cdot x}+b )= \frac{1}{1+\exp({\rm w \cdot x}+b)}$$
$y$는 0 또는 1만 값으로 가질 수 있기 때문에 $y$가 0이 될 확률은 $ P(y=0|{\rm x}) = 1 - P(y=1|{\rm x}) $이다. 그런데 앞서 언급한 시그모이드 함수의 성질 ①을 이용하면 $ P(y=0|{\rm x}) $도 다음과 같이 간단히 표현 가능하다.
$$\begin{align} P(y=0|{\rm x}) &= 1 - P(y=1|{\rm x}) \\ &= 1 - \sigma( {\rm w \cdot x}+b ) \\ &= \sigma(-({\rm w \cdot x} + b)) \end{align}$$
과정은 생략하고 결론만 쓰면 다음과 같다.
교재에서 로짓 함수(logit function 또는 log-odds function)라는 개념도 정리되어 있어서 그 개념을 간단히 설명하면 다음과 같다. 시그모이드 함수의 입력값 ${\rm w \cdot x}+b$을 로짓(logit)이라고 하는데, 시그모이드 함수가 로짓값 ${\rm w \cdot x}+b$를 입력값으로 받아 $y=1$이 될 확률을 계산하는 함수였다면, 로짓 함수는 거꾸로 $y=1$이 될 확률 $p$를 입력으로 받아 $ P(y=1|{\rm x}) =p$가 되기 위해서 로짓값 ${\rm w \cdot x}+b$이 어떻게 되어야 할 것인지 계산하는 함수이다. 즉, 로짓 함수는 다음과 시그모이드 함수의 역함수로 정의된다.
$${\rm logit}(p) = \sigma^{-1}(p) = \ln \frac{p}{1-p} $$
$ \sigma^{-1}(p) = \ln \frac{p}{1-p}$가 되는 것은 쉽게 유도할 수 있으니 한 번 유도해보고 넘어가기로 하자.
(3) 이진 로지스틱 회귀 예제
로지스틱 회귀는 자연어 처리 분야에서 이용할 수 있다고 한다. 다음과 같은 영화 리뷰가 있다고 하자.
It's hokey . There are virtually no surprises , and the writing is second-rate . So why was it so enjoyable ? For one thing , the cast is great . Another nice touch is the music . I was overcome with the urge to get off the couch and start dancing . It sucked me in , and it'll do the same to you .
영어로 되어 있는데, 필자가 이를 한국어로 번역해보겠다.
이 영화는 진부해요. 놀랍다 할 포인트가 거의 없고, 대본은 완전 B급입니다. 그런데 이 영화가 왜 재밌었을까요? 그 이유 중 하나는 출연진들이 좋았던 점을 들 수 있습니다. 좋았던 부분을 또 하나 꼽자면 음악이 있습니다. 음악을 듣고 당장 소파에서 일어나서 춤을 추고 싶었지만 꾹 참았습니다. 나는 이 음악에 흠뻑 빠졌고, 여러분들도 마찬가지일 것이라 생각합니다.
앞에서는 부정적인 이야기를 짧게 하다가 뒤에서는 출연진과 음악이 좋았다는 점을 길게 언급하여 전반적으로는 영화를 긍정적으로 평가하는 느낌의 리뷰이다. 지금부터 로지스틱 회귀를 이용해 이 리뷰의 감성을 분석해볼 것이다.
먼저 교재에서는 입력 벡터 $\rm x$에 들어갈 6개의 입력값 $x_1$, $x_2$, $\cdots$, $x_6$을 다음과 같이 정의하였다.
$x_1$ : 긍정적인 어휘의 개수
$x_2$ : 부정적인 어휘의 개수
$x_3$ : 리뷰에 "no"라는 단어가 있으면 1, 없으면 0의 값을 가지는 변수
$x_4$ : 1인칭 대명사(나, 우리) 또는 2인칭 대명사(너)의 개수
$x_5$ : 리뷰에 느낌표가 있으면 1, 없으면 0의 값을 가지는 변수
$x_6$ : $\ln(\rm 단어\ \ 개수)$
$x_6$의 정의가 좀 애매할 수 있는데, 띄어쓰기로 구분되는 하나의 어절을 한 단어로 보면 된다. 즉, 영어 리뷰 문자열이 저장된 변수를 review라고 하면 len(review.split())으로 단어의 개수를 구할 수 있다.
원문에서 문장 부호가 모두 띄어쓰기 되어 있어서 문장 부호도 하나의 단어로 취급된다. 이렇게 단어의 개수를 세면 단어의 개수가 총 66개가 나온다. 따라서 $x_6 = \ln 66 \approx 4.19$이다.
나머지 $x_1$, $x_2$, $\cdots$, $x_5$의 값도 구해보자.
① 긍정적인 어휘 : enjoyable, great, nice로 총 3개. 따라서 $x_1 = 3$
② 부정적인 어휘 : hokey, second-rate로 총 2개. 따라서 $x_2 = 2$
③ 첫 번째 문장에 'no'라는 단어 존재. 따라서 $x_3 = 1$
④ 1인칭 또는 2인칭 대명사 : I, me, you로 총 3개. 따라서 $x_4 = 3$
⑤ 리뷰에 느낌표 없음. 따라서 $x_5 = 0$
⑥ 아까 말한것처럼 $x_6 = \ln 66$
그리고 각 변수에 대한 가중치 값을 담은 가중치 벡터 $\rm w$와 편향 $b$가 다음과 같이 정의되었다고 하자.
가중치 값을 보면 긍정적인 리뷰의 개수 $x_1$에 양수 가중치 값 2.5가 할당되었고, 부정적인 리뷰의 개수 $x_2$에 음수 가중치 값 -5.0이 할당되었음을 알 수 있다. 따라서 로짓값 ${\rm w \cdot x}+b$를 계산했을 때 결과가 양수이면 긍정적일 리뷰일 확률이, 음수이면 부정적인 리뷰일 확률이 높다고 판단할 수 있다.
그렇다면 이제 시그모이드 함수의 입력으로 들어갈 로짓값을 직접 구해보자.
$$ {\rm w \cdot x}+b = \begin{bmatrix} 2.5 \\ -5.0 \\ -1.2 \\ 0.5 \\ 2.0 \\ 0.7 \end{bmatrix} \cdot \begin{bmatrix} 3 \\ 2 \\ 1 \\ 3 \\ 0 \\ \ln 66 \end{bmatrix} + 0.1 = 0.7\ln 66 - 2.1 \approx 0.83276$$
따라서 이 리뷰가 긍정적일 리뷰일 확률은
$$P(y=1|{\rm x}) = \sigma(0.83276) \approx 0.697$$
이고, 부정적인 리뷰일 확률은
$$P(y=0|{\rm x}) = 1 - 0.697 = 0.303$$
이다.
결론적으로 이 리뷰가 긍정적인 리뷰일 확률은 69.7%, 부정적인 리뷰일 확률은 30.3%이므로 로지스틱 회귀에서는 이 리뷰를 긍정적인 리뷰로 판단한다.
(4) 다항 로지스틱 회귀(Multinomial Logistic Regression)
위 영화 리뷰 감성 분석에서는 클래스가 '긍정적인' 또는 '부정적인'으로 2개였다.
하지만 클래스를 3개 이상으로 지정하고 싶을 수 있다. 예를 들어, 긍정적이지도 않고 부정적이지도 않은 중립적인 리뷰가 있을 수 있다. 이 클래스를 추가해서 '긍정적인', '부정적인', '중립적인'으로 총 3개의 클래스를 만들 수 있다. 이렇게 3개 이상의 클래스로 분류를 시행하고 싶을 때 다항 로지스틱 회귀를 사용한다.
소프트맥스 함수(Softmax Function)
소프트맥스 함수(소맥?)는 시그모이드 함수를 일반화한 형태라고 보면 된다. $K$개의 값이 들어 있는 입력 벡터 ${\rm z}=\begin{bmatrix} z_1 & z_2 & \cdots & z_K \end{bmatrix}$에 대해 $\rm z$의 한 요소 $z_i$에 대한 소프트맥스 값은 다음과 같이 정의된다.
$${\rm softmax}(z_i) = \frac{\exp(z_i)}{\sum_{j=1}^{K} \exp(z_j)} = \frac{e^{z_i}}{e^{z_1}+e^{z_2}+\cdots+e^{z_K}}$$
예를 들어, 입력 벡터가 ${\rm z} = \begin{bmatrix} 0.6 & 1.1 & -1.5 & 1.2 & 3.2 & -1.1 \end{bmatrix}$로 주어졌다고 할 때, $\rm softmax(z)$은 다음과 같이 계산된다.
즉, 이 예시에서는 6개의 타깃 클래스가 존재하고, 5번 클래스의 예측 확률이 73.81%로 가장 유력하게 예측되고 있고, 3번 클래스의 예측 확률이 0.67%로 가장 낮게 예측된다. 당연한 이야기지만 $\rm softmax(z)$의 요소를 모두 합하면 1이 된다.
(5) 교차 엔트로피(Cross Entropy)
베르누이 분포(Bernoulli distribution)
확률 변수 $X$가 0 또는 1의 값만을 가지고 $X$가 1이 될 확률을 $p$라고 할 때, 확률 변수 $X$는 베르누이 분포 ${\rm Bernoulli}(p)$를 따른다고 하며, 이를 $X \sim {\rm Bernoulli}(p)$라고 표현한다.
확률 변수 $X$가 베르누이 분포 ${\rm Bernoulli}(p)$를 따를 때, 확률 변수 $X$가 어떤 값 $x \in \{ 0, 1 \}$를 가질 확률 $P(X=x)$는 다음과 같이 표현할 수 있다.
또는 케이스를 나누지 않고 다음과 같이 한 줄에 쓸 수도 있다.
$$P(X=x)=p^{x}(1-p)^{1-x}$$
이를 이진 로지스틱 회귀에서 타깃값에 대한 확률 $P(y|{\rm x})$에 적용하면 다음과 같다.
$$P(y|x)={\hat{y}}^{y}(1-\hat{y})^{1-y}$$
여기서 ${\hat{y}}$는 예측 확률이고, $y$는 실제 타깃값(정답)이라고 생각하면 될 것 같다.
이진 교차 엔트로피(Binary Cross Entropy, BCE)
이진 교차 엔트로피는 다음과 같이 정의된다.
$${\rm BCEloss} = -\ln P(y|{\rm x})$$
이 수식을 풀면 다음과 같다.
$$\begin{align} {\rm BCEloss} &= -\ln P(y|{\rm x}) \\\\ &= -\ln {\hat{y}}^{y}(1-\hat{y})^{1-y} \\\\ &= -\{y\ln{\hat{y}} + (1-y)\ln(1-\hat{y})\} \end{align}$$
로지스틱 회귀에서는 손실 함수로 주로 이 이진 교차 엔트로피를 사용한다.
이를 위에서 했던 영화 리뷰 감성 분석 예제에 적용하면 다음과 같다. 리뷰가 실제로 긍정적인 경우와 부정적인 경우로 나누어 계산해볼 것이다.
(1) 영화 리뷰가 실제로 긍정적일 때 ($y=1$, 예측이 옳음)
$${\rm BCEloss} = -(1 \ln 0.697 + 0 \ln 0.303) \approx 0.361$$
(2) 영화 리뷰가 실제로 부정적일 때 ($y=0$, 예측이 틀림)
$${\rm BCEloss} = -(0 \ln 0.697 + 1 \ln 0.303) \approx 1.194$$
예측이 맞을 경우 BCE 값이 작게 나오고, 예측이 틀릴 경우 BCE 값이 높게 나옴을 알 수 있다.
참고로 $-\ln 0.5 \approx 0.693$이므로, 완전히 중립적인 예측을 했을 때 BCE 값은 0.693이 된다. 따라서 '맞다/틀리다'로만 따지자면 BCE 값이 0.693보다 작으면 맞은 예측이고, 0.693보다 크면 틀린 예측이라고 할 수 있다.
단순한 '맞다/틀리다' 여부 뿐만 아니라 예측의 확신 강도도 BCE 값에 큰 영향을 주는 요소이다. 로지스틱 회귀를 통해 계산한 확률값이 1에 가까우면 강한 긍정 예측이고, 0에 가까우면 강한 부정 예측이다. 그리고 0.5에 가까울수록 약한 예측이라고 할 수 있다.
만약에 감성 분석 예제에서 이 리뷰가 긍정적일 확률을 69.7%가 아니라 90%로 예측했다고 하자. 그렇다면 BCE 값은 다음과 같이 바뀐다.
(1) 영화 리뷰가 실제로 긍정적일 때 ($y=1$, 예측이 옳음)
$${\rm BCEloss} = -(1 \ln 0.697 + 0 \ln 0.303) \approx 0.105$$
(2) 영화 리뷰가 실제로 부정적일 때 ($y=0$, 예측이 틀림)
$${\rm BCEloss} = -(0 \ln 0.697 + 1 \ln 0.303) \approx 2.303$$
예측의 확신 강도가 강할 때, 이 예측이 맞으면 손실 함수 값은 상당히 작아진다. 하지만 반대로 강한 예측이 틀리면 페널티도 그만큼 커진다. 이렇듯 예측의 확신 강도가 크면 예측이 맞았을 때 베네핏도 크지만 틀렸을 때 페널티도 크게 나온다고 생각하면 된다. 반면에 예측의 확신 강도가 작으면 예측이 맞았을 때 베네핏도 작고 틀렸을 때 페널티도 작게 나온다.
범주형 교차 엔트로피(Categorical Cross Entropy, CCE)
범주형 교차 엔트로피는 다항 로지스틱 회귀에서 사용하는 손실 함수이다. 범주형 교차 엔트로피는 다음과 같이 정의된다.
$${\rm CCEloss}=-\sum_{i=1}^{K}y_{i} \ln{\hat{y_{i}}}$$
$y_{i}$는 i번째 클래스가 정답이면 1, 오답이면 0이 들어가는 변수이다. 따라서 $t$번째 클래스가 정답 클래스라면 $y_{t}=1$이고 그 외 $i \neq t$인 경우는 모두 $y_{i}=0$이다. 따라서 CCE를 다음과 같이 간단히 쓸 수도 있다.
$${\rm CCEloss}=-\ln{\hat{y_t}}$$
이해를 돕기 위해 다음 예제를 다시 보자.
5번 클래스가 73.81%로 가장 높게 예측되는데, 만약에 5번 클래스가 실제로 정답이라고 하자. 그렇다면 CCE 값은 다음과 같다.
$${\rm CCEloss} = -\ln 0.7381 \approx 0.3037$$
그렇다면 오답으로 예측한 경우는 어떨까? 5번 클래스를 제외하고 그나마 예측 확률이 가장 높은 4번 클래스가 실제 정답이었다고 해보자. 그렇다면 CCE 값은 다음과 같이 된다.
$${\rm CCEloss} = -\ln 0.0999 \approx 2.304$$
BCE와 똑같은 맥락으로 CCE도 예측 결과가 틀리면 값이 매우 커지고, 정답 클래스 확률을 낮게할수록 더더욱 커진다. 만약에 3번 클래스가 실제 정답이었다면 CCE 값은 5까지 올라간다.
(6) 경사 하강법
다음과 같은 BCE 손실 함수를 가중치 벡터 $\rm w$에 미분해보자.
$$ {\rm BCEloss} = -\{y\ln{\hat{y}} + (1-y)\ln(1-\hat{y})\} $$
여기서 $\hat{y} = \sigma({\rm w \cdot x} + b)$이므로 위 수식을 $\rm w$에 대해 미분하는 과정은 다음과 같다.
이 과정 중에서 $\frac{\partial \hat{y}}{\partial {\rm x}}$를 구하는 과정은 다음과 같다.
시그모이드 함수의 미분법은 시그모이드 함수를 소개할 때 성질 ②로 언급한 바 있다. 다시 끌어오자면 $\frac{d \sigma(x)}{dx} = \sigma(x)(1-\sigma(x))$이므로 이를 이용해 미분을 수행할 수 있다. 단, 합성함수의 미분법이므로 $\rm x$가 하나 튀어나오는 것에 유의하면 된다.
따라서 경사 하강법을 이용해 가중치를 갱신하는 방법은 다음과 같다.
$$\begin{align} {\rm w}_{(i+1)}&={\rm w}_{(i)}-\alpha \times \frac{\partial \rm BCEloss}{\partial \rm w} \\\\ &= {\rm w}_{(i)}+\alpha \times (y-\hat{y}) \rm x \end{align}$$
(7) 코드 실습
이번에는 내가 임의로 만든 데이터가 아니라 사이킷런에 존재하는 토이 데이터셋을 이용할 것이다.
from sklearn.datasets import load_iris
iris = load_iris()
사이킷런에 존재하는 load_iris 데이터는 붓꽃(iris) 품종에 관련된 데이터셋이다. 이 데이터셋에 대한 설명은 다음 문서에 잘 나와있다.
load_iris 데이터셋 설명 : https://scikit-learn.org/stable/datasets/toy_dataset.html#iris-dataset
load_iris 함수 API : https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_iris.html#
불러온 데이터셋을 데이터프레임으로 만들어서 데이터프레임 내용을 살펴보자.
import pandas as pd
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df["target"] = iris.target
df["target"] = df["target"].map({0:'setosa', 1:'versicolor', 2:'virginica'})
df
꽃받침(sepal)의 길이(length)와 너비(width), 그리고 꽃잎(petal)의 길이와 너비가 주어져 있고, 붓꽃 품종이 타깃값으로 주어져 있다. 타깃값은 원래 0, 1, 2로 되어있지만, 문서를 참고해서 실제 명칭으로 바꿨다.
문서에 따르면
0은 Iris-Setosa(부채붓꽃)라는 품종이고, 1은 Iris-Versicolour이라는 품종, 2는 Iris-Viginica라는 품종이라고 한다.
from sklearn.model_selection import train_test_split
X = df.drop(["target"], axis=1)
y = df["target"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
X를 통해 y를 예측해야 하므로, X에는 정답인 target을 제외하고 나머지 피처값들이 들어가도록 하였고, y에는 정답인 target만 들어가도록 하였다.
그리고 훈련 데이터와 테스트 데이터의 비율을 7:3이 되도록 분리했다.
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("accuracy:", accuracy_score(y_test, y_pred))
그런 다음, 로지스틱 회귀 클래스를 불러와 훈련 데이터에 적합시키고, 이를 테스트 데이터에 대해 예측시킨다. 그리고 예측의 정확도를 출력한다.
accuracy: 1.0
정확도는 놀랍게도 100%이다. 전체 데이터 샘플의 개수가 150개이고 테스트 데이터의 비율이 0.3이므로 테스트 데이터 샘플의 개수는 45인데 45개 붓꽃 품종을 모두 정확하게 맞혔다.
사실 이는 운 좋게 모델이 맞히기 힘들어하는 데이터 샘플이 test 데이터셋에 들어가지 않아서 그런 것일 수 있다. random_state를 바꾸면 100%가 안 나오는 경우를 확인할 수 있다. 그래도 대부분 95% 이상의 준수한 정확도를 보인다(random_state 46으로 하면 88.89% 뜨는건 비밀)
'머신러닝, 딥러닝 개념 > 지도 학습' 카테고리의 다른 글
지도 학습(6) : 서포트 벡터 머신 (1) | 2025.03.14 |
---|---|
지도 학습(5) : k-최근접 이웃 (3) | 2025.03.10 |
지도 학습(3) : 다항 회귀, 릿지 회귀, 라쏘 회귀, 엘라스틱 넷 (1) | 2025.03.05 |
지도 학습(2) : 다중 선형 회귀 (1) | 2025.03.01 |
지도 학습(1) : 단순 선형 회귀 (0) | 2025.02.27 |