[Project] Natural Language Processing with Disaster Tweets - Kaggle


|接頭辞|


専門科目データマイニングコースの2ヶ月間のプロジェクトとして、LSTMを利用して災害性ツイッター分類を選択しました.前学期はタイタニック分類予測問題をしましたが、教授はLSTMを利用する問題を処理させてくれました.これはケゲルがもたらした問題です.自然言語をバイナリ分類してみましたが、数字値(タイタニック号など)ではありません.

問題の定義
特定のツイッターデータを分析して、災害に関連しているかどうかを確認します.
n/a.ターゲット
本課題では,自然言語をどのように前処理するかを練習し,kerasでLSTMを用いたモデルを作成することを試みる.keras layerを構築する際には、Embeddingも使用します.LSTMを含む様々な分類手法を用いて作成したモデルでは、最も精度の高いモデルを選択し、精度と損失値を可視化します.

[目次]

  • ライブラリおよびデータ負荷
  • EDA
  • テキストプリプロセッシング
    - Mislabeled data
    - removing Functions
  • Import GloVe Embedding
  • TRAIN MODEL
  • 評価
  • |データの表示|


    Columns
  • id – a unique identifier for each tweet
  • text – the text of the tweet
  • location – 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")
  • では、赤い線はプローブ値を表し、キーワードとlocation列にプローブ値があります.モデル構築ではテキストとターゲットのみが使用されるため、位置の測定値は処理されません.
  • #결측치 비율
    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()
  • train setとtest setは、キーワードの0.8%と位置の33%が欠けています.
    トレーニングセットとテストセットとの間の測定値比率が近すぎるため,同じサンプルから得られる可能性が高い.
    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 data
    train 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
    最初の関数:
  • 歳のデータセットからすべての文を取得し、文のすべての単語にインデックス番号を割り当てて、インタビュー年齢
  • を定義します.
  • の3つのデータセットは、チャットツールの語彙に含まれるすべての単語で、
  • です.
    2番目の関数:
  • チャットツールを使用して、すべての文を代表文として符号化するインデックス番号配列
  • .
  • 文を0に塗りつぶし、トレーニングデータセットの最長文と同じサイズ(インデックス0を保持する)
  • にします.
    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),
    ]
  • モデルが最適な状態を超えないように、2つ以上のフォークリフトに障害が発生した場合に学習速度を低下させてみました.
    また、処理時間を節約するため、5回の損失が発生しなければ処理を停止する.
  • history = model.fit(
        tf_data, 
        validation_data = tf_val_data,
        epochs = 30,
        callbacks = callbacks
    )
  • epoch 30の結果
    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))
  • f1 score 0.90
  • #모델이 훈련 중일 때 에포크당 생성된 로스값과 정확도 시각화
    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を使用することが望ましい.