用逻辑回归BP神经网络做二分预测的例子
平面上指定区域的一些点组成一条曲线,预测该曲线是否位于固定圆内的某半侧.
曲线的采样点:X轴范围[-1,1],Y轴范围[-1,1],由这些采样点组成曲线的特征向量(即输入节点)
单位圆中分线:y=-x,第一象限方向为上半侧圆
训练集中,如果曲线处于圆上半侧,期望值是1,否则为0
结论:特征向量维数比训练样本数量更加重要,增加输入节点可以很少的训练样本数达到很高精度
/代码如下/
#include <cstdio>
#include <windows.h>
#include<math.h>
#include<assert.h>
#include "LMNISTParser.h"
#include "LRegression.h"
//用逻辑回归BP神经网络做二分预测的例子
//平面上指定区域的一些点组成一条曲线,预测该曲线是否位于固定圆内的某半侧.
//曲线的采样点:X轴范围[-1,1],Y轴范围[-1,1],由这些采样点组成曲线的特征向量(即输入节点)
//单位圆中分线:y=-x,第一象限方向为上半侧圆
//训练集中,如果曲线处于圆上半侧,期望值是1,否则为0
//最大随机数,用于生成足够精度的特征值
const int MAX_resolution = 2000000000;
//检测(x,y)点是否在上侧半圆内
//单位圆被y=-x斜线划分成上下两个半圆
int InCell(const float x, const float y);
//生成测试数据的函数
//单位圆被y=-x斜线划分成上下两个半圆
//根据给定x轴位置index,生成y轴随机值,可指定检测是否在上半侧半圆内
int GetXi(float& y, const int index, const bool check_r);
int main()
{
unsigned int LOGISTIC_X_DIM = 150;//特征向量维数(输入节点数)
const unsigned int LOGISTIC_TRAIN_SAMPLES = 50; //训练集样本数
const unsigned int LOGISTIC_TEST_SAMPLES = 10000;//测试集样本数
const unsigned int trainTimes = 1; // 训练次数
float learnRate = 0.1f; // 学习速度,学习率
int RANGE_X = int((float)MAX_resolution * 0.7 / 2);//便于生成所需样本数据的设定范围
int step_x = (int)round(((RANGE_X * 2) / LOGISTIC_X_DIM) * 0.90);//生成特征值的步长
printf("用逻辑回归BP神经网络做二分预测的例子\n\n特征向量维数:%d\n", LOGISTIC_X_DIM);
printf("训练集样本数量:%d\n学习速率:%0.2f\n", LOGISTIC_TRAIN_SAMPLES, learnRate);
LRegressionMatrix xMatrix(LOGISTIC_TRAIN_SAMPLES, LOGISTIC_X_DIM, 0.0f);//训练集数据矩阵
LRegressionMatrix yVector(LOGISTIC_TRAIN_SAMPLES, 1, 0.0f);//训练集标签向量
LLogisticRegression logisticReg(LOGISTIC_TRAIN_SAMPLES, LOGISTIC_X_DIM);//softmax二分类
srand(GetTickCount());
int max_no = int(round(LOGISTIC_TRAIN_SAMPLES * 0.10) + 1);//训练集中曲线不在上半圆侧的样本数量
int YES = 0;//曲线位于上侧半圆内的训练样本数量
int NO = 0;
//生成训练集数据
for (int row = 0;row < LOGISTIC_TRAIN_SAMPLES;row++)
{
int R=1;//R==0生成一条不在上半侧圆内的曲线,R==1生成的曲线在上半侧圆内
if (NO < max_no)
{
R = rand() % 2;
if (0 == R) NO++;
}
if (1 == R) YES++;
float y;
int ret = 0;
int index = -RANGE_X;//从左到右生成曲线特征值
for (unsigned int c = 0;c < LOGISTIC_X_DIM;c++)
{
index = index + step_x;
do {
ret = GetXi(y, index, R == 1);
} while (ret != R);
xMatrix[row][c] = y;
}
if (1 == ret)
{
yVector[row][0] = 1;
}
else
{
yVector[row][0] = 0;
}
}
printf("训练集中位于上侧圆内样本比例:%0.1f%%=%d/%d\n", 100.0 * (float)YES / (float)LOGISTIC_TRAIN_SAMPLES, YES, LOGISTIC_TRAIN_SAMPLES);
for (unsigned int t = 1;t <= trainTimes;t++)
{
logisticReg.TrainModel(xMatrix, yVector, learnRate);
printf("循环训练:%d/%d\r", t, trainTimes);
}
printf("\n开始测试...\n测试集样本数量:%d\n", LOGISTIC_TEST_SAMPLES);
LRegressionMatrix x_t_Matrix(LOGISTIC_TEST_SAMPLES, LOGISTIC_X_DIM, 0.0f);//测试集数据矩阵
LRegressionMatrix y_t_Vector(LOGISTIC_TEST_SAMPLES, 1, 0.0f);//测试集标签向量
LRegressionMatrix y_out_Vector(LOGISTIC_TEST_SAMPLES, 1, 0.0f);//测试集输出向量
srand(GetTickCount());
int t_YES = 0;
//生成测试集数据
for (int row = 0;row < LOGISTIC_TEST_SAMPLES;row++)
{
int R = rand() % 2;//R==0生成一条不在上半侧圆内的曲线,R==1生成的曲线在上半侧圆内
float y;
int ret = 0;
int index = -RANGE_X;//从左到右生成曲线特征值
for (unsigned int c = 0;c < LOGISTIC_X_DIM;c++)
{
index = index + step_x;
do {
ret = GetXi(y, index, R == 1);
} while (ret != R);
x_t_Matrix[row][c] = y;
}
if (1 == R)
{
y_t_Vector[row][0] = 1.0f;
t_YES++;
}
else
{
y_t_Vector[row][0] = 0.0f;
}
}
logisticReg.Predict(x_t_Matrix, y_out_Vector);
int er = 0;
for (int row = 0;row < LOGISTIC_TEST_SAMPLES;row++)
{
float y_t = y_t_Vector[row][0];
float ret_y = y_out_Vector[row][0];
if (y_t > ret_y || y_t < ret_y)
{
er++;
}
}
float error_rate = float(er) / (LOGISTIC_TEST_SAMPLES);
printf("测试集中位于上侧圆内样本数:%d\n识别出错:%d\t出错率:%.2f%%\n", t_YES, er, error_rate * 100);
system("pause");
return 0;
}
//检测(x,y)点是否在上侧半圆内
//单位圆被y=-x斜线划分成上下两个半圆
int InCell(const float x, const float y)
{
const float R = 1.0f;
int ret = 0;
if (x > R || x<-R || y>R || y < -R)
{
return ret;
}
if (x < 0 && y < -x)
{
return ret;
}
if (x > 0 && -y > x)
{
return ret;
}
float yLimit = (float)sqrt(R * R - x * x);
if (x < 0 && y>0)
{
if (yLimit > y)
{
ret = 1;
}
}
if (x > 0 && y <= 0)
{
if (-yLimit < y)
{
ret = 1;
}
}
if (x >= 0 && y >= 0)
{
float Rxy = x * x + y * y;
if (Rxy < R)
{
ret = 1;
}
}
return ret;
}
//生成测试数据的函数
//单位圆被y=-x斜线划分成上下两个半圆
//根据给定x轴位置index,生成y轴随机值,可指定检测是否在上半侧半圆内
int GetXi(float& y, const int index, const bool check_r)
{
float max_y = 1.5f;//如果无须检测生成的点是否在上半侧圆内,生成的值可以超出单位圆半径
float min_y = -max_y;
float x = (1.0f / ((float)MAX_resolution / 2)) * index;
float r = 0;//原点为圆心,新生成的点(x,y)的圆半径
if (check_r)
{
min_y = -x;
float z = x;
max_y = (float)(sqrt(1 - z * z));
}
float step_y = (max_y - min_y) / (float)MAX_resolution;
do {
int ry = rand();
ry = ry << 15;
ry = ry + rand();
ry = ry % MAX_resolution;
y = step_y * ry + min_y;
} while (y<min_y || y>max_y);
r = (float)(x * x + y * y);
int ret = 0;
if (x >= 0 && y >= 0)
{
ret = 1;
}
if (x >= 0 && y <= 0)
{
if (x > -y)
{
ret = 1;
}
}
if (x <= 0 && y >= 0)
{
if (-x < y)
{
ret = 1;
}
}
if (r > 1)
{
ret = 0;
}
#ifdef _DEBUG
if (ret)
{
int cell = InCell(x, y);
assert(ret == cell);
}
#endif // _DEBUG
return ret;
}