---
# System prepended metadata

title: 'Logistic Regression : 巨星和矮星的分類'
tags: [Logistic Regression]

---

# 0. 前言

<img src="https://upload.wikimedia.org/wikipedia/commons/1/10/Blue_giant_star_artistic_recreation-bpk.jpg" height =ˇ200; style="display:block; margin-left:0; width:100%" />
    
(一顆漂亮的藍色巨星，[圖片來源](<https://upload.wikimedia.org/wikipedia/commons/1/10/Blue_giant_star_artistic_recreation-bpk.jpg> "https://upload.wikimedia.org/wikipedia/commons/1/10/Blue_giant_star_artistic_recreation-bpk.jpg"))

Logistic Regression，一個廣泛被用於許多領域(金融、社會學等...)的模型，主要用於分類問題。今天就跟著筆者豪豪，讓我們以[Kaggle 上巨星和矮星的資料](<https://www.kaggle.com/vinesmsuic/star-categorization-giants-and-dwarfs> "https://www.kaggle.com/vinesmsuic/star-categorization-giants-and-dwarfs")，來看看這個模型吧。

當<font color=#0000FF>巨星(Giant star)</font>和<font color=#0000FF>矮星(Dwarf star)</font> 有著相同的表面溫度時，巨星的亮度會比矮行星大。因此我們應該可以<mark>根據<font color=#0000FF>亮度(Luminosity)</font>和<font color=#0000FF>表面溫度(surface temperture)</font>來進行巨星和矮星的分類</mark>。

在天文學中，我們可以藉由<font color=#0000FF>色指數(B-V color index)</font>來衡量恆星的表面溫度，但恆星亮度會隨著距離我們遠近而受到影響，因此有什麼東西可以用來較客觀地量化亮度呢？

答案是<font color=#0000FF>絕對星等(Absolute magnitude)</font>，因為絕對星等相當於把天體放在相同的距離，去對亮度做比較。因此有了這兩項衡量單位，我們就可以試著使用Logistic regression，去對巨星和矮星做分類囉。

簡而言之，底下是我們的目的 :arrow_down: 

:::info
<font color=#></font> <font color=#FF0000>**使用 Logistic regression，猜測 B-V color index 和 Absolute Magnitude 的 <br>Decision Boundary，進而分類巨星和矮星。**</font>
&ensp;
<font color=#>資料來源：</font>https://www.kaggle.com/vinesmsuic/star-categorization-giants-and-dwarfs

<font color=#>P.S. Decision Boundary 詳見後面介紹</font>
:::
廢話不多說，就讓我們馬上開始吧~~~
# 1. 資料分析流程

底下是我們的資料分析流程

:::info
Step 1. 讀取原始資料
:::
:::warning
Step 2. 使用Python畫出資料的散佈圖，猜測 Decision Boundary
:::
:::danger
Step 3. 對資料進行 Logistic regression，分類巨星和矮星
:::
:::success
Step 4. 藉由 Test Data 評估準確率。
:::
## 1.1 資料讀取
首先我們先下載資料，來源裡的「Star3642_balanced.csv」，打開後可以發現裡面有 3642顆恆星的features，欄位如下(欄位描述詳見資料連結網址):

| 欄位名稱|Vmag | Plx |e_Plx|B-V|SpType|Amag|TargetClass|
| - | - | - |-|-|-|-|-|
| 描述| 視星等|地球和恆星的距離| 地球和恆星的距離的標準誤差 |色指數|恆星光譜|絕對星等|是否為巨星，0 代表矮星，1代表巨星|

再來使用下方的 Python Code 進行讀取並觀察資料; 
```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 檔案存放路徑
trainingSet_Path = 'Data\Star3642_balanced.csv'

# 使用 panda.read_csv 方法讀取檔案路徑內的資料
star_Df = pd.read_csv(trainingSet_Path)

# 觀察資料
print('資料總筆數:', len(star_Df))
print('取得資料欄位名稱:', star_Df.columns)
print('前五筆資料如下:')
print(star_Df.head(5))
```
觀察結果如下:

![](https://hackmd.io/_uploads/BJJ3-5_i2.png)


# 2. 畫出資料散佈圖並猜測 Decision Boundary

## 2.1 畫出資料散佈圖
接著我們可以將散佈圖畫出來進行觀察

```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 訓練集檔案存放路徑
trainingSet_Path = 'Data\Star3642_balanced.csv'

# 使用 panda.read_csv 方法讀取檔案路徑內的資料
star_Df = pd.read_csv(trainingSet_Path)

# 觀察資料
print('資料總筆數:', len(star_Df))
print('取得資料欄位名稱:', star_Df.columns)
print('前五筆資料如下:')
print(star_Df.head(5))

# 使用 matplotlib 套件繪製訓練集資料 
for i in range(len(star_Df)):
    
    # 色指數, bv-color-index
    bv = star_Df.loc[i,"B-V"]
    # 絕對星等, absolute magnitude
    abMag = star_Df.loc[i,"Amag"]
    # 是否是巨星，1 代表是巨星，反之 0 代表是矮星
    isGiant = star_Df.loc[i,"TargetClass"]

    # 是巨星的資料標計為紅色，反之為矮星，標記為藍色
    color=''

    if(isGiant == 1):
        color = 'ro'
    else:
        color = 'bo'

    # 將資料畫在畫布上
    plt.plot(bv, abMag, color, markersize = 2)

# 標記 X 軸和 Y軸名稱
plt.xlabel('B_V')        
plt.ylabel('AbMag')

# 圖示方框
plt.plot(1.318,16.678352,'bo',markersize=2,label='Drawf')
plt.plot(-0.015,15.324928,'ro',markersize=2,label='Giant')
plt.legend(borderpad=1)

# 將畫布 Show 出來
plt.show()

```

 畫出來的結果如下:
 
![](https://i.imgur.com/sex4JC0.png)

## 2.2 What is Decision Boundary?

什麼是 Decision Boundary 呢? 簡單來說，它是一個可以用來進行分類的超曲面(hyperSurface)。

這樣講實際上還是蠻抽象的，但如果以下圖來看，就可以看出橢圓形狀的邊界徹底將裡面的圖案分成兩類，例如橢圓邊界內部的就歸類為藍色的 :large_blue_circle: ，邊界外部歸類為紅色的:heavy_multiplication_x:。 像這樣的邊界，就可以被稱作 Decision Boundary。

![](https://hackmd.io/_uploads/HkeXvCdon.png)

## 2.3 Decision Boundary 猜測
觀察了散佈圖之後，我們發現可以簡單地拿下方 <font color=#green>綠色虛線</font> 去當作 Decision Boundary，將綠色虛線上方的點分類為巨星，下方的點分類為矮星，則應該可以將多數的訓練點成功分類。

![](https://i.imgur.com/keduQLi.png)

因此我們應該可以假設此綠色虛線(Decision Boundary)如下，並以此條直線去做 logistic regression。
:::info
<font color=red id="decisionboundary">$$\mathbf{\theta_0+\theta_1(bv)+\theta_2(abMag)=0},\ \text{for some}\ \theta_0,\ \theta_1\ \text{and} \ \theta_2\in\mathbb{R}.$$</font>
:::

求出 $\theta_0,\ \theta_1,$ 和 $\theta_2$ 的估計值後，就可以依此 decision Boundary 配合下面的函數進行分類囉。

$L(bv, abMag) =  \begin{cases}
    1,   & \quad \text{if } h(bv,abMag)\geq0\\
    0,  &\quad otherwise
  \end{cases},
  where \ h(bv,abMag)=\theta_0+\theta_1(bv)+\theta_2(abMag).$

# 3. Logistic Regression

## 3.1 Logistic function

Logistic Regression 主要會用到的 function 為

$$f(z) = \displaystyle\frac{1}{1+e^{-z}},\ z \in\mathbb{R}.$$

![](https://hackmd.io/_uploads/B1XZWt0in.png)

從此函數的圖形可以觀察到下列的事實
:::success
$f(0)=0.5,\ f(z)>0.5\ \text{when}\ z >0,\ f(z)\leq0.5\ \text{when}\ z \leq 0.$
:::

若此時我們很直覺地將函數 $f(z)$ 的值視為機率，機率大於 0.5 的分成一類(標註為 1)，機率小於0.5的分成一類(標註為 0 )並令$$z(bv,abMag)=\mathbf{\theta_0+\theta_1(bv)+\theta_2(abMag)},$$則會發現若 $z(bv,abMag)>0$ 時，會將其分類為 $1$，反之則分類為 $0$，如下圖，

![](https://hackmd.io/_uploads/SyK93t0i3.png)

我們可以發現若找出來的綠色斜線如上圖為例，則有多數的藍色的點因為在斜線上方，所以被歸類為巨星，跟我們想要的分類結果不太吻合，因此我們知道我們必須找到適當的參數 $\theta_0,\theta_1,\theta_2$，才可以較"精準"的分類巨星和矮星。

## 3.2 Hypothesis function and Cost function (TODO: Python 程式)

Logistic Regression 和 Hypothesis function $h_{\theta}$ 和 Loss function $L(\theta)$ 分別為
<font color=##0000FF>
$$h_{\theta}(x)=\frac{1}{1+e^{-\theta\cdot x}}\ ,\theta=(\theta_0,\theta_1,\theta_2)\ \text{and}\ x=(1,bv,abMag),$$
$$L(\theta)=-\frac{1}{N}\sum_{i=1}^{N}[  y_i\log(h_{\theta}(x_i))+(1-y_i)log(1-h_{\theta}(x_i)) ]，$$
</font>

其中 $\theta=(\theta_0,\theta_1,\theta_2),\ x=(1,bv,abMag),\ N$ 為資料總筆數，$x_i=(1,bv_{(i)},abMag_{(i)}),\ bv_{(i)} 和\ abMag_{(i)}$ 分別為第 $i$ 筆資料的色指數, 絕對星等。
$y_i=1$ 如果第 $i$ 筆資料為 Giant，反之則為 0 。

為了找到適當的參數 $\theta_0,\theta_1$ 和 $\theta_2$，我們必須求得 $$\displaystyle\arg\min_{\theta}J(\theta).$$

而因為 Loss function $L$ 是 convex function，所以一定存在極值 $\theta$ 使得 $J(\theta)$ 最小，此時我們可以藉由 Stochastic Gradient Descent 來幫助我們求得 $\theta=(\theta_0,\theta_1,\theta_2)$ 的近似值。

令 $l=[y\log(h_{\theta}(x))+(1-y)log(1-h_{\theta}(x))]$，則 
$$\begin{align*}
\displaystyle\frac{\partial l }{\partial \theta_0} &= y\frac{\frac{\partial h}{\partial \theta_0}}{h_\theta}+(1-y)\frac{\frac{\partial h}{\partial \theta_0}}{h_\theta-1}
\\&= \frac{\partial h}{\partial \theta_0}[\frac{y}{h_{\theta}}+\frac{1-y}{h_{\theta}-1}] \\
&=\frac{\partial h}{\partial \theta_0}[\frac{h_{\theta}-y}{h_{\theta}(h_{\theta}-1)}]，
\end{align*}
$$

我們可以發現
$$\begin{align*}
\displaystyle\frac{\partial h}{\partial \theta_0} &= \frac{-\cdot e^{-\theta\cdot x}}{(1+e^{-\theta\cdot x})^2} \\
&=-\cdot e^{-\theta\cdot x}(h_{\theta})^2，
\end{align*}$$

將此式代入上式，可以得到
$$\begin{align*}
\displaystyle\frac{\partial l }{\partial \theta_0} 
&=\frac{\partial h}{\partial \theta_0}[\frac{h_{\theta}-y}{h_{\theta}(h_{\theta}-1)}]\\
&=(h_{\theta}-y)[\frac{-h_{\theta}e^{-\theta\cdot x}}{h_{\theta}-1}] \\
&=(h_{\theta}-y)[\frac{-h_{\theta}e^{-\theta\cdot x}}{\frac{e^{-\theta\cdot x}}{1+e^{-\theta\cdot x}}}] \\
&=(h_\theta-y)[\frac{-h_{\theta}e^{-\theta\cdot x}}{h_{\theta}e^{-\theta\cdot x}}]\\
&=y-h_{\theta}，
\end{align*}
$$，
同理，我們也會有
$$\displaystyle\frac{\partial l }{\partial \theta_1} =(y-h_{\theta})bv $$
和
$$\displaystyle\frac{\partial l }{\partial \theta_2} =(y-h_{\theta})abMag.$$

選定起始點 $\theta_0$ 後，便可藉由 Stochastic Gradient Descent 的遞迴關係 
:::info
$$\displaystyle\theta_{i+1}=\theta_i-\eta\frac{\partial l}{\partial \theta_i},i=0,1,2.$$
:::
我們便可藉由調整 learning rate $\eta$ 求得係數  $\theta_0,\theta_1,\theta_2$ 的近似解，底下為我們求得係數 $\theta_0,\theta_1,\theta_2$ 的近似解的程式;

```python
import math 
import random as rd
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime


def stochastic_Gradient_Descent(filePath, initial_point, learningRate, iteratedNumber):

    # 讀取資料
    star_Df = pd.read_csv(filePath)
    # 資料總筆數
    dataSize = len(star_Df)
    
    # theta_0, theta_1, theta_2 初始值
    theta_0 = initial_point[0]
    theta_1 = initial_point[1]
    theta_2 = initial_point[2]

    partial_L_wrt_theta_0 = 0
    partial_L_wrt_theta_1 = 0
    partial_L_wrt_theta_2 = 0

    start_time = datetime.datetime.now()
    print("StartTime:", start_time)

    # 進行迭代
    for i in range(iteratedNumber):
                     
        # 從 n 筆資料隨機選取一筆資料
        randomIndex = rd.randint(0,dataSize-1)
        bv = star_Df.loc[randomIndex,"B-V"]
        abMag = star_Df.loc[randomIndex,"Amag"]
        isGiant = star_Df.loc[randomIndex,"TargetClass"]

        z = theta_0 + theta_1*bv + theta_2*abMag
        a = 1/(1+math.exp((-1)*z)) - isGiant

        #Loss Function 的偏微分， wrt 為 with respect to 縮寫，L代表 Loss function
        partial_L_wrt_theta_0 = a
        partial_L_wrt_theta_1 = a * bv
        partial_L_wrt_theta_2 = a * abMag

        theta_0 -= (learningRate) * partial_L_wrt_theta_0
        theta_1 -= (learningRate) * partial_L_wrt_theta_1
        theta_2 -= (learningRate) * partial_L_wrt_theta_2

    print('theta_0:',theta_0)
    print('theta_1:',theta_1)
    print('theta_2:',theta_2)

    current_time = datetime.datetime.now()
    print("EndTime:", current_time)
    print("Time Cost", current_time-start_time)
    
    cost = 0

    try:
        for i in range(dataSize):

            # 從 n 筆資料隨機選取一筆資料
            randomIndex = rd.randint(0,dataSize-1)
            bv = star_Df.loc[randomIndex,"B-V"]
            abMag = star_Df.loc[randomIndex,"Amag"]
            isGiant = star_Df.loc[randomIndex,"TargetClass"]
            z = theta_0+ theta_1 * bv + theta_2* abMag
            h =1/(1+math.exp(-z))

            cost -= isGiant *(math.log(h)) + (1-isGiant)*(math.log(1-h))
    
        print('loss:',cost/dataSize)
    except:
        print('Something Wrong while Calculating loss')
    

    return (theta_0, theta_1, theta_2)
```

## 3.3 調整參數求得 $\theta$ 近似值

藉由選定起始點 $\theta =(\theta_0,\theta_1,\theta_2) = (0,0,0),$ learning rate = 0.0001, 迭代 100000 次，
配合下方的 Code，可以求得下面的圖和訓練結果
```python

# 訓練集檔案存放路徑
trainingSet_Path = 'Data\Star3642_balanced.csv'

theta = stochastic_Gradient_Descent(trainingSet_Path, (0,0,0), 0.0001, 100000)


# 訓練集檔案存放路徑
trainingSet_Path = 'Data\Star3642_balanced.csv'

# 使用 panda.read_csv 方法讀取檔案路徑內的資料
star_Df = pd.read_csv(trainingSet_Path)

plt.figure(figsize=(7,4))
# 使用 matplotlib 套件繪製訓練集資料 
for i in range(len(star_Df)):
    
    # 色指數, bv-color-index
    bv = star_Df.loc[i,"B-V"]
    # 絕對星等, absolute magnitude
    abMag = star_Df.loc[i,"Amag"]
    # 是否是巨星, 1 代表是巨星，反之 0 代表是矮星
    isGiant = star_Df.loc[i,"TargetClass"]

    # 是巨星的資料標計為紅色，反之為矮星，標記為藍色
    color=''

    if(isGiant == 1):
        color = 'ro'
    else:
        color = 'bo'

    # 將資料畫在畫布上
    plt.plot(bv, abMag, color, markersize = 2)

t = np.arange(-0.5, 3., 0.001)
theta0 = theta[0]
theta1 = theta[1]
theta2 = theta[2]
a = -(theta1/theta2)
b= -(theta0/theta2)

print("The Decision Boundary is ABMag = ",a, "* B_V+",b,".")

plt.plot(t, a*t+b ,'go',markersize=0.5)
plt.xlabel('B_V')        
plt.ylabel('AbMag')

plt.show()   
```

迭代結果:
![](https://hackmd.io/_uploads/Hk5EWet2n.png)

![](https://hackmd.io/_uploads/SkqKZxF23.png)


根據圖表，可以觀察到綠色的線分割了大多數的紅點和藍點。

# 4. 評估選取的Decision Boundary 準確率

最後我們可以利用資料來源內的「Star39552_balanced.csv」裡面的 39552筆資料，去估算此次分類的準確率。


```python=
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

count = 0

#parameters of hypothesis function
theta_0 = -0.3396695976738397
theta_1 =  -1.2728283043292683
theta_2 = 0.09319430690186153

# 預測的函數
def predict(bv, abMag):

    predictClass = 0
    
    if( (theta_0 + theta_1*bv+ theta_2*abMag) >= 0 ):
        predictClass = 1

    return predictClass 


# 測試集檔案存放路徑
testingSet_Path = 'Data\Star39552_balanced.csv'

# 使用 panda.read_csv 方法讀取檔案路徑內的資料
star_Df = pd.read_csv(testingSet_Path)

dataSize = len(star_Df)

# 使用 matplotlib 套件繪製訓練集資料 
for i in range(dataSize):
    
    # 色指數, bv-color-index
    bv = star_Df.loc[i,"B-V"]
    # 絕對星等, absolute magnitude
    abMag = star_Df.loc[i,"Amag"]
    # 是否是巨星，1 代表是巨星，反之 0 代表是矮星
    isGiant = star_Df.loc[i,"TargetClass"]


    #判別分類是否正確
    if(predict(bv, abMag) == isGiant):
        count += 1

print("The accuracy rate is",(100*count)/dataSize,"%.")
```

使用上方的 Python code，可以得到<font color=#0000FF>**準確率約為 86.453%。**</font>，看起來還算是個不錯的數字。
![](https://hackmd.io/_uploads/HJGWmxK32.png)

最後這39552筆的資料畫出來的圖為以下結果:

![](https://i.imgur.com/v2gOlbH.png)

# 5. 使用 Scikit Learn 進行 Logistic Regression

除了上述的程式外，我們也可以用其他套件簡單地進行 Logistic Regression，底下以 Scikit Learn 為範例；

```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn import metrics


# 訓練集檔案存放路徑
trainingSet_Path = 'Data\Star3642_balanced.csv'
# 使用 panda.read_csv 方法讀取訓練集內的資料
star_Df_ForTraining = pd.read_csv(trainingSet_Path)

# 取得訓練集
X_train = star_Df_ForTraining[['B-V','Amag']].to_numpy()
y_train = star_Df_ForTraining[['TargetClass']].to_numpy().ravel()

# 測試集檔案存放路徑
testingSet_Path = 'Data\Star39552_balanced.csv'
# 使用 panda.read_csv 方法讀取測試集內的資料
star_Df_ForTesting = pd.read_csv(testingSet_Path)

# 取得測試集
X_test = star_Df_ForTesting[['B-V','Amag']].to_numpy()
y_test = star_Df_ForTesting[['TargetClass']].to_numpy().ravel()

# Logistic Regression fit 測試集
logistic_regression = LogisticRegression(random_state = 100)
logistic_regression.fit(X_train, y_train)

#使用測試集預測結果
y_pred = logistic_regression.predict(X_test)
# 取得 Confusion Matrix
cnf_matrix = metrics.confusion_matrix(y_test, y_pred)

# 印出 Confusion Matrix
print("The Confusion Matrix is:")
print(cnf_matrix)

# 取得 Accuracy
testSet_Size = len(star_Df_ForTesting)
print("Accuracy:", (cnf_matrix[0][0]+cnf_matrix[1][1])/testSet_Size)

```
底下為使用 Scikit Learn 套件的結果，可以發現 Accuracy 也差不多是同樣的數字。

![](https://hackmd.io/_uploads/HJjxQWY33.png)


# 6. Summary

仔細觀察一下，就能發現該手法能簡單的對 N 類問題進行分類囉，例如將資料分成"A類" 和"非A類"，再從 "非A類" 中分出 "B類" 和 "非B類"，接著再從"非B類" 中分出 "C類" 和 "非C類"，以此類推，就能分出 "N類"了ＸＤ

以上就是 Logistic Regression 最簡單基本的範例與用法 :))

# 參考資料來源：
1.Giant star : https://en.wikipedia.org/wiki/Giant_star
2.Dwarf star : https://en.wikipedia.org/wiki/Dwarf_star
3.Absolute magnitude : https://en.wikipedia.org/wiki/Absolute_magnitude
4.Luminosity: https://en.wikipedia.org/wiki/Luminosity
5.B_V color index: https://en.wikipedia.org/wiki/Color_index
6.數據來源:https://www.kaggle.com/vinesmsuic/star-categorization-giants-and-dwarfs
7.sklearn Logistic Regression:https://www.datacamp.com/tutorial/understanding-logistic-regression-python
