Softmax回归C++实现

2023-10-27

 前言
Softmax回归模型的理论知识上一篇博文已经介绍。C++代码来源于一个开源项目,链接地址我忘了可怜,哪天找到了再附上。对原代码改动不大,只是进行了一些扩充。

实验环境

Visual Studio 2013

数据

数据来自http://archive.ics.uci.edu/ml/datasets/Optical+Recognition+of+Handwritten+Digits,包含了26个大写字母。

里面共有20000个样本,每个样本16维。

实验目的

完成对数据集中字符样本的分类。

实验代码

1.定义一个LogisticRegression的类:

头文件 LogisticRegression.h

#include <iostream>
#include <math.h>
#include<algorithm> 
#include <functional> 
#include <string>
#include <cassert>
#include <vector>
using namespace std;
class LogisticRegression {
public:
	LogisticRegression(int inputSize, int k, int dataSize, int num_iters,double learningRate);
	~LogisticRegression();
	bool loadData(const string& filename);//加载数据
	void train();//训练函数
	void softmax(double* thetaX);//得到样本对应的属于某个类别的概率
	double predict(double* x);//预测函数
	double** getX();
	double** getY();
	void printX();
	void printY();
	void printTheta();
private:
	int inputSize;//输入特征数,不包括bias项
	int k;//类别数
	int dataSize;//样本数
	int num_iters;//迭代次数
	double **theta;//学习得到的权值参数
	double alpha;//学习速率
	double** x;//训练数据集
	double** y;//训练数据集对应的标号
};

实现文件 LogisticRegression.cpp

#include "LogisticRegression.h"
LogisticRegression::LogisticRegression(int in, int out,int size, int num_iters,double learningRate) {
	inputSize = in;
	k = out;
	alpha = learningRate;
	dataSize = size; 
	this->num_iters = num_iters;
	// initialize theta
	theta = new double*[k];
	for (int i = 0; i<k; i++) theta[i] = new double[inputSize];
	for (int i = 0; i<k; i++) {
		for (int j = 0; j<inputSize; j++) {
			theta[i][j] = 0;
		}
	}
	//initialize x
	x = new double*[dataSize];
	for (int i = 0; i<dataSize; i++) x[i] = new double[inputSize];
	for (int i = 0; i<dataSize; i++) {
		for (int j = 0; j<inputSize; j++) {
			x[i][j] = 0;
		}
	}
	//initialize y
	y = new double*[dataSize];
	for (int i = 0; i<dataSize; i++) y[i] = new double[k];
	for (int i = 0; i<dataSize; i++) {
		for (int j = 0; j<k; j++) {
			y[i][j] = 0;
		}
	}
}

LogisticRegression::~LogisticRegression() {
	for (int i = 0; i<k; i++) delete[] theta[i];
	delete[] theta;
	for (int i = 0; i < dataSize; i++)
	{
		delete[] x[i];
		delete[] y[i];
	}
	delete[] x;
	delete[] y;
}

void LogisticRegression::train() {
	for (int n = 0; n < num_iters; n++)
	{
		for (int s = 0; s < dataSize; s++)
		{
			double *py_x = new double[k];
			double *dy = new double[k];
			//1.求出theta*x
			for (int i = 0; i<k; i++) {
				py_x[i] = 0;
				for (int j = 0; j<inputSize; j++) {
					py_x[i] += theta[i][j] * x[s][j];
				}
			}
			//2.求出概率
			softmax(py_x);
			for (int i = 0; i<k; i++) {
				dy[i] = y[s][i] - py_x[i];//真实值与预测值的差异

				for (int j = 0; j<inputSize; j++) {
					theta[i][j] += alpha * dy[i] * x[s][j] / dataSize;
				}
			}
			delete[] py_x;
			delete[] dy;
		}
	}
}

void LogisticRegression::softmax(double *x) {
	double max = 0.0;
	double sum = 0.0;

	for (int i = 0; i<k; i++) if (max < x[i]) max = x[i];
	for (int i = 0; i<k; i++) {
		x[i] = exp(x[i] - max);
		sum += x[i];
	}

	for (int i = 0; i<k; i++) x[i] /= sum;
}


double LogisticRegression::predict(double *x) {
	double clsLabel;
	double* predictY = new double[k];
	for (int i = 0; i < k; i++) {
		predictY[i] = 0;
		for (int j = 0; j < inputSize; j++) {
			predictY[i] += theta[i][j] * x[j];
		}
	}
	softmax(predictY);
	double max = 0;
	for (int i = 0; i < k; i++)
	{
		if (predictY[i]>max) {
			clsLabel = i;
			max = predictY[i];
		}
	}
	return clsLabel;
}

double** LogisticRegression::getX()
{
	return x;
}
double** LogisticRegression::getY()
{
	return y;
}

bool LogisticRegression::loadData (const string& filename)
{
	const int M = 1024;
	char buf[M + 2];
	int i;
	vector<int> responses;
	FILE* f = fopen(filename.c_str(), "rt");
	if (!f)
	{
		cout << "Could not read the database " << filename << endl;
		return false;
	}
	int rowIndex = 0;
	for (;;)
	{
		char* ptr;
		if (!fgets(buf, M, f) || !strchr(buf, ','))// char *strchr(const char *s,char c):查找字符串s中首次出现字符c的位置
			break;
		y[rowIndex][buf[0] - 'A'] = 1;
		ptr = buf + 2;
		for (i = 0; i < inputSize; i++)
		{
			int n = 0;//存放sscanf当前已读取了的总字符数
			int m = 0;
			sscanf(ptr, "%d%n", &m, &n);//sscanf() - 从一个字符串中读进与指定格式相符的数据
			x[rowIndex][i] = m;
			ptr += n + 1;
		}
		rowIndex++;
		if (rowIndex >= dataSize) break;
		if (i < inputSize)
			break;
	}
	fclose(f);
	cout << "The database " << filename << " is loaded.\n";
	return true;
}

void LogisticRegression::printX()
{
	for (int i = 0; i<dataSize; i++) {
		for (int j = 0; j<inputSize; j++) {
			cout << x[i][j] << " ";
		}
		cout << endl;
	}
}
void LogisticRegression::printY()
{
	for (int i = 0; i<dataSize; i++) {
		for (int j = 0; j<k; j++) {
			cout << y[i][j] << " ";
		}
		cout << endl;
	}
}

void LogisticRegression::printTheta()
{
	for (int i = 0; i < k; i++) {
		for (int j = 0; j < inputSize; j++) {
			cout << theta[i][j] << " ";
		}
		cout << endl;
	}

}
2.代码测试:
#include "LogisticRegression.h"
void letter_recog()
{
	double learning_rate = 0.1;
	int num_iters = 500;//迭代次数
	int train_N =10000;//训练样本个数
	int test_N = 8;//测试样本个数
	int n_in = 16;//输入特征维数
	int n_out = 26;//类别数
	LogisticRegression classifier(n_in, n_out, train_N, num_iters, learning_rate);
	classifier.loadData("letter-recognition.data");
	//训练
	classifier.train();
	// test data
	double test_X[8][16] = {
		{ 5, 10, 6, 8, 4, 7, 7, 12, 2, 7, 9, 8, 9, 6, 0, 8 },//M
		{ 6, 12, 7, 6, 5, 8, 8, 3, 3, 6, 9, 7, 10, 10, 3, 6 },//W
		{ 3, 8, 4, 6, 4, 7, 7, 12, 1, 6, 6, 8, 5, 8, 0, 8 },//N
		{ 1, 0, 1, 0, 0, 7, 8, 10, 1, 7, 5, 8, 2, 8, 0, 8 },//H
		{ 3, 6, 5, 5, 6, 6, 8, 3, 3, 6, 5, 9, 6, 7, 5, 9 },//R
		{ 7, 11, 11, 8, 7, 4, 8, 2, 9, 10, 11, 9, 5, 8, 5, 4 }, //X
		{ 6, 9, 6, 4, 4, 8, 9, 5, 3, 10, 5, 5, 5, 10, 5, 6 },//P	
		{ 4, 7, 6, 5, 5, 8, 5, 7, 4, 6, 7, 9, 3, 7, 6, 9 }//Q
	};
	// test
	for (int i = 0; i<test_N; i++) {
		double predict = classifier.predict(test_X[i]);
		char char_predict = 'A' + predict;
		cout << "predict:" << char_predict << endl;
	}
}

int main() {
	letter_recog();
	getchar();
	return 0;

输出结果:

程序中用了前1w个样本来训练分类器,整个训练过程花了328.117s。为了加快程序的运行速度,决定使用OpenMP来加速for循环。
在Visual Studio里面使用OpenMP很简单。
点击项目-->属性,进入属性页。在c/c++下面的Language中开启Open MP Support即可。
修改过后的train函数:
void LogisticRegression::train() {
	for (int n = 0; n < num_iters; n++)
	{
#pragma omp parallel for  
		for (int s = 0; s < dataSize; s++)
		{
			double *py_x = new double[k];
			double *dy = new double[k];
			//1.求出theta*x
			for (int i = 0; i<k; i++) {
				py_x[i] = 0;
				for (int j = 0; j<inputSize; j++) {
					py_x[i] += theta[i][j] * x[s][j];
				}
			}
			//2.求出概率
			softmax(py_x);
#pragma omp parallel for  
			for (int i = 0; i<k; i++) {
				dy[i] = y[s][i] - py_x[i];//真实值与预测值的差异			
				for (int j = 0; j<inputSize; j++) {
					theta[i][j] += alpha * dy[i] * x[s][j] / dataSize; //- lambda*theta[i][j];
				}
			}
			delete[] py_x;
			delete[] dy;
		}
	}
}
修改过后的softmax函数:
void LogisticRegression::softmax(double *x) {
	double max = 0.0;
	double sum = 0.0;
	for (int i = 0; i<k; i++) if (max < x[i]) max = x[i];
#pragma omp parallel for  
	for (int i = 0; i<k; i++) {
		x[i] = exp(x[i] - max);//防止数据溢出
		sum += x[i];
	}
#pragma omp parallel for  
	for (int i = 0; i<k; i++) x[i] /= sum;
}
输出结果:
训练时间从之前的328.117s较少到49.081s,提升了6.68倍。
从测试结果来看,分类器把R预测成了K,把X预测成了U。本文没有对分类器的准确率进行严格的测试,有兴趣的同学可以自己去测一下。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Softmax回归C++实现 的相关文章

  • 每个托管线程是否都有自己对应的本机线程?

    我想知道是否在 Net 中创建托管线程 通过调用Thread Start 导致在后台创建一个本机线程 那么托管线程是否有对应的本机线程呢 如果是 当托管线程等待或睡眠时 是否意味着相应的本机线程也在等待或睡眠 是的 NET 线程映射到所有当
  • 在 C++ 中使用 matlab 结构(matlab 函数调用的返回值)(由 matlab 编译器生成的库)

    你好 我有一个相当简单的 matlab 函数 例如 function MYSTRUCT myfunc MYSTRUCT prop1 test MYSTRUCT prop2 foo MYSTRUCT prop3 42 end 我用 matla
  • 未提供参数时如何指定 C# System.Commandline 行为?

    在我的控制台应用程序中 当未提供控制台参数时 将执行我指定列表 在本例中为参数 3 的任何处理程序 调用该处理程序时 布尔参数设置为 false 但对我来说 根本不调用它更有意义 如何防止这种情况发生并显示帮助文本 using System
  • 如何在c++中读取pcap文件来获取数据包信息?

    我想用 C 编写一个程序来读取 pcap 文件并获取数据包的信息 例如 len sourc ip flags 等 现在我找到了如下代码 我认为它会帮助我获取信息 但是我有一些疑问 首先我想知道应该将哪个库添加到我的程序中 然后什么是 pca
  • 如何让 Swagger 插件在自托管服务堆栈中工作

    我已经用 github 上提供的示例重新提出了这个问题 并为任何想要自己运行代码的人提供了一个下拉框下载链接 Swagger 无法在自托管 ServiceStack 服务上工作 https stackoverflow com questio
  • 在 LINQ 中按 Id 连接多表和分组

    我想按categoryId显示列表产品的名称组 这是我的代码 我想要我的视图显示结果 Desktop PC HP Red PC Dell Yellow PC Asus Red SmartPhone Lumia 720 Blue 我的组模型
  • 在 C 中匹配二进制模式

    我目前正在开发一个 C 程序 需要解析一些定制的数据结构 幸运的是我知道它们是如何构造的 但是我不确定如何在 C 中实现我的解析器 每个结构的长度都是 32 位 并且每个结构都可以通过其二进制签名来识别 举个例子 有两个我感兴趣的特定结构
  • 当我们想要返回对象的引用时,为什么我们在赋值运算符中返回 *this 而通常(而不是 this)?

    我正在学习 C 和指针 我以为我理解了指针 直到我看到这个 一方面 asterix 运算符是解引用的 这意味着它返回值所指向的地址中的值 而与号 运算符则相反 它返回值存储的地址记忆 现在阅读有关赋值重载的内 容 它说 我们返回 this因
  • 使用 LINQ2SQL 在 ASP.NET MVC 中的各种模型存储库之间共享数据上下文

    我的应用程序中有 2 个存储库 每个存储库都有自己的数据上下文对象 最终结果是我尝试将从一个存储库检索到的对象附加到从另一个存储库检索到的对象 这会导致异常 Use 构造函数注入将 DataContext 注入每个存储库 public cl
  • 复制目录内容

    我想将目录 tmp1 的内容复制到另一个目录 tmp2 tmp1 可能包含文件和其他目录 我想使用C C 复制tmp1的内容 包括模式 如果 tmp1 包含目录树 我想递归复制它们 最简单的解决方案是什么 我找到了一个解决方案来打开目录并读
  • 单个对象的 Monogame XNA 变换矩阵?

    我读过一些解释 XNA Monogame 变换矩阵的教程 问题是这些矩阵应用于 SpriteBatch Begin matrix 这意味着所有 Draw 代码都将被转换 如何将变换矩阵应用于单个可绘制对象 就我而言 我想转换滚动背景 使其自
  • 获取两个工作日之间的天数差异

    这听起来很简单 但我不明白其中的意义 那么获取两次之间的天数的最简单方法是什么DayOfWeeks当第一个是起点时 如果下一个工作日较早 则应考虑在下周 The DayOfWeek 枚举 http 20 20 5B1 5D 3a 20htt
  • 是否有实用的理由使用“if (0 == p)”而不是“if (!p)”?

    我倾向于使用逻辑非运算符来编写 if 语句 if p some code 我周围的一些人倾向于使用显式比较 因此代码如下所示 if FOO p some code 其中 FOO 是其中之一false FALSE 0 0 0 NULL etc
  • 如何在 Xaml 文本中添加电子邮件链接?

    我在 Windows Phone 8 应用程序中有一些大文本 我希望其中有电子邮件链接 例如 mailto 功能 这是代码的一部分
  • 外键与独立关系 - Entity Framework 5 有改进吗?

    我读过了several http www ladislavmrnka com 2011 05 foreign key vs independent associations in ef 4 文章和问题 https stackoverflow
  • CMake 无法确定目标的链接器语言

    首先 我查看了this https stackoverflow com questions 11801186 cmake unable to determine linker language with c发帖并找不到解决我的问题的方法 我
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • C++ 条件编译

    我有以下代码片段 ifdef DO LOG define log p record p else define log p endif void record char data 现在如果我打电话log hello world 在我的代码中
  • 使用 .NET Process.Start 运行时挂起进程 - 出了什么问题?

    我在 svn exe 周围编写了一个快速而肮脏的包装器来检索一些内容并对其执行某些操作 但对于某些输入 它偶尔会重复挂起并且无法完成 例如 一个调用是 svn list svn list http myserver 84 svn Docum
  • 从列表中选择项目以求和

    我有一个包含数值的项目列表 我需要使用这些项目求和 我需要你的帮助来构建这样的算法 下面是一个用 C 编写的示例 描述了我的问题 int sum 21 List

随机推荐