day16_推荐系统和总结
文章目录
- day16_推荐系统和总结
- 一、推荐实现
- 1、基于流行度推荐(掌握)
- 1.1 近期热门商品推荐
- 1.2 个人热门商品推荐
- 2、基于隐语义模型的协同过滤推荐(了解)
- 2.1 ALS算法介绍
- 2.2 推荐代码
- 3、基于物品的协同过滤推荐(了解)
- 4、基于用户的协同过滤推荐(了解)
- 5、基于关联规则的推荐(熟悉)
- 5.1 关联规则详解
- 5.2 FP-growth算法理解
- 5.3 SparkMLlib中的FP-growth算法
- 5.4 完整代码
- 6、服务部署(了解)
day16_推荐系统和总结
一、推荐实现
推荐系统一般是由Java后端与前端人员进行开发的,大数据开发人员比较少参与主要是提供数据。
为了实现推荐功能,需要启动Hadoop、Hive、ES、Doris、SparkSubmit
启动Hadoop、启动Hivecd /./up01.sh start启动ES1- 切换用户su es2- 进入目录cd /home/es/elasticsearch-7.10.2/bin3- 启动elasticsearch -d4- 退出es用户exit启动Doris/export/server/doris/fe/bin/start_fe.sh --daemon/export/server/doris/be/bin/start_be.sh --daemon/export/server/doris/apache_hdfs_broker/bin/start_broker.sh --daemon启动SparkSubmitcd /export/server/spark/sbin./start-thriftserver.sh \--hiveconf hive.server2.thrift.port=10001 \--hiveconf hive.server2.thrift.bind.host=up01 \--hiveconf spark.sql.warehouse.dir=hdfs://up01:8020/user/hive/warehouse \--master local[*]
1、基于流行度推荐(掌握)
基于流行度推荐,也就是基于统计的推荐,主要用来解决用户的冷启动问题,对于新用户首次登录。基于流行度的推荐也可以用于单独的热门商品模块。
1.1 近期热门商品推荐
可以按商品销售的单量进行倒序排序,然后存入Doirs中。表中可以多存入一些数据,在使用时,根据品类进行倒序查询,取到相关商品即可。
- 计算的sql如下
-- 近期热门商品推荐
selectcurrent_date() as recommend_date, -- 推荐时间,区别是什么时候推荐的third_category_no,third_category_name,goods_no,goods_name,count(order_no) as order_count -- 订单量
from dwm.dwm_sold_goods_sold_dtl_i
where-- 过滤最近一段时间内的销售数据datediff(current_date(),to_date(trade_date))<=40 and goods_no is not null
group by third_category_no,third_category_name,goods_no,goods_name
order by order_count desc
limit 300 -- 推荐比项目经理要求的推荐数目多一些
- Doris的建表语句如下
create database if not exists recommend_db;
CREATE TABLE IF NOT EXISTS recommend_db.popular_hot_goods (recommend_date DATE comment '计算日期',goods_no bigint comment '商品编码',third_category_no STRING comment '三级品类编码',third_category_name STRING comment '三级品类名称',goods_name STRING comment '商品名称',order_count INT comment '销售数量'
)
UNIQUE KEY(recommend_date, goods_no)
comment '热门商品推荐'
PARTITION BY RANGE(recommend_date) ()
DISTRIBUTED BY HASH(goods_no) BUCKETS 1
sql ("dynamic_partition.create_history_partition" = "true","dynamic_partition.enable" = "true","dynamic_partition.time_unit" = "DAY","dynamic_partition.start" = "-365","dynamic_partition.end" = "3","dynamic_partition.prefix" = "p","dynamic_partition.buckets" = "10","replication_allocation" = "tag.location.default: 1"
);注意: 1- 如果多个字段作为UNIQUE KEY,那么String类型不能够使用。因此这里将goods_no的类型进行了强制转换。为了将不同日期的数据分开存放,这里使用动态分区表。2- 建表中的字段顺序需要与UNIQUE KEY中字段顺序保持一致。并且UNIQUE KEY中的字段要放在最上面。如果不遵守会报如下的错:
- 推荐代码
from pyspark.sql import SparkSession
import os
import pyspark.sql.functions as Fos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'if __name__ == '__main__':# 1- 创建SparkSession对象spark = SparkSession.builder\.appName("topn_goods_recommend")\.master("local[*]") \.config("spark.sql.warehouse.dir", "hdfs://up01:8020/user/hive/warehouse") \.config("hive.metastore.uris", "thrift://up01:9083") \.config("spark.sql.shuffle.partitions",2)\.enableHiveSupport() \.getOrCreate()# 2- 读取分析Hive中的数据,获取TOPN的热门商品topn_goods_df = spark.sql("""selectcurrent_date() as recommend_date, -- 推荐时间,区别是什么时候推荐的third_category_no,third_category_name,goods_no,goods_name,count(order_no) as order_count -- 订单量from dwm.dwm_sold_goods_sold_dtl_iwhere-- 过滤最近一段时间内的销售数据datediff(current_date(),to_date(trade_date))<=40 and goods_no is not nullgroup by third_category_no,third_category_name,goods_no,goods_nameorder by order_count desclimit 300 -- 推荐比项目经理要求的推荐数目多一些""")# 3- 数据存储到Doris中topn_goods_df.write.jdbc(url="jdbc:mysql://192.168.88.166:9030/recommend_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",table="popular_hot_goods",mode="append",sql={ 'user' : 'root', 'password' : '123456' })# 4- 释放资源spark.stop()
1.2 个人热门商品推荐
也就是根据销售单量统计每个人喜欢购买的前N个商品
- 计算的sql如下
selectcurrent_date() as recommend_date, -- 推荐时间,区别是什么时候推荐的zt_id as user_id,goods_no,goods_name,third_category_no,third_category_name,order_count
from (select*,row_number() over(partition by zt_id order by order_count desc) as rnfrom (selectzt_id,third_category_no,third_category_name,goods_no,goods_name,count(order_no) as order_count -- 订单量from dwm.dwm_sold_goods_sold_dtl_iwhere-- 过滤最近一段时间内的销售数据datediff(current_date(),to_date(trade_date))<=40 and goods_no is not nulland zt_id is not null and zt_id!=0group by zt_id,third_category_no,third_category_name,goods_no,goods_name) tmp_1
) tmp_2 where rn<=20
- Doris建表语句
CREATE TABLE IF NOT EXISTS recommend_db.popular_person_hot_goods (recommend_date DATE comment '计算日期',user_id INT comment '会员ID',goods_no STRING comment '商品编码',goods_name STRING comment '商品名称',third_category_no STRING comment '三级品类编码',third_category_name STRING comment '三级品类名称',order_count INT comment '订单数'
)
UNIQUE KEY(recommend_date, user_id)
comment '个人热门商品推荐'
PARTITION BY RANGE(recommend_date) ()
DISTRIBUTED BY HASH(user_id) BUCKETS 1
sql ("dynamic_partition.create_history_partition" = "true","dynamic_partition.enable" = "true","dynamic_partition.time_unit" = "DAY","dynamic_partition.start" = "-365","dynamic_partition.end" = "3","dynamic_partition.prefix" = "p","dynamic_partition.buckets" = "10","replication_allocation" = "tag.location.default: 1"
);
- 推荐代码
from pyspark.sql import SparkSession
import os
import pyspark.sql.functions as Fos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'if __name__ == '__main__':# 1- 创建SparkSession对象spark = SparkSession.builder\.appName("topn_user_goods_recommend")\.master("local[*]") \.config("spark.sql.warehouse.dir", "hdfs://up01:8020/user/hive/warehouse") \.config("hive.metastore.uris", "thrift://up01:9083") \.config("spark.sql.shuffle.partitions",8)\.enableHiveSupport() \.getOrCreate()# 2- 读取分析Hive中的数据,获取TOPN的热门商品topn_goods_df = spark.sql("""selectcurrent_date() as recommend_date, -- 推荐时间,区别是什么时候推荐的zt_id as user_id,goods_no,goods_name,third_category_no,third_category_name,order_countfrom (select*,row_number() over(partition by zt_id order by order_count desc) as rnfrom (selectzt_id,third_category_no,third_category_name,goods_no,goods_name,count(order_no) as order_count -- 订单量from dwm.dwm_sold_goods_sold_dtl_iwhere-- 过滤最近一段时间内的销售数据datediff(current_date(),to_date(trade_date))<=40 and goods_no is not nulland zt_id is not null and zt_id!=0group by zt_id,third_category_no,third_category_name,goods_no,goods_name) tmp_1) tmp_2 where rn<=20""")# 3- 数据存储到Doris中topn_goods_df.write.jdbc(url="jdbc:mysql://192.168.88.166:9030/recommend_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",table="popular_person_hot_goods",mode="append",sql={ 'user' : 'root', 'password' : '123456' })# 4- 释放资源spark.stop()
2、基于隐语义模型的协同过滤推荐(了解)
基于隐语义模型的协同过滤方法结合了协同过滤的思想和隐语义模型的技术,通过矩阵分解等方法,将用户-项目交互矩阵分解为两个低维矩阵,分别表示用户在隐空间中的向量和项目在隐空间中的向量。
2.1 ALS算法介绍
ALS算法是2008年以来,用的比较多的协同过滤算法。它已经集成到Spark的Mllib库中,使用起来比较方便。从协同过滤的分类来说,ALS算法属于User-Item CF,也叫做混合CF。它同时考虑了User和Item两个方面。
spark.ml目前支持基于模型的协同过滤,使用交替最小二乘法(ALS)算法实现。
spark.ml的实现具有以下参数:
- numBlocks:用户和物品将被分成的块数,以便并行计算(默认为10)
- rank:模型中的潜在因子数量(默认为10)
- maxIter:运行的最大迭代次数(默认为10)
- regParam:在ALS中指定的正则化参数(默认为1.0)
- implicitPrefs:指定是否使用显式反馈ALS变体或适用于隐式反馈数据的变体(默认为false,表示使用显式反馈)
- alpha:适用于ALS隐式反馈变体的参数,决定了对偏好观察的基本置信度(默认为1.0)
- nonnegative:指定是否对最小二乘法使用非负约束(默认为false)
注意:基于DataFrame的ALS API目前仅支持整数类型的用户和物品ID。
2.2 推荐代码
如果使用基于ALS的协同过滤模型进行推荐,关键是要构造用户对商品的评分数据。评分主要来源于用户的行为,包括浏览、加购、下单、购买、退单、评论、收藏等,一般在企业中,都会将这些因素考虑进去。
具体的评分方法是:浏览 1分,加购 2分,下单 3分, 支付 5分,退单 -5分。
- Doris建表语句
CREATE DATABASE IF NOT EXISTS recommend_db;
CREATE TABLE IF NOT EXISTS recommend_db.als_goods_for_user (user_id INT comment '用户id',goods_nos STRING comment '推荐的商品列表'
)
UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
sql("replication_num" = "1");
- 推荐代码
import os
from datetime import datetime
import numpy as np
from pyspark.ml.evaluation import RegressionEvaluator
from pyspark.ml.recommendation import ALS, ALSModel
from pyspark.ml.tuning import ParamGridBuilder, CrossValidator
import pyspark.sql.functions as F
from pyspark.sql import SparkSession
from pyspark.sql.types import IntegerType, StringType, DoubleTypeos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python'def get_best_parameter(df):# 切分数据集training, test = df.randomSplit([0.8, 0.2], seed=88)# 使用ALS构建推荐模型# 将冷启动策略设置为“drop”,以确保不会获得NaN评估指标als = ALS(userCol='user_id', itemCol='goods_no', ratingCol='score', coldStartStrategy='drop')# 创建参数网格param_grid = ParamGridBuilder() \.addGrid(als.rank, [5, 10, 15]) \.addGrid(als.maxIter, [5, 10, 20]) \.addGrid(als.regParam, [0.01, 0.05, 0.1]) \.build()# 创建评估器evaluator = RegressionEvaluator(metricName='rmse', labelCol='score', predictionCol='prediction')# 创建交叉验证器crossval = CrossValidator(estimator=als,estimatorParamMaps=param_grid,evaluator=evaluator,numFolds=3) # 3折交叉验证# 训练模型cv_model = crossval.fit(training)# 选择最佳模型best_model = cv_model.bestModel# 评估最佳模型predictions = best_model.transform(test)rmse = evaluator.evaluate(predictions)print('最优模型的均方根误差为:' + str(rmse))# 获取最佳参数rank = best_model._java_obj.parent().getRank()maxIter = best_model._java_obj.parent().getMaxIter()regParam = best_model._java_obj.parent().getRegParam()print('最佳参数组合:')print('rank: ', rank)print('maxIter: ', maxIter)print('regParam: ', regParam)return rank, maxIter, regParamif __name__ == '__main__':# 1)创建整合Hive的SparkSession# 1- 创建SparkSession对象spark = SparkSession.builder \.appName("recommend") \.master("local[*]") \.config("spark.sql.warehouse.dir", "hdfs://up01:8020/user/hive/warehouse") \.config("hive.metastore.uris", "thrift://up01:9083") \.config("spark.sql.shuffle.partitions", 5) \.enableHiveSupport() \.getOrCreate()# 2)从业务库中计算历史评分select_sql = """select zt_id as user_id, goods_no, sum(if(trade_type in(2, 5), -1, 1)) * 5 as scorefrom dwm.dwm_sold_goods_sold_dtl_iwhere dt >= date_sub(current_date, 90) and dt <= date_sub(current_date, 1)and zt_id != 0 and zt_id is not nullgroup by zt_id, goods_no"""hive_df = spark.sql(select_sql)hive_df.show()# 3)读取并解析日志数据file_path = 'hdfs://up01:8020/xtzg/etl/dwd_user_event_etl_result/dt=2025-02-14'# 读取ORC格式的数据log_df = spark.read.format('orc').load(file_path).select('user_id', F.split('goods_name', '=')[0].alias('goods_no'), (F.col('is_browse')*1 + F.col('is_cart')*2 + F.col('is_order')*3 + F.col('is_buy')*5 - F.col('is_back_order')*5).alias('score') )log_df.show()# 4) 数据合并并聚合# 因为als模型中,需要userCol和itemCol都是整型,所以需要将类型转成int,又因为goods_no有0开头的,所以需要再前边拼接一个数字# 因为频次过多会导致评分过大,所以可以使用log将数据变平滑union_df = hive_df.unionAll(log_df).groupby('user_id', 'goods_no').agg(F.sum('score').alias('score')).\select(F.col('user_id').astype(IntegerType()).alias('user_id'), F.concat(F.lit('1'), F.col('goods_no')).astype(IntegerType()).alias('goods_no'), 'score')# union_df.printSchema()union_df.show()# 5) 训练模型并得到推荐结果# 获取最佳超参数# rank, maxIter, regParam = get_best_parameter(union_df)rank, maxIter, regParam = 15, 20, 0.1als = ALS(rank=rank, maxIter=maxIter, regParam=regParam, userCol='user_id', itemCol='goods_no', ratingCol='score',coldStartStrategy='drop')als_model: ALSModel = als.fit(union_df)# 为每个用户生成十大商品推荐userRecs = als_model.recommendForAllUsers(10)userRecs.printSchema()# userRecs.show(truncate=False)# 处理 goods_no,将int转为str,并去掉前缀1doris_df = userRecs.withColumn('goods_nos',F.expr("""TRANSFORM(recommendations, x -> named_struct('goods_no', substr(CAST(x.goods_no AS STRING), 2),'rating', x.rating))""")).select('user_id', F.col('goods_nos').astype(StringType()).alias('goods_nos'))doris_df.printSchema()doris_df.show(truncate=False)# 保存到 Dorisdoris_df.write.jdbc(url="jdbc:mysql://192.168.88.166:9030/recommend_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",table="als_goods_for_user",mode="append",sql={'user': 'root', 'password': '123456'})# 释放资源spark.stop()
3、基于物品的协同过滤推荐(了解)
基于物品的协同过滤就是计算出每个标的物最相似的标的物列表,然后就可以为用户推荐用户喜欢的标的物相似的标的物。 这里可以借助ALS算法生成的矩阵来完成。
离线计算的ALS 算法,算法最终会为用户、商品分别生成最终的特征矩阵,分别是表示用户特征矩阵的U(K x U)矩阵,每个用户由 K 个特征描述;表示物品特征矩阵的V(I x K)矩阵,每个物品也由 K 个特征描述。
- Doris建表语句
CREATE TABLE IF NOT EXISTS recommend_db.als_sim_goods_list (id INT comment 'id',goods_no STRING comment '商品编码',sim_goods_list STRING comment '相似的商品列表'
)
UNIQUE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
sql("replication_num" = "1");
- 推荐代码:写到ALS的后面
# 6)为每个商品生成十大用户推荐
# goodsRecs = als_model.recommendForAllItems(10)# 通过 itemFactors 获得商品的特征表达
# als_model.itemFactors.show(truncate=False)
# 获取商品id及对应的特征表达
item_factors_df = als_model.itemFactors.select(F.expr("substr(cast(id as string), 2) as goods_no"), 'features')
item_factors_df.show(truncate=False)# 定义计算余弦相似度的 UDF
def consin_sim(vec1, vec2):vec1 = np.array(vec1)vec2 = np.array(vec2)num = np.dot(vec1, vec2)# np.linalg.norm()用于求范数,默认是二范数denom = np.linalg.norm(vec1) * np.linalg.norm(vec2)if denom == 0:return 0.0return round(float(num / denom), 4)consin_sim_udf = F.udf(consin_sim, DoubleType())# item_factors_df自关联,计算相似度,再将相似度小于0.75的过滤掉
cartesian_goods_df = item_factors_df.alias('df1') \.crossJoin(item_factors_df.alias('df2')) \.filter(F.col('df1.goods_no') != F.col('df2.goods_no')) \.withColumn('simScore', consin_sim_udf(F.col('df1.features'), F.col('df2.features'))) \.filter('simScore >= 0.75')# 按照 goods_no 进行分组并构建推荐结果
goods_recs_df = cartesian_goods_df.groupBy('df1.goods_no') \.agg(F.collect_list(F.struct(F.col('df2.goods_no').alias('rec_goods_no'), F.col('simScore').alias('score'))).alias('rec_goods_nos'))# 对相似的goods_no列表进行排序,并选取前10个【因为个别商品相似的商品太多,所以只保留10个即可】
# 使用 expr 和 array_sort 函数进行排序,并使用 slice 函数只保留前10个元素
sorted_df = goods_recs_df.withColumn('sim_goods_list',F.expr("slice(array_sort(rec_goods_nos, (x, y) -> case when x.score > y.score then -1 when x.score < y.score then 1 else 0 end), 1, 10)")
).withColumn('id',F.expr("cast(concat('1', goods_no) as int)")
).select('id', 'goods_no', F.col('sim_goods_list').astype(StringType()).alias('sim_goods_list'))# 显示结果
sorted_df.printSchema()
sorted_df.show(truncate=False)# 保存到 Doris
write_to_doris(sorted_df, 'recommend_db.als_sim_goods_list')
4、基于用户的协同过滤推荐(了解)
UserCF算法主要是考虑用户与用户之间的相似度,给用户推荐和他兴趣相似的其他用户喜欢的物品。俗话说"物以群分,人以类聚",人们总是倾向于跟自己志同道合的人交朋友。同理,你朋友喜欢的东西你大概率也可能会喜欢,UserCF算法正是利用了这个原理。
- Doris建表语句
CREATE TABLE IF NOT EXISTS recommend_db.user_cf_goods_for_user (user_id INT comment '用户id',goods_nos STRING comment '推荐的商品列表'
)
UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
sql("replication_num" = "1");
- 推荐代码
import osfrom pyspark.ml.feature import CountVectorizer, Normalizer
from pyspark.sql import DataFrame, Window, SparkSession
from pyspark.sql import functions as F
from pyspark.sql.types import DoubleType, ArrayType, StructType, StructField, StringType, FloatTypeos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python'if __name__ == '__main__':# 1- 创建SparkSession对象spark = SparkSession.builder \.appName("recommend") \.master("local[*]") \.config("spark.sql.warehouse.dir", "hdfs://up01:8020/user/hive/warehouse") \.config("hive.metastore.uris", "thrift://up01:9083") \.config("spark.sql.shuffle.partitions", 5) \.enableHiveSupport() \.getOrCreate()# 2)从es读取标签数据并将标签数据合并es_df = spark.read.format("es") \.option("es.nodes", "192.168.88.166:9200") \.option("es.resource", "user_profile_tags") \.option("es.read.field.include", "user_id,tags_id_times,tags_id_once,tags_id_streaming") \.load()temp_df = es_df.select('user_id', F.concat(F.coalesce('tags_id_times', F.lit('')), F.lit(','),F.coalesce('tags_id_once', F.lit('')), F.lit(','),F.coalesce('tags_id_streaming', F.lit(''))).alias('tags'))temp_df.show()# 3)将标签数据转换成向量# 使用split函数将字符串切分成数组,然后使用filter将''的元素过滤掉tags_df = temp_df.select('user_id', F.split('tags', ',').alias('tags')).select('user_id', F.expr("filter(tags, x -> x != '')").alias('tags'))# tags_df.show(truncate=False)# 将标签数组转换为向量cv = CountVectorizer(inputCol='tags', outputCol='features')model = cv.fit(tags_df)user_df = model.transform(tags_df)# 因为数据量比较大,容易运行不出来,所以可以抽样,取少量数据# user_df = user_df.sample(fraction=0.01, seed=66)user_df.show(truncate=False)# 4)计算用户相似度# 标准化向量normalizer = Normalizer(inputCol="features", outputCol="norm_features")norm_df = normalizer.transform(user_df).select('user_id', 'norm_features')norm_df.printSchema()# norm_df.show(truncate=False)# 将稀疏向量列转换为稠密向量列def to_dense(vector):return vector.toArray().tolist()to_dense_udf = F.udf(to_dense, ArrayType(DoubleType()))dense_df = norm_df.withColumn('dense_features', to_dense_udf('norm_features'))# dense_df.show(truncate=False)# 计算用户之间的余弦相似度join_df = dense_df.alias('u1').join(dense_df.alias('u2'), F.col('u1.user_id') != F.col('u2.user_id')) \.select(F.col('u1.user_id').alias('user1'), F.col('u2.user_id').alias('user2'),F.col('u1.dense_features').astype(ArrayType(FloatType())).alias('f1'),F.col('u2.dense_features').astype(ArrayType(FloatType())).alias('f2'))user_sim = join_df.select('user1', 'user2', F.zip_with('f1', 'f2', lambda x, y: x * y).alias('f3')) \.withColumn('cosine_sim', F.round(F.aggregate('f3', F.lit(0.0), lambda acc, x: acc + x), 4)) \.select('user1', 'user2', 'cosine_sim')# print('-----------------------',user_sim.count(),'------------------------------')user_sim.printSchema()# user_sim.show(truncate=False)# 5)获取每个用户最相似的10个用户# 定义窗口函数windowSpec = Window.partitionBy('user1').orderBy(F.col('cosine_sim').desc())# 取rn前10的列rn_df = user_sim.withColumn('rn', F.row_number().over(windowSpec)).filter('rn <= 10')# rn_df.show(truncate=False)# 6)查询每个用户评分最高的商品# 计算评分时,因为不同用户购买频次不同会导致评分差距过大,在进行商品推荐时,该评分对结果影响很大,所以可以对score使用log函数,将这种变化变平缓些select_sql = """select user_id, goods_no, round(log(score), 3) as scorefrom (selectuser_id, goods_no, score, rank() over(partition by user_id order by score desc) as rnfrom (select zt_id as user_id, goods_no, sum(if(trade_type in(2, 5), -1, 1)) * 5 as scorefrom dwm.dwm_sold_goods_sold_dtl_iwhere dt >= date_sub(current_date, 90) and dt <= date_sub(current_date, 1)and zt_id != 0 and zt_id is not nullgroup by zt_id, goods_no) tmpwhere score > 0 -- score为0或负的不推荐) twhere rn <= 10"""hive_df = spark.sql(select_sql)# hive_df.show()# 按照 goods_no 进行分组并构建推荐结果prefer_goods_df = hive_df.groupBy('user_id') \.agg(F.collect_list(F.struct('goods_no', 'score')).alias('prefer_goods_nos'))# prefer_goods_df.show(truncate=False)# 7)用户关联商品,给用户进行推荐# 关联商品join_df = rn_df.join(prefer_goods_df, rn_df['user1'] == prefer_goods_df['user_id'], 'inner').select('user1', 'user2', 'cosine_sim', F.col('prefer_goods_nos').alias('user1_goods_no'))join_df = join_df.join(prefer_goods_df, join_df['user2'] == prefer_goods_df['user_id'], 'inner').select('user1', 'user2', 'cosine_sim', 'user1_goods_no', F.col('prefer_goods_nos').alias('user2_goods_no'))# join_df.show(truncate=False)join_df.printSchema()# 定义一个udf,将cosine_sim,user1_goods_no和user2_goods_no都传进去,去掉user2_goods_no中的user1_goods_no,并计算user2_goods_no的分数def calculate_score(cosine_sim, user1_goods_no, user2_goods_no):user1_goods = [item.goods_no for item in user1_goods_no]user2_goods = []for item in user2_goods_no:if item['goods_no'] not in user1_goods:user2_goods.append({'goods_no': item['goods_no'], 'score': round(item['score'] * cosine_sim, 3)})return user2_goods# 返回值类型schema = ArrayType(StructType([StructField('goods_no', StringType(), nullable=False),StructField('score', DoubleType(), nullable=False)]))calculate_score_udf = F.udf(calculate_score, schema)# 获取用户及推荐的商品rec_df = join_df.select(F.col('user1').alias('user_id'), calculate_score_udf('cosine_sim', 'user1_goods_no', 'user2_goods_no').alias('rec_goods')).\filter(F.size(F.col('rec_goods')) > 0)# rec_df.show(truncate=False)# 展开 rec_goods 中的元素,然后按照 user_id 进行分组并聚合成列表goods_recs_df = rec_df.withColumn('goods_no_element', F.explode(F.col('rec_goods'))).groupBy('user_id').agg(F.collect_list('goods_no_element').alias('rec_goods_nos'))# 对推荐的goods_no列表进行排序,并选取前10个【因为个别用户推荐的商品太多,所以只保留10个即可】# 使用 expr 和 array_sort 函数进行排序,并使用 slice 函数只保留前10个元素sorted_df = goods_recs_df.withColumn('rec_goods_list',F.expr('slice(array_sort(rec_goods_nos, (x, y) -> case when x.score > y.score then -1 when x.score < y.score then 1 else 0 end), 1, 10)')).select('user_id', F.col('rec_goods_list').astype(StringType()).alias('goods_nos'))# sorted_df.show(truncate=False)# 8)结果保存# 保存到 Dorissorted_df.write.jdbc(url="jdbc:mysql://192.168.88.166:9030/recommend_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",table="user_cf_goods_for_user",mode="append",sql={'user': 'root', 'password': '123456'})spark.stop()
5、基于关联规则的推荐(熟悉)
5.1 关联规则详解
- 什么是关联规则(Association Rules)?
答:关联规则是数据挖掘中的概念,通过分析数据,找到数据之间的关联。电商中经常用来分析购买物品之间的相关性,例如,“购买尿布的用户,有大概率购买啤酒”,这就是一个关联规则。
- 什么是关联规则推荐(Association Rule Based Recommendaion)?
答:顾名思义,利用关联规则,来实施推荐。关联规则推荐的目标,是希望达到“将尿布放入购物车之后,再推荐啤酒”比“直接推荐啤酒”获取有更好的售卖效果。
- 关联规则推荐的典型应用?
线下,可以将尿布和啤酒放在一起;
线上,可以在用户将尿布放入购物车后,立刻推荐啤酒。
- 如何实施?
假设某电商会售卖ABCD四种商品,历史上共5笔订单,分别卖出{A,B,C}, {B,C,D}, {A,B,C,D}, {A,C}, {C}
5.2 FP-growth算法理解
常用的算法有Aprior算法和FP-growth算法,FP-growth算法比Apriori算法效率更高,并且在PySpark中对FP-growth算法进行了实现,所以这里重点讲一下FP-growth算法原理。
FP-growth(Frequent Pattern Tree, 频繁模式树),是韩家炜老师提出的挖掘频繁项集的方法,是将数据集存储在一个特定的称作FP树的结构之后发现频繁项集或频繁项对,即常在一块出现的元素项的集合FP树。
5.3 SparkMLlib中的FP-growth算法
spark.ml中提供了FPGrowth()方法来实现FP-growth算法。spark.ml的FP-growth实现接受以下(超)参数:
- minSupport:将一个项目集识别为频繁项目集的最低支持度。例如,如果一个项目在5个事务中出现了3次,它的支持度就是3/5=0.6。
- minConfidence:生成关联规则的最低置信度。置信度是表明一个关联规则被发现为真的频率。例如,如果在事务中项目集X出现了4次,而X和Y共同出现了2次,则规则X => Y的置信度为2/4=0.5。该参数不会影响频繁项目集的挖掘,但会指定从频繁项目集中生成关联规则的最低置信度。
- numPartitions:用于分配工作的分区数。默认情况下,该参数未设置,使用输入数据集的分区数。
模型训练完成后,会生成 FPGrowthModel 对象。FPGrowthModel 提供以下方法或属性:
-
freqItemsets:频繁项目集,以包含以下列的数据框格式提供:
- items:array:一个给定的项目集。
- freq:long:根据配置的模型参数,该项目集出现的次数。
-
associationRules:生成的置信度高于 minConfidence 的关联规则,以包含以下列的数据框格式提供:
- antecedent:array:作为关联规则假设的项目集。如果关联规则为A->B,则 antecedent 为 A。
- consequent:array:总是包含一个元素的项目集,代表关联规则的结论。如果关联规则为A->B,则 antecedent 为 B。
- confidence:double:置信度,定义参见上文中的 minConfidence。
- lift:double:提升度,计算方法为 support(antecedent ∪ consequent) / (support(antecedent) x support(consequent))。
- support:double:频繁项目集的支持度,定义参见上文中的 minSupport。
-
transform:根据传入的items,比对关联规则,将符合规则的结果添加到预测结果中。transform 方法将汇总所有适用规则的结果作为预测。预测列的数据类型与 items 列相同,并且不包含 items 列中的现有项目。
5.4 完整代码
- Doris建表语句
CREATE TABLE IF NOT EXISTS recommend_db.fpgrowth_association_goods
( `calculate_date` DATETIME COMMENT "计算时间",`antecedent` ARRAY<STRING> COMMENT "购买的商品",`consequent` ARRAY<STRING> COMMENT "关联(推荐)商品",`confidence` DOUBLE COMMENT "置信度",`lift` DOUBLE COMMENT "提升度",`support` DOUBLE COMMENT "支持度"
)
DUPLICATE KEY(calculate_date)
comment '关联规则推荐'
PARTITION BY RANGE(calculate_date) ()
DISTRIBUTED BY HASH(calculate_date) BUCKETS 1
sql ("dynamic_partition.create_history_partition" = "true","dynamic_partition.enable" = "true","dynamic_partition.time_unit" = "DAY","dynamic_partition.start" = "-365","dynamic_partition.end" = "3","dynamic_partition.prefix" = "p","dynamic_partition.buckets" = "10","replication_allocation" = "tag.location.default: 1"
);因为在doris中只是为了备份,所以存储成冗余模型即可。为了区分不同时间计算的结果,在表中添加了calculate_date字段,作为区分。然后为了分区存储,使用了动态分区的方式。
- 推荐代码
from pyspark.ml.fpm import FPGrowth, FPGrowthModel
from pyspark.sql import SparkSession
import os
import pyspark.sql.functions as F
from pyspark.sql.types import StringType, StructType, StructField, ArrayType
from tags.utils.hdfs_utils import HDFSUtilos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'# 实现商品推荐的功能
def get_recommend_goods(current_goods_no, spark, fpg_model):schema = StructType([StructField("items",ArrayType(StringType()))])current_goods_no_df = spark.createDataFrame(data=[(current_goods_no,)],schema=schema)# 使用模型进行商品推荐result_df = fpg_model.transform(current_goods_no_df)result_df.show()result_df.printSchema()print(result_df.collect())# 返回最终的推荐商品IDreturn result_df.collect()[0][1]# 基于关联规则的推荐
if __name__ == '__main__':# 1- 创建SparkSession对象spark = SparkSession.builder\.appName("fp_growth")\.master("local[*]") \.config("spark.sql.warehouse.dir", "hdfs://up01:8020/user/hive/warehouse") \.config("hive.metastore.uris", "thrift://up01:9083") \.config("spark.sql.shuffle.partitions",5) \.enableHiveSupport() \.getOrCreate()# 2- 数据输入:分析商品间的关联关系order_df = spark.sql("""selectorder_no,collect_set(goods_no) as items -- 将当前订单下的多个商品合到一个Set集合中from dwm.dwm_sold_goods_sold_dtl_iwhere datediff(current_date(),to_date(trade_date))<=40and goods_no is not nulland parent_order_no is not nulland order_no is not nullgroup by order_no""")# 3- 通过FP-growth分析商品间的关联关系的频率path = "/xtzg/recommend/fpg"if HDFSUtil().exists(path):# 如果之前已经训练好了模型,那么直接加载出来使用即可fpg_model = FPGrowthModel.load("hdfs://192.168.88.166:8020"+path)else:# 3.1- 创建算法模型实例对象fpGrowth = FPGrowth(itemsCol="items", minSupport=0.001, minConfidence=0.6)# 3.2- 对算法模型使用数据进行训练fpg_model = fpGrowth.fit(order_df)# 3.3- 再将训练好的模型存储到HDFSfpg_model.save("hdfs://192.168.88.166:8020"+path)rule_result = fpg_model.associationRulesrule_result.show(n=100)rule_result.printSchema()# 4- 模型训练后的商品关联信息存放到Doris中# doris_df = rule_result.withColumn("calculate_date",F.current_timestamp())doris_df = rule_result.select(F.current_timestamp().alias("calculate_date"),rule_result.antecedent.cast(StringType()).alias("antecedent"),rule_result.consequent.cast(StringType()).alias("consequent"),"confidence","lift","support")doris_df.write.jdbc(url="jdbc:mysql://192.168.88.166:9030/recommend_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",table="fpgrowth_association_goods",mode="append",sql={ 'user' : 'root', 'password' : '123456' })# 5- 使用训练好的模型来进行商品的推荐:这里是模拟后面顾客来购买东西的时候,进行推荐的效果current_goods_no = ["3224064"]recommend_goods = get_recommend_goods(current_goods_no, spark, fpg_model)print(recommend_goods)# 6- 释放资源spark.stop()
可能遇到的错误:
原因: 将数据输入到Doris的array字段中的时候,需要在输入前将对应的数据格式进行类型转换为字符串
6、服务部署(了解)
配置好get_recommend_goods方法后,每次调用都需要spark session和fpg_model,如果每次调用都新建非常浪费资源,时效也会非常差。所以这里需要将get_recommend_goods方法布置成接口服务。服务启动后,则可以只实例化一个spark session和fgp_model,并实时响应查询推荐商品的请求
这里借助Flask来实现。Flask是一个用Python编写的Web应用程序框架。Flask中文官网:https://dormousehole.readthedocs.io/en/latest/
Flask安装命令
pip install Flask -i https://mirrors.aliyun.com/pypi/simple/
Flask代码
import ast
import os
from flask import Flask, request
from pyspark.ml.fpm import FPGrowthModel
from pyspark.sql import SparkSessionfrom tags.recommend.fpgrowth_association_goods import get_recommend_goodsos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python'# 初始化SparkSession
spark = SparkSession.builder \.config("spark.sql.shuffle.partitions", 5) \.appName("recommend_api") \.getOrCreate()# 加载模型
hdfs_path = "/xtzg/recommend/fpg"
fpg_model: FPGrowthModel = FPGrowthModel.load("hdfs://192.168.88.166:8020"+hdfs_path)app = Flask(__name__)@app.route('/recommend')
def recommend():# 处理get请求,获取?后边的参数data = request.args.to_dict()# print(data)# 使用literal_eval将字符串转换为listgoods_list = ast.literal_eval(data['goods_list'])print('-----数据来了:', goods_list)# 方法调用recommended_goods = get_recommend_goods(goods_list, spark, fpg_model)return recommended_goods@app.route('/')
def hello_world():return '欢迎来到小兔智购商品推荐系统'if __name__ == "__main__":app.run(host='0.0.0.0', port=5000)
启动Flask,然后可以看到生成的URL。
访问:
这里模拟发送一个get请求,在url后加?,然后是key=value,如下
http://192.168.88.166:5000/recommend?goods_list=[‘3215330’]
则可以得到响应的结果
在工作中,由后端程序调用接口,得到响应结果后,再发生给前端进行渲染,生成推荐结果。
相关文章:
day16_推荐系统和总结
文章目录 day16_推荐系统和总结一、推荐实现1、基于流行度推荐(掌握)1.1 近期热门商品推荐1.2 个人热门商品推荐 2、基于隐语义模型的协同过滤推荐(了解)2.1 ALS算法介绍2.2 推荐代码 3、基于物品的协同过滤推荐(了解&…...
Scifinder数据库专利检索实操教程
在上期的内容里,我为大家分享了查询专利的数据库。发出后有小伙伴问,怎么没有大佬Scifinder!这不,应大家的呼声,今天就来给大家好好讲讲 Scifinder专利检索!! SciFinder,由美国化学会…...
Linux下 <用户名> is not in the sudoers file
参考链接 https://blog.csdn.net/weixin_49192027/article/details/114702099 原因 当前的用户没有加入到sudo的配置文件里 解决方案 切换到root用户 su 编辑配置文件 vim /etc/sudoers 如果没有安装vim 运行命令 sudo apt-get install vim vim的使用教程 参考链接…...
Linux下基本指令(4)
Linux权限的概念 Linux下有两种用户:超级用户(root)、普通用户。 超级用户:可以再linux系统下做任何事情,不受限制 普通用户:在linux下做有限的事情。 超级用户的命令提示符是“#”,普通用户…...
【算法与数据结构】字典树(Trie)详解
目录 一,字典树的定义 二,字典树的代码实现 完整代码详细注释: 测试用例测试结果: 三,处理其他字符 四,内存优化与扩展 1. 内存优化 2. 扩展功能 五,扩展功能支持通配符匹配 六&…...
el-table树状表格,默认展开第一个节点的每一层
效果如图 <template><el-table:data"tableData"style"width: 100%":tree-props"{ children: children, hasChildren: hasChildren }":expand-row-keys"expandRowKeys"row-key"id"expand-change"handleExpan…...
RPA-实例(UiPath )
UiPath 是一个流行的机器人流程自动化(RPA)工具,用于自动化重复性任务。以下是一个简单的实例,展示如何使用 UiPath 自动化一个常见的任务:从 Excel 文件中读取数据并将其输入到网页表单中。 实例:从 Excel 读取数据并自动填写网页表单 步骤 1:准备工作 安装 UiPath S…...
【RabbitMQ业务幂等设计】RabbitMQ消息是幂等的吗?
在分布式系统中,RabbitMQ 自身不直接提供消息幂等性保障机制,但可通过业务逻辑设计和技术组合实现消息处理的幂等性。以下是 8 种核心实现方案及最佳实践: 一、消息唯一标识符 (Message Deduplication) 原理 每条消息携带全局唯一IDÿ…...
Spring Boot项目开发常见问题及解决方案(上)
启动相关问题 问题 1:项目启动时报错“找不到主类” 在使用 Spring Boot 打包成可执行 JAR 文件后启动,有时会遇到这个头疼的问题。通常是因为打包配置有误或者项目结构不符合要求。 解决方案: 首先,检查 pom.xml(Ma…...
具有整合各亚专科医学领域知识能力的AI智能体开发纲要(2025版)
整合各亚专科医学领域知识能力的AI代理的开发与研究 一、引言 1.1 研究背景 在科技飞速发展的当下,人工智能(AI)已成为推动各行业变革的关键力量,医疗领域也不例外。近年来,AI 在医疗行业的应用取得了显著进展,从医学影像诊断到疾病预测,从药物研发到个性化医疗,AI 技…...
Selenium实战案例1:论文pdf自动下载
在上一篇文章中,我们介绍了Selenium的基础用法和一些常见技巧。今天,我们将通过中国科学:信息科学网站内当前目录论文下载这一实战案例来进一步展示Selenium的web自动化流程。 目录 中国科学:信息科学当期目录论文下载 1.网页内…...
进程的介绍--进程状态/切换
1.冯 • 诺依曼体系结构 1.1 体系结构 冯•诺依曼结构也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。数学家冯•诺依曼提出了计算机制造的三个基本原则,即采用二进制逻辑、程序存储执行以及计算机由五个部分组成&#x…...
一文详解U盘启动Legacy/UEFI方式以及GPT/MBR关系
对于装系统的老手而说一直想研究一下装系统的原理,以及面对一些问题时的解决思路,故对以前的方法进行原理上的解释,主要想理解其底层原理。 引导模式 MBR分区可以同时支持UEFI和Legacy引导,我们可以看一下微pe制作的启动盘&#…...
【面试】Redis 常见面试题
一、介绍一下什么是 Redis,有什么特点? Redis 是一个高性能的 key-value 内存数据库。 不同于传统的 MySQL 这样的关系型数据库,Redis 主要使用内存存储数据(当然也支持持久化存储到硬盘上),并非是使用 “表” 这样…...
扩散模型中,Flow Matching的训练方式相比于 DDPM 训练方法有何优势?
在扩散模型中,Flow Matching(FM)相比DDPM(Denoising Diffusion Probabilistic Models)的训练方法具有以下核心优势: 1. 更简单的训练目标 DDPM:通过逐步预测噪声来间接优化数据分布的变分下界(ELBO),需要设计多步的噪声调度策略,训练目标依赖马尔可夫链的分解。Flow…...
Unity FBXExport导出的FBX无法在Blender打开
将FBX转换为obj: Convert 3D models online - free and secure...
【无标题】基于Unity写一个DelayInvoke方法
没想到来得这么块,程序员可能比司机先失业了。。。。。。。。 //测试过一定要这么调用??奇怪的是,不能(mono 直接引用)??///但AI还是给出了能用的代码 MonoBehaviourExtensions.DelayInvoke(this,()=> { },3); /* 方案一,使用示例(): public class ExampleUsag…...
JavaScript 语言基础之标签语句
标签语句的语法 label: statement label 表示标签名,可以是任何合法的标识符,但不能是 JavaScript 中的保留字。statement 表示被标记的语句块,可以是任何合法的 JavaScript 语句。 用法 标签语句的主要用途是在代码中进行跳转࿰…...
【网络编程】网络编程基础:TCP/UDP 协议
一、什么是网络? 网络是信息传输,接收和共享的虚拟世界,通过把网络上的信息汇聚在一起,将这些资源进行共享。 初衷:知识共享。这里不得不提到Internet 的历史-它其实是“冷战”的产物: 1957年…...
idea 部署 AJ-Report 启动的注意事项
AJ-Report 入门参考: AJ-Report 初学(入门教程) gitee 下载:https://gitee.com/anji-plus/report/releases 根据上面提供的 gitee 下载链接,点击直接下载 最上面的就是最新版本的,旧版本往下拉就可以找到,有三个下载…...
C# 生成二维码隐藏ASCII码
在 C# 中生成二维码时,如果需要隐藏或过滤掉 ASCII 码中的控制字符或不可见字符,可以在生成二维码之前对输入文本进行处理。以下是完整的实现步骤和代码示例: 1. 过滤 ASCII 码中的控制字符 ASCII 码中,0 到 31 以及 127 是控制字…...
python有没有不同精度的整型类型?
在 Python 中,不像 C、Java 等语言有明确的不同精度整型类型(如 int8、int16、int32、int64 等),Python 提供了统一的整数类型 int,它可以处理任意大小的整数,没有固定的精度限制。不过,Python …...
Python多线程编程理解面试题解析
一、多线程介绍 Python 的多线程是一种实现并发编程的方式,允许程序同时执行多个任务。然而,由于 Python 的全局解释器锁(GIL)的存在,多线程在某些场景下可能无法充分利用多核 CPU 的性能。以下是对 Python 多线程的理…...
网络协议相关知识有哪些?
前言 网络协议的基础是OSI和TCP/IP模型,这两个模型是理解协议分层的关键。 正文(仅是个人理解,如有遗漏望海涵) 网络协议是网络中设备间通信的规则和标准,涉及数据传输、路由、错误控制等多个方面。以下是网络协议相关知识的系统梳理: 一、网络协议分层模型 1、OSI七…...
【并发压测】高并发下Linux流量监控
在高并发环境下,Linux流量监控至关重要,可以帮助您确保网络性能和稳定性。以下是一些常用的Linux流量监控工具和方法: 1. **iftop**:iftop 是一款实时的网络流量监控工具,可以显示当前服务器上每个网络接口的流量使用情…...
Spring Boot项目中解决跨域问题(四种方式)
目录 一,跨域产生的原因二,什么情况下算跨域三,实际演示四,解决跨域的方法 1,CrossOrigin注解2,添加全局过滤器3,实现WebMvcConfigurer4,Nginx解决跨域5,注意 开发项目…...
革新之力:数字科技——重塑未来的超越想象之旅
在21世纪的科技浪潮中,数字科技如同一股不可阻挡的洪流,正以前所未有的速度和广度改变着我们的生活、工作乃至整个社会的结构。它不仅是技术的简单迭代,更是对人类社会认知边界的拓宽,对经济模式、社会治理、文化形态等多方面的深…...
matlab和java混合编程经验分享
最常用的就是可以查到再控制栏deploytool选择library complier打包,但是有问题就是比如果用了外部的求解器比如yalmip或者cplex的话用这个方法会找不到外部的求解器,网上找了很多,基本都大同小异。 后面分享一个亲测有效的打包方法࿰…...
rk3588/3576板端编译程序无法运行视频推理
图片推理可以,但是视频不行,运行视频推理报错:segment fault. 我遇到的问题原因是ffmpeg安装有问题,可以先在板端运行:ffmpeg -version ffmpeg version 4.2.4-1ubuntu1.0firefly6 Copyright (c) 2000-2020 the FFmpe…...
MATLAB在数据分析和绘图中的应用:从基础到实践
引言 股票数据分析是金融领域中的重要研究方向,通过对历史价格、成交量等数据的分析,可以帮助投资者更好地理解市场趋势和做出决策。MATLAB作为一种强大的科学计算工具,提供了丰富的数据处理和可视化功能,非常适合用于股票数据的…...
【CS285】高斯策略对数概率公式的学习笔记
公式介绍 在【CS285】中提到了高斯策略对数概率公式的公式如下: log π θ ( a t ∣ s t ) − 1 2 ∥ f ( s t ) − a t ∥ Σ 2 const \log \pi_{\theta}(\mathbf{a}_t | \mathbf{s}_t) -\frac{1}{2} \left\| f(\mathbf{s}_t) - \mathbf{a}_t \right\|_{\S…...
windows环境下用docker搭建php开发环境dnmp
安装WSL WSL即Linux子系统,比虚拟机占用资源少,安装的前提是系统必须是win10以上。 WSL的安装比较简单,网上有很多教程,例如:WSL简介与安装流程(Windows 下的 Linux 子系统)_wsl安装-CSDN博客&…...
区块链中的递归长度前缀(RLP)序列化详解
文章目录 1. 什么是RLP序列化?2. RLP的设计目标与优势3. RLP处理的数据类型4. RLP编码规则详解字符串的编码规则列表的编码规则 5. RLP解码原理6. RLP在以太坊中的应用场景7. 编码示例分析8. 总结 1. 什么是RLP序列化? 递归长度前缀(RLP&…...
PHP建立MySQL持久化连接(长连接)及mysql与mysqli扩展的区别
如果在 PHP 5.3 的版本以前想要创建MySQL的持久化连接(长连接),需要显式调用 pconnect 创建: $con mysql_pconnect($server[host], $server[username], $server[password]); if (!($con false)) { if (mysql_select_db($server[database], $con) fals…...
基于Python+Django+Vue的旅游景区推荐系统系统设计与实现源代码+数据库+使用说明
运行截图 功能介绍 前台功能包括:首页、详情页、订单、用户中心。后台功能包括:首页、轮播图管理、管理员、卖家管理、买家管理、景区管理、订单管理非开源功能(分类管理,地区管理,收藏管理,评论管理&a…...
架构学习第七周--Prometheus
目录 一、监控系统基础 二、Prometheus介绍 三、Prometheus单机部署 四、服务发现与告警功能 4.1,服务发现 4.2,告警功能实现 五、Prometheus与Kubernetes 5.1,Kubernetes指标 5.2,Prometheus集群部署 一、监控系统基础…...
基于Nanopi duo2的WiFi智能摄像头
1.固件包烧录 https://wiki.friendlyelec.com/wiki/index.php/NanoPi_Duo2/zh#.E8.BF.9E.E6.8E.A5WiFi 固件包链接以及烧录工具都在上面链接中 烧录过程 使用读卡器将SD卡插入到电脑,然后打开烧录工具 2.通过串口工具连接板子使其连接WiFi 对应的串口工具,就是这个HyperT…...
Hive Orc表数据导出和导入
导出到hdfs:hive执行 INSERT OVERWRITE DIRECTORY /test/hdfs_dir ROW FORMAT DELIMITED FIELDS TERMINATED BY \t STORED AS ORC SELECT * FROM hive_table; HDFS导出到本地:shell执行 hdfs dfs -get /test/hdfs_dis/file_name /linux_dir/xxx 本…...
Python爬虫实战:从零到一构建数据采集系统
文章目录 前言一、准备工作1.1 环境配置1.2 选择目标网站 二、爬虫实现步骤2.1 获取网页内容2.2 解析HTML2.3 数据保存 三、完整代码示例四、优化与扩展4.1 反爬应对策略4.2 动态页面处理4.3 数据可视化扩展 五、注意事项六、总结互动环节 前言 在大数据时代,数据采…...
Ubuntu 的RabbitMQ安装
目录 1.安装Erlang 查看erlang版本 退出命令 2. 安装 RabbitMQ 3.确认安装结果 4.安装RabbitMQ管理界面 5.启动服务并访问 1.启动服务 2.查看服务状态 3.通过IP:port 访问界面 4.添加管理员用户 a)添加用户名:admin,密码࿱…...
七星棋牌源码高阶技术指南:6端互通、200+子游戏玩法深度剖析与企业级搭建实战(完全开源)
在棋牌游戏行业高速发展的今天,如何构建一个具备高并发、强稳定性与多功能支持的棋牌游戏系统成为众多开发者和运营团队关注的焦点。七星棋牌全开源修复版源码 凭借其 六端互通、200子游戏玩法、多省区本地化支持,以及 乐豆系统、防沉迷、比赛场、AI智能…...
cuda安装
cuda WSL2急速搭建CUDA体验环境_wsl2 cuda-CSDN博客 cudnn cuDNN Archive | NVIDIA Developer pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118...
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_os_init 函数
ngx_os_init 声明在 src/os/unix/ngx_os.h ngx_int_t ngx_os_init(ngx_log_t *log); 定义在 src\os\unix\ngx_posix_init.c ngx_int_t ngx_os_init(ngx_log_t *log) {ngx_time_t *tp;ngx_uint_t n; #if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)long size; #endif#if (NGX…...
记录一次部署PC端网址全过程
当我查看我之前写的文章时、顿时惊奇发出感慨:啥时候写的?是我写的么?疑惑重重… 所以说,好记性不如烂笔头。 记录一次部署PC端网址全过程 部署PC端网址分是三步:第一步:申请域名并映射到外网IP ࿰…...
QML 实现一个动态的启动界面
QML 实现一个动态的启动界面 一、效果查看二、源码分享三、所用到的资源下载 一、效果查看 二、源码分享 工程结构 main.qml import QtQuick import QtQuick.Controls import QtQuick.Dialogs import Qt.labs.platformWindow {id:windowwidth: 640height: 400visible: truetit…...
视频HDR技术详解,你的电脑怎么播放HDR视频?
闲聊:前两天在b站上面看到影视飓风的视频,让我有点疑惑,我不知道为什么播放视频有设备撑不住一说,所以感兴趣去ytb下载了4k原片30hz刷新的,然后测试一下我的电脑能不能播放,发现还是可以的,视觉…...
Spring统一功能处理:拦截器、响应与异常的统一管理
目录 一.拦截器 二.统一数据返回格式 三.统一异常处理 一.拦截器 拦截器是Spring框架提供的核功能之,主要来拦截的请求,在指定法前后,根据业务需要执预先设定的代码。 也就是说,允许开发员提前预定义些逻辑,在的请…...
2025年度福建省职业院校技能大赛高职组“信息安全管理与评估”赛项规程
2025 年度福建省职业院校技能大赛 高 职组“ 信息安全管理与评估 ”赛项规程 一、赛项名称 省赛编号:GZ032 赛项名称:信息安全管理与评估 赛项组别:高职组 竞赛形式: 团体赛 二、竞赛目的 为全面贯彻落实国家网络强国战略&#x…...
Vue 中 nextTick 的原理详解
1. 为什么需要 nextTick Vue 采用 异步渲染机制,当响应式数据发生变化时,Vue 并不会立即更新 DOM,而是将这些变化放入一个 队列 中,并在 同一事件循环(Event Loop)中合并相同的修改,最后执行批…...
vue 手写分页
【先看效果】 (1)内容小于2页 不展示页码 (2)1 < 内容页数< 限定展示页码 展示:页码、上下页;隐藏:首页、末页图标,上、下一区间码。即:(页数&#…...