Always in the middle of something.

Chasing ideas across ML, AI, and data. Building tools when the rabbit hole gets interesting enough.

JSON Formatter: Format and Debug JSON for APIs and Config Files

If you’ve ever tried reading a minified API response by eye, you know how painful it gets. One long line, no indentation, nested objects buried inside nested objects — it’s basically unreadable. That’s the main reason I built a JSON Formatter tool. Paste your JSON in, hit format, and it immediately becomes something a human can actually parse. This post walks through why JSON formatting matters in practice, and covers a few real scenarios where having a formatter nearby saves time. ...

2026-03-13 · 3 min · 575 words · KbWen

Token 是什麼?LLM 為何只讀 Token?

前言 上篇講到LLM,這片就來說說裡面很常提到的字「Token」。Token 是語言模型可理解的最小單位,它像積木一樣把長句拆成小塊,讓模型逐一處理。這篇文章用更平易近人的方式解釋什麼是 token、為何 LLM 不直接處理完整的字詞,以及常見的斷詞方法,幫助你輕鬆掌握這個看似陌生卻無所不在的概念。 Token 是什麼?為何要用它? LLM 是數學模型,必須把文字轉成向量才能運算。最簡單的做法是把每個單詞賦予一個向量,但這樣會遇到兩個問題: 無法處理新詞或拼錯字:如果訓練時沒有見過某個單字,模型就不知道如何表示它。 忽略語素結構:許多語言中,一個詞可以拆成詞根和詞綴,例如「running」「runner」都來自「run」。 為了兼顧彈性與效率,LLM 會先把輸入拆解成更小的 token。有人將 token 定義是「字、字元或包含標點的組合」。有些文中也強調,token 是模型用來處理文字的原子單位。透過 token,模型得以把複雜的語言拆成固定大小的向量,並對每個 token 指派唯一編號。 幾種常見的斷詞方法 不同 LLM 可能採用不同的分割策略。以下三種是最常見的斷詞方法: 字級(Word):按空格切割。例如 “unbelievable performance” 被當作兩個 token。優點是數量少,但遇到新詞就無法處理。 字元級(Character):每個字母和空白都是一個 token。它能處理任何輸入,但 token 數大幅增加,效率低下。 子詞級(Subword):介於上述兩者之間,把常見詞根或片段視為 token,是現在主流 LLM 的做法。例如 “unbelievable performance” 可以拆成 ["un", "bel", "iev", "able", "per", "form", "ance"]。 圖中展示同一句話經過三種方法切分後的樣子: 把詞拆成小塊,看出不同斷詞方式產生的 token 數量差異。 簡易 Python 範例:手寫子詞切分 以下程式碼示範如何使用簡單的片段詞表(模擬 BPE 結果)把長詞拆成 token。一樣,雖然不是完整的演算法,但能幫你理解 tokenization 的動作。 # 定義一組常見片段 subwords = ["un", "bel", "iev", "able", "per", "form", "ance"] # 簡易子詞切分函式 def tokenize_subwords(text, subwords): tokens = [] i = 0 while i < len(text): match = None for sw in sorted(subwords, key=len, reverse=True): if text[i:].startswith(sw): match = sw break if match: tokens.append(match) i += len(match) else: tokens.append(text[i]) i += 1 return tokens # 輸入與輸出示範 print(tokenize_subwords("unbelievable performance".replace(" ", ""), subwords)) # 可能輸出: ['un', 'bel', 'iev', 'able', 'per', 'form', 'ance'] 每次優先匹配片段詞表中最長的項目,若無匹配則輸出單個字母,呈現出子詞分割的概念。 ...

2025-12-01 · 1 min · 180 words · KbWen

《大語言模型 LLM:其實做的事情比你想像中更單純》

前言 Introduction 如果你最近有用過 ChatGPT、Claude、Gemini,你已經在跟 LLM(Large Language Model)聊天了。這些模型看起來像懂很多、會推理、甚至比朋友還健談,但它們的核心動作其實無比樸實:預測下一個字。 聽起來太簡單?沒錯,但模型規模一大、資料一多、演算法一調整,這個「下一字遊戲」就能演變成看起來像魔法的語言能力。 這篇文章會用工程師看得順、初學者不會暈的方式,把 LLM 的概念、原理與常見應用一次講清楚。 LLM 是什麼? LLM 的任務比你想像的還簡單 從理論上看,LLM 是一種深度學習模型,被訓練去完成一件事情: 在語境下,挑選「最可能出現的下一個 token」。 token 可以是中文字、英文單字的一部分、符號、甚至數字。 當模型知道怎麼選下一個 token,然後不停重複這件事,就能組出一整段看起來像人寫的句子。 為什麼它看起來「懂很多」? 因為它被餵了大量內容:百科、文章、科技文、論壇討論…… 在海量語料裡找模式後,它自然會「講得像很懂」。 我們的感官上就感覺它懂很多、很能理解。 圖 1:LLM 下一字預測核心概念示意圖 LLM 是怎麼「學會」語言的? LLM 的學習流程大致分成四個步驟,其實蠻務實的: 1. 收集大量文本(資料越多,模型越穩) 來源包含書籍、文章、程式碼、論壇、維基百科等。 資料不是越亂越好,但越多越有機會讀懂語言中的隱性規律。 2. 分詞(Tokenization) 模型不直接處理字,而是處理 token。 你可以把它想像成:「把一個蛋糕切成很多比較好吞的碎片」。 3. 預測下一個 token(核心任務) 模型會計算所有候選 token 的機率: 哪個最可能? 哪個跟前文最適合? 哪個不太會讓模型出糗? 機率最高者 → 輸出。 4. 誤差反向調整(Backpropagation) 預測錯了? → 重新調參 → 再預測 → 再調 → 重複幾十億次 這就是 LLM 的訓練人生。 ...

2025-11-23 · 1 min · 179 words · KbWen

推薦系統中的冷啟動問題

什麼是冷啟動? 推薦系統主要是把物品推薦給喜歡的使用者,在使用的環境中,物品和使用者皆會持續的增長變化,也因此會持續面對有新物品和新使用者的情境;有新的物品和使用者使得無法做好的推薦就稱為冷啟動,我們要討論在這種情況下如何做合適的推薦。 基本的冷啟動可以分成三類: 使用者冷啟動:新用戶產生時沒有有任何瀏覽購買紀錄,如何推薦用品的問題。 物品冷啟動:新物品如何推薦給合適的用戶。 系統冷啟動:新系統上線時,物品、使用者、資料皆不足的推薦問題。 圖片來源:https://www.researchgate.net/figure/Illustration-of-Cold-Start-problem-in-recommender-systems-New-user-problem-left-and_fig2_332511384 接下來我們介紹幾個常見的解決方式 排行榜推薦 新物品的推薦 秉持著大家會喜新厭舊的心態,推薦新的物品給使用者;像是電影、影集等等就很適合,有新影片上線時,不管是新或舊的使用者都會想看。 熱門物品的推薦 推薦大家都喜歡的物品給使用者,這是非常常見的做法,簡單有效;也可以用作新演算法的AB test或benchmark,等到資料足夠後才做個性化推薦。 常用物品、必需品推薦 推薦生活必需品、常用物品給新使用者;這種情況適合一些居家用品、常用家電、廚房用具等等的情境。 標籤推薦 這種方式是針對前三項作更細微的推薦,像是ott 串流平台可以直接選擇喜劇、動作片、愛情片等等的推薦;但要注意標籤要先設計完整。 圖片來源:https://deepai.org/publication/addressing-the-cold-start-problem-in-outfit-recommendation-using-visual-preference-modelling 簡易的使用者推薦 簡易的使用者推薦 根據使用者簡易的年齡、性別等等資料分出人群進行推薦。 授權平台推薦 導入第三方社交平台facebook、google等等,根據這些平台的歷史數據進行推薦。 問答題推薦 目前有許多平台都是這種方式,註冊登入時會問幾個問題,了解你的喜好,根據你的反饋來推薦物品。 新物品的相似度推薦 新物品推薦系統在不同的平台有不同的重要程度,在新聞媒體等等時效性強的平台就特別重要,必須要快速的推薦出去,不然時間過了這個資訊、消息也就不重要了。 新物品跟用戶的相似度 計算新物品的特徵(標籤、cos相似度、TF-IDF、影像相似度等等)和使用者的行為特徵進行推薦。 新物品和舊物品的相似度 新物品進入平台後,根據平台的標籤、屬性資訊等等,計算出相似的物品,再推薦給喜歡此物品的使用者。 試探策略 這幾年短影片的流行,無論是純做短影片的網站,抑或是一般影音平台的短影片,皆是爆發性的成長;快速試探策略就很適合,快速、隨機推薦影片給使用者,再根據使用者觀看、點擊、滑動瀏覽網站、停留時間等等的行為,快速獲得使用者的資訊,再進行推薦。 結尾 上面是簡單介紹幾個常用的方式,還有許多做法可以解決遇到的冷啟動問題,主要必須先定義好問題,知道自己產品、平台的特徵、類型、使用者如何才會滿意等等的資訊,才能真正的對症下藥。 最後推薦幾篇論文,大家有興趣可以看看他們遇到的問題是什麼又是如何解決的。 Behavior-based popularity ranking on Amazon Video Billion-scale Commodity Embedding for E-commerce Recommendation in Alibaba Performance of recommender algorithms on top-N recommendation tasks ...

2021-11-29 · 1 min · 69 words · KbWen

Python Chunks

當我們要把list分成好幾個chunk時的幾種做法 yield def chunks1(input_list, n): for i in range(0, len(input_list), n): yield input_list[i:i + n] input_list = [i for i in range(0, 15)] print(list(chunks1(input_list, 4))) ## [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14]] 一行for迴圈 input_list = [i for i in range(0, 15)] n = 3 output_list = [input_list[i:i+ n] for i in range(0, len(input_list), n)] print(output_list) ## [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]] iterable 針對任何iterable from itertools import islice def chunks2(input_iter, n): input_list = iter(input_iter) return iter(lambda: tuple(islice(input_list, n)), ()) input_list = [i for i in range(0, 15)] n = 4 print(list(chunks2(input_list, n))) ## [(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14)] Numpy import numpy as np input_list = [i for i in range(0, 15)] np.array_split(input_list, 5) ## [array([0, 1, 2]), ## array([3, 4, 5]), ## array([6, 7, 8]), ## array([ 9, 10, 11]), ## array([12, 13, 14])] 上述幾種簡單的方式皆可達成 ...

2021-11-26 · 1 min · 168 words · KbWen

Python 爬取每日股價(2)

上篇文章Python 爬取每日股價(1)學會了找到所需資料和爬取的方法。 接下來資料要儲存成xlsx格式。 台灣證券交易所 先安裝pandas和xlsxwriter pip install pandas pip install xlsxwriter 如果是colab,使用!pip install xlsxwriter 藉由上篇找到的資料位置"data9",以及觀察到資料是根據每天做儲存。 因此我們使用基於每天的資料處理方式,把所需要的股票資料、開盤價、收盤價等等存放。 import requests import pandas as pd from pprint import pprint as pprint date = "20210827" url = f"https://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&date={date}&type=ALLBUT0999&_=1630244648174" res = requests.get(url) data = res.json() data_list = data["data9"] columns = data["fields9"] df = pd.DataFrame(data_list, columns=columns) writer = pd.ExcelWriter('twse_data.xlsx', engine='xlsxwriter') df.to_excel(writer, sheet_name=date, index=False) writer.save() # pprint(data_list) f-strings in Python PEP 498 打開儲存的"twse_data.xlsx" 每日收盤行情 我們可以依靠改變日期獲得過去的資料, 存成不同分頁或是檔案。 也可以依據未來需要的使用資料方式來改變儲存格式。

2021-09-06 · 1 min · 70 words · KbWen

How to scrape Yahoo Finance stock data with Python

This time, we are going to learn the hands-on ability to scrape Yahoo financial data. Set-up python environment. Yahoo Finance page. For example, Alphabet Inc. (GOOG). Scrape & parse the page. The page we will be scrape. In the “network” page, we can’t find the json data. You can get all the data from the page source in script. View page source - script - root.App.main from bs4 import BeautifulSoup import re import json import requests response = requests.get("https://finance.yahoo.com/quote/GOOG?p=GOOG&.tsrc=fin-srch") soup = BeautifulSoup(response.text, "html.parser") ## print(soup.prettify()) script = soup.find('script', text=re.compile('root.App.main')).text data = json.loads(re.search("root.App.main\\s+=\\s+({.*})", script).group(1)) stores = data["context"]["dispatcher"]["stores"] print(stores) Response data ...

2021-09-02 · 2 min · 358 words · KbWen

Python 爬取每日股價(1)

如何取得每日的股價資訊 進入證交所每日收盤行情,選擇全部(不含…),可以看到有許多選項可點。 找到每日收盤行情 110.08.27每日收盤行情 點擊F12進入開發者環境,再點選Network,觀察我們要的數據資訊 Python及時股價 點選XHR找到傳送數據的Requests import requests url = "https://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&date=20210827&type=ALLBUT0999&_=1630244648174" res = requests.get(url) res.json() 得到Json,並在data9找到全部的股票數據 {'alignsStyle1': [['center', 'center', 'center', 'center', 'center', 'center'], ... ... 'data9': [['0050', '元大台灣50', '16,875,047', '9,673', '2,328,482,421', '136.70', '138.50', '136.45', '138.15', '<p style= color:red>+</p>', '1.15', '138.15', '4', '138.20', '103', '0.00'], ['0051', '元大中型100', '17,810', '63', '1,003,448', '56.20', '56.60', '56.20', '56.60', '<p style= color:red>+</p>', '0.35', '56.55', '1', '56.60', '9', '0.00'], ... ... 'subtitle9': '110年08月27日每日收盤行情(全部(不含權證、牛熊證))'} 幾個重要的數據 ['0050', #股票代號 '元大台灣50', '16,875,047', #成交股數 '9,673', '2,328,482,421', '136.70', #開盤價 '138.50', #最高價 '136.45', #最低價 '138.15', #收盤價 '<p style= color:red>+</p>', '1.15', '138.15', '4', '138.20', '103', '0.00'], 輕鬆完成,在爬取過程中還是非常簡單的, ...

2021-08-29 · 1 min · 96 words · KbWen

Deep Reinforcement learning

Reinforcement learning (RL) is a framework where agents learn to perform actions in an environment so as to maximize a reward. It’s actually training an AI to learn through every mistake and find the correct path without any label. The two main components are the environment and the agent. Deep Reinforcement learning (DRL) combined with deep learning technology is even more powerful. AlphaGo, is a typical application of deep reinforcement learning. ...

2020-10-26 · 3 min · 472 words · KbWen

Tensorflow2 -- MNIST

Tensorflow2.X和1.X有多了很多差別和使用方式, 今天用tf2來實作MNIST分類問題 MNIST MNIST是一個很標準的手寫數字分類問題, 數據集下載有很多方式,這次直接使用tf API提供的 28 * 28 且只有黑白的數據 開發 在local 起 jupyter lab 先看看GPU是否啟用 %matplotlib widget import matplotlib.pyplot as plt import tensorflow as tf import numpy as np # check gpu tf.config.list_physical_devices('GPU') tf.test.is_built_with_cuda() # output True 方法一 繼承 tf.keras.model class MLP(tf.keras.Model): def __init__(self): super().__init__() self.flatten = tf.keras.layers.Flatten() self.dense1 = tf.keras.layers.Dense(units=100, activation=tf.nn.relu) self.dense2 = tf.keras.layers.Dense(units=20, activation=tf.nn.leaky_relu) self.dense3 = tf.keras.layers.Dense(units=10) @tf.function def call(self, inputs): # [batch_size, 28, 28, 1] flat1 = self.flatten(inputs) # [batch_size, 784] dens1 = self.dense1(flat1) # [batch_size, 100] dens2 = self.dense2(dens1) # [batch_size, 20] dens3 = self.dense3(dens2) # [batch_size, 10] output = tf.nn.softmax(dens3) return output 使用tf.GradientTape訓練 # @tf.function def one_batch_step(X, y, **kwargs): with tf.GradientTape() as tape: y_pred = model(X) loss = tf.keras.losses.sparse_categorical_crossentropy(y_true=y, y_pred=y_pred) loss = tf.reduce_mean(loss) tf.print(f"{batch_index} loss {loss}", [loss]) with summary_writer.as_default(): tf.summary.scalar("loss", loss, step=batch_index) grads = tape.gradient(loss, model.variables) optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables)) for epoch_index in range(num_epochs): for batch_index in range(num_batches): X, y = data_loader.get_batch(batch_size) one_batch_step(X, y, batch_index=batch_index) with summary_writer.as_default(): tf.summary.trace_export(name="model_trace", step=0, profiler_outdir=log_dir) tf.saved_model.save(model, f"saved/{model_name}") 方法二 使用keras Pipeline來疊每一層要用的函數,彈性較低,但非常適合簡單的Model ...

2020-09-26 · 1 min · 194 words · KbWen