由 Unsplash 的 Joao Tzanno 拍攝html
在 天然語言處理 中,詞性標註是一件衆所周知的任務。它指的是將單詞按詞性分類(也稱爲詞類或詞性類別)。這是一種有監督的學習方法。前端
人工神經網絡 已成功應用於詞性標註,而且表現卓越。咱們將重點關注多層感知器網絡,這是一種很是流行的網絡結構,被視爲解決詞性標註問題的最新技術。(譯者注:對於詞性標註問題,RNN 有更好的效果)python
讓咱們把它付諸實踐!android
在本文中,你將得到一個關於如何在 Keras 中實現簡單的多層感知器的快速教程,並在已標註的語料庫上進行訓練。ios
爲了保證咱們的實驗可以復現,咱們須要設置一個隨機種子:git
import numpy as np
CUSTOM_SEED = 42
np.random.seed(CUSTOM_SEED)
複製代碼
Penn Treebank 是一個詞性標註語料庫。Python 庫中有個示例 [NLTK](https://github.com/nltk/nltk)
包含可以用於訓練和測試某些天然語言處理模型(NLP models)的語料庫。github
首先,咱們下載已標註的語料庫:後端
import nltk
nltk.download('treebank')
複製代碼
而後咱們載入標記好的句子。api
from nltk.corpus import treebank
sentences = treebank.tagged_sents(tagset='universal')
複製代碼
而後咱們隨便挑個句子看看:網絡
import random
print(random.choice(sentences))
複製代碼
這是一個元組列表 (term, tag)
.
[('Mr.', 'NOUN'), ('Otero', 'NOUN'), (',', '.'), ('who', 'PRON'), ('apparently', 'ADV'), ('has', 'VERB'), ('an', 'DET'), ('unpublished', 'ADJ'), ('number', 'NOUN'), (',', '.'), ('also', 'ADV'), ('could', 'VERB'), ("n't", 'ADV'), ('be', 'VERB'), ('reached', 'VERB'), ('.', '.')]
複製代碼
這是一個包含四十多個不一樣類別的多分類問題。 Treebank 語料庫上的詞性標註是一個衆所周知的問題,咱們指望模型精度能超過 95%。
tags = set([
tag for sentence in treebank.tagged_sents()
for _, tag in sentence
])
print('nb_tags: %sntags: %s' % (len(tags), tags))
複製代碼
產生了一個:
46
{'IN', 'VBZ', '.', 'RP', 'DT', 'VB', 'RBR', 'CC', '#', ',', 'VBP', 'WP$', 'PRP', 'JJ',
'RBS', 'LS', 'PRP$', 'WRB', 'JJS', '``', 'EX', 'POS', 'WP', 'VBN', '-LRB-', '-RRB-',
'FW', 'MD', 'VBG', 'TO', '$', 'NNS', 'NNPS', "''", 'VBD', 'JJR', ':', 'PDT', 'SYM',
'NNP', 'CD', 'RB', 'WDT', 'UH', 'NN', '-NONE-'}
複製代碼
### 監督式學習的數據集預處理
咱們將標記的句子劃分紅 3 個數據集:
咱們使用大約 60% 的標記句子進行訓練,20% 做爲驗證集,20% 用於評估咱們的模型。
train_test_cutoff = int(.80 * len(sentences))
training_sentences = sentences[:train_test_cutoff]
testing_sentences = sentences[train_test_cutoff:]
train_val_cutoff = int(.25 * len(training_sentences))
validation_sentences = training_sentences[:train_val_cutoff]
training_sentences = training_sentences[train_val_cutoff:]
複製代碼
咱們的特徵集很是簡單。 對於每個單詞而言,咱們根據提取單詞的句子建立一個特徵字典。 這些屬性包含該單詞的先後單詞以及它的前綴和後綴。
def add_basic_features(sentence_terms, index):
""" 計算基本的單詞特徵 :param sentence_terms: [w1, w2, ...] :type sentence_terms: list :param index: the index of the word :type index: int :return: dict containing features :rtype: dict """
term = sentence_terms[index]
return {
'nb_terms': len(sentence_terms),
'term': term,
'is_first': index == 0,
'is_last': index == len(sentence_terms) - 1,
'is_capitalized': term[0].upper() == term[0],
'is_all_caps': term.upper() == term,
'is_all_lower': term.lower() == term,
'prefix-1': term[0],
'prefix-2': term[:2],
'prefix-3': term[:3],
'suffix-1': term[-1],
'suffix-2': term[-2:],
'suffix-3': term[-3:],
'prev_word': '' if index == 0 else sentence_terms[index - 1],
'next_word': '' if index == len(sentence_terms) - 1 else sentence_terms[index + 1]
}
複製代碼
咱們將句子列表映射到特徵字典列表。
def untag(tagged_sentence):
""" 刪除每一個標記詞語的標籤。 :param tagged_sentence: 已標註的句子 :type tagged_sentence: list :return: a list of tags :rtype: list of strings """
return [w for w, _ in tagged_sentence]
def transform_to_dataset(tagged_sentences):
""" 將標註的句子切分爲 X 和 y 以及添加一些基本特徵 :param tagged_sentences: 已標註的句子列表 :param tagged_sentences: 元組列表之列表 (term_i, tag_i) :return: """
X, y = [], []
for pos_tags in tagged_sentences:
for index, (term, class_) in enumerate(pos_tags):
# Add basic NLP features for each sentence term
X.append(add_basic_features(untag(pos_tags), index))
y.append(class_)
return X, y
複製代碼
對於訓練、驗證和測試句子,咱們將屬性分爲 X(輸入變量)和 y(輸出變量)。
X_train, y_train = transform_to_dataset(training_sentences)
X_test, y_test = transform_to_dataset(testing_sentences)
X_val, y_val = transform_to_dataset(validation_sentences)
複製代碼
咱們的神經網絡將向量做爲輸入,因此咱們須要將咱們的字典特徵轉換爲向量。 sklearn
的內建函數 [DictVectorizer](http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.DictVectorizer.html)
提供一種很是直接的方法進行向量轉換。
from sklearn.feature_extraction import DictVectorizer
# 用咱們的特徵集擬合字典向量生成器
dict_vectorizer = DictVectorizer(sparse=False)
dict_vectorizer.fit(X_train + X_test + X_val)
# 將字典特徵轉換爲向量
X_train = dict_vectorizer.transform(X_train)
X_test = dict_vectorizer.transform(X_test)
X_val = dict_vectorizer.transform(X_val)
複製代碼
咱們的 y 向量必須被編碼。輸出變量包含 49 個不一樣的字符串值,它們被編碼爲整數。
from sklearn.preprocessing import LabelEncoder
# 用類別列表訓練標籤編碼器
label_encoder = LabelEncoder()
label_encoder.fit(y_train + y_test + y_val)
# 將類別值編碼成整數
y_train = label_encoder.transform(y_train)
y_test = label_encoder.transform(y_test)
y_val = label_encoder.transform(y_val)
複製代碼
而後咱們須要將這些編碼值轉換爲虛擬變量(獨熱編碼)。
# 將整數轉換爲虛擬變量(獨熱編碼)
from keras.utils import np_utils
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
y_val = np_utils.to_categorical(y_val)
複製代碼
[Keras](https://github.com/fchollet/keras/)
是一個高級框架,用於設計和運行神經網絡,它擁有多個後端像是 [TensorFlow](https://github.com/tensorflow/tensorflow/)
, [Theano](https://github.com/Theano/Theano)
以及 [CNTK](https://github.com/Microsoft/CNTK)
。
咱們想建立一個最基本的神經網絡:多層感知器。這種線性疊層能夠經過序貫(Sequential
)模型輕鬆完成。該模型將包含輸入層,隱藏層和輸出層。 爲了克服過擬合,咱們使用 dropout 正則化。咱們設置斷開率爲 20%,這意味着在訓練過程當中每次更新參數時按 20% 的機率隨機斷開輸入神經元。
咱們對隱藏層使用 Rectified Linear Units (ReLU) 激活函數,由於它們是可用的最簡單的非線性激活函數。
對於多分類問題,咱們想讓神經元輸出轉換爲機率,這可使用 softmax 函數完成。咱們決定使用多分類交叉熵(categorical cross-entropy)損失函數。 最後咱們選擇 Adam optimizer 由於彷佛它很是適合分類任務.
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
def build_model(input_dim, hidden_neurons, output_dim):
""" 構建、編譯以及返回一個用於擬合/預測的 Keras 模型。 """
model = Sequential([
Dense(hidden_neurons, input_dim=input_dim),
Activation('relu'),
Dropout(0.2),
Dense(hidden_neurons),
Activation('relu'),
Dropout(0.2),
Dense(output_dim, activation='softmax')
])
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
複製代碼
[Keras](https://github.com/fchollet/keras/)
提供了一個名爲 [KerasClassifier](https://keras.io/scikit-learn-api/)
的包裝器。它實現了 Scikit-Learn 分類器接口。
全部的模型參數定義以下。咱們須要提供一個返回神經網絡結構的函數 (build_fn
)。 隱藏的神經元的數量和批量大小的選擇很是隨意。咱們將迭代次數設置爲 5,由於隨着迭代次數增多,多層感知器就會開始過擬合(即便用了 Dropout Regularization)。
from keras.wrappers.scikit_learn import KerasClassifier
model_params = {
'build_fn': build_model,
'input_dim': X_train.shape[1],
'hidden_neurons': 512,
'output_dim': y_train.shape[1],
'epochs': 5,
'batch_size': 256,
'verbose': 1,
'validation_data': (X_val, y_val),
'shuffle': True
}
clf = KerasClassifier(**model_params)
複製代碼
最後,咱們在訓練集上訓練多層感知器。
hist = clf.fit(X_train, y_train)
複製代碼
經過回調歷史(callback history),咱們可以可視化模型的 log loss 和 accuracy 隨時間的變化。
import matplotlib.pyplot as plt
def plot_model_performance(train_loss, train_acc, train_val_loss, train_val_acc):
""" 繪製模型損失和準確度隨時間變化的曲線 """
blue= '#34495E'
green = '#2ECC71'
orange = '#E23B13'
# 繪製模型損失曲線
fig, (ax1, ax2) = plt.subplots(2, figsize=(10, 8))
ax1.plot(range(1, len(train_loss) + 1), train_loss, blue, linewidth=5, label='training')
ax1.plot(range(1, len(train_val_loss) + 1), train_val_loss, green, linewidth=5, label='validation')
ax1.set_xlabel('# epoch')
ax1.set_ylabel('loss')
ax1.tick_params('y')
ax1.legend(loc='upper right', shadow=False)
ax1.set_title('Model loss through #epochs', color=orange, fontweight='bold')
# 繪製模型準確度曲線
ax2.plot(range(1, len(train_acc) + 1), train_acc, blue, linewidth=5, label='training')
ax2.plot(range(1, len(train_val_acc) + 1), train_val_acc, green, linewidth=5, label='validation')
ax2.set_xlabel('# epoch')
ax2.set_ylabel('accuracy')
ax2.tick_params('y')
ax2.legend(loc='lower right', shadow=False)
ax2.set_title('Model accuracy through #epochs', color=orange, fontweight='bold')
複製代碼
而後,看看模型的性能:
plot_model_performance(
train_loss=hist.history.get('loss', []),
train_acc=hist.history.get('acc', []),
train_val_loss=hist.history.get('val_loss', []),
train_val_acc=hist.history.get('val_acc', [])
)
複製代碼
模型性能隨迭代次數的變化。
兩次迭代以後,咱們發現模型過擬合。
因爲咱們模型已經訓練好了,因此咱們能夠直接評估它:
score = clf.score(X_test, y_test)
print(score)
[Out] 0.95816
複製代碼
咱們在測試集上的準確率接近 96%,當你查看咱們在模型中輸入的基本特徵時,這一點使人印象很是深入。 請記住,即便對於人類標註者來講,100% 的準確性也是不可能的。咱們估計人類詞性標註的準確度大概在 98%。
from keras.utils import plot_model
plot_model(clf.model, to_file='model.png', show_shapes=True)
複製代碼
保存 Keras 模型很是簡單,由於 Keras 庫提供了一種本地化的方法:
clf.model.save('/tmp/keras_mlp.h5')
複製代碼
保存了模型的結構,權重以及訓練配置(損失函數,優化器)。
Keras
: Python 深度學習庫:[doc]在本文中,您學習如何使用 Keras 庫定義和評估用於多分類的神經網絡的準確性。 代碼在這裏:[.py|.ipynb].
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。