逻辑回归进行分类的主要思想就是:根据现有的数据对分类边界线建立回归公式,对此进行分类。训练分类器就是要找到最佳拟合参数。使用的就是最优化算法。
logistic回归的优缺点
优点:计算代价不大,易于分类或理解
缺点:容易欠拟合,分类精度可能不高
逻辑回归
对于一般的分类情况,用一条直接就可以清晰的划分边界。
egin{aligned}
h_θ(x) = f(θ_0 + x_1θ_1 + x_2θ_2)
end{aligned}
其中θ用向量表示为[3,1,1],则当(-3+x_1+x_2)大于等于0,即(x_1+x_2)大于等于3 时,模型将预测 y=1,反之,预测为0。由此,可以得到分界线如下图所示。
但是对于线性不可分的情况,我们就无法用一条直线去区分不同的类别。此时,引入了逻辑回归就是为了解决线性不可分的情况。
对于分类我们需要的函数应该是能接受所有的输入然后预测出类别,比如,在两个类别的情况下,改函数会输出0或者1,对此,我们可以用sigmoid函数,sigmoid的公式为:
egin{aligned}
f(z) = frac{1}{1 + e^{-z}}
end{aligned}
sigmoid函数图像的理解。当x=0时,sigmoid函数值对应的是0.5,随着x的增大,sigmoid函数值也随之增大,无限趋近与1,随着x的减小,sigmoid函数值随之减小,无限趋近于0。
所以,sigmoid函数的到的值是一个0到1反之的数,任何大于0.5的数据将被分为1类,小于0.5的数据将被分为0类。,所以,sigmoid也可以被看做为一种概率估计。
有了分类器的函数形式之后,我们就需要找到最佳的回归系数。
基于最优化算法的最佳回归系数确定
sigmoid函数的输入记为z,有下面的公式得出:
egin{aligned}
z = w_0x_0+w_1x_1+w_2x_2+.....+w_nx_n
end{aligned}
采用向量的写法,该公式就表示为
egin{aligned}
z = w^Tx
end{aligned}
该公式表示将这两个数值向量的对应元素相乘然后全部加起来的z值,其中向量x是分类器的输入数据,向量w是我们需要的最佳参数集,为了寻找到最佳参数,此处可以采用梯度下降算法。
梯度下降算法
对于线性回归,我们知道它的代价函数为
由此,我们可以定义逻辑回归的损失函数为
将概率取对数,函数的单调性保持不变,那么逻辑回归的代价函数可以表示为
上面两个式子也就是在样本x和参数θ的情况下,样本x属于正样本(y=1)和负样本(y=0)的条件概率,但是这是非常理想的情况下,我们并不是只有把sigmoid值为1才分为1类,把sigmoid值为0才分为0类,而是把sigmoid值大于0.5分为1类,把sigmoid值小于0.5分为0类。
逻辑回归假设
因为y值非0即1,所以我们用指数的方式将这两个公式合并成一个公式。
由此可以得到似然函数
取对数,简化运算
合并出来的这个新的函数称之为cost函数,也为代价函数(Cost Function)。此时可以用梯度上升法求极大释然函数,求出来的(Theta)就是最佳的参数。但是我们希望在参数更新或衡量模型优劣时是需要一个能充分反映模型表现误差的代价函数,希望损失函数越小越好,所以我们取上面对数函数概率的相反数。此时就可以通过梯度下降法去更新参数。
那么逻辑回归的损失函数为
其中,m为样本的总数,y(i)表示第i个样本的类别,x(i)表示第i个样本,需要注意的是θ是多维向量,x(i)也是多维向量。
对J(θ)求极小值,就得到了θ的估计值。
得到损失函数之后,就可以通过梯度下降法去反复更新每一个参数。梯度下降法为:
求导后得到
具体的推导过程参见链接
虽然得到的梯度下降算法表面上看上去与线性回归的梯度下降算法一样,但是这里的 (h_θ(x)=f(θ^Tx))与线性回归中不同,所以实际上是不一样的。另外,在运行梯度下降算法之前,进行特征缩放依旧是非常必要的。
除了梯度下降算法以外,还有一些常被用来令代价函数最小的算法,这些算法更加复杂和优越,而且通常不需要人工选择学习率,通常比梯度下降算法要更加快速。这些算法有:共轭梯度,共轭梯度法 BFGS (变尺度法) 和L-BFGS (限制变尺度法)。
梯度下降法求最佳参数
梯度下降法的基本思想是:找到某函数的最小值,最好的方式就是按照该函数的梯度方向寻找,因为梯度的方向总是验证函数值变化最快的方向,这儿将步长记为(alpha),用向量的表示方法,梯度下降的算法的迭代公式为
egin{aligned}
w:= w - alphafrac{partial f(w)}{partial w}
end{aligned}
该公式会一直被执行,直到达到某种条件之后,比达到指定次数之后或者允许误差范围之内。
对于函数f(x,y),它的梯度由下面公式表示:
egin{aligned}
Delta f(x,y) = inom{frac{partial f(x,y)}{partial x}}{frac{partial f(x,y)}{partial y}}
end{aligned}
这个梯度着要向沿x的方向移动(frac{partial f(x,y)}{partial x}),沿y的方向移动(frac{partial f(x,y)}{partial y}),其中,函数f(x,y)必须要在待计算的点有定义并且可微。
根据以下伪代码实现梯度下降法
每个回归系数初始化为1
重复R次
计算整个数据集的梯度
使用alpha * gradient更新回归系数的向量
返回回归系数
多元分类
我用三种不同的符号来代表三个类别,问题怎样在给出的三种类别数据集上得到一个学习算法,从而进行分类?
我们现在已经知道如何进行二元分类,可以使用逻辑回归,对于直线或许你也知道,可以将数据集一分为二为正类和负类。用一对多的分类思想,我们可以将其用在多类分类问题上,
现在我们有一个训练集,好比上图表示的有三个类别,我们用三角形表示 y=1,方框表示 y=2,叉叉表示 y=3。我们下面要做的就是使用一个训练集,将其分成三个二元分类问题。
我们先从用三角形代表的类别1开始,实际上我们可以创建一个,新的"伪"训练集,类型2和类型3定为负类,类型1设定为正类。就变成一个二分类的问题了。
# _*_ encoding=utf8 _*_
import codecs
import numpy as np
def loadData():
dataList = []
labelList = []
with codecs.open("./data/testSet.txt","r","utf-8") as fr:
for index,line in enumerate(fr):
lineArr = line = line.strip().split(" ")
dataList.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelList.append(int(lineArr[2]))
return dataList,labelList
def sigmoid(X):
return 1.0/(1+np.exp(-X))
def gradAscent(dataMaxIn,classlabels):
dataMatrix = np.mat(dataMaxIn)
labelMat = np.mat(classlabels).transpose()
m,n = np.shape(dataMatrix)
alpha = 0.001
maxCycle = 500
weights = np.ones((n, 1))
for k in range(maxCycle):
h = sigmoid(dataMatrix * weights)
error = (labelMat -h)
weights = weights + alpha*dataMatrix.transpose()*error
return weights
if __name__ == '__main__':
dataArr,labelMat = loadData()
weights = gradAscent(dataArr,labelMat)
print(weights)
数据下载地址
loadData函数就是加载数据,每行的前两个值是X1,X2,第三个值就是对应的类别。同时为了计算,将X0的值设置为1,gradAscent就是梯度上升法,dataMatrix是一个二维数组,一共有100行,每一行就是每一个训练样本,每一列则表示不同的特征,特征目前包括了文件里面的x1,x2还有为了方便计算添加的x0,所以dataMatrix是一个100*3的矩阵,np.mat()是将序列转为np的二维数组,np.transpose()是将数组转置。
变量alpha是步长,maxCycle是迭代的次数。训练完成就可以返回回归系数。weights的初始化权重都是1,shape=(3,1),error就是正确分份额里结果和预测的分类结果的损失值。该损失值用于更新权重weights
labelMat是一个[100,1]的矩阵,m=100,n=3这两个可以理解为dataMatrix的行和列。
使用逻辑回归对mnist数据集进行分类预测
# _*_ encoding=utf8 _*_
import tensorflow as tf
# mnist数据集大小为 [55000,784] 的 mnist.train.images 和大小为 [55000,10] 的 mnist.train.labels
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)
x = tf.placeholder(tf.float32, [None, 784], name = 'X')
y = tf.placeholder(tf.float32, [None, 10], name = 'Y')
max_epochs = 1000
batch_size = 100
# 初始化权重和偏置
W = tf.Variable(tf.zeros([784, 10]), name="W")
b = tf.Variable(tf.zeros([10]), name="b")
# 创建模型
with tf.name_scope("wx_b") as scope:
# softmax通常用于解决多分类的问题,把输出值归一化为到每一类的概率,所有的概率之和呢为1,sigmoid一般解决二分类问题
y_ = tf.nn.softmax(tf.matmul(x, W) + b)
# 定义交叉损失熵
with tf.name_scope('cross_entropy') as scope:
loss = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=y_))
tf.summary.scalar('cross-entropy', loss)
# 选择使用梯度下降优化器,学习率设置为0.01
with tf.name_scope("train") as scope:
optimizer = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for epoch in range(max_epochs):
batch_xs,batch_yx = mnist.train.next_batch(batch_size)
sess.run([optimizer,loss],feed_dict={x:batch_xs,y:batch_yx})
# 当axis=1时,以行为单位 找出行中最大的值得索引,然后比较预测出来的索引和实际最大的索引是否一致
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))
print(sess.run(accuracy,feed_dict={x:mnist.test.images,y:mnist.test.labels}))