二分類類型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)模型進行分類
目標:構建一個邏輯迴歸分類器,主要求出參數(,,)
(備註:使用獲取的模型預測,當結果則認爲可被錄取,反之不行)
主要模塊
- sigmoid函數:將數據映射到[0,1]範圍
- model函數:計算參數和特徵x的結果,也就是一個線性函數,返回預測結果只
- cost函數:根據參數返回損失值
- gradient函數:根據參數計算分量的梯度
- 優化函數:根據參數使用梯度下降算法更新參數
- accuracy函數:計算模型的精度
- 繪製決策邊界
3 sigmoid公式
# 定義一個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')
4 model模型,即參數與特徵的關係:
特徵對應的線性函數如下:
邏輯迴歸模型函數如下:
其中g(z)表達式如下:
模型使用,我們預測:
當時,預測
當時,預測
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
損失函數公式:
平均損失:
參數說明: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 計算梯度
梯度公式與線性迴歸相似:
# 參數的梯度
def gradient(theta, X, y):
# X爲特徵數據集 mxn
# y爲標籤數據集 mx1
# theta爲一個行向量 1xn
grad = ((model(X, theta) - y).T@X).flatten()/ len(X)
# 返回行向量
return grad
8 使用優化函數計算參數
已經瞭解過了梯度算法的相關概念,以及如何實現,爲了提高編程效率,減少重複造輪子的時間(況且輪子還沒有別人造的好),這裏使用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^Tx $取0即可得到對應的決策邊界函數。那麼就有:
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編程有道"中回覆“機器學習”即可獲取。