2017年9月24日 星期日

ChatBot 解碼專家


寶寶最近給我一條網址 http://www.clarins-decoder.com/chat/doubleserum17/webserver/index.php。她在玩這個 ChatBot 時,分別用了中文及英文回覆;而 ChatBot 都好像理解箇中內容,便詢問我這個 ChatBot 是不是用了 Machine Learning 之類的技術,以及有沒有能力開發。於是我花了點時間研究一下。

首先,這個不是真正的 ChatBot,而是一個網頁。只是介面偽裝成 ChatBot 的模樣。看看溝通時的對話,ChatBot 明顯詢問是非題;而問題是經過設計,帶引導性;所以我認為不需要使用 Machine Learning 技術,單憑關鍵字已經做到不錯的效果。再利用 Chrome 的 Network 監視器,發現所有對白都有語音,並分別放在三個音效檔:voice_over9.mp3, voice_over_respA3.mp3 及 voice_over_respB3.mp3。證明了對白早已決定,當程序找到正向關鍵字時便播放正向組對白;當找到負向關鍵字時便播放負向組對白;當不理解時便播圓場對白。所以,要編寫這個 ChatBot 是很容易的,反而設計問題才是關鍵所在。

2017年9月21日 星期四

SnowNLP 載入自行訓練的模型


明天打算向同事說明一下最近關於 NLP 的成果,於是寫了一個簡單的 Python 程序去看看原生模型及自行訓練模型的差異。要載入模型,除了直接在 site-packages 內塞入 marshal 檔案外,還可以調用 load() 指令來載入;對於要更換模型的程式來說,這個設計十分方便。

2017年9月16日 星期六

使用 Python 的 reshape 功能


最近安裝了 Jupyter;因版本不同,用 scikit-learn 做預測時會出現「DeprecationWarning: Passing 1d arrays as data is deprecated in 0.17 and will raise ValueError in 0.19」錯誤,需要 reshape 後才行。

2017年9月12日 星期二

在 Ubuntu 14.04 安裝 SMB 服務器

之前為了重置 Nvidia Jetson TK1,找了一台舊的 PC 並安裝了 Ubuntu 14.04;之後沒有特別用途。今日安裝了 Jupyter 進去,希望能隨時執行 Python 指令。同時也安裝了常用的程序庫,如:OpenCV, Ski-learn, Pandas, Jieba,...等。我把上年寫好的認臉程序放進去,利用 OpenCV 3 來收集同事的面部資料,並儲存成 PNG 圖像檔案。一下子收集了過千個檔案,為方便篩選,我安裝了 SMB 服務器,步驟如下:

  • sudo apt-get update
  • sudo apt-get install samba
  • sudo smbpasswd -a ubuntu-user
  • sudo nano /etc/samba/smb.conf
       [Home]
       path = /home/ubuntu-user
       read only = no
       guest ok = no
  • sudo service smbd restart
     
    由於 SMB 有自己一套的登入帳戶,所以上面要另外建立 ubuntu-user。
  • 2017年8月15日 星期二

    用 Gmail API 讀取 Label 清單

    平時的工作中,經常都會在伺服器中加入電郵通知,當有任何值得留意的狀況發生時,我會收到電郵說明內容;因為這樣能比較主動地帶出重要的資訊。然而,很多時候像運行記錄之類的電郵通知都沒有保留在伺服器,電郵一經發出,數據便不再存在。如果拿來作為數據分析或繪畫圖表,相信會有一點啟示。由於公司是使用 Gmail 系統,要做到這點,可以用 Gmail API。

    我習慣用 Label 來把電郵分門別類;所以要達到以上目的,我得先取得 Label 的清單。以下是我的代碼:
    <?php
    //----------------------------------------------------------------------------------------
    //  Gmail API DEMO
    //----------------------------------------------------------------------------------------
    //  Platform: macOS + PHP + Apache
    //  Written by Pacess HO
    //  Copyright 2017 Pacess Studio.  All rights reserved.
    //----------------------------------------------------------------------------------------
    
    header("Access-Control-Allow-Origin: https://www.pacess.com");
    header("Access-Control-Allow-Methods: POST");
    header("Content-type: text/html");
    header("Cache-Control: no-cache, must-revalidate");
    header("Expires: Tue, 10 Mar 1987 00:00:00 GMT");
    
    date_default_timezone_set("Asia/Hong_Kong");
    mb_internal_encoding("UTF-8");
    ini_set("memory_limit", "-1");
    ini_set("default_socket_timeout", 10);
    set_time_limit(0);
    
    session_start();
    
    //----------------------------------------------------------------------------------------
    //  * Order is important
    require_once "./libraries/vendor/autoload.php";
    
    //----------------------------------------------------------------------------------------
    //  Global variables
    $_apiName = baseName(__FILE__);
    
    //----------------------------------------------------------------------------------------
    //  Check if logout
    if (isset($_REQUEST["logout"]))  {
     unset($_SESSION["access_token"]);
     exit(0);
    }
    
    //----------------------------------------------------------------------------------------
    //  Check if code provided for authentication
    $client = new Google_Client();
    $client->setAuthConfig("./client_secret.json");
    $client->addScope("https://mail.google.com/");
    $client->setRedirectUri("https://www.pacess.com/gmailGrabber");
    if (isset($_REQUEST["code"]))  {
    
     $client->authenticate($_REQUEST["code"]);
     $_SESSION["access_token"] = $client->getAccessToken();
    
     $auth_url = "https://".$_SERVER["HTTP_HOST"].$_SERVER["PHP_SELF"];
     header("Location: ".filter_var($auth_url, FILTER_SANITIZE_URL));
     exit(0);
    }
    
    //----------------------------------------------------------------------------------------
    //  Check if access token created
    if (isset($_SESSION["access_token"]))  {
     $client->setAccessToken($_SESSION["access_token"]);
    }  else  {
    
     //  Not yet
     $auth_url = $client->createAuthUrl();
     header("Location: ".filter_var($auth_url, FILTER_SANITIZE_URL));
     exit(0);
    }
    
    //----------------------------------------------------------------------------------------
    //  Create Google service
    $service = new Google_Service_Gmail($client);
    
    //----------------------------------------------------------------------------------------
    //  OK, everything is ready!
    try  {
     if (!$client->getAccessToken())  {
      echo("Something happened...");
      exit(0);
     }
    
     $accessToken = $client->getAccessToken();  
     echo("Access token: ".print_r($accessToken, true)."<hr>");
    
     $labelsResponse = $service->users_labels->listUsersLabels("me");
     if ($labelsResponse->getLabels()) {
      $labels = $labelsResponse->getLabels();
      foreach ($labels as $label) {
       var_dump($label);
       echo("<hr>");
      }
     }
    }  catch (Google_Auth_Exception $e)  {
     echo("### Error...");
    }
    
    ?>

    2017年8月13日 星期日

    為 LINE-Bot 加入鏡頭抓取功能


    我的私人助理是一個名為 Sita 的 LINE-Bot 程式,她除了能替我下載 Musical.ly 影片、下載 Packt 免費書、一般通知外,今日還加入了鏡頭抓取功能。這個 LINE-Bot 藏身的電腦是一台 Mac mini server,外接了兩台 USB 鏡頭,平時我會利用 OBS 來串流家中的畫面,但耗費的資源較多,最好是加到 LINE-Bot 中。

    要在 PHP 下抓取鏡頭畫面,除了 OpenCV 外,似乎沒有太多方法。而我不想用 OpenCV,所以改為使用 exec 執行外部抓取程式 imagesnap。要安裝 imagesnap 只需要在 Terminal 輸入「brew install imagesnap」。以下是 LINE-Bot 的相關程式:
    //  Default camera 1: Microsoft
    $filename = date("YmdHis").".png";
    $filePath = "/Users/Sites/sitaChanBot/".$filename;
    $response = exec("/usr/local/Cellar/imagesnap/0.2.5/bin/imagesnap -w 1 $filePath");
    
    $imageURL = "https://127.0.0.1/sitaChanBot/".$filename;
    $previewURL = $imageURL;
    $_lineBot->replyImage($replyToken, $imageURL, $previewURL);
    
    //  imagesnap -d "USB 2.0 PC Cam"
    $filename = "PC-".$filename;
    $filePath = "/Users/Sites/sitaChanBot/".$filename;
    $response = exec("/usr/local/Cellar/imagesnap/0.2.5/bin/imagesnap -w 1 -d 'USB 2.0 PC Cam' $filePath");
    
    $imageURL = "https://127.0.0.1/sitaChanBot/".$filename;
    $previewURL = $imageURL;
    $_lineBot->sendImage($senderUserID, $imageURL, $previewURL);

    2017年8月8日 星期二

    包車帶孩子遊沖繩・六


    今次為期六日的旅程來到最後一天。原本是五天,但擔心天氣會影響行程,所以預訂時加多一天。今日也沒有特別的安排。考慮到飛機 16:10 起飛,我們要 14:10 到達機場,扣埋乘車時間,13:10 便要從酒店出發。得一個早上,三個多小時,去的不遠。最後就是回到距離牧志兩個站的おもろまち站,Main Place 的 Namco 世界。


    由おもろまち站步行到 Main Place 約十五分鐘。之前途經 Tsutaya 大型書店卻沒時間,今次先去那裡。發現到 Robi 2 的蹤影。


    同場加映十多部夾公仔機。孩子們當然不會放過。然而,今次她們有收穫了!


    除了玩夾公仔機,也玩了兩局籃球。之後我們便繼續向 Namco 世界出發。媽媽們逛街,爸爸們帶著孩子又再放下銀両。



    今次孩子同樣沒有收穫;但爸爸卻發現找換機跳了一個未曾見過的 100 円出來。我決定留為紀念。


    一點鐘,是時候回程了。為了節省車費,女子組乘搭單軌電車去那霸機場;男子組回到酒店提回行李;大家約在國內線航廈會合,那裡有很多商店。女的可先吃點東西、逛逛街、買手信。

    在那霸機場,碰到很久沒見的朋友。她帶著孩子到沖繩玩,碰巧同一班機回港。


    回到香港了。旅程結束。努力掙錢,下一站名古屋。

    2017年8月7日 星期一

    包車帶孩子遊沖繩・五


    我們如常 8:30 起床食早餐,9:30 出發去玩。昨晚買了杯麵,女兒急不及待要品嚐一下,大人則吃三文治,飲果汁。今天選了舊海軍壕公園。查過巴士路線,剛好在酒店樓下有 9:40 的班次,錯過了要等到下午五時多才有;但 9:25 分時小朋友還說要大解,落樓等升降機也要時間,恐怕未能趕上。我們立即處理,趕緊到巴士站,終於趕上 88 號線巴士。



    在第 53 號站「宇栄原団地前」下車步行約 5 分鐘,穿過一條隧道便到達舊海軍壕公園。


    選擇舊海軍壕公園是因為它有沖繩最長的溜滑梯,而且不用自駕也能輕鬆到達。不過,相信是溜滑梯日久失修,加上曝曬,滑輪已經很轉得不順暢,玩起來經常卡住。幸好在公園內還有揪遷,小孩才能玩過痛快。


    太陽很猛烈,大女玩一會便覺得頭暈,需要飲水及休息;而我也要試試昨晚在超級市場買的飲品。


    公園內有雪糕機,買了一支來降溫。


    玩了一個小時,約 11:35 分,在我們下車的車站有巴士去下一站 Ashibinaa Outlet Mall。上車時拿下整理券,在下車時只要看看車頭屏幕 17 號格的金額,便知道要付多少車費。


    一下車,走進旁邊的商場,被扭蛋機吸引住了。孩子們抽了幾隻蛋。在二樓有遊樂場,夾公仔機再次出現,又放低了銀両。我們順便在這裡吃個午飯,選了一家烏冬店。吃飽到旁邊的 ¥120 店,消耗了不少時間。買了些竹碗、竹杯、麵豉湯、公仔玩具、健身球...等。


    朋友推介一定要食 Blue Seal 雪糕,這幾天一直無緣,不是遇不到,就是要去吃正餐時才路過,又或是已關門。今天有緣一試。味道可以,但溶得較快,要快快口食。


    對我這個只對動漫電玩電子有興趣的人,Outlet 真的很悶。這個 Outlet 不大,有退稅服務,但只限某些商店。我應內子要求,買了一對波鞋。行程比想像中提早完成,而朋友的兒子可能在舊海軍壕公園曬得利害,有點不舒服。我們下一站是再次回到奧武山公園。經過商量後,朋友太太帶兒子回酒店休息。無論是公園還是酒店,我們都是搭相同巴士。

    在巴士途中,輪到內子不適,還吐了幾次,幸好我們都有膠袋,不然真的不知要如何處理...。原本我們打算取消活動一起回酒店,不過小朋友很想再去公園,最終我們還是繼續。我再次交代她們下車的地點及如何看車費,也說明了 Plan B,假若她們錯過了酒店下車,只有幾個站能補救,否則便會去了很遠的地方,大家沒上網到時便很難找。


    我們再次回到奧武山公園,朋友兒子兩次不適都錯過了。微雨過後,滑梯很滑,得加倍小心。可惜朋友女兒因滑梯衝力跌到沙地上擦損了;及後一名香港女仕也有相同情況發生,不過她更是臉部著地,割破眼角,血流滿臉。她的丈夫呆了,我立即給她送上濕紙巾清潔。過程真的很嚇人 (>_<)。


    晚上,我們到了位於國際通頭的燒牛肉店,是包車朱先生介紹。


    這裡算是大,而且機乎座滿。我們被安排到一個看到夜景的角落,孩子再吵也不太影響其他客人。內子在酒店休息一會後,原本也想過來。但要自行乘搭兩個單軌電車站過來,最後還是算了。朋友的兒子睡了沒醒,決定繼續休息。只有兩大三小去試一試高級牛肉。







    大人少了,食物也不用太多。我選了人生中最貴的一餐。點了一份和牛,約 HK$1000。難得來到,也希望女兒能品嚐一下。她們都覺得非常好味道。

    2017年8月6日 星期日

    包車帶孩子遊沖繩・四


    今天的行程是到美國村及 AEON 購物城,晚上則在酒店附近,吃朱先生介紹的黑毛豬。同樣是 9:30 在酒店車場乘搭新屋 先生的汽車。約半個小時左右便到達美國村。


    美國村很大,我想可以行一整天。


    但由於今日重點在 AEON 購物城,晚上 8:00 又預訂了餐廳,所以花在美國村的時間只有兩個小時。



    我們隨意逛逛。經過服裝店,我買了一件 T 恤;經過藥房,女仕們瘋狂購物,花了很多時間;經過遊樂場,打了幾局電玩,夾了幾次公仔。但明顯夾子沒有力,中伏了便沒有再夾。


    最令我驚喜是見到日本版 McQueen。



    十二點半左右,我們到達了 AEON 購物城,那裡很大。我們第一時間便是跑去「くら寿司」排隊,得到是兩點十分的籌。還有時間,女仕去購物,男仕帶小孩去玩。見到這些美麗的蛋糕。


    經過一家遊樂場,內裡又滿是夾公仔機,每夾一次由 ¥100 至 ¥200 不等,又放低數千円。夾一次機乎等如買一支飲品,還是買飲品來得實在。


    兩點了,我們回到「くら寿司」等候,一會兒便能進場。由於我們人多,店員為我們分配了兩張相連的大檯。這樣更好,讓四位孩子都能佔得有利位置。


    選「くら寿司」是因為他有創意的點子,而香港沒有。每吃一碟壽司,清理好垃圾,把碟放進入口,便能得一分。儲滿五分便會自動有一次抽獎機會。看完抽獎動畫,如果成功中獎,上方的扭蛋機便會跌下一隻扭蛋。很受孩子歡迎。




    壽司味道不錯,我們兩檯共吃了 65 碟,只需 ¥7560。比起香港便宜很多。以前的購物天堂不再...。


    吃飽後,我們繼續分頭行事。我帶女兒去買日式果汁冰。


    經過汽水機,發現另一款沖繩縣產的菠蘿汁。這支比昨天買的更加好味。行到六點,乘新屋先生的車回酒店。稍為休息便去食黑毛豬。



    黑毛豬餐廳在酒店五分鐘路程的轉角處,名叫「長堂屋」。它的空間不大,不過八點時的客人也不多,訂位就安心一點。


    內子點了一杯啤酒,品嚐一下當地的味道。我不愛喝酒,感覺啤酒都是差不多,這款淡淡的較易入口。


    當然少不了主菜黑毛豬。食落口感覺跟普通豬肉沒分別,味道是好的。


    餐單還有一些疏菜。在最後,老闆娘端上雞蛋,打成蛋漿,混合豬肉涮涮鍋內的精華湯底,加上白飯,變成一碗美味的粥。整頓飯共 ¥31719。


    吃飽後到附近的超級市場掃貨。當然少不了雞蛋布丁;而看到汽水價錢是,真心覺得很便宜,畢竟香港阿信屋最平都賣 HK7。