賊好理解,這個項目教你如何用百行代碼搞定各類NLP模型

2020-12-16 機器之心Pro

機器之心報導

參與:思源、賈偉

NLP 的研究,從詞嵌入到 CNN,再到 RNN,再到 Attention,以及現在正紅火的 Transformer,模型已有很多,代碼庫也成千上萬。對於初學者如何把握其核心,並能夠自己用代碼一一實現,殊為不易。如果有人能夠將諸多模型和代碼去粗取精,只保留核心,並能夠「一鍵執行」,對於初學者不啻為天大的福音。

近日,來自韓國慶熙大學的 Tae Hwan Jung 在 Github 上創建了這樣一個項目:「nlp-tutorial」。

項目地址:https://github.com/graykode/nlp-tutorial

這個項目並不複雜,但卻包含了基本的嵌入式表徵模型、CNN、RNN、注意力模型、Transformer 等的 13 個重要模型的核心代碼實現。整體而言,基本所有代碼都是作者自己完成的,當然都會借鑑已有的實現。很多模型都同時有 TensorFlow 和 PyTorch 兩種版本,但像 Transformer 和 BERT 等擁有谷歌官方實現的模型,作者只提供了 PyTorch 實現。據作者介紹,隨後他計劃將添加 Keras 版本的實現。

引入矚目的是,這個項目中幾乎所有模型的代碼實現長度都在 100 行左右(除了注釋和空行外),很多預處理、模型持久化和可視化等操作都被簡化或刪除了。因此精簡後的代碼非常適合學習,我們不需要從複雜的大型模型實踐中抽絲剝繭地找出核心部分,只要懂一點深度學習框架的入門者就能很容易理清整個模型的實現過程。

另外值得注意的是,每一個模型都只有一個文件;如果你要訓練,那麼只需要「一鍵」運行即可。對於剛入行的小白簡直再美好不過了。

當然這裡還需要注意配置問題。據作者介紹,他的運行是在谷歌 Colab 上使用 GPU 跑的,這樣就免除了不同機器的環境配置問題。因此如果你想測試一下他的代碼能不能正常運行,只需要直接將代碼複製粘貼到 Colab 即可。而對於想在本地運行代碼的同學,環境配置也非常簡單,基本上所有代碼都只依賴 Tensorflow 1.12.0+ 和 Pytorch 0.4.1+兩個庫,Python 也是常見的 3.5。

項目目錄

下面為項目的基本框架以及每個模型的功能:

1、基本嵌入模型

NNLM - 預測下一個單詞Word2Vec(Skip-gram) - 訓練詞嵌入並展示詞的類推圖FastText(Application Level) - 情感分類

2、CNN

TextCNN - 二元情感分類DCNN(進行中……)

3、RNN

TextRNN - 預測下一步TextLSTM - 自動完成Bi-LSTM - 在長句子中預測下一個單詞

4、注意力機制

Seq2Seq - 同類詞轉換Seq2Seq with Attention - 翻譯Bi-LSTM with Attention - 二元情感分類

5、基於 Transformer 的模型

Transformer - 翻譯BERT - 分類是否是下一句和預測 Mask 掉的詞

模型示例

在這一部分中,我們將以帶注意力機制的 Bi-LSTM 與 Transformer 為例分別介紹 TensorFlow 和 PyTorch 的代碼實現。當然我們也只會介紹模型部分的核心代碼,其它訓練迭代和可視化等過程可以查閱原項目。

基於注意力機制的雙向 LSTM

作者用不到 90 行代碼簡單介紹了如何用雙向 LSTM 與注意力機制構建情感分析模型,即使使用 TensorFlow 這種靜態計算圖,Tae Hwan Jung 藉助高級 API 也能完成非常精簡代碼。總的而言,模型先利用雙向 LSTM 抽取輸入詞嵌入序列的特徵,再使用注意力機制選擇不同時間步上比較重要的信息,最後用這些信息判斷輸入句子的情感傾向。

首先對於構建雙向 LSTM,我們只需要定義前向和後向 LSTM 單元(lstm_fw_cell 與 lstm_bw_cell),並傳入高級 API tf.nn.bidirectional_dynamic_rnn() 就行了:

# LSTM ModelX = tf.placeholder(tf.int32, [None, n_step])Y = tf.placeholder(tf.int32, [None, n_class])out = tf.Variable(tf.random_normal([n_hidden * 2, n_class]))embedding = tf.Variable(tf.random_uniform([vocab_size, embedding_dim]))input = tf.nn.embedding_lookup(embedding, X) # [batch_size, len_seq, embedding_dim]lstm_fw_cell = tf.nn.rnn_cell.LSTMCell(n_hidden)lstm_bw_cell = tf.nn.rnn_cell.LSTMCell(n_hidden)# output : [batch_size, len_seq, n_hidden], states : [batch_size, n_hidden]output, final_state = tf.nn.bidirectional_dynamic_rnn(lstm_fw_cell,lstm_bw_cell, input, dtype=tf.float32)

第二個比較重要的步驟是構建注意力模塊,注意力機制其實就是衡量不同時間步(不同單詞)對最終預測的重要性,它的過程也就計算重要性並根據重要性合成上下文語義特徵兩部分。下圖展示了全局注意力的具體過程,它確定不同時間步的權重(alpha),並加權計算得出上下文向量(context vextor)。如果讀者希望詳細了解 Attention,查閱下圖的來源論文就好了,當然也可以跳過原理直接進入實戰部分~

選自論文:Notes on Deep Learning for NLP, arXiv: 1808.09772。

如下所示,模型主要根據前面雙向 LSTM 輸出的結果(output)與最終隱藏狀態之間的餘弦相似性計算怎樣為輸出結果 output 加權,加權得到的上下文向量 context 可進一步用於計算最終的預測結果。

# Attentionoutput = tf.concat([output[0], output[1]], 2) # output[0] : lstm_fw, output[1] : lstm_bwfinal_hidden_state = tf.concat([final_state[1][0], final_state[1][1]], 1) # final_hidden_state : [batch_size, n_hidden * num_directions(=2)]final_hidden_state = tf.expand_dims(final_hidden_state, 2) # final_hidden_state : [batch_size, n_hidden * num_directions(=2), 1]attn_weights = tf.squeeze(tf.matmul(output, final_hidden_state), 2) # attn_weights : [batch_size, n_step]soft_attn_weights = tf.nn.softmax(attn_weights, 1)context = tf.matmul(tf.transpose(output, [0, 2, 1]), tf.expand_dims(soft_attn_weights, 2)) # context : [batch_size, n_hidden * num_directions(=2), 1]context = tf.squeeze(context, 2) # [batch_size, n_hidden * num_directions(=2)]model = tf.matmul(context, out)

當然,實際上這個模型還有更多關於損失函數、最優化器和訓練過程等模塊的定義,感興趣的讀者可以在 Colab 上跑一跑。

Transformer

機器之心曾解讀過基於 TensorFlow 的 Transformer 代碼,總體而言代碼量還是比較大的,其中包括了各模塊的可視化與預處理過程。對 Transformer 原理及實現代碼感興趣的讀者可查閱以下文章:

基於注意力機制,機器之心帶你理解與訓練神經機器翻譯系統

Transformer 比較重要的結構主要是經過縮放的點乘注意力和 Multi-head 注意力,其它前饋網絡、位置編碼等結構主要起到協助作用,它們共同可以構建 Transformer。在 Tae Hwan Jung 的實現中,他只使用了兩百行代碼就完成了核心過程,而且大量使用類和實例的結構更能理清整體架構。這一部分主要介紹點乘注意力和 Multi-head 注意力兩個類。

首先對於點乘注意力,它率先形式化地定義了整個注意力過程,過程和上面雙向 LSTM 案例使用的注意力機制基本差不多,只不過 Transformer 會有一個縮放過程。如下所示,scores 即表示模型對輸入(Value/V)所加的權重,最後算出來的為上下文信息 context。

classScaledDotProductAttention(nn.Module):def__init__(self): super(ScaledDotProductAttention, self).__init__()defforward(self, Q, K, V, attn_mask=None): scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k) # scores : [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]if attn_mask isnotNone: scores.masked_fill_(attn_mask, -1e9) attn = nn.Softmax(dim=-1)(scores) context = torch.matmul(attn, V)return context, attn

最後,下圖展示了 Transformer 中所採用的 Multi-head Attention 結構,它其實就是多個點乘注意力並行地處理並最後將結果拼接在一起。一般而言,我們可以對三個輸入矩陣 Q、V、K 分別進行 h 個不同的線性變換,然後分別將它們投入 h 個點乘注意力函數並拼接所有的輸出結果。

選自:Attention Is All You Need, arXiv: 1706.03762。

最後核心的 MultiHeadAttention 同樣很精簡,讀者可以感受一下:

classMultiHeadAttention(nn.Module):def__init__(self): super(MultiHeadAttention, self).__init__() self.W_Q = nn.Linear(d_model, d_k * n_heads) self.W_K = nn.Linear(d_model, d_k * n_heads) self.W_V = nn.Linear(d_model, d_v * n_heads)defforward(self, Q, K, V, attn_mask=None):# q: [batch_size x len_q x d_model], k: [batch_size x len_k x d_model], v: [batch_size x len_k x d_model] residual, batch_size = Q, Q.size(0)# (B, S, D) -proj-> (B, S, D) -split-> (B, S, H, W) -trans-> (B, H, S, W) q_s = self.W_Q(Q).view(batch_size, -1, n_heads, d_k).transpose(1,2) # q_s: [batch_size x n_heads x len_q x d_k] k_s = self.W_K(K).view(batch_size, -1, n_heads, d_k).transpose(1,2) # k_s: [batch_size x n_heads x len_k x d_k] v_s = self.W_V(V).view(batch_size, -1, n_heads, d_v).transpose(1,2) # v_s: [batch_size x n_heads x len_k x d_v]if attn_mask isnotNone: # attn_mask : [batch_size x len_q x len_k] attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1) # attn_mask : [batch_size x n_heads x len_q x len_k]# context: [batch_size x n_heads x len_q x d_v], attn: [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)] context, attn = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask=attn_mask) context = context.transpose(1, 2).contiguous().view(batch_size, -1, n_heads * d_v) # context: [batch_size x len_q x n_heads * d_v] output = nn.Linear(n_heads * d_v, d_model)(context)return nn.LayerNorm(d_model)(output + residual), attn # output: [batch_size x len_q x d_model]

本文為機器之心報導,轉載請聯繫本公眾號獲得授權

相關焦點

  • 【乾貨】NLP中「詞袋」模型和詞嵌入模型的比較(附代碼)
    • 詞袋模型中的三種基本方法  • 我們如何通過幾行代碼就可以建立詞袋模型?一. 為什麼詞嵌入可以直接而輕鬆地解決問題?在以下情況下,您更傾向使用詞袋模型而不是詞嵌入:1. 建立一個基線(baseline)模型。通過使用scikit-learn,只需要幾行代碼就可以構建模型。
  • 用Spark-NLP建立文本分類模型
    你可以從這個連結下載數據。下載以下數據後,使用spark代碼加載;https://www.kaggle.com/yufengdev/bbc-text-categorization?現在我們的NLP管道已經準備好了,讓我們根據訓練數據訓練我們的模型。
  • NLP詳細教程:手把手教你用ELMo模型提取文本特徵(附代碼&論文)
    在python中應用ELMo模型進行文本分類:理解問題陳述數據集介紹導入庫導入和檢查數據文本清洗和預處理簡要介紹TensorFlow Hub準備ELMo模型向量構建模型並評估5. 我們還能用ELMo做什麼?6.
  • 幾行代碼搞定ML模型,低代碼機器學習Python庫正式開源
    使用命令行界面或 notebook 環境,運行下面的代碼進行安裝:pip install pycaretAzure notebook 和 Google Colab 用戶,可以運行下列代碼進行安裝:!PyCaret 有 60 多個開源即用型算法。模型調優tune_model 函數用於自動調優機器學習模型的超參數。PyCaret 在預定義的搜索空間上使用隨機網格搜索。此函數返回具有 k 折交叉驗證分數和訓練好的模型對象的表格。
  • 獨家 | NLP詳細教程:手把手教你用ELMo模型提取文本特徵(附代碼&論文)
    建議你查看ELMo的初始論文(https://arxiv.org/pdf/1802.05365.pdf)。通常我不會建議大家去讀學術論文因為它們往往又長又複雜,但這篇論文不同,它很好地解釋了ELMo原理和設計過程。2. 理解ELMo工作原理在實踐之前讓我們需要先直觀了解一下ELMo是如何運作的。為什麼說這一步很重要?
  • 提升效率,幾行代碼輕鬆搞定模型
    (low-code)機器學習庫,支持在「低代碼」環境中訓練和部署有監督以及無監督的機器學習模型,提升機器學習實驗的效率。現在,讓我們一起來領略下:如何用僅僅幾行代碼搞定一個機器學習模型吧。首發 PyCaret 1.0.0我們很高興能宣布PyCaret,這是一個使用Python的開源機器學習庫,用於在Windows上訓練和部署有監督和無監督的機器學習模型低碼環境。通過PyCaret,您可以在選擇筆記本電腦環境後的幾秒鐘內,從準備數據到部署模型。
  • 支持53種語言預訓練模型,斯坦福發布全新NLP工具包StanfordNLP
    或者,你還可以從該 git repo 中安裝 StanfordNLP,這樣你可以更加靈活地基於 StanfordNLP 開發,以及訓練自己的模型。該項目提供另一個 demo 腳本,展示如何使用 CoreNLP 客戶端以及如何從中提取不同的標註。
  • 教程 | 利用AllenNLP,百行Python代碼訓練情感分類器
    >機器之心編譯參與:Geek AI、路本文介紹了如何利用 AllenNLP,使用不到一百行代碼訓練情感分類器。要想正確地對上述例句的極性進行分類,你需要理解否定詞(neither ... nor ...)對語義的影響。由於 SST 具備這樣的特性,它被用作獲取句子句法結構的神經網絡模型的標準對比基準(https://nlp.stanford.edu/~socherr/EMNLP2013_RNTN.pdf)。
  • 谷歌開源NLP模型可視化工具LIT,模型訓練不再「黑箱」
    假如有一款可視化的工具,能夠幫助研究人員更好地理解模型行為,這應該是件非常棒的事。近日,Google 研究人員發布了一款語言可解釋性工具 (Language Interpretability Tool, LIT),這是一個開源平臺,用於可視化和理解自然語言處理模型。
  • 計算機如何理解我們的語言?NLP is fun!
    如果計算機想要更好的理解人類的語言,擁有更好的人機互動體驗,都離不開 NLP。那麼,計算機到底是如何理解人類語言的?接下來讓我們跟著作者 Adam Geitgey ,和他一起體會自然語言處理技術裡那些有意思的事情。計算機非常擅長處理像電子表格、資料庫這樣的結構化數據。但是,人與人之間是用語言來交流的,而不是用表格。
  • NLP領域最優秀的8個預訓練模型(附開源地址)
    自然語言處理應用能夠快速增長,很大程度上要歸功於通過預訓練模型實現遷移學習的概念。在本文中,我將介紹一些頂級的預訓練模型,你可以用它們來開始你的自然語言處理之旅,並複製該領域的最新研究成果。如今,自然語言處理(Natural Language Processing,NLP)應用已經變得無處不在。
  • 手把手教你用seq2seq模型創建數據產品(附代碼)
    本文將教你如何使用Keras和TensorFlow來對Github項目進行文本摘要和特徵提取。目標:訓練一個模型來對Github項目進行總結其中所蘊含的思想是通過多個問題的描述和標題一個模型能夠學習到如何去總結、概括新的問題。如果你並非Github的員工,那麼獲取Github上的數據最好的方法是利用這個出色的開源項目(https://www.githubarchive.org/),它被描述為:「一個記錄、存檔Github公開時間軸並使之可簡易應用於分析的項目。」本文的附錄提供了使用這個項目獲取數據的教程。
  • 這套1600贊的NLP課程已開放,面向實戰,視頻代碼都有丨資源
    既然偏向實戰,那代碼必須全。這套課程用的是PyTorch和fast.a庫所有的Python代碼都在Jupyter Notebook中,還有全套視頻與你為伴。服用指南直接看課程裡面有什麼乾貨。這項工作已經被BERT、GPT-2和XLNet等模型採用,在這一節中,主要分享了構建英語以外其他語言模型的技巧,包括用ULMFit建立越南語和土耳其語語言模型的技巧。
  • GitHub 最受歡迎的 NLP 相關項目 | 資源推薦
    無論是入門,還是精進 NLP ,這些項目足以滿足你的需求!收藏本文慢慢學習吧。NLP-progresshttps://github.com/sebastianruder/NLP-progress跟蹤 NLP 最新進展。
  • 谷歌開源 BERT 模型原始碼
    驚鴻一瞥後,人們都在期待谷歌何時會放出 BERT 模型原始碼。直至今日,谷歌終於一鼓作氣發布了包括 BERT 模型 TensorFlow 代碼、BERT-Base 與 BERT-Large 模型的預訓練檢查點、微調實驗結果的自動化復現 TensorFlow 代碼、預訓練數據生成和數據訓練的代碼在內的「BERT 模型大禮包」。
  • 史丹福大學發布 StanfordNLP,支持多種語言
    還有一個辦法,是從 github 存儲庫的原始碼安裝,這可以使基於 StanfordNLP 的開發和模型訓練具有更大的靈活性。雷鋒網git clone git@github.com:stanfordnlp/stanfordnlp.gitcd stanfordnlppip install -e .
  • 一文學會最常見的10種NLP處理技術(附資源&代碼)
    詞向量化是用一組實數構成的向量代表自然語言的叫法。這種技術非常實用,因為電腦無法處理自然語言。詞向量化可以捕捉到自然語言和實數間的本質關係。通過詞向量化,一個詞語或者一段短語可以用一個定維的向量表示,例如向量的長度可以為100。例如:「Man」這個詞語可以用一個五維向量表示。這裡的每個數字代表了詞語在某個特定方向上的量級。
  • NLP入門+實戰必讀:一文教會你最常見的10種自然語言處理技術(附代碼)
    在這篇文章中,你將學習到最常見的10個NLP任務,以及相關資源和代碼。對於處理NLP問題,我也研究了一段時日。這期間我需要翻閱大量資料,通過研究報告,博客和同類NLP問題的賽事內容學習該領域的最新發展成果,並應對NLP處理時遇到的各類狀況。因此,我決定將這些資源集中起來,打造一個對NLP常見任務提供最新相關資源的一站式解決方案。
  • 深度學習的NLP工具
    有一天早上他看到了Google 最著名的 論文「注意力是你全部需要的」,其中介紹了Transformer 模型,完全基於注意力機制。被結果驚豔到了,Alex搜索了一下google,結果告訴他T恩送染髮咯哇的 Tensor2Tensor包已經包含了這個模型的實現。他的實驗室裡充滿了快樂的尖叫;他應該可以在下午就復現出來結果,並且享受一個放鬆的晚上來和朋友在酒吧看世界盃!他還想。。。
  • 教你用Python進行自然語言處理(附代碼)
    首先,我們加載spaCy的管線,按照約定,它存儲在一個名為nlp的變量中。需要花幾秒鐘時間聲明該變量,因為spaCy預先將模型和數據加載到前端,以節省時間。實際上,這樣做可以提前完成一些繁重的工作,使得nlp解析數據時開銷不至於過大。 請注意,在這裡,我們使用的語言模型是英語,同時也有一個功能齊全的德語模型,在多種語言中均可實現標記化(將在下面討論)。