大家好,我是Mac Jiang,今天和大家分享Coursera-NTU-機器學習基石(Machine Learning Foundations)-作业三 Q18-20的C++实现。虽然有很多大神已经在很多博客中给出了Phython的实现,但是给出C++实现的文章明显较少,这里为大家提供一条C++实现的思路!我的代码虽然能够得到正确答案,但是其中可能有某些思想或者细节是错误的,如果各位博友发现,请及时留言纠正,谢谢!再次声明,博主提供实现代码的原因不是为了让各位通过测试,而是为学习有困难的同学提供一条解决思路,希望我的文章对您的学习有一些帮助!
本文出处:http://blog.csdn.net/a1015553840/article/details/51085835
其他问题解答请看汇总帖:http://blog.csdn.net/a1015553840/article/details/51085129
这部分内容讲的主要是利用梯度下降发实现逻辑回归。
(1)逻辑回归
逻辑回归Hyphothesis:
逻辑回归单个点错误:
逻辑回归总代价:
(2)梯度下降法
逻辑回归的batch gradient descent:
逻辑回归的stostic gradient descent:不需要N个样本求平均了,直接取其中一个的梯度
(3)逻辑回归的实现步骤
1.第十八题
(1)题意:分别从两个网站中下载训练样本和测试样本用于做逻辑回归。取迭代步长ita = 0.001,迭代次数T=2000,求Eout
(2)实现:注意,这里实现总w的初始化最好全为0,因为最优解的X就在0附近。由于T=2000只迭代了2000次太少了,如果初始化别的数如全为1,那么2000次迭代后是找不到最优解的,至少要迭代的几万次!题目中没有给出默认初始化w值,但是我试出来是初始化全为0。 这也说明逻辑回归迭代几千次都不小数目,要得到比较好的解需要几万次,或者采用高级优化的梯度下降法。
#include "stdafx.h"
#include<iostream>
#include<fstream>
#include<vector>
using namespace std;
#define DEMENSION 20//数据维度
struct Record{ //数据格式
double x[DEMENSION+1];
int y;
};
struct Weight{ //参数格式
double w[DEMENSION+1];
};
int sign(double x){ //sign
if(x > 0)return 1;
else return -1;
}
void getData(fstream &datafile,vector<Record> &data){ //读取数据
while(!datafile.eof()){
Record temp;
temp.x[0] = 1;
for(int i = 1; i <= DEMENSION; i++)
datafile>>temp.x[i];
datafile>>temp.y;
data.push_back(temp);
}
datafile.close();
}
double sigmoid(double x){ //sigmoid函数,逻辑函数,s形函数
return 1.0 / (1.0 + exp(-x));
}
double vectorMul(double *a,double *b,int demension){ //两个向量相乘返回内积
double temp = 0.0;
for(int i = 0; i <demension; i++)
temp += a[i] * b[i];
return temp;
}
void calcuBatchGradient(vector<Record> &data,Weight weight,int N,double *grad){ //批量梯度下降法
for(int i = 0; i < N; i++){
double temp = sigmoid(-1 * vectorMul(weight.w,data[i].x,DEMENSION+1) * (double)data[i].y);
for(int j = 0; j <= DEMENSION; j++)
grad[j] += -1.0 * temp * data[i].x[j] * data[i].y;
}
for(int i = 0; i <= DEMENSION; i++)
grad[i] = grad[i] / N;
}
void calcuStochasticGradient(Record data,Weight weight,double *grad){ //随机梯度下降法
double temp = sigmoid(-1 * vectorMul(weight.w,data.x,DEMENSION+1) * (double)data.y);
for(int j = 0; j <= DEMENSION; j++)
grad[j] += -1.0 * temp * data.x[j] * data.y;
}
void updateW(Weight &weight,double ita,double *grad){ //利用得到的梯度更新参数weight
for(int i = 0; i <= DEMENSION; i++){
weight.w[i] = weight.w[i] - (ita * grad[i]);
}
}
double calcuLGError(vector<Record> &data,Weight weight,int N){ //计算逻辑回归的错误计算方法计算错误
double error = 0.0;
for(int i = 0; i < N; i++){
error += log(1 + exp(-data[i].y * vectorMul(weight.w,data[i].x,DEMENSION+1)));
}
return double(error / N);
}
void logisticRegression(vector<Record> &data,Weight &weight,int N,double ita,int iteration){ //逻辑回归
for(int i = 0; i < iteration; i++){ //利用batch梯度下降法计算逻辑回归
double grad[DEMENSION+1] = {0.0};
calcuBatchGradient(data,weight,N,grad);
updateW(weight,ita,grad);
cout<<"iter = "<<i<<",训练样本的逻辑回归错误Ein = "<<calcuLGError(data,weight,N)<<endl;
}
/*int i = 0; //利用Stochastic梯度下降法计算逻辑回归
while(i < iteration){
double grad[DEMENSION+1] = {0.0};
calcuStochasticGradient(data[i%N],weight,grad);
updateW(weight,ita,grad);
cout<<"iter = "<<i<<",训练样本的逻辑回归错误Ein = "<<calcuLGError(data,weight,N)<<endl;
i++;
}*/
}
double calcuError(vector<Record> &data,Weight weight,int N){ //利用逻辑回归做二元分类,计算0/1错误
double error = 0.0;
for(int i = 0; i < N; i++){
if(sign(vectorMul(data[i].x,weight.w,DEMENSION+1)) != data[i].y)
error++;
}
return double(error / N);
}
void main(){
vector<Record> trainingData; //训练样本
vector<Record> testData; //测试样本
fstream file1("trainingData.txt");//读取训练样本数据
fstream file2("testData.txt");//读取测试样本数据
if(file1.is_open() && file2.is_open()){
getData(file1,trainingData);
getData(file2,testData);
}
else{
cout<<"can not open file!"<<endl;
exit(1);
}
int train_N = trainingData.size();//训练样本个数
int test_N = testData.size();//测试样本个数
double ita = 0.001;//步长ita
int interation = 2000;//迭代次数
Weight weight;//逻辑回归参数
for(int i = 0; i <= DEMENSION; i++)//参数初始化为0;注意,这里要是全为1迭代2000次是得不到结果的,因为最优解在0附近;要想得到结果iteration必须在几万次次左右
weight.w[i] = 1;
logisticRegression(trainingData,weight,train_N,ita,interation);
cout<<"训练样本的0/1错误Ein = "<<calcuError(trainingData,weight,train_N)<<endl;
cout<<"测试样本的0/1错误Eout = "<<calcuError(testData,weight,test_N)<<endl;
}
(3)答案:0.475
2.第十九题
(1)题意:把第18题的步长ita=0.001改为0.01,求Eout
(2)分析:这个更简单,只要把main函数里的ita改为0.01就可以了
(3)答案:迭代后Ein = 0.195 ,Eout = 0.22; 如果迭代20000次的话,Ein=0.172,Eout=0.182此时就基本达到局部最优了!
答案为0.220
3.第二十题
(1)题意:ita取0.001,迭代2000次,利用随机梯度下降法(Stostic Gradieng Descent),求迭代2000次后的Eout
(2)分析:我在18题给出的程序中已经写出了随机梯度下降的计算方法!第18题用的是batch gradient descent,只要在logisticRegression这个函数中,把上面的注释掉,把下面注释中的代码拿出来就是随机梯度下降法的逻辑回归实现了。
(3)答案:a.迭代2000次,Ein=0.466,Eout = 0.475,所以答案是0.475
b.如果迭代20000次,Ein=0.186,Eout=0.196,所以说2000次太少了!需要几万次才能达到最优解
对于这道题而言,由于T=2000,答案为0.475
本文出处:http://blog.csdn.net/a1015553840/article/details/51085835
其他问题解答请看汇总帖:http://blog.csdn.net/a1015553840/article/details/51085129