统计机器学习有两种常见的机器学习算法:logistic回归和SVM,这两种算法都涉及到优化问题,是非常重要的两种机器学习算法,尤其是logistic回归可能是工程上用的最广泛的机器学习算法了。在logistic回归中训练分类器就是寻找最佳拟合参数,使用的是最简单常见优化算法 - 梯度下降法。
logistic回归的核心是给每个特征xi训练出一个最佳回归系数wi,得到W'X,带入到sigmoid函数,然后sigmoid函数输出的值与0.5作比较,进行分类,而最佳回归系数的确定就是两部分:极大似然估计,梯度下降。
此处仅仅写了大概过程,详细的推导过程,见本人总结的文档:http://download.csdn.net/detail/zk_j1994/9899047
1. 极大似然估计
极大似然估计是一种参数估计方法,在logistic回归中被用来估计模型的参数W。简单的讲就是每个样本的概率密度连乘积最大。
2. 梯度下降
梯度下降用来优化参数,使模型最优。
3. 向量化
将梯度下降的过程向量化,就很容易实现logistic算法。
4. 梯度下降的优化 - SGD
在梯度下降中,哪怕更新一个回归系数,所有的样本也得参与运算, 因此我们每次迭代只选择一个样本或者一个bitch(当然label与之保持一致)来参与运算。收敛速度会大大提升。
5. logistic回归的优缺点
优点:
1)本质上是线性模型,计算简单,训练速度快。
2)引入了sigmoid函数来进行分类,因此能够近似的把输出结果看做一个概率(当然绝对不是概率),因此其输出结果比较直观。
缺点:
1)线性,分类精度简单,容易欠拟合。
6. 实现logistic回归
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import time
import random
random.seed
def timing(f):
""" 装饰器计时, 梯度下降和随机梯度下降的时间 """
def fun(*args, **kwargs):
begin = time.clock()
returnVal = f(*args, **kwargs)
print("{0}运行时间为: {1}".format(f.__name__, time.clock() - begin))
return returnVal
return fun
def load_data():
""" 1. 导入testSet.txt的数据 """
train_data = []
with open("testSet.txt", "r") as f:
for line in f.readlines():
train_data.append(line.strip().split("\t"))
train_data = np.array(train_data, np.float)
train_x = np.concatenate((np.ones((len(train_data),1)), train_data[:, 0:2]), axis=1)
train_y = train_data[:, 2].astype(int)
return train_x, train_y
def sigmoid(x):
""" 计算sigmoid(x)的值, numpy的exp能够直接操作矩阵 """
return 1 / (1 + np.exp(-x))
@timing
def gradientAscent(train_x, train_y, max_iter, step = 0.001):
""" 2. 梯度下降进行训练 """
train_x = np.mat(train_x)
train_y = np.mat(train_y).T
rows, cols = train_x.shape
weights = np.ones((cols,1))
for i in range(max_iter):
# 内部每个样本都参与了计算
value = sigmoid(train_x * weights)
error = train_y - value
weights = weights + step * train_x.T * error
return weights
@timing
def randomGradientAscent(train_x, train_y, max_iter, initStep = 0.001, batchSize = 10):
""" 3. 随机梯度下降训练 """
rows, cols = train_x.shape
weights = np.ones((cols, ))
length = len(train_x)
for i in range(max_iter):
# 选取一个batch进行迭代
sampleIndex = random.sample(range(length), batchSize)
step = initStep + 2 / (i + 1)
value = sigmoid(np.dot(train_x[sampleIndex], weights))
error = train_y[sampleIndex] - value
weights = weights + step * np.dot(train_x[sampleIndex].T, error)
return weights
def drawResult(train_x, train_y, weigths, title):
""" 4. 绘制分类结果 """
weigths = np.array(weigths).reshape((len(weigths),))
plt.figure()
class_0 = train_x[train_y == 0]
class_1 = train_x[train_y == 1]
plt.scatter(class_0[:,1], class_0[:, 2],color="red", alpha=1.0)
plt.scatter(class_1[:,1], class_1[:, 2],color="blue", alpha=0.5)
x = list(range(-3,4,1))
y = [ -(weigths[0] + weigths[1]*i) / weigths[2] for i in x ]
plt.plot(x, y)
plt.title(title)
return plt
if __name__ == "__main__":
train_x, train_y = load_data()
weights = gradientAscent(train_x, train_y, 500, 0.001)
drawResult(train_x, train_y, weights, "gradient ascent").show()
weights = randomGradientAscent(train_x, train_y, 50, 0.001, 10)
drawResult(train_x, train_y, weights, "random gradient ascent").show()
7. 注意
1)logistic回归使用之前要对特征进行归一化,最好使其满足0均值,单位方差。处理方式是对每一维特征减去均值除以方差。
2)可以将标准线性回归改为ridge,lasso形式,再应用到sigmoid函数。
参考书籍:《机器学习实战》,《统计学习方法》
http://blog.csdn.net/ariessurfer/article/details/41310525
http://blog.csdn.net/dongtingzhizi/article/details/15962797