Moses 官網其實是有 macOS 二進制包的,你不需要從源代碼編譯它們。但總之,由於 Moses 開發者已經不再用 Mac,所以他沒辦法更新,這導致了目前最新版(4.0)的代碼中有一個bug,使得二進製文件不能直接使用,作者說“反正從源碼編譯也不是很難……”但總之,從 BigSur 上編譯 Moses 已經幾乎是不可能的了,各種奇怪的報錯,令人頭疼。
其實,我們是可以直接修正二進製文件中的錯誤,直接運行的。
修復報錯
直接下載的 Moses 二進製文件,執行任意一個都會遇到如下錯誤:
1 |
'./moses/bin/consolidate' terminated by signal SIGKILL (Forced quit) |
以及:
1 2 3 4 |
dyld: Library not loaded: /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_xmltok.3.39.dylib Referenced from: ~/MosesTest/moses/bin/biconcor Reason: image not found Abort trap: 6 |
我們先處理第二個報錯,這個報錯有些複雜,顯然,這些二進製文件鏈接到了一個不存在的動態鏈接庫,這就很棘手了,因為代碼已經被打包成了二進製文件,我們無法直接修改代碼或者編譯器參數來修復這個錯誤……嗎?
分析
使用命令 工具 -大號 ./摩西/箱子/鞏固 來查看 鞏固 這個可執行文件,我們得到如下結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
./moses/bin/consolidate: /opt/local/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.6) /opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_xmltok.3.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_xmlparse.3.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_util++.8.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_util.3.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_server_abyss++.8.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_server_abyss.3.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_server++.8.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_server.3.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_abyss.3.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc++.8.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc.3.39.dylib (compatibility version 0.0.0, current version 0.0.0) /Users/hieu/workspace/cmph-2.0/lib/libcmph.0.dylib (compatibility version 1.0.0, current version 1.0.0) /Users/hieu/workspace/irstlm/irstlm-5.80.08/trunk/lib/libirstlm.0.dylib (compatibility version 1.0.0, current version 1.0.0) /opt/local/lib/libiconv.2.dylib (compatibility version 9.0.0, current version 9.0.0) /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0) |
這就是這個文件鏈接的所有動態庫了,理論上我們只要把那些不存在的路徑改成存在的路徑就可以讓這個程序正常運行,這有兩步:
- 找到真正被鏈接的動態庫;
- 改這個二進製文件的鏈接庫地址。
其中第二點我們可以使用 install_name_tool 這個 Xcode 自帶命令完成,第一點就是去找到 xmlrpc-c 了,最好是 1.39.07 這個版本(我已經試過用 brew 直接裝一個最新版,但由於太新了,有兩個動態鏈接庫直接被去掉了,所以最好還是同一個版本,確保具體 API 不變)。好在 xmlrpc-c 有官方提供歷史版本,我們可以從這裡直接下載 1.39.07 這個版本的源代碼,進行編譯。
修復錯誤
要編譯 xmlrpc-c,需要使用 gcc-10,如果你沒有裝的話,可以使用命令 釀造 安裝 gcc 一鍵安裝,然後就是編譯安裝了:
1 2 3 |
./configure --prefix=/usr/local/lib/xmlrpc/ make make install |
安裝到哪裡你隨意,但要記得這個地址,一會要去找到這個路徑的。
接下來就是修改鏈接地址了:
1 |
install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_xmltok.3.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_xmltok.3.39.dylib ./moses/bin/consolidate |
比如對於 libxmlrpc_xmltok.3.39.dylib 這一條,我們就這樣把它替換掉,變成真正可用的動態鏈接庫。但每個二進製文件都有11個錯誤鏈接需要替換……手動處理還是有些麻煩,於是我寫了一個簡單的腳本,你可以把它複製下來寫到一個 .sh 文件裡,然後使用 sh XXX.sh ./摩西/箱子 這樣的形式來給對應的二進製文件進行替換,這個腳本可以直接將給定目錄下的所有可執行文件進行處理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#!/bin/bash updateLink() { install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_xmltok.3.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_xmltok.3.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_xmlparse.3.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_xmlparse.3.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_util++.8.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_util++.8.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_util.3.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_util.3.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_server_abyss++.8.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_server_abyss++.8.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_server_abyss.3.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_server_abyss.3.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_server++.8.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_server++.8.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_server.3.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_server.3.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc_abyss.3.39.dylib /usr/local/lib/xmlrpc/libxmlrpc_abyss.3.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc++.8.39.dylib /usr/local/lib/xmlrpc/libxmlrpc++.8.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/xmlrpc-c/xmlrpc-c-1.39.07/lib/libxmlrpc.3.39.dylib /usr/local/lib/xmlrpc/libxmlrpc.3.39.dylib ${1} install_name_tool -change /Users/hieu/workspace/irstlm/irstlm-5.80.08/trunk/lib/libirstlm.0.dylib /usr/local/lib/xmlrpc/libxmlrpc.3.39.dylib ${1} } for file in [crayon-673f27b08794f879960309 inline="true" ]ls -F ${1} | grep "*" |
做
迴聲 “處理$ file …”
更新鏈接 “${1}/${文件}”
DONE
[/蠟筆]
在把報錯的命令進行修復後,我們就可以正常地使用 Moses 了。
這裡我們再說第一個錯誤,是 macOS 新引入的 gatekeeper,這個安全機制會默認阻止你運行任何沒有簽名的二進製文件……顯然,Moses 裡的所有二進製文件都是沒有簽名的……總之,由於我們已經用腳本將這些二進製文件修改了一下,現在系統就認為它們是我們自己生成的,於是就不會再阻擋運行。不過以防萬一,如果你遇到了,就用 Finder 去目錄裡,鼠標右鍵點擊它,選擇“打開”。 這樣系統就會用自帶的終端來運行這個二進製文件了,接下來再回到你的終端重新執行這個命令,就可以正常執行了(每一個新命令都要這樣處理一次,好在只是第一次才需要,以後就不要了。)
注意這裡有個 誹謗.0.dylib 我們其實並沒有去生成,但沒關係,因為我們沒有用到這個庫,只要把它的依賴替換成任意能找到的路徑就行了,只要不用這部分功能,那麼理論上不會有任何影響~
準備數據
這裡我們用聯合國公開平行語料進行實驗,由於數據過於龐大,這裡僅取中文英文各前 60 萬行,下載到的數據是 tar.gz 的分包文件,這裡我們用 cat 第1版.0.小號 n-zh.tar.gz.* >>一種.小號 n-zh.tar.gz 命令將分包文件進行合併,然後再解壓縮。
這裡我們將截取出來的數據分別命名為 en60w.文本 和 zh60w.文本 ,這兩個文件基本上是一句一行,兩個文件的格式一致,內容一致,唯一的不同就是語言不同。
分詞
我們需要對數據進行分詞,注意英文語料也需要分詞,這會將一些標點符號與英文詞彙或者數字分隔開,方便後續操作。
對中文進行分詞,我們使用 jieba:
1 |
python3 -m jieba -d " " zh60w.txt > zh60w_cuted.txt |
注意這裡用了 -ð " " 這個參數,把 jieba 默認的斜杠分詞符號改為空格。這樣我們就得到了分詞結果 zh60w_cuted.文本 。
對英文進行分詞,使用 Moses 自帶工具:
1 |
./moses/scripts/tokenizer/tokenizer.perl -l en -lines 20000 -time -threads 6 < en60w.txt > en60w.tok.txt |
這裡我用了 -時間 來顯示最終時間消耗,用 -線程數 6 註明使用多線程加速處理,用 -線 20000 設置每個線程每次處理 20000 行,默認是 2000. 這樣我們就得到了英文分詞結果 en60w.拿.文本 。
至此, en60w.文本 和 zh60w.文本 就可以刪掉了。
處理大小寫
把英文數據中的大寫都換成小寫,這有助於加快翻譯速度,我們首先要訓練 Truecase,然後再用它來快速處理語料:
1 2 |
./moses/scripts/recaser/train-truecaser.perl --model truecase-model.en --corpus en60w.tok.txt ./moses/scripts/recaser/train-truecaser.perl --model truecase-model.cn --corpus zh60w.tok.txt |
這樣我們就得到了 真實情況-模型.小號 n 和 真實情況-模型.CN 兩個模型,然後我們使用這兩個模型對分詞後的語料進行處理:
1 2 |
./moses/scripts/recaser/truecase.perl --model truecase-model.en < en60w.tok.txt > en-zh60w.true.en ./moses/scripts/recaser/truecase.perl --model truecase-model.cn < zh60w.tok.txt > en-zh60w.true.cn |
這樣我們就得到了處理過的 小號 n-zh60w.true.小號 n 和 小號 n-zh60w.true.CN ,注意從這裡開始,我們的命名具有了一定的規則,因為後續的命令會用到。
至此, en60w.拿.文本 和 zh60w.拿.文本 就可以刪掉了。
去掉過長語句
最後,我們再來對語料進行一下修剪,比如太長的語句會明顯減慢訓練速度且影響最終的準確性:
1 |
./moses/scripts/training/clean-corpus-n.perl en-zh60w.true cn en en-zh60w.clean 1 50 |
這樣,我們就又得到了 小號 n-zh60w.乾淨的.CN 和 小號 n-zh60w.乾淨的.小號 n 這兩個清洗後的語料文件。
生成語言模型
語言模型用來保證翻譯後的內容是流利可讀的:
1 |
./moses/bin/lmplz -o 3 < en-zh60w.true.cn > en-zh60w.arpa.cn |
然後將生成的模型壓縮成二進制,加快查詢速度:
1 |
./moses/bin/build_binary en-zh60w.arpa.cn en-zh60w.blm.cn |
這樣我們就得到了 小號 n-zh60w.blm.CN 這個模型文件, 小號 n-zh60w.大麥.CN 可以刪掉了。
使用命令測試模型: 迴聲 "我 愛 北京 天安門" | ./摩西/箱子/詢問 小號 n-zh60w.blm.CN 得到輸出:
1 2 3 4 5 6 |
我=23055 2 -2.4262204 爱=3881 1 -6.498771 北京=14065 1 -4.4601955 天安门=33807 1 -6.3538356 </s>=2 1 -2.6495044 Total: -22.388527 OOV: 0 Perplexity including OOVs: 30040.377320675794 Perplexity excluding OOVs: 30040.377320675794 OOVs: 0 Tokens: 5 RSSMax:166760448 kB user:0.002757 sys:0.098377 CPU:0.101134 real:0.092774 |
至此, 小號 n-zh60w.true.小號 n 和 小號 n-zh60w.true.CN 就可以刪掉了。
訓練翻譯模型
現在,萬事俱備,我們可以開始訓練翻譯模型了:
1 2 |
mkdir working cd working |
我們先創建一個獨立的目錄,在這裡邊執行訓練命令:
1 |
../moses/scripts/training/train-model.perl -root-dir train -corpus ../en-zh60w.clean -f en -e cn -alignment grow-diag-final-and -reordering msd-bidirectional-fe -lm 0:3:/【这里需要使用绝对路径】/Downloads/MosesTest/lm/en-zh60w.blm.cn:8 -external-bin-dir ../bin/training-tools -cores 6 -mgiza -mgiza-cpus 6 |
這裡註意 -米吉薩 -米吉薩-中央處理器 6 是必須要加上的,因為 Moses 的 macOS 包中只帶有 mgiza 這個工具,如果不使用它,則會默認使用一個單線程的處理工具,最終導致找不到命令而報錯。其中 -米吉薩-中央處理器 6 表明了我要使用 6 個線程來進行訓練。
調優參數
模型訓練完成了,但是現在的超參都是默認值,並不是最優的,我們需要對參數進行調優。
首先從最一開始下載的平行語料中再次截取 10 萬語料,這裡我截取了第 60 萬到 70 萬這10萬行數據,保存為 選擇.小號 n 和 選擇.CN 這兩個小語料,我們將使用這 10 萬數據作為調試集進行參數調優。
分詞和處理大小寫
還是類似的步驟,將數據進行處理:
1 2 3 4 5 6 7 |
python3 -m jieba -d " " opt.cn > opt.tok.cn ./moses/scripts/tokenizer/tokenizer.perl -l en -lines 2000000 -time < opt.en > opt.tok.en ./moses/scripts/recaser/truecase.perl --model truecase-model.cn < opt.tok.cn > opt.true.cn ./moses/scripts/recaser/truecase.perl --model truecase-model.en < opt.tok.en > opt.true.en rm opt.en opt.cn |
現在我們得到 選擇.true.CN 和 選擇.true.小號 n ,這下就可以用來給參數調優了。
調優
1 2 |
cd working ../moses/scripts/training/mert-moses.pl ../opt.true.en ../opt.true.cn ../moses/bin/moses train/model/moses.ini --mertdir /【绝对路径】/moses/bin/ --multi-moses --multi-moses --decoder-flags='-threads 6' |
--死了 /【絕對路徑】/摩西/箱子/ 注意這個參數,要寫成絕對路徑,雖然寫成相對路徑程序也可以執行,但到末尾調優結束後無法正確生成導出腳本,會導致實際目錄錯位。
這裡我們使用 --多-摩西參數開啟多進程並使用參數 --解碼器-旗='線程6' 直接給 decoder 傳送指令,表示使用 6 個進程,加速處理。
這裡參數名雖然是 threads,但實際上格式是這樣的 進程數:每個進程的線程數:額外進程的線程數 ,如果你像我這樣只給了一個數字,那麼意思就是 6:1:0 ,也就是6個進程每個進程1線程,不要額外進程。這是最快也是最消耗內存的方案。
實測不使用 --多-摩西即使設定6線程,實際也只使用2線程,佔用內存4.2GB(這個大小是依據不同的模型大小來的,你要根據你的實際情況進行處理,比如我內存是 32GB,得到這個內存佔用量後,就可以結束進程,然後使用6個進程重新開始,加速處理)。調優的過程非常非常緩慢,我的建議是你選擇一個10的倍數級別的樣本進行調優,這樣就可以根據數量計算當前進度。
程序會先根據你要測試的數據對模型進行過濾,去掉模型中肯定用不到的條目,這樣就會大大提升加載速度而不影響測試結果,但實際使用時請不要使用這個過濾後的專用模型,且更換了測試數據也要重新生成過濾的模型。
注意,調優不會自動停止,它會一遍又一遍的迭代下去,在你覺得差不多的時候自行停止它,並使用最好的那個結果。
二進制模型壓縮
生成的模型是文本的,我們可以將模型進行壓縮,生成二進制數據,這樣能夠極大程度提升 Moses 的載入速度。我們創建一個目錄來存放生成的二進制模型: MKDIR 在職的/二值化-模型
然後使用兩個命令來生成兩個模型文件:
1 2 3 |
../moses/bin/processPhraseTableMin -in train/model/phrase-table.gz -nscores 4 -out binarised-model/phrase-table ../moses/bin/processLexicalTable -in train/model/reordering-table.wbe-msd-bidirectional-fe.gz -out binarised-model/reordering-table |
將 火車/模型/摩西.此 複製到 二值化-模型/摩西.此
編輯它,找到 # 特徵 功能 這一塊, 詞法重排 這個字段中的參數 path= 為 二值化-模型/重新排序-桌子 的絕對路徑, 短語詞典記憶 這個字段改為 詞組詞典緊湊 並將其中的參數 path= 改為 二值化-模型/短語-桌子.phr 的絕對路徑。
然後我們就可以使用命令 ../摩西/箱子/摩西 -F 二值化-模型/摩西.此 來啟動 Moses 了。
批量測試
批量測試也需要對應的平行語料,英文用於翻譯,中文用於最後的比對準確率。同樣的英文要用 標記器.perl的 進行分詞,中文要用 jieba 等分詞庫進行分詞,然後使用 真實情況.perl的 進行處理。
準備模型
同樣的,我們先將模型針對測試集進行過濾,去掉完全不會用到的條目,這樣能大大加快測試速度卻不影響結果:
1 2 |
cd working ../moses/scripts/training/filter-model-given-input.pl filtered mert-work/run4.moses.ini ../opt.true.en -Binarizer ../moses/bin/processPhraseTableMin |
這裡我直接用調優的數據進行測試了。
批量處理
使用命令讓 moses 批量翻譯所有內容:
1 |
./moses/bin/moses -f working/filtered/moses.ini -i < opt.true.en > translated.cn 2> test.out |
計算 BLEU
BLEU 是一個評判翻譯結果準確度的算法,得到的結果是一個百分比:
1 |
./moses/scripts/generic/multi-bleu.perl -lc opt.true.cn translated.cn |
比如按照本次例子中的語料,我們得到結果如下:
1 |
BLEU = 26.53, 63.3/32.7/19.4/12.3 (BP=1.000, ratio=1.000, hyp_len=2354049, ref_len=2353369) |
參考鏈接
本文由 落格博客 原創撰寫:落格博客 » macOS 運行和訓練 Moses
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/3487.html