此文章衔接【SpringBoot结合Elasticsearch实现个性化搜索推荐模型-04【推荐模型训练】文章
我们在第四章的时候, 完成了ALS模型和LR模型的构建,并且ALS模型我们将推荐的召回结果存储到了MYSQL当中,接下来我们将正式结合我们的ALS召回模型和LR逻辑回归模型完成推荐,在此之前首先要区分一下我们所做的搜索和推荐是独立的,搜索仍然是之前的queryByElasticSearch接口, 而推荐是一个全新的接口,也就是说用户没有指明我要搜索时,就是推荐接口返回数据,一旦用户有搜索意图了,就是搜索接口返回数据
代码实现
ALS模型接入
- 创建一个推荐的服务
/**
* 推荐服务
*
* @author 随风
*/
public interface RecommendService {
/**
* 获取ALS的召回结果
*
* @param userId 用户id
* @return 应推荐的商家id列表
*/
List<String> recall(String userId);
我们的推荐就不需要像之前做搜索的时候那么多参数了,什么经纬度,名称都不要了,只需要知道当前用户是谁就行了
- 实现接口
/**
* 推荐服务实现
*
* @author 随风
*/
@Service
public class RecommendServiceImpl implements RecommendService {
@Resource
private RecommendDOMapper recommendDOMapper;
@Override
public List<String> recall(String userId) {
// 用户对应的召回结果 是 1对多
RecommendDO recommendDO = recommendDOMapper.selectByUserId(userId);
// 我们在将推荐的信息查出来的时候 要思考一个问题 如果说我没有查到当前用户所应该推荐的内容怎么办 也就是没有userId所对应的记录
// 那我们就应该给与一个默认策略 查不到的时候 取数据库当中的默认推荐数据返回
if (null == recommendDO) {
recommendDO = recommendDOMapper.selectByDefaultData();
}
return Arrays.asList(recommendDO.getRecommend().split(","));
}
}
- 引入推荐服务
@Resource
private RecommendService recommendService;
- 创建推荐接口通过查到的门店id集合反查数据库构建门店信息
List<ShopModel> recommend(String userId);
- 反查数据
@Override
public List<ShopModel> recommend(String userId) {
// 实际我们在真实开发环境中 一般用户的信息 我们从token里获取 这里就直接传递了
List<String> shopIdList = recommendService.recall(userId);
// 将查到的商家信息返回给前端
return shopSerive.queryByIds(shopIdList);
}
- 控制层调用recommend方法返回给前端,至此,第一步的召回粗排就完成了,接下来进行LR模型的接入
LR模型接入
-
LR模型我们没有进行任何的解析直接存储到了磁盘上,怎么使用它呢?
-
创建一个含有排序的映射类
@Data
public class ShopSortMddel {
private String shopId;
private Double score;
}
- 创建LR模型服务
/**
* LR模型服务
* @author 随风
*/
public interface LrService {
List<String> sort(List<String> shopIdList, String userId);
}
- 服务实现
/**
* LR模型服务实现
* @author 随风
*/
@Service
public class LrServiceImpl implements LrService {
SparkSession sparkSession;
LogisticRegressionModel logisticRegressionModel;
@PostConstruct
public void init() {
// 创建Spark环境
sparkSession = SparkSession.builder().master("local").appName("business_info").getOrCreate();
// 读取我们训练好的LR模型
logisticRegressionModel = LogisticRegressionModel.load("D:/lrModel");
}
@Override
public List<String> sort(List<String> shopIdList, String userId) {
// shopIdList 就是我们第一次根据ALS模型的粗排召回结果
// 接下来我们需要构建特征
// 特征怎么构建呢 还记得 我们在上一个章节中 构建的用于描述特性的CSV文件吗 (11维的数组)
// 同样的我们需要将特征进行处理 转化为我们的数组
// 举个例子 当前用户的性别 转化为 one - hot 编码放到对应的列上 第五列 男 第六列 女 如果是男 就在第五列标记为 1 反之 放到第六列
// 这里就直接写死了
List<ShopSortMddel> resultList = new ArrayList<>();
for (String shopId : shopIdList) {
// 造的假数据
Vector dense = Vectors.dense(1, 0, 0, 0, 0, 1, 0.6, 0, 0, 1, 0);
Vector result = logisticRegressionModel.predictProbability(dense);
double[] array = result.toArray();
double score = array[1];
ShopSortMddel shopSortMddel = new ShopSortMddel();
shopSortMddel.setShopId(shopId);
shopSortMddel.setScore(score);
resultList.add(shopSortMddel);
}
// 根据评分对结果列表进行降序排序,并返回排序后的商家ID列表
resultList.sort((o1, o2) -> {
if (o1.getScore() < o2.getScore()) {
return 1;
} else if (o1.getScore() > o2.getScore()) {
return -1;
}
return 0;
});
return resultList.stream().map(ShopSortMddel::getShopId).collect(Collectors.toList());
}
}
- 改造recall方法 加入精排
@Override
public List<String> recall(String userId) {
// 第一步 ALS粗排
// 用户对应的召回结果 是 1对多
RecommendDO recommendDO = recommendDOMapper.selectByUserId(userId);
// 我们在将推荐的信息查出来的时候 要思考一个问题 如果说我没有查到当前用户所应该推荐的内容怎么办 也就是没有userId所对应的记录
// 那我们就应该给与一个默认策略 查不到的时候 取数据库当中的默认推荐数据返回
if (null == recommendDO) {
recommendDO = recommendDOMapper.selectByDefaultData();
}
// 第二步 LR精排
List<String> shopIdList = Arrays.asList(recommendDO.getRecommend().split(","));
return lrService.sort(shopIdList, userId);
}
疑问解释
- 为什么使用了predictProbability方法 而不用predict方法呢?
如果你只需要得到一个确定的类别标签,而不关心具体概率是多少,那么可以使用 predict 方法。predict 方法通常用于那些只需明确分类结果的场景。很明显我们的场景是需要知道概率的,因为我们需要猜测用户点击哪个商家的可能性大
predictProbability方法返回的是一个概率分布数组,包含了输入特征向属于不同类别的概率,对于二分类问题,它会返回一个包含两个元素的数组,分别表示属于负类和正类的概率。负类放在数组的0号下标位置,正类放在数组的1号下标位置,自然我们要的是1号下标的正类概率,值越高代表用户对这个商家的感兴趣的程度越高,点击它的概率也越大
完结