HAWQ + MADlib 玩转数据挖掘之(四)——低秩矩阵分解实现推荐算法

2023-10-14 02:10

本文主要是介绍HAWQ + MADlib 玩转数据挖掘之(四)——低秩矩阵分解实现推荐算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、潜在因子(Latent Factor)推荐算法

        本算法整理自知乎上的回答 @nick lee。应用领域:“网易云音乐歌单个性化推荐”、“豆瓣电台音乐推荐”等。
        这种算法是在NetFlix(没错,就是用大数据捧火《纸牌屋》的那家公司)的推荐算法竞赛中获奖的算法,最早被应用于电影推荐中,在实际应用中比现在排名第一的 @邰原朗所介绍的算法误差(RMSE)会小不少,效率更高。下面仅利用基础的矩阵知识来介绍下这种算法。
        该算法的思想是:每个用户(user)都有自己的偏好,比如A喜欢带有小清新的、吉他伴奏的、王菲等元素(latent factor),如果一首歌(item)带有这些元素,那么就将这首歌推荐给该用户,也就是用元素去连接用户和音乐。每个人对不同的元素偏好不同,而每首歌包含的元素也不一样。

1. 潜在因子矩阵

        我们希望能找到这样两个矩阵:
(1)潜在因子-用户矩阵Q,表示不同的用户对于不用元素的偏好程度,1代表很喜欢,0代表不喜欢。比如图1这样:
图1

(2)潜在因子-音乐矩阵P,表示每种音乐含有各种元素的成分,比如图2中,音乐A是一个偏小清新的音乐,含有小清新这个Latent Factor的成分是0.9,重口味的成分是0.1,优雅的成分是0.2……
图2

        利用这两个矩阵,我们能得出张三对音乐A的喜欢程度是:张三对小清新的偏好*音乐A含有小清新的成分+对重口味的偏好*音乐A含有重口味的成分+对优雅的偏好*音乐A含有优雅的成分+……即:0.6*0.9+0.8*0.1+0.1*0.2+0.1*0.4+0.7*0=0.69
        每个用户对每首歌都这样计算可以得到不同用户对不同歌曲的评分矩阵 ,如图3所示。(注,这里的破浪线表示的是估计的评分,接下来我们还会用到不带波浪线的R表示实际的评分):
图3

        因此我们对张三推荐四首歌中得分最高的B,对李四推荐得分最高的C,王五推荐B。如果用矩阵表示即为:

2. 如何得到潜在因子

        下面问题来了,这个潜在因子(latent factor)是怎么得到的呢?
        由于面对海量的让用户自己给音乐分类并告诉我们自己的偏好系数显然是不现实的,事实上我们能获得的数据只有用户行为数据。我们沿用 @邰原朗的量化标准:单曲循环=5, 分享=4, 收藏=3, 主动播放=2 , 听完=1, 跳过=-2 , 拉黑=-5,在分析时能获得的实际评分矩阵R,也就是输入矩阵大概是这个样子(图4):
图4


        推荐系统的目标就是预测出空白对应位置的分值。推荐系统基于这样一个假设:用户对项目的打分越高,表明用户越喜欢。因此,预测出用户对未评分项目的评分后,根据分值大小排序,把分值高的项目推荐给用户。这是个非常稀疏的矩阵,因为大部分用户只听过全部音乐中很少一部分。如何利用这个矩阵去找潜在因子呢?这里主要应用到的是矩阵的UV分解,如图5所示。
图5

        矩阵分解的想法来自于矩阵补全,就是依据一个矩阵给定的部分数据,把缺失的值补全。一般假设原始矩阵是低秩的,我们可以从给定的值来还原这个矩阵。由于直接求解低秩矩阵从算法以及参数的复杂度来说效率很低,因此常用的方法是直接把原始矩阵分解成两个子矩阵相乘。例如将图5所示的评分矩阵分解为两个低维度的矩阵,用Q和P两个矩阵的乘积去估计实际的评分矩阵,而且我们希望估计的评分矩阵和实际的评分矩阵不要相差太多,也就是求解下面的目标函数:

        这里涉及到最优化理论,在实际应用中,往往还要在后面加上2范数的罚项,然后利用梯度下降法就可以求得这P,Q两个矩阵的估计值。例如我们上面给出的那个例子可以分解成为这样两个矩阵(图6):
图6
        这两个矩阵相乘就可以得到估计的得分矩阵(图7):
图7

        将用户已经听过的音乐剔除后,选择分数最高音乐的推荐给用户即可(红体字)。在这个例子里面用户7和用户8有强的相似性(图8):
图8
        从推荐的结果来看,正好推荐的是对方评分较高的音乐(图9):
图9

二、MADlib低秩矩阵分解函数

        从前面的介绍可以知道,Latent Factor推荐算法关键点在于评分矩阵的UV分解,求得P/Q两个矩阵。MADlib的lmf_igd_run函数能够实现这个功能。

1. lmf_igd_run函数语法

lmf_igd_run( rel_output,  rel_source,  col_row,  col_column,  col_value,  row_dim,  column_dim,  max_rank,  stepsize,  scale_factor,  num_iterations,  tolerance  ) 

2. 参数说明

(1)rel_output
        TEXT类型,接收输出的表名。输出的因子矩阵U和V是扁平格式。
RESULT AS (
        matrix_u    DOUBLE PRECISION[],
        matrix_v    DOUBLE PRECISION[],
        rmse        DOUBLE PRECISION
);
        行i对应的特征是matrix_u[i:i][1:r],列j对应的特征是matrix_v[j:j][1:r]。

(2)rel_source
        TEXT类型,包含输入数据的表名。输入矩阵的格式如下:
{TABLE|VIEW} input_table (
    row    INTEGER,
    col    INTEGER,
    value  DOUBLE PRECISION
)
        输入包含一个描述稀疏矩阵的表,数据被指定为(row, column, value)。输入矩阵的行列值大于等于1,不应该有NULL值。

(3)col_row
        TEXT类型,包含行号的列名。

(4)col_column
        TEXT类型,包含列号的列名。

(5)col_value
        DOUBLE PRECISION(FLOAT8)类型,(row, col)位置对应的值。

(6)row_dim(可选)
        INTEGER类型,缺省为:“SELECT max(col_row) FROM rel_source”,矩阵中的行数。

(7)column_dim(可选)
        INTEGER类型,缺省为:“SELECT max(col_col) FROM rel_source”,矩阵中的列数。

(8)max_rank
        INTEGER类型,缺省为20,期望逼近阶。

(9)stepsize(可选)
        DOUBLE PRECISION(FLOAT8)类型,缺省值为0.01。超参数,决定梯度步长。

(10)scale_factor(可选)
        DOUBLE PRECISION(FLOAT8)类型,缺省值为0.1。超参数,决定初始因子标度。

(11)num_iterations(可选)
        INTEGER类型,缺省值为10。不收敛时最大的迭代次数。

(12)tolerance(可选)
        DOUBLE PRECISION(FLOAT8)类型,缺省值为0.0001,收敛误差。


三、低秩矩阵分解函数实现推荐算法示例

          用lmf_igd_run函数分解图4所示的矩阵,并生成相应的推荐矩阵。

1. 建立输入表

(1)建立索引表
        从前面的解释可以看到,推荐矩阵的行列下标分别表示用户和音乐作品。然而在业务系统中,userid和musicid很可能不是按从0到N的规则顺序生成的,因此需要建立矩阵下标值与业务表ID之间的映射关系,这里使用HAWQ的BIGSERIAL自增数据类型对应推荐矩阵的索引下标。
-- 用户索引表    
drop table if exists tbl_idx_user;    
create table tbl_idx_user (user_idx bigserial, userid varchar(10));    
-- 音乐索引表    
drop table if exists tbl_idx_music;    
create table tbl_idx_music (music_idx bigserial, musicid varchar(10));

(2)建立用户行为表
drop table if exists lmf_data;    
create table lmf_data (    row int,    col int,    val float8    
);

2. 生成输入表数据

-- 用户表    
insert into tbl_idx_user (userid)     
values ('u1'),('u2'),('u3'),('u4'),('u5'),('u6'),('u7'),('u8'),('u9'),('u10');    
-- 音乐表    
insert into tbl_idx_music (musicid)     
values ('m1'),('m2'),('m3'),('m4'),('m5'),('m6'),('m7'),('m8'),('m9'),('m10'),('m11'),('m12'),('m13'),('m14'),('m15');    
-- 用户行为表    
insert into lmf_data values (1, 1, 5), (1, 6, -5), (1, 9, 5), (1, 11, 3), (1, 12, 1), (1, 13, 5);    
insert into lmf_data values (2, 4, 3), (2, 9, 3), (2, 13, 4);    
insert into lmf_data values (3, 3, 1), (3, 5, 2), (3, 6, -5), (3, 7, 4), (3, 11, -2), (3, 12, -2), (3, 13, -2);    
insert into lmf_data values (4, 2, 4), (4, 3, 4), (4, 4, 3), (4, 7, -2), (4, 9, -5), (4, 12, 3);    
insert into lmf_data values (6, 2, 5), (6, 3, -5), (6, 5, -5), (6, 7, 4), (6, 8, 3), (6, 11, 4);    
insert into lmf_data values (7, 3, 4), (7, 6, 3), (7, 9, 4);    
insert into lmf_data values (8, 2, -2), (8, 6, 5), (8, 11, 4), (8, 12, 4), (8, 13, -2);    
insert into lmf_data values (9, 2, -2), (9, 6, 5), (9, 8, 5), (9, 11, 4), (9, 13, -2);   
        在生成原始数据时对图4的例子做了适当的修改。用户表中u5和u10用户没有给任何作品打分,而音乐表中的m10、m14、m15无评分。我们希望看到的结果是,除了与打分行为相关的用户和作品以外,也能为u5、u10推荐作品,并可能将m10、m14、m15推荐给用户。

3. 调用lmf_igd_run函数分解矩阵

drop table if exists lmf_model;    
select madlib.lmf_igd_run( 'lmf_model',    'lmf_data',    'row',    'col',    'val',    11,    16,    7,    0.1,    1,    10,    1e-9    );   
        说明:
  • 最大行列数可以大于实际行列数,如这里传入的参数是11和16,而实际的用户数与作品数是10和15。
  • max_rank参数为最大秩数,要小于min(row_dim, column_dim),否则函数会报错:
    NOTICE:  Matrix lmf_data to be factorized: 11 x 16  
    ERROR:  plpy.SPIError: Function "madlib.lmf_igd_transition(double precision[],integer,integer,double precision,double precision[],integer,integer,integer,double precision,double precision)": Invalid parameter: max_rank >= row_dim || max_rank >= column_dim (plpython.c:4663)  
  • 既然假设是低秩矩阵,抛开复杂的数学定义,实际可以简单理解为最大的潜在因子数,也就是例子中的最大量化指标个数。本例中共有7个指标,因此参数传7。
  • stepsize和scale_factor参数对于结果的影响巨大,但是文档中只标注了缺省值,并没有说明如何定义这两个参数的相关指南。而且不同的学习数据,参数值也不同。在本例中,使用缺省值的误差巨大。经过反复测试,对于测试矩阵,stepsize和scale_factor分别传0.1和1误差是相对小的。
        执行结果的控制台输出如下:
NOTICE:  Matrix lmf_data to be factorized: 11 x 16  
NOTICE:  CREATE TABLE will create implicit sequence "lmf_model_id_seq" for serial column "lmf_model.id"  
CONTEXT:  SQL statement "  CREATE TABLE lmf_model (  id          SERIAL,  matrix_u    DOUBLE PRECISION[],  matrix_v    DOUBLE PRECISION[],  rmse        DOUBLE PRECISION)"  
PL/pgSQL function "lmf_igd_run" line 49 during exception cleanup  
NOTICE:    
Finished low-rank matrix factorization using incremental gradient  
DETAIL:    * table : lmf_data (row, col, val)  
Results:  * RMSE = 0.0032755443518  
Output:  * view : SELECT * FROM lmf_model WHERE id = 1  lmf_igd_run   
-------------  1  
(1 row)  Time: 2477.339 ms  
        可以看到,误差值为0.0033,分解用时2秒多。

4. 检查结果

        从上一步的输出看到,lmf_igd_run()函数返回的模型ID是1,需要用它作为查询条件。
select array_dims(matrix_u) as u_dims, array_dims(matrix_v) as v_dims  from lmf_model  where id = 1;
        结果:
   u_dims    |   v_dims      
-------------+-------------  [1:11][1:7] | [1:16][1:7]  
(1 row)  Time: 158.163 ms
        结果表中包含分解成的两个矩阵,U(用户潜在因子)矩阵11行7列,V(音乐潜在因子)矩阵16行7列。

5. 查询结果值

select matrix_u, matrix_v from lmf_model where id = 1;
        结果:
matrix_u | {{1.54648885311,1.63203242967,1.19529757016,1.03141840085,0.844437687891,0.0769111939022,0.645542689815},{0.757818584934,0.716903440182,1.19021031149,0.578927767269,0.954646881167,0.219129157675,0.70922183389},{1.33531138374,1.30363952312,0.0919517138103,-0.142114607359,-0.668429861574,0.279744342425,-1.47956275838},{0.190393941069,-0.0157012997592,-1.59761714858,0.303397793911,2.15476796766,0.741260761563,0.702753056216},{0.123468743637,0.843539815862,0.668331681751,0.429033627734,0.144201364834,0.402088313829,0.240613154601},{-1.73675064226,1.72484359814,0.990104966913,0.646877613553,0.905835307162,-0.707346214017,-0.36038050429},{0.622993679823,-0.00543774810147,0.163193819662,0.91213474814,-0.598496246148,1.78565072923,1.34895433137},{-1.18926160921,0.17719838284,0.478695134653,-1.14668758686,-0.352168758657,1.26421385298,1.10551649305},{-1.15853595317,-0.119913407821,1.07440704269,-0.686910624669,-0.297405263117,1.79703846266,0.535631232667},{0.831467553042,0.979984433856,0.130160186905,0.453018658794,0.0628163618967,0.0531226526946,0.00863399589434},{0.195625970606,0.171808642801,0.60626720218,0.761990434024,0.90873805061,0.63927074708,0.453554459848}}  
matrix_v | {{0.91664850933,0.295100137999,0.649386785896,0.740600691525,1.04761610381,0.3951938048,0.998669662318},{-0.211657274194,1.23775531428,-0.0990159936749,1.50500061334,1.62828521114,-0.345089550207,0.277028536078},{1.48907962612,-0.244764864271,-1.11798478253,0.172149124925,0.212554639601,1.4013240539,0.535152168373},{0.182228641514,0.710136204482,0.199561470359,0.952762531103,1.08504062754,0.273794511904,0.662804958119},{1.83870481391,-0.377896107431,0.177281723558,-0.027566718438,-0.621172199773,0.841672155605,0.427967335391},{-1.4208947449,-1.52877812059,-0.279978617646,-0.438163656333,-0.506157691773,1.30685274721,1.25037856931},{0.293946423431,1.64459334677,0.642146247552,0.446804095655,0.000142813943154,-0.505397837783,-1.08734103352},{-1.00316820867,0.533775424924,1.38128161314,-0.29340575407,0.0619412924369,1.21026893492,0.112172980617},{0.928571674012,0.917879383969,1.81854196905,0.783856521179,-1.48198191488,0.510083892664,0.457647107996},{0.307900905609,0.227393480018,0.079794999212,0.0367207694799,0.755868535955,0.623352447525,0.124734496698},{-0.936350840061,1.00012249484,1.20991171631,0.0803342204453,0.39871563714,0.646403662259,1.39754259822},{-0.495918823256,0.358190982574,0.13668876922,-0.475142499674,0.759641070407,1.34038757262,1.18405353287},{0.889504598477,-0.0854584870533,0.411028314388,1.22217493352,1.48080784437,-0.445754117769,1.23355880197},{0.658764299937,0.689042912796,0.878053414635,0.757899995893,0.478946132585,0.446365167852,0.523622296751},{0.226923980284,0.493002902251,0.0254670833237,0.218542233109,0.719660140574,0.841996903066,0.750140952412},{0.695838811807,0.0145157109946,0.92957209982,0.420896829572,0.364341121167,0.229911166709,0.85705531016}}  Time: 89.038 ms
  

6. 矩阵相乘生成推荐矩阵

        MADlib的矩阵相乘函数是matrix_mult,支持稠密和稀疏两种矩阵表示。
        稠密矩阵需要指定矩阵对应的表名、row和val列:
select madlib.matrix_mult('mat_a', 'row=row_id, val=row_vec',  'mat_b', 'row=row_id, val=vector, trans=true',  'mat_r');
        稀疏矩阵需要指定矩阵对应的表名、row、col和val列:
select madlib.matrix_mult('mat_a_sparse', 'row=rownum, col=col_num, val=entry',  'mat_b_sparse', 'row=row_id, col=col_id, val=val, trans=true',  'matrix_r');
        ‘trans=true’表示在相乘前该矩阵先进行转置。需要将lmf_igd_run函数输出的矩阵装载到表中。如果使用稠密形式,需要将二维矩阵转为一维矩阵。array_unnest_2d_to_1d是madlib 1.11版本的新增的函数,用于将二维数组展开为一维数组。1.10版本并无次函数,但可以创建一个UDF实现,具体参见“ HAWQ + MADlib 玩转数据挖掘之(二)——矩阵”
        如果使用稀疏形式,只要二维矩阵的行、列、值插入表中即可,这里使用稀疏方式。
-- 建立用户稀疏矩阵表  
drop table if exists mat_a_sparse;  
create table mat_a_sparse as  
select d1,d2,matrix_u[d1][d2] val from   
(select matrix_u,  generate_series(1,array_upper(matrix_u,1)) d1,  generate_series(1,array_upper(matrix_u,2)) d2  from lmf_model) t;  -- 建立音乐稀疏矩阵表  
drop table if exists mat_b_sparse;  
create table mat_b_sparse as  
select d1,d2,matrix_v[d1][d2] val from   
(select matrix_v,  generate_series(1,array_upper(matrix_v,1)) d1,  generate_series(1,array_upper(matrix_v,2)) d2  from lmf_model) t;  -- 执行矩阵相乘  
drop table if exists matrix_r;  
select madlib.matrix_mult('mat_a_sparse', 'row=d1, col=d2, val=val',  'mat_b_sparse', 'row=d1, col=d2, val=val, trans=true',  'matrix_r');
        结果:
 matrix_mult   
-------------  (matrix_r)  
(1 row)  Time: 5785.798 ms
        这两个矩阵相(11 x 3与16 x 3)乘用时将近6秒。生成的结果表是稠密形式的11 x 16矩阵,这就是我们需要的推荐矩阵。为了方便与原始的索引表关联,将结果表转为稀疏表示。
drop table if exists matrix_r_sparse;  
select madlib.matrix_sparsify('matrix_r', 'row=d1, val=val',  'matrix_r_sparse', 'col=d2, val=val');
        结果:
  matrix_sparsify    
-------------------  (matrix_r_sparse)  
(1 row)  Time: 2252.413 ms
        最后与原始的索引表关联,过滤掉用户已经已经听过的音乐,选择分数最高音乐的推荐。
select t2.userid,t3.musicid,t1.val   from (select d1,d2,val,row_number() over (partition by d1 order by val desc) rn  from matrix_r_sparse t1  where not exists (select 1 from lmf_data t2 where t1.d1 = t2.row and t1.d2 = t2.col)) t1,  tbl_idx_user t2, tbl_idx_music t3  where t1.rn = 1 and t2.user_idx= t1.d1 and t3.music_idx = t1.d2  order by t2.user_idx;
        结果:
 userid | musicid |       val          
--------+---------+------------------  u1     | m7      | 4.46508576767804  u2     | m1      | 3.45259331270197  u3     | m14     | 1.21085044974838  u4     | m15     | 2.87854763370041  u5     | m12     | 3.07991997971589  u6     | m9      |  2.8073075471921  u7     | m8      | 5.70400322091688  u8     | m8      |   4.229015803831  u9     | m12     | 4.14559001844873  u10    | m8      | 2.16407771238165  
(10 rows)  Time: 743.415 ms
        这就是为每个用户推荐的音乐作品。可以看到,用户u5、u10分别推荐了m12和m8,m14和m15也推荐给了用户。

四、总结与思考

  1. MADlib的低秩矩阵分解函数可以作为推荐类应用的算法实现,并且适合于传统DBA或数据库开发人员使用。
  2. 算法本身包含很专业的数学理论,对我这样从事应用开发的人来说理解门槛很高。
  3. 正因上面一条,存在对这个算法的一些疑问:(1)从函数调用角度看,madlib.lmf_igd_run函数是一个非确定函数,也就是说,同样一组输入数据,函数生成的结果数据却不同,结果就是每次的推荐可能不一样,是否合适?(2)对于大型系统,如今日头条这样的应用,推荐需要计算的是一个 几亿 x 几亿的大型矩阵,如何保证实时性、扩展性和性能?
  4. 要熟悉并运用数据仓库的全栈知识技能(建模、ETL、数据存储、OLAP、数据可视化、数据挖掘、BI等等),还需要大量的学习与实践。不过从MADlib开始研究数据挖掘是一个不错的起点,用句老俗话给自己来碗鸡汤补充能量:“好的开始是成功的一半”!

参考文献:

  1. Low-rank Matrix Factorization:官方文档对低秩矩阵分解的说明。
  2. 推荐算法之潜在因子(Latent Factor)算法:本文所引用的示例。
  3. 推荐系统中的矩阵分解,假设推荐矩阵是两个低秩矩阵相乘,有何依据:说明假设低秩的意义。
  4. 浅谈矩阵分解在推荐系统中的应用:矩阵分解的数学推导。
  5. Machine Learning第九讲[推荐系统] --(三)低秩矩阵分解:描述了实现细节中的均值归一化。
  6. 使用LFM(Latent factor model)隐语义模型进行Top-N推荐:LFM算法说明及伪代码。

这篇关于HAWQ + MADlib 玩转数据挖掘之(四)——低秩矩阵分解实现推荐算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/207430

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time