统计机器学习有两种常见的机器学习算法: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