一、邏輯迴歸
二、判定邊界
當將訓練集的樣本以其各個特徵爲座標軸在圖中進行繪製時,通常可以找到某一個 判定邊界 去將樣本點進行分類。例如:
線性判定邊界:
非線性判定邊界:
三、二分類和sigmoid函數
sigmoid函數圖像如下:
四、損失函數
1. 定義
2. 極大似然估計
上面是一種求損失函數的方式,我們也可以換一種方式來求損失函數,即極大似然估計。用極大似然估計來作爲損失函數
3. 正則化
五、最小化損失函數
同樣,上式中的a爲學習率(下山步長)。將上式的偏導展開,可得:
非正則化的損失函數的偏導:
含正則化項的損失函數的偏導:
其中 λ 爲正則化的強度。
同線性迴歸般,可以通過學習率a對特徵係數向量中的元素不斷進行迭代,直到元素值收斂到某一值即可,這時可以得到損失函數較小時的特徵向量係數Θ。
六、從二分類過渡到多分類
在上面,我們主要使用邏輯迴歸解決二分類的問題,那對於多分類的問題,也可以用邏輯迴歸來解決?
1. one vs rest
由於概率函數 hΘ(X) 所表示的是樣本標記爲某一類型的概率,但可以將一對一(二分類)擴展爲一對多(one vs rest):
-
將類型class1看作正樣本,其他類型全部看作負樣本,然後我們就可以得到樣本標記類型爲該類型的概率p1;
-
然後再將另外類型class2看作正樣本,其他類型全部看作負樣本,同理得到p2;
-
以此循環,我們可以得到該待預測樣本的標記類型分別爲類型class i時的概率pi,最後我們取pi中最大的那個概率對應的樣本標記類型作爲我們的待預測樣本類型。
2. softmax函數
使用softmax函數構造模型解決多分類問題。
softmax迴歸分類器需要學習的函數爲 : (這裏下面的公式有問題,括號中的每一項應該都是以e爲底的)
其中 k 個 類別的個數 , 和 爲 第 i 個 類別對應的 權重向量 和 偏移標量。
其中 可看作樣本 X 的標籤 爲 第 j 個 類別的概率,且有 。
與 logistic迴歸 不同的是,softmax迴歸分類模型會有多個的輸出,且輸出個數 與 類別個數 相等,輸出爲樣本 X 爲各個類別的概率 ,最後對樣本進行預測的類型爲 概率最高 的那個類別。
我們需要通過學習得到 和 ,因此建立目標損失函數爲:
上式的代價函數也稱作:對數似然代價函數。
在二分類的情況下,對數似然代價函數 可以轉化爲 交叉熵代價函數。
其中 m 爲訓練集樣本的個數,k 爲 類別的個數, 爲示性函數,當 爲真時,函數值爲 1 ,否則爲 0 ,即 樣本類別正確時,函數值才爲 1 。
繼續展開:
通過 梯度下降法 最小化損失函數 和 鏈式偏導,使用 對 求偏導:
化簡可得:
再次化簡可有:
因此由 梯度下降法 進行迭代:
同理 通過梯度下降法最小化損失函數也可以得到 的最優值。
同邏輯迴歸一樣,可以給損失函數加上正則化項。
3. 選擇的方案
當標籤類別之間是互斥時,適合選擇softmax迴歸分類器 ;當標籤類別之間不完全互斥時,適合選擇建立多個獨立的logistic迴歸分類器。
4. tensorflow代碼示例:
- 使用softmax迴歸對sklearn中的digit手寫數據進行分類
import tensorflow as tf
-
from sklearn.datasets import load_digits
-
import numpy as np
-
digits = load_digits()
-
X_data = digits.data.astype(np.float32)
-
Y_data = digits.target.reshape(-1,1).astype(np.float32)
-
print X_data.shape
-
print Y_data.shape
-
(1797, 64)
-
(1797, 1)
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_data = scaler.fit_transform(X_data)
from sklearn.preprocessing import OneHotEncoder
Y = OneHotEncoder().fit_transform(Y_data).todense() #one-hot編碼
Y
-
matrix([[ 1., 0., 0., ..., 0., 0., 0.],
-
[ 0., 1., 0., ..., 0., 0., 0.],
-
[ 0., 0., 1., ..., 0., 0., 0.],
-
...,
-
[ 0., 0., 0., ..., 0., 1., 0.],
-
[ 0., 0., 0., ..., 0., 0., 1.],
-
[ 0., 0., 0., ..., 0., 1., 0.]])
print Y.shape
-
(1797, 10)
-
1797
batch_size = 10 # 使用MBGD算法,設定batch_size爲10
-
def generatebatch(X,Y,n_examples, batch_size):
-
for batch_i in range(n_examples // batch_size):
-
start = batch_i*batch_size
-
end = start + batch_size
-
batch_xs = X[start:end, :]
-
batch_ys = Y[start:end]
-
yield batch_xs, batch_ys # 生成每一個batch
-
tf.reset_default_graph()
-
tf_X = tf.placeholder(tf.float32,[None,64])
-
tf_Y = tf.placeholder(tf.float32,[None,10])
-
tf_W_L1 = tf.Variable(tf.zeros([64,10]))
-
tf_b_L1 = tf.Variable(tf.zeros([1,10]))
pred = tf.nn.softmax(tf.matmul(tf_X,tf_W_L1)+tf_b_L1)
-
loss = -tf.reduce_mean(tf_Y*tf.log(tf.clip_by_value(pred,1e-11,1.0)))
-
# 也可以直接使用tensorflow的版本:
-
# loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=tf_Y,logits=pred))
train_step = tf.train.GradientDescentOptimizer(0.2).minimize(loss)
-
y_pred = tf.arg_max(pred,1)
-
bool_pred = tf.equal(tf.arg_max(tf_Y,1),y_pred)
accuracy = tf.reduce_mean(tf.cast(bool_pred,tf.float32)) # 準確率
-
with tf.Session() as sess:
-
sess.run(tf.global_variables_initializer())
-
for epoch in range(2001): # 迭代2001個週期
-
for batch_xs,batch_ys in generatebatch(X_data,Y,Y.shape[0],batch_size): # 每個週期進行MBGD算法
-
sess.run(train_step,feed_dict={tf_X:batch_xs,tf_Y:batch_ys})
-
if(epoch%1000==0):
-
res = sess.run(accuracy,feed_dict={tf_X:X_data,tf_Y:Y})
-
print (epoch,res)
-
res_ypred = y_pred.eval(feed_dict={tf_X:X_data,tf_Y:Y}).flatten()
-
print res_ypred
-
(0, 0.86866999)
-
(1000, 0.99332219)
-
(2000, 0.99833053)
-
[0 1 2 ..., 8 9 8]
from sklearn.metrics import accuracy_score
print accuracy_score(Y_data,res_ypred.reshape(-1,1))
0.998330550918
八、Logistic Loss的另一種表達
在上面的邏輯迴歸的二分類問題中,我們令正樣本的標籤 y = 1 ,負樣本的標籤 y = 0。對於單個樣本來說,其損失函數Cost(hΘ(X),y)可以表示爲:(hΘ(X)的值表示正樣本的概率)
若我們 令正樣本的標籤 y = 1 ,負樣本的標籤 y = -1,則有:
其中(待續)
七、代碼示例
- 使用ovr多分類的邏輯迴歸判斷鳶尾屬植物的類型
-
from sklearn import datasets
-
iris = datasets.load_iris() # 加載數據
-
X = iris.data
-
y = iris.target
-
print X.shape
-
print y.shape
-
(150L, 4L)
-
(150L,)
-
from sklearn.model_selection import train_test_split
-
#分隔訓練集和測試集
-
X_train, X_test, y_train, y_test = train_test_split(X, y ,test_size = 1/3.,random_state = 8)
-
from sklearn.preprocessing import PolynomialFeatures
-
featurizer = PolynomialFeatures(degree=2) # 特徵多項式化
-
X_train = featurizer.fit_transform(X_train)
-
X_test = featurizer.transform(X_test)
-
from sklearn.preprocessing import StandardScaler # 對數據歸一化
-
scaler = StandardScaler()
-
X_std_train = scaler.fit_transform(X_train)
-
X_std_test = scaler.transform(X_test)
-
from sklearn.linear_model import LogisticRegression
-
from sklearn.linear_model import SGDClassifier
-
# penalty:正則化 l2/l1
-
# C :正則化強度
-
# multi_class:多分類時使用 ovr: one vs rest
-
lor = LogisticRegression(penalty='l1',C=100,multi_class='ovr')
-
lor.fit(X_std_train,y_train)
-
print lor.score(X_std_test,y_test)
-
sgdv = SGDClassifier(penalty='l1')
-
sgdv.fit(X_std_train,y_train)
-
print sgdv.score(X_std_test,y_test)
-
0.94
-
0.92
-
LogisticRegression對參數的計算採用精確解析的方式,計算時間長但模型的性能高;SGDClassifier採用隨機梯度下降/上升算法估計模型的參數,計算時間短但模型的性能較低。
- 使用Tensorflow實現線性邏輯迴歸:
-
from sklearn.datasets import make_classification
-
import matplotlib.pyplot as plt
-
import numpy as np
-
X, y = make_classification(n_features=2, n_redundant=0, n_informative=2,
-
n_clusters_per_class=1,random_state=78,n_samples=200)
-
X = X.astype(np.float32)
-
y = y.astype(np.float32)
-
plt.scatter(X[:,0],X[:,1],c=y)
-
plt.show()
-
from sklearn import preprocessing
-
scaler = preprocessing.StandardScaler().fit(X)
-
X = scaler.transform(X)
-
print X.shape
(200, 2)
-
b = tf.Variable(tf.zeros([1,1]))
-
W = tf.Variable(tf.zeros([2,1]))
-
X_DATA = tf.placeholder(tf.float32,[None,2])
-
Y = tf.placeholder(tf.float32,[None,1])
-
H = 1 / (1 + tf.exp(-(tf.matmul(X_DATA, W) + b)))
-
loss = tf.reduce_mean(- Y* tf.log(tf.clip_by_value(H,1e-11,1.0)) - (1 - Y) * tf.log(1 - tf.clip_by_value(H,1e-11,1.0)))
-
# 也可以使用tensorflow的版本:
-
#loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=tf_Y,logits=pred))
optimizer = tf.train.GradientDescentOptimizer(0.1)
train = optimizer.minimize(loss)
-
init_vals = tf.global_variables_initializer()
-
with tf.Session() as sess:
-
sess.run(init_vals)
-
for step in range(15501):
-
sess.run(train,feed_dict={X_DATA:X,Y:y.reshape(-1,1)})
-
if(step%5000==0):
-
print(step,sess.run(W).flatten(),sess.run(b).flatten())
-
w1 = sess.run(W).flatten()
-
b1 = sess.run(b).flatten()
-
(0, array([ 0.04289575, 0.04343094], dtype=float32), array([-0.0005], dtype=float32))
-
(5000, array([ 3.44468737, 3.617342 ], dtype=float32), array([-1.10549724], dtype=float32))
-
(10000, array([ 3.46032 , 4.07498837], dtype=float32), array([-1.60735476], dtype=float32))
-
(15000, array([ 3.45384622, 4.39454508], dtype=float32), array([-1.95797122], dtype=float32))
print (w1,b1)
(array([ 3.45412397, 4.42197132], dtype=float32), array([-1.9879719], dtype=float32))
-
x1_min, x1_max = X[:,0].min(), X[:,0].max(),
-
x2_min, x2_max = X[:,1].min(), X[:,1].max(),
-
xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max))
-
f = w1[0]*xx1+w1[1]*xx2+b1[0]
-
plt.contour(xx1, xx2, f, [0], colors = 'r') # 繪製分隔超平面
-
plt.scatter(X[:,0],X[:,1],c=y)
-
plt.show()