机器学习算法实现解析——libFM之libFM的模型处理部分
时间:2022-05-04
本文章向大家介绍机器学习算法实现解析——libFM之libFM的模型处理部分,主要内容包括3.1、libFM中FM模型的定义、3.2、FM的初始化、3.3、利用FM模型对样本进行预测、3.4、其他、参考文献、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
本节主要介绍的是libFM源码分析的第三部分——libFM的模型处理。
3.1、libFM中FM模型的定义
libFM模型的定义过程中主要包括模型中参数的设置及其初始化,利用模型对样本进行预测。在libFM中,首先定义FM模型,在fm_model类
中实现对FM模型的定义,fm_model类在“libfm-1.42.srcsrcfm_corefm_model.h”中。在定义fm_model类
之前,使用到了一些数据类:
#include "../util/matrix.h"
#include "../util/fmatrix.h"
#include "fm_data.h"
数据类的具体定义在“机器学习算法实现解析——libFM之libFM的数据处理部分”中定义。fm_model类
的代码如下所示:
// fm_model模型类
class fm_model {
private:
DVector<double> m_sum, m_sum_sqr;// 分别对应着交叉项的中的两项
public: //fm模型中的参数
double w0;// 常数项
DVectorDouble w;// 一次项的系数
DMatrixDouble v;// 交叉项的系数矩阵
public:
// 属性
// the following values should be set:
uint num_attribute;// 特征的个数
bool k0, k1;// 是否包含常数项和一次项
int num_factor;// 交叉项因子的个数
double reg0;// 常数项的正则参数
double regw, regv;// 一次项和交叉项的正则系数
double init_stdev;// 初始化参数时的方差
double init_mean;// 初始化参数时的均值
// 函数
fm_model();// 构造函数,主要完成参数的初始化
void debug();// debug函数
void init();// 初始化函数,主要用于生成各维度系数的初始值
// 对样本进行预测
double predict(sparse_row<FM_FLOAT>& x);
double predict(sparse_row<FM_FLOAT>& x, DVector<double> &sum, DVector<double> &sum_sqr);
};
3.2、FM的初始化
完成FM模型初始化过程主要包括两个部分:
- 构造函数fm_model()
- init()函数
构造函数fm_model()的具体实现如下所示:
// fm_model类的构造函数
fm_model::fm_model() {
num_factor = 0;// 交叉项中因子的个数
init_mean = 0;// 初始化的均值
init_stdev = 0.01;// 初始化的方差
reg0 = 0.0;// 常数项的正则化参数
regw = 0.0;// 一次项的正则化参数
regv = 0.0;// 交叉项的正则化参数
k0 = true;// 是否包含常数项
k1 = true;// 是否包含一次项
}
init()函数的具体实现如下所示:
// 初始化fm模型的函数
void fm_model::init() {
w0 = 0;// 常数项的系数
w.setSize(num_attribute);// 设置一次项系数的个数
v.setSize(num_factor, num_attribute);// 设置交叉项的矩阵大小
w.init(0);// 初始化一次项系数为0
v.init(init_mean, init_stdev);// 按照均值和方差初始化交叉项系数
// 交叉项中的两个参数,设置其大小为num_factor
m_sum.setSize(num_factor);
m_sum_sqr.setSize(num_factor);
}
在初始化的过程中,除了基本的数据类型外,还涉及到自定义的三种数据类型,分别为:DVectorDouble,DMatrixDouble和DVector,这三种数据类型在“机器学习算法实现解析——libFM之libFM的数据处理部分”中有详细说明。
3.3、利用FM模型对样本进行预测
利用上面的计算公式,libFM中的两个函数的实现如下所示:
// 对样本进行预测,其中x表示的是一行样本
double fm_model::predict(sparse_row<FM_FLOAT>& x) {
return predict(x, m_sum, m_sum_sqr);
}
double fm_model::predict(sparse_row<FM_FLOAT>& x, DVector<double> &sum, DVector<double> &sum_sqr) {
double result = 0;// 最终的结果
// 第一部分
if (k0) {// 常数项
result += w0;
}
// 第二部分
if (k1) {// 一次项
for (uint i = 0; i < x.size; i++) {// 对样本中的每一个特征
assert(x.data[i].id < num_attribute);// 验证样本的正确性
// w * x
result += w(x.data[i].id) * x.data[i].value;
}
}
// 第三部分
// 交叉项,对应着公式,有两重循环
for (int f = 0; f < num_factor; f++) {// 外层循环
sum(f) = 0;
sum_sqr(f) = 0;
for (uint i = 0; i < x.size; i++) {
double d = v(f,x.data[i].id) * x.data[i].value;
sum(f) += d;
sum_sqr(f) += d*d;
}
result += 0.5 * (sum(f)*sum(f) - sum_sqr(f));// 得到交叉项的值
}
return result;
}
在交叉项的计算过程中,sum(f)和sum_sqr(f)与公式中的对应关系为:
3.4、其他
剩下的代码便是debug
函数,debug
函数用于打印中间的结果,其具体的代码如下所示:
// debug函数,主要用于输出中间调试的结果
void fm_model::debug() {
std::cout << "num_attributes=" << num_attribute << std::endl;// 特征的个数
std::cout << "use w0=" << k0 << std::endl;//是否包含常数项
std::cout << "use w1=" << k1 << std::endl;//是否包含一次项
std::cout << "dim v =" << num_factor << std::endl;//交叉项中因子的个数
std::cout << "reg_w0=" << reg0 << std::endl;//常数项的正则化参数
std::cout << "reg_w=" << regw << std::endl;//一次项的正则化参数
std::cout << "reg_v=" << regv << std::endl;//交叉项的正则化参数
std::cout << "init ~ N(" << init_mean << "," << init_stdev << ")" << std::endl;//初始化的均值和初始化的方差
}
参考文献
- 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.
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python文件处理实用指南
- 2. Pandas系列 - Series基本功能
- 1.3 广告算法专题 - 交叉验证
- 最好用的内网穿透工具合集
- JVM垃圾回收之垃圾回收器,程序员必须掌握的知识
- 5分钟Flink - 时间与语义案例详解
- 5分钟Flink - 时间语义和Watermark
- 3. Pandas系列 - DataFrame操作
- 4. Pandas系列 - 基本功能和统计操作
- 面经手册 · 第8篇《LinkedList插入速度比ArrayList快?你确定吗?》
- 无所不能的Embedding 2. FastText词向量&文本分类
- 5. Pandas系列 - 重建索引
- 6. Pandas系列 - 迭代
- Java 虚拟机基础原理:功能,内存管理,类的加载机制,分析字节码执行过程
- Stack有性能问题?推荐用ArrayDeque队列!队列是什么?什么是双端队列、延迟系列、阻塞队列,全是知识盲区!