前言
本文的实现主要是参照了Andrew NG的机器学习课程所讲的内容。理论知识上一篇博文已经介绍。由于刚接触C++,代码写得比较粗糙,望见谅。
实验环境
Visual Studio 2013
OpenCV 2.4
实验代码
1.定义一个线性回归的类Regression:
头文件 Regression.h
#ifndef _Regression_H_
#define _Regression_H_
#include <vector>
#include <string>
#include <iostream>
#include <strstream>
#include <fstream>
#include <opencv2\core\core.hpp>
using namespace cv;
using namespace std;
class Regression
{
public:
//加载数据
void loadData(string filePath, string regx);
//数据返回
Mat& getX();
Mat& getEX();
Mat& getTheta();
Mat& getY();
//特征归一化
void featureNormalize(Mat& x);
//预测
float predict(Mat& x);
//假设函数,此处设成静态函数,是方便在其他类中直接调用
static void calculateHx(Mat& x, Mat& theta, Mat& Hx);
private:
Mat originalX;
Mat extendX;
Mat y;
int dim;//数据维数
Mat theta;//列向量
};
#endif
实现文件 Regression.cpp
#include "Regression.h"
//把字符串转换成数值类型
template<typename T>
T convertStringData(string data)
{
strstream ss;
T y;
ss <<data;
ss >> y;
return y;
}
//字符串分割函数
inline vector<string> split(string str, string pattern)
{
string::size_type pos;
vector<string> result;
str += pattern;//扩展字符串以方便操作
int size = str.size();
for (int i = 0; i<size; i++)
{
pos = str.find(pattern, i);
if (pos<size)
{
std::string s = str.substr(i, pos - i);
result.push_back(s);
i = pos + pattern.size() - 1;
}
}
return result;
}
//加载数据
void Regression::loadData(string filePath,string regx)
{
ifstream fin(filePath);
while (!fin.eof())
{
string temp;
getline(fin, temp);
if (strcmp(temp.c_str(), "") == 0) continue;
vector<string> resStr = split(temp, regx);
//设置x的维数
dim = resStr.size() - 1;
float yi=convertStringData<float>(resStr[resStr.size() - 1]);
y.push_back(yi);
Mat data = Mat::zeros(1, dim,CV_32FC1);//临时存放每一行的数据
for (int i = 0; i < dim; i++)
{
data.row(0).col(i) = convertStringData<float>(resStr[i]);;
}
originalX.push_back(data);
}
extendX = Mat::ones(originalX.rows, dim + 1, CV_32FC1);
originalX.copyTo(extendX.colRange(1, extendX.cols));
extendX.col(0) = Mat::ones(extendX.rows, 1, extendX.type());
theta = Mat::zeros(dim+1,1,originalX.type());
fin.close();
}
//数据返回
Mat& Regression::getX()
{
return originalX;
}
Mat& Regression::getY()
{
return y;
}
Mat& Regression::getEX()
{
return extendX;
}
Mat& Regression::getTheta()
{
return theta;
}
//特征归一化
void Regression::featureNormalize(Mat& ex)
{
if (ex.cols <= 2) return;//只有一个特征的时候不用归一化
for (int col = 1; col < ex.cols; col++)
{
Mat mean;
Mat stddev;
meanStdDev(ex.col(col), mean, stddev);
//归一化
ex.col(col) = ex.col(col) - mean;
ex.col(col) = ex.col(col) / stddev;
}
}
//预测
//x:一个样本
//theta 训练得到的参数
float Regression::predict(Mat& x)
{
Mat hx;
calculateHx(x, this->getTheta(), hx);
return hx.at<float>(0,0);
}
//计算假设函数
void Regression::calculateHx(Mat& x, Mat& theta, Mat& Hx)
{
Hx = x*theta;
}
2.定义一个梯度下降的类GradientDescent:
头文件 GradientDescent.h
#ifndef _GRADIENTDESCENT_H
#define _GRADIENTDESCENT_H
#include <opencv2\core\core.hpp>
using namespace cv;
class GradientDescent
{
public:
//批量梯度下降
void gradientDescent(Mat& x, Mat& y, Mat &theta, int num_iters, float alpha);
//计算代价
float computeCost(Mat& x, Mat& y, Mat &theta);
void normalizeEquation(Mat& x, Mat& y, Mat &theta);
};
#endif
实现文件 GradientDescent.cpp
#include "GradientDescent.h"
#include "Regression.h"
#include <iostream>
using namespace std;
void GradientDescent::gradientDescent(Mat& x, Mat& y, Mat &theta, int num_iters, float alpha)
{
int dataSize = x.rows;//数据总数
int dim = theta.rows;//theta的维数
for (int i = 0; i < num_iters; i++)
{
//矢量化编程
Mat hx;
Regression::calculateHx(x, theta, hx);
Mat thetaNew = theta-alpha*x.t()*(hx - y)/dataSize;
thetaNew.copyTo(theta);
}
}
float GradientDescent::computeCost(Mat& x, Mat& y, Mat &theta)
{
float error = .0;
Mat hx;
Mat j;
hx = x*theta;
j = (hx - y).t()*(hx - y);
error = j.at<float>(0, 0) / (2 * x.rows);
return error;
}
void GradientDescent::normalizeEquation(Mat& x, Mat& y, Mat &theta)
{
theta=(x.t()*x).inv()*x.t()*y;
}
3.代码测试:
样本集由Andrew NG机器学习课程提供,可在网上下载
main.cpp内容如下:
#include "Regression.h"
#include "GradientDescent.h"
#include <iostream>
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Regression regression;
regression.loadData("test_data/ex1data1.txt",",");
regression.featureNormalize(regression.getEX());
GradientDescent gd;
float error=gd.computeCost(regression.getEX(), regression.getY(), regression.getTheta());
cout << "cost:" << error << endl;
gd.gradientDescent(regression.getEX(), regression.getY(), regression.getTheta(), 1500, 0.01);
//gd.normalizeEquation(regression.getEX(), regression.getY(), regression.getTheta());
cout << regression.getTheta() << endl;
getchar();
return 0;
}
输出结果:
与matlab运行结果一致