SpringBoot结合Elasticsearch实现个性化搜索推荐模型-04【推荐模型训练】

此文章衔接【SpringBoot结合Elasticsearch实现个性化搜索推荐模型-03】文章

推荐原理

什么是推荐呢?

当用户在进行关键词搜索时,我们可以通过关键词找出对应的特征,然后将根据特征将数据进行召回,但是用户如果什么关键词都没有输入怎么办呢?我们必然感知不到用户的意图了,那么也就只能像我们之前做搜索一样,只能给一个默认的规则进行召回,这样势必就造成了说不管是哪个用户,进入到应用首页会发现看到的内容一模一样,如果平时打开淘宝这样的APP,会发现每个人登录之后看到的内容都不一样,这就是推荐模型在起作用,我们要让应用能够根据用户的一个历史行为推断出他大概对什么感兴趣,并将他感兴趣的内容优先展示给他,当然并不是说一个用户喜欢什么就一直给他推荐相关的内容,比如说一个用户平时喜欢吃辣的,系统就永远推荐一些川菜,湘菜之类的给他,是不是也不是很合理,说不定用户哪天想换换口味呢

场景决定推荐规则

不同场景下需要有不同的推荐规则,但是有些场景看似无关,实际上有关系,比如说用户搜索了一个旅游的城市,那么我们应该推荐什么呢?当地的美食,旅游的景点,包括住宿方面的内容都应该做出一个推荐

推荐的方法有哪些?
  1. 基于定制的规则做出推荐
    这个是相对简单的一种推荐方式,比如给首先推荐评分高,推荐便宜的,推荐距离近的,但是这种样子的推荐相对死板一点,实际上还是不够聪明
  2. 基于机器学习的推荐
    基于用户的的历史行为和特征进行挖掘信息,并结合数据库的数据,进行推荐,这是比较好的一种推荐方式
    ,但是这样的推荐模型对于我们的数据质量要求比较高,如果了解大数据的话就明白,数据要的是高质量,而不是单纯的多,也就是在海量的数据当中做提纯,只获取重要的数据
  3. 基于深度学习的推荐
    这也是最复杂的一种推荐方式,它在机器学习的基础上又提高了一个等级,基于深度学习的推荐系统是一种先进的技术,它利用深度神经网络的强大功能来改进传统的推荐系统,这类系统可以通过分析用户的行为、偏好以及其他相关信息来提供个性化的推荐,深度学习模型可以捕捉到用户和项目之间的复杂非线性关系,因此在推荐准确性上有显著的优势,YouTube就是通过深度学习技术,提高视频推荐的点击率和观看时间

无论是基于机器学习还是深度学习都要求数据的高质量,因为一旦数据当中夹杂了错误的信息,或者说一些特征信息丢失,那么大概率推荐出来的内容就是错误的

模型好快的评价指标
  1. 查全率 查准率 (这个之前章节说过了)
  2. 点击率 交易转化率
    推荐出来的内容 点击率是多少? 交易转化又是多少? 比如一个商品被推荐出来,有多少人点击查看了,又有多少人实际购买了
常见的模型测试方法 (A/B测试)

就是说在同样的数据前提下,训练出两个模型A和B,分别查看A和B模型到底哪个更符合实际的业务诉求,谁的点击率更高,交易转化率更高,最后来决定长期使用哪个模型,并且实际上交易转化率的指标往往更重要,因为绝大部分的业务出发点都是盈利,只有点击量没有好的收益也不行

推荐系统的常见架构

在这里插入图片描述

  1. 首先推荐系统一般来说是基于业务系统的,但是他们之间实际的物理部署方式又很可能是解耦的,当前端发起一次请求,我们就需要根据特定的场景来决定我的推荐模型的选择,实际上,一个大型的面向C端的系统对应的推荐模型往往不止一个
  2. 对于推荐系统,我们往往将召回分为三层结构,粗排,精排,重排,这三层可能独立,也可能整合在一起
  3. 对于粗排,也是我们对召回结果的第一次分析,基于类似协同过滤的算法,als的算法来针对结果进行拆选,比如10000条数据,经过第一次的粗排,只剩下了200条数据
  4. 当粗排完成后,要基于粗排的结果进行第二次精排,精排往往基于排序模型,排序也是为了优先级的概念,我们在做ElasticSearch搜索分析的时候,对于score分值,有独立和整合的概念,独立就是说要么基于关键词的打分,要么根据函数策略的打分,整合就是两者相加的打分,而应用到上图当中,粗排的返回结果是一个分值,精排又是一个分值,那么我们应该相加还是独立的? 试想一下如果说粗排是0.9分,精排是0.1分,一旦我们相加就会破坏掉原本精排的模型预期,精细度不够了,原本应该排到后面的数据,一相加又给我排前面去了,实际情况下我们应该基于精排作为最后返回的一个基准,因为它是更准确的,粗排它只负责召回,不负责排序
  5. 重排可能存在也可能不存在,重排是基于特殊业务定义的规则进行的排序,它甚至可以脱离我们原本的推荐模型的推荐逻辑
  6. 最下方是一个数据收集层,无论是数据同步,还是页面埋点等等都是数据的收集方式,训练模型要从数据平台将数据拿到并训练它

个性化召回als算法

  1. ALS(交替最小二乘,Alternating Least Squares)算法是一种用于矩阵填充或推荐系统中的方法,它通过分解一个已知部分元素的矩阵来发现隐藏特征,并使用这些特征进行预测或填补缺失值,这种方法特别适用于处理稀疏数据集,如用户-物品评分矩阵,在这种矩阵中,大多数条目是没有被评分过的
  2. 矩阵分解的目标是将原始评分矩阵分解为两个较低维度的矩阵的乘积,在ALS中,这两个矩阵分别代表用户特征矩阵和物品特征矩阵。每个用户或物品都由向量表示,这个向量捕捉了该用户或物品的一组隐藏特征,ALS通过迭代地优化这些特征向量来最小化预测评分与实际评分之间的平方误差
  3. 我们通过画图来解释
    在这里插入图片描述

假设我们现在有三个商品,四个用户,我们组成了上图的矩阵,可以看到矩阵中有些地方没值,有些地方有值,它们分别代表什么意思呢? 比如说user2 它对应的product2 是1.0分,代表说user2它查看了product2,那么我们就给1.0分,那么图中的3.0分呢,也很好解释,比如user1它对应的product1是3.0分,代表说user1不光查看product1,他还收藏并且购买了product1,那就给它加上2.0分也就是浏览行为1分剩下的收藏和交易分别是1分,就是1(浏览行为)+ 1(收藏行为) + 1(购买行为)

我们说完了矩阵中有值的地方,那没值就代表没有显式的特征,没有任何对应的相关行为,没值的地方就是als算法要实现的事儿,把矩阵当中的空格填充上,比如说user2对应product1感兴趣的分数应该是多少呢?

既然无法将不同user和product的相关性直接关联出来,我们就要找别的办法,比如不同用户之间的特征,商品之间的特性,这个相关性我们是好找的,什么意思呢?我们看下图
在这里插入图片描述
这个矩阵代表用户行为对应的特征得分
在这里插入图片描述
这个矩阵代表商品对应的特征得分,比如说一个商品:电脑,电脑是有特征的,它既是电脑这个具体的类目,它也属于电子设备,这都算它的特征

我们可以将这两个矩阵做一个矩阵相乘,怎么乘呢,也就是user1的f1 * product1的f1 + user1的f2 * product1的f2 以此类推, 最后将得到的数值放回最开始的矩阵,此时矩阵中就没有空值了,可以看到als算法就是一个预测打分的算法

个性化排序lr(Logistic Regression)算法

  1. 什么是逻辑回归
    逻辑回归是一种预测工具,用来判断某件事情是否会发生, 比如说,你可以用它来预测一个人是否会得某种病,或者是否会购买某个产品
  2. 它是怎么工作的?
    逻辑回归的工作原理有点像我们在纸上画一条分界线, 想象一下你在玩一个游戏,你要根据一些线索(比如年龄、性别、生活习惯等)来猜测一个人会不会喜欢足球, 逻辑回归就是帮助我们找到一条最好的“分界线”,这条线能够最好地区分开喜欢足球的人和不喜欢足球的人
    在这里插入图片描述
  3. 红色部分的线是什么意思?
    红色的线实际上代表了一个决策边界,这条线帮助我们将数据分成两部分:一边是“喜欢足球”的人,另一边是“不喜欢足球”的人
  4. 路中的绿色点和紫色点代表什么呢?
    红色的线我们可以以解为 是否喜欢足球, 在红色线条的上方, 绿色点距离红线越近代表喜欢它喜欢足球的可能性越大,而在红线的下方,紫色点距离红线越远代表它越是不喜欢足球
  5. 距离决策边界(红线)的远近反映了模型对于分类的置信度,大白话的意思就是,红线上部分的点我认为你可能喜欢足球,点位距离越近我认为你越喜欢,相反越远我可以大概猜测你可能是喜欢,但是喜欢的程度不高,红线的下方,点位越远我认为你越讨厌足球,但是如果点位在下方但是距离红线很近,我认为你可能讨厌足球,但不十分确定
  6. 基于这样的一个模型训练完成后,当一个新的点加入后,我们通过判断新加入的点是在红线的上方还是下方,并且根据红线的距离远近,就可以做出决策,越是红线上方并且接近红线的,推荐模型就应该优先将它推荐出去

Spark

  1. 做大数据的应该听说过Spark,或者说实际工作就在用Spark,那这里就简单介绍一下Spark,Spark是为大规模数据处理而设计的计算引擎,它的家族很庞大,有Spark Core, Spark SQL, Spark on Hive, Spark Streaming等等,在介绍Spark之前,先说一下另一个模型 MapReduce (耳熟能详的Hadoop实现了MapReduce模型)
  2. 那MapReduce是干嘛的呢?
    MapReduce 的核心思想是将大规模数据处理任务分解成可以在集群上并行执行的小任务,试想一下,如果java当中我们将一个集合转为另一个集合会怎么做,for循环赋值新对象 或者用stream流实现对吧,但是我们一旦我们的数据量庞大到一定级别之后,怎么办呢,你能一次性加载到内存中吗?不行,那这时候来办法了,批量处理嘛,在单机场景下这确实也是一个结局方案,但是我如果是分布式部署的架构呢,你还单机批次处理吗?这时候又来办法了,假设100条数据拆分成4个任务,每个25条给4台机器处理,ok,没问题,那我需要你汇总处理完的数据呢?如果其中一台机器执行任务失败了怎么办呢?如果4台机器挂了一台怎么办?你能将挂掉那台机器未执行完的任务很快的放到正常的机器上去重跑吗? MapReduce不仅解决了数据量庞大的问题,拆分的问题,数据汇总的问题,还提供了高可用性和容错能力,当然MapReduce如果你的项目没有集成Hadoop,就用不了Hadoop提供的API了,那么就需要手动实现MapReduce了,这个就复杂了,那接下来再讲一下Spark
  3. Apache Spark 是一个用于大规模数据处理的开源分布式计算系统。它提供了一个可扩展的计算平台,支持多种计算模式,包括批处理、实时流处理、机器学习和图形计算。Spark 的设计目的是为了克服 Hadoop MapReduce 的一些局限性,特别是在迭代计算和数据交互分析方面,Spark MLlib 库(机器学习库)将是我们接下来基于机器学习的推荐模型的重点部分

Spark 和 MapReduce的相同点和不同点

相同点

目标相似:两者都是为了解决大规模数据处理问题而设计的。 分布式计算:两者都支持在分布式集群上进行数据处理,可以利用多台机器的计算资源。
容错机制:两者都内置了容错机制,能够在任务失败时重新调度和执行。

不同点

计算模型

MapReduce: 计算模型:MapReduce 主要有两个阶段:Map 和 Reduce。Map
阶段将数据映射为中间键值对,Reduce 阶段将这些键值对归约为最终结果。 数据处理方式:MapReduce
是基于磁盘的操作模式,每个任务完成后需要将结果写入磁盘,然后再从磁盘读取进行下一步处理。 Spark: 计算模型:Spark 使用
RDD(Resilient Distributed Datasets)模型,支持多种操作,包括 transform(转换)和
action(动作)。 数据处理方式:Spark 可以将中间结果缓存在内存中,减少了磁盘 I/O 的开销,从而提高了处理速度。

性能

MapReduce: 性能:MapReduce 在处理迭代计算时相对较慢,因为每次迭代都需要从磁盘读取数据,然后将结果写回磁盘。
适用场景:更适合批处理任务,如日志分析、大规模数据的初步处理等。 Spark: 性能:Spark
通过内存缓存大大加快了迭代计算的速度,因此在机器学习、图计算等领域表现更好。
适用场景:适合需要频繁迭代计算的任务,如机器学习、实时数据分析等。

程序模型

MapReduce: 编程模型:MapReduce 的编程模型较为简单,但不够灵活,通常需要手动编写大量的代码来处理数据。
API:MapReduce 提供了基本的 API 来实现 Map 和 Reduce 功能。 Spark: 编程模型:Spark
提供了更高级的编程模型,支持多种语言(Java、Scala、Python、R),并且提供了丰富的 API。 API:除了 RDD,Spark
还支持 DataFrame 和 DataSet API,使得处理结构化数据更为方便。

生态系统

MapReduce: 生态系统:MapReduce 通常与 Hadoop 生态系统结合使用,如 HDFS、Hive、Pig 等。
扩展性:Hadoop MapReduce 生态系统相对成熟,支持多种数据处理工具。 Spark: 生态系统:Spark
也有自己的生态系统,包括 Spark SQL、Spark Streaming、MLlib、GraphX 等。 扩展性:Spark
的生态系统更加现代化,提供了更多的功能和支持,如实时流处理、机器学习库等。

SpringBoot接入Spark MLlib

使用ALS算法训练模型并进行结果预测

  1. 创建一个训练文件 当然这里主要是给予我们的原本的业务的示范的假数据
    在这里插入图片描述
    第一列代表用户id 当然实际在分布式系统肯定是生成的是UUID的方式而不是主键自增
    第二列代表商户id
    第三列代表这个用户给对应的门店打了多少分(1-5分之间)

  2. 尝试离线的方式通过ALS算法去训练我的召回模型,并且将预测结果存储DB当中,代码如下,分为两部分AlsTraining是训练模型,AlsModelPrediction是通过训练好的模型进行预测并将结果存入DB当中

        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-mllib_2.12</artifactId>
            <version>2.4.4</version>
        </dependency>
/**
 * ALS召回算法模型的训练
 * @author 随风
 */
@Data
public class AlsTraining implements Serializable {

    public static void main(String[] args) throws IOException {
        // 1.初始化Spark运行环境
        SparkSession sparkSession = SparkSession.builder().master("local").appName("business_info").getOrCreate();

        // 2.读取A.csv文件
        JavaRDD<String> csvFile = sparkSession.read().textFile("/src/main/java/org/jeecg/modules/eoa/cms/service/impl/A.csv").toJavaRDD();

        // 3.解析csvFile文件转为Dataset
        Dataset<Row> dataFrame = sparkSession.createDataFrame(csvFile.map((Function<String, TrainingModel>) TrainingModel::parseStr), TrainingModel.class);

        // 4.将dataFrame进行2 8拆分 百分之80做模型训练 百分之20做测试
        Dataset<Row>[] datasetArray = dataFrame.randomSplit(new double[]{0.8, 0.2});

        Dataset<Row> trainingData = datasetArray[0];
        Dataset<Row> testData = datasetArray[1];

        ALS als = new ALS().setMaxIter(10).setRank(5).setRegParam(0.01).setUserCol("userId").setItemCol("shopId").setRatingCol("score");

        // 调通fit进行ALS算法的训练 并得到经过计算后返回的模型
        ALSModel alsModel = als.fit(trainingData);

        // 训练完成之后我们可以通过testData对训练好的模型进行测试一下
        Dataset<Row> predictions = alsModel.transform(testData);

        // 均方根误差(Root Mean Square Error,简称RMSE)是一种衡量预测值与真实值之间差异的指标
        // 它表示预测误差的大小 并且以同样的单位度量了预测误差的幅度。RMSE 越小,说明预测模型的拟合优度越高;反之,则拟合优度越差。
        // 如果想在Spark中计算RMSE,可以使用RegressionEvaluator类来评估模型性能。

        RegressionEvaluator regressionEvaluator = new RegressionEvaluator().setMetricName("rmse").setLabelCol("score").setPredictionCol("prediction");
        // 计算RMSE
        double rmse = regressionEvaluator.evaluate(predictions);

        System.out.println("rmse:" + rmse);

        // RMSE的值 依赖于我们循环数据的质量 并且跟 setMaxIter(10).setRank(5).setRegParam(0.01) 有直接关系
        // maxIter 表示最大迭代次数 ALS算法是一个迭代优化过程 在每次迭代中都会更新用户和项目的因子矩阵 设置这个参数可以控制算法的最大迭代次数 以避免不必要的计算或过拟合 我们设置的是10次
        // rank 是指隐语义模型的维度,即用户和项目的因子向量的长度。在推荐系统中,通常会将用户和物品映射到一个低维空间,以便捕捉到它们之间的相似性。较高的秩可能会提高模型的表达能力,但也可能导致过拟合。这里设置的秩为5
        // regParam 正则化参数,用于控制模型复杂度,防止过拟合。较高的正则化参数会使得模型更加简单,而较低的正则化参数可能会让模型更复杂。在这里设置的正则化参数为0.01
        // 当rmse的值越低, 那么越是符合我们的预期, 比如0.5分, 代表我的真实值与模型预测值的误差很小,反之则认为模型不是很符合预期
        // 如果发现rmse比较高,可以调整als算法的参数,来逐步查看效果

        // 最后将我们训练好的模型存储下来
        alsModel.save("D:/AlsModel");
    }

    @Data
    @AllArgsConstructor
    public static class TrainingModel implements Serializable {
        private String userId;
        private String shopId;
        private Integer score;

        public static TrainingModel parseStr(String str) {
            // 将CSV文件的数据 转化为对应的对象
            String[] split = str.split(";");
            return new TrainingModel(split[0], split[1], Integer.parseInt(split[2]));
        }
    }

}
/**
 * ALS召回算法模型的预测
 *
 * @author 随风
 */
@Slf4j
public class AlsModelPrediction {

    private final static int USERID_INDEX = 1;
    private final static int RECOMMEND_INDEX = 2;


    public static void main(String[] args) {
        // 1. 初始化Spark运行环境
        SparkSession sparkSession = SparkSession.builder().master("local").appName("business_info").getOrCreate();

        // 2. 拿到我们训练好的模型
        ALSModel alsModel = ALSModel.load("D:/AlsModel");

        // 3. 跟一批用户做离线的召回结果预测
        Dataset<Row> dataFrame = sparkSession.read().format("csv").option("header", "true").load("/src/main/java/org/jeecg/modules/eoa/cms/service/impl/user.csv");

        // 假设我们给10个用户做召回结果预测
        Dataset<Row> users = dataFrame.select(alsModel.getUserCol()).distinct().limit(10);
        Dataset<Row> userRecs = alsModel.recommendForUserSubset(users, 20);

        userRecs.foreachPartition((ForeachPartitionFunction<Row>) iterator -> {
            // 创建数据库连接池
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:mysql://192.xxx.xxx.xxx:3306/dbName?characterEncoding=UTF-8&useUnicode=true&useSSL=false");
            config.setUsername("username");
            config.setPassword("password");

            try (HikariDataSource dataSource = new HikariDataSource(config); Connection connection = dataSource.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO table(id, recommend) VALUES (?, ?)")) {

                List<Map<String, Object>> data = new ArrayList<>();

                while (iterator.hasNext()) {
                    Row action = iterator.next();
                    String userId = action.getString(0);
                    List<Row> recommendList = action.getList(1);
                   
					List<String> shopIdList = recommendList.stream()
					                                       .map(row -> row.getString(0))
					                                       .collect(Collectors.toList());

                    String recommendData = String.join(",", shopIdList);
                    Map<String, Object> map = new HashMap<>(shopIdList.size());
                    map.put("userId", userId);
                    map.put("recommend", recommendData);
                    data.add(map);
                }

                data.forEach(map -> {
                    try {
                        preparedStatement.setString(USERID_INDEX, (String) map.get("userId"));
                        preparedStatement.setString(RECOMMEND_INDEX, (String) map.get("recommend"));
                        preparedStatement.addBatch();
                    } catch (SQLException e) {
                        log.error("inserting data into database error", e);
                    }
                });

                preparedStatement.executeBatch();
            }
        });

        sparkSession.stop();
    }

}

我们在实际的应用场景当中预测数据也可以存储到缓存中间件中,比如Redis中, 避免我们后续使用还要去查询DB, 但是接入Redis之后要考虑数据同步的问题和缓存失效时间的问题,接入任何一个中间件都要考虑全面

使用LR算法进行逻辑回归

  1. 还记得上面说的LR算法(逻辑回归)的原理吗,其中有一个坐标系的图片,X轴和Y轴, X轴就是特征,X轴特征的好坏就决定了最后数据拟合的好快,而对应的概率我们放在Y轴上
  2. 我们的业务中对应LR的逻辑回归模型本质上就是一个点击率的预估模型
特征处理
  1. X轴既然是特征,我们就需要知道什么才算特征,怎么让LR算法的模型能够使用这些特征
  2. 试想一下对于用户表来讲 哪些是特征 哪些不是特征呢?比如id,id能算做特征吗?显然是不能的,id没有任何实际的特征意义,而对于年龄,则算作一个明显的特征,对于我们的模型来讲,要尽量将特征进行一个区间化,什么意思呢?假设我们的年龄区间为1-100,每个年龄都算做一个特征的话,那么就有100个特征了,我们可以将年龄进行区间化,1 - 15 岁为少年,16岁 - 30 岁为青年,31岁 - 50 岁为中年,51岁 - 100岁为老年,将年龄区间化作为特征,这种方法可以帮助逻辑回归模型更好地理解和预测不同年龄段用户的行为差异
  3. 我们再换一个属性,假设现在是商家的人均消费字段,又该如何提取特征呢?每个商家的人均消费都不一样,第一种方式我们同样可以利用区间化进行处理,第二种方式可以使用归一化进行处理,比如将人均价格归一化到特定的范围内(0-1之间),可以通过最小最大缩放(min-max scaling)或 z-score标准化来实现
  4. 我们以如下例子来解释最小最大缩放(min-max scaling)和 z-score标准化
    假设我们有一组人均价格的数据[50,100,150,200,250]
    最小最大缩放(min-max scaling)
    在这里插入图片描述

z-score标准化
在这里插入图片描述
我们的数据中不能出现极端值,比如一个极其小的值或者极其大的值,比如一组数据[1,2,3,4,5,1000]会直接影响到最后的结果,1000就是一个极大值

最后我们需要编写代码 将我们用户的一些特征,通过编码实现特征处理 进行抽象,写入到一个文件当中
最后的文件(csv)类似如下
在这里插入图片描述
解释一下这个文件什么意思
1 - 4 列代表这个用户的年龄 第一列就是少年 第二列青年 一次类推到第四列 (我们可以将这些列视为独热编码(one-hot encoding)的特征)
5 - 6 列就是这个用户的行为 第四列是男 第五列是女
7 列是用户对一个商家的评分 但是做了预处理 由于满分是 5 分 直接将用户的打分 / 5 得到第7列, 比如用户给的打分为4分,则4/5 = 0.8
8 - 11 列是当前用户的一个消费水准 8列代表低挡消费 9列中档消费 以此类推到11列
12列是当前用户有没有浏览过当前的商家 1 是浏览过 0是没有浏览过

代码实现训练LR模型
/**
 * LR逻辑回归模型的训练
 *
 * @author 随风
 */
public class LrTraining {

    public static void main(String[] args) throws IOException {
        // 1.初始化Spark运行环境
        SparkSession sparkSession = SparkSession.builder().master("local").appName("business_info").getOrCreate();

        // 2.加载特征训练文件
        JavaRDD<String> csvFile = sparkSession.read().textFile("/src/main/java/org/jeecg/modules/eoa/cms/service/impl/C.csv").toJavaRDD();

        JavaRDD<Row> rowJavaRdd = csvFile.map((Function<String, Row>) s -> {
            // s 就是每一行数据
            // 对于我们的坐标系来说 y轴就是是否浏览 1是0不是 对应我们的csv文件 就是第12列 这里下标从0开始 就是取11
            String[] split = s.split(",");
            double column12 = Double.parseDouble(split[11]);
            return RowFactory.create(column12, Vectors.dense(Double.parseDouble(split[0])), Vectors.dense(Double.parseDouble(split[1])), Vectors.dense(Double.parseDouble(split[2])), Vectors.dense(Double.parseDouble(split[3])), Vectors.dense(Double.parseDouble(split[4])), Vectors.dense(Double.parseDouble(split[5])), Vectors.dense(Double.parseDouble(split[6])), Vectors.dense(Double.parseDouble(split[7])), Vectors.dense(Double.parseDouble(split[8])), Vectors.dense(Double.parseDouble(split[9])), Vectors.dense(Double.parseDouble(split[10])));
        });

        StructType schema = new StructType(new StructField[]{new StructField("label", DataTypes.DateType, false, Metadata.empty()), new StructField("features", new VectorUDT(), false, Metadata.empty())});


        Dataset<Row> data = sparkSession.createDataFrame(rowJavaRdd, schema);
        // 分开训练和测试
        Dataset<Row>[] dataArr = data.randomSplit(new double[]{0.8, 0.2});
        Dataset<Row> trainingData = dataArr[0];
        Dataset<Row> testData = dataArr[1];

        // 初始化 LR 逻辑回归模型
        LogisticRegression lr = new LogisticRegression().setMaxIter(10).setRegParam(0.3).setElasticNetParam(0.8).setFamily("multinomial");

        // 调用fit进行训练 得到LR模型
        LogisticRegressionModel lrModel = lr.fit(trainingData);

        // 测试评估
        Dataset<Row> predictions = lrModel.transform(testData);

        MulticlassClassificationEvaluator evaluator = new MulticlassClassificationEvaluator();
        double accuracy = evaluator.setMetricName("accuracy").evaluate(predictions);

        System.out.println("accuracy:" + accuracy);

        // 存储lr模型
        lrModel.save("D:/lrModel");
    }

}

至此我们的ALS个性化的召回模型以及LR回归模型就完成了,下一章节将使用我们训练好的模型接入到我们的应用当中,实现个性化推荐

在传统的开发中,我们可能会使用定时任务去实现数据同步的操作,但是这种方案存在一定的不足,例如数据同步的实时性可能不太好;此外,数据条数增多也可能会导致定时任务处理的效率变低。因此,我们可以使用springboot结合es(elasticsearch实现数据同步,来解决上述的问题。 首先,我们需要在springboot中引入es的相关依赖,例如spring-boot-starter-data-elasticsearch,这样我们就可以在项目中使用es的功能。接着,我们需要定义es的索引,并且定义好与数据库表的映射关系。这里可以使用spring-data-elasticsearch来完成。因为在es中没有表的概念,而是使用索引和类型来代替,因此我们需要将数据库表的数据转换成es中的文档格式。 在实现数据同步的功能时,我们可以使用JPA的监听器,例如@PostPersist,@PostUpdate,@PostRemove等注解,当数据库表的数据发生变化时,监听器会自动触发相应的方法,我们可以在这些方法中编写将数据同步到es的代码逻辑,确保数据同步的实时性。 除了使用监听器来实现数据同步,我们还可以考虑使用消息队列(MQ)的方式,以便将数据同步的逻辑异步执行,降低数据同步时的压力。在MQ应用中,当数据源发生变化时,我们只需要将变化的数据同步到MQ中,之后再使用MQ监听器将数据异步地同步到es中。 总的来说,springboot结合es实现数据同步,能够提升数据同步的实时性和效率,使整个系统更加稳定可靠,适用于数据实时同步场景,同时也为数据分析、搜索等场景提供了更好的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值