From d05f5c1ddbdcfc708662a9be4289e23f4f58f00a Mon Sep 17 00:00:00 2001 From: lihaibineric Date: Thu, 11 Jan 2024 14:35:33 +0800 Subject: [PATCH] Site updated: 2024-01-11 14:35:33 --- 2024/01/01/leetcode/index.html | 67 +++++- 2024/01/02/rec/index.html | 353 ++++++++++++++++++++++++----- index.html | 4 +- local-search.xml | 4 +- search.xml | 401 ++++++++++++++++++++++++++++----- 5 files changed, 700 insertions(+), 129 deletions(-) diff --git a/2024/01/01/leetcode/index.html b/2024/01/01/leetcode/index.html index b9ec019..dccc481 100644 --- a/2024/01/01/leetcode/index.html +++ b/2024/01/01/leetcode/index.html @@ -16,19 +16,21 @@ - + - + + - + + @@ -209,7 +211,7 @@ - 26 words + 2.8k words @@ -220,7 +222,7 @@ - 1 mins + 24 mins @@ -263,8 +265,63 @@

【算法题】LeetCode算法汇总

+

语言细节

+

vector的长度:

+ +

for循环:

+

数组

二分查找

+

题目描述

+

链接:https://leetcode.cn/problems/binary-search/description/

+

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target +,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 +-1。

+

示例 1:

+
1
2
3
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
+

示例 2:

+
1
2
3
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
+

思路

+

题目表示的是有序数组,而且题目没有重复元素。在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1 # 定义target在左闭右闭的区间里,[left, right]

while left <= right:
middle = left + (right - left) // 2

if nums[middle] > target:
right = middle - 1 # target在左区间,所以[left, middle - 1]
elif nums[middle] < target:
left = middle + 1 # target在右区间,所以[middle + 1, right]
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值
+

注意这里给出的题解法:当 +left <= right的时候,以下的条件中全部都不取到等号 +nums[middle] > target nums[middle] < target

+

需要注意的是:right=nums.size()-1

+

C++版本

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while(left<=right)
{
// int middle = (left+right)/2; 这样写会溢出
int middle = left + ((right - left) / 2);
if(nums[middle]>target)
{
right = middle-1;
}
else if(nums[middle]<target)
{
left = middle+1;
}
else{
return middle;
}
}
return -1;
}
};
+

Go版本

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func search(nums []int, target int) int {
right:=len(nums)-1
left:=0
for left<=right{
middle:= left+(right-left)/2
if nums[middle]<target{
left = middle+1
}else if nums[middle]>target{
right = middle-1
}else{
return middle
}
}
return -1
}
+

移除元素

+

https://leetcode.cn/problems/remove-element/description/

+

题目描述

+

示例 1:

+
1
2
3
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
+

示例 2:

+
1
2
3
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
+

思路

+

双指针法(快慢指针法): +通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

+

定义快慢指针

+ +

双指针题解

+

C++版本

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowindex=0;
for(int fastindex = 0; fastindex<nums.size();fastindex++)
{
if(val!=nums[fastindex]){
nums[slowindex] = nums[fastindex];
slowindex++;
}
}
return slowindex;
}
};
+

python版本

+
1
2
3
4
5
6
7
8
9
10
class Solution(object):
def removeElement(self, nums, val):
slowindex=0
fastindex=0
while fastindex<len(nums):
if val!=nums[fastindex]:
nums[slowindex]=nums[fastindex]
slowindex = slowindex+1
fastindex+=1
return slowindex
+

GO版本:

+
1
2
3
4
5
6
7
8
9
10
func removeElement(nums []int, val int) int {
slow:=0
for i:=0;i<len(nums);i++{
if nums[i]!=val{
nums[slow]=nums[i]
slow++
}
}
return slow
}

哈希表

链表

二叉树

diff --git a/2024/01/02/rec/index.html b/2024/01/02/rec/index.html index 01506ad..b3334e6 100644 --- a/2024/01/02/rec/index.html +++ b/2024/01/02/rec/index.html @@ -16,19 +16,22 @@ - + - + + + - + + @@ -209,7 +212,7 @@ @@ -220,7 +223,7 @@ - 13 mins + 26 mins @@ -264,73 +267,301 @@

【深度学习】推荐系统算法

推荐系统

+

深度推荐系统 王喆

+

https://zhuanlan.zhihu.com/p/407871839

+

搜索引擎需要用户主动输入自己的意图,有些需求、意愿,是用户自己都意识不到的。将自己拥有的、用户可能喜欢的内容主动展示给用户,从而留住用户花费更多的时间与金钱。这就是推荐系统

+

关键词:

+
    +
  1. 记忆与扩展
  2. +
  3. 特征的Embedding
  4. +
  5. 高维稀疏的类别特征
  6. +
  7. 特征交叉结构
  8. +
+

推广搜

+

推荐、广告、搜索

+

用户需求表达方式:推荐、搜索

+

信息服务对象:推搜、广告

+

相同点:

+ +

推荐&搜索 \[ +F_{search}(t|q,u)\\ +F_{recommend}(t|u) +\] +q表示物料对用户查询的匹配程度,搜索有显式的匹配程度,但是推荐没有

+

搜索:查询语句与物料信息交叉

+

推荐:用户信息与物料信息交叉

+

推搜&广告

+

最终目标性:(优化指标)

-

协同过滤算法

-

经典算法

-

基本思想:根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品

-

分类:

+

实现流程:

-

基于用户的协同过滤算法

-

计算向量之间的相似性

+

推荐系统简介

+

召回:根据喜好标签进行选择

+

排序:根据召回的视频进行排序

+
+ + +
+

根据用户行为进行同步更新

+ +

推荐系统功能

    -
  1. 杰卡德相似性系数

    -

    衡量两个集合的相似度一种指标。 -两个集合A和B的交集元素在A,B的并集中所占的比例,称为两个集合的杰卡德相似系数

  2. -
  3. 余弦相似度

    -

    用户向量i和j之间的向量夹角的大小, 夹角越小, 说明相似度越大, -两个用户越相似

    -
    1
    2
    3
    4
    from sklearn.metrics.pairwise import cosine_similarity
    i = [1, 0, 0, 0]
    j = [1, 0.5, 0.5, 0]
    consine_similarity([a, b])
  4. -
  5. 皮尔逊相关系数

    -

    相比余弦相似度, -皮尔逊相关系数通过使用用户平均分对个独立评分进行修正, -减少了用户评分偏置的影响(每个向量先进行处理,减去了向量之间的均值)

    -
    1
    2
    3
    4
    from scipy.stats import pearsonr
    i = [1, 0, 0, 0]
    j = [1, 0.5, 0.5, 0]
    pearsonr(i, j)
  6. +
  7. 学习模型,并输入用户u和物料t,输入模型中返回score
  8. +
  9. 返回最匹配的score最高的物料t
-

最终结果预测

+

存在的问题是数据量太大且时间开销太大无法满足实时要求

+

RS功能架构

+
+ + +
+

召回

+ +

精排

+ +

粗排

+ +

重排

+ +

RS数据架构

+

存在的问题:

+
    +
  1. 统计窗口长导致涉及的计算量会比较大,线上预测时间紧张
  2. +
  3. 回溯历史存在Hadoop分布式文件系统(HDFS)中存的是冷数据,但是仍然有一批热数据没有落HDFS中,从而导致数据没办法全面接收
  4. +
+

Lambda架构

+
+ + +
+

image-20240109110904180

+

离线层

+ +

近线层

+ +

在线层

+ +

RS特征工程

+

必要性:

+ +

特征提取

+

物料特征

+ +

用户特征

+ +

交叉特征

+ +

image-20240109144352476

+ +

偏差特征

+ +

数据特征处理

+

处理缺失值

+ +

标准化数据

+ +

数据平滑与消偏

-

\[ -\mathrm{R}_{\mathrm{u}, \mathrm{p}}=\frac{\sum_{\mathrm{s} \in -\mathrm{S}}\left(\mathrm{w}_{\mathrm{u}, \mathrm{s}} \cdot -\mathrm{R}_{\mathrm{s}, \mathrm{p}}\right)}{\sum_{\mathrm{s} \in -\mathrm{S}} \mathrm{w}_{\mathrm{u}, \mathrm{s}}} -\]

-

​ 权重 \(w_{u,s}\)是用户\(u\)和用户\(s\)的相似度,\(R_{s,p}\) 是用户\(s\)对物品\(p\)的评分

+

分桶离散化

+

类别特征处理

+

推荐系统的特征空间:高维、稀疏

+

增强类别特征表达

-

方法的缺点

+

类别特征的高维性

+ +

类别特征的稀疏

+ +

特征如何表征

+ +
+ + +
+

RS的Embedding

+

为什么要Embedding

+

对于推荐系统而言:记忆+拓展

+

记忆能够处理80%的需求,但是个性化太弱(评分)

+

拓展将细粒度变成粗粒度,借助深度学习的Embedding

+

共享&独占Embedding

+

共享Embedding

+ +

独占Embedding

+ +

Parameter Server加速器

+

传统利用Hadoop/Spark的分布式训练方法忽略了高维稀疏特征空间 +直接让master处理会参数量很大

+

使用PS架构的好处:

+
+ + +
+
+ + +
+

一个Server只负责处理海量参数中的一部分

+

PS中的并发策略

+

同步并发

+
+ + +
+

异步并发

+

无须等待,不存在短板效应,有明显的速度优势

+

梯度失效的问题:

+

半同步半异步

+

允许Worker节点在一定迭代轮数中保持异步,但是如果最快节点和最慢节点的迭代步数之差已经超过了允许的最大值,那么所有的参数都需要停下来进行一次参数的同步

+

精排

+

特征交叉方法

+

传统方法:FTRL

+

FM:引入了二阶特征交叉(手动二阶特征交叉)

+

Wide&Deep:兼顾记忆与扩展

+

DeepFM :融合二阶交叉(实现了自动二阶特征交叉)

+

DCN:能够指定任意显式交叉

+

Autolnt:基于Transformer作特征交叉

+

用户行为序列建模

+

本质:将用户行为序列提炼并压缩成用户兴趣的Embedding

+

DIN:利用Attention,将当前的物料t作为用户历史序列的Query

+

双层Attention行为序列:第一层Attention历史行为序列内部的依赖关系;第二层是当前物料和历史行为序列

+

SIM:将“软过滤”变成“硬过滤”,相当于从长序列选择短序列进行DIN

+

召回

+

向量化召回统一建模框架

+

定义:将召回问题建模成向量空间中的近邻搜索问题

+

分类:两类实体QT,可以是物料-用户、用户-用户、物料-物料

+

方法流程:

+
    +
  1. 训练一个模型,并将QT映射到同一个向量空间
  2. +
  3. 构建起索引向量数据库
  4. +
  5. 对于传入的Q实例先进行Embedding 再进行近邻搜索
  6. +
+

建模方法:

diff --git a/index.html b/index.html index 5a5834a..832a7a8 100644 --- a/index.html +++ b/index.html @@ -218,7 +218,7 @@

- 推荐系统 搭建音视频系统 数据收集对用户进行建模,对视频建模 内嵌推荐系统算法 协同过滤算法 经典算法 基本思想:根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品 分类: 基于用户的协同过滤算法(UserCF): 给用户推荐和他兴趣相似的其他用户喜欢的产品 基于物品的协同过滤算法(ItemCF): 给用户推荐和他之前喜欢的物品相似的物品、 基于用户的协同过滤算法 计算向量之 + 推荐系统 深度推荐系统 王喆 https://zhuanlan.zhihu.com/p/407871839 搜索引擎需要用户主动输入自己的意图,有些需求、意愿,是用户自己都意识不到的。将自己拥有的、用户可能喜欢的内容主动展示给用户,从而留住用户花费更多的时间与金钱。这就是推荐系统。 关键词: 记忆与扩展 特征的Embedding 高维稀疏的类别特征 特征交叉结构 推广搜 推荐、广告、搜索 用
@@ -281,7 +281,7 @@

- 数组 二分查找 哈希表 链表 二叉树 回溯算法 贪心算法 动态规划 + 语言细节 vector的长度: C++:nums.size() Python:len(nums) GO:len(nums) for循环: C++:条件小括号+循环体中括号 Python:冒号且不需要小括号包条件 GO:循环体中括号,条件按照C++写但是不需要小括号 数组 二分查找 题目描述 链接:https://leetcode.cn/problems/binary-search/des
diff --git a/local-search.xml b/local-search.xml index 58b87bd..39ce6d8 100644 --- a/local-search.xml +++ b/local-search.xml @@ -8,7 +8,7 @@ /2024/01/02/rec/ -

推荐系统

  • 搭建音视频系统
  • 数据收集对用户进行建模,对视频建模
  • 内嵌推荐系统算法

协同过滤算法

经典算法

基本思想:根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品

分类:

  • 基于用户的协同过滤算法(UserCF):给用户推荐和他兴趣相似的其他用户喜欢的产品
  • 基于物品的协同过滤算法(ItemCF):给用户推荐和他之前喜欢的物品相似的物品、

基于用户的协同过滤算法

计算向量之间的相似性

  1. 杰卡德相似性系数

    衡量两个集合的相似度一种指标。两个集合A和B的交集元素在A,B的并集中所占的比例,称为两个集合的杰卡德相似系数

  2. 余弦相似度

    用户向量i和j之间的向量夹角的大小, 夹角越小, 说明相似度越大,两个用户越相似

    1
    2
    3
    4
    from sklearn.metrics.pairwise import cosine_similarity
    i = [1, 0, 0, 0]
    j = [1, 0.5, 0.5, 0]
    consine_similarity([a, b])
  3. 皮尔逊相关系数

    相比余弦相似度,皮尔逊相关系数通过使用用户平均分对个独立评分进行修正,减少了用户评分偏置的影响(每个向量先进行处理,减去了向量之间的均值)

    1
    2
    3
    4
    from scipy.stats import pearsonr
    i = [1, 0, 0, 0]
    j = [1, 0.5, 0.5, 0]
    pearsonr(i, j)

最终结果预测

  • 利用用户相似度和相似用户的评价加权平均获得用户的评价预测

\[\mathrm{R}_{\mathrm{u}, \mathrm{p}}=\frac{\sum_{\mathrm{s} \in\mathrm{S}}\left(\mathrm{w}_{\mathrm{u}, \mathrm{s}} \cdot\mathrm{R}_{\mathrm{s}, \mathrm{p}}\right)}{\sum_{\mathrm{s} \in\mathrm{S}} \mathrm{w}_{\mathrm{u}, \mathrm{s}}}\]

​ 权重 \(w_{u,s}\)是用户\(u\)和用户\(s\)的相似度,\(R_{s,p}\) 是用户\(s\)对物品\(p\)的评分

  • 推荐物品评分与此用户的所有评分的差值进行加权平均,这时候考虑用户内心的评分标准不一 \[\mathrm{P}_{\mathrm{i},\mathrm{j}}=\overline{\mathrm{R}}_{\mathrm{i}}+\frac{\sum_{\mathrm{k}=1}^{\mathrm{n}}\left(\mathrm{S}_{\mathrm{i},\mathrm{k}}\left(\mathrm{R}_{\mathrm{k},\mathrm{j}}-\overline{\mathrm{R}}_{\mathrm{k}}\right)\right)}{\sum_{\mathrm{k}=1}^{\mathrm{n}}\mathrm{S}_{\mathrm{j}, \mathrm{k}}}\] 权重 \(S_{j,k}\)是用户\(i\)和用户\(k\)的相似度,获得用户uuu对不同物品的评价预测后, 最终的推荐列表根据预测评分进行排序得到。

方法的缺点

  • 数据稀疏性:

    大型的电子商务推荐系统一般有非常多的物品,用户可能买的其中不到1%的物品,不同用户之间买的物品重叠性较低,导致算法无法找到一个用户的邻居,即偏好相似的用户

  • 算法的扩展性

    基于用户的协同过滤需要维护用户相似度矩阵以便快速的找出Topn相似用户,该矩阵的存储开销非常大,存储空间随着用户数量的增加而增加,不适合用户数据量大的情况使用

]]>
+

推荐系统

深度推荐系统 王喆

https://zhuanlan.zhihu.com/p/407871839

搜索引擎需要用户主动输入自己的意图,有些需求、意愿,是用户自己都意识不到的。将自己拥有的、用户可能喜欢的内容主动展示给用户,从而留住用户花费更多的时间与金钱。这就是推荐系统

关键词:

  1. 记忆与扩展
  2. 特征的Embedding
  3. 高维稀疏的类别特征
  4. 特征交叉结构

推广搜

推荐、广告、搜索

用户需求表达方式:推荐、搜索

信息服务对象:推搜、广告

相同点:

  • 功能架构相同:先召回在精排
  • 数据架构相同:Lambda
  • 技术栈相同,面向的功能相同,都需要高度个性化设计

推荐&搜索 \[F_{search}(t|q,u)\\F_{recommend}(t|u)\]q表示物料对用户查询的匹配程度,搜索有显式的匹配程度,但是推荐没有

搜索:查询语句与物料信息交叉

推荐:用户信息与物料信息交叉

推搜&广告

最终目标性:(优化指标)

  • 推搜是为了制造流量,给予用户最佳的使用体验
  • 广告是为了变现流

实现流程:

  • 推搜目标即刻完成,比如点击和播放
  • 广告是深层次的转化,需要用户选择并下载APP,存在较大的延时反馈问题

推荐系统简介

召回:根据喜好标签进行选择

排序:根据召回的视频进行排序

根据用户行为进行同步更新

  • 更新用户的喜好权重
  • 更新某个视频的质量权重

推荐系统功能

  1. 学习模型,并输入用户u和物料t,输入模型中返回score
  2. 返回最匹配的score最高的物料t

存在的问题是数据量太大且时间开销太大无法满足实时要求

RS功能架构

召回

  • 离线计算+在线缓存(缺少用户信息和物料信息的交叉)
  • 多路召回弥补精度的不足
  • 召回和排序存在很大的差别

精排

  • 提升预测精度
  • 让物料信息和用户信息充分交叉,使用复杂的交叉结构和特征来提升精度

粗排

  • 不用物料信息和用户信息充分交叉
  • 仍然依赖离线计算+在线缓存

重排

  • 调整精排的结果顺序,将相似内容打散,保证看到的推荐结果更加丰富多样

RS数据架构

存在的问题:

  1. 统计窗口长导致涉及的计算量会比较大,线上预测时间紧张
  2. 回溯历史存在Hadoop分布式文件系统(HDFS)中存的是冷数据,但是仍然有一批热数据没有落HDFS中,从而导致数据没办法全面接收

Lambda架构

image-20240109110904180

离线层

  • 冷数据请求的计算
  • 启动小时级的定时任务,每个小时都向前回溯一周的用户行为日志,统计时间窗口中每个视频的曝光数和点击数

近线层

  • KV数据库存储,缓存加速访问
  • 提高查询速度

在线层

  • 流式计算框架,对接用户的行为数据存储在Redis数据库汇总快速查询

RS特征工程

必要性:

  • DNN万能函数模拟器并不可靠,输入数据未经处理会影响DNN性能的发挥
  • DNN的自动化特征工程对于大量的数据存在耗时等问题

特征提取

物料特征

  • 物料自身属性信息,Item ID
  • 物料的类别与标签,通过DL的方式学习到物料的静态画像,可以是List等概率存储
  • 内容的Embedding,中间层输出作为输入以丰富物料特征
  • 物料的动态画像,如过去6小时的CTR、过去1天的平均播放时长
  • 反向打标签,选择物料->用户标签->丰富物料标签

用户特征

  • 用户的静态画像:个人信息User ID
  • 用户的动态画像:历史行为中提取出兴趣爱好,反映用户的兴趣迁移

交叉特征

  • 笛卡尔积,交叉后的特征喂入模型

image-20240109144352476

  • 内积交叉:某个维度上的讨论(用户在维度上的兴趣和物料在维度上的属性向量之间的相似性)

偏差特征

  • 无法做到绝对公平,没点击不代表不喜欢,点击的未必喜欢,偏差Bias

  • AboveClick:只有在点击物料上方的未点击的物料才被当作负样本,没被点击的且不在点击上方的就不放进训练的集合中

数据特征处理

处理缺失值

  • 训练模型来预测缺失值

标准化数据

  • 目的:将不同量纲、不同取值范围的数值压缩到一个数值范围内
  • 标准化z-score方法:\(x^*=(x-\mu)/(\sigma)\)分别是均值和标准差

数据平滑与消偏

  • 目标:克服小样本的负面影响,提高计算结果的置信区间

分桶离散化

类别特征处理

推荐系统的特征空间:高维、稀疏

增强类别特征表达

  • Embedding自动学习并拓展内涵,学习隐语义拓展单个特征的内涵
  • 多特征交叉,选择用户多个不同的特征进行交叉一次来增强特征的表达能力

类别特征的高维性

  • Parameter Server架构
  • 分散了参数存储检索的压力,降低了带宽资源与时间开销

类别特征的稀疏

  • 自适应调整学习率,自适应调整正则系数

特征如何表征

  • 建立字符串到数字的映射表,缺点在于需要更新维护且对于大规模而言比较难以负担

  • 采用特征哈希Feature Hashing

RS的Embedding

为什么要Embedding

对于推荐系统而言:记忆+拓展

记忆能够处理80%的需求,但是个性化太弱(评分)

拓展将细粒度变成粗粒度,借助深度学习的Embedding

共享&独占Embedding

共享Embedding

  • 指需要喂入模型的多个地方
  • 有利于缓解特征稀疏、数据不足所导致的问题
  • 无法很好满足推荐系统的多目标性

独占Embedding

  • 不同的任务使用不同的Embedding的方法

  • 如果在特征交叉的时候使用的是共享Embedding,会产生相互干扰的情况(不同需求要求不同Embedding)

  • FFM:每个特征在与不同特征交叉的时候,根据对方特征所属的Field要使用不同的Embedding

  • CAN:既要使用不同的Embedding进行特征交叉,但是又不想要太多的参数导致训练的难度增加

Parameter Server加速器

传统利用Hadoop/Spark的分布式训练方法忽略了高维稀疏特征空间直接让master处理会参数量很大

使用PS架构的好处:

一个Server只负责处理海量参数中的一部分

PS中的并发策略

同步并发

异步并发

无须等待,不存在短板效应,有明显的速度优势

梯度失效的问题:

半同步半异步

允许Worker节点在一定迭代轮数中保持异步,但是如果最快节点和最慢节点的迭代步数之差已经超过了允许的最大值,那么所有的参数都需要停下来进行一次参数的同步

精排

特征交叉方法

传统方法:FTRL

FM:引入了二阶特征交叉(手动二阶特征交叉)

Wide&Deep:兼顾记忆与扩展

DeepFM :融合二阶交叉(实现了自动二阶特征交叉)

DCN:能够指定任意显式交叉

Autolnt:基于Transformer作特征交叉

用户行为序列建模

本质:将用户行为序列提炼并压缩成用户兴趣的Embedding

DIN:利用Attention,将当前的物料t作为用户历史序列的Query

双层Attention行为序列:第一层Attention历史行为序列内部的依赖关系;第二层是当前物料和历史行为序列

SIM:将“软过滤”变成“硬过滤”,相当于从长序列选择短序列进行DIN

召回

向量化召回统一建模框架

定义:将召回问题建模成向量空间中的近邻搜索问题

分类:两类实体QT,可以是物料-用户、用户-用户、物料-物料

方法流程:

  1. 训练一个模型,并将QT映射到同一个向量空间
  2. 构建起索引向量数据库
  3. 对于传入的Q实例先进行Embedding 再进行近邻搜索

建模方法:

  • 如何定义正样本
  • 如何定义负样本
  • 如何将q和t映射成Embedding
  • 如何定义优化目标和损失函数
]]>
@@ -35,7 +35,7 @@ /2024/01/01/leetcode/ -

数组

二分查找

哈希表

链表

二叉树

回溯算法

贪心算法

动态规划

]]>
+

语言细节

vector的长度:

  • C++:nums.size()
  • Python:len(nums)
  • GO:len(nums)

for循环:

  • C++:条件小括号+循环体中括号
  • Python:冒号且不需要小括号包条件
  • GO:循环体中括号,条件按照C++写但是不需要小括号

数组

二分查找

题目描述

链接:https://leetcode.cn/problems/binary-search/description/

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回-1。

示例 1:

1
2
3
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

1
2
3
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

思路

题目表示的是有序数组,而且题目没有重复元素。在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1 # 定义target在左闭右闭的区间里,[left, right]

while left <= right:
middle = left + (right - left) // 2

if nums[middle] > target:
right = middle - 1 # target在左区间,所以[left, middle - 1]
elif nums[middle] < target:
left = middle + 1 # target在右区间,所以[middle + 1, right]
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值

注意这里给出的题解法:当left <= right的时候,以下的条件中全部都不取到等号nums[middle] > target nums[middle] < target

需要注意的是:right=nums.size()-1

C++版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while(left<=right)
{
// int middle = (left+right)/2; 这样写会溢出
int middle = left + ((right - left) / 2);
if(nums[middle]>target)
{
right = middle-1;
}
else if(nums[middle]<target)
{
left = middle+1;
}
else{
return middle;
}
}
return -1;
}
};

Go版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func search(nums []int, target int) int {
right:=len(nums)-1
left:=0
for left<=right{
middle:= left+(right-left)/2
if nums[middle]<target{
left = middle+1
}else if nums[middle]>target{
right = middle-1
}else{
return middle
}
}
return -1
}

移除元素

https://leetcode.cn/problems/remove-element/description/

题目描述

示例 1:

1
2
3
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

1
2
3
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

思路

双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

定义快慢指针

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • 慢指针:指向更新 新数组下标的位置

双指针题解

C++版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowindex=0;
for(int fastindex = 0; fastindex<nums.size();fastindex++)
{
if(val!=nums[fastindex]){
nums[slowindex] = nums[fastindex];
slowindex++;
}
}
return slowindex;
}
};

python版本

1
2
3
4
5
6
7
8
9
10
class Solution(object):
def removeElement(self, nums, val):
slowindex=0
fastindex=0
while fastindex<len(nums):
if val!=nums[fastindex]:
nums[slowindex]=nums[fastindex]
slowindex = slowindex+1
fastindex+=1
return slowindex

GO版本:

1
2
3
4
5
6
7
8
9
10
func removeElement(nums []int, val int) int {
slow:=0
for i:=0;i<len(nums);i++{
if nums[i]!=val{
nums[slow]=nums[i]
slow++
}
}
return slow
}

哈希表

链表

二叉树

回溯算法

贪心算法

动态规划

]]>
diff --git a/search.xml b/search.xml index e1c27ec..42bb3db 100644 --- a/search.xml +++ b/search.xml @@ -2449,8 +2449,63 @@ alt="编译错误" /> 【算法题】LeetCode算法汇总 /2024/01/01/leetcode/ +

语言细节

+

vector的长度:

+
    +
  • C++:nums.size()
  • +
  • Python:len(nums)
  • +
  • GO:len(nums)
  • +
+

for循环:

+
    +
  • C++:条件小括号+循环体中括号
  • +
  • Python:冒号且不需要小括号包条件
  • +
  • GO:循环体中括号,条件按照C++写但是不需要小括号
  • +

数组

二分查找

+

题目描述

+

链接:https://leetcode.cn/problems/binary-search/description/

+

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target +,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 +-1。

+

示例 1:

+
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
+

示例 2:

+
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
+

思路

+

题目表示的是有序数组,而且题目没有重复元素。在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则

+
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1 # 定义target在左闭右闭的区间里,[left, right]

while left <= right:
middle = left + (right - left) // 2

if nums[middle] > target:
right = middle - 1 # target在左区间,所以[left, middle - 1]
elif nums[middle] < target:
left = middle + 1 # target在右区间,所以[middle + 1, right]
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值
+

注意这里给出的题解法:当 +left <= right的时候,以下的条件中全部都不取到等号 +nums[middle] > target nums[middle] < target

+

需要注意的是:right=nums.size()-1

+

C++版本

+
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while(left<=right)
{
// int middle = (left+right)/2; 这样写会溢出
int middle = left + ((right - left) / 2);
if(nums[middle]>target)
{
right = middle-1;
}
else if(nums[middle]<target)
{
left = middle+1;
}
else{
return middle;
}
}
return -1;
}
};
+

Go版本

+
func search(nums []int, target int) int {
right:=len(nums)-1
left:=0
for left<=right{
middle:= left+(right-left)/2
if nums[middle]<target{
left = middle+1
}else if nums[middle]>target{
right = middle-1
}else{
return middle
}
}
return -1
}
+

移除元素

+

https://leetcode.cn/problems/remove-element/description/

+

题目描述

+

示例 1:

+
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
+

示例 2:

+
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
+

思路

+

双指针法(快慢指针法): +通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

+

定义快慢指针

+
    +
  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • +
  • 慢指针:指向更新 新数组下标的位置
  • +
+

双指针题解

+

C++版本

+
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowindex=0;
for(int fastindex = 0; fastindex<nums.size();fastindex++)
{
if(val!=nums[fastindex]){
nums[slowindex] = nums[fastindex];
slowindex++;
}
}
return slowindex;
}
};
+

python版本

+
class Solution(object):
def removeElement(self, nums, val):
slowindex=0
fastindex=0
while fastindex<len(nums):
if val!=nums[fastindex]:
nums[slowindex]=nums[fastindex]
slowindex = slowindex+1
fastindex+=1
return slowindex
+

GO版本:

+
func removeElement(nums []int, val int) int {
slow:=0
for i:=0;i<len(nums);i++{
if nums[i]!=val{
nums[slow]=nums[i]
slow++
}
}
return slow
}

哈希表

链表

二叉树

@@ -2471,73 +2526,301 @@ alt="编译错误" /> /2024/01/02/rec/

推荐系统

+

深度推荐系统 王喆

+

https://zhuanlan.zhihu.com/p/407871839

+

搜索引擎需要用户主动输入自己的意图,有些需求、意愿,是用户自己都意识不到的。将自己拥有的、用户可能喜欢的内容主动展示给用户,从而留住用户花费更多的时间与金钱。这就是推荐系统

+

关键词:

+
    +
  1. 记忆与扩展
  2. +
  3. 特征的Embedding
  4. +
  5. 高维稀疏的类别特征
  6. +
  7. 特征交叉结构
  8. +
+

推广搜

+

推荐、广告、搜索

+

用户需求表达方式:推荐、搜索

+

信息服务对象:推搜、广告

+

相同点:

+
    +
  • 功能架构相同:先召回在精排
  • +
  • 数据架构相同:Lambda
  • +
  • 技术栈相同,面向的功能相同,都需要高度个性化设计
  • +
+

推荐&搜索 \[ +F_{search}(t|q,u)\\ +F_{recommend}(t|u) +\] +q表示物料对用户查询的匹配程度,搜索有显式的匹配程度,但是推荐没有

+

搜索:查询语句与物料信息交叉

+

推荐:用户信息与物料信息交叉

+

推搜&广告

+

最终目标性:(优化指标)

+
    +
  • 推搜是为了制造流量,给予用户最佳的使用体验
  • +
  • 广告是为了变现流
  • +
+

实现流程:

    -
  • 搭建音视频系统
  • -
  • 数据收集对用户进行建模,对视频建模
  • -
  • 内嵌推荐系统算法
  • +
  • 推搜目标即刻完成,比如点击和播放
  • +
  • 广告是深层次的转化,需要用户选择并下载APP,存在较大的延时反馈问题
-

协同过滤算法

-

经典算法

-

基本思想:根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品

-

分类:

+

推荐系统简介

+

召回:根据喜好标签进行选择

+

排序:根据召回的视频进行排序

+
+ + +
+

根据用户行为进行同步更新

    -
  • 基于用户的协同过滤算法(UserCF): -给用户推荐和他兴趣相似的其他用户喜欢的产品
  • -
  • 基于物品的协同过滤算法(ItemCF): -给用户推荐和他之前喜欢的物品相似的物品、
  • +
  • 更新用户的喜好权重
  • +
  • 更新某个视频的质量权重
-

基于用户的协同过滤算法

-

计算向量之间的相似性

+

推荐系统功能

    -
  1. 杰卡德相似性系数

    -

    衡量两个集合的相似度一种指标。 -两个集合A和B的交集元素在A,B的并集中所占的比例,称为两个集合的杰卡德相似系数

  2. -
  3. 余弦相似度

    -

    用户向量i和j之间的向量夹角的大小, 夹角越小, 说明相似度越大, -两个用户越相似

    -
    from sklearn.metrics.pairwise import cosine_similarity
    i = [1, 0, 0, 0]
    j = [1, 0.5, 0.5, 0]
    consine_similarity([a, b])
  4. -
  5. 皮尔逊相关系数

    -

    相比余弦相似度, -皮尔逊相关系数通过使用用户平均分对个独立评分进行修正, -减少了用户评分偏置的影响(每个向量先进行处理,减去了向量之间的均值)

    -
    from scipy.stats import pearsonr
    i = [1, 0, 0, 0]
    j = [1, 0.5, 0.5, 0]
    pearsonr(i, j)
  6. +
  7. 学习模型,并输入用户u和物料t,输入模型中返回score
  8. +
  9. 返回最匹配的score最高的物料t
-

最终结果预测

+

存在的问题是数据量太大且时间开销太大无法满足实时要求

+

RS功能架构

+
+ + +
+

召回

    -
  • 利用用户相似度和相似用户的评价加权平均获得用户的评价预测
  • +
  • 离线计算+在线缓存(缺少用户信息和物料信息的交叉)
  • +
  • 多路召回弥补精度的不足
  • +
  • 召回和排序存在很大的差别
-

\[ -\mathrm{R}_{\mathrm{u}, \mathrm{p}}=\frac{\sum_{\mathrm{s} \in -\mathrm{S}}\left(\mathrm{w}_{\mathrm{u}, \mathrm{s}} \cdot -\mathrm{R}_{\mathrm{s}, \mathrm{p}}\right)}{\sum_{\mathrm{s} \in -\mathrm{S}} \mathrm{w}_{\mathrm{u}, \mathrm{s}}} -\]

-

​ 权重 \(w_{u,s}\)是用户\(u\)和用户\(s\)的相似度,\(R_{s,p}\) 是用户\(s\)对物品\(p\)的评分

-
    -
  • 推荐物品评分与此用户的所有评分的差值进行加权平均, -这时候考虑用户内心的评分标准不一 \[ -\mathrm{P}_{\mathrm{i}, -\mathrm{j}}=\overline{\mathrm{R}}_{\mathrm{i}}+\frac{\sum_{\mathrm{k}=1}^{\mathrm{n}}\left(\mathrm{S}_{\mathrm{i}, -\mathrm{k}}\left(\mathrm{R}_{\mathrm{k}, -\mathrm{j}}-\overline{\mathrm{R}}_{\mathrm{k}}\right)\right)}{\sum_{\mathrm{k}=1}^{\mathrm{n}} -\mathrm{S}_{\mathrm{j}, \mathrm{k}}} -\] 权重 \(S_{j,k}\)是用户\(i\)和用户\(k\)的相似度,获得用户u -uu对不同物品的评价预测后, 最终的推荐列表根据预测评分进行排序得到。
  • -
-

方法的缺点

-
    -
  • 数据稀疏性:

    -

    大型的电子商务推荐系统一般有非常多的物品,用户可能买的其中不到1%的物品,不同用户之间买的物品重叠性较低,导致算法无法找到一个用户的邻居,即偏好相似的用户

  • -
  • 算法的扩展性

    -

    基于用户的协同过滤需要维护用户相似度矩阵以便快速的找出Topn相似用户, -该矩阵的存储开销非常大,存储空间随着用户数量的增加而增加,不适合用户数据量大的情况使用

  • +

    精排

    +
      +
    • 提升预测精度
    • +
    • 让物料信息和用户信息充分交叉,使用复杂的交叉结构和特征来提升精度
    • +
    +

    粗排

    +
      +
    • 不用物料信息和用户信息充分交叉
    • +
    • 仍然依赖离线计算+在线缓存
    • +
    +

    重排

    +
      +
    • 调整精排的结果顺序,将相似内容打散,保证看到的推荐结果更加丰富多样
    • +
    +

    RS数据架构

    +

    存在的问题:

    +
      +
    1. 统计窗口长导致涉及的计算量会比较大,线上预测时间紧张
    2. +
    3. 回溯历史存在Hadoop分布式文件系统(HDFS)中存的是冷数据,但是仍然有一批热数据没有落HDFS中,从而导致数据没办法全面接收
    4. +
    +

    Lambda架构

    +
    + + +
    +

    image-20240109110904180

    +

    离线层

    +
      +
    • 冷数据请求的计算
    • +
    • 启动小时级的定时任务,每个小时都向前回溯一周的用户行为日志,统计时间窗口中每个视频的曝光数和点击数
    • +
    +

    近线层

    +
      +
    • KV数据库存储,缓存加速访问
    • +
    • 提高查询速度
    • +
    +

    在线层

    +
      +
    • 流式计算框架,对接用户的行为数据存储在Redis数据库汇总快速查询
    • +
    +

    RS特征工程

    +

    必要性:

    +
      +
    • DNN万能函数模拟器并不可靠,输入数据未经处理会影响DNN性能的发挥
    • +
    • DNN的自动化特征工程对于大量的数据存在耗时等问题
    • +
    +

    特征提取

    +

    物料特征

    +
      +
    • 物料自身属性信息,Item ID
    • +
    • 物料的类别与标签,通过DL的方式学习到物料的静态画像,可以是List等概率存储
    • +
    • 内容的Embedding,中间层输出作为输入以丰富物料特征
    • +
    • 物料的动态画像,如过去6小时的CTR、过去1天的平均播放时长
    • +
    • 反向打标签,选择物料->用户标签->丰富物料标签
    • +
    +

    用户特征

    +
      +
    • 用户的静态画像:个人信息User ID
    • +
    • 用户的动态画像:历史行为中提取出兴趣爱好,反映用户的兴趣迁移
    • +
    +

    交叉特征

    +
      +
    • 笛卡尔积,交叉后的特征喂入模型
    • +
    +

    image-20240109144352476

    +
      +
    • 内积交叉:某个维度上的讨论(用户在维度上的兴趣和物料在维度上的属性向量之间的相似性)
    • +
    +

    偏差特征

    +
      +
    • 无法做到绝对公平,没点击不代表不喜欢,点击的未必喜欢,偏差Bias

      +
      + + +
    • +
    • Above +Click:只有在点击物料上方的未点击的物料才被当作负样本,没被点击的且不在点击上方的就不放进训练的集合中

    • +
    +

    数据特征处理

    +

    处理缺失值

    +
      +
    • 训练模型来预测缺失值
    • +
    +

    标准化数据

    +
      +
    • 目的:将不同量纲、不同取值范围的数值压缩到一个数值范围内
    • +
    • 标准化z-score方法:\(x^*=(x-\mu)/(\sigma)\) +分别是均值和标准差
    • +
    +

    数据平滑与消偏

    +
      +
    • 目标:克服小样本的负面影响,提高计算结果的置信区间

      +
      + + +
    • +
    +

    分桶离散化

    +

    类别特征处理

    +

    推荐系统的特征空间:高维、稀疏

    +

    增强类别特征表达

    +
      +
    • Embedding自动学习并拓展内涵,学习隐语义拓展单个特征的内涵
    • +
    • 多特征交叉,选择用户多个不同的特征进行交叉一次来增强特征的表达能力
    • +
    +

    类别特征的高维性

    +
      +
    • Parameter Server架构
    • +
    • 分散了参数存储检索的压力,降低了带宽资源与时间开销
    • +
    +

    类别特征的稀疏

    +
      +
    • 自适应调整学习率,自适应调整正则系数
    • +
    +

    特征如何表征

    +
      +
    • 建立字符串到数字的映射表,缺点在于需要更新维护且对于大规模而言比较难以负担

      +
      + + +
    • +
    • 采用特征哈希Feature Hashing

    • +
    +
    + + +
    +

    RS的Embedding

    +

    为什么要Embedding

    +

    对于推荐系统而言:记忆+拓展

    +

    记忆能够处理80%的需求,但是个性化太弱(评分)

    +

    拓展将细粒度变成粗粒度,借助深度学习的Embedding

    +

    共享&独占Embedding

    +

    共享Embedding

    +
      +
    • 指需要喂入模型的多个地方
    • +
    • 有利于缓解特征稀疏、数据不足所导致的问题
    • +
    • 无法很好满足推荐系统的多目标性
    • +
    +

    独占Embedding

    +
      +
    • 不同的任务使用不同的Embedding的方法

    • +
    • 如果在特征交叉的时候使用的是共享Embedding,会产生相互干扰的情况(不同需求要求不同Embedding)

    • +
    • FFM:每个特征在与不同特征交叉的时候,根据对方特征所属的Field要使用不同的Embedding

    • +
    • CAN:既要使用不同的Embedding进行特征交叉,但是又不想要太多的参数导致训练的难度增加

      +
      + + +
    • +
    +

    Parameter Server加速器

    +

    传统利用Hadoop/Spark的分布式训练方法忽略了高维稀疏特征空间 +直接让master处理会参数量很大

    +

    使用PS架构的好处:

    +
    + + +
    +
    + + +
    +

    一个Server只负责处理海量参数中的一部分

    +

    PS中的并发策略

    +

    同步并发

    +
    + + +
    +

    异步并发

    +

    无须等待,不存在短板效应,有明显的速度优势

    +

    梯度失效的问题:

    +

    半同步半异步

    +

    允许Worker节点在一定迭代轮数中保持异步,但是如果最快节点和最慢节点的迭代步数之差已经超过了允许的最大值,那么所有的参数都需要停下来进行一次参数的同步

    +

    精排

    +

    特征交叉方法

    +

    传统方法:FTRL

    +

    FM:引入了二阶特征交叉(手动二阶特征交叉)

    +

    Wide&Deep:兼顾记忆与扩展

    +

    DeepFM :融合二阶交叉(实现了自动二阶特征交叉)

    +

    DCN:能够指定任意显式交叉

    +

    Autolnt:基于Transformer作特征交叉

    +

    用户行为序列建模

    +

    本质:将用户行为序列提炼并压缩成用户兴趣的Embedding

    +

    DIN:利用Attention,将当前的物料t作为用户历史序列的Query

    +

    双层Attention行为序列:第一层Attention历史行为序列内部的依赖关系;第二层是当前物料和历史行为序列

    +

    SIM:将“软过滤”变成“硬过滤”,相当于从长序列选择短序列进行DIN

    +

    召回

    +

    向量化召回统一建模框架

    +

    定义:将召回问题建模成向量空间中的近邻搜索问题

    +

    分类:两类实体QT,可以是物料-用户、用户-用户、物料-物料

    +

    方法流程:

    +
      +
    1. 训练一个模型,并将QT映射到同一个向量空间
    2. +
    3. 构建起索引向量数据库
    4. +
    5. 对于传入的Q实例先进行Embedding 再进行近邻搜索
    6. +
    +

    建模方法:

    +
      +
    • 如何定义正样本
    • +
    • 如何定义负样本
    • +
    • 如何将q和t映射成Embedding
    • +
    • 如何定义优化目标和损失函数
    ]]>