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
函數。我們可以設定我們檔名要附加那些資訊,像是 epoch
、val_loss
等等,當我們要讀取某個最佳紀錄點時,我們可以藉由 re 模組來取出我們要的部分,並將其群組起來。打完發現自己不知道在供三小?
讓我們直接來看程式,首先 KERAS 的 callback
函數中的 ModelCheckpoint
需要設定儲存路徑。那設定這個路徑時,我們可以將 KERAS 監控的評估指標 (像是 loss
、val_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
比對,取出 epoch
、val_loss
、val_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
中所設定的路徑,將其分成四個部分來看,分別是weights
、epoch
、val_loss
、val_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')]]
從結果可以看出程式已經將 epoch
、val_loss
、val_accuracy
分離出來了,如果要更精準的去使用資料,我們也可以把第三行換成下面的程式。
for ckpt in checkpoints:
ckpt = re.match(pattern, ckpt)
epoch, val_loss, val_accuracy = ckpt.groups()
這樣可以更精準的將結果用變數儲存起來,程式碼也非常直覺,更進一步的還可以將上面的程式,加上尋找最佳準確度的功能,去把最好的那個權重檔案找出來,根據需要也更容易附加其他功能上去。
結語
這次其實主要是為了展示如何將儲存的東西給快速取出來,那為了能夠讓程式可以方便搜尋,除了需要決定儲存的命名的方式,也搭配 re 正則表達式來完成搜尋查找的工作。