【Kaggle入門ー自然言語処理】Natural Language Processing with Disaster Tweets-EDA、データ前処理、モデル作成、提出まで

※アフィリエイト広告を利用しています。

はじめに

Kaggleは、英語のページしかありません。そこで、日本語で読みたい方向けに記事を作成しました。

Natural Language Processing with Disaster Tweetsの概要の説明が前の記事で済みました。

次に実際にGoogle Colabを動かしてみましょう(https://www.kaggle.com/code/shahules/basic-eda-cleaning-and-gloveを参考にして説明)。

データのインポート

必要なものをインポートします。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns 

import warnings
warnings.filterwarnings('ignore')
%matplotlib inline

ドライブをマウントしてください。

from google.colab import drive
drive.mount('/content/drive')

そして、データをインポートします。

train = pd.read_csv('/content/drive/MyDrive/kaggle/Natual Language/train.csv')
test = pd.read_csv('/content/drive/MyDrive/kaggle/Natual Language/test.csv')
sub = pd.read_csv('/content/drive/MyDrive/kaggle/Natual Language/sample_submission.csv')

注意‘/kaggle/Digit Recognizer/train.csv’←の部分はドライブのフォルダ構成によって内容が異なるので注意してください。

EDA

traintarget可視化します。

t = train.target.value_counts()
sns.barplot(t.index,t)

target0(災害なし)ツイート1(災害のツイート)よりも多いことがわかります。

次に、ツイートの文字数可視化します。

まず、target1text文字数を見てみます。

train_len_1 = train[train['target']==1]['text'].str.len()
train_len_1

そして、target0text文字数を見てみます。

train_len_0 = train[train['target']==0]['text'].str.len()
train_len_0

text文字数ヒストグラムを見てみます。

fig,(ax1,ax2) = plt.subplots(1,2,figsize=(10,5))
ax1.hist(train_len_1,color='red')
ax1.set_title('disaster_tweets')
ax2.hist(train_len_0,color='blue')
ax2.set_title('Not disaster_tweets')
fig.suptitle('Characters in tweets')
plt.show()

ツイートのtext単語数可視化します。

まず、target1text単語数を見てみます。

train_len_1 = train[train['target']==1]['text'].str.split().map(lambda x: len(x))
train_len_1

そして、targettext単語数を見てみます。

train_len_0 = train[train['target']==0]['text'].str.split().map(lambda x: len(x))
train_len_0

text単語数ヒストグラムを見てみます。

fig,(ax1,ax2) = plt.subplots(1,2,figsize=(10,5))
ax1.hist(train_len_1,color='red')
ax1.set_title('disaster tweets')
ax2.hist(train_len_0,color='blue')
ax2.set_title('Not disaster tweets')
fig.suptitle('Words in a tweet')
plt.show()

ツイート内の各単語の平均の文字数可視化します。

まず、target1text各単語の文字数を見てみます。

word_1 = train[train['target']==1]['text'].str.split().apply(lambda x : [len(i) for i in x])
word_1

そして、target0text各単語の文字数を見てみます。

word_0 = train[train['target']==0]['text'].str.split().apply(lambda x : [len(i) for i in x])
word_0

text各単語の平均の文字数ヒストグラムを見てみます。

fig,(ax1,ax2) = plt.subplots(1,2,figsize=(10,5))
sns.distplot(word_1.map(lambda x : np.mean(x)),ax=ax1,color='red')
ax1.set_title('disaster')
sns.distplot(word_0.map(lambda x : np.mean(x)),ax=ax2,color='blue')
ax2.set_title('Not disaster')
fig.suptitle('Average word length in each tweet')

次は、text単語ごとに区切って分析してみます。

単語ごとのリスト化した関数を作成します。

def create_corpus(target):
    corpus = []

    for x in train[train['target'] ==target]['text'].str.split():
        for i in x:
            corpus.append(i)
    return corpus

target0text単語ごとに区切って、表示します。

corpus = create_corpus(0)
corpus

自然言語処理解析精度上げるために、不要な記号単語等をデータセットから除去する必要があります。

そこで、ツイートに含まれるストップワードを抽出します。

ストップワードとは、情報量が少ない単語出現頻度が少ない単語タスクに関係が無い単語です。日本語でいうと、「は」、「です」等です。

from nltk.corpus import stopwords
import nltk
nltk.download('stopwords')
stop = set(stopwords.words('english'))

from collections import defaultdict

dic = defaultdict(int)
for word in corpus:
    if word in stop:
        dic[word]+=1

なお、defaultdictは、どのようなキーがきても指定したデフォルト値を初期値として要素を追加します。

dicストップワードその数格納しているので、見てみます。

上位10ストップワードを表示します。

top = sorted(dic.items(),key = lambda x:x[1],reverse=True)[:10]
top

こちらを可視化してみます。

x,y = zip(*top)
plt.bar(x,y)

次は、target1textストップワードを可視化してみます。

corpus = create_corpus(1)

dic = defaultdict(int)
for word in corpus:
    if word in stop:
        dic[word]+=1

top = sorted(dic.items(), key = lambda x : x[1],reverse=True)[:10]

x,y = zip(*top)
plt.bar(x,y)

次は、句読点を見てみます。

target1text句読点を確認します。

corpus = create_corpus(1)

dic = defaultdict(int)
import string
special = string.punctuation
for i in (corpus):
    if i in special:
        dic[i]+=1

可視化してみます。

plt.figure(figsize=(10,5))
x,y = zip(*dic.items())
plt.bar(x,y,color='red')

次は、target0text句読点を可視化します。

plt.figure(figsize=(10,5))
corpus = create_corpus(0)

dic = defaultdict(int)
for i in (corpus):
    if i in special:
        dic[i]+=1

x,y = zip(*dic.items())
plt.bar(x,y,color='blue')

さて、今度は、単語上位40を可視化(ストップワードを除く)してみます。

from collections import  Counter
counter = Counter(corpus)
most = counter.most_common()
x=[]
y=[]
for word,count in most[:40]:
    if (word not in stop):
        x.append(word)
        y.append(count)
sns.barplot(x=x,y=y)

N-gram解析をしてみます。 

N-gram解析とは、テキストをN文字単位分解して解析することをいいます。

たとえば、abcdefgというテキストがあるとき、N=2とするとabbccddeef, fg2文字づつ分解して解析します。

from sklearn.feature_extraction.text import CountVectorizer
def get_top_tweet_bigrams(corpus,n=None):
    vec = CountVectorizer(ngram_range=(2,2)).fit(corpus)
    bag_of_words = vec.transform(corpus)
    sum_words = bag_of_words.sum(axis=0)
    words_freq = [(word,sum_words[0,idx]) for word,idx in vec.vocabulary_.items() ]
    words_freq = sorted(words_freq,key = lambda x:x[1],reverse=True)
    return words_freq[:n]

traintextを可視化します。

plt.figure(figsize=(10,5))
top_tweet_bigrams = get_top_tweet_bigrams(train['text'])[:10]
x,y = map(list,zip(*top_tweet_bigrams))
sns.barplot(x=x,y=y)

データ前処理~スペルチェック、句読点の除去、htmlタグや絵文字の除去

まずは、URL除去します。

example = "New competition launched :https://www.kaggle.com/c/nlp-getting-started"
import re
def remove_URL(text):
    url = re.compile(r'https?://\S+|www\.\S+')
    return url.sub(r'',text)

remove_URL(example)
df_train['text'] = df_train['text'].apply(lambda x : remove_URL(x))
df_train

次は、htmlタグ除去します。

example = """<div>
<h1>Real or Fake</h1>
<p>Kaggle </p>
<a href="https://www.kaggle.com/c/nlp-getting-started">getting started</a>
</div>"""
def remove_html(text):
    html=re.compile(r'<.*?>')
    return html.sub(r'',text)
print(remove_html(example))
df_train['text'] = df_train['text'].apply(lambda x : remove_html(x))
df_train

絵文字除去します(https://gist.github.com/slowkow/7a7f61f495e3dbb7e3d767f97bd7304b)。

def remove_emoji(text):
    emoji_pattern = re.compile("["
                           u"\U0001F600-\U0001F64F"  
                           u"\U0001F300-\U0001F5FF"  
                           u"\U0001F680-\U0001F6FF" 
                           u"\U0001F1E0-\U0001F1FF"  
                           u"\U00002702-\U000027B0"
                           u"\U000024C2-\U0001F251"
                           "]+", flags=re.UNICODE)
    return emoji_pattern.sub(r'', text)

remove_emoji("Omg another Earthquake 😔😔")
df_train['text'] = df_train['text'].apply(lambda x: remove_emoji(x))

句読点除去する。

def remove_punct(text):
    table = str.maketrans('','',string.punctuation)
    return text.translate(table)

example="I am a #king"
print(remove_punct(example))
df_train['text'] = df_train['text'].apply(lambda x : remove_punct(x))

スペルチェック(今回はないので、#(コメントアウト)しています。)

!pip install pyspellchecker
from spellchecker import SpellChecker

spell = SpellChecker()
def correct_spellings(text):
    corrected_text = []
    misspelled_words = spell.unknown(text.split())
    for word in text.split():
        if word in misspelled_words:
            corrected_text.append(spell.correction(word))
        else:
            corrected_text.append(word)
    return " ".join(corrected_text)
text = 'corect me plese'
correct_spellings(text)
#df_train['text'] = df_train['text'].apply(lambda x : correct_spellings(x))

次は、GloVeによるベクトル化します。

GloVeは、テキストのベクトル表現を取得するための教師なし学習アルゴリズムです。

GloVe50D、100D、200Dと3種類ありますが、ここでは100Dで試してみます。

import nltk
nltk.download('punkt')
from nltk.tokenize import word_tokenize
from tqdm import tqdm

def create_corpus(df_train):
    corpus = []
    for train in tqdm(df_train['text']):
        words = [word.lower() for word in word_tokenize(train) if((word.isalpha()==1) & (word not in stop))]
        corpus.append(words)
    return corpus
corpus = create_corpus(df_train)

glove.6B.100d.txtは、以下からダウンロードしてください。https://www.kaggle.com/datasets/rtatman/glove-global-vectors-for-word-representation

embedding_dict={}
with open('/content/drive/MyDrive/kaggle/Natual Language/glove.6B.100d.txt','r') as f:
    for line in f:
        values=line.split()
        word=values[0]
        vectors=np.asarray(values[1:],'float32')
        embedding_dict[word]=vectors
f.close()

注意‘/content/drive/MyDrive/kaggle/Natual Language/glove.6B.100d.txt’←の部分はドライブのフォルダ構成によって内容が異なるので注意してください。

from tensorflow import keras
from keras.preprocessing.text import Tokenizer
MAX_LEN=50
tokenizer_obj=Tokenizer()
tokenizer_obj.fit_on_texts(corpus)
sequences=tokenizer_obj.texts_to_sequences(corpus)

tweet_pad = keras.utils.pad_sequences(sequences,maxlen=MAX_LEN,truncating='post',padding='post')
word_index = tokenizer_obj.word_index
print('Number of unique words:',len(word_index))
num_words=len(word_index)+1
embedding_matrix=np.zeros((num_words,100))

for word,i in tqdm(word_index.items()):
    if i > num_words:
        continue
    
    emb_vec=embedding_dict.get(word)
    if emb_vec is not None:
        embedding_matrix[i]=emb_vec

モデルの作成

from keras.models import Sequential
from keras.layers import Embedding,LSTM,Dense,SpatialDropout1D
from keras.initializers import Constant
from keras.optimizers import Adam

model=Sequential()

embedding=Embedding(num_words,100,embeddings_initializer=Constant(embedding_matrix),
                   input_length=MAX_LEN,trainable=False)

model.add(embedding)
model.add(SpatialDropout1D(0.2))
model.add(LSTM(64, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(1, activation='sigmoid'))


optimzer=Adam(learning_rate=1e-5)

model.compile(loss='binary_crossentropy',optimizer=optimzer,metrics=['accuracy'])
model.summary()
train_ = tweet_pad[:train.shape[0]]
test_ = tweet_pad[train.shape[0]:]

データを訓練用テスト用分割します。

from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test=train_test_split(train_,train['target'].values,test_size=0.15)
print('Shape of train',X_train.shape)
print("Shape of Validation ",X_test.shape)

訓練

history = model.fit(X_train,y_train,batch_size=4,epochs=15,validation_data=(X_test,y_test),verbose=2)

予測し、ファイル提出します。

y_pred = model.predict(test_)
y_pred = np.round(y_pred).astype(int).reshape(3263)
sub = pd.DataFrame({'id':sub['id'].values.tolist(),'target':y_pred})
sub.to_csv('sub.csv',index=False)

Kaggleで悩んだら

「Kaggle で勝つデータ分析の技術」

データ分析の最も有名なコンペティションプラットフォームであるKaggleにおけるテクニックや事例を、現時点で最新のものを整理した書籍。特徴量の作り方バリデーションパラメータチューニングなどについて、一般的な書籍ではあまり言及されないポイント等に解説。
>>【書評】Kaggleで勝つデータ分析の技術

以下の書籍は、Kaggleを始める方には本当にオススメの書籍です。Kaggleわからないこと悩んだことがあった方は、購入を検討してみください。

本だけでは物足りないという方は、動画のプラットフォームで学ぶこともオススメです。興味がございましたら、以下の無料オンライン説明会に参加してみてはいかがでしょうか。