一、逻辑回归
在 机器学习之线性回归 中,我们可使用梯度下降的方法得到一个映射函数hΘ(X)来去贴近样本点,这个函数是对连续值的一个预测。
而逻辑回归是解决分类问题的一个算法,我们可以通过这个算法得到一个映射函数f:X-> y,其中X为特征向量,X 为 { x0 x1 x2 x3 x4 … xn },y为预测的结果。在逻辑回归这里,标签y为一个离散的值。
二、判定边界
当将训练集的样本以其各个特征为坐标轴在图中进行绘制时,通常可以找到某一个 判定边界 去将样本点进行分类。例如:
线性判定边界:

非线性判定边界:

在图中,样本的标记类型有两种类型,一种为正样本,另一种为负样本,样本的特征x0和x1为坐标轴。根据样本的特征值,可将样本绘制在图上。
在图中,可找到某个 判定边界 来对不同标签的样本进行划分。根据这个判定边界,我们可以知道哪些样本是正样本,哪些样本为负样本。
因此我们可以通过学习得到一个方程 EΘ(X) = 0 来表示 判定边界,即 判定边界 为EΘ(X) = 0 的点集。(可以看作是等高线/面等)

其中 Θ = { Θ0 Θ1 Θ2 Θ3 … Θn } ,为保留EΘ(X)中的常数项,特征向量X = {1 x1 x2 x3 …. xn}。为使得我们的边界可以非线性化,对于特征xi可以为特征的高次幂或相互的乘积。
对于位于判定边界上的样本,其特征向量X可使得EΘ(X) = 0。因此,判定边界 是 满足EΘ(X) = 0 的特征向量X表示的点的集合。
三、二分类和sigmoid函数
在上面,可以通过找到一个判定边界来区别样本的标签,得到一个方程EΘ(X)=0 来表示判定边界。
对于 二分类问题,即样本标签的类型只有两种类型。
当样本标记的类型只有两种时,其中一类的样本点在 判定边界的一边,其会有EΘ(X) > 0,而另一类的样本会在判定边界的另一边,会有EΘ(X) < 0。
当样本点离 判定边界 越远时,EΘ(X)的绝对值越大于0,这时样本的标签是某种类型的概率会很大,可能会等于1;当样本点离 判定边界 越近时,EΘ(X)的接近0,样本的标签是某种类型的概率会在0.5左右。
因此,我们可以将 EΘ(X)函数 转换 为一种概率函数,通过概率来判断样本的标签是某一种类型的概率会是多少。而这种 转换 就是sigmoid函数:

sigmoid函数图像如下:

从sigmoid函数图像可看出:当z为0左右时,函数值为0.5左右;z越大于0时,函数值越大于0.5越收敛于1;z越小于0时,函数值越小于0.5越收敛于0。
因此,sigmoid函数可适用于在二分类问题中将EΘ(X)函数 转换为概率函数。
当EΘ(X) > 0时,样本标记的类型为某一类型的概率会大于0.5;当EΘ(X) < 0时,样本标记的类型为某一类型的概率会小于0.5;当EΘ(X) 约等于 0时,样本标记的类型为某一类型的概率会在0.5左右。
在二分类问题中,可以找到逻辑回归函数hΘ(X) = sigmoid(EΘ(X)),判定边界可看作 hΘ(X) = 0.5 时的等高线。


四、损失函数
由上面,找到了二分类问题中的一个逻辑回归函数
。
在逻辑回归函数中,特征向量系数Θ是未知的,需要从样本中学习得来的。当从样本中学习得到一个特征向量系数Θ时,怎么知道它对应的hΘ(X) 函数的预测能力会更好?判断更准确?因此,需要一个损失函数来表示逻辑回归函数hΘ(X) 的好坏程度。
1. 定义
在二分类问题中,若用 hΘ(X)的值 表示正类型的概率,需要的损失函数应该是这样的:
【 hΘ(X) ∈(0,1)】
-
当样本标签的类型是正类型时,若该样本对应的hΘ(X)值为1时,即为正类型的概率为1,这时候损失函数值应为0;若该样本对应的hΘ(X)值为0.0001时,即为正样本的概率为0.0001,这时候损失函数值应该是一个很大的值。
-
当样本标记的类型是负类型时,若该样本对应的hΘ(X)值为0时,即为正样本的概率为0,这时损失函数值应为0;若该样本对应的hΘ(X)值为0.9999时,即为正样本的概率为0.9999,这时候损失函数值应该是一个很大的值。
因此二分类问题中,为满足这种需求,对于单个样本来说,其损失函数Cost(hΘ(X),y)可以表示为:
(hΘ(X)的值表示正样本的概率)

其中 y = 1 表示样本为正样本,y = 0 表示样本为负样本。
结合起来的写法:

对于训练集所有样本来说,共同造成的损失函数的均值J(Θ)可以表示为:

Cost代入J(Θ)中:

对于样本来说,其标记的类型为1 (正样本)或者 0(负样本),对于预测概率函数hΘ(X)来说,预测到样本为正样本的概率值在0到1之间。
2. 正则化
同时,当预测概率函数hΘ(X)过拟合,会导致高次项的特征向量系数Θi过大(因为EΘ(X)= 0 为分清每个样本点的类型会使得它足够的扭曲,这种扭曲一般由高次项的特征向量系数造成)。因此,为防止过拟合可以添加正则化项,即在损失函数的后面加个“尾巴”。
添加L2正则化项后的损失函数表示为:

五、最小化损失函数
在上面得到了 二分类问题 的逻辑回归的损失函数J(Θ)。为达到不错的分类效果,需要对损失函数进行最小化。
与 线性回归 相类似的是,这里的损失函数也是一个凸函数,因此,可以通过梯度下降法来得到合适的特性系数向量Θ。

同样,上式中的a为学习率(下山步长)。将上式的偏导展开,可得:
非正则化的损失函数的偏导:

含正则化项的损失函数的偏导:

其中 λ 为正则化的强度。
同线性回归般,可以通过学习率a对特征系数向量中的元素不断进行迭代,直到元素值收敛到某一值即可,这时可以得到损失函数较小时的特征向量系数Θ。
六、从二分类过渡到多分类
在上面,我们主要使用逻辑回归解决二分类的问题,那对于多分类的问题,也可以用逻辑回归来解决?
由于概率函数 hΘ(X) 所表示的是样本标记为某一类型的概率,但可以将一对一(二分类)扩展为一对多(one vs rest):
-
将类型class1看作正样本,其他类型全部看作负样本,然后我们就可以得到样本标记类型为该类型的概率p1;
-
然后再将另外类型class2看作正样本,其他类型全部看作负样本,同理得到p2;
-
以此循环,我们可以得到该待预测样本的标记类型分别为类型class i时的概率pi,最后我们取pi中最大的那个概率对应的样本标记类型作为我们的待预测样本类型。
七、代码示例
使用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 X, y = make_classification(n_features=2, n_redundant=0, n_informative=2,
n_clusters_per_class=1,random_state=55,n_samples=200)
X = X.astype(np.float32)
y = y.astype(np.float32) import matplotlib.pyplot as plt
import numpy as np plt.scatter(X[:,0],X[:,1],c=y)
plt.show()

from sklearn import preprocessing
scaler = preprocessing.StandardScaler().fit(X)
X = scaler.transform(X) b = tf.Variable([[0.]])
W = tf.Variable(tf.zeros([2,1])) H = 1 / (1 + tf.exp(-tf.matmul(X, W) + b))
print H
loss = tf.reduce_mean(- y.reshape(-1, 1) * tf.log(H) - (1 - y.reshape(-1, 1)) * tf.log(1 - H)) Tensor("div_47:0", shape=(200, 1), dtype=float32)
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)
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.03941517, 0.03908936], dtype=float32), array([ 0.], dtype=float32))
(5000, array([ 4.22050524, 4.22722149], dtype=float32), array([-0.16140348], dtype=float32))
(10000, array([ 4.70834684, 4.72686815], dtype=float32), array([-0.20475632], dtype=float32))
(15000, array([ 4.99622679, 5.02235842], dtype=float32), array([-0.23194873], dtype=float32))
print (w1,b1) (array([ 5.01959515, 5.04635715], dtype=float32), array([-0.2342049], 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()
