使用 monpa 以 GPU 做批次斷詞

Wen-Chao Yeh
8 min readMay 30, 2021

--

MONPA 罔拍是一個提供正體中文斷詞、詞性標註以及命名實體辨識的多任務模型。

monpa 也提供應用 GPU 運算能力的 cut_batch function,將文字資料以 list 格式單批次輸入,不超過 GPU 可使用的記憶體容量內可快速斷詞,回傳值亦是 list 格式。初次啟動需耗費額外時間將程序及資料轉換到 GPU 做運算,建議若非大量斷詞,可使用 cut function 即可。

此範例的文字資料是100 則 wiki 正體中文條目摘要,每則幾乎都會超過 200 字元,所以 pandas 讀入後,再用 monpa 的 short_sentence function 將長句切成短句再來斷詞。作法可參考先前文章。

首先,你的機器要有 GPU 設備,而且原先安裝的就是支援 GPU 運算的 pytorch 版本,並測試 GPU 是否可用。

def print_gpu_memory():
import torch
total_memory = torch.cuda.get_device_properties(0).total_memory/1024/1024/1024
reversed_memory = torch.cuda.memory_reserved(0)/1024/1024
allocated_memory = torch.cuda.memory_allocated(0)/1024/1024
free_memory = reversed_memory - allocated_memory/1024/1024
print(f"有沒有 GPU 可以使用:{torch.cuda.is_available()}\nGPU 所有記憶體: {total_memory} MB\n預先保留作為執行程序使用的記憶體: {reversed_memory} MB\n程式執行使用的記憶體: {allocated_memory} MB\n預先保留的記憶體剩下空間: {free_memory} MB")print_gpu_memory()### output ###
有沒有 GPU 可以使用:True
GPU 所有記憶體: 15109.75 MB
預先保留作為執行程序使用的記憶體: 0.0 MB
程式執行使用的記憶體: 0.0 MB
預先保留的記憶體剩下空間: 0.0 MB

這是 colab 提供的基本 GPU 設備,至少有近 14.75 GB 的記憶體可以使用。以 monpa.use_gpu(True) 開啟使用 GPU 功能。

第一種用法:依每排做批次斷詞

%%time
monpa.use_gpu(True)
df["cut_batch_a"] = df["chunk"].map(monpa.cut_batch)
### output ###
CPU times: user 6.83 s, sys: 1.28 s, total: 8.11 s
Wall time: 15.2 s

第一種作法是很直覺地將 chunk 欄位以 map function 把 list 格式的資料送到 monpa.cut_batch 做批次斷詞,例如第一排(row)有 3 個短句會被批次斷詞,第11 排(row, df["chunk"][10])有 32 個短句會被批次斷詞,依此類推。總共做完 100 輪批次斷詞,整個 cell 花費 15.2 秒。

print_gpu_memory()### output ###
有沒有 GPU 可以使用:True
GPU 所有記憶體: 15109.75 MB
預先保留作為執行程序使用的記憶體: 512.0 MB
程式執行使用的記憶體: 8.8115234375 MB
預先保留的記憶體剩下空間: 511.9999915966764 MB

注意預先保留的記憶體大小就要 512 MB,所以不是只有計算輸入文字大小還有 pytorch 等運行環境也需要記憶體喔。

第二種用法:暴力斷詞,一次全來

因為範例的資料量不太大,可以來試試暴力一次斷詞法。先將每篇文章的短句集合成一個大 list 格式,並先預算各文章的短句個數以備後續將斷詞結果回存到各自的欄位。

chunk_list = []
df["chunk"].map(chunk_list.extend)
df["chunk_len"] = df["chunk"].map(len)

第二種作法是將 chunk_list 一次送給 monpa.cut_batch 做斷詞。

%%time
cut_batch_list = monpa.cut_batch(chunk_list)
cut_batch = []
for i in df["chunk_len"].tolist():
cut_batch.append(cut_batch_list[:i])
cut_batch_list = cut_batch_list[i:]
df["cut_batch_b"] = cut_batch### output ###
CPU times: user 4.13 s, sys: 24 ms, total: 4.15 s
Wall time: 4.15 s

斷詞結果是一個 cut_batch_list 的 list 格式資料,使用 for 迴圈依照 chunk_len 欄位的每篇文章短句數量來分配斷詞結果並回存到 cut_batch_b 欄位。做完 一輪大批次斷詞,整個 cell 花費 4.15 秒。超快的啦!

print_gpu_memory()### output ###
有沒有 GPU 可以使用:True
GPU 所有記憶體: 15109.75 MB
預先保留作為執行程序使用的記憶體: 4732.0 MB
程式執行使用的記憶體: 8.8115234375 MB
預先保留的記憶體剩下空間: 4731.999991596676 MB

從上面資訊可以看出一大批次就要用到 4732.0 MB,若是 4 倍的範例資料量一次送入就會發生 GPU 記憶體不足問題。這是要開快車前必須注意的事!

第三種用法:折衷斷詞,指定批次量

假設範例的資料量很大,我們可以指定每一輪固定批次量的作法來提升斷詞效率,也不操爆 GPU 的記憶體。同上述做法,先將短句集合成一個大 list 格式,並先預算各排的短句個數以備後續將斷詞結果回存到各自的欄位。

chunk_list = []
df["chunk"].map(chunk_list.extend)
df["chunk_len"] = df["chunk"].map(len)

作法是將 chunk_list 依指定數量一輪一輪送給 monpa.cut_batch 做斷詞。

%%timecut_batch_list = []
process_num = 100
for item_idx in range(0, len(chunk_list), process_num):
if item_idx < len(chunk_list):
cut_batch_list.extend(monpa.cut_batch(chunk_list[item_idx:item_idx+process_num]))
else:
cut_batch_list.extend(monpa.cut_batch(chunk_list[item_idx:]))
cut_batch = []
for i in df["chunk_len"].tolist():
cut_batch.append(cut_batch_list[:i])
cut_batch_list = cut_batch_list[i:]
df["cut_batch_c"] = cut_batch### output ###
CPU times: user 4.14 s, sys: 14.1 ms, total: 4.15 s
Wall time: 4.16 s

斷詞結果以 cut_batch_list 的 list 格式暫存。使用 for 迴圈依照指定批次數量 process_numchunk_list 的資料取出送入 monpa.cut_batch 斷詞,並將結果加到 cut_batch_list。完成所有批次斷詞後,再依 chunk_len 欄位的每篇文章短句數量來分配斷詞結果並回存到 cut_batch_c 欄位。做完幾輪批次斷詞,整個 cell 花費 4.16 秒。也是超快的啦!

print_gpu_memory()### output ###
有沒有 GPU 可以使用:True
GPU 所有記憶體: 15109.75 MB
預先保留作為執行程序使用的記憶體: 4754.0 MB
程式執行使用的記憶體: 8.8115234375 MB
預先保留的記憶體剩下空間: 4753.999991596676 MB

從上面資訊可以看出指定批次量要用到 4754.0 MB,還不會操爆 GPU 記憶體。

(df["cut"]==df["cut_batch_a"]).value_counts()### output ###
True 100
dtype: int64
(df["cut"]==df["cut_batch_b"]).value_counts()### output ###
True 100
dtype: int64
(df["cut"]==df["cut_batch_c"]).value_counts()
### output ###
True 100
dtype: int64

驗證三種方式的斷詞結果都相同。

monpa 斷詞不慢,只是要會用。

--

--

Wen-Chao Yeh
Wen-Chao Yeh

Written by Wen-Chao Yeh

行動通訊業界工作十多年,曾任系統架構師與產品經理。清華大學資訊系統與應用研究所博士候選人,研究領域包含但不限於 Generative AI, LLM, RAG, Semantic Search, Sentiment Analysis。

No responses yet