Выходной слой — функция мягкого максимума, подсчитывающая распределение выходных меток по 44 возможным вариантам.
Как обычно, мы воспользуемся оптимизатором Adam с настройками гиперпараметров по умолчанию, будем обучать сеть в течение 1000 эпох и применим пакетную нормализацию для регуляризации.
Сеть будет очень похожа на те, которые мы уже реализовали раньше. Самое сложное в создании разметчика частей речи — подготовка набора данных. Мы применим предварительно обученные векторные представления слов из Google News [81]. Набор содержит векторы для 3 миллионов слов и обучен примерно на 100 млрд слов. Для его чтения можно воспользоваться пакетом gensim Python. Устанавливаем его при помощи pip:
$ pip install gensim
Теперь можно загрузить векторы в память при помощи следующей команды:
from gensim.models import Word2Vec
model = Word2Vec.load_word2vec_format(‘/path/to/googlenews.bin', binary=True)
Проблема в том, что эта операция невероятно медленна: может занимать до часа времени в зависимости от быстродействия вашего компьютера. Чтобы не загружать в память полный набор данных при каждом запуске программы, особенно при отладке кода или экспериментах с гиперпараметрами, мы кэшируем нужный поднабор векторов на диске при помощи быстрой базы данных LevelDB [82]. Чтобы создать необходимые привязки в Python (что позволит обеспечить взаимодействие LevelDB и Python), введем следующую команду:
$ pip install leveldb
Как мы уже говорили, модель gensim содержит 3 миллионов слов, что превышает размер нашего набора данных. Для эффективности мы кэшируем векторы слов из нашего набора и игнорируем всё остальное. Чтобы понять, какие слова кэшировать, загрузим набор данных POS из задачи CoNLL-2000 [83].
$ wget http://www.cnts.ua.ac.be/conll2000/chunking/train.txt.gz
— O — | gunzip |
cut — f1,2 — d" " > pos.train.txt
$ wget http://www.cnts.ua.ac.be/conll2000/chunking/test.txt.gz
— O — | gunzip |
cut — f1,2 — d " " > pos.test.txt
Набор данных состоит из текста, отформатированного как последовательность строк, где первый элемент — слово, а второй — соответствующая ему часть речи. Вот первые несколько строк обучающего набора данных:
Confidence NN
in IN
the DT
pound NN
is VBZ
widely RB
expected VBN
to TO
take VB
another DT
sharp JJ
dive NN
if IN
trade NN
figures NNS
for IN
September NNP
,
due JJ
for IN
release NN
tomorrow NN
…
Чтобы сопоставить форматирование набора данных с моделью gensim, нужна предварительная обработка. Например, модель заменяет цифры знаками #, объединяет при необходимости отдельные слова в словосочетания (например, рассматривает New_York как одну единицу, а не два отдельных слова) и заменяет дефисы в исходных данных подчеркиваниями. Мы подвергаем набор данных предварительной обработке с помощью следующего кода (аналогичный используется и при обработке данных для обучения):
with open("/path/to/pos.train.txt") as f:
train_dataset_raw = f.readlines()
train_dataset_raw = [e.split() for e in
train_dataset_raw if len(e.split()) > 0]
counter = 0
while counter < len(train_dataset_raw):
pair = train_dataset_raw[counter]
if counter < len(train_dataset_raw) — 1:
next_pair = train_dataset_raw[counter + 1]
if (pair[0] + "_" + next_pair[0] in model) and (pair[1] == next_pair[1]): train_dataset.append([pair[0] + "_" +
next_pair[0], pair[1]])
counter += 2
continue
word = re.sub("\d", "#", pair[0])
word = re.sub("-", "_", word)
if word in model:
train_dataset.append([word, pair[1]])
counter += 1
continue
if "_" in word:
subwords = word.split("_")
for subword in subwords:
if not (subword.isspace() or len(subword) == 0):
train_dataset.append([subword, pair[1]])
counter += 1
continue
train_dataset.append([word, pair[1]])
counter += 1
with open(‘/path/to/pos.train.processed.txt', ‘w')
as train_file: for item in train_dataset:
train_file.write("%s\n" % (item[0] + " " + item[1]))
Теперь, подготовив данные к использованию, можно загрузить слова в LevelDB. Если слово или фраза присутствуют в модели gensim, мы кэшируем их в LevelDB. Если же нет, мы случайным образом выбираем вектор, который будет представлять слово или фразу, и кэшируем так, чтобы использовать тот же вектор при дальнейших вхождениях:
db = leveldb.LevelDB("data/word2vecdb")
counter = 0
for pair in train_dataset + test_dataset:
dataset_vocab[pair[0]] = 1
if pair[1] not in tags_to_index:
tags_to_index[pair[1]] = counter
index_to_tags[counter] = pair[1]
counter += 1
nonmodel_cache = {}
counter = 1
total = len(dataset_vocab.keys())
for word in dataset_vocab:
if counter % 100 == 0:
print "Inserted %d words out of %d total" % (counter, total)
if word in model:
db.Put(word, model[word])
elif word in nonmodel_cache:
db.Put(word, nonmodel_cache[word])
else:
print word
nonmodel_cache[word] = np.random.uniform(-0.25, 0.25, 300).
astype(np.float32)
db.Put(word, nonmodel_cache[word])
counter += 1
После запуска скрипта в первый раз мы можем просто загружать данные из базы, если она уже существует:
db = leveldb.LevelDB("data/word2vecdb")
with open("data/pos_data/pos.train.processed.txt") as f:
train_dataset = f.readlines()
train_dataset = [element.split() for element in
train_dataset if
len(element.split()) > 0]
with open("data/pos_data/pos.train.processed.txt") as f:
test_dataset = f.readlines()
test_dataset = [element.split() for element in test_dataset
Читать дальше
Конец ознакомительного отрывка
Купить книгу