机器学习算法实现解析——libFM之libFM的训练过程概述

机器学习算法实现解析——libFM之libFM的训练过程概述

本节主要介绍的是libFM源码分析的第四部分——libFM的训练。

FM模型的训练是FM模型的核心的部分。

4.1、libFM中训练过程的实现

在FM模型的训练过程中,libFM源码中共提供了四种训练的方法,分别为:Stochastic Gradient Descent(SGD),Adaptive SGD(ASGD),Alternating Least Squares(ALS)和Markov Chain Monte Carlo(MCMC),其中ALS是MCMC的特殊形式,实际上其实现的就是SGD,ASGD和MCMC三种训练方法,三者的类之间的关系如下图所示:   ![这里写图片描述](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNTI3MTQwODEwMDQy?x-oss-process=image/format,png)

FM模型训练的父类为fm_learn,其定义在文件fm_learn.h中,fm_learn_sgd类和fm_learn_mcmc类分别继承自fm_learn类。其中,fm_learn_sgd是基于梯度的实现方法,fm_learn_mcmc是基于蒙特卡洛的实现方法。

fm_learn_sgd_element类和fm_learn_sgd_element_adapt_reg类是fm_learn_sgd类的子类,是两种具体的基于梯度方法的实现,分别为SGD和ASGD。

fm_learn_mcmc_simultaneous类是fm_learn_mcmc类的子类,是具体的基于蒙特卡洛方法的实现。

4.2、训练过程的父类

在所有的训练过程中,fm_learn类为所有模型训练类的父类。

4.2.1、头文件

#include <cmath>
#include "Data.h"
#include "../../fm_core/fm_model.h"
#include "../../util/rlog.h"
#include "../../util/util.h"

4.2.2、第一部分的protected属性和方法

在这部分中定义了交叉项中需要用到两个数据,分别为sum和sum_sqr,这两个数的具体使用可以参见“”。除此之外,还定义了预测predict_case函数,具体代码如下所示:

protected:
		DVector<double> sum, sum_sqr;// FM模型的交叉项中的两项
		DMatrix<double> pred_q_term;
		
		// this function can be overwritten (e.g. for MCMC)
		// 预测,使用的是fm_model中的predict函数
		virtual double predict_case(Data& data) {
			return fm->predict(data.data->getRow());
		}

其中,预测predict_case函数使用的是fm_model类中的predict函数,对于该函数,可以参见“”。

4.2.3、第二部分的public属性和方法

在这部分中,主要构造函数fm_learn函数,初始化init函数以及评估evaluate函数,其具体代码如下所示:

public:
		DataMetaInfo* meta;
		fm_model* fm;// 对应的fm模型
		double min_target;// 设置的预测值的最小值
		double max_target;// 设置的预测值的最大值
		
		// task用于区分不同的任务:0表示的是回归,1表示的是分类
		int task; // 0=regression, 1=classification	
		// 定义两个常量,分别表示的是回归和分类
		const static int TASK_REGRESSION = 0;
		const static int TASK_CLASSIFICATION = 1;
 
		Data* validation;// 验证数据集	
		RLog* log;// 日志指针
		
		// 构造函数,初始化变量,实例化的过程在main函数中
		fm_learn() { log = NULL; task = 0; meta = NULL;} 		
		
		virtual void init() {
			// 日志
			if (log != NULL) {
				if (task == TASK_REGRESSION) {
					log->addField("rmse", std::numeric_limits<double>::quiet_NaN());
					log->addField("mae", std::numeric_limits<double>::quiet_NaN());
				} else if (task == TASK_CLASSIFICATION) {
					log->addField("accuracy", std::numeric_limits<double>::quiet_NaN());
				} else {
					throw "unknown task";
				}
				log->addField("time_pred", std::numeric_limits<double>::quiet_NaN());
				log->addField("time_learn", std::numeric_limits<double>::quiet_NaN());
				log->addField("time_learn2", std::numeric_limits<double>::quiet_NaN());
				log->addField("time_learn4", std::numeric_limits<double>::quiet_NaN());
			}
			// 设置交叉项中的两项的大小
			sum.setSize(fm->num_factor);
			sum_sqr.setSize(fm->num_factor);
			
			pred_q_term.setSize(fm->num_factor, meta->num_relations + 1);
		}
		
		// 对数据的评估
		virtual double evaluate(Data& data) {
			assert(data.data != NULL);// 检查数据不为空
			if (task == TASK_REGRESSION) {// 回归
				return evaluate_regression(data);// 调用回归的评价方法
			} else if (task == TASK_CLASSIFICATION) {// 分类
				return evaluate_classification(data);// 调用分类的评价放啊
			} else {
				throw "unknown task";
			}
		}

在评估evaluate函数中,根据task的值判断是分类问题还是回归问题,分别调用第四部分中的evaluate_regressionevaluate_classification函数。

4.2.4、第三部分的public属性和方法

在这部分中分别定义了模型的训练函数,模型的预测函数和debug输出函数,代码的具体过程如下所示:

public:
		// 模型的训练过程
		virtual void learn(Data& train, Data& test) { }
		
		// 纯虚函数
		virtual void predict(Data& data, DVector<double>& out) = 0;
		
		// debug函数,用于打印中间的结果
		virtual void debug() { 
			std::cout << "task=" << task << std::endl;
			std::cout << "min_target=" << min_target << std::endl;
			std::cout << "max_target=" << max_target << std::endl;		
		}

其中模型的训练learn函数没有定义具体的实现,由上述的继承关系,其具体的训练过程在具体的子类中实现;模型的预测predict函数是一个纯虚函数。对于纯虚函数的概念,可以参见;最后一个函数是一个debug函数,debug函数用于打印中间的结果。

4.2.5、第四部分的protected属性和方法

在这部分中定义了两个评价函数,分别用于处理分类问题和回归问题,代码的具体过程如下所示:

protected:
		// 对分类问题的评价
		virtual double evaluate_classification(Data& data) {
			int num_correct = 0;// 准确类别的个数
			double eval_time = getusertime();
			for (data.data->begin(); !data.data->end(); data.data->next()) {
				double p = predict_case(data);// 对样本进行预测
				// 利用预测值的符号与原始标签值的符号是否相同,若相同,则预测是准确的
				if (((p >= 0) && (data.target(data.data->getRowIndex()) >= 0)) || ((p < 0) && (data.target(data.data->getRowIndex()) < 0))) {
					num_correct++;
				}	
			}	
			eval_time = (getusertime() - eval_time);
			// log the values
			// log文件
			if (log != NULL) {
				log->log("accuracy", (double) num_correct / (double) data.data->getNumRows());
				log->log("time_pred", eval_time);
			}
			
			return (double) num_correct / (double) data.data->getNumRows();// 返回准确率
		}
		
		// 对回归问题的评价
		virtual double evaluate_regression(Data& data) {
			double rmse_sum_sqr = 0;// 误差的平方和
			double mae_sum_abs = 0;// 误差的绝对值之和
			double eval_time = getusertime();
			for (data.data->begin(); !data.data->end(); data.data->next()) {
				// 取出每一条样本
				double p = predict_case(data);// 计算该样本的预测值
				p = std::min(max_target, p);// 防止预测值超出最大限制
				p = std::max(min_target, p);// 防止预测值超出最小限制
				double err = p - data.target(data.data->getRowIndex());// 得到预测值与真实值之间的误差
				rmse_sum_sqr += err*err;// 计算误差平方和
				mae_sum_abs += std::abs((double)err);// 计算误差的绝对值之和	
			}	
			eval_time = (getusertime() - eval_time);
			// log the values
			// log文件
			if (log != NULL) {
				log->log("rmse", std::sqrt(rmse_sum_sqr/data.data->getNumRows()));
				log->log("mae", mae_sum_abs/data.data->getNumRows());
				log->log("time_pred", eval_time);
			}
			return std::sqrt(rmse_sum_sqr/data.data->getNumRows());// 返回均方根误差
		}

其中,在分类问题中,使用的评价标准是准确率:

# ( y ^ ⋅ y > 0 ) m \frac{\#\left ( \hat{y}\cdot y >0 \right )}{m} m#(y^​⋅y>0)​

在回归问题中,使用的评价标准是均方根误差:

( y ^ − y ) 2 m \sqrt{\frac{\left ( \hat{y}-y \right )^2}{m}} m(y^​−y)2​   ​

其中, y ^ \hat{y} y^​表示的是对样本的预测值, y y y表示的是样本的原始标签, # ( y ^ ⋅ y > 0 ) \#\left ( \hat{y}\cdot y >0 \right ) #(y^​⋅y>0)表示的是预测值 y ^ \hat{y} y^​与原始标签 y y y同号的样本的个数(原始标签 y ∈ { − 1 , 1 } y\in \left \{ -1,1 \right \} y∈{−1,1}), m m m表示的是样本的个数。

在对样本进行预测时用到了predict_case函数,该函数在“第一部分的protected属性和方法“中定义。在回归问题中,为预测值设置了最大的上限(std::max(min_target, p))和最小的下限(std::min(max_target, p))。为了能够记录时间,代码中使用到了getusertime函数,该函数的定义在util.h文件中。

参考文献

  • Rendle S. Factorization Machines[C]// IEEE International Conference on Data Mining. IEEE Computer Society, 2010:995-1000.
  • Rendle S. Factorization Machines with libFM[M]. ACM, 2012.

Read more

60个“特征工程”计算函数(Python代码)

60个“特征工程”计算函数(Python代码)

转自:coggle数据科学 近期一些朋友询问我关于如何做特征工程的问题,有没有什么适合初学者的有效操作。 特征工程的问题往往需要具体问题具体分析,当然也有一些暴力的策略,可以在竞赛初赛前期可以带来较大提升,而很多竞赛往往依赖这些信息就可以拿到非常好的效果,剩余的则需要结合业务逻辑以及很多其他的技巧,此处我们将平时用得最多的聚合操作罗列在下方。 最近刚好看到一篇文章汇总了非常多的聚合函数,就摘录在下方,供许多初入竞赛的朋友参考。 聚合特征汇总 pandas自带的聚合函数 * 其它重要聚合函数 其它重要聚合函数&分类分别如下。 def median(x):     return np.median(x) def variation_coefficient(x):     mean = np.mean(x)     if mean != 0:         return np.std(x) / mean     else:         return np.nan def variance(x):     return

By Ne0inhk
90w,确实可以封神了!

90w,确实可以封神了!

要说24年一定最热的技术,还得是AIGC! 前段时间阿里旗下的开源项目,登上GitHub热榜! AI大热,如今ChatGPT的优异表现,必然会出现各种细分场景应用的工具软件,和大量岗位项目! 山雨欲来风满楼,强人工智能的出现,所有科技公司已经开始巨量扩招此领域的人才。算法的岗位,近三个月已经增长68%!这件事在HR届也是相当震撼的。 目前各行各业都不景气的市场,人工智能岗位却一直保持常青!甚至同属AI边缘岗都比其他岗薪资高40%! 与此同时,AI算法岗上岸也不简单,竞争激烈,好公司核心岗位不用说,谁都想去。 所以事实就是,想要上岸,门槛也逐渐变高,项目经历、实习经历都很重要,越早明白这个道理就越能提前建立起自己的优势。 但我在b站逛知识区的时候,经常看到有些同学,因为一些客观原因导致无法参加实习,这种情况下,如果你想提升背景,增加项目经历的话,可以试试这个《CV/NLP 算法工程师培养计划》。 目前已经有上千位同学通过该计划拿到offer了,最新一期学员就业薪资最高能拿到78K!年薪94w! 优势就是有BAT大厂讲师带领,手把手带做AI真实企业项目(包含CV、NLP等

By Ne0inhk
再见nohup!试试这个神器,Python Supervisor!

再见nohup!试试这个神器,Python Supervisor!

👇我的小册 45章教程:() ,原价299,限时特价2杯咖啡,满100人涨10元。 作者丨Ais137 https://juejin.cn/post/7354406980784373798 1. 概述 Supervisor 是一个 C/S 架构的进程监控与管理工具,本文主要介绍其基本用法和部分高级特性,用于解决部署持久化进程的稳定性问题。 2. 问题场景 在实际的工作中,往往会有部署持久化进程的需求,比如接口服务进程,又或者是消费者进程等。这类进程通常是作为后台进程持久化运行的。 一般的部署方法是通过 nohup cmd & 命令来部署。但是这种方式有个弊端是在某些情况下无法保证目标进程的稳定性运行,有的时候 nohup 运行的后台任务会因为未知原因中断,从而导致服务或者消费中断,进而影响项目的正常运行。 为了解决上述问题,通过引入 Supervisor 来部署持久化进程,提高系统运行的稳定性。 3. Supervisor 简介 Supervisor is a client/

By Ne0inhk
第一本给程序员看的AI Agent图书上市了!

第一本给程序员看的AI Agent图书上市了!

AI Agent火爆到什么程度? OpenAI创始人奥特曼预测,未来各行各业,每一个人都可以拥有一个AI Agent;比尔·盖茨在2023年层预言:AI Agent将彻底改变人机交互方式,并颠覆整个软件行业;吴恩达教授在AI Ascent 2024演讲中高赞:AI Agent是一个令人兴奋的趋势,所有从事AI开发的人都应该关注。而国内的各科技巨头也纷纷布局AI Agent平台,如:钉钉的AI PaaS、百度智能云千帆大模型平台等等。 Agent 是未来最重要的智能化工具。对于程序员来说,是时候将目光转向大模型的应用开发了,率先抢占AI的下一个风口AI Agent。 小异带来一本新书《大模型应用开发 动手做 AI Agent》,这本书由《GPT图解》的作者黄佳老师创作,从0到1手把手教你做AI Agent。现在下单享受5折特惠! ▼点击下方,即可5折起购书 有这样一本秘籍在手,程序员们这下放心了吧,让我们先来揭开 Agent 的神秘面纱。 AI Agent 面面观

By Ne0inhk