Deep Learning,  Python

python re 正則化表達式搜尋

前言

如果說今天許多檔案的命名具有一定規則時,代表這些檔案是有一些關係存在,要是這些檔案數量超過好幾百、好幾千筆、甚至好幾萬筆,這些規則可以幫助我們快速找到資料。那要如何根據命名規則來快速篩選檔案呢?對於 Python 來說,可以使用 re 正則表達式,來幫助我們完成上面的工作。

string_rule = r"\d\d\d"
rule = re.complie(string_rule )
results = rule.search(rule, msg)

可以直接套用 re 模組的函數,就不用先 re.complie(r"string_rule")

string_rule = r"\d\d\d"
re.search(string_rule , msg)    # 回傳 MatchObject,搭配 group、groups 使用
re.findall(string_rule , msg)   # 回傳 ListObject
re.match(string_rule , msg)     # 回傳 MatchObject,搭配 group、groups 使用

Note:search()match() 大致相同,只是 match() 從字串開頭就會比對,個人認為能更精準篩選出目標。

小括號

用來進行分組。通常使用 search()match() 並搭配 group()groups() 來分組。

i.e. 假設某個地區的電話號碼,是以一組 2 個數字的區域碼再加上一組 6 個數字的號碼,所組成的電話號碼。如果要更精準的把區域碼和號碼取出來,我們可以透過小括號的方式來達成。

phone = "02-123456"
pattern = r"(\d\d)-(\d\d\d\d\d\d\d)"
tel = re.match(pattern, phone)
loc, num = tel.groups()
print("location:", loc)
print("Number:", num)

將會得到的輸出是

location: 02
Number: 123456

大括號

指定重複次數。

如果今天像上面的例子一樣,有六個數字要取出,通常會寫成 \d\d\d\d\d\d,但如果有很多數字要取出,這不會是好方法,這時候就可以利用大括號來指定這個字元會出現多少數量。如果不確定字元的數量的話,也可以設一個區間來進行搜尋。

i.e.

\d{6} 一定要出現 6 次數字

\d{2,6} → 至少出現 2 次 ~ 6 次的數字,都會被搜尋到。

特殊字元

名稱 說明
\d 0-9
\D 0-9以外
\s 空白、定位、Tab、換行、換頁
\S 空白、定位、Tab、換行、換頁 以外
\w 數字、字母、底線
\W 數字、字母、底線 以外

Example:以 KERAS 的 ModelCheckpoint 為例

假設今天使用 KERAS 的 callback 函數。我們可以設定我們檔名要附加那些資訊,像是 epochval_loss 等等,當我們要讀取某個最佳紀錄點時,我們可以藉由 re 模組來取出我們要的部分,並將其群組起來。打完發現自己不知道在供三小?

讓我們直接來看程式,首先 KERAS 的 callback 函數中的 ModelCheckpoint 需要設定儲存路徑。那設定這個路徑時,我們可以將 KERAS 監控的評估指標 (像是 lossval_loss …等等) 或是訓練到哪個 epoch,當作檔名的一部份給加上去,設定方式如下:

checkpoint of KERAS weights:

weights.{epoch:05d}-{val_loss:.5f}-{val_accuracy:.5f}.h5
評估指標 格式化 說明 結果
epoch {epoch:05d} 會自動補零 00007
val_loss {val_loss:.5f} 只取小數點五位 1.52348
val_accuracy {val_accuracy:.5f} 只取小數點五位 0.97542

那可以看到經由上面的命名規則,我們就可以存出一連串的權重資料出來,那其中一個檔案檔名可能如下:

weights.000127-1.52348-0.97542.h5

那問題來了,假設今天訓練的 epochs 很多,可能好幾百次的訓練,又或是訓練很多組不同參數的模型,如果每個都要手動去看其實很麻煩也很浪費時間,這時候就可以利用 re 正則表達式來搜尋我們所存下的數值。

下面這個程式就是去比對我目前所有的 checkpoints 路徑,用迴圈去迭代每個路徑並和 pattern 比對,取出 epochval_lossval_accuracy 的數值。

checkpoints = next(os.walk(logpath))[2]
pattern = r"weights.(\d{5})-(\d.\d{5})-(\d.\d{5})"
a = [re.findall(pattern, i) for i in checkpoints]
  • 第一行:用來取出某個路徑下所有檔案名稱。
  • 第二行:pattern 的比對方式。根據我們在 ModelCheckpoint 中所設定的路徑,將其分成四個部分來看,分別是 weightsepochval_lossval_accuracy。所以我們目標取出這四個部分
  • 第三行:根據 pattern 取出我們要的部分。我們先來看看 re.findall() 的結果。
In [9]: a
Out[9]:
[[('00001', '1.26895', '0.65417')],
 [('00002', '1.00972', '0.78467')],
 [('00003', '0.90439', '0.79617')],
 [('00004', '0.85069', '0.80233')],
 [('00005', '0.81258', '0.80283')],
 [('00006', '0.77820', '0.81683')],
 [('00007', '0.75935', '0.81650')],
 [('00008', '0.75671', '0.81900')],
 [('00009', '0.74019', '0.81583')],
 [('00010', '0.74077', '0.81417')]]

從結果可以看出程式已經將 epochval_lossval_accuracy 分離出來了,如果要更精準的去使用資料,我們也可以把第三行換成下面的程式。

for ckpt in checkpoints:
    ckpt = re.match(pattern, ckpt)
    epoch, val_loss, val_accuracy = ckpt.groups()

這樣可以更精準的將結果用變數儲存起來,程式碼也非常直覺,更進一步的還可以將上面的程式,加上尋找最佳準確度的功能,去把最好的那個權重檔案找出來,根據需要也更容易附加其他功能上去。

結語

這次其實主要是為了展示如何將儲存的東西給快速取出來,那為了能夠讓程式可以方便搜尋,除了需要決定儲存的命名的方式,也搭配 re 正則表達式來完成搜尋查找的工作。

留下一個回覆

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *