<jupyter_start><jupyter_text>機械学習ハンズオン（自然言語処理編1）  1. ハンズオンの概要[IMDbのデータセット](https://ai.stanford.edu/~amaas/data/sentiment/)を使って、映画のレビュー文章から、そのレビューが肯定的か否定的かを判定する二値分類を行います。このハンズオンの流れは次の通りです。 1. データの取得 1. テキストの前処理 1. 学習モデルの作成・評価  2. 事前準備  2.1. ライブラリのロード必要なライブラリをimportします。Google Colabでは、これらのライブラリはすべてインストール済なので、改めてインストールする必要はありません。<jupyter_code>import re

from bs4 import BeautifulSoup
from gensim.models import word2vec
import matplotlib.pyplot as plt
import nltk
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.manifold import TSNE
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.model_selection import train_test_split<jupyter_output><empty_output><jupyter_text>2.2. NLTKの利用準備 後で利用するNLTKのAPIで必要なデータをあらかじめダウンロードしておきます。<jupyter_code>nltk.download('stopwords')
nltk.download('wordnet')<jupyter_output><empty_output><jupyter_text>3. データ取得  3.1. データのダウンロード・展開データ作成処理の簡略化のため、データ作成者が公開しているデータを使うのではなく、githubで公開されている別のデータファイルをダウンロードして使用することにします。 ダウンロード<jupyter_code>!wget https://github.com/aaronkub/machine-learning-examples/raw/master/imdb-sentiment-analysis/movie_data.tar.gz<jupyter_output><empty_output><jupyter_text>ファイル展開<jupyter_code>!tar zxvf movie_data.tar.gz<jupyter_output><empty_output><jupyter_text>3.2. データの読込pandasのAPIを使ってデータファイルを読みます。ヘッダーは自分で設定します。<jupyter_code>train_df = pd.read_csv('./movie_data/full_train.txt', sep='\t', names=['review'])
train_df.head(5)<jupyter_output><empty_output><jupyter_text>レビューの文章を一つ見てみましょう。<jupyter_code>train_df['review'][7]<jupyter_output><empty_output><jupyter_text>3.3. ラベル作成データファイルのうち、前半の12,500行が肯定的なレビュー、後半の12,500行が否定的なレビューです。肯定的なレビューには1、否定的なレビューには0のラベルを付与します。<jupyter_code>Ys = np.array([1 if i < 12500 else 0 for i in range(25000)])<jupyter_output><empty_output><jupyter_text>4. テキストの前処理  4.1. テキストのクリーニングここでは次の処理を行います。 * 不要な要素の除去   * HTMLタグ（BeautifulSoupというライブラリを使います）   * 角かっこ(`[ ]`)で囲まれた部分   * 英字以外の文字 * 小文字化<jupyter_code>def clean_text(text):
  text = BeautifulSoup(text, 'html.parser').get_text()
  text = re.sub('\[[^]]*\]', ' ', text)
  text = re.sub('[^a-zA-Z]', ' ', text)
  text = text.lower()
  return text
train_df['review'] = train_df['review'].apply(clean_text)
train_df.head(5)<jupyter_output><empty_output><jupyter_text>サンプルをみてみましょう<jupyter_code>train_df['review'][7]<jupyter_output><empty_output><jupyter_text>4.2. ストップワードの除去頻繁に登場しすぎて特徴にするには不適切な単語を除去します。除去の候補となる単語は、NLTKに登録済みのものとします。<jupyter_code>english_stopwords = nltk.corpus.stopwords.words('english')
def remove_stopwords(text):
  return ' '.join([word for word in text.split() if word not in english_stopwords])
train_df['review'] = train_df['review'].apply(remove_stopwords)
train_df.head(5)
train_df['review'][7]<jupyter_output><empty_output><jupyter_text>4.3. レンマ化単語を原型に変換します。<jupyter_code>lemmatizer = nltk.stem.WordNetLemmatizer()
def lemmatize(text):
  return ' '.join([lemmatizer.lemmatize(word) for word in text.split()])
train_df['review'] = train_df['review'].apply(lemmatize)
train_df.head(5)
train_df['review'][7]<jupyter_output><empty_output><jupyter_text>5. 特徴量の生成 → 学習 →評価前処理したテキストから各テキストの特徴量を生成し、それを学習させ、精度を評価しましょう。ここでは次の手法の評価をします。 * 単語カウント * TF-IDF * word2vec  5.1. 単語カウントscikit-learnの`CountVectorizer`を使って特徴量を生成します。 特徴量生成<jupyter_code>cv = CountVectorizer()
Xs = cv.fit_transform(train_df['review'].values)
Xs[0]<jupyter_output><empty_output><jupyter_text>訓練データと検証データに分割<jupyter_code>X_train, X_val, y_train, y_val = train_test_split(
  Xs, Ys, train_size = 0.8
)<jupyter_output><empty_output><jupyter_text>学習・評価（学習率を変えて6パターンで実験）<jupyter_code>learning_rates = [0.01, 0.03, 0.1, 0.3, 1.0, 3.0]
for lr in learning_rates:
  model = LogisticRegression(C=lr, max_iter=1000)
  model.fit(X_train, y_train)
  print ("Accuracy for C=%s: %s" % (lr, accuracy_score(y_val, model.predict(X_val))))

len(model.coef_[0])<jupyter_output><empty_output><jupyter_text>係数の大きい／小さい単語を確認<jupyter_code>words = cv.get_feature_names()
coefs = {word: coef for word, coef in zip(words, model.coef_[0])}
for best_positive in sorted(coefs.items(), key=lambda x: x[1], reverse=True)[:5]:
    print (best_positive)
for best_negative in sorted(coefs.items(), key=lambda x: x[1], reverse=False)[:5]:
    print (best_negative)<jupyter_output><empty_output><jupyter_text>5.2. TF-IDFscikit-learnの`TfidfVectorizer`を使って特徴量を生成します。 特徴量生成<jupyter_code>tfidf = TfidfVectorizer()
Xs = tfidf.fit_transform(train_df['review'].values)
Xs[0]<jupyter_output><empty_output><jupyter_text>訓練データと検証データに分割<jupyter_code>X_train, X_val, y_train, y_val = train_test_split(
  Xs, Ys, train_size = 0.8
)<jupyter_output><empty_output><jupyter_text>学習・評価 （学習率を変えて6パターンで実験）<jupyter_code>learning_rates = [0.01, 0.03, 0.1, 0.3, 1.0, 3.0]
for lr in learning_rates:
  model = LogisticRegression(C=lr, max_iter=1000)
  model.fit(X_train, y_train)
  print ("Accuracy for C=%s: %s" % (lr, accuracy_score(y_val, model.predict(X_val))))<jupyter_output><empty_output><jupyter_text>係数の大きい／小さい単語を確認<jupyter_code>words = cv.get_feature_names()
coefs = {word: coef for word, coef in zip(words, model.coef_[0])}
for best_positive in sorted(coefs.items(), key=lambda x: x[1], reverse=True)[:5]:
    print (best_positive)
for best_negative in sorted(coefs.items(), key=lambda x: x[1], reverse=False)[:5]:
    print (best_negative)<jupyter_output><empty_output><jupyter_text>5.3. word2vecword2vecを使って単語のベクトルを生成します。 単語ベクトル生成<jupyter_code>sentences = [text.split() for text in train_df['review'].values]
w2v = word2vec.Word2Vec(sentences,
                        size=100,
                        window=5,
                        min_count=1)<jupyter_output><empty_output><jupyter_text>単語ベクトルからテキストの特徴量作成（出現する単語のベクトルの平均をテキストの特徴量とする）<jupyter_code>Xs = np.array([list(np.mean(w2v[[word for word in sentence if word in w2v]], axis=0)) for sentence in sentences])
Xs.shape<jupyter_output><empty_output><jupyter_text>訓練データと検証データに分割<jupyter_code>X_train, X_val, y_train, y_val = train_test_split(
  Xs, Ys, train_size = 0.8
)<jupyter_output><empty_output><jupyter_text>学習・評価 （学習率を変えて6パターンで実験）<jupyter_code>learning_rates = [0.01, 0.03, 0.1, 0.3, 1.0, 3.0]
for lr in learning_rates:
  model = LogisticRegression(C=lr, max_iter=1000)
  model.fit(X_train, y_train)
  print ("Accuracy for C=%s: %s" % (lr, accuracy_score(y_val, model.predict(X_val))))<jupyter_output><empty_output><jupyter_text>単語のベクトルを可視化<jupyter_code>limit = 1000  # プロットする単語数
embeddings = []
for word in w2v.wv.vocab:
  embeddings.append(w2v[word])
    
tsne = TSNE(n_components=2)
new_values = tsne.fit_transform(embeddings[:limit])

Xs = [coords[0] for coords in new_values]
Ys = [coords[1] for coords in new_values]

labels = list(w2v.wv.vocab)[:limit]
plt.figure(figsize=(20, 20)) 
for i in range(limit):
  plt.scatter(Xs[i],Ys[i])
  plt.annotate(labels[i], xy=(Xs[i], Ys[i]), xytext=(5, 2),
                 textcoords='offset points', ha='right', va='bottom')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13968162914097232, 'fraction_numerical': 0.02452316076294278, 'mean_word_length': 7.918158567774936, 'pattern_counts': {'":': 0, '<': 98, '<?xml version=': 0, '>': 97, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '24', 'dir': 'jupyter-structured-clean-dedup', 'id': '552721', 'license': 'MIT', 'n_tokens_mistral': 3696, 'n_tokens_neox': 3530, 'n_words': 392, 'path': 'nlp_sentiment_analysis.ipynb', 'repo_name': 'nsstnaka/machine_learning_handson', 'size': '6973'}