<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>YongYuan&apos;s homepage</title>
    <description>Yong Yuan (Willard) - Algorithm Engineer at Shopee specializing in Recommendation System, and Machine Learning Research.</description>
    <link>https://yongyuan.name</link>
    <atom:link href="https://yongyuan.name/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>LLM推荐系统 A Simple Training-free Approach for Recommendations using LLM</title>
        <description>&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;思路动机&quot;&gt;思路动机&lt;/h2&gt;

&lt;p&gt;LLM最新进展为推荐系统任务提供了有前途的新方法。&lt;strong&gt;当前最先进的方法依赖于微调 LLM 来实现最佳结果，但此过程成本高昂且带来了显著的工程复杂性&lt;/strong&gt;。相反，绕过微调并直接使用 LLM 的方法资源密集程度较低，但&lt;strong&gt;往往无法完全捕获语义和协作信息，导致与经过微调的方法相比性能不佳&lt;/strong&gt;。在本文中，提出了一种简单的无需训练的推荐方法 (STAR)，这是一个利用 LLM 的框架，可应用于&lt;strong&gt;各种推荐任务而无需微调&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;comment: &lt;strong&gt;这篇论文，提供了一个将语义与协同信息融合起来的视角，这是这篇论文提供的最大的启发性视角&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;提供了一种普适推荐场景下，快速搞一个基于llm推荐的基线。在此基础上，特定业务场景，还是需要针对性改进。&lt;/li&gt;
  &lt;li&gt;模型还是需要微调的，不能应该微调成本高、工程复杂就放弃。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在LLM里面，考虑user-item interactions交互行为，真的非常非常重要！实验观察到的现象：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Beauty Hits@10： +23.8%&lt;/li&gt;
  &lt;li&gt;Toys and Games：+37.5%&lt;/li&gt;
  &lt;li&gt;Sports and Outdoors ：-1.8%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sports and Outdoors 为啥结果提不上来&lt;/strong&gt;？因为Sports and Outdoors上用户与item的交互，相比其他两个数据集，是很低的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;方法&quot;&gt;方法&lt;/h2&gt;

&lt;p&gt;两阶段方法，方法全局概览:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;检索&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_4.png&quot; alt=&quot;&quot; /&gt;
通过上图得到检索结果。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;排序&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_5.png&quot; alt=&quot;&quot; /&gt;
对检索结果，通过LLMRank排序。&lt;/p&gt;

&lt;h3 id=&quot;检索&quot;&gt;检索&lt;/h3&gt;

&lt;p&gt;检索阶段，考虑了语义信息、和CI信息。在融合这两者的时候（融合公式里），语义信息和CI信息，有一个权重平衡因子。&lt;/p&gt;

&lt;p&gt;同时会考虑：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;时间因子：用户与item交互时间越近的，越重要；&lt;/li&gt;
  &lt;li&gt;打分因子：用户与item交互打分，越大越重要；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;怎么计算语义信息矩阵、CI信息矩阵？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;预先计算好语义信息矩阵（n&lt;em&gt;n，n是item的数量）、以及CI信息矩阵（n&lt;/em&gt;n，怎么计算，用的非常典型的itemCF计算item之间的相似度矩阵）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;llm embedding: 使用的是google自己内部的Geckotext-embedding-004&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;怎么融合&quot;&gt;怎么融合？&lt;/h4&gt;

&lt;p&gt;如果不考虑语义信息，则对item4的打分，就是一个非常典型的itemCF对item4的打分逻辑：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_8.png&quot; alt=&quot;&quot; /&gt;
需要融合语义信息矩阵，是在itemCF打分的基础上，做了如下方式的融合：a=0，就是itemCF打分逻辑，只是里面包含了时间因子t、打分因子r:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_9.png&quot; alt=&quot;&quot; /&gt;
打分因子r_j: user对item_j的打分，实验发觉r_j全部取1，对结果没什么影响。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_10.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_14.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;a=0和a=1，有消融实验结论，没有贴具体数据。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_15.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;检索打分公式的影响：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_16.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;排序&quot;&gt;排序&lt;/h2&gt;

&lt;p&gt;输入：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_rank_1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;排序打分逻辑，不同打分方式结果:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_rank_2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;候选长度、交互序列的长度，对结果的影响：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_rank_3.png&quot; alt=&quot;&quot; /&gt;
rank里面加入一些交互行为的影响：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_rank_4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;感觉差异不大。&lt;/p&gt;

&lt;p&gt;不同模型对结果的影响：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/star_rank_5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 22 Nov 2024 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/star-paper.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/star-paper.html</guid>
      </item>
    
      <item>
        <title>MHA、MQA、GQA的差异与共性</title>
        <description>&lt;h2 id=&quot;mhamqagqa差异&quot;&gt;MHA、MQA、GQA差异&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/mha_mqa_gqa.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;MHA：Multi-Head Attention，QKV 三部分有相同数量的头，且一一对应。每次做 Attention，head_i 的 QKV 做好自己的运算就可以，输出时各个头加起来就行。&lt;/p&gt;

&lt;p&gt;MQA：Multi-Query Attention，让 Q 仍然保持原来的头数，但 K 和 V 只有一个头，相当于所有的 Q 头共享一组 K 和 V 头，所以叫做 Multi-Query 了。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;实现改变了会不会影响效果呢？确实会影响，但相对它能带来的收益，性能微降是可以接受的。能带来多大的收益呢？实验发现一般能提高 30%-40%左右的吞吐。收益主要来源于降低了 KV cache。实际上 MQA 运算量和 MHA 是差不多的，可理解为读取一组 KV 头之后，给所有 Q 头用，但因为之前提到的内存和计算的不对称，所以是有利的。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GQA：Grouped-Query Attention，是 MHA 和 MQA 的折衷方案，既不想损失性能太多，又想获得 MQA 带来的推理加速好处。具体思想：不是所有 Q 头共享一组 KV，而是进行分组，一定头数 Q 共享一组 KV，比如上面图片就是两组 Q 共享一组 KV。&lt;/p&gt;

&lt;h2 id=&quot;mhamqagqa共性&quot;&gt;MHA、MQA、GQA共性&lt;/h2&gt;

&lt;p&gt;实际上，MHA、MQA可以看做是GQA两个特例版本：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;MQA对应GQA-1，即只有一个分组，对应一个K和V；&lt;/li&gt;
  &lt;li&gt;MHA对应GQA-H，对应H个head，对应H个K和V；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;从mha模型得到mqa和gqa&quot;&gt;从MHA模型得到MQA和GQA&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;从MHA得到MQA：将MHA中H个head的的K和V，分别做mean pooling后得到一个K和V，用得到的K和V继续训练；&lt;/li&gt;
  &lt;li&gt;从MHA得到GQA：将MHA中H个head的的K和V，分别做mean pooling得到H个K和V，用得到的K和V继续训练；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/kv_mean_pooling.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;mhamqagqa效果&quot;&gt;MHA、MQA、GQA效果&lt;/h2&gt;

&lt;p&gt;在LLAMA2中，在不同的数据数据集上对比的效果（注意：为了维持参数量一致，对于MQA、GQA的FFN layer的维度，会有一定的拓宽）：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/mha_mqa_cqa_performance.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;MHA: &lt;a href=&quot;https://arxiv.org/pdf/1706.03762.pdf&quot;&gt;Attention is All You Need&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;MQA：&lt;a href=&quot;https://arxiv.org/pdf/1911.02150.pdf&quot;&gt;Fast Transformer Decoding: One Write-Head is All You Need&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;GQA: &lt;a href=&quot;https://arxiv.org/pdf/2305.13245.pdf&quot;&gt;Training Generalized Multi-Query Transformer Models fromMulti-Head Checkpoints&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sat, 02 Sep 2023 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/mha-mqa-gqa.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/mha-mqa-gqa.html</guid>
      </item>
    
      <item>
        <title>点击率预估之长序列建模Clustering based Behavior Sampling</title>
        <description>&lt;p&gt;SIGIR 2022 &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3477495.3531829&quot;&gt;Clustering based Behavior Sampling with Long Sequential Data for CTR Prediction&lt;/a&gt; paper reading分享时记录的笔记。&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;对当前target item预估时，序列建模要解决的两个主要问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;用户行为序列中存在很多噪声：First, there is a lot of noise in such long histories, which can seriously hurt the prediction performance.&lt;/li&gt;
  &lt;li&gt;行为序列太大会导致推理耗时与存储消耗比较大：Second, feeding the long behavior sequence directly results in infeasible inference time and storage cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;User Behavior Clustering Sampling (UBCS)解决上述两个问题的思路：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;建模一种同时考虑相关性（行为序列与target item的相关性）、又考虑时间信息的行为序列采样模块（&lt;font color=&quot;red&quot;&gt;Behavior Sampling module&lt;/font&gt;）；&lt;/li&gt;
  &lt;li&gt;将行为序列item进行小数量级别的聚类，即&lt;font color=&quot;red&quot;&gt;Item Clustering module&lt;/font&gt;；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于行为序列中，包含噪声，怎么处理，总体的原则是：对于每一个target CTR prediction，从用户行为序列中，挑选出那些与target CTR prediction最相关的那些样本出来。&lt;/p&gt;

&lt;h2 id=&quot;整体框图&quot;&gt;整体框图&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_1.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;behavior-sampling-module&quot;&gt;Behavior Sampling Module&lt;/h3&gt;

&lt;p&gt;用户的长行为序列，不仅包含了用户丰富的兴趣信息，也包含了很多不相关行为和噪声。所以需要做相关性提取。&lt;/p&gt;

&lt;h4 id=&quot;相关性提取&quot;&gt;相关性提取&lt;/h4&gt;

&lt;p&gt;用户行为长序列中的每个item和candidate item分别计算相关性分：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_2.png&quot; width=&quot;200&quot; height=&quot;50&quot; alt=&quot;ubcs_2&quot; align=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中，e_i是行为序列embedding，e_t是target item embedding。&lt;/p&gt;

&lt;h4 id=&quot;时间感知采样&quot;&gt;时间感知采样&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_3.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;计算行为序列中的item和target item之间的时间差，embedding后，和相关性相加，在通过softmax选取top K, 就是采样出来的K个item序列。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_4.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于长序列用户行为建模，输入的用户行为特征序列过长，使得原有的监督学习标签变得相对稀疏，模型难以获得足够的训练信号。&lt;/p&gt;

&lt;h2 id=&quot;ctr-prediction-module&quot;&gt;CTR Prediction Module&lt;/h2&gt;

&lt;p&gt;ctr预估，二分类，交叉熵，直接看公式，没啥可解释的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_5.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这个模块，在训练的时候，通过构造自监督信号来进行对比学习，进行模型的预训练。&lt;strong&gt;为啥要进行训练&lt;/strong&gt;？&lt;/p&gt;

&lt;p&gt;作者的解释：长序列用户行为建模，输入的用户行为特征序列过长，使得原有的监督学习label变得相对稀疏，从而使模型难以获得足够的训练信号。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_6.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;构造的自监督信号：锁定user_id、target item，label则也固定了，在采用的时候，采样出两个子序列p和q。p的正负样本，分别为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;正样本为q；&lt;/li&gt;
  &lt;li&gt;负样本为q帽子，q帽子是从一个batch里面随机负采样来源不同user的item构成的子序列；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_7.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;用对比学习先进行预训练。&lt;/p&gt;

&lt;h2 id=&quot;item-clustering-module&quot;&gt;Item Clustering Module&lt;/h2&gt;

&lt;p&gt;item clustering module要解决的问题：由于用户行为序列很长，U个用户，M个target，计算下面内积的复杂度O(UMT)。有没有办法把M压下去，让C « M？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_8.png&quot; width=&quot;200&quot; height=&quot;50&quot; alt=&quot;ubcs_2&quot; align=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;item clustering module，给出的方式是，可以借助聚类的方式，先聚类，然后计算每个用户的行为序列中的item与聚类中心的距离，在训练的时候，查表即可实现加速。整个加速过程，跟&lt;a href=&quot;https://yongyuan.name/blog/vector-ann-search.html&quot;&gt;PQ&lt;/a&gt;极为相似。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在每一轮训练之前，先用target item的embedding，训练聚类C；&lt;/li&gt;
  &lt;li&gt;计算用户行为序列与C之间的距离；&lt;/li&gt;
  &lt;li&gt;开始训练，查表；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_9.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;实验&quot;&gt;实验&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_10.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SIM: sim hard&lt;/li&gt;
  &lt;li&gt;MIMN: Multi-channel user Interest Memory Network&lt;/li&gt;
  &lt;li&gt;HPMN: Hierarchical Periodic Memory Network&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/ubcs_11.png&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Sun, 27 Nov 2022 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/ubcs-for-ctr-prediction.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/ubcs-for-ctr-prediction.html</guid>
      </item>
    
      <item>
        <title>搜索系统：视频搜索离线指标与在线指标</title>
        <description>&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;搜索是一个以相关性为基础，辅助消费、权威、质量、时鲜等多目标的复杂系统。&lt;/p&gt;

&lt;p&gt;各位看官在搜索引擎输入一个Query=“如何在mate 40上连接wifi”，我们优先考虑的是排序的doc是否与Query相关，根据相关的程度，可以有不一样的定义：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;doc内容&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;相关性得分&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;解释&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;doc内容就是在介绍华为mate 40怎么连接wifi&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;用户输入Query完全匹配doc，给最高分&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;doc内容在介绍华为mate 30怎么连接wifi&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;用户输入Query意图匹配，且mate 40和mate 30基本上都是一个系列，看了mate30的连接wifi方法，也能解决mate 40连接wifi的方法&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;doc内容在介绍华为 mate 40，但是没有介绍怎么连接wifi&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;核心实体完全匹配，但是用户的主需求没有得到满足&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;doc内容基本上都在介绍联想电脑怎么连接wifi&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;用户输入Query与doc主题完全不匹配&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;从上述定义看出，不考虑权威、质量等，相关性的评估就是一件很难的事情。首先标准的确定就需要很强的业务视角做背书，3分和0分的区别很容易，但是如何拉开2分和1分区分度，也是一个世纪难题。作为一个算法工程师，我们要为业务效果负责，常见的工作目标设定就是定一个合理的过程指标，来帮助我们进行目标的迭代。&lt;/p&gt;

&lt;p&gt;很多做推荐的同学有可能会说，我们离线搞一个点击的AUC，AUC提升证明模型效果不错，然后在线去看点击率不就能衡量了么？举一个真实的用户Query=“杨颖”，如果出只包含“黄晓明”的结果，用户的点击不但不掉，还会升，这个时候我们发现指标失效了，因为用户输入Query的意图很明确就是找“杨颖”这个人，而不是找他老公“黄晓明”，且在相关性的评估角度，这个就是典型的0分问题。&lt;/p&gt;

&lt;p&gt;搜索和推荐最大的不同是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;推荐可以依托自动化的指标来辅助我们迭代，毕竟推得好不好，可以通过用户的点击行为来进行衡量，推荐的相关一般定义为沾边就可以了。搜索的相关可是有明确的定义的，用户本身用推荐就是一个被动行为，容错率会更高一些，而搜索是一个主动行为，容错率会更低。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在用户和doc之间加入了一个枷锁，这个枷锁就是Query，只有Query和doc满足相关性的要求，我们才能在相关性的基础上刻画时效、权威、质量、满意度等其他多维度的指标。那么我们进行搜索项目的迭代，到底要依托什么样子的过程指标呢？什么才是搜索系统的北极星指标呢？&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;在线与离线指标&quot;&gt;在线与离线指标&lt;/h2&gt;

&lt;h3 id=&quot;在线指标&quot;&gt;在线指标&lt;/h3&gt;

&lt;p&gt;做过搜索的人应该都知道，业界主流的搜索AB指标会有很多，北极星指标是DAU，重要指标包括有点比、首点位置和换Query率，以及其他一些消费指标。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;DAU：当用户搜索心智建立之后，需要去刺激用户去不断使用，进而提升整体DAU，这个是北极星指标，其他指标都是参考，正常搜索做的好，DAU是不断提升的。&lt;/li&gt;
  &lt;li&gt;PV：搜索核心业务指标，重要性仅次于DAU，搜索商业化和这个指标非常相关，但是PV涨不见得是一个好事，当我们搜索服务不稳定的时候，用户频繁发生换query检索，换query率负向，但是PV是涨的。&lt;/li&gt;
  &lt;li&gt;首页有点比：有点击的query/所有query，这个指标一定程度能反应出线上相关性，但这个指标有天花板，想突破80%都很难。&lt;/li&gt;
  &lt;li&gt;换query率：用户一般在一个session中，如果已经搜到满意的结果，是不会换一个query来进一步检索的，换query率变低，相关性满足的还不错；但是也不能完全迷信这个指标，有可能用户觉得搜索结果很差，就不愿意相信搜索结果了。&lt;/li&gt;
  &lt;li&gt;首次点击位置：那些最相关的结果更应该排在靠前的位置，这个很符合NDCG的定义，3分的最相关的结果更应该排在第一位，首次点击位置一定程度能反应相关性。&lt;/li&gt;
  &lt;li&gt;浏览深度：搜索是满足既走，如果第一条已经满足我们的要求了，那么就没有必要再去浏览下一条，这个指标更多的是一个横向参考指标，需要和其他指标集合看，如果我们的换query正向，消费指标也正向，浏览深度反而能帮助我们提升用户感知，更好的消费；但是如果换query变多，消费指标一般，而浏览深度增加，证明我们整体满足用户搜索需求做的不好。&lt;/li&gt;
  &lt;li&gt;消费指标：点击率、停留时长、完播率、点赞率等常见的消费指标，这些指标是短期的业务指标，主要是通过多目标帮助提升用户体验，进而长期提升DAU，因为相关性好，不一定消费指标好，我们更应该把相关性和满意度好的都呈现给我们的用户。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们做一个假设，用户输入了一个Query，如果相关，那么用户肯定会点下方的Doc，有点比会变高。理论上越相关的Doc应该排在更靠前的位置，用户不需要向下浏览就可以找到目标的Doc，首次点击的位置会靠前。如果满足了用户的搜索需求，用户大概率不会对之前的Query做修正来重新找到目标的Doc。&lt;/p&gt;

&lt;p&gt;但是上述的AB指标只是用户用脚投票的结果，而相关性是一个相对先验的东西，除非有很大的效果升级，且影响面够高，我们才能撼动AB指标。经过很多的经验论证，相关性提升是一个顿感很强的模块，用户心智的养成需要时间培养，那么我们的单次实验迭代如果没有撼动指标，我们还去参加LR么？&lt;/p&gt;

&lt;h3 id=&quot;离线指标&quot;&gt;离线指标&lt;/h3&gt;

&lt;p&gt;其实业界各家主流的做法，会依赖于一个偏主观的NDCG或者side by side的人工GSB的评估：&lt;/p&gt;

&lt;h4 id=&quot;排序评估ndcg刻画相关性&quot;&gt;排序评估（NDCG）刻画相关性&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;NDCG（DCG/IDCG）是学术界给搜索定的指标，会构造一套理想态的搜索结果（IDCG），然后观察当前搜索结果达到理想态的分数（是一个小于1的数，如果是0.9，代表达到理想态的90分）；但是搜索本身是一个长尾需求，数据分布随着需求变更，随着时间变化，数据集会差别很大。
我们以视频搜索为例，大家都会以抖音作为IDCG，然后去评比达到抖音的多少分，但是抖音是否是最优的，不确定。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;还有一个指标就是DCG，就是自己和自己比，通过分数来看，目前能到8分基本上就是非常好的搜索引擎。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通过上述分析，NDCG是一个还不错的指标，但是不能作为整个搜索迭代唯一看的离线指标，因为数据集无法真正反应线上真实情况，只是作为一个Ground Truth参考。&lt;/p&gt;

&lt;h4 id=&quot;人工评估gsb刻画相关性&quot;&gt;人工评估（GSB）刻画相关性&lt;/h4&gt;

&lt;p&gt;GSB的名称叫做Good: Same: Bad，各家搜索引擎真正相关性的评估标准，是遵循一个套严格的评判标准。。每次实验的迭代我们会将实验组和对照组进行随机词包（就是线上用户实际输入的Query包）抽取Diff，然后送给行业专家进行人工GSB评估，如果实验组好就会把当前的Query评估为Good，如果效果持平那么就是Same，否则就是Bad。&lt;/p&gt;

&lt;p&gt;实际迭代过程中，会采用一套严格的人工标注流程，遵循side by side的评估手法，通过对相关性、权威性、质量、时效、多样等维度进行人工评估来去打出区分度，真正的做到离线迭代抽diff，gsb真正反应线上效果。&lt;/p&gt;

&lt;p&gt;上述做法依赖于行业专家定义的一套标准，分门别类的进行评估的一套规则系统，偏先验的东西需要有标准进行牵引。如果觉得评估过于主观，那么其实可以采用奇数人（如果是偶数的话，正好变成1:1，需要一个裁判介入做决定）评估的方式，并且将词包数量变大来减少Bias。&lt;/p&gt;

&lt;p&gt;当然这种方式势必会被做行为建模的同学所诟病，但是还是那个问题，点击不点击只是相关不相关的一个子集而已，毕竟不相关也可以获得高点击，所以我们需要结合AB指标和人工评估统一来看，人工评估更多的是一种体感映射，依赖于行业专家，有时候会不客观，但实在是找不到更好的过程指标了。就相当于高考，很多人诟病它一考定终生，但是还有比它更好的评估方式么，毕竟存在也上千年了。&lt;/p&gt;

&lt;p&gt;当然，现在移动端带搜索功能的app，大部分会提供一些人工反馈的入口，让用户去点击是否相关的按钮来进行反馈，也能获得一些有用的输入。这些也可以做成过程指标，它也可以作为一个参考来辅助决策。&lt;/p&gt;

&lt;h2 id=&quot;反直觉误区探讨&quot;&gt;反直觉误区探讨&lt;/h2&gt;

&lt;h3 id=&quot;用户点击是否代表相关&quot;&gt;用户点击是否代表相关&lt;/h3&gt;

&lt;p&gt;首先搜索的点击和推荐广告场景不一样，是先验和后验的融合，另外标题党严重，纯用点击来刻画相关性很不现实，直接会学飞，一般都是拿点击做pre train。搜索是一个主动的行为习惯，不是一个被动的行为习惯：包括质量、权威、时效、新鲜度等。而这些都是很难通过在线指标来刻画的，毕竟我们也发现，中国用户长搜的那些点击高的都是色情query，很容易带歪模型。另外短视频模式下，单列自动播放，怎么衡量用户点击？&lt;/p&gt;

&lt;h3 id=&quot;ab指标是否是搜索最优的流量实验&quot;&gt;AB指标是否是搜索最优的流量实验？&lt;/h3&gt;

&lt;p&gt;AB指标更多是做分桶实验，而且是占用少量流量来做实验，很多公司都会采用类似于反转的操作保证指标不受短期影响，因为短期指标有可能是收到波动的影响，长期拉平没有任何收益，有可能是负向；常见一类情况，如果分桶后，桶内用户都是活跃的搜索用户，就会产生bias。&lt;/p&gt;

&lt;p&gt;所以搜索更多的是采用interleaving实验，通过将不同策略的结果交织在一起来判断，这样可以有效的避开活跃用户这个坑。但是interleaving是否是最优方案，也不一定，只是说相对AB实验来说，更适合搜索场景。&lt;/p&gt;

&lt;p&gt;搜索是一个复杂的系统，评估更是一个很难被科学量化的工作，往往也被人challenge，但是确实也没有更好的方式方法。&lt;/p&gt;
</description>
        <pubDate>Wed, 12 Oct 2022 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/video-search-system-evaluation-one.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/video-search-system-evaluation-one.html</guid>
      </item>
    
      <item>
        <title>C++漫谈：继承、dynamic_cast、纯虚函数、protected，一场多态之旅</title>
        <description>&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;做算法策略过程中，常常会存在这样一种情形：由于业务压力或者岗位性质的缘故，做算法、策略的同学（也包括我自己），对于一些工程上的实现，往往只追求尽快实现、拿到收益，而忽略了实现过程中，代码的复用性、可读性。&lt;/p&gt;

&lt;p&gt;举个例子，某模型AB版本不断迭代，当模型特征输入、推理、业务发生小的差异时，为了追求尽快实现拿到收益，会出现很多的冗余代码，代码量急剧膨胀，可行性也变得比较差，推高维护成本。比如在实际中遇到的一个例子：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NNPredict&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteNN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteNNAB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteNNKMM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteNNDailyMM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteNNKMMSg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;刚开始的时候，线上模型请求RemoteNN()方法，AB实验模型请求RemoteNNAB()方法。RemoteNN()和RemoteNNAB()，有比较多的冗余代码，并且后面随着模型、业务迭代，出现了RemoteNNKMM()、RemoteNNDailyMM()和RemoteNNKMMSg()。这些方法，可能只是输入、推理、业务存在一些差异，为了尽快实验看效果，就直接对方法进行copy改造，导致一个文件，代码超过五六千行，代码高度冗余。&lt;/p&gt;

&lt;h2 id=&quot;基类设计&quot;&gt;基类设计&lt;/h2&gt;

&lt;p&gt;虽然模型版本在不断AB迭代，但不同版本的模型，正如前面所说的，只是特征输入、推理、业务不一样导致的差异。针对上面遇到的场景，我们可以对这样AB请求模型的不同版本，进行抽样，涉及如下基类接口：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SetClient函数设置AB版本对应不同的远程调用grpc的client；&lt;/li&gt;
  &lt;li&gt;Run函数暴露的最终接口，对于请求需要的所有信息，都可以把它放到context里面；&lt;/li&gt;
  &lt;li&gt;PrepareData函数用于特征输入与预处理，RemoteApply函数远程调用，GetResult函数获取最终返回的结果，这3个函数，在基类中都定义为了纯虚函数，且都为private；&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseRemoteInfer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseRemoteInfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;// 初始化阶段调用&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared_ptr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InferKessClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infer_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 执行函数&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 
 &lt;span class=&quot;nl&quot;&gt;private:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 准备数据&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PrepareData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 远程调用，可以设计成统一的非虚函数&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteApply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 获取结果，填充 debug 信息&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 业务填充一些公共的执行函数，在上述阶段中使用 ...&lt;/span&gt;
 &lt;span class=&quot;nl&quot;&gt;private:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared_ptr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InferKessClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infer_client_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseRemoteInfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;BaseRemoteInfer input error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrepareData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Prepare data error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoteApply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Remote apply error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ERROR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Get result error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;shared_ptr传引用or传值&quot;&gt;shared_ptr传引用or传值&lt;/h3&gt;

&lt;p&gt;在上面代码SetClient函数中，我们在使用shared_ptr作为传递参数的类型时，使用的是传引用方式，并且由于传递的参数，在函数内部不会改变，为了保持良好的习惯，对传递参数类型限定了const。在使用shared_ptr传递参数时，到时是传引用好，还是传值好？这是一个好问题，stackoverflow上有人对这个问题做了解释，可以详细阅读&lt;a href=&quot;https://stackoverflow.com/questions/3310737/should-we-pass-a-shared-ptr-by-reference-or-by-value&quot;&gt;Should we pass a shared_ptr by reference or by value?&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;纯虚函数protected&quot;&gt;纯虚函数、protected&lt;/h3&gt;

&lt;p&gt;对于上述设计的基类，由于GetResult方法、SetClient方法，在基类、子类实现方法无差异，将其放在基类中实现就可以，因而GetResult不必将其设计成纯虚函数，将GetResult方法设计为虚函数即可，GetResult方法、SetClient方法都放在基类里，作为基类方法供子类调用。此外，在子类中，如果需要访问到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;infer_client_&lt;/code&gt;的话，如果将其设置为private属性，显然子类不能直接访问到该成员变量，因而将其设置为protected是比较合理的。最终，基类设计为如下：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseRemoteInfer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseRemoteInfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;// 初始化阶段调用&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared_ptr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InferKessClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infer_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 执行函数&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 
 &lt;span class=&quot;nl&quot;&gt;private:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 准备数据&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PrepareData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 远程调用，可以设计成统一的非虚函数&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteApply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 获取结果，填充 debug 信息&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 业务填充一些公共的执行函数，在上述阶段中使用 ...&lt;/span&gt;
 &lt;span class=&quot;nl&quot;&gt;protected:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared_ptr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InferKessClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infer_client_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;派生类&quot;&gt;派生类&lt;/h2&gt;

&lt;p&gt;由于派生类和基类具有相同的GetResult方法、SetClient方法实现，派生类不用再单独实现GetResult方法、SetClient方法了。只需要实现PrepareData方法和RemoteApply。一个示意实现的派生类如下：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DSNNRemoteInfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseRemoteInfer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DSNNRemoteInfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;nl&quot;&gt;private:&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PrepareData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteApply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NNContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;上述命令会对项目进行编译，并将cvtk包安装在本地，在Python中导入包名验证即可。&lt;/p&gt;

&lt;h2 id=&quot;dynamic_cast&quot;&gt;dynamic_cast&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dynamic_cast &amp;lt; type-id &amp;gt; ( expression)&lt;/code&gt;，该运算符把expression转换成type-id类型的对象。使用的时候，应注意：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Type-id必须是类的指针、类的引用或者void*；&lt;/li&gt;
  &lt;li&gt;如果type-id是类指针类型，那么expression也必须是一个指针，如果type-id是一个引用，那么expression也必须是一个引用；&lt;/li&gt;
  &lt;li&gt;dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的（也就说，如果基类指针或者引用确实指向一个派生类对象）这个运算符会传回适当转型过的指针。如果downcast不安全，这个运算符会传回空指针（也就是说，基类指针或者引用没有指向一个派生类对象）；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;dynamic_cast主要用于类层次间的上行转换和下行转换，还可以用于类之间的交叉转换。将一个基类对象指针（或引用）cast到继承类指针，dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;对指针进行dynamic_cast，失败返回null，成功返回正常cast后的对象指针；&lt;/li&gt;
  &lt;li&gt;对引用进行dynamic_cast，失败抛出一个异常，成功返回正常cast后的对象引用；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在类层次间进行上行转换时，dynamic_cast和static_cast的效果是一样的；在进行下行转换时，dynamic_cast具有类型检查的功能，比static_cast更安全。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ds_nn_infer_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make_shared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DSNNRemoteInfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ds_nn_infer_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exp_client_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;succ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;dynamic_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseRemoteInfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ds_nn_infer_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在实际使用时，如果不需要使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dynamic_cast&lt;/code&gt;，则尽量少用。比如上面做举的例子，完全可以将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run()&lt;/code&gt;定义为基类的普通成员函数或者基类的虚函数。下面的例子可以示意说明：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseClass&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;BaseClass Print Function Called...&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DerivedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseClass&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DerivedClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/*void Print() {
        std::cout &amp;lt;&amp;lt; &quot;DerivedClass Print Function Called...&quot; &amp;lt;&amp;lt; std::endl;
    }*/&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared_ptr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DerivedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr_derived_class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ptr_derived_class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make_shared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DerivedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ptr_derived_class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ptr_derived_class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果注释掉子类中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Print()&lt;/code&gt;函数，则输出的结果为：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;BaseClass Print Function Called...
BaseClass Print Function Called...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果保留子类中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Print()&lt;/code&gt;函数，即子类进行重载，输出结果则为：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DerivedClass Print Function Called...
BaseClass Print Function Called...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 20 Mar 2022 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/cpp-polymorphism-and-dynamic-cast.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/cpp-polymorphism-and-dynamic-cast.html</guid>
      </item>
    
      <item>
        <title>C++漫谈：为C++实现的功能提供Python API接口-Pybind11</title>
        <description>&lt;p&gt;兴趣与业余时间开发的&lt;a href=&quot;https://yongyuan.name/cvtk/#/&quot;&gt;CVTK&lt;/a&gt;项目，除了继续完善和添加算法到&lt;a href=&quot;https://yongyuan.name/cvtk/#/&quot;&gt;CVTK&lt;/a&gt;外，还计划提供Python API，便于Python调用。在为&lt;a href=&quot;https://yongyuan.name/cvtk/#/&quot;&gt;CVTK&lt;/a&gt;添加Python API支持的过程中，调研了下C++/Python绑定库的情况。目前支持C++/Python绑定库，比较主流的有四大流行库，分别是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://pybind11.readthedocs.io/en/stable/&quot;&gt;Pybind11&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.boost.org/doc/libs/1_70_0/libs/python/doc/html/index.html&quot;&gt;Boost.Python&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.swig.org/&quot;&gt;SWIG&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://cython.org/&quot;&gt;Cython&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于Pybind11、Boost.Python、Cython、SWIG（&lt;a href=&quot;https://github.com/facebookresearch/faiss&quot;&gt;Faiss&lt;/a&gt;的Python接口，是用&lt;a href=&quot;https://github.com/facebookresearch/faiss/blob/main/faiss/python/setup.py&quot;&gt;SWIG桥接的&lt;/a&gt;）这4个库对Cpp的支持与对比情况，可以详阅&lt;a href=&quot;https://zyxin.xyz/blog/2019-08/GluePythonCpp&quot;&gt;如何选择 Python与C++ 之间的胶水&lt;/a&gt;。这里主要记录下自己在用Pybind11为&lt;a href=&quot;https://yongyuan.name/cvtk/#/&quot;&gt;CVTK&lt;/a&gt;桥接Python API的过程。如果工作中碰到类似需求，可以考虑用PyBind11或者上面提到的其他三个库。&lt;/p&gt;

&lt;h2 id=&quot;效果&quot;&gt;效果&lt;/h2&gt;

&lt;p&gt;在&lt;a href=&quot;https://pypi.org/&quot;&gt;pypi.org&lt;/a&gt;上发布CVTK Python包，可以通过项目主页&lt;a href=&quot;https://pypi.org/project/cvtk-release/&quot;&gt;cvtk-release&lt;/a&gt;的说明进行安装，目前完成CVTK Cpp与Python之间的桥接，Cpp实现的功能会逐步提供对应的Python接口。&lt;/p&gt;

&lt;p&gt;CVTK提供Python API的开发功能，会先发布在&lt;a href=&quot;https://github.com/willard-yuan/cvtk-pypi&quot;&gt;cvtk-pypi&lt;/a&gt;进行测试，如果测试没问题了，最终会同步到&lt;a href=&quot;https://github.com/willard-yuan/cvtk&quot;&gt;CVTK&lt;/a&gt;上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/cvtk-pybind11.jpg&quot; alt=&quot;drawing&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;pybind11桥接cpp与python&quot;&gt;Pybind11桥接Cpp与Python&lt;/h2&gt;

&lt;p&gt;假设在&lt;a href=&quot;https://yongyuan.name/cvtk/#/&quot;&gt;CVTK&lt;/a&gt;中实现了某一个功能，举个简单的例子，比如实现了一个相加的功能，那通过Pybind11为这个函数提供Python接口时，可以这样：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;pybind11/pybind11.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)
&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;py&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pybind11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;PYBIND11_MODULE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cvtk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;R&quot;pbdoc(
        Pybind11 example plugin
        -----------------------
        .. currentmodule:: cmake_example
        .. autosummary::
           :toctree: _generate
           add
           subtract
    )pbdoc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;add&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;R&quot;pbdoc(
        Add two numbers
        Some other explanation about the add function.
    )pbdoc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;subtract&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[](&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;R&quot;pbdoc(
        Subtract two numbers
        Some other explanation about the subtract function.
    )pbdoc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#ifdef VERSION_INFO
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;__version__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MACRO_STRINGIFY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERSION_INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#else
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;__version__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#endif
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PYBIND11_MODULE&lt;/code&gt;这个宏用来告诉Python import设置的包名称，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cvtk&lt;/code&gt;是这个模块的名字，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt;是一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;py::module&lt;/code&gt;类型的变量，代表module本身，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module::def()&lt;/code&gt;也就是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m.def&lt;/code&gt; 负责把Cpp实现的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt;方法暴露给Python。&lt;/p&gt;

&lt;h2 id=&quot;设置setup与本地编译&quot;&gt;设置setup与本地编译&lt;/h2&gt;

&lt;p&gt;Pybind11提供了cmake编译系统的模板&lt;a href=&quot;https://github.com/pybind/cmake_example&quot;&gt;cmake_example&lt;/a&gt;，使用起来特别的方便。在使用编译模板的时候，需要关注的地方：&lt;a href=&quot;https://github.com/willard-yuan/cvtk-pypi/blob/main/setup.py&quot;&gt;setup.py&lt;/a&gt;和&lt;a href=&quot;https://github.com/willard-yuan/cvtk-pypi/blob/main/CMakeLists.txt&quot;&gt;CMakeLists.txt&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;本地编译，执行下面命令：&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; ./cvtk-pypi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;上述命令会对项目进行编译，并将cvtk包安装在本地，在Python中导入包名验证即可。&lt;/p&gt;

&lt;h2 id=&quot;编译并发布到pypi&quot;&gt;编译并发布到Pypi&lt;/h2&gt;

&lt;p&gt;发布到&lt;a href=&quot;https://pypi.org/&quot;&gt;pypi.org&lt;/a&gt;上，主要有两步，分别是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;生成发布包。也就是我们平时在安装Python第三方库时，常见的带&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.whl&lt;/code&gt;后缀的预编译包。&lt;/li&gt;
  &lt;li&gt;发布到pypi上。将生成的发布包，上传到pypi上，用户就可以通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install xxx&lt;/code&gt;安装发布的包了。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;编译并发布到Pypi，PyPA上提供了一个非常好的教程&lt;a href=&quot;https://packaging.python.org/en/latest/tutorials/packaging-projects/#uploading-your-project-to-pypi&quot;&gt;Packaging Python Projects&lt;/a&gt;。实际操作下来，行云流水。&lt;/p&gt;

&lt;p&gt;参考：&lt;a href=&quot;https://packaging.python.org/en/latest/tutorials/packaging-projects/#uploading-your-project-to-pypi&quot;&gt;Packaging Python Projects&lt;/a&gt;;&lt;/p&gt;
</description>
        <pubDate>Tue, 22 Feb 2022 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/pyblind11-cpp-to-python-api.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/pyblind11-cpp-to-python-api.html</guid>
      </item>
    
      <item>
        <title>总结回顾：2021年，歌且从容，杯且从容</title>
        <description>&lt;p&gt;又是一年，人生仿佛一旦过了某个&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%9E%8D%E9%BB%9E&quot;&gt;鞍点&lt;/a&gt;后，时间越过越快。每年写年终总结，是一个很好的习惯，虽然都是一年的流水账，但至少隔个五年、十年后，还能把那些年的经历，串成一根主线，知道它们是怎么关联起来的。乔布斯曾说过：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can’t connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future. You have to trust in something – your gut, destiny, life, karma, whatever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://yongyuan.name/blog/year-end-summary.html&quot;&gt;2019年，十年&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yongyuan.name/blog/2018-year-end-summary.html&quot;&gt;2018年，云深不知处&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yongyuan.name/blog/2017-year-end-summary.html&quot;&gt;2017年，毕业一周年&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yongyuan.name/blog/2016-year-end-summary.html&quot;&gt;2016年，归零清空&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yongyuan.name/blog/2015-year-end-summary.html&quot;&gt;2015年，忙碌的一年&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yongyuan.name/blog/2014-year-end-summary.html&quot;&gt;2014年，三件小事&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;工作&quot;&gt;工作&lt;/h2&gt;

&lt;p&gt;从20年到21年，2年的时间，不论是工作所在的部门、公司还是整个行业，个人都经历了很多的巨变。用个不恰当的比喻，上了船，茫茫大海，只能跟着船一起沉浮飘荡。&lt;/p&gt;

&lt;h3 id=&quot;方向调整&quot;&gt;方向调整&lt;/h3&gt;

&lt;p&gt;从20年11月份开始，工作内容的主线，转移到视频搜索消费精排模型预估上，当然除了主线外，一些于业务无关痒痛的杂活，也做了些打理。在主线业务上，个人收获的两个比较开心、以及自我也认可的点：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;从外围跑龙套业务，逐渐深入视频搜索业务核心组件之一，和团队同学一起推全过一些效果不错的实验；&lt;/li&gt;
  &lt;li&gt;从0搭建了整个消费精排预估的pipeline，线上推理的代码，几乎全部出自自己的手里。当然，日常工作中，主线工作顶的业务压力与挑战，非常的大，一则不是是学术圈、还是业界关于这方面可以查询到的工作，确实比较少；二则，作为一个计算机视觉科班出身无搜索系统实践经验之人，摸着石头过河。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;角色转换&quot;&gt;角色转换&lt;/h3&gt;

&lt;p&gt;从一个指导与执行并行的角色，切换成一个纯碎的一线工程师，在视频搜索上，也写了很多一线代码。角色的转换，对自己而言，也没有什么任何影响，相反，让自己释放出来了很多精力，去关注编码实现、系统架构、获取好的结果。在这个过程中，自己成长的方面：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;编码、架构与系统的稳定性、时延等，系统工程能力，有了比较大的提升。放在此时，如果说对外需要提供一个接口服务，从数据、算法、模型、部署提供稳定的接口服务，自己单独一人还是可以做下来的；&lt;/li&gt;
  &lt;li&gt;Let’s make it happen，给一点星星之火，就成燎原之势。无论技术怎么艰难，只要环境差得没跑路，还是可以继续铁着头，让它发生，努力获取好的结果；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有时候，我常会记起&lt;a href=&quot;https://book.douban.com/subject/20424526/&quot;&gt;《邓小平时代》&lt;/a&gt;，改革开放总设计师邓公在江西的日子。&lt;/p&gt;

&lt;h2 id=&quot;工作上的认知收获&quot;&gt;工作上的认知收获&lt;/h2&gt;

&lt;h3 id=&quot;做适合而正确的事&quot;&gt;做适合而正确的事&lt;/h3&gt;

&lt;p&gt;那些灌输做难而正确的事的人，都当远离。庄周《逍遥游》有云：故九万里，则风斯在下矣，而后乃今培风；背负青天而莫之夭阏者，而后乃今将图南。所谓御风而行，明“势”之人，熟谙借力之道，包括自己的内力和环境的外力。&lt;/p&gt;

&lt;p&gt;如果有选择（在工作中，很多时候没得选）的权利，当看清楚的自己拥有的“势”，不管是自己的长板优势，还是外部优势，去做合适而正确的事；如果没有选择，铁着头上Let’s make it happen就好了。做难而正确的事，是一种政治正确，但随着经历得越来越多，越来越发现，它是一种毒鸡汤，不适合芸芸众生如我辈之人。&lt;/p&gt;

&lt;p&gt;感谢时代，让我借到了深度学习最后一波红利的“势”力。&lt;/p&gt;

&lt;h3 id=&quot;平衡与卷&quot;&gt;平衡与卷&lt;/h3&gt;

&lt;p&gt;从2016年到2022年，工作5年多，经历了两个部门，这两个部门代表的业务非常典型、有趣。这两个业务具备的特点：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;中台部门。一个中台部门，通常会有好几个主要方向，这些主要方向，日常工作一般重叠度比较小。比如一个多媒体内容理解部门，计算机视觉、语音识别、自然语言处理等，每个方向都是一个团队，一个团队内部，又可以细分除很多子方向。团队与团队之间，overlap比较小，业务的形态，使得这样的一个组织职责容易划分清晰。管理上，内耗较小，是一个相对容易管理的组织，更多的精力，是放在技术深度与挖掘核心业务上。&lt;/li&gt;
  &lt;li&gt;业务部门。通常业务部门都有自己的核心主业务，而且核心业务经常是一个高度内聚的系统。这种内聚系统，会把整个组织都调动起来，围绕着这个系统去改进与完善，不过这种内聚系统，通常是牵一发而动全身，不容易把问题与责任归属得很清楚。对组织的管理要求更高，人才密集度提高的过程中，更容易出现团队内耗、组织平衡等问题。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;都说管理是一门艺术，是一门平衡的艺术。有时候，我非常难以理解上层做的一些安排，可能上层是在平衡，可在一线的工作中，有时我却确确实实看到的内耗与人心不齐。&lt;/p&gt;

&lt;h3 id=&quot;识人与跟人&quot;&gt;识人与跟人&lt;/h3&gt;

&lt;p&gt;如何短时间内，怎么识别并招纳到有才德的人才，怎么跟对可以抱大腿的带头大哥？我个人最大的感受就是，这些问题归结到底，其实就是一个信任互相印证的过程。谈谈自己的一点经验：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;招纳人才的时候，可要可不要的，那就不要；让你心里存疑的，也不要。这个印证的过程，过程其实非常短，可能就是面试过程中的1-2个小时。当然在招纳到有才德人才上，还有一个更捷径靠谱的方法，就是团队内部靠谱的同学推荐他认识的人，所谓物以类聚，人以群分，那些靠谱的人周围，往来得多的，也通常是靠谱之人。&lt;/li&gt;
  &lt;li&gt;跟人的印证过程，是一个时间较长的过程，需要用时间与产出向大哥证明，你在他眼里，是一个靠得住的人，而他也会逐步给予你更多机会去施展自己的才能。那些不愿成就下属的，显然不值得跟随。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;读书&quot;&gt;读书&lt;/h2&gt;
&lt;p&gt;21年，在微信读书上显示读完了14本书，读完印象比较深且值得推荐的书：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://book.douban.com/subject/35009826/&quot;&gt;《一生的旅程：迪士尼CEO自述》&lt;/a&gt;，一本可以在职业生涯中，放在案头，隔一段时间反复阅读的好书。对职业生涯的成长、管理与人生体验，都非常的有帮助。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://book.douban.com/subject/34821264/&quot;&gt;《半小时漫画经济学系列》&lt;/a&gt;，将经济学以通俗易懂的漫画形式道来，获益良多。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://book.douban.com/subject/35455510/&quot;&gt;《半小时漫画世界史》&lt;/a&gt;，同上。&lt;/li&gt;
  &lt;li&gt;《罗织经》，来俊臣写的，请君入瓮。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://book.douban.com/subject/30179602/&quot;&gt;《股票魔法师II：像冠军一样思考和交易》&lt;/a&gt;，不断学习，反复实践，从交易中反思总结出适合自己交易风格与纪律。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://book.douban.com/subject/30482551/&quot;&gt;《炒股的智慧》&lt;/a&gt;，理财的都应该读读。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;还有一些其他书，有时候读得比较乱且杂，甚至不完整。不过整体而言，在选书上，我对书的评分是有要求的，通常8分才入坑，光阴有限，与好书为伍。&lt;/p&gt;

&lt;h2 id=&quot;理财尝试&quot;&gt;理财尝试&lt;/h2&gt;

&lt;h3 id=&quot;尝试a股与港股&quot;&gt;尝试A股与港股&lt;/h3&gt;

&lt;p&gt;A股全年收益率-4.99%，还是把打新收益包含进去了的，就这还跑赢沪深300指数16.43%，可见21年白马蓝筹有多难做。21年大部分仓位都在白酒ETF和中概互联ETF上，行业巨变带来中概互联大幅回调，好在10月份，因为买房，基金和股票全部清仓。&lt;/p&gt;

&lt;p&gt;被迫开通港股账户，全年时间加权收益率13.68%，包含了打新收益。同样10月份，全部清仓。&lt;/p&gt;

&lt;h3 id=&quot;买房&quot;&gt;买房&lt;/h3&gt;

&lt;p&gt;21年最大的一笔花销是买了个房子，和妹子使出了洪荒之力。在北京漂了5年多，终于有了个家。从年初看房子看到10月份开始锁定，再到12月份30拿到房本，今年干的最重要的一件事尘埃落定，真的是太不容易了。把买房放进理财里，从资产大类分类上来讲，确实属于理财。不过房子毕竟是刚需，买来是住的，先就这样放在理财里面吧。&lt;/p&gt;

&lt;h2 id=&quot;理财上的认知&quot;&gt;理财上的认知&lt;/h2&gt;

&lt;h3 id=&quot;财务自由&quot;&gt;财务自由&lt;/h3&gt;

&lt;p&gt;2021胡润财富自由门槛是这样定义的：&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;中国一线城市入门级财富自由门槛1900万元，二线城市1200万元，三线城市600万元&lt;/p&gt;

  &lt;p&gt;一线城市中级财富自由门槛6500万元，二线城市4100万元，三线城市1500万元&lt;/p&gt;

  &lt;p&gt;一线城市高级财富自由门槛1.9亿元，二线城市1.2亿元，三线城市6900万元&lt;/p&gt;

  &lt;p&gt;国际级财富自由门槛3.5亿元人民币，相当于5000万美金&lt;/p&gt;

&lt;/blockquote&gt;

&lt;p&gt;孟子说：行有不得反求诸己。5年多来，对财富的追求，我发觉自己离这个目标还是非常遥远，这个世俗或者大多数比较认可的一个可以用具体数字度量的财富自由标准，越来越不贴合自身实际情况。我对自己的财务自由自己定义，也在发生改变，即我逐步趋于下面自己任务的财务自由：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;每天上下班，做一份自己热爱、不断深耕的工作，青春贡献给了自己；&lt;/li&gt;
  &lt;li&gt;更多的自由支配自己的时间。休假具备较好的弹性，比如爸妈等亲人来京，我能没有心理包袱的去陪他们好好游一游，弹性时间去处理一些生活上的突发事情；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;21-年目标回顾&quot;&gt;21 年目标回顾&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;读完了14本书；&lt;/li&gt;
  &lt;li&gt;在自己力所能及范围，买了一个小套间；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;22-年目标&quot;&gt;22 年目标&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;落户，结婚；&lt;/li&gt;
  &lt;li&gt;一家能支持自己下一个至少5年职业发展的创业公司；&lt;/li&gt;
  &lt;li&gt;做好长期资产配置，年化收益10%+；&lt;/li&gt;
  &lt;li&gt;读15本书；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这两年，疫情反复、行业巨变、社会整体经济环境等都不容易，虽然过程走得很艰难。再回首，歌且从容，杯且从容。正如辛弃疾《一剪梅·中秋元月》所云：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;忆对中秋丹桂丛，花也杯中，月也杯中。今宵楼上一尊同，云湿纱窗，雨湿纱窗。&lt;/p&gt;

  &lt;p&gt;浑欲乘风问化工，路也难通，信也难通。满堂唯有烛花红，歌且从容，杯且从容。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
        <pubDate>Thu, 30 Dec 2021 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/year-end-summary.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/year-end-summary.html</guid>
      </item>
    
      <item>
        <title>C++漫谈：从std::move到移动构造函数</title>
        <description>&lt;p&gt;半年之前，写到的一个代码（示例）：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push_back&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因为在搜素场景，耗时毫秒必争。cr的时候，review的同学建议改成：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push_back&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样修改后的好处：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;C++ 标准库使用比如vector::push_back等这类函数时，会对参数的对象进行复制，连数据也会复制.这就会造成对象内存的额外创建，本来原意是想把参数push_back进去就行了，通过std::move，可以避免不必要的拷贝操作；&lt;/li&gt;
  &lt;li&gt;std::move是将对象的状态或者所有权从一个对象转移到另一个对象，只是转移，没有内存的搬迁或者内存拷贝所以可以提高利用效率，改善性能；&lt;/li&gt;
  &lt;li&gt;对指针类型的标准库对象并不需要这么做；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在看move函数的原理之前，先看下面一个例子：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;str1: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;str2: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;str3: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;str1: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;str1: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;打印结果：&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;str2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;str3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;str1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;Program&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ended&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果对打印结果感到有点迷惑，再回头看std::move的原理：&lt;strong&gt;std::move并不能移动任何东西，它唯一的功能是将一个左值强制转化为右值引用，继而可以通过右值引用使用该值，以用于移动语义。从实现上讲，std::move基本等同于一个类型转换：static_cast(lvalue)&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;具体的函数原型：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 可能的实现， C++14 起&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 返回类型这么写是为了避免转发引用，即保持左值引用不变&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;templateconstexpr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remove_reference_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;noexcept&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只是将左值强制转化为右值引用，所以前4个结果都打印hello，就不足为怪了。对于第5个打印的结果，move(str1)返回的右值作为参数，就实现了真正的”move”？这里面，真正实现“move”操作的，以上面的例子为例，是string数据类型的移动构造函数。&lt;/p&gt;

&lt;p&gt;为了更形象说明这个问题，看个例子：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;cstring&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;cstdlib&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;普通构造函数...&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;strcpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;拷贝构造函数...&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;strcpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;移动构造函数...&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;析构函数&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//vs.push_back(move(s));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push_back&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;重点关注上面Str(Str &amp;amp;&amp;amp;s)移动构造函数，在移动构造函数中，原始指针赋值给了类指针变量，只涉及指针操作，不需要拷贝，所以速度快）；同时将原始指针指向空指针。所以在最上面的例子中，最后一个结果，打印出来的空（经过移动构造函数后，原始指针指向空指针）。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;移动构造函数的参数和拷贝构造函数不同，拷贝构造函数的参数是一个左值引用，但是移动构造函数的初值是一个右值引用。这意味着，移动构造函数的参数是一个右值或者将亡值的引用。也就是说，只用用一个右值，或者将亡值初始化另一个对象的时候，才会调用移动构造函数。而那个move语句，就是将一个左值变成一个将亡值。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;参考：&lt;a href=&quot;https://www.cnblogs.com/qingergege/p/7607089.html&quot;&gt;C++移动构造函数以及move语句简单介绍 &lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 25 Dec 2021 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/from-cpp-std-move-to-move-constructor.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/from-cpp-std-move-to-move-constructor.html</guid>
      </item>
    
      <item>
        <title>知行笔记：如何了解一个新行业</title>
        <description>&lt;p&gt;周末读&lt;a href=&quot;https://weread.qq.com/web/reader/f3b3255072563759f3b55c2kc81322c012c81e728d9d180&quot;&gt;《碳中和：从绿到金（2021年第5期）》&lt;/a&gt;时候，发现这是一本好书，让我了解了代码之外的另一个世界—碳中和行业发展基本情况、以及该行业龙头企业在做的事情和布局。在读的过程中，有比尔·盖茨写的一篇「气候经济的五大关键问题」文章，里面比尔·盖茨讲的他怎么研究气候变化这一全球性课题的思考方法。讲得特别有启发性，当你试着去了解一个不熟悉行业的时候。&lt;/p&gt;

&lt;p&gt;用比尔·盖茨自己的话说，这套思维框架对他大有助益：&lt;strong&gt;先试着掌握了整体情况，因为这可以让我获得相关的背景知识，便于我理解新的信息。另外，这也让我更容易记住这些信息，这个框架会帮助你厘清思路、直抵要害&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;作为一个外行，当我们试图去了解一个行业或者技术领域时，可能会存在『忽悠家』，喜欢用大实际上没多大用数字、特定术语，来描述这个行业的情况。不明真相的群众，往往容易被忽悠住、被吓唬住。在这个问题上，我们可以看看比尔·盖茨，在面对气候变化这一行业时，他是怎样去调研这个行业的。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;在刚开始研究气候变化时，我总是遇到一些令人费解的事实。&lt;strong&gt;一个问题是各种数字太大了，大到令人难以想象。谁知道510亿吨气体是什么样子？我看到的数据往往缺乏上下文，没有相关背景可查&lt;/strong&gt;。比如，有称欧洲的一项碳交易计划每年可使航空部门的碳足迹减少1700万吨。&lt;strong&gt;1700万吨听起来的确很多，果真如此吗？它在总量中的占比是多少？&lt;/strong&gt;我为我正在学习的知识建起了一个思维框架，这个框架让我明白了多少是很多、多少是很少、某个东西可能有多贵，等等。它帮我梳理出最具前景的想法。&lt;/p&gt;

  &lt;p&gt;1、在510亿吨中占多大比例？&lt;/p&gt;

  &lt;p&gt;每当读到与温室气体排放量相关的数字时，&lt;strong&gt;我都会迅速换算一下，看看它在总计510亿吨的年排放量中所占的比例&lt;/strong&gt;。开篇提到的航空部门的例子，那项计划一年可减少1700万吨的温室气体排放。用这个数字除以510亿吨后换算成百分比，这一幅度的减排量约占全球年排放量的0.03%。&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;这是不是一个有意义的贡献？答案取决于这个数字可能会上升还是保持不变？&lt;/strong&gt;如果该项目的起始点是1700万吨，后续还有巨大的减排潜力，这是一回事；如果该项目只能维持1700万吨的量，之后也不会发生变化，则是另外一回事。&lt;/p&gt;

  &lt;p&gt;令人遗憾的是，答案并不总是显而易见的，但这是一个很重要的问题。&lt;/p&gt;

  &lt;p&gt;我们为突破能源联盟资助的技术项目设定了门槛：在相关技术项目研发成功和全面实施之后，每年至少可以减少5亿吨的排放量，约为全球年排放量的1%。&lt;strong&gt;减排幅度永远都达不到1%的技术，不应该占用我们为实现零排放目标而安排的有限资源&lt;/strong&gt;。&lt;/p&gt;

  &lt;p&gt;对于这类技术的研发，可能还有其他很好的理由，但其中并不包括可以大规模减少温室气体排放量。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;气候变化，是一个极其宏大的全球性课题，要了解这一行业整体概貌，需要极强的宏观性思维。比尔·盖茨对气候变化这一行业宏观调研情况，反复咀嚼起来，非常有意思，他虽然没有具体说明他的这套思维框架完整体系，但是从比尔·盖茨在几个问题的思考方式上，可以见到一些端倪，甚至在做决策时的考虑角度。推荐感兴趣的或有心人，去读读这部分的内容。&lt;/p&gt;
</description>
        <pubDate>Sun, 24 Oct 2021 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/how-to-know-about-a-industry.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/how-to-know-about-a-industry.html</guid>
      </item>
    
      <item>
        <title>量化索引：Scalar Quantization标量量化</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;本篇是对之前写过的几篇涉及到向量索引博文的系统整理和补充，分别为：&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://yongyuan.name/blog/vector-ann-search.html&quot;&gt;向量索引&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://yongyuan.name/blog/asymmetry-problem-in-computer-vision.html&quot;&gt;Asymmetry Problem in Computer Vision&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://yongyuan.name/blog/ann-search.html&quot;&gt;再叙ANN Search&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://yongyuan.name/blog/index-billion-deep-descriptors.html&quot;&gt;十亿规模的深度描述子如何有效索引&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://yongyuan.name/blog/cbir-technique-summary.html&quot;&gt;基于内容的图像检索技术&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;在工作中遇到这样一个场景：通过多模态学习到的64维video embedding，在搜索精排的时候，需要实时取到前K（K&amp;gt;=300）个结果对应的video embedding，由于模型比较大，这个video embedding，不支持实时计算，而是在视频上传的时候，就被计算好。工程架构对存储和读取性能是有要求的，即不能直接将这64维embedding直接写到kiwi（redis改造后的数据库）里面。&lt;/p&gt;

&lt;p&gt;这个问题，可以简化为：有没有一种量化方法，将一个d维float型向量，encode为一个d维int8型的向量，这个d维int8型的向量经过decode后，与原始向量的误差尽可能小？这样一来，存储空间降低为原来的1/4倍，并且读取int8的性能比float型会快很多。答案是肯定的，这也是本篇博文要介绍总结的Scalar Quantization。&lt;/p&gt;

&lt;p&gt;Scalar Quantization，即标量量化。关于Scalar Quantization，网上资料比较多（&lt;a href=&quot;https://www.google.com.hk/search?q=Scalar+Quantization&amp;amp;newwindow=1&amp;amp;safe=strict&amp;amp;biw=1389&amp;amp;bih=766&amp;amp;sxsrf=ALeKk01QFkem3Lrzgoe3vrfd5uyeVr2RPQ%3A1624178770171&amp;amp;ei=UgDPYOjkCMWXr7wP98CqkA0&amp;amp;oq=Scalar+Quantization&amp;amp;gs_lcp=Cgdnd3Mtd2l6EAMyBwgjEOoCECcyBwgjEOoCECcyBwgjEOoCECcyBwgjEOoCECcyBwgjEOoCECcyBwgjEOoCECcyBwgjEOoCECcyBwgjEOoCECcyBwgjEOoCECcyBwgjEOoCECdQ06k-WOSrPmDwrD5oAXACeACAAckBiAHJAZIBAzItMZgBAKABAaABAqoBB2d3cy13aXqwAQrAAQE&amp;amp;sclient=gws-wiz&amp;amp;ved=0ahUKEwjo1ZW16aXxAhXFy4sBHXegCtIQ4dUDCBI&amp;amp;uact=5&quot;&gt;梯子&lt;/a&gt;），但小白菜在查过很多资料后，发觉能把Scalar Quantization向量量化过程讲清楚，并且还能剖析faiss中实现的Scalar Quantization，几乎没有。为了方便后面的同学理解，小白菜结合自己对Scalar Quantization原理与实现，做了整理。&lt;/p&gt;

&lt;h2 id=&quot;scalar-quantization原理&quot;&gt;Scalar Quantization原理&lt;/h2&gt;

&lt;p&gt;Scalar Quantization标量量化，分为3个过程：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;training过程，主要是训练encode过程，需要的一些参数，这些的参数，主要是每1维对应的最大值、最小值；&lt;/li&gt;
  &lt;li&gt;encode过程，将float向量量化为int8向量（int8是其中一种数据压缩形式，还有4比特之类的，这里主要以8比特说明原理）；&lt;/li&gt;
  &lt;li&gt;decode过程，将int8向量解码为float向量；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为了更好的说明Scalar Quantization的原理，小白菜画了Scalar Quantization标量量化原理框图，如下图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://yongyuan.name/imgs/posts/scalar-quantization-encode-decode1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;整个Scalar Quantization其实是很容易理解的，下面对训练、编码和解码做些说明。&lt;/p&gt;

&lt;h3 id=&quot;scalar-quantization训练&quot;&gt;Scalar Quantization训练&lt;/h3&gt;

&lt;p&gt;Scalar Quantization训练过程，如上图最左边所示，从样本中随机采样出N个样本后，训练过程主要是得到N个样本中每1维的最大值、最小值。得到最大值、最小值后，将它们保存下来即可。实际在训练的时候，N能大的时候，尽量大点。&lt;/p&gt;

&lt;h3 id=&quot;scalar-quantization编码&quot;&gt;Scalar Quantization编码&lt;/h3&gt;

&lt;p&gt;Scalar Quantization在编码的时候，对于一个d维的待编码的float型向量x = {x_1, x_2, …., x_d}，编码过程主要包含如下步骤：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;对每1维，求value_i = (x_i - min_i)/(max_i - min_i)；&lt;/li&gt;
  &lt;li&gt;对每1维，如果value_i &amp;lt; 0, 则value_i重置为value_i=0；如果value_i &amp;gt; 1, 则value_i重置为value_i=1。这里主要是对边界情况做下异常处理，理论情况下，是不会出现value_i &amp;lt; 0或者value_i &amp;gt; 1的；&lt;/li&gt;
  &lt;li&gt;对每1维，对应的编码 code_i = int(255*value_i)。为什么是255？可以思考下；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;整个过程，如上图中的中间图所示。这样就完成了float型向量x = {x_1, x_2, …., x_d}的编码，将向量的每1维，都变成了一个用int8表示的整型数据，也就是对应的Scalar Quantization的编码。&lt;/p&gt;

&lt;h3 id=&quot;scalar-quantization解码&quot;&gt;Scalar Quantization解码&lt;/h3&gt;

&lt;p&gt;Scalar Quantization解码过程，是解码的逆过程。解码过程步骤如下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;对每1维，x_i = min_i + (code_i + 0.5)*(max_i-min_i)/255，通过该式子，即可完成对第i维的解码。留个问题：为啥code_i需要加上0.5？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解码的过程，如上图最右边图所示。可以看到，整个训练、编码、解码过程，都是很容易理解的。下面再看看Scalar Quantization的实现。&lt;/p&gt;

&lt;h2 id=&quot;scalar-quantization实现&quot;&gt;Scalar Quantization实现&lt;/h2&gt;

&lt;p&gt;Scalar Quantization的训练、编码、解码实现，可以参考小白菜的实现&lt;a href=&quot;https://github.com/willard-yuan/cvtk/tree/master/scalar_quantization&quot;&gt;scalar_quantization&lt;/a&gt;。训练过程，就是计算各维最大值、最小值，自己实现的话，具体可以看&lt;a href=&quot;https://github.com/willard-yuan/cvtk/blob/master/scalar_quantization/train/src/sq_train.cpp#L68&quot;&gt;L68-L97&lt;/a&gt;。使用faiss的话，如下：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;faiss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexScalarQuantizer&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SQuantizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;faiss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScalarQuantizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QT_8bit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;faiss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;METRIC_L2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SQuantizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;train&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// SQuantizer.add(num_db, xb);    &lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;faiss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SQuantizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sq_train.cpp&lt;/code&gt;里面，对比了自己实现的训练过程结果和faiss训练出来的结果，训练出来的参数结果，两者是一致的。&lt;/p&gt;

&lt;p&gt;faiss encode的实现，如&lt;a href=&quot;https://github.com/facebookresearch/faiss/blob/master/faiss/impl/ScalarQuantizer.cpp#L328&quot;&gt;L328&lt;/a&gt;所示：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encode_vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vdiff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vdiff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Codec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode_component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中vdiff = max - min。faiss decode的实现，如&lt;a href=&quot;https://github.com/facebookresearch/faiss/blob/master/faiss/impl/ScalarQuantizer.cpp#L344&quot;&gt;L344&lt;/a&gt;所示：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decode_vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Codec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode_component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vmin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vdiff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;针对小白菜Scalar Quantization，小白菜实现的编解码过程，同时提供了faiss实现的接口调用，也提供了自己实现的接口调用，具体可以阅读&lt;a href=&quot;https://github.com/willard-yuan/cvtk/blob/master/scalar_quantization/scalar_quantization/int8_quan.cc&quot;&gt;int8_quan.cc&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;另外，关于Faiss实现的decode接口，由于采用了多线程方式，在实际使用的时候，&lt;strong&gt;当请求解码的数据量不够大的时候，多线程的方式，性能反而下降&lt;/strong&gt;，具体可以看这里提到的Issue: &lt;a href=&quot;https://github.com/facebookresearch/faiss/issues/1530&quot;&gt;&lt;strong&gt;Scale quantization decodes does not fast&lt;/strong&gt;&lt;/a&gt;。&lt;/p&gt;
</description>
        <pubDate>Sun, 20 Jun 2021 00:00:00 +0800</pubDate>
        <link>https://yongyuan.name//blog/scalar-quantization.html</link>
        <guid isPermaLink="true">https://yongyuan.name//blog/scalar-quantization.html</guid>
      </item>
    
  </channel>
</rss>
