線性模型用於分類,分類的原理還要從公式說起。
線性模型的公司的結果y
是一個連續的輸出結果,如果將y
的值與0
作一個界限上的區分,那y
的值將被分成兩個區域。公式的表達如下:
也就是說,如果該公式(函數)預測的結果值小於0,就歸類爲-1,如果結果值大於0,就歸類爲1.
需要重點理解的地方在於:
- 對於用於迴歸的線性模型來說,輸出
y
是特徵的線性函數,是直線、平面或超平面(對於更高維的數據集)。 - 對於用於分類的線性模型,決策邊界是輸入的線性函數。
不同線性模型算法之間的區別在於:
- 係數和截距的特定組合對訓練集數據擬合好壞的度量方法;
- 是否使用正則化,以及使用哪種正則化方法。
最覺的兩種線性分類算法是Logistic迴歸和線性支持向量機。前者在linear_model.LogisticRegression
中實現,後者在svm.LinearSVC
(SVC代表支持再是分類器)中實現 。
接下來對這兩個模型做一個初步認識吧!
# 在學習之前,先導入這些常用的模塊
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import mglearn
Logistic 與 LinearSVC 模型
下面,將這兩個模型用在二分類數據forge
數據集上,並顯示模型得出的決策邊界。
先展示下二分類的展示圖:
# 導入二分類數據
X, y = mglearn.datasets.make_forge()
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.show()
C:\Users\Administrator\Anaconda3\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function make_blobs is deprecated; Please import make_blobs directly from scikit-learn
warnings.warn(msg, category=DeprecationWarning)
# 導入Logistic
from sklearn.linear_model import LogisticRegression
# 導入LinearSVC
from sklearn.svm import LinearSVC
# 導入二分類數據
X, y = mglearn.datasets.make_forge()
# 建立一個幕布,兩個繪圖區
fig, axes = plt.subplots(1, 2, figsize=(10, 3))
# 分別在兩個繪圖區上繪製兩個模型的決策邊界
for model, ax in zip([LinearSVC(), LogisticRegression()], axes):
# 在模型上訓練數據
clf = model.fit(X, y)
# 繪製決策邊界
mglearn.plots.plot_2d_separator(clf, X, fill=False, eps=.5, ax=ax, alpha=.7)
# 繪製二分類的訓練數據
mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
# 設置標題爲模型名稱
ax.set_title('{}'.format(clf.__class__.__name__))
# 設置x座標軸標籤名稱
ax.set_xlabel('Feature 0')
# 設置y座標軸標籤名稱
ax.set_ylabel('Feature 1')
# 在第一個繪圖區上顯示圖例
axes[0].legend()
plt.show()
說明:
- forge數據集有兩個特徵,分別對應X軸與Y軸。
- LinearSVC與LogisticRegression得到的決策邊界,都是直線,將數據分爲了上下兩個區域。
- 兩個模型默認使用了L2正則化。
繼續探討:
- 對於LinearSVC與LogisticRegression而言,決定正則化強度的權衡參數叫做
C
。C
值越大,對就的正則化越弱。 - 也就是說,越大的
C
將擁有越上的約束力,即係數的大小更自由,模型對於數據的貼合度將變得更復雜。 - 如果
C
越小,則對係數的約束越大,甚至趨向於0,使模型更能貼合大多數數據,模型也更簡單。
下面直接展示一下LinearSVC模型的C
分別取0.01
、1
、100
時模型的決策邊界效果:
mglearn.plots.plot_linear_svc_regularization()
對上圖的總結:
- 最左側的圖,C值很小,所以對應強正則化。要求模型更貼合於大多數數據,因此對於圖中兩個錯誤的點進行了忽略。
- 中間的圖,C值稍大,由於對模型的約束變小,模型對於數據的反應則變得更加第三一些,決策線向着兩個錯誤的點進行偏移,希望更好的貼合訓練數據。
- 右側圖C值很大,因爲對模型的約束很小,導致模型的決策邊界要求對所有數據都貼合,造成了過擬合現象。
再來看看使用乳腺癌數據集對LogisticRegression模型做出分析:
# 導入乳腺癌數據
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
# 將數據分爲訓練集測試集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=42)
# 使用模型訓練數據
logreg = LogisticRegression().fit(X_train, y_train)
# 查看模型的評估值
print('訓練集評估分數:', logreg.score(X_train, y_train))
print('測試集評估分數:', logreg.score(X_test, y_test))
訓練集評估分數: 0.9553990610328639
測試集評估分數: 0.958041958041958
這裏模型置信的C
值是1.如果訓練集的評分與測試集的評分差不多,那可能存在欠擬合的現象。
現在給模型增大C
值,再看看評估結果:
logreg100 = LogisticRegression(C=100).fit(X_train, y_train)
print('訓練集評估分數:', logreg100.score(X_train, y_train))
print('測試集評估分數:', logreg100.score(X_test, y_test))
訓練集評估分數: 0.971830985915493
測試集評估分數: 0.965034965034965
通過增大模型的C
值,發現模型的精度變高了。
現在再減小C
值看看模型的評估分數:
logreg001 = LogisticRegression(C=0.01).fit(X_train, y_train)
print('訓練集評估分數:', logreg001.score(X_train, y_train))
print('測試集評估分數:', logreg001.score(X_test, y_test))
訓練集評估分數: 0.9342723004694836
測試集評估分數: 0.9300699300699301
可以看出,模型的精度變小了,並且存在欠擬合的可能。
按照老辦法,我們當不同C
值情況下,模型得出的係數圖例化,看看結果:
plt.plot(logreg.coef_.T, 'o', label='C=1')
plt.plot(logreg100.coef_.T, '^', label='C=100')
plt.plot(logreg001.coef_.T, 'v', label='C=0.01')
plt.xticks(range(cancer.data.shape[1]), cancer.feature_names, rotation=90)
plt.hlines(0, 0, cancer.data.shape[1])
plt.ylim(-5, 5)
plt.xlabel('Codefficient index')
plt.ylabel('Coefficient magnitude')
plt.legend()
plt.show()
從這個圖例可以看出,C
值越小,模型的係數就越趨向於0.
另外,該模型默認使用L2正則,也可以將其改成L1正則,以減少模型使用的特徵:
# 評估
for C, marker in zip([0.001, 1, 100], ['o', '^', 'v']):
lr_l1 = LogisticRegression(C=C, penalty='l1').fit(X_train, y_train)
print('訓練集,C={0},評估分數={1}'.format(C, lr_l1.score(X_train, y_train)))
print('測試集,C={0},評估分數={1}'.format(C, lr_l1.score(X_test, y_test)))
# 繪圖
plt.plot(lr_l1.coef_.T, marker, label='C={}'.format(C))
plt.xticks(range(cancer.data.shape[1]), cancer.feature_names, rotation=90)
plt.hlines(0, 0, cancer.data.shape[1])
plt.ylim(-5, 5)
plt.xlabel('Codefficient index')
plt.ylabel('Coefficient magnitude')
plt.legend(loc=3)
plt.show()
訓練集,C=0.001,評估分數=0.9131455399061033
測試集,C=0.001,評估分數=0.9230769230769231
訓練集,C=1,評估分數=0.960093896713615
測試集,C=1,評估分數=0.958041958041958
訓練集,C=100,評估分數=0.9859154929577465
測試集,C=100,評估分數=0.9790209790209791
Ok,上圖對於不同C
值所對應的L
正則下的係數分佈有了直觀的瞭解,也就明白了對應的約束力。
這裏主要需要明白設置L1或L2需要通過參數penalty
來設置。
用於多分類的線性模型
多分類其實也是一處二分類的模式,它是一對其餘的方法。
這裏展示一個三類的gf數據:
from sklearn.datasets import make_blobs
X, y = make_blobs(random_state=42)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.legend(['Class 0', 'Class 1', 'Class 2'])
plt.show()
然後使用該數據對LinearSVC
分類器進行訓練:
linear_svm = LinearSVC().fit(X, y)
print('模型斜率集:', linear_svm.coef_.shape)
print('模型截距集:', linear_svm.intercept_.shape)
模型斜率集: (3, 2)
模型截距集: (3,)
通過形狀可以明白:斜率集有3行,每行代表類別之一的一個係數向量;有2列,每列包含某個特徵對應的係數值。而截距是個一維數據,包含每個類別的截距值。
現在將分類器給出的直線進行可視化:
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
line = np.linspace(-15, 15)
for coef, intercept, color in zip(linear_svm.coef_, linear_svm.intercept_, ['b', 'r', 'g']):
plt.plot(line, -(line * coef[0] + intercept) / coef[1], c=color)
plt.ylim(-10, 15)
plt.xlim(-10, 8)
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.legend(['Class 0', 'Class 1', 'Class 2'], loc=(1.01, 0.3))
plt.show()
在這裏,線條的顏色與各點的顏色是一致的。從圖中可以很直觀的看到這三個點是如何被分成三類的。
但是,這三條線交叉的地方,有一個空白的三角區,那這個區域屬於哪個類別呢?
答案是分類方程結果最大的那個類別,即最接近的那條結對應的類別!
下面將展示整個二維空間是如何被分類的:
mglearn.plots.plot_2d_classification(linear_svm, X, alpha=.7)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
line = np.linspace(-15, 15)
for coef, intercept, color in zip(linear_svm.coef_, linear_svm.intercept_, ['b', 'r', 'g']):
plt.plot(line, -(line * coef[0] + intercept) / coef[1], c=color)
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.legend(['Class 0', 'Class 1', 'Class 2'], loc=(1.01, 0.3))
plt.show()
通過上圖很明白的就看出中間的三角區是如何被分類的了!