# 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>
 
<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))
```
觀察結果如下:

# 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()
```
畫出來的結果如下:

## 2.2 What is Decision Boundary?
什麼是 Decision Boundary 呢? 簡單來說,它是一個可以用來進行分類的超曲面(hyperSurface)。
這樣講實際上還是蠻抽象的,但如果以下圖來看,就可以看出橢圓形狀的邊界徹底將裡面的圖案分成兩類,例如橢圓邊界內部的就歸類為藍色的 :large_blue_circle: ,邊界外部歸類為紅色的:heavy_multiplication_x:。 像這樣的邊界,就可以被稱作 Decision Boundary。

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

因此我們應該可以假設此綠色虛線(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}.$$

從此函數的圖形可以觀察到下列的事實
:::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$,如下圖,

我們可以發現若找出來的綠色斜線如上圖為例,則有多數的藍色的點因為在斜線上方,所以被歸類為巨星,跟我們想要的分類結果不太吻合,因此我們知道我們必須找到適當的參數 $\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()
```
迭代結果:


根據圖表,可以觀察到綠色的線分割了大多數的紅點和藍點。
# 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>,看起來還算是個不錯的數字。

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

# 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 也差不多是同樣的數字。

# 6. Summary
仔細觀察一下,就能發現該手法能簡單的對 N 類問題進行分類囉,例如將資料分成"A類" 和"非A類",再從 "非A類" 中分出 "B類" 和 "非B類",接著再從"非B類" 中分出 "C類" 和 "非C類",以此類推,就能分出 "N類"了XD
以上就是 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