Java+AI实战:从零构建智能推荐系统(一)

简介: 教程来源 https://tmywi.cn/category/jiankang.html 本文详解如何用Java从零构建生产级智能推荐系统SmartRec,覆盖数据采集、特征工程、多路召回、深度排序、重排及A/B测试全链路。聚焦高并发、实时性与可扩展性,助你掌握AI落地核心能力。

在当今数据驱动的时代,人工智能已经渗透到各行各业,而推荐系统作为AI最成功的商业应用之一,驱动着电商、短视频、新闻资讯等平台的用户增长和商业变现。Java作为企业级应用开发的主流语言,凭借其卓越的性能、完善的生态系统和强大的并发处理能力,在构建大规模、高可用的推荐系统中扮演着核心角色。然而,将AI算法落地到生产环境面临着诸多挑战:如何处理海量用户行为数据?如何设计高效的召回和排序算法?如何保证推荐结果的实时性?如何进行A/B测试和模型迭代?

本文将从零构建一个生产级智能推荐系统,完整呈现Java+AI项目的实战流程,涵盖数据采集、特征工程、召回算法、排序模型、在线服务、A/B测试等全链路环节。通过这个实战项目,你将掌握Java在大规模机器学习系统中的应用技巧,理解推荐系统的核心原理,并具备独立构建智能推荐引擎的能力。

第一部分:项目概述与架构设计

1.1 项目背景与目标
在电商场景中,用户面对海量商品时往往难以找到自己感兴趣的商品。个性化推荐系统通过分析用户的历史行为、实时行为和商品属性,主动为用户推荐可能感兴趣的商品,从而提升用户体验和平台转化率。

项目名称: SmartRec - 智能推荐系统

核心目标:
image.png
1.2 推荐系统核心概念
推荐系统的本质是一个信息过滤系统,它从海量物品中筛选出用户最可能感兴趣的物品。从数学角度来看,推荐系统要解决的核心问题是:如何预测用户u对物品i的评分(或点击概率)。

推荐系统的三个核心阶段:

┌─────────────────────────────────────────────────────────────────────┐
│                         推荐系统架构                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                      数据层                                   │    │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐        │    │
│  │  │用户行为日志│ │商品信息 │ │用户画像 │ │上下文信息│        │    │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘        │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                              │                                       │
│                              ▼                                       │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                      召回层(Recall)                         │    │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐        │    │
│  │  │协同过滤  │ │向量召回  │ │热门召回  │ │品类召回  │        │    │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘        │    │
│  │                    从百万商品 → 千级候选                        │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                              │                                       │
│                              ▼                                       │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                      排序层(Ranking)                        │    │
│  │  ┌──────────────────────────────────────────────────────┐   │    │
│  │  │              深度学习排序模型(DNN)                   │   │    │
│  │  │       特征交叉 | 用户兴趣网络 | 商品特征网络           │   │    │
│  │  └──────────────────────────────────────────────────────┘   │    │
│  │                    千级候选 → 排序后的Top-N                     │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                              │                                       │
│                              ▼                                       │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                      重排层(Re-ranking)                     │    │
│  │        多样性调整 | 业务规则过滤 | 去重 | 打散                │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                              │                                       │
│                              ▼                                       │
│                    ┌─────────────────┐                              │
│                    │   最终推荐结果    │                              │
│                    └─────────────────┘                              │
└─────────────────────────────────────────────────────────────────────┘

核心概念解释:

召回(Recall):从海量候选集中快速筛选出用户可能感兴趣的一小部分物品(通常几百到几千)。召回阶段要求速度快、覆盖广,可以接受一定的误差。
排序(Ranking):对召回阶段的候选物品进行精细化的点击率预测,按照预测分数排序。排序阶段要求精度高,通常使用复杂的机器学习模型。
重排(Re-ranking):对排序结果进行后处理,包括多样性调整、业务规则过滤、去重等,保证推荐结果的用户体验。

1.3 技术选型
image.png
1.4 项目结构

smart-rec/
├── pom.xml                           # Maven父POM
├── README.md
│
├── smart-rec-common/                 # 公共模块
│   ├── src/main/java/
│   │   └── com/smartrec/common/
│   │       ├── entity/               # 公共实体
│   │       ├── enums/                # 枚举定义
│   │       ├── exception/            # 异常定义
│   │       └── utils/                # 工具类
│   └── src/main/resources/
│
├── smart-rec-data/                   # 数据模块
│   ├── src/main/java/
│   │   └── com/smartrec/data/
│   │       ├── collector/            # 数据采集
│   │       ├── processor/            # 数据加工
│   │       ├── feature/              # 特征工程
│   │       └── storage/              # 数据存储
│   └── src/main/resources/
│
├── smart-rec-recall/                 # 召回模块
│   ├── src/main/java/
│   │   └── com/smartrec/recall/
│   │       ├── collaborative/        # 协同过滤
│   │       ├── vector/               # 向量召回
│   │       ├── hot/                  # 热门召回
│   │       ├── context/              # 上下文召回
│   │       └── merge/                # 多路召回合并
│   └── src/main/resources/
│
├── smart-rec-ranking/                # 排序模块
│   ├── src/main/java/
│   │   └── com/smartrec/ranking/
│   │       ├── feature/              # 特征工程
│   │       ├── model/                # 排序模型
│   │       ├── predict/              # 在线预测
│   │       └── rerank/               # 重排
│   └── src/main/resources/
│
├── smart-rec-api/                    # API服务模块
│   ├── src/main/java/
│   │   └── com/smartrec/api/
│   │       ├── controller/           # REST控制器
│   │       ├── service/              # 推荐服务
│   │       ├── cache/                # 结果缓存
│   │       └── abtest/               # A/B测试
│   └── src/main/resources/
│
├── smart-rec-flink/                  # 实时计算模块
│   ├── src/main/java/
│   │   └── com/smartrec/flink/
│   │       ├── source/               # 数据源
│   │       ├── transform/            # 数据转换
│   │       └── sink/                 # 数据输出
│   └── src/main/resources/
│
├── scripts/                          # 运维脚本
│   ├── deploy.sh
│   ├── train.sh
│   └── benchmark.sh
│
└── docker/                           # Docker配置
    ├── Dockerfile
    └── docker-compose.yml

第二部分:数据采集与特征工程

2.1 数据采集架构
推荐系统的质量很大程度上取决于数据的质量和丰富度。我们需要采集三类核心数据:

  1. 用户行为数据(User Behavior Log)

用户行为数据是推荐系统最重要的信号,包括:
image.png

  1. 商品属性数据(Item Attributes)
    商品信息是推荐系统的另一个重要输入:
    基础信息:商品ID、标题、价格、品牌、类目
    内容信息:图片特征、描述文本
    统计信息:销量、点击率、转化率
  2. 用户画像数据(User Profile)
    用户画像描述用户的长期兴趣:
    人口属性:年龄、性别、地域
    长期兴趣:类目偏好、品牌偏好、价格偏好
    行为统计:活跃度、购买力、复购率
    ```
    // smart-rec-data/src/main/java/com/smartrec/data/collector/UserBehaviorCollector.java
    package com.smartrec.data.collector;

import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.springframework.stereotype.Component;

import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**

  • 用户行为数据采集器
  • 用户行为日志是推荐系统最重要的数据源。本采集器负责:
    1. 从前端埋点SDK接收用户行为事件
    1. 对事件进行格式化和校验
    1. 异步发送到Kafka消息队列
  • 数据流向:
  • 前端埋点 → Nginx → Logstash → Kafka → Flink实时计算
  • HDFS离线存储
    */
    @Slf4j
    @Component
    public class UserBehaviorCollector {

    private static final String TOPIC_USER_BEHAVIOR = "user-behavior-topic";

    private final KafkaProducer producer;
    private final ExecutorService asyncExecutor;

    public UserBehaviorCollector() {
    // 配置Kafka生产者
    Properties props = new Properties();
    props.put("bootstrap.servers", "localhost:9092");
    props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
    props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
    props.put("acks", "1"); // 只等待leader确认,平衡可靠性和性能
    props.put("compression.type", "snappy"); // 启用压缩,减少网络传输
    props.put("linger.ms", "5"); // 批量发送延迟,提高吞吐量

    this.producer = new KafkaProducer<>(props);
    this.asyncExecutor = Executors.newSingleThreadExecutor();

    log.info("UserBehaviorCollector initialized");
    }

    /**

    • 记录用户行为事件
    • @param event 用户行为事件
    • 事件结构示例:
    • {
    • "userId": 12345,
    • "itemId": 67890,
    • "actionType": "click",
    • "timestamp": 1700000000000,
    • "sessionId": "abc-def-ghi",
    • "page": "home",
    • "position": 3,
    • "duration": 2500
    • }
      */
      public void collect(BehaviorEvent event) {
      // 异步发送,不阻塞主线程
      asyncExecutor.submit(() -> {
      try {
      String json = event.toJson();
      ProducerRecord record = new ProducerRecord<>(
      TOPIC_USER_BEHAVIOR,
      String.valueOf(event.getUserId()), // 使用userId作为key,保证同一用户的事件有序
      json
      );

    producer.send(record, (metadata, exception) -> {
    if (exception != null) {
    log.error("Failed to send behavior event: {}", json, exception);
    } else {
    log.debug("Behavior event sent: offset={}", metadata.offset());
    }
    });

    } catch (Exception e) {
    log.error("Failed to collect behavior event", e);
    }
    });
    }

    /**

    • 批量采集
    • 适用于离线批处理场景,如每日全量同步
      */
      public void batchCollect(List events) {
      for (BehaviorEvent event : events) {
      collect(event);
      }
      }

    @PreDestroy
    public void destroy() {
    asyncExecutor.shutdown();
    try {
    if (!asyncExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
    asyncExecutor.shutdownNow();
    }
    } catch (InterruptedException e) {
    asyncExecutor.shutdownNow();
    }
    producer.close();
    log.info("UserBehaviorCollector destroyed");
    }
    }

    **2.2 特征工程**
    特征工程是机器学习中最重要的环节。好的特征能让简单的模型发挥出惊人效果,而糟糕的特征即使使用深度学习也难以弥补。
    

    // smart-rec-data/src/main/java/com/smartrec/data/feature/FeatureExtractor.java
    package com.smartrec.data.feature;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**

  • 特征提取器
  • 特征工程的核心思想是将原始数据转换为模型可以理解的数值特征。
  • 特征类型:
    1. 数值特征(Numerical Feature):用户年龄、商品价格、历史点击次数
    1. 类别特征(Categorical Feature):用户性别、商品类目、品牌
    1. 序列特征(Sequence Feature):用户最近点击的商品ID序列
    1. 交叉特征(Cross Feature):用户年龄×商品价格区间
    1. 统计特征(Statistical Feature):商品近7天点击率
  • 特征处理的三个关键步骤:
    1. 缺失值处理:填充默认值或使用统计值
    1. 归一化:将不同量纲的特征映射到相同范围
    1. 离散化:将连续值转换为离散桶(适用于线性模型)
      */
      @Slf4j
      @Component
      public class FeatureExtractor {

      // 特征名称常量
      public static final String FEAT_USER_ID = "user_id";
      public static final String FEAT_ITEM_ID = "item_id";
      public static final String FEAT_USER_AGE = "user_age";
      public static final String FEAT_USER_GENDER = "user_gender";
      public static final String FEAT_USER_CITY = "user_city";
      public static final String FEAT_USER_PURCHASE_POWER = "user_purchase_power";
      public static final String FEAT_ITEM_PRICE = "item_price";
      public static final String FEAT_ITEM_CATEGORY = "item_category";
      public static final String FEAT_ITEM_BRAND = "item_brand";
      public static final String FEAT_ITEM_SALES_7D = "item_sales_7d";
      public static final String FEAT_ITEM_CTR_7D = "item_ctr_7d";
      public static final String FEAT_USER_ITEM_CLICK_COUNT = "user_item_click_count";
      public static final String FEAT_USER_ITEM_BUY_COUNT = "user_item_buy_count";
      public static final String FEAT_USER_CATEGORY_PREF = "user_category_pref";
      public static final String FEAT_USER_BRAND_PREF = "user_brand_pref";
      public static final String FEAT_USER_PRICE_PREF = "user_price_pref";
      public static final String FEAT_HOUR = "hour";
      public static final String FEAT_WEEKDAY = "weekday";
      public static final String FEAT_IS_WEEKEND = "is_weekend";
      public static final String FEAT_USER_ACTIVE_LEVEL = "user_active_level";

      // 用户特征缓存(定期从数据库加载)
      private final Map userFeatureCache = new ConcurrentHashMap<>();
      // 商品特征缓存
      private final Map itemFeatureCache = new ConcurrentHashMap<>();
      // 统计特征缓存(实时更新)
      private final Map statFeatureCache = new ConcurrentHashMap<>();

      private final RedisTemplate redisTemplate;

      public FeatureExtractor(RedisTemplate redisTemplate) {
      this.redisTemplate = redisTemplate;
      // 启动定时刷新任务
      startRefreshTask();
      }

      /**

      • 提取用户特征
      • @param userId 用户ID
      • @return 用户特征向量
        */
        public UserFeatures extractUserFeatures(Long userId) {
        // 先从缓存获取
        UserFeatures features = userFeatureCache.get(userId);
        if (features != null) {

        return features;
        

        }

        // 从数据库加载
        features = loadUserFeaturesFromDB(userId);
        userFeatureCache.put(userId, features);
        return features;
        }

      /**

      • 提取商品特征
      • @param itemId 商品ID
      • @return 商品特征向量
        */
        public ItemFeatures extractItemFeatures(Long itemId) {
        ItemFeatures features = itemFeatureCache.get(itemId);
        if (features != null) {

        return features;
        

        }

        features = loadItemFeaturesFromDB(itemId);
        itemFeatureCache.put(itemId, features);
        return features;
        }

      /**

      • 提取交叉特征
      • 交叉特征是推荐系统中最重要的特征类型。
      • 例如:用户是否点击过该商品、用户对该类目的偏好程度等。
      • 交叉特征的计算通常涉及实时查询用户历史行为。
        */
        public CrossFeatures extractCrossFeatures(Long userId, Long itemId) {
        CrossFeatures features = new CrossFeatures();

        // 1. 用户-商品交互特征
        features.setUserItemClickCount(getUserItemClickCount(userId, itemId));
        features.setUserItemBuyCount(getUserItemBuyCount(userId, itemId));
        features.setUserItemLastClickTime(getUserItemLastClickTime(userId, itemId));

        // 2. 用户-类目偏好
        ItemFeatures itemFeatures = extractItemFeatures(itemId);
        String category = itemFeatures.getCategory();
        features.setUserCategoryClickCount(getUserCategoryClickCount(userId, category));
        features.setUserCategoryBuyCount(getUserCategoryBuyCount(userId, category));
        features.setUserCategoryPreference(calculateUserCategoryPreference(userId, category));

        // 3. 用户-品牌偏好
        String brand = itemFeatures.getBrand();
        features.setUserBrandClickCount(getUserBrandClickCount(userId, brand));
        features.setUserBrandPreference(calculateUserBrandPreference(userId, brand));

        // 4. 用户-价格偏好匹配
        double itemPrice = itemFeatures.getPrice();
        double userPricePreference = getUserPricePreference(userId);
        features.setPriceMatchScore(calculatePriceMatchScore(itemPrice, userPricePreference));

        // 5. 实时兴趣特征(最近1小时的点击类目)
        List recentCategories = getRecentUserCategories(userId, 60);
        features.setIsCategoryHot(recentCategories.contains(category));
        features.setCategoryRecencyScore(calculateRecencyScore(recentCategories, category));

        return features;
        }

      /**

      • 提取上下文特征
      • 上下文特征描述用户当前的场景,如时间、设备、地理位置等。
      • 这些特征可以帮助模型理解用户的即时需求。
        */
        public ContextFeatures extractContextFeatures() {
        ContextFeatures features = new ContextFeatures();

        Calendar cal = Calendar.getInstance();
        features.setHour(cal.get(Calendar.HOUR_OF_DAY));
        features.setWeekday(cal.get(Calendar.DAY_OF_WEEK));
        features.setIsWeekend(features.getWeekday() == Calendar.SATURDAY ||

                           features.getWeekday() == Calendar.SUNDAY);
        

        features.setIsWorkTime(features.getHour() >= 9 && features.getHour() <= 18);

        // 季节性特征
        int month = cal.get(Calendar.MONTH);
        features.setIsShoppingFestival(isShoppingFestival(month));

        return features;
        }

      /**

      • 提取序列特征
      • 序列特征记录了用户行为的时序信息,对于捕捉用户兴趣演化至关重要。
      • 常用处理方法:
        1. 序列Embedding:使用RNN/LSTM对序列建模
        1. Attention机制:对序列中不同位置赋予不同权重
        1. 序列统计:最近N次行为的类目分布、价格变化趋势
          */
          public SequenceFeatures extractSequenceFeatures(Long userId, int sequenceLength) {
          SequenceFeatures features = new SequenceFeatures();

        // 获取用户最近的行为序列
        List recentActions = getUserRecentActions(userId, sequenceLength);

        // 提取类目序列
        List categorySequence = recentActions.stream()

        .map(action -> getItemCategory(action.getItemId()))
        .collect(Collectors.toList());
        

        features.setCategorySequence(categorySequence);

        // 提取商品ID序列(用于序列召回)
        List itemIdSequence = recentActions.stream()

        .map(UserAction::getItemId)
        .collect(Collectors.toList());
        

        features.setItemIdSequence(itemIdSequence);

        // 序列统计特征
        features.setUniqueCategoriesCount(categorySequence.stream().distinct().count());
        features.setCategoryDiversity(calculateDiversity(categorySequence));

        // 时间间隔序列
        List timeIntervals = calculateTimeIntervals(recentActions);
        features.setAvgTimeInterval(timeIntervals.stream().mapToLong(Long::longValue).average().orElse(0));

        // 行为类型分布
        Map actionTypeCount = recentActions.stream()

        .collect(Collectors.groupingBy(UserAction::getActionType, Collectors.counting()));
        

        features.setClickCount(actionTypeCount.getOrDefault(ACTION_CLICK, 0L));
        features.setCartCount(actionTypeCount.getOrDefault(ACTION_CART, 0L));
        features.setBuyCount(actionTypeCount.getOrDefault(ACTION_BUY, 0L));

        return features;
        }

      /**

      • 构建完整的特征向量
      • 这是特征工程的核心方法,将上述所有特征整合成一个特征向量,
      • 供排序模型使用。
        */
        public FeatureVector buildFeatureVector(Long userId, Long itemId, ContextFeatures context) {
        FeatureVector featureVector = new FeatureVector();

        // 1. 用户特征
        UserFeatures userFeatures = extractUserFeatures(userId);
        featureVector.putAll(userFeatures.toMap());

        // 2. 商品特征
        ItemFeatures itemFeatures = extractItemFeatures(itemId);
        featureVector.putAll(itemFeatures.toMap());

        // 3. 交叉特征
        CrossFeatures crossFeatures = extractCrossFeatures(userId, itemId);
        featureVector.putAll(crossFeatures.toMap());

        // 4. 上下文特征
        featureVector.putAll(context.toMap());

        // 5. 序列特征(压缩为统计值)
        SequenceFeatures sequenceFeatures = extractSequenceFeatures(userId, 20);
        featureVector.putAll(sequenceFeatures.toStatFeatures());

        // 6. 归一化处理
        normalize(featureVector);

        return featureVector;
        }

      /**

      • 特征归一化
      • 将不同量纲的特征映射到[0,1]区间,避免某些特征主导模型。
      • 常用归一化方法:
        1. Min-Max归一化:x' = (x - min) / (max - min)
        1. Z-Score归一化:x' = (x - μ) / σ
        1. Log变换:x' = log(1 + x)(适用于长尾分布的特征)
          */
          private void normalize(FeatureVector features) {
          // 对价格特征做log变换(价格分布通常是长尾的)
          if (features.containsKey(FEAT_ITEM_PRICE)) {
          double price = (double) features.get(FEAT_ITEM_PRICE);
          features.put(FEAT_ITEM_PRICE + "_log", Math.log1p(price));
          }

        // 对点击次数做Min-Max归一化
        if (features.containsKey(FEAT_USER_ITEM_CLICK_COUNT)) {

        double count = (double) features.get(FEAT_USER_ITEM_CLICK_COUNT);
        double maxCount = 100.0;  // 可根据历史数据确定
        features.put(FEAT_USER_ITEM_CLICK_COUNT + "_norm", Math.min(count / maxCount, 1.0));
        

        }

        // 对时间间隔做指数衰减
        if (features.containsKey("user_item_last_click_time")) {

        long lastClickTime = (long) features.get("user_item_last_click_time");
        long now = System.currentTimeMillis();
        long hoursSinceLastClick = (now - lastClickTime) / (3600 * 1000);
        // 指数衰减:越近的点击权重越大
        double recencyWeight = Math.exp(-hoursSinceLastClick / 24.0);
        features.put("recency_weight", recencyWeight);
        

        }
        }

      // ========== 以下是从Redis/数据库加载特征的实现 ==========

      private UserFeatures loadUserFeaturesFromDB(Long userId) {
      // 从数据库或Redis加载用户画像
      // 实际实现中会查询用户表
      UserFeatures features = new UserFeatures();
      features.setUserId(userId);
      features.setAge(25);
      features.setGender(1);
      features.setCity("beijing");
      features.setPurchasePower(2); // 1低 2中 3高
      features.setActiveLevel(3); // 1-5
      return features;
      }

      private ItemFeatures loadItemFeaturesFromDB(Long itemId) {
      ItemFeatures features = new ItemFeatures();
      features.setItemId(itemId);
      features.setPrice(99.9);
      features.setCategory("electronics");
      features.setBrand("apple");
      features.setSales7d(5000);
      features.setCtr7d(0.032);
      return features;
      }

      private int getUserItemClickCount(Long userId, Long itemId) {
      String key = String.format("user_item_click:%d:%d", userId, itemId);
      Integer count = (Integer) redisTemplate.opsForValue().get(key);
      return count != null ? count : 0;
      }

      private int getUserItemBuyCount(Long userId, Long itemId) {
      String key = String.format("user_item_buy:%d:%d", userId, itemId);
      Integer count = (Integer) redisTemplate.opsForValue().get(key);
      return count != null ? count : 0;
      }

      private long getUserItemLastClickTime(Long userId, Long itemId) {
      String key = String.format("user_item_last_click:%d:%d", userId, itemId);
      Long time = (Long) redisTemplate.opsForValue().get(key);
      return time != null ? time : 0;
      }

      private int getUserCategoryClickCount(Long userId, String category) {
      String key = String.format("user_category_click:%d:%s", userId, category);
      Integer count = (Integer) redisTemplate.opsForValue().get(key);
      return count != null ? count : 0;
      }

      private double calculateUserCategoryPreference(Long userId, String category) {
      // 计算用户对某个类目的偏好分数
      // 偏好 = 该类目点击占比 权重 + 该类目购买占比 更高权重
      int totalClicks = getUserTotalClicks(userId);
      int categoryClicks = getUserCategoryClickCount(userId, category);

      int totalBuys = getUserTotalBuys(userId);
      int categoryBuys = getUserCategoryBuyCount(userId, category);

      if (totalClicks == 0) return 0;

      double clickScore = (double) categoryClicks / totalClicks;
      double buyScore = totalBuys > 0 ? (double) categoryBuys / totalBuys : 0;

      // 购买行为的权重更高
      return clickScore 0.3 + buyScore 0.7;
      }

      private void startRefreshTask() {
      // 定时刷新特征缓存
      ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
      scheduler.scheduleAtFixedRate(() -> {

        log.info("Refreshing feature cache...");
        // 刷新统计特征
        refreshStatFeatures();
        // 清理过期的用户特征缓存
        userFeatureCache.clear();
        itemFeatureCache.clear();
      

      }, 1, 1, TimeUnit.HOURS);
      }

      private void refreshStatFeatures() {
      // 从数据库加载最新的统计特征
      // 如商品7日CTR、用户活跃度等
      }

      private List getUserRecentActions(Long userId, int limit) {
      // 从Redis获取用户最近的行为序列
      // 使用Sorted Set存储,时间戳作为score
      List actions = new ArrayList<>();
      // 实现省略
      return actions;
      }

      private List getRecentUserCategories(Long userId, int minutes) {
      // 获取最近几分钟内用户点击的类目
      return new ArrayList<>();
      }

      private double getUserPricePreference(Long userId) {
      // 获取用户的平均购买价格
      return 200.0;
      }

      private double calculatePriceMatchScore(double itemPrice, double userPricePref) {
      // 计算价格匹配度
      double ratio = Math.min(itemPrice, userPricePref) / Math.max(itemPrice, userPricePref);
      return ratio;
      }

      private double calculateRecencyScore(List recentCategories, String category) {
      // 计算类目在最近序列中的新鲜度分数
      if (recentCategories.isEmpty()) return 0;
      for (int i = 0; i < recentCategories.size(); i++) {

        if (recentCategories.get(i).equals(category)) {
            // 越近的位置权重越高
            return Math.exp(-i);
        }
      

      }
      return 0;
      }

      private double calculateDiversity(List sequence) {
      // 计算序列多样性
      long uniqueCount = sequence.stream().distinct().count();
      return (double) uniqueCount / sequence.size();
      }

      private List calculateTimeIntervals(List actions) {
      List intervals = new ArrayList<>();
      for (int i = 1; i < actions.size(); i++) {

        intervals.add(actions.get(i).getTimestamp() - actions.get(i-1).getTimestamp());
      

      }
      return intervals;
      }

      private boolean isShoppingFestival(int month) {
      // 双11、618等购物节
      return month == Calendar.NOVEMBER || month == Calendar.JUNE;
      }

      private String getItemCategory(Long itemId) {
      // 获取商品类目
      return "electronics";
      }

      private int getUserTotalClicks(Long userId) {
      // 获取用户总点击次数
      return 100;
      }

      private int getUserTotalBuys(Long userId) {
      // 获取用户总购买次数
      return 10;
      }

      private int getUserCategoryBuyCount(Long userId, String category) {
      // 获取用户在某类目的购买次数
      return 2;
      }
      }
      ```
      来源:
      https://tmywi.cn/

相关文章
|
8天前
|
人工智能 数据可视化 安全
王炸组合!阿里云 OpenClaw X 飞书 CLI,开启 Agent 基建狂潮!(附带免费使用6个月服务器)
本文详解如何用阿里云Lighthouse一键部署OpenClaw,结合飞书CLI等工具,让AI真正“动手”——自动群发、生成科研日报、整理知识库。核心理念:未来软件应为AI而生,CLI即AI的“手脚”,实现高效、安全、可控的智能自动化。
34505 22
王炸组合!阿里云 OpenClaw X 飞书 CLI,开启 Agent 基建狂潮!(附带免费使用6个月服务器)
|
20天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
45367 142
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
2天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
3053 11
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
9天前
|
人工智能 JSON 监控
Claude Code 源码泄露:一份价值亿元的 AI 工程公开课
我以为顶级 AI 产品的护城河是模型。读完这 51.2 万行泄露的源码,我发现自己错了。
5008 21
|
2天前
|
人工智能 监控 安全
阿里云SASE 2.0升级,全方位监控Agent办公安全
AI Agent办公场景的“安全底座”
1136 1
|
8天前
|
人工智能 API 开发者
阿里云百炼 Coding Plan 售罄、Lite 停售、Pro 抢不到?最新解决方案
阿里云百炼Coding Plan Lite已停售,Pro版每日9:30限量抢购难度大。本文解析原因,并提供两大方案:①掌握技巧抢购Pro版;②直接使用百炼平台按量付费——新用户赠100万Tokens,支持Qwen3.5-Max等满血模型,灵活低成本。
1983 6
阿里云百炼 Coding Plan 售罄、Lite 停售、Pro 抢不到?最新解决方案