【機器學習】Logistic迴歸ex2data2

漆黑丶發表於2020-11-23

題目

在訓練的第二部分,我們將要通過加入正則項提升邏輯迴歸演算法。簡而言之,正則化是成本函式中的一個術語,它使演算法更傾向於“更簡單”的模型(在這種情況下,模型將更小的係數)。這個理論助於減少過擬合,提高模型的泛化能力。這樣,我們開始吧。

設想你是工廠的生產主管,你有一些晶片在兩次測試中的測試結果。對於這兩次測試,你想決定是否晶片要被接受或拋棄。為了幫助你做出艱難的決定,你擁有過去晶片的測試資料集,從其中你可以構建一個邏輯迴歸模型。

答案

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.optimize as opt

#畫出散點圖,觀察是否呈線性
def plot_data():
    positive = data2[data2['Accepted'].isin([1])]   # 將Accepted列中是1的資料提出來,單獨呈一個表格
    negative = data2[data2['Accepted'].isin([0])]   # 同上,提出Accepted是0的表格
    # 接下來畫圖
    fig, ax = plt.subplots(figsize=(8, 5))
    ax.scatter(positive['Test 1'], positive['Test 2'], s=50, c='b', marker='o', label='Accepted')
    ax.scatter(negative['Test 1'], negative['Test 2'], s=50, c='r', marker='x', label='Rejected')
    ax.legend(loc=2)    # 畫出圖例
    ax.set_xlabel('Test 1 Score')
    ax.set_ylabel('Test 2 Score')


#定義一個特徵對映函式:直到六次冪(power)
def feature_mapping(x1, x2, power):
    data = {}    #定義一個字典,從而在之後可以直接新增元素的名稱和內容
    for i in np.arange(power + 1):    #i=n時,就是n次冪的所有組合。一共最多power次冪
        for p in np.arange(i + 1):
            data["f{}{}".format(i - p, p)] = np.power(x1, i - p) * np.power(x2, p)  #format是格式化函式。
    return pd.DataFrame(data)  #將字典轉化為一種二維表,類似excel

# 定義代價函式:正則化的代價函式,解決過擬合現象
def cost(theta, X, y):  # 這是未正則化的代價函式(之前的那個)
    first = (-y) * np.log(sigmoid(X @ theta))  # 注意array中有三種乘法
    second = (1 - y) * np.log(1 - sigmoid(X @ theta))
    return np.mean(first - second)

def sigmoid(z):  # 之前那個代價函式要用到這個logist函式
    return 1 / (1 + np.exp(- z))

def costReg(theta, X, y, l=1):  # 這是正則化之後的代價函式
    _theta = theta[1:]  # 不懲罰第一項
    reg = (l / (2 * len(X))) * (_theta @ _theta)
    return cost(theta, X, y) + reg  # 即之前那個代價函式再加一部分

# 定義批量的梯度函式:同樣是正則化後的,是那個導數
def gradient(theta, X, y):  # 這是之前未正則化的那個
    return (X.T @ (sigmoid(X @ theta) - y)) / len(X)

def gradientReg(theta, X, y, l=1):  # 這是正則化後的
    reg = (1 / len(X)) * theta
    reg[0] = 0  # 同樣不懲罰第一個θ
    return gradient(theta, X, y) + reg
    gradientReg(theta, X, y, 1)  # 這個結果和網頁上的那個結果順序不一樣,但是本質相同

def predict(theta, X):
    probability = sigmoid(X @ theta)
    return [1 if x >= 0.5 else 0 for x in probability]

data2 = pd.read_csv('ex2data2.txt', names=['Test 1', 'Test 2', 'Accepted'])
#初始化資料
plot_data()
# 注意到其中的正負兩類資料並沒有線性的決策界限。
x1 = data2['Test 1'].values   #定義x1,x2的資料
x2 = data2['Test 2'].values
_data2 = feature_mapping(x1, x2, power=6)  #得到特徵對映後的二維表
# _data2.head()
x = _data2.values   #將二維錶轉化為array格式
y = data2['Accepted'].values  #初始化y
theta = np.zeros(x.shape[1])
# print(X.shape, y.shape, theta.shape)  #檢查維度,期待輸出((118, 28), (118,), (28,))

#通過高階優化來進行梯度下降(計算速度很快,且不需要人為設定α)

result2 = opt.fmin_tnc(func=costReg, x0=theta, fprime=gradientReg, args=(x, y, 2))  #另一種優化方法在網頁上

#判斷擬合的模型結果的精確性
final_theta = result2[0]
predictions = predict(final_theta, x)
correct = [1 if a==b else 0 for (a, b) in zip(predictions, y)]  #通過模型預測的結果與真實結果相比
accuracy = sum(correct) / len(correct)
# print(accuracy)  #輸出0.8多



#畫出決策邊界:不能像第一題那樣ax.plot(x,y)那樣畫了,因為那樣要列出y等於多少多少x,把y看成x的函式。這裡冪太高式子複雜,很難變形。
#xx,yy是指二維圖上的所有點(網格線)。
#思路是:1.初始化網格點 2.所有網格點變成高次冪的形式、再×theta。這個式子命名為z,z等於0時,y=0.5。contour函式是預設z=0
x = np.linspace(-1, 1.5, 250)  #x維度是250
xx, yy = np.meshgrid(x, x)  #xx維度是250,250  xx.ravel()的維度是62500,ravel即多維陣列轉化為一維陣列,後面featuremapping的引數是要一維的。
z = feature_mapping(xx.ravel(), yy.ravel(), 6).values  #z的維度是62500,28
z = z @ final_theta  #z的維度是62500
z = z.reshape(xx.shape)  #z的維度是250,250
plot_data()  #這個是一開始定義的那個函式,即執行後畫出散點圖.
plt.contour(xx, yy, z, 0)  #這裡z是一個關於xx、yy的高次冪的函式,預設z=0畫圖
plt.ylim(-.8, 1.2)
#plt.show()  #最後顯圖,注意要所有元素都定義過後才能顯圖

"""<!-- 我的測試:1.六次冪不正則化的過擬合影像、二十次冪的正則化和非正則化
我的測試:2.用本題的最後畫圖的contour(xx, yy, z, 0)方法來畫第一題的圖,結果和第一題完全一樣。
我認為這是一種普遍的方法,以後都用這種了。就是注意xx和yy是二維網格,z的維度要和他們相同。z是預設關於xx和yy的函式為0 -->
"""

相關文章