ステップ 2: データを探索する

モデルの構築とトレーニングは、ワークフローの一部にすぎません。データの特性を事前に理解することで、より優れたモデルを構築できます。つまり、より高い精度が得られるということです。また、トレーニングに必要なデータが少なくなることや、計算リソースが少なくなる可能性もあります。

データセットを読み込む

まず、データセットを Python に読み込みましょう。

def load_imdb_sentiment_analysis_dataset(data_path, seed=123):
    """Loads the IMDb movie reviews sentiment analysis dataset.

    # Arguments
        data_path: string, path to the data directory.
        seed: int, seed for randomizer.

    # Returns
        A tuple of training and validation data.
        Number of training samples: 25000
        Number of test samples: 25000
        Number of categories: 2 (0 - negative, 1 - positive)

    # References
        Mass et al., http://www.aclweb.org/anthology/P11-1015

        Download and uncompress archive from:
        http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
    """
    imdb_data_path = os.path.join(data_path, 'aclImdb')

    # Load the training data
    train_texts = []
    train_labels = []
    for category in ['pos', 'neg']:
        train_path = os.path.join(imdb_data_path, 'train', category)
        for fname in sorted(os.listdir(train_path)):
            if fname.endswith('.txt'):
                with open(os.path.join(train_path, fname)) as f:
                    train_texts.append(f.read())
                train_labels.append(0 if category == 'neg' else 1)

    # Load the validation data.
    test_texts = []
    test_labels = []
    for category in ['pos', 'neg']:
        test_path = os.path.join(imdb_data_path, 'test', category)
        for fname in sorted(os.listdir(test_path)):
            if fname.endswith('.txt'):
                with open(os.path.join(test_path, fname)) as f:
                    test_texts.append(f.read())
                test_labels.append(0 if category == 'neg' else 1)

    # Shuffle the training data and labels.
    random.seed(seed)
    random.shuffle(train_texts)
    random.seed(seed)
    random.shuffle(train_labels)

    return ((train_texts, np.array(train_labels)),
            (test_texts, np.array(test_labels)))

データを確認する

データを読み込んだ後は、データに対してチェックを実行することをおすすめします。いくつかのサンプルを選択し、期待値と一致するかどうかを手動でチェックします。たとえば、いくつかのサンプルをランダムに出力し、感情ラベルがクチコミの感情に対応しているかどうかを確認します。IMDb データセットからランダムに選択したレビューは次のとおりです。「10 分間分の記事が 2 時間のうちにかなり長く延びました。ハーフポイントで有意なものが何も起こらなかった場合は、そのままにしておかなければなりません。」期待される感情(ネガティブ)は、サンプルのラベルと一致する。

主な指標の収集

データを確認したら、テキスト分類の問題を特定するために、次の重要な指標を収集します。

  1. サンプル数: データに含まれるサンプルの合計数。

  2. クラス数: データに含まれるトピックまたはカテゴリの合計数。

  3. クラスあたりのサンプル数: クラスあたりのサンプル数(トピック/カテゴリ)バランスの取れたデータセットの場合、すべてのクラスのサンプル数は似ていますが、不均衡なデータセットの場合、各クラスのサンプルの数は大きく異なります。

  4. サンプルあたりの単語数: 1 つのサンプル内の単語数の中央値。

  5. 単語の頻度の分布: データセット内の各単語の頻度(出現回数)を示す分布。

  6. サンプル長の分布: データセット内のサンプルあたりの単語数を示す分布。

指標の値が IMDb レビュー用データセットでどのような値になるかを見てみましょう(図の3および4で、単語頻度とサンプル長の分布のプロット)。

指標名 指標値
サンプルの数 25000
クラスの数 2
クラスあたりのサンプル数 12500
サンプルあたりの単語数 174

表 1: IMDb によるデータセット指標の確認

explore_data.py には、これらの指標の計算と分析を行う関数が含まれています。いくつか例を挙げましょう。

import numpy as np
import matplotlib.pyplot as plt

def get_num_words_per_sample(sample_texts):
    """Returns the median number of words per sample given corpus.

    # Arguments
        sample_texts: list, sample texts.

    # Returns
        int, median number of words per sample.
    """
    num_words = [len(s.split()) for s in sample_texts]
    return np.median(num_words)

def plot_sample_length_distribution(sample_texts):
    """Plots the sample length distribution.

    # Arguments
        samples_texts: list, sample texts.
    """
    plt.hist([len(s) for s in sample_texts], 50)
    plt.xlabel('Length of a sample')
    plt.ylabel('Number of samples')
    plt.title('Sample length distribution')
    plt.show()

IMDb の単語の頻度の分布

図 3: IMDb の単語の頻度の分布

IMDb のサンプル長の分布

図 4: IMDb のサンプル長の分布