吴恩达机器学习之线性逻辑回归实现部分

C++实现

“linear_regression.h”

//二分类逻辑回归模型
struct elem_log
{
	double y;
	double* x;              //用数组传入自变量数据(x[0]=1,便于之后的计算)
};

class logistic_reg
{//与线性回归的不同点在于hx不同
public:
	logistic_reg(int xnum, elem_log* p, int size, double rate);               //初始化(训练集的x0全部固定为1) 
	~logistic_reg();                                                           //析构
	void scaling();                                                            //特征缩放器(将所有特征值统一缩放至1--10)
	void scalback();                                                           //特征还原(参数缩放)
	double hx(int i);                                                          //sigmoid函数:计算第i组训练集数据对应的hx函数值(0--setsize-1)
	double cost_fuction();                                                     //返回当前预测方程对应代价函数的值
	void update();                                                             //同时更新方程参数
	void find();                                                               //最小化代价函数,找到收敛点时对应的方程参数
	double* get_par(int &par_num);                                             //获得当前方程的参数
	double est_val(double* x);                                                 //使用拟合后的回归方程进行预测

private:
	int x_num;                         //自变量个数
	elem_log* tran_set;                //训练集(读入时将x【0】赋值为1)
	int setsize;                       //训练集数据量
	double* par;                       //参数数组(大小为实际x_num+1):h(x)=par0*x0(赋值为1)+par1*x1+par2+x^2+...+par(x_num)*x^x_num
	double learn_rate;                 //学习速率
	double* x_scal;                    //特征缩放率


};

“linear_regression.cpp”

//二分类逻辑回归
logistic_reg::logistic_reg(int xnum, elem_log* p, int size, double rate)
{//初始化(训练集的x0全部固定为1)
 //参数列表:自变量数目,训练集地址,训练集容量,学习速率
	x_num = xnum + 1;                //设置自变量数目
	setsize = size;                  //获取训练集大小
	tran_set = p;                    //指针指向训练集数组
	learn_rate = rate;               //设置学习速率
	par = new double[xnum + 1];      //系数初始化为0
	memset(par, 0, sizeof(double)*(xnum + 1));
	x_scal = new double[xnum + 1];   //特征缩放率初始化为1
	for (int i = 0;i < x_num;i++)
		x_scal[i] = 1;
}

logistic_reg::~logistic_reg()
{//析构
	tran_set = NULL;
	setsize = 0;
	delete[]par;
	par = NULL;
	delete[]x_scal;
	x_scal = NULL;
}

void logistic_reg::scaling()
{//特征缩放器(将所有特征值统一缩放至1--10)
	for (int j = 0;j < x_num;j++)
	{//以第一组数据确定缩放率
		while (tran_set[0].x[j] > 10 || tran_set[0].x[j] < 1)
		{
			if (tran_set[0].x[j] > 10)
			{
				tran_set[0].x[j] /= 10.0;
				x_scal[j] *= 10.0;
			}
			else
			{
				tran_set[0].x[j] *= 10.0;
				x_scal[j] /= 10.0;
			}
		}
	}
	for (int i = 1;i < setsize;i++)
	{//对剩余数据进行缩放
		for (int j = 0;j < x_num;j++)
		{
			tran_set[i].x[j] /= x_scal[j];
		}
	}
}

void logistic_reg::scalback()
{//特征还原(参数缩放)
	for (int i = 0;i < x_num;i++)
		par[i] /= x_scal[i];
}

double logistic_reg::hx(int i)
{//sigmoid函数:计算第i组训练集数据对应的hx函数值(0--setsize - 1)
	double sum=0;
	for (int j = 0;j < x_num;j++)
		sum += par[j] * tran_set[i].x[j];
	double hx = 1 / (1 + exp(0 - sum));            //double exp(double x)----求e的x次幂
	return hx;
}

double logistic_reg::cost_fuction()
{//返回当前预测方程对应代价函数的值
	double cost = 0;
	for (int i = 0;i < setsize;i++)
	{
		cost += (tran_set[i].y*log(hx(i)) + (1 - tran_set[i].y)*log(1 - hx(i)));
	}
	return -1.0 / (double)setsize*cost;
}

void logistic_reg::update()
{//同时更新方程参数
	double* sum = new double[x_num];
	for (int j = 0;j < x_num;j++)
	{
		sum[j] = 0;
		for (int i = 0;i < setsize;i++)
		{
			sum[j] += (hx(i) - tran_set[i].y)*tran_set[i].x[j];
		}
	}
	for (int i = 0;i < x_num;i++)
		par[i] -= learn_rate * sum[i] / (double)setsize;
	delete[]sum;
}

void logistic_reg::find()
{//最小化代价函数,找到收敛点时对应的方程参数
	scaling();                     //数据放缩
	double cost_pre, cost_last;
	cost_pre = cost_fuction();
	update();                      //更新参数
	cost_last = cost_fuction();
	while (cost_pre != cost_last)
	{//寻找收敛点

		/*cout << cost_pre << " " << cost_last << endl;        //用来选择学习率*/

		cost_pre = cost_last;
		update();
		cost_last = cost_fuction();
	}
	//获得假设函数最优拟合时的参数
	scalback();
	//特征还原,参数缩放
}

double* logistic_reg::get_par(int &par_num)
{//获得当前方程的参数
	par_num = x_num;
	return par;
}

double logistic_reg::est_val(double* x)
{//使用拟合后的回归方程进行预测
	double sum = 0;
	for (int j = 0;j < x_num;j++)
		sum += par[j] * x[j];
	double hx = 1 / (1 + exp(0 - sum));            //double exp(double x)----求e的x次幂
	return hx;
}

主函数部分:

//二分类逻辑回归测试
int main()
{
	int size, xnum;
	cout << "请输入训练集容量:";
	cin >> size;
	cout << "请输入变量个数: ";
	cin >> xnum;
	elem_log transet[200];
	for (int i = 0;i < size;i++)
	{
		transet[i].y = 0;
		transet[i].x = new double[xnum + 1];
		memset(transet[i].x, 0, sizeof(double)*(xnum + 1));
	}

	cout << "请输入训练集数据:" << endl;
	for (int i = 0;i < size;i++)
	{
		transet[i].x[0] = 1;
		for (int j = 1;j <= xnum;j++)
			cin >> transet[i].x[j];
		cin >> transet[i].y;
	}
	logistic_reg obj(xnum, transet, size, 0.4);
	obj.find();
	double*par = NULL;
	int parnum;
	par = obj.get_par(parnum);
	cout << "h(x)=" << par[0];
	for (int i = 1;i < parnum;i++)
	{
		if (par[i] > 0)
			cout << '+' << par[i] << "*x" << i;
		else
		{
			if (par[i] < 0)
				cout << par[i] << "*x" << i;
		}
	}
	cout << endl;

	
	double*x = new double[xnum + 1];
	memset(x, 0, sizeof(double)*(xnum + 1));
	string str;
	double ans;
	while (cin >> str)
	{//预测部分
	if (str == "end")
	break;
	x[0] = 1;
	for (int i = 1;i <= xnum;i++)
	cin >> x[i];
	ans = obj.est_val(x);
	cout << ans << endl;
	}
	delete[]x;
	
	
	for (int i = 0;i < size;i++)
	{
		delete[]transet[i].x;
	}


	return 0;
}

运行结果

在这里插入图片描述
在这里插入图片描述
用octave可视化:

>> A=load('ex2data1.txt');
>>  x1=A(:,[1]);
>> x2=A(:,[2]);
>> y=A(:,[3]);
>> pos = find(y==1); neg = find(y == 0);
>> X=[x1,x2];
>> plot(X(pos, 1), X(pos, 2), 'k+','LineWidth', 2,'MarkerSize', 7);
>> hold on;
>> plot(X(neg, 1), X(neg, 2), 'ko', 'MarkerFaceColor', 'y','MarkerSize', 7);
>> hold on;
>> x2=(-0.206231*x1+25.1612)/0.201471;
>> plot(x1,x2,'r');
>> xlabel('Exam 1 score');
>> ylabel('Exam 2 score');
>> title('logistic regression');
>> legend('Admitted','Not admitted');
>> print -dpng 'linear_logistic.png';

在这里插入图片描述
按照自己做出的模型得出的决策边界进行分类如上图。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值