前阵子听说一个面试题:你实现一个logistic Regression需要多少分钟?搞数据挖掘的人都会觉得实现这个简单的分类器分分钟就搞定了吧?

因为我做数据挖掘的时候,从来都是顺手用用工具的,尤其是微软内部的TLC相当强大,各种机器学习的算法都有,于是自从离开学校后就没有自己实现过这些基础的算法。当有一天心血来潮自己实现一个logistic regression的时候,我会说用了3个小时么?。。。羞羞

---------------------------------------------------前言结束----------------------------------------------

当然logistic regression的渊源还是有点深的,想复习理论知识的话可以去http://en.wikipedia.org/wiki/Logistic_regression , 我这里就直接讲实现啦。

首先要了解一个logistic function

这个函数的图像是这个样子的:

而我们要实现的logistic regression model,就是要去学习出一组权值w:

x 指feature构成的向量。 这个向量w就可以将每个instance映射到一个实数了。

假如我们要出里的是2分类问题,那么问题就被描述为学习出一组w,使得h(正样本)趋近于1, h(负样本)趋近于0.

现在就变成了一个最优化问题,我们要让误差最小化。 现在问题来了,怎么定义误差函数呢?

首先想到的是L2型损失函数啦,于是啪啪啪写上了

很久没有复习logistic regression的人最容易犯错的就是在这了。正确的写法是:

然后对它求偏导数得到梯度下降法的迭代更新方程:

于是你会发现这个迭代方程是和线性回归的是一样的!

理清了过程时候,代码就变得异常简单了:

  public class LogisticRegression
{
private int _maxIteration = ;
private double _stepSize = 0.000005;
//private double _stepSize = 0.1;
private double _lambda = 0.1;
private double decay = 0.95; public int dim;
public double[] theta; public LogisticRegression(int dim)
{
this.dim = dim;
} public LogisticRegression(int dim, double stepSize)
: this(dim)
{
this._stepSize = stepSize;
} public void Train(Instance[] instances)
{
Initialize(); int instCnt = instances.Length;
double[] dev =new double[this.dim];
for (int t = ; t < this._maxIteration; t++)
{
double cost = ;
for (int i = ; i < instCnt; i++)
{
double h_x = MathLib.Logistic(MathLib.VectorInnerProd(instances[i].featureValues, this.theta));
// calculate cost function
cost += instances[i].label * Math.Log(h_x) + ( - instances[i].label) * Math.Log( - h_x);
}
cost *= -1.0 / instCnt;
Console.WriteLine("{0},{1}", t, cost); for (int i = ; i < instCnt; i++)
{
ResetArray(dev);
double h_x = MathLib.Logistic(MathLib.VectorInnerProd(instances[i].featureValues, this.theta));
double error = h_x- instances[i].label ;
for (int j = ; j < this.dim; j++)
{
dev[j] += error*instances[i].featureValues[j] + *dev[j]*this._lambda;
this.theta[j] -= this._stepSize * dev[j] ;
//BoundaryLimiting(ref this.theta[j], 0, 1);
}
}
//this._stepSize *= decay;
//if (this._stepSize > 0.000001)
//{
// this._stepSize = 0.000001;
//}
}
} private void BoundaryLimiting(ref double alpha, double lowerbound, double upperbound)
{
if (alpha < lowerbound)
{
alpha = lowerbound;
}
else if (alpha > upperbound)
{
alpha = upperbound;
}
} public double[] Predict(Instance[] instances)
{
double[] results = new double[instances.Length];
for (int i = ; i < results.Length; i++)
{
results[i] = MathLib.Logistic(MathLib.VectorInnerProd(instances[i].featureValues, this.theta));
}
return results;
} private void ResetArray(double[] dev)
{
for (int i = ; i < dev.Length; i++)
{
dev[i] = ;
}
} private void Initialize()
{
Random ran = new Random(DateTime.Now.Second); this.theta = new double[this.dim];
for (int i = ; i < this.dim; i++)
{
this.theta[i] = ran.NextDouble() * ; // initialize theta with a small value
}
} public static void Test()
{
LogisticRegression lr = new LogisticRegression(); List<Instance> instances = new List<Instance>();
using (StreamReader rd = new StreamReader(@"D:\\local exp\\data.csv"))
{
string content = rd.ReadLine();
while ((content = rd.ReadLine()) != null)
{
instances.Add(Instance.ParseInstance(content,','));
}
} // MinMaxNormalize(instances); lr.Train(instances.ToArray()); } private static void MinMaxNormalize(List<Instance> instances)
{
int dim = instances[].dim;
double[] min = new double[dim];
double[] max = new double[dim]; int instCnt = instances.Count;
for (int i = ; i < instCnt; i++)
{
for (int j = ; j < dim; j++)
{
if (i == || instances[i].featureValues[j] < min[j])
{
min[j] = instances[i].featureValues[j];
}
if (i == || instances[i].featureValues[j] > max[j])
{
max[j] = instances[i].featureValues[j];
}
}
} for (int j = ; j < dim; j++)
{
double gap = max[j] - min[j];
if (gap <= )
{
continue;
}
for (int i = ; i < instCnt; i++)
{
instances[i].featureValues[j] = (instances[i].featureValues[j] - min[j]) / gap;
}
} }
}

前面提到说我花了3个小时,其中很大一部分原因是在debug算法为啥没有收敛。这里有个很重要的步骤是把feature规范化到[0,1] 。 如果不normalize的话,参数调起来比较麻烦,loss function也经常蹦到NaN去了。

以下是对比normalize和不加normalization的收敛曲线图:

我用的实现数据可以在 http://pingax.com/wp-content/uploads/2013/12/data.csv  下载到。 它是一个2维的数据, 分布如下:

快速用梯度下降法实现一个Logistic Regression 分类器的更多相关文章

  1. Logistic Regression分类器

    1. 两类Logistic回归 Logistic回归是一种非常高效的分类器.它不仅可以预测样本的类别,还可以计算出分类的概率信息. 不妨设有$n$个训练样本$\{x_1, ..., x_n\}$,$x ...

  2. Stanford机器学习---第三讲. 逻辑回归和过拟合问题的解决 logistic Regression & Regularization

    原文:http://blog.csdn.net/abcjennifer/article/details/7716281 本栏目(Machine learning)包括单参数的线性回归.多参数的线性回归 ...

  3. 李宏毅机器学习笔记3:Classification、Logistic Regression

    李宏毅老师的机器学习课程和吴恩达老师的机器学习课程都是都是ML和DL非常好的入门资料,在YouTube.网易云课堂.B站都能观看到相应的课程视频,接下来这一系列的博客我都将记录老师上课的笔记以及自己对 ...

  4. Python机器学习笔记 Logistic Regression

    Logistic回归公式推导和代码实现 1,引言 logistic回归是机器学习中最常用最经典的分类方法之一,有人称之为逻辑回归或者逻辑斯蒂回归.虽然他称为回归模型,但是却处理的是分类问题,这主要是因 ...

  5. 梯度下降法原理与python实现

    梯度下降法(Gradient descent)是一个一阶最优化算法,通常也称为最速下降法. 要使用梯度下降法找到一个函数的局部极小值,必须向函数上当前点对应梯度(或者是近似梯度)的反方向的规定步长距离 ...

  6. 梯度下降法(BGD、SGD)、牛顿法、拟牛顿法(DFP、BFGS)、共轭梯度法

    一.梯度下降法 梯度:如果函数是一维的变量,则梯度就是导数的方向:      如果是大于一维的,梯度就是在这个点的法向量,并指向数值更高的等值线,这就是为什么求最小值的时候要用负梯度 梯度下降法(Gr ...

  7. 使用Logistic Regression Algorithm进行多分类数字识别的Octave仿真

    所需解决的问题是,训练一个Logistic Regression系统,使之能够识别手写体数字1-10,每张图片为20px*20px的灰度图.训练样例的输入X是5000行400列的一个矩阵,每一行存储一 ...

  8. pytorch梯度下降法讲解(非常详细)

    pytorch随机梯度下降法1.梯度.偏微分以及梯度的区别和联系(1)导数是指一元函数对于自变量求导得到的数值,它是一个标量,反映了函数的变化趋势:(2)偏微分是多元函数对各个自变量求导得到的,它反映 ...

  9. Logistic Regression - Formula Deduction

    Sigmoid Function \[ \sigma(z)=\frac{1}{1+e^{(-z)}} \] feature: axial symmetry: \[ \sigma(z)+ \sigma( ...

随机推荐

  1. 项目中 Spring 配置文件的选型问题 (xml和注解的抉择)

    要改的东西用xml 不改的东西用注解:这句是对用不用注解或者 xml的一个不错的解决方案 xml使用场景: 1.外部jar包依赖bean配置 2.用注解无法实现,或者用注解无法轻易实现的情形 3.项目 ...

  2. vs2019编译redis

    版本信息 使用Redis源码版本,解压工程右键生成hiredis项目正常,编译Win32_Interop项目报下图错误(error C2039:system_error:不是std成员;error C ...

  3. spring cloud网关通过Zuul RateLimit 限流配置

    目录 引入依赖 配置信息 RateLimit源码简单分析 RateLimit详细的配置信息解读 在平常项目中为了防止一些没有token访问的API被大量无限的调用,需要对一些服务进行API限流.就好比 ...

  4. 树莓派3b无驱动打印

    Linux系统下很少有对打印机做驱动支持,自己做起来又有非常麻烦,还好大多数打印机都能够支持escpos协议,因此我们可以做到无驱动打印. 1.安装python-usb库 git clone http ...

  5. JUC——线程同步辅助工具类(Exchanger,CompletableFuture)

    Exchanger交换空间 如果现在有两个线程,一个线程负责生产数据,另外一个线程负责消费数据,那么这个两个线程之间一定会存在一个公共的区域,那么这个区域的实现在JUC包之中称为Exchanger. ...

  6. Spring Boot之发送HTTP请求(RestTemplate详解)

    原文作者:微笑面对生活 https://www.javazhiyin.com/19714.html#comment-345 RestTemplate是Spring提供的用于访问Rest服务的客户端,R ...

  7. VM虚拟机系统时间同步网络时间并登录用户自动校正时间

    原文出处: http://blog.51cto.com/wutou/1932317 VM虚拟机大家都用,我在用完后,经常使用"挂起客户机",但是这样一来,系统恢复启动很快,但是少了 ...

  8. Next Generation MSP 和传统MSP的区别

    1. 速度 -- 基础架构代码化快速部署 由于物理硬件已经在云上启动并运行,因此配置服务器要快得多.配置完成后,只有当MSP采用DevOps并将环境“基础设施作为代码”时,配置才能快速.云MSP可以轻 ...

  9. FFT(快速傅里叶变换)算法详解

    多项式的点值表示(Point Value Representation) 设多项式的系数表示(Coefficient Representation): \[ \begin{align*} \mathr ...

  10. IDEA2017.3.4破解方式及lombok图文配置详解

    下载jetbrainsCrack-2.7-release-str.jar包 下载地址: https://files.cnblogs.com/files/xifenglou/JetBrains.zip ...