我想將自定義標記器合併到正在開發的Web應用程序(在Pyramid上運行)。我使用NLTK在我的本地機器上正常工作,但我讀過NLTK生產相對較慢的問題。在生產Web應用程序上的NLTK
看起來存儲標記器的標準方式是Pickle它。在我的機器上,加載11.7MB pickle文件需要幾秒鐘的時間。
NLTK是否適用於生產?我應該看看scikit-learn甚至像Mahout?
如果NLTK足夠好,確保正確使用內存等的最佳方法是什麼?
我想將自定義標記器合併到正在開發的Web應用程序(在Pyramid上運行)。我使用NLTK在我的本地機器上正常工作,但我讀過NLTK生產相對較慢的問題。在生產Web應用程序上的NLTK
看起來存儲標記器的標準方式是Pickle它。在我的機器上,加載11.7MB pickle文件需要幾秒鐘的時間。
NLTK是否適用於生產?我應該看看scikit-learn甚至像Mahout?
如果NLTK足夠好,確保正確使用內存等的最佳方法是什麼?
我運行了text-processing及其關聯的NLP APIs,它使用了大約24種不同的酸洗模型,這些模型由Django應用程序加載(gunginorn在nginx後面加載)。模型在需要時立即加載,一旦加載,它們將保留在內存中。這意味着無論何時重新啓動gunicorn服務器,需要模型的第一個請求都必須等待幾秒鐘才能加載,但每個後續請求都會使用已緩存在RAM中的模型。重新啓動只發生在我部署新功能時,通常需要更新模型,所以我需要重新加載它們。因此,如果您不希望經常更改代碼,並且對一致的請求時間沒有很強的要求,那麼您可能不需要單獨的守護進程。
除了初始加載時間,主要的限制因素是內存。我目前只有1個工作進程,因爲當所有模型加載到內存中時,單個進程最多可能需要1GB(YMMV,而對於一個11MB的pickle文件,內存要求會更低)。使用已經加載的模型處理單個請求的速度足夠快(通常爲< 50ms),我目前不需要超過1個工作人員,如果是這樣,最簡單的解決方案是添加足夠的RAM以運行更多工作進程。
如果您擔心內存問題,那麼請研究一下scikit-learn,因爲等效模型可以使用的內存比NLTK少得多。但是,它們不一定更快或更準確。
謝謝您的詳細解答,這非常有幫助。你能否指點我如何在初始化時將模型加載到內存中?我一直在谷歌搜索「Django緩存」等,但我想確保我正在查看正確的材料。 – abroekhof
在我的一些模塊中,我聲明瞭一個空字典,然後在需要模型的函數中,檢查模型是否在字典中,如果沒有,我使用nltk.data.load和pickle文件名來加載它,並在字典中存儲引用。然後所有未來對該函數的調用都將使用已經加載的模型。如果您想立即加載模型,那麼您可以在模塊級別執行此操作,並聲明它,例如「mymodel = nltk.data.load('path/to/model.pickle')」。 – Jacob
減少啓動延遲的最佳方法是將標記器作爲守護程序(持久服務)運行,以便Web應用程序將標記的片段發送給標記。這樣,只有在系統啓動時以及是否需要重新啓動守護程序時,您的標記器纔會加載。
只有您可以決定NLTK是否足夠滿足您的需求。一旦加載了標記器,您可能已經注意到NLTK可以標記幾頁文本而沒有可感知的延遲。但資源消耗和併發用戶數量可能會使事情複雜化。
首先,你在使用Python 2嗎?如果是這樣,你使用'pickle'還是'cPickle'?切換到'cPickle'(或Python 3,其中兩個實現合併成一個模塊)可能會把你的幾秒鐘變成幾十毫秒。或者,如果您需要爲所有請求使用相同的標記器,爲什麼爲每個請求加載它?加載一次(或每個進程一次,或任何 - 我不知道金字塔),然後它不需要多長時間。 – abarnert
對於上述證據,試試這個:'p = cPickle.dumps(range(1250000));打印timeit.timeit(lambda:pickle.loads(s),number = 1);打印timeit.timeit(lambda:cPickle.loads(s),number = 1)'。在我的系統上,我得到了4.96s和0.35s。 – abarnert
我使用Python 2和普通的泡菜。我會看看cPickle。是的,我並不完全確定加載文件的最佳方式。我需要查看Pyramid的加載選項 – abroekhof