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

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

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

1第十三題

(1)題意:給定的target fuction的表示式如上圖所示,他是用一個圓圈做二元分類。我們的工作是在X=[-1,1]x[-1,1]上隨機產生1000個點,利用f(x1,x2)計算它的值,然後在基礎上新增10%的噪聲(二元分類的噪聲就是把10%的樣本的y值取相反數)。如果不做feacher transform 直接利用資料做線性迴歸,利用得到的引數做線性分類器,問此事得到的Ein是多少。執行1000次取平均值。

(2)分析:首先要隨機產生訓練樣本並新增噪聲,對於C++如何隨機產生隨機樣本和噪聲我們以前的程式碼中就已經說過了,這裡不再重複。

                    其次,我們要利用訓練樣本計算線性迴歸。

                    最後,我們用得到的線性迴歸引數w作為二元分類器的引數,計算sign(w*x)得到預測值,計算他與y的0/1錯誤,得到錯誤率EiN

                    提示:由於線性迴歸需要用到求pseudo-inverse的操作,這裡要求逆矩陣,這裡要是自己寫逆矩陣的演算法比較複雜,可以直接呼叫已經寫好的C++庫。比較常用的C++庫有eigen,cuda等等,本人用的是eigen。如果你嫌複雜,可以直接使用Matlab實現本題,操作會簡單的多。

                    對於第13題的程式碼和第14題的很像,只要把第14題的featureTransform操作去掉就可以了,所以這裡不再附上。

(3)答案:0.5

2.第十四題

(1)題意:在第13題,直接利用邏輯迴歸做分類是很不理想的,錯誤率為50%,沒有實際意義。但是我們可以先進行特徵轉換,正確率就會高很多。特徵轉化的操作也很簡單,上課老師都說過,這裡就不再累述。

(2)分析:具體的程式碼實現如下。其中,(Xtest,ytest)是第15題才需要進行的操作,為了簡潔都寫在一起了。

#include "stdafx.h"
#include<iostream>
#include<Eigen/Eigen>//C++下的一個常用的矩陣運算庫
using namespace Eigen;
using namespace std;

#define X1min  -1 //定義第一維度的最大最小值
#define X1max  1
#define X2min  -1//定義第二維度的最大最小值
#define X2max 1
#define N 1000//定義樣本數


int sign(double x){

	if(x <= 0)return -1;
	else return 1;
}

void getRandData(Matrix<double,N,3> &X,Matrix<double,N,1> &y){ //在(X1min,X1max)x(X2min,X2max)區間初始化點
	for(int i = 0; i < N; i++){
		X(i,0) = 1.0;
		X(i,1) = double(X1max - X1min) * rand()/RAND_MAX - (X1max - X1min)/2.0;
		X(i,2) = double(X2max - X2min) * rand()/RAND_MAX - (X2max - X2min)/2.0;
		y(i,0) = sign(X(i,1)*X(i,1) + X(i,2)*X(i,2) - 0.6);
	}
}

void getNoise(Matrix<double,N,3> &X,Matrix<double,N,1> &y){//加入噪聲
	for(int i =0; i < N; i++)
		if(rand()/RAND_MAX < 0.1)
			y(i,0) = - y(i,0);
}

void transform(Matrix<double,N,3> &X,Matrix<double,N,6> &Z){//將X空間轉換為Z空間
	for(int i = 0; i < N; i++){
		Z(i,0) = 1;
		Z(i,1) = X(i,1);
		Z(i,2) = X(i,2);
		Z(i,3) = X(i,1) * X(i,2);
		Z(i,4) = X(i,1) * X(i,1);
		Z(i,5) = X(i,2) * X(i,2);
	}
}

void linearRegression(Matrix<double,N,6> &Z,Matrix<double,N,1> &y,Matrix<double,6,1> &weight){//邏輯迴歸計算,得引數weight
	weight = (Z.transpose() *Z).inverse() * Z.transpose() * y;
}

double calcuE(Matrix<double,N,6> &Z,Matrix<double,N,1> &y,Matrix<double,6,1> &weight){//計算E_in
	double E_in = 0.0;
	Matrix<double,N,1> temp = Z * weight;
	for(int i = 0; i < N; i++)
		if((double)sign(temp(i,0)) != y(i,0))
			E_in++;
	return double(E_in/N);
}


void main(){
	int seed[1000];//種子
	double total_Ein = 0.0;
	double total_Eout = 0.0;
	Matrix<double,N,3> X;//X組成的矩陣
	Matrix<double,N,3> Xtest;//測試樣本
	Matrix<double,N,6> Z;//Z組成矩陣
	Matrix<double,N,6> Ztest;//測試樣本
	Matrix<double,N,1> y;//y組成的向量
	Matrix<double,N,1> ytest;//測試樣本
	Matrix<double,6,1> weight;//引數weight
	Matrix<double,6,1> totalWeight;
	totalWeight<<0,0,0,0,0,0;

	for(int i = 0; i < N; i++)//進行1000次,每次需要1個種子,所以先利用rand初始化種子
		seed[i] = rand();
	for(int k =0; k < N; k++){
		srand(seed[k]);//每次取一個種子進行試驗
		getRandData(X,y);//得到隨機樣本
		getNoise(X,y);//新增噪聲
		getRandData(Xtest,ytest);
		getNoise(Xtest,ytest);
		transform(X,Z);
		transform(Xtest,Ztest);
		linearRegression(Z,y,weight);//線性迴歸計算引數weight
		total_Ein += calcuE(Z,y,weight);//計算每次E_in錯誤和
		total_Eout += calcuE(Ztest,ytest,weight);
		totalWeight += weight;
		cout<<"k="<<k<<",Ein = "<<calcuE(Z,y,weight)<<",Eout = "<<calcuE(Ztest,ytest,weight)<<endl;
	}
	cout<<"Average E_in:"<<total_Ein / 1000.0<<endl;
	cout<<"Average E_out:"<<total_Eout / 1000.0<<endl;
	cout<<totalWeight/1000;
	

}

(3)答案:最後一項。其實用腦子想想就知道是最後一個,應為f(x1,x2)=sign(x1^2+x2^2-0.6)是一個圓,那麼得到的肯定也差不多是個圓。加上噪聲可以與原來的圓稍微偏離一些,但不會太過分。

15.第十五題


(1)題意:在14題得到的最優w的基礎上,我們利用產生訓練樣本的方法一樣產生1000個測試樣本,計算誤差。重複1000次求平均

(2)實現:已經在14題給出

Average E_in:訓練樣本平均誤差

Average E_out:測試樣本平均誤差

最下面的六行資料是第14題的w的六個引數

(3)答案:0.1