最近也是学习了吴恩达老师的神经网络课程,跟着网上的一个教程做了一遍。这里推荐一下吴恩达老师的课后作业的一个人整理的特别好的博客:【目录】【中文】【deplearning.ai】【吴恩达课后作业目录】
这个文章用的内容都是python库来进行的,因为平时对c++较为熟悉,所以用c++又实现了一遍,下面是用win32控制台程序实现的结果(分别展示了不同学习率和不同的迭代次数对于最终预测结果的正确性影响):
如何安装Eigen矩阵库
最新版的visual studio 2019提供了程序自动安装库的方法,通过这种方式就不需要配置程序头文件目录等操作,非常方便,其他类似的库都可以通过这种方式安装:
- 首先打开一个项目,然后选择这个菜单项:管理NuGet程序包
- 搜索对应的程序包——Eigen
3.选择任意一个包都行,点击安装,等待安装完成
Eigen库的使用
注意,使用前包含头文件的写法以及using namespace …的设置。
#include <iostream>
#include <Eigen/dense>
#include <math.h>
using Eigen::MatrixXd;
using namespace std;
int main()
{
cout << "hello world!" << endl;
}
Logistic Regression 代码编写以及测试
吴恩达老师的logistic regression是一个简单的cat分类器,但是要想用c++加载那个h5文件感觉得需要很多代码,后来一想,取了个折中的办法!
伟大的毛主席告诉我们:“有条件要上,没有条件创造条件也要上!”
咱没有数据集就不能自己创造么,真的是。所以我想了个办法创造数据集,数据集的特征值只有两个 x1 和 x2,其中如果x1大于x2的平方,那么对应的输出 y 就给他设置为 1,否则就设置为 0。对于所有的测试和训练的 x1 和 x2 由随机数生成,这样子我们就有对应的训练集和测试集了,哦耶!下面是代码内容:
//================================== 构造数据集内容 ===============================
// 构造的数据中 x1 和 x2 的关系如果满足 x2 > x1^2 那么输出的 y = 1 ,否则为 -1
// 训练集
XTrain.setRandom();
XTrain =
XTrain * 5 + MatrixXd::Ones(XTrain.rows(), XTrain.cols()) * 5; //将数据集中在 0-10 * 0-10
YTrain = XTrain.row(1).array() - XTrain.row(0).array().pow(2);
YTrain = YTrain.array() / YTrain.array().abs();
YTrain = (YTrain + MatrixXd::Ones(YTrain.rows(), YTrain.cols())) * 0.5;
// 测试集
Xtest.setRandom();
Xtest =
Xtest * 5 + MatrixXd::Ones(Xtest.rows(), Xtest.cols()) * 5; //将数据集中在 0-10 * 0-10
Ytest = Xtest.row(1).array() - Xtest.row(0).array().pow(2);
Ytest = Ytest.array() / Ytest.array().abs();
Ytest = (Ytest + MatrixXd::Ones(Ytest.rows(), Ytest.cols())) * 0.5;
有了数据集,然后就按照对应的数学推导过程,一步一步的迭代计算了呗,整个测试的cpp文件内容如下,如有错误,敬请指正!
// logistic regression.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Eigen/dense>
#include <math.h>
using Eigen::MatrixXd;
using namespace std;
int main()
{
cout << "欢迎使用本测试 logistic regression 的程序\n";
int i_nx = 2; //这个表示的是整个logistic回归的输入参数数量
int iTrainNum = 300; // iTrainNum 表示多少个训练样本
int iTestNum = 300; // iTestNum 表示多少个测试样本
int iIterations = 500; //迭代次数
float bNum = 0;
Eigen::MatrixXd XTrain(i_nx, iTrainNum); //将所有样本竖着排列为矩阵 X
Eigen::MatrixXd w(i_nx, 1);
Eigen::MatrixXd dw(i_nx, 1);
Eigen::MatrixXd b(1,iTrainNum);
Eigen::MatrixXd YTrain(1, iTrainNum); //标准结果
Eigen::MatrixXd A(1, i_nx);
Eigen::MatrixXd db(1, iTrainNum);
Eigen::MatrixXd Xtest(i_nx, iTestNum);
Eigen::MatrixXd Ytest(1, iTestNum);
Eigen::MatrixXd YtestPredict(1, iTestNum);
double a = 0.001; //学习率
int i;
double cost = 0; //成本函数
MatrixXd ones = MatrixXd::Ones(YTrain.rows(), YTrain.cols());
//================================== 构造数据集内容 ===============================
// 构造的数据中 x1 和 x2 的关系如果满足 x2 > x1^2 那么输出的 y = 1 ,否则为 -1
// 训练集
XTrain.setRandom();
XTrain =
XTrain * 5 + MatrixXd::Ones(XTrain.rows(), XTrain.cols()) * 5; //将数据集中在 0-10 * 0-10
YTrain = XTrain.row(1).array() - XTrain.row(0).array().pow(2);
YTrain = YTrain.array() / YTrain.array().abs();
YTrain = (YTrain + MatrixXd::Ones(YTrain.rows(), YTrain.cols())) * 0.5;
// 测试集
Xtest.setRandom();
Xtest =
Xtest * 5 + MatrixXd::Ones(Xtest.rows(), Xtest.cols()) * 5; //将数据集中在 0-10 * 0-10
Ytest = Xtest.row(1).array() - Xtest.row(0).array().pow(2);
Ytest = Ytest.array() / Ytest.array().abs();
Ytest = (Ytest + MatrixXd::Ones(Ytest.rows(), Ytest.cols())) * 0.5;
//查看对应的数据集效果
// Eigen::IOFormat CleanFmt(4, 0, ", ", "\n", "[", "]");
// cout << XTrain.format(CleanFmt) << endl;
// cout << YTrain.format(CleanFmt) << endl;
// cout << Xtest.format(CleanFmt) << endl;
// cout << Ytest.format(CleanFmt) << endl;
//初始化参数内容
b.setZero(); //初始化 b 为 0
w.setZero(); //初始化 w 为 0 矩阵
// w(0, 0) = -1.20146;
// w(1, 0) = 0.478334;
// b(0, 0) = -0.0533565;
//迭代学习过程
MatrixXd temp(1, iTrainNum);
for (i=0;i<iIterations;i++)
{
//正向传播
A = 1 / (1 + (-(w.transpose() * XTrain + b)).array().exp());
cost =
(-1.0 / iTrainNum) *
((YTrain.array() * A.array().log() + (ones - YTrain).array() * ((ones - A).array().log())).sum()); //计算成本函数
//反向传播
dw = (1.0 / iTrainNum) * ((XTrain * (A - YTrain).transpose()).array());
db(0,0) = (1.0 / iTrainNum) * (A - YTrain).sum(); //赋值给对应的 b 偏差
//参数学习
w = w - a * dw;
b = b - a * db.setConstant(db(0, 0));
if (i % 100 == 0)
{
cout << "成本函数为:" << cost << endl;
}
}
cout << "数据迭代完成,最终的结果参数为:\n";
cout << "w0 = " << w(0, 0) << "\nw1 = " << w(1, 0) << endl;
cout << "b = " << b(0,0) << endl;
//接下来对最终结果进行预测
YtestPredict.setZero();
bNum = b(0, 0);
b.resize(1, iTestNum);
b.setConstant(bNum);
A = 1 / (1 + (-(w.transpose() * Xtest + b)).array().exp());
for (i = 0; i < iTestNum; i++)
{
YtestPredict(0, i) = A(0, i) > 0.5 ? 1 : 0;
}
cout << "测试集准确性为:"
<< 1 - (YtestPredict - Ytest).array().abs().sum() / iTestNum << endl;
return 0;
}
还有一个大问题,如果没用过 Eigen 库,读取这些代码可能会比较困难,我制作过程中发现较为复杂的地方主要是矩阵的点乘和叉乘,点乘可能需要转换为 array(),利用array()运算是矩阵内所有元素都进行某项运算。
结语
差不多了,今天就先写这么多吧,谢谢大家!
时间:2021/9/24 21:20
其他
最近还把第二周的那个作业也实现了一个c++版本,此处也放过来记录一下!
// Planar Data Clasification.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Eigen/dense>
#include <math.h>
#include <time.h>
#include <stdlib.h>
using Eigen::MatrixXd;
using namespace std;
int main()
{
srand(time(0));
cout << "Hello World!\n";
//设置对应的各个层的 layer 大小
int i_nx = 2; //输入层
int i_nh = 4; //隐藏层
int i_ny = 1; //一个输出层
int iTrainNum = 300; //训练数据
int iTestNum = iTrainNum; //测试数据
int iIterations = 1000; //表示的是迭代次数
int i = 0;
double cost = 0;
double learning_rate = 0.001; //学习率
//首先定义部分用于训练的数据内容
MatrixXd XTrain(i_nx, iTrainNum); //将所有样本竖着排列为矩阵 X
MatrixXd YTrain(1, iTrainNum); //标准结果
MatrixXd Xtest(i_nx, iTestNum);
MatrixXd Ytest(1, iTestNum);
MatrixXd YtestPredict(1, iTestNum);
//来个显式for循环来创建数据集吧
//================================== 构造数据集内容 ===============================
// 构造的数据中 x1 和 x2 的关系如果满足 x2 > x1^2 那么输出的 y = 1 ,否则为 -1
// 训练集
XTrain.setRandom();
XTrain =
XTrain * 5 + MatrixXd::Ones(XTrain.rows(), XTrain.cols()) * 5; //将数据集中在 0-10 * 0-10
for (i = 0; i < iTrainNum; i++)
{
//设置数据位于 +-1 斜率的直线中间全是 1 ,其他全为 0
if (abs(XTrain(1, i) / XTrain(0, i)) < 1)
YTrain(0, i) = 1;
else
YTrain(0, i) = 0;
}
// 测试集
Xtest.setRandom();
Xtest =
Xtest * 5 + MatrixXd::Ones(Xtest.rows(), Xtest.cols()) * 5; //将数据集中在 0-10 * 0-10
for (i = 0; i < iTestNum; i++)
{
//设置数据位于 +-1 斜率的直线中间全是 1 ,其他全为 0
if (abs(Xtest(1, i) / Xtest(0, i)) < 1)
Ytest(0, i) = 1;
else
Ytest(0, i) = 0;
}
cout << "数据集构造完成!\n";
//参数矩阵初始化
MatrixXd W1 = MatrixXd::Random(i_nh, i_nx) * 0.01;
MatrixXd b1 = MatrixXd::Zero(i_nh, iTrainNum); //第二个参数应该是有多少个样本决定的
MatrixXd W2 = MatrixXd::Random(i_ny, i_nh) * 0.01;
MatrixXd b2 = MatrixXd::Zero(i_ny, iTrainNum);
W1 << 0.0439316, -0.0210343,
0.02383, -0.00907748,
-0.0646114, 0.0231469,
-0.0686853, 0.0291914;
W2 << 0.0497446, 0.0241708, -0.0702413, -0.0768112;
//部分中间计算矩阵初始化
MatrixXd Z1(W1.rows(), XTrain.cols());
MatrixXd A1(Z1.rows(), Z1.cols());
MatrixXd Z2(W2.rows(), W1.cols());
MatrixXd A2(Z2.rows(), Z2.cols());
MatrixXd ones =
MatrixXd::Ones(YTrain.rows(), YTrain.cols());
MatrixXd dZ2, dW2, db2(1, iTrainNum), dZ1, dW1, db1(i_nh, iTrainNum); //用于梯度下降的各个中间变量
//迭代学习过程
MatrixXd temp(1, iTrainNum);
int iOption = 0;
while (true)
{
for (i = 0; i < iIterations; i++)
{
//正向传播
Z1 = W1 * XTrain + b1;
A1 = Z1.array().tanh();
Z2 = W2 * A1 + b2;
A2 = 1 / (1 + (-Z2).array().exp());
//成本函数
cost =
(-1.0 / iTrainNum) *
((YTrain.array() * A2.array().log() + (ones - YTrain).array() * ((ones - A2).array().log())).sum()); //计算成本函数
//反向传播
dZ2 = A2 - YTrain;
dW2 = (1.0 / iTrainNum) * (dZ2 * A1.transpose());
db2(0, 0) = (1.0 / iTrainNum) * dZ2.sum();
dZ1 = (W2.transpose() * dZ2).array() * (1 - A1.array().pow(2));
dW1 = (1.0 / iTrainNum) * (dZ1 * XTrain.transpose());
db1(0, 0) = (1.0 / iTrainNum) * dZ1.sum();
//参数学习
db1.setConstant(db1(0, 0));
db2.setConstant(db2(0, 0));
W1 = W1 - learning_rate * dW1;
b1 = b1 - learning_rate * db1;
W2 = W2 - learning_rate * dW2;
b2 = b2 - learning_rate * db2;
if (i % 100 == 0)
{
cout << "成本函数为:" << cost << endl;
}
}
// cout << "参数迭代完成,对应的各个参数数值为:\n";
// cout << "W1 = \n" << W1 << endl;
// //cout << b1 << endl;
// cout << "W2 = \n" << W2 << endl;
// //cout << b2 << endl;
//接下来对最终结果进行预测
YtestPredict.setZero();
//正向传播
Z1 = W1 * Xtest + b1;
A1 = Z1.array().tanh();
Z2 = W2 * A1 + b2;
A2 = 1 / (1 + (-Z2).array().exp());
for (i = 0; i < iTestNum; i++)
{
YtestPredict(0, i) = A2(0, i) > 0.5 ? 1 : 0;
}
cout << "测试集准确性为:"
<< 1 - (YtestPredict - Ytest).array().abs().sum() / iTestNum << endl;
cout << "是否继续迭代 1000 次?\n1 == 是/0 == 否\n";
cin >> iOption;
if (iOption == 1)
continue;
else
break;
}
cout << "欢迎使用本程序进行 Planer Data Classification!\n";
return 0;
}
简单贴一下运行效果哦: