zoukankan      html  css  js  c++  java
  • FM因子分解机模型简介

    因子分解机模型简介

     

      Steffen Rendle于2010年提出Factorization Machines(下面简称FM),并发布开源工具libFM。

    一、与其他模型的对比

      与SVM相比,FM对特征之间的依赖关系用factorized parameters来表示。对于输入数据是非常稀疏(比如自动推荐系统),FM搞的定,而SVM搞不定,因为训出的SVM模型会面临较高的bias。还有一点,通常对带非线性核函数的SVM,需要在对偶问题上进行求解;而FM可以不用转为对偶问题,直接进行优化。

      目前还有很多不同的factorization models,比如matrix factorization和一些特殊的模型SVD++, PITF, FPMC。这些模型的一个缺点是它们只适用于某些特定的输入数据,优化算法也需要根据问题专门设计。而经过一些变换,可以看出FM囊括了这些方法。

    二、模型简介

      2-way FM(degree = 2)是FM中具有代表性,且比较简单的一种。就以其为例展开介绍。其对输出值是如下建模:

    Factorization Machines介绍 - vividfree - 罗维的BLOG

       公式说明:

      (1) 模型的参数包括:Factorization Machines介绍 - vividfree - 罗维的BLOG

      (2) n是特征维度;

      (3) k是定义factorization维度的超参数,是正整数。

      (4) 在FM中,如何对特征之间的依赖关系建模?首先用k维向量去描述每个维度,然后对2个维度所对应的vi和vj求点积,以此来刻画2个特征之间的特征关系。如果需要刻画x(x>2)个特征之间的关系,可以用x-way FM模型。

      (5) 表面上看式子的第3项的计算复杂度为O(kn2),但其实可以经过简单的数学处理,计算复杂度降为O(kn)。

    三、模型分析

      他的思想应该是从推荐系统中经典的SVD模型(因子分解模型)得到的,经典的SVD模型当中相当于只有两种类型的feature,一类feature是user,一类feature是item,而libFM是把这个模型推广到了多类feature的情况。为简单起见,考虑因子维数为1的情况,SVD模型用aba∗b来作为对打分的预测。而libFM要面对的是多类feature,假设是3类,那么就用ab+bc+caa∗b+b∗c+c∗a来作为对结果的预测。这时候就要问了,如果feature很多,这不就有平方量级的乘法次数了么?当然不是,libFM的文章中提到,他利用((a+b+c)2a2b2c2)/2((a+b+c)2−a2−b2−c2)/2来计算刚才的式子,但是你可以看到,他们其实是相等的,不同的是,这样的计算量只是线性复杂度的。当然libFM也同时支持bias项,这和经典SVD模型类似。
      以上就是libFM的创新之处,其实如果很了解SVD模型,那这个改进并不难理解。
    论文中还提到,经典的SVD++模型等对于SVD模型的改进,也只是libFM的一个子集而已。只要合适的去添加feature即可。比如SVD++模型就相当于对每个item增加一个feature,来描述用户是否也给这个item打过分即可。所以有了libFM以后,最需要人工解决的问题就是添加合适的feature了。
    另外再说明一下推荐系统的数据如何转化成libFM接受的形式。假设User ID范围是[0,99],Item ID范围是[0,199],则定义feature 0到feature 99对应于User,feature 100到feature 299对应于Item,假设第一条打分记录是User 4对Item 9的打分,则feature 4和feature 109的取值为1,其余feature取值都是0。由于数据文件是稀疏格式的,所以取值为0的feature都不用写,这样文件不会太大。其余对经典SVD模型的改进就需要增加一些对应feature。他的代码中,每条记录是使用map存储feature的,可以随机存取任意一个feature的值(但是可能用链表就可以了?因为一般都是顺序访问的)。

      FM可以用于多种预测问题,包括回归、分类和排序。对不同的预测问题,可以选择合适的loss term和regularization term。另外,FM的梯度也很容易求。

    简单易学的机器学习算法——因子分解机(Factorization Machine)

    https://blog.csdn.net/google19890102/article/details/45532745/
    实验(求解二分类问题)

    1、实验的代码:

    from __future__ import division
    from math import exp
    from numpy import *
    from random import normalvariate#正态分布
    from datetime import datetime
     
    trainData = 'E://data//diabetes_train.txt'
    testData = 'E://data//diabetes_test.txt'
    featureNum = 8
     
    def loadDataSet(data):
        dataMat = []
        labelMat = []
        
        fr = open(data)#打开文件
        
        for line in fr.readlines():
            currLine = line.strip().split()
            #lineArr = [1.0]
            lineArr = []
            
            for i in xrange(featureNum):
                lineArr.append(float(currLine[i + 1]))
            dataMat.append(lineArr)
            
            labelMat.append(float(currLine[0]) * 2 - 1)
        return dataMat, labelMat
     
    def sigmoid(inx):
        return 1.0 / (1 + exp(-inx))
     
    def stocGradAscent(dataMatrix, classLabels, k, iter):
        #dataMatrix用的是mat, classLabels是列表
        m, n = shape(dataMatrix)
        alpha = 0.01
        #初始化参数
        w = zeros((n, 1))#其中n是特征的个数
        w_0 = 0.
        v = normalvariate(0, 0.2) * ones((n, k))
        
        for it in xrange(iter):
            print it
            for x in xrange(m):#随机优化,对每一个样本而言的
                inter_1 = dataMatrix[x] * v
                inter_2 = multiply(dataMatrix[x], dataMatrix[x]) * multiply(v, v)#multiply对应元素相乘
                #完成交叉项
                interaction = sum(multiply(inter_1, inter_1) - inter_2) / 2.
                
                p = w_0 + dataMatrix[x] * w + interaction#计算预测的输出
            
                loss = sigmoid(classLabels[x] * p[0, 0]) - 1
                print loss
            
                w_0 = w_0 - 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])
            
        
        return w_0, w, v
     
    def getAccuracy(dataMatrix, classLabels, w_0, w, v):
        m, n = shape(dataMatrix)
        allItem = 0
        error = 0
        result = []
        for x in xrange(m):
            allItem += 1
            inter_1 = dataMatrix[x] * v
            inter_2 = multiply(dataMatrix[x], dataMatrix[x]) * multiply(v, v)#multiply对应元素相乘
            #完成交叉项
            interaction = sum(multiply(inter_1, inter_1) - inter_2) / 2.
            p = w_0 + dataMatrix[x] * w + interaction#计算预测的输出
            
            pre = sigmoid(p[0, 0])
            
            result.append(pre)
            
            if pre < 0.5 and classLabels[x] == 1.0:
                error += 1
            elif pre >= 0.5 and classLabels[x] == -1.0:
                error += 1
            else:
                continue
            
        
        print result
        
        return float(error) / allItem
            
       
    if __name__ == '__main__':
        dataTrain, labelTrain = loadDataSet(trainData)
        dataTest, labelTest = loadDataSet(testData)
        date_startTrain = datetime.now()
        print "开始训练"
        w_0, w, v = stocGradAscent(mat(dataTrain), labelTrain, 20, 200)
        print "训练准确性为:%f" % (1 - getAccuracy(mat(dataTrain), labelTrain, w_0, w, v))
        date_endTrain = datetime.now()
        print "训练时间为:%s" % (date_endTrain - date_startTrain)
        print "开始测试"
        print "测试准确性为:%f" % (1 - getAccuracy(mat(dataTest), labelTest, w_0, w, v)) 

    2、实验结果:

    五、几点疑问
        在传统的非稀疏数据集上,有时效果并不是很好。在实验中,我有一点处理,即在求解Sigmoid函数的过程中,在有的数据集上使用了带阈值的求法:
    def sigmoid(inx):
    #return 1.0 / (1 + exp(-inx))
    return 1. / (1. + exp(-max(min(inx, 15.), -15.)))

    欢迎更多的朋友一起讨论这个算法。


    参考文章
    1、Rendle, Factorization Machines.
    2、Factorization Machines with libFM

  • 相关阅读:
    [转]javaweb学习总结(二十二)——基于Servlet+JSP+JavaBean开发模式的用户登录注册
    [转]javaweb学习总结(二十一)——JavaWeb的两种开发模式
    [转]javaweb学习总结(二十)——JavaBean总结
    [转]javaweb学习总结(十九)——JSP标签
    [转]javaweb学习总结(十八)——JSP属性范围
    [转]JavaWeb学习总结(十七)——JSP中的九个内置对象
    [转]javaweb学习总结(十六)——JSP指令
    [转]javaweb学习总结(十五)——JSP基础语法
    TypeScript
    TypeScript
  • 原文地址:https://www.cnblogs.com/timssd/p/12575748.html
Copyright © 2011-2022 走看看