隨著 Meta 的 Llama 2 開放商用, 很多公司也摩拳擦掌想把 Llama 2 應用在自己的產品上。 然而 Pre-train 的模型不見的符合公司或使用者的需求, 為了求更好的表現,fine-tune LLM 就成了其中一個不錯的方法。本篇文章會介紹 Fine tune LLM 的基本概念,以及訓練的程式碼還有一些注意的事項。
現在越來越多開源的大型語言模型釋出,除了早前僅供學術使用的模型,現在還多了像是 Llama 2, Mistral 7B 的模型可以應用在商業上。雖然有些模型透過 RLHF 的訓練方式讓模型在一開始就能流暢的對話,且表現也相當亮眼。但對於特定的需求,像是回答公司內部的資訊,或是解決不常見的任務需求,可能就不盡理想。
舉例來說,我們公司在內容審查上面,常會需要審核客戶上傳的內容。由於 Llama 2 特地加強在安全性上的表現,在我們的政策上它都會以非常嚴格的標準審視。譬如我們有個政策是不能誇大,模型在看到「最佳」、「最好」等字詞就會拒絕這個客戶的內容;甚至在遇到非常母湯的內容時,模型會直接說這個內容不安全而拒絕執行接下來的任務。我們也試過 prompt engineering 或是 in-context learning,但是結果就是不如預期。
Fine-tune 確實能讓模型更好的學習我們的問題,但是想要 Fine-tune LLM 就會有幾個痛點需要克服:
硬體資源
Llama 2 在 Hugging Face 的文件上說明要 deploy 70B 的資源至少也要兩個 A100, 儘管是 7B 也需要一個 A10G. 這還只是推論所需要的資源,更不要說訓練可能需要兩倍以上的顯卡記憶體需求。
過度訓練導致破壞模型原有的能力
一般我們想要 Fine-tune 模型都會採用監督式的訓練方式,由於模型會根據我們提供的 label 計算 loss 並更新權重,我們很難確保在訓練過程中,模型不會為了在我們的任務上獲得更準確的結果,而喪失了模型原有的能力,像是溝通能力等。
我們會用 QLoRA 跟 Instruction tuning 來解決上述的兩個問題, 並附上程式說明如何實現。
QLoRA 其實就是將 LoRA adapter 應用在 Quantized 的 Pre-trained model 上。當中不管是 LoRA 或是 Quantization, 目的都是在降低訓練對記憶體的依賴,讓我們可以在更少的資源下訓練模型,且很多實驗證明這樣的效果不見得輸給直接 fine-tune 整個模型,甚至有可能更好。
這裡舉個簡單的例子, 假設我們有兩層神經網路, 第一層有 3 個節點, 第二層有 5 個節點, 在不計算 bias 的情況下, 要訓練的參數共有 3x5=15 個.
LoRA 是透過在輸入及輸出層中多加一層節點數量較少的層 (秩), 來降低訓練參數. 譬如我們這裡加了一層 1 個節點的 layer, 這樣輸入/輸出的緯度維持不變, 總訓練參數在不計算 bias 的情況下, 降低至 3x1 + 1x5 = 8 個.
在舉一個實際一點的案例, 假設我們有個神經網路, 第一層有 100 個 nodes, 第二層有 100 個 nodes. 則總參數量為 100 x 100 (weights) + 100 (biases) = 10,100 個.
1 | class SimpleNetwork(nn.Module): |
如果我們改用 LoRA 進行訓練, 中間的秩 r 選擇 10, 則訓練參數變為 100 x 10 + 10 x 100 (weights) + 100 (biases) = 2,100 個.
1 | import loralib as lora |
降低的訓練參數幅度高達 79.2%!
事實上在訓練時並不是用 LoRA 的層直接取代舊有的層, 而是將 LoRA 掛載在特定的層上. 基本上會凍結 Pretrained model 的 weights, 在訓練時僅對 LoRA 的權重做梯度更新, forward 時再把 LoRA 的參數加回去原本的 weights 中.
透過 LoRA adapter, 我們在訓練時可以很大程度的降低訓練的成本. 但現在大型語言模型動輒數十甚至數百 gigabytes, 以 Llama 2 70B chatt 的模型, 光是載下來就要 129G, 更不要說讀進顯卡記憶體需要 200G 以上. 由於 Llama 2 保存及訓練權重的 weights 精度採用 FP16, 當參數量越大 (7B, 13B, 70B), 所需要的內存就會劇烈上升. 也因此便有了降低一點精度 (FP16 → NF4) 的 Quantization 方法. 訓練時再將它轉成 16 bits 進行計算.
這樣的好處是大幅減少了讀進模型所需要的記憶體. 以 Llama 2 70B chat 來說, 顯卡記憶體只要 30 G 左右就可以把模型讀進來, 並進行預測. QLoRA 的 paper 證明了透過這樣去訓練模型, 模型的預測表現跟直接 fine-tuned model 的表現比不會比較差.
QLoRA 主要有幾個創新:
Meta 在訓練 Llama 2 chat 模型時, 採用的 Reinforcement Learning from Human Feedback (RLHF). 這樣的訓練方式讓模型可以學到大量知識外, 還能保有自然語言的溝通能力. 然而要採用這個方法訓練, 必須先建出一個 Reward model. 這個模型關係到 LLM 的品質, 他的參數甚至比 LLM 還要大非常多. 像我們這種市井小民實在是很難透過這種方式來 fine-tune 模型.
好在 Google 在 2021 年提出了 Instruction fine-tuning 的方法. 透過訓練模型在不同類型的問題上, 模型在沒見過任務的表現也可以顯著提升. 這樣當我們將自己想要訓練的資料合併在其他任務上一起訓練時, 模型不僅可以在特定任務上表現很好, 同時也還是能在其他任務上保有一定的表現, 不會因為訓練我們的任務而把模型原本的能力給破壞掉.
具體來說要訓練的資料集應該長什麼樣子, 其實並不限定於特定格式, 在 HuggingFace 的 SFTTrainer 也有一些示例. 這裡以 Stanford Alpaca 做說明.
這裡先展示資料應該長什麼樣子:
1 | Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request. |
主要我們需要填入的地方是大括號的部分:
這裡要注意的是資料的品質要求越高越好, 就像 QLoRA 的 paper 說的:
Data quality is far more important than dataset size, e.g., a 9k sample dataset (OASST1) outperformed a 450k sample dataset (FLAN v2, subsampled) on chatbot performance, even when both are meant to support instruction following generalization.
我們看下面幾個範例會更好理解, 這些都是從 Databricks 提供的高品質開源 instruction dataset 抽出來的
Open QA
1 | Below is an instruction that describes a task. Write a response that appropriately completes the request. |
Brainstorming
1 | Below is an instruction that describes a task. Write a response that appropriately completes the request. |
Closed QA
1 | Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request. |
這些任務會全部整理成上述的形式, 放在同一個資料集一起訓練. 由於我們自己準備的資料任務可能相對單一, 我們可以透過合併開源的資料集來增加高品質的資料. 像上述 Databricks 提供的這個資料集 databricks-dolly-15k 就是很好的資源.
實作的部分主要有幾個重點, 將依序說明.
這裡以開源的資料 databricks-dolly-15k 做示範, 自己的資料也可以整理成同樣的格式加進來.
1 | from datasets import load_dataset |
1 | { |
將資料轉成 pandas 的 df, 並濾除 tokens 超過模型 context length 的資料. 而後轉換成 csv 保存起來
1 | import pandas as pd |
之後也可以透過 datasets
套件把資料讀進來
1 | from datasets import load_dataset |
要將模型透過 quantization 的方式讀進來, 我們可以靠 bitsandbytes
這個套件來做到這一點. 首先做參數設定
1 | import bitsandbytes as bnb |
接下來就可以將模型讀進來了
1 | from transformers import AutoModelForCausalLM, AutoTokenizer |
首先我們必須找出要替換掉的神經網路, 根據 QLoRA 的那篇 paper 說, 選擇全部的 linear layers 效果比只選擇 adaptation 的效果來的好 (ref. https://arc.net/l/quote/ediwtoyl). 因此我們這裡就選擇所有的 linear layers.
1 | from peft import LoraConfig |
為了將 dataset 轉成 instruction 的形式, 我們可以在訓練時添加轉換的函數讓模型在訓練前自動轉換資料, 以下是轉換的函數.
1 | # Stanford Alpaca 的形式 |
所有程序準備就緒後, 我們就可以開始來訓練模型了.
1 | from transformers import EarlyStoppingCallback |
建議在合併之前將 kernel 清空, 因為合併需要將原始模型讀進來, 並將 adapter 的權重合上去, 所需要的記憶體會相當大. 可能有人會問我直接用掛載的就好, 為什麼一定要合併回去原始的模型, 主要是因為現在如果採用掛載的, LLM 推論的時間會非常長 (ref. https://arc.net/l/quote/jyetbfcv), 基本上根本沒辦法當作服務使用, 目前不確定是 bugs 還是什麼, 建議合併比較保險.
1 | # 將模型讀入, max_memory 可以選擇 gpu 或是 cpu 可以使用多少 memory, 確保不會因為 memory 不足爆掉 |
我們公司確實在 fine-tuned LLM 上取得很亮眼的成績, 而且 LLM 與以往的語言模型不同, 透過 instruction-tuning 的訓練方式, 也可以把多種不同的任務需求都放在同一個資料集讓 LLM 學習, 也就是不同任務只要一個模型就可以處理. 唯一需要注意的是訓練的資料集品質, 只要能確保是高品質的資料集, 相信訓練出來的 LLM 也可以讓你眼睛為之一亮的.
有投資美股的人應該都有一樣的經歷,因為美國跟台灣的時差12小時,看盤可能都得要通宵坐鎮。因為我實在太愛睡覺了,年紀越大也越經不起熬夜折磨,所以就設法用最簡單的方式完成投資時需要做的事情。
為了讓解決方案能夠滿足我的需求,我思考了需要具備的特性:
經過摸索後,Telegram bots 可以很簡單快速的達到我的需求,而且彈性且自由。因此這篇主要會著重在如何架構出 Telegram 機器人,並幫助我達到最基本的需求。後續我會再不斷優化這個機器人,有興趣的可以關注我的Github。
本篇文章重點:
要使用 Telegram 的機器人其實並不困難,官方提供的文件就足以建立自己的機器人。這裡我們手把手把流程跑一次。
首先,用自己的 Telegram 新增好友 BotFather,會出現一個很帥的機器人大叔。
加入他後輸入 /start
就可以取得建立一個機器人所需要的指令。這裡我們得知建立機器人輸入指令/newbot
,並依序輸入顯示名稱以及使用者帳號(用來搜尋以及呼叫用的)。
只要名稱帳號沒有重複,你會看到以下成功訊息,就代表你的機器人建好囉!基本上現在你去搜尋好友的地方打上你剛剛輸入的使用者名稱,就可以找到你的機器人。不過你的機器人現在還是空殼,沒有駕駛人坐在裡面。你要能駕駛它,就需要用到 BotFather 提供的鑰匙(就是下面紅色那一塊 API )。要特別注意的是,只要誰有這個鑰匙,誰就可以操作你的機器人,所以要格外小心這個鑰匙外流。
到這一步我們就可以使用 Python 的套件 telegram 來連結我們建立好的機器人了。根據 官方文件 我們知道需要用 Updater 及 dispatcher 來接收及傳輸資訊。
1 | import telegram |
我們簡單設定一個歡迎語,確定我們有連結到機器人。這個歡迎語會在你第一次加入機器人為好友時發送。如想要手動啟動這個歡迎語,也可以直接在與機器人的對話框中輸入 /start
。
1 | def start(update, context): |
這個 CommandHandler 是接受指令的一個物件,當收到使用者傳送 / 開頭的指令內容時,就會交由這個 CommandHandler 來處理。Telegram 在新增機器人為好友時,就會送一個 /start
給機器人,這也是為什麼用戶加入時會收到這個歡迎語。
除了 CommandHandler 之外,還有 MessageHandler, ConversationHandler…等,用來處理使用者發送出的不同情境,詳細可以看官方網站做出不同變化。
到這裡還不算完成,直到執行下述指令。
1 | updater.start_polling() # 開始推送任務 |
這時候再去 Telegram 中加入機器人,就可以看到你設定的歡迎語了。
為了讓我們的機器人更厲害一點點,這裡來架設互動式的選單,讓他可以富含不同的功能。看起來就像這樣:
要做到選單功能,底層的流程架構必須借助 ConversationHandler ,我草畫了這個函數的功用,並不會很難理解。
簡單的用下圖來說明,一開始會由 start 函數開場,在使用者做出動作後,這個動作 (CHOOSING) 會傳至 ConversationHandler,並依據使用者的動作內容進行判斷,看接下來應該由哪個函數接手後續動作。以這個例子來說,ConversationHandler 判斷並將流程交給 check_and_store 函數,而後這個函數完成後,會再將結果傳給 COMFIRM_STOCK 進行判斷。依此不斷循環。
了解架構後,讓我們直接進入程式的部分。首先,我們先把功能選單的介面用出來。
1 | # 載入功能選單的函數 |
接下來,稍微修改一下剛剛的 start 函數。
1 | # 建立回應變數,就是剛剛架構裡所介紹的東西 |
函數的型態大致就如上面的 start
相同,因此我們先來介紹 ConverstationHandler 該如何設置。
1 | from telegram.ext import (MessageHandler, Filters, |
這裡分幾個部分說明:
entry_points
是進到這個 ConversationHandler 的入口,也就是我們剛剛所建立的 start
函數,可以注意的是 start
函數中使用者輸入的結果會送到 CHOOSING
處理。states
就是剛剛架構圖的主軸,是字典的資料型態,key 值就是我們剛剛設定的 CHOOSING, COMFIRM_STOCK, TYPING_CHOICE 等。MessageHandler
跟 CommandHandler
功能差不多,但因為使用者在點選選單時,回傳的結果會是 Message 的型態,因此採用 MessageHandler 處理。要特別注意的是第二個參數會放入接著處理流程的函數,舉例來說,在 start
函數中使用者點選 輸入/移除追蹤股票
選項,那麼第二個參數就會放入 input_stock
這個函數來處理後續流程。MessageHandler
第一個參數放入判斷值,這裡使用 Filters
來做判別,以 MessageHandler(Filters.regex('^輸入/移除追蹤股票$') & ~(Filters.command | Filters.regex('^結束$')), input_stock)
舉例,用正則表達式判別為 輸入/移除追蹤股票
且不是指令或是 結束
,則進到下一個步驟 input_stock
。fallbacks
是結束這個 ConverstationHandler
執行的函數。完成這個 ConversationHandler
物件的設定後,我們就可以把他加到我們的機器人中了。
1 | dispatcher.add_handler(conv_handler) |
如果想要多瞭解其他的函數及關係,可以參考我的 GitHub,裡面有更詳盡的代碼。
有了以上的選單,機器人就可以依據我們的要求來回應結果了。但如果希望機器人不是單純的接受/回傳訊息,而是能主動根據不同情境跳出通知來警示我們,這樣就更有價值了。因此,這裡會實作如何讓機器人在特定時間或特定事件發生時跳出通知。
要使用這個功能,就必須借助 JobQueue
來實作。
1 | # 建立 JobQueue 物件 |
這個 JobQueue
分別有以下幾種功能
job.run_once
job.run_repeating
job.run_daily
job.run_monthly
照著字面上的意思不難了解功用為何,我們來實作一個例子來幫助了解。這裡我們要建立一個提醒通知,每分鐘都會提醒現在過了幾分鐘。
1 | # 初始化分鐘數 |
如果不是 job.run_once
,我們必須手動關閉該工作,可以透過以下指令來完成。
1 | # 暫時停用該工作 |
透過上述函數,就可以達到每分鐘跳出提醒。依此類推,我們也可以設置每小時的檢查 JobQueue
,關注的股票有無最新新聞,如果有的話就跳通知,沒有的話則略過;或是建立一個在晚上期間關注股票變動超過 10% 時跳出告警等功能。
透過上述對 telegram
套件的說明,我們簡單的介紹了如何用 Python 來架設 telegram 機器人,以幫助我們進一步追蹤股票或是應用在其他生活面向。因為隨著功能越多,設置的架構就會越複雜,在這篇文章就不多著墨在這些部分,有興趣的可以關注我的 GitHub。
如果你覺得這篇文章對你有幫助,還請熱情給我按個讚😂。期望你能順利製作出屬於你的機器人!
]]>黃斑前膜與黃斑部病變不同,黃斑部病變會有個黑點在視線中心並不斷擴大,而黃斑前膜則像是在你的眼睛前面放一張透明塑膠板,仍然看的到東西,但視線都是糊糊的,有光的時候更嚴重,有點像下圖那樣。
如果拖得久一點,所看到的畫面會開始扭曲。像我走在路上時看稍微有點距離的路人,是看不到五官的,因為兩側臉頰都凹了進去。基本上這一階段就需要開刀了,否則再嚴重可能造成視網膜玻璃,這是很嚴重的不可逆傷害了。
我的狀況是在寫程式的時候突然一陣暈眩,大約過個幾分鐘後再看螢幕就無法對焦了。這時候的左眼是糊的,右眼非常清楚。
我前前後後看了國內外三家醫院五個醫生,我的狀況歸納出來主要有兩個可能:
有三個醫生斬釘截鐵的說我一定有撞到,兩個醫生不排除是老化。但因為我沒印象有撞到頭,再加上我的確非常操我的眼睛,實在也不敢確定到底是什麼原因造成的。
唯一的治療方法就是開刀。有極少數的人是會自己復原的,但是機率不高。目前是沒有任何口服或眼藥水可以治療的。一般來說只要狀況不嚴重(景物沒有扭曲),醫生會建議持續觀察並每三個月回診,畢竟手術還是有風險。
我的狀況是2020年六月初出現狀況,六月中看醫生確診。醫生希望我多觀察一個月,在七月中複檢時說狀況還很輕微,後續每三個月回診就好。但我這次看完醫生後兩天併發飛蚊症,等九月中回台灣看醫生時,醫生說黃斑出現裂孔,狀況嚴重需要緊急開刀。所以即便仍在觀察期,只要有新狀況或視線開始扭曲,都應該盡快回診。
如果提早治療的話,視力即便不能完全恢復,也能有個八九成。不過因為初期沒有急迫性也有自己好的機率(雖然非常低),還是跟醫生討論手術與否。
因為我情況比較緊急,回台灣看完門診的後一個禮拜就開刀了,其實也沒什麼時間給我焦慮或緊張。
手術採局部麻醉,就只有麻醉針打下去的時候會痛,其他時候頂多就是有感覺有東西進到眼球。雖然不痛,但其實很難受,因為醫生為了看清眼球內部,會用夾子把眼球撐開,並用強光打在眼球上切除玻璃體。當下一直不斷想「以前的酷刑難不成就是這樣嗎?」、「照胃鏡是不是也這麼難受?」之類的跑馬燈,最後還是靠不斷的深呼吸以及把注意力集中在身體其他部位,才讓自己好過些。
為了要讓黃斑前膜能夠徹底移除乾淨,醫生會點一種藥水讓黃斑前膜染色。點入藥水後,原本只看得到強光的左眼會很像在看萬花筒,又融合了深藍色水墨藝術的感覺。靜置數分鐘後醫生會把墨水移除,這時原本看不見東西的左眼可以清楚看見醫生的手術鉗子在夾著前膜,而被移除的前膜則會在眼球內飄來飄去,也算是很奇特的體驗。前膜去除乾淨後,醫生在我的眼中灌了讓黃斑癒合的氣體,手術就告一個段落了。
為了讓手術時灌入的氣體能夠24小時頂壓著黃斑部,下手術台的那一刻就要開始全程趴著的生活,這當中包含吃飯、睡覺及洗澡…等,頭部都得要隨時保持向下。這大概是整個治療中最痛苦的時候,首先得要找很多事情打發這段漫長的時間(還好有 Podcast 可以聽),再來躺太久背會痛,坐著則是脖子跟肩膀痛。最後睡覺因為頭部要正面向下,實在很難好好呼吸。別說能夠睡了,一個晚上醒來數次都很正常,甚至根本睡不著。這樣的狀況我持續了12天,等到眼中的氣體完全消散之後才可以恢復正常的站立生活。
這裡有個小插曲,我在術後隔天檢查時眼壓過低,正常是13-20,而我當時是6,後一天沒改善還掉到 5。醫生很緊張,推論是因為年輕人的發炎情況會比較嚴重,導致眼壓無法恢復正常水位。持續下去的話可能會有後遺症,在恢復上可能都有風險,醫生後續加強類固醇的使用後才度過這關。
我在術後第四天開始可以看的到模糊的視野,第五天測量視力恢復到 0.3 。醫生可以看得出這幾天是否有好好趴著,有正確落實的話手術的血塊會跑到眼球前端。
雖然黃斑前膜的手術告一個段落,但因為這種手術會高機率誘發白內障,所以基本上還得要動一次刀。雖然視力不能像以往那樣,但只要不會持續惡化,心境上都還是能調適的。現在我們周遭充斥著太多的3C產品,都可能直接或間接傷害我們的眼睛,希望透過我的案例能警示其他科技產品重度使用者,多吃點葉黃素,長時間使用一定要間隔時間讓眼睛休息,以免造成不可逆的永久傷害。
]]>在這篇文章中主要會實作 開啟遠端Jupyter伺服器、用電腦/iPad連結遠端Jupyter伺服器 等兩部分。讓我們開始吧!
要開啟遠端伺服器相當簡單,只要在遠端主機上開啟 cmd,並輸入以下指令就可以開啟了。其中 port 可以是其他的值,只要不要跟其他已佔用 port 衝突就好。
1 | jupyter notebook —-no-browser —-port=8080 |
但如果你沒辦法直接操作遠端主機,當然也是可以透過上篇文章提到的,從手中的電腦開啟 ssh 連接回遠端主機,並輸入上述指令開啟 Jupyter notebook。不過要注意的是,這個視窗只要一關閉,伺服器端的 Jupyter notebook 也會同步關閉。
如果想要一勞永逸,開啟後就不讓遠端電腦關掉的話,可以用類似 tmux 來達到目的,可惜的是 Windows 不能使用。我們可以用 TeamViewer 直接在遠端電腦輸入上述指令並且讓他一直開著,也是可行的方法。
在我們開啟遠端 Jupyter 伺服器後,接下來只要用你手上這台機器連上伺服器即可。這裡我會分別依據 電腦/iPad 來說明。
用電腦連結其實相對簡單,僅需要在所使用的電腦打開終端,並輸入以下指令,就可以連結上遠端伺服器。
1 | ssh -CNL 8080:localhost:8080 遠端使用者名稱@遠端實體IP |
這時在打開瀏覽器並輸入 localhost:8080
就可以看到跑在遠端的 Jupyter notebook 了。
用 iPad 連接有兩種方法,一種是要錢的一種是不要錢的。當然要錢的方式簡單殘暴,無腦連結。如果你的 iPad 版本是 iOS 13 或以上,也僅建議用這種方式連接,我會在後續說明。
Termius 是採用訂閱制的,但是免費的功能就已經可以達到我們的需求了。首先至 App Store 下載 Termius 。打開後點選左邊選單的 Host 並點擊右上角 +,就會看到以下畫面。
這裡是為了建立到遠端主機的連線,依序填入內容:
Alias 建立連線的名稱(隨意填)
Hostname 遠端伺服器的 IP
Port 填預設的 22 即可
Username 遠端伺服器使用者名稱
Password 遠端伺服器使用者密碼
接下來我們到左邊選單的 Port Forwarding 並點擊右上角的 +,會來到這個畫面。
我們只要在 Local 的標籤中,填入下列內容:
Lobel 建立名稱(隨意填)
Host 遠端使用者名稱@遠端實體IP
Port From 填寫 8080
Destination localhost
Port To 填寫 8080
儲存後執行,並且用多工的方式開啟瀏覽器輸入 localhost:8080。就可以看到成功運行的 Jupyter notebook。
不過這裡你一定會想,為什麼我們一定要用分屏的方式執行對吧!這就是免費的缺點了,因為更新到 iOS13 之後,只要你將 termius 縮小或是藏到旁邊,20秒後就會直接關閉。所以這裡得用分屏的方式讓他一直開著,連線才不會斷開。
這是用 iPad 連線最簡單暴力的方式,我們只要開啟後點選左邊選單的 Add Jupyter Server…
輸入下列資訊:
Description 建立名稱(隨意填)
Type 選擇 Local port forwarding
Host 遠端伺服器的 IP
Port 填預設的 22 即可
User 遠端伺服器使用者名稱
Password 遠端伺服器使用者密碼
—
Host localhost
Port 8080
完成後執行,恭喜你可以直接使用 Jupyter notebook了!
用 Windows 當 Server 真的會遇到比較多問題,譬如說前陣子我將 Windows 版本更新,結果 OpenSSH server 在使用者認證上就一直出問題,最後還是用降版本的方式才保證我的 Windows 能繼續連線。但為了因應許多人不會特地安裝 Linux 到電腦中,用這方式連線也是可以接受的。
另外,如果是用 iPad 連線的使用者,Junoconnect 是非常建議買的(非業配),使用體驗很好,幾乎跟直接用本機計算的體驗相當,真的可以只帶一台 iPad 就隨時隨地寫程式的XDD
由於桌電是 Windows 10 的作業系統,基本上已經支援 SSH 的伺服器部署。不過這不是一般人會使用到的功能,所以預設是關閉的。我們必須到設定>應用程式與功能的地方把他打開。
到應用程式畫面後,點選管理選用功能
進到裡面之後,確認清單中有沒有 OpenSSH 伺服器 並且安裝,如果沒有的話在新增功能 的選項裡安裝。要特別注意是 OpenSSH 伺服器,不是 OpenSSH 用戶端。
接下來用系統管理員身分打開 PowerShell,並輸入以下指令:
1 | Start-Service sshd |
第一行是用來開啟 Server,第二行則是讓系統開機是自動啟用。
接下來要確認防火牆是否有阻擋 SSH 伺服器的運行,我們可以在設定裡找到防火牆,並確認是否有在允許的清單中。如果不是採用 Windows 放火牆的人則要到防火牆的軟體中確認。
基本上到這一步 Windows 的 SSH 伺服器就設定完畢了。
為了讓其他電腦可以辨識這台主機,我們通常都會採用 IP 來定位。但如果今天有在家中安裝 wifi 分享器等東西,電腦接收的 IP 是由路由器配給的。即便我們在 CMD 輸入 ipconfig
得到的 IP 位置也不是真正的 IP 位置,就如同下面的地址一樣。
這有點像你要寄信給公家機關的某個人,填寫的地址一定公家機關地址,而後再由總機轉交給你要寄信的人。遠端連線進來也是要找到路由器所在的位置,再有路由器轉到要連接的電腦主機。
因為我是採用小米路由器,這裡就以小米來介紹。首先用瀏覽器輸入 192.168.31.1 進到小米的操作頁面,並點選「常用設置」的「上網設定」,在上網信息所顯示的就是路由器所在的位置。
這裡可以看到 IP 位置與剛剛電腦顯示的不同,這是別人從外連進來可以辨識的真正 IP 位置。但我們從外面連進來會先進到路由器,我們必須告訴路由器要連線的桌機在哪,所以我們還必須多一層設置。
這裡點選「進階設定」的「通訊埠轉發」,並點選「新增規則」。因為 ssh 預設閘道為 22, 這裡為了方便我們就設定「外部埠」及「內部埠」為22,名稱隨意填,協議選擇 TCP,內部 IP 位置則填入剛剛用 ipconfig
查到的 IP。
新增完成後,別忘了把網頁拉到最底下點選「儲存並生效」。我在這裡就是忘記點選這個,結果搞了幾個小時一直連不進來。
到這一步總算是完成啦!基本上最後是最簡單的部分,也就是用其他電腦連線到你的桌機。要注意的是如果其他電腦是 Windows 10,也是要到設定去安裝 OpenSSH 用戶端。
接下來輸入以下指令
1 | ssh 使用者名稱@實體IP位置 |
使用者名稱為主機的使用者,實體IP位置則是剛剛小米路由器顯示的IP位置,輸入完主機的密碼就大功告成啦!
如果有想要進一步了解如何用手上電腦連接遠端的 Jupyter 伺服器,可以參考我的下面這篇文章。
祝大家連線愉快~
因為我是從 R 跳到 Python 的使用者,R 搭配 RStudio 有很多好用的資料處理操作,其中之一就是可以直接視覺化瀏覽 dataframe,操作 filter 等。其實 Python 也是可以做到這一點的,這就是本篇要介紹的主角——**Qgrid**。
Qgrid 是一個可以用互動的方式來操作 Dataframe 的一個套件。目前主要優點有以下二點:
有興趣的話讓我們開始吧!
基本上依據官方文檔就可以順利安裝了,基本上不外乎就是 pip 及 conda 兩種管道。
1 | # pip 安裝方式 |
官網還有提及需安裝 Jupyterlab ,我試過使用 Jupyter notebook 也是可以運行的,這裡是否要安裝 Jupyterlab 就依自己習慣即可。
一開始,我們先建立所需要的 Dataframe 資料來做測試,為了之後測試效能,我們把數量調大一點。
1 | # 載入所需套件 |
可以看到基本上在 Jupyter notebook,只要不特地去設定 pandas 的 display 值,過多的資料都會用 … 帶過。接下來我們想用 Qgrid 來接管這個 Datafram,我們只要下以下語法就好。
1 | qgrid_widget = qgrid.show_grid(df, show_toolbar=True) |
我們可以直接在 Dataframe 上排序、篩選(數值、類別、布林)資料。
甚至可以直接更改 Dataframe 的值。
除此之外,瀏覽資料的速度也是相當迅速的。
如果想要擷取這些更動過的資料,可以下以下指令。
1 | qgrid_widget.get_changed_df() |
擷取的資料保留了排序、篩選及我們更動後的資料,這對想直接動手調整資料的人來說相當方便。
Qgrid 還在不斷的更新及維護,舉例來說,最近更新的版本,可以在修改資料後直接反應結果在圖形上。官方舉例的文件中用數據畫了迴歸線及資料點的圖形,在更改 Qgrid 的資料時,圖形會即時更動。其實語法也不會很難,但因我們在這討論此套件如何幫助我們在 EDA 流程中更好掌握資料,有興趣的人就自己再看一下官方文檔吧!
jieba
這類型的套件,因他已收集足夠豐富的字詞,所以斷字基本上不會有太大誤差。儘管是一些比較特殊的情境,像假如你要分析哈利波特小說的文字內容,或是魔戒的影評分析,網路上大多都有相對應的字典供你載入。但若你想分析的文章你苦苦找尋就是沒有找著字典,又或者是你想分析公司資料但特有的專業術語太多,這時候就得嘗試自己創造一個專有的字典。你當然可以選擇將專有名詞一個一個填上去,但身為一個數據分析師,當然應該嘗試讓程式來幫我們完成囉!我參考了陳嘉葳寫的推廣PPT提供的方法,依步驟來動手完成這一隻程式。
我拿之前寫的一篇文章淺談文字探勘 - 轉換非結構化文字來做原始資料,當中包含文字探勘的一些專有名詞。同樣方法可應用在其他的文章或評論中,只是怕牽涉版權只好先拿自己的文章來玩玩。
操作的環境如下所示:
- 作業系統:macOS High Sierra
- 程式語言:R version 3.4.3
- IDE:RStudio version 1.1.414
過程中所需用到的套件如下:
1 | library(tidyverse) |
因為需要做文字上的處理,stringr
可以幫助我們在文字上做任意的轉換。tm
主要是用來做文字探勘的一個套件,但他對中文的支援度很差,一般來說建議使用tidytext
。不過在清理資料中的標點符號或是英文數字等,它有不錯的函數可以用,因此這裡也把它載入。RWeka
是用來個別斷詞用的,下方會說明它的用途。slam
則是因為文字的運算量相當龐大,過程中我們會轉換成matrix來做運算,這是可以使用這個套件會比較方便。snowfall
也是運算龐大的原因,因此用平行運算的方式來加速結果產出。
這裡我直接將文章的內容儲存成.txt檔,以方便直接讀取。現在的目的是製作專屬字典,因此標點符號及數字不是我們需考量的東西(當然如果需要也可以將它們納進來),在資料處理時就將它們移除。
1 | dat <- read_lines("./創建字典.txt") |
在將它們處理成一整個文字塊後,我們要將它們分開斷詞,期望處理成以下的形式:
“我們在分” “們在分析” “在分析時” “分析時都” “析時都習” “時都習慣” …
“我們在” “們在分” “在分析” “分析時” “析時都” “時都習” “都習慣” …
“我們” “們在” “在分” “分析” “析時” “時都” “都習” “習慣” …
這樣就可以將有可能形成字詞的字斷開,再用一些方法將專有的字詞跳出來。這裡要注意的是,NGramTokenizer
這個函式原來是為了拉丁語句做個別斷詞使用的,所以他對每一個字的斷詞判斷是以空格為主,因此我們用來處理中文時,就先將字與字之間加入空格,等斷完字再將空格移除。作法如下:
1 | full <- dat %>% str_split_fixed("", n = Inf) %>% |
接下來我們將出現較少的字詞移除,一方面是字詞太少時我們很難判斷它是否為專有名詞,另一方面是我們需將無關緊要的雜訊給移除。至於移除的標準就見仁見智,也有人不是用次數而是用比例當門檻。
1 | seg_base <- full %>% filter(n >= 2) # 將出現次數較少的詞彙濾除 |
對於專有名詞來說,我們當然希望它能被完整的切出來,但跟專有名詞高關聯度的字詞很常會一起被切出來。像下面這樣:
“做文字探勘” “在文字探勘”
”文字探勘時“ “文字探勘的”
我們要如何將「做」、「在」、「時」、「的」等字眼去除呢?這裡可以用一些機率論的原理,舉例來說,「文字探勘」跟「做」應該是要被切分開的詞,那麼與就應該彼此獨立,也就是說,
我們做個簡單的換位,
當近似於1時,代表「做文字探勘」不是一個獨立詞彙,那麼它就極有可能非專有名詞。仔細觀察可以發現,越大作為一個字詞的可能性就越高,因此我們可以設置一個門檻,來濾除非我們想要的字詞。
因為母體的詞彙總數是相同的,所以這裡可以用次數來取代比率,會比較好計算。另外我們這裡只擷取四個字內的專有名詞,但切割需切割前後各一個字詞,這也是上面NGramTokenizer
取5個字的原因。
1 | ## 計算字詞的獨立關係 |
除了多餘的字詞來添亂,因為我們是個別斷詞的關係,零碎的字也會被抓進來。舉例來說:「文字探勘」會被斷出來,但是「文字探」或「字探勘」這類型的字一樣也會被抓出。為了解決這樣的問題,就引入了訊息量—「熵」這個概念。一個專有名詞周遭通常伴隨較豐富的訊息量,但像「文字探」後面通常只會加「勘」,所以訊息量就很低。所以只要抓住這個概念,只取兩側有豐富訊息量的字詞,就很有可能為專屬名詞。
1 | freq_all <- matrix(full$proportion, dimnames = list(full$name)) |
廢話不多說,先來看看這樣產出後的結果:
1 | 一向量 |
可以看到雖然分出的字詞有些道理,但卻不那麼漂亮。這個原因在所準備的文字資料不夠大量所致,像「個詞彙在」這個詞,隨著「個」及「在」的比例增加,這個詞在獨立字詞階段就會被去除。所幸的是我們還是抓出了想要做成字典的字詞:
1 | 向量指標 |
我們在分析時,都習慣將資料整理成Tidy Data,以便我們建模來解釋或是預測所關注的事情。但在整理成Tidy Data之前,這些資料基本上都已經是結構化的。所以當我們試圖去分析文字、影像這種非結構化資料時,第一個會碰到的困難點就是如何將這些資料變成我們熟悉的結構化數據。凡通則必有例外,尤其是牽涉到人的時候,那大概只有例外這件事是通則了。與人貼身相關的語言就更不用說了,要能從千變萬化的語言中提煉出「結構化」的型態本身就不是一件容易的事。尤其《Ethnologue: Languages of the World》指稱語言在這世界上起碼有7,102種,即便是同一種語言隨時間、地點的不同也都可能各自發展,就像「擦子」跟「擦布」之類的。
那麼在提取結構化的文字資料之前,可能得先問問文字探勘想要做到的事情是什麼?最基本的不外乎就是從一段文字或一篇文章中,找到他真正想要表達的事情。我們可以看看下面的句子:
我想在這裡表達的是,我過去學了很多東西,但我現在想要分享…欸…等等,我想要分享什麼?喔!對了!是那個嘛!就是文字探勘真的很有意思哦!你可以去學一下!
先不要火大XD,我知道這句話很多贅字,讓人摸不著你到底想幹嘛。但這句話最重要的其實只有一句話,就是「文字探勘很有意思」。也就是說,只要我們能將這句話具代表性的詞彙標記出來,我們就能大略知道這句話到底想表達的意思是什麼。(雖然也是有很多話不明所以啦!)
那麼首先在文字探勘中,最首要的就是把字切出來。上面那段話我們可以切成下面這個樣子:
“我” “想” “在” “這裡” “表達” “的” “是” “我” “過去” “學” “了” “很多” “東西” “但” “我” “現在” “想” “要” “分享” “欸” “等等” “我” “想” “要” “分享” “什麼” “喔” “對了” “是” “那個” “嘛” “就” “是” “文字探勘” “真的” “很” “有意思” “哦” “你” “可以” “去” “學” “一下”
將這些字按照出現次數排列之後,就可以形成以下表格:
詞彙 | 次數 |
---|---|
想 | 3 |
分享 | 2 |
學 | 2 |
一下 | 1 |
什麼 | 1 |
文字 | 1 |
去 | 1 |
有意思 | 1 |
那個 | 1 |
東西 | 1 |
表達 | 1 |
很 | 1 |
很多 | 1 |
真的 | 1 |
文字探勘 | 1 |
欸 | 1 |
現在 | 1 |
這裡 | 1 |
喔 | 1 |
過去 | 1 |
對了 | 1 |
依「想」跟「分享」出現的頻率,就可以知道這段話到底有多想分享東西給別人了。不過仔細看會發現,我們剛剛認為最重要的「文字探勘」卻被排在相當後面。這段話若單純用次數來挖掘意義,就只會知道他想分享東西而已。所以關鍵在於,次數雖可以挖掘出一定的代表性,卻不見得能表達出整句話真正的含意。因此,另一個用來取代次數的方法就出來了,它也是現在搜尋引擎相當常用的方法 - TF-IDF(Term Frequency - Inverse Document Frequency)
在剛剛的例子中,我們知道「文字探勘」會是這句話的主軸,而不是「對了」、「欸」之類比較沒有意義的字。主要在於「文字探勘」在其他的文章或談話內容中,沒有「對了」或是「欸」這麼頻繁地出現。也因此一旦出現,就很容易成為那句話的焦點。所以我們要將「文字探勘」的重要性標記出來,單看這句話是不夠的。必須跟其他話語或文字做比較,才能知道「文字探勘」真的比較少出現。
假設我們比對了許多文章,將出現次數整理出以下表格:
詞彙 | 文章一 | 文章二 | 文章三 | … |
---|---|---|---|---|
對了 | 13 | 17 | 12 | … |
真的 | 14 | 17 | 13 | … |
文字探勘 | 7 | 0 | 0 | … |
不難發現雖然「文字探勘」在文章一中出現的次數不及其他詞彙,但它跟其他文章比卻具有它的代表性在。順帶一提,這個表格所呈現的形式也就是文字探勘中常會用到的表格型態Term-Document Matrix(TDM) 或是 它的轉置矩陣 Document-Term Matrix(DTM)。
因此,在分析文件時,假設文件當成一向量,而其可能出現的詞彙共有N種,則向量就可以表示成 ,其中 就是個詞彙在這篇文章 的權重。不過這 應該要是什麼東西呢?有些人會用這個詞彙在文章中出現的次數作為權重,這種做法很直觀,但卻有一點不妥,如同剛剛所說,在這裡就不多做解釋。因此,TF-IDF考量詞彙在該文章中出現的頻率TF(Term Frequency),以及詞彙在其他文章中出現的頻率IDF(Inverse Document Frequency)兩部分,TF代表 在 出現的頻率,可以表示成下列式子:
如同我們之前所說,代表詞在文件出現的次數,而上述公式則代表詞組跟其他詞組相比後的佔比是多少。而這僅能顯示他在文章中出現的次數,無法顯示這個詞組僅在這篇出現的獨特性。因此,引入了IDF來做考量的因子:
這個因子是考量總文件數除以出現過詞的文件數所得的商,再取對數。將文件的獨特性考量進來。而同時考量TF-IDF的指標,就出來了:
有了這個指標後,我們就可以利用文字雲的方式,將重要的詞彙呈現出來,也更能顯露出文章的特性。另一方面,在應用上的層面也較為廣泛。像現在的搜尋引擎很多都採用此種權重計算方式。
除了找出文章主要闡述的內容外,我們想找有相似主題的文章也可以利用此種方式。我們都學過線性代數,當中的內積很適合拿來作為相似與否的媒介。假設我們把目標文章的詞彙完整轉換成一個向量代表是 ,比對的文章則是 ,則可以透過角度大小來判斷兩向量是否接近,因 ,當 越接近1,則代表兩文章越相近。而這種評估方式也被稱作為Cosine Similarity。
此種方式也很適合應用在搜尋引擎上,當我們使用倒排索引來紀錄資料,可以輕鬆計算出每份文件中每個詞的TF-IDF指標,作為該文件的向量指標。便可進一步用來評估文件,並將搜尋的關鍵字作為一向量指標來判定最高相關的文章。
除此之外,TF-IDF還可以應用在關鍵字的判定上,只要將該文章TF-IDF最高的前幾名挑出來,就可以自動生成文章的關鍵字詞組。不僅如此,還可應用在文章摘要,將文章內包含重要字詞的句子給挑出來。
其實除了單純的意義提取外,這些結果都可以當作建立預測或解釋型模型很好的養分。譬如藉由爬蟲抓取PTT的資料,就可以關聯習慣逛那些文章類型的人會喜歡看什麼電影,藉此應用在電影推薦預測上。又或者是從文章找出企業跟時事的關聯,以作為投資股票的評估等等。在這個資訊爆炸的年代,已經不是只有人家準備好的結構化資料能夠拿來建模了,越孰悉將非結構化資料轉換成有用的資訊,就越能在這變動快速的時代抓緊先機。一起加油吧!
由於Hexo本身沒有支援數學公式的顯示,因此即便在Markdown文件輸入數學公式,他也僅會顯示成$\cos\theta = \frac{p\cdot q}{\|p\|\|q\|}$
這樣的形式,為了讓Hexo能正確顯示我們想呈現的數學字串,我們需加入mathJax
的插件。
mathJax
安裝mathJax
的方式相當簡單,只需在terminal中輸入下面指令:
1 | npm install hexo-math --save |
接下來在Hexo中的_config.yml
裡新增以下指令:
1 | math: |
並在主題的_config.yml
下開啟mathJax
的功能:
1 | # MathJax Support |
由於每次讀取文章時皆會載入這個套件,過多數學公式有可能造成文章讀取速度變慢。因此若想要在特定文章開啟這個功能,可以做一些調配。由於我使用的是Next的主題,這裡以Next的方式做說明。首先先確認themes/next/layout/_layout.swig
有沒有將mathjsx.swig
引入:
1 | {% include '_third-party/mathjax.swig' %} |
若沒有請自行將上述指令放入文件中。之後找到themes/next/layout/_third-party/mathjax.swig
,裡面的原本內容如下:
1 | {% if theme.mathjax.enable %} |
將之改為:
1 | {% if page.mathjax %} |
基本上這樣就大功告成了,之後在寫文章時,只要在front-matter中掛載插件mathjax: true
即可:
1 | --- |
然而,只是安裝插件有些數學符號可能無法正確顯示,主要原因是跟Hexo的渲染引擎有關係。拿下標舉例,在Latex用下標是使用下底線_
,但Markdown的斜體或粗體也是用下底線_斜體_
,當數學式出現很多下標時,Hexo渲染就會把它優先轉成html形式。為了解決這個問題,我們須對Hexo的渲染做一些處理。
網路上有許多資料及做法,因原Hexo渲染插件為hexo-renderer-marked
,有些方式會將此插件改為其他支援mathJax
的插件,但許多做法我使用後沒有效果,不然就是要改變書寫markdown的方式,所以最後還是決定修改渲染引擎的js
腳本。不過此方法也有它的缺點,其一是換一次電腦就必須變更一次設定,其二是原來使用底線做斜體的配置會失效,不過對於第二點可以使用*取代,倒也不是太大的問題。
更改方式不是太困難,找到node_modules\marked\lib\marked.js
,將裡面的第451行:
1 | escape: /^\\([\\`*{}\[\]()# +\-.!_>])/, |
更改為:
1 | escape: /^\\([`*\[\]()# +\-.!_>])/, |
此步驟將原本\\
轉換為\
的轉譯取消。再找到459行:
1 | em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, |
更改為:
1 | em:/^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, |
這樣就把底線斜體取消了。到此應該就能完整呈現數學公式囉!好好享受吧!
還記得最後寫網誌已經是大學的事了,那時候還是無名小站稱霸的年代。對於這麼久沒接觸Blog的我來說,需求也已經跟以往相差甚遠了。因為主要是記錄我的學習心得,所以一定要包含:
在尋尋覓覓後,我總算找到了Hexo這個平台。在很多的Blog中都可以發現它的身影,厲害的是它的作者Tommy Chen竟然是台灣人,大家可以去看看他一手打造的心路歷程。
由於我僅對R及Matlab的語法較熟悉,大學雖然有修過一點系統程式,但也年代久遠,所以在打造這個Blog花了我不少時間。我會將我架設的過程盡可能完整的寫出來,方便讓跟我一樣沒什麼基礎的人可以直接上手!
如果你之前沒有碰過一些程式語言,在使用Hexo當Blog前就必須先有心理準備,會一定程度上接觸程式語法。不過倒也是不用太擔心,一方面是佈置的語法不難;另一方面是等架設好之後,除非想大改整個佈置,不然不用在碰太難的程式語言。首先,此Blog是架設在以下環境:
- 作業系統:macOS High Sierra
- 編譯器:VScode
- Markdown編輯器:Typora
雖然是用mac來操作,但在Windows上差異也不會太大。如沒有以上軟體,現在可以安裝,因為我們會使用這些軟體逐步實現我們的Blog,當然如果你有比較習慣的編譯器或Markdown編輯器,也可以使用自己上手的。
VScode會是後續整理Hexo的一個主要平台,它本身是一個編譯器,被定義在「Editor以上,IDE未滿」。也因此想要什麼功能都可以彈性調整,譬如說程式碼的高亮或是專案功能等等。詳細的VScode部署網上已有很多教學與討論,這裡就只說Hexo會用到的功能。先按 ^`
開啟Terminal,mac本身的shell使用的是bash,但他沒有npm的指令,因此我們換個shell先,改成zsh及他的架構oh-my-zsh。
為了方便安裝,那就一定得要把Homebrew掛上。
1 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" |
安裝zsh及補全功能。
1 | brew install zsh zsh-completions |
將shell從bash轉換為zsh。
1 | chsh -s /bin/zsh |
重新啟動後,就可以開始著手安裝Hexo了。如果你想讓Terminal的介面好看一點,可以參考此網誌的設置,內容相當詳盡。
查看官網就可以知道其實安裝過程相當簡單,只不過在安裝前必須先把Git及Node.js裝上。好在剛剛裝上了zsh及Homebrew,現在就非常方便了。
用Terminal來安裝Git,輸入:
1 | brew install git |
再來用wget安裝Node.js,先輸入:
1 | wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash |
執行完後重新啟動Terminal,先安裝nvm,並輸入以下指令安裝Node.js:
1 | brew install nvm |
接下來就可以安裝Hexo囉!只要輸入以下指令即可安裝:
1 | npm install -g hexo-cli |
在安裝完Hexo之後,緊接著就是來建構個人的Blog了,這裡我們就將要管理的目錄取名叫Blog
,而建構的方式只要輸入下列指令即可完成:
1 | hexo init Blog |
建好的Blog資料夾中可以發現以下的資料夾
其中幾個比較主要的文件可以留意一下:
_config.yml
:Blog的主要設定文件,會經常使用到。themes
:不想使用預設的畫面,想用別人設計好的主題就可以放裡面。source
:網頁的內容像是文章,或是標籤、分類等都在這。到這一步其實就已經建構好了你的Blog,迫不及待想看看初步雛形可以輸入以下指令:
1 | hexo server |
他會出現下列指令
1 | INFO Start processing |
點選連結後就可以看到你的Blog了。這個指令在之後把網頁部署到Github後相當有用,如果網頁有新文章,或是有做一些套件的增加之類的修改時,可以先用這個指令看看成果,再決定要不要上傳。
如果你跟我一樣美術天份很差,或是懶得一步一步去修改Blog的介面,可以使用別人已經架好的樣板。官網就有許多主題可以讓你選擇。我使用的是NexT的主題,就先以這個主題來說明。點選名字後可以進入該主題的Github,基本上下面都會有說明文件。以NexT來說,安裝只要輸入以下指令:
1 | mkdir themes/next |
你可以在themes
裡發現多了next
主題的資料夾,這代表NexT已經安裝好了。接下來就是把Blog的主題切換成NexT就好了。找到Blog
下的_config.yml
去修改以下內容:
1 | # Extensions |
這是應該就可以看到你的Blog已經換成不同的樣子了。如果想要變換更多設定,譬如說讓目錄列表從左邊改到右邊這種,可以看該主題在Github提供的說明文件,會有更詳盡的說明。
這篇文章標題底下可以看到有多少人看過這篇文章,另外在頁面最下方也可以看到訪問這個Blog的人次。而要加入這樣的計算功能,有兩種管道,一是不蒜子,但不蒜子的文章統計要點進文章裡才看得到,如果想在首頁就顯示文章閱讀量,則可以選擇LeanCloud。
不蒜子的安裝方式相當簡單,指令都是放入themes/你的主题/layout/_partial/footer.ejs
的文件中。因為需要安裝腳本,把以下指令放在文件的最前端。
1 | <script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"> |
其中src
後面的網址是不蒜子提供的,如你擔心這個腳本失效,也是可以將腳本載下來放在自己的雲端,將路徑改向自己的空間即可。
站點訪問量分成兩種,一種是站點訪問次數(pv),如果同一人逛了五次網站,記數會顯示5;另一種則是站點訪客數(uv),上述情形記數只會顯示1。看個人喜好去做選擇。將以下指令放入文件內,自己在調整位置即可。
pv
1 | <span id="busuanzi_container_site_pv"> |
uv
1 | <span id="busuanzi_container_site_uv"> |
由於我希望首頁即可看到文章瀏覽量,所以文章的記數我是採用LeanCloud來做。只不過這種方法相對於不蒜子來說就稍稍麻煩一些。先到官網註冊帳號,完成後點擊畫面中的創建應用。
在新應用名稱輸入你想要的名字,完成創建,之後點擊進入設定。在存儲中創建Class,名稱務必輸入Counter
,並在設置數據條目的默認中選擇無限制。
現在點選左邊的設置,進入「應用Key」應該可以看到「App ID」及「App Key」,這時可以填入Blog中themes
下的_config.yml
文件裡了。
1 | # Show number of visitors to each article. |
這樣基本上計數就大功告成了,如果擔心計數被亂用,也可以到LeanCloud設置的安全中心加入自己的網域。
評論系統的管道有很多,我自己是使用disqus。在註冊之後,點選首頁的Get Started,再選擇下面的「I want to install Disqus on my site」,之後照著程序走即可,唯一要注意的是Website Name所設定的名稱,會配置在主題文件下的_config.yml
中,完整修改如下。
1 | # Disqus |
值得注意的是,如果你有開啟Tag跟Categories的配置,那麼評論系統在這些畫面的預設是開啟的。如果不想要評論出現在這些頁面中,可以到Hexo中的source/tag/index.md
修改成以下指令。(categories
也是一樣做法)
1 | --- |
費了一番苦心,總算把Blog整理成想要的樣子了吧!剩下最後一哩路,就是把你的Blog丟到網路上讓大家瞧瞧吧!
首先到Github去創建一個repository,並且將它取名為你的帳號.github.io
,進入這個repository的設定,找到GitHub Pages,選擇master branch,然後儲存。
回到主目錄,點選Hexo主目錄的_config.yml
中,指令如下。
1 | # Deployment |
之後只要在Terminal輸入:
1 | hexo d -g |
就可以在 https://你的帳號.github.io/ 看到你的Blog囉!
為了不要每次部署都要輸入密碼,可以藉由創建SSH Key來達成。首先在Terminal輸入以下指令:
1 | ssh-keygen -t rsa -C "your_email@example.com" |
他會出現
1 | Generating public/private rsa key pair. |
按下return後出現
1 | Enter passphrase (empty for no passphrase): |
接連按下return跳過passphrase的輸入,而後輸入一下指令將SSH密鑰複製起來。
1 | pbcopy < ~/.ssh/id_rsa.pub |
到Github的Settings中,
點選SSH and GPG keys,點擊NewSSH Key,Title可以隨便輸入,只要將剛剛複製的東西貼到key裡面就好。為了測試有沒有成功,到Terminal輸入:
1 | ssh -T git@github.com |
他會出現
1 | The authenticity of host 'github.com (207.97.227.239)' can't be established. |
輸入yes
後,如果看到以下畫面,就代表你成功了!
1 | Hi username! You've successfully authenticated, but GitHub does not |
接下來就輸入下列指令來設定帳號資訊吧。
1 | git config --global user.name "yourusername" # 你的帳號 |
基本上到這裡就可以開始你的記錄旅程囉!好好享受吧!
一直以來都有在做筆記的習慣,只是以往都是放在OneNote, Evernote之類的比較多。隨著吸收得知識越來越多,漸漸了解到有一個自己的Blog來經營還是重要的。倒也不是要給誰看,就算是我自己的一個成長歷程吧!
我是數學背景出身,碩士轉工業工程,主要研究隨機最佳化的領域。也因為這樣的經歷,讓我對*用數學的方法解決現實問題* 很感興趣,順理成章就往數據分析師這條路走下去。只是這條路畢竟是新的領域,不足的地方還太多,雖然一路上程式的底子還過得去,但畢竟不是資工背景。機器學習、深度學習就是要讓機器去挖掘一些我們尚不可見的資訊,那麼不懂機器又怎麼可以呢?
所以這個部落格的走向也就很明顯了,它不會只是我用來記錄數據分析領域的地方,當然還囊括了程式語言、網頁設計甚至是讀書心得等等的內容。就讓我們拭目以待吧!
]]>