1. 程式人生 > >機器學習基石(Machine Learning Foundations) 機器學習基石 作業三 Q18-20 C++實現

機器學習基石(Machine Learning Foundations) 機器學習基石 作業三 Q18-20 C++實現

        大家好,我是Mac Jiang,今天和大家分享Coursera-NTU-機器學習基石(Machine Learning Foundations)-作業三 Q18-20的C++實現。雖然有很多大神已經在很多部落格中給出了Phython的實現,但是給出C++實現的文章明顯較少,這裡為大家提供一條C++實現的思路!我的程式碼雖然能夠得到正確答案,但是其中可能有某些思想或者細節是錯誤的,如果各位博友發現,請及時留言糾正,謝謝!再次宣告,博主提供實現程式碼的原因不是為了讓各位通過測試,而是為學習有困難的同學提供一條解決思路,希望我的文章對您的學習有一些幫助!

        這部分內容講的主要是利用梯度下降發實現邏輯迴歸。

(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