《算法撕裂者》02 - 推荐算法
推荐算法之协同过滤及其改进与实现
- 前言
- 协同过滤算法
- (1)基于用户的协同过滤算法
- (2)基于物品的协同过滤算法
- 改进的协同过滤算法
- (1)用户相似度计算的改进
- (2)加权系数惩罚热门物品
- 具体实现
- 1、请求入口
- 2、推荐实现类(获取最近K邻居)
- 3、推荐实现类(获取相应的推荐歌曲)
- 4、 封装返回结果
- 最后的总结和絮叨
前言
因为我的项目和简历中都提及了推荐算法,也有几次面试官问到,现在想想回答的虽然没有问题,但是总感觉表述不是很清晰。今天抽空把推荐算法理一理。
协同过滤算法
因为我使用的推荐算法是基于协同过滤,所以我们先聊聊协同过滤算法吧。
协同过滤(Collaborative Filtering,简称CF)是一种最经典的推荐算法,这个算法的出现对于推荐系统具有划时代的意义。1992年第一次提出协同过滤算法。
协同过滤算法主要通过分析用户的历史数据,用以构建用户模型并进行推荐。协同过滤算法主要分为以下两类:一类是基于用户的协同过滤算法(User-Based Collaborative Filtering,简称UserCF),另一类是基于物品的协同过滤算法(Item-Based Collaborative Filtering,简称ItemCF)。
(1)基于用户的协同过滤算法
UserCF算法的核心是用户,思想是“人以群分”。算法的基本思路是:首先找到与目标用户喜好相似的邻居用户集体,然后以此为基础计算出目标用户对未操作物品的喜好预测评分,根据评分的高低推荐前N个给目标用户。
如图3-3所示,形象的显示了基于用户的协同过滤算法的一般概念:假设用户C为目标用户,根据图3-3可知,因为用户A和用户C由相同评分物品C和D,可知用户C的相似用户为用户A;又因为用户A已评分的集合物品A是用户C未操作的,所以将物品A推荐给目标用户C。
(2)基于物品的协同过滤算法
ItemCF算法的核心是物品,思想是“物以类聚”。算法的基本思路是:首先计算物品之间的相似度,然后根据目标用户的历史行为,将与之相似度较高的物品推荐给目标用户。
基于物品的协同过滤算法的理论如图3-4所示,假设用户A为目标用户,根据图3-4可知,因为物品A和物品C同时被用户B和用户C评分,因此物品C的相似邻居为物品A,又因为目标用户A对物品C已评分,所以将物品A推荐给目标用户A。
改进的协同过滤算法
传统的协同过滤推荐算法的思想如下。
(1)使用评分计算其他用户和目标用户的相似度;
(2)找出K个和目标用户相似度最高的邻居用户;
(3)向目标用户推荐邻居喜欢的物品且目标用户没有操作过的物品。
- 音乐推荐应用场景中,传统的协同过滤算法有如下几点可以改进。
(1)用户相似度计算的改进
传统的协同过滤算法在音乐推荐时实现的思想是,构建一个稀疏的用户-歌曲评分矩阵,根据用户对歌曲的评分(如收藏、分享、加心心等操作),计算出用户的相似度。
但对于音乐方面,我认为用户偏好并不是评分就能笼统表达的,仅利用评分的填充会使得填充后的矩阵出现一定偏差,比如两个用户都收藏过《海阔天空》,这并不能说明他们相似度高,因为听歌时还带有用户的情感,可能用户A感觉斗志昂扬,但用户B感觉情绪低落。如果两个用户的感受相似,则可以更好地说明他们的相似性。
因此,针对上述这个问题,考虑到用户听歌时带有情感性,引入情感相似度,以此来提高系统推荐的准确性,改善用户的体验。基于音乐互动模块对用户情感进行分析,将用户情感数值化,基于情感相似度结合协同过滤推荐算法找出与目标用户最相似的邻居用户。
此改进方法的具体思想如下。
首先,通过音乐互动模块将用户互动所得的用户情感相似性数值化后,使用欧几里德距离公式计算用户间的情感相似度。欧几里德度量公式是一种简单易懂的用以计算相似度的方法。它是将用户共同参与的互动作为坐标轴,然后,将参与互动的用户放置在坐标系中,并计算两个用户之间的直线距离。在二维坐标中,两用户的欧几里德距离如图3-5所示。
由此可推导到i维坐标中,两用户之间的欧几里德距离如公式(4.1)所示。
上述欧几里德距离公式计算出来的是一个大于或等于0的数,使用公式(4.2)将其规范到(0, 1]之间,以便更直观地反映用户之间的相似度。
可以将数据库中的数据构建出一个用户-互动分值矩阵,如图3-6所示,通过矩阵的构建和计算可以找到u1的情感相似用户u3和u5。
用户u和用户v的情感相似度计算公式如(4.3)所示。
其中,N(u)表示用户u已经参与过的互动集合,N(v)表示用户v已经参与过的互动集合。u(i)表示用户u对互动i的偏好,v(i)表示用户v对互动i的偏好。n表示用户u和用户v共同参与过的互动数。
(2)加权系数惩罚热门物品
在音乐场景下经常产生热门歌曲,如果热门歌曲出现次数较多,就会影响实际相似度的计算结果,从而导致推荐的歌曲都是热门歌曲,无法满足用户的实际需求。
为了减小这种影响,可以考虑加入一个加权系数用以惩罚热门歌曲的影响,即惩罚因子。
因此本文对相似度的计算公式加以改进,将歌曲出现次数的倒数作为惩罚因子。歌曲出现的次数越多,即改歌曲越热门,同时,该歌曲对用户喜好相似度的贡献则越少。带有惩罚因子的公式可减弱热门歌曲造成的影响,改进后的公式如(4.4)所示。
其中,N(i)表示歌曲i出现的次数,可以看出,该公式加入歌曲出现次数的倒数计算用户u和用户v的共同爱好列表中的相似度,从而惩罚了热门歌曲的影响。
改进后的算法的具体流程,如图3-7所示。
- 改进后的算法的具体步骤如下。
(1)用户情感分析信息、用户评分信息。
(2)在音乐互动分析的用户情感信息上,进行情感数值化构建用户情感矩阵,计算用户情感相似度。
(3)根据用户情感相似度找出目标用户的K个最近邻居集,并按递减顺序将这些结果值排序。
(4)根据用户听歌的历史行为,构建用户评分矩阵,并加入惩罚因子,计算用户评分相似度。
(5)将最终评分值由高到低排序,并将排序结果推荐给目标用户。
(6)输出目标用户的推荐用户和推荐歌曲集合。
具体实现
先贴下推荐算法时序图,如下。
如果熟悉的朋友可能看出来了,这是基于Mahout框架实现的。在架子里面实现自己逻辑即可。
这里主要贴出重点的代码块,详细点击文末阅读原文移步GitHub。
1、请求入口
请求入口和相应参数设置,很简单就不多解释了。
- RecommendController
@Controller
@RequestMapping("/recommendAction")
public class RecommendController {private static final Logger log = LoggerFactory.getLogger(RecommendController.class);final static int NEIGHBORHOOD_NUM = 3; //用户邻居数量final static int RECOMMENDER_NUM = 3; //推荐结果个数static DataModel dataModel = null; //Mahout提供的数据模型(用于将数据库的数据转为带构建的数据模型)
如下代码构建推荐系统,并调用相关的方法。
//基于用户的协同过滤算法,基于物品的协同过滤算法UserSimilarity user = new EuclideanDistanceSimilarity(dataModel); //计算欧式距离,欧式距离来定义相似性,用s=1/(1+d)来表示,范围在[0,1]之间,值越大,表明d越小,距离越近,则表示相似性越大//指定用户邻居数量NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, user, dataModel);//构建基于用户的推荐系统Recommender r = new GenericUserBasedRecommender(dataModel, neighbor, user);//获取目标用户的K个最近邻居集long[] theNeighborhood = r.recommendUser(userID, RECOMMENDER_NUM);//获取最终推荐结果List<MyRec> myRecList = r.recommendSong(theNeighborhood, userID, RECOMMENDER_NUM);
2、推荐实现类(获取最近K邻居)
- GenericUserBasedRecommender
/*** 推荐方法:获取目标用户的邻居用户id* @param userID 需要推荐的目标用户id* @param howMany 推荐结果个数* @return 邻居用户id数组* @throws TasteException*/@Overridepublic long[] recommendUser(long userID, int howMany) throws TasteException {Preconditions.checkArgument(howMany >= 1, "howMany must be at least 1");log.debug("Recommending items for user ID '{}'", userID);long[] theNeighborhood = neighborhood.getUserNeighborhood(userID);System.out.println(userID+"'s theNeighborhood:"+Arrays.toString(theNeighborhood));return theNeighborhood;}/*** 推荐方法:根据邻居用户id推荐最喜欢的歌曲* @param theNeighborhood* @param userID* @param howMany* @return 推荐歌曲集合* @throws TasteException*/@Overridepublic List<MyRec> recommendSong(long[] theNeighborhood, long userID, int howMany) throws TasteException {List<MyRec> myRecList = new ArrayList<>();for (long oneNeighborhood : theNeighborhood) {FastIDSet theItemIDs = getTheItems(oneNeighborhood, userID);TopItems.Estimator<Long> estimator = new Estimator(userID, null , oneNeighborhood);List<RecommendedItem> topItems = TopItems.getTopSongs(howMany, theItemIDs.iterator(),null, estimator);log.debug("Recommendations are: {}", topItems);System.out.println("Recommendations: userId:"+oneNeighborhood + " ,songs:"+topItems);User user = new User();user.setId((int)oneNeighborhood);List<Song> songList = new ArrayList<>();for (int i = 0; i<topItems.size(); i++){Song song = new Song();song.setId((int)topItems.get(i).getItemID());songList.add(song);}MyRec myRec = new MyRec();myRec.setUser(user);myRec.setSongList(songList);myRecList.add(myRec);System.out.println("myRecList:"+ myRecList);}return myRecList;}
- NearestNUserNeighborhood
/*** 获取目标用户的邻居用户* @param userID* ID of user for which a neighborhood will be computed* @return* @throws TasteException*/@Overridepublic long[] getUserNeighborhood(long userID) throws TasteException {DataModel dataModel = getDataModel();UserSimilarity userSimilarityImpl = getUserSimilarity();TopItems.Estimator<Long> estimator = new Estimator(userSimilarityImpl, userID, minSimilarity);LongPrimitiveIterator userIDs = SamplingLongPrimitiveIterator.maybeWrapIterator(dataModel.getUserIDs(),getSamplingRate());return TopItems.getTopUsers(n, userIDs, null, estimator);}
- TopItems.getTopUsers:这里就是通过优先队列获得K个最近邻居集(不懂的朋友可以参看我之前文章写的 Top K问题 )
/*** 根据相似度排序邻居用户* @param howMany* @param allUserIDs* @param rescorer* @param estimator* @return* @throws TasteException*/public static long[] getTopUsers(int howMany,LongPrimitiveIterator allUserIDs,IDRescorer rescorer,Estimator<Long> estimator) throws TasteException {Queue<SimilarUser> topUsers = new PriorityQueue<SimilarUser>(howMany + 1, Collections.reverseOrder());boolean full = false;double lowestTopValue = Double.NEGATIVE_INFINITY;while (allUserIDs.hasNext()) {long userID = allUserIDs.next();if (rescorer != null && rescorer.isFiltered(userID)) {continue;}double similarity;try {similarity = estimator.estimate(userID);} catch (NoSuchUserException nsue) {continue;}double rescoredSimilarity = rescorer == null ? similarity : rescorer.rescore(userID, similarity);if (!Double.isNaN(rescoredSimilarity) && (!full || rescoredSimilarity > lowestTopValue)) {topUsers.add(new SimilarUser(userID, rescoredSimilarity));if (full) {topUsers.poll();} else if (topUsers.size() > howMany) {full = true;topUsers.poll();}lowestTopValue = topUsers.peek().getSimilarity();}}int size = topUsers.size();if (size == 0) {return NO_IDS;}List<SimilarUser> sorted = Lists.newArrayListWithCapacity(size);sorted.addAll(topUsers);Collections.sort(sorted);long[] result = new long[size];int i = 0;for (SimilarUser similarUser : sorted) {result[i++] = similarUser.getUserID();}return result;}
- AbstractSimilarity. userSimilarity 市重点!这里就是将数据模型的数据提取出来并进行相关计算。
/*** 估算目标用户和其他用户的相似度!!!* @param userID1* @param userID2* @return* @throws TasteException*/@Overridepublic double userSimilarity(long userID1, long userID2) throws TasteException {DataModel dataModel = getDataModel();PreferenceArray xPrefs = dataModel.getPreferencesFromUser(userID1);PreferenceArray yPrefs = dataModel.getPreferencesFromUser(userID2);int xLength = xPrefs.length();int yLength = yPrefs.length();if (xLength == 0 || yLength == 0) {return Double.NaN;}long xIndex = xPrefs.getItemID(0);long yIndex = yPrefs.getItemID(0);int xPrefIndex = 0;int yPrefIndex = 0;double sumX = 0.0;double sumX2 = 0.0;double sumY = 0.0;double sumY2 = 0.0;double sumXY = 0.0;double sumXYdiff2 = 0.0;int count = 0;boolean hasInferrer = inferrer != null;boolean hasPrefTransform = prefTransform != null;while (true) {int compare = xIndex < yIndex ? -1 : xIndex > yIndex ? 1 : 0;if (hasInferrer || compare == 0) {double x;double y;if (xIndex == yIndex) {// Both users expressed a preference for the itemif (hasPrefTransform) {x = prefTransform.getTransformedValue(xPrefs.get(xPrefIndex));y = prefTransform.getTransformedValue(yPrefs.get(yPrefIndex));} else {x = xPrefs.getValue(xPrefIndex);y = yPrefs.getValue(yPrefIndex);}} else {// Only one user expressed a preference, but infer the other one's preference and tally// as if the other user expressed that preferenceif (compare < 0) {// X has a value; infer Y'sx = hasPrefTransform? prefTransform.getTransformedValue(xPrefs.get(xPrefIndex)): xPrefs.getValue(xPrefIndex);y = inferrer.inferPreference(userID2, xIndex);} else {// compare > 0// Y has a value; infer X'sx = inferrer.inferPreference(userID1, yIndex);y = hasPrefTransform? prefTransform.getTransformedValue(yPrefs.get(yPrefIndex)): yPrefs.getValue(yPrefIndex);}}sumXY += x * y;sumX += x;sumX2 += x * x;sumY += y;sumY2 += y * y;double diff = x - y;sumXYdiff2 += diff * diff;count++;}if (compare <= 0) {if (++xPrefIndex >= xLength) {if (hasInferrer) {// Must count other Ys; pretend next X is far awayif (yIndex == Long.MAX_VALUE) {// ... but stop if both are done!break;}xIndex = Long.MAX_VALUE;} else {break;}} else {xIndex = xPrefs.getItemID(xPrefIndex);}}if (compare >= 0) {if (++yPrefIndex >= yLength) {if (hasInferrer) {// Must count other Xs; pretend next Y is far awayif (xIndex == Long.MAX_VALUE) {// ... but stop if both are done!break;}yIndex = Long.MAX_VALUE;} else {break;}} else {yIndex = yPrefs.getItemID(yPrefIndex);}}}// "Center" the data. If my math is correct, this'll do it.double result;if (centerData) {double meanX = sumX / count;double meanY = sumY / count;// double centeredSumXY = sumXY - meanY * sumX - meanX * sumY + n * meanX * meanY;double centeredSumXY = sumXY - meanY * sumX;// double centeredSumX2 = sumX2 - 2.0 * meanX * sumX + n * meanX * meanX;double centeredSumX2 = sumX2 - meanX * sumX;// double centeredSumY2 = sumY2 - 2.0 * meanY * sumY + n * meanY * meanY;double centeredSumY2 = sumY2 - meanY * sumY;result = computeResult(count, centeredSumXY, centeredSumX2, centeredSumY2, sumXYdiff2);} else {result = computeResult(count, sumXY, sumX2, sumY2, sumXYdiff2);}if (similarityTransform != null) {result = similarityTransform.transformSimilarity(userID1, userID2, result);}if (!Double.isNaN(result)) {result = normalizeWeightResult(result, count, cachedNumItems);}return result;}
- EuclideanDistanceSimilarity:最终调用到这里的欧几里德距离计算公式
@Overridedouble computeResult(int n, double sumXY, double sumX2, double sumY2, double sumXYdiff2) {return 1.0 / (1.0 + Math.sqrt(sumXYdiff2) / Math.sqrt(n));}
以上就是获取K个最近邻居的整体流程。
注:传统的协同过滤推荐算法并不会直接返回最近K邻居,而是根据用户对歌曲的评分矩阵,返回推荐的歌曲。
而我因为业务需要,需要返回最近K邻居,并且在通过最近K邻居和歌曲评分矩阵,返回相应的推荐歌曲。
3、推荐实现类(获取相应的推荐歌曲)
- GenericUserBasedRecommender
/*** 推荐方法:根据邻居用户id推荐最喜欢的歌曲* @param theNeighborhood* @param userID* @param howMany* @return 推荐歌曲集合* @throws TasteException*/@Overridepublic List<MyRec> recommendSong(long[] theNeighborhood, long userID, int howMany) throws TasteException {List<MyRec> myRecList = new ArrayList<>();for (long oneNeighborhood : theNeighborhood) {FastIDSet theItemIDs = getTheItems(oneNeighborhood, userID);TopItems.Estimator<Long> estimator = new Estimator(userID, null , oneNeighborhood);List<RecommendedItem> topItems = TopItems.getTopSongs(howMany, theItemIDs.iterator(),null, estimator);log.debug("Recommendations are: {}", topItems);System.out.println("Recommendations: userId:"+oneNeighborhood + " ,songs:"+topItems);User user = new User();user.setId((int)oneNeighborhood);List<Song> songList = new ArrayList<>();for (int i = 0; i<topItems.size(); i++){Song song = new Song();song.setId((int)topItems.get(i).getItemID());songList.add(song);}MyRec myRec = new MyRec();myRec.setUser(user);myRec.setSongList(songList);myRecList.add(myRec);System.out.println("myRecList:"+ myRecList);}return myRecList;}
- GenericUserBasedRecommender:注:这里有和传统的不同,我没有去除目标用户已接触过的物品
private FastIDSet getTheItems(long oneNeighborhood, long theUserID) throws TasteException {DataModel dataModel = getDataModel();FastIDSet possibleItemIDs = new FastIDSet();
// 添加所有邻居用户的物品possibleItemIDs.addAll(dataModel.getItemIDsFromUser(oneNeighborhood));
// 去除目标用户已接触过的物品
// possibleItemIDs.removeAll(dataModel.getItemIDsFromUser(theUserID));return possibleItemIDs;}
- TopItems:同理获取推荐歌曲
public static List<RecommendedItem> getTopSongs(int howMany,LongPrimitiveIterator possibleItemIDs,IDRescorer rescorer,Estimator<Long> estimator) throws TasteException {Preconditions.checkArgument(possibleItemIDs != null, "argument is null");Preconditions.checkArgument(estimator != null, "argument is null");Queue<RecommendedItem> topItems = new PriorityQueue<RecommendedItem>(howMany + 1,Collections.reverseOrder(ByValueRecommendedItemComparator.getInstance()));boolean full = false;double lowestTopValue = Double.NEGATIVE_INFINITY;while (possibleItemIDs.hasNext()) {long itemID = possibleItemIDs.next();if (rescorer == null || !rescorer.isFiltered(itemID)) {double preference;try {preference = estimator.estimateSong(itemID);} catch (NoSuchItemException nsie) {continue;}double rescoredPref = rescorer == null ? preference : rescorer.rescore(itemID, preference);
// if (Double.isNaN(rescoredPref)) {// rescoredPref = 0;
// }if (!Double.isNaN(rescoredPref) && (!full || rescoredPref > lowestTopValue)) {topItems.add(new GenericRecommendedItem(itemID, (float) rescoredPref));if (full) {topItems.poll();} else if (topItems.size() > howMany) {full = true;topItems.poll();}lowestTopValue = topItems.peek().getValue();}}}int size = topItems.size();if (size == 0) {return Collections.emptyList();}List<RecommendedItem> result = Lists.newArrayListWithCapacity(size);result.addAll(topItems);Collections.sort(result, ByValueRecommendedItemComparator.getInstance());return result;}
- GenericUserBasedRecommender.estimateSong:
@Overridepublic double estimateSong(Long itemID) throws TasteException {return doEstimatePreferenceSong(theUserID, oneNeighborhood, itemID);}
- GenericUserBasedRecommender.doEstimatePreferenceSong:评估歌曲(注:这里我也有所改动)
protected float doEstimatePreferenceSong(long theUserID, long oneNeighborhood, long itemID) throws TasteException {DataModel dataModel = getDataModel();double preference = 0.0;double totalSimilarity = 0.0;int count = 0;if (oneNeighborhood != theUserID) {// See GenericItemBasedRecommender.doEstimatePreference() tooFloat pref = dataModel.getPreferenceValue(oneNeighborhood, itemID);if (pref != null) {double theSimilarity = similarity.userSimilarity(theUserID, oneNeighborhood);if (!Double.isNaN(theSimilarity)) {preference += theSimilarity * pref;totalSimilarity += theSimilarity;count++;}else {// preference += pref;
// totalSimilarity += theSimilarity;
// count++;return pref;}}}
// if (count <= 1) {// return Float.NaN;
// }float estimate = (float) (preference / totalSimilarity);if (capper != null) {estimate = capper.capEstimate(estimate);}return estimate;}
4、 封装返回结果
- RecommendController:在controller层封装结果返回给前端
jsonResult jr = new jsonResult();List<MyRec> myRecInfoList = getRecInfo(myRecList);jr.add(myRecInfoList);System.out.println("myRecommend json---:"+jr);return jr;
- RecommendController:调用方法
public List<MyRec> getRecInfo(List<MyRec> myRecList) {UserServiceDao userService = new UserServiceDao();SongServiceDao songService = new SongServiceDao();System.out.println("get myRecList:" + myRecList);// 循环遍历到数据库中查询详细信息List<MyRec> myRecListInfo = new ArrayList<>();for (MyRec myRec : myRecList) {System.out.println("myRec.getUser().getUserId():" + myRec.getUser().getId());// 获取用户idInteger userId = myRec.getUser().getId();
// 查询用户详情信息User user = userService.getUserInfoById(userId);System.out.println("user:" + user);
// 设置更新后的用户myRec.setUser(user);
// myRecListInfo.add(myRec);System.out.println("myRec.getSongList():" + myRec.getSongList());List<Song> songList = new ArrayList<>();for (Song song : myRec.getSongList()) {// System.out.println("song.getSongId():"+song.getSongId());Integer songId = song.getId();// 获取歌曲详细信息song = songService.getSongInfoById(songId);
// System.out.println("song:"+song);// 如果本地曲库有这首歌则添加。否则加的是null影响体验if (song.getId() != null){songList.add(song);}}myRec.setSongList(songList);
// 添加myRec到数组中myRecListInfo.add(myRec);}System.out.println("myRecListInfo:" + myRecListInfo);return myRecListInfo;}
以上。大致就是推荐功能的重点代码块(注:惩罚因子目前并没实现)。如需完整代码,欢迎前往全球最大同性交友网站GayHub 哦不GitHub查看(由于时间和经验的限制,代码写得可能不是很好,但整体还是可供参考的)
- 哦!顺便推广一下实现推荐算法的这个作品 “寻找最佳音缘”,是一款微信小程序,正式版已经发布上线,可以直接在微信中搜索“寻找最佳音缘”或者扫下图小程序码进入。主要功能是音乐服务(希望不要有网易云音乐的伙伴给我发律师函,至于为什么你用了就知道啦hhh)这个作品完全是出于自己对音乐的喜爱和臆想,从设计到实现以及部署都是自己完全的,还是希望大家支持一下吧~ 给我加加用户量或者GitHub点小星星!说不定面试官会给我加分呢
《算法撕裂者》02 - 推荐算法相关推荐
- 02 推荐算法-(01) Model-Based 协同过滤算法
Model-Based 协同过滤算法 随着机器学习技术的逐渐发展与完善,推荐系统也逐渐运用机器学习的思想来进行推荐.将机器学习应用到推荐系统中的方案真是不胜枚举.以下对Model-Based CF算法 ...
- fm算法详解_fm算法(基于fm推荐算法)
在简单频率调制中,两个振荡器都只用正弦曲线(Sinusoidal)的波形.不过,由于. 这使得作曲家也不必用频谱过于复杂的波形完成FM合成.事实上,如用一个频谱成分. 豆瓣FM的推荐算法没有停止,反而 ...
- mahout 推荐算法 java_Mahout之推荐算法基本实例
Mahout中主要核心的三大算法为推荐,聚类及分类算法,今天就最基本的推荐算法做总结,推荐中常用的两个推荐算法是"user_based"和"item_based" ...
- 推荐系统 --- 推荐算法 --- 基于知识的推荐算法
基于知识的推荐方法 基本思想 通过交互.会话等方式直接了解到用户需求("问"),然后再寻找匹配的视频("找") 解决思路 优点 不存在冷启动的问题 由于与用户存 ...
- 基于神经网络的推荐算法,协同过滤推荐算法python
大数据运维的主要工作内容是什么? . 大数据相关工作岗位很多,有大数据分析师.大数据挖掘算法工程师.大数据研发工程师.数据产品经理.大数据可视化工程师.大数据爬虫工程师.大数据运营专员.大数据架构师. ...
- 基于土壤数据与机器学习算法的农作物推荐算法代码实现
1.摘要 近年来,机器学习方法在农业领域的应用取得巨大成功,广泛应用于科 学施肥.产量预测和经济效益预估等领域.根据土壤信息进行数据挖掘,并在此基础上提出区域性作物的种植建议,不仅可以促进农作物生长从 ...
- fm算法 c语言,推荐算法之—FM
1.什么是FM算法 FM即Factor Machine,因子分解机 2.为什么需要FM 1).特征组合是许多机器学习建模过程中遇到的问题,如果对特征直接建模,很有可能忽略掉特征与特征之间的关联信息,一 ...
- 推荐算法为啥这么“灵”,又为啥会“失灵”?
导读:一句话概括推荐算法的原理. 作者:木羊同学 来源:华章计算机(ID:hzbook_jsj) 推荐系统是为用户推荐所需物品的软件工具和技术.提供的推荐旨在通过各种决策过程来支持用户,例如,买什么物 ...
- 想要成为推荐算法工程师,都要准备哪些东西
作者在<推荐算法工程师的成长之道>这篇文章中讲到推荐算法工程师是一个好的职业选择,并且讲解了职业发展路径及定位.怎么成长等话题(还没看的可以看起来). 如果大家认可我讲的并且也愿意将来从事 ...
最新文章
- python中utf8占几个字节_为什么utf8占用3个字节
- 为什么资本主义生产的一般趋势是资本有机构成的提高?2017-12-26
- git clone时出现gnutls_handshake() failed: The TLS connection was non-properly terminated.
- c++ httpserver 服务器
- php中smarty扩展类问题
- GSON的用法(处理对象和JSON的相互转化)
- 学计算机逻辑思维能力测试题,逻辑思维能力测试题5道含答案
- 最新Javascript 基础知识全总结(持续更新)
- python金额数字转大写完整代码
- bandizip右键选项设置方法步骤
- 电脑怎么录屏幕视频带声音?电脑录屏教程介绍
- oracle大型数据库系统在aix/unix上的实战详解 pdf,Oracle10g在AIX上的安装准备工作《Oracle大型数据库系统在AIX/unix上的实战详解》集中答疑40...
- 斐波那契(Fibonacci)数列问题
- 大二物竞金牌转北大计算机,靠竞赛进入清北的学生,都能选哪些专业?| 2019竞赛优惠专业分析...
- Vue2切换生产环境、测试环境和开发环境
- 小学妹听了都说棒的:国王试毒酒问题
- 阿里的人工智能之路 与谷歌亚马逊还有多大差距
- 2012年7月 逐鹿反APT
- 通过URL下载HTML页面
- Qt窗口最大化/最小化/窗口状态判断
热门文章
- 计算机联网记录能删除吗,电脑怎么删除路由器wifi记录
- php正则替换%3cbr%3e_php中的正则函数主要有三个-正则匹配,正则替换
- MATLAB算法实战应用案例精讲-【数模应用】多元线性回归(MLR)(附Java、R语言、python和matlab代码实现)
- 阿里云OSS搭建图床
- 美国的华人码农,正在成为IT届的吠舍?
- 显示所有年龄大于18岁的男生
- 播放量接连破1000w,这个腰部账号已找对爆款突破口?
- 阿里云OSS不同账号之间迁移
- selenium.模拟键盘操作(Keys)
- 二十一:微信公众帐号开发符号表情的发送上
- 02 推荐算法-(01) Model-Based 协同过滤算法