<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/transform.xsl" type="text/xsl"?>
<!-- 根据 https://developer.mozilla.org/en-US/docs/XSL_Transformations_in_Mozilla_FAQ 的规定，FireFox 等某些浏览器必须在 rss 或 feed 标签之前有大于等于 512 个字节的内容，才会使用自定义 xslt，否则将会使用浏览器自带的 xslt 覆盖。-->
<!-- 欢迎来到蔓草札记的博客，这是一个记录自己生活的地方，没有固定的话题，也不会定期更新，只是有了一个自由的地方，方便的表达和记录自己的想法而已。
感受生活，感悟工作，感触心灵，蔓草札记欢迎你的订阅！。-->

<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>蔓草札记</title>
	<atom:link href="https://xuhehuan.com/feed" rel="self" type="application/rss+xml" />
	<link>https://xuhehuan.com</link>
	<description>感受生活，感悟工作，感触心灵，许合欢的博客。</description>
	<lastBuildDate>Tue, 29 Oct 2024 11:41:28 +0000</lastBuildDate>
	<language>zh-Hans</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	

<image>
	<url>https://xuhehuan.com/wp-content/uploads/2016/04/cropped-clover-512-32x32.jpg</url>
	<title>蔓草札记</title>
	<link>https://xuhehuan.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>图解卡尔曼滤波是如何工作的</title>
		<link>https://xuhehuan.com/2995.html</link>
		<comments>https://xuhehuan.com/2995.html#comments</comments>
		<pubDate>Mon, 27 Sep 2021 08:27:31 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[图像处理]]></category>
		<category><![CDATA[卡尔曼滤波]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2995</guid>
		<description><![CDATA[本文是国外博主 Bzarg 在 2015 年写的一篇图解。卡尔曼滤波可以被认为是一种数据融合算法，已有 50  [&#8230;]]]></description>
				<content:encoded><![CDATA[
<blockquote>
<p>本文是国外博主 Bzarg 在 2015 年写的<a href="https://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/" target="_blank" rel="noopener" rel="external nofollow" >一篇图解</a>。卡尔曼滤波可以被认为是一种数据融合算法，已有 50 多年的历史，是当今使用最重要和最常见的数据融合算法之一，它解决的是如何从多个不确定数据中提取相对精确的数据，而这包含了两个前提条件：<br />1，实践前提：这些数据满足高斯分布；<br />2，理论前提：一个高斯分布乘以另一个高斯分布可以得到第三个高斯分布，第三个高斯分布即为提取到相对精确的数据范围。</p>
</blockquote>
<div>
<p>我必须要告诉你一些关于卡尔曼滤波的知识，因为它的作用是非常惊人的。</p>
<p>令人惊讶的是，似乎很少有软件工程师和科学家了解它，这让我有点小失望，因为在一些含有不确定因素的场景里，如何去综合获取有效的信息，卡尔曼滤波是一个通用并且强有力的算法，有时候它提取精确信息的能力看上去就像是“见证奇迹的时刻”。如果看到这里你认为我说的话里有夸大的水分，你可以看下我<a href="http://www.bzarg.com/p/improving-imu-attitude-estimates-with-velocity-data" target="_blank" rel="noopener" rel="external nofollow" >之前发布的效果视频</a>，在这个 demo 里我通过检测角速度来获取一个自由物体的姿态，效果奇佳。</p>
<h2>什么是卡尔曼滤波？</h2>
<div>
<p>你可以在任何含有不确定因素的动态系统里使用卡尔曼滤波，而且你应该可以通过某种数学建模对系统下一步动向做一个大概的预测。尽管系统总是会受到一些未知因素的干扰，但是卡尔曼滤波总是可以用来提高系统预估的精确度，这样你就可以更加准确地知道到底发生了什么事情。而且它可以有效利用多个粗糙数据之间的关系，而单独面对这些数据你可能都无从下手。</p>
<p>卡尔曼滤波尤其适合动态系统，它对于内存要求极低（它仅需要保留系统上一个状态的数据，而不是一段跨度很长的历史数据），并且它运算很快，这使得它非常适合解决实时问题和应用于嵌入式系统。</p>
<p>如果你尝试去谷歌搜索相关资料，对于卡尔曼滤波的数学表达总是很枯燥并且难理解。这增加了大家的学习成本甚至打击了大家的学习兴趣，因为卡尔曼滤波真的是超级简单，当然前提是你用正确的方式去理解它。因此这就形成了一个很有意义的学术话题，我将会通过很多清晰、漂亮的图片以及颜色标注来阐述这个话题。对学习者的预备知识要求很简单，你只需要对概率论和矩阵运算有一些简单的基础知识。</p>
<p>我们从一个简单的例子入手，看下卡尔曼滤波可以解决什么问题。如果你想直接看公式推导，可以跳到<a href="#mathybits" target="_self" rel="noopener" target="_blank"  rel="external nofollow" >下一节</a>。</p>
<h2>利用卡尔曼滤波我们可以做什么？</h2>
<p>我们举一个玩具的例子：你开发了一款小型机器人，它可以在树林里自主移动，并且这款机器人需要明确自己的位置以便进行导航。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/robot_forest.png" class="highslide-image" onclick="return hs.expand(this);"><img decoding="async" class="alignnone size-full wp-image-2998" src="https://xuhehuan.com/wp-content/uploads/2021/09/robot_forest.png" alt="" width="300" height="160" /></a></p>
<p>我们可以通过一组状态变量 $\vec{x_k}$ 来描述机器人的状态，包括位置和速度：</p>
<p>$$<br />\vec{x_k} = (\vec{p}, \vec{v})<br />$$</p>
<p>注意这个状态仅仅是系统所有状态中的一部分，你可以选取任何数据变量作为观测的状态。在我们这个例子中选取的是位置和速度，它也可以是水箱中的水位，汽车引擎的温度，一个用户的手指在平板上划过的位置，或者任何你想要跟踪的数据。</p>
<div>
<div>我们的机器人同时拥有一个 GPS 传感器，精度在 10m。这已经很好了，但是对我们的机器人来说它需要以远高于 10m 的这个精度来定位自己的位置。在机器人所处的树林里有很多溪谷和断崖，如果机器人对位置误判了哪怕只是几步远的距离，它就有可能掉到坑里，所以仅靠 GPS 是不够的。</div>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/robot_ohnoes.png" class="highslide-image" onclick="return hs.expand(this);"><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-2999" src="https://xuhehuan.com/wp-content/uploads/2021/09/robot_ohnoes.png" alt="" width="403" height="380" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/robot_ohnoes.png 403w, https://xuhehuan.com/wp-content/uploads/2021/09/robot_ohnoes-300x283.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/robot_ohnoes-150x141.png 150w" sizes="(max-width: 403px) 100vw, 403px" /></a></p>
<p>同时我们可以获取到一些机器人的运动的信息：驱动轮子的电机指令对我们也有用处。如果没有外界干扰，仅仅是朝一个方向前进，那么下一个时刻的位置只是比上一个时刻的位置在该方向上移动了一个固定距离。当然我们无法获取影响运动的所有信息：机器人可能会受到风力影响，轮子可能会打滑，或者碰到了一些特殊的路况；所以轮子转过的距离并不能完全表示机器人移动的距离，这就导致通过轮子转动预测机器人位置不会非常准确。</p>
<div>
<p>GPS 传感器也会告知我们一些关于机器人状态的信息，但是会包含一些不确定性因素。我们通过轮子转动可以预知机器人是如何运动的，同样也有一定的不准确度。</p>
<p>如果我们综合两者的信息呢？可以得到比只依靠单独一个信息来源更精确的结果么？答案当然是 YES，这就是卡尔曼滤波要解决的问题。</p>
</div>
</div>
<h2>卡尔曼滤波如何看待你的问题<a id="mathybits"></a></h2>
<div>
<p>我们再来看下需要解决的问题，同样是上边的系统，系统状态包括位置和速度。</p>
</div>
<div>\begin{aligned}<br />\vec{x} = \begin{bmatrix} <br />p\\ <br />v <br />\end{bmatrix}<br />\end{aligned}</div>
</div>
<div>
<p>我们不知道位置和速度的准确值；但是我们可以列出一个准确数值可能落在的区间。在这个范围里，一些数值组合的可能性要高于另一些组合的可能性。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_0.png" class="highslide-image" onclick="return hs.expand(this);"><img decoding="async" class="alignnone size-full wp-image-3002" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_0.png" alt="" width="621" height="650" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_0.png 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_0-287x300.png 287w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_0-143x150.png 143w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
</div>
<div>卡尔曼滤波假设所有的变量（在我们的例子中为位置和速度）是随机的且符合高斯分布。每个变量有一个平均值 $\mu$，代表了随机分布的中心值（也表示这是可能性最大的值），和一个方差 $\sigma^2$，代表了不确定度。</div>
<div>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_1.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3003" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_1.png" alt="" width="621" height="553" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_1.png 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_1-300x267.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_1-150x134.png 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>在上图中位置和速度是无关联的，即系统状态中的一个变量并不会告诉你关于另一个变量的任何信息。</p>
</div>
<div>
<p>下图则展示了一些有趣的事情：在现实中，速度和位置是有关联的，如果已经确定位置的值，那么某些速度值存在的可能性更高。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_3.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3005" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_3.png" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_3.png 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_3-300x276.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_3-150x138.png 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>假如我们已知上一个状态的位置值，现在要预测下一个状态的位置值。如果我们的速度值很高，我们移动的距离会远一点。相反，如果速度慢，机器人不会走的很远。</p>
<p>这种关系在跟踪系统状态时很重要，因为它给了我们更多的信息：一个测量值告诉我们另一个测量值可能是什么样子。这就是卡尔曼滤波的目的，我们要尽量从所有不确定信息中提取有价值的信息！</p>
<p>这种关系可以通过一个称作<a href="https://xuhehuan.com/2975.html" target="_blank" rel="noopener">协方差</a>的矩阵来表述，简而言之，矩阵中的每个元素 $\Sigma_{ij}$ 表示了第 $i$ 个状态变量和第 $j$ 个状态变量之间的关系（你可能猜到了协方差矩阵是对称的，即交换下标 $i$ 和 $j$ 并无任何影响）。协方差矩阵通常表示为 $\Sigma$，它的元素则表示为 $\Sigma_{ij}$ 。</p>
<h2>利用矩阵描述问题</h2>
<p>我们对系统状态的分布建模为高斯分布，所以在 $k$ 时刻我们需要两个信息：最佳预估值 $\mathbf{\hat{x}_k}$（平均值，有些地方也表示为 $\mathbf{\mu}$），和它的协方差矩阵 $\mathbf{P_k}$</p>
<p>\begin{equation} \label{eq:statevars} <br />\begin{aligned} <br />\mathbf{\hat{x}}_k &amp;= \begin{bmatrix} <br />\text{position}\\ <br />\text{velocity} <br />\end{bmatrix}\\ <br />\mathbf{P}_k &amp;= <br />\begin{bmatrix} <br />\Sigma_{pp} &amp; \Sigma_{pv} \\ <br />\Sigma_{vp} &amp; \Sigma_{vv} \\ <br />\end{bmatrix} <br />\end{aligned} <br />\end{equation}</p>
<p>（这里我们只记录了位置和速度，但是如果需要的话我们可以把任何数据变量放进我们的系统状态里）</p>
<p>下一步，我们需要通过当前阶段（$k-1$ 时刻）的状态来预测下一阶段（$k$ 时刻）的状态。请注意，我们不知道状态的准确值，但是我们的预测函数并不在乎，它仅仅是对 $k-1$ 时刻所有可能值的范围进行预测转移，然后得出一个 $k$ 时刻新值的范围。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_7.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3009" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_7.jpg" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_7.jpg 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_7-300x276.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_7-150x138.jpg 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>我们可以通过一个状态转移矩阵 $\mathbf{F_k}$ 来描述这个转换</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_8.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3010" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_8.jpg" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_8.jpg 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_8-300x276.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_8-150x138.jpg 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>它把 $k-1$ 时刻所有可能的状态值转移到一个新的范围内，这个新的范围代表了系统新的状态值可能存在的范围，如果 $k-1$ 时刻估计值的范围是准确的话。</p>
<p>通过一个运动公式来表示这种预测下个状态的过程：</p>
<p>\begin{split} <br />\color{deeppink}{p_k} &amp;= \color{royalblue}{p_{k-1}} + \Delta t &amp;\color{royalblue}{v_{k-1}} \\ <br />\color{deeppink}{v_k} &amp;= &amp;\color{royalblue}{v_{k-1}} <br />\end{split}</p>
<p>整理为矩阵：</p>
<p>\begin{align} <br />\color{deeppink}{\mathbf{\hat{x}}_k} &amp;= \begin{bmatrix} <br />1 &amp; \Delta t \\ <br />0 &amp; 1 <br />\end{bmatrix} \color{royalblue}{\mathbf{\hat{x}}_{k-1}} \\ <br />&amp;= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} \label{statevars} <br />\end{align}</p>
<p>我们现在有了一个状态转移矩阵，可以简单预测下个状态，但仍不知道如何更新协方差矩阵。</p>
<p>这里我们需要另一个公式。如果我们对每个点进行矩阵 $\color{firebrick}{\mathbf{A}}$ 转换，它的协方差矩阵  $\Sigma$ 会发生什么变化呢？</p>
<p>这个简单，直接告诉你结果。</p>
<p>\begin{equation} <br />\begin{split} <br />Cov(x) &amp;= \Sigma\\ <br />Cov(\color{firebrick}{\mathbf{A}}x) &amp;= \color{firebrick}{\mathbf{A}} \Sigma \color{firebrick}{\mathbf{A}}^T <br />\end{split} \label{covident} <br />\end{equation}</p>
<p>结合 \eqref{covident} 和 \eqref{statevars}：</p>
<p>\begin{equation} <br />\begin{split} <br />\color{deeppink}{\mathbf{\hat{x}}_k} &amp;= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} \\ <br />\color{deeppink}{\mathbf{P}_k} &amp;= \mathbf{F_k} \color{royalblue}{\mathbf{P}_{k-1}} \mathbf{F}_k^T <br />\end{split} <br />\end{equation}</p>
<h2>外界作用力</h2>
<p>我们并没有考虑到所有影响因素。系统状态的改变并不只依靠上一个系统状态，外界作用力可能会影响系统状态的变化。</p>
<p>例如，跟踪一列火车的运动状态，火车驾驶员可能踩了油门使火车提速。同样，在我们机器人例子中，导航软件可能发出一些指令启动或者制动轮子。如果我们知道这些额外的信息，我们可以通过一个向量 $\color{darkorange}{\vec{\mathbf{u}_k}}$ 来描述这些信息，把它添加到我们的预测方程里作为一个修正。</p>
<p>假如我们通过发出的指令得到预期的加速度 $\color{darkorange}{a}$，上边的运动方程可以变化为：</p>
<p>\begin{split} <br />\color{deeppink}{p_k} &amp;= \color{royalblue}{p_{k-1}} + {\Delta t} &amp;\color{royalblue}{v_{k-1}} + &amp;\frac{1}{2} \color{darkorange}{a} {\Delta t}^2 \\ <br />\color{deeppink}{v_k} &amp;= &amp;\color{royalblue}{v_{k-1}} + &amp; \color{darkorange}{a} {\Delta t} <br />\end{split}</p>
<p>矩阵形式：</p>
<p>\begin{equation} <br />\begin{split} <br />\color{deeppink}{\mathbf{\hat{x}}_k} &amp;= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} + \begin{bmatrix} <br />\frac{\Delta t^2}{2} \\ <br />\Delta t <br />\end{bmatrix} \color{darkorange}{a} \\ <br />&amp;= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} + \mathbf{B}_k \color{darkorange}{\vec{\mathbf{u}_k}} <br />\end{split} <br />\end{equation}</p>
<p>$\mathbf{B}_k$ 称作控制矩阵， $\color{darkorange}{\vec{\mathbf{u}_k}}$ 称作控制向量（没有任何外界动力影响的系统，可以忽略该项）。</p>
<p>我们增加另一个细节，假如我们的预测转换矩阵不是 100% 准确呢，会发生什么？</p>
<h2>外界不确定性</h2>
<p class="md-end-block md-p"><span class="md-plain">如果状态只会根据系统自身特性演变那将不会有任何问题。如果我们可以把所有外界作用力对系统的影响计算清楚那也不会有任何问题。</span></p>
<p class="md-end-block md-p"><span class="md-plain">但是如果有些外力我们无法预测呢？假如我们在跟踪一个四轴飞行器，它会受到风力影响。如果我们在跟踪一个轮式机器人，轮子可能会打滑，或者地面上的突起会使它降速。我们无法跟踪这些因素，并且这些事情发生的时候上述的预测方程可能会失灵。</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们可以把“世界”中的这些不确定性统一建模，在预测方程中增加一个不确定项。</span></p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_9.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3011" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_9.jpg" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_9.jpg 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_9-300x276.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_9-150x138.jpg 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>这样，原始状态中的每一个点可以都会预测转换到一个范围，而不是某个确定的点。可以这样描述： $\color{royalblue}{\mathbf{\hat{x}}_{k-1}}$ 中的每个点移动到一个符合方差 $\color{mediumaquamarine}{\mathbf{Q}_k}$ 的高斯分布里。另一种说法，我们把这些不确定因素描述为方差为 $\color{mediumaquamarine}{\mathbf{Q}_k}$ 的高斯噪声。</p>
</div>
</div>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10a.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3012" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10a.jpg" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10a.jpg 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10a-300x276.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10a-150x138.jpg 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>这会产生一个新的高斯分布，方差不同，但是均值相同。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10b.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3013" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10b.jpg" alt="" width="621" height="621" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10b.jpg 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10b-300x300.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_10b-150x150.jpg 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>对 ${\color{mediumaquamarine}{\mathbf{Q}_k}}$ 简单叠加，可以拿到扩展的方差，这样就得到了完整的预测转换方程。</p>
<p>\begin{equation} <br />\begin{split} <br />\color{deeppink}{\mathbf{\hat{x}}_k} &amp;= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} + \mathbf{B}_k \color{darkorange}{\vec{\mathbf{u}_k}} \\ <br />\color{deeppink}{\mathbf{P}_k} &amp;= \mathbf{F_k} \color{royalblue}{\mathbf{P}_{k-1}} \mathbf{F}_k^T + \color{mediumaquamarine}{\mathbf{Q}_k} <br />\end{split} <br />\label{kalpredictfull} <br />\end{equation}</p>
<p>新的预测转换方程只是引入了已知的可以预测的外力影响因素。</p>
<p>新的不确定性可以通过老的不确定性计算得到，通过增加外界无法预测的、不确定的因素成分。</p>
<p>到这里，我们得到了一个模糊的估计范围，一个通过 $\color{deeppink}{\mathbf{\hat{x}}_k}$ 和 $\color{deeppink}{\mathbf{P}_k}$ 描述的范围。如果再结合我们传感器的数据呢？</p>
<h2>通过测量值精炼预测值</h2>
<p>我们可能还有一些传感器来测量系统的状态。目前我们不用太关心所测量的状态变量是什么。也许一个测量位置一个测量速度。每个传感器可以提供一些关于系统状态的数据信息，每个传感器检测一个系统变量并且产生一些读数。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_12.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3015" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_12.jpg" alt="" width="1242" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_12.jpg 1242w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_12-300x138.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_12-800x368.jpg 800w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_12-150x69.jpg 150w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_12-768x354.jpg 768w" sizes="(max-width: 1242px) 100vw, 1242px" /></a></p>
<p>注意传感器测量的范围和单位可能与我们跟踪系统变量所使用的范围和单位不一致。我们需要对传感器做下建模：通过矩阵 $\mathbf{H}_k$</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_13.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3016" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_13.jpg" alt="" width="1242" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_13.jpg 1242w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_13-300x138.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_13-800x368.jpg 800w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_13-150x69.jpg 150w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_13-768x354.jpg 768w" sizes="(max-width: 1242px) 100vw, 1242px" /></a></p>
<p>我们可以得到传感器读数分布的范围：</p>
<p>\begin{equation} <br />\begin{aligned} <br />\vec{\mu}_{\text{expected}} &amp;= \mathbf{H}_k \color{deeppink}{\mathbf{\hat{x}}_k} \\ <br />\mathbf{\Sigma}_{\text{expected}} &amp;= \mathbf{H}_k \color{deeppink}{\mathbf{P}_k} \mathbf{H}_k^T <br />\end{aligned} <br />\end{equation}</p>
<p>卡尔曼滤波也可以处理传感器噪声。换句话说，我们的传感器有自己的精度范围，对于一个真实的位置和速度，传感器的读数受到高斯噪声影响会使读数在某个范围内波动。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_14.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3017" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_14.jpg" alt="" width="1242" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_14.jpg 1242w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_14-300x138.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_14-800x368.jpg 800w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_14-150x69.jpg 150w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_14-768x354.jpg 768w" sizes="(max-width: 1242px) 100vw, 1242px" /></a></p>
<p>我们观测到的每个数据，可以认为其对应某个真实的状态。但是因为存在不确定性，某些状态的可能性比另外一些可能性更高。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_11.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3014" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_11.jpg" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_11.jpg 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_11-300x276.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_11-150x138.jpg 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>我们将这种不确定性（如传感器噪声）的协方差表示为 $\color{mediumaquamarine}{\mathbf{R}_k}$，读数的分布均值等于我们观察到传感器的读数，我们将其表示为 $\color{yellowgreen}{\vec{\mathbf{z}_k}}$。</p>
<p>所以现在我们有了两个高斯分布，一个来自于我们通过状态转移预测的预测值，另一个来自于我们实际传感器读数的测量值。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_4.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3006" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_4.jpg" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_4.jpg 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_4-300x276.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_4-150x138.jpg 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>我们必须尝试去把两者的数据预测值（<span style="color: #ff00ff;"><strong>粉色</strong></span>）与观测值（<span style="color: #99cc00;"><strong>绿色</strong></span>）融合起来。</p>
<p>所以我们得到的新的数据会长什么样子呢？ 对于任何可能的读数 $(z_1,z_2)$，我们都有两个相关的概率：<span style="color: #99cc00;">（1）</span>我们的传感器读数 $\color{yellowgreen}{\vec{\mathbf{z}_k}}$ 是 $(z_1,z_2)$ 的测量值的概率，以及<span style="color: #ff00ff;">（2）</span>先前估计值被认为是我们应该看到的读数的概率。</p>
<p>如果我们有两个概率，并且想知道两个概率都为真的机会，则将它们相乘。因此，我们对两个高斯分布进行了相乘处理：</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6a.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3008" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6a.png" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6a.png 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6a-300x276.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6a-150x138.png 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>相乘之后得到的即为重叠部分，这个区域同时属于两个高斯<span style="color: initial;">分布</span>。并且比单独任何一个区域都要精确。这个区域的平均值取决于我们更取信于哪个数据来源，这样我们也通过我们手中的数据得到了一个最好的估计值。</p>
<p>唔~ 这看上去像另一个高斯<span style="color: initial;">分布</span>。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3007" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6.png" alt="" width="621" height="572" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6.png 621w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6-300x276.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_6-150x138.png 150w" sizes="(max-width: 621px) 100vw, 621px" /></a></p>
<p>事实证明，两个独立的高斯分布相乘之后会得到一个新的具有其均值和协方差矩阵的高斯分布！新高斯分布的均值和方差均可以通过老的均值方差求得。下面开始推公式。</p>
<h2>高斯分布相乘</h2>
<p>首先考虑一维高斯情况：一个均值为 $\mu$，方差为 $\sigma^2$ 的高斯分布的形式为：</p>
<p>\begin{equation} \label{gaussformula} <br />\mathcal{N}(x, \mu,\sigma) = \frac{1}{ \sigma \sqrt{ 2\pi } } e^{ -\frac{ (x – \mu)^2 }{ 2\sigma^2 } } <br />\end{equation}</p>
<p>我们想知道两个高斯分布相乘会发生什么。蓝色曲线代表了两个高斯分布的交集部分。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_joint.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3018" src="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_joint.png" alt="" width="589" height="381" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gauss_joint.png 589w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_joint-300x194.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gauss_joint-150x97.png 150w" sizes="(max-width: 589px) 100vw, 589px" /></a></p>
<p>\begin{equation} \label{gaussequiv} <br />\mathcal{N}(x, \color{fuchsia}{\mu_0}, \color{deeppink}{\sigma_0}) \cdot \mathcal{N}(x, \color{yellowgreen}{\mu_1}, \color{mediumaquamarine}{\sigma_1}) \stackrel{?}{=} \mathcal{N}(x, \color{royalblue}{\mu’}, \color{mediumblue}{\sigma’}) <br />\end{equation}</p>
<p>将公式 $\eqref{gaussformula}$ 代入公式 $\eqref{gaussequiv}$，我们可以得到新的高斯分布的均值和方差如下所示：</p>
<p>\begin{equation} \label{fusionformula} <br />\begin{aligned} <br />\color{royalblue}{\mu’} &amp;= \mu_0 + \frac{\sigma_0^2 (\mu_1 – \mu_0)} {\sigma_0^2 + \sigma_1^2}\\ <br />\color{mediumblue}{\sigma’}^2 &amp;= \sigma_0^2 – \frac{\sigma_0^4} {\sigma_0^2 + \sigma_1^2} <br />\end{aligned} <br />\end{equation}</p>
<p>我们将其中的一小部分重写为 $\color{purple}{\mathbf{k}}$：</p>
<p>\begin{equation} \label{gainformula} <br />\color{purple}{\mathbf{k}} = \frac{\sigma_0^2}{\sigma_0^2 + \sigma_1^2} <br />\end{equation}</p>
<p>\begin{equation} <br />\begin{split} <br />\color{royalblue}{\mu’} &amp;= \mu_0 + &amp;\color{purple}{\mathbf{k}} (\mu_1 – \mu_0)\\ <br />\color{mediumblue}{\sigma’}^2 &amp;= \sigma_0^2 – &amp;\color{purple}{\mathbf{k}} \sigma_0^2 <br />\end{split} \label{update} <br />\end{equation}</p>
<p>这样一来，公式的形式就简单多了！我们顺势将公式 $\eqref{gainformula}$ 和 $\eqref{update}$ 的矩阵形式写在下面：</p>
<p>\begin{equation} \label{matrixgain} <br />\color{purple}{\mathbf{K}} = \Sigma_0 (\Sigma_0 + \Sigma_1)^{-1} <br />\end{equation}</p>
<p>\begin{equation} <br />\begin{split} <br />\color{royalblue}{\vec{\mu}’} &amp;= \vec{\mu_0} + &amp;\color{purple}{\mathbf{K}} (\vec{\mu_1} – \vec{\mu_0})\\ <br />\color{mediumblue}{\Sigma’} &amp;= \Sigma_0 – &amp;\color{purple}{\mathbf{K}} \Sigma_0 <br />\end{split} \label{matrixupdate} <br />\end{equation}</p>
<p>$\color{purple}{\mathbf{K}}$ 被称为卡尔曼增益，待会会用到。</p>
<p>简单，我们快结束了。</p>
<h2>公式汇总</h2>
<p>我们有两个高斯分布，一个是我们的预测值 $(\color{fuchsia}{\mu_0}, \color{deeppink}{\Sigma_0}) = (\color{fuchsia}{\mathbf{H}_k \mathbf{\hat{x}}_k}, \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T})$，另外一个是实际的测量值 $(\color{yellowgreen}{\mu_1}, \color{mediumaquamarine}{\Sigma_1}) = (\color{yellowgreen}{\vec{\mathbf{z}_k}}, \color{mediumaquamarine}{\mathbf{R}_k})$，我们将这两个高斯分布带入公式 $\eqref{matrixupdate}$ 中就可以得到二者的重叠区域：</p>
<p>\begin{equation} <br />\begin{aligned} <br />\mathbf{H}_k \color{royalblue}{\mathbf{\hat{x}}_k’} &amp;= \color{fuchsia}{\mathbf{H}_k \mathbf{\hat{x}}_k} &amp; + &amp; \color{purple}{\mathbf{K}} ( \color{yellowgreen}{\vec{\mathbf{z}_k}} – \color{fuchsia}{\mathbf{H}_k \mathbf{\hat{x}}_k} ) \\ <br />\mathbf{H}_k \color{royalblue}{\mathbf{P}_k’} \mathbf{H}_k^T &amp;= \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} &amp; – &amp; \color{purple}{\mathbf{K}} \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} <br />\end{aligned} \label {kalunsimplified} <br />\end{equation}</p>
<p>从公式 $\eqref{matrixgain}$ 我们可以知道，卡尔曼增益是：</p>
<p>\begin{equation} \label{eq:kalgainunsimplified} <br />\color{purple}{\mathbf{K}} = \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} ( \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} + \color{mediumaquamarine}{\mathbf{R}_k})^{-1} <br />\end{equation}</p>
<p>然后我们将公式 $\eqref{kalunsimplified}$ 与公式 $\eqref{eq:kalgainunsimplified}$ 中的 $\mathbf{H}_k$ 去除，同时将 $\color{royalblue}{\mathbf{P}_k’}$ 后面的 $\mathbf{H}_k^T$ 去除，我们可以得到最终的化简形式的更新方程：</p>
<p>\begin{equation} <br />\begin{split} <br />\color{royalblue}{\mathbf{\hat{x}}_k’} &amp;= \color{fuchsia}{\mathbf{\hat{x}}_k} &amp; + &amp; \color{purple}{\mathbf{K}’} ( \color{yellowgreen}{\vec{\mathbf{z}_k}} – \color{fuchsia}{\mathbf{H}_k \mathbf{\hat{x}}_k} ) \\ <br />\color{royalblue}{\mathbf{P}_k’} &amp;= \color{deeppink}{\mathbf{P}_k} &amp; – &amp; \color{purple}{\mathbf{K}’} \color{deeppink}{\mathbf{H}_k \mathbf{P}_k} <br />\end{split} <br />\label{kalupdatefull} <br />\end{equation}</p>
<p>\begin{equation} <br />\color{purple}{\mathbf{K}’} = \color{deeppink}{\mathbf{P}_k \mathbf{H}_k^T} ( \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} + \color{mediumaquamarine}{\mathbf{R}_k})^{-1} <br />\label{kalgainfull} <br />\end{equation}</p>
<p>至此，我们得到了每个状态的更新步骤，$\color{royalblue}{\mathbf{\hat{x}}_k’}$ 是我们最佳的预测值，接下来我们可以持续进行预测（通过 $\color{royalblue}{\mathbf{P}_k’}$），然后更新，重复上述过程！。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2021/09/kalflow.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3019" src="https://xuhehuan.com/wp-content/uploads/2021/09/kalflow.png" alt="" width="850" height="1100" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/kalflow.png 850w, https://xuhehuan.com/wp-content/uploads/2021/09/kalflow-232x300.png 232w, https://xuhehuan.com/wp-content/uploads/2021/09/kalflow-618x800.png 618w, https://xuhehuan.com/wp-content/uploads/2021/09/kalflow-116x150.png 116w, https://xuhehuan.com/wp-content/uploads/2021/09/kalflow-768x994.png 768w" sizes="(max-width: 850px) 100vw, 850px" /></a></p>
<h2>总结</h2>
<p>在上述所有数学公式中，你需要实现的只是公式 $\eqref{kalpredictfull}, \eqref{kalupdatefull}$ 和 $\eqref{kalgainfull}$（或者，如果你忘记了这些，可以从等式 $\eqref{covident}$ 和 $\eqref{matrixupdate}$ 重新推导所有内容。）</p>
<p>这将使你能够准确地对任何线性系统建模。对于非线性系统，我们使用扩展卡尔曼滤波器，该滤波器通过简单地线性化预测和测量值的均值进行工作。</p>
<p>如果我讲的还不错的话，希望读者也可以认识到卡尔曼滤波有多酷，并且在某个新的领域使用它。</p>
<p><br />欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2995.html">图解卡尔曼滤波是如何工作的</a></p>]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2995.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>一种协方差矩阵的几何解释</title>
		<link>https://xuhehuan.com/2975.html</link>
		<comments>https://xuhehuan.com/2975.html#respond</comments>
		<pubDate>Wed, 15 Sep 2021 10:04:05 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[机器学习]]></category>
		<category><![CDATA[协方差]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2975</guid>
		<description><![CDATA[这是一篇关于协方差矩阵几何解释的翻译文章。 原文地址：https://www.visiondummy.com/ [&#8230;]]]></description>
				<content:encoded><![CDATA[
<blockquote>
<p>这是一篇关于协方差矩阵几何解释的翻译文章。</p>
<p>原文地址：<a href="https://www.visiondummy.com/2014/04/geometric-interpretation-covariance-matrix/" target="_blank" rel="noopener" rel="external nofollow" >https://www.visiondummy.com/2014/04/geometric-interpretation-covariance-matrix/</a></p>
</blockquote>
<h2 id="介绍">介绍</h2>
<p>本文我们将通过探索线性变换与所得数据协方差之间的关系为协方差矩阵提供一个直观的几何解释。大部分教科书基于协方差矩阵的概念解释数据的形状，这里，我们采取一个相反的做法，根据数据的形状来解释协方差矩阵的概念。</p>
<p>在《<a href="https://www.visiondummy.com/2014/03/divide-variance-n-1/" target="_blank" rel="noopener" rel="external nofollow" >为什么样本方差除以 N-1？</a>》的文章中，我们讨论了方差的概念，并提供了众所周知的估算样本方差公式的推导和证明。这篇文章中使用的图 1 表明标准差（方差的平方根）提供了数据在特征空间上传播多少的量度。 </p>
<div id="attachment_2976" style="width: 524px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/gaussiandensity.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2976" class="size-full wp-image-2976" src="https://xuhehuan.com/wp-content/uploads/2021/09/gaussiandensity.png" alt="" width="514" height="396" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/gaussiandensity.png 514w, https://xuhehuan.com/wp-content/uploads/2021/09/gaussiandensity-300x231.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/gaussiandensity-150x116.png 150w" sizes="(max-width: 514px) 100vw, 514px" /></a><p id="caption-attachment-2976" class="wp-caption-text"><b>图 1. </b>高斯密度函数。对于正态分布的数据，68% 的样本落在平均值加减标准差定义的区间内。</p></div>
<p>我们发现，样本方差的无偏估计可由下式获得：</p>
<p>\begin{align}<br />\tag{1} \label{eq1}<br />\begin{split}<br />\sigma_{x}^{2} &amp;= \frac{1}{N-1} \sum_{i=1}^{N}(x_{i}-\mu)^{2} \\<br />&amp;= E[(x-E(x))(x-E(x))] \\<br />&amp;= \sigma ( x , x ) <br />\end{split}<br />\end{align}</p>
<p>然而，方差只能用于解释平行于特征空间轴方向的数据传播。考虑图 2 所示的 2D 特征空间： </p>
<div id="attachment_2977" style="width: 391px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/transformeddata.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2977" class="size-full wp-image-2977" src="https://xuhehuan.com/wp-content/uploads/2021/09/transformeddata.png" alt="" width="381" height="369" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/transformeddata.png 381w, https://xuhehuan.com/wp-content/uploads/2021/09/transformeddata-300x291.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/transformeddata-150x145.png 150w" sizes="(max-width: 381px) 100vw, 381px" /></a><p id="caption-attachment-2977" class="wp-caption-text"><b>图 2. </b>数据的对角线传播由协方差捕获</p></div>
<p>对于这个数据，我们可以计算出 x 方向上的方差 $\sigma(x, x)$ 和 y 方向上的方差 $\sigma(y, y)$。然而，数据的水平传播和垂直传播都不能解释明显的对角线传播相关性。图 2 清楚地显示，如果一个数据点的 x 值增加，则 y 值也将增加，这产生了正相关，这种相关性可以通过扩展方差概念到所谓的数据“协方差”捕捉到： </p>
<p>\begin{align}<br />\tag{2} \label{eq2}<br />\sigma(x,y)=E[(x-E(x))(y-E(y))]<br />\end{align}</p>
<p>对于 2D 数据，我们得到 $\sigma(x,x)$，$\sigma(y,y)$，$\sigma(x,y)$ 和 $\sigma(y,x)$，这些值可以用矩阵来表示，该矩阵叫做协方差矩阵： </p>
<p>\begin{align}<br />\tag{3} \label{eq3}<br />\Sigma = \left[ \begin{matrix} \sigma(x,x)&amp;\sigma(x,y)\\<br />\sigma(y,x)&amp;\sigma(y,y)<br />\end{matrix} \right]<br />\end{align}</p>
<p>如果 x 与 y 是正相关的，那么 y 和 x 也是正相关的，也就是说，$\sigma(x,y)=\sigma(y,x)$。因此，协方差矩阵始终是一个对称矩阵，其对角线上是方差，非对角线上是协方差。二维正态分布数据完全由其均值和 2&#215;2 协方差矩阵就可以完全解释，同样，一个 3&#215;3 协方差矩阵用于捕捉 3D 数据的传播，一个 NxN 协方差矩阵捕获 N 维数据的传播。</p>
<p>图 3 说明了数据的整体形状如何定义协方差矩阵： </p>
<div id="attachment_2978" style="width: 503px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/covariances.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2978" class="size-full wp-image-2978" src="https://xuhehuan.com/wp-content/uploads/2021/09/covariances.png" alt="" width="493" height="479" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/covariances.png 493w, https://xuhehuan.com/wp-content/uploads/2021/09/covariances-300x291.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/covariances-150x146.png 150w" sizes="(max-width: 493px) 100vw, 493px" /></a><p id="caption-attachment-2978" class="wp-caption-text"><b>图 3. </b>协方差矩阵定义了数据的形状。对角线传播由协方差捕获，而轴对齐传播由方差捕获。</p></div>
<h2 id="协方差矩阵的特征值分解">协方差矩阵的特征值分解</h2>
<p>在下一节，我们将讨论如何将协方差矩阵解释为白数据转换成我们观察到数据的线性算子。然而，在深入技术细节之前，对特征向量和特征值如何唯一地确定协方差矩阵（数据形状）有一个直观的认识是非常重要的。</p>
<p>正如我们在图 3 看到的，协方差矩阵定义了我们数据的传播（方差）和方向（协方差）。因此，如果我们想用一个向量和它的大小来表示协方差矩阵，我们应该简单地尝试找到指向数据最大传播方向上的向量，其大小等于这个方向上的传播（方差）。</p>
<p>如果我们定义这个向量为 $\vec{v}$，那么我们数据 $D$ 到这个向量上的映射为 $\vec{v}^\mathrm{T}D$，映射数据的方差是 $\vec{v}^\mathrm{T}\Sigma\vec{v}$。由于我们正在寻找指向最大方差方向的向量 $\vec{v}$，所以我们应该选择它的成分，使得映射数据的协方差矩阵 $\vec{v}^\mathrm{T}\Sigma\vec{v}$ 尽可能的大。最大化 $\vec{v}$ 的形式为 $\vec{v}^\mathrm{T}\Sigma\vec{v}$ 的任何函数，其中 $\vec{v}$ 是归一化单位向量，可以用一个所谓的瑞利商表示。通过设置 $\vec{v}$ 等于矩阵的最大特征特征向量 $\Sigma$ 可以获得这样瑞利商的最大值。</p>
<p>换句话说，协方差矩阵的最大特征向量总是指向数据最大方差的方向，并且该向量的大小等于相应的特征值，第二大特征向量总是正交于最大特征向量，并指向第二大数据的传播方向。</p>
<p>现在，让我们来看一些例子，在文章《<a href="https://xuhehuan.com/2916.html" target="_blank" rel="noopener">特征值和特征向量</a>》中，我们看到一个线性变换矩阵 $T$ 完全由它的特征向量和特征值定义，应用到协方差矩阵，这意味着： </p>
<p>\begin{align}<br />\tag{4} \label{eq4}<br />\Sigma\vec{v}=\lambda\vec{v}<br />\end{align}</p>
<p>其中 $\vec{v}$ 是 $\Sigma$ 的一个特征向量，而 $\lambda$ 是其对应的特征值。</p>
<p>如果我们数据的协方差矩阵是对角矩阵，且协方差为零，那么这意味着方差必须等于特征值 $\lambda$，如图 4 所示，特征向量用绿色和洋红色表示，特征值显然等于协方差矩阵的方差分量。 </p>
<div id="attachment_2979" style="width: 810px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors-1.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2979" class="size-full wp-image-2979" src="https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors-1.png" alt="" width="800" height="383" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors-1.png 800w, https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors-1-300x144.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors-1-150x72.png 150w, https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors-1-768x368.png 768w" sizes="(max-width: 800px) 100vw, 800px" /></a><p id="caption-attachment-2979" class="wp-caption-text"><b>图 4. </b>协方差矩阵的特征向量</p></div>
<p>然而，如果协方差矩阵不是对角的，即协方差不为零，那么情况就会复杂一些。特征值仍代表数据最大传播方向的方差大小，协方差矩阵的方差分量仍然表示 x 轴和 y 轴方向上的方差大小，但由于数据不是轴对齐的，所以这些值不再相同，如图 5 所示。</p>
<div id="attachment_2980" style="width: 810px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors_covariance.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2980" class="size-full wp-image-2980" src="https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors_covariance.png" alt="" width="800" height="382" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors_covariance.png 800w, https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors_covariance-300x143.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors_covariance-150x72.png 150w, https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors_covariance-768x367.png 768w" sizes="(max-width: 800px) 100vw, 800px" /></a><p id="caption-attachment-2980" class="wp-caption-text"><b>图 5. </b>特征值与方差</p></div>
<p>通过比较图 5 与图 4，很明显可以看到特征值表示沿特征向量方向数据的方差，而协方差矩阵的方差分量表示沿轴的传播，如果没有协方差，则这两个值是相等的。</p>
<h2 id="协方差矩阵作为线性变换">协方差矩阵作为线性变换</h2>
<p>现在，让我们暂时忘记协方差矩阵，图 3 中的每个示例都可以简单地认为是图 6 的一个线性变换实例： </p>
<div id="attachment_2981" style="width: 391px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2981" class="size-full wp-image-2981" src="https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata.png" alt="" width="381" height="369" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata.png 381w, https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata-300x291.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata-150x145.png 150w" sizes="(max-width: 381px) 100vw, 381px" /></a><p id="caption-attachment-2981" class="wp-caption-text"><b>图 6. </b>具有单位协方差矩阵的数据称为白数据。</p></div>
<p>设图 6 所示的数据为 $D$，则图 3 所示的每个实例可以通过一个线性变换从 $D$ 得到：</p>
<p>\begin{align}<br />\tag{5} \label{eq5}<br />D^\prime = TD<br />\end{align}</p>
<p>其中 $T$ 是变换矩阵，包括一个旋转矩阵 $R$ 和缩放矩阵 $S$： </p>
<p>\begin{align}<br />\tag{6} \label{eq6}<br />T = RS<br />\end{align}</p>
<p>这些矩阵定义为： </p>
<p>\begin{align}<br />\tag{7} \label{eq7}<br />R = \left[ \begin{matrix}<br />cos(\theta) &amp; -sin(\theta) \\<br />sin(\theta) &amp; cos(\theta)<br />\end{matrix}\right]<br />\end{align}</p>
<p>其中 $\theta$ 是旋转角度，以及：</p>
<p>\begin{align}<br />\tag{8} \label{eq8}<br />S = \left[ \begin{matrix}<br />s_x &amp; 0 \\<br />0 &amp; s_y<br />\end{matrix}\right]<br />\end{align}</p>
<p>$s_x$ 和 $s_y$ 分别是 x 方向和 y 方向的比例因子。</p>
<p>在下面的段落中，我们将讨论协方差矩阵 $\Sigma$ 与线性变换矩阵 $T= RS$ 之间的关系。</p>
<p>让我们先从未缩放（缩放相当于 1）和未旋转的数据开始，在统计学中，这往往为“白数据’，因为它的样本是从标准正态分布中抽取的，因此对应于白（不相关）噪声：</p>
<div id="attachment_2981" style="width: 391px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2981" class="size-full wp-image-2981" src="https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata.png" alt="" width="381" height="369" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata.png 381w, https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata-300x291.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/whiteneddata-150x145.png 150w" sizes="(max-width: 381px) 100vw, 381px" /></a><p id="caption-attachment-2981" class="wp-caption-text"><b>图 7. </b>白数据是具有单位协方差矩阵的数据。</p></div>
<p>这个“白”数据的协方差矩阵等于单位矩阵，使得方差和标准差等于 1，协方差等于 0： </p>
<p>\begin{align}<br />\tag{9} \label{eq9}<br />\Sigma = \left[ \begin{matrix}<br />\sigma_x^2 &amp; 0 \\<br />0 &amp; \sigma_y^2<br />\end{matrix}\right]<br />= \Bigg [ \begin{matrix}<br />1 &amp; 0 \\ 0 &amp; 1<br />\end{matrix} \Bigg]<br />\end{align}</p>
<p>现在让我们用因子 4 在 x 方向缩放数据：</p>
<p>\begin{align}<br />\tag{10} \label{eq10}<br />D^\prime = \left[ \begin{matrix}<br />4 &amp; 0 \\<br />0 &amp; 1<br />\end{matrix}\right]<br />\end{align}</p>
<p>数据 $D^\prime$ 现在如下： </p>
<div id="attachment_2982" style="width: 391px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/stretcheddata.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2982" class="wp-image-2982 size-full" src="https://xuhehuan.com/wp-content/uploads/2021/09/stretcheddata.png" alt="" width="381" height="369" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/stretcheddata.png 381w, https://xuhehuan.com/wp-content/uploads/2021/09/stretcheddata-300x291.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/stretcheddata-150x145.png 150w" sizes="(max-width: 381px) 100vw, 381px" /></a><p id="caption-attachment-2982" class="wp-caption-text"><b>图 8.</b> x 方向的方差导致水平缩放。</p></div>
<p>$D^\prime$ 的协方差 $\Sigma^\prime$ 现在是：</p>
<p>\begin{align}<br />\tag{11} \label{eq11}<br />\Sigma^\prime = \left[ \begin{matrix}<br />\sigma_x^2 &amp; 0 \\<br />0 &amp; \sigma_y^2<br />\end{matrix}\right]<br />= \Bigg [ \begin{matrix}<br />16 &amp; 0 \\ 0 &amp; 1<br />\end{matrix} \Bigg]<br />\end{align}</p>
<p>因此，$D^\prime$ 的协方差 $\Sigma^\prime$ 与线性变换矩阵 $T$ 有关系，$D^\prime=TD$，其中: </p>
<p>\begin{align}<br />\tag{12} \label{eq12}<br />T = \sqrt{\Sigma^\prime}<br />= \left[ \begin{matrix}<br />4 &amp; 0 \\<br />0 &amp; 1<br />\end{matrix}\right]<br />\end{align}</p>
<p>然而，尽管数据在 x 和 y 方向上缩放时等式（$\ref{eq12}$）成立，但当应用旋转时是否依然成立呢？为了研究一般情况下线性变换矩阵 $T$ 和协方差矩阵 $\Sigma^\prime$ 之间的关系，我们试图分解协方差矩阵为旋转矩阵和缩放矩阵的乘积。</p>
<p>正如我们之前所看到的，我们可以用特征向量和特征值表示协方差矩阵： </p>
<p>\begin{align}<br />\tag{13} \label{eq13}<br />\Sigma\vec{v}=\lambda\vec{v}<br />\end{align}</p>
<p>其中 $\vec{v}$ 是 $\Sigma$ 的一个特征向量，而 $\lambda$ 是其对应的特征值。</p>
<p>等式（$\ref{eq13}$）对矩阵 $\Sigma$ 的每个特征向量和特征值都成立。在 2D 情况下，我们会得到两个特征值和两个特征值，由公式（$\ref{eq13}$）定义的两个方程组可以使用矩阵符号来表示：</p>
<p>\begin{align}<br />\tag{14} \label{eq14}<br />\Sigma V = VL<br />\end{align}</p>
<p>其中 $V$ 的列是由 $\Sigma$ 的特征向量组成的矩阵，$L$ 是由对应特征值组成的对角矩阵。</p>
<p>这意味着我们可以将协方差矩阵表示为特征向量和特征值的函数： </p>
<p>\begin{align}<br />\tag{15} \label{eq15}<br />\Sigma = VLV^{-1}<br />\end{align}</p>
<p>方程（$\ref{eq15}$）就是所谓协方差矩阵的特征值分解，并可以使用奇异值分解算法来获得，而特征向量表示数据最大方差的方向，特征值表示那些方向方差的大小。换句话说，$V$ 表示旋转矩阵，而 $\sqrt{L}$ 表示一个缩放矩阵。协方差矩阵可以进一步分解为： </p>
<p>\begin{align}<br />\tag{16} \label{eq16}<br />\Sigma = RSSR^{-1}<br />\end{align}</p>
<p>其中 $R=V$ 是一个旋转矩阵，$S=\sqrt{L}$ 是一个缩放矩阵。</p>
<p>在等式（$\ref{eq6}$）中，我们定义了一个线性变换 $T= RS$。由于 $S$ 是对角缩放矩阵，所以 $S=S^\mathrm{T}$，此外，由于 $R$ 为正交矩阵，$R^{-1}=R^\mathrm{T}$。因此，$T^\mathrm{T}(RS)^\mathrm{T}=S^\mathrm{T}R^\mathrm{T}=SR^{-1}$ ，则协方差矩阵可以写为：</p>
<p>\begin{align}<br />\tag{17} \label{eq17}<br />\Sigma = RSSR^{-1}=TT^\mathrm{T}<br />\end{align}</p>
<p>换言之，如果我们应用由 $T=RS$ 定义的线性变换到图 7 的原始白数据 $D$，我们将得到旋转和缩放的数据 $D^\prime$ 及协方差矩阵 $TT^\mathrm{T} = \Sigma^\prime = RSSR^{-1}$。如图 10 所示：</p>
<div id="attachment_2983" style="width: 950px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/lineartrans.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2983" class="size-full wp-image-2983" src="https://xuhehuan.com/wp-content/uploads/2021/09/lineartrans.png" alt="" width="940" height="451" srcset="https://xuhehuan.com/wp-content/uploads/2021/09/lineartrans.png 940w, https://xuhehuan.com/wp-content/uploads/2021/09/lineartrans-300x144.png 300w, https://xuhehuan.com/wp-content/uploads/2021/09/lineartrans-800x384.png 800w, https://xuhehuan.com/wp-content/uploads/2021/09/lineartrans-150x72.png 150w, https://xuhehuan.com/wp-content/uploads/2021/09/lineartrans-768x368.png 768w" sizes="(max-width: 940px) 100vw, 940px" /></a><p id="caption-attachment-2983" class="wp-caption-text"><b>图 10. </b>协方差矩阵表示原始数据的线性变换。</p></div>
<p>图 10 的彩色箭头表示特征向量。最大特征向量，即与最大特征值对应的特征向量，总是指向数据最大方差的方向，并由此确定其方向。因为旋转矩阵的正交性，次特征向量总是正交于最大特征向量。</p>
<h2>总结</h2>
<p>在本文中，我们展示了观察数据的协方差矩阵与白（不相关）数据的线性变换直接相关，这种线性变换完全由数据的特征向量和特征值确定，而特征向量表示旋转矩阵，特征值对应于每个维度上缩放因子的平方。</p>
<h2>扩展阅读</h2>
<p>1，<a href="https://zhuanlan.zhihu.com/p/338335181" target="_blank" rel="noopener" rel="external nofollow" >神奇又好玩的协方差矩阵</a></p>
<p><br />欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2975.html">一种协方差矩阵的几何解释</a></p>]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2975.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>什么是特征值和特征向量？</title>
		<link>https://xuhehuan.com/2916.html</link>
		<comments>https://xuhehuan.com/2916.html#respond</comments>
		<pubDate>Tue, 14 Sep 2021 08:50:10 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[机器学习]]></category>
		<category><![CDATA[特征值]]></category>
		<category><![CDATA[特征向量]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2916</guid>
		<description><![CDATA[这是一篇关于特征值和特征向量理解和计算的翻译文章。 原文地址：https://www.visiondummy. [&#8230;]]]></description>
				<content:encoded><![CDATA[
<blockquote>
<p>这是一篇关于特征值和特征向量理解和计算的翻译文章。</p>
<p>原文地址：<a href="https://www.visiondummy.com/2014/03/eigenvalues-eigenvectors/" target="_blank" rel="noopener" rel="external nofollow" >https://www.visiondummy.com/2014/03/eigenvalues-eigenvectors/</a></p>
</blockquote>
<h5 id="介绍">介绍</h5>
<p>特征向量和特征值在计算机视觉和机器学习中有许多重要的应用，众所周知的例子是用于降维的 <a href="https://www.visiondummy.com/2014/05/feature-extraction-using-pca/" target="_blank" rel="noopener" rel="external nofollow" >PCA（主成分分析）</a>或用于人脸识别是<a href="https://www.visiondummy.com/2014/05/feature-extraction-using-pca/#A_practical_PCA_application_Eigenfaces" target="_blank" rel="noopener" rel="external nofollow" >特征脸</a>，特征向量和特征值的一个有趣应用在我的另一篇有关<a href="https://www.visiondummy.com/2014/04/draw-error-ellipse-representing-covariance-matrix/" target="_blank" rel="noopener" rel="external nofollow" >误差椭圆</a>的博文中提到。此外，特征值分解形成协方差矩阵几何解释的基础。在这篇文章中，我将简单的介绍这个数学概念，并且说明如何手动计算二维方形矩阵的特征值分解。</p>
<p>特征向量是一个特殊的向量，当在它上面应用线性变换时其方向保持不变。我们来看下面的图像，其中有三个向量被展示出来，绿色正方形表示施加到这三个向量上的线性变换。</p>
<div id="attachment_2918" style="width: 597px" class="wp-caption alignnone"><a href="https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors.png" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-2918" class="wp-image-2918 " src="https://xuhehuan.com/wp-content/uploads/2021/09/eigenvectors.png" alt="" width="587" height="252" /></a><p id="caption-attachment-2918" class="wp-caption-text"><em>当对特征向量进行线性变换 (如：缩放) 时，特征向量 (红色) 不会改变方向，但其他向量 (黄色) 会。</em></p></div>
<p>在这种情况下变换仅仅是水平方向乘以因子 2 和垂直方向乘以因子 0.5，所以变换矩阵 $A$ 定义为：</p>
<p>$$ A = \left[ \begin{array} { l l } { 2 } &amp; { 0 } \\ { 0 } &amp; { 0.5 } \end{array} \right] $$</p>
<p>通过应用这个变换，向量 $\vec{v} = (x , y)$ 被缩放为 $\vec{v{\prime}}=A\vec{v} $。上图表明一些向量（以红色显示）的方向不受此线性变换的影响，这些向量被称为变换的特征向量，并且唯一的定义了方阵 $A$。这种独特的、确定性的关系正是这些向量被称为“特征向量”（Eigen 在德语意思是“特定的”）的原因。</p>
<p>通常，$A$ 矩阵的特征向量 $\vec{v}$ 满足下列式子：</p>
<p><span style="font-size: revert; color: initial;">$$ \begin{equation} A\vec{v}=\lambda\vec{v}  \tag{1} \label{eq1} \end{equation}$$</span></p>
<p>其中 $\lambda$ 是所谓的“特征值”，它是一个标量值，这意味着，向量 $\vec{v}$ 上的线性变换 $A$ 完全由 $\lambda$ 定义。</p>
<p>我们可以重写（$\ref{eq1}$）式为：</p>
<p>\begin{align}<br />\tag{2} \label{eq2}<br />\begin{split}<br />A\vec{v}-\lambda\vec{v} = 0\\<br />\Rightarrow\vec{v}(A-\lambda I)=0<br />\end{split}<br />\end{align}</p>
<p>其中 $I$ 是和矩阵 $A$ 有相同维数的单位矩阵。</p>
<p>此时，假定 $\vec{v}$ 不是零向量，那么等式（$\ref{eq2}$）只能在 $(A-\lambda I)$ 不可逆的时候才能被定义，而如果一个方阵是不可逆的，这意味着它的行列式必须等于零，因此，要找到 $A$ 的特征向量，我们只需求解以下公式：</p>
<p>\begin{align} <br />Det(A-\lambda I)=0 \tag{3} \label{eq3}<br />\end{align}</p>
<p>在以下部分我们将通过解等式（$\ref{eq3}$）来确定矩阵 $A$ 的特征向量和特征值。本例中的矩阵 $A$ 被定义为：</p>
<p>$$ A = \left[ \begin{matrix} { 2 } &amp; { 3 } \\ { 2 } &amp; { 1 } \end{matrix} \right] \tag{4} \label{eq4} $$</p>
<h2 id="计算特征值">计算特征值</h2>
<p>为了确定本例中的特征值，我们将等式（$\ref{eq4}$）的矩阵 $A$ 代入到等式（$\ref{eq3}$）中，得到：</p>
<p>$$ Det \left( \begin{matrix} { 2 &#8211; \lambda } &amp; { 3 } \\ { 2 } &amp; { 1 &#8211; \lambda } \end{matrix} \right) = 0 \tag{5} \label{eq5} $$</p>
<p>计算行列式：</p>
<p>\begin{align} <br />\tag{6} \label{eq6}<br />\begin{split}<br />&amp; \quad {( 2 &#8211; \lambda ) ( 1 &#8211; \lambda ) &#8211; 6 = 0 } \\ <br />&amp; \Rightarrow {2 &#8211; 2 \lambda &#8211; \lambda + \lambda ^ { 2 } &#8211; 6 = 0 } \\ <br />&amp; \Rightarrow { \lambda ^ { 2 } &#8211; 3 \lambda &#8211; 4 = 0 }<br />\end{split}<br />\end{align}</p>
<p>为了解 $\lambda$ 的二次方程，我们找到判别式：</p>
<p>$$ D = b ^ { 2 } &#8211; 4 a c = ( &#8211; 3 ) ^ { 2 } &#8211; 4 * 1 * ( &#8211; 4 ) = 9 + 16 = 25 $$</p>
<p>由于判别式严格为正，这意味着对于 $\lambda$ 有两个不同的值：</p>
<p>\begin{align}<br />\tag{7} \label{eq7}<br />\begin{split}<br />\begin{array} { l }<br />{ \lambda _ { 1 } = \frac { &#8211; b &#8211; \sqrt { D } } { 2 a } = \frac { 3 &#8211; 5 } { 2 } = &#8211; 1 } \\ <br />{ \lambda _ { 2 } = \frac { &#8211; b + \sqrt { D } } { 2 a } = \frac { 3 + 5 } { 2 } = 4 } <br />\end{array}<br />\end{split}<br />\end{align}</p>
<p>现在我们已经确定了两个特征值 $\lambda_1$ 和 $\lambda_2$。需要注意的是大小为 $N*N$ 的方阵总是具有 $N$ 个特征值，每一个特征值对应一个特征向量，特征值指定特征向量的大小。</p>
<h2 id="计算第一个特征向量">计算第一个特征向量</h2>
<p>现在，我们可以将等式（$\ref{eq7}$）的特征值代入到等式（$\ref{eq1}$）来确定特征向量，然后通过求解方程组得到特征向量。</p>
<p>我们首先对特征值 $\lambda_1$ 求解其对应的特征向量：</p>
<p>\begin{align}<br />\left[ \begin{array} { l } { 2 } &amp; { 3 } \\ { 2 } &amp; { 1 } \end{array} \right] <br />\left[ \begin{array} { l } { x _ { 11 } } \\ { x _ { 12 } } \end{array} \right]<br />= &#8211; 1 <br />\left[ \begin{array} { l } { x _ { 11 } } \\ { x _ { 12 } } \end{array} \right]<br />\end{align}</p>
<p>由于这仅仅是方程组的矩阵符号，我们写出它等价形式的方程组：</p>
<p>\begin{align}<br />\tag{8} \label{eq8} <br />\left\{<br />\begin{array} { l } <br />{ 2 x _ { 11 } + 3 x _ { 12 } = &#8211; x _ { 11 } } \\<br />{ 2 x _ { 11 } + x _ { 12 } = &#8211; x _ { 12 } } <br />\end{array}<br />\right.<br />\end{align}</p>
<p>根据方程组第一个等式可以得到：</p>
<p>\begin{align}<br />\tag{9} \label{eq9} <br />x _ { 11 } = &#8211; x _ { 12 }<br />\end{align}</p>
<p>因为特征向量仅仅代表一个方向（相应特征值表示幅度），特征向量的所有标量倍数都是平行于该特征向量的向量，因此它们是等效的（如果我们对它做向量标准化，则它们是相等的）。为进一步求解上面的方程组，我们可以任意选择一个 $x_{11}$ 或 $x_{12}$ 的真实值，并用等式（\ref{eq9}）来确定另一个。</p>
<p>对于这个例子，我们随意地选择 $x_{12}= 1$，进而得到 $x_{11}=-1$，因此，对应于特征值 $\lambda_1$ 的特征向量是：</p>
<p>\begin{align}<br />\tag{10} \label{eq10} <br />\vec{v_1} = \left[ \begin{matrix} { &#8211; 1 } \\ { 1 } \end{matrix} \right]<br />\end{align}</p>
<h2 id="计算第二个特征向量">计算第二个特征向量</h2>
<p>第二个特征向量的计算类似于第一特征向量。我们现在将 $\lambda_2 = 4$ 代入等式（\ref{eq1}），得到：</p>
<p>\begin{align}<br />\tag{11} \label{eq11} <br />\left[ \begin{matrix} { 2 } &amp; { 3 } \\ { 2 } &amp; { 1 } \end{matrix} \right] <br />\left[ \begin{matrix} { x _ { 21 } } \\ { x _ { 22 } } \end{matrix} \right] <br />= 4 * \left[ \begin{array} { l } { x _ { 21 } } \\ { x _ { 22 } } \end{array} \right]<br />\end{align}</p>
<p>写成方程组的形式，等价于：</p>
<p>\begin{align}<br />\tag{12} \label{eq12} <br />\left\{<br />\begin{array} { l } <br />{ 2 x _ { 21 } + 3 x _ { 22 } = 4 x _ { 21 } } \\ <br />{ 2 x _ { 21 } + x _ { 22 } = 4 x _ { 22 } } <br />\end{array}<br />\right.<br />\end{align}</p>
<p>根据方程组第一个等式可以得到：</p>
<p>\begin{align}<br />\tag{13} \label{eq13} <br />x _ { 22 } = \frac { 3 } { 2 } x _ { 21 }<br />\end{align}</p>
<p>然后，我们任意地选择 $x_{21}= 2$，并找到 $x_{22}= 3$，因此，对应于特征值 $\lambda_2=4$ 的特征向量是：</p>
<p>\begin{align}<br />\tag{14}\label{eq14}<br />\vec{v_2} = \left[ \begin{array} { l } { 3 } \\ { 2 } \end{array} \right]<br />\end{align}</p>
<h2 id="总结">总结</h2>
<p>在本文中，我们回顾了特征向量和特征值的理论概念。这些概念对于计算机视觉和机器学习中使用的许多技术都非常重要，例如通过 PCA 进行降维，或用 EigenFaces 进行脸部识别。</p>
<p><br />欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2916.html">什么是特征值和特征向量？</a></p>]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2916.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>手机分辨率多少才够用？</title>
		<link>https://xuhehuan.com/2906.html</link>
		<comments>https://xuhehuan.com/2906.html#respond</comments>
		<pubDate>Fri, 12 Mar 2021 09:43:00 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[业界动态]]></category>
		<category><![CDATA[分辨率]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2906</guid>
		<description><![CDATA[2012 年 10 月 18 日 HTC 在日本召开发布会，推出了一款被命名为 HTC J Butterfly [&#8230;]]]></description>
				<content:encoded><![CDATA[
<div>2012 年 10 月 18 日 HTC 在日本召开发布会，推出了一款被命名为 HTC J Butterfly 的机型，它是全球首款配备了 1080P 分辨率屏幕的智能手机产品，它的诞生正式宣告智能手机告别 720P 时代，进入到了“全高清”的纪元。然而，在 J Butterfly 诞生八年多后的今天，整个智能手机行业的主流屏幕分辨率依旧停滞不前。2021 年 1 月 15 日，安兔兔方面发布了 2020 年第四季度的用户偏好排行调查报告，在其中所有参与统计的数据中，屏幕横向分辨率为 1080P 的机型比例高达 86% 以上，占据了绝对主流的市场份额。</div>



<figure class="wp-block-image size-large"><a href="https://xuhehuan.com/wp-content/uploads/2021/03/an-tu-tu-shou-ji-fen-bian-lv-fen-bu.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" width="650" height="542" src="https://xuhehuan.com/wp-content/uploads/2021/03/an-tu-tu-shou-ji-fen-bian-lv-fen-bu.jpg" alt="" class="wp-image-2913" srcset="https://xuhehuan.com/wp-content/uploads/2021/03/an-tu-tu-shou-ji-fen-bian-lv-fen-bu.jpg 650w, https://xuhehuan.com/wp-content/uploads/2021/03/an-tu-tu-shou-ji-fen-bian-lv-fen-bu-300x250.jpg 300w, https://xuhehuan.com/wp-content/uploads/2021/03/an-tu-tu-shou-ji-fen-bian-lv-fen-bu-150x125.jpg 150w" sizes="(max-width: 650px) 100vw, 650px" /></a></figure>



<div>众所周知，智能手机的技术进步水平和性能提升速度，近年来绝对是“突飞猛进”的，如今主流智能手机在性能上也早已超过 2012 年的 HTC J Butterfly 数倍之多。那么，为什么智能手机的屏幕分辨率却并没有什么进步呢？</div>



<p><strong>首先，屏幕技术和手机性能不背锅。</strong>事实上早在 2013 年 12 月 18 日，vivo 就推出了行内第一款屏幕分辨率达到 2560×1440 的 Xplay 3S；到了 2015 年 9 月 2 日，我们也迎来了首款 4K 屏幕分辨率的智能手机产品 —— 索尼 Z5 Premium。</p>



<p>当然，可能有的朋友知道，近年来智能手机用户比起分辨率，普遍更重视高刷新率，这是否意味着手机厂商会为了更高的刷新率，而放弃高分屏呢？某种程度上来说这种看法有一定的道理，因为无论高分辨率还是高刷新率，本身都需要更高的屏幕传输带宽。对于性能平庸的一些主控来说，确实可能存在两者无法同时共存的情况，但对于当今的主流高端平台来说，它们的 GPU 性能与内存带宽，应对“高分屏 + 高刷屏“其实都毫无问题。比如说 OPPO 在 2020 年发布的 Find X2 Pro、一加在 2020 年推出的一加 8 Pro，都使用了骁龙 865+120Hz 3K 屏的组合，并且从实际体验上来说，也完全不存在卡顿的问题。</p>



<p>不仅如此，得益于近年来的技术进步，顶级高分辨率屏幕的功耗问题也早以得到解决。以三星近日刚刚推出的 Galaxy S21 Ultra 为例，通过采用 10Hz-120Hz 的可变刷新率设计以及新的 M12 发光材料，其所使用的那块 6.8 英寸 3200×1440 屏幕，在维持 3K 分辨率和 1500nit 峰值亮度的同时，还能同时开启高刷新率模式并保持不错的续航能力。</p>



<p><strong>事实上，人眼的分辨率才是高分屏的痛点。</strong>人眼其实没有“分辨率”这个指标，应该用“视觉张角”来评价人眼的分辨能力。根据目前的研究表明，人眼的理论分辨能力大约为 20 角秒（1 度＝60 分＝3600 秒的“秒”），但是实际分辨能力没这么高。对于最容易分辨的 5000 纳米波长左右的黄绿光（其他波长的光线分辨能力会更差一些），眼神比较好的（像白天能看到星星二战时期日本王牌飞行员），可以达到 1 角分；视力 1.5 的普通人，3～5 角分；近视眼、远视眼，带散光……就比较杯具了。</p>



<p>假设用智能手机的都是人类中眼神比较好的人，按照分辨能力 1 角分来分析计算：1 角分＝1/60 度＝2Pi/（60×360）＝0.0003 弧度，即：在 1 米处能够看到的最小点距为 0.3 毫米；相应的，在 1 米处放置一个屏幕的话，它的分辨率如果达到 1 英寸 /0.3 毫米＝25.4/0.3＝85PPI，就应该足够了。当然，你并不是每次都把每种屏幕放在离你 1 米远的地方：如果屏幕离你近，分辨率需要相应增加；如果屏幕离你远，分辨率减小一些你也不会觉得观看感觉差。</p>



<p>下面咱们就计算一下，在各种屏幕的典型使用距离下，分辨率应该达到什么等级：</p>



<blockquote class="wp-block-quote"><p>手机：一般观看距离在 25 厘米～30 厘米之间，分辨率应该达到：85×（100/30～100/25）＝283PPI～340PPI；<br>平板：一般观看距离在 40 厘米～50 厘米之间，分辨率应该达到：85×（100/50～100/40）＝170PPI～213PPI；<br>液晶电视：一般观看距离在 2 米～4 米之间，分辨率应该达到：85×（1/4～1/2）＝21PPI～43PPI；</p></blockquote>



<p>应该说，苹果把 300PPI 叫做视网膜屏幕还是有点依据的，在手机的正常观看距离内，没有必要追求太高的 PPI，不是技术达不到，而是你的眼睛没有那么好。因此，按照 300PPI 够用，350PPI 过极限来算，目前主流的 1920*1080 屏幕可以支持到 6-7 寸的屏幕，而 7 寸已经算是平板手机了，也就是说对于手机，1080P 的分辨率足够用，再高不是不行，而是在正常距离内大部分人的眼睛分辨率不出来，没有实际的意义。</p>



<p>至于 2K 屏幕，甚至更高 PPI 的屏幕流行，更多是市场宣传的需要，否者，除非你真有鹰的眼睛，或者把手机贴到鼻子尖上用（屏幕距离眼睛 10cm，你就需要 850PPI 的屏幕，2K 屏幕就不够用了，4K 屏幕才能满足你）。虽然 2K 屏幕从体验上说没有必要，从功耗上看有一些劣势，但是市面上 2K 屏幕的产品依然不少，而且没有大规模的反应出来问题。这说明目前的技术条件下 2K 屏幕依然有生命力。所以，对于 2K 屏幕，消费者无可无不可，用了即使感知不出来但是看着 2K 的参数就爽，增加的那点功耗对现在的电池来说完全不是问题。</p>
<p><br />欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2906.html">手机分辨率多少才够用？</a></p>]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2906.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>记 Python whl is not a supported wheel on this platform 的解决方案</title>
		<link>https://xuhehuan.com/2807.html</link>
		<comments>https://xuhehuan.com/2807.html#respond</comments>
		<pubDate>Fri, 15 Mar 2019 03:13:28 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[机器学习]]></category>
		<category><![CDATA[pip]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2807</guid>
		<description><![CDATA[按照官方教程安装 PyTorch 时出现了类似 *****.whl is not a supported wh [&#8230;]]]></description>
				<content:encoded><![CDATA[
<p>按照官方教程安装 PyTorch 时出现了类似  *****.whl is not a supported wheel on this platform 的错误，最早怀疑是 Python 版本的问题，查了些资料但都没找到点子上，偶然在查找过程中发现了一个查看 pip 支持文件格式的方法，几番确认下来，发现是 pip 版本太老，不支持这个 whl 文件的缘故。 </p>



<p>那么如何根据 Python 和 pip 查看本机支持哪些文件格式， 选择合适的 whl 文件呢？</p>



<p>可以用下面两个命令尝试下，前面一个是高版本 pip 的，后面一个是低版本的。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># 高版本
import pip._internal
print(pip._internal.pep425tags.get_supported())
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># 低版本
import pip
print(pip.pep425tags.get_supported())</pre>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="600" height="263" src="https://xuhehuan.com/wp-content/uploads/2019/03/pip-python-get-supported.jpg" alt="" class="wp-image-2808" srcset="https://xuhehuan.com/wp-content/uploads/2019/03/pip-python-get-supported.jpg 600w, https://xuhehuan.com/wp-content/uploads/2019/03/pip-python-get-supported-150x66.jpg 150w, https://xuhehuan.com/wp-content/uploads/2019/03/pip-python-get-supported-300x132.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></figure>



<p>在这次碰到的问题中，先是用后面一个方法查看了系统所能支持的文件版本，对比发现官方没有合适的 whl  文件，接着就升级 pip，升级后就得用前面一个高版本的方法查看支持的文件格式了，选择正确的版本，顺利安装。</p>
<p><br />欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2807.html">记 Python whl is not a supported wheel on this platform 的解决方案</a></p>]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2807.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress 代码高亮插件 Enlighter</title>
		<link>https://xuhehuan.com/2789.html</link>
		<comments>https://xuhehuan.com/2789.html#respond</comments>
		<pubDate>Mon, 04 Mar 2019 12:19:58 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[博客经验]]></category>
		<category><![CDATA[Enlighter]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2789</guid>
		<description><![CDATA[2018 年 12 月，WordPress 5.0 正式版发布，主要有两个更新：内置默认编辑器由 TinyMC [&#8230;]]]></description>
				<content:encoded><![CDATA[
<p>2018 年 12 月，WordPress 5.0 正式版发布，主要有两个更新：内置默认编辑器由 TinyMCE 更换为更换为 Gutenberg（古腾堡）；新增官方主题 Twenty Nineteen。因为之前在老版本时通过插件的方法体验过古登堡编辑器，没什么大的问题，这次升级后便也懒得改回老的编辑器了，与时俱进还是挺重要的嘛。</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="600" height="314" src="https://xuhehuan.com/wp-content/uploads/2019/03/wordpress-enlighter-code-format.jpg" alt="" class="wp-image-2804" srcset="https://xuhehuan.com/wp-content/uploads/2019/03/wordpress-enlighter-code-format.jpg 600w, https://xuhehuan.com/wp-content/uploads/2019/03/wordpress-enlighter-code-format-150x79.jpg 150w, https://xuhehuan.com/wp-content/uploads/2019/03/wordpress-enlighter-code-format-300x157.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></figure>



<p>但在使用时发现之前使用的 <a href="https://wordpress.org/plugins/crayon-syntax-highlighter/" target="_blank"  rel="external nofollow" >Crayon Syntax Highlighter</a> 代码高亮插件不好用了，插件页面也显示三年未更新了，得重新寻找一个，最后确定为 <a href="https://wordpress.org/plugins/enlighter/" target="_blank"  rel="external nofollow" >Enlighter</a>。体验下来，虽然对 Enlighte 的样式并不十分满意，但最终确定为 Enlighter 是其对原来的 Crayon Syntax Highlighter 代码块可以兼容。</p>



<p>换起来挺简单的：安装 Enlighter，停用 / 删除 Crayon Syntax Highlighter，再按照下面配置一下，就可以实现对原有 Crayon Syntax Highlighter 代码块的兼容了：</p>



<p>1、Enlighter ->  Option -> Block CSS Selector 设为 </p>



<pre class="wp-block-preformatted">pre.EnlighterJSRAW, pre[class="lang:"][class~="decode:true"] </pre>



<p>2、Enlighter ->  Option -> Inline CSS Selector 设为 </p>



<pre class="wp-block-preformatted">code.EnlighterJSRAW, span[class="lang:"][class~="decode:true"][class~="crayon-inline"]</pre>



<p>3、Enlighter ->  BETA -> Dynamic Resource Invocation (DRI) 必须关闭</p>



<p>虽然 Enlighte 的配置选项不少，但基本都用的默认，最后只是选了个看得顺眼的主题，配置些基础选项就完成了。本博客设置的是 Droide 主题， Code-Indent 设为 4 Spaces，去除了 Info-button 的勾选。</p>



<p>参考文章：<br>1，<a href="http://www.spirithy.com/2019/02/15/enlighter-replace-crayon-syntax-highlighter/" target="_blank" rel="noreferrer noopener" aria-label="使用 Enlighter 替换 Crayon Syntax Highlighter（在新窗口打开）" rel="external nofollow" >使用 Enlighter 替换 Crayon Syntax Highlighter</a></p>
<p><br />欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2789.html">WordPress 代码高亮插件 Enlighter</a></p>]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2789.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PyTorch 中 Tensor 和 PILImage 的相互转换</title>
		<link>https://xuhehuan.com/2769.html</link>
		<comments>https://xuhehuan.com/2769.html#comments</comments>
		<pubDate>Wed, 27 Feb 2019 08:44:45 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[机器学习]]></category>
		<category><![CDATA[PyTorch]]></category>
		<category><![CDATA[Tensor]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2769</guid>
		<description><![CDATA[在 PyTorch 实现图像的 Normalize 和 反 Normalize 的实验中，发现经过这两个转换后 [&#8230;]]]></description>
				<content:encoded><![CDATA[
<p>在 PyTorch 实现图像的 Normalize 和 反 Normalize 的实验中，发现经过这两个转换后存储的图像和原始图像虽然视觉上没什么差异，但在二进制上却不能完全匹配，这里记录下问题的原因分析及最终的解决过程。</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="600" height="400" src="https://xuhehuan.com/wp-content/uploads/2019/02/ancient-meditation-architecture.jpg" alt="" class="wp-image-2785" srcset="https://xuhehuan.com/wp-content/uploads/2019/02/ancient-meditation-architecture.jpg 600w, https://xuhehuan.com/wp-content/uploads/2019/02/ancient-meditation-architecture-150x100.jpg 150w, https://xuhehuan.com/wp-content/uploads/2019/02/ancient-meditation-architecture-300x200.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></figure>



<p>下面是抽象出来的问题及解决问题的代码：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import torch
from PIL import Image
import torchvision.transforms as transforms

# 1. Read image
imgFolder = "/home/test/image/"
imgSrc = Image.open(imgFolder + "src.jpg")
imgSrc.save(imgFolder + "./00src.png")

# 2. Save source image
tensorSrc = transforms.ToTensor()(imgSrc)
imgRlt = transforms.ToPILImage()(tensorSrc)
imgRlt.save(imgFolder + "./00rlt.png")

# Normalized transform
tensorTrans = tensorSrc.clone()
tensorTrans = transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))(tensorTrans)
tensorRevert = tensorTrans * 0.5 + 0.5

# 3. Usual revert transform
imgRevert = transforms.ToPILImage()(tensorRevert)
imgRevert.save(imgFolder + "01rlt.png")

# 4. Rectified revert transform
if isinstance(tensorTrans, torch.FloatTensor):
    imgRevert = tensorRevert.mul(255).round().byte()
    imgRevert = transforms.ToPILImage()(imgRevert)
    imgRevert.save(imgFolder + "02rlt.png")</pre>



<p>主要对比的是上面代码中存储的几幅图像：</p>



<p>（1）输入图像 src.jpg，读入后为了防止编码差异， 便于和后面的结果图进行二进制对比，直接存储为 00src.png；<br>（2）将图像直接转换成 Tensor 并立即重新转换成 PILImage，存储为 00rlt.png；<br>（3）将图像转换成的 Tensor 经过 Normalize 和按照 Normalize 定义推算出的反向计算反转回图像，存储为 01rlt.png；<br>（4）通过对前面 1，2，3 中结果不一致的原因分析得到的修正方案，结果图像存储为 02rlt.png。</p>



<p>对于存储的图像，通过 Beyond Compare 进行对比，发现 00src.png 和 00rlt.png 是完全一致的（符合预期），但 00src.png 和 01rlt.png 却不完全一致（不符合预期）。出现这个现象后，直观的想法是由于精度不够引起的，但具体哪一步的精度出了问题，还需进一步调查。</p>



<p>在查看了 transforms.ToTensor 和 transforms.ToPILImage 的源代码后，对问题进行进一步抽象，见下面代码：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import torch
torch.set_printoptions(precision = 32)

a = torch.tensor(4, dtype=torch.uint8)
b = a.float().div(255)
c = (b - 0.5) / 0.5
d = c * 0.5 + 0.5
e = d.mul(255)
f = e.byte()
print("a = " + str(a) + "\nb = " + str(b) + "\nc = " + str(c))
print("d = " + str(d) + "\ne = " + str(e) + "\nf = " + str(f))</pre>



<p>输出结果为：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">a = tensor(4, dtype=torch.uint8)
b = tensor(0.01568627543747425079345703125000)
c = tensor(-0.96862745285034179687500000000000)
d = tensor(0.01568627357482910156250000000000)
e = tensor(3.99999976158142089843750000000000)
f = tensor(3, dtype=torch.uint8)</pre>



<p>为便于查看问题，通过 torch.set_printoptions 设置了输出精度。对于每个变量代表的意义，大致理解如下：a  相当于原始图像，b 相当于图像转换为 Tensor，c 相当于 Normalize，d 相当于反 Normalize，e 为反转回图像的一个中间结果，f 为最终结果。很显然按此步骤，图像像素值 4 在经历 Normalize 和反 Normalize 过程后，最终在新的图像上像素值变成了 3。</p>



<p>接着将上面的计算结果和 <a rel="noreferrer noopener" aria-label="wolframalpha（在新窗口打开）" href="https://www.wolframalpha.com/input/?i=4%2F255" target="_blank" rel="external nofollow" >wolframalpha</a> 中计算的结果进行对比，可以发现，b 的精度已经出问题了，高精度计算中 4/255 的结果如下：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">4/255 = 0.015686274509803921568627450980392156862745098039215686274...</pre>



<p>很明显，问题的原因就是算法精度不够而最后的转换又是直接取整，导致出现了不符合预期的结果（ 有兴趣的话，也可以对比验证下其余步骤的结果）。 既然搞清楚了原因，修正的方法就很简单了，再取整前加个 round 函数就可以了，见最上面得到 02rlt.png  图像的代码，最终可验证 00src.png 和 02rlt.png 是二进制一致的。</p>
<p><br />欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2769.html">PyTorch 中 Tensor 和 PILImage 的相互转换</a></p>]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2769.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>SourceCounter 注册序列号生成</title>
		<link>https://xuhehuan.com/2756.html</link>
		<comments>https://xuhehuan.com/2756.html#respond</comments>
		<pubDate>Tue, 07 Aug 2018 12:49:04 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[网络技巧]]></category>
		<category><![CDATA[SourceCounter]]></category>
		<category><![CDATA[序列号]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2756</guid>
		<description><![CDATA[SourceCounter 是一款十分好用的源代码统计工具（官方下载地址），支持 30 多种代码格式，能够统计 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>SourceCounter 是一款十分好用的源代码统计工具（<a href="https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/boomworks/SourceCounter-3.5.33.73.zip" target="_blank" rel="noopener" rel="external nofollow" >官方下载地址</a>），支持 30 多种代码格式，能够统计包括：代码行数、注释、空行、文件大小等数据；另外，它还支持对软件开发项目的各个开发阶段的工数、成本、质量指标等进行分析和预测。如果只是简单查看下代码信息，那么免费版就够用了，但如果想把详细的报表都导出来，就需要注册序列号了。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2018/08/SourceCounter-register-serial-number.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2763" src="https://xuhehuan.com/wp-content/uploads/2018/08/SourceCounter-register-serial-number.jpg" alt="" width="600" height="476" srcset="https://xuhehuan.com/wp-content/uploads/2018/08/SourceCounter-register-serial-number.jpg 600w, https://xuhehuan.com/wp-content/uploads/2018/08/SourceCounter-register-serial-number-150x119.jpg 150w, https://xuhehuan.com/wp-content/uploads/2018/08/SourceCounter-register-serial-number-300x238.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>但由于此软件过于久远，连开发者都没有维护了，序列号也就没办法通过正常渠道拿到了，在飘云阁上倒是看到了一个<a href="https://www.chinapyg.com/thread-82469-1-1.html" target="_blank" rel="noopener" rel="external nofollow" >破解版本</a>，但没有账号，无法下载。最后从一个很老的博客中找到了一点提示：</p>

<blockquote class="wp-block-quote">
<p>C 盘序列号 xor 0x0160821B</p>
</blockquote>

<p>原文在这里：<a href="http://pb.itsong.com/2013/12/sourcecounter%E6%B3%A8%E5%86%8C%E7%A0%81%E7%94%9F%E6%88%90.html" target="_blank" rel="noopener" rel="external nofollow" >SourceCounter 注册码生成</a> 。第一次看到这个提示，我是拒绝的，太随意了，完全搞不懂，但在其它各种方法的尝试都失败后，只能回头好好研究这仅有的提示了，尝试之后居然成功了，这里稍微展开说下步骤，方便有需要的朋友。</p>
<p>首先运行 cmd，输入命令 dir，会得到系统盘的序列号，如下面的是 02B2-1A17。</p>

<pre class="wp-block-preformatted">Microsoft Windows [ 版本 10.0.17134.1]
(c) 2018 Microsoft Corporation。保留所有权利。

C:\Users\xhh2113&gt;dir
 驱动器 C 中的卷没有标签。
 卷的序列号是 02B2-1A17

 C:\Users\xhh2113 的目录

2018/07/31  17:02              .
2018/07/31  17:02              ..</pre>

<p>然后用上面得到的卷的序列号去异或十六进制的 0160821B 就生成了最终的 SourceCounter 注册码。这里用的是 win10 自带的计算器，选择“程序员”，然后点击“HEX”按十六进制计算，算得结果 03D2980C（如果不足 8 位，前面补 0）。</p>
<p>最后将这个序列号填入 SourceCounter，就可以正常使用导出功能了，注册效果见上面图片。</p><p><br />欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2756.html">SourceCounter 注册序列号生成</a></p>]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2756.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>写在 WordPress 博客被黑之后</title>
		<link>https://xuhehuan.com/2747.html</link>
		<comments>https://xuhehuan.com/2747.html#comments</comments>
		<pubDate>Mon, 23 Jul 2018 08:36:06 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[博客经验]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[博客]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2747</guid>
		<description><![CDATA[前段时间，博客接连被黑了几次，这对于我来说还是头一次遭遇。第一次被黑的时候，后台登陆不进去，查看数据库发现账号 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>前段时间，博客接连被黑了几次，这对于我来说还是头一次遭遇。第一次被黑的时候，后台登陆不进去，查看数据库发现账号和密码被改了，以为是密码泄漏了，于是重置主机内容，更换账号密码，用备份数据重新上线；隔了一周，发现又登陆不上了，首页还被篡改了一部分内容，真是无语，简单处理了下又可以访问了；没想到过了几天，又被黑了，真是心累，放了好几天都懒得处理，后果就越来越严重了，主机被上传了新的目录和文件，接着网站链接也被用 .htaccess 改的指向别处了，看来不得不好好收拾一下了。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2018/07/blog-is-hacked.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2749" src="https://xuhehuan.com/wp-content/uploads/2018/07/blog-is-hacked.jpg" alt="" width="600" height="411" srcset="https://xuhehuan.com/wp-content/uploads/2018/07/blog-is-hacked.jpg 600w, https://xuhehuan.com/wp-content/uploads/2018/07/blog-is-hacked-150x103.jpg 150w, https://xuhehuan.com/wp-content/uploads/2018/07/blog-is-hacked-300x206.jpg 300w, https://xuhehuan.com/wp-content/uploads/2018/07/blog-is-hacked-220x150.jpg 220w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>去 Google 上搜了一下，找到一篇不错的文章—— <a href="https://zhuanlan.zhihu.com/p/33754392" target="_blank" rel="noopener" rel="external nofollow" >WordPress 终极安全指南</a>，参考着把里面提到的绝大部分操作都实现了。</p>
<p>下面简单说下这次处理的几个地方：</p>
<p>一、自动更新</p>
<p>通过插件 Companion Auto Update 让 WordPress 核心、插件和主题的一直保持最新。</p>
<p>二、自动备份</p>
<p>这个之前就设置了，用的是 BackWPup 插件定期备份网站数据到 Dropbox，这次在文章中看到推荐的是 UpdraftPlus，后面可以对比试下哪个好用。</p>
<p>三、安全扫描</p>
<p>通过 WordFence 插件来搞定，功能还挺强大的，设置了隐藏 WordPress 版本号、禁止上传文件夹执行代码，同时还限制了登陆尝试次数，防止暴力破解。</p>
<p>四、保护登陆入口</p>
<p>安装了 WPS Hide Login 插件，禁止对 /wp-admin 和 /wp-login.php 的访问，并把登录入口修改成自定义 URL。</p>
<p>五、修改数据库前缀</p>
<p>防止 SQL 注入，这次数据库被改，怀疑这个可能性极大，之前装 WordPress 都喜欢用默认前缀 wp_ 的，看来后面得改下习惯。</p>
<p>六、关闭 XML-RPC</p>
<p>通过 Disable XML-RPC 插件，彻底关闭了 XML-RPC 功能。</p>
<p>七，启用 Https</p>
<p>之前怕麻烦，一直懒得升级，这次被迫弄了下，借助 Really Simple SSL 插件升级到了 Https，还算比较快的，就是要验证的细节比较多，后面把 Google Webmasters 中的相关信息也更新了下 。</p>
<p>重新部署之后到现在也有两三周了，通过 WordFence 后台发现了一些异常的访问，也屏蔽了一些 IP，但至少目前博客看起来还是安全的，没有被渗透的迹象。</p>
<p>上周的时候，Google 搜索了下博客名字，本来是想看下是否还有之前加上的垃圾链接时，发现居然有全站链接了，不清楚是不是改了 Https 之后带来的，算是个意外之喜。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2018/07/man-cao-zha-ji-sitelinks.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2748" src="https://xuhehuan.com/wp-content/uploads/2018/07/man-cao-zha-ji-sitelinks.jpg" alt="" width="600" height="347" srcset="https://xuhehuan.com/wp-content/uploads/2018/07/man-cao-zha-ji-sitelinks.jpg 600w, https://xuhehuan.com/wp-content/uploads/2018/07/man-cao-zha-ji-sitelinks-150x87.jpg 150w, https://xuhehuan.com/wp-content/uploads/2018/07/man-cao-zha-ji-sitelinks-300x174.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>个人博客虽小，安全也得注意，尤其对于 WordPress 这么流行的博客系统，安装后一定要检查是否做到了以下三点：自动更新、安全插件定期扫描和自动备份，做到了这三点，基本可保网站无虞。</p>
<p>欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2747.html">写在 WordPress 博客被黑之后</a></p>
]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2747.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>简述 LLVM 与 Clang 及其关系</title>
		<link>https://xuhehuan.com/2738.html</link>
		<comments>https://xuhehuan.com/2738.html#respond</comments>
		<pubDate>Thu, 19 Jul 2018 10:05:19 +0000</pubDate>
		<dc:creator><![CDATA[xhhjin]]></dc:creator>
				<category><![CDATA[网络技巧]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Clang]]></category>
		<category><![CDATA[LLVM]]></category>

		<guid isPermaLink="false">https://xuhehuan.com/?p=2738</guid>
		<description><![CDATA[随着 Android P 的逐步应用，越来越多的客户要求编译库时用 libc++ 来代替 libstdc++。 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>随着 Android P 的逐步应用，越来越多的客户要求编译库时用 libc++ 来代替 libstdc++。libc++ 和 libstdc++ 这两个库有关系呢？它们两个都是 C++ 标准库，libc++ 是针对 Clang 编译器特别重写的 C++ 标准库，而 libstdc++ 则是 GCC 的对应 C++ 标准库了。从 Android 市场来说，Android NDK 已在具体应用中放弃了 GCC，全面转向 Clang，正如很早前 Android NDK 在 Changelog 中提到的那样：</p>
<blockquote><p>Everyone should be switching to Clang.<br />
GCC in the NDK is now deprecated.</p></blockquote>
<p>Android NDK 从 r11 开始建议大家切换到 Clang，并且把 GCC 标记为 deprecated，将 GCC 版本锁定在 GCC 4.9 不再更新；<br />
Android NDK 从 r13 起，默认使用 Clang 进行编译，但是暂时也没有把 GCC 删掉，Google 会一直等到 libc++ 足够稳定后再删掉 GCC；<br />
Android NDK 在 r17 中宣称不再支持 GCC 并在后续的 r18 中删掉 GCC，具体可见 NDK 的<a href="https://developer.android.com/ndk/downloads/revision_history" target="_blank" rel="noopener" rel="external nofollow" >版本历史</a>。</p>
<p>接下来，简要的介绍一下 Clang。Clang 是一个 C、C++、Objective-C 和 Objective-C++ 编程语言的编译器前端，采用底层虚拟机（LLVM）作为后端。至于为什么有了 GCC 还要开发 Clang？Clang 相比 GCC 又有什么优势呢？网上有很多信息可以参考，这里只简单提两点：（1）Clang 采用的是 BSD 协议的许可证，而 GCC 采用的是 GPL 协议，显然前者更为宽松；（2）Clang 是一个高度模块化开发的轻量级编译器，编译速度快、占用内存小、有着友好的出错提示。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM-cover.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2745" src="https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM-cover.jpg" alt="" width="600" height="324" srcset="https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM-cover.jpg 600w, https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM-cover-150x81.jpg 150w, https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM-cover-300x162.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>然后说下 Clang 背后的 LLVM（<strong>L</strong>ow <strong>L</strong>evel <strong>V</strong>irtual <strong>M</strong>achine）。LLVM 是以 BSD 许可来开发的开源的编译器框架系统，基于 C++ 编写而成，利用虚拟技术来优化以任意程序语言编写的程序的编译时间、链接时间、运行时间以及空闲时间，最早以 C/C++ 为实现对象，对开发者保持开放，并兼容已有脚本。LLVM 计划启动于 2000 年，最初由 University of Illinois at Urbana-Champaign 的 Chris Lattner 主持开展，2006 年 Chris Lattner 加盟苹果公司并致力于 LLVM 在苹果公司开发体系中的应用，所以苹果公司也是 LLVM 计划的主要资助者。目前 LLVM 因其宽松的许可协议，更好的模块化、更清晰的架构，成为很多厂商或者组织的选择，已经被苹果 IOS 开发工具、Facebook、Google 等各大公司采用，像 Swift、Rust 等语言都选择了以 LLVM 为后端。</p>
<p>在理解 LLVM 之前，先说下传统编译器的工作原理，基本上都是三段式的，可以分为前端、优化器和后端。前端负责解析源代码，检查语法错误，并将其翻译为抽象的语法树；优化器对这一中间代码进行优化，试图使代码更高效；后端则负责将优化器优化后的中间代码转换为目标机器的代码，这一过程后端会最大化的利用目标机器的特殊指令，以提高代码的性能。基于这个认知，我们可以认为 LLVM 包括了两个概念：一个广义的 LLVM 和一个狭义的 LLVM 。广义的 LLVM 指的是一个完整的 LLVM 编译器框架系统，包括了前端、优化器、后端、众多的库函数以及很多的模块；而狭义的 LLVM 则是聚焦于编译器后端功能的一系列模块和库，包括代码优化、代码生成、JIT 等。</p>
<p>下面大概讲一讲 LLVM 和 Clang 的关系。我们将它们对应于传统的编译器当中的几个独立的部分，这样能够更加方便明确的表述出它们之前的关系。</p>
<p><a href="https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM.jpg" class="highslide-image" onclick="return hs.expand(this);"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2739" src="https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM.jpg" alt="" width="623" height="422" srcset="https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM.jpg 623w, https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM-150x102.jpg 150w, https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM-300x203.jpg 300w, https://xuhehuan.com/wp-content/uploads/2018/07/Clang-LLVM-220x150.jpg 220w" sizes="(max-width: 623px) 100vw, 623px" /></a></p>
<p>对应到这个图中，可以非常明确的找出它们的关系。整体的编译器架构就是 LLVM 架构；Clang 大致可以对应到编译器的前端，主要处理一些和具体机器无关的针对语言的分析操作；编译器的优化器和后端部分就是之前提到的 LLVM 后端，即狭义的 LLVM。</p>
<p>此外，由于 LLVM 的命名最早源自于底层虚拟机（Low Level Virtual Machine） 的首字母缩写，但这个项目的范围并不局限于创建一个虚拟机，这个缩写导致了大量的疑惑。LLVM 成长之后已成为众多编译工具及低级工具技术的统称，使得这个名字变得更不贴切，所以开发者决定放弃这个缩写的涵义，现在 LLVM 已独立成为一个品牌，适用于 LLVM 下的所有项目，包括 LLVM 中介码、LLVM 除错工具、LLVM C++ 标准库等。</p>
<p>欢迎转载，转载请注明出处：<a href="https://xuhehuan.com">蔓草札记</a> &raquo; <a href="https://xuhehuan.com/2738.html">简述 LLVM 与 Clang 及其关系</a></p>
]]></content:encoded>
			<wfw:commentRss>https://xuhehuan.com/2738.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
