Sunday, April 4, 2021

安裝 WSL2 GUI

引言
Ubuntu 近年經過不斷的改良,已經十分好用。不過,由於 Linux 始終缺乏許多常用的辦公軟件(例如 Office、IxChariot、Zoom 等等),要把它當成取代 Windows 的日常作業系統,的確有點困難。就算 Linux 上可以用 Wine 去執行 Win32 API,反向支援 Windows Apps,但結果總是強差人意。不過,由於 Windows 實在缺少了很多 POSIX-compliant API、shell 支援、以及 automake 等開發工具,所以,用它來做日常開發,又總是令人覺得綁手綁腳。

有見及此,Microsoft 在 2019 年決定推出 Windows Subsystem for Linux (WSL)。這個輕量版的 Translation Layer(註:WSL2 為 Hyper-V VM),既能提供接近原生 Linux 系統的效能,又能幫助用家繼續沿用 Windows 作為主要 OS。不過唯一缺點是,Microsoft 直至 2021 年 4 月,仍然未推出對 WSL 的官方 GUI 支援。 

這次實作,我們決定自行用 VcXsrv 加上輕量版的 Xfce 介面,使 Windows Host 也能輸出 Ubuntu 的 GUI。事實上,整個安裝過程,也只是涉及數行 commands,難度並不高,特別適合 Linux 新手操作。

---

安裝步驟
假設你已經成功安裝 WSL2 (Ubuntu 20.04),你只需在 WSL2 的 Terminal 中進行以下步驟:

1. 執行以下命令,安裝 xfce4 介面及 GNOME 圖示:

sudo apt update
sudo apt upgrade
sudo apt install tasksel
sudo taskel install xubuntu-desktop
sudo apt install gtk2-engines
sudo apt install gnome-icon-theme-full

2. 在 ~/.bashrc 的最尾,加上以下內容:

export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
export LIBGL_ALWAYS_INDIRECT=1

3. 在 Windows Host 中安裝 VcXsrv https://sourceforge.net/projects/vcxsrv,並注意:

  • 安裝最新穩定版  vcxsrv-64.1.20.9.0
  • 安裝過程中,記得將防火牆 Allow

4. 安裝完成後,重新開啟 WSL2 Terminal,並在 Windows 中開啟 XLaunch。
(記得在 Extra Settings 中,需剔選 Disable Access Control)

5. 在 WSL2 Terminal 中安裝 Meld,並測試 Meld GUI 是否能投射到 VcXsrv 中:

sudo apt install meld
meld ./

---

演示圖片


---

小結
使用 WSL2,能在 Windows OS 上,以接近原生的效能,運行 Ubuntu 的 GUI Apps。
本文使用 X Server 及 Display Redirection,以非官方的做法,實現 GUI 的支援。

---

參考:

https://medium.com/@japheth.yates/the-complete-wsl2-gui-setup-2582828f4577


Wednesday, March 31, 2021

安裝 Gerrit Missed Event Playback

引言
不少公司都會採用 Gerrit 管理代碼,並且結合 Jenkins 作持續交付之用。例如,在 Jenkins 與 Gerrit 之間,其實可以透過 Gerrit Trigger Plugin 互連:當每次有新的 Code Review Event,Jenkins 就可以對新的 Code Patch 進行編譯、測試、然後 Tag Verified。這樣,Jenkins 就可以用作檢查代碼的穩定性,以及協助開發者快速找出有問題的代碼。

不過,因為不同原因(例如關機維護),有時候 Jenkins 與 Gerrit 會需要斷開。當兩者斷開之後,若果 Gerrit 出現的新 events,Jenkins 就會出現 event miss。即使 Jenkins 重啟之後,所有 missed events 在預設的狀態下,並不會自動同步。是次實作,我們會透過在 Gerrit 上安裝 Events-Log Plugin,使 Jenkins Gerrit Trigger 能自動同步(並執行)所有 missed events。

---

Step-by-Step 步驟

A. 手動編譯 events-log 插件

1. 從 Google Source 下載 events-log 的 git:
git clone https://gerrit.googlesource.com/plugins/events-log

2. 從 git 中 checkout 最新的穩定版本 (for example 3.1):

cd events-log/
git checkout stable-3.1

3. 安裝編譯需要的 dependencies:

sudo apt install openjdk-11-jdk
sudo apt install npm
npm install -g @bazel/bazelisk

4. 編譯 events-log:

bazel build events-log

5. 查看 build 輸出:

ls bazel-bin/events-log.jar

B. 打開 Gerrit 的 Remote Plugin Admin

1. 開啟  <gerrit-install-path>/etc/gerrit.conf 並加上以下兩行:

[plugins]
        allowRemoteAdmin = true

2. 重新啟動 Gerrit:

sudo /etc/init.d/gerrit restart

3. 登入 Gerrit 的 Web UI 並查看 Plugin Manager 是否存在:

http://<your gerrit url>/plugins/plugin-manager/static/index.html
C. 用 SSH 安裝插件

1. 安裝 events-log 插件到 Gerrit:

cd bazel-bin/
ssh -p 29418 localhost gerrit plugin install -n events-log.jar events-log.jar

2. 重新啟動 Gerrit Server:

sudo /etc/init.d/gerrit restart

3. 開啟 Gerrit Web UI,查看 enabled plugins 中是否含有 events-log:

http://<your gerrit server url>/admin/plugins
D. 設置 Jenkins Gerrit Trigger

1. 開啟 Jenkins 上的 Gerrit Trigger Server Configuration:

Manage Jenkins -> Gerrit Trigger -> Add new server / Server Settings 

2. 按 "Advanced...",找到 "REST API" 一項,並填上以下資料:

[v] 剔選 "Use REST API"
Gerrit HTTP Username:  <your jenkins username in Gerrit>
Gerrit HTTP Password:  <your jenkins HTTP Credentials>
[v] 剔選 "Enable Code-Review"
[v] 剔選 "Enable Verified"

3. 儲存,並重開 Gerrit 與 Jenkins 的連線。

---

小結
由於 event-log 並沒有已經預先編譯好的 jar 執行檔,所以是次實作,我們自行編譯它,並開啟 Gerrit 的插件安裝工具,最後把它順利安裝到 Gerrit 中。安裝完成後,Jenkins 內的 Gerrit Trigger Plugin 將能自動同步 event history,以減少不必要的手動 trigger 或 event miss。

---

參考:

[1] - https://plugins.jenkins.io/gerrit-trigger/ 

[2] - https://docs.bazel.build/versions/master/install-bazelisk.html

[3] - https://gerrit-review.googlesource.com/Documentation/dev-build-plugins.html

[4] - https://gerrit-review.googlesource.com/Documentation/cmd-plugin-install.html

Tuesday, January 19, 2021

使用 LSTM Autoencoder 進行異常檢測

簡介
上回提到,因為 LSTM 能記住事件的先後次序,所以它特別適合用來處理一些,具有時間順序的數據 (Time Series Data) 。上一期,我們實現了如何使用監督學習 (Supervised Learning) 做未來預測。而這一次,我們會用 LSTM 實現另一種常用的系統:非監督式異常檢測 (Unsupervised Anomaly Detection)。

所謂異常檢測 (Anomaly Detection),其實是指:當我們訓練系統的時候,我們可以透過提供大量正常數據,繼而讓系統自己找出這些數據的規律。然後,當系統突然收到一些與既定規律相違背的數據,它就能自動把這些數據判別為異常。

這種模型,特別適合用來做入侵偵測系統 (Intrusion Detection System):因為黑客入侵等事件並不尋常, 而且入侵模式千變萬化。所以,我們只能大約知道正常的網絡流量會是怎樣。相反,對於黑客進行攻擊的情況,我們所知的並不多。故此,系統設計者能只提供正常情況的數據,以及極少量的入侵數據(這種數據,在 ML 世界中,也稱為非平衡數據 (Unbalanced Data)),然後就讓系統自行找出正常情況的規律。

是次實作,我們會用另一個較簡單的例子:心跳規律來做示範。我們只需要提供正常的心跳數據,讓系統自行訓練,持之以恆,它就能辨認出何謂正常的心跳。訓練完成後,只要系統發現一些奇怪的心跳規律, 它就會回報異常。這次的實作,是改自 Curiousily - Time Series Anomaly Detection using LSTM Autoencoders with PyTorch in Python 的範例。我改進了以下兩點:

  • 使用 DataLoader 來進行 batch training
  • 簡化代碼,使其更易閱讀及改裝

---

實作模型:LSTM Autoencoder
Autoencoder,顧名思義, 就是一種自動編碼 (Encode) 及解碼 (Decode) 的轉碼器。

簡單來講,你可以把它想像成一個有損壓縮 (Lossy Compression) 的工具:首先,它會把收到的訊號,編譯 (Encode) 成一個非常細小的編碼 (Embedding Code,下圖紅色部分,又稱為 Bottleneck Layer)。然後, 當解碼器 (Decoder) 收到這一條小編碼,就會試圖把它解譯成原本的訊號。若果轉譯來回的數據十分相似,就代表它帶來的損失很少,也代表這對 Encoder 和 Decoder 的效能很強。相反,若果它在經過來回轉譯後,輸出與原本大相逕庭,就代表這對 Encoder 和 Decoder 失真的情況嚴重,十分垃圾,並不可靠。

Information Theory 告訴我們,由於數據儲存密度有限,若果壓縮要做到可靠,只有兩個方法:一是加大 Embedding Code,讓它存放更多資料,另一方法,則是按照數據的規律,度身訂造 Encoder 和 Decoder,以達至極致的壓縮比率。 由於我們的系統會固定 Embedding Code 的大小 (Embedding Code 的大小,通常固定在原數據十分之一左右) ,所以系統不能使用第一個方法保存資料。也就正說:系統會被迫用第二個方法達成極致壓縮。

正因如此,隨著訓練時間越長,系統將會越來越依靠訓練數據(也就是正常數據)中的一些固定規律去運作。最後,它會變成一個專為正常數據而設的壓縮器。換句話說,這個壓縮器,能在你放入正常數據的情況下,完美地進行極致壓縮。但如果你放入一些異常數據,這個壓縮器就會突然失控,結果變得強差人意。而我們這次,正正就是利用這一種特性,來判斷數據是否異常。

---

演示代碼

https://github.com/cmcvista/MLHelloWorld/blob/main/LSTMAutoencoder/HeartbeatAutoencoder.ipynb

---

幾個重要變數
是次實作,有幾個變數,需要仔細解釋:

  • 一條時間順序的資料 (data_seq_len) 的長度為 140,特徵 (data_n_features) 為 1。
    這是指一個數據長度為 140, 而維度只有 1( y 座標為 1 )。
  • Embedding code 的長度為 (data_embedding_dim) 為 64,
    也代表它的 Tensor Dimension 是 64*1。
  • 整個系統的大小為 [140, 128, 64, 64, 128, 140]
    (格式:[Input, Hidden, Embedding, Embedding, Hidden, Output] )
  • 訓練 100 次對 LSTM 來講,其實並不足夠,但由於只作示範,就懶得執行太久。

---

Losses 計算方法
損失 (Losses) 的定義,是指解碼後的結果,跟原數據的差異大小。在這次實作中,為了與原例子一樣,我使用了兩組數據之間的 L1Loss 的總和去定義。不過,其實用其他方法(例如 Mean Square Error (MSE) Loss)也能達至相同效果。

---

Threshold 的定義方法
這次因為懶惰,我只用肉眼判斷 Threshold 的值。這個 Threshold 是指:只要任何數據的 total loss 比它小,就應把它當成正常數據。事實上,隨著訓練次數越多,系統會對正常數據的規律更熟悉,輸出會更接近完美,所以它的 total loss 必定會越來越小。故此,訓練越多,Threshold 其實也應該變得越小,以達至更準確的辨識率。

 ---

小結
運用 LSTM Autoencoder 做異常偵測,能達至頗高的成功率,而且原理也不難懂。
不過,由於變數不少,LSTM 訓練時間也頗長, 所以部署時,應先考慮其他方法。

總而言之,這次的實驗也算成功,至少加上 batch support 後,仍能達至預期結果。

---

參考:

[1] Time Series Anomaly Detection using LSTM Autoencoders with PyTorch in Python - https://curiousily.com/posts/time-series-anomaly-detection-using-lstm-autoencoder-with-pytorch-in-python/

[2] 【深度學習】一個簡單又神奇的結構:自編碼機 AutoEncoder - https://jason-chen-1992.weebly.com/home/-autoencoder