テキスト前処理の話(part2)

書いてる理由

やったこと

前回の前処理を使ってロジスティック回帰でレビューのレート予測

参考

Amazon CAPTCHA

詳細

nlp_work/train_amazon_review.py at nlp_intro · ys201810/nlp_work · GitHub

アマゾンのレビューの日本語テキストファイルと同時に、レビュー点数が含まれるデータセットをダウンロードし、これを使って前処理から学習やってみる。

github.com

んー、データの読み込みと学習に時間かかってるので、コードの解説だけ。

データロード箇所

# Amazonのレビューのテキストを引数に、テキストの日本語の割合が90%を超えるものだけにしてテキストとレビュー点数をreturn。
def load_dataset(data_file, n=5000, state=6):
    df = pd.read_csv(data_file, sep='\t')
    is_jp = df.review_body.apply(filter_by_ascii_rate)  # 取得したテキストが日本語かどうかを判断して、True/Falseの配列を作成。
    df = df[is_jp]  # True:日本語レビューのみを残す。

    df = df.sample(frac=1, random_state=state)
    grouped = df.groupby('star_rating')
    df = grouped.head(n=n)
    return df.review_body.values, df.star_rating.values

# df['review_body']の1行ずつに90%以上日本語だったらの条件を当てるための関数
def filter_by_ascii_rate(text, threshould=0.9):
    """ asciiコードの集合体で対象のレビューの文章がその集合体に含まれる数 / テキスト全体のながさ でこの割合が一定以上なら日本語レビュー判断 """
    ascii_letters = set(string.printable)
    rate = sum(c in ascii_letters for c in text) / len(text)
    return rate <= threshould

データ分割箇所

from sklearn.model_selection import train_test_split

# 天下の宝刀train_test_splitで学習データ80%, 検証用データ20%に分割
train_texts, test_texts, train_rating, test_rating = train_test_split(texts, rating, test_size=0.2, random_state=1)

学習箇所

# 出現した単語のインデックシングをするCountVectorizerで今回のデータの辞書({0: "これ", 1: "私"}みたいなイメージ)を作り、それらのインデックスのベクトルで学習/予測

def train_and_eval(x_train, y_train, x_test, y_test, lowercase=False, tokenize=None, preprocesser=None):
    from sklearn.feature_extraction.text import CountVectorizer
    from sklearn.linear_model import LogisticRegression
    from sklearn.metrics import accuracy_score

    vectorizer = CountVectorizer(lowercase=lowercase, tokenizer=tokenize, preprocessor=preprocesser)
    x_train_vec = vectorizer.fit_transform(x_train)
    x_test_vec = vectorizer.transform(x_test)

    classifier = LogisticRegression(solver='liblinear')
    classifier.fit(x_train_vec, y_train)
    y_pred = classifier.predict(x_test_vec)
    score = accuracy_score(y_test, y_pred)
    return score

前処理を色々指定して実行

    # tokenizeだけ
    print('{} accuracy:{}'.format('tokenize only', train_and_eval(train_texts, train_rating, test_texts, test_rating,
                                                                  tokenize=tokenize)))
    # tokenizeとタグを抜く
    print('{} accuracy:{}'.format('tokenize and html erase', train_and_eval(train_texts, train_rating, test_texts,
                                  test_rating, tokenize=tokenize, preprocessor=html_preprocess)))
    # tokenizeと数字を0に置き換え
    print('{} accuracy:{}'.format('tokenize and number erase', train_and_eval(train_texts, train_rating, test_texts,
                                  test_rating, tokenize=tokenize, preprocessor=number_0_change)))
    # tokenizeして原型に変換だけ
    print('{} accuracy:{}'.format('tokenize base form', train_and_eval(train_texts, train_rating, test_texts,
                                  test_rating, tokenize=tokenize_base_form)))
    # tokenizeして原型に変換して小文字に整える
    print('{} accuracy:{}'.format('tokenize base form', train_and_eval(train_texts, train_rating, test_texts,
                                  test_rating, tokenize=tokenize_base_form, lowercase=True)))

感想

ロジスティック回帰でレート予測するためのデータ加工系が結構学べた。
精度はまだ学習中なのでわからない(大したことない)から置いといて、CountVectorizerからのfit_transformでのベクトル化とかが簡単で良いなぁ。
単語のベクトル表現がone-hot的な感じなのは楽だけど荒らそう。
ゆくゆくword2vecとかと比較したい。