JavaDay20

学习来源:日撸 Java 三百行(61-70天,决策树与集成学习)_闵帆的博客——CSDN博客

矩阵分解

1.奇异值分解

        奇异值分解(SVD)是一种矩阵分解算法,用于将矩阵简化为其组成部分,从而简化后续矩阵计算。SVD的表达式为:

A = U\Sigma V^{T}

其中: \Sigma 为以 AA^{T} 或 A^{T}A 的 特征值的平方根为主对角线元素的对角阵; U 是由 AA^{T} 的特征向量组成的矩阵; V

        例:矩阵  A = \begin{bmatrix} 1 & 2\\ 3 & 6 \end{bmatrix} , 则 AA^{T}= \begin{bmatrix} 5 &15 \\ 15&45 \end{bmatrix}  ,特征值为50和0,特征向量为 \begin{bmatrix} \frac{1}{\sqrt{10}}\\\frac{3}{\sqrt{10}} \end{bmatrix} 和\begin{bmatrix} \frac{-3}{\sqrt{10}}\\\frac{1}{\sqrt{10}} \end{bmatrix};  A^{T}A = \begin{bmatrix} 10 &20 \\ 20& 40 \end{bmatrix}  ,特征向量为 \begin{bmatrix} \frac{1}{\sqrt{5}}\\ \frac{2}{\sqrt{5}} \end{bmatrix} 和 \begin{bmatrix} \frac{-2}{\sqrt{5}}\\\frac{1}{\sqrt{5}} \end{bmatrix};因此得到SVD分解:

A = \begin{bmatrix} \frac{1}{\sqrt{10}}& \frac{-3}{\sqrt{10}} \\ \frac{3}{\sqrt{10}}& \frac{1}{\sqrt{10}} \end{bmatrix} \times \begin{bmatrix} 5\sqrt{2} & 0\\ 0 &0 \end{bmatrix} \times \begin{bmatrix} \frac{1}{\sqrt{5}} &\frac{2}{\sqrt{5}} \\ \frac{-2}{\sqrt{5}}& \frac{1}{\sqrt{5}} \end{bmatrix}

 2.矩阵分解

        矩阵分解算法是由奇异值分解算法(SVD)演变而来,传统的SVD只能对数据稠密的矩阵进行分解。而在推荐系统中,评分矩阵是极度稀疏的,因此对SVD进行改进来对用户对项目的评分进行预测。

3.BasicSVD

        BasicSVD是最简单的矩阵分解方法,它将评分矩阵分解为两个低阶矩阵的乘积,在实际推荐计算时不再使用原矩阵,而是使用分解得到的两个低阶矩阵。

img

 图1.评分矩阵例子

 图1描述了5个用户(U_{1},U_{2},U_{3},U_{4},U_{5})对4个物品(D_{1},D_{2},D_{3},D_{4})的评分(1-5分) ,  “ – ”代表没有评分。通过矩阵分解可以对缺失的评分进行预测。对于矩阵分解有公式:

R\approx U \times I^{T} =\hat{R}

 R 对应图1的评分矩阵,U 为 5 \times k 的矩阵,I^{T} 为k \times 4 的矩阵, \hat{R} (5\times 4)为预测评分矩阵。

4.代码部分

        1)衡量矩阵分解优劣的指标采用的是MAE。即预测评分与实际评分差值的绝对值之和与预测评分数之比。MAE的值越小越好。

        2)由学习因子 \alpha 和预测评分误差共同作用于每轮训练子矩阵的更新。

        3)代码如下:

package JavaDay20;

import java.io.*;
import java.util.Random;

/**
 * Matrix factorization for recommender systems.
 *
 * @author Ke-Xiong Wang
 */

public class MatrixFactorization {
    /**
     * Used to generate random numbers.
     */
    Random rand = new Random();

    /**
     * Number of users.
     */
    int numUsers;

    /**
     * Number of items.
     */
    int numItems;

    /**
     * Number of ratings.
     */
    int numRatings;

    /**
     * Training data.
     */
    Triple[] dataset;

    /**
     * A parameter for controlling learning regular.
     */
    double alpha;

    /**
     * A parameter for controlling the learning speed.
     */
    double lambda;

    /**
     * The low rank of the small matrices.
     */
    int rank;

    /**
     * The user matrix U.
     */
    double[][] userSubspace;

    /**
     * The item matrix V.
     */
    double[][] itemSubspace;

    /**
     * The lower bound of the rating value.
     */
    double ratingLowerBound;

    /**
     * The upper bound of the rating value.
     */
    double ratingUpperBound;

    /**
     ************************
     * The first constructor.
     *
     * @param paraFilename
     *            The data filename.
     * @param paraNumUsers
     *            The number of users.
     * @param paraNumItems
     *            The number of items.
     * @param paraNumRatings
     *            The number of ratings.
     ************************
     */
    public MatrixFactorization(String paraFilename, int paraNumUsers, int paraNumItems,
                               int paraNumRatings, double paraRatingLowerBound, double paraRatingUpperBound) {
        numUsers = paraNumUsers;
        numItems = paraNumItems;
        numRatings = paraNumRatings;
        ratingLowerBound = paraRatingLowerBound;
        ratingUpperBound = paraRatingUpperBound;

        try {
            readData(paraFilename, paraNumUsers, paraNumItems, paraNumRatings);
            // adjustUsingMeanRating();
        } catch (Exception ee) {
            System.out.println("File " + paraFilename + " cannot be read! " + ee);
            System.exit(0);
        } // Of try
    }// Of the first constructor

    /**
     ************************
     * Set parameters.
     *
     * @param paraRank
     *            The given rank.
     * @throws IOException
     ************************
     */
    public void setParameters(int paraRank, double paraAlpha, double paraLambda) {
        rank = paraRank;
        alpha = paraAlpha;
        lambda = paraLambda;
    }// Of setParameters

    /**
     ************************
     * Read the data from the file.
     *
     * @param paraFilename
     *            The given file.
     * @throws IOException
     ************************
     */
    public void readData(String paraFilename, int paraNumUsers, int paraNumItems,
                         int paraNumRatings) throws IOException {
        File tempFile = new File(paraFilename);
        if (!tempFile.exists()) {
            System.out.println("File " + paraFilename + " does not exists.");
            System.exit(0);
        } // Of if
        BufferedReader tempBufferReader = new BufferedReader(new FileReader(tempFile));

        // Allocate space.
        dataset = new Triple[paraNumRatings];
        String tempString;
        String[] tempStringArray;
        for (int i = 0; i < paraNumRatings; i++) {
            tempString = tempBufferReader.readLine();
            tempStringArray = tempString.split(",");
            dataset[i] = new Triple(Integer.parseInt(tempStringArray[0]),
                    Integer.parseInt(tempStringArray[1]), Double.parseDouble(tempStringArray[2]));
        } // Of for i

        tempBufferReader.close();
    }// Of readData

    /**
     ************************
     * Initialize subspaces. Each value is in [0, 1].
     ************************
     */
    void initializeSubspaces() {
        userSubspace = new double[numUsers][rank];

        for (int i = 0; i < numUsers; i++) {
            for (int j = 0; j < rank; j++) {
                userSubspace[i][j] = rand.nextDouble();
            } // Of for j
        } // Of for i

        itemSubspace = new double[numItems][rank];
        for (int i = 0; i < numItems; i++) {
            for (int j = 0; j < rank; j++) {
                itemSubspace[i][j] = rand.nextDouble();
            } // Of for j
        } // Of for i
    }// Of initializeSubspaces

    /**
     ************************
     * Predict the rating of the user to the item
     *
     * @param paraUser
     *            The user index.
     ************************
     */
    public double predict(int paraUser, int paraItem) {
        double resultValue = 0;
        for (int i = 0; i < rank; i++) {
            // The row vector of a user and the column vector of an item
            resultValue += userSubspace[paraUser][i] * itemSubspace[paraItem][i];
        } // Of for i
        return resultValue;
    }// Of predict

    /**
     ************************
     * Train.
     *
     * @param paraRounds
     *            The number of rounds.
     ************************
     */
    public void train(int paraRounds) {
        initializeSubspaces();

        for (int i = 0; i < paraRounds; i++) {
            updateNoRegular();
            if (i % 50 == 0) {
                // Show the process
                System.out.println("Round " + i);
                System.out.println("MAE: " + mae());
            } // Of if
        } // Of for i
    }// Of train

    /**
     ************************
     * Update sub-spaces using the training data.
     ************************
     */
    public void updateNoRegular() {
        for (int i = 0; i < numRatings; i++) {
            int tempUserId = dataset[i].user;
            int tempItemId = dataset[i].item;
            double tempRate = dataset[i].rating;

            double tempResidual = tempRate - predict(tempUserId, tempItemId); // Residual

            // Update user subspace
            double tempValue = 0;
            for (int j = 0; j < rank; j++) {
                tempValue = 2 * tempResidual * itemSubspace[tempItemId][j];
                userSubspace[tempUserId][j] += alpha * tempValue;
            } // Of for j

            // Update item subspace
            for (int j = 0; j < rank; j++) {
                tempValue = 2 * tempResidual * userSubspace[tempUserId][j];

                itemSubspace[tempItemId][j] += alpha * tempValue;
            } // Of for j
        } // Of for i
    }// Of updateNoRegular

    /**
     ************************
     * Compute the RSME.
     *
     * @return RSME of the current factorization.
     ************************
     */
    public double rsme() {
        double resultRsme = 0;
        int tempTestCount = 0;

        for (int i = 0; i < numRatings; i++) {
            int tempUserIndex = dataset[i].user;
            int tempItemIndex = dataset[i].item;
            double tempRate = dataset[i].rating;

            double tempPrediction = predict(tempUserIndex, tempItemIndex);// +
            // DataInfo.mean_rating;

            if (tempPrediction < ratingLowerBound) {
                tempPrediction = ratingLowerBound;
            } else if (tempPrediction > ratingUpperBound) {
                tempPrediction = ratingUpperBound;
            } // Of if

            double tempError = tempRate - tempPrediction;
            resultRsme += tempError * tempError;
            tempTestCount++;
        } // Of for i

        return Math.sqrt(resultRsme / tempTestCount);
    }// Of rsme

    /**
     ************************
     * Compute the MAE.
     *
     * @return MAE of the current factorization.
     ************************
     */
    public double mae() {
        double resultMae = 0;
        int tempTestCount = 0;

        for (int i = 0; i < numRatings; i++) {
            int tempUserIndex = dataset[i].user;
            int tempItemIndex = dataset[i].item;
            double tempRate = dataset[i].rating;

            double tempPrediction = predict(tempUserIndex, tempItemIndex);

            if (tempPrediction < ratingLowerBound) {
                tempPrediction = ratingLowerBound;
            } // Of if
            if (tempPrediction > ratingUpperBound) {
                tempPrediction = ratingUpperBound;
            } // Of if

            double tempError = tempRate - tempPrediction;

            resultMae += Math.abs(tempError);
            // System.out.println("resultMae: " + resultMae);
            tempTestCount++;
        } // Of for i

        return (resultMae / tempTestCount);
    }// Of mae

    /**
     ************************
     * Compute the MAE.
     *
     * @return MAE of the current factorization.
     ************************
     */
    public static void testTrainingTesting(String paraFilename, int paraNumUsers, int paraNumItems,
                                           int paraNumRatings, double paraRatingLowerBound, double paraRatingUpperBound,
                                           int paraRounds) {
        try {
            // Step 1. read the training and testing data
            MatrixFactorization tempMF = new MatrixFactorization(paraFilename, paraNumUsers,
                    paraNumItems, paraNumRatings, paraRatingLowerBound, paraRatingUpperBound);

            tempMF.setParameters(5, 0.0001, 0.005);

            // Step 3. update and predict
            System.out.println("Begin Training ! ! !");
            tempMF.train(paraRounds);

            double tempMAE = tempMF.mae();
            double tempRSME = tempMF.rsme();
            System.out.println("Finally, MAE = " + tempMAE + ", RSME = " + tempRSME);
        } catch (Exception e) {
            e.printStackTrace();
        } // Of try
    }// Of testTrainingTesting

    /**
     ************************
     * @param args
     ************************
     */
    public static void main(String args[]) {
        testTrainingTesting("D:/data/movielens-943u1682m.txt", 943, 1682, 10000, 1, 5, 2000);
    }// Of main

    public class Triple {
        public int user;
        public int item;
        public double rating;

        /**
         *********************
         * The constructor.
         *********************
         */
        public Triple() {
            user = -1;
            item = -1;
            rating = -1;
        }// Of the first constructor

        /**
         *********************
         * The constructor.
         *********************
         */
        public Triple(int paraUser, int paraItem, double paraRating) {
            user = paraUser;
            item = paraItem;
            rating = paraRating;
        }// Of the first constructor

        /**
         *********************
         * Show me.
         *********************
         */
        public String toString() {
            return "" + user + ", " + item + ", " + rating;
        }// Of toString
    }// Of class Triple
}// Of class MatrixFactorization

运行结果

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2022年5月26日
下一篇 2022年5月26日

相关推荐