[Project] Natural Language Processing with Disaster Tweets - Kaggle
17113 ワード
|接頭辞|
専門科目データマイニングコースの2ヶ月間のプロジェクトとして、LSTMを利用して災害性ツイッター分類を選択しました.前学期はタイタニック分類予測問題をしましたが、教授はLSTMを利用する問題を処理させてくれました.これはケゲルがもたらした問題です.自然言語をバイナリ分類してみましたが、数字値(タイタニック号など)ではありません.
問題の定義
特定のツイッターデータを分析して、災害に関連しているかどうかを確認します.
n/a.ターゲット
本課題では,自然言語をどのように前処理するかを練習し,kerasでLSTMを用いたモデルを作成することを試みる.keras layerを構築する際には、Embeddingも使用します.LSTMを含む様々な分類手法を用いて作成したモデルでは、最も精度の高いモデルを選択し、精度と損失値を可視化します.
[目次]
- Mislabeled data
- removing Functions
|データの表示|
Columns
id
– a unique identifier for each tweettext
– the text of the tweetlocation
– the location the tweet was sent from (may be blank)keyword
- a particular keyword from the tweet (may be blank)target
– in train.csv only, this denotes whether a tweet is about a real - disaster (1) or not (0)全部で5つのコラムからなり、idは各ツイッターに記録された識別子であり、テキストはツイッターテキストであり、位置はツイッターの送信場所であり、キーワードはツイッターの特定のキーワードであり、targetはツイッターが本当に災難であるかどうか(1)(0)である.
1.ライブラリ&データインポート #numpy, pandas, seaborn
import numpy as np
import pandas as pd
import seaborn as sns
#tesorflow
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding, LSTM, Dropout
from tensorflow.keras import optimizers
#nltk
import re
from nltk.corpus import stopwords
from nltk.stem import SnowballStemmer
#matplot
import matplotlib.pyplot as plt
pd.set_option('display.max_colwidth', -1)
# 값의 길이가 길어 ...으로 나오는 점을 한 행에 출력할 수 있는 개수를 지정하여 다 나오게 함
train_data = pd.read_csv('train.csv',
dtype={'text': str, 'target': np.int64}
)
len(train_data) #7613개의 train data
train_data['text'].head().values #head를 이용해 확인
test_data = pd.read_csv(
'test.csv',
usecols=['text', 'id'],
dtype={'text': str, 'id': str}
) #test data는 사용할 열을 지정해서 불러왔음
2. EDA #결측치 확인
sns.set(rc={'figure.figsize':(11,8)})
sns.heatmap(train_data.isnull(),yticklabels=False,cbar=False,cmap="coolwarm")
#numpy, pandas, seaborn
import numpy as np
import pandas as pd
import seaborn as sns
#tesorflow
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding, LSTM, Dropout
from tensorflow.keras import optimizers
#nltk
import re
from nltk.corpus import stopwords
from nltk.stem import SnowballStemmer
#matplot
import matplotlib.pyplot as plt
pd.set_option('display.max_colwidth', -1)
# 값의 길이가 길어 ...으로 나오는 점을 한 행에 출력할 수 있는 개수를 지정하여 다 나오게 함
train_data = pd.read_csv('train.csv',
dtype={'text': str, 'target': np.int64}
)
len(train_data) #7613개의 train data
train_data['text'].head().values #head를 이용해 확인
test_data = pd.read_csv(
'test.csv',
usecols=['text', 'id'],
dtype={'text': str, 'id': str}
) #test data는 사용할 열을 지정해서 불러왔음
#결측치 확인
sns.set(rc={'figure.figsize':(11,8)})
sns.heatmap(train_data.isnull(),yticklabels=False,cbar=False,cmap="coolwarm")
#결측치 비율
missing_cols = ['keyword', 'location']
fig, axes = plt.subplots(ncols=2, figsize=(17, 4), dpi=100)
sns.barplot(x=train_data[missing_cols].isnull().sum().index, y=train_data[missing_cols].isnull().sum().values, ax=axes[0])
sns.barplot(x=train_data[missing_cols].isnull().sum().index, y=train_data[missing_cols].isnull().sum().values, ax=axes[1])
axes[0].set_ylabel('Missing Value Count', size=15, labelpad=20)
axes[0].tick_params(axis='x', labelsize=15)
axes[0].tick_params(axis='y', labelsize=15)
axes[1].tick_params(axis='x', labelsize=15)
axes[1].tick_params(axis='y', labelsize=15)
axes[0].set_title('Training Set', fontsize=13)
axes[1].set_title('Test Set', fontsize=13)
plt.show()
トレーニングセットとテストセットとの間の測定値比率が近すぎるため,同じサンプルから得られる可能性が高い.
locationは自動生成ではなく、ユーザーが入力したため、featureとして使用できない一意の値が多すぎます.
幸いなことに、キーワード自体はフィーチャーとしてもテキストに追加された単語としても使用できます.トレーニングセットのすべての単一のキーワードはテストセットに存在し、キーワードでターゲットコードを使用することもできます.
plt.figure(figsize=(8,6))
colors = ["blue", "red"]
sns.countplot(x = 'target', data=train_data, palette=colors)
plt.title('Target Distributions \n (0: Non Disaster || 1: Disaster)', fontsize=20)
頻繁に出てくるキーワード上位20位
chains=train_data['keyword'].value_counts()[:20]
sns.barplot(x=chains,y=chains.index,palette='deep')
plt.title("Top 20 Keywords")
plt.xlabel("Count of Keywords")
キーワード上位20位は災害ツイッターと災害ツイッターではありません
disaster_keywords = train_data.loc[train_data["target"] == 1]["keyword"].value_counts()
nondisaster_keywords = train_data.loc[train_data["target"] == 0]["keyword"].value_counts()
fig, ax = plt.subplots(1,2, figsize=(20,8))
sns.barplot(y=disaster_keywords[0:20].index, x=disaster_keywords[0:20], orient='h', ax=ax[0], palette="Reds_d")
ax[0].set_title("Top 20 Keywords - Disaster Tweets")
ax[0].set_xlabel("Keyword Frequency")
sns.barplot(y=nondisaster_keywords[0:20].index, x=nondisaster_keywords[0:20], orient='h', ax=ax[1], palette="Blues_d")
ax[1].set_title("Top 20 Keywords - Non-Disaster Tweets")
ax[1].set_xlabel("Keyword Frequency")
plt.tight_layout()
plt.show()
キーワード内のターゲットのセミコロン
train_data['target_mean'] = train_data.groupby('keyword')['target'].transform('mean')
fig = plt.figure(figsize=(8, 72), dpi=100)
sns.countplot(y=train_data.sort_values(by='target_mean', ascending=False)['keyword'],
hue=train_data.sort_values(by='target_mean', ascending=False)['target'])
plt.tick_params(axis='x', labelsize=15)
plt.tick_params(axis='y', labelsize=12)
plt.legend(loc=1)
plt.title('Target Distribution in Keywords')
plt.show()
train_data.drop(columns=['target_mean'], inplace=True)
top_disaster_keyword = train_data.groupby('keyword').mean()['target'].sort_values(ascending = False).head(20)
top_nondisaster_keyword = train_data.groupby('keyword').mean()['target'].sort_values().head(20)
fig, ax = plt.subplots(1,2, figsize=(20,8))
sns.barplot(y=top_disaster_keyword[0:20].index, x=disaster_keywords[0:20], orient='h', ax=ax[0], palette="Reds_d")
ax[0].set_title("Top 20 Keywords - Highest used Disaster Keyword")
ax[0].set_xlabel("Keyword Frequency")
sns.barplot(y=top_nondisaster_keyword[0:20].index, x=top_nondisaster_keyword[0:20], orient='h', ax=ax[1], palette="Blues_d")
ax[1].set_title("Top 20 Keywords - Least used Non-Disaster Tweets")
ax[1].set_xlabel("Keyword Frequency")
plt.tight_layout()
plt.show()
似たようなキーワードがあるかどうかを比較することができます.出現回数が最も多い場所上位20位
locations = train_data["location"].value_counts()
plt.figure(figsize=(10,7))
sns.barplot(y=locations[0:20].index, x=locations[0:20], orient='h')
plt.title("Top 20 Locations")
plt.show()
3.テキスト前処理
train_data = pd.read_csv(
'train.csv',
usecols=['text', 'target'],
dtype={'text': str, 'target': np.int64}
) #사용할 열만 가져옴
Mislabeled datatrain setでは、ラベルエラーの行のtarget値がすべて置き換えられます.(出力結果を省略)
indices = [4415, 4400, 4399,4403,4397,4396, 4394,4414, 4393,4392,4404,4407,4420,4412,4408,4391,4405]
#행번호 배열을 만들어주고
train_data.loc[indices]
#train data에서 그 행만
train_data.loc[indices, 'target'] = 0
#target값을 올바른 값으로 고쳐줌
indices = [6840,6834,6837,6841,6816,6828,6831]
train_data.loc[indices]
train_data.loc[indices, 'target'] = 0
indices = [3913,3914,3936,3921,3941,3937,3938,3136,3133,3930,3933,3924,3917]
train_data.loc[indices]
train_data.loc[indices, 'target'] = 1
indices = [246,270,266,259,253,251,250,271]
train_data.loc[indices]
train_data.loc[indices, 'target'] = 0
indices = [6119,6122,6123,6131,6160,6166,6167,6172,6212,6221,6230,6091,6108]
train_data.loc[indices]
train_data.loc[indices, 'target'] = 0
indices = [7435,7460,7464,7466,7469,7475,7489,7495,7500,7525,7552,7572,7591,7599]
train_data.loc[indices]
train_data.loc[indices, 'target'] = 0
removing Functionsテキストを処理するために純粋な関数を用いた.
def cleaned(text):
text = re.sub(r"\n","",text)
text = text.lower()
text = re.sub(r"\d","",text) #Remove digits
text = re.sub(r'[^\x00-\x7f]',r' ',text) # remove non-ascii
text = re.sub(r'[^\w\s]','',text) #Remove punctuation
text = re.sub(r'http\S+|www.\S+', '', text) #Remove http
return text
train_data['text'] = train_data['text'].apply(lambda x : cleaned(x))
#split train data
val_data = train_data.tail(1500)
train_data = train_data.head(6113)
# 토큰화
def define_tokenizer(train_sentences, val_sentences, test_sentences):
sentences = pd.concat([train_sentences, val_sentences, test_sentences])
# 토크나이저는 모든 고유 단어에 숫자 인덱스를 할당하여 모델이 범주 값처럼 취급할 수 있도록 합니다.
tokenizer = tf.keras.preprocessing.text.Tokenizer()
tokenizer.fit_on_texts(sentences)-
return tokenizer
def encode(sentences, tokenizer):
encoded_sentences = tokenizer.texts_to_sequences(sentences)
encoded_sentences = tf.keras.preprocessing.sequence.pad_sequences(encoded_sentences, padding='post')
return encoded_sentences
最初の関数:2番目の関数:
tokenizer = define_tokenizer(train_data['text'], val_data['text'], test_data['text'])
encoded_sentences = encode(train_data['text'], tokenizer)
val_encoded_sentences = encode(val_data['text'], tokenizer)
encoded_test_sentences = encode(test_data['text'], tokenizer)
#토크나이저 구성을 파이썬 사전으로 반환하는지 확인
print('Lower: ', tokenizer.get_config()['lower'])
print('Split: ', tokenizer.get_config()['split'])
print('Filters: ', tokenizer.get_config()['filters'])
4. Import GloVe Embedding
Global Vectors for Word Representationは、数百万の英語Wordに対して事前トレーニングを行うことができる事前トレーニングのEmbeddingです.次に、GloVeを使用してEmbeddingレイヤを配置し、結果を表示します.
GloVeダウンロードリンク
#다운로드한 glove 사용 위해 마운트
from google.colab import drive
drive.mount('/gdrive', force_remount=True)
embedding_dict = {}
with open('/gdrive/My Drive/Colab Notebooks/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()
#토큰라이저와 임베딩의 인코딩을 동기화하기 위해 임베딩의 인코딩된 단어를 토큰라이저의 인코딩으로 업데이트
num_words = len(tokenizer.word_index) + 1
embedding_matrix = np.zeros((num_words, 100))
for word, i in tokenizer.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
tf_data = tf.data.Dataset.from_tensor_slices((encoded_sentences, train_data['target'].values))
def pipeline(tf_data, buffer_size=100, batch_size=32):
tf_data = tf_data.shuffle(buffer_size)
tf_data = tf_data.prefetch(tf.data.experimental.AUTOTUNE)
tf_data = tf_data.padded_batch(batch_size, padded_shapes=([None],[]))
return tf_data
tf_data = pipeline(tf_data, buffer_size=1000, batch_size=32)
print(tf_data)
tf_val_data = tf.data.Dataset.from_tensor_slices((val_encoded_sentences, val_data['target'].values))
def val_pipeline(tf_data, batch_size=1):
tf_data = tf_data.prefetch(tf.data.experimental.AUTOTUNE)
tf_data = tf_data.padded_batch(batch_size, padded_shapes=([None],[]))
return tf_data
tf_val_data = val_pipeline(tf_val_data, batch_size=len(val_data))
print(tf_val_data)
5.TRAIN MODEL
デザインケラスロムベディン
embedding = tf.keras.layers.Embedding(
len(tokenizer.word_index) + 1,
100,
embeddings_initializer = tf.keras.initializers.Constant(embedding_matrix),
trainable = True
)
model = tf.keras.Sequential([
embedding,
tf.keras.layers.SpatialDropout1D(0.2),
tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(128, dropout=0.2, recurrent_dropout=0.2)),
tf.keras.layers.Dense(1, activation='sigmoid')
])
次に,訓練関数(adam)と損失関数(log loss)を定義するモデルをコンパイルする.フォークリフトでモデルの精度を出力するための公製パラメータも追加されました. model.compile(
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
optimizer=tf.keras.optimizers.Adam(0.0001),
metrics=['accuracy', 'Precision', 'Recall']
)
callbacks = [
tf.keras.callbacks.ReduceLROnPlateau(monitor='loss', patience=2, verbose=1),
tf.keras.callbacks.EarlyStopping(monitor='loss', patience=5, verbose=1),
]
また、処理時間を節約するため、5回の損失が発生しなければ処理を停止する.
history = model.fit(
tf_data,
validation_data = tf_val_data,
epochs = 30,
callbacks = callbacks
)
train setの精度は90.94%であった
valid setの精度は92.40%
6.評価
F1 Score
metrics = model.evaluate(tf_val_data)
precision = metrics[2]
recall = metrics[3]
f1 = 2 * (precision * recall) / (precision + recall)
print('F1 score: ' + str(f1))
#모델이 훈련 중일 때 에포크당 생성된 로스값과 정확도 시각화
fig, axs = plt.subplots(1, 2, figsize=(20, 5))
axs[0].set_title('Loss')
axs[0].plot(history.history['loss'], label='train')
axs[0].plot(history.history['val_loss'], label='val')
axs[0].legend()
axs[1].set_title('Accuracy')
axs[1].plot(history.history['accuracy'], label='train')
axs[1].plot(history.history['val_accuracy'], label='val')
axs[1].legend()
Google Collabを使用する場合、epoch回数を50に設定しますが、時間がかかりすぎてコードを変更するたびにKerasが呼び出されるのが面倒です.教授が帰る時間が長すぎて、epochを減らす方法しかないと思いますが、教授はGoogle ColabがJupyterNotebookを使うか、Colabを使うほうがGoogle Colabを使うよりも料金を払うほうが速いと言っています.次の分析から、チームプロジェクトでは共有しやすい実験室を使用し、個人ではJupyterを使用することが望ましい.
Reference
この問題について([Project] Natural Language Processing with Disaster Tweets - Kaggle), 我々は、より多くの情報をここで見つけました https://velog.io/@qtly_u/Project-Natural-Language-Processing-with-Disaster-Tweets-Kaggleテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol