美文网首页我爱编程
深度学习开篇-感知机(perceptron)原理和实现

深度学习开篇-感知机(perceptron)原理和实现

作者: 孙小五哥哥 | 来源:发表于2017-12-03 21:54 被阅读0次

    近几年科技领域很流行大数据、机器学习、深度学习之类的字眼,其实,这些东西原理很早的时候就有了,只不过当时不叫这些名词,等我们慢慢学习就会发现,这些玩意儿结合了高数、概率统计学、矩阵论、线性代数等,然后以代码实现来解决实际问题。深度学习可以认为被包含在机器学习之中。至于目前他们的应用和研究领域,包括图像识别、NLP(自然语言处理)等等,大家自行查阅。废话不多说,现在我从一个最简单的模型向大家展示这类玩意儿怎么玩起来

    1.感知机

    为了了解什么是感知机,我们需要先理解一下几点:

    第一点:感知机是二类分类的线性分类模型(为了新手理解,还是多说两句,什么叫做二类分类,从程序的角度,感知机的输入是某个事物,而它的输出只有两种状态,从现实的角度,我们把具体的人这个事物输入给感知机要她判别性别,最后感知机只能输出两种结果:“男人”或“女人”,不会有第三种~~,如果我们需要有第三种结果,则这个问题不能用感知机来解决(有其他办法)。至于什么叫做线性,接下来会讲到)

    第二点:把某个事物输入到感知机模型的理解。(太抽象了吧,映射到程序是怎样的啊)这种输入其实有很多种形式,我举个最简单的例子帮助读者理解。对于某个特定的人,比如我,身高2.0米,体重150,帅气程度100分(满分100) 可爱程度0(满分100),数据提取出来 (2.0 150 100 0) 其中的每一个标识都称之为一个特征点,然后我们就定义一个向量 a = (2.0,150,100,0)来表示“我”这个人,所以输入就是向量a(特征向量)。(ps:这只是最基础的一种形式,不局限于此)

    第三点:“学习”的简单理解。机器学习、深度学习中都有学习二字,此二字代表什么意思?直观理解,就是我们从已知信息中学习其中的规律规则,从而预测未知。还是以上述例子阐述。现在我们碰到这样的一个问题:我有1000条人的信息,现在我在路上看到一个背影,目测身高165,体重90、帅气程度30,可爱程度80,想知道他(她)是男是女。这种情况就可以考虑采用感知机处理类似问题。(埋下铺垫,实际中的应用场景很有限)。我们提取1000个人信息中的五个特征点(身高、体重、帅气程度、可爱程度,性别)转化成1000个特征向量,1000个标记向量(只有性别,男用+1表示,女用-1表示,对应于特征向量)作为感知机的输入进行训练,让其学习其中的关联,得到一个训练后的模型,当输入向量(1.65,90,30,80)后,感知机就可以根据训练好的模型预测出最后的结果(+1 或 -1)

    第四点:我们从数学的角度深入推敲这个问题的实际转化(其实也就是一个数学建模的过程)

    可以理解如下

    人               特征向量                        标记                                                      序号

    我           (2.0,150,100,0)              +1(男)           已知                                1

    夏目     (1.78,135,80,20)         +1 ( 男)           已知                                2

    鲁路修  (1.80,140,99,1)          +1(男)          已知                                3

    魔女cc  (1.63,88,10,99)           -1 (女)         已知                                4

    ....                                                        ...                      ...                                   ...

    路人      (1.65,90,20,90)             ?(要求解的量)   

    那么,建模开始

    第五点:"学习"的数学表达式直观理解(以下以感知机为例,其实很多类型神经网络都类似)

    通过上述建模,现在我们的问题变成了如何调整系数W(w0,w1,w2.... )和b,(注意,此处的系数W是向量),使得模型f(X) = sign(W*X+b)具有更好的表达能力。此处好的表达能力指的什么?还是举上述列子,对于已知的1000个特征向量,输入模型f计算后得到的结果尽可能和对应标签一致(其实也就是一个拟合过程),然后我们再输入表示刚才路人的特征向量,经过f计算的结果就是对于此路人的性别预测结果。

    那么,现在问题变成了如何调整系数W和b,使得f(X)学习到的表达能力最好。这里扯起来又可以写几篇文章,我们以后一步步展开,目前我只做陈述,有兴趣的同学可以查看我的csdn博客(代价函数 )。目前咱们暂且要记住,如何调整系数W和b,使得f(X)学习到的表达能力最好问题,可以转换为调整模型参数W,b,使得代价函数最小。代价函数有许多定义形式,此处我们依照李航《统计学习方法》P26 P27(大家有兴趣的可以看看,其中有cost代价函数的推导过程。此处我直接给出

    其中M表示误分类点的集合 ,xi表示第i个样本(第i个已知信息的特征向量),yi对应+1/-1

    第六点:“学习算法”

    怎样使得代价函数最小(其实变成了一个纯高数问题),对于L(W,b),xi和yi已知,W和b是变量,L是一个多元方程,求W和b,使得L为最小值,采用梯度下降算法(ps:高数中有一个梯度的概念,大家需要回顾,可以参考机器学习-梯度下降法实现线性回归

    最后的步骤如下:

    上述训练集中xi可以是多维的,即训练集T = {(x11,x12,x13,x14,...,x1n,y1),(x21,x22,x23,x24,...,x2n,y2,...,(xN1,xN2,xN3,xN4,...,xNn,yN))

    读者要特别注意的几点:上述文字只是帮助初学者更容易上手理解,没有涉及推导过程,《统计学习方法》李航著  第一二章一定要看看,学会手工推算,损失函数、梯度下降都要分别查阅文献和推导

    读到这估计很多人已经迷糊了,但最大的问题在于,为什么建模的时候使用多元线性方程,难道多元自变量和结果之间的关系一定是线性的吗?(如果读到这都没想过这个问题,那~~)当然不一定,怎么可能,而且绝大部分不是线性可分的,所以,大家要记住,单个感知机的表达能力很弱。此处可以联想一下,如果在二维坐标中有很多离散的红蓝点,我们有多少概率可以用一条线就将其划分开,在三维坐标中又有多少概率可以用平面将其划分开,依次类推到n维。既然单个感知机的能力这么挫,我为什么要开篇讲它? 因为单个感知机可以看成组成神经网络的神经元的一部分,以后详解。

    2.代码实例

    扯了半天不写代码和咸鱼又有什么区别

    代码下载(整个项目打包了,安装vs2013 ,直接双击.sln即可调试查看)

    感知机简单实现c++

    如果我们用上述代码学习训练无法线性可分的数据集,就会导致W,b无法收敛,陷入死循环,若一定要用感知机训练,就只能手工设置循环次数,但这种方式最后会导致训练效果不好,预测的正确率就会大幅降低。以后会更新更好的神经网络模型来解决这个问题

    以统计学习方法P29页题目为例

    #include<iostream>

    #include<vector>

    #include<string>

    #include<fstream>

    using namespace std;

    typedef vectorfeatureVct;

    typedef int label;

    class Perceptron;

    void printRes(Perceptron pp);

    class Perceptron

    {private:

    vectorFtSet; //特征向量数据集vectorlabelSet; //二分类label数据集 +1或-1

    double LearnRate; //学习率

    featureVct w; //感知机模型参数

    double bias; //偏置项

    int dimension; //特征向量维度

    int count;

    public:

    Perceptron(int dimension,featureVct w,double bias = 0.0,double learnRate = 1);

    int GetCount() const;  //获取迭代次数

    const featureVct& GetW() const ;     //获取模型参数

    void SetW(const featureVct& w);   //设置模型参数

    double GetBias() const; //获取偏置项

    void SetBias(double bias); //设置骗置项

    bool ReadSource(const string& filePath); //读取数据源

    void Train(); //训练模型,得到感知机模型

    public:

    double VectorDotProduct(const featureVct& f1, const featureVct& f2);  //向量点积运算)

    featureVct VectorScalarMulti(double num, const featureVct f); //向量数乘运算

    featureVct VectorAdd(const featureVct& f1, const featureVct& f2);  //向量加法运算

    };

    const featureVct& Perceptron::GetW() const

    {

    return w;

    }

    void Perceptron::SetW(const featureVct& w)

    {

    this->w = w;

    }

    double Perceptron::GetBias() const

    {

    return bias;

    }

    void Perceptron::SetBias(double bias)

    {

    this->bias = bias;

    }

    /*文件格式:每行表示一个样本点,特征值之间用空格隔开,最后一列存储类别信息1或-1*/

    bool Perceptron::ReadSource(const string& filePath)

    {

    ifstream file(filePath);

    if (!file)

    return false;

    while (!file.eof())

    {

    featureVct dataTmp;

    double tmp;

    for (int i = 0; i < dimension; ++i)

    {

    file >> tmp;

    dataTmp.push_back(tmp);

    }

    FtSet.push_back(dataTmp);

    label labelData;

    file >> labelData;

    labelSet.push_back(labelData);

    }

    return true;

    }

    void Perceptron::Train()

    {

    //感知机训练过程

    int flag = true;

    while (flag)

    {

    for (int i = 0; i < FtSet.size(); i++)

    {

    flag = false;

    if (labelSet[i] * (VectorDotProduct(w,FtSet[i]) + bias) <= 0)

    {

    //此处的打印只是为了更直观的给大家展示学习过程,工程中最后不要直接在类中打印信息

    printRes(*this);

    flag = true;

    w = VectorAdd(w, VectorScalarMulti(labelSet[i]*LearnRate,FtSet[i]));

    bias += LearnRate * labelSet[i];

    ++count;

    break;

    }

    }

    //此处的打印只是为了更直观的给大家展示学习过程,工程中最后不要直接在类中打印信息

    if(!flag)  printRes(*this);

    }

    }

    Perceptron::Perceptron(int dimension, featureVct w, double bias, double learnRate)

    {

    this->dimension = dimension;

    this->bias = bias;

    this->LearnRate = learnRate;

    this->w = w;

    count = 0;

    }

    double Perceptron::VectorDotProduct(const featureVct& f1, const featureVct& f2)

    {

    double sum = 0.0;

    for (int i = 0; i != f1.size(); ++i)

    {

    sum += f1[i] * f2[i];

    }

    return sum;

    }

    featureVct Perceptron::VectorScalarMulti(double num, const featureVct f)

    {

    featureVct tmp;

    for (int i = 0; i != f.size(); ++i)

    {

    tmp.push_back(num*f[i]);

    }

    return tmp;

    }

    featureVct Perceptron::VectorAdd(const featureVct& f1, const featureVct& f2)

    {

    featureVct tmp(0);

    for (int i = 0; i != f1.size(); ++i)

    {

    tmp.push_back(f1[i] + f2[i]);

    }

    return tmp;

    }

    int Perceptron::GetCount() const

    {

    return count;

    }

    void printRes(Perceptron pp)

    {

    cout << "迭代次数:" << pp.GetCount() << endl;

    cout << "w:";

    featureVct tmp = pp.GetW();

    for (int i = 0; i < tmp.size() - 1; ++i)

    {

    cout << tmp[i] << " ";

    }

    cout << tmp[tmp.size() - 1] << endl;

    cout << "bias:";

    cout << pp.GetBias() << endl;

    cout << "---------------------------" << endl;

    }

    int main()

    {

    featureVct w;

    w.push_back(0.0); //设置w初始化参数

    w.push_back(0.0);

    Perceptron pp(2, w);  //创建特征向量为2维的感知机对象

    if (!pp.ReadSource("sun.txt"))

    {

    cout << "读取文件失败";

    exit(-1);

    }

    pp.Train();  //训练得到感知机模型

    }

    相关文章

      网友评论

        本文标题:深度学习开篇-感知机(perceptron)原理和实现

        本文链接:https://www.haomeiwen.com/subject/pmqnbxtx.html