NLP 利器 Gensim 中 word2vec 模型的訓練損失計算,和對比基準的選擇

本文為系列文章之一,前面的幾篇請點擊鏈接:
NLP 利器 gensim 庫基本特性介紹和安裝方式
NLP 利器 Gensim 庫的使用之 Word2Vec 模型案例演示
NLP 利器 Gensim 來訓練自己的 word2vec 詞向量模型
NLP 利器 Gensim 來訓練 word2vec 詞向量模型的參數設置
NLP 利器 Gensim 中 word2vec 模型的記憶體需求,和模型評估方式
NLP 利器 Gensim 中 word2vec 模型的恢復訓練:載入存儲模型並繼續訓練


一、訓練損失計算

通過設置 compute_loss 可以設定是否計算 loss,然後訓練的時候就會自動計算 loss 了。

計算完成的 loss 被存儲在 running_training_loss 這一屬性中。

我們可以通過 get_latest_training_loss 方法獲取。

# instantiating and training the Word2Vec model
model_with_loss = gensim.models.Word2Vec(
    sentences,
    min_count=1,
    compute_loss=True,
    hs=0,
    sg=1,
    seed=42
)

# getting the training loss value
training_loss = model_with_loss.get_latest_training_loss()
print(training_loss)

運行結果:

1371947.25

參數說明:

  • sg ({0**, 1}**, optional) – 訓練演算法: 1 是 skip-gram; 否則是 CBOW。
  • hs ({0**, 1}**, optional) – 1:使用分級 softmax,0:使用負取樣。
  • seed (int*,* optional) – 隨機數生成器的種子。

二、比較基準

我們可以運行一些比較基準,看訓練時候損失計算程式碼產生的影響。

用以下兩個數據集作為比較基準:

  • Lee Background 語料
  • Text8 語料(為了看不同語料大小的影響,會對前 1MB,10MB,50MB,以及整個語料數據進行評估)
import io
import os

import gensim.models.word2vec
import gensim.downloader as api
import smart_open


def head(path, size):
    with smart_open.open(path) as fin:
        return io.StringIO(fin.read(size))


def generate_input_data():
    lee_path = datapath('lee_background.cor')
    ls = gensim.models.word2vec.LineSentence(lee_path)
    ls.name = '25kB'
    yield ls

    text8_path = api.load('text8').fn
    labels = ('1MB', '10MB', '50MB', '100MB')
    sizes = (1024 ** 2, 10 * 1024 ** 2, 50 * 1024 ** 2, 100 * 1024 ** 2)
    for l, s in zip(labels, sizes):
        ls = gensim.models.word2vec.LineSentence(head(text8_path, s))
        ls.name = l
        yield ls


input_data = list(generate_input_data())

我們可以對比一下訓練時間的差別,使用不同的輸入數據和訓練參數設置。

對於每種組合,我們重複測試幾次,以獲得測試持續時間的平均值和標準偏差。

import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

# Temporarily reduce logging verbosity
logging.root.level = logging.ERROR

import time
import numpy as np
import pandas as pd

train_time_values = []
seed_val = 42
sg_values = [0, 1]
hs_values = [0, 1]

fast = True
if fast:
    input_data_subset = input_data[:3]
else:
    input_data_subset = input_data


for data in input_data_subset:
    for sg_val in sg_values:
        for hs_val in hs_values:
            for loss_flag in [True, False]:
                time_taken_list = []
                for i in range(3):
                    start_time = time.time()
                    w2v_model = gensim.models.Word2Vec(
                        data,
                        compute_loss=loss_flag,
                        sg=sg_val,
                        hs=hs_val,
                        seed=seed_val,
                    )
                    time_taken_list.append(time.time() - start_time)

                time_taken_list = np.array(time_taken_list)
                time_mean = np.mean(time_taken_list)
                time_std = np.std(time_taken_list)

                model_result = {
                    'train_data': data.name,
                    'compute_loss': loss_flag,
                    'sg': sg_val,
                    'hs': hs_val,
                    'train_time_mean': time_mean,
                    'train_time_std': time_std,
                }
                print("Word2vec model #%i: %s" % (len(train_time_values), model_result))
                train_time_values.append(model_result)

train_times_table = pd.DataFrame(train_time_values)
train_times_table = train_times_table.sort_values(
    by=['train_data', 'sg', 'hs', 'compute_loss'],
    ascending=[False, False, True, False],
)
print(train_times_table)

測試結果:

   train_data  compute_loss  sg  hs  train_time_mean  train_time_std
4        25kB          True   1   0         0.454762        0.012580
5        25kB         False   1   0         0.439228        0.008768
6        25kB          True   1   1         0.951644        0.085889
7        25kB         False   1   1         0.951292        0.039065
0        25kB          True   0   0         0.240479        0.005060
1        25kB         False   0   0         0.250178        0.005493
2        25kB          True   0   1         0.405198        0.009490
3        25kB         False   0   1         0.408087        0.038706
12        1MB          True   1   0         1.655859        0.113092
13        1MB         False   1   0         1.680051        0.189026
14        1MB          True   1   1         3.492107        0.428911
15        1MB         False   1   1         3.093424        0.147582
8         1MB          True   0   0         0.619222        0.037100
9         1MB         False   0   0         0.705076        0.028860
10        1MB          True   0   1         1.168661        0.011428
11        1MB         False   0   1         1.058264        0.020380
20       10MB          True   1   0        24.787724        1.964204
21       10MB         False   1   0        21.996642        0.341242
22       10MB          True   1   1        43.136896        1.458909
23       10MB         False   1   1        46.764473        3.284557
16       10MB          True   0   0         8.531959        0.376124
17       10MB         False   0   0         8.237493        0.313006
18       10MB          True   0   1        16.261160        0.557908
19       10MB         False   0   1        17.740904        1.768709

總結:

  • 語料越大,訓練時間越長(顯然):10 倍語料,對應 13~15 倍訓練時間。
  • 計算 loss:影響不大。
  • 訓練演算法 sg:skip-gram(1)是 CBOW(0)訓練時間的 2.5 ~3 倍左右。
  • hs:使用分級 softmax(1)是採用負取樣(0)訓練時間的 1.8~2 倍左右。