大家好,我是Mac Jiang,今天和大家分享Coursera-NTU-機器學習基石(Machine Learning Foundations)-作业三 Q6-10的C++实现。虽然有很多大神已经在很多博客中给出了Phython的实现,但是给出C++实现的文章明显较少,这里为大家提供一条C++实现的思路!我的代码虽然能够得到正确答案,但是其中可能有某些思想或者细节是错误的,如果各位博友发现,请及时留言纠正,谢谢!再次声明,博主提供实现代码的原因不是为了让各位通过测试,而是为学习有困难的同学提供一条解决思路,希望我的文章对您的学习有一些帮助!
本文出处:http://blog.csdn.net/a1015553840/article/details/51084922
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实现本题,操作会简单的多。
对于eigen的用法有很多前人已经写过,比较好的链接有:http://blog.csdn.net/augusdi/article/details/12907341.其实我们用到的只是其中的一些很基础的操作,最重要的是求逆矩阵和求广义逆矩阵的操作,大家不妨看看。
对于第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
本文出处:http://blog.csdn.net/a1015553840/article/details/51084922