Я хочу использовать Mahout в качестве системы рекомендаций.
В моем проекте есть содержимое, теги, реакции.Пользователь делится контентом после помеченного тегом, а другие пользователи могут читать его и давать ответы.
Я хочу рекомендовать контент, когда любой пользователь читает контент.
В этом случае у меня не будет значения предпочтения, если я использую только информацию о считывателе, поэтому я хочу использовать реакции содержимого и тегов содержимого со сходством элементов mahout по умолчанию.
Я мог быне будьте уверены, что использование информации о реакции и метках является правильным способом в системе рекомендаций.Это проблема рекомендации или нет.
Если это правильный способ, который я объяснил выше, я думаю, использовать составной объект как ItemShoity, который обертывает реализацию сходства Mahout (например, TanimotoCoefficientShoity), затем добавить результат вычисления сходства с результатами сходства тегов и реакции.
Модель:
user_id viewed_content_id
--------- -----------------
1 102
1 1032
2 105
content_id reaction_id reaction_count
---------- ----------- -------------
102 5 10000
105 3 500
206 5 2000
content_id tag_id
---------- ------
1 3
1 4
1 3
2 3
2 1
3 3
3 3
(Любой контент будет иметь около 5 вариантов реакции и около 5 тегов.)
Класс подобия предметов:
public class ContentSimilarity implements ItemSimilarity {
private ItemSimilarity similarity;
private FastByIDMap<ContentItem> map = new FastByIDMap<>();
private ContentSimilarity() {
}
/**
*
* @param dataModel
* @param similarity
* ---> let assume that TanimotoCoefficientSimilarity
* @return
* @throws TasteException
*/
public static ContentSimilarity createWith(DataModel dataModel, ItemSimilarity similarity) throws TasteException {
ContentSimilarity customSimilarity = new ContentSimilarity();
customSimilarity.setSimilarity(similarity);
return customSimilarity;
}
@Override
public void refresh(Collection<Refreshable> alreadyRefreshed) {
similarity.refresh(alreadyRefreshed);
}
@Override
public double itemSimilarity(long itemID1, long itemID2) throws TasteException {
double similarityResult = similarity.itemSimilarity(itemID1, itemID2) + customSimilarity(itemID1, itemID2);
return similarityResult;
}
int threshold = 10;
public double customSimilarity(long itemID1, long itemID2) throws TasteException {
ContentItem item1 = map.get(itemID1);
ContentItem item2 = map.get(itemID2);
double score = 0.0;
try {
// tag similarity
int tagIntersection = item1.getTagsAsFastIDSet().intersectionSize(item2.getTagsAsFastIDSet());
// reactionSimilarity
FastByIDMap<Long> item1Map = item1.getReactionsAsFastByIDMap();
FastByIDMap<Long> item2Map = item2.getReactionsAsFastByIDMap();
LongPrimitiveIterator item1Itr = item1Map.keySetIterator();
int reactionScore = 0;
while (item1Itr.hasNext()) {
long item1Key = item1Itr.next();
long item1Val = item1Map.get(item1Key);
Long item2ValO = item2Map.get(item1Key);
if (item1Val > threshold && item2ValO > threshold) {
reactionScore += item2ValO == null ? 0 : 1;
}
}
score = tagIntersection + reactionScore; // max score is 10
} catch (IOException e) {
e.printStackTrace();
}
return score / 10;
}
@Override
public double[] itemSimilarities(long itemID1, long[] itemID2s) throws TasteException {
return similarity.itemSimilarities(itemID1, itemID2s);
}
@Override
public long[] allSimilarItemIDs(long itemID) throws TasteException {
return similarity.allSimilarItemIDs(itemID);
}
public ItemSimilarity getSimilarity() {
return similarity;
}
public void setSimilarity(ItemSimilarity similarity) {
this.similarity = similarity;
}
}