<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://narongdej.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://narongdej.dev/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2024-11-13T22:23:06+07:00</updated><id>https://narongdej.dev/feed.xml</id><title type="html">Narongdej Sarnsuwan - Personal Website</title><subtitle>A personal website of Narongdej Sarnsuwan documenting what he learned throughout his professional journey.</subtitle><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><entry><title type="html">วิธีแก้ TypeError: Failed to fetch ใน Line LIFF บน Android</title><link href="https://narongdej.dev/blog/th/2024/04/04/%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B9%81%E0%B8%81%E0%B9%89-type-error-failed-to-fetch-%E0%B9%83%E0%B8%99-line-liff-android" rel="alternate" type="text/html" title="วิธีแก้ TypeError: Failed to fetch ใน Line LIFF บน Android" /><published>2024-04-04T00:00:00+07:00</published><updated>2024-04-04T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2024/04/04/%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B9%81%E0%B8%81%E0%B9%89-type-error-failed-to-fetch-%E0%B9%83%E0%B8%99-line-liff-android</id><content type="html" xml:base="https://narongdej.dev/blog/th/2024/04/04/%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B9%81%E0%B8%81%E0%B9%89-type-error-failed-to-fetch-%E0%B9%83%E0%B8%99-line-liff-android"><![CDATA[<p>ผมมีแอพพลิเคชั่นตัวนึงที่พัฒนาโดยใช้ <a href="https://www.npmjs.com/package/@line/liff">Line Liff SDK</a> เพื่อทำการ Auth user กับ Line และใช้ function อื่นๆของ Liff</p>

<p>แต่อยู่ดีๆก็มี user จำนวนนึงที่ใช้ Android แจ้งว่าไม่สามารถเข้าใช้งานได้ และเมื่อตรวจสอบก็เจอว่ามี error throw มาว่า <code class="language-plaintext highlighter-rouge">TypeError: Failed to fetch</code> จาก function liff.init()</p>

<p>ที่แปลกก็คือถ้า user ใช้งานบนเครื่อง Android เดียวกัน แต่ใช้จาก browser ข้างนอกแอพไลน์ ก็จะสามารถเข้าใช้งานได้ปกติ และถ้า user ทำการ clear cache ในไลน์ก็จะสามารถเข้าใช้งานผ่าน browser ในไลน์ได้ช่วงเวลานึง (ผ่านลิงค์ Liff)</p>

<p>และเมื่อไปตรวจสอบเพิ่มเติมก็พบว่า error นั้นเกิดจากการที่ liff.init() พยายามจะไป fetch json ลิงค์นี้ https://liffsdk.line-scdn.net/xlt/manifest.json แต่ว่าไม่สามารถทำได้</p>

<p>ที่ผมเข้าใจคือลิงค์นี้เป็นไฟล์ที่บอกว่าควรจะดึง translation json จากที่ไหน ซึ่งถ้าสมมุติคุณใช้ภาษาไทย มันก็จะไปโหลดไฟล์ translation ตัวนี้ต่อ https://liffsdk.line-scdn.net/xlt/messages_th.7afa9cb30405f1251edecc41476e5f74.json</p>

<p><strong>แต่เมื่อมันไม่สามารถโหลดได้ ก็จะทำให้เว็บแอพพลิเคชั่นพังไปเลย</strong></p>

<p>ผมเดาว่า เกิดจากการจัดการ caching แปลกๆที่อยู่ใน WebView ที่ฝังอยู่ในแอพ Line บน Android และ WebView นั้นไม่สามารถไปโหลดไฟล์จาก cache ได้เมื่อเวลาผ่านไปสักพัก <em>ย้ำว่าเดาล้วนๆ อาจจะไม่ถูกประเด็นเลย เพราะไม่ได้เอา Android มา debug ด้วยซ้ำ</em></p>

<p>หนึ่งในวิธีแก้ที่ง่ายที่สุดวิธีนึง ที่ผมได้มาจากคุณ <a href="https://www.facebook.com/groups/LINEDEVTH/posts/1211364652860772/">Somsak</a> จากใน <a href="https://www.facebook.com/groups/119701752027073">Line Developer FB Group</a> ก็คือให้มันไม่โดน cache ไปเลย โดยเติม random query string ต่อท้าย url json 2 อันนั้น 😂</p>

<p>ในเมื่อ Liff package ใช้ fetch API ของ browser ในการโหลดไฟล์ json สองอันนั้น เราก็ override fetch ใน browser มันซะเลย ว่าถ้าเจอ url ที่เป็น json และ url เป็นอันที่เราสนใจ ก็ให้เติม random number ต่อท้ายไป</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">originalFetch</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">fetch</span>
<span class="kd">function</span> <span class="nf">customFetch</span><span class="p">(</span><span class="nx">url</span><span class="p">:</span> <span class="nx">RequestInfo</span> <span class="o">|</span> <span class="nx">URL</span><span class="p">,</span> <span class="nx">options</span><span class="p">?:</span> <span class="nx">RequestInit</span><span class="p">):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">Response</span><span class="o">&gt;</span> <span class="p">{</span>
<span class="k">if </span><span class="p">(</span><span class="nx">url</span><span class="p">.</span><span class="nf">toString</span><span class="p">().</span><span class="nf">startsWith</span><span class="p">(</span><span class="dl">'</span><span class="s1">https://liffsdk.line-scdn.net/xlt/</span><span class="dl">'</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nx">url</span><span class="p">.</span><span class="nf">toString</span><span class="p">().</span><span class="nf">endsWith</span><span class="p">(</span><span class="dl">'</span><span class="s1">.json</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
    <span class="nx">url</span> <span class="o">=</span> <span class="nx">url</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">?ts=</span><span class="dl">'</span> <span class="o">+</span> <span class="nb">Math</span><span class="p">.</span><span class="nf">random</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nf">originalFetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span>
<span class="p">}</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">fetch</span> <span class="o">=</span> <span class="nx">customFetch</span>
</code></pre></div></div>
<p>เอาโค้ดด้านบนไปแปะสักตรงนึงของแอพ ก่อนที่จะ call liff.init() แค่นี้แอพพลิเคชั่นของคุณก็จะกลับมาใช้งานบน Android ได้แล้ว</p>

<p>เหมือนว่าปัญหานี้เกิดขึ้นมาสักพักแล้ว ดูจากโพสคุณ Somsak ก็ตั้งแต่เดือน 6 ปี 2023 และเมื่อไม่กี่วันก่อนก็มีคนโพสในกลุ่มเดียวกันว่าเจอปัญหาแบบนี้ คงต้องรอให้ไลน์อัพเดทแอพพลิเคชั่นใน Android ให้แก้ปัญหานี้หรือว่าแก้ตัว CDN เราถึงจะเอาโค้ดด้านบนออกได้จากแอพพลิเคชั่นของเราได้</p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="programming" /><category term="javascript" /><category term="android" /><summary type="html"><![CDATA[ผมมีแอพพลิเคชั่นตัวนึงที่พัฒนาโดยใช้ Line Liff SDK เพื่อทำการ Auth user กับ Line และใช้ function อื่นๆของ Liff]]></summary></entry><entry><title type="html">Using Statement ใน C# กับใน Unity คืออะไร</title><link href="https://narongdej.dev/blog/th/2021/04/20/using-statement-%E0%B9%83%E0%B8%99-csharp-%E0%B8%81%E0%B8%B1%E0%B8%9A%E0%B9%83%E0%B8%99-unity-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3" rel="alternate" type="text/html" title="Using Statement ใน C# กับใน Unity คืออะไร" /><published>2021-04-20T00:00:00+07:00</published><updated>2021-04-20T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/04/20/using-statement-%E0%B9%83%E0%B8%99-csharp-%E0%B8%81%E0%B8%B1%E0%B8%9A%E0%B9%83%E0%B8%99-unity-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/04/20/using-statement-%E0%B9%83%E0%B8%99-csharp-%E0%B8%81%E0%B8%B1%E0%B8%9A%E0%B9%83%E0%B8%99-unity-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3"><![CDATA[<p>เวลาเราเขียนเกมส์ใน Unity  หลายๆครั้งเมื่อเราไม่ต้องการใช้ Textures หรือ GameObject บางตัวแล้ว เราต้องทำการลบมันออกจาก memory ด้วยการเรียน Dispose()</p>

<p>ซึ่งถ้าเราลืมก็อาจจะเกิด Memory overflow ได้ ถึงแม้ใน C# จะมี garbage collector หรือตัวกำจัด resource ที่ไม่ใช้แล้วก็ตาม แต่เราก็ไม่รู้ว่ามันจะกำจัดให้เราเมื่อไหร่ ถ้าเราดันสร้าง object มาเร็วมากๆแล้วไม่ได้ลบมันทิ้ง mem เกินที่จะใช้ได้ เกมส์เราก็ crash</p>

<p>using statement จะมาช่วยคุณได้ เผื่อกันคุณลืมเรียก Dispose() เมื่อเลิกใช้</p>

<p>ตัวอย่างการใช้งานก็ให้ครอบสิ่งที่เราต้องการ Dispose เมื่อ statement จบไว้ตามนี้</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">reader</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">StringReader</span><span class="p">(</span><span class="n">manyLines</span><span class="p">))</span>
<span class="p">{</span>
    <span class="kt">string</span><span class="p">?</span> <span class="n">item</span><span class="p">;</span>
    <span class="k">do</span> <span class="p">{</span>
        <span class="n">item</span> <span class="p">=</span> <span class="n">reader</span><span class="p">.</span><span class="nf">ReadLine</span><span class="p">();</span>
        <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">while</span><span class="p">(</span><span class="n">item</span> <span class="p">!=</span> <span class="k">null</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Object ที่ implement <a href="https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=net-5.0">IDisposable</a> หรือ <a href="https://docs.microsoft.com/en-us/dotnet/api/system.iasyncdisposable?view=net-5.0">IAsyncDisposable</a> จะสามารถใช้กับ keyword using ได้ครับ เพราะฉะนั้นคุณสามารถสร้าง object ที่สามารถใช้ using keyword เองด้วยก็ได้นะ</p>

<p>ระวังว่า object ที่อยู่ใน using เป็น read-only ไม่สามารถแก้ไขหรือ reassign ได้ และ using ก็จะเรียก Dispose ให้เราด้วยเมื่อเกิด exception ใน using block</p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="programming" /><category term="csharp" /><summary type="html"><![CDATA[เวลาเราเขียนเกมส์ใน Unity หลายๆครั้งเมื่อเราไม่ต้องการใช้ Textures หรือ GameObject บางตัวแล้ว เราต้องทำการลบมันออกจาก memory ด้วยการเรียน Dispose()]]></summary></entry><entry><title type="html">อะไรคือ coroutine</title><link href="https://narongdej.dev/blog/th/2021/04/12/%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-coroutine" rel="alternate" type="text/html" title="อะไรคือ coroutine" /><published>2021-04-12T00:00:00+07:00</published><updated>2021-04-12T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/04/12/%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-coroutine</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/04/12/%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-coroutine"><![CDATA[<p>ปกติแล้วเวลาเราเขียน function สัก function นึง มันจะรันตั้งแต่จนจบถึงจะสามารถรัน function ต่อๆไปได้เช่น</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">FirstFunction</span><span class="p">()</span> <span class="c1">// &lt;-- อันนี้ต้องรันจนเสร็จก่อนถึงจะไป SecondFunction ได้</span>
<span class="nc">SecondFunction</span><span class="p">()</span>
</code></pre></div></div>

<p>แต่ coroutine ทำให้แต่ละ function สามารถ cooperate (ทำงานร่วมกันได้) โดยที่แต่ละ function ยังจำได้ว่าครั้งล่าสุดอยู่ตรงไหนละสามารถทำงานต่อได้โดยไม่ต้องเริ่มใหม่</p>

<p>เช่นตัวอย่างนี้จาก <a href="https://en.wikipedia.org/wiki/Coroutine#:~:text=Coroutines%20are%20computer%20program%20components,to%20be%20suspended%20and%20resumed.">Wikipedia</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var q := new queue

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield to consume

coroutine consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield to produce

call produce
</code></pre></div></div>

<p>สมมุติว่า queue สามารถรับได้ 10 items มากสุดแต่เรามี item ที่ต้อง create 20 อัน
flow การทำงานจะเริ่มจาก</p>
<ol>
  <li>produce ทำการ loop สร้าง item 10 อันใน queue (item 1-10)</li>
  <li>พอครบก็ yield ไปให้ consume เอา queue ออกจนเหลือ 0</li>
  <li>product ทำการ loop ต่ออีก 10 item (item 11-20)</li>
  <li>yield การทำงานไปให้ consume เพื่อ dequeue ออกทั้งหมด</li>
</ol>

<p>จะเห็นได้ว่าแต่ละ function จะสามารถจำ state ของตัวเองได้เมื่อถูก yield และเมื่อถูก call อีกครั้งก็จะสามารถกลับไปรันต่อตรงจุดเดิม</p>

<p><strong>ระวังเข้าใจผิดว่า coroutine คือการทำแบบ parallel นะครับ!</strong> ตามที่อธิบายด้านบน coroutine ทำแค่ปล่อย (yield) สิ่งที่ตัวเองรันอยู่ให้ function อื่นรันก่อนแล้วจำสิ่งที่ตัวเองทำอยู่ไว้ เมื่อคนอื่นมา call อีกครั้งถึงจะรันต่อจากจุดเดิม</p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="programming" /><summary type="html"><![CDATA[ทำความเข้าใจ coroutine แบบง่ายๆสั้นๆ]]></summary></entry><entry><title type="html">p-value คืออะไร</title><link href="https://narongdej.dev/blog/th/2021/04/03/p-value-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3" rel="alternate" type="text/html" title="p-value คืออะไร" /><published>2021-04-03T00:00:00+07:00</published><updated>2021-04-03T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/04/03/p-value-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/04/03/p-value-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3"><![CDATA[<p>หลายคนชอบคิดว่า p-value คือ probability แต่จริงๆแล้วไม่ใช่</p>

<p>p-value คือตัวเลขที่บอกว่าค่าที่คุณได้ออกมานั้นมีโอกาสเกิดจากดวง หรือเป็นค่าที่ยากที่จะเกิด เช่น ถ้าคุณได้ค่า p-value ที่ 0.0125 เอามาแปลงเป็น % จะหมายความว่า ค่าที่คุณได้มา มีโอกาส 1.25% ที่จะสุ่มมาโดนซึ่งน้อยมาก แต่ถ้าคุณได้ p-value 0.9 แปลว่ามีโอกาส 90% ที่จะสุ่มมาโดน</p>

<p>เพราะฉะนั้นยิ่งค่า p-value น้อยยิ่งแปลว่าค่านั้นมีความสำคัญ และยากที่จะเกิด</p>

<p>p-value เอาไว้ใช้บ่อยเพื่อช่วย support หรือ reject hypothesis ถ้าค่า p-value น้อยกว่าค่าที่เราเช็ตไว้เช่น 5% (0.05 p-value เรียกค่านี้ว่า alpha level หรือเป็นค่า confidence ใน hypothesis ของเรา) ให้เรา reject hypothesis แต่ถ้ามากกว่าให้ support</p>

<h2 id="แล้วเลข-p-value-มากจากไหนละ">แล้วเลข p-value มากจากไหนละ</h2>
<p>สมมุติคุณต้องการรู้ว่า p-value ของการโยนเหรียญ สองเหรียญให้ได้หัวทั้งคู่
probability ของการโยนเหรียญสองเหรียญจะออกมาแบบนี้</p>

<p><img src="/assets/imgs/p-value/coin-p-probability@2x.png" alt="" /></p>

<p>เลข p-value เกิดจากสามส่วน</p>
<ol>
  <li>probability ของที่เราต้องการ observe</li>
  <li>probability ของการสิ่งที่เกิดยากเท่ากับที่เรา observe</li>
  <li>probability ของการสิ่งที่เกิดที่ยากกว่าที่เรา observe</li>
</ol>

<p>โดยเราจะได้ค่า p-value ของการโยนเหรียญนี้ดังนี้</p>
<ol>
  <li>0.25 (เกิดจาก probability ของ HH)</li>
  <li>0.25 (เกิดจาก probability ของ TT)</li>
  <li>0 (เพราะไม่มีอะไรต่ำกว่า 0.25)
รวมกันเป็น p-value 0.5</li>
</ol>

<p>สมมุติว่าเราพยายามจะพิสูจน์ว่า แม้เราจะโยนเหรียญได้หัว 2 อัน เหรียญเรายังเป็นเหรียญปกตินะ โดยมีค่า alpha level ที่ 0.05</p>

<p>เราได้ค่า p-value 0.5 ซึ่งสูงกว่า 0.05 แปลว่าเหรียญเราปกติ</p>

<h2 id="p-value-ใน-distribution">p-value ใน distribution</h2>
<p>การคำนวน probability ใน distribution ก็ยังคงใช้หลักการเดียวกันกับข้างบน สมมุติใน normal distribution ด้านล่าง</p>

<p><img src="/assets/imgs/p-value/normal-distribution@2x.png" alt="" /></p>

<p>เป็นการ distribution ที่บอกค่าความสูงของคนไทย (ค่ามั่วนะ) ถ้าเราต้องการรู้ว่า Probability ที่คนไทยจะสูง 140 - 180cm มีโอกาสเท่าไหร่ ให้หา area ใต้เส้น หรือตรงที่ระบายสีแดง เช่นในที่วาด เรา assume ว่า area คำนวนออกได้มาได้ 95% ของ area ทั้งหมด ก็ถือว่ามีโอกาส 95%</p>

<p>ที่นี้เราก็จะสามารถคำนวน p-value ได้แล้ว โดยใช้วิธีได้บน เช่นเราต้องการ p-value ของการวัดค่าความสูงที่ 182cm ก็ให้คำนวน probability 182cm (ส่วนที่ 1) และ 2. probability 138 (ส่วนที่ 2) และ probability ของค่าที่มากกว่า 182 และน้อยกว่า 138 (ส่วนที่ 3)</p>

<p>P.S ไว้เดียวบทความต่อๆไปหาโปรแกรมวาด graph ดีๆให้นะครับ 5555</p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="math" /><summary type="html"><![CDATA[ทำความเข้าใจ p-value และการคำนวนหาค่า p-value]]></summary></entry><entry><title type="html">สร้าง UML diagrams สำหรับ graph และ module ใน Python ด้วย Pyreverse</title><link href="https://narongdej.dev/blog/th/2021/03/22/%E0%B8%AA%E0%B8%A3%E0%B9%89%E0%B8%B2%E0%B8%87-uml-diagrams-%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A-graph-%E0%B9%81%E0%B8%A5%E0%B8%B0-module-%E0%B9%83%E0%B8%99-python-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-pyreverse" rel="alternate" type="text/html" title="สร้าง UML diagrams สำหรับ graph และ module ใน Python ด้วย Pyreverse" /><published>2021-03-22T00:00:00+07:00</published><updated>2021-03-22T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/03/22/%E0%B8%AA%E0%B8%A3%E0%B9%89%E0%B8%B2%E0%B8%87-uml-diagrams-%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A-graph-%E0%B9%81%E0%B8%A5%E0%B8%B0-module-%E0%B9%83%E0%B8%99-python-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-pyreverse</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/03/22/%E0%B8%AA%E0%B8%A3%E0%B9%89%E0%B8%B2%E0%B8%87-uml-diagrams-%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A-graph-%E0%B9%81%E0%B8%A5%E0%B8%B0-module-%E0%B9%83%E0%B8%99-python-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-pyreverse"><![CDATA[<p>มีโปรแกรม Python ตัวนึงที่อยากแนะนำให้ใช้กันในการวาด UML diagram ว่า class และ module ของเราเชื่อมกันยังไง</p>

<p>โปรแกรมตัวนั้นก็คือ Pyreverse ซึ่งอยู่ใน package ของ pylint ครับ วิธีการลงก็คือให้ลง Pylint ก่อน</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>pylint
</code></pre></div></div>

<p>หลังจากเราลง pylint เสร็จเรียบร้อยแล้ว ก็สามารถใช้ Pyreverse ได้เลยครับ</p>

<p>วิธีใช้งาน ให้เข้าไปที่ folder ที่เราต้องการรันตัว Pyreverse และใช้ command ตามนี้</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pyreverse <span class="o">[</span>options] &lt;packages&gt;
</code></pre></div></div>

<p>Example</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pyreverse <span class="nt">-o</span> png package_1 package_2 file_name.py
</code></pre></div></div>

<p>เราก็จะได้รูปภาพออกมาประมาณนี้ครับ สำหรับ classes.png และถ้าเราใส่ package หลายอันก็จะได้ packages.png อีกอันที่บอกเราว่า packages มันเชื่อมต่อกันยังไงบ้าง</p>

<p><img src="/assets/imgs/pyreverse-classes.png" alt="" /></p>

<p>หรือจะดู command เพิ่มเติมที่ Pyreverse สามารถรับได้โดยใช้</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pyreverse <span class="nt">--help</span>
</code></pre></div></div>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="python" /><summary type="html"><![CDATA[สร้าง UML diagrams ง่ายๆใน Python ด้วย Pyreverse ทีอยู่ใน Pylint]]></summary></entry><entry><title type="html">อะไรคือ P, NP, NP-Complete และ NP-Hard</title><link href="https://narongdej.dev/blog/th/2021/03/20/%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-p-np-np-complete-np-hard" rel="alternate" type="text/html" title="อะไรคือ P, NP, NP-Complete และ NP-Hard" /><published>2021-03-20T00:00:00+07:00</published><updated>2021-03-20T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/03/20/%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-p-np-np-complete-np-hard</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/03/20/%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-p-np-np-complete-np-hard"><![CDATA[<p>สำหรับคนที่ศึกษา Theory Computer Science อาจจะได้เคยเห็นคนพูดถึงว่าปัญหานี้เป็น P และปัญหานี้เป็น NP มันคืออะไรกันแน่</p>

<hr />

<p>Quick recap เผื่อลืมว่าแต่ละ Big O time complexity ใช้ running time เท่าไร เรียงจากใช้เวลาน้อยสุด ไปใช้เวลาเยอะสุด</p>

<p>\(O(1)\) =&gt; constant-time<br />
\(O(\log_2(n))\) =&gt; logarithmic - time<br />
\(O(n)\) =&gt; linear-time<br />
\(O(n^2)\) =&gt; quadratic-time<br />
\(O(n^k)\) =&gt; polynomial-time<br />
\(O(k^n)\) =&gt; exponential-time<br />
\(O(n!)\) =&gt; factorial-time</p>

<p>โดยที่ \(k\) เป็น constant และ \(n\) เป็นจำนวน input</p>

<hr />

<p>สมมุติว่าเราเป็น researcher ที่มีเป้าหมายในการหา algorithm ที่สามารถแก้ปัญหาได้เร็วขึ้น</p>

<p>เช่นเรารู้ว่าการค้นหาของใน array สามารถหาคำตอบได้ใน polynomial time หรือ \(O(log(n))\) แต่เราต้องการ algorithm ที่ทำการค้นหาได้ใน constant time เช่น \(O(1)\)</p>

<p>หรือเรารู้ว่าปัญหาอย่าง traveling salesman สามารถหาคำตอบได้ใน exponential time \(O(2^n)\)  แต่เราต้องการแก้ปัญหาในได้ใน polynomial time</p>

<p>แต่เรา<strong>ยังไม่รู้คำตอบว่าจะเขียน algorithm นี้ยังไง ใน polynomial time</strong> สิ่งที่เราพยายามทำได้ก่อนก็คือแสดงว่ามันมีคำตอบนะโดย</p>

<ol>
  <li>เราจะพยายามแสดงว่าปัญหาใน time complexity นั้นๆ มันเกี่ยวข้องกัน เช่นปัญหา knapsack สามารถแปลงไปเป็นปัญหา traveling salesman ได้ ถ้าเราสามารถแก้ได้อันใดอันหนึ่งก็ถือว่ามันมีคำตอบ</li>
  <li>ถ้าเราไม่สามารถเขียน algorithm แบบ deterministic ได้ เราก็เขียน algorithm แบบ non-deterministic บางส่วนไปก่อน</li>
</ol>

<h3 id="deterministic-algorithm">Deterministic Algorithm</h3>

<p>deterministic คือ algorithm ที่เวลาเราใส่ input ตัวหนึ่ง ไม่ว่ากี่รอบมันก็จะออกมาเหมือนเดิม ส่วน non-deterministic ก็คือตรงกันข้าม</p>

<p><img src="/assets/imgs/p-np-problem/deterministic@2x.png" alt="" /></p>

<p>โดยที่เราสามารถเขียน non-deterministic search algorithm ที่เป็น constant time ได้ดังนี้</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nf">search</span><span class="p">(</span><span class="nx">A</span><span class="p">,</span> <span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">j</span> <span class="o">=</span> <span class="nf">choice</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="c1">// คิดซะว่าเป็น constant O(1)</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">A</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span> <span class="o">==</span> <span class="nx">key</span><span class="p">)</span> <span class="nx">then</span>
    <span class="p">{</span>
        <span class="nf">write</span><span class="p">(</span><span class="nx">j</span><span class="p">);</span>
        <span class="nf">success</span><span class="p">();</span> <span class="c1">// คิดซะว่าเป็น constant O(1)</span>
    <span class="p">}</span>
    <span class="nf">write</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
    <span class="nf">failure</span><span class="p">();</span> <span class="c1">// คิดซะว่าเป็น constant O(1)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>โดยที่เรายังไม่รู้ว่า choice() นั้นทำงานยังไง แต่เรา assume ว่ามันทำงานได้ และใช้เวลาแค่ \(O(1)\) ในอนาคตอาจจะมีคนมาแก้ปัญหา choice() นี้แทนเราได้</p>

<h2 id="p--deterministic-polynomial-time">P = Deterministic polynomial time</h2>
<p>P หรือ Polynomial time คือ</p>

<p>ปัญหานั้นต้องสามารถแก้ได้ในเวลา \(O(C * n^k)\) โดยที่ \(C &gt; 0\), \(k &gt; 0\), \(C\) และ \(k\) เป็น constant และ \(n\) เป็นจำนวน input โดยที่ค่า \(k\) ควรจะน้อยกว่า \(n\) ไม่งั้น algorithm จะคล้าย exponential มากกว่า</p>

<p>เราอยากได้ algorithm ที่อยู่ในกลุ่มนี้เพราะว่าเราสามารถให้ computer ณ ปัจจุบันที่เป็น Deterministic Turing Machine แก้ปัญหาได้เสร็จในระยะเวลาที่ไม่นานเกินไป</p>

<h2 id="np--non-deterministic-polynomial-time">NP = Non-deterministic Polynomial Time</h2>
<p>NP คือปัญหาที่ไม่สามารถแก้ได้โดยภายใน Polynomial Time <strong>แต่สามารถเช็คคำตอบได้ใน Polynomial Time</strong> โดยเป็นปัญหาที่ใช้เวลาแก้เป็น exponential  เช่นปัญหาที่มี Time Complexity เป็น \(O(n^n)\), หรือ \(O(2^n)\)</p>

<p>เช่นปัญหา Integer Factorization หรือการแยกตัวประกอบ ที่มี Complexity เป็น \(O(k^n)\) แต่เราสามารถเช็คได้ใน polynomial time เพียงแค่คูณมันกลับเข้าไป</p>

<p><img src="/assets/imgs/p-np-problem/p-np@2x.png" alt="" /></p>

<p>ภาพนี้ที่เป็นภาพที่เราจะเห็นค่อนข้างบ่อย ซึ่งแปลว่าปัญหาที่เป็น P เป็น subset ของ NP และเคยเป็น NP มาก่อน</p>

<p>เช่นก่อนหน้าที่จะมี merge sort เราไม่รู้ว่าจะแก้ปัญหานี้ยังไงใน polynomial time แต่เราสามารถเขียนให้มันเป็น non-deterministic polynomial time ได้ พอเราค้นพบ algorithm merge sort ปัญหาที่เคยเป็น NP ก็กลายเป็น P</p>

<h2 id="np-complete">NP-Complete</h2>
<p>ข้อแตกต่างระหว่าง NP และ NP-Complete คือสิ่งที่เรียกว่า completeness</p>

<p>ความหมายของมันคือ \(Y\) เป็น NP-Complete ถ้าเราสามารถแปลง NP Problem \(X\) ไปเป็น \(Y\) ได้โดยใช้ polynomial time</p>

<p>ซึ่งหมายความว่าถ้าสามารถแก้ NP-Complete อันใดสักอันหนึ่งได้ใน polynomial time ได้ อีกอันหนึ่งก็สามารถแก้ได้เช่นเดียวกัน</p>

<p>วิธีการแสดงว่า \(X\) สามารถเป็น \(Y\) ได้เรียกว่าการทำ Reduction</p>

<p>ใน research <a href="https://www.researchgate.net/publication/221580898_Reducibility_Among_Combinatorial_Problems">Reducibility Among Combinatorial Problems</a> ของ Karp ได้ prove 21 algorithms ว่าสามารถ reduce ไปมาได้ และแสดงให้เห็นว่าพวกมันเหล่านั้นเป็น NP-Complete ยกตัวอย่างเช่นปัญหา Satisfiability, Traveling Salesman, และ Knapsack</p>

<h2 id="np-hard">NP-Hard</h2>
<p>สุดท้าย ปัญหาที่ยากที่สุดใน Computer science ที่ไม่แค่แก้ไขปัญหายาก ยังไม่รู้ด้วยซ้ำว่าจะตอบคำถามยังไง ซึ่งมีความยากอย่างน้อยเท่ากับ NP แต่อาจจะยากยิ่งกว่า</p>

<p>ความหมายของ NP-Hard คือ ปัญหา \(Y\) เป็น NP-Hard ถ้าเราสามารถแปลง NP-Complete \(X\) เป็น \(Y\) ได้ใน polynomial time</p>

<p>สิ่งที่ผมไม่ได้บอกก่อนหน้านี้ก็คือ P, NP, NP-Complete คือปัญหาที่เป็น Decision Problem หรือ ปัญหาที่เราสามารถตอบได้ว่ามันถูก หรือไม่ถูก แต่ NP-Hard ไม่จำเป็นต้องเป็น Decision problem</p>

<p>แต่ก็ยังมีปัญหาที่เป็น NP-Hard ที่เป็น decision แต่ไม่ใช่ NP เช่น halting problem อีกด้วย เพราะฉะนั้น NP-hard problem ไม่จำเป็นต้องเป็น NP</p>

<h2 id="หน้าตา-diagram-p-np-np-complete-และ-np-hard">หน้าตา Diagram P, NP, NP-Complete และ NP-Hard</h2>
<p>สุดท้ายเราก็จะได้ diagram ตัวนี้ที่เราเคยเห็นค่อนข้างบ่อย</p>

<p><img src="/assets/imgs/p-np-problem/p-np-diagram@2x.png" alt="" /></p>

<p>ซึ่งที่เราอธิบายและทำความเข้ามาทั้งหมดอยู่บนพื้นฐานว่า P != NP แต่ปัญหาคือเรายัง prove มันไม่ได้ ถ้าเกิด P = NP จริง แปลว่าปัญหาที่เป็น NP และ NP-Complete จะสามารถแก้ได้ใน polynomial time มีรางวัลให้สำหรับคนที่สามารถ prove ได้ว่า P != NP ด้วยนะครับ <a href="https://en.wikipedia.org/wiki/Millennium_Prize_Problems">Millennium Prize Problems - Wikipedia</a></p>

<p>ถ้าเกิด P = NP จริง จะทำให้หลาย encryption และ cryptography algorithm พัง เพราะตอนนี้หลาย algorithm ที่เกี่ยวกับ security ทั้งหลายแหล่ assume ว่าการโจมตี algorithm นั้นๆใช้เวลานานเกินกว่าจะเป็นไปได้จริง</p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="theory" /><category term="complexity" /><summary type="html"><![CDATA[ทำความเข้าใจว่า Polynomial Time คืออะไรแล้วมันสำคัญยังไงกับ P, NP, NP-Complete, และ NP-Hard]]></summary></entry><entry><title type="html">Covariance กับ Correlation คืออะไร</title><link href="https://narongdej.dev/blog/th/2021/03/09/covariance-%E0%B8%81%E0%B8%B1%E0%B8%9A-correlation-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3" rel="alternate" type="text/html" title="Covariance กับ Correlation คืออะไร" /><published>2021-03-09T00:00:00+07:00</published><updated>2021-03-09T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/03/09/covariance-%E0%B8%81%E0%B8%B1%E0%B8%9A-correlation-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/03/09/covariance-%E0%B8%81%E0%B8%B1%E0%B8%9A-correlation-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3"><![CDATA[<p>สมมุติว่าคุณไปเดินเข้าร้านสะดวกชื้อแล้วไปนับดูว่ามีแอปเปิลแดงกี่อัน แล้วแอปเปิลเขียวกี่อัน คุณสามารถนำตัวเลขที่นับได้มาวาด graph แบบนี้</p>

<p><img src="/assets/imgs/covariance-graph1@2x.png" alt="" /></p>

<p>โดยที่แกน x เป็นแอปเปิลเขียว และแกน y เป็นแอปเปิลแดง เราสามารถทำการวาดจุดแอปเปิลแดงและเขียวให้เป็นจุดเดียวได้ดังแบบจุดน้ำเงิน เมื่อเราได้จุดน้ำเงิน เราก็จะสามารถดู relationship trend line ได้ดังรูปด้านล่าง
<img src="/assets/imgs/covariance-graph2@2x.png" alt="" /></p>

<p>เส้นนี้บอกเราว่า ถ้า slope มีค่าเป็นบวก เพราะฉะนั้น ถ้าในร้านนั้นมีแอปเปิลเขียวเยอะ แอปเปิลแดงในร้านนั้นก็มีจำนวนเยอะตามกัน</p>

<p>กลับกัน ถ้าวาดออกมาแล้ว slope เป็นลบ แสดงว่าเมื่อแอปเปิลเขียวเพิ่มขึ้น แอปเปิลแดงจะน้อยลง; relationship ตรงกันข้าม</p>

<p>สุดท้ายถ้าเส้น slope เป็นแนวนอน หรือแนวตั้งแปลว่า แอปเปิลแดงกับแอปเปิลเขียวไม่มี relationship กันเลย</p>

<p><strong>ตัวเลข covariance ใช้บอก relationship ที่ว่ามาได้ โดยเมื่อคำนวนออกมาจะได้เลข +, - หรือ 0</strong></p>

<p>สูตรคำนวน Covariance ก็ตามนี้เลย</p>

\[\dfrac{(x - \bar{x})(y - \bar{y})}{n - 1}\]

<p>\(\bar{x}\) ก็ตือ mean ของแอปเปิลเขียว ส่วน \(\bar{y}\) ก็คือ mean ของแอปเปิลแดง</p>

<p>วิธีการหา mean ในที่นี้ก็คือ</p>

\[\bar{x} = \dfrac{\text{sum of green apple}}{\text{number of store}}\]

<p>ถ้าเราเปลี่ยนแกน y ให้เป็นแอปเปิลเขียวเหมือนกับแกน x สูตรของเราก็จะกลายเป็นสูตร Variance ธรรมดาตามนี้</p>

\[\dfrac{\sum(x - \bar{x})^2}{n - 1}\]

<h2 id="ปัญหาของ-covariance">ปัญหาของ Covariance</h2>
<p>คือตัวเลขไม่ได้บอกว่า relationship นั้นเกี่ยวข้องกันขนาดไหน แค่บอกว่าเกี่ยวข้องแบบ positive slope (ไปในทางเดียวกัน) negative slope (ทางตรงข้าม) หรือเป็น 0 คือไม่เกี่ยวข้องกัน</p>

<p>ถ้า scale ของ data เปลี่ยนไป ก็ยังจะทำให้ค่า covariance เปลี่ยนอีกด้วย</p>

<p>แต่ covariance เป็นสูตรสำคัญที่เอาไว้ใช้คำนวนสูตรอื่นๆเช่น correlation และเป็นตัวที่สามารถมาแก้ปัญหาดังกล่าวได้</p>

<h2 id="correlation">Correlation</h2>
<p>ปัญหาของ covariance ที่ทำให้ไม่สามารถบอกได้ว่าแต่ละ relationship นั้น strong (ค่าต่างๆอยู่ใกล้เส้น trend line ที่เราวาด) หรือ weak (ค่าต่างๆอยู่ไกล)</p>

<p><img src="/assets/imgs/correlation@2x.png" alt="" /></p>

<p>วิธีการคำนวน correlation ก็คือ</p>

\[\dfrac{covariance(x, y)}{\sqrt{variance(x)}\sqrt{variance(y)}}\]

<p>ซึ่งที่สูตรนี้ทำก็แค่บีบค่าโดยใช้ตัวหารให้อยู่ระหว่าง -1 ถึง 1 เพื่อที่เราจะได้สามารถเข้าใจตัวเลขได้ง่าย</p>

<p>โดยที่ค่า 1 หมายถึง strong positive correlation และ -1 หมายถึง strong negative correlation ถ้า correlation ต่ำก็จะมีค่าใกล้เลข 0 ทั้งฝั่งบวกและลบ ถ้าเกิดเป็น 0 เลยแปลว่าไม่ correlate กันเลย</p>

<p>ถ้าเรามี data มากพอ ก็จะทำให้เราสามารถทำ educated guessed ค่าโดยใช้อีกค่าหนึ่งได้</p>

<p>สุดท้ายอย่าลืมว่า correlation วาด trend เป็นเส้นตรงเท่านั้น</p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="math" /><summary type="html"><![CDATA[มาทำความเข้าใจและความแตกต่างของ Covariance และ Correlation กันเถอะแบบฉบับคนโง่เลข]]></summary></entry><entry><title type="html">รัน Neural Network model ใน Unity ด้วย Barracuda</title><link href="https://narongdej.dev/blog/th/2021/03/02/%E0%B8%A3%E0%B8%B1%E0%B8%99-neural-network-model-%E0%B9%83%E0%B8%99-unity-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-barracuda" rel="alternate" type="text/html" title="รัน Neural Network model ใน Unity ด้วย Barracuda" /><published>2021-03-02T00:00:00+07:00</published><updated>2021-03-02T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/03/02/%E0%B8%A3%E0%B8%B1%E0%B8%99-neural-network-model-%E0%B9%83%E0%B8%99-unity-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-barracuda</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/03/02/%E0%B8%A3%E0%B8%B1%E0%B8%99-neural-network-model-%E0%B9%83%E0%B8%99-unity-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-barracuda"><![CDATA[<p>วันนี้ผมจะมาสอนวิธีการรัน Neural Network model ใน Unity แบบง่ายๆ โดยใช้ Package Barracuda ของ Unity กันครับ</p>

<p>โดย Barracuda จะรับ model เป็นไฟล์ ONNX นะครับ ถ้าคุณทำ model ใน tensor flow ต้องทำการแปลงก่อนโดยใช้โปรแกรมเช่น <a href="https://github.com/onnx/tensorflow-onnx">GitHub - onnx/tensorflow-onnx: Convert TensorFlow models to ONNX</a> หรือถ้าเป็น ML Lib ตัวอื่นก็ต้องหาตัว converter เพื่อแปลงเป็นไฟล์ .onnx ก่อนนะครับ</p>

<h2 id="1-ลง-barracuda-ใน-unity">1. ลง Barracuda ใน Unity</h2>
<p>ก่อนอื่นเลย เราต้องมาลง Barracuda ใน Unity กัน ซึ่งทำได้ไม่ยาก โดยให้กดเข้าไปที่ Window -&gt; Package Manager</p>

<p><img src="/assets/imgs/unity-package-add.png" alt="" /></p>

<p>กด + และเลือก <strong>Add package from git URL</strong> แล้วใส่</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://github.com/Unity-Technologies/barracuda-release.git
</code></pre></div></div>

<p>แล้วกด Add ครับ</p>

<blockquote>
  <p>จริงๆตามใน Doc ของ Unity บอกว่าลงผ่าน Package Manager ก็ได้ แต่ผมหาไม่เจอ</p>
</blockquote>

<h2 id="2-ใช้งาน-barracuda">2. ใช้งาน Barracuda</h2>
<p>ที่นี้คุณก็แค่ลากไฟล์ model .onnx ของคุณเข้า project หากลง package ถูกต้อง มันจะขึ้นว่าเป็น NNModel หน้าตาแบบนี้</p>

<p><img src="/assets/imgs/unity-barracuda-nnmodel.png" alt="" /></p>

<p>หลังจากนั้นคุณก็พร้อมใช้งานแล้วครับ!</p>

<p>วิธีการใช้งานหลักๆมีดังนี้</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="n">NNModel</span> <span class="n">modelSource</span><span class="p">;</span> <span class="c1">// ลากไฟล์ model เข้าตรง Inspector</span>
<span class="p">&lt;..&gt;</span>
<span class="kt">var</span> <span class="n">model</span> <span class="p">=</span> <span class="n">ModelLoader</span><span class="p">.</span><span class="nf">Load</span><span class="p">(</span><span class="n">modelSource</span><span class="p">);</span> <span class="c1">// Load model</span>
<span class="kt">var</span> <span class="n">mWorker</span> <span class="p">=</span> <span class="n">WorkerFactory</span><span class="p">.</span><span class="nf">CreateWorker</span><span class="p">(</span><span class="n">WorkerFactory</span><span class="p">.</span><span class="n">Type</span><span class="p">.</span><span class="n">ComputePrecompiled</span><span class="p">,</span> <span class="n">model</span><span class="p">);</span> <span class="c1">// สร้าง Worker เพื่อรัน Model</span>

<span class="c1">// รัน Model ให้โยน input เป็น Tensor object เข้าไป</span>
<span class="kt">var</span> <span class="n">inputs</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="n">Tensor</span><span class="p">&gt;();</span>
<span class="n">inputs</span><span class="p">[</span><span class="n">name1</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Tensor</span><span class="p">(...);</span>
<span class="n">inputs</span><span class="p">[</span><span class="n">name2</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Tensor</span><span class="p">(...);</span>
<span class="n">worker</span><span class="p">.</span><span class="nf">Execute</span><span class="p">(</span><span class="n">inputs</span><span class="p">);</span>

<span class="c1">// เอา output ออกมาจาก worker</span>
<span class="kt">var</span> <span class="n">O</span> <span class="p">=</span> <span class="n">worker</span><span class="p">.</span><span class="nf">Peek</span><span class="p">(</span><span class="n">outputName</span><span class="p">);</span> <span class="c1">// output name คือชื่อ layer ที่จะดูนะครับ</span>

<span class="c1">// สุดท้ายอย่าลืม Free Resource จาก GPU</span>
<span class="n">O</span><span class="p">.</span><span class="nf">Dispose</span><span class="p">();</span>
<span class="n">worker</span><span class="p">.</span><span class="nf">Dispose</span><span class="p">();</span>
</code></pre></div></div>

<p>แค่นี้คุณก็สามารถรัน Neural Net บน Unity แบบ Cross Platform ได้แล้วครับ 🎉</p>

<p>ความยากจริงๆจะอยู่ตรง Preprocess ข้อมูลเพื่อยัดเข้า model ของเรา เพราะอาจจะต้องเขียนเองครับ ไม่ได้มีตัวช่วยง่ายๆแบบ Keras Preprocessor</p>

<p>สุดท้าย สามารถไปอ่านเพิ่มเติมเกี่ยวกับ Barracuda ได้ที่ <a href="https://docs.unity3d.com/Packages/com.unity.barracuda@0.3/manual/index.html">ลิงค์นี้</a> และก็ Barracuda ยังไม่ได้ Support ทุก Operatations กับ Activations นะครับ ลองเข้าดู list ที่ support ได้ที่ลิงค์ด้านบนเช่นเดียวกัน</p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="unity" /><category term="datasci" /><summary type="html"><![CDATA[รัน Neural Network model ใน Unity ง่ายๆ แบบ Cross Platform โดยใช้ package Barracuda]]></summary></entry><entry><title type="html">Eth2 คืออะไร อะไรคือ Proof of Stake</title><link href="https://narongdej.dev/blog/th/2021/02/20/eth-2-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3-%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-proof-of-stake" rel="alternate" type="text/html" title="Eth2 คืออะไร อะไรคือ Proof of Stake" /><published>2021-02-20T00:00:00+07:00</published><updated>2021-02-20T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/02/20/eth-2-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3-%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-proof-of-stake</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/02/20/eth-2-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3-%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3%E0%B8%84%E0%B8%B7%E0%B8%AD-proof-of-stake"><![CDATA[<p>หลังจากไม่ได้ติดตามเรื่อง Crypto มานาน ก็ไปเห็นข่าวเรื่อง Eth2 ค่อนข้างเยอะ ไอเราก็มี Ether อยู่จำนวนนึง ก็สงสัยว่ามันคืออะไร มันจะมีผลอะไรกับเราไหม เลยไปนั่งอ่านแล้วสรุปมาได้ดังนี้ครับ</p>

<h2 id="ปัญหาของ-ethereum">ปัญหาของ Ethereum</h2>
<p>Ethereum 1 นั้นทำงานแบบ Proof of Work (PoW) ในการตรวจสอบ (validate) และเก็บข้อมูล ในระบบ PoW นั้น ผู้ขุดเหมืองทั้งหลายช่วยกัน Generate Hash ใครที่สามารถ Generate Hash ได้ตรงตามเงื่อนไขของ Network ก็จะได้ Reward</p>

<p>ตัวอย่างเงื่อนไขในการขุดใน Bitcoin คือ hash ที่ออกมาต้องมี 0 นำหน้าจำนวนกี่ตัว ยิ่งเยอะก็ยิ่งยาก ซึ่งความปลอดภัยมาจากการปรับเงื่อนไขตัวนี้ ยิ่งมีคนขุดเยอะ ก็ยิ่งเงื่อนไขยาก ยิ่งเงื่อนไขยาก ก็ยากที่ปลอม Block</p>

<p><img src="/assets/imgs/bitcoin-block-hash@2x.png" alt="Bitcoin Block Hash" /></p>

<p>ปัญหาคือมัน Scale ไม่ดี  ตอนนี้ Eth 1 สามารถ process ได้แค่ประมาณ 10 transactions per second ซึ่งน้อยมากๆ โดยเฉพาะเมื่อมีคนใช้งานเยอะขึ้นเรื่อยๆ</p>

<p>อีกหนึ่งประเด็นก็คือ Gas price หรือค่าที่เราต้องจ่ายเวลาจะทำ Transaction ก็แพงขึ้นเพราะ Demand/Supply คนที่ขุด มีไม่พอ Demand ของคุณใช้เหรียญ แค่ทำ Transaction ง่ายๆ เช่นโอนเหรียญใน Eth ตอนเขียนตอนนี้ก็ 150 บาทบวกๆแล้ว</p>

<p>ประเด็นอีกอย่างนึงที่สำคัญก็คือเรื่องการสิ้นเปลืองพลังงาน เนื่องจาก PoW นั้นเป็นเน้นจำนวนคุณขุด (miner) เป็นหลักในการ validate block ยิ่งมีการใช้งานมากยิ่งขึ้นก็ยิ่งต้องมี miners มากขึ้น เมื่อราคาขึ้น คนก็สนใจ mining มากขึ้นอีก พลังงานจากคอมที่ใช้ในการขุดเหมืองตอนนี้ มีมากพอจะให้ Iceland ใช้ได้เลยทีเดียว</p>

<h2 id="proof-of-stake-คืออะไร">Proof of Stake คืออะไร</h2>
<p>สิ่งที่ Eth2 พยายามจะแก้นั้น หลักๆคือ</p>

<ol>
  <li>Scalability - ต้องสามารถ Validate Transaction ได้รวดเร็วมากยิ่งขึ้น พร้อมรองรับคนใช้งานที่เยอะกว่าปัจจุบัน (หลักพันหรือแสน Transaction ต่อวิ)</li>
  <li>Security</li>
  <li>Sustainable - ไม่สิ้นเปลืองพลังงาน</li>
</ol>

<p>ทางออกของทีมผู้พัฒนา Ethereum นั้นก็คือ เปลี่ยนจาก PoW มาเป็น Proof of Stake (PoS)</p>

<p>แทนที่จะให้ใครก็สามารถ Validate บล็อกได้ ก็ให้คนที่อยาก Validate เอาเหรียญที่ตนเองมีมาค้ำเผื่อที่จะสามารถทำตัวเป็น Validator</p>

<p>เมื่อมี Block ใหม่เกิดขึ้น ทุกคนจะได้เสียงโหวตว่าจะให้ Block นั้นเข้าไปอยู่ใน Network หรือไม่อยู่ แทนที่จะต้องมานั่ง Random nonce รัวๆและสิ้นเปลืองพลังงาน คนที่ทำตัวเป็น Validator ก็จะได้ Reward แบ่งๆกันไปตามจำนวน Validator ใน network (ยิ่งมีเยอะยิ่งได้น้อย)</p>

<p>คนที่ต้องการจะเป็น Validator จะต้องทำการวางเหรียญ (stake) <strong>อย่างน้อย 32 Eth</strong> (มูลค่าตอนนี้ก็ประมาณ 2 ล้านบาท) เพื่อเปิด Validator หากคุณพยายามจะโกงก็จะโดนหักตรงนี้ไป หรือถ้า Node คุณ offline คุณก็โดนลงโทษและหักออกจากก้อนนี้ไปเช่นเดียวกัน</p>

<p>แต่ถ้าสมมุติคุณมีเงินเยอะมาก แล้วเปิด Validator หลายๆตัวแล้วโกงละ? เป็นไปได้ไหม ก็เป็นไปได้ แต่คุณต้องมีเงินมากกว่า 51% ของเงินที่ Stake ทั้งหมด ซึ่งตอนนี้มีมากกว่า 3 ล้าน ETH แล้ว หรือเกือบแสน validator เพราะฉะนั้นคุณจะต้องมีเงินประมาณ 1 แสนล้านบาท เพื่อโกง network ตอนนี้ (ณ ตอนเขียนยังเป็นแค่ phase 0; เดียวจะอธิบายต่อด้านล่าง ยังไม่ใช่ network หลักด้วยซ้ำนะ)</p>

<p>แต่ถ้าถามผมว่ามีโอกาสโดนโกงได้มั้ย มันก็ดูเป็นไปได้นะครับ เพราะว่าแทนที่จะต้องใช้เครื่องขุดเป็นกำลังในการโกง (Mining Power) ซึ่งก็หาได้ยากเพราะเป็นของจับต้องได้ ก็แค่ต้องใช้เงินในการโกง ซึ่งสามารถกู้ยืมได้อีก</p>

<h2 id="ตอนนี้ไปถึงไหนแล้ว-เรามาถึง-eth2-เต็มตัวรึยัง">ตอนนี้ไปถึงไหนแล้ว เรามาถึง Eth2 เต็มตัวรึยัง?</h2>
<p>ตอบสั้นๆว่ายัง การปล่อย Eth2 มีทั้งหมด 4 Phase (0, 1, 1.5, 2)</p>

<p><img src="/assets/imgs/eth2-phases.png" alt="Eth2 Phases" /></p>

<p><strong>Phase 0</strong> - คือเปิด Beacon Chain ซึ่งจะทำงานแบบ PoS แบบที่อธิบายไปด้านบน งานหลักๆของ Validator ใน Step นี้ก็คือการ Validate เงินที่ถูกโอนมาจาก Mainnet</p>

<p><strong>Phase 1</strong> - คือการเริ่มทำ Shard chain, คือแทนที่จะมี Blockchain ใหญ่ๆตัวเดียว ก็สามารถมี Chain ย่อยๆ เพื่อกระจายหน้าที่ แต่ใน Phase นี้ Shard แต่ละตัวจะยังไม่มีการทำ transaction หรือ smart contract จะเป็นแค่ถังเก็บข้อมูล</p>

<p><strong>Phase 1.5</strong> - เอา Mainnet มาเป็นอีกหนึ่ง shard ใน Beacon และเป็นจุดจบของ PoW ของ Ethereum</p>

<p><strong>Phase 2</strong> - เปิดให้ Shard สามารถ execute Smart Contract และ Features ต่างๆของ Eth2 ได้</p>

<p>ซึ่งเวลาที Rollout แต่ละ Phase นี้ยังไม่แน่นอน แต่ Phase 0 ได้ถูกปล่อยไปแล้วเมื่อปลายปี 2020</p>

<p>สิ่งที่จะออกมาก็สามารถถูกเปลี่ยนได้เรื่อยๆ เรามาคอยมาติดตามกันครับ ว่าสุดท้ายแล้วจะเกิดอะไรขึ้น จะมีคนสามารถโจมตี Eth2 สำเร็จไหม แล้วจะเกิดอะไรขึ้นกับนักขุดเหมืองทั้งหลายเมื่อ Eth เปลี่ยนจาก PoW เป็น PoS หรือเหรียญ Eth2 จะกลายเป็นสิ่งที่คนไม่ใช้ไปเลย</p>

<p><em>ถ้าหากผมเขียนอธิบายอะไรผิดพลาดไป ขอโทษล่วงหน้าด้วยนะครับ และสามารถ Comment แก้ไขได้ด้านล่างเลยครับ</em></p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="crypto" /><summary type="html"><![CDATA[ทำความเข้าใจว่า Eth2 คืออะไร และทำไมต้องเปลี่ยนเป็น Proof of stake]]></summary></entry><entry><title type="html">วิธีการ Deploy ง่ายๆด้วย CapRover</title><link href="https://narongdej.dev/blog/th/2021/02/15/%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B8%81%E0%B8%B2%E0%B8%A3-deploy-%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2%E0%B9%86%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-caprover" rel="alternate" type="text/html" title="วิธีการ Deploy ง่ายๆด้วย CapRover" /><published>2021-02-15T00:00:00+07:00</published><updated>2021-02-15T00:00:00+07:00</updated><id>https://narongdej.dev/blog/th/2021/02/15/%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B8%81%E0%B8%B2%E0%B8%A3-deploy-%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2%E0%B9%86%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-caprover</id><content type="html" xml:base="https://narongdej.dev/blog/th/2021/02/15/%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B8%81%E0%B8%B2%E0%B8%A3-deploy-%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2%E0%B9%86%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-caprover"><![CDATA[<p>ปัจจุบันมีวิธีการ Deploy Website หรือระบบของเราหลากหลายวิธีมากๆ จะพยายาม Setup Server ตัวเองขึ้นมา ลง Stack ที่ต้องใช้ หรือจะใช้ Service แบบ PaaS แบบ Heroku</p>

<p>แต่ปัญหาของมันคือ Heroku ไม่มี Server อยู่ใกล้ประเทศไทย ใกล้สุดตอนนี้อยู่ที่ญี่ปุ่น ซึ่งมันก็ทำให้เว็บไซต์ของเราโหลดช้าลง (อาจจะไม่เยอะ แต่มันก็ช้ากว่านะ😂)</p>

<p>ผมขอแนะนำให้รู้จักกับ <a href="https://caprover.com">CapRover</a></p>

<p>CapRover คือโปรแกรม Open Source ที่ให้คุณสามารถมี PaaS ของตัวเอง ไว้บน server ของคุณเอง หรือจะวางไว้บน VPS ประเทศไทย หรือ VPS ใกล้ๆบ้านเราแบบ DigitalOcean ได้ฟรี และสามารถ Deploy ได้ง่าย เหมือนใช้ Heroku</p>

<p>ก่อนอื่นเลยเราต้องมา Setup ระบบ CapRover บน Server ของเราก่อน</p>

<h2 id="requirement">Requirement</h2>
<p>สิ่งที่คุณต้องมี</p>
<ol>
  <li>Server (ที่สามารถลง Docker ได้ และมี Public IP)</li>
  <li>Domain  (ให้ point มาที่ Server ที่ต้องการจะใช้ก่อนด้วยนะครับ)</li>
</ol>

<h2 id="setup-server">Setup Server</h2>
<p>ผมจะทำการ Setup บน Server Ubuntu 20.04 ให้ดูนะครับ</p>

<h3 id="1-firewall">1. Firewall</h3>
<p>ก่อนอื่นต้องเช็คว่าคุณได้ตั้ง Firewall ไว้รึเปล่า ถ้าไม่ได้ตั้งให้ข้ามไปได้เลย ถ้าตั้งต้อง allow ports ดังนี้ก่อนนะครับ</p>

<p>TCP
<code class="language-plaintext highlighter-rouge">80,443,3000,996,7946,4789,2377</code></p>

<p>UDP
<code class="language-plaintext highlighter-rouge">7946,4789,2377</code></p>

<p>VPS อย่าง DigitalOcean หรือ Nipa Cloud มี external firewall service (อยู่นอก VPS ของเรา)
อย่าลืมไปตั้งตรงนั้นให้เรียบร้อยนะครับ หรือถ้าใช้ UFW ก็อย่าลืม allow</p>

<h3 id="1-ลง-docker">1. ลง Docker</h3>
<p>CapRover ทำงานบน docker เพราะฉะนั้นบนเครื่อง Server ของคุณจะต้องลง docker ไว้ก่อน</p>

<p><a href="https://docs.docker.com/engine/install/ubuntu/">ลง Docker บน Ubuntu</a> ให้ทำตามนี้เลย</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get update

<span class="nb">sudo </span>apt-get <span class="nb">install</span> <span class="se">\</span>
    apt-transport-https <span class="se">\</span>
    ca-certificates <span class="se">\</span>
    curl <span class="se">\</span>
    gnupg-agent <span class="se">\</span>
    software-properties-common

curl <span class="nt">-fsSL</span> https://download.docker.com/linux/ubuntu/gpg | <span class="nb">sudo </span>apt-key add -

<span class="nb">sudo </span>add-apt-repository <span class="se">\</span>
   <span class="s2">"deb [arch=amd64] https://download.docker.com/linux/ubuntu </span><span class="se">\</span><span class="s2">
   </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="s2"> </span><span class="se">\</span><span class="s2">
   stable"</span>

<span class="nb">sudo </span>apt-get update
<span class="nb">sudo </span>apt-get <span class="nb">install </span>docker-ce docker-ce-cli containerd.io
</code></pre></div></div>

<p>ตบท้ายด้วย mod user ให้สามารถใช้  <code class="language-plaintext highlighter-rouge">docker</code> ได้โดยไม่ต้อง <code class="language-plaintext highlighter-rouge">sudo</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>usermod <span class="nt">-aG</span> docker <span class="nv">$USER</span>
</code></pre></div></div>

<h3 id="2-ลง-caprover">2. ลง CapRover</h3>
<p>ลงง่ายมาก command เดียวจบ!</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">-p</span> 80:80 <span class="nt">-p</span> 443:443 <span class="nt">-p</span> 3000:3000 <span class="nt">-v</span> /var/run/docker.sock:/var/run/docker.sock <span class="nt">-v</span> /captain:/captain caprover/caprover
</code></pre></div></div>

<h3 id="3-ใช้งาน-caprover">3. ใช้งาน CapRover</h3>
<p>เมื่อมันขึ้นว่า</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">***</span> CapRover is initializing <span class="k">***</span>
Please <span class="nb">wait </span>at least 60 seconds before trying to access CapRover.
</code></pre></div></div>

<p>ให้รอหนึ่งนาที แล้วเข้าไปที่ http:{IP ของคุณ}:3000 และใส่รหัส captain42</p>

<p>ก่อนอื่นเลย <strong>รีบเปลี่ยนรหัสผ่าน</strong>! และก็เสร็จเรียบร้อย คุณมี Heroku ใช้บน Server ของคุณเรียบร้อยแล้ว!</p>

<p>การใช้งานก่อนอื่นเลย ให้คุณสร้าง App โดยใส่ชื่อที่ต้องการและกด Create New App ถ้าต้องแอพคุณต้องมี persistent data หรือถ้าต้องการให้ data ไม่หายเมื่อเกิดการ restart เกิดขึ้น ให้ติ้กช่อง Has Persistant Data ด้วย</p>

<p>เสร็จแล้ว!</p>

<p>หลังจากคุณสามารถ Deploy App ของคุณลง CapRover ได้ 6 วิธี</p>
<ol>
  <li>ผ่าน  <a href="https://caprover.com/docs/get-started.html#step-4-deploy-the-test-app">CapRover CLI</a></li>
  <li>Zip ไฟล์เป็น .tar และอัพโหลดผ่าน browser (ต้องมี <a href="https://caprover.com/docs/captain-definition-file.html">Captain Definition File</a> ด้วยนะ)</li>
  <li>Deploy ผ่าน Webhook ของ Git Repository</li>
  <li>เขียน Dockerfile ลง CapRover</li>
  <li>เขียน Captain-definition ลง CapRover</li>
  <li>เลือกใช้ Docker image</li>
</ol>

<p>ถ้าหากคุณต้องการใช้แอพเช่น Wordpress หรือต้องการ MySQL Database สามารถกด One-Click Apps/Databases ได้ด้วยนะ โดยการกด One-Click Apps/Database ตอนกดสร้าง Create a new app</p>]]></content><author><name>{&quot;username&quot;=&gt;&quot;narongdejsrn&quot;, &quot;github&quot;=&gt;&quot;narongdejsrn&quot;, &quot;email&quot;=&gt;&quot;narongdej@sarnsuwan.com&quot;}</name><email>narongdej@sarnsuwan.com</email></author><category term="blog" /><category term="th" /><category term="sysadmin" /><summary type="html"><![CDATA[มีระบบ PaaS แบบ Heroku บน Server ของตัวเองง่ายๆด้วย CapRover]]></summary></entry></feed>