NTU-Coursera机器学习:HomeWork 1 Q15-20

Question15

训练数据格式如下:

输入有4个维度,输出为{-1,+1}。共有400条数据。

题目要求将权向量元素初始化为0,然后使用“Naive Cycle”遍历训练集,求停止迭代时共对权向量更新了几次。

所谓“Naive Cycle”指的是在某数据条目x(i)上发现错误并更新权向量后,下次从x(i+1)继续读数据,而不是回到第一条数据x(0)从头开始。

#include <fstream>
#include <iostream>
#include <vector>

using namespace std;

#define DEMENSION 5                //数据维度

double weights[DEMENSION];         //权重向量
int step= 0;                       //迭代次数
int length = 0;                    //数据条目个数
int index = 0;                     //当前数据条目索引
bool isFinished = false;           //迭代终止状态
char *file = "training_data.txt";

struct record {
    double input[DEMENSION];   //输入
    int output;                //输出
};
vector<record> trainingSet;        //训练数据

int sign(double x)
{
    if(x<0)       return -1;
    else if(x>0) return 1;
    else         return -1;
}

//两个向量相加,更新第一个向量
void add(double *v1, double *v2, int demension)
{
    for(int i=0;i<demension;++i){ v1[i] = v1[i] + v2[i]; }
}

//两个向量相乘,返回内积
double multiply(double *v1, double *v2, int demension)
{
    double innerProd = 0.0;
    for(int i=0;i<demension;++i){
        innerProd += v1[i] * v2[i];
    }
    return innerProd;
}

//向量与实数相乘,结果通过*result返回,不改变参与计算的向量
void multiply(double *result, double *v, double num, int demension)
{
    for(int i=0;i<demension;++i){ result[i] = v[i] * num; }
}

//读取所有训练数据
void getData(ifstream & dataFile)
{
    while(!dataFile.eof()){
        record curRecord;
        curRecord.input[0] = 1;
        for(int i=1;i<DEMENSION;++i){ dataFile>>curRecord.input[i]; }
        dataFile>>curRecord.output;
        trainingSet.push_back(curRecord);
    }
    dataFile.close();
    length = trainingSet.size();
}

void PLA()
{
    int start = index;
    double curInput[DEMENSION];

        //找到下一个错误记录的index
    while( trainingSet[index].output ==
    sign(multiply(weights,trainingSet[index].input,DEMENSION)) )
    {
        if(index==length-1) {index = 0;}
        else            {index++;}
        if(index==start)    {isFinished = true; break;}  //没发现错误,迭代结束
    }

    if(isFinished){
        cout<<"计算结果:step = "<<step<<", weights = \n";
        for(int i=0;i<DEMENSION;++i){ cout<<weights[i]<<endl; }
        return;
    }else{
        step++;

                //更新: weights = weights + curOutput * curInput
        multiply( curInput, trainingSet[index].input,
            trainingSet[index].output, DEMENSION );
        add( weights, curInput, DEMENSION );   

        if(index==length-1) {index = 0;}
        else            {index++;}

        PLA();
    }
    return;
}

void main()
{
    ifstream dataFile(file);

    if(dataFile.is_open()){
        getData(dataFile);
    }else{
        cerr<<"ERROR ---> 文件打开失败"<<endl;
        exit(1);
    }

    for(int i=0;i<DEMENSION;++i){ weights[i] = 0.0; }

    PLA();
}

测试结果如下所示:

多次运行程序,迭代次数均为45次。

Question16

该题要求使用“fixed,pre-determined random cycle”对数据进行遍历,即对400条数据进行随机排序,然后在这轮计算中始终使用这一排序,直到下一轮计算开始再重新排序,重复2000次,求对权向量的平均修正次数。

#include <fstream>
#include <iostream>
#include <vector>
#include <ctime>

using namespace std;

#define DEMENSION 5                //数据维度

int step= 0;                       //迭代次数
int index = 0;                     //当前数据条目索引
bool isFinished = false;           //迭代终止状态
char *file = "training_data.txt";

struct record {
    double input[DEMENSION];   //输入
    int output;                //输出
};

int sign(double x)
{
    //同Q15
}

void add(double *v1, double *v2, int demension)
{
    //同Q15
}

//两个向量相乘,返回内积
double multiply(double *v1, double *v2, int demension)
{
    //同Q15
}

//向量与实数相乘,结果通过*result返回,不改变参与计算的向量
void multiply(double *result, double *v, double num, int demension)
{
    //同Q15
}

//对 traininig set 创建一个随机排序
void setRandomOrder(vector<record> &trainingSet, vector<int> &randIndexes)
{
    srand((unsigned)time(NULL));
    int length = trainingSet.size();
    vector<bool> assignedIndexes(length,false);    

    for(int i=0;i<length;++i){
        int randIndex = rand()%length;
        while(assignedIndexes[randIndex]==true){
            randIndex = rand()%length;
        }
        randIndexes.push_back(randIndex);
        assignedIndexes[randIndex] = true;
    }
}

//读取所有训练数据
void getData(ifstream & dataFile, vector<record> &trainingSet)
{
    while(!dataFile.eof()){
        record curRecord;
        curRecord.input[0] = 1;
        for(int i=1;i<DEMENSION;++i){ dataFile>>curRecord.input[i]; }
        dataFile>>curRecord.output;
        trainingSet.push_back(curRecord);
    }
    dataFile.close();
}

void PLA(vector<record> &trainingSet, vector<int> &randIndexes, double *weights)
{
    int length = trainingSet.size();
    int start = index;
    double curInput[DEMENSION];

        //找到下一个错误记录的index
    while( trainingSet[randIndexes[index]].output ==
        sign(multiply(weights,trainingSet[randIndexes[index]].input,DEMENSION)) ){
        if(index==length-1) {index = 0;}
        else                {index++;}
        if(index==start)    {isFinished = true; break;}  //没发现错误,迭代结束
    }

    if(isFinished){
        return;
    }else{
        step++;

                //更新: weights = weights + curOutput * curInput
        multiply( curInput, trainingSet[randIndexes[index]].input,
            trainingSet[randIndexes[index]].output, DEMENSION );
        add( weights, curInput, DEMENSION );   

        if(index==length-1) {index = 0;}
        else                {index++;}

        PLA(trainingSet, randIndexes, weights);
    }
    return;
}

void main()
{
    int totalSteps = 0;

    for(int i=0;i<2000;++i){

        double weights[DEMENSION];      //权重向量
        vector<record> trainingSet;     //训练数据
        vector<int> randIndexes;        //访问数据的随机索引列表
        ifstream dataFile(file);

        step = 0;
        index = 0;
        isFinished = false;   

        if(dataFile.is_open()){
            getData(dataFile,trainingSet);
            setRandomOrder(trainingSet,randIndexes);
        }else{
            cerr<<"ERROR ---> 文件打开失败"<<endl;
            exit(1);
        }

        for(int i=0;i<DEMENSION;++i){ weights[i] = 0.0; }

        PLA(trainingSet, randIndexes, weights);
        totalSteps += step;
    }

    cout<<"Average Step = "<<totalSteps/2000<<endl;
}

输出结果:

Question17

本题要求在更新权向量时乘以一个0.5的系数,代码变动很少。

测试结果:

Question18

第18题要求在第16题 Random PLA 算法的基础上使用 Pocket 算法对数据做二元划分。Pocket算法在博文NTU-Coursera机器学习:機器學習問題与二元分類介绍过,通常用来处理有杂质的数据集,在每一次更新 Weights(权向量)之后,把当前犯错最少的Weights放在pocket中,直至达到指定迭代次数(50次),pocket中的Weights即为所求。然后用测试数据验证W(pocket)的错误率,进行2000次计算取平均。

#include <fstream>
#include <iostream>
#include <vector>
#include <ctime>

using namespace std;

#define DEMENSION 5                    //数据维度

int index = 0;                         //当前数据条目索引
int step = 0;                          //当前权向量更新次数
char *file = "training_data.txt";
char *file_test = "test_data.txt";

struct record {
    double input[DEMENSION];       //输入
    int output;                    //输出
};

int sign(double x)
{
    //同Q16
}

//两个向量相加,更新第一个向量
void add(double *v1, double *v2, int demension)
{
    //同Q16
}

//两个向量相乘,返回内积
double multiply(double *v1, double *v2, int demension)
{
    //同Q16
}

//向量与实数相乘,结果通过*result返回,不改变参与计算的向量
void multiply(double *result, double *v, double num, int demension)
{
    //同Q16
}

//对 traininig set 创建一个随机排序
void setRandomOrder(vector<record> &trainingSet, vector<int> &randIndexes)
{
    //同Q16
}

//读取数据
void getData(ifstream & dataFile, vector<record> &data)
{
    //同Q16
}

//错误统计及Pocket向量更新
void errCountAndPocketUpdate(vector<record> &trainingSet, vector<int> &randIndexes,
        double *weights, double *pocketWeights, double &trainingErrRate, int dataLength)
{
    int errCount = 0;
    double curTrainingErrRate = 1.0;

    for(int i=0;i<dataLength;++i){
        if(trainingSet[randIndexes[i]].output
            != sign(multiply(weights,trainingSet[randIndexes[i]].input,DEMENSION))){
            errCount++;
        }
    }

    curTrainingErrRate = double(errCount)/double(dataLength);

    if(curTrainingErrRate < trainingErrRate){
        trainingErrRate = curTrainingErrRate;
        for(int j=0; j<DEMENSION; ++j){
            pocketWeights[j] = weights[j];
        }
    }
}

void Pocket(vector<record> &trainingSet, vector<int> &randIndexes, double *weights,
        double *pocketWeights, double &trainingErrRate)
{
    int length = trainingSet.size();
    double curInput[DEMENSION];   

    errCountAndPocketUpdate(trainingSet, randIndexes, weights,
        pocketWeights, trainingErrRate, length);

        //找到下一个错误记录的index
    while( trainingSet[randIndexes[index]].output ==
        sign(multiply(weights,trainingSet[randIndexes[index]].input,DEMENSION)) ){
        if(index==length-1) {index = 0;}
        else                {index++;}
    }

    if(step<50){
        step++;

        //更新: weights = weights + curOutput * curInput
        multiply( curInput, trainingSet[randIndexes[index]].input,
            trainingSet[randIndexes[index]].output, DEMENSION );
        add( weights, curInput, DEMENSION );   

        if(index==length-1) {index = 0;}
        else            {index++;}

        Pocket(trainingSet, randIndexes, weights, pocketWeights, trainingErrRate);
    }else{
        return;
    }
}

//统计 W(pocket) 在测试数据集上的错误率
double getTestErrRate(vector<record> &testSet, double *pocketWeights, int dataLength)
{
    int errCount = 0;

    for(int i=0;i<dataLength;++i){
        if(testSet[i].output !=
            sign(multiply(pocketWeights,testSet[i].input,DEMENSION))){
            errCount++;
        }
    }

    return double(errCount)/double(dataLength);
}

void main()
{
    double totalTestErrRate = 1.0;

    for(int i=0;i<2000;++i){

        double weights[DEMENSION];              //当前权重向量
        double pocketWeights[DEMENSION];        //当前最优权重向量
        vector<record> trainingSet;             //训练数据
        vector<record> testSet;                 //测试数据
        vector<int> randIndexes;                //访问数据的随机索引列表
        ifstream dataFile(file);
        ifstream testDataFile(file_test);
        double trainingErrRate = 1.0;           //训练集中的错误率[0.0, 1.0]
        double testErrRate = 1.0;               //测试集中的错误率[0.0, 1.0]

        step = 0;
        index = 0;                

        if( dataFile.is_open() && testDataFile.is_open() ){
            getData(dataFile,trainingSet);
            getData(testDataFile,testSet);
            setRandomOrder(trainingSet,randIndexes);
        }else{
            cerr<<"ERROR ---> 文件打开失败"<<endl;
            exit(1);
        }

        for(int j=0;j<DEMENSION;++j){
            weights[j] = 0.0;
            pocketWeights[j] = 0.0;
        }

        Pocket(trainingSet, randIndexes, weights, pocketWeights, trainingErrRate);

        testErrRate = getTestErrRate(testSet,pocketWeights,testSet.size());
        totalTestErrRate += testErrRate;

        cout<<"\n  ********************   第 "<<i+1
        <<" 次计算结束   *************************  \n"<<endl;
    }

    cout<<"Average Perportion = "<<totalTestErrRate/2000<<endl;
}

测试结果:

Question19

题19要求用经过50次更新的W(50)进行验证,而不是W(pocket),由于W(50)未必是当下最优,所以平均错误率一定会升高。代码几乎没有改动,只需在调用 getTestErrRate 函数是传入W(50)的指针即可。

测试结果:

Question20

本题要求把 Weights 的更新次数从50增加到100,可以预计平均错误率是降低的。

测试结果:

关于Machine Learning更多讨论与交流,敬请关注本博客和新浪微博songzi_tea.

时间: 2024-10-04 08:58:57

NTU-Coursera机器学习:HomeWork 1 Q15-20的相关文章

Coursera机器学习-第六周-Advice for Applying Machine Learning

Evaluating a Learning Algorithm Desciding What to Try Next 先来看一个有正则的线性回归例子: 当在预测时,有很大的误差,该如何处理? 1.得到更多的训练样本 2.选取少量的特征 3.得到更多的特征项 4.加入特征多项式 5.减少正则项系数λ 6.增加正则项系数λ 很多人,在遇到预测结果并不理想的时候,会凭着感觉在上面的6个方案中选取一个进行,但是往往花费了大量时间却得不到改进. 于是引入了机器学习诊断,在后面会详细阐述, Evaluati

coursera机器学习

上周出差回来,开始找了一篇论文看<ScSPM>,这里有源代码,自己希望能认真看懂:毕竟这篇文章包含了dense sift特征提取+Spare coding+linear svm知识很全面,希望能看懂代码.这个过程却发现自己缺少了很多东西,他自己的sift提取方法,Sc,svm都是自己实现的:感觉看懂好难.然后周六开始实验室有“学术交流”,师兄师姐交流他们整个小论文的过程,针对梯度下降这些基本的方法,我们都没有认真的理解.发现图像和机器学习自己都没有认真的系统的学习:自己在博客上零零散散的看了很

吴恩达Coursera机器学习

涉及 Logistic 回归.正则化. 六.逻辑回归(Logistic Regression) 6.1 分类问题 6.2 假说表示 6.3 判定边界 6.4 代价函数 6.5 简化的成本函数和梯度下降 6.6 高级优化 6.7 多类别分类:一对多 七.正则化(Regularization) 7.1 过拟合的问题 7.2 代价函数 7.3 正则化线性回归 7.4 正则化的逻辑回归模型 六.逻辑回归(Logistic Regression) 6.1 分类问题 参考文档: 6 - 1 - Classi

Coursera 机器学习笔记(八)

主要为第十周内容:大规模机器学习.案例.总结 (一)随机梯度下降法 如果有一个大规模的训练集,普通的批量梯度下降法需要计算整个训练集的误差的平方和,如果学习方法需要迭代20次,这已经是非常大的计算代价. 首先,需要确定大规模的训练集是否有必要.当我们确实需要一个大规模的训练集,可以尝试用随机梯度下降法来替代批量梯度下降法. 在随机梯度下降法中,定义代价函数一个单一训练实例的代价: 随机梯度下降算法如下: 随机梯度下降算法在每一次计算之后便更新参数Θ,而不需要首先将所有的训练集求和,在梯度下降算法

coursera 机器学习课程 GraphLab环境准备

在网上看到coursera有机器学习的课程,正好再学习学习,温固一下,还有很多其他的课程也很好.收费的哟! 手机APP和网站收取的费用有差异,网站上要便宜一下,费用差的挺多的,果断在网站上支付了. 有兴趣的同学可以学一下,推荐,老师也是国外的老师,课程还是蛮精良的,不用担心听不懂,有中文字幕. 这里是coursera的网址: https://www.coursera.org/ 学习的环境用的是GraphLab,所以这里用将环境的搭建简单介绍一下: 学习的语言主要是使用python,GraphLa

Coursera机器学习-第三周-逻辑回归Logistic Regression

Classification and Representation 1. Classification Linear Regression (线性回归)考虑的是连续值([0,1]之间的数)的问题,而Logistic Regression(逻辑回归)考虑的是离散值(例如只能取0或1而不能取0到1之间的数)的问题.举个例子,你需要根据以往季度的电力数据,预测下一季度的电力数据,这个时候需要使用的是线性回归,因为这个值是连续的,而不是离散的.而当你需要判断这个人抽烟还是不抽烟的问题时,就需要使用逻辑回

Coursera机器学习week10 笔记

Large scale machine learning Learning with large datasets 如果我们有一个低方差的模型,增加数据集的规模可以帮助你获得更好的结果.我们应该怎样应对一个有 100 万条记录的训练集? 以线性回归模型为例,每一次梯度下降迭代,我们都需要计算训练集的误差的平方和,如果我们的学习算法需要有 20 次迭代,这便已经是非常大的计算代价. 首先应该做的事是去检查一个这么大规模的训练集是否真的必要,也许我们只用 1000 个训练集也能获得较好的效果,我们可

coursera机器学习公开课笔记15: anomaly-detection

Note This personal note is written after studying the opening course on the coursera website, Machine Learning by Andrew NG . And images, audios of this note all comes from the opening course. 01_density-estimation In this next set of videos, I'd lik

Coursera 机器学习 第5章 Neural Networks: Learning 学习笔记

5.1节 Cost Function神经网络的代价函数. 上图回顾神经网络中的一些概念: L  神经网络的总层数. sl  第l层的单元数量(不包括偏差单元). 2类分类问题:二元分类和多元分类. 上图展现的是神经网络的损失函数,注意这是正则化的形式. 正则化部分,i.j不为0.当然i.j可以为0,此时的损失函数不会有太大的差异,只是当i.j不为0的形式更为常见. 5.2节 Backpropagation Algorithm最小化损失函数的算法——反向传播算法:找到合适的参数是J(θ)最小. 如