YouTube中Weight Logistic Regression 的介绍和使用

目录

1. Weight LR的原理理解

在常见的LR模型当中,假设数据服从伯努利分布,当某件事情发生, 认为其概率为p,那么这件事情不发生的概率为1-p,对应的几率比(odd)为:

\[odds = \frac{p}{1-p}\]

几率比(odds):是指一个事件发生的概率与不发生概率的比值。

对几率比求对数,并且将几率比作为输入特征的线性表达式,那么可以得到:

\[logit(\frac{p}{1-p}) = w^T x\]

这个时候得到:

\[Odds = e^{w^Tx}\]

这个时候我们根据概率p,再去推导出$logit$函数的反函数,得到的就是sigmoid函数:

\[\phi(x) = \frac{1}{1+e^{-w^T x}}\]

2. YouTube DNN中的使用

在短视频的CTR预估当中,点击发生的概率就是发生点击的视频个数/总曝光的视频个数。 假设发生点击的视频个数为M,公共曝光的视频个数为N,则$p$:

\[p=\frac{M}{N}\]

这个时候可以得到:

\[Odds = \frac{\frac{M}{N}}{\frac{N-M}{N}} = \frac{\frac{M}{N}}{1-\frac{M}{N}}\]

在这种情况下,我们对正样本添加权重$w_i$,那么上述公式的表述如下:

\[Odds(i) = \frac{w_i \times \frac{M}{N}}{1-w_i \times \frac{M}{N}} = \frac{w_i p}{1-w_ip}\]

在YouTube DNN的推荐中,关键的地方在于正样本权重的选择, 在YouTube DNN中使用了播放时长$T_i$作为权重。 由于在视频推荐场景中,用户打开一个视频的概率$p$通常是一个很小的值, 因此,上面的公式可以继续简化:

\[Odds(i) = w_ip = t_ip\]

由于p就是用户打开该视频的概率,$T_i$是观看的时长, 因此$T_i \times p$就是用户观看某视频的期望时长。 这里有一个好处就是,在CTR进行Serving的时候, 我们只关注相对位置,不关注绝对值,这种情况下只需要计算$Odds$就可以啦! 也就是只是需要计算$e^{W^T x}$, 这样就转化成了根据观看某视频的期望时长进行排序。

3. Weight LR的应用实现

针对YouTube DNN中的weight LR,可以通过两种方法实现:

  1. up-sampling方式

通过up-sampling根据视频的播放时长增加播放时长长的正样本的数量, 提高训练数据中正样本的权重。

  1. magnify weight方式

up-sampling通过改变样本数据的分布, 来提高播放时间较长的视频正样本在训练中所占的比重, 从而起到weight LR应该达到的效果。

除了使用up-sampling的方式之外, 还可以在计算损失的时候引入权重,计算交叉熵。

在TensorFlow中的tf.nn.weighted_cross_entropy_with_logitsAPI中实现了 加权交叉熵的计算。

TensorFlow提供的接口如下:

tf.nn.weighted_cross_entropy_with_logits(
    labels, logits, pos_weight, name=None
)
  • labels: 是样本对应的标签
  • logits: 是样本预测概率的对数比
  • pos_weight: 表示正样本的权重
  • name: 操作名称

在常见的二分类问题中,原本交叉熵的计算方式如下:

\[loss = \frac{1}{N} \sum_{i} -[y_i \cdot log(p_i) + (1-y_i) \cdot (1-p_i)]\]

在Weight LR中二分类问题loss的计算如下:

\[loss = \frac{1}{N} \sum_{i} -[y_i \cdot log(p_i) \cdot w_i + (1-y_i) \cdot (1-p_i)]\]

这里值得注意的一点是,在交叉熵损失里面只是对正样本最weight加权, 不对负样本做加权。

[1] 非均衡数据处理–如何学习?

[2] tf.nn.weighted_cross_entropy_with_logits

[3] 使用带权重交叉熵损失函数定向提升模型的召回率

[4] weighted—-LR的理解与推广

[5] Notes On Youtube Dnn

[6] 分类机器学习中,某一标签占比太大(标签稀疏),如何学习?

模型选择值AIC&BIC

目录

1. TensorFlow Serving的基本结构

Tensorflow Serving是一个针对生产环境设计的灵活、 高性能的机器学习模型服务系统。 TensorFlow Serving使得我们能够在保持相同服务架构和API 的情况下很容易的部署新算法和实验。

TensorFlow Serving针对TensorFlow模型提供开箱即用的集成, 同时又能很容易地扩展到其他类型的模型服务。

1.1 核心概念

为了更好的理解TensorFlow Serving,我们需要了解以下的一些基本概念。

1. Servables

Servables是TensorFlow Serving中的核心抽象, 是客户端用来执行计算(例如:一个lookup操作或者是一个inference)的底层对象。。

Servable具有非常灵活的大小和粒度,可以包含任何东西。 Servable可以是一个lookup table操作的切片, 也可以包含单独一个模型推断的tuple。 为了确保灵活性和未来的可扩展性,Servables可以是任何一种类型和接口。 例如:

  • streaming results
  • experimental APIs
  • asynchronous models of operation

Servables不管理自身的生命周期。

典型的Servables包含以下几种:

  • 一个TensorFlow SavedModelBundle(Tensorflow::Session)

  • 一个用于embedding或者vocabulary查询的lookup table

2. Servable Version

在单个服务的实例生命周期中, TensorFlow Serving可以处理一个或者多个版本的Servable。 也就是说在这段时间内可以加载新算法、配置、权重、其他数据。 Servable Version使得TensorFlow Serving可以并行加载多个模型, 支持渐进式回滚和实验。在服务的时候,client可以请求最新版本 或者通过模型的id指定特定的模型版本。

3. Servable Streams

一个Servable Streams是一个Servable的version序列,通过递增的版本号进行排序。

4. Models

TensorFlow Serving中的一个Models包含一个Servable或者多个Servable。 一个机器学习模型可以包含一个或者多个算法(包含学习到的权重)以及lookup或者 embedding tables。

在TensorFlow可以将一个复合模型表示成以下两种方式之一:

  • 多个独立的Servable
  • 单个复合Servable

一个Servable也可以对应一个模型的一部分。 例如,一个大的lookup table可以在多个TensorFlow Serving实例共享。

5. Loaders

Loaders管理着Servable的生命周期。 Loader API使得公共结构独立于特定的机器学习算法,数据或者产品用例。 例如Loaders使用标准化API来加载或者卸载一个Servable。

6. Sources

Sources是用来查找和提供Servables的插件。每个Source提供零个或者多个 Servable Streams。对于每一个Servable Stream, 一个Source会为每个Version提供一个Loader实例, 这个Loader使得Servables Stream可以被加载。 (一个Source实际上会使用零或多个SourceAdapter链接到一起, 链上的最后一个item会触发(emits)该Loaders。)

Tensorflow Serving的Source接口能够从存储系统中查找并发现Servables。 TensorFlow Serving中包含了Source公共索引的实现。 例如,Source可以访问RPC等机制并且可以轮询文件系统。

Sources可以维持跨多个Servable和跨多个Version的状态。 这对于使用delta(diff)来在 versions间进行更新的Servables很有作用。 这对于不同版本之间使用增量的模型更新的Servables来说非常有用。

7. Aspired Versions

Aspired Versions表示需要被加载和准备的Servable Version集合。 Source会与该Servable Version集合进行通讯,一次一个Servable Stream。 当一个Source给出一个aspired versions的新列表到Manager时, 它会为该Servables stream取代之前的列表。该Manager会unload任何之前 已加载的versions,使它们不再出现在该列表中。

8. Managers

Managers处理了Servable的完整生命周期,包括:

  • loadding Servable
  • serving Servable
  • unloading Servable Managers会监听Source,并跟踪所有的versions。Mananger会尝试满足Source的请求, 但如果需要的resources不提供的话,也可以拒绝加载一个aspired version。 Managers也可以延期一个”unload”操作。 例如,一个Manager可以等待unload直到一个更新的version完成loading, 基于一个策略(Policy)来保证所有时间内至少一个version被加载。

TensorFlow Serving Manager提供一个单一的接口——GetServableHandle()——给client来访问已加载的Servable实现。

9. Core

TensorFlow Serving Core(通过标准TensorFlow Serving API)管理着一下的Servables:

  • lifecycle
  • metrics TensorFlow Serving Core将Servables和loaders看成是透明的对象。

1.2 Servable的生命周期

图1    servable的生命周期

通俗地讲:

  • source会为Servable Version创建一个Loaders。
  • Loaders会作为Aspired Versions被发送给Manager,manager会加载和提供服务给客户端请求。

更详细的:

  1. 一个Source插件会为一个特定的Version创建一个Loader。 该Loaders包含了一个加载Servable时所需的任何元数据。
  2. Source会使用一个callback来通知该Aspired Version的Manager。
  3. 该Manager应用该配置过的Version Policy来决定下一个要采用的动作, 它会被unload一个之前已经加载过的Version,或者加载一个新的Version。
  4. 如果该Manager决定它是否安全,它会给Loader所需的资源, 并告诉该Loader来加载新的Version。
  5. 一个Client会向最新Version的模型请求一个Handle, 接着Dynamic Manager返回一个handle给Servables的新version。

1.3 Extensibility

Tensorflow serving提供了一些扩展点,使你可以添加新功能。

1. Version Policy

Version Policy可以指定version序列,在单个servable stream中加载或卸载。

tensorflow serving包含了两个策略(policy)来适应大多数已知用例。 分别是:Availability Preserving Policy(避免没有version加载的情况; 在卸载一个老version前通常会加载一个新的version), Resource Preserving Policy(避免两个version同时被加载,这需要两倍的资源; 会在加载一个新version前卸载老version)。对于tensorflow serving的简单用法, 一个模型的serving能力很重要,资源消耗要低,Availability Preserving Policy会确保在新version加载前卸载老version。对于TensorFlow Serving的复杂用法, 例如跨多个server实例管理version,Resource Preserving Policy需要最少的资源(对于加载新version无需额外buffer)

2. Source

新的资源(Source)可以支持新的文件系统、云供应商、算法后端。 TensorFlow Serving提供了一些公共构建块来很方便地创建新的资源。 例如,TensorFlow Serving包含了围绕一个简单资源polling行为的封装工具类。 对于指定的算法和数据宿主Servables,Source与Loaders更紧密相关。

3. Loaders

Loaders是用于添加算法和数据后端的扩展点。Tensorflow就是这样的一个算法后端。 例如,你实现了一个新的Loader来进行加载、访问、卸载一个新的servable类型的机器学习模型实例。 我们会为lookup tables和额外的算法创建Loaders。

4. Batcher 会将多个请求打包(Batching)成单个请求,可以极大减小执行inference的开销, 特别是像GPU这样的硬件加速存在的时候。Tensorflow Serving包含了一个请求打包组件 (request batching widget),使得客户端(clients) 可以很容易地将特定类型的inferences跨请求进行打包成一个batch, 使得算法系统可以更高效地处理。详见Batching Guide。

[1] 使用 TensorFlow Serving 和 Flask 部署 Keras 模型

[2] Google TFX 文档

基于TF Serving部署TensorFlow模型

目录

1. TensorFlow Serving的基本结构

Tensorflow Serving是一个针对生产环境设计的灵活、 高性能的机器学习模型服务系统。 TensorFlow Serving使得我们能够在保持相同服务架构和API 的情况下很容易的部署新算法和实验。

TensorFlow Serving针对TensorFlow模型提供开箱即用的集成, 同时又能很容易地扩展到其他类型的模型服务。

1.1 核心概念

为了更好的理解TensorFlow Serving,我们需要了解以下的一些基本概念。

1. Servables

Servables是TensorFlow Serving中的核心抽象, 是客户端用来执行计算(例如:一个lookup操作或者是一个inference)的底层对象。。

Servable具有非常灵活的大小和粒度,可以包含任何东西。 Servable可以是一个lookup table操作的切片, 也可以包含单独一个模型推断的tuple。 为了确保灵活性和未来的可扩展性,Servables可以是任何一种类型和接口。 例如:

  • streaming results
  • experimental APIs
  • asynchronous models of operation

Servables不管理自身的生命周期。

典型的Servables包含以下几种:

  • 一个TensorFlow SavedModelBundle(Tensorflow::Session)

  • 一个用于embedding或者vocabulary查询的lookup table

2. Servable Version

在单个服务的实例生命周期中, TensorFlow Serving可以处理一个或者多个版本的Servable。 也就是说在这段时间内可以加载新算法、配置、权重、其他数据。 Servable Version使得TensorFlow Serving可以并行加载多个模型, 支持渐进式回滚和实验。在服务的时候,client可以请求最新版本 或者通过模型的id指定特定的模型版本。

3. Servable Streams

一个Servable Streams是一个Servable的version序列,通过递增的版本号进行排序。

4. Models

TensorFlow Serving中的一个Models包含一个Servable或者多个Servable。 一个机器学习模型可以包含一个或者多个算法(包含学习到的权重)以及lookup或者 embedding tables。

在TensorFlow可以将一个复合模型表示成以下两种方式之一:

  • 多个独立的Servable
  • 单个复合Servable

一个Servable也可以对应一个模型的一部分。 例如,一个大的lookup table可以在多个TensorFlow Serving实例共享。

5. Loaders

Loaders管理着Servable的生命周期。 Loader API使得公共结构独立于特定的机器学习算法,数据或者产品用例。 例如Loaders使用标准化API来加载或者卸载一个Servable。

6. Sources

Sources是用来查找和提供Servables的插件。每个Source提供零个或者多个 Servable Streams。对于每一个Servable Stream, 一个Source会为每个Version提供一个Loader实例, 这个Loader使得Servables Stream可以被加载。 (一个Source实际上会使用零或多个SourceAdapter链接到一起, 链上的最后一个item会触发(emits)该Loaders。)

Tensorflow Serving的Source接口能够从存储系统中查找并发现Servables。 TensorFlow Serving中包含了Source公共索引的实现。 例如,Source可以访问RPC等机制并且可以轮询文件系统。

Sources可以维持跨多个Servable和跨多个Version的状态。 这对于使用delta(diff)来在 versions间进行更新的Servables很有作用。 这对于不同版本之间使用增量的模型更新的Servables来说非常有用。

7. Aspired Versions

Aspired Versions表示需要被加载和准备的Servable Version集合。 Source会与该Servable Version集合进行通讯,一次一个Servable Stream。 当一个Source给出一个aspired versions的新列表到Manager时, 它会为该Servables stream取代之前的列表。该Manager会unload任何之前 已加载的versions,使它们不再出现在该列表中。

8. Managers

Managers处理了Servable的完整生命周期,包括:

  • loadding Servable
  • serving Servable
  • unloading Servable Managers会监听Source,并跟踪所有的versions。Mananger会尝试满足Source的请求, 但如果需要的resources不提供的话,也可以拒绝加载一个aspired version。 Managers也可以延期一个”unload”操作。 例如,一个Manager可以等待unload直到一个更新的version完成loading, 基于一个策略(Policy)来保证所有时间内至少一个version被加载。

TensorFlow Serving Manager提供一个单一的接口——GetServableHandle()——给client来访问已加载的Servable实现。

9. Core

TensorFlow Serving Core(通过标准TensorFlow Serving API)管理着一下的Servables:

  • lifecycle
  • metrics TensorFlow Serving Core将Servables和loaders看成是透明的对象。

1.2 Servable的生命周期

图1    servable的生命周期

通俗地讲:

  • source会为Servable Version创建一个Loaders。
  • Loaders会作为Aspired Versions被发送给Manager,manager会加载和提供服务给客户端请求。

更详细的:

  1. 一个Source插件会为一个特定的Version创建一个Loader。 该Loaders包含了一个加载Servable时所需的任何元数据。
  2. Source会使用一个callback来通知该Aspired Version的Manager。
  3. 该Manager应用该配置过的Version Policy来决定下一个要采用的动作, 它会被unload一个之前已经加载过的Version,或者加载一个新的Version。
  4. 如果该Manager决定它是否安全,它会给Loader所需的资源, 并告诉该Loader来加载新的Version。
  5. 一个Client会向最新Version的模型请求一个Handle, 接着Dynamic Manager返回一个handle给Servables的新version。

1.3 Extensibility

Tensorflow serving提供了一些扩展点,使你可以添加新功能。

1. Version Policy

Version Policy可以指定version序列,在单个servable stream中加载或卸载。

tensorflow serving包含了两个策略(policy)来适应大多数已知用例。 分别是:Availability Preserving Policy(避免没有version加载的情况; 在卸载一个老version前通常会加载一个新的version), Resource Preserving Policy(避免两个version同时被加载,这需要两倍的资源; 会在加载一个新version前卸载老version)。对于tensorflow serving的简单用法, 一个模型的serving能力很重要,资源消耗要低,Availability Preserving Policy会确保在新version加载前卸载老version。对于TensorFlow Serving的复杂用法, 例如跨多个server实例管理version,Resource Preserving Policy需要最少的资源(对于加载新version无需额外buffer)

2. Source

新的资源(Source)可以支持新的文件系统、云供应商、算法后端。 TensorFlow Serving提供了一些公共构建块来很方便地创建新的资源。 例如,TensorFlow Serving包含了围绕一个简单资源polling行为的封装工具类。 对于指定的算法和数据宿主Servables,Source与Loaders更紧密相关。

3. Loaders

Loaders是用于添加算法和数据后端的扩展点。Tensorflow就是这样的一个算法后端。 例如,你实现了一个新的Loader来进行加载、访问、卸载一个新的servable类型的机器学习模型实例。 我们会为lookup tables和额外的算法创建Loaders。

4. Batcher 会将多个请求打包(Batching)成单个请求,可以极大减小执行inference的开销, 特别是像GPU这样的硬件加速存在的时候。Tensorflow Serving包含了一个请求打包组件 (request batching widget),使得客户端(clients) 可以很容易地将特定类型的inferences跨请求进行打包成一个batch, 使得算法系统可以更高效地处理。详见Batching Guide。

[1] 使用 TensorFlow Serving 和 Flask 部署 Keras 模型

[2] Google TFX 文档

HashMap相关常见的问题

目录

1. HashMap的结构

在HashMap中Key-Value数据的存储采用哈希表的方式实现也就是基本的数组 + 链表结构。

图1     HashMap的存储结构

在HashMap中定义了一个Node数组存储所有的key-value关系,Node的定义如下。

static class Node<K,V> implements Map.Entry<K,V> {
	final int hash;
	final K key;
	V value;
	Node<K,V> next;

	Node(int hash, K key, V value, Node<K,V> next) 

	public final K getKey() 
	public final V getValue() 
	public final String toString() 

	public final int hashCode()

	public final V setValue(V newValue)

	public final boolean equals(Object o)
}

Node是一个内部类,实现了Map.Entry的接口,本质上是一个(键值对)。 在上图中每个黑点表示一个Node对象。

在了解HashMap我们先来了解一下HashMap实现时候的存储结构,也就是HashMap当中定义了哪些属性字段:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final int MAXIMUM_CAPACITY = 1 << 30;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
transient Node<K,V>[] table;
transient Set<Map.Entry<K,V>> entrySet;
transient int size;
transient int modCount;
int threshold;
final float loadFactor;
  • DEFAULT_INITIAL_CAPACITY: 表示默认的初始化容量,必须是2的指数

  • DEFAULT_INITIAL_CAPACITY: 最大容量, 如果两个代参构造函数指定了该值并且大于这个值的话, 就使用这个值,表示HashMap的最大容量。

  • DEFAULT_LOAD_FACTOR: 在构造函数中没有指定装载系数的时候,默认使用该装载系数。

  • TREEIFY_THRESHOLD: 在HashMap中将链表转化成红黑树的阈值。 当链表的长度大于8的时候链表转换成红黑树。

  • UNTREEIFY_THRESHOLD:

  • MIN_TREEIFY_CAPACITY:

  • table: table定义了HashMap的hash表结构。 在HashMap第一次使用的时候进行初始化,数组大小根据需要调整大小, 分配的时候长度总是2的幂。(在某些操作中,允许大小为0)

  • entrySet: 所有map记录的集合

  • size: 表示所有mapping映射中key-value键值对的个数。

  • modCount: 这个散列表被结构性修改的次数。

  • threshold: 接下来做resize()操作的阈值。

  • loadFactor: 哈希表的加载因子。

在HashMap中数据通过Hash表来存储,Hash表为了解决数据的冲突可以采取两种方式来解决问题。 一种是开放地址法,另外一种是链地址法。在Java中采用链地址法,也就是数组 + 链表的方式。

在HashMap中通过将”key”值进行hashCode()得到哈希编码, 然后通过高位运算和取模运算来确定键值对的存储位置。 当两个key定位到相同的位置的时候,表示发生了Hash冲撞。

Hash算法的结果越分散均匀,Hash碰撞的概率越小,Map存取的效率会越高

Hash桶数组越大,Hash数组越分散,如果哈希桶数组很小, 即使好的Hash算法也会出现碰撞, 所以HashMap的操作需要在空间成本和时间成本之间进行权衡。 也就是根据实际情况确定HashMap中数组分桶的大小。

怎么样能够使得HashMap访问的效率更高, 同时数组的分桶又占用更少的空间,就是采用好的Hash算法和扩容机制

2. HashMap如何扩容

在确定hashMap如何扩容的时候,我们先了解以下几个字段:

int threshold;             // 所能容纳的key-value对极限 
final float loadFactor;    // 负载因子
int modCount;  
int size;  

Node[] table初始化的长度length(默认长度是16), threshold表示HashMap所能够容纳的最大的键值对的个数。 threshold = length * load fractor 当数组定义好之后, 加载因子越大能够存储的键值个数越多。

默认0.75的加载因子是对空间和时间效率的平衡选择,建议不要进行修改。

在HashMap中键值对的个数操作阈值之后,通过resize进行扩容, 扩容后HashMap的容量是原来的两倍。

size是HashMap中实际键值对存在的数量和table length以及threshold还是有区别的。 modCount是用来记录HashMap内部结构发生变化的次数,主要用于迭代的快速失败。 put新建键值对的时候,HashMap的结构发生变化,但是如果key的值被新的值覆盖, 不属于HashMap的结构发生变化。

在哈希桶数组中,一般将table的长度length设置为素数。 相对来说素数导致冲突的概率要小于合数。 HashMap采用这种非常规的设计主要是为了取模和扩容时做优化,同时为了减少冲突。

3. 链表转红黑树

在HashMap中,除了通过合理的桶大小和哈希数组设置来减少查找时间, 还采用另外一种方式,来避免桶链表过长导致对HashMap性能的影响。

在JDK1.8中引入了红黑树,当链表长度太长(默认超过8)时, 链表转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能。

4. 功能实现

在第四个小章节中,我们主要介绍一下Hash表的基本功能的定义。

4.1 确定Hash桶数组索引位置

不增加、删除、查找键值对,定位到哈希数组的位置都是很关键的一步。

HashMap的数据结构是数组和链表的结合, 这个时候我们希望HashMap里面的元素尽量分布得均匀些, 尽量使每个位置只有一个。那么这种情况下, 我们需要使用hash算法来求得这个索引位置, 这样能够快速知道想要的元素减少遍历,优化查询效率。

HashMap定位数组索引位置,直接决定了hash方法的离散性能。具体的源码实现如下:

方法一
static final int hash(Object key) {   //jdk1.8 & jdk1.7
     int h;
     // h = key.hashCode() 为第一步 取hashCode值
     // h ^ (h >>> 16)  为第二步 高位参与运算
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
方法二
static int indexFor(int h, int length) {  //jdk1.7的源码,jdk1.8没有这个方法,但是实现原理一样的
     return h & (length-1);  //第三步 取模运算
}

在通过哈希算法求索引的操作中本质上包含三个步骤:

  • 取key的hashCode

  • 高位运算

  • 取模运算

对于给定的任意对象,只要hashCode()返回值相同, 程序调用的方法所计算得到的Hash编码都相同。 那么我们首先想到的是将hash值对应的数组长度取模运算, 这样一来,元素分布相对来说比较均匀。

可是取模运算的计算开销比较大,在HashMap中通常调用 方法二来计算该对象应该保存在table数组的哪一个索引位置。

在方法二中通过h&(table.length -1)来得到对象的保存位, 而HashMap底层数组的长度总是2的$n$次方, 这是HashMap在速度上的优化。当Length总是2次方的时候, h&(table.length -1)运算等价于对length取模,也就是h%length。

在JDK1.8中,优化了高位运算的算法, 通过hashCode()的高16位异或低16位实现:(h = k.hashCode()) ^ (h >>> 16)

4.2 分析HashMap的put方法

在美团技术团队的文章中,给出HashMap中put方法的执行流程, 对应如下图2。

图2     HashMap put流程

4.3 扩容机制

扩容(resize)就是重新计算容量。当我们向HashMap对象中不断添加元素, 而HashMap对象内部的数组没有办法装载更多的元素的时候, 对象就需要扩大数组的长度,以便能够装入更多的元素。

在java里的数组没有办法自动扩容,方法是采用新的数组代替掉已有的小数组。

YouTube DNN实践过程的一些细节

目录

这篇文章中主要记录一下YouTube DNN实现的具体细节和迭代步骤。 在整个YouTube DNN的迭代过程中我们主要分为以下几个过程:

  • BaseLine模型(history 版本)

  • Feature版本

  • Share Weight 版本

  • Online Serving版本

  • Filter版本

评估指标的修昔底德陷阱?

推荐系统到底是什么?

Long Tail和Hot Top之间的取舍?

群众的力量?

1.

图1    模型训练中的不同状态