1 逻辑回归
前面介绍的线性回归处理的因变量都是数值型区间变量,建立的模型描述是因变量的期望与自变量之间的线性关系。
(1.1)
z
=
θ
T
x
+
b
z = \theta^Tx+b\tag{1.1}
z=θTx+b(1.1)
而在采用回归模型分析实际问题中,可能我们的期待输出值并不是一个连续值,而是离散值,比如判断一封邮件是否为垃圾邮件,这时输出就只有1(垃圾邮件)或者0(正常邮件),这时我们怎么处理呢?
我们可以通过一个函数将预测值f(x)转换为0/1值,这样就可以实现分类判别了。最理想的转换函数就是"单位阶跃函数"(unit-step function)
(1.2)
y
=
{
0
,
z
<
0
0.5
,
z
=
0
1
,
z
>
0
y = \begin{cases} 0,&z<0\\ 0.5,&z=0\\ 1,&z>0\\\tag{1.2} \end{cases}
y=⎩⎪⎨⎪⎧0,0.5,1,z<0z=0z>0(1.2)
但是单位阶跃函数并不连续,即不可导,于是我们希望找到能在一定程度上近似单位阶跃函数的替代函数,且希望它单调可微,sigmoid函数就是这样的一个替代函数。
(1.3)
g
(
z
)
=
1
1
+
e
−
z
g(z) = \frac{1}{1+e^{-z}} \tag{1.3}
g(z)=1+e−z1(1.3)
sigmoid函数的导数如式1,4所示
(1.4)
g
′
(
z
)
=
(
1
1
+
e
−
z
)
=
g
(
z
)
∗
(
1
−
g
(
z
)
)
\begin{aligned} g^{'}(z) &= \left(\frac{1}{1+e^{-z}}\right)\\ &=g(z)*(1-g(z))\\\tag{1.4} \end{aligned}
g′(z)=(1+e−z1)=g(z)∗(1−g(z))(1.4)
sigmoid函数的图像为
由上图可以看出,sigmoid函数将z值转化为一个接近0或1的y值,并且其输出值在z=0附件变换很陡,将式1.1代入1.3中,可得
(1.5)
h
θ
=
g
(
θ
T
x
)
=
1
1
+
e
−
(
θ
T
x
+
b
)
h_\theta = g(\theta^T x) = \frac{1}{1+e^{-(\theta^Tx + b)}}\tag{1.5}
hθ=g(θTx)=1+e−(θTx+b)1(1.5)
下面我们来看如何确定式1.5中的
θ
\theta
θ和b,假设我们在做一个二分类任务,则y=1的概率可设为
h
θ
h_\theta
hθ,则y=0的概率就为
1
−
h
θ
1-h_\theta
1−hθ
(1.6)
P
(
y
=
1
∣
x
;
θ
)
=
h
θ
(
x
)
P(y=1|x;\theta) = h_\theta (x)\tag{1.6}
P(y=1∣x;θ)=hθ(x)(1.6)
(1.7) P ( y = 0 ∣ x ; θ ) = 1 − h θ ( x ) P(y=0|x;\theta) = 1 - h_\theta (x)\tag{1.7} P(y=0∣x;θ)=1−hθ(x)(1.7)
(1.8) 整合: P ( y ∣ x ; θ ) = ( h θ ( x ) ) y ( 1 − h θ ( x ) ) 1 − y \text{整合:}P(y|x;\theta) = (h_\theta (x))^y(1-h_\theta (x))^{1-y}\tag{1.8} 整合:P(y∣x;θ)=(hθ(x))y(1−hθ(x))1−y(1.8)
此时我们即可得到我们的似然函数
(1.9)
L
(
θ
)
=
∏
i
−
1
m
(
h
θ
(
x
(
i
)
)
)
y
(
i
)
(
1
−
h
θ
(
x
(
i
)
)
)
1
−
y
(
i
)
L(\theta) = \prod_{i-1}^m (h_\theta (x^{(i)}))^{y^{(i)}}(1-h_\theta (x^{(i)}))^{1-y^{(i)}}\tag{1.9}
L(θ)=i−1∏m(hθ(x(i)))y(i)(1−hθ(x(i)))1−y(i)(1.9)
先两边同时取log再化简
(1.10)
l
(
θ
)
=
l
o
g
L
(
θ
)
=
∑
i
=
1
m
(
y
i
l
o
g
h
θ
(
x
i
)
+
(
1
−
y
i
)
l
o
g
(
1
−
h
θ
(
x
i
)
)
)
l(\theta) = logL(\theta) = \sum_{i=1}^m (y_ilogh_\theta (x_i) +(1-y_i)log(1-h_\theta (x_i))) \tag{1.10}
l(θ)=logL(θ)=i=1∑m(yiloghθ(xi)+(1−yi)log(1−hθ(xi)))(1.10)
就跟我们在之前线性回归里面一样,我们要取得最大似然,也就是最大化
l
(
θ
)
l(\theta)
l(θ),不过考虑到我们会用梯度下降法来求解,所以我们引入
(1.11)
J
(
θ
)
=
−
1
m
l
(
θ
)
J(\theta) = -\frac{1}{m} l(\theta) \tag{1.11}
J(θ)=−m1l(θ)(1.11)
此时我们的任务就变为用梯度下降法来最小化
J
(
θ
)
J(\theta)
J(θ),跟之前线性回归一样,我们对其进行求导
(1.12)
δ
δ
(
θ
j
)
=
−
1
m
∑
i
=
1
m
(
y
i
1
h
θ
(
x
i
)
δ
δ
(
θ
i
)
h
θ
(
x
i
)
−
(
1
−
y
i
)
1
1
−
h
θ
(
x
i
)
δ
δ
(
θ
i
)
h
θ
(
x
i
)
)
=
−
1
m
∑
i
=
1
m
(
y
i
1
g
(
θ
T
x
i
)
−
(
1
−
y
i
)
1
1
−
g
(
θ
T
x
i
)
)
g
(
θ
T
x
i
)
(
1
−
g
(
θ
T
x
i
)
)
δ
δ
(
θ
i
)
θ
T
x
i
=
1
m
∑
i
=
1
m
(
h
θ
(
x
i
)
−
y
i
)
x
i
j
\begin{aligned} \frac{\delta}{\delta_(\theta_j)} &= -\frac{1}{m} \sum_{i=1}^m \left(y_i\frac{1}{h_\theta (x_i)}\frac{\delta}{\delta _(\theta_i)}h_\theta(x_i) - (1-y_i)\frac{1}{1-h_\theta (x_i)}\frac{\delta}{\delta _(\theta_i)}h_\theta(x_i)\right)\\ &=-\frac{1}{m} \sum_{i=1}^m \left(y_i\frac{1}{g{(\theta^Tx_i)}} - (1-y_i)\frac{1}{1-g(\theta^T x_i)}\right)g(\theta ^Tx_i)(1 - g(\theta ^Tx_i))\frac{\delta}{\delta _(\theta_i)}\theta^Tx_i\\ &=\frac{1}{m}\sum_{i=1}^m(h_\theta(x_i)-y_i)x_i^j\\\tag{1.12} \end{aligned}
δ(θj)δ=−m1i=1∑m(yihθ(xi)1δ(θi)δhθ(xi)−(1−yi)1−hθ(xi)1δ(θi)δhθ(xi))=−m1i=1∑m(yig(θTxi)1−(1−yi)1−g(θTxi)1)g(θTxi)(1−g(θTxi))δ(θi)δθTxi=m1i=1∑m(hθ(xi)−yi)xij(1.12)
得到
J
(
θ
)
J(\theta)
J(θ) 的导数后,我们即可利用梯度下降法去更新参数了
(1.13)
θ
j
=
θ
j
−
α
1
m
∑
i
=
1
m
(
h
θ
(
x
i
)
−
y
i
)
x
i
j
\theta_j = \theta_j - \alpha\frac{1}{m}\sum_{i=1}^m(h_\theta(x_i)-y_i)x_i^j\tag{1.13}
θj=θj−αm1i=1∑m(hθ(xi)−yi)xij(1.13)
2 算法实现
#sigmoid函数实现
def sigmoid(self,x):
return 1.0/(1+np.exp(-x))
#使用批量梯度下降算法
def train(self,X,Y,learningRate=1e-3,numIters=1000,reg=0.0,verbose=False):
m,n = X.shape
n = np.shape(X)[1] #特征个数
#权值初始化
if self.w == None:
self.w = np.ones((n,1))
lossHistory = []
for i in range(numIters):
h = self.sigmoid(X.dot(self.w))
loss = h-Y
lossHistory.append(np.sum(loss))
#更新参数
self.w = self.w - learningRate*X.T.dot(loss)
if verbose and i%100 == 0:
print("迭代次数:%d/%d loss: %f"%(i,numIters,np.sum(loss)))
return lossHistory
#预测函数
def predict(self,X):
return np.mat([1 if x >= 0.5 else 0 for x in self.sigmoid(X.dot(self.w))])
核心函数均实现后开始进行测试
首先加载数据
def load_data():
#加载iris数据
data = datasets.load_iris()
#给数据加上一行偏置
offset = np.ones(100)
feature_data = data['data'][:100]
feature_data = np.insert(feature_data, 0, values=offset, axis=1)
label_data = data['target'][:100].reshape((-1,1))
#80个数据作为训练集 20个作为测试集
trainX = feature_data[:40]
trainX = np.concatenate((trainX, feature_data[50:90]))
trainY = label_data[:40]
trainY = np.concatenate((trainY, label_data[50:90]))
testX = feature_data[40:50]
testX = np.concatenate((testX, feature_data[90:100]))
testY = label_data[40:50]
testY = np.concatenate((testY, label_data[90:100]))
return trainX,trainY,testX,testY
print("-----------1.load_data-----------")
trainX,trainY,testX,testY = load_data()
print("-----------2.training-----------")
model = logisticRegression()
lossHistory = model.train(trainX,trainY,learningRate=1e-3,verbose=True,numIters=1000)
ax = plt.subplot(111)
ax.plot(lossHistory)
ax.set_xlabel('Iterations ')
ax.set_ylabel('Cost')
#验证集测试
predY = model.predict(testX)
print("验证集正确率: %f"%((predY.T==testY).mean()))
训练过程如下图所示
使用验证集进行测试时,因为数据量本身很小,而且不同类别数据分的很开,很轻松就得到了100%的正确率
代码点此获取GitHub