经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
统计学习方法c++实现之一 感知机
来源:cnblogs  作者:bobxxxl  时间:2018/12/17 9:47:48  对本文有异议

感知机

2018/12/17 代码结构更新,详见https://github.com/bBobxx/statistical-learning

前言

最近学习了c++,俗话说‘光说不练假把式’,所以决定用c++将《统计学习方法》里面的经典模型全部实现一下,代码在这里,请大家多多指教。

感知机虽然简单,但是他可以为学习其他模型提供基础,现在先简单回顾一下基础知识。

感知机模型

感知机

首先,感知机是用来分类的模型,上图就是简单的感知机模型,其中\(f\) 我们一般取符号函数

\[sign(x)=\begin{cases} -1,\quad x<0 \\\\ +1,\quad x\geq0 \end{cases} \]

所以感知机的数学形式就是

\[y=sign(wx+b)\]

其中w和x都是n维的向量。当n为2时,\(sign\)里面的公式有没有特别熟悉?就是直线的公式,n>2就是超平面,用一下课本里面的图就是如下图

这就是分类的根据,必须要注意,感知机只能分离线性可分数据,非线性的不行。

感知机学习策略

提到学习就不得不提到梯度下降算法。感知机的学习策略就是随机梯度下降算法。

具体的在书中讲的很详细,我这里就不赘述了,直接看学习算法吧:

(1) 选取初值w,b。

(2) 选取一组训练数据(x, y)。

(3) 如果\(y(wx+b)\leq0\),则

\[ w += lr*yx\]

\[b+=lr*y\]

(4)转至(2)直到没有误分类点。

c++实现感知机

首先我有一个基类Base,为了以后的算法继承用的,它包含一个run()的纯虚函数,这样以后就可以在main里面实现多态。

我的数据都存储在私有成员里:

  1. std::vector<std::vector<double>> inData;//从文件都的数据
  2. std::vector<std::vector<double>> trainData;//分割后的训练数据,里面包含真值
  3. std::vector<std::vector<double>> testData;
  4. unsigned long indim = 0;
  5. std::vector<double> w;
  6. double b;
  7. std::vector<std::vector<double>> trainDataF;//真正的训练数据,特征
  8. std::vector<std::vector<double>> testDataF;
  9. std::vector<double> trainDataGT;//真值
  10. std::vector<double> testDataGT;

在main函数里只需要调用每个模型的run()方法,声明的是基类指针:

  1. int main() {
  2. Base* obj = new Perceptron();
  3. obj->run();
  4. delete obj;
  5. return 0;
  6. }

第一步,读取数据并分割。这里用的vector存储。

  1. getData("../data/perceptrondata.txt");
  2. splitData(0.6);//below is split data , and store it in trainData, testData

第二步初始化

  1. std::vector<double> init = {1.0,1.0,1.0};
  2. initialize(init);

第三步进行训练。

在训练时,函数调用顺序如下:

  • 调用computeGradient,进行梯度的计算。对于满足\(y(wx+b)>0\)的数据我们把梯度设为0。

    1. std::pair<std::vector<double>, double> Perceptron::computeGradient(const std::vector<double>& inputData, const double& groundTruth) {
    2. double lossVal = loss(inputData, groundTruth);
    3. std::vector<double> w;
    4. double b;
    5. if (lossVal > 0.0)
    6. {
    7. for(auto indata:inputData) {
    8. w.push_back(indata*groundTruth);
    9. }
    10. b = groundTruth;
    11. }
    12. else{
    13. for(auto indata:inputData) {
    14. w.push_back(0.0);
    15. }
    16. b = 0.0;
    17. }
    18. return std::pair<std::vector<double>, double>(w, b);//here, for understandable, we use pair to represent w and b.
    19. //you also could return a vector which contains w and b.
    20. }

    在调用computeGradient时又调用了loss,即计算\(-y(wx+b)\),loss里调用了inference,用来计算\(wx+b\),看起来有点多余对吧,inference函数存在的目的是为了后面预测时候用的。

    1. double Perceptron::loss(const std::vector<double>& inputData, const double& groundTruth){
    2. double loss = -1.0 * groundTruth * inference(inputData);
    3. std::cout<<"loss is "<< loss <<std::endl;
    4. return loss;
    5. }
    1. double Perceptron::inference(const std::vector<double>& inputData){
    2. //just compute wx+b , for compute loss and predict.
    3. if (inputData.size()!=indim){
    4. std::cout<<"input dimension is incorrect. "<<std::endl;
    5. throw inputData.size();
    6. }
    7. double sum_tem = 0.0;
    8. sum_tem = inputData * w;
    9. sum_tem += b;
    10. return sum_tem;
    11. }
  • 根据计算的梯度更新w, b

    1. void Perceptron::train(const int & step, const float & lr) {
    2. int count = 0;
    3. createFeatureGt();
    4. for(int i=0; i<step; ++i){
    5. if (count==trainDataF.size()-1)
    6. count = 0;
    7. count++;
    8. std::vector<double> inputData = trainDataF[count];
    9. double groundTruth = trainDataGT[count];
    10. auto grad = computeGradient(inputData, groundTruth);
    11. auto grad_w = grad.first;
    12. double grad_b = grad.second;
    13. for (int j=0; j<indim;++j){//这里更新参数
    14. w[j] += lr * (grad_w[j]);
    15. }
    16. b += lr * (grad_b);
    17. }
    18. }
  • 预测用的数据也是之前就分割好的,注意这里的参数始终存在

  1. std::vector<double> paraData;

进行预测的代码

  1. int Perceptron::predict(const std::vector<double>& inputData, const double& GT) {
  2. double out = inference(inputData);
  3. std::cout<<"The right class is "<<GT<<std::endl;
  4. if(out>=0.0){
  5. std::cout<<"The predict class is 1"<<std::endl;
  6. return 1;
  7. }
  8. else{
  9. std::cout<<"The right class is -1"<<std::endl;
  10. return -1;
  11. }
 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号