機器學習基石(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