logistic回归是统计学习中经典的分类方法,他属于对数线性模型。本博文主要给出logistic的c++实现,具体理论请读者自行google。

  本文用到的数据集来自于一个医学网站,具体出处不记得了(非常歉意)。数据的格式如下:

  10009 1 0 0 1 0 1
  10025 0 0 1 2 0 0
  20035 0 0 1 0 0 1
  20053 1 0 0 0 0 0
  30627 1 0 1 2 0 0
  30648 2 0 0 0 1 0

每行有7个列值,第一列是一个ID号,在具体操作中,忽略该列。之后的5列,每一个都表示一个特征的取值;最后一列是分类标记(0或1)。

  在具体实现时,将分隔数据为训练数据和测试数据,并保存到文件中,文件组织形式如下:

  

其中testdata.txt,保存测试数据;traindata.txt保存训练数据;logistic.cpp是代码源文件。三个文件保存在同一目录下。

实现代码如下:

/*********
logistic回归(c++) by 姜富春
**********/
#include<iostream>
#include<fstream>
#include<vector>
#include<sstream>
#include<cmath>
using namespace std;
struct Data{
vector<int> features;
int cls;
Data(vector<int> f,int c):features(f),cls(c){ }
};
struct Param{
vector<double> w;
double d;
Param(vector<double> w1,double d1):w(w1),d(d1){};
Param():w(vector<double>()),d(0.0){}
};
class Logistic{
public:
Logistic(){
//载入traindata文件构造dataSet;
loadDataSet(dataSet);
//初始化Param,w的长度与数据特征的长度相同,初值为0.0。d的初值也为0.0
vector<double> pw(dataSet[0].features.size(),0.0);
Param pt(pw,0.0);
param=pt; };
void loadDataSet(vector<Data>& ds,string dataFile="./traindata.txt"){
ifstream fin(dataFile.c_str());
if(!fin){
cout<<"文件打开失败"<<endl;
exit(0);
}
while(fin){
string line;
getline(fin,line);
if(line.size()>3){
stringstream sin(line);
int t;
sin>>t;
vector<int> fea;
while(sin){
char c=sin.peek();
if(int(c)!=-1){
sin>>t;
fea.push_back(t);
} }
int cl=fea.back();
fea.pop_back();
ds.push_back(Data(fea,cl));
}
}
} void displayDataSet(){
for(int i=0;i<dataSet.size();i++){
for(int j=0;j<dataSet[i].features.size();j++){
cout<<dataSet[i].features[j]<<" ";
}
cout<<" 分类:"<<dataSet[i].cls;
cout<<endl;
}
}
void logisticRegression(){
//由目标函数为最大似然,因此最终求得的是目标函数的最大值,
//因此迭代过程是梯度上升,而非梯度下降
double lamda=0.1;//梯度下降的步长
double delta=0.0001;//结束迭代的阈值
//目标函数的值
double objLw=Lw(param);
//cout<<objLw<<endl;
Param tpa(param.w,param.d);
gradient(lamda);
double newObjLw=Lw(param);
int iter=0;
cout<<"初始:"<<endl;
displayIterProcess(iter,objLw,newObjLw,1);
while(fabs(newObjLw-objLw)>delta||!samewb(tpa,param,delta)){
objLw=newObjLw;
tpa=Param(param.w,param.d);
gradient(lamda);
newObjLw=Lw(param);
++iter;
displayIterProcess(iter,objLw,newObjLw,5);
}
cout<<"迭代结束共迭代"<<iter<<"步"<<endl;
displayIterProcess(iter,objLw,newObjLw,1); }
bool samewb(const Param &tparam,const Param& param,double delta){
for(int i=0;i<tparam.w.size();i++){
if(fabs(tparam.w[i]-param.w[i])>delta){
return false;
}
}
if(fabs(tparam.d-param.d)>delta){
return false;
}
return true;
}
void displayIterProcess(int iter,double objLw,double newObjLw,int mod){
//每mod步打印一次迭代过程
if(iter%mod==0){
cout<<"迭代"<<iter<<":目标函数值【"<<newObjLw<<"】,两次迭代目标函数差值【 "<<(newObjLw-objLw)<<"】"<<endl;
cout<<"模型参数:";
for(int i=0;i<param.w.size();i++){
cout<<param.w[i]<<" ";
}
cout<<param.d<<endl<<endl;
} }
//梯度上升更新w和b
void gradient(double lam){
for(int i=0;i<param.w.size();i++){
double tmp=0.0L;//保存梯度上升过程的中间值
for(int j=0;j<dataSet.size();j++){
tmp+=(dataSet[j].cls-logiFun(param,dataSet[j]))*dataSet[j].features[i]*lam;
}
param.w[i]+=(tmp);
}
double tmp=0.0L;
for(int j=0;j<dataSet.size();j++){
tmp+=(dataSet[j].cls-logiFun(param,dataSet[j]))*lam;
}
param.d+=tmp; }
//计算logistic函数的值,即f(x)=exp(wx)/(1+exp(wx)),该表达式在求解梯度过程中出现,
//因此计算这个值是为了辅助梯度上升计算过程
inline double logiFun(const Param &p,const Data &d){
double inner=innerWX(p,d);
double le=exp(inner)/(1+exp(inner));
return le;
}
//计算对数似然函数的值
double Lw(Param p){
double l=0.0L;
for(int i=0;i<dataSet.size();i++){
double inner=innerWX(p,dataSet[i]);
l+=(dataSet[i].cls*inner-(log10(1+exp(inner))));
//cout<<"l="<<l<<endl;
} return l;
}
//计算wx+b的值
inline double innerWX(const Param &p,const Data &data){
if(p.w.size()!=data.features.size()){
cout<<"参数与实例的维度不匹配,不能进行内积计算"<<endl;
exit(0);
}
double innerP=0.0L;
for(int i=0;i<p.w.size();i++){
innerP+=(p.w[i]*data.features[i]);
}
innerP+=p.d;
return innerP;
}
//给定测试集,预测分类
void predictClass(){
vector<Data> testDataSet;
loadDataSet(testDataSet,"./testdata.txt");
/*******************
分别计算
P(Y=1|x)=exp(w.x)/(1+exp(w.x))

P(Y=0|x)=1/(1+exp(w.x))
然后取值大的作为x的分类
*******************/
cout<<endl<<"预测分类:"<<endl;
for(int i=0;i<testDataSet.size();i++){
double py1=0.0L;
double py0=0.0L;
double inner=innerWX(param,testDataSet[i]);
py1=exp(inner)/(1+exp(inner));
py0=1-py1;
cout<<"实例: ";
for(int j=0;j<testDataSet[i].features.size();j++){
cout<<testDataSet[i].features[j]<<" ";
}
cout<<"标记分类【"<<testDataSet[i].cls<<"】,";
if(py1>=py0){ cout<<"预测分类【"<<1<<"】"<<endl;
}else{
cout<<"预测分类【"<<0<<"】"<<endl;
}
}
}
private:
vector<Data> dataSet;
Param param;
};
int main(){
Logistic logist;
//logist.displayDataSet();
logist.logisticRegression();
logist.predictClass();
system("pause");
return 0;
}

  程序运行结果如下:

  1. 迭代训练模型过程如下

  2. 测试分类过程如下:

本例中分类测试的效果并不是太好,在调试的时候,看到这样的结果我也仔细地审查代码,并未发现程序的错误(也希望读者帮忙审查)。考虑可能的原因:

  (1)数据太少

  (2)数据特征不能提供足够信息

  (3)线性模型,无法很好地划分数据

附件:

训练数据:traindata.txt

10009 1 0 0 1 0 1
10025 0 0 1 2 0 0
10038 1 0 0 1 1 0
10042 0 0 0 0 1 0
10049 0 0 1 0 0 0
10113 0 0 1 0 1 0
10131 0 0 1 2 1 0
10160 1 0 0 0 0 0
10164 0 0 1 0 1 0
10189 1 0 1 0 0 0
10215 0 0 1 0 1 0
10216 0 0 1 0 0 0
10235 0 0 1 0 1 0
10270 1 0 0 1 0 0
10282 1 0 0 0 1 0
10303 2 0 0 0 1 0
10346 1 0 0 2 1 0
10380 2 0 0 0 1 0
10429 2 0 1 0 0 0
10441 0 0 1 0 1 0
10443 0 0 1 2 0 0
10463 0 0 0 0 0 0
10475 0 0 1 0 1 0
10489 1 0 1 0 1 1
10518 0 0 1 2 1 0
10529 1 0 1 0 0 0
10545 0 0 1 0 0 0
10546 0 0 0 2 0 0
10575 1 0 0 0 1 0
10579 2 0 1 0 0 0
10581 2 0 1 1 1 0
10600 1 0 1 1 0 0
10627 1 0 1 2 0 0
10653 1 0 0 1 1 0
10664 0 0 0 0 1 0
10691 1 1 0 0 1 0
10692 1 0 1 2 1 0
10711 0 0 0 0 1 0
10714 0 0 1 0 0 0
10739 1 0 1 1 1 0
10750 1 0 1 0 1 0
10764 2 0 1 2 0 0
10770 0 0 1 2 1 0
10780 0 0 1 0 1 0
10784 2 0 1 0 1 0
10785 0 0 1 0 1 0
10788 1 0 0 0 0 0
10815 1 0 0 0 1 0
10816 0 0 0 0 1 0
10818 0 0 1 2 1 0
11095 0 1 1 0 0 0
11146 0 1 0 0 1 0
11206 2 1 0 0 0 0
11223 2 1 0 0 0 0
11236 1 1 0 2 0 0
11244 1 1 0 0 0 1
11245 0 1 0 0 0 0
11278 2 1 0 0 1 0
11322 0 1 0 0 1 0
11326 2 1 0 2 1 0
11329 2 1 0 2 1 0
11344 1 1 0 2 1 0
11358 0 1 0 0 0 1
11417 2 1 1 0 1 0
11421 2 1 0 1 1 0
11484 1 1 0 0 0 1
11499 2 1 0 0 0 0
11503 1 1 0 0 1 0
11527 1 1 0 0 0 0
11540 2 1 0 1 1 0
11580 1 1 0 0 1 0
11583 1 0 1 1 0 1
11592 2 1 0 1 1 0
11604 0 1 0 0 1 0
11625 1 0 1 0 0 0
20035 0 0 1 0 0 1
20053 1 0 0 0 0 0
20070 0 0 0 2 1 0
20074 1 0 1 2 0 1
20146 1 0 0 1 1 0
20149 2 0 1 2 1 0
20158 2 0 0 0 1 0
20185 1 0 0 1 1 0
20193 1 0 1 0 1 0
20194 0 0 1 0 0 0
20205 1 0 0 2 1 0
20206 2 0 1 1 1 0
20265 0 0 1 0 1 0
20311 0 0 0 0 1 0
20328 2 0 0 1 0 1
20353 0 0 1 0 0 0
20372 0 0 0 0 0 0
20405 1 0 1 1 1 1
20413 2 0 1 0 1 0
20427 0 0 0 0 0 0
20455 1 0 1 0 1 0
20462 0 0 0 0 1 0
20472 0 0 0 2 0 0
20485 0 0 0 0 0 0
20523 0 0 1 2 0 0
20539 0 0 1 0 1 0
20554 0 0 1 0 0 1
20565 0 0 0 2 1 0
20566 1 0 1 1 1 0
20567 1 0 0 1 1 0
20568 0 0 1 0 1 0
20569 1 0 0 0 0 0
20571 1 0 1 0 1 0
20581 2 0 0 0 1 0
20583 1 0 0 0 1 0
20585 2 0 0 1 1 0
20586 0 0 1 2 1 0
20591 1 0 1 2 0 0
20595 0 0 1 2 1 0
20597 1 0 0 0 0 0
20599 0 0 1 0 1 0
20607 0 0 0 1 1 0
20611 1 0 0 0 1 0
20612 2 0 0 1 1 0
20614 1 0 0 1 1 0
20615 1 0 1 0 0 0
21017 1 1 0 1 1 0
21058 2 1 0 0 1 0
21063 0 1 0 0 0 0
21084 1 1 0 1 0 1
21087 1 1 0 2 1 0
21098 0 1 0 0 0 0
21099 1 1 0 2 0 0
21113 0 1 0 0 1 0
21114 1 1 0 0 1 1
21116 1 1 0 2 1 0
21117 1 0 0 2 1 0
21138 2 1 1 1 1 0
21154 0 1 0 0 1 0
21165 0 1 0 0 1 0
21181 2 1 0 0 0 1
21183 1 1 0 2 1 0
21231 1 1 0 0 1 0
21234 1 1 1 0 0 0
21286 2 1 0 2 1 0
21352 2 1 1 1 0 0
21395 0 1 0 0 1 0
21417 1 1 0 2 1 0
21423 0 1 0 0 1 0
21426 1 1 0 1 1 0
21433 0 1 0 0 1 0
21435 0 1 0 0 0 0
21436 1 1 0 0 0 0
21439 1 1 0 2 1 0
21446 1 1 0 0 0 0
21448 0 1 1 2 0 0
21453 2 1 0 0 1 0
30042 2 0 1 0 0 1
30080 0 0 1 0 1 0
301003 1 0 1 0 0 0
301009 0 0 1 2 1 0
301017 0 0 1 0 0 0
30154 1 0 1 0 1 0
30176 0 0 1 0 1 0
30210 0 0 1 0 1 0
30239 1 0 1 0 1 0
30311 0 0 0 0 0 1
30382 0 0 1 2 1 0
30387 0 0 1 0 1 0
30415 0 0 1 0 1 0
30428 0 0 1 0 0 0
30479 0 0 1 0 0 1
30485 0 0 1 2 1 0
30493 2 0 1 2 1 0
30519 0 0 1 0 1 0
30532 0 0 1 0 1 0
30541 0 0 1 0 1 0
30567 1 0 0 0 0 0
30569 2 0 1 1 1 0
30578 0 0 1 0 0 1
30579 1 0 1 0 0 0
30596 1 0 1 1 1 0
30597 1 0 1 1 0 0
30618 0 0 1 0 0 0
30622 1 0 1 1 1 0
30627 1 0 1 2 0 0
30648 2 0 0 0 1 0
30655 0 0 1 0 0 1
30658 0 0 1 0 1 0
30667 0 0 1 0 1 0
30678 1 0 1 0 0 0
30701 0 0 1 0 0 0
30703 2 0 1 1 0 0
30710 0 0 1 2 0 0
30713 1 0 0 1 1 1
30716 0 0 0 0 1 0
30721 0 0 0 0 0 1
30723 0 0 1 0 1 0
30724 2 0 1 2 1 0
30733 1 0 0 1 0 0
30734 0 0 1 0 0 0
30736 2 0 0 1 1 1
30737 0 0 1 0 0 0
30740 0 0 1 0 1 0
30742 2 0 1 0 1 0
30743 0 0 1 0 1 0
30745 2 0 0 0 1 0
30754 1 0 1 0 1 0
30758 1 0 0 0 1 0
30764 0 0 1 0 0 1
30765 2 0 0 0 0 0
30769 2 0 0 1 1 0
30772 0 0 1 0 1 0
30774 0 0 0 0 1 0
30784 2 0 1 0 0 0
30786 1 0 1 0 1 0
30787 0 0 0 0 1 0
30789 1 0 1 0 1 0
30800 0 0 1 0 0 0
30801 1 0 1 0 1 0
30803 1 0 1 0 1 0
30806 1 0 1 0 1 0
30817 0 0 1 2 0 0
30819 2 0 1 0 1 1
30822 0 0 1 0 1 0
30823 0 0 1 2 1 0
30834 0 0 0 0 0 0
30836 0 0 1 0 1 0
30837 1 0 1 0 1 0
30840 0 0 1 0 1 0
30841 1 0 1 0 0 0
30844 0 0 1 0 1 0
30845 0 0 1 0 0 0
30847 1 0 1 0 0 0
30848 0 0 1 0 1 0
30850 0 0 1 0 1 0
30856 1 0 0 0 1 0
30858 0 0 1 0 0 0
30860 0 0 0 0 1 0
30862 1 0 1 1 1 0
30864 0 0 0 2 0 0
30867 0 0 1 0 1 0
30869 0 0 1 0 1 0
30887 0 0 1 0 1 0
30900 1 0 0 1 1 0
30913 2 0 0 0 1 0
30914 1 0 0 0 0 0
30922 2 0 0 2 1 0
30923 0 0 1 2 1 0
30927 1 0 1 0 0 1
30929 0 0 1 2 1 0
30933 0 0 1 2 1 0
30940 0 0 1 0 1 0
30943 1 0 1 2 1 0
30945 0 0 0 2 0 0
30951 1 0 0 0 0 0
30964 0 0 0 2 1 0
30969 0 0 1 0 1 0
30979 2 0 0 0 1 0
30980 1 0 0 0 0 0
30982 1 0 0 1 1 0
30990 1 0 1 1 1 0
30991 1 0 1 0 1 1
30999 0 0 1 0 1 0
31056 1 1 0 2 1 0
31068 1 1 0 1 0 0
31108 2 1 0 2 1 0
31168 1 1 1 0 0 0
31191 0 1 1 0 0 0
31229 0 1 1 0 0 1
31263 0 1 0 0 1 0
31281 1 1 1 0 0 0
31340 1 1 1 0 1 0
31375 0 1 0 0 1 0
31401 0 1 1 0 0 1
31480 1 1 1 1 1 0
31501 1 1 0 2 1 0
31514 0 1 0 2 0 0
31518 1 1 0 2 1 0
31532 0 0 1 2 1 0
31543 2 1 1 1 1 0
31588 0 1 0 0 1 0
31590 0 0 1 0 1 0
31591 2 1 0 1 1 0
31595 0 1 0 0 1 0
31596 1 1 0 0 0 0
31598 1 1 0 0 1 0
31599 0 1 0 0 0 0
31605 0 1 1 0 0 0
31612 2 1 0 0 1 0
31615 2 1 0 0 0 0
31628 1 1 0 0 1 0
31640 2 1 0 1 1 0

测试数据:testdata.txt

10009 1 0 0 1 0 1
10025 0 0 1 2 0 0
20035 0 0 1 0 0 1
20053 1 0 0 0 0 0
30627 1 0 1 2 0 0
30648 2 0 0 0 1 0

logistic回归 c++ 实现的更多相关文章

  1. 神经网络、logistic回归等分类算法简单实现

    最近在github上看到一个很有趣的项目,通过文本训练可以让计算机写出特定风格的文章,有人就专门写了一个小项目生成汪峰风格的歌词.看完后有一些自己的小想法,也想做一个玩儿一玩儿.用到的原理是深度学习里 ...

  2. 机器学习——Logistic回归

    1.基于Logistic回归和Sigmoid函数的分类 2.基于最优化方法的最佳回归系数确定 2.1 梯度上升法 参考:机器学习--梯度下降算法 2.2 训练算法:使用梯度上升找到最佳参数 Logis ...

  3. logistic回归

    logistic回归 回归就是对已知公式的未知参数进行估计.比如已知公式是$y = a*x + b$,未知参数是a和b,利用多真实的(x,y)训练数据对a和b的取值去自动估计.估计的方法是在给定训练样 ...

  4. Logistic回归 python实现

    Logistic回归 算法优缺点: 1.计算代价不高,易于理解和实现2.容易欠拟合,分类精度可能不高3.适用数据类型:数值型和标称型 算法思想: 其实就我的理解来说,logistic回归实际上就是加了 ...

  5. Logistic回归的使用

    Logistic回归的使用和缺失值的处理 从疝气病预测病马的死亡率 数据集: UCI上的数据,368个样本,28个特征 测试方法: 交叉测试 实现细节: 1.数据中因为存在缺失值所以要进行预处理,这点 ...

  6. 如何在R语言中使用Logistic回归模型

    在日常学习或工作中经常会使用线性回归模型对某一事物进行预测,例如预测房价.身高.GDP.学生成绩等,发现这些被预测的变量都属于连续型变量.然而有些情况下,被预测变量可能是二元变量,即成功或失败.流失或 ...

  7. SPSS数据分析—配对Logistic回归模型

    Lofistic回归模型也可以用于配对资料,但是其分析方法和操作方法均与之前介绍的不同,具体表现 在以下几个方面1.每个配对组共有同一个回归参数,也就是说协变量在不同配对组中的作用相同2.常数项随着配 ...

  8. SPSS数据分析—多分类Logistic回归模型

    前面我们说过二分类Logistic回归模型,但分类变量并不只是二分类一种,还有多分类,本次我们介绍当因变量为多分类时的Logistic回归模型. 多分类Logistic回归模型又分为有序多分类Logi ...

  9. SPSS数据分析—二分类Logistic回归模型

    对于分类变量,我们知道通常使用卡方检验,但卡方检验仅能分析因素的作用,无法继续分析其作用大小和方向,并且当因素水平过多时,单元格被划分的越来越细,频数有可能为0,导致结果不准确,最重要的是卡方检验不能 ...

  10. Logistic回归分类算法原理分析与代码实现

    前言 本文将介绍机器学习分类算法中的Logistic回归分类算法并给出伪代码,Python代码实现. (说明:从本文开始,将接触到最优化算法相关的学习.旨在将这些最优化的算法用于训练出一个非线性的函数 ...

随机推荐

  1. 在vc正在使用xtremetoolkit接口库-----使用简单的控制

    首先,我们需要在StdAfx.h增加头文件: #include "XTToolkitPro.h" #include "XTPResource.h" 在test. ...

  2. SQL于DML(数据库操作语言)采用

    1.Insert语句: INSERT [INTO] table [(column1, column2, column3, . . .)] VALUES(value1, value2, value3, ...

  3. java web.xml listener servlet 和filter加载顺序

    在该项目中总会遇到一些关于加载的优先问题.最近遇到了同样的类别似的,所以,如果你发现信息汇总下,以下是转载其他一些人,毕竟,人们写的不错.它不重复创建的轮.只是略作修改自己的观点. 首先能够肯定的是, ...

  4. Access to the temp directory is denied. Identity 'NT AUTHORITY\NETWORK SERVICE' under which XmlSerializer is running does not have sufficient permiss

    造成错误的原因是用bat代码清理系统垃圾时造成的权限丢失而引起的 错误描述 1.An error occurred creating the configuration section handler ...

  5. tcp接收队列

    对于接收数据包,内核维护如下几个队列:1>全局 backlog 队列,驱动程序调用 netif_rx 将接收到的数据包缓存于该队列中.2>sock 结构中 back_log 队列,网络层在 ...

  6. 如何完成Nexus 9上电后激活过程

    所述被激活,因为它是Nexus 9经过努力获得启动OTA最新更新包,而且因为Google关闭一堵墙.原因无法下载更新包. 因为是第一次开机,它不能被设置usb debugging, 无法adb去杀死w ...

  7. 网站静态化处理—web前端优化—中(12)

    网站静态化处理—web前端优化—中(12) Web前端很多优化原则都是从如何提升网络通讯效率的角度提出的,但是这些原则使用的时候还是有很多陷阱在里面,如果我们不能深入理解这些优化原则背后所隐藏的技术原 ...

  8. Java Persistence with MyBatis 3(中国版) 第五章 与Spring集成

    MyBatis-Spring它是MyBatis子模块框.它用来提供流行的依赖注入框架Spring无缝集成. Spring框架是一个基于依赖注入(Dependency Injection)和面向切面编程 ...

  9. Android超炫日期日历控件:TimesSquare

    先看效果图: 使用说明: 在布局文件里: <com.squareup.timessquare.CalendarPickerView android:id="@+id/calendar_ ...

  10. 第5章1节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 官方简介(原创)

    天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文“寻求合作伙伴编写<深入理解 MonkeyRunner>书籍“.但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在 ...