【Tensorflow 2.12 简单智能商城商品推荐系统搭建】

Tensorflow 2.12 简单智能商城商品推荐系统搭建

  • 前言
  • 架构
  • 数据
  • 召回
  • 排序
  • 部署
  • 调用
  • 结尾

前言

基于 Tensorflow 2.12 搭建一个简单的智能商城商品推荐系统demo~
主要包含6个部分,首先是简单介绍系统架构,接着是训练数据收集、处理,然后是召回模型、排序模型的搭建以及训练、导出等,最后是部署模型提供REST API接口以及如何调用进行推荐。

Tensorflow是谷歌开源的机器学习框架,可以帮助我们轻松地构建和部署机器学习模型。这里记录学习使用Tensorflow来搭建一个简单的智能商城商品推荐系统。
相关版本:

  • python 3.1.0
  • pandas 2.0.3
  • tensorflow 2.12.0
  • tensorflow-recommenders 0.7.3

架构

通常一个商城系统主要包含前端、后台,前端包含网站、移动H5、APP、小程序、公众号等,后台主要包含相关的API服务以及存储层等。
首先运营通过后台来录入商品信息、进行上架,然后前端用户通过商城APP、小程序来浏览商品、搜索商品、进行下单购买、关注、收藏等。
一般系统中都会保留用户的这些操作数据,例如浏览数据、搜索数据、下单数据等,结合运营录入的商品数据,通过定时Job生成用户的特征数据、商品的特征数据,然后使用机器学习来找到用户喜欢的但又没有购买过的商品,进行简单的AI推荐。
这里简单的画了一个图:
在这里插入图片描述

数据

数据收集部分这里就不展开讲了,相信现在大部分电商系统都有保留用户的一些搜索数据、下单数据等,以及后台录入的商品数据,通过一个定时JOB,定时把数据查询出来导出为CSV文件即可,作为后续进行模型训练的数据集。
当然没有也没关系,可以写一个简单的Java方法、或者python方法随机生成数据,写到CSV文件中。
这里先从最简单的开始,只使用用户的下单数据以及商品数据来进行训练(主要特征为用户Id、商品Id)。
在这里插入图片描述

召回

主要是训练一个双塔召回模型,根据用户的历史下单数据以及商品数据,召回几百到数千条用户喜欢的商品(基于内容的推荐)。
召回模型搭建分为以下几部分:
1、数据处理
2、模型搭建
3、模型训练
4、模型评估
5、模型导出

import pprint
import pandas as pdfrom typing import Dict, Textimport numpy as np
import tensorflow as tf
import tensorflow_recommenders as tfrs# -*- coding:utf-8 -*-# 使用pandas加载数据
order_df = pd.read_csv("C:\\data\\python\\data\\blog\\order.csv", encoding="gbk")
pprint.pprint(order_df.shape)
pprint.pprint(order_df.head(5))# 处理数据类型(转为字符串)
order_df['user_id'] = order_df['user_id'].astype(str)
order_df['product_id'] = order_df['product_id'].astype(str)# 将DataFrame(pandas)转换为Dataset(tensorflow)
order_df = tf.data.Dataset.from_tensor_slices(dict(order_df))
for x in order_df.take(1).as_numpy_iterator():pprint.pprint(x)# 准备嵌入向量的词汇表(用户的)
user_ds = order_df.map(lambda x: {"user_id": x["user_id"],"product_id": x["product_id"]
})
# 准备嵌入向量的词汇表(商品的)
product_ds = order_df.map(lambda x: x["product_id"])# 随机数种子
tf.random.set_seed(42)
# 打乱数据
shuffled = user_ds.shuffle(100_000, seed=42, reshuffle_each_iteration=False)
# 切分数据,分为训练数据以及验证数据
train = shuffled.take(80_000)
test = shuffled.skip(80_000).take(20_000)# 数据分片
user_ids = user_ds.batch(1_000_000).map(lambda x: x["user_id"])
product_ids = product_ds.batch(1_000)
# 获取唯一的用户Id列表以及商品列表
unique_user_ids = np.unique(np.concatenate(list(user_ids)))
pprint.pprint(unique_user_ids[:5])
unique_product_ids = np.unique(np.concatenate(list(product_ids)))
pprint.pprint(unique_product_ids[:5])# 将用户特征映射到用户embedding中,查询塔
user_model = tf.keras.Sequential([tf.keras.layers.StringLookup(vocabulary=unique_user_ids, mask_token=None),tf.keras.layers.Embedding(len(unique_user_ids) + 1, 32)
])
# 将商品特征映射到商品embedding中,候选条目塔
product_model = tf.keras.Sequential([tf.keras.layers.StringLookup(vocabulary=unique_product_ids, mask_token=None),tf.keras.layers.Embedding(len(unique_product_ids) + 1, 32)
])# 定义模型指标
metrics = tfrs.metrics.FactorizedTopK(candidates=product_ds.batch(128).map(product_model)
)
# 任务类,把损失函数和指标计算放在一起,本身是一个keras层
task = tfrs.tasks.Retrieval(metrics=metrics
)# 完整的双塔召回模型类,负责循环训练模型
class RecModel(tfrs.Model):# 初始化方法def __init__(self, user_model, product_model):super().__init__()self.user_model: tf.keras.Model = user_modelself.product_model: tf.keras.Model = product_modelself.task: tf.keras.layers.Layer = task# 计算损失def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:# 获取用户特征user_embeddings = self.user_model(features["user_id"])# 获取商品特征positive_product_embeddings = self.product_model(features["product_id"])# 计算损失return self.task(user_embeddings, positive_product_embeddings)# 创建模型实例
model = RecModel(user_model, product_model)
model.compile(optimizer=tf.keras.optimizers.Adagrad(learning_rate=0.05))# 缓存数据
cached_train = train.shuffle(100_000).batch(8192).cache()
cached_test = test.batch(4096).cache()# 开始训练
model.fit(cached_train, epochs=3)# 评估模型
model.evaluate(cached_test, return_dict=True)# 为训练好的模型建立索引,这里设置召回前100条商品条目
index = tfrs.layers.factorized_top_k.BruteForce(model.user_model, k=100)
index.index_from_dataset(tf.data.Dataset.zip((product_ds.batch(100), product_ds.batch(100).map(model.product_model)))
)# 进行预测
scores, products = index(tf.constant(["42"]))
pprint.pprint(f"用户Id 42 召回结果: {products[0, :5]}")# 保存、加载模型路径
path = "C:\\data\\python\\data\\blog\\index\\"
# 保存模型
tf.saved_model.save(index, path)
# 加载模型
loaded = tf.saved_model.load(path)
# 进行预测
scores, products = loaded(["42"])
pprint.pprint(f"用户Id 42 召回结果: {products[0, :5]}")

排序

排序部分,主要是根据用户对商品的评分数据训练出一个排序模型,当然有的系统不一定有评分模块,可以以用户的订单数据为基础,结合系统中的其他数据,经过一定的转换算法来生成评分数据,如用户下单记录可以先简单理解为用户喜欢这个商品,记录用户对这个商品的基础评分1分,假如用户多次购买这个商品,在基础分上再加1分,其它的数据如用户分享过的、点赞过的、搜索过的商品都可以加1分或者作为基础分,这样来把系统中的隐式反馈数据转换为显式评分数据,方便后续进行排序模型训练。这里先简单的以用户的下单数据来作为评分数据进行训练,即用户下单的商品都赋值为1分(下图中rating列)。
在这里插入图片描述

排序模型搭建同样分为以下几部分:
1、数据处理
2、模型搭建
3、模型训练
4、模型评估
5、模型导出

import pprintfrom typing import Dict, Textimport pandas as pd
import numpy as np
import tensorflow as tf
import tensorflow_recommenders as tfrs# 使用pandas加载数据
order_df = pd.read_csv("C:\\data\\python\\data\\blog\\order.csv", encoding="gbk")
pprint.pprint(order_df.shape)
pprint.pprint(order_df.head(5))# 处理数据类型
order_df['user_id'] = order_df['user_id'].astype(str)
order_df['product_id'] = order_df['product_id'].astype(str)
order_df['rating'] = order_df['rating'].astype(float)# 将DataFrame(pandas)转换为Dataset(tensorflow)
order_df = tf.data.Dataset.from_tensor_slices(dict(order_df))
for x in order_df.take(1).as_numpy_iterator():pprint.pprint(x)# 准备嵌入向量的词汇表
ratings = order_df.map(lambda x: {"user_id": x["user_id"],"product_id": x["product_id"],"rating": x["rating"]
})# 随机数种子
tf.random.set_seed(42)
# 打乱数据
shuffled = ratings.shuffle(100_000, seed=42, reshuffle_each_iteration=False)
# 切分数据,分为训练数据以及验证数据
train = shuffled.take(80_000)
test = shuffled.skip(80_000).take(20_000)# 数据分片
user_ids = ratings.batch(1_000_000).map(lambda x: x["user_id"])
product_ids = ratings.batch(1_000_000).map(lambda x: x["product_id"])
# 获取唯一的用户Id列表以及商品列表
unique_user_ids = np.unique(np.concatenate(list(user_ids)))
pprint.pprint(unique_user_ids[:5])
unique_product_ids = np.unique(np.concatenate(list(product_ids)))
pprint.pprint(unique_product_ids[:5])# 实现排序模型
class BaseModel(tf.keras.Model):def __init__(self):super().__init__()embedding_dimension = 32# 用户 embeddingsself.user_embeddings = tf.keras.Sequential([tf.keras.layers.StringLookup(vocabulary=unique_user_ids, mask_token=None),tf.keras.layers.Embedding(len(unique_user_ids) + 1, embedding_dimension)])# 商品 embeddingsself.product_embeddings = tf.keras.Sequential([tf.keras.layers.StringLookup(vocabulary=unique_product_ids, mask_token=None),tf.keras.layers.Embedding(len(unique_product_ids) + 1, embedding_dimension)])# 预测模型self.ratings = tf.keras.Sequential([tf.keras.layers.Dense(256, activation="relu"),tf.keras.layers.Dense(64, activation="relu"),tf.keras.layers.Dense(1)])def call(self, inputs):# 获取输入user_id, product_id = inputs# 获取用户特征、商品特征user_embedding = self.user_embeddings(user_id)product_embedding = self.product_embeddings(product_id)# 计算评分return self.ratings(tf.concat([user_embedding, product_embedding], axis=1))# 完整的模型
class RankModel(tfrs.models.Model):def __init__(self):super().__init__()self.ranking_model: tf.keras.Model = BaseModel()# 定义损失函数以及评估指标self.task: tf.keras.layers.Layer = tfrs.tasks.Ranking(loss = tf.keras.losses.MeanSquaredError(),metrics=[tf.keras.metrics.RootMeanSquaredError()])def call(self, features: Dict[str, tf.Tensor]) -> tf.Tensor:return self.ranking_model((features["user_id"], features["product_id"]))def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:# 获取实际评分labels = features.pop("rating")# 获取预测评分rating_predictions = self(features)# 计算损失return self.task(labels=labels, predictions=rating_predictions)# 实例化模型
model = RankModel()
model.compile(optimizer=tf.keras.optimizers.Adagrad(learning_rate=0.05))# 缓存训练和评估数据
cached_train = train.shuffle(100_000).batch(8192).cache()
cached_test = test.batch(4096).cache()# 训练模型
model.fit(cached_train, epochs=3)# 评估模型
model.evaluate(cached_test, return_dict=True)# 测试排序模型
test_ratings = {}
test_product_ids = ["1", "2", "3", "4", "5"]
for product_id in test_product_ids:test_ratings[product_id] = model({"user_id": np.array(["1"]),"product_id": np.array([product_id])})# 倒序打印排序结果
for product, score in sorted(test_ratings.items(), key=lambda x: x[1], reverse=True):print(f"排序结果 {product}: {score}")# 保存模型
tf.saved_model.save(model, "C:\\data\\python\\data\\blog\\rank\\")# 加载模型
loaded = tf.saved_model.load("C:\\data\\python\\data\\blog\\rank\\")# 进行排序
print(loaded({"user_id": np.array(["42"]), "product_id": ["42"]}).numpy())

部署

召回以及排序模型训练好之后,我们需要进行部署,提供接口给前端应用进行调用,TensorFlow也提供了部署的组件(TensorFlow Serving),方便开发者快速进行部署,以便提供推荐服务。
1、拉取镜像

docker pull tensorflow/serving

2、配置文件
这里我们有两个训练好的模型,一个是召回模型(index),一个是排序模型(rank),还需要再编写一个配置文件(models.config),配置两个模型的相关信息(包含模型的名称、路径、以及生成模型的平台),具体如下:

model_config_list:{config:{name:"index",base_path:"/models/index",model_platform:"tensorflow"},config:{name:"rank",base_path:"/models/rank",model_platform:"tensorflow"}
}

编写完成后,总的文件以及相关目录如下:
在这里插入图片描述
这些文件在dockerfile中进行添加或者启动容器后上传都可以,这里注意一个点,模型文件夹里面还需要再建一个版本号文件夹,例如1,用来表示版本号,具体可见下面截图:
在这里插入图片描述
3、启动容器
这里直接以上面拉取的tensorflow/serving镜像为基础,启动容器,同时挂载本地的models文件夹到容器的models文件夹下,以及指定多模型配置文件(models.config),即可运行,具体如下:

docker run -t -p 8501:8501 --mount type=bind,source=E:\data\python\simple\models,target=/models -e MODEL_NAME=index -t tensorflow/serving --model_config_file=/models/models.config &

如果有用docker desktop,可以在containers界面看到启动的容器,点击进去查看日志,可以看到两个模型已经成功加载,服务也成功启动,相关截图如下:
在这里插入图片描述
查看容器的文件目录,可以看到我们挂载进去的models文件夹以及下面的相关模型、配置文件,截图如下:
在这里插入图片描述

调用

推荐模型部署好之后,我们就可以用http post的方式来调用推荐服务提供的rest api接口了。召回服务的调用路径为:http://localhost:8501/v1/models/index:predict,其中v1对应之前建立的版本号文件夹1,index对应的是召回模型名称,predict是表示进行预测的意思,同理排序模型的调用路径为:http://localhost:8501/v1/models/rank:predict。
这里模拟为用户Id为1和2的两个用户进行商品推荐,示例如下:

# 1、先进行召回推荐,这里使用curl命令来进行调用
curl -d '{"instances":["1","2"]}' -X POST http://localhost:8501/v1/models/index:predict
# 召回推荐返回如下:
{"predictions": [ # 推荐列表{ # 用户Id为1召回结果"output_1": [3.94739556,2.27907419, ...... ], # 召回的依据:分数,100个"output_2": ["59001","3039", ...... ] # 召回商品Id,与分数一一对应,100个},{ # 用户Id为2召回结果"output_1": [2.272927,2.263876706, ...... ],"output_2": ["11632","7338", ...... ]}]
}
# 2、将召回推荐的结果再输入排序模型来进行排序,得到最终的推荐结果,这里对用户Id为1的召回结果进行排序,用户Id为2同理:
curl -d '{"inputs":{"customer_id":["1","1", ...... ],"product_id":["59001","3039", ......(这里是召回的100个商品Id,太长了省略) ]}}' -X POST http://localhost:8501/v1/models/rank:predict
# 排序返回如下,可以取排序后分数最高的前10个商品作为最终的结果推荐给用户:
{"outputs": [ # 排序列表[1.19891763], # 排序的依据分数,与请求参数中的商品Id一一对应,100条[1.15422344],......]
}

结尾

一个简单的推荐系统demo搭建到这里就结束了,当然生产的推荐肯定不会这么简单,需要我们收集更多的用户特征、商品特征,以及优化、调整超参数~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/116865.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Word Power S

题目描述 约翰想要计算他那N(l < N < 1000)只奶牛的名字的能量.每只奶牛的名字由不超过1000个字 符构成&#xff0c;没有一个名字是空字体串. 约翰有一张“能量字符串表”&#xff0c;上面有M(1 < M < 100)个代表能量的字符串.每个字符串 由不超过30个字体构成&a…

使用.NET设计一个Epub电子书生成工具

1. 背景 可能我们接触到更多的小说文件都是普普通通的TXT格式&#xff0c;用于分享的文档更多的是PDF。TXT虽然轻巧&#xff0c;但是不如PDF丰富和强大。而 Epub 电子书格式因为其丰富的展示效果和较小的文件大小&#xff0c;这样一个微妙的平衡就刚刚好。作为一个喜欢看小说的…

apache开启https

本文基于windows平台。 个人感觉使用apache配置起来比较繁琐&#xff0c;而使用upupw或者xmpp等集成开发工具更方便。 在httpd.conf中&#xff0c;将下一行的注释去掉&#xff1a;LoadModule ssl_module modules/mod_ssl.so。另外&#xff0c;千万不要注释掉下面的一行&#…

How to install mongodb 7.0 to Ubuntu 22.04

How to install mongodb 7.0 to Ubuntu 22.04 1、安装1.1、添加gpg1.2、添加apt源1.3、更新1.4、安装 2、管理2.1、服务管理2.1.1、查看服务状态2.1.2、启动服务2.1.3、 设置服务为开机启动2.1.4、取消服务开机启动2.1.5、关闭服务2.1.6、服务重启 2.2、mongosh2.2.1、进入mong…

“深入探讨Java JUC中的ReentrantLock锁:实现多线程同步与并发控制“

简介 1、从Java5开始&#xff0c;Java提供了一种功能更强大的线程同步机制——通过显式定义同步锁对象来实现同步&#xff0c;在这种机制下&#xff0c;同步锁由Lock对象充当。 2、Lock 提供了比synchronized方法和synchronized代码块更广泛的锁定操作&#xff0c;Lock允许实…

arcgis js api FeatureLayer加载时返回数据带*问题

接着这一问题衍生出来的问题 arcgis的MapServer服务查询出来的结果geometry坐标点带*的问题-CSDN博客 个人感觉像是server版本的问题&#xff0c;具体不清楚&#xff0c;pg数据库里面的shape点集合坐标点的精度是8&#xff0c;但是server服务查出来的默认都十几位。所以存在一…

c_指针

文章目录 *(p1)1表示第 1 行第 1 个元素的地址。如何理解呢&#xff1f;下标运算符的规则括号 int a; // 1.一个整数 int *a; // 2.一个指向整数的指针 int **a; // 3.一个指向指针的指针, 它所指向的指针又指向一个整数型数据 ;一个指向 …

用Python绘制简单曲线的几个方法

画曲线的基本方法 1.用圆的一部分来画曲线&#xff1a; circle(半径&#xff0c;度数) 比如说想画一个半径为50的半圆的曲线&#xff0c;就可以用circle(50,180)来表示 如果想截取一个半径为100的圆中110的部分&#xff0c;就可以用circle(100,110)来表示 若要更改方向&am…

使用CPR库和Python编写程序

以下是一个使用CPR库和Python编写的爬虫程序&#xff0c;用于爬取。此程序使用了proxy的代码。 import requests from cpr import CPR ​ def get_proxy():url "https://www.duoip.cn/get_proxy"headers {"User-Agent": "Mozilla/5.0 (Windows NT …

C语言可变参数函数及其实现

概述 本文讨论C语言中的可变参数函数&#xff0c;特别关注printf和scanf&#xff0c;它们允许根据需要确定参数的个数。这篇文章还介绍了可变参数函数的实现细节和相关宏 1. 可变参数函数的概念 C语言引入了可变参数函数的概念&#xff0c;允许函数的参数个数根据需要确定。…

如何用.bat文件直接安装jar包

大家应该都知道一个maven引入jar包&#xff0c;如果直接把jar包放到目录&#xff0c;这样是没用的&#xff0c;引入还是会失败 这里我们可以创建一个.bat的windows系统文件&#xff0c;写入pom.xml对应的groupid&#xff0c;artifactId&#xff0c;version pom.xml中进入jar包…

RDB.js:适用于 Node.js 和 Typescript 的终极对象关系映射器

RDB.js 是适用于 Node.js 和 Typescript 的终极对象关系映射器&#xff0c;可与 Postgres、MS SQL、MySQL、Sybase SAP 和 SQLite 等流行数据库无缝集成。无论您是使用 TypeScript 还是 JavaScript&#xff08;包括 CommonJS 和 ECMAScript&#xff09;构建应用程序&#xff0c…

Ceres 使用笔记

文章目录 Part.I IntroductionChap.I 预备知识Chap.II 概念理解 Part.II 简单使用Chap.I Ceres 中主要函数简介Chap.II 一个简单的实例 Reference Part.I Introduction Ceres 1 是由 Google 开发的开源 C 通用非线性优化库&#xff0c;与 g2o 并列为目前视觉 SLAM 中应用最广泛…

使用 conda 在 Ubuntu 16.04 上安装 Python 3.9 的步骤:和 VSCode配置

一、使用conda在 Ubuntu 16.04 上安装 Python 3.9 的步骤: 当然可以,conda 是一个非常强大的包管理器,它可以方便地管理不同版本的 Python 和各种库包。以下是使用 conda 在 Ubuntu 16.04 上安装 Python 3.9 的步骤: 1. 安装 Miniconda Miniconda 是 Anaconda 的轻量级版…

k8s-----3、kubernetes集群部署(kubeadm部署)

集群部署 1、kubeadm流程&#xff08;重新配置&#xff09;1.1 安装要求1.2 准备环境 1.3. 所有节点安装Docker/kubeadm/kubelet1.3.1 安装Docker1.3.2 添加阿里云YUM软件源1.3.3 安装kubeadm&#xff0c;kubelet和kubectl 1.4 部署Kubernetes Master1.5. 加入Kubernetes Node1…

springboot+vue开发的视频弹幕网站动漫网站

springbootvue开发的视频弹幕网站动漫网站 演示视频 https://www.bilibili.com/video/BV1MC4y137Qk/?share_sourcecopy_web&vd_source11344bb73ef9b33550b8202d07ae139b 功能&#xff1a; 前台&#xff1a; 首页&#xff08;猜你喜欢视频推荐&#xff09;、轮播图、分类…

28、Flink 的SQL之DROP 、ALTER 、INSERT 、ANALYZE 语句

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

cookie过大导致request 400 错误研究

问&#xff1a;get请求太长报400的错误&#xff0c;如何解决&#xff1f;生成系统中经常偶现此问题 问&#xff1a;get请求URL的长度是谁限制的&#xff1f; 问&#xff1a;每一个cookie的value的大小还是同域下cookie的个数做的限制&#xff1f; 现象&#xff1a;出现 400 Ba…

【Python · PyTorch】数据基础

数据基础 1. 数据操作1.1 入门1.2 运算符1.3 广播机制1.4 索引和切片1.5 节省内存1.6 转化为其他Python对象 2. 数据预处理2.1 读取数据集2.2 处理缺失值2.3 转换为张量格式 本文介绍了PyTorch数据基础&#xff0c;Python版本3.9.0&#xff0c;代码于Jupyter Lab中运行&#xf…

人工智能之深度学习

1. 引言 时至今日&#xff0c;人们常用的计算机程序几乎都是软件开发人员从零编写的。 比如&#xff0c;现在开发人员要编写一个程序来管理网上商城。 经过思考&#xff0c;开发人员可能提出如下一个解决方案&#xff1a; 首先&#xff0c;用户通过Web浏览器&#xff08;或移动…