LSTM 기반 텍스트 감정 분석(Keras 버전)

I. 소개

텍스트 감정 분석은 자연어 처리에 있어 매우 기본적인 작업으로, 우리 삶의 많은 부분이 이 범주에 속합니다. 예를 들어 쇼핑 웹사이트에 대한 긍정적인 리뷰와 부정적인 리뷰, 스팸 필터링, 스팸 SMS 필터링 등이 있습니다. 텍스트 감정 분석을 위한 구현 방법도 다양하며 전통적인 Naive Bayes와 의사결정 트리를 사용할 수도 있고, 딥러닝 기반의 CNN, RNN 등을 사용할 수도 있습니다. 이 기사에서는 IMDB 영화 리뷰 데이터 세트를 사용하여 RNN 네트워크를 기반으로 텍스트 감정 분석을 구현합니다.

2. 데이터 처리

2.1 데이터 미리보기

먼저 해당 데이터( http://ai.stanford.edu/~amaas/data/sentiment/ )를 다운로드해야 합니다 . 아래 사진의 위치를 ​​클릭하세요.

이미지.png
데이터의 압축을 풀면 다음과 같은 디렉터리 구조가 얻어집니다.

- aclImdb
    - test
        - neg
        - pos
        - labeledBow.feat
        - urls_neg.txt
        - urls_pos.txt
    - train
        - neg
        - pos

영화 리뷰 데이터 세트인데, neg에 포함된 리뷰는 낮은 평점을 받은 리뷰이고, pos에 포함된 리뷰는 높은 평점을 받은 리뷰입니다. 우리에게 필요한 데이터는 테스트에서는 neg와 pos이고, train에서는 neg와 pos입니다(neg는 음수를 의미하고 pos는 양수를 의미함). 아래에서 처리를 시작하겠습니다.

2.2 가져오기 모듈

코드 작성을 시작하기 전에 관련 모듈을 가져와야 합니다.

import os
import re
import string

import numpy as np
from tensorflow.keras import layers
from tensorflow.keras.models import Model

내 환경은 tensorflow2.7입니다. tensorflow의 일부 버전은 다음과 같이 가져옵니다.

from keras import layers
from keras.models import Model

사용자의 환경에 따라 교체할 수 있습니다.

2.3 데이터 읽기

주석 파일을 읽으려면 여기에 함수를 정의하십시오.

def load_data(data_dir=r'/home/zack/Files/datasets/aclImdb/train'):
    """
    data_dir:train的目录或test的目录
    输出:
        X:评论的字符串列表
        y:标签列表(0,1)
    """
    classes = ['pos', 'neg']
    X, y = [], []
    for idx, cls in enumerate(classes):
        # 拼接某个类别的目录
        cls_path = os.path.join(data_dir, cls)
        for file in os.listdir(cls_path):
            # 拼接单个文件的目录
            file_path = os.path.join(cls_path, file)
            with open(file_path, encoding='utf-8') as f:
                X.append(f.read().strip())
                y.append(idx)
    return X, np.array(y)

위 함수는 나중에 처리할 수 있도록 두 개의 목록을 가져옵니다.

2.4 어휘력 구축

리뷰 텍스트를 얻은 후에는 어휘를 구축해야 합니다. 즉, 나타나는 모든 단어의 수를 세고 각 단어에 숫자를 부여합니다(또한 일부를 계산하고 초과분을 표시하기 위해 unk를 사용할 수도 있습니다). 词到id的映射이 단계에서는 합계를 얻습니다 id到词的映射. 구체적인 코드는 다음과 같습니다.

def build_vocabulary(sentences):
    """
    sentences:文本列表
    输出:
        word2idx:词到id的映射
        idx2word:id到词的映射
    """
    word2idx = {
    
    }
    idx2word = {
    
    }
    # 获取标点符号及空字符
    punctuations = string.punctuation + "\t\n "
    for sentence in sentences:
        # 分词
        words = re.split(f'[{
      
      punctuations}]', sentence.lower())
        for word in words:
            # 如果是新词
            if word not in word2idx:
                word2idx[word] = len(word2idx)
                idx2word[len(word2idx) - 1] = word
    return word2idx, idx2word

위의 두 매핑을 사용하면 문장을 id 시퀀스로 변환하거나 id 시퀀스를 문장으로 변환할 수 있는데, 이 경우 전자만 필요합니다.

2.5 단어 토큰화(tokenize)

우리 모델에는 고정 길이 데이터가 필요하기 때문에 토큰화 중에 문장 길이를 제한합니다.

def tokenize(sentences, max_len=300):
    """
    sentences:文本列表
    tokens:标记化后的id矩阵,形状为(句子数量, 句子长度)
    """
    # 生产一个形状为(句子数量, 句子长度)的矩阵,默认用空字符的id填充,类型必须为int
    tokens = np.full((len(sentences), max_len),
                     fill_value=word2idx[''], dtype=np.int32)
    punctuations = string.punctuation + "\t\n "
    for row, sentence in enumerate(sentences):
        # 分词
        words = re.split(f'[{
      
      punctuations}]', sentence.lower())
        for col, word in enumerate(words):
            if col >= max_len:
                break
            # 把第row个句子的第col个词转成id
            tokens[row, col] = word2idx.get(word, word2idx[''])
    return tokens

이 함수를 사용하여 문장 목록을 ndarray로 변환합니다.

3. 모델 구축 및 학습

3.1 모델 구축

이를 구현하기 위해 RNN을 사용하였으며, 모델 구조는 다음과 같습니다.

임베딩.png

아래에서는 프로그램을 사용하여 이 모델을 구현합니다.

def build_model():
    vocab_size = len(word2idx)
    # 构建模型
    inputs = layers.Input(shape=max_len)
    x = layers.Embedding(vocab_size, embedding_dim)(inputs)
    x = layers.LSTM(64)(x)
    outputs = layers.Dense(1, activation='sigmoid')(x)
    model = Model(inputs, outputs)
    return model

다음은 참고할 몇 가지 사항입니다.

  1. Embedding 레이어의 입력은 (batch_size,max_len)이고 출력은 (batch_size,max_len, embedding_dim)입니다.
  2. LSTM 레이어의 입력은 (batch_size, max_len, embedding_dim), 출력은 (batch_size, 유닛)이며, 단위는 LSTM 생성 시 전달된 값입니다.

3.2 훈련 모델

이제 이전에 구현한 방법을 사용하여 모델 학습을 시작할 수 있습니다. 코드는 다음과 같습니다.

# 超参数
max_len = 200
batch_size = 64
embedding_dim = 256
# 加载数据
X_train, y_train = load_data()
X_test, y_test = load_data('/home/zack/Files/datasets/aclImdb/test')
X = X_train + X_test
word2idx, idx2word = build_vocabulary(X)
X_train = tokenize(X_train, max_len=max_len)
X_test = tokenize(X_test, max_len=max_len)

# 构建模型
model = build_model()
model.summary()
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=[X_test, y_test],
)

20번의 학습 후 학습 세트 정확도는 99%에 도달할 수 있고 검증 세트 정확도는 약 80%입니다. 모델에는 어느 정도의 과적합이 있으며, 이는 모델 구조를 수정하거나 하이퍼파라미터를 조정하여 최적화할 수 있습니다.

예를 들어 max_len 크기 수정, 사전 훈련된 단어 임베딩 사용, RNN 단위 크기 수정, embedding_dim 크기 수정 등이 있습니다. BatchNormalization 및 Dropout 레이어를 추가할 수도 있습니다.

4. 사용 모델

모델이 훈련된 후 예측을 사용하여 예측할 수 있습니다. 예측의 입력은 임베딩 레이어의 입력과 동일합니다.

while True:
    sentence = input("请输入句子:")
    tokenized = tokenize([sentence], max_len)
    output = model.predict(tokenized)
    print('消极' if output[0][0] >= 0.5 else '积极')

다음은 몇 가지 테스트 결과입니다.

请输入句子:this is a bad movie
消极
请输入句子:this is a good movie
积极
请输入句子:i like this movie very much
积极
请输入句子:i hate this movie very much
积极
请输入句子:i will never see this movie again
积极

훈련 샘플은 일반적으로 긴 텍스트이고 현재 테스트는 짧은 텍스트이기 때문에 효과가 특별히 이상적이지는 않습니다.

추천

출처blog.csdn.net/ZackSock/article/details/129436420