教程目录
阅读:
因子分解机FM算法原理及实现过程(超详细)
对于FM算法的求解,在前几章中,主要是利用了梯度下降法,在梯度下降法中,在每一个的迭代过程中,利用全部的数据进行模型参数的学习,对于数据量特别大的情况,每次迭代求解所有样本需要花费大量的计算成本。
随机梯度下降法的优化过程为:
对于度为 2 的因子分解机 FM 模型,其主要的参数有一次项和常数项的参数 w0 ,w1 ,…,wn 以及交叉项的系数矩阵 V。在利用随机梯度对模型的参数进行学习的过程中,主要是对损失函数:
求导数,即:
而
为:
1) 初始化权重 w0,w1,…,wn 和 V;
2) 对每一个样本:
对特征 i∈{1,…,n}:
对 f ∈{1,…,k}:
3) 重复步骤 2),直到满足终止条件。
现在,让我们一起利用 Python 实现上述因子分解机 FM 的更新过程,首先,我们需要导入 numpy:
在程序中的 stocGradAscent 函数中,首先是初始化权重,其中在一次项和常数项的权重 w0 ,w1 ,…,wn 的初始化过程中,使用了比较简单的策略,即全部初始化为 0。对交叉项系数矩阵的初始化过程中,使用的正态分布对其进行初始化,函数 initialize_v 的具体实现如下程序所示。
随机梯度下降(Stochastic Gradient Descent)
随机梯度下降算法在每次迭代的过程中,仅根据一个样本对模型中的参数进行调整。随机梯度下降法的优化过程为:

基于随机梯度的方式求解
假设数据集中有m个训练样本,即 {X(1) ,X(2),…,X(m)},每一个样本 X(i) 有 n 个特征,即:
对于度为 2 的因子分解机 FM 模型,其主要的参数有一次项和常数项的参数 w0 ,w1 ,…,wn 以及交叉项的系数矩阵 V。在利用随机梯度对模型的参数进行学习的过程中,主要是对损失函数:

求导数,即:

而

为:

FM算法流程
利用随机梯度下降法对因子分解机 FM 模型中的参数进行学习的基本步骤如下:1) 初始化权重 w0,w1,…,wn 和 V;
2) 对每一个样本:

对特征 i∈{1,…,n}:

对 f ∈{1,…,k}:

3) 重复步骤 2),直到满足终止条件。
现在,让我们一起利用 Python 实现上述因子分解机 FM 的更新过程,首先,我们需要导入 numpy:
import numpy as np利用随机梯度下降法训练因子分解机 FM 模型的参数的具体过程如下程序所示。
def stocGradAscent(dataMatrix, classLabels, k, max_iter, alpha): '''利用随机梯度下降法训练FM模型 input: dataMatrix(mat)特征 classLabels(mat)标签 k(int)v的维数 max_iter(int)最大迭代次数 alpha(float)学习率 output: w0(float),w(mat),v(mat):权重 ''' m, n = np.shape(dataMatrix) # 1、初始化参数 w = np.zeros((n, 1)) # 其中n是特征的个数 w0 = 0 # 偏置项 v = initialize_v(n, k) # 初始化V # 2、训练 for it in xrange(max_iter): for x in xrange(m): # 随机优化,对每一个样本而言的 inter_1 = dataMatrix[x] * v inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * np.multiply(v, v) # multiply对应元素相乘 # 完成交叉项 interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2. p = w0 + dataMatrix[x] * w + interaction # 计算预测的输出 loss = sigmoid(classLabels[x] * p[0, 0]) - 1 w0 = w0 - alpha * loss * classLabels[x] for i in xrange(n): if dataMatrix[x, i] != 0: w[i, 0] = w[i, 0] - alpha * loss * classLabels[x] * dataMatrix[x, i] for j in xrange(k): v[i, j] = v[i, j] - alpha * loss * classLabels[x] * (dataMatrix[x, i] * inter_1[0, j] - v[i, j] * dataMatrix[x, i] * dataMatrix[x, i]) # 计算损失函数的值 if it % 1000 == 0: print " ------- iter: ", it, " , cost: ", getCost(getPrediction(np.mat(dataMatrix), w0, w, v), classLabels) # 3、返回最终的FM模型的参数 return w0, w, v该程序中的 stocGradAscent 函数实现了对因子分解机 FM 模型的学习,stocGradAscent 函数的输入 dataMatrix 为特征,classLabels 为样本的标签,k 为 FM 模型的度,max_iter 为随机梯度下降法的最大迭代次数,alpha 为随机梯度下降法的学习率。stocGradAscent 函数的输出为模型中的权重,包括一次项和常数项的权重 w0 ,w1 ,…,wn 以及交叉项的系数矩阵 V。
在程序中的 stocGradAscent 函数中,首先是初始化权重,其中在一次项和常数项的权重 w0 ,w1 ,…,wn 的初始化过程中,使用了比较简单的策略,即全部初始化为 0。对交叉项系数矩阵的初始化过程中,使用的正态分布对其进行初始化,函数 initialize_v 的具体实现如下程序所示。
def initialize_v(n, k): '''初始化交叉项 input: n(int)特征的个数 k(int)FM模型的超参数 output: v(mat):交叉项的系数权重 ''' v = np.mat(np.zeros((n, k))) for i in xrange(n): for j in xrange(k): # 利用正态分布生成每一个权重 v[i, j] = normalvariate(0, 0.2) return vinitialize_v 函数实现了对交叉项的权重进行初始化,initialize_v 函数的输入为特征的个数 n 和 FM 模型的度 k,其输出为交叉项的权重 v。在初始化的过程中,使用了正态分布对权重矩阵 V 中的每一个值生成随机数,为了能够使用正态分布对权重进行初始化,我们需要导入 normalvariate 函数:
from random import normalvariate #正态分布在初始化完所有模型参数后,利用每一个样本对其参数进行学习,并对常数项权重 w0、一次项权重 wi、交叉项的系数矩阵进行修正。sigmoid 函数的具体实现如下所示:
def sigmoid(inx): return 1.0 / (1 + np.exp(-inx))在每完成 1000 次迭代后,计算当前的损失函数的值,函数 getCost 的具体实现如下程序所示。
def getCost(predict, classLabels): '''计算预测准确性 input: predict(list)预测值 classLabels(list)标签 output: error(float)计算损失函数的值 ''' m = len(predict) error = 0.0 for i in xrange(m): error -= np.log(sigmoid(predict[i] * classLabels[i] )) return error函数 getCost 用于计算当前的损失函数的值,函数的输入是利用当前的 FM 模型对数据集的预测结果 predict 和样本的标签 classLabels,最终得到当前的损失函数值 error。