<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Zhenxiang Jin</name>
    <email>zhenxiang.shawn@zohomail.com</email>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <icon>https://www.gravatar.com/avatar/f586adbeb6f0b5dd715f510356ff79ea</icon>
  <id>https://zhenxiang-shawn.github.io/</id>
  <link href="https://zhenxiang-shawn.github.io/" rel="alternate"/>
  <link href="https://zhenxiang-shawn.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Zhenxiang Jin</rights>
  <subtitle>J. Tech. — 求知若饥，虚心若愚。个人技术博客，记录 AI Agent 系统设计、深度学习、Python、算法等学习与工程实践。</subtitle>
  <title>J. Tech.</title>
  <updated>2026-08-08T02:03:20.082Z</updated>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <content>
      <![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start">Quick Start</h2><h3 id="Create-a-new-post">Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server">Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files">Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites">Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2026/08/08/hello-world/</id>
    <link href="https://zhenxiang-shawn.github.io/2026/08/08/hello-world/"/>
    <published>2026-08-08T02:03:20.082Z</published>
    <summary>
      <![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for]]>
    </summary>
    <title>Hello World</title>
    <updated>2026-08-08T02:03:20.082Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Python" scheme="https://zhenxiang-shawn.github.io/tags/Python/"/>
    <content>
      <![CDATA[<p>Python 中有一个著名的彩蛋: <code>import this</code>. 这是由 Python 的创建者 Guido van Rossum 以及 Tim Peters 所编写。当你在 Python 解释器中输入 import this 并按下回车键时，它会打印出一段著名的 Python 之禅（The Zen of Python），这是一组指导 Python 编程哲学的格言。</p><span id="more"></span><h2 id="Python-之禅">Python 之禅</h2><blockquote><p>The Zen of Python, by Tim Peters</p><p>Beautiful is better than ugly.<br>Explicit is better than implicit.<br>Simple is better than complex.<br>Complex is better than complicated.<br>Flat is better than nested.<br>Sparse is better than dense.<br>Readability counts.<br>Special cases aren’t special enough to break the rules.<br>Although practicality beats purity.<br>Errors should never pass silently.<br>Unless explicitly silenced.<br>In the face of ambiguity, refuse the temptation to guess.<br>There should be one-- and preferably only one --obvious way to do it.<br>Although that way may not be obvious at first unless you’re Dutch.<br>Now is better than never.<br>Although never is often better than <em>right</em> now.<br>If the implementation is hard to explain, it’s a bad idea.<br>If the implementation is easy to explain, it may be a good idea.<br>Namespaces are one honking great idea – let’s do more of those!</p></blockquote><h2 id="如何实现">如何实现</h2><p>打开 python 编辑器: <code>python3</code>, 直接输入:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> this</span><br><span class="line"><span class="built_in">print</span>(this.__file__)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>/path/to/lib/python3<span class="number">.10</span>/this.py</span><br></pre></td></tr></table></figure><p>然后直接在 terminal 输入 <code>cat /path/to/lib/python3.10/this.py</code>, 就会得到 <code>this.py</code>的源码.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#bash cat &quot;/Users/user/anaconda3/lib/python3.10/this.py&quot;</span></span><br><span class="line">s = <span class="string">&quot;&quot;&quot;Gur Mra bs Clguba, ol Gvz Crgref</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Ornhgvshy vf orggre guna htyl.</span></span><br><span class="line"><span class="string">Rkcyvpvg vf orggre guna vzcyvpvg.</span></span><br><span class="line"><span class="string">Fvzcyr vf orggre guna pbzcyrk.</span></span><br><span class="line"><span class="string">Pbzcyrk vf orggre guna pbzcyvpngrq.</span></span><br><span class="line"><span class="string">Syng vf orggre guna arfgrq.</span></span><br><span class="line"><span class="string">Fcnefr vf orggre guna qrafr.</span></span><br><span class="line"><span class="string">Ernqnovyvgl pbhagf.</span></span><br><span class="line"><span class="string">Fcrpvny pnfrf nera&#x27;g fcrpvny rabhtu gb oernx gur ehyrf.</span></span><br><span class="line"><span class="string">Nygubhtu cenpgvpnyvgl orngf chevgl.</span></span><br><span class="line"><span class="string">Reebef fubhyq arire cnff fvyragyl.</span></span><br><span class="line"><span class="string">Hayrff rkcyvpvgyl fvyraprq.</span></span><br><span class="line"><span class="string">Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.</span></span><br><span class="line"><span class="string">Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.</span></span><br><span class="line"><span class="string">Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh&#x27;er Qhgpu.</span></span><br><span class="line"><span class="string">Abj vf orggre guna arire.</span></span><br><span class="line"><span class="string">Nygubhtu arire vf bsgra orggre guna *evtug* abj.</span></span><br><span class="line"><span class="string">Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg&#x27;f n onq vqrn.</span></span><br><span class="line"><span class="string">Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.</span></span><br><span class="line"><span class="string">Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg&#x27;f qb zber bs gubfr!&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">d = &#123;&#125;</span><br><span class="line"><span class="keyword">for</span> c <span class="keyword">in</span> (<span class="number">65</span>, <span class="number">97</span>):</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">26</span>):</span><br><span class="line">        d[<span class="built_in">chr</span>(i+c)] = <span class="built_in">chr</span>((i+<span class="number">13</span>) % <span class="number">26</span> + c)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;&quot;</span>.join([d.get(c, c) <span class="keyword">for</span> c <span class="keyword">in</span> s]))</span><br></pre></td></tr></table></figure><p>根据源码可以看到, 其实就是一个文件, 里面存储了这段话, 然后通过一个字典, 将每个字母替换为对应的字母, 最后输出结果. 这个转换使用了 ROT 13. 在 Python3 中, 可以使用 <code>codecs</code> 来实现这个功能. 下面是示例代码:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> codecs</span><br><span class="line"></span><br><span class="line">my_state = (</span><br><span class="line">    <span class="string">&quot;Guvf vf zl phfgbz vzcbeg guvf. Lbh pna hfr vg gb fubj lbhe fgngrzrag sbe &quot;</span></span><br><span class="line">    <span class="string">&quot;lbhe yvoenel. Nhgube: Murakvnat Wva. Vs lbh yvxr guvf, cyrnfr fgne guvf &quot;</span></span><br><span class="line">    <span class="string">&quot;ercb.&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">print_my_state</span>():</span><br><span class="line">    <span class="built_in">print</span>(codecs.decode(my_state, <span class="string">&#x27;rot_13&#x27;</span>))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">print_my_state()</span><br></pre></td></tr></table></figure><p>详细代码可以在 <a href="https://github.com/zhenxiang-shawn/Python_Import_custom_this">Github</a> 查看.</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2024/07/30/2024-07-30-python-import-this/</id>
    <link href="https://zhenxiang-shawn.github.io/2024/07/30/2024-07-30-python-import-this/"/>
    <published>2024-07-29T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Python 中有一个著名的彩蛋: <code>import this</code>. 这是由 Python 的创建者 Guido van Rossum 以及 Tim Peters 所编写。当你在 Python 解释器中输入 import this 并按下回车键时，它会打印出一段著名的 Python 之禅（The Zen of Python），这是一组指导 Python 编程哲学的格言。</p>]]>
    </summary>
    <title>Python 中的 import this</title>
    <updated>2024-07-29T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="解决方案" scheme="https://zhenxiang-shawn.github.io/categories/%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/"/>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Python" scheme="https://zhenxiang-shawn.github.io/tags/Python/"/>
    <content>
      <![CDATA[<p>Python 中的修饰器（decorator）是一个函数，它接受一个函数或方法作为其唯一的参数，并返回一个新的函数或方法，其中整合了修饰后的函数或方法，以及附带的一些额外功能。<br>简单来说，修饰器可以在不修改被修饰函数原有代码的情况下，为其添加额外的功能。例如用于性能分析、日志记录、权限控制、缓存、参数验证、事务处理、重试机制、异常处理等。</p><span id="more"></span><h3 id="实现原理">实现原理</h3><p>装饰器的实现原理基于 Python 的函数对象和闭包的概念。</p><p>在 Python 中，函数是“一等公民”，这意味着函数可以像其他对象一样被传递、赋值和返回。</p><p>当使用装饰器时，例如 <code>@decorator</code> 应用于一个函数 <code>func</code> ，实际上发生了以下过程：</p><ol><li>定义装饰器函数 <code>decorator</code> ，它接受一个函数作为参数。</li><li>当解释器遇到 <code>@decorator</code> 应用于 <code>func</code> 时，它会将 <code>func</code> 作为参数传递给 <code>decorator</code> 。</li><li><code>decorator</code> 内部通常会定义一个新的函数（比如 <code>wrapper</code> ），在这个新函数中，会执行一些额外的操作（例如在函数执行前后添加代码），然后调用被装饰的函数 <code>func</code> 。</li><li>最后，<code>decorator</code> 返回这个新定义的函数 <code>wrapper</code> 。</li><li>后续对 <code>func</code> 的调用实际上是在调用装饰器返回的 <code>wrapper</code> 函数，从而实现了在不修改原始函数代码的情况下，为函数添加了额外的功能。</li></ol><p>这种机制利用了函数可以作为参数传递和返回，以及在函数内部可以定义新函数并形成闭包（即新函数可以访问外部函数的变量）的特性。</p><p>比如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">my_decorator</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Before function execution&quot;</span>)</span><br><span class="line">        func()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;After function execution&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@my_decorator</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_function</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Inside my_function&quot;</span>)</span><br><span class="line"></span><br><span class="line">my_function()</span><br></pre></td></tr></table></figure><p>在这个例子中，<code>my_decorator</code> 就是一个装饰器函数，它返回了一个新的函数 <code>wrapper</code> ，当调用 <code>my_function</code> 时，实际上是在调用 <code>wrapper</code> ，从而实现了在 <code>my_function</code> 执行前后打印额外信息的功能。</p><h2 id="Timeit-修饰器">Timeit 修饰器</h2><p>正常使用 time 库来测试方法的性能的时候,通常需要写很多代码,而且几乎都是重复的. 这里创建 timeit 修饰器的话,可以在被测试的方法上直接添加修饰器使用,使用起来更方便而且还能减少代码量.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;根据上面的实现原理,我们可以实现一个 timeit 修饰器,使用的时候可以直接在方法前加 @timeit 就可以查看方法运行时间了.&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timeit</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        start = time.time()</span><br><span class="line">        res = func(*args, **kwargs)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Function <span class="subst">&#123;func.__name__&#125;</span> took <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span> seconds&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> res</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>这里需要注意的是, 方法 <code>wrapper</code> 的命名不是强制的, 把名字改为 <code>wrapper2</code> 的话该修饰器一样有效.</p></blockquote>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2024/07/22/2024-07-22-wrap-timeit/</id>
    <link href="https://zhenxiang-shawn.github.io/2024/07/22/2024-07-22-wrap-timeit/"/>
    <published>2024-07-21T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Python 中的修饰器（decorator）是一个函数，它接受一个函数或方法作为其唯一的参数，并返回一个新的函数或方法，其中整合了修饰后的函数或方法，以及附带的一些额外功能。<br>
简单来说，修饰器可以在不修改被修饰函数原有代码的情况下，为其添加额外的功能。例如用于性能分析、日志记录、权限控制、缓存、参数验证、事务处理、重试机制、异常处理等。</p>]]>
    </summary>
    <title>Python 把 timeit 模块打包成修饰函数</title>
    <updated>2024-07-21T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Ubuntu" scheme="https://zhenxiang-shawn.github.io/tags/Ubuntu/"/>
    <content>
      <![CDATA[<p>有时忘记 Ubuntu 系统中一些用户密码的话,可以在<code>/etc/passwd</code> 中找到相关信息. 但是密码信息一般是以加密的形式放在<code>/etc/shadow</code>中. 要拿到明文密码还需要解密一下.</p><span id="more"></span><p>Linux 用户系统创建/修改新用户时，将修改以下文件:</p><ol><li><code>/etc/passwd</code>： 用户账户的详细信息在此文件中更新。</li><li><code>/etc/shadow</code>： 用户账户密码在此文件中更新。</li><li><code>/etc/group</code>： 新用户群组的详细信息在此文件中更新。</li><li><code>/etc/gshadow</code>： 新用户群组密码在此文件中更新。</li></ol><h2 id="TL-DR-直接安装-John-然后使用-John-暴力破解"><strong>TL;DR</strong> 直接安装 John 然后使用 John 暴力破解</h2><h3 id="安装-John">安装 John</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install john</span><br></pre></td></tr></table></figure><h3 id="使用-John-暴力破解">使用 John 暴力破解</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># sudo john /etc/shadow</span></span><br><span class="line">Loaded 4 password hashes with 4 different salts (crypt, generic crypt(3) [?/64])</span><br><span class="line">Warning: OpenMP is disabled; a non-OpenMP build may be faster</span><br><span class="line">Press <span class="string">&#x27;q&#x27;</span> or Ctrl-C to abort, almost any other key <span class="keyword">for</span> status</span><br><span class="line">0g 0:00:00:48 20% 1/3 0g/s 43.65p/s 43.65c/s 43.65C/s Shawn99999/..99999nwahs!</span><br><span class="line">yifan123         (yifan)</span><br></pre></td></tr></table></figure><h2 id="查看-passwd-信息">查看 passwd 信息</h2><p>使用命令<code>cat /etc/passwd</code>就可以列出所有的用户信息.</p><p><strong>例子</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 比如:</span></span><br><span class="line"><span class="comment"># cat /etc/passwd</span></span><br><span class="line">root:x:0:0:root:/root:/bin/bash</span><br><span class="line">bin:x:1:1:bin:/bin:/sbin/nologin</span><br><span class="line">daemon:x:2:2:daemon:/sbin:/sbin/nologin</span><br></pre></td></tr></table></figure><p>7 个字段的详细信息如下:</p><ul><li><strong>用户名</strong> （<code>magesh</code>）： 已创建用户的用户名，字符长度 1 个到 12 个字符。</li><li><strong>密码</strong>（<code>x</code>）：代表加密密码保存在 <code>/etc/shadow</code> 文件中。</li><li><strong>用户 ID</strong>（<code>506</code>）：代表用户的 ID 号，每个用户都要有一个唯一的 ID 。UID 号为 0 的是为 <code>root</code> 用户保留的，UID 号 1 到 99 是为系统用户保留的，UID 号 100-999 是为系统账户和群组保留的。</li><li>**群组 ID **（<code>507</code>）：代表群组的 ID 号，每个群组都要有一个唯一的 GID ，保存在 <code>/etc/group</code> 文件中。</li><li><strong>用户信息</strong>（<code>2g Admin - Magesh M</code>）：代表描述字段，可以用来描述用户的信息（LCTT 译注：此处原文疑有误）。</li><li><strong>家目录</strong>（<code>/home/mageshm</code>）：代表用户的家目录。</li><li><strong>Shell</strong>（<code>/bin/bash</code>）：代表用户使用的 shell 类型。</li></ul><h2 id="查看-shadow-信息">查看 shadow 信息</h2><p>使用命令<code>cat /etc/shadow</code>就可以列出所有的用户信息. 一般其中的信息都是加密的.</p><p><strong>例子</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># cat /etc/shadow</span></span><br><span class="line">root:$6$Mv12D1VT<span class="variable">$maznMnrEBDMmry7A6YhW6lRD2MhdOuPXTQXs9XoIxy1h05B</span>.oOj3hvlw9.9FDs2q/Mcll/I7TX.U.vAY3c0ld1:19585:0:99999:7:::</span><br><span class="line">daemon:*:19213:0:99999:7:::</span><br><span class="line">bin:*:19213:0:99999:7:::</span><br></pre></td></tr></table></figure><p>文件中每行代表一个用户，同样使用 “:” 作为分隔符，不同之处在于，每行用户信息被划分为 9 个字段。每个字段的含义如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">用户名：加密密码：最后一次修改时间：最小修改时间间隔：密码有效期：密码需要变更前的警告天数：密码过期后的宽限时间：账号失效时间：保留字段</span><br></pre></td></tr></table></figure><blockquote><ul><li><p>用户名。就是一串代表用户身份的字符串。</p></li><li><p>加密密码。这里保存的是真正加密的密码。所有伪用户的密码都是 “!!” 或 “*”，代表没有密码是不能登录的</p></li><li><p>最后一次修改时间。此字段表示最后一次修改密码的时间</p></li><li><p>最小修改时间间隔。也就是说，该字段规定了从第 3 字段（最后一次修改密码的日期）起，多长时间之内不能修改密码。如果是 0，则密码可以随时修改；如果是 10，则代表密码修改后 10 天之内不能再次修改密码。</p></li><li><p>密码有效期。为了强制要求用户变更密码，这个字段可以指定距离第 3 字段（最后一次更改密码）多长时间内需要再次变更密码，否则该账户密码进行过期阶段。该字段的默认值为 99999，也就是 273 年，可认为是永久生效。如果改为 90，则表示密码被修改 90 天之后必须再次修改，否则该用户即将过期。管理服务器时，通过这个字段强制用户定期修改密码。</p></li><li><p>密码需要变更前的警告天数。与第 5 字段相比较，当账户密码有效期快到时，系统会发出警告信息给此账户，提醒用户 “再过 n 天你的密码就要过期了，请尽快重新设置你的密码！”。该字段的默认值是 7，也就是说，距离密码有效期的第 7 天开始，每次登录系统都会向该账户发出 “修改密码” 的警告信息。</p></li><li><p>密码过期后的宽限天数。也称为“口令失效日”，简单理解就是，在密码过期后，用户如果还是没有修改密码，则在此字段规定的宽限天数内，用户还是可以登录系统的；如果过了宽限天数，系统将不再让此账户登陆，也不会提示账户过期，是完全禁用。</p></li><li><p>账号失效时间。同第 3 个字段一样，使用自 1970 年 1 月 1 日以来的总天数作为账户的失效时间。该字段表示，账号在此字段规定的时间之外，不论你的密码是否过期，都将无法使用！</p></li><li><p>保留。这个字段目前没有使用，等待新功能的加入。</p></li></ul></blockquote><h2 id="解密-shadow-信息">解密 shadow 信息</h2><p>由于shadow文件加密的密码具有固定格式:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$<span class="built_in">id</span>$salt<span class="variable">$encrypted</span></span><br></pre></td></tr></table></figure><ul><li>id：表示加密算法，1 代表 MD5，5 代表 SHA-256，6 代表 SHA-512。</li><li>salt：表示密码学中的 Salt，由系统随机生成 。</li><li>encrypted：表示密码的 hash。<br>冒号后面的数值是一些日期和密码修改间隔天数的信息，可以不用管，以上述示例中 root 账号为例</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">root:$6$Mv12D1VT$maznMnrEBDMmry7A6YhW6lRD2MhdOuPXTQXs9XoIxy1h05B.oOj3hvlw9.9FDs2q/Mcll/I7TX.U.vAY3c0ld1:19585:0:99999:7:::</span><br></pre></td></tr></table></figure><blockquote><p>id=6，即使用SHA-512哈希算法</p><p>salt=Mv12D1VT</p><p>encrypted=maznMnrEBDMmry7A6YhW6lRD2MhdOuPXTQXs9XoIxy1h05B.oOj3hvlw9.9FDs2q/Mcll/I7TX.U.vAY3c0ld1</p></blockquote><p>我们可以直接使用 john 去破解 shadow 文件：</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2024/02/26/2024-02-26-ubuntu-decipher-etc-shadow/</id>
    <link href="https://zhenxiang-shawn.github.io/2024/02/26/2024-02-26-ubuntu-decipher-etc-shadow/"/>
    <published>2024-02-25T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>有时忘记 Ubuntu 系统中一些用户密码的话,可以在<code>/etc/passwd</code> 中找到相关信息. 但是密码信息一般是以加密的形式放在<code>/etc/shadow</code>中. 要拿到明文密码还需要解密一下.</p>]]>
    </summary>
    <title>Ubuntu破解 /etc/shadow 密码</title>
    <updated>2024-02-25T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Golang" scheme="https://zhenxiang-shawn.github.io/tags/Golang/"/>
    <content>
      <![CDATA[<p>写后端的时候,前后端的端口制定需要一定的规范,前后端都需要了解 HTTP 协议的请求分类和使用场景.</p><span id="more"></span><h2 id="9种请求方法">9种请求方法</h2><p>HTTP1.0 定义了三种请求方法：</p><ol><li>GET</li><li>POST</li><li>HEAD</li></ol><p>HTTP1.1 新增了六种请求方法：</p><ol start="4"><li>OPTIONS</li><li>PUT</li><li>PATCH</li><li>DELETE</li><li>TRACE</li><li>CONNECT</li></ol><p>因此截止到 HTTP1.1, 一共有 9 种请求方式.</p><h2 id="使用场景">使用场景</h2><table><thead><tr><th>命令</th><th>目的</th><th>使用场景</th><th>备注</th><th>特点</th></tr></thead><tbody><tr><td>GET</td><td>获取资源</td><td>1. 获取网页内容。2. 查询API以获取数据列表或单个资源。</td><td>发送请求取得服务器上的某种资源，Get请求本身不应当携带任何呈现数据</td><td>1. 参数显示在URL查询字符串中。2. 无副作用（幂等性操作），多次执行相同GET请求结果不变。能够被浏览器缓存。3. 安全性较低，因为参数暴露在URL中。4. 有限制的请求数据大小，具体由服务器决定，但通常不超过几KB。</td></tr><tr><td>POST</td><td>传输实体文本</td><td>1. 提交表单数据。2. 创建新用户账户。3. 更新用户资料。4. 发送评论或其他类型的数据到服务器。</td><td>向指定资源提交数据进行处理请求，数据在请求题里</td><td>1. 参数包含在请求正文中，不显示在URL上。 2. 可能产生副作用，例如新建、更新或删除资源。 3. 请求数据量相对较大，理论上没有明确限制，实际受限于服务器配置 4. 默认情况下不是幂等的，即重复相同的POST请求可能会有不同的结果。</td></tr><tr><td>HEAD</td><td>获得报文首部</td><td>检查资源状态而不需要传输整个资源内容。</td><td>类似于 GET，但是返回的响应中没有具体的内容，欲判断某个资源是否存在时使用。</td><td></td></tr><tr><td>OPTIONS</td><td>询问支持的方法</td><td>预检请求（CORS跨域资源共享时会用到），了解服务器功能。</td><td>询问服务器可以提交哪些请求方法，若请求成功，则它会在HTTP头中包含一个名为“Allow”的头，值是所支持的方法，如“GET, POST”。</td><td></td></tr><tr><td>PUT</td><td>传输文件</td><td>1. 更新完整的资源对象。</td><td>向服务器传送的数据取代指定的文档的内容，指定上传资源存放路径，HTML表单也不支持这个.<br />重要区别：PUT通常指定了资源的存放位置，而POST则没有，POST的数据存放位置由服务器自己决定。</td><td>与POST相似，但具有幂等性，多次PUT同一资源应当产生相同的结果。</td></tr><tr><td>PATCH</td><td>局部更新文件</td><td>针对资源的部分属性进行更新。</td><td>是对 PUT 方法的补充，用来对已知资源进行局部更新</td><td>只提交需要更改的部分，而非整个资源内容。</td></tr><tr><td>DELETE</td><td>删除文件</td><td>删除账户、文件或者其他可删除的资源。</td><td>请求服务器删除指定的资源。</td><td>幂等性，多次请求删除同一样资源应该导致相同的结果（即资源都已经被删除）。</td></tr><tr><td>TRACE</td><td>追踪路径</td><td></td><td>要求服务器回显收到的请求，让Web服务器端将之前的请求通信还给客户端的方法。主要用于测试或诊断。</td><td></td></tr><tr><td>CONNECT</td><td>要求用隧道协议连接代理</td><td></td><td>HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器，CONNECT方法要求在与代理服务器通信时建立隧道，实现用隧道协议进行TCP通信。主要***L（安全套接层）和TLS（传输层安全）协议把通信内容加密后经网络隧道传输。</td><td></td></tr></tbody></table><h2 id="建立Websocket">建立Websocket</h2><p>HTTP 协议中的 GET 和 POST 请求都可以作为升级到 WebSocket 连接的起点。在 HTTP 1.1 规范中定义了协议升级机制，允许客户端通过 Upgrade 首部字段发起将连接从 HTTP 更改为另一种协议（如 WebSocket）的请求。</p><p>但是用于启动 WebSocket 连接的 HTTP 请求通常是 GET 方法，因为 WebSocket 的目的是建立一个持久的双向通信通道，而 GET 请求更符合这一目的，而且更容易被代理服务器和中间件处理。但理论上 POST 方法也可以用来升级为 WebSocket.</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2024/02/11/2024-02-10-web-request-types/</id>
    <link href="https://zhenxiang-shawn.github.io/2024/02/11/2024-02-10-web-request-types/"/>
    <published>2024-02-10T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>写后端的时候,前后端的端口制定需要一定的规范,前后端都需要了解 HTTP 协议的请求分类和使用场景.</p>]]>
    </summary>
    <title>HTTP协议的请求方式汇总</title>
    <updated>2024-02-10T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="解决方案" scheme="https://zhenxiang-shawn.github.io/categories/%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/"/>
    <category term="Docker" scheme="https://zhenxiang-shawn.github.io/tags/Docker/"/>
    <content>
      <![CDATA[<p>开发时,有时需要直接在 docker 中开发或者 debug. 使用 VSCode 会方便很多. 但是有时候安装了 <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack">Remote Development</a> 还是会报错: <code>Failed to connect. Is docker running?</code></p><span id="more"></span><blockquote><p>原因是docker使用unix socket进行通讯，但是unix socket属于root用户，但是普通用户需要使用sudo才能开启root权限，但是普通的操作并没有root权限。</p></blockquote><h2 id="Ubuntu">Ubuntu:</h2><ul><li><code>sudo usermod -aG docker $&#123;USER&#125;</code></li></ul><h2 id="Mac">Mac:</h2><ol><li><code>cmd-shift-p</code></li><li>点击 <code>&quot;Preferences: Open Workspace Settings&quot;</code></li><li>搜索 “docker path”</li><li>输入docker client的绝对路径 (通常在 <code>&quot;/usr/local/bin/docker&quot;</code>)</li></ol><h2 id="Windows">Windows:</h2><p>同 Mac</p><h2 id="Windows-WSL">Windows WSL:</h2><p>暂未解决</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2024/01/30/2024-01-29-vscode-connect-docker-failed/</id>
    <link href="https://zhenxiang-shawn.github.io/2024/01/30/2024-01-29-vscode-connect-docker-failed/"/>
    <published>2024-01-29T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>开发时,有时需要直接在 docker 中开发或者 debug. 使用 VSCode 会方便很多. 但是有时候安装了 <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack">Remote Development</a> 还是会报错: <code>Failed to connect. Is docker running?</code></p>]]>
    </summary>
    <title>VSCode 连接 Docker 镜像失败</title>
    <updated>2024-02-10T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Golang" scheme="https://zhenxiang-shawn.github.io/tags/Golang/"/>
    <content>
      <![CDATA[<p>Golang 很多包在国内下载很慢，换国内镜像可以加快下载速度。</p><span id="more"></span><h2 id="启用-Go-Modules-功能">启用 Go Modules 功能</h2><p><code>go env -w GO111MODULE=on</code></p><h2 id="配置-GOPROXY-环境变量，以下三选一">配置 GOPROXY 环境变量，以下三选一</h2><h3 id="七牛-CDN">七牛 CDN</h3><p>号称中国最值得信赖的Go模块代理。</p><p><code>go env -w  GOPROXY=https://goproxy.cn,direct</code></p><h3 id="阿里云">阿里云</h3><p><code>go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct</code></p><h3 id="官方">官方</h3><p><code>go env -w  GOPROXY=https://goproxy.io,direct</code></p><h2 id="确认是否已更新">确认是否已更新</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; go env | grep GOPROXY</span><br><span class="line">&gt;&gt;&gt; GOPROXY=&quot;https://goproxy.cn&quot; # 检查输出</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2024/01/30/2024-01-30-golang-set-goproxy/</id>
    <link href="https://zhenxiang-shawn.github.io/2024/01/30/2024-01-30-golang-set-goproxy/"/>
    <published>2024-01-29T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Golang 很多包在国内下载很慢，换国内镜像可以加快下载速度。</p>]]>
    </summary>
    <title>Golang Module 换国内镜像</title>
    <updated>2024-01-29T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Python" scheme="https://zhenxiang-shawn.github.io/tags/Python/"/>
    <category term="Pandas" scheme="https://zhenxiang-shawn.github.io/tags/Pandas/"/>
    <category term="Data Science" scheme="https://zhenxiang-shawn.github.io/tags/Data-Science/"/>
    <content>
      <![CDATA[<p>相似度计算在数据挖掘中指的是:计算在数据集里使用多个维度数据来表示的object 的距离. 一些常用的受欢迎的相似度测量方法有:</p><ol><li>Euclidean Distance.<br>欧几里得距离(euclidean distance) 在二维和三维空间中的欧氏距离就是两点之间的实际距离。</li><li>Manhattan Distance.<br>在 二维空间 内，两个点之间的曼哈顿距离为它们横坐标之差的绝对值与纵坐标之差的绝对值之和。两个点的曼哈顿距离是会随着坐标系的改变而改变的.</li><li>Minkowski Distance.<br>闵科夫斯基距离是规范化向量空间（N维实空间）中两点之间的距离/相似性，是欧氏距离和曼哈顿距离的概括。</li></ol><p>相似度一般都是基于某种距离的定义来计算的.</p><h2 id="余弦相似度-Cosine-Similarity">余弦相似度 (Cosine Similarity).</h2><p>余弦相似度是一种度量，有助于确定数据对象的相似程度，无论其大小如何。在余弦相似度的计算中,所有的数据集都被认为是向量(vector, 通常是 1xN的 shape). 求解相似度的公式是:<br><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="30.198ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 13347.6 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D436" d="M50 252Q50 367 117 473T286 641T490 704Q580 704 633 653Q642 643 648 636T656 626L657 623Q660 623 684 649Q691 655 699 663T715 679T725 690L740 705H746Q760 705 760 698Q760 694 728 561Q692 422 692 421Q690 416 687 415T669 413H653Q647 419 647 422Q647 423 648 429T650 449T651 481Q651 552 619 605T510 659Q484 659 454 652T382 628T299 572T226 479Q194 422 175 346T156 222Q156 108 232 58Q280 24 350 24Q441 24 512 92T606 240Q610 253 612 255T628 257Q648 257 648 248Q648 243 647 239Q618 132 523 55T319 -22Q206 -22 128 53T50 252Z"></path></g><g data-mml-node="mi" transform="translate(760,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mi" transform="translate(1245,0)"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"></path></g><g data-mml-node="mo" transform="translate(1714,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2103,0)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path></g><g data-mml-node="mo" transform="translate(2675,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"></path></g><g data-mml-node="mi" transform="translate(3119.7,0)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(3609.7,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(4276.4,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mi" transform="translate(5332.2,0)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path></g><g data-mml-node="mo" transform="translate(6126.4,0)"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"></path></g><g data-mml-node="mi" transform="translate(7126.7,0)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(7838.9,0)"><path data-c="F7" d="M318 466Q318 500 339 518T386 537Q418 537 438 517T458 466Q458 438 440 417T388 396Q355 396 337 417T318 466ZM56 237T56 250T70 270H706Q721 262 721 250T706 230H70Q56 237 56 250ZM318 34Q318 68 339 86T386 105Q418 105 438 85T458 34Q458 6 440 -15T388 -36Q355 -36 337 -15T318 34Z"></path></g><g data-mml-node="mtext" transform="translate(8839.1,0)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mtext" transform="translate(9117.1,0)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mi" transform="translate(9395.1,0)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path></g><g data-mml-node="mtext" transform="translate(9967.1,0)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mtext" transform="translate(10245.1,0)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mo" transform="translate(10745.3,0)"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"></path></g><g data-mml-node="mtext" transform="translate(11745.6,0)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mtext" transform="translate(12023.6,0)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mi" transform="translate(12301.6,0)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mtext" transform="translate(12791.6,0)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mtext" transform="translate(13069.6,0)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g></g></g></svg></mjx-container></p><p>比如我们有两个向量: <code>x = {3,2,0,5} &amp; y = {1, 0, 0, 0}</code> 其相似度的计算就是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">x . y = 3*1 + 2*0 + 0*0 + 5*0 = 3</span><br><span class="line"></span><br><span class="line">||x|| = √ (3)^2 + (2)^2 + (0)^2 + (5)^2 = 6.16</span><br><span class="line"></span><br><span class="line">||y|| = √ (1)^2 + (0)^2 + (0)^2 + (0)^2 = 1</span><br><span class="line"></span><br><span class="line">Cos(x, y) = 3 / (6.16 * 1) = 0.49 </span><br><span class="line"></span><br><span class="line"># 两个向量的差异度就是用 100% 减去相似度.</span><br><span class="line">Dis(x, y) = 1 - Cos(x, y) = 1 - 0.49 = 0.51</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="扩展-计算句子的相似度">扩展:计算句子的相似度</h3><h4 id="Sentence-Encode">Sentence Encode</h4><p>计算句子的相似度之前,需要使用多维向量来表示一个句子. 一般 NLP 领域都有一个 tokenizer 来加密句子. 在这个例子中我们使用了常用的<a href="https://www.nltk.org/">NLTK :: Natural Language Toolkit</a> : <code>nltk</code>.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Program to measure the similarity between</span></span><br><span class="line"><span class="comment"># two sentences using cosine similarity.</span></span><br><span class="line"><span class="keyword">from</span> nltk.corpus <span class="keyword">import</span> stopwords</span><br><span class="line"><span class="keyword">from</span> nltk.tokenize <span class="keyword">import</span> word_tokenize</span><br><span class="line"></span><br><span class="line"><span class="comment"># X = input("Enter first string: ").lower()</span></span><br><span class="line"><span class="comment"># Y = input("Enter second string: ").lower()</span></span><br><span class="line">X =<span class="string">"I love horror movies"</span></span><br><span class="line">Y =<span class="string">"Lights out is a horror movie"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># tokenization</span></span><br><span class="line">X_list = word_tokenize(X)</span><br><span class="line">Y_list = word_tokenize(Y)</span><br><span class="line"></span><br><span class="line"><span class="comment"># sw contains the list of stopwords</span></span><br><span class="line">sw = stopwords.words(<span class="string">'english'</span>)</span><br><span class="line">l1 =[];l2 =[]</span><br><span class="line"></span><br><span class="line"><span class="comment"># remove stop words from the string</span></span><br><span class="line">X_set = {w <span class="keyword">for</span> w <span class="keyword">in</span> X_list <span class="keyword">if</span> <span class="keyword">not</span> w <span class="keyword">in</span> sw}</span><br><span class="line">Y_set = {w <span class="keyword">for</span> w <span class="keyword">in</span> Y_list <span class="keyword">if</span> <span class="keyword">not</span> w <span class="keyword">in</span> sw}</span><br><span class="line"></span><br><span class="line"><span class="comment"># form a set containing keywords of both strings</span></span><br><span class="line">rvector = X_set.union(Y_set)</span><br><span class="line"><span class="keyword">for</span> w <span class="keyword">in</span> rvector:</span><br><span class="line"><span class="keyword">if</span> w <span class="keyword">in</span> X_set: l1.append(<span class="number">1</span>) <span class="comment"># create a vector</span></span><br><span class="line"><span class="keyword">else</span>: l1.append(<span class="number">0</span>)</span><br><span class="line"><span class="keyword">if</span> w <span class="keyword">in</span> Y_set: l2.append(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">else</span>: l2.append(<span class="number">0</span>)</span><br><span class="line">c = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># cosine formula</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(rvector)):</span><br><span class="line">c+= l1[i]*l2[i]</span><br><span class="line">cosine = c / <span class="built_in">float</span>((<span class="built_in">sum</span>(l1)*<span class="built_in">sum</span>(l2))**<span class="number">0.5</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"similarity: "</span>, cosine)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>优点:</p><ol><li>余弦相似度是很有用的，因为即使两个相似的数据对象由于大小而相距欧几里得距离很远，它们之间仍然可以具有较小的角度。角度越小，相似度越高。</li><li>当在多维空间上绘制时，余弦相似度捕获数据对象的方向（角度）从而减少幅度的影响。</li></ol>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2023/06/28/2023-07-02-similarity-measurement/</id>
    <link href="https://zhenxiang-shawn.github.io/2023/06/28/2023-07-02-similarity-measurement/"/>
    <published>2023-06-27T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>相似度计算在数据挖掘中指的是:计算在数据集里使用多个维度数据来表示的object 的距离. 一些常用的受欢迎的相似度测量方法有:</p>
<ol>
<li>Euclidean Distance.<br>
欧几里得距离(euclidean distance) 在二维和三维空间]]>
    </summary>
    <title>相似度计算: Cosine Similarity</title>
    <updated>2023-07-01T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Python" scheme="https://zhenxiang-shawn.github.io/tags/Python/"/>
    <category term="Pandas" scheme="https://zhenxiang-shawn.github.io/tags/Pandas/"/>
    <category term="Data Science" scheme="https://zhenxiang-shawn.github.io/tags/Data-Science/"/>
    <content>
      <![CDATA[<p>前一段时间发现从一个 DataFrame 中提取出一个Series. 不管被提取的 Series 的每一个值是不是一个 ndarray,返回值永远是一个一维的 ndarray.</p><span id="more"></span><h2 id="发现问题">发现问题</h2><p>比如有一个 Dataframe</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># each label_embed_vector contains 100 vectors.</span><br><span class="line">                  img_key  ...                                 label_embed_vector</span><br><span class="line">0   1628112148035278_1_31  ...  0.006821885239,-0.068260461092,-0.010561314411...</span><br><span class="line">1  1647947462308801_2_622  ...  0.028957819566,-0.027704223990,-0.001513824915...</span><br></pre></td></tr></table></figure><p>直接用 <code>df['label_embed_vector']</code>来读取数据的话,返回的是一个小的 dataframe. 这个时候就需要使用 <a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.values.html">Dataframe.values</a> 来读取数据. 这个方法会返回一个包含数据的 numpy array. 但是这个 ndarry 只是 1 维的.</p><p>在pandas.DataFrame.values的官方文档里,说推荐<code>pandas.DataFrame.to_numpy</code> 来代替 values 方法. 但是使用推荐的方法以后返回的结果还是 1 维的:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用`to_numpy`方法来读取`label_embed_vector`的数据以后发现他返回的是一个 shape 为 (86,)的 ndarray.</span></span><br><span class="line">data = df[<span class="string">&#x27;label_embed_vector&#x27;</span>].to_numpy()</span><br><span class="line"><span class="built_in">print</span>(data.shape)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>(<span class="number">86</span>,)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取第一个数据</span></span><br><span class="line"><span class="built_in">print</span>(data[<span class="number">0</span>])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="built_in">list</span>([<span class="number">0.006821885239</span>,-<span class="number">0.068260461092</span>,-<span class="number">0.010561314411</span>...])</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>上面的输出很奇怪, 从读取的数据来看的话, data 是一个(86,100)的 ndarray. 但是 data 的 shape 却是(86,).</p><h2 id="原因和解决方案">原因和解决方案</h2><p>通过 print 来检查数据以后发现, 虽然数据结构类似而且读取数据的时候和多维数组一样, 不过输出的数组确实不是多维度的:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># printout 检查数据</span></span><br><span class="line"><span class="built_in">print</span>(df[<span class="string">&#x27;label_embed_vector&#x27;</span>].to_numpy())</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>array([arrar([<span class="number">0.006821885239</span>,-<span class="number">0.068260461092</span>,-<span class="number">0.010561314411</span>...], array([<span class="number">0.028957819566</span>,-<span class="number">0.027704223990</span>,-<span class="number">0.001513824915</span>..], ... , array([-<span class="number">0.000122768164</span>,-<span class="number">0.020344628021</span>,<span class="number">0.018706960604</span>...])]</span><br><span class="line"><span class="comment"># .values 和 to_numpy 返回的是 1个包含了 86 个独立 array 的 1 维array.而不是多维的.</span></span><br></pre></td></tr></table></figure><h3 id="解决方案">解决方案</h3><p><strong>经过搜索发现 Pandas 这样做的原因是不能确定每一个 row 的 array 的长度是一样的, 因此返回的是一维数据.</strong> 这个时候可以用<code>np.vstack</code>来手动把一位数据来转化成多维数据. 比如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">data = np.vstack(df[<span class="string">&#x27;label_embed_vector&#x27;</span>].to_numpy())</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2023/06/25/2023-06-25-ndarray-not-show-all-dimensions/</id>
    <link href="https://zhenxiang-shawn.github.io/2023/06/25/2023-06-25-ndarray-not-show-all-dimensions/"/>
    <published>2023-06-24T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>前一段时间发现从一个 DataFrame 中提取出一个Series. 不管被提取的 Series 的每一个值是不是一个 ndarray,返回值永远是一个一维的 ndarray.</p>]]>
    </summary>
    <title>[Debug] Numpy Array 不显示全部维度的 shape</title>
    <updated>2023-06-24T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="解决方案" scheme="https://zhenxiang-shawn.github.io/categories/%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/"/>
    <category term="PyTorch" scheme="https://zhenxiang-shawn.github.io/tags/PyTorch/"/>
    <category term="Metal" scheme="https://zhenxiang-shawn.github.io/tags/Metal/"/>
    <content>
      <![CDATA[<p>M1 Max 的芯片的 GPU 算力已经很不错了, 想着自己不是视频编辑工作者,总不能这么浪费这么好的资源.偶尔用这电脑来跑跑中小型的模型也是不错的选择. TensorFlow 到现在对 M1 的支持还是有点一塌糊涂的感觉. 知名的 YOLO 模型也基本只有 PyTorch 版本. 想想 PyTorch 就是数据科学家必须了解的知识点.</p><span id="more"></span><p>我所用的云服务器基本都在外网,由于众所周知的因素,链接很慢也不是很稳定. 测试学习之类的还是在本地比较好.</p><h2 id="安装-PyTorch">安装 PyTorch</h2><p>下面是用来安装的命令以及注释:<br>如果你不确定这是不是你需要的版本, 请移步到<a href="https://pytorch.org/get-started/locally/">PyTorch 官网</a>来查看官网的文档.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 官网推荐安装 python 3.7或以上版本.</span></span><br><span class="line"><span class="comment"># 由于其他包的关系,这里推荐 3.8 的最新版</span></span><br><span class="line">conda create -n torch-gpu python=3.8</span><br><span class="line">conda activate torch-gpu</span><br><span class="line"><span class="comment"># 安装 PyTorch. MPS acceleration is available on MacOS 12.3+</span></span><br><span class="line">conda install pytorch torchvision torchaudio -c pytorch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装其他工具或依赖</span></span><br><span class="line">conda install -c conda-forge jupyter jupyterlab</span><br><span class="line">conda install  numpy</span><br><span class="line">conda install matplotlib</span><br><span class="line">conda install pandas</span><br></pre></td></tr></table></figure><h2 id="测试">测试</h2><p>如果是这种输出,那么恭喜你,安装完成.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">python</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> torch</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>torch.backends.mps.is_available()</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2023/02/23/2023-02-23-setup-pytorch-gpu-on-m1-mac/</id>
    <link href="https://zhenxiang-shawn.github.io/2023/02/23/2023-02-23-setup-pytorch-gpu-on-m1-mac/"/>
    <published>2023-02-22T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>M1 Max 的芯片的 GPU 算力已经很不错了, 想着自己不是视频编辑工作者,总不能这么浪费这么好的资源.偶尔用这电脑来跑跑中小型的模型也是不错的选择. TensorFlow 到现在对 M1 的支持还是有点一塌糊涂的感觉. 知名的 YOLO 模型也基本只有 PyTorch 版本. 想想 PyTorch 就是数据科学家必须了解的知识点.</p>]]>
    </summary>
    <title>在 M1 Mac上搭建 Pytorch GPU 加速环境</title>
    <updated>2023-02-22T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="解决方案" scheme="https://zhenxiang-shawn.github.io/categories/%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/"/>
    <category term="Hexo" scheme="https://zhenxiang-shawn.github.io/tags/Hexo/"/>
    <category term="LaTeX" scheme="https://zhenxiang-shawn.github.io/tags/LaTeX/"/>
    <content>
      <![CDATA[<p>Hexo默认的markdown渲染器是不支持数学公式的输入的. 一般来说都会用hexo的一些插件来实现对数学公式的支持. 这里数学公式的输入一般都是用的<code>LaTeX</code>.</p><span id="more"></span><h2 id="安装-hexo-renderer-pandoc">安装 hexo-renderer-pandoc</h2><p>因为Hexo默认的渲染插件对数学公式不支持,我们需要把默认的markdown渲染插件先移出,然后再安装我们所需要的插件: <a href="https://github.com/hexojs/hexo-renderer-pandoc">hexo-renderer-pandoc</a></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 卸载Hexo默认渲染插件：</span></span><br><span class="line">npm uninstall hexo-renderer-marked --save</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装pandoc渲染插件：</span></span><br><span class="line">npm install hexo-renderer-pandoc --save</span><br></pre></td></tr></table></figure><h2 id="安装-mathjax">安装 mathjax</h2><p>这一步对于某些运行环境来说不是必须的. 但是如果你安装pandoc以后还是没有成功渲染出来数学公式的话,那么就需要安装mathjax了.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-filter-mathjax --save</span><br></pre></td></tr></table></figure><h2 id="配置-config-文件">配置 config 文件</h2><p>在hexo 的配置文件: <code>_config.yml</code> 中添加以下代码:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">math:</span></span><br><span class="line">  <span class="string">...</span></span><br><span class="line">  <span class="attr">mathjax:</span></span><br><span class="line">    <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># Next v6.3.0 后的版本 tags 要设置成 ams</span></span><br><span class="line">    <span class="comment"># Available values: none | ams | all</span></span><br><span class="line">    <span class="attr">tags:</span> <span class="string">ams</span></span><br></pre></td></tr></table></figure><h2 id="清除缓存并重新构建文件">清除缓存并重新构建文件</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hexo clean</span><br><span class="line">hexo generate</span><br></pre></td></tr></table></figure><h2 id="部署或者使用本地服务测试">部署或者使用本地服务测试</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用本地服务测试</span></span><br><span class="line">hexo s</span><br><span class="line"></span><br><span class="line"><span class="comment"># 部署</span></span><br><span class="line">hexo deploy</span><br></pre></td></tr></table></figure><h2 id="使用数学公式的页面">使用数学公式的页面</h2><p><a href="https://zhenxiang-shawn.github.io/wiki/deep_learning/index.html#start">Deep Learning Intro</a></p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2023/02/03/2023-02-03-hexo-set-up-math-support/</id>
    <link href="https://zhenxiang-shawn.github.io/2023/02/03/2023-02-03-hexo-set-up-math-support/"/>
    <published>2023-02-02T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Hexo默认的markdown渲染器是不支持数学公式的输入的. 一般来说都会用hexo的一些插件来实现对数学公式的支持. 这里数学公式的输入一般都是用的<code>LaTeX</code>.</p>]]>
    </summary>
    <title>如果启用 Hexo 博客的数学公式支持</title>
    <updated>2023-02-02T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Computer Vision" scheme="https://zhenxiang-shawn.github.io/tags/Computer-Vision/"/>
    <category term="Machine Learning" scheme="https://zhenxiang-shawn.github.io/tags/Machine-Learning/"/>
    <category term="Deep Learning" scheme="https://zhenxiang-shawn.github.io/tags/Deep-Learning/"/>
    <content>
      <![CDATA[<p>数据增强是比较机器学习中比较常用的方法. 但是并不是所有的数据增强都可以有效的提升精度. 有些数据增强的方法很可能会增加噪声,使模型表现比数据增强之前还要差.</p><span id="more"></span><h2 id="什么是数据增强">什么是数据增强</h2><p>数据增强（Data Augmentation）是一种通过让有限的数据产生更多的等价数据来人工扩展训练数据集的技术。它是克服训练数据不足的有效手段，目前在深度学习的各个领域中应用广泛。但是由于生成的数据与真实数据之间的差异，也不可避免地带来了噪声问题。</p><h2 id="为什么需要数据增强">为什么需要数据增强</h2><p>深度神经网络在许多任务中表现良好，但这些网络通常需要大量数据才能避免过度拟合。遗憾的是，许多场景无法获得大量数据，例如医学图像分析。数据增强技术的存在是为了解决这个问题，这是针对有限数据问题的解决方案。数据增强一套技术，可提高训练数据集的大小和质量，以便您可以使用它们来构建更好的深度学习模型。在计算视觉领域，生成增强图像相对容易。即使引入噪声或裁剪图像的一部分，模型仍可以对图像进行分类，数据增强有一系列简单有效的方法可供选择，有一些机器学习库来进行计算视觉领域的数据增强，比如：它封装了很多数据增强算法，给开发者提供了方便。</p><h2 id="CV中常见的图像增强的方法">CV中常见的图像增强的方法</h2><p>CV领域的数据增强大致可以分为两类: 第一类是基于基本图像处理技术的数据增强，第二个类别是基于深度学习的数据增强算法.</p><h3 id="基于图像处理的数据增强方法">基于图像处理的数据增强方法</h3><ul><li><p>Flipping (翻转)<br>一般都是水平方向翻转,很少用垂直方向(镜像变换).</p></li><li><p>Rotation (旋转)<br>一般使用旋转的时候,需要注意旋转角度. 以数据集MNIST为例, 轻微旋转(1-20°)可能有用.</p></li><li><p>Color Space (色彩空间)<br>简单做法是隔离单个色彩通道，例如Red®，Green(G)或Blue(B)，此外可以通过简单的矩阵运算以增加或减少图像的亮度。更高级的做法从颜色直方图着手，更改这些直方图中的强度值(想到了图像处理中的直方图均衡)。一般可以调节的有对比度(contrast),亮度(brighteness),色调(hue)</p></li><li><p>Cropping (裁剪)<br>分统一裁剪和随机裁剪。统一裁剪将不同尺寸的图像裁剪至设定大小，随机裁剪类似translation，不同之处在于translation保留原图尺寸而裁剪会降低尺寸。裁剪要注意不要丢失重要信息以至于改变图像标签。<br>eg. ssd_crop (详解?)</p></li><li><p>translation(位置变换)<br>向左，向右，向上或向下移动图像可能是非常有用的转换，以避免数据中的位置偏差。例如人脸识别数据集中人脸基本位于图像正中，位置变换可以增强模型泛化能力。</p></li><li><p>noise injection(添加噪声)</p></li><li><p>color space transformations(色彩空间增强)</p></li></ul><p>照明偏差是图像识别问题中最常见的挑战之一，因此色彩空间转换（也称为光度转换）的比较直观有效。</p><ol><li>遍历图像以恒定值减少或增加像素值（过亮或过暗）</li><li>拼接出（splice out）各个RGB颜色矩阵</li><li>将像素值限制为某个最小值或最大值</li><li>操作色彩直方图以改变图像色彩空间特征</li></ol><p>注意将彩色图转换黑白虽然简化了这些操作，但精度会降低</p><ul><li>geometric versus photometric transformations(几何与光度转换)</li></ul><p><strong>几何变换</strong>改变图像中像素的空间位置关系，即改变物体的形状、大小、方向和位置，但像素值本身不变。常见操作包括：翻转、旋转、裁剪、缩放、平移、剪切等。这类变换直接模拟了现实中物体在不同视角下的几何外观变化，有助于模型学习空间不变性。</p><p><strong>光度变换</strong>改变图像的像素值但不改变像素的空间位置，即影响图像的&quot;外观&quot;而非&quot;形状&quot;。常见操作包括：亮度调节、对比度调节、饱和度调整、色调偏移、添加噪声、高斯模糊等。这类变换模拟了不同光照条件、摄像头参数或天气条件下的成像差异，有助于模型学习对光照和颜色变化的鲁棒性。</p><p>下表总结了二者的核心区别：</p><table><thead><tr><th>对比维度</th><th>几何变换</th><th>光度变换</th></tr></thead><tbody><tr><td>操作对象</td><td>像素坐标（位置）</td><td>像素值（颜色/亮度）</td></tr><tr><td>典型操作</td><td>翻转、旋转、裁剪、缩放</td><td>亮度、对比度、色调、噪声</td></tr><tr><td>保持语义</td><td>需谨慎（避免裁剪掉关键目标）</td><td>通常保持（除非极端变色）</td></tr><tr><td>实现难度</td><td>低（OpenCV/MATLAB 内置函数）</td><td>较低（直接像素级操作）</td></tr><tr><td>适用场景</td><td>位置/姿态变化频繁的任务</td><td>光照/颜色多变的任务</td></tr></tbody></table><p>在实际应用中，几何变换和光度变换通常组合使用，以最大化数据多样性。但需注意，过强的光度变换可能导致颜色分布失真，而过度的几何变换则可能破坏图像语义——这正是 KeepAugment 等新方法试图解决的问题。</p><h3 id="基于深度学习的数据增强算法">基于深度学习的数据增强算法</h3><ul><li>mixing images(图像混合)<br>做法是通过平均图像像素值将图像混合在一起:<br><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/mixing_img.png" alt="image"></li></ul><p>研究发现是当混合来自整个训练集的图像而不是仅来自同一类别的实例的图像时，可以获得更好的结果。其它一些做法：</p><ol><li>一种线性方法是将图像组合成新的训练实例:<br><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/img_data_aug1.png" alt="image"></li><li>另一方法是随机裁剪图像并将裁剪后的图像连接在一起以形成新图像:<br><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/img_data_aug2.png" alt="image"></li></ol><ul><li>Random Crop then montage 随机裁剪再拼接<br>这类方法从人的视角看毫无意义，但确实提升了精度。可能解释是数据集大小的增加导致了诸如线和边之类的低级特征的更可靠表示。</li><li>random erasing随机擦除<br>这一点受到dropout正规化的启发，随机擦除迫使模型学习有关图像的更多描述性特征，从而防止过拟合某个特定视觉特征。随机擦除的好处在于可以确保网络关注整个图像，而不只是其中的一部分。随机擦除一般分为(1)Image-ware random Erasing(2) Object-ware Random Erasing 和 (3)Image and Object-ware Random Erasing. 最后随机擦除的一个缺点是不一定会保留标签.</li></ul><h2 id="KeepAugment-保持增强">KeepAugment (保持增强)</h2><p>新方法控制数据增强的保真度，从而减少有害的错误信息。研究者的想法是通过显著性映射测量图像中矩形区域的重要性，并确保数据增强后始终呈现得分最高的区域：对于裁剪，通过避免切割重要区域(见下图a5和b5);对于图像级转换，通过将重要区域粘贴到转换图像顶部<br><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/keep_aug_exp1.png" alt="image"><br>KeepAugment的伪代码:<br><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/keep_aug_equation.png" alt="image"></p><h2 id="如何选择数据增强">如何选择数据增强</h2><p>数据增强需要根据数据集而定。不过一般的像图像旋转，图像翻转等方法比较通用，在任何数据集上都有良好的表现。</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/06/21/2022-06-21-cv-data-augmentation/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/06/21/2022-06-21-cv-data-augmentation/"/>
    <published>2022-06-20T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>数据增强是比较机器学习中比较常用的方法. 但是并不是所有的数据增强都可以有效的提升精度. 有些数据增强的方法很可能会增加噪声,使模型表现比数据增强之前还要差.</p>]]>
    </summary>
    <title>计算机视觉中的数据增强</title>
    <updated>2022-06-20T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Computer Vision" scheme="https://zhenxiang-shawn.github.io/tags/Computer-Vision/"/>
    <category term="Big Data" scheme="https://zhenxiang-shawn.github.io/tags/Big-Data/"/>
    <content>
      <![CDATA[<p>物体检测中一般模型会在一个物体上检测出多个拥有不同confidence score的格子, 但是对于输出结果来说,我们并不需要那么多的预测结果.因此,我们会用一些集成方法(ensemble method)来把这些预测结果合并成一个.</p><span id="more"></span><h2 id="Non-maximum-Suppression-NMS-非最大化抑制">Non-maximum Suppression (NMS, 非最大化抑制)</h2><p>NMS的思路是对所有类别的检测框进行循环过滤.对于某个类别, NMS 会根据预测结果的confidence score来把预测结果排序,然后选择有最大confidence的预测结果. 在此之后,通过一个IOU(Intersection over Union) 的threshold,就可以过滤其他的预测结果从而减少预测结果的重复性.</p><p>基于这种计算逻辑的NMS有两个缺点。首先，NMS算法需要一个超参即<code>IOU Threshold</code>，这个阈值在不同任务中很难平衡。其次，NMS会将相邻或者重叠的两个物体对应的两个大概率目标框去掉一个，造成漏检。</p><h2 id="Soft-NMS">Soft-NMS</h2><p>Soft NMS方法总流程和NMS算法流程相同. Soft NMS算法会根据IOU成比例的减少confidence score的值. 对于重叠的框,重叠区(IOU)越大,置信度衰减越严重. 一般的, Soft NMS降低置信度权重的计算方法有两个:<br>1. 线性法<br>2. 高斯法<br>NMS是一种特殊的线性法的Soft-NMS.(如下)</p><p>$$<br>s_i = \left.<br>\begin{cases}<br>s_i,  iou &lt; T \<br>0,  iou \ge T\<br>\end{cases}<br>\right.<br>$$</p><p>计算方法最好选用连续性的函数,因为采用不连续的函数会导致box集合中的confidence score出现断层.</p><h3 id="WBF-Weighted-Boxes-Fusion-method">WBF (Weighted Boxes Fusion) method</h3><p>因为NMS和Soft-NMS会删掉很多的检测结果,所以可以说这些集成方法并没有用到模型预测出来的所有信息. 而WBF使用所有预测结果的置信度来计算出一个平均的检测结果,所以WBF可以在所有结果都不太准确的时候修正结果.</p><h3 id="NMW-Non-maximum-Weighted-method">NMW (Non-maximum Weighted) method</h3><p>NMW方法与WBF方法使用了相似的思路，只是NMW方法使用了IoU来调整检测结果的权重。</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/06/10/2022-06-16-nms-vs-softnms-vs-wbf/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/06/10/2022-06-16-nms-vs-softnms-vs-wbf/"/>
    <published>2022-06-09T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>物体检测中一般模型会在一个物体上检测出多个拥有不同confidence score的格子, 但是对于输出结果来说,我们并不需要那么多的预测结果.因此,我们会用一些集成方法(ensemble method)来把这些预测结果合并成一个.</p>]]>
    </summary>
    <title>优化物体检测结果时常用的集成方法</title>
    <updated>2022-06-15T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="BigData" scheme="https://zhenxiang-shawn.github.io/tags/BigData/"/>
    <content>
      <![CDATA[<p>Sorted String Table(SSTable)–是存储，处理和交换数据集的最流行的输出之一。正如名字本身所包含的意思一样，SSTable是一个简单的抽象，用来高效地存储大量的键-值对数据，同时做了优化来实现顺序读/写操作的高吞吐量。</p><span id="more"></span><h2 id="什么是-SSTable？">什么是 SSTable？</h2><p>Google 发布的Bigtable论文中解释道：SSTable提供一个可持久化[persistent]，有序的、不可变的从键到值的映射关系，其中键和值都是任意字节长度的字符串。SSTable提供了以下操作：按照某个键来查询关联值，可以指定键的范围，来遍历其中所有的键值对。每个SSTable内部由一系列块(block)组成(通常每块大小为64KB，是可配置的)。使用存储在SSTable结尾的块索引(block index)来定位块；当SSTable打开时，索引会被加载到内存里。一次磁盘寻道(disk seek)就可以完成查询(lookup)操作：首先通过二分查找在存储在内存的索引中找到对应的块，然后从磁盘上读取这块内容。SSTable也可以完整地映射到内存里，这样在执行查询和扫描(scan)的时候就不用操作磁盘了.</p><div class="tag-plugin image"><div class="image-bg"><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/sstable_diagram.png" alt="SSTable 结构"/></div><div class="image-meta"><span class="image-caption center">SSTable 结构</span></div></div><p>Scylladb在其官网(<a href="https://www.scylladb.com/glossary/sstable/">What is a SSTable? Definition &amp; FAQs | ScyllaDB</a>)中做出了以下解释：</p><blockquote><p>Sorted Strings Table (SSTable) is a persistent file format used by ScyllaDB, Apache Cassandra, and other NoSQL databases to take the in-memory data stored in memtables, order it for fast access, and store it on disk in a persistent, ordered, immutable set of files. Immutable means SSTables are never modified. They are later merged into new SSTables or deleted as data is updated.</p></blockquote><p>其实除了Scylladb以外，当有很多公司使用SSTable，这些SSTable有些许轻微的不同。这里我们只研究BigTable中的SSTable。</p><div class="tag-plugin colorful note" color="cyan"><div class="title">什么是memtable？</div><div class="body"><p>MemTable是一种在内存中保存数据的数据结构，然后再在合适的时机，MemTable中的数据会flush到SST file中。MemTable既可以支持读服务也可以支持写服务，写操作会首先将数据写入Memtable，读操作在query SST files之前会首先从MemTable中query数据（因为MemTable中的数据一直是最新的）。一旦MemTable满了，就会转换为只读的不可改变的，然后会创建一个新的MemTable来提供新的写操作。后台线程负责将MemTable中的数据flush到SST file，然后这个MemTable就会被销毁。</p></div></div><h2 id="SSTable-有何作用？">SSTable 有何作用？</h2><h3 id="01：储存格式">01：储存格式</h3><p>在新数据写入时，这个操作首先提交到日志中作为redo纪录，最近的数据存储在内存的排序缓存memtable中；旧的数据存储在一系列的SSTable 中。在recover中，tablet server从METADATA表中读取metadata，metadata包含了组成Tablet的所有SSTable（纪录了这些SSTable的元 数据信息，如SSTable的位置、StartKey、EndKey等）以及一系列日志中的redo点。Tablet Server读取SSTable的索引到内存，并replay这些redo点之后的更新来重构memtable。在读时，完成格式、授权等检查后，读会同时读取SSTable、memtable（HBase中还包含了BlockCache中的数据）并合并他们的结果，由于SSTable和memtable都是字典序排列，因而合并操作可以很高效完成。</p><h3 id="02：压缩（Compaction）过程中的使用">02：压缩（Compaction）过程中的使用</h3><p>随着memtable大小增加到一个阀值，这个memtable会被冻住而创建一个新的memtable以供使用，而旧的memtable会转换成一个SSTable而写道GFS中，这个过程叫做minor compaction。这个minor compaction可以减少内存使用量，并可以减少日志大小，因为持久化后的数据可以从日志中删除。在minor compaction过程中，可以继续处理读写请求。<br>每次minor compaction会生成新的SSTable文件，如果SSTable文件数量增加，则会影响读的性能，因而每次读都需要读取所有SSTable文件，然后合并结果，因而对SSTable文件个数需要有上限，并且时不时的需要在后台做merging compaction，这个merging compaction读取一些SSTable文件和memtable的内容，并将他们合并写入一个新的SSTable中。当这个过程完成后，这些源SSTable和memtable就可以被删除了。<br>如果一个merging compaction是合并所有SSTable到一个SSTable，则这个过程称做major compaction。一次major compaction会将mark成删除的信息、数据删除，而其他两次compaction则会保留这些信息、数据（mark的形式）。Bigtable会时不时的扫描所有的Tablet，并对它们做major compaction。这个major compaction可以将需要删除的数据真正的删除从而节省空间，并保持系统一致性。</p><h2 id="SSTable-设计成immutable的优点">SSTable 设计成immutable的优点</h2><ol><li>在读SSTable是不需要同步。读写同步只需要在memtable中处理，为了减少memtable的读写竞争，Bigtable将memtable的row设计成copy-on-write，从而读写可以同时进行。</li><li>永久的移除数据转变为SSTable的Garbage Collect。每个Tablet中的SSTable在METADATA表中有注册，master使用mark-and-sweep算法将SSTable在GC过程中移除。</li><li>可以让Tablet Split过程变的高效，我们不需要为每个子Tablet创建新的SSTable，而是可以共享父Tablet的SSTable。</li></ol><h2 id="更多">更多</h2><p><a href="https://dl.acm.org/doi/abs/10.1145/1365815.1365816">《Bigtable: A Distributed Storage System for Structured Data》</a><br><a href="https://mrcroxx.github.io/posts/paper-reading/bigtable-osdi06/#5-%E5%AE%9E%E7%8E%B0">《Bigtable: A Distributed Storage System for Structured Data》论文翻译</a></p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/05/18/2022-05-18-upper-level-datatype-sstable/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/05/18/2022-05-18-upper-level-datatype-sstable/"/>
    <published>2022-05-17T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Sorted String Table(SSTable)–是存储，处理和交换数据集的最流行的输出之一。正如名字本身所包含的意思一样，SSTable是一个简单的抽象，用来高效地存储大量的键-值对数据，同时做了优化来实现顺序读/写操作的高吞吐量。</p>]]>
    </summary>
    <title>高级数据结构 SSTable</title>
    <updated>2022-05-17T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Computer Vision" scheme="https://zhenxiang-shawn.github.io/tags/Computer-Vision/"/>
    <content>
      <![CDATA[<p>计算机视觉可以分为两大方向：基于学习的方法和基于几何的方法。其中基于学习的方法最火的就是深度学习，而基于几何方法最火的就是视觉SLAM(Simultaneous Localization and Mapping)</p><span id="more"></span><h2 id="图像分割">图像分割</h2><p><strong>图像分割</strong>：图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。 它是由图像处理到图像分析的关键步骤。图形分割是Target Segmentation，是data/image segmentation的一种。任务是把目标对应的部分分割出来。对于一般的光学图像而言，分割像素是一个比较常见的目标，就是要提取哪一些像素是用于表述已知目标的。这种Segmentation可以是一个分类（classificatio）问题，就是把每一个pixel做labeling，提出感兴趣的那一类label的像素。也可以是clustering的问题，即是不知道label，但需要满足一些optimality，比如要cluster之间的correlation最小之类的。（像素级的对前景与背景进行分类，将背景剔除）</p><div class="tag-plugin image"><div class="image-bg"><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/img_segmentation_eg.png" alt="图像分割"/></div><div class="image-meta"><span class="image-caption center">图像分割</span></div></div><h2 id="目标检测">目标检测</h2><p><strong>目标检测</strong>：目标检测是Target Detection。最早的detection system应该是搞雷达的人首先提出并且heavily study的，最简单的任务就是从看似随机（random）又充满干扰（interference）和噪音（noise）的信号中，抓取到有信息的特征（information-bearing pattern）。最简单的一个栗子，就是当你拿到一段随机的雷达回波，可以设置一个threshold，当高于这个threshold，就认为是探测到了高速大面积飞行器之类的高回波的目标。当然，这里面的threshold该怎么设计，涉及到False Alarm和Miss Detection之间的平衡。人们往往需要寻找最佳的transform或者domain去对信号进行分析。（定位目标，确定目标位置及大小）</p><div class="tag-plugin image"><div class="image-bg"><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/object_detection_eg.png" alt="图像检测" style="width:800px;height:512px;"/></div><div class="image-meta"><span class="image-caption center">图像检测</span></div></div><p>从输入和输出来比较的话,物体检测的输入和图像分割的输入都是一样的. 一般情况下都是<code>x * y * 3</code>的数组(可以理解为RGB各一层)或者是<code>x * y</code>的 binary image. 物体检测的输出一般都是一个bounding box的列表,而图像分割的输出是一个矩阵, 用来表示mask.</p><h2 id="分割和检测融合">分割和检测融合?</h2><p>目前也有很多算法将分割和检测进行融合：比如:</p><ul><li><a href="https://arxiv.org/abs/1912.05070">RDSNet: A New Deep Architecture for Reciprocal Object Detection and Instance Segmentation</a></li><li><a href="https://openaccess.thecvf.com/content_CVPR_2020/papers/Zhou_Joint_3D_Instance_Segmentation_and_Object_Detection_for_Autonomous_Driving_CVPR_2020_paper.pdf">Joint 3D Instance Segmentation and Object Detection for Autonomous Driving</a></li><li><a href="https://arxiv.org/abs/1907.05740">Gated-SCNN: Gated Shape CNNs for Semantic Segmentation</a></li><li>etc.</li></ul><p>通过一个/一种深度学习的结构是很难把这两种不同的任务结合到一起的,以<a href="https://arxiv.org/abs/1907.05740">Gated-SCNN: Gated Shape CNNs for Semantic Segmentation</a>为例, 作者认为通过一个深度CNN网络同时处理图像的颜色，形状和纹理信息用于像素级分类可能不是理想的做法，因此该论文提出two-stream CNN结构，将形状信息作为单独处理的分支。两分支包括shape stream和regular stream，二者相互协作且是并行的，通过设计的门控卷积层和局部监督损失有效地移除了噪声并帮助shape stream仅仅处理与边界相关的信息，然后通过ASPP模块保留多尺度上下文信息，融合regular stream的语义区域特征和shape stream的边界特征产生refined的分割输出，尤其refine在边界周围的像素分割。</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/04/24/2022-04-24-img-segmentation/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/04/24/2022-04-24-img-segmentation/"/>
    <published>2022-04-23T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>计算机视觉可以分为两大方向：基于学习的方法和基于几何的方法。其中基于学习的方法最火的就是深度学习，而基于几何方法最火的就是视觉SLAM(Simultaneous Localization and Mapping)</p>]]>
    </summary>
    <title>图像分割与图像检测</title>
    <updated>2022-06-20T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Bash" scheme="https://zhenxiang-shawn.github.io/tags/Bash/"/>
    <content>
      <![CDATA[<p>如果你希望通过脚本运算获得的结果是你想要的，那么你需要应用到算术运算符参与算术运算以获得你所需的结果. 在Bash脚本中,一般比较语法都会放在方括号中.</p><span id="more"></span><h2 id="单括号与双括号的对比">单括号与双括号的对比</h2><table><thead><tr><th>属性</th><th>单括号 [ ]</th><th>双括号 [[ ]]</th></tr></thead><tbody><tr><td>括号两边都要有空格分隔</td><td>√</td><td>√</td></tr><tr><td>内部操作符与变量之间要有空格</td><td>√</td><td>√</td></tr><tr><td>字符串比较时需要在<code>&lt; &gt;</code>之前加入<code>\</code>进行转义</td><td>√</td><td></td></tr><tr><td>括号中字符串或者${}变量尽量使用”” 双引号扩住</td><td>√</td><td>√</td></tr><tr><td>括号中如若未使用<code>&quot;&quot;</code>会进行模式和元字符匹配</td><td></td><td>√</td></tr><tr><td>括号中可以使用<code>-a</code>或者<code>-o</code>等进行逻辑运算</td><td>√</td><td></td></tr><tr><td>括号中可以使用<code>&amp;&amp;</code>或者<code>||</code>进行逻辑运算</td><td></td><td>√</td></tr><tr><td>括号可以配合<code>!</code>使用</td><td>√</td><td>√</td></tr></tbody></table><p>此外, <strong>[ ] 是bash 内置命令</strong> 而**[[ ]] 是bash  关键词, 双方括号表达式会返回一个退出码**.</p><h2 id="与-eq的对比以及延伸">==与-eq的对比以及延伸</h2><p>一般来说, 使用 <code>=</code>或者<code>==</code>来做字符串的对比; 使用<code>-eq</code>来做数值的对比.</p><h3 id="使用-来对比字符串">使用==来对比字符串</h3><table><thead><tr><th>str命令</th><th>含义</th></tr></thead><tbody><tr><td><code>[[ str ]]</code></td><td>判断字符串是否不为空</td></tr><tr><td><code>[[ -z str ]]</code></td><td>判断字符串是否为空,</td></tr><tr><td><code>[[ -n str ]]</code></td><td>判断字符串是否不为空</td></tr><tr><td><code>[[ str1 = str2 ]]</code></td><td>判断字符串是否相同</td></tr><tr><td><code>[[ str1 == str2 ]]</code></td><td>判断字符串是否相同</td></tr><tr><td><code>[[ str1 != str2 ]]</code></td><td>判断字符串是否不相等</td></tr><tr><td><code>[[ str1 \\&lt; str2 ]]</code></td><td>判断按字典序排序str1是否在str2之前</td></tr><tr><td><code>[[ str1 \\&gt; str2 ]]</code></td><td>判断按字典序排序str1是否在str2之后</td></tr></tbody></table><h3 id="使用-eq-等命令来对比数值">使用 -eq 等命令来对比数值</h3><table><thead><tr><th>num命令</th><th>含义</th></tr></thead><tbody><tr><td><code>[[ val1 -eq val2 ]]</code></td><td>判断val1是否与val2相等</td></tr><tr><td><code>[[ val1 -ne val2 ]]</code></td><td>判断val1是否与val2不相等</td></tr><tr><td><code>[[ val1 -lt val2 ]]</code></td><td>判断val1是否小于val2</td></tr><tr><td><code>[[ val1 -le val2 ]]</code></td><td>判断val1是否小于等于val2</td></tr><tr><td><code>[[ val1 -gt val2 ]]</code></td><td>判断val1是否大于val2</td></tr><tr><td><code>[[ val1 -ge val2 ]]</code></td><td>判断val1是否大于等于val2</td></tr></tbody></table><h3 id="文件状态检查">文件状态检查</h3><table><thead><tr><th>命令</th><th>含义</th></tr></thead><tbody><tr><td><code>-b filename</code></td><td>检查是否文件存在并且是<em>块文件</em>*</td></tr><tr><td><code>-c filename</code></td><td>检查是否文件存在并且是字符文件</td></tr><tr><td><code>-d path</code></td><td>检查是否文件存在并且是目录</td></tr><tr><td><code>-f filename</code></td><td>检查是否文件存在并且为正规文件</td></tr><tr><td><code>-g path</code></td><td>检查是否文件存在并且设置了SGID</td></tr><tr><td><code>-h filename</code></td><td>检查是否文件存在并且是符号链接文件</td></tr><tr><td><code>-p filename</code></td><td>见怕是否文件存在并且是命名管道</td></tr><tr><td><code>-s filename</code></td><td>检查是否文件存在并且文件大小大于0</td></tr><tr><td><code>-S filename</code></td><td>检查是否文件存在并且是socket</td></tr><tr><td><code>file1 -nt file2</code></td><td>检查是否file1比file2新</td></tr><tr><td><code>file1 -ot file2</code></td><td>检查是否file1比file2旧</td></tr><tr><td><code>file1 -ef file2</code></td><td>检查是否file1和file2 链接到了相同的文件</td></tr></tbody></table><blockquote><p><strong>块存储</strong>的用户是可以读写块设备的软件系统.</p><p><strong>文件存储</strong>的用户是自然人.</p><p><strong>对象储存</strong>的用户是其他计算机软件.</p></blockquote><p>Linux系统中所有文件/文件夹都是已文件的格式保存的. 但是为了更容易区分.文件和文件/文件夹不同的命令区分开来列出.</p><table><thead><tr><th>命令</th><th>含义</th></tr></thead><tbody><tr><td>-u path</td><td>检查是否path制定的文件或目录存在并且设置了SUID</td></tr><tr><td>-w path</td><td>检查是否path制定的文件或目录存在并且可写</td></tr><tr><td>-x path</td><td>检查是否path指定的文件或目录存在并且可执行</td></tr><tr><td>-r path</td><td>检查是否文件/目录存在并且可读</td></tr><tr><td>-e filename</td><td>检查是否文件/目录存在</td></tr><tr><td>-O path</td><td>检查是否path存在并且被当前进程的有效用户id 的用户拥有</td></tr><tr><td>-G path</td><td>检查是否path 存在并且属于当前进程的有效用户id 的用户的用户组</td></tr></tbody></table><h2 id="双括号-对比单括号-的优势">双括号 [[ ]] 对比单括号 [ ] 的优势</h2><ol><li><p>[[是 bash 程序语言的关键字。并不是一个命令，[[ ]] 结构比[ ]结构更加通用。在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割，但是会发生参数扩展和命令替换。</p></li><li><p>支持字符串的模式匹配，使用=~操作符时甚至支持shell的正则表达式。字符串比较时可以把右边的作为一个模式，而不仅仅是一个字符串，比如[[ hello == hell? ]]，结果为真。[[ ]] 中匹配字符串或通配符，不需要引号。</p></li><li><p>使用[[ … ]]条件判断结构，而不是[… ]，能够防止脚本中的许多逻辑错误。比如，&amp;&amp;、||、&lt;和&gt; 操作符能够正常存在于[[ ]]条件判断结构中，但是如果出现在[ ]结构中的话，会报错。</p></li><li><p>bash把双中括号中的表达式看作一个单独的元素，并返回一个退出状态码。</p></li></ol><p><strong>使用[[ … ]]条件判断结构, 而不是[ … ], 能够防止脚本中的许多逻辑错误. 比如,&amp;&amp;, ||, &lt;, 和&gt; 操作符能够正常存在于[[]]条件判断结构中, 但是如果出现在[ ]结构中的话, 会报错. 因此推荐直接选用双括号.</strong></p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/04/05/2022-04-04-bash_eq_talk/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/04/05/2022-04-04-bash_eq_talk/"/>
    <published>2022-04-04T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>如果你希望通过脚本运算获得的结果是你想要的，那么你需要应用到算术运算符参与算术运算以获得你所需的结果. 在Bash脚本中,一般比较语法都会放在方括号中.</p>]]>
    </summary>
    <title>Bash中的比较语法详解</title>
    <updated>2022-04-05T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Algorithm" scheme="https://zhenxiang-shawn.github.io/tags/Algorithm/"/>
    <content>
      <![CDATA[<p>“稳定婚姻问题”在生活中是一个典型的问题,通俗地可叙述为：当前有N位男生和N位女生最后要组成稳定的婚姻家庭，过程开始之前男生和女生在各自的心目中都按照喜爱程度对N位异性有了各自的排序，男生和女生结婚后，对于每一对男生女生，不会出现比起当前匹配的伴侣互相更喜爱的一对男生女生，即可认为婚姻是稳定的。</p><span id="more"></span><p>1962 年，美国数学家 David Gale 和 Lloyd Shapley 发明了一种寻找稳定婚姻的策略。不管男女各有多少人，不管他们各自的偏好如何，应用这种策略后总能得到一个稳定的婚姻搭配。换句话说，他们证明了稳定的婚姻搭配总是存在的。</p><h2 id="解决算法">解决算法</h2><p>先对所有男士进行落选标记，称其为自由男。当存在自由男时，进行以下操作：</p><ol><li>每一位自由男在所有尚未拒绝她的女士中选择一位被他排名最优先的女士</li><li>每一位女士将正在追求她的自由男与其当前男友进行比较，选择其中排名优先的男士作为其男友，即若自由男优于当前男友，则抛弃前男友；否则保留其男友，拒绝自由男。</li><li>若某男士被其女友抛弃，重新变成自由男。</li></ol><p>在算法执行期间，自由男们主动出击，依次对最喜欢和次喜欢的女人求爱，一旦被接受，即失去自由身，进入订婚状态；而女人们则采取“守株待兔”和“喜新厌旧”策略，对前来求爱的男士进行选择：若该男子比未婚夫强，则悔婚，选择新的未婚夫；否则拒绝该男子的求婚。被女友抛弃的男人重获自由身，重新拥有了追求女人的权利——当然，新的追求对象比不过前女友。</p><p>这样，在算法执行期间，每个人都有可能订婚多次——也有可能一开始就找到了自己的最爱，从一而终——每订一次婚，女人们的选择就会更有利，而男人们的品味则越来越差。只要男女生的数量相等，则经过多轮求婚，订婚，悔婚和再订婚之后，每位男女最终都会找到合适的伴侣——虽然不一定是自己的最爱（男人没能追到自己的最爱，或女人没有等到自己的最爱来追求），但绝对不会出现“虽然彼此相爱，却不能在一起”的悲剧，所有人都会组成稳定的婚姻。</p><h3 id="示例代码">示例代码</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python3 program for stable marriage problem</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Number of Men or Women</span></span><br><span class="line">N = <span class="number">4</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># This function returns true if</span></span><br><span class="line"><span class="comment"># woman &#x27;w&#x27; prefers man &#x27;m1&#x27; over man &#x27;m&#x27;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">wPrefersM1OverM</span>(<span class="params">prefer, w, m, m1</span>):</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Check if w prefers m over her</span></span><br><span class="line">    <span class="comment"># current engagement m1</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(N):</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># If m1 comes before m in list of w,</span></span><br><span class="line">        <span class="comment"># then w prefers her current engagement,</span></span><br><span class="line">        <span class="comment"># don&#x27;t do anything</span></span><br><span class="line">        <span class="keyword">if</span> (prefer[w][i] == m1):</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># If m comes before m1 in w&#x27;s list,</span></span><br><span class="line">        <span class="comment"># then free her current engagement</span></span><br><span class="line">        <span class="comment"># and engage her with m</span></span><br><span class="line">        <span class="keyword">if</span> (prefer[w][i] == m):</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Prints stable matching for N boys and N girls.</span></span><br><span class="line"><span class="comment"># Boys are numbered as 0 to N-1.</span></span><br><span class="line"><span class="comment"># Girls are numbered as N to 2N-1.</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">stableMarriage</span>(<span class="params">prefer</span>):</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Stores partner of women. This is our output</span></span><br><span class="line">    <span class="comment"># array that stores passing information.</span></span><br><span class="line">    <span class="comment"># The value of wPartner[i] indicates the partner</span></span><br><span class="line">    <span class="comment"># assigned to woman N+i. Note that the woman numbers</span></span><br><span class="line">    <span class="comment"># between N and 2*N-1. The value -1 indicates</span></span><br><span class="line">    <span class="comment"># that (N+i)&#x27;th woman is free</span></span><br><span class="line">    wPartner = [-<span class="number">1</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(N)]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># An array to store availability of men.</span></span><br><span class="line">    <span class="comment"># If mFree[i] is false, then man &#x27;i&#x27; is free,</span></span><br><span class="line">    <span class="comment"># otherwise engaged.</span></span><br><span class="line">    mFree = [<span class="literal">False</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(N)]</span><br><span class="line"></span><br><span class="line">    freeCount = N</span><br><span class="line"></span><br><span class="line">    <span class="comment"># While there are free men</span></span><br><span class="line">    <span class="keyword">while</span> (freeCount &gt; <span class="number">0</span>):</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># Pick the first free man (we could pick any)</span></span><br><span class="line">        m = <span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> (m &lt; N):</span><br><span class="line">            <span class="keyword">if</span> (mFree[m] == <span class="literal">False</span>):</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            m += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># One by one go to all women according to</span></span><br><span class="line">        <span class="comment"># m&#x27;s preferences. Here m is the picked free man</span></span><br><span class="line">        i = <span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> i &lt; N <span class="keyword">and</span> mFree[m] == <span class="literal">False</span>:</span><br><span class="line">            w = prefer[m][i]</span><br><span class="line"></span><br><span class="line">            <span class="comment"># The woman of preference is free,</span></span><br><span class="line">            <span class="comment"># w and m become partners (Note that</span></span><br><span class="line">            <span class="comment"># the partnership maybe changed later).</span></span><br><span class="line">            <span class="comment"># So we can say they are engaged not married</span></span><br><span class="line">            <span class="keyword">if</span> (wPartner[w - N] == -<span class="number">1</span>):</span><br><span class="line">                wPartner[w - N] = m</span><br><span class="line">                mFree[m] = <span class="literal">True</span></span><br><span class="line">                freeCount -= <span class="number">1</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                </span><br><span class="line">                <span class="comment"># If w is not free</span></span><br><span class="line">                <span class="comment"># Find current engagement of w</span></span><br><span class="line">                m1 = wPartner[w - N]</span><br><span class="line"></span><br><span class="line">                <span class="comment"># If w prefers m over her current engagement m1,</span></span><br><span class="line">                <span class="comment"># then break the engagement between w and m1 and</span></span><br><span class="line">                <span class="comment"># engage m with w.</span></span><br><span class="line">                <span class="keyword">if</span> (wPrefersM1OverM(prefer, w, m, m1) == <span class="literal">False</span>):</span><br><span class="line">                    wPartner[w - N] = m</span><br><span class="line">                    mFree[m] = <span class="literal">True</span></span><br><span class="line">                    mFree[m1] = <span class="literal">False</span></span><br><span class="line">            i += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># End of Else</span></span><br><span class="line">        <span class="comment"># End of the for loop that goes</span></span><br><span class="line">        <span class="comment"># to all women in m&#x27;s list</span></span><br><span class="line">    <span class="comment"># End of main while loop</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># Print solution</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Woman &quot;</span>, <span class="string">&quot; Man&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(N):</span><br><span class="line">        <span class="built_in">print</span>(i + N, <span class="string">&quot;\t&quot;</span>, wPartner[i])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Driver Code</span></span><br><span class="line">prefer = [[<span class="number">7</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">4</span>], [<span class="number">5</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">7</span>],</span><br><span class="line">        [<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>], [<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>],</span><br><span class="line">        [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>],</span><br><span class="line">        [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]]</span><br><span class="line"></span><br><span class="line">stableMarriage(prefer)</span><br><span class="line"></span><br><span class="line"><span class="comment"># This code is contributed by Mohit Kumar</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="数学问题的现实应用价值">数学问题的现实应用价值</h2><p>现实中的婚姻问题比这个模型要复杂的多, 不过这个模型的思想可以用来在实际社会中的匹配任务. 比如医学生毕业时的工作分配.</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/04/04/2022-04-04-stable-marriage/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/04/04/2022-04-04-stable-marriage/"/>
    <published>2022-04-03T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>“稳定婚姻问题”在生活中是一个典型的问题,通俗地可叙述为：当前有N位男生和N位女生最后要组成稳定的婚姻家庭，过程开始之前男生和女生在各自的心目中都按照喜爱程度对N位异性有了各自的排序，男生和女生结婚后，对于每一对男生女生，不会出现比起当前匹配的伴侣互相更喜爱的一对男生女生，即可认为婚姻是稳定的。</p>]]>
    </summary>
    <title>稳定婚姻算法</title>
    <updated>2022-04-09T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Computer Vision" scheme="https://zhenxiang-shawn.github.io/tags/Computer-Vision/"/>
    <category term="OpenCV" scheme="https://zhenxiang-shawn.github.io/tags/OpenCV/"/>
    <content>
      <![CDATA[<p>OpenCV 使用 C/C++ 开发，同时也提供了 Python、Java、MATLAB 等其他语言的接口。如果你不了解 C/C++，请阅读《<a href="http://c.biancheng.net/c/">C语言教程</a>》和《<a href="http://c.biancheng.net/cplus/">C++教程</a>》。</p><span id="more"></span><p>OpenCV 是跨平台的，可以在 Windows、Linux、Mac OS、Android、iOS 等操作系统上运行。</p><p>OpenCV 的应用领域非常广泛，包括图像拼接、图像降噪、产品质检、人机交互、人脸识别、动作识别、动作跟踪、无人驾驶等。</p><h2 id="IPPICV-加速">IPPICV 加速</h2><p>如果希望得到更多在英特尔架构上的自动优化，可以购买英特尔的集成性能基元（IPP）库，该库包含了许多算法领域的底层优化程序。在库安装完毕的情况下 OpenCV 在运行的时候会自动调用合适的 IPP 库。</p><p>从 OpenCV 3.0 开始，英特尔许可 OpenCV 研发团队和 OpenCV 社区拥有一个免费的 IPP 库的子库（称 IPPICV），该子库默认集成在 OpenCV 中并在运算时发挥效用。如果你使用的是英特尔的处理器，那么 OpenCV 会自动调用 IPPICV。</p><h2 id="OpenCV的应用">OpenCV的应用</h2><p>自从测试版本在 1999 年 1 月发布以来，<a href="http://c.biancheng.net/opencv/">OpenCV</a> 已经广泛用于许多应用、产品以及科研工作中。这些应用包括在卫星和网络地图上拼接图像，图像扫描校准，医学图像的降噪，目标分析，安保以及工业检测系统，自动驾驶和安全系统，制造感知系统，相机校正，军事应用，无人空中、地面、水下航行器。</p><p>它也被运用于声音和音乐的识别，在这些场景中，视觉识别方法被运用于声音的频谱图像。因为计算机的视觉只是一串串数字, OpenCV就是根据统计学,数学和计算机科学在这串数字上的分析建立起来的.</p><h2 id="OpenCV-的组织关系">OpenCV 的组织关系</h2><p><a href="http://c.biancheng.net/opencv/">OpenCV</a> 是由很多模块组成的，这些模块可以分成很多层：</p><ul><li>最底层是基于硬件加速层（HAL）的各种硬件优化。</li><li>再上一层是 opencv_contrib 模块所包含的 OpenCV 由其他开发人员所贡献的代码，其包含大多数高层级的函数功能。这就是OpenCV的核心。</li><li>接下来是语言绑定和示例应用程序。</li><li>处于最上层的是 OpenCV 和操作系统的交互。</li></ul><p><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/opencv-structure.jpeg" alt="opencv structure"></p><h3 id="OpenCV的模块">OpenCV的模块</h3><p>下表给出了 OpenCV 包含的具体模块，虽然这些模块会随着时间推移而不断的发展，但模块始终是组成这个库的基本单位，每个函数都是一个模块的一部分。</p><table><thead><tr><th>模块</th><th>说明</th></tr></thead><tbody><tr><td>Core</td><td>该模块包含 OpenCV 库的基础结构以及基本操作。</td></tr><tr><td>Improc</td><td>图像处理模块包含基本的图像转换，包括滤波以及类似的卷积操作。</td></tr><tr><td>Highgui</td><td>在 OpenCV 3.0中，分割为 imcodecs、videoio 以及 highgui 三部分。这个模块包含可以用来显示图像或者简单的输入的用户交互函数。这可以看作是一个非常轻量级的 Windows UI 工具包。</td></tr><tr><td>Video</td><td>该模块包含读取和写视频流的函数。</td></tr><tr><td>Calib3d</td><td>这个模块包括校准单个、双目以及多个相机的算法实现。</td></tr><tr><td>Feature2d</td><td>这个模块包含用于检测、描述以及匹配特征点的算法。</td></tr><tr><td>Objectect</td><td>这个模块包含检测特定目标，比如人脸或者行人的算法。也可以训练检测器并用来检测其他物体。</td></tr><tr><td>MI</td><td>机器学习模块本身是一个非常完备的模块，包含大量的机器学习算法实现并且这些算法都能和 OpenCV 的数据类型自然交互。</td></tr><tr><td>Flann</td><td>Flann 的意思是“快速最邻近库”。这个库包含一些你也许不会直接使用的方法，但是其他模块中的函数会调用它在数据集中进行最邻近搜索。</td></tr><tr><td>GPU</td><td>在 OpenCV 中被分割为多个 cuda* 模块。GPU 模块主要是函数在 CUDA GPU 上的优化实现，此外，还有一些仅用于 GPU 的功 能。其中一些函数能够返回很好的结果，但是需要足够好的计算资源，如果硬件没有GPU，则不会有什么提升。</td></tr><tr><td>Photo</td><td>这是一个相当新的模块，包含计算摄影学的一些函数工具。</td></tr><tr><td>Stitching</td><td>本模块是一个精巧的图像拼接流程实现。这是库中的新功能，但是，就像 Photo 模块一样，这个领域未来预计有很大的增长。</td></tr><tr><td>Nonfree</td><td>在 OpenCV 3.0 中，被移到 opencv_contrib/xfeatures2d。OpenCV 包含一些受到专利保护的或者受到使用限制的（比如 SIFT 算法）算法。这些算法被隔离到它们自己的模块中，以表明你需要做一些特殊的工作，才可以在商业产品中使用它们。</td></tr><tr><td>Contrib</td><td>在 OpenCV 3.0 中，融合进了 opencv_contrib。这个模块包含一些新的、还没有被集成进 OpenCV 库的东西。</td></tr><tr><td>Legacy</td><td>在 OpenCV 3.0 中，被取消。</td></tr><tr><td>ocl</td><td>在OpenCV 3.0 中，被取消，取而代之的是 T-API。这是一个较新的模块，可以认为它和 GPU 模块相似，它实现了开放并行编程的 Khronos OpenCL 标准。虽然现在模块的特性比 GPU 模块少很多，但 ocl 模块的目标是提供可以运行在任何 GPU 或者是其他可以搭载 Khronos 的并行设备。这与 GPU 模 块形成了鲜明的对比，后者使用 Nividia CUDA 工具包进行开发，因此只能在 Nividia GPU 设备上工作。</td></tr></tbody></table><h2 id="OpenCV的下载和安装">OpenCV的下载和安装</h2><p>OpenCV的下载和安装请参考<a href="http://c.biancheng.net/view/1104.html">OpenCV下载和安装（包含所有平台）</a></p><h2 id="OpenCV的使用">OpenCV的使用</h2><ul><li><a href="https://github.com/zhenxiang-shawn/opencv-learning/blob/main/01-find_contours.py">Find Contour</a></li><li><a href="https://github.com/zhenxiang-shawn/opencv-learning/blob/main/02-facial_detect.py">Face Detection</a></li></ul>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/03/24/2022-03-24-openCV-01/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/03/24/2022-03-24-openCV-01/"/>
    <published>2022-03-23T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>OpenCV 使用 C/C++ 开发，同时也提供了 Python、Java、MATLAB 等其他语言的接口。如果你不了解 C/C++，请阅读《<a href="http://c.biancheng.net/c/">C语言教程</a>》和《<a href="http://c.biancheng.net/cplus/">C++教程</a>》。</p>]]>
    </summary>
    <title>OpenCV 入门</title>
    <updated>2022-03-24T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Computer Vision" scheme="https://zhenxiang-shawn.github.io/tags/Computer-Vision/"/>
    <category term="Big Data" scheme="https://zhenxiang-shawn.github.io/tags/Big-Data/"/>
    <content>
      <![CDATA[<p>数据都是以二进制的格式存在计算机中。图片作为一种数据，当然也是以二进制的格式存在计算机中。此篇不讲图片数据格式的底层，只是概述一下各个图片格式的特点和优缺点。</p><span id="more"></span><h2 id="常见的图片格式">常见的图片格式</h2><p>一般的，生活中常见、常用的图片格式有png，jpeg，gif，webp。工作中偶尔会用的有BMP，TIF，svg。各个格式有各个格式的优缺点，目前没有一种统一的图片格式可以替代所有。GIF/PNG/JPG/WEBP/APNG等都是属于位图，而SVG是矢量图。</p><div class="tag-plugin colorful note" ><div class="title">这里提一下PDF格式。很多人错误的以为PDF是图片的格式，但是PDF的全称是Portable</div><div class="body"><p>Document Format，意为&quot;可携带文档格式”。是由Adobe Systems用于与应用程序、操作系统、硬件无关的方式进行文件交换所发展出的文件格式。PDF文件以PostScript语言图象模型为基础，使其在各型号打印机上都可再现原稿的字符、颜色以及图象信息。简单的说，PDF除了有图片信息（图片）以外，还有一些肉眼看不到的信息和metadata。</p></div></div><h2 id="各种图片的优缺点">各种图片的优缺点</h2><h3 id="WebP">WebP</h3><p>WebP 的优势体现在它具有更优的图像数据压缩算法，能带来更小的图片体积，而且拥有肉眼识别无差异的图像质量；此外他还具备了无损和有损的压缩模式、Alpha 透明以及动画的特性，在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。WebP支持的像素最大数量是16383x16383。iSparta做了一个PNG图片和WebP图片压缩和不压缩的质量对比和大小对比。<a href="https://isparta.github.io/compare-webp/index.html#12345">点此查看</a>. WebP有静态与动态两种模式。动态WebP（Animated WebP）支持有损与无损压缩、ICC色彩配置、XMP诠释资料、Alpha透明通道。相对于GIF，WebP格式有近乎相同的图片质量和相当小的数据大小。通过官方给出的<a href="https://isparta.github.io/compare-webp/index_a.html#12">例子</a>我们可以明显的看出WebP的优点。缺点是你压缩的时候需要的时间更久了，而且使用WebP的时候CPU的负载更多。</p><h3 id="GIF（Graphics-Interchange-Format）">GIF（Graphics Interchange Format）</h3><p>GIF图形交换格式是一种位图图形文件格式,以8位色（即256种颜色）重现真彩色的图像。它实际上是一种压缩文档,采用LZW压缩算法进行编码,有效地减少了图像文件在网络上传输的时间。它有很多优点，比如：</p><ol><li>优秀的压缩算法使其在一定程度上保证图像质量的同时将体积变得很小。</li><li>可插入多帧,从而实现动画效果。</li><li>可设置透明色以产生对象浮现于背景之上的效果。</li></ol><p>但是由于采用了8位压缩,最多只能处理256种颜色（2的8次方）,故不宜应用于真彩图像。</p><h3 id="PNG（Portable-Network-Graphics）">PNG（Portable Network Graphics）</h3><p>便携式网络图片（Portable Network Graphics）,简称PNG,是一种无损数据压缩位图图形文件格式。PNG格式是无损数据压缩的,PNG格式有8位、24位、32位三种形式,其中8位PNG支持两种不同 的透明形式（索引透明和alpha透明）,24位PNG不支持透明,32位 PNG 在24位基础上增加了8位透明通道（32-24===8）,因此可展现256级透明程度。</p><p>PNG这种类型的图片就是为了取代GIF图片而生的, 除了GIF不支持动画的优势, 能用PNG的地方就用PNG, 原因是压缩比高,色彩好；</p><p>相比较GIF图片，PNG图片的优点更多：</p><ol><li>支持256色调色板技术以产生小体积文件</li><li>最高支持48位真彩色图像以及16位灰度图像。</li><li>支持Alpha通道的半透明特性。</li><li>支持图像亮度的gamma校正信息。</li><li>支持存储附加文本信息,以保留图像名称、作者、版权、创作时间、注释等信息。</li><li>使用无损压缩。</li><li>渐近显示和流式读写,适合在网络传输中快速显示预览效果后再展示全貌。</li><li>使用CRC循环冗余编码防止文件出错。</li><li>最新的PNG标准允许在一个文件内存储多幅图像。</li></ol><h3 id="JPG（Joint-Photographic-Experts-Group）">JPG（Joint Photographic Experts Group）</h3><p>JPEG是一种针对相片影像而广泛使用的一种失真压缩标准方法。JPEG的压缩方式通常是破坏性资料压缩（lossy compression）,意即在压缩过程中图像的品质会遭受到可见的破坏。</p><p>JPEG/JFIF是最普遍在万维网（World Wide Web）上被用来储存和传输照片的格式。JPG压缩算法将不易被人眼察觉的图像颜色删除，从而达到较大的压缩比（可达到2:1甚至40:1），可以说是用最少的磁盘空间得到较好的图像质量。JPEG在色调及颜色平滑变化的相片或是写实绘画（painting）上可以达到它最佳的效果。在这种情况下,它通常比完全无失真方法作得更好,仍然可以产生非常好看的影像（事实上它会比其他一般的方法像是GIF产生更高品质的影像,因为GIF对于线条绘画（drawing）和图示的图形是无失真,但针对全彩影像则需要极困难的量化)。但是，它并不适合于线条绘图（drawing）和其他文字或图示（iconic）的图形,因为它的压缩方法用在这些图形的型态上,会得到不适当的结果；</p><p>更多关于JPEG的详细解释，请参考<a href="https://www.jianshu.com/p/f5557c0e689e">这篇文章</a></p><h3 id="BMP">BMP</h3><p>位图（Bitmap）格式的数据没有经过压缩，或最多只采用行程长度编码（RLE，run-length encoding）来进行轻度的无损数据压缩。以至于，LaTeX 并不能像插入 .jpg 甚至于矢量图那样便捷地插入 BMP 图片。</p><p>BMP文件的数据按照从文件头开始的先后顺序分为四个部分：</p><ul><li><p>位图文件头(bmp file header)：  提供文件的格式、大小等信息</p></li><li><p>位图信息头(bitmap information)：提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息</p></li><li><p>调色板(color palette)：可选，如使用索引来表示图像，调色板就是索引与其对应的颜色的映射表</p></li><li><p>位图数据(bitmap data)：图像数据区</p></li></ul><p>对于压缩方式，虽然 Bitmap 格式提供简单的压缩功能，但是绝大多数情况下，并没有采用任何压缩手段。</p><p>拿最常见的 24BPP RGB （24 比特每像素，红绿蓝三通道） 位图来说，每种颜色需要 8 比特，或者说 1 字节，来存储。在二进制文件中，通常情况下，RGB 按照蓝、绿、红的顺序依次表示图片中的像素点，而 RGBA 则按照蓝、绿、红、透明的顺序（从左下开始，横向逐行向上扫描）。特殊时候，也会出现顺序与上述情况不同的特例，这时色彩顺序会写在 DIB Header 的 Bit Fields 中，以不同色彩通道的 Mask 的形式进行规定。由于 BI_BITFIELDS 也是一种压缩方式，而通常 BMP 不采用任何压缩方式，所以绝大多数时候，我们都是按照前面说的顺序进行排序。</p><p>数据按照像素行进行包装，便于读取。但是这并不是全部，因为其中还可能会有补零（zero-padding）。这涉及到计算机的数据结构对齐（data structure alignment）的问题。如果要把BMP讲透彻的话，细节太多，这里就不一一讲解了。想要了解更多，请参考<a href="https://zhuanlan.zhihu.com/p/25119530">这篇文章</a></p><h3 id="TIF-TIFF">TIF &amp; TIFF</h3><p>TIFF（Tag Image File Format）图像文件是图形图像处理中常用的格式之一，其图像格式很复杂，但由于它对图像信息的存放灵活多变，可以支持很多色彩系统，而且独立于操作系统，因此得到了广泛应用。TIFF文件以.tif为扩展名。其数据格式是一种3级体系结构，Ti内部结构可以分成三个部分，分别是：文件头信息区、标识信息区和图像数据区。其中所有的标签都是以升序排列，这些标签信息是用来处理文件中的图像信息的。在每一个TIFF文件中第一个数据结构称为图像文件头或IFH，它是图像文件体系结构的最高层。这个结构在一个TIFF文件中是惟一的，有固定的位置。它位于文件的开始部分，包含了正确解释TIFF文件的其他部分所需的必要信息。IFD是TIFF文件中第2个数据结构，它是一个名为标记(tag)的用于区分一个或多个可变长度数据块的表，标记中包含了有关于图像的所有信息。IFD提供了一系列的指针(索引)，这些指针告诉我们各种有关的数据字段在文件中的开始位置，并给出每个字段的数据类型及长度。这种方法允许数据字段定位在文件的任何地方，且可以是任意长度，因此文件格式十分灵活。根据IFD所指向的地址，相关的图像信息存储在图像数据区域。</p><h3 id="SVG">SVG</h3><p>SVG（Scalable Vector Graphics）图像使用XML来定义图形。这种方式定义的图像，在放大缩小，或者改变尺寸以后图片质量都不会有损失。此外，SVG图像可以被很多编辑器编辑，甚至你可以用普通的文本编辑器来编辑SVG图像文件。因此，SVG图像中的文本都是可选的，易搜索的，所以很适合来做地图。一般前端网页DOM都用SVG来添加图片。不过因为SVG实际上是文本描述文件，在表示动图的时候需要频繁的改动，因此SVG动画一般性能较低。</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/03/24/2022-05-30-common-images-format/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/03/24/2022-05-30-common-images-format/"/>
    <published>2022-03-23T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>数据都是以二进制的格式存在计算机中。图片作为一种数据，当然也是以二进制的格式存在计算机中。此篇不讲图片数据格式的底层，只是概述一下各个图片格式的特点和优缺点。</p>]]>
    </summary>
    <title>常见的图片格式概览</title>
    <updated>2022-06-11T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="解决方案" scheme="https://zhenxiang-shawn.github.io/categories/%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/"/>
    <category term="Optimization" scheme="https://zhenxiang-shawn.github.io/tags/Optimization/"/>
    <category term="CDN" scheme="https://zhenxiang-shawn.github.io/tags/CDN/"/>
    <content>
      <![CDATA[<p>在使用博客搭建的时候,一般来说我的图片和博客都是开源放在github上的. 但是如果用相对路径来读取图片的话,图片的加载速度收到github流量速度限制,会变得很慢,而且有一些地区也对GitHub加了一道防火墙,这使GitHub Pages加载速度更慢了.目前有两种方式可以缓解图片加载速度.</p><span id="more"></span><h2 id="Github-Raw-文件读取">Github Raw 文件读取</h2><p>使用Github Raw来加载文件通常比相对路径配置的更快些, 可通过以下几种URL来加载raw文件：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">https://raw.githubusercontent.com/user/repo/branch/README.md</span><br><span class="line">https://github.com/user/repo/raw/branch/README.md</span><br><span class="line">https://github.com/user/repo/blob/branch/README.md?raw=true</span><br></pre></td></tr></table></figure><p>对于不支持 Raw 查看的文件，在浏览器中输入或点击上述查看 raw 文件的 URL 时，会对文件进行下载。因此可以通过这个特性，在 GitHub 上直接提供文件的下载功能。支持 Raw 查看的文件，可在浏览器打开 raw 页面，直接 <code>另存为…</code> 保存即可</p><h2 id="内容分发网络-Content-Delivery-Network-CDN">内容分发网络(Content Delivery Network (CDN))</h2><p>关于CDN, 百度百科给出的解释是:</p><blockquote><p>CDN是构建在现有网络基础之上的智能虚拟网络，依靠部署在各地的边缘服务器，通过中心平台的负载均衡、内容分发、调度等功能模块，使用户就近获取所需内容，降低网络拥塞，提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。</p></blockquote><p>CDN的基本原理是广泛采用各种缓存服务器，将这些缓存服务器分布到用户访问相对集中的地区或网络中，在用户访问网站时，利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上，由缓存服务器直接响应用户请求.</p><p>CDN的基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节，使内容传输的更快、更稳定。通过在网络各处放置<a href="https://baike.baidu.com/item/%E8%8A%82%E7%82%B9%E6%9C%8D%E5%8A%A1%E5%99%A8/4576219">节点服务器</a>所构成的在现有的互联网基础之上的一层智能<a href="https://baike.baidu.com/item/%E8%99%9A%E6%8B%9F%E7%BD%91%E7%BB%9C/855117">虚拟网络</a>，CDN系统能够实时地根据<a href="https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E6%B5%81%E9%87%8F/7489548">网络流量</a>和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容，解决 Internet网络拥挤的状况，提高用户访问网站的响应速度。根据我的理解,简单的说就是<strong>用更近的互联网数据中心(Internet Data Center,IDC)缓存内容,让客户端有更快的速度来访问此项资源</strong>.</p><h2 id="如何搭建CDN服务">如何搭建CDN服务</h2><h3 id="CDN服务-jsDelivr">CDN服务: <a href="https://www.jsdelivr.com/">jsDelivr</a></h3><p>jsDelivr 是一个免费的开源CDN. 这是在中国大陆唯一有 license 的公有 CDN，而且实际使用中的访问速度也是极快的. 它可以引用的资源包括NPM、github、wordpress的所有资源，github可以是任意体积小于50M的仓库。</p><p>以github为例，只需要通过符合 JSDelivr 规则的 URL 引用，即可直接使用 Github 中的资源:</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">// load any GitHub release, commit, or branch</span><br><span class="line">// note: we recommend using npm for projects that support it</span><br><span class="line">https://cdn.jsdelivr.net/gh/user/repo@version/file</span><br><span class="line"></span><br><span class="line">// load jQuery v3.2.1</span><br><span class="line">https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/dist/jquery.min.js</span><br><span class="line"></span><br><span class="line">// use a version range instead of a specific version</span><br><span class="line">https://cdn.jsdelivr.net/gh/jquery/jquery@3.2/dist/jquery.min.js</span><br><span class="line">https://cdn.jsdelivr.net/gh/jquery/jquery@3/dist/jquery.min.js</span><br><span class="line"></span><br><span class="line">// omit the version completely to get the latest one</span><br><span class="line">// you should NOT use this in production</span><br><span class="line">https://cdn.jsdelivr.net/gh/jquery/jquery/dist/jquery.min.js</span><br><span class="line"></span><br><span class="line">// add &quot;.min&quot; to any JS/CSS file to get a minified version</span><br><span class="line">// if one doesn&#x27;t exist, we&#x27;ll generate it for you</span><br><span class="line">https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/src/core.min.js</span><br><span class="line"></span><br><span class="line">// add / at the end to get a directory listing</span><br><span class="line">https://cdn.jsdelivr.net/gh/jquery/jquery/</span><br></pre></td></tr></table></figure><p>详细配置,请参考<a href="https://www.jsdelivr.com/">jsDelivr</a></p><p>这是使用github raw加载和使用CDN来加载图片的对比:</p><div class="tag-plugin tabs" align="center"id="tab_1"><div class="nav-tabs"><div class="tab"><a href="#tab_1-1">GitHub Raw</a></div><div class="tab active"><a href="#tab_1-2">jsDelivr(CDN)</a></div></div><div class="tab-content"><div class="tab-pane" id="tab_1-1"><div class="tag-plugin image"><div class="image-bg"><img src="https://github.com/zhenxiang-shawn/zhenxiang-shawn.github.io/blob/main/source/_imgs/python_logo.png?raw=true"/></div></div></div><div class="tab-pane active" id="tab_1-2"><div class="tag-plugin image"><div class="image-bg"><img src="https://cdn.jsdelivr.net/gh/zhenxiang-shawn/zhenxiang-shawn.github.io@main/source/_imgs/python_logo.png"/></div></div></div></div></div><h2 id="其他免费并且不限速的CDN推荐">其他免费并且不限速的CDN推荐</h2><h3 id="staticaly"><a href="https://statically.io/">staticaly</a></h3><p>它可以轻松地从GitHub / GitLab / Bitbucket等加载您的项目 没有流量限制或限制。</p><p>文件通过超快速全球CDN提供。 在URL（不是分支）中使用特定标记或提交哈希。<br>根据URL永久缓存文件。 除master分支外，文件在浏览器中缓存1年</p>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/03/23/2022-03-23-optimize-img-load/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/03/23/2022-03-23-optimize-img-load/"/>
    <published>2022-03-22T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>在使用博客搭建的时候,一般来说我的图片和博客都是开源放在github上的. 但是如果用相对路径来读取图片的话,图片的加载速度收到github流量速度限制,会变得很慢,而且有一些地区也对GitHub加了一道防火墙,这使GitHub Pages加载速度更慢了.目前有两种方式可以缓解图片加载速度.</p>]]>
    </summary>
    <title>如何在Server上优化博客上图片的加载速度</title>
    <updated>2022-03-23T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="技术加油站" scheme="https://zhenxiang-shawn.github.io/categories/%E6%8A%80%E6%9C%AF%E5%8A%A0%E6%B2%B9%E7%AB%99/"/>
    <category term="Computer Vision" scheme="https://zhenxiang-shawn.github.io/tags/Computer-Vision/"/>
    <category term="Python" scheme="https://zhenxiang-shawn.github.io/tags/Python/"/>
    <category term="Data Clean" scheme="https://zhenxiang-shawn.github.io/tags/Data-Clean/"/>
    <content>
      <![CDATA[<h2 id="为什么要做模糊检测">为什么要做模糊检测 ?</h2><p>在机器学习准备数据的时候,有些数据并不尽如人意. 比如在计算机视觉训练时,图片数据集中有一些收集的图片清晰度不够,这会影响到后续的训练和模型的准确度. 准备图片数据的时候一般会根据图片质量选择是否先给图片降噪,然后把较为模糊的图片给去掉. 除此以外还可以用来做自动图像分级, 在视频流做OCR的时候可以过滤掉一些质量不好的图片来节省算力. 本文主要为讨论了模糊图片的检测的两种方法.</p><span id="more"></span><h2 id="方法1-使用OpenCV和拉普拉斯算子计算">方法1: 使用OpenCV和拉普拉斯算子计算</h2><p>一般来说，如果要检测图片的模糊程度，首先要考虑的方法是计算图像的快速傅里叶变换，然后检查低频和高频的分布：如果图像只有有少量的高频，那么图像就会被认为是模糊的．然而，定义什么算低数量的高频或者什么是高数量的高频是相当困难的。</p><p>根据<a href="https://www.semanticscholar.org/paper/Analysis-of-focus-measure-operators-for-Pertuz-Puig/8c675bf5b542b98bf81dcf70bd869ab52ab8aae9?p2df">Pertuz 的研究</a>, 仅使用基本的灰度像素强度统计, 评估图像的局部二值模式就可以计算一个单一的浮点值来表示一个给定图像的模糊程度. 一般的,我们使用<strong>Tenengrad梯度方法</strong>或者<strong>Laplacian梯度方法</strong>. 这两个不同算法在模糊检测时的结果几近相同,而使用Laplacian算子会减少运算量(Tenengrad梯度方法会计算x,y两个方向的梯度, 而Laplacian梯度方法只用计算Laplacian算子矩阵和灰度图的卷积), 所以我们选择Laplacian算子来检测图片的模糊程度.</p><p>下边是使用opencv检测图片的代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">detect_pic_blur</span>(<span class="params">img_path</span>) -&gt; <span class="built_in">bool</span>:</span><br><span class="line">    <span class="comment"># read picture</span></span><br><span class="line">    img = cv2.imread(img_path)</span><br><span class="line">    <span class="comment"># remove color</span></span><br><span class="line">    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) </span><br><span class="line">    <span class="comment"># calculate the Laplacian value of the img</span></span><br><span class="line">    fm = cv.Laplacian(gray, cv2.CV_64F).var() </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&#x27;图片的拉普拉斯值为:<span class="subst">&#123;fm&#125;</span>&#x27;</span>)</span><br><span class="line">    threshold = <span class="number">120</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">False</span> <span class="keyword">if</span> fm &lt;= threshold <span class="keyword">else</span> <span class="literal">True</span></span><br></pre></td></tr></table></figure><p>使用此方法的话,一般需要对不同的数据集采用不同的threshold.</p><h2 id="方法2-使用OpenCV-和-Fast-Fourier-Transform-FFT-检测模糊图片">方法2: 使用OpenCV 和 Fast Fourier Transform (FFT)检测模糊图片</h2><p>当数据集多的时候,拉普拉斯方法需要大量的手动调整来定义图像是否模糊的“阈值”。如果您可以控制您的照明条件、环境和图像捕获过程，那么它的效果会非常好. 但是如果实验数据收集的时候变量太大(不同拍照设备,不同天气)的话,这样拉普拉斯算子算法就不是一个很好的选择.<br>这个方法的思路是使用快速傅里叶变换来计算图片的模糊值. 在计算机视觉方面，我们经常将 FFT 视为一种图像处理工具，可以在两个域中表示图像：</p><ul><li>傅里叶域</li><li>空间域</li></ul><p>因此，FFT 用实部和虚部表示图像。通过分析这些值，我们可以执行图像处理例程，例如模糊、边缘检测、阈值处理、纹理分析，甚至是模糊检测。更多关于傅里叶变换的内容请参考<a href="https://homepages-inf-ed-ac-uk.translate.goog/rbf/HIPR2/fourier.htm?_x_tr_sl=auto&amp;_x_tr_tl=zh-CN&amp;_x_tr_hl=zh-CN&amp;_x_tr_pto=op">FFT and it’s relation to image processing</a></p><p>下边是使用opencv检测图片的代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">detect_img_blur_fft</span>(<span class="params">img_path, size=<span class="number">60</span>, threshold=<span class="number">10</span>, vis=<span class="literal">False</span></span>):</span><br><span class="line">    img = cv2.imread(img_path)</span><br><span class="line">    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)</span><br><span class="line">    (height, width) = img_gray.shape</span><br><span class="line">    <span class="comment"># compute center coordinates</span></span><br><span class="line">    (cX, cY) = (<span class="built_in">int</span>(width / <span class="number">2.0</span>), <span class="built_in">int</span>(height / <span class="number">2.0</span>))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># compute FFT </span></span><br><span class="line">    fft = np.fft.fft2(img_gray)</span><br><span class="line">    fft_shift = np.fft.fftshift(fft)</span><br><span class="line">    <span class="comment"># Zero-out the center of our FFT shift (i.e., to remove low frequencies) </span></span><br><span class="line">    fft_shift[cY - size:cY + size, cX - size:cX + size] = <span class="number">0</span></span><br><span class="line">    <span class="comment"># Apply the inverse shift to put the DC component back in the top-left</span></span><br><span class="line">    fftShift = np.fft.ifftshift(fft_shift)</span><br><span class="line">    <span class="comment"># apply inverse FFT</span></span><br><span class="line">    recon = np.fft.ifft2(fftShift)</span><br><span class="line">    magnitude = <span class="number">20</span> * np.log(np.<span class="built_in">abs</span>(recon))</span><br><span class="line">    mean = np.mean(magnitude)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&#x27;Mean: <span class="subst">&#123;mean&#125;</span>&#x27;</span>)</span><br><span class="line">    <span class="comment"># the image will be considered &quot;blurry&quot; if the mean value of the</span></span><br><span class="line">    <span class="comment"># magnitudes is less than the threshold value</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">False</span> <span class="keyword">if</span> mean &lt;=threshold <span class="keyword">else</span> <span class="literal">True</span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2022/03/22/2022-03-22-blur-detection/</id>
    <link href="https://zhenxiang-shawn.github.io/2022/03/22/2022-03-22-blur-detection/"/>
    <published>2022-03-21T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2 id="为什么要做模糊检测">为什么要做模糊检测 ?</h2>
<p>在机器学习准备数据的时候,有些数据并不尽如人意. 比如在计算机视觉训练时,图片数据集中有一些收集的图片清晰度不够,这会影响到后续的训练和模型的准确度. 准备图片数据的时候一般会根据图片质量选择是否先给图片降噪,然后把较为模糊的图片给去掉. 除此以外还可以用来做自动图像分级, 在视频流做OCR的时候可以过滤掉一些质量不好的图片来节省算力. 本文主要为讨论了模糊图片的检测的两种方法.</p>]]>
    </summary>
    <title>检测模糊图片的几种方法</title>
    <updated>2022-03-21T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Zhenxiang Jin</name>
      <email>zhenxiang.shawn@zohomail.com</email>
    </author>
    <category term="解决方案" scheme="https://zhenxiang-shawn.github.io/categories/%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/"/>
    <category term="Git" scheme="https://zhenxiang-shawn.github.io/tags/Git/"/>
    <content>
      <![CDATA[<h1>Git <code>.ignore</code> 编辑后文件没有变化</h1><p>如果在文件编辑、修改之后再更新的<code>.gitignore</code>, 那么这个时候Git并不会作用于现在当前文件夹里的文件。因为这个时候Git已经认为这些文件已经是这个Repository的一部分了。这个时候只需要用以下命令来清理缓存然后再重新添加文件就好。</p><span id="more"></span><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">rm</span> -rf --cached .   <span class="comment"># remove cache</span></span><br><span class="line">git add .               <span class="comment"># add contents to the index</span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhenxiang-shawn.github.io/2019/01/03/2019-01-03-git-refresh-gitignore/</id>
    <link href="https://zhenxiang-shawn.github.io/2019/01/03/2019-01-03-git-refresh-gitignore/"/>
    <published>2019-01-02T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h1>Git <code>.ignore</code> 编辑后文件没有变化</h1>
<p>如果在文件编辑、修改之后再更新的<code>.gitignore</code>, 那么这个时候Git并不会作用于现在当前文件夹里的文件。因为这个时候Git已经认为这些文件已经是这个Repository的一部分了。这个时候只需要用以下命令来清理缓存然后再重新添加文件就好。</p>]]>
    </summary>
    <title>Git `.gitignore` 更新后没有作用</title>
    <updated>2022-04-23T16:00:00.000Z</updated>
  </entry>
</feed>
