【機器學習】吳恩達機器學習視頻作業-邏輯迴歸二分類 I

二分類類型1

本文件的程序是基於吳恩達機器學習視頻邏輯迴歸的作業,使用的數據是ex2data1.txt。數據背景是:使用學生以往考試的成績,預測其能否被國外的學校錄取。
數據特點是可以使用特徵的一次項就可以將數據分爲兩份,具體可以從後面的數據看出,如同線性迴歸。
程序運行環境:window10 Jupyter

1 加載數據和數據查看

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 在jupyter整的魔法函數,圖形不需要plot即可顯示
%matplotlib inline
# 源數據是沒有標題的,一行一個樣本,每行的前兩列是兩次成績,最後一列是是否被錄取,每列數據由","隔開,即類似於csv格式數據
# 讀取數據到pandas中,並設置列名
pdData = pd.read_csv("ex2data1.txt", header=None, names=['Exam1', 'Exam2', 'Admitted'])
pdData.head()            # 查看數據的前5行信息
"""
Exam1	Exam2	Admitted
0	34.623660	78.024693	0
1	30.286711	43.894998	0
2	35.847409	72.902198	0
3	60.182599	86.308552	1
4	79.032736	75.344376	1
"""

查看數據信息,可以看出數據總共100條,每行數據即爲一個實體,每個實體有三列,即兩個特徵,一個標籤。

pdData.info()    # 查看數據信息
"""
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 3 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Exam1     100 non-null    float64
 1   Exam2     100 non-null    float64
 2   Admitted  100 non-null    int64  
dtypes: float64(2), int64(1)
memory usage: 2.5 KB
"""

將數據可視化

positive = pdData[pdData['Admitted'] == 1]   # 挑選出錄取的數據
negative = pdData[pdData['Admitted'] == 0]   # 挑選出未被錄取的數據
fig, ax = plt.subplots(figsize=(10, 5))      # 獲取繪圖對象
# 對錄取的數據根據兩次考試成績繪製散點圖
ax.scatter(positive['Exam1'], positive['Exam2'], s=30, c='b', marker='o', label='Admitted')
# 對未被錄取的數據根據兩次考試成績繪製散點圖
ax.scatter(negative['Exam1'], negative['Exam2'], s=30, c='r', marker='x', label='Not Admitted')
# 添加圖例
ax.legend()
# 設置x,y軸的名稱
ax.set_xlabel('Exam1 Score')
ax.set_ylabel('Exam2 Score')

可視化數據結果

2 構建邏輯迴歸(logistic regression)模型進行分類

目標:構建一個邏輯迴歸分類器,主要求出參數θ\theta(θ0\theta_0,θ1\theta_1,θ2\theta_2)

(備註:使用獲取的模型預測,當結果0.5\ge 0.5則認爲可被錄取,反之不行)

主要模塊

  • sigmoid函數:將數據映射到[0,1]範圍
  • model函數:計算參數θ\theta和特徵x的結果,也就是一個線性函數,返回預測結果只
  • cost函數:根據參數返回損失值
  • gradient函數:根據參數計算θ\theta分量的梯度
  • 優化函數:根據參數使用梯度下降算法更新參數θ\theta
  • accuracy函數:計算模型的精度
  • 繪製決策邊界

3 sigmoid公式

g(z)=11+ez g(z) = \frac{1}{1+ e^{-z}}

# 定義一個sigmoid函數
def sigmoid(z):
    # z可以是一個數,也可以是np中的矩陣
    return 1.0/(1+np.exp(-z))
# 繪製sigmoid圖形
nums = np.arange(-10, 10, 0.1)
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(nums, sigmoid(nums), 'r')

sigmoid圖形

4 model模型,即參數與特徵的關係:

特徵對應的線性函數如下:
θTx=(θ0θ1θ2θn)×(x0x1xn)=θ0x0+θ1x1++θnxn\theta^{T} x=\left(\theta_{0} \theta_{1} \theta_{2} \cdots \theta_{n}\right) \times\left(\begin{array}{c} x_{0} \\ x_{1} \\ \vdots \\ x_{n} \end{array}\right)=\theta_{0} x_{0}+\theta_{1} x_{1}+\cdots+\theta_{n} x_{n}
邏輯迴歸模型函數如下:
hθ(x)=g(θTx)h_\theta(x) = g(\theta^T x)
其中g(z)表達式如下:
g(z)=11+ez g(z) = \frac{1}{1+e^{-z}}
模型使用,我們預測:

hθ(x)0.5h_\theta(x) \ge 0.5時,預測 y=1y=1

hθ(x)<0.5h_\theta(x) < 0.5時,預測 y=0y=0

def model(X, theta):
    # 單個數據樣本維一個行向量 n個特徵, theta爲長度爲n的行向量
    # 當X爲m行n列的數據,返回結果爲m行1列的數據
    theta = theta.reshape(1, -1)  # 轉成符合矩陣計算
    return sigmoid(X.dot(theta.T))

5 模型數據預處理

pdData.insert(0, 'Ones', 1)               # 爲原始數據增加值爲1的一列
orig_data = np.array(pdData)              # 將dataframe數據轉換成numpy數組類型 
cols = orig_data.shape[1]                 # 獲取樣本數據的數目
X = np.array(orig_data[:, 0:cols-1])      # 得到樣本的特徵集
y = np.array(orig_data[:, cols-1:cols])   # 得到樣本的標記集
theta = np.zeros((1, X.shape[1]))         # 初始化參數爲1行n列的數據
X[:5]          # 查看特徵集中前5條數據
"""
array([[ 1.        , 34.62365962, 78.02469282],
       [ 1.        , 30.28671077, 43.89499752],
       [ 1.        , 35.84740877, 72.90219803],
       [ 1.        , 60.18259939, 86.3085521 ],
       [ 1.        , 79.03273605, 75.34437644]])

"""
theta         # 查看初始化的參數
"""
array([[0., 0., 0.]])
"""
y[:5]         # 查看標記集中前5條數據
"""
array([[0.],
       [0.],
       [0.],
       [1.],
       [1.]])
"""
X.shape, y.shape, theta.shape     # 查看幾個數據的形狀,矩陣乘法中會用到
"""
((100, 3), (100, 1), (1, 3))
"""

6 損失函數cost

損失函數公式:
Loss(hθ(x),y)=ylog(hθ(x))(1y)log(1hθ(x))Loss(h_\theta(x),y) = -y\log(h_\theta(x)) - (1-y)\log(1-h_\theta(x))
平均損失:
J(θ)=1mi=1mLoss(hθ(xi),yi)J(\theta) = \frac{1}{m} \sum_{i=1}^{m}Loss(h_\theta(x_i), y_i)

參數說明:m爲樣本個數,n爲特徵索引(從0開始)

(補充:這種損失函數也稱爲交叉損失函數)

# 損失函數
def cost(theta, X, y):
    # X爲特徵數據集 單個特徵數據爲行向量形狀要爲mxn
    # y爲標籤數據集 爲一個列向量形狀要爲mx1
    # theta爲一個行向量 形狀要爲1xn
    # 返回一個標量
    left = -y*np.log(model(X, theta))
    right = (1-y)*np.log(1 - model(X, theta))
    return np.sum(left - right)/len(X)

cost(theta, X, y)    # 查看損失值
"""
0.6931471805599453
"""

7 計算梯度

梯度公式與線性迴歸相似:
J(θ)θj=1mi=1m(yihθ(xi))xij \frac{\partial J(\theta)}{\partial \theta_j} = -\frac{1}{m}\sum_{i=1}^m(y_i - h_\theta(x_i))x_{i}^{j}

# 參數的梯度
def gradient(theta, X, y):
    # X爲特徵數據集 mxn
    # y爲標籤數據集 mx1
    # theta爲一個行向量 1xn
    grad = ((model(X, theta) - y).T@X).flatten()/ len(X)
    # 返回行向量
    return grad

8 使用優化函數計算參數θ\theta

已經瞭解過了梯度算法的相關概念,以及如何實現,爲了提高編程效率,減少重複造輪子的時間(況且輪子還沒有別人造的好),這裏使用scipy中的optimize模塊,通過最小化損失值函數計算參數

import scipy.optimize as opt
res = opt.minimize(fun=cost, x0=theta, args=(X, y), jac=gradient, method='Newton-CG')
res
"""
fun: 0.20349770249067073
     jac: array([-1.29015205e-05, -7.87933956e-04, -9.06046726e-04])
 message: 'Optimization terminated successfully.'
    nfev: 72
    nhev: 0
     nit: 28
    njev: 243
  status: 0
 success: True
       x: array([-25.15887187,   0.20621202,   0.20145168])
"""
theta_res = res.x         # 得到參數

9 根據訓練的模型參數對數據進行預測

# 對數據進行預測
def predict(X, theta):
    # X 訓練數據
    # theta 參數
    return [1 if x>=0.5 else 0 for x in model(X, theta).flatten()]
predictions = predict(X, theta_res)
correct = [1 if ((a == 1 and b ==1) or (a ==0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = 100*(sum(map(int, correct))/len(correct))         # 計算正確率
# 查看正確率
print('accuracy = {0}%'.format(accuracy))
"""
accuracy = 89.0%
"""

10 可視化決策邊界

這裏已經得到了θ\theta的參數,將$\theta^Tx $取0即可得到對應的決策邊界函數。那麼就有:
θ0+θ1x1+θxx2=0θ0θ3+θ1θ3x1+x2=0x2=θ0θ3θ1θ3x1 \begin{aligned} \theta_0 + \theta_1 x_1 + \theta_x x_2 = 0 \\ \frac{\theta_0}{\theta_3} + \frac{\theta_1}{\theta_3}x_1 + x_2 = 0\\ x_2 = - \frac{\theta_0}{\theta_3} - \frac{\theta_1}{\theta_3}x_1 \end{aligned}

cols = orig_data.shape[1]                 # 獲取樣本數據的數目
X = np.array(orig_data[:, 0:cols-1])      # 得到樣本的特徵集
y = np.array(orig_data[:, cols-1:cols])   # 得到樣本的標記集
exam1 = np.arange(X[:,1].min(), X[:,1].max(), 0.01)  
theta_res = theta_res/theta_res[2]
theta_res = - theta_res.flatten()         # 將數據壓平,變成行向量
exam2 = theta_res[0] + theta_res[1]*exam1

positive = pdData[pdData['Admitted'] == 1]   # 挑選出錄取的數據
negative = pdData[pdData['Admitted'] == 0]   # 挑選出未被錄取的數據
fig, ax = plt.subplots(figsize=(10, 5))      # 獲取繪圖對象
# 對錄取的數據根據兩次考試成績繪製散點圖
ax.scatter(positive['Exam1'], positive['Exam2'], s=30, c='b', marker='o', label='Admitted')
# 對未被錄取的數據根據兩次考試成績繪製散點圖
ax.scatter(negative['Exam1'], negative['Exam2'], s=30, c='r', marker='x', label='Not Admitted')
# 添加圖例
ax.legend()
# 設置x,y軸的名稱
ax.set_xlabel('Exam1 Score')
ax.set_ylabel('Exam2 Score')
plt.title("fitted curve vs sample")
plt.plot(exam1, exam2)

決策邊界圖形

後記

本部分先介紹簡單的二分類方法,數據使用一條直線就可以劃分,但是如果一條直線不能劃分怎麼辦?下一部分介紹邏輯迴歸如何對複雜的非線性數據進行分類,程序源碼可在訂閱號"AIAS編程有道"中回覆“機器學習”即可獲取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章