Как улучшить результат моей рекомендации? Я использую искры ALS неявной
Во-первых, у меня есть история использования приложения пользователя.
Например:
user1, app1, 3 (время запуска)
user2, app2, 2 (время запуска)
user3, app1, 1 (время запуска)
У меня есть два основных требования:
- Посоветуйте какое-нибудь приложение для каждого пользователя.
- Рекомендовать подобное приложение для каждого приложения.
Поэтому я использую ALS(неявный) из MLLib на спарк для его реализации. Сначала я просто использую исходные данные для обучения модели. Результат ужасен. Я думаю, что это может быть вызвано диапазоном времени запуска. И время запуска колеблется от 1 до тысячи. Поэтому я обрабатываю исходные данные. Я думаю, что оценка может отражать реальную ситуацию и больше регуляризации.
оценка = lt / uMlt + lt / aMlt
оценка - это результат процесса обучения модели.
Это время запуска в исходных данных.
uMlt - среднее время запуска пользователя в исходных данных. uMlt(все времена запуска пользователя) / (число приложений, когда-либо запущенных этим пользователем)
aMlt - среднее время запуска приложения в исходных данных. aMlt(все времена запуска приложения) / (количество пользователей, которые когда-либо запускали это приложение)
Вот пример данных после обработки.
Рейтинг (95788,20992,0.14167073369026184)
Рейтинг (98696,20992,5.92363166809082)
Рейтинг (160020,11264,2.261538505554199)
Рейтинг (67904,11264,2.261538505554199)
Рейтинг (268430,11264,0.13846154510974884)
Рейтинг (201369,11264,1.7999999523162842)
Рейтинг (180857,11264,2.2720916271209717)
Рейтинг (217692,11264,1.3692307472229004)
Рейтинг (186274,28672,2.4250855445861816)
Рейтинг (120820,28672,0.4422124922275543)
Рейтинг (221146,28672,1.0074234008789062)
После того, как я это сделал и агрегировал приложения с разными именами пакетов, результат кажется лучше. Но все еще недостаточно хорош.
Я считаю, что характеристики пользователей и продуктов настолько малы, и большинство из них отрицательные.
Вот 3-х строчный пример характеристик продуктов, 10 размеров для каждой линии:
((CompactBuffer (com.youlin.xyzs.shoumeng, com.youlin.xyzs.juhe.shoumeng)), (- 4.798973236574966E-7, -7.641608021913271E-7,6.040852440492017E-7,2.82689171626771E-7804 767 755 767 7677557 -7,1.815822798789668E-7,5.000047167413868E-7,2.0220664964654134E-7,6.386763402588258E-7, -4.289261710255232E-7))
((CompactBuffer (com.dncfcjaobhegbjccdhandkba.huojia)), (- 4.769295992446132E-5, -1.7072002810891718E-4,2.1351299074012786E-4,1.6345139010809362E-4, -1.4456869394052774E-4,2.3657752899453044E-4, -4.508546771830879E- 5,2.0895185298286378E-4,2.968782791867852E-4,1.9461760530248284E-4))
((CompactBuffer (com.tern.rest.pron)), (- 1.219763362314552E-5, -2.8371430744300596E-5,2.9869115678593516E-5,2.0747662347275764E-5, -2.0555471564875916E-5,2.632938776514493E-5,2.934047643066151E -6,2.296348611707799E-5,3.8075613701948896E-5,1.2197584510431625E-5))
Вот 3-строчный пример пользовательских функций, по 10 измерений для каждой строки:
(96768, (- +0,0010857731103897095, -0.001926362863741815,0.0013726564357057214,6.345533765852451E-4, -9.048808133229613E-4, -4.1544197301846E-5,0.0014421759406104684, -9.77902309386991E-5,0.0010355513077229261, -0,0017878251383081079))
(97280, (- +0,0022841691970825195, -0.0017134940717369318,0.001027365098707378,9.437055559828877E-4, -0.0011165080359205604,0.0017137592658400536,9.713359759189188E-4,8.947265450842679E-4,0.0014328152174130082, -5.738904583267868E-4))
(97792, (- 0,0017802991205826402, -0.003464450128376484,0.002837196458131075,0.0015725698322057724, -0.0018932095263153315,9.185600210912526E-4,0.0018971719546243548,7.250450435094535E-4,0.0027060359716415405, -0,0017731878906488419))
Таким образом, вы можете себе представить, как мало, когда я получаю точечное произведение векторов объектов для вычисления значения матрицы пользовательского элемента.
Мой вопрос здесь:
- Есть ли другой способ улучшить результат рекомендации?
- Мои функции кажутся правильными или что-то идет не так?
- Правильный ли мой способ обработки исходного времени запуска (конвертировать в счет)?
Я положил немного кода здесь. И это абсолютно программный вопрос. Но, возможно, не может быть решена несколькими строками кода.
val model = ALS.trainImplicit(ratings, rank, iterations, lambda, alpha)
print("recommendForAllUser")
val userTopKRdd = recommendForAllUser(model, topN).join(userData.map(x => (x._2._1, x._1))).map {
case (uid, (appArray, mac)) => {
(mac, appArray.map {
case (appId, rating) => {
val packageName = appIdPriorityPackageNameDict.value.getOrElse(appId, Constants.PLACEHOLDER)
(packageName, rating)
}
})
}
}
HbaseWriter.writeRddToHbase(userTopKRdd, "user_top100_recommendation", (x: (String, Array[(String, Double)])) => {
val mac = x._1
val products = x._2.map {
case (packageName, rating) => packageName + "=" + rating
}.mkString(",")
val putMap = Map("apps" -> products)
(new ImmutableBytesWritable(), Utils.getHbasePutByMap(mac, putMap))
})
print("recommendSimilarApp")
println("productFeatures ******")
model.productFeatures.take(1000).map{
case (appId, features) => {
val packageNameList = appIdPackageNameListDict.value.get(appId)
val packageNameListStr = if (packageNameList.isDefined) {
packageNameList.mkString("(", ",", ")")
} else {
"Unknow List"
}
(packageNameListStr, features.mkString("(", ",", ")"))
}
}.foreach(println)
println("productFeatures ******")
model.userFeatures.take(1000).map{
case (userId, features) => {
(userId, features.mkString("(", ",", ")"))
}
}.foreach(println)
val similarAppRdd = recommendSimilarApp(model, topN).flatMap {
case (appId, similarAppArray) => {
val groupedAppList = appIdPackageNameListDict.value.get(appId)
if (groupedAppList.isDefined) {
val similarPackageList = similarAppArray.map {
case (destAppId, rating) => (appIdPriorityPackageNameDict.value.getOrElse(destAppId, Constants.PLACEHOLDER), rating)
}
groupedAppList.get.map(packageName => {
(packageName, similarPackageList)
})
} else {
None
}
}
}
HbaseWriter.writeRddToHbase(similarAppRdd, "similar_app_top100_recommendation", (x: (String, Array[(String, Double)])) => {
val packageName = x._1
val products = x._2.map {
case (packageName, rating) => packageName + "=" + rating
}.mkString(",")
val putMap = Map("apps" -> products)
(new ImmutableBytesWritable(), Utils.getHbasePutByMap(packageName, putMap))
})
ОБНОВИТЬ:
Я нашел что-то новое в своих данных после прочтения статьи ("Совместная фильтрация для наборов данных неявной обратной связи"). Мои данные слишком редки по сравнению с набором данных IPTV, описанным в статье.
Бумага: 300 000 (пользователи) 17 000 (продукты) 32 000 000 (данные)
Шахта: 300 000 (пользователи) 31 000 (продукты) 700 000 (данные)
Таким образом, матрица элементов пользователя в наборе данных статьи была заполнена на 0,00627 = (32 000 000 / 300 000 / 17 000). Соотношение моего набора данных составляет 0,0000033. Я думаю, это означает, что моя матрица элементов пользователя в 2000 раз меньше, чем в статье.
Должно ли это привести к плохому результату? И есть ли способ улучшить это?
1 ответ
Есть две вещи, которые вы должны попробовать:
- Стандартизируйте ваши данные так, чтобы они имели нулевое среднее значение и единичную дисперсию для каждого вектора пользователя. Это общий шаг во многих машинного обучения. Это помогает уменьшить влияние выбросов, которые вызывают близкие к нулю значения, которые вы видите.
- Удалить всех пользователей, которые имеют только одно приложение. Единственное, чему вы научитесь у этих пользователей, - это немного лучшее "среднее" значение для баллов приложения. Они не помогут вам узнать какие-либо значимые отношения, а это то, что вы действительно хотите.
После удаления пользователя из модели вы потеряете возможность получать рекомендации для этого пользователя непосредственно из модели, указав идентификатор пользователя. Тем не менее, они имеют только один рейтинг приложения в любом случае. Таким образом, вместо этого вы можете запустить поиск KNN по матрице продуктов, чтобы найти приложения, наиболее похожие на приложения пользователей.