All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hivemall.factorization.mf.FactorizedModel Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package hivemall.factorization.mf;

import hivemall.utils.math.MathUtils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;

import java.util.Random;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;

@NotThreadSafe
public final class FactorizedModel {

    @Nonnull
    private final RatingInitializer ratingInitializer;
    @Nonnegative
    private final int factor;

    // rank matrix initialization
    private final RankInitScheme initScheme;

    private int minIndex, maxIndex;
    @Nonnull
    private Rating meanRating;
    private Int2ObjectMap users;
    private Int2ObjectMap items;
    private Int2ObjectMap userBias;
    private Int2ObjectMap itemBias;

    private final Random[] randU, randI;

    public FactorizedModel(@Nonnull RatingInitializer ratingInitializer, @Nonnegative int factor,
            @Nonnull RankInitScheme initScheme) {
        this(ratingInitializer, factor, 0.f, initScheme, 136861);
    }

    public FactorizedModel(@Nonnull RatingInitializer ratingInitializer, @Nonnegative int factor,
            float meanRating, @Nonnull RankInitScheme initScheme) {
        this(ratingInitializer, factor, meanRating, initScheme, 136861);
    }

    public FactorizedModel(@Nonnull RatingInitializer ratingInitializer, @Nonnegative int factor,
            float meanRating, @Nonnull RankInitScheme initScheme, int expectedSize) {
        this.ratingInitializer = ratingInitializer;
        this.factor = factor;
        this.initScheme = initScheme;
        this.minIndex = 0;
        this.maxIndex = 0;
        this.meanRating = ratingInitializer.newRating(meanRating);
        this.users = new Int2ObjectOpenHashMap(expectedSize);
        this.items = new Int2ObjectOpenHashMap(expectedSize);
        this.userBias = new Int2ObjectOpenHashMap(expectedSize);
        this.itemBias = new Int2ObjectOpenHashMap(expectedSize);
        this.randU = newRandoms(factor, 31L);
        this.randI = newRandoms(factor, 41L);
    }

    public enum RankInitScheme {
        random /* default */, gaussian;

        @Nonnegative
        private float maxInitValue;
        @Nonnegative
        private double initStdDev;

        @Nonnull
        public static RankInitScheme resolve(@Nullable String opt) {
            if (opt == null) {
                return random;
            } else if ("gaussian".equalsIgnoreCase(opt)) {
                return gaussian;
            } else if ("random".equalsIgnoreCase(opt)) {
                return random;
            }
            return random;
        }

        public void setMaxInitValue(float maxInitValue) {
            this.maxInitValue = maxInitValue;
        }

        public void setInitStdDev(double initStdDev) {
            this.initStdDev = initStdDev;
        }

    }

    @Nonnull
    private static Random[] newRandoms(@Nonnull final int size, final long seed) {
        final Random[] rand = new Random[size];
        for (int i = 0, len = rand.length; i < len; i++) {
            rand[i] = new Random(seed + i);
        }
        return rand;
    }

    public int getMinIndex() {
        return minIndex;
    }

    public int getMaxIndex() {
        return maxIndex;
    }

    @Nonnull
    public Rating meanRating() {
        return meanRating;
    }

    public float getMeanRating() {
        return meanRating.getWeight();
    }

    public void setMeanRating(final float rating) {
        meanRating.setWeight(rating);
    }

    @Nullable
    public Rating[] getUserVector(final int u) {
        return getUserVector(u, false);
    }

    @Nullable
    public Rating[] getUserVector(final int u, final boolean init) {
        Rating[] v = users.get(u);
        if (init && v == null) {
            v = new Rating[factor];
            switch (initScheme) {
                case random:
                    uniformFill(v, randU[0], initScheme.maxInitValue, ratingInitializer);
                    break;
                case gaussian:
                    gaussianFill(v, randU, initScheme.initStdDev, ratingInitializer);
                    break;
                default:
                    throw new IllegalStateException(
                        "Unsupported rank initialization scheme: " + initScheme);

            }
            users.put(u, v);
            this.maxIndex = Math.max(maxIndex, u);
            this.minIndex = Math.min(minIndex, u);
        }
        return v;
    }

    @Nullable
    public Rating[] getItemVector(final int i) {
        return getItemVector(i, false);
    }

    @Nullable
    public Rating[] getItemVector(int i, boolean init) {
        Rating[] v = items.get(i);
        if (init && v == null) {
            v = new Rating[factor];
            switch (initScheme) {
                case random:
                    uniformFill(v, randI[0], initScheme.maxInitValue, ratingInitializer);
                    break;
                case gaussian:
                    gaussianFill(v, randI, initScheme.initStdDev, ratingInitializer);
                    break;
                default:
                    throw new IllegalStateException(
                        "Unsupported rank initialization scheme: " + initScheme);

            }
            items.put(i, v);
            this.maxIndex = Math.max(maxIndex, i);
            this.minIndex = Math.min(minIndex, i);
        }
        return v;
    }

    @Nonnull
    public Rating userBias(final int u) {
        Rating b = userBias.get(u);
        if (b == null) {
            b = ratingInitializer.newRating(0.f); // dummy
            userBias.put(u, b);
        }
        return b;
    }

    public float getUserBias(final int u) {
        final Rating b = userBias.get(u);
        if (b == null) {
            return 0.f;
        }
        return b.getWeight();
    }

    public void setUserBias(final int u, final float value) {
        Rating b = userBias.get(u);
        if (b == null) {
            b = ratingInitializer.newRating(value);
            userBias.put(u, b);
        }
        b.setWeight(value);
    }

    @Nonnull
    public Rating itemBias(final int i) {
        Rating b = itemBias.get(i);
        if (b == null) {
            b = ratingInitializer.newRating(0.f); // dummy
            itemBias.put(i, b);
        }
        return b;
    }

    @Nullable
    public Rating getItemBiasObject(final int i) {
        return itemBias.get(i);
    }

    public float getItemBias(final int i) {
        final Rating b = itemBias.get(i);
        if (b == null) {
            return 0.f;
        }
        return b.getWeight();
    }

    public void setItemBias(final int i, final float value) {
        Rating b = itemBias.get(i);
        if (b == null) {
            b = ratingInitializer.newRating(value);
            itemBias.put(i, b);
        }
        b.setWeight(value);
    }

    private static void uniformFill(final Rating[] a, final Random rand, final float maxInitValue,
            final RatingInitializer init) {
        for (int i = 0, len = a.length; i < len; i++) {
            float v = rand.nextFloat() * maxInitValue / len;
            a[i] = init.newRating(v);
        }
    }

    private static void gaussianFill(final Rating[] a, final Random[] rand, final double stddev,
            final RatingInitializer init) {
        for (int i = 0, len = a.length; i < len; i++) {
            float v = (float) MathUtils.gaussian(0.d, stddev, rand[i]);
            a[i] = init.newRating(v);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy