电商AI导购系统设计:基于深度学习的商品推荐算法与架构实践
大家好,我是省赚客APP研发者阿可!在省赚客APP(juwatech.cn)中,用户日均浏览商品超千万次,传统基于规则或协同过滤的推荐已无法满足个性化与实时性需求。为此,我们构建了一套融合用户行为序列建模与多源特征交叉的深度学习推荐系统,采用双塔DNN+在线向量检索架构,实现"千人千面"的高转化导购体验。本文将从特征工程、模型训练到线上服务部署,结合核心 Java 与 Python 代码详解落地过程。
整体架构:离线训练 + 在线推理
系统分为三部分:
- 离线层:Flink 实时采集用户点击/加购/下单行为,写入 Kafka;
- 训练平台:PyTorch 构建双塔模型,每日增量训练并导出 Item Embedding;
- 在线服务:Java 微服务加载 User Embedding,通过 Faiss 向量库召回 Top-K 商品。
特征工程与样本构造
用户行为日志经 Flink 处理后生成训练样本:
python
# 样本格式:user_id, item_id, label(1=成交, 0=曝光未成交)
# 用户侧特征:历史点击品类分布、活跃时段、设备类型
# 商品侧特征:类目、价格分段、佣金率、7日CTR
def build_features(df):
user_features = df.groupby('user_id').agg({
'category': lambda x: Counter(x).most_common(3),
'hour': 'mean',
'device': 'first'
})
item_features = df[['item_id', 'category', 'price_bin', 'commission_rate', 'ctr_7d']].drop_duplicates()
return user_features, item_features
正负样本按 1:5 采样,确保模型聚焦高价值转化。

双塔DNN模型结构(PyTorch)
用户塔与商品塔分别编码,内积计算匹配度:
python
import torch
import torch.nn as nn
class UserTower(nn.Module):
def __init__(self, user_feat_dim):
super().__init__()
self.mlp = nn.Sequential(
nn.Linear(user_feat_dim, 256),
nn.ReLU(),
nn.Linear(256, 128),
nn.ReLU(),
nn.Linear(128, 64)
)
def forward(self, x):
return self.mlp(x)
class ItemTower(nn.Module):
def __init__(self, item_feat_dim):
super().__init__()
self.mlp = nn.Sequential(
nn.Linear(item_feat_dim, 256),
nn.ReLU(),
nn.Linear(256, 128),
nn.ReLU(),
nn.Linear(128, 64)
)
def forward(self, x):
return self.mlp(x)
class TwoTowerModel(nn.Module):
def __init__(self, user_dim, item_dim):
super().__init__()
self.user_tower = UserTower(user_dim)
self.item_tower = ItemTower(item_dim)
def forward(self, user_x, item_x):
u_emb = self.user_tower(user_x)
i_emb = self.item_tower(item_x)
logits = torch.sum(u_emb * i_emb, dim=1)
return torch.sigmoid(logits)
训练完成后,遍历全量商品生成 Item Embedding 并存入 HDFS。
Java 在线服务:User Embedding 实时生成
用户请求时,Java 服务实时拼接特征并调用 TorchScript 模型:
java
package juwatech.cn.recommend;
@Service
public class UserEmbeddingService {
private final Module torchModel;
private final FeatureEncoder featureEncoder;
public UserEmbeddingService() throws Exception {
this.torchModel = LiteLoader.load("models/user_tower.pt");
this.featureEncoder = new FeatureEncoder();
}
public float[] generateUserEmbedding(Long userId) {
// 获取用户实时特征
UserFeature feat = userFeatureService.getLatest(userId);
float[] denseFeatures = featureEncoder.encode(feat);
// 调用 TorchScript
Tensor input = Tensor.fromBlob(denseFeatures, new long[]{1, denseFeatures.length});
Tensor output = torchModel.forward(IValue.from(input)).toTensor();
return output.getDataAsFloatArray();
}
}
Faiss 向量召回与结果融合
预加载商品向量索引,执行近邻搜索:
java
@Component
public class FaissItemRetriever {
private IndexFlatIP index;
private List<String> itemIdList;
@PostConstruct
public void loadIndex() throws IOException {
// 从 HDFS 加载 item_id 列表与 embedding
List<float[]> embeddings = hdfsClient.readEmbeddings("hdfs://.../item_emb_20251211.bin");
itemIdList = hdfsClient.readItemIds("hdfs://.../item_ids.txt");
int dim = embeddings.get(0).length;
index = new IndexFlatIP(dim);
float[][] data = embeddings.toArray(new float[0][]);
index.add(data);
}
public List<RecommendedItem> recall(float[] userEmb, int topK) {
float[][] query = new float[][]{userEmb};
long[] indices = new long[topK];
float[] distances = new float[topK];
index.search(query, topK, distances, indices);
List<RecommendedItem> results = new ArrayList<>();
for (int i = 0; i < topK; i++) {
String itemId = itemIdList.get((int) indices[i]);
results.add(new RecommendedItem(itemId, distances[i]));
}
return results;
}
}
兜底策略与AB测试
为防冷启动或模型异常,融合热门榜与类目偏好:
java
public List<RecommendedItem> recommend(Long userId) {
try {
float[] userEmb = userEmbeddingService.generateUserEmbedding(userId);
List<RecommendedItem> aiRecs = faissRetriever.recall(userEmb, 50);
return ranker.rerank(aiRecs); // 加入佣金、时效等业务规则重排
} catch (Exception e) {
log.warn("AI recall failed, fallback to hot list", e);
return hotItemService.getTop50();
}
}
所有流量通过统一实验平台分流,支持多模型 AB 测试。
本文著作权归聚娃科技省赚客app开发者团队,转载请注明出处!