代码可在Github上下载:代码下载
今天看了一下《统计学习方法》里的逻辑斯谛回归,结合下《机器学习实战》里面的代码,很精炼。公式如下:
模型:
P ( Y = 1 ∣ x ) = e x p ( w ⋅ x + b ) 1 + e x p ( w ⋅ x + b ) P(Y=1|x)=\frac {exp(w\cdot x+b)}{1+exp(w\cdot x+b)} P(Y=1∣x)=1+exp(w⋅x+b)exp(w⋅x+b)
P ( Y = 0 ∣ x ) = 1 − P ( Y = 1 ∣ x ) = 1 1 + e x p ( w ⋅ x + b ) P(Y=0|x)=1-P(Y=1|x)=\frac {1}{1+exp(w\cdot x+b)} P(Y=0∣x)=1−P(Y=1∣x)=1+exp(w⋅x+b)1
策略:对数损失函数
算法:梯度下降算法
假设 P ( Y = 1 ∣ x ) = π ( x ) , P ( Y = 0 ∣ x ) = 1 − π ( x ) P(Y = 1|x){\rm{ = }}\pi \left( x \right),P(Y = 0|x){\rm{ = 1 - }}\pi \left( x \right) P(Y=1∣x)=π(x),P(Y=0∣x)=1−π(x),那么则服从的是贝努利分布,结合贝努利分布的概率密度函数 [ π ( x i ) ] y i [ 1 − π ( x i ) ] y i {\left[ {\pi \left( {{x_i}} \right)} \right]^{{y_i}}}{\left[ {1 - \pi \left( {{x_i}} \right)} \right]^{{y_i}}} [π(xi)]yi[1−π(xi)]yi,进而得到似然函数 ∏ i = 1 N [ π ( x i ) ] y i [ 1 − π ( x i ) ] y i \prod\limits_{i = 1}^N {{{\left[ {\pi \left( {{x_i}} \right)} \right]}^{{y_i}}}{{\left[ {1 - \pi \left( {{x_i}} \right)} \right]}^{{y_i}}}} i=1∏N[π(xi)]yi[1−π(xi)]yi,为了减少计算量,再进一步得到替代品对数似然函数 L ( w ) = ∑ i = 1 N [ y i log π ( x i ) + ( 1 − y i ) log ( 1 − π ( x i ) ) ] L\left( w \right) = \sum\limits_{i = 1}^N {\left[ {{y_i}\log \pi \left( {{x_i}} \right) + \left( {1 - {y_i}} \right)\log \left( {1 - \pi \left( {{x_i}} \right)} \right)} \right]} L(w)=i=1∑N[yilogπ(xi)+(1−yi)log(1−π(xi))],便是逻辑斯蒂回归的损失函数了。
首先需要加载数据,加载数据的函数。
def loadDataSet(self, fileName = 'testSet.txt'): #加载数据
dataMat = []
labelMat = []
fr = open(fileName)
for line in fr.readlines(): #遍历文件
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) #数据集
labelMat.append(int(lineArr[-1])) #类别标签
return dataMat, labelMat
其中dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
这句代码使将输入向量做了一个扩充,将1放在输入向量的末尾,变成 x = ( 1 , x ( 1 ) , x ( 2 ) , x ( 3 ) ⋯   , x ( n ) ) x = ({\rm{1,}}{x^{(1)}},{x^{(2)}},{x^{(3)}} \cdots ,{x^{(n)}}) x=(1,x(1),x(2),x(3)⋯,x(n)),然后将权值向量写成 w = ( b , w ( 1 ) , w ( 2 ) , w ( 3 ) ⋯   , w ( n ) ) w = (b,{w^{(1)}},{w^{(2)}},{w^{(3)}} \cdots ,{w^{(n)}}) w=(b,w(1),w(2),w(3)⋯,w(n)),这样可以方便计算。
接下来看sigmoid函数,它的值域是[0, 1]。对应的就是刚才上面的第一个公式。如果写成扩充向量的形式的话,就是 P ( Y = 1 ∣ x ) = e x p ( w ⋅ x ) 1 + e x p ( w ⋅ x ) P(Y=1|x)=\frac {exp(w\cdot x)}{1+exp(w\cdot x)} P(Y=1∣x)=1+exp(w⋅x)exp(w⋅x)这个公式了(要同除以 e x p ( w ⋅ x ) exp(w\cdot x) exp(w⋅x))。
def sigmoid(self, inX):
return np.exp(inX) / (1 + np.exp(inX))
最后就是我们的训练的函数了。
def train(self, dataSet, labels): #训练
dataMat = np.mat(dataSet) #将数据集转成矩阵的形式
labelMat = np.mat(labels).transpose()#将类别集合转成矩阵的形式
m, n = np.shape(dataSet) #行列
alpha = 0.01
maxIter = 500
weights = np.ones((n, 1))
for i in range(maxIter): #迭代
h = self.sigmoid(dataMat * weights)
error = h - labelMat #预测值和标签值所形成的误差
weights = weights - alpha * dataMat.transpose() * error #权重的更新
return weights
这里使用了梯度下降算法来进行训练,根据以下公式。
∇ w L ( w ) = X T ( h − y ) {\nabla _w}L\left( w \right) = {{\bf{X}}^T}\left( {{\bf{h - y}}} \right) ∇wL(w)=XT(h−y)
对应下面的代码。
error = h - labelMat #预测值和标签值所形成的误差
weights = weights - alpha * dataMat.transpose() * error #权重的更新
最后我们运行一下。
logistic = Logistic()
dataSet, labels = logistic.loadDataSet()
weights = logistic.gradDescent(dataSet, labels)
print weights
完整代码和数据可以在逻辑斯蒂回归代码下载。
如果您觉得好用的话,麻烦点个赞。