大家好,我是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