LoRA 小複習

LoRA = Low Rank Adaptation, 先前也提到過這個 fine tuning 的技術. 本來想好好複習一下, 但這次看的 IBM 課程真的速度太快了. 為了確定我在幹什麼? 我決定來整理一遍今天 Lab 的成果.

本課程使用 AG News (新聞) dataset 訓練出來的 model, 希望把它調整為適用於 IMDB (影評) dataset. 兩個 dataset 的性質不同, label 也不同. 前者的 label 是把所有的新聞分為四個類別:世界新聞(World News)、體育新聞(Sports News)、商業新聞(Business News)和科技新聞(Technology News)。後者是把所有的影評分為正面情緒和負面情緒兩類.

現在我們要把 model 從適用於 AG News 分類, 改為適用 IMDB 分類, 並且儘量借用已經訓練好的文字理解能力. 當然原本的四類輸出就不能用了, 我們要攔胡中間的成果, 將它重新分為兩類.

首先課程會安裝很多 lib, 這點做得比 Google 課程好. Google cloud platform (GCP) 不是為教學而生的, 自己猛進版, 然後課程的版本會慢慢變得跟 GCP 不相容, 解答版都有 pip error, 做個 Lab 還要解決相容性問題. IBM 這邊沒有這個狀況. 只是課講得超快, 我都要放 0.75 倍才聽得懂, 而投影片則是會一閃即逝, 沒學會速讀都不知道看到啥了.

總之, 基本的東西都安裝定義好之後, 下載 IMDB dataset, 分為 train 和 test 兩部分, 前者隨機抓 95%, 後者抓剩下5%.

接下來定義一個 IMDB 適用的 mode, 將詞彙表中最多 400,000 個詞彙轉換成 100 維的向量表示(Embedding), 接著經過 2 個全連接層 (FC1, FC2) 和中間夾的 RELU. 如果沒有非線性層, 根據線性代數, 不管幾層都可以等效為一層. 所以這邊每層的 node 為 100 –> 128 –> 2.

我們把這上面這個簡單的 model 拿來訓練 IMDB dataset. 為節省時間, Lab 只跑 2 個 epoch. 不過可以看到跑 300 epoch 的結果, 正確率 66.176%. 其實這個部分跟 Lora 還無關.

接下來開始做 AG News re-trained model, 它的模型只有輸出是 4 個 classes. 其他和前面一樣.

為了準備後續的 LoRA, 先把這個 mode 叫做 model_lora, 且設定為 gradient 不更新, 也就是 parameter freeze. 包括整個 neural network 和 embedding layer.

把 LoRA 放進 AG News model 的版本, 首先在 FC1 後面加上 LoRALayer(), 很顯然地它的輸入是 128 個 node, 輸出也是 128. 還有呢, 輸出的地方也配合 IMDB 改成 2 個 classes 了.

然後我們用 IMDB dataset 訓練這個 model_RoLA, 因為前面的 parameter 都 freeze, 所以真正訓練的是 LoRALayer() 到 FC2這一段. 訓練完 300 個 epoch, 正確率來到 69.156%. 也就是說藉由 AG News pre-trained model 的加持, 在其他條件不變的狀況下, 效果提升 3%. 而且沒有動到 AG News model FC1 的參數.

故事還沒有結束. LoRALayer 是一個低階數的連接層. 它相對於原來的網路是多出來的. 數學上理解為參數的調整. 原本的 h(x) = Wox. 其中 W 是參數矩陣, x 是輸入向量, h 是輸出向量.

有了 LoRA 之後, h(x) 當然還是 Wox 的函數, 而且這部分不能動. 能動的部分就是多了 ΔWx. h(x) = Wox + ΔWx.

而 ΔWx 又表現為 B 和 A 兩個低 rank 的矩陣. 甚至是 d * r 乘 r*k 中的 r = 1這麼 “low", 只要乘出來是 dxk, 就可以和 Wo 相加了. 當然 r 低得太誇張效果可能不好.

對於訓練過的新 model, 我們只要存 B 和 A, 其它可以套用既有的 pre-trained model. 上面的例子中因為 FC2 也變了, 所以就不能只存 B 和 A.

walk through 範例之後, 完整的課程還要做一個 excise, 這樣總共只給一小時, 真的太看得起我了. 不過其實 IBM 的課很佛心, 只要繼續用, 它就不會把你踢掉.

反觀 Google 的課程都有倒數計時, 明明時間都是被它自己雲端吃掉的, 只要時間一到就關門了. 想要拿到 credit, 只能靠瘋狂執行 “run all cell" 來搶時間, 其中需要手動改的 code, 更新 tool 版本的 script 都先存好, 趁前面還在安裝中, 後面找 cell 貼 patch, 才能避免一再"重修"~~還好噩夢已經過去了.

股東大會投票小筆記

今天收到殼牌股東大會的電子投票通知, 好奇點進去看一下, 發現幾個有趣的表決項目. 雖然我股份低微, 但是能在這幾個議題表示意見也是滿民主的. 每個項目可以選 for, against, 或是 withdraw, 三個可以選, 並且有預設選項和董事會建議選項.

下面幾個項目比較新奇, 和印象中台灣股東會的做法不同. 喔! 原來這個也可以表決. 所以記錄下來.

Remuneration of Auditors(審計師酬金)- 指的是公司支付給審計師(Auditors)的報酬或費用,以作為其提供審計服務或其他相關服務的酬勞。

Authority to allot shares (發行新股) – 指公司股東授予董事會的權力,使董事會能夠在特定條件下發行新的股份,並將這些股份分配給投資者或其他股東。此授權通常需透過公司股東大會(Annual General Meeting, AGM 或 Extraordinary General Meeting, EGM)批准。

Disapplication of pre-emption rights(排除優先認購權)- 搭配前者, 如果公司發行新股, 某些人可以優先認購. Disapplication 就是不同意有優先認購權.

Authority to make on/off-market purchases of own shares – 授權同意在市場上或是場外議價買自家股票. 這是分開的兩個選項, 如果 on-market 買可能會造成股價波動.

Authority to make certain donations/incur expenditure – 石油公司可能要發些補償金, 和解費之類的.

shareholder resolution – 這邊是指股東提案. 我看到 resolution 就想到 4K/2K, 這算職業病. 這邊 resolution 是對應到字典上的 “the action of solving a problem or contentious matter".

在發行新股方面, 台灣通常都是董事會已經搞定一切了, 然後才在年度股東大會表決追認. 提案幾乎也不太可能被否決掉. 但殼牌這個做法好像比較正常, 事先拿到股東的同意, 有了授權, 然後在任期中就能自由發揮.

ONNX 的 version 小筆記

ONNX (Open Neural Network Exchange) 是一種 AI 檔案交換標準 最近在 porting DeepSeek 到 NPU 的過程中, 卡住了一下, 所以特別記錄下來.

首先 ONNX 有兩個主要的 domain: ai.onnx 和 ai.onnx.ml. 雖然他們都是為 AI 而生, 但ai.onnx domain 的 operator (=運算子, 算子, 運算符) 更加常用, 像是加減、乘加這種很基本的運算, 或是比較複雜的 convolution, activation function, 以及 DNN 有關的 RNN, dropput…等等.

ai.onnx.ml 的算子適用於傳統的機器學習, 包括非深度學習的算法, 像是 one hot encoder, 還有 SVM, imputer…等等. 至於其他的 domain, 屬於客製化擴展, 官方不維護 [1]. 包括: ai.onnx.training, com.microsoft, com.ms.internal….等等.

接下來也很妙. ONNX 進版後, 雖然看起來也是以 1.0, 1.1,….這樣長大, 但是官方又出了個 IR version, 用來表示是否有 breaking change. 所以顯然很多版 version 會對應到同一版 IR version.

“The IR format is versioned using simple numbers, which MUST be monotonically increasing. Breaking changes to the format or semantics of the ONNX specification require an increment of the version. Non-breaking changes to the IR format do not require changing the version number."

光是這樣也就罷了, 由於還有 ai.onnx 和 ai.onnx.ml 兩個 domain 之分. 如果對某個 domain 是 breaking change, 但對另外一個 domain 不是呢? 所以對於不同的 domain, 又有 Opset 的 version.

“ONNX uses operator sets to group together immutable operator specifications. An operator set represents a specific version of a domain, indicated by a pair (domain, version). This represents the set of all operators belonging to the specified domain with the specified version (referred to as the opset_version). When the inventory of a given operator set changes either by addition, removal, or a change in semantics of a contained operator, its version MUST increase."

因此我們可以得到這樣的表格 – Released Versions

ONNX versionIR versionOpset version ai.onnxOpset version ai.onnx.mlOpset version ai.onnx.training
1.0311
1.1351
1.1.2361
1.2371
1.3381
1.4.1491
中間省略, 詳情參考 [1]
1.14.191931
1.15.092041
1.16.0102151
1.17.0102251

當我試圖將 ONNX INT4 的 DeepSeek 翻成 NPU 指令時, converter 跑出下面這個 error, 表示在 domain version 14 當中找不到 SimplifiedLayerNormalization 這個算子. 而我們安裝的 ONNX 通常是 ai.onnx domain. 既然在預設的 domain 裡面找不到, 我們就要檢查一下從 huggingface 抓來的 DeepSeek 是哪個 domain 的 ONNX?

File "/local/workspace/hailo_virtualenv/lib/python3.10/site-packages/onnx/checker.py", line 163, in check_model
    C.check_model_path(
onnx.onnx_cpp2py_export.checker.ValidationError: No Op registered for SimplifiedLayerNormalization with domain_version of 14
 
==> Context: Bad node spec for node. Name: /model/layers.0/input_layernorm/LayerNorm OpType: SimplifiedLayerNormalization

寫個小程式檢查 node 的 domain:

import onnx
model = onnx.load("model.onnx")

# 查看所有算子的 domain
for node in model.graph.node:
    print(f"Op: {node.op_type}, Domain: {node.domain}")

結果悲劇了… 大部分都是 com.microsoft domain, 難怪會出錯. 經過測試, 這個 int4 版本除了回答品質不太穩之外, 還會無限迴圈重複自己的話, 所以暫且就不 debug 下去了, 僅記錄下 ONNX version 的複雜性.

[REF]

  1. https://github.com/onnx/onnx/blob/main/docs/Versioning.md

2025 年 Q1 投資回顧

這篇文章是陸續寫成的, 因為 3 月底我打算出國去玩, 屆時人在國外可能就沒辦法好好回顧這一季發生了什麼事? 所以打算一點一點地寫, 最後只要更新數字就好!

這一季股市算是高檔開局, 買點不好拿捏. 但一月就放農曆年假, 如果美股在這段期間有什麼波動, 手上又有年終獎金; 在這有錢有閒的狀態下, 豈不是能趁機撿個便宜! 想想就覺得這時機妙極了!

既然此計大妙, 當然要多備一些子彈. 所以我從近期上漲但我想拋棄的持股中選中了 VIP. 把 VIP 這檔 ETF 賣一賣, 子彈就變得相當充裕! 果不其然, 意外一定有, 只是看哪種? 年假中DeepSeek 橫空出世, 美股跌得咪咪貓貓~~~於是我買進很大一波!

從學理上來看, 訓練單一大語言模型已經遇到瓶頸。誰錢多人傻就第一個去訓練,其他人等著蒸餾你的結果就好。這會導致爭著燒錢的意願低落, 老黃(Jensen )最近都開始強調 AI 們分工合作的重要性, 這樣需求量才能再上看 100 倍. 不過分工合作的 expert, 每個都模型都不用太大. DeepSeek 也間接証明了這點.

接下來美股慢慢回神, 一路持續上漲到 2/20 時, 我創下生涯資產最高的記錄. 這種記錄其實一年會有很多次, 原本不是很稀奇. 不過這次不一樣, 那就是川普總統正好在這天重新回鍋, 讓美國先摔個 @#$%、以便再次偉大! 至於後面的發展大家都知道啦! 後續的一個多月中, 美股大盤上沖下洗, 多次創下單日身價大漲和大大跌的悲悲喜劇.

那這波的子彈呢? 沒關係! 科技股雖然偏跌, 但民生消費股偏漲. 除了 NOBL 逼近歷史高點, 連可口可樂、殼牌石油也都漲到 52 週新高附近. 比起英國品牌的 Shel, 美國品牌的 KO 比較可能受到抵制吧?既然 KO 配息不太給力、成長幅度低於大盤, 所以我選擇把 NOBL 賣光, KO 賣掉一點.

另外, Firstrade 那邊也有小雨滴助陣! 前幾天看到一筆 “Fully Paid Lending Rebate"入賬, 中文也記帳做利息. 其實這個就是 Firstrade 把我的股票拿去借人放空的收益. 因為它不像國內的證券那樣: (1) 把股票轉到別人名下, (2) 載明借多久? (3) 利率多少?反而是突襲式的, 股票也一直都在帳上, 可能是美國有借人當沖的玩法吧? 總之, 我也無法介入, 只能含(淚) 笑收錢~~~

主要的投資大致就這樣規畫了. 接下來就是要照顧老婆的所得. 因為即使我退休了, 但老婆並沒有從 “家管" 職位退休, 所以她的收入不能減少, 甚至還要能逐年上調 3~5% 抗通膨. 基於這個神聖的理由, 這一季我佈局買進一點美國 7~10 年公債 (非 ETF), 增持 PFF. 這樣無論股市漲跌, 我都發得出老婆的薪水, 確保她的所得替代率.

近幾年來, 業外收入雖然都能超過上班收入某個倍數, 但多少有僥倖的成分. 好運並不會年年有, 以 2025 Q1 來說, 有好幾天的資產都比去年年底還縮水! 更何況帳面上的盈餘又不能當飯吃. 因此, 明知增持固定收益, 就會減少整體財產增張的速度, 我認為仍有必要持有債券和特別股這種穩定收息的證券 – 當然配息不會來自本金.

OK! 講了半天, 回到我的成績單。這一季較去年年底虧損 1.53%. 這部分在愚人節中午已經收復, 因為美金匯率漲回了一些. 帳面上看似虛耗了三個月, 但這一季也不是做白工, 我的投資成分股做了很大的調整. 基本上已經符合全市場和固定收益雙軌的佈局。

在全市場方面, 本季增持 QQQ, SPY, 繼續持有0050 和日本 TOPIX. 固定收益方面, 增持 PFF, 美債 7 年和 10 年. 属性中立的可口可樂雖然還沒有出清, 但子彈還夠用, 故維持沒賣的部份. Shel 漲高之後, 殖利率小於 4%, 但還在誤差範圍內, 算是留校察看.

另外, 這一季的MVP和戰犯也都出爐了. 虧最大的就是近年的獲利王QQQ, 這算均值回歸吧. 因為虧的部份不適合畫圓餅圖, 所以這次換個方式.

QQQ 佔整個絕對值總和的 43.63%. 虧第二多的是台灣50, 貢獻 12.52%. 賺最多的是殼牌石油, 佔 27.69%. 賺第二多的是可口可樂, 佔 8.74%. 總之是敗多勝少. 至於已經賣掉的股票, 就沒有追蹤他們的貢獻了, 它們已經功成身退, 變成了QQQ 的樣子.

因為配息股在第一季末也未必會入帳, 所以我的算法是把全年配息除以4來比較. 如果整年配息都算進去, 狀況差不多, 圖示如下.

展望未來的一季, 川普老大的招式應該差不多出盡, 市場應該也都反應完了. 我想未來的能見度會逐漸比較明朗, 波動應該會小一點吧!

優先股小筆記

雖然我買了一些優先股 (特別股) 的 ETF, 不過我並不需要了解優先股約定的細節, 只要知道它不會倒, 會配息就好了. 不過其實裡面還是有一點學問, 值得做個筆記.

  1. Senior/Junior(優先級別)
    • 這是關於清算優先權的順序
    • 決定在公司清算或出售時,誰先獲得資金返還
    • Senior 比 Junior 具有更高的優先權
  2. Participating/Non-participating(參與權)
    • 這是關於額外收益分配的權利
    • 決定在獲得優先清算金額後,是否還可以繼續參與剩餘資產的分配

這兩個觀念可以排列組合出 4 種股權.

  1. Senior Participating(優先參與式)
  2. Senior Non-participating(優先非參與式)
  3. Junior Participating(次級參與式)
  4. Junior Non-participating(次級非參與式)

假如公司遭到清算, 還可以約定優先權倍數 (preference multiple), 例如:1X 表示獲得原始投資金額, 2X 表示獲得兩倍於原始投資金額. 分配的方式當然又和參與式有關:

  1. 參與式(Participating)
    • 先獲得優先權金額
    • 然後按持股比例參與剩餘資產分配
  2. 非參與式(Non-participating)
    • 只能選擇優先權金額
    • 按普通股比例參與分配(取較高者)

舉例說明

假設某投資者投資 $100萬,持有 20% 股份,有 1.5X 優先權:

情境 A:公司以 $300萬賣出

  1. 參與式:
    • 先獲得 $150萬(1.5X 優先權)
    • 剩餘 $150萬 中再獲得 20%($30萬)
    • 總計獲得 $180萬
  2. 非參與式:
    • 可選擇 $150萬(優先權金額)
    • 或 $60萬(20% 的 $300萬)
    • 會選擇較高的 $150萬

當然, 公司沒倒的話. 優先股可以領取約定的利率, 例如 9%. 那就是 100 萬 * 9%, 每年都可以領 9 萬回來. 假設公司虧損, 又可以約定欠到下一次一起發 (累積), 或是跳過不發. 但只要優先股不發錢, 普通股就不能發錢.

另外, 公司也可以選擇召回優先股. 以目前利率走高的狀態, 要借新還舊相當困難就是了.

案例設定

假設 ABC 公司有:

  • 舊優先股:
    • 發行總額:$1000萬
    • 年股息率:8%
    • 每年股息支出:$80萬

再融資方案

ABC 公司決定在當前低利率環境下:

  1. 發行新優先股:
    • 發行總額:$1000萬
    • 年股息率:5%
    • 每年股息支出:$50萬
  2. 用新籌得的資金贖回舊優先股:
    • 可能需支付贖回溢價(例如面值的 102%)
    • 贖回金額:$1020萬(假設 2% 贖回溢價)

財務影響分析

  1. 年度節省
    • 原本股息支出:$80萬/年
    • 新的股息支出:$50萬/年
    • 每年節省:$30萬
  2. 成本考量
    • 贖回溢價:$20萬(一次性支出)
    • 新股發行成本:包括承銷費用、法律費用等
    • 假設總交易成本:$30萬
  3. 投資回收期
    • 總初始成本:$50萬($20萬溢價 + $30萬交易成本)
    • 年度節省:$30萬
    • 回收期:約 1.67 年