書いてる理由
- NLPをやりたい
- 機械学習・深層学習よる自然言語処理入門をやってて、その内容で使えるものを残す。
やったこと
前回の前処理を使ってロジスティック回帰でレビューのレート予測
参考
詳細
nlp_work/train_amazon_review.py at nlp_intro · ys201810/nlp_work · GitHub
アマゾンのレビューの日本語テキストファイルと同時に、レビュー点数が含まれるデータセットをダウンロードし、これを使って前処理から学習やってみる。
んー、データの読み込みと学習に時間かかってるので、コードの解説だけ。
データロード箇所
# 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とかと比較したい。