1. 概要#
感情分析は自然言語処理における一般的なケースであり、主に言語が表現する積極的または消極的な感情を判断することです。以前、云帆氏が書いた 《自然言語処理による定量分析》 を読んで非常に勉強になり、最近自然言語処理のプロジェクトにしばらく関わっていたこともあり、この記事は自然言語処理における感情分析のトピックに関する簡単な心得のまとめとします。
この記事では、云帆氏が使用した Keras フレームワークを引き続き使用し、神経ネットワークは Embedding+LSTM または Embedding+CNN+Pooling を主に用います。(この記事では自然言語処理特有の Embedding についていくつかの説明を重点的に行います。)
訓練データセットと検証データセットは Sarcasm_Headlines_Dataset(Misra,Rishabh and Arora, Prahal,2019)を使用します。
このデータは記事のタイトルを皮肉的または非皮肉的に分類し、具体的には 3 つの属性を含みます(この記事では主に前 2 つの属性を使用します):
1)is_sarcastic: レコードが皮肉的であれば 1、そうでなければ 0
2)headline: ニュース記事の見出し
3)article_link: 元のニュース記事へのリンク。補足データを収集するのに役立ちます
事前訓練された単語ベクトルデータは GloVe(Jeffrey Pennington, Richard Socher, and Christopher D. Manning,2014)を使用します。この単語ベクトルは 2014 年の英語ウィキペディアで訓練されており、400k の異なる単語があり、各単語は 100 次元のベクトルで表されます。GloVe 以外にも、word2vec-apiは多くの事前訓練された単語ベクトルライブラリを提供しています。中国語単語ベクトルも多くの中国語に基づく事前訓練された単語ベクトルライブラリを提供しています。
2. データ取得#
2.1 ファイルからデータを取得#
def parse_data(file):
for l in open(file,'r'):
yield json.loads(l)
data = list(parse_data('./Sarcasm_Headlines_Dataset_v2.json'))
2.2 データを訓練セットと検証セットに分割#
X = []
y = []
for s in data:
X.append(s['headline'])
y.append(s['is_sarcastic'])
訓練セット(検証セットを含む):テストセット = 9:1#
trainval_size = int(len(X) * train_ratio)
訓練セットと検証セット#
X_trainval = X[_size]
y_trainval = y[_size]
訓練セット:検証セット = 8:2#
X_train, X_valid,y_train, y_valid = train_test_split(X_trainval,
y_trainval,
test_size = valid_ratio,
random_state=12)
テストセット#
X_test = X[trainval_size:]
y_test = y[trainval_size:]
2.3 訓練および検証用のシーケンスを生成#
1、トークナイザーを生成
トークナイザーの主な役割は、すべての単語をインデックスに変換することです。
# トークナイザーを定義
tokenizer = Tokenizer(num_words = num_words ,oov_token=oov_token)
トークナイザーを訓練#
tokenizer.fit_on_texts(trainning_sentences)
tokenizer.word_index#
word_index = tokenizer.word_index
2、単語から構成された文をインデックスから構成されたシーケンスに変換
ここで、上記のコードの num_words パラメータの効果が現れます。各シーケンスには、必ずしもすべての単語にインデックスが対応するわけではなく、頻度が最も高い(Top num_words)の単語のみがインデックスに対応し、低頻度の単語はこの時点で無視されます。したがって、理論的には num_words<=word_index です。
sequences = token.texts_to_sequences(sentences)
3、シーケンスを整列
トークナイザーを使用して文を数字のシーケンスに変換しましたが、各文の単語数が異なるため、文の長さが異なり、シーケンスが整然としないシーケンスになります。この時点で、シーケンスを整列する処理が必要です。
シーケンスを整列する方法は、最長の文の長さ(または指定された長さ)に合わせて整列し、空白の部分を 0 で埋めます。0 を埋める方法は、前に 0 を埋めるか後ろに 0 を埋めるかの 2 つに分けられます。この記事では、16 ビットを文の最長の長さとして、不足する 16 ビットの文には前に 0 を埋める方法を使用してシーケンスを整列します。
paddedSequecnes = pad_sequences(sequences,
padding=padding,
maxlen=MAX_SEQUENCE_LENGTH,
truncating=trunc_type)
3. モデル構築および訓練#
3.1 モデル構築#
1、Embedding 層 + Flatten 層
これまでのところ、整数のシーケンスを得ました。このシーケンスは 2D テンソルで、その shape は(batch_size,sequence_length)(ここで batch_size は特に指定されていない場合、None です)。しかし、この方法では単語間の意味の関連性を捉えていません。
一般的な方法は、整数を One-hot に変換して高次元の疎ベクトルにし、さらに低次元の密なベクトルに変換することです。したがって、単語は 100D、300D、または 500D のベクトルで表され、単語間の類似度はベクトル間の内積で表されます。このプロセスは word2vec と呼ばれ、単語をベクトルに変換するプロセスです。これは Embedding () を使用して実現されます。
まず、シーケンス内の各単語を One-hot エンコーディングに変換し、0 と 1 だけの疎行列にします。このようにする目的は何でしょうか?答えは:計算が簡単だからです。疎行列で行列計算を行うときは、1 に対応する位置の数を掛け算して合計すればよいのです。以前の 0-9 の数字から構成された 1D リストは明らかに計算が遅くなります。
次に、疎行列 (m,n) を単語ベクトル行列 (n, output_dim) と掛け算し、新しい単語ベクトル行列(m, output_dim)を形成します。
最後に、すべての入力シーケンス内の整数を対応する単語ベクトルに置き換え、新しいテンソルを形成します。このテンソルは 3D テンソルに拡張され、その shape は(batch_size,sequence_length,output_dim)であり、output_dim は単語ベクトルの次元を指します。
自然言語処理にとって、Embedding 層は必須であり、必ず最初の層として配置する必要があります。詳細な Embedding の原理については以下を参照してください:
Embedding(num_words,
EMBEDDING_DIM,
input_length=MAX_SEQUENCE_LENGTH,
trainable=True),
Flatten(),
2、LSTM 層
LSTM 層は 1 層でも複数層でも構成できますが、複数層の場合は、最初の層のパラメータ return_sequences を True に設定する必要があります。
Bidirectional(LSTM(64,return_sequences=True)),
Bidirectional(LSTM(32)),
3、Conv1D 層 + GlobalMaxPooling1D 層
畳み込み層 + プーリング層。
Conv1D(128, 5, activation='relu'),
GlobalMaxPooling1D(),
3.2 事前訓練された単語ベクトルの導入#
再び Embedding 層に戻ります。これまでのコードでは、Keras の Embedding 層を使用する際に事前訓練された単語ベクトルを導入していませんでした。その単語ベクトルは最初にランダムに初期化され、その後訓練データの過程で自動的に訓練されます。
今、事前訓練された単語ベクトル GloVe を導入してみましょう。
まず、embedding_matrix を構築します。この matrix の次元は GloVe の次元と一致する必要があります。
次に、事前訓練された単語ベクトルの値を embedding_matrix にマッピングします。
最後に、Embedding 層で embedding_matrix を指定します。embeddings_initializer=[embeddings_matrix] を指定し、trainable=False(訓練の過程で単語ベクトルを更新しないことを示します)とします。
Embedding(num_words,
EMBEDDING_DIM,
embeddings_initializer=Constant(embedding_matrix),
input_length=MAX_SEQUENCE_LENGTH,
trainable=False),
3.3 モデル訓練#
異なるレイヤー設計を使用し、事前訓練された単語ベクトルの導入の有無に応じて、コンパイルと訓練を行います。
4. モデル予測#
レイヤー設計の違いや事前訓練された単語ベクトルの導入の有無に応じて、モデルの予測結果は以下の通りです:
case6 を例にとると、acc と loss の図示は以下の通りです:
上記から分かることは、
1)現在のパラメータ設定の前提の下で、CNN+Polling の設計は LSTM に劣らず、さらに良い結果を示しています。。。しかし、これは特に問題を示すものではなく、重要なのはパラメータの設計です。ただし、LSTM は確かにリソースを消費します。
2)事前訓練された単語ベクトルの導入は訓練時間を短縮できますが、モデルの優位性を保証するものではありません。
5. 中国語感情分析の応用#
これまで、私たちは英語を基に研究を行ってきました。では、中国語はどのように行うのでしょうか?
実際、中国語と英語の最大の違いは、英語の単語は自然に分割されているのに対し、中国語は連続していることです。したがって、中国語の場合、まず行うべきことはトークナイザーの前に中国語の分詞を行うことです。現在一般的に使用されている分詞方法、例えば jieba 分詞 は、この問題をうまく解決できます。
次に解決すべきは事前訓練された単語ベクトルです。上記の結果から、事前訓練された単語ベクトルを特徴として使用することが非常に効果的であることがわかります。したがって、できるだけ事前訓練された単語ベクトルを導入する必要があります。一般的に、事前訓練された単語ベクトルには 2 つの取得方法があります:1 つは他の人が訓練した百科データです。利点:多くの単語を含み、日常用語の意味に合致する;欠点:固有名詞が不足し、スペースを多く占有する;もう 1 つは自分で訓練することです。利点:固有名詞が含まれ、特定のタスクに対する意味がより正確;欠点:汎用性が低い。
意味認識に関しては、現在良好な Python ライブラリとして gensim があり、多くの単語ベクトルのインポートや訓練の方法が含まれています。
分詞と単語ベクトルライブラリの問題を解決した後、残りの考え方は基本的に同じです。
この記事で使用されているファイルの百度網盤ダウンロードリンクは以下の通りです:
リンク:https://pan.baidu.com/s/1jH\_4Vzj1kh4af6domUCObQ
提取码:cr0t